Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.app.cts;
     18 
     19 import static org.mockito.ArgumentMatchers.any;
     20 import static org.mockito.ArgumentMatchers.anyInt;
     21 import static org.mockito.ArgumentMatchers.eq;
     22 import static org.mockito.ArgumentMatchers.nullable;
     23 import static org.mockito.Mockito.atLeast;
     24 import static org.mockito.Mockito.mock;
     25 import static org.mockito.Mockito.never;
     26 import static org.mockito.Mockito.spy;
     27 import static org.mockito.Mockito.verify;
     28 
     29 import android.app.WallpaperColors;
     30 import android.app.WallpaperManager;
     31 import android.content.BroadcastReceiver;
     32 import android.content.Context;
     33 import android.content.Intent;
     34 import android.content.IntentFilter;
     35 import android.graphics.Bitmap;
     36 import android.graphics.Canvas;
     37 import android.graphics.Color;
     38 import android.graphics.Point;
     39 import android.os.Handler;
     40 import android.os.HandlerThread;
     41 import android.os.Looper;
     42 import android.support.test.InstrumentationRegistry;
     43 import android.support.test.runner.AndroidJUnit4;
     44 import android.util.Log;
     45 import android.view.Display;
     46 import android.view.WindowManager;
     47 
     48 import org.junit.Assert;
     49 import org.junit.Before;
     50 import org.junit.Test;
     51 import org.junit.runner.RunWith;
     52 import org.mockito.MockitoAnnotations;
     53 
     54 import android.app.stubs.R;
     55 
     56 import java.io.IOException;
     57 import java.util.ArrayList;
     58 import java.util.concurrent.CountDownLatch;
     59 import java.util.concurrent.TimeUnit;
     60 
     61 @RunWith(AndroidJUnit4.class)
     62 public class WallpaperManagerTest {
     63 
     64     private static final boolean DEBUG = false;
     65     private static final String TAG = "WallpaperManagerTest";
     66 
     67     private WallpaperManager mWallpaperManager;
     68     private Context mContext;
     69     private Handler mHandler;
     70 
     71     @Before
     72     public void setUp() throws Exception {
     73         MockitoAnnotations.initMocks(this);
     74         mContext = InstrumentationRegistry.getTargetContext();
     75         mWallpaperManager = WallpaperManager.getInstance(mContext);
     76         final HandlerThread handlerThread = new HandlerThread("TestCallbacks");
     77         handlerThread.start();
     78         mHandler = new Handler(handlerThread.getLooper());
     79     }
     80 
     81     @Test
     82     public void setBitmapTest() {
     83         Bitmap tmpWallpaper = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
     84         Canvas canvas = new Canvas(tmpWallpaper);
     85         canvas.drawColor(Color.RED);
     86 
     87         try {
     88             int which = WallpaperManager.FLAG_SYSTEM;
     89             mWallpaperManager.setBitmap(tmpWallpaper);
     90             int oldWallpaperId = mWallpaperManager.getWallpaperId(which);
     91             canvas.drawColor(Color.GREEN);
     92             mWallpaperManager.setBitmap(tmpWallpaper);
     93             int newWallpaperId = mWallpaperManager.getWallpaperId(which);
     94             Assert.assertNotEquals(oldWallpaperId, newWallpaperId);
     95         } catch (IOException e) {
     96             throw new RuntimeException(e);
     97         } finally {
     98             tmpWallpaper.recycle();
     99         }
    100     }
    101 
    102     @Test
    103     public void setResourceTest() {
    104         try {
    105             int which = WallpaperManager.FLAG_SYSTEM;
    106             int oldWallpaperId = mWallpaperManager.getWallpaperId(which);
    107             mWallpaperManager.setResource(R.drawable.robot);
    108             int newWallpaperId = mWallpaperManager.getWallpaperId(which);
    109             Assert.assertNotEquals(oldWallpaperId, newWallpaperId);
    110         } catch (IOException e) {
    111             throw new RuntimeException(e);
    112         }
    113     }
    114 
    115     @Test
    116     public void wallpaperChangedBroadcastTest() {
    117         Bitmap tmpWallpaper = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
    118         Canvas canvas = new Canvas(tmpWallpaper);
    119         canvas.drawColor(Color.BLACK);
    120 
    121         CountDownLatch latch = new CountDownLatch(1);
    122         mContext.registerReceiver(new BroadcastReceiver() {
    123             @Override
    124             public void onReceive(Context context, Intent intent) {
    125                 latch.countDown();
    126             }
    127         }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
    128 
    129         try {
    130             mWallpaperManager.setBitmap(tmpWallpaper);
    131 
    132             // Wait for up to 5 sec since this is an async call.
    133             // Should fail if Intent.ACTION_WALLPAPER_CHANGED isn't delivered.
    134             if (!latch.await(5, TimeUnit.SECONDS)) {
    135                 throw new AssertionError("Intent.ACTION_WALLPAPER_CHANGED not received.");
    136             }
    137         } catch (InterruptedException | IOException e) {
    138             throw new AssertionError("Intent.ACTION_WALLPAPER_CHANGED not received.");
    139         } finally {
    140             tmpWallpaper.recycle();
    141         }
    142     }
    143 
    144     @Test
    145     public void wallpaperClearBroadcastTest() {
    146         CountDownLatch latch = new CountDownLatch(1);
    147         mContext.registerReceiver(new BroadcastReceiver() {
    148             @Override
    149             public void onReceive(Context context, Intent intent) {
    150                 latch.countDown();
    151             }
    152         }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
    153 
    154         try {
    155             mWallpaperManager.clear(WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM);
    156 
    157             // Wait for 5 sec since this is an async call.
    158             // Should fail if Intent.ACTION_WALLPAPER_CHANGED isn't delivered.
    159             if (!latch.await(5, TimeUnit.SECONDS)) {
    160                 throw new AssertionError("Intent.ACTION_WALLPAPER_CHANGED not received.");
    161             }
    162         } catch (InterruptedException | IOException e) {
    163             throw new AssertionError(e);
    164         }
    165     }
    166 
    167     @Test
    168     public void invokeOnColorsChangedListenerTest_systemOnly() {
    169         int both = WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM;
    170         // Expect both since the first step is to migrate the current wallpaper
    171         // to the lock screen.
    172         verifyColorListenerInvoked(WallpaperManager.FLAG_SYSTEM, both);
    173     }
    174 
    175     @Test
    176     public void invokeOnColorsChangedListenerTest_lockOnly() {
    177         verifyColorListenerInvoked(WallpaperManager.FLAG_LOCK, WallpaperManager.FLAG_LOCK);
    178     }
    179 
    180     @Test
    181     public void invokeOnColorsChangedListenerTest_both() {
    182         int both = WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM;
    183         verifyColorListenerInvoked(both, both);
    184     }
    185 
    186     @Test
    187     public void invokeOnColorsChangedListenerTest_clearLock() throws IOException {
    188         verifyColorListenerInvokedClearing(WallpaperManager.FLAG_LOCK);
    189     }
    190 
    191     @Test
    192     public void invokeOnColorsChangedListenerTest_clearSystem() throws IOException {
    193         verifyColorListenerInvokedClearing(WallpaperManager.FLAG_SYSTEM);
    194     }
    195 
    196     /**
    197      * Removing a listener should not invoke it anymore
    198      */
    199     @Test
    200     public void addRemoveOnColorsChangedListenerTest_onlyInvokeAdded() throws IOException {
    201         ensureCleanState();
    202 
    203         final CountDownLatch latch = new CountDownLatch(1);
    204         WallpaperManager.OnColorsChangedListener counter = (colors, whichWp) -> latch.countDown();
    205 
    206         // Add and remove listener
    207         WallpaperManager.OnColorsChangedListener listener = getTestableListener();
    208         mWallpaperManager.addOnColorsChangedListener(listener, mHandler);
    209         mWallpaperManager.removeOnColorsChangedListener(listener);
    210 
    211         // Verify that the listener is not called
    212         mWallpaperManager.addOnColorsChangedListener(counter, mHandler);
    213         try {
    214             mWallpaperManager.setResource(R.drawable.robot);
    215             if (!latch.await(5, TimeUnit.SECONDS)) {
    216                 throw new AssertionError("Registered listener not invoked");
    217             }
    218         } catch (InterruptedException | IOException e) {
    219             throw new RuntimeException(e);
    220         }
    221         verify(listener, never()).onColorsChanged(any(WallpaperColors.class), anyInt());
    222         mWallpaperManager.removeOnColorsChangedListener(counter);
    223     }
    224 
    225     /**
    226      * Suggesting desired dimensions is only a hint to the system that can be ignored.
    227      *
    228      * Test if the desired minimum width or height the WallpaperManager returns
    229      * is greater than 0. If so, then we check whether that the size is at least the
    230      * as big as the screen.
    231      */
    232     @Test
    233     public void suggestDesiredDimensionsTest() {
    234         final Point min = getScreenSize();
    235         final int w = min.x * 3;
    236         final int h = min.y * 2;
    237         assertDesiredMinimum(new Point(min.x / 2, min.y / 2), min);
    238 
    239         assertDesiredMinimum(new Point(w, h), min);
    240 
    241         assertDesiredMinimum(new Point(min.x / 2, h), min);
    242 
    243         assertDesiredMinimum(new Point(w, min.y / 2), min);
    244     }
    245 
    246     private void assertDesiredMinimum(Point suggestedSize, Point minSize) {
    247         mWallpaperManager.suggestDesiredDimensions(suggestedSize.x, suggestedSize.y);
    248         Point actualSize = new Point(mWallpaperManager.getDesiredMinimumWidth(),
    249                 mWallpaperManager.getDesiredMinimumHeight());
    250         if (actualSize.x > 0 || actualSize.y > 0) {
    251             if ((actualSize.x < minSize.x || actualSize.y < minSize.y)) {
    252                 throw new AssertionError("Expected at least x: " + minSize.x + " y: "
    253                         + minSize.y + ", got x: " + actualSize.x +
    254                         " y: " + actualSize.y);
    255             }
    256         }
    257     }
    258 
    259     private Point getScreenSize() {
    260         WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    261         Display d = wm.getDefaultDisplay();
    262         Point p = new Point();
    263         d.getRealSize(p);
    264         return p;
    265     }
    266 
    267     /**
    268      * Helper to set a listener and verify if it was called with the same flags.
    269      * Executes operation synchronously.
    270      *
    271      * @param which FLAG_LOCK, FLAG_SYSTEM or a combination of both.
    272      */
    273     private void verifyColorListenerInvoked(int which, int whichExpected) {
    274         ensureCleanState();
    275         int expected = 0;
    276         if ((whichExpected & WallpaperManager.FLAG_LOCK) != 0) expected++;
    277         if ((whichExpected & WallpaperManager.FLAG_SYSTEM) != 0) expected++;
    278         ArrayList<Integer> received = new ArrayList<>();
    279 
    280         final CountDownLatch latch = new CountDownLatch(expected);
    281         Handler handler = new Handler(Looper.getMainLooper());
    282 
    283         WallpaperManager.OnColorsChangedListener listener = getTestableListener();
    284         WallpaperManager.OnColorsChangedListener counter = (colors, whichWp) -> {
    285             handler.post(()-> {
    286                 received.add(whichWp);
    287                 boolean ok = false;
    288                 if ((whichWp & WallpaperManager.FLAG_LOCK) != 0 &&
    289                         (whichExpected & WallpaperManager.FLAG_LOCK) != 0) {
    290                     latch.countDown();
    291                     ok = true;
    292                 }
    293                 if ((whichWp & WallpaperManager.FLAG_SYSTEM) != 0 &&
    294                         (whichExpected & WallpaperManager.FLAG_SYSTEM) != 0) {
    295                     latch.countDown();
    296                     ok = true;
    297                 }
    298                 if (!ok) {
    299                     throw new AssertionError("Unexpected which flag: " + whichWp +
    300                             " should be: " + whichExpected);
    301                 }
    302             });
    303         };
    304 
    305         mWallpaperManager.addOnColorsChangedListener(listener, mHandler);
    306         mWallpaperManager.addOnColorsChangedListener(counter, mHandler);
    307 
    308         try {
    309             mWallpaperManager.setResource(R.drawable.robot, which);
    310             if (!latch.await(5, TimeUnit.SECONDS)) {
    311                 throw new AssertionError("Didn't receive all color events. Expected: " +
    312                         whichExpected + " received: " + received);
    313             }
    314         } catch (InterruptedException | IOException e) {
    315             throw new RuntimeException(e);
    316         }
    317 
    318         mWallpaperManager.removeOnColorsChangedListener(listener);
    319         mWallpaperManager.removeOnColorsChangedListener(counter);
    320     }
    321 
    322     /**
    323      * Helper to clear a wallpaper synchronously.
    324      *
    325      * @param which FLAG_LOCK, FLAG_SYSTEM or a combination of both.
    326      */
    327     private void verifyColorListenerInvokedClearing(int which) {
    328         ensureCleanState();
    329 
    330         final CountDownLatch latch = new CountDownLatch(1);
    331 
    332         WallpaperManager.OnColorsChangedListener listener = getTestableListener();
    333         WallpaperManager.OnColorsChangedListener counter = (colors, whichWp) -> {
    334             latch.countDown();
    335         };
    336 
    337         mWallpaperManager.addOnColorsChangedListener(listener, mHandler);
    338         mWallpaperManager.addOnColorsChangedListener(counter, mHandler);
    339 
    340         try {
    341             mWallpaperManager.clear(which);
    342             latch.await(5, TimeUnit.SECONDS);
    343         } catch (InterruptedException | IOException e) {
    344             throw new RuntimeException(e);
    345         }
    346 
    347         verify(listener, atLeast(1))
    348                 .onColorsChanged(nullable(WallpaperColors.class), anyInt());
    349 
    350         mWallpaperManager.removeOnColorsChangedListener(listener);
    351         mWallpaperManager.removeOnColorsChangedListener(counter);
    352     }
    353 
    354     /**
    355      * Helper method to make sure a wallpaper is set for both FLAG_SYSTEM and FLAG_LOCK
    356      * and its callbacks were already called. Necessary to cleanup previous tests states.
    357      *
    358      * This is necessary to avoid race conditions between tests
    359      */
    360     private void ensureCleanState() {
    361         Bitmap bmp = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
    362         // We expect 5 events to happen when we change a wallpaper:
    363         //  Wallpaper changed
    364         //  System colors are null
    365         //  Lock colors are null
    366         //  System colors are known
    367         //  Lock colors are known
    368         final int expectedEvents = 5;
    369         CountDownLatch latch = new CountDownLatch(expectedEvents);
    370         if (DEBUG) {
    371             Log.d("WP", "Started latch expecting: " + latch.getCount());
    372         }
    373         BroadcastReceiver receiver = new BroadcastReceiver() {
    374             @Override
    375             public void onReceive(Context context, Intent intent) {
    376                 latch.countDown();
    377                 if (DEBUG) {
    378                     Log.d("WP", "broadcast state count down: " + latch.getCount());
    379                 }
    380             }
    381         };
    382         WallpaperManager.OnColorsChangedListener callback = (colors, which) -> {
    383             if ((which & WallpaperManager.FLAG_LOCK) != 0) {
    384                 latch.countDown();
    385             }
    386             if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
    387                 latch.countDown();
    388             }
    389             if (DEBUG) {
    390                 Log.d("WP", "color state count down: " + which + " - " + colors);
    391             }
    392         };
    393         mContext.registerReceiver(receiver, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
    394         mWallpaperManager.addOnColorsChangedListener(callback, mHandler);
    395 
    396         try {
    397             mWallpaperManager.setBitmap(bmp);
    398 
    399             // Wait for up to 10 sec since this is an async call.
    400             // Will pass as soon as the expected callbacks are executed.
    401             latch.await(10, TimeUnit.SECONDS);
    402             if (latch.getCount() != 0) {
    403                 Log.w(TAG, "Did not receive all events! This is probably a bug.");
    404             }
    405         } catch (InterruptedException | IOException e) {
    406             throw new RuntimeException("Can't ensure a clean state.");
    407         } finally {
    408             mContext.unregisterReceiver(receiver);
    409             mWallpaperManager.removeOnColorsChangedListener(callback);
    410             bmp.recycle();
    411         }
    412     }
    413 
    414     public WallpaperManager.OnColorsChangedListener getTestableListener() {
    415         // Unfortunately mockito cannot mock anonymous classes or lambdas.
    416         return spy(new TestableColorListener());
    417     }
    418 
    419     public class TestableColorListener implements WallpaperManager.OnColorsChangedListener {
    420         @Override
    421         public void onColorsChanged(WallpaperColors colors, int which) {
    422         }
    423     }
    424 }