1 /* 2 * Copyright (C) 2024 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.server.wm.jetpack.embedding; 18 19 import static android.server.wm.jetpack.embedding.MultiDisplayTestHelper.createLandscapeLargeScreenSimulatedDisplay; 20 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.createWildcardSplitPairRule; 21 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifySplitAttributes; 22 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.verifyStandaloneActivityStackIfNeeded; 23 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertResumed; 24 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndAssertResumedAndFillsTask; 25 import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.waitAndGetTaskBounds; 26 27 import static org.junit.Assert.assertEquals; 28 import static org.junit.Assert.assertNotNull; 29 import static org.junit.Assert.assertNull; 30 import static org.junit.Assume.assumeTrue; 31 32 import android.app.Activity; 33 import android.graphics.Rect; 34 import android.platform.test.annotations.Presubmit; 35 import android.platform.test.flag.junit.CheckFlagsRule; 36 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 37 import android.server.wm.WindowManagerState; 38 import android.server.wm.jetpack.utils.TestActivityWithId2; 39 import android.server.wm.jetpack.utils.TestConfigChangeHandlingActivity; 40 import android.util.ArrayMap; 41 42 import androidx.annotation.NonNull; 43 import androidx.annotation.Nullable; 44 import androidx.test.ext.junit.runners.AndroidJUnit4; 45 import androidx.window.extensions.core.util.function.Consumer; 46 import androidx.window.extensions.embedding.ActivityEmbeddingComponent; 47 import androidx.window.extensions.embedding.EmbeddedActivityWindowInfo; 48 import androidx.window.extensions.embedding.SplitPairRule; 49 50 import com.android.compatibility.common.util.ApiTest; 51 52 import com.google.common.collect.Sets; 53 54 import org.junit.Rule; 55 import org.junit.Test; 56 import org.junit.runner.RunWith; 57 58 import java.util.Map; 59 60 /** 61 * Tests for the ActivityEmbedding implementation about {@link EmbeddedActivityWindowInfo}. 62 * 63 * Build/Install/Run: 64 * atest CtsWindowManagerJetpackTestCases:EmbeddedActivityWindowInfoTests 65 */ 66 @Presubmit 67 @RunWith(AndroidJUnit4.class) 68 public class EmbeddedActivityWindowInfoTests extends ActivityEmbeddingTestBase { 69 70 @Rule 71 public final CheckFlagsRule mCheckFlagsRule = 72 DeviceFlagsValueProvider.createCheckFlagsRule(); 73 74 /** 75 * Tests the returned value from 76 * {@link ActivityEmbeddingComponent#getEmbeddedActivityWindowInfo} is correct for non-embedded 77 * activity. 78 */ 79 @ApiTest(apis = { 80 "androidx.window.extensions.ActivityEmbeddingComponent#getEmbeddedActivityWindowInfo" 81 }) 82 @Test testGetEmbeddedActivityWindowInfo_nonEmbeddedActivity()83 public void testGetEmbeddedActivityWindowInfo_nonEmbeddedActivity() { 84 final Activity nonEmbeddedActivity = startFullScreenActivityNewTask( 85 TestConfigChangeHandlingActivity.class); 86 waitAndAssertResumed(nonEmbeddedActivity); 87 88 mInstrumentation.runOnMainSync(() -> { 89 final EmbeddedActivityWindowInfo info = mActivityEmbeddingComponent 90 .getEmbeddedActivityWindowInfo(nonEmbeddedActivity); 91 assertEmbeddedActivityWindowInfo(info, nonEmbeddedActivity, false /* isEmbedded */); 92 }); 93 } 94 95 /** 96 * Tests the returned value from 97 * {@link ActivityEmbeddingComponent#getEmbeddedActivityWindowInfo} is correct for embedded 98 * activities. 99 */ 100 @ApiTest(apis = { 101 "androidx.window.extensions.ActivityEmbeddingComponent#getEmbeddedActivityWindowInfo" 102 }) 103 @Test testGetEmbeddedActivityWindowInfo_embeddedActivity()104 public void testGetEmbeddedActivityWindowInfo_embeddedActivity() { 105 final SplitPairRule splitPairRule = createWildcardSplitPairRule(); 106 mActivityEmbeddingComponent.setEmbeddingRules(Sets.newHashSet(splitPairRule)); 107 108 // Launch two activities into a split 109 final Activity primaryActivity = 110 startFullScreenActivityNewTask(TestConfigChangeHandlingActivity.class); 111 final Activity secondaryActivity = 112 startActivityAndVerifySplitAttributes( 113 primaryActivity, 114 TestActivityWithId2.class, 115 splitPairRule, 116 "secondaryActivity" /* secondActivityId */, 117 mSplitInfoConsumer, 118 mActivityStackCallback); 119 120 mInstrumentation.runOnMainSync(() -> { 121 final EmbeddedActivityWindowInfo primaryInfo = mActivityEmbeddingComponent 122 .getEmbeddedActivityWindowInfo(primaryActivity); 123 final EmbeddedActivityWindowInfo secondaryInfo = mActivityEmbeddingComponent 124 .getEmbeddedActivityWindowInfo(secondaryActivity); 125 126 assertEmbeddedActivityWindowInfo(primaryInfo, primaryActivity, true /* isEmbedded */); 127 assertEmbeddedActivityWindowInfo( 128 secondaryInfo, secondaryActivity, true /* isEmbedded */); 129 }); 130 } 131 132 /** 133 * Tests the returned value from 134 * {@link ActivityEmbeddingComponent#getEmbeddedActivityWindowInfo} is correct for embedded 135 * activities. 136 */ 137 @ApiTest(apis = { 138 "androidx.window.extensions.ActivityEmbeddingComponent#getEmbeddedActivityWindowInfo" 139 }) 140 @Test testGetEmbeddedActivityWindowInfo_embeddedActivity_secondaryDisplay()141 public void testGetEmbeddedActivityWindowInfo_embeddedActivity_secondaryDisplay() { 142 assumeTrue(supportsMultiDisplay()); 143 144 final WindowManagerState.DisplayContent secondaryDisplay = 145 createLandscapeLargeScreenSimulatedDisplay(createManagedVirtualDisplaySession()); 146 final SplitPairRule splitPairRule = createWildcardSplitPairRule(); 147 mActivityEmbeddingComponent.setEmbeddingRules(Sets.newHashSet(splitPairRule)); 148 149 // Launch two activities into a split 150 final Activity primaryActivity = 151 startFullScreenActivityNewTask( 152 TestConfigChangeHandlingActivity.class, 153 null /* activityId */, 154 secondaryDisplay.mId); 155 final Activity secondaryActivity = 156 startActivityAndVerifySplitAttributes( 157 primaryActivity, 158 TestActivityWithId2.class, 159 splitPairRule, 160 "secondaryActivity" /* secondActivityId */, 161 mSplitInfoConsumer, 162 mActivityStackCallback); 163 164 mInstrumentation.runOnMainSync(() -> { 165 final EmbeddedActivityWindowInfo primaryInfo = mActivityEmbeddingComponent 166 .getEmbeddedActivityWindowInfo(primaryActivity); 167 final EmbeddedActivityWindowInfo secondaryInfo = mActivityEmbeddingComponent 168 .getEmbeddedActivityWindowInfo(secondaryActivity); 169 170 assertEmbeddedActivityWindowInfo(primaryInfo, primaryActivity, true /* isEmbedded */); 171 assertEmbeddedActivityWindowInfo( 172 secondaryInfo, secondaryActivity, true /* isEmbedded */); 173 }); 174 } 175 176 /** 177 * Tests that the 178 * {@link ActivityEmbeddingComponent#setEmbeddedActivityWindowInfoCallback} callback will be 179 * triggered whenever the {@link EmbeddedActivityWindowInfo} is changed. 180 */ 181 @ApiTest(apis = { 182 "androidx.window.extensions.ActivityEmbeddingComponent" 183 + "#setEmbeddedActivityWindowInfoCallback", 184 "androidx.window.extensions.ActivityEmbeddingComponent" 185 + "#clearEmbeddedActivityWindowInfoCallback" 186 }) 187 @Test testEmbeddedActivityWindowInfoCallback()188 public void testEmbeddedActivityWindowInfoCallback() { 189 final TestWindowInfoChangeListener listener = new TestWindowInfoChangeListener(); 190 mActivityEmbeddingComponent.setEmbeddedActivityWindowInfoCallback(Runnable::run, listener); 191 final SplitPairRule splitPairRule = createWildcardSplitPairRule(); 192 mActivityEmbeddingComponent.setEmbeddingRules(Sets.newHashSet(splitPairRule)); 193 194 // Report info when activity is launched. 195 final Activity primaryActivity = startFullScreenActivityNewTask( 196 TestConfigChangeHandlingActivity.class); 197 waitAndAssertResumed(primaryActivity); 198 199 mInstrumentation.runOnMainSync( 200 () -> { 201 final EmbeddedActivityWindowInfo nonEmbeddedInfo = 202 listener.getLastReportedInfo(primaryActivity); 203 assertEmbeddedActivityWindowInfo( 204 nonEmbeddedInfo, primaryActivity, false /* isEmbedded */); 205 }); 206 207 // Report info when activity enters split. 208 final Activity secondaryActivity = 209 startActivityAndVerifySplitAttributes( 210 primaryActivity, 211 TestActivityWithId2.class, 212 splitPairRule, 213 "secondaryActivity" /* secondActivityId */, 214 mSplitInfoConsumer, 215 mActivityStackCallback); 216 217 mInstrumentation.runOnMainSync(() -> { 218 final EmbeddedActivityWindowInfo primaryInfo = listener 219 .getLastReportedInfo(primaryActivity); 220 final EmbeddedActivityWindowInfo secondaryInfo = listener 221 .getLastReportedInfo(secondaryActivity); 222 223 assertEmbeddedActivityWindowInfo(primaryInfo, primaryActivity, true /* isEmbedded */); 224 assertEmbeddedActivityWindowInfo( 225 secondaryInfo, secondaryActivity, true /* isEmbedded */); 226 }); 227 228 // Report info when the primary activity exit split. Not update anymore for the secondary 229 // activity that has been finished. 230 final EmbeddedActivityWindowInfo lastSecondaryInfo = listener.getLastReportedInfo( 231 secondaryActivity); 232 secondaryActivity.finish(); 233 234 waitAndAssertResumedAndFillsTask(primaryActivity); 235 verifyStandaloneActivityStackIfNeeded(mActivityStackCallback, primaryActivity); 236 237 mInstrumentation.runOnMainSync(() -> { 238 final EmbeddedActivityWindowInfo primaryInfo2 = listener 239 .getLastReportedInfo(primaryActivity); 240 final EmbeddedActivityWindowInfo secondaryInfo2 = listener 241 .getLastReportedInfo(secondaryActivity); 242 assertEmbeddedActivityWindowInfo(primaryInfo2, primaryActivity, false /* isEmbedded */); 243 assertEquals(lastSecondaryInfo, secondaryInfo2); 244 }); 245 246 // No more update after #clearEmbeddedActivityWindowInfoCallback. 247 final EmbeddedActivityWindowInfo lastPrimaryInfo = 248 listener.getLastReportedInfo(primaryActivity); 249 mActivityEmbeddingComponent.clearEmbeddedActivityWindowInfoCallback(); 250 // The last split state is back to fullscreen. Clear queue to wait for the new split update. 251 mSplitInfoConsumer.clearQueue(); 252 mActivityStackCallback.clearQueue(); 253 final Activity secondaryActivity2 = 254 startActivityAndVerifySplitAttributes( 255 primaryActivity, 256 TestActivityWithId2.class, 257 splitPairRule, 258 "secondaryActivity" /* secondActivityId */, 259 mSplitInfoConsumer, 260 mActivityStackCallback); 261 262 mInstrumentation.runOnMainSync(() -> { 263 assertEquals(lastPrimaryInfo, listener.getLastReportedInfo(primaryActivity)); 264 assertNull(listener.getLastReportedInfo(secondaryActivity2)); 265 }); 266 } 267 268 /** 269 * Tests that the 270 * {@link ActivityEmbeddingComponent#setEmbeddedActivityWindowInfoCallback} callback will be 271 * triggered whenever the {@link EmbeddedActivityWindowInfo} is changed. 272 */ 273 @ApiTest(apis = { 274 "androidx.window.extensions.ActivityEmbeddingComponent" 275 + "#setEmbeddedActivityWindowInfoCallback", 276 "androidx.window.extensions.ActivityEmbeddingComponent" 277 + "#clearEmbeddedActivityWindowInfoCallback" 278 }) 279 @Test testEmbeddedActivityWindowInfoCallbackOnSecondaryDisplay()280 public void testEmbeddedActivityWindowInfoCallbackOnSecondaryDisplay() { 281 assumeTrue(supportsMultiDisplay()); 282 283 final WindowManagerState.DisplayContent secondaryDisplay = 284 createLandscapeLargeScreenSimulatedDisplay(createManagedVirtualDisplaySession()); 285 final TestWindowInfoChangeListener listener = new TestWindowInfoChangeListener(); 286 mActivityEmbeddingComponent.setEmbeddedActivityWindowInfoCallback(Runnable::run, listener); 287 final SplitPairRule splitPairRule = createWildcardSplitPairRule(); 288 mActivityEmbeddingComponent.setEmbeddingRules(Sets.newHashSet(splitPairRule)); 289 290 // Report info when activity is launched. 291 final Activity primaryActivity = startFullScreenActivityNewTask( 292 TestConfigChangeHandlingActivity.class, null /* activityId */, 293 secondaryDisplay.mId); 294 waitAndAssertResumed(primaryActivity); 295 296 mInstrumentation.runOnMainSync( 297 () -> { 298 final EmbeddedActivityWindowInfo nonEmbeddedInfo = 299 listener.getLastReportedInfo(primaryActivity); 300 assertEmbeddedActivityWindowInfo( 301 nonEmbeddedInfo, primaryActivity, false /* isEmbedded */); 302 }); 303 304 // Report info when activity enters split. 305 final Activity secondaryActivity = 306 startActivityAndVerifySplitAttributes( 307 primaryActivity, 308 TestActivityWithId2.class, 309 splitPairRule, 310 "secondaryActivity" /* secondActivityId */, 311 mSplitInfoConsumer, 312 mActivityStackCallback); 313 314 mInstrumentation.runOnMainSync(() -> { 315 final EmbeddedActivityWindowInfo primaryInfo = listener 316 .getLastReportedInfo(primaryActivity); 317 final EmbeddedActivityWindowInfo secondaryInfo = listener 318 .getLastReportedInfo(secondaryActivity); 319 320 assertEmbeddedActivityWindowInfo(primaryInfo, primaryActivity, true /* isEmbedded */); 321 assertEmbeddedActivityWindowInfo( 322 secondaryInfo, secondaryActivity, true /* isEmbedded */); 323 }); 324 } 325 assertEmbeddedActivityWindowInfo(@ullable EmbeddedActivityWindowInfo info, @NonNull Activity activity, boolean isEmbedded)326 private void assertEmbeddedActivityWindowInfo(@Nullable EmbeddedActivityWindowInfo info, 327 @NonNull Activity activity, boolean isEmbedded) { 328 assertNotNull(info); 329 330 final Rect taskBounds = waitAndGetTaskBounds(activity, true /* shouldWaitForResume */); 331 final Rect activityStackBounds = getActivityBounds(activity); 332 333 final String errorMessage = "Expected value: \nisEmbedded=" + isEmbedded 334 + "\nactivity=" + activity 335 + "\nactivityStackBounds=" + activityStackBounds 336 + "\ntaskBounds=" + taskBounds 337 + "\nActual value: " + info; 338 339 assertEquals(errorMessage, isEmbedded, info.isEmbedded()); 340 assertEquals(errorMessage, activity, info.getActivity()); 341 assertEquals(errorMessage, activityStackBounds, info.getActivityStackBounds()); 342 assertEquals(errorMessage, taskBounds, info.getTaskBounds()); 343 } 344 345 private static class TestWindowInfoChangeListener implements 346 Consumer<EmbeddedActivityWindowInfo> { 347 348 private final Map<Activity, EmbeddedActivityWindowInfo> mLastReportedInfos = 349 new ArrayMap<>(); 350 351 @Override accept(EmbeddedActivityWindowInfo embeddedActivityWindowInfo)352 public void accept(EmbeddedActivityWindowInfo embeddedActivityWindowInfo) { 353 final Activity activity = embeddedActivityWindowInfo.getActivity(); 354 mLastReportedInfos.put(activity, embeddedActivityWindowInfo); 355 } 356 357 @Nullable getLastReportedInfo(@onNull Activity activity)358 EmbeddedActivityWindowInfo getLastReportedInfo(@NonNull Activity activity) { 359 return mLastReportedInfos.get(activity); 360 } 361 } 362 } 363