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.nullable;
     22 import static org.mockito.Mockito.atLeast;
     23 import static org.mockito.Mockito.mock;
     24 import static org.mockito.Mockito.never;
     25 import static org.mockito.Mockito.spy;
     26 import static org.mockito.Mockito.verify;
     27 
     28 import android.app.WallpaperColors;
     29 import android.app.WallpaperManager;
     30 import android.app.stubs.R;
     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.util.Log;
     43 import android.view.Display;
     44 import android.view.WindowManager;
     45 
     46 import androidx.test.InstrumentationRegistry;
     47 import androidx.test.runner.AndroidJUnit4;
     48 
     49 import org.junit.Assert;
     50 import org.junit.Before;
     51 import org.junit.Test;
     52 import org.junit.runner.RunWith;
     53 import org.mockito.MockitoAnnotations;
     54 
     55 import java.io.IOException;
     56 import java.util.ArrayList;
     57 import java.util.concurrent.CountDownLatch;
     58 import java.util.concurrent.TimeUnit;
     59 
     60 @RunWith(AndroidJUnit4.class)
     61 public class WallpaperManagerTest {
     62 
     63     private static final boolean DEBUG = false;
     64     private static final String TAG = "WallpaperManagerTest";
     65 
     66     private WallpaperManager mWallpaperManager;
     67     private Context mContext;
     68     private Handler mHandler;
     69 
     70     @Before
     71     public void setUp() throws Exception {
     72         MockitoAnnotations.initMocks(this);
     73         mContext = InstrumentationRegistry.getTargetContext();
     74         mWallpaperManager = WallpaperManager.getInstance(mContext);
     75         final HandlerThread handlerThread = new HandlerThread("TestCallbacks");
     76         handlerThread.start();
     77         mHandler = new Handler(handlerThread.getLooper());
     78     }
     79 
     80     @Test
     81     public void setBitmapTest() {
     82         Bitmap tmpWallpaper = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
     83         Canvas canvas = new Canvas(tmpWallpaper);
     84         canvas.drawColor(Color.RED);
     85 
     86         try {
     87             int which = WallpaperManager.FLAG_SYSTEM;
     88             mWallpaperManager.setBitmap(tmpWallpaper);
     89             int oldWallpaperId = mWallpaperManager.getWallpaperId(which);
     90             canvas.drawColor(Color.GREEN);
     91             mWallpaperManager.setBitmap(tmpWallpaper);
     92             int newWallpaperId = mWallpaperManager.getWallpaperId(which);
     93             Assert.assertNotEquals(oldWallpaperId, newWallpaperId);
     94         } catch (IOException e) {
     95             throw new RuntimeException(e);
     96         } finally {
     97             tmpWallpaper.recycle();
     98         }
     99     }
    100 
    101     @Test
    102     public void setResourceTest() {
    103         try {
    104             int which = WallpaperManager.FLAG_SYSTEM;
    105             int oldWallpaperId = mWallpaperManager.getWallpaperId(which);
    106             mWallpaperManager.setResource(R.drawable.robot);
    107             int newWallpaperId = mWallpaperManager.getWallpaperId(which);
    108             Assert.assertNotEquals(oldWallpaperId, newWallpaperId);
    109         } catch (IOException e) {
    110             throw new RuntimeException(e);
    111         }
    112     }
    113 
    114     @Test
    115     public void wallpaperChangedBroadcastTest() {
    116         Bitmap tmpWallpaper = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
    117         Canvas canvas = new Canvas(tmpWallpaper);
    118         canvas.drawColor(Color.BLACK);
    119 
    120         CountDownLatch latch = new CountDownLatch(1);
    121         mContext.registerReceiver(new BroadcastReceiver() {
    122             @Override
    123             public void onReceive(Context context, Intent intent) {
    124                 latch.countDown();
    125             }
    126         }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
    127 
    128         try {
    129             mWallpaperManager.setBitmap(tmpWallpaper);
    130 
    131             // Wait for up to 5 sec since this is an async call.
    132             // Should fail if Intent.ACTION_WALLPAPER_CHANGED isn't delivered.
    133             if (!latch.await(5, TimeUnit.SECONDS)) {
    134                 throw new AssertionError("Intent.ACTION_WALLPAPER_CHANGED not received.");
    135             }
    136         } catch (InterruptedException | IOException e) {
    137             throw new AssertionError("Intent.ACTION_WALLPAPER_CHANGED not received.");
    138         } finally {
    139             tmpWallpaper.recycle();
    140         }
    141     }
    142 
    143     @Test
    144     public void wallpaperClearBroadcastTest() {
    145         CountDownLatch latch = new CountDownLatch(1);
    146         mContext.registerReceiver(new BroadcastReceiver() {
    147             @Override
    148             public void onReceive(Context context, Intent intent) {
    149                 latch.countDown();
    150             }
    151         }, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
    152 
    153         try {
    154             mWallpaperManager.clear(WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM);
    155 
    156             // Wait for 5 sec since this is an async call.
    157             // Should fail if Intent.ACTION_WALLPAPER_CHANGED isn't delivered.
    158             if (!latch.await(5, TimeUnit.SECONDS)) {
    159                 throw new AssertionError("Intent.ACTION_WALLPAPER_CHANGED not received.");
    160             }
    161         } catch (InterruptedException | IOException e) {
    162             throw new AssertionError(e);
    163         }
    164     }
    165 
    166     @Test
    167     public void invokeOnColorsChangedListenerTest_systemOnly() {
    168         int both = WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM;
    169         // Expect both since the first step is to migrate the current wallpaper
    170         // to the lock screen.
    171         verifyColorListenerInvoked(WallpaperManager.FLAG_SYSTEM, both);
    172     }
    173 
    174     @Test
    175     public void invokeOnColorsChangedListenerTest_lockOnly() {
    176         verifyColorListenerInvoked(WallpaperManager.FLAG_LOCK, WallpaperManager.FLAG_LOCK);
    177     }
    178 
    179     @Test
    180     public void invokeOnColorsChangedListenerTest_both() {
    181         int both = WallpaperManager.FLAG_LOCK | WallpaperManager.FLAG_SYSTEM;
    182         verifyColorListenerInvoked(both, both);
    183     }
    184 
    185     @Test
    186     public void invokeOnColorsChangedListenerTest_clearLock() throws IOException {
    187         verifyColorListenerInvokedClearing(WallpaperManager.FLAG_LOCK);
    188     }
    189 
    190     @Test
    191     public void invokeOnColorsChangedListenerTest_clearSystem() throws IOException {
    192         verifyColorListenerInvokedClearing(WallpaperManager.FLAG_SYSTEM);
    193     }
    194 
    195     /**
    196      * Removing a listener should not invoke it anymore
    197      */
    198     @Test
    199     public void addRemoveOnColorsChangedListenerTest_onlyInvokeAdded() throws IOException {
    200         ensureCleanState();
    201 
    202         final CountDownLatch latch = new CountDownLatch(1);
    203         WallpaperManager.OnColorsChangedListener counter = (colors, whichWp) -> latch.countDown();
    204 
    205         // Add and remove listener
    206         WallpaperManager.OnColorsChangedListener listener = getTestableListener();
    207         mWallpaperManager.addOnColorsChangedListener(listener, mHandler);
    208         mWallpaperManager.removeOnColorsChangedListener(listener);
    209 
    210         // Verify that the listener is not called
    211         mWallpaperManager.addOnColorsChangedListener(counter, mHandler);
    212         try {
    213             mWallpaperManager.setResource(R.drawable.robot);
    214             if (!latch.await(5, TimeUnit.SECONDS)) {
    215                 throw new AssertionError("Registered listener not invoked");
    216             }
    217         } catch (InterruptedException | IOException e) {
    218             throw new RuntimeException(e);
    219         }
    220         verify(listener, never()).onColorsChanged(any(WallpaperColors.class), anyInt());
    221         mWallpaperManager.removeOnColorsChangedListener(counter);
    222     }
    223 
    224     /**
    225      * Suggesting desired dimensions is only a hint to the system that can be ignored.
    226      *
    227      * Test if the desired minimum width or height the WallpaperManager returns
    228      * is greater than 0. If so, then we check whether that the size is the dimension
    229      * that was suggested.
    230      */
    231     @Test
    232     public void suggestDesiredDimensionsTest() {
    233         final Point min = getScreenSize();
    234         final int w = min.x * 3;
    235         final int h = min.y * 2;
    236         assertDesiredDimension(new Point(min.x / 2, min.y / 2), new Point(min.x / 2, min.y / 2));
    237 
    238         assertDesiredDimension(new Point(w, h), new Point(w, h));
    239 
    240         assertDesiredDimension(new Point(min.x / 2, h), new Point(min.x / 2, h));
    241 
    242         assertDesiredDimension(new Point(w, min.y / 2), new Point(w, min.y / 2));
    243     }
    244 
    245     @Test
    246     public void wallpaperColors_primary() {
    247         Bitmap tmpWallpaper = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
    248         Canvas canvas = new Canvas(tmpWallpaper);
    249         canvas.drawColor(Color.RED);
    250 
    251         try {
    252             mWallpaperManager.setBitmap(tmpWallpaper);
    253             WallpaperColors colors = mWallpaperManager.getWallpaperColors(
    254                     WallpaperManager.FLAG_SYSTEM);
    255 
    256             // Check that primary color is almost red
    257             Color primary = colors.getPrimaryColor();
    258             final float delta = 0.1f;
    259             Assert.assertEquals("red", 1f, primary.red(), delta);
    260             Assert.assertEquals("green", 0f, primary.green(), delta);
    261             Assert.assertEquals("blue", 0f, primary.blue(), delta);
    262 
    263             Assert.assertNull(colors.getSecondaryColor());
    264             Assert.assertNull(colors.getTertiaryColor());
    265         } catch (IOException e) {
    266             throw new RuntimeException(e);
    267         } finally {
    268             tmpWallpaper.recycle();
    269         }
    270     }
    271 
    272 
    273     @Test
    274     public void wallpaperColors_secondary() {
    275         Bitmap tmpWallpaper = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
    276         Canvas canvas = new Canvas(tmpWallpaper);
    277         canvas.drawColor(Color.RED);
    278         // Make 20% of the wallpaper BLUE so that secondary color is BLUE
    279         canvas.clipRect(0, 0, 100, 20);
    280         canvas.drawColor(Color.BLUE);
    281 
    282         try {
    283             mWallpaperManager.setBitmap(tmpWallpaper);
    284             WallpaperColors colors = mWallpaperManager.getWallpaperColors(
    285                     WallpaperManager.FLAG_SYSTEM);
    286 
    287             // Check that the secondary color is almost blue
    288             Color secondary = colors.getSecondaryColor();
    289             final float delta = 0.1f;
    290             Assert.assertEquals("red", 0f, secondary.red(), delta);
    291             Assert.assertEquals("green", 0f, secondary.green(), delta);
    292             Assert.assertEquals("blue", 1f, secondary.blue(), delta);
    293 
    294             Assert.assertNull(colors.getTertiaryColor());
    295         } catch (IOException e) {
    296             throw new RuntimeException(e);
    297         } finally {
    298             tmpWallpaper.recycle();
    299         }
    300     }
    301 
    302     private void assertDesiredDimension(Point suggestedSize, Point expectedSize) {
    303         mWallpaperManager.suggestDesiredDimensions(suggestedSize.x, suggestedSize.y);
    304         Point actualSize = new Point(mWallpaperManager.getDesiredMinimumWidth(),
    305                 mWallpaperManager.getDesiredMinimumHeight());
    306         if (actualSize.x > 0 || actualSize.y > 0) {
    307             if ((actualSize.x != expectedSize.x || actualSize.y != expectedSize.y)) {
    308                 throw new AssertionError("Expected x: " + expectedSize.x + " y: "
    309                         + expectedSize.y + ", got x: " + actualSize.x +
    310                         " y: " + actualSize.y);
    311             }
    312         }
    313     }
    314 
    315     private Point getScreenSize() {
    316         WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
    317         Display d = wm.getDefaultDisplay();
    318         Point p = new Point();
    319         d.getRealSize(p);
    320         return p;
    321     }
    322 
    323     /**
    324      * Helper to set a listener and verify if it was called with the same flags.
    325      * Executes operation synchronously.
    326      *
    327      * @param which FLAG_LOCK, FLAG_SYSTEM or a combination of both.
    328      */
    329     private void verifyColorListenerInvoked(int which, int whichExpected) {
    330         ensureCleanState();
    331         int expected = 0;
    332         if ((whichExpected & WallpaperManager.FLAG_LOCK) != 0) expected++;
    333         if ((whichExpected & WallpaperManager.FLAG_SYSTEM) != 0) expected++;
    334         ArrayList<Integer> received = new ArrayList<>();
    335 
    336         final CountDownLatch latch = new CountDownLatch(expected);
    337         Handler handler = new Handler(Looper.getMainLooper());
    338 
    339         WallpaperManager.OnColorsChangedListener listener = getTestableListener();
    340         WallpaperManager.OnColorsChangedListener counter = (colors, whichWp) -> {
    341             handler.post(()-> {
    342                 received.add(whichWp);
    343                 boolean ok = false;
    344                 if ((whichWp & WallpaperManager.FLAG_LOCK) != 0 &&
    345                         (whichExpected & WallpaperManager.FLAG_LOCK) != 0) {
    346                     latch.countDown();
    347                     ok = true;
    348                 }
    349                 if ((whichWp & WallpaperManager.FLAG_SYSTEM) != 0 &&
    350                         (whichExpected & WallpaperManager.FLAG_SYSTEM) != 0) {
    351                     latch.countDown();
    352                     ok = true;
    353                 }
    354                 if (!ok) {
    355                     throw new AssertionError("Unexpected which flag: " + whichWp +
    356                             " should be: " + whichExpected);
    357                 }
    358             });
    359         };
    360 
    361         mWallpaperManager.addOnColorsChangedListener(listener, mHandler);
    362         mWallpaperManager.addOnColorsChangedListener(counter, mHandler);
    363 
    364         try {
    365             mWallpaperManager.setResource(R.drawable.robot, which);
    366             if (!latch.await(5, TimeUnit.SECONDS)) {
    367                 throw new AssertionError("Didn't receive all color events. Expected: " +
    368                         whichExpected + " received: " + received);
    369             }
    370         } catch (InterruptedException | IOException e) {
    371             throw new RuntimeException(e);
    372         }
    373 
    374         mWallpaperManager.removeOnColorsChangedListener(listener);
    375         mWallpaperManager.removeOnColorsChangedListener(counter);
    376     }
    377 
    378     /**
    379      * Helper to clear a wallpaper synchronously.
    380      *
    381      * @param which FLAG_LOCK, FLAG_SYSTEM or a combination of both.
    382      */
    383     private void verifyColorListenerInvokedClearing(int which) {
    384         ensureCleanState();
    385 
    386         final CountDownLatch latch = new CountDownLatch(1);
    387 
    388         WallpaperManager.OnColorsChangedListener listener = getTestableListener();
    389         WallpaperManager.OnColorsChangedListener counter = (colors, whichWp) -> {
    390             latch.countDown();
    391         };
    392 
    393         mWallpaperManager.addOnColorsChangedListener(listener, mHandler);
    394         mWallpaperManager.addOnColorsChangedListener(counter, mHandler);
    395 
    396         try {
    397             mWallpaperManager.clear(which);
    398             latch.await(5, TimeUnit.SECONDS);
    399         } catch (InterruptedException | IOException e) {
    400             throw new RuntimeException(e);
    401         }
    402 
    403         verify(listener, atLeast(1))
    404                 .onColorsChanged(nullable(WallpaperColors.class), anyInt());
    405 
    406         mWallpaperManager.removeOnColorsChangedListener(listener);
    407         mWallpaperManager.removeOnColorsChangedListener(counter);
    408     }
    409 
    410     /**
    411      * Helper method to make sure a wallpaper is set for both FLAG_SYSTEM and FLAG_LOCK
    412      * and its callbacks were already called. Necessary to cleanup previous tests states.
    413      *
    414      * This is necessary to avoid race conditions between tests
    415      */
    416     private void ensureCleanState() {
    417         Bitmap bmp = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
    418         // We expect 5 events to happen when we change a wallpaper:
    419         //  Wallpaper changed
    420         //  System colors are null
    421         //  Lock colors are null
    422         //  System colors are known
    423         //  Lock colors are known
    424         final int expectedEvents = 5;
    425         CountDownLatch latch = new CountDownLatch(expectedEvents);
    426         if (DEBUG) {
    427             Log.d("WP", "Started latch expecting: " + latch.getCount());
    428         }
    429         BroadcastReceiver receiver = new BroadcastReceiver() {
    430             @Override
    431             public void onReceive(Context context, Intent intent) {
    432                 latch.countDown();
    433                 if (DEBUG) {
    434                     Log.d("WP", "broadcast state count down: " + latch.getCount());
    435                 }
    436             }
    437         };
    438         WallpaperManager.OnColorsChangedListener callback = (colors, which) -> {
    439             if ((which & WallpaperManager.FLAG_LOCK) != 0) {
    440                 latch.countDown();
    441             }
    442             if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
    443                 latch.countDown();
    444             }
    445             if (DEBUG) {
    446                 Log.d("WP", "color state count down: " + which + " - " + colors);
    447             }
    448         };
    449         mContext.registerReceiver(receiver, new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
    450         mWallpaperManager.addOnColorsChangedListener(callback, mHandler);
    451 
    452         try {
    453             mWallpaperManager.setBitmap(bmp);
    454 
    455             // Wait for up to 10 sec since this is an async call.
    456             // Will pass as soon as the expected callbacks are executed.
    457             latch.await(10, TimeUnit.SECONDS);
    458             if (latch.getCount() != 0) {
    459                 Log.w(TAG, "Did not receive all events! This is probably a bug.");
    460             }
    461         } catch (InterruptedException | IOException e) {
    462             throw new RuntimeException("Can't ensure a clean state.");
    463         } finally {
    464             mContext.unregisterReceiver(receiver);
    465             mWallpaperManager.removeOnColorsChangedListener(callback);
    466             bmp.recycle();
    467         }
    468     }
    469 
    470     public WallpaperManager.OnColorsChangedListener getTestableListener() {
    471         // Unfortunately mockito cannot mock anonymous classes or lambdas.
    472         return spy(new TestableColorListener());
    473     }
    474 
    475     public class TestableColorListener implements WallpaperManager.OnColorsChangedListener {
    476         @Override
    477         public void onColorsChanged(WallpaperColors colors, int which) {
    478         }
    479     }
    480 }