1 /* 2 * Copyright (C) 2023 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.wallpapers; 18 19 import static android.app.WallpaperManager.FLAG_LOCK; 20 import static android.app.WallpaperManager.FLAG_SYSTEM; 21 22 import static com.google.common.truth.Truth.assertThat; 23 24 import android.app.WallpaperManager; 25 import android.content.ComponentName; 26 import android.util.Log; 27 28 import java.io.IOException; 29 import java.util.ArrayList; 30 import java.util.List; 31 import java.util.stream.Stream; 32 33 public class WallpaperManagerTestUtils { 34 35 private static final String TAG = "WallpaperManagerTest"; 36 37 public static final ComponentName TEST_LIVE_WALLPAPER_COMPONENT = new ComponentName( 38 TestLiveWallpaper.class.getPackageName(), 39 TestLiveWallpaper.class.getName()); 40 41 public static final ComponentName TEST_LIVE_WALLPAPER_NO_UNFOLD_COMPONENT = new ComponentName( 42 TestLiveWallpaperNoUnfoldTransition.class.getPackageName(), 43 TestLiveWallpaperNoUnfoldTransition.class.getName()); 44 45 public static final ComponentName TEST_LIVE_WALLPAPER_AMBIENT_COMPONENT = new ComponentName( 46 TestLiveWallpaperSupportingAmbientMode.class.getPackageName(), 47 TestLiveWallpaperSupportingAmbientMode.class.getName()); 48 49 /** 50 * enumeration of all wallpapers used for test purposes: 3 static, 3 live wallpapers: <br> 51 * static1 <=> red bitmap <br> 52 * static2 <=> green bitmap <br> 53 * static3 <=> blue bitmap <br> 54 * <br> 55 * live1 <=> TestLiveWallpaper (cyan) <br> 56 * live2 <=> TestLiveWallpaperNoUnfoldTransition (magenta) <br> 57 * live3 <=> TestLiveWallpaperSupportingAmbientMode (yellow) <br> 58 */ 59 public enum TestWallpaper { 60 STATIC1(R.drawable.icon_red, null), 61 STATIC2(R.drawable.icon_green, null), 62 STATIC3(R.drawable.icon_blue, null), 63 LIVE1(null, TEST_LIVE_WALLPAPER_COMPONENT), 64 LIVE2(null, TEST_LIVE_WALLPAPER_NO_UNFOLD_COMPONENT), 65 LIVE3(null, TEST_LIVE_WALLPAPER_AMBIENT_COMPONENT); 66 67 private final Integer mBitmapResourceId; 68 private final ComponentName mComponentName; 69 TestWallpaper(Integer bitmapResourceId, ComponentName componentName)70 TestWallpaper(Integer bitmapResourceId, ComponentName componentName) { 71 mBitmapResourceId = bitmapResourceId; 72 mComponentName = componentName; 73 } 74 getBitmapResourceId()75 int getBitmapResourceId() { 76 return mBitmapResourceId; 77 } 78 getComponentName()79 ComponentName getComponentName() { 80 return mComponentName; 81 } 82 type()83 private String type() { 84 return isStatic() ? "static" : "live"; 85 } 86 isStatic()87 private boolean isStatic() { 88 return mComponentName == null; 89 } 90 isLive()91 private boolean isLive() { 92 return !isStatic(); 93 } 94 } 95 allStaticTestWallpapers()96 private static List<TestWallpaper> allStaticTestWallpapers() { 97 return List.of(TestWallpaper.STATIC1, TestWallpaper.STATIC2, TestWallpaper.STATIC3); 98 } 99 allLiveTestWallpapers()100 private static List<TestWallpaper> allLiveTestWallpapers() { 101 return List.of(TestWallpaper.LIVE1, TestWallpaper.LIVE2, TestWallpaper.LIVE3); 102 } 103 104 public static class WallpaperChange { 105 TestWallpaper mWallpaper; 106 int mDestination; WallpaperChange( TestWallpaper wallpaper, int destination)107 public WallpaperChange( 108 TestWallpaper wallpaper, int destination) { 109 this.mWallpaper = wallpaper; 110 this.mDestination = destination; 111 } 112 } 113 114 /** 115 * Class representing a state in which our WallpaperManager may be during our tests. 116 * A state is fully represented by the wallpaper that are present on home and lock screen. 117 */ 118 public static class WallpaperState { 119 private final TestWallpaper mHomeWallpaper; 120 private final TestWallpaper mLockWallpaper; 121 122 /** 123 * it is possible to have two copies of the same engine on home + lock screen, 124 * in which this flag would be false. 125 * True means that mHomeWallpaper == mLockWallpaper and there is only one active engine. 126 */ 127 private final boolean mSingleEngine; 128 WallpaperState( TestWallpaper homeWallpaper, TestWallpaper lockWallpaper, boolean singleEngine)129 public WallpaperState( 130 TestWallpaper homeWallpaper, TestWallpaper lockWallpaper, boolean singleEngine) { 131 mHomeWallpaper = homeWallpaper; 132 mLockWallpaper = lockWallpaper; 133 assertThat(!singleEngine || (homeWallpaper == lockWallpaper)).isTrue(); 134 mSingleEngine = singleEngine; 135 } 136 pickUnused(List<TestWallpaper> choices)137 private TestWallpaper pickUnused(List<TestWallpaper> choices) { 138 return choices.stream() 139 .filter(wallpaper -> wallpaper != mHomeWallpaper && wallpaper != mLockWallpaper) 140 .findFirst().orElseThrow(); 141 } 142 pickUnusedStatic()143 private TestWallpaper pickUnusedStatic() { 144 return pickUnused(allStaticTestWallpapers()); 145 } 146 pickUnusedLive()147 private TestWallpaper pickUnusedLive() { 148 return pickUnused(allLiveTestWallpapers()); 149 } 150 151 /** 152 * Enumerate all the possible logically different {@link WallpaperChange} changes from 153 * this state. <br> 154 * Two changes are considered logically different if their destination is different, 155 * or if their wallpaper type (static or live) is different. 156 */ allPossibleChanges()157 public List<WallpaperChange> allPossibleChanges() { 158 TestWallpaper unusedStatic = pickUnusedStatic(); 159 TestWallpaper unusedLive = pickUnusedLive(); 160 161 // one can always add a new wallpaper, either static or live, at any destination 162 List<WallpaperChange> result = new ArrayList<>(Stream.of(unusedStatic, unusedLive) 163 .flatMap(newWallpaper -> Stream 164 .of(FLAG_LOCK, FLAG_SYSTEM, FLAG_LOCK | FLAG_SYSTEM) 165 .map(destination -> new WallpaperChange(newWallpaper, destination))) 166 .toList()); 167 168 // if we have a lock & home single engine, we can separate it 169 if (mSingleEngine) { 170 result.addAll(List.of( 171 new WallpaperChange(mHomeWallpaper, FLAG_SYSTEM), 172 new WallpaperChange(mHomeWallpaper, FLAG_LOCK) 173 )); 174 175 // else if we have the same engine twice, we can merge it 176 } else if (mHomeWallpaper == mLockWallpaper) { 177 result.add(new WallpaperChange(mHomeWallpaper, FLAG_SYSTEM | FLAG_LOCK)); 178 } 179 180 // if we have different engines on home / lock, 181 // we can set one of them at the other location or at both locations 182 if (mHomeWallpaper != mLockWallpaper) { 183 result.addAll(List.of( 184 new WallpaperChange(mHomeWallpaper, FLAG_LOCK | FLAG_SYSTEM), 185 new WallpaperChange(mLockWallpaper, FLAG_LOCK | FLAG_SYSTEM), 186 new WallpaperChange(mHomeWallpaper, FLAG_LOCK), 187 new WallpaperChange(mLockWallpaper, FLAG_SYSTEM) 188 )); 189 } 190 return result; 191 } 192 193 /** 194 * Given a change, return the number of times we expect an engine.onCreate operation 195 * of a live wallpaper from this state 196 */ expectedNumberOfLiveWallpaperCreate(WallpaperChange change)197 public int expectedNumberOfLiveWallpaperCreate(WallpaperChange change) { 198 199 if (change.mWallpaper.isStatic()) return 0; 200 switch (change.mDestination) { 201 case FLAG_SYSTEM | FLAG_LOCK: 202 return change.mWallpaper != mHomeWallpaper ? 1 : 0; 203 case FLAG_SYSTEM: 204 return mSingleEngine || (change.mWallpaper != mHomeWallpaper) ? 1 : 0; 205 case FLAG_LOCK: 206 return mSingleEngine || (change.mWallpaper != mLockWallpaper) ? 1 : 0; 207 default: 208 throw new IllegalArgumentException(); 209 } 210 } 211 212 213 /** 214 * Given a change, return the number of times we expect an engine.onDestroy operation 215 * of a live wallpaper from this state 216 */ expectedNumberOfLiveWallpaperDestroy(WallpaperChange change)217 public int expectedNumberOfLiveWallpaperDestroy(WallpaperChange change) { 218 219 if (mSingleEngine) { 220 return mHomeWallpaper.isLive() 221 && mHomeWallpaper != change.mWallpaper 222 && change.mDestination == (FLAG_LOCK | FLAG_SYSTEM) ? 1 : 0; 223 } 224 225 boolean changeSystem = (change.mDestination & FLAG_SYSTEM) != 0; 226 boolean changeLock = (change.mDestination & FLAG_LOCK) != 0; 227 boolean systemReplaced = changeSystem && change.mWallpaper != mHomeWallpaper; 228 boolean lockReplaced = 229 changeLock && (change.mWallpaper != mLockWallpaper || changeSystem); 230 231 int result = 0; 232 if (systemReplaced && mHomeWallpaper.isLive()) result += 1; 233 if (lockReplaced && mLockWallpaper.isLive()) result += 1; 234 return result; 235 } 236 237 /** 238 * Describes how to reproduce a failure obtained from this state with the given change 239 */ reproduceDescription(WallpaperChange change)240 public String reproduceDescription(WallpaperChange change) { 241 return String.format("To reproduce, start with:\n%s\nand %s", 242 description(), changeDescription(change)); 243 } 244 description()245 private String description() { 246 String homeType = mHomeWallpaper.type(); 247 String lockType = mLockWallpaper.type(); 248 return mLockWallpaper == mHomeWallpaper 249 ? String.format(" - the same %s wallpaper on home & lock screen (%s)", homeType, 250 mSingleEngine ? "sharing the same engine" : "each using its own engine") 251 : String.format(" - a %s wallpaper on home screen\n" 252 + " - %s %s wallpaper on lock screen", 253 homeType, homeType.equals(lockType) ? "another" : "a", lockType); 254 } 255 changeDescription(WallpaperChange change)256 private String changeDescription(WallpaperChange change) { 257 String newWallpaperDescription = 258 change.mWallpaper == mHomeWallpaper || change.mWallpaper == mLockWallpaper 259 ? String.format("the same %s wallpaper as %s screen", 260 change.mWallpaper.type(), 261 change.mWallpaper == mHomeWallpaper ? "home" : "lock") 262 : String.format("a different %s wallpaper", change.mWallpaper.type()); 263 264 String destinationDescription = 265 change.mDestination == FLAG_SYSTEM ? "home screen only" 266 : change.mDestination == FLAG_LOCK ? "lock screen only" 267 : "both home & lock screens"; 268 269 String methodUsed = change.mWallpaper.isLive() 270 ? "setWallpaperComponentWithFlags" : "setResource"; 271 272 String flagDescription = 273 change.mDestination == FLAG_SYSTEM ? "FLAG_SYSTEM" 274 : change.mDestination == FLAG_LOCK ? "FLAG_LOCK" 275 : "FLAG_SYSTEM|FLAG_LOCK"; 276 277 return String.format("apply %s on %s (via WallpaperManager#%s(..., %s))", 278 newWallpaperDescription, destinationDescription, methodUsed, flagDescription); 279 } 280 } 281 282 /** 283 * Uses the provided wallpaperManager instance to perform a {@link WallpaperChange}. 284 */ performChange( WallpaperManager wallpaperManager, WallpaperChange change)285 public static void performChange( 286 WallpaperManager wallpaperManager, WallpaperChange change) throws IOException { 287 if (change.mWallpaper.isStatic()) { 288 wallpaperManager.setResource( 289 change.mWallpaper.getBitmapResourceId(), change.mDestination); 290 } else { 291 wallpaperManager.setWallpaperComponentWithFlags( 292 change.mWallpaper.getComponentName(), change.mDestination); 293 } 294 try { 295 // Allow time for callbacks 296 Thread.sleep(500); 297 } catch (InterruptedException e) { 298 Log.e(TAG, "wallpaper wait interrupted", e); 299 } 300 } 301 302 /** 303 * Sets a wallpaperManager in some state. Always proceeds the same way: <br> 304 * - put the home wallpaper on lock and home screens <br> 305 * - put the lock wallpaper on lock screen, if it is different from the home screen wallpaper 306 */ goToState(WallpaperManager wallpaperManager, WallpaperState state)307 public static void goToState(WallpaperManager wallpaperManager, WallpaperState state) 308 throws IOException { 309 WallpaperChange change1 = new WallpaperChange( 310 state.mHomeWallpaper, FLAG_SYSTEM | FLAG_LOCK); 311 performChange(wallpaperManager, change1); 312 313 WallpaperChange change2 = new WallpaperChange(state.mLockWallpaper, FLAG_LOCK); 314 if (!state.mSingleEngine) performChange(wallpaperManager, change2); 315 } 316 317 /** 318 * Return a list of all logically different states 319 * Two states are logically different if at least one of this statement: <br> 320 * - home screen is live <br> 321 * - lock screen is live <br> 322 * - home screen and lock screen are the same wallpaper <br> 323 * - home screen and lock screen share the same engine <br> 324 * is different between the two states. 325 */ allPossibleStates()326 public static List<WallpaperState> allPossibleStates() { 327 return List.of( 328 new WallpaperState(TestWallpaper.LIVE1, TestWallpaper.LIVE1, true), 329 new WallpaperState(TestWallpaper.LIVE1, TestWallpaper.LIVE1, false), 330 new WallpaperState(TestWallpaper.LIVE1, TestWallpaper.LIVE2, false), 331 new WallpaperState(TestWallpaper.LIVE1, TestWallpaper.STATIC1, false), 332 new WallpaperState(TestWallpaper.STATIC1, TestWallpaper.STATIC1, true), 333 new WallpaperState(TestWallpaper.STATIC1, TestWallpaper.STATIC1, false), 334 new WallpaperState(TestWallpaper.STATIC1, TestWallpaper.STATIC2, false), 335 new WallpaperState(TestWallpaper.STATIC1, TestWallpaper.LIVE1, false) 336 ); 337 } 338 339 public static final WallpaperState TWO_DIFFERENT_LIVE_WALLPAPERS = new WallpaperState( 340 TestWallpaper.LIVE1, TestWallpaper.LIVE3, false); 341 342 public static final WallpaperState TWO_SAME_LIVE_WALLPAPERS = new WallpaperState( 343 TestWallpaper.LIVE2, TestWallpaper.LIVE2, true); 344 } 345