• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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