1 /*
2  * Copyright (C) 2021 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.other;
18 
19 import static android.server.wm.app.Components.KEEP_CLEAR_RECTS_ACTIVITY;
20 import static android.server.wm.app.Components.KEEP_CLEAR_RECTS_ACTIVITY2;
21 import static android.server.wm.app.Components.KeepClearRectsActivity.EXTRA_KEEP_CLEAR_RECTS;
22 import static android.view.Display.DEFAULT_DISPLAY;
23 
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertTrue;
26 import static org.junit.Assume.assumeTrue;
27 
28 import static java.util.Collections.EMPTY_LIST;
29 
30 import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
31 import android.app.Activity;
32 import android.content.ComponentName;
33 import android.content.Intent;
34 import android.graphics.Color;
35 import android.graphics.Rect;
36 import android.os.Bundle;
37 import android.platform.test.annotations.Presubmit;
38 import android.server.wm.WindowManagerState;
39 import android.server.wm.WindowManagerTestBase;
40 import android.server.wm.cts.R;
41 import android.view.Gravity;
42 import android.view.View;
43 import android.view.ViewConfiguration;
44 import android.view.ViewGroup;
45 import android.view.WindowManager;
46 import android.view.accessibility.AccessibilityNodeInfo;
47 import android.widget.RelativeLayout;
48 import android.widget.RelativeLayout.LayoutParams;
49 
50 import com.android.compatibility.common.util.ApiTest;
51 import com.android.compatibility.common.util.PollingCheck;
52 
53 import org.junit.After;
54 import org.junit.Before;
55 import org.junit.Rule;
56 import org.junit.Test;
57 
58 import java.util.ArrayList;
59 import java.util.Arrays;
60 import java.util.Collections;
61 import java.util.List;
62 import java.util.concurrent.Callable;
63 
64 @Presubmit
65 public class KeepClearRectsTests extends WindowManagerTestBase {
66     private static final long SAME_ELEMENT_ASSERTION_TIMEOUT = 3000;
67     private static final List<Rect> TEST_KEEP_CLEAR_RECTS =
68             Arrays.asList(new Rect(0, 0, 25, 25),
69                           new Rect(30, 0, 50, 25),
70                           new Rect(25, 25, 50, 50),
71                           new Rect(10, 30, 20, 50));
72     private static final List<Rect> TEST_KEEP_CLEAR_RECTS_2 =
73             Arrays.asList(new Rect(55, 0, 75, 15),
74                           new Rect(50, 15, 60, 25),
75                           new Rect(75, 25, 90, 50),
76                           new Rect(90, 0, 100, 10));
77     private static final Rect TEST_VIEW_BOUNDS = new Rect(0, 0, 100, 100);
78     private static final String USE_KEEP_CLEAR_ATTR_LAYOUT = "use_keep_clear_attr_layout";
79 
80     private TestActivitySession<TestActivity> mTestSession;
81 
82     @Rule
83     public InstrumentedAccessibilityServiceTestRule<AccessibilityTestService>
84             mAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
85             AccessibilityTestService.class, false);
86 
87     @Before
setUp()88     public void setUp() throws Exception {
89         super.setUp();
90         mTestSession = createManagedTestActivitySession();
91     }
92 
93     @After
tearDown()94     public void tearDown() throws Exception {
95         mWmState.setSuppressAccessibilityServices(true);
96     }
97 
98     @Test
99     @ApiTest(apis = {"android.view.View#attr_android:preferKeepClear"})
testSetPreferKeepClearAttr()100     public void testSetPreferKeepClearAttr() throws Exception {
101         final Intent intent = new Intent(mContext, TestActivity.class);
102         intent.putExtra(USE_KEEP_CLEAR_ATTR_LAYOUT, true);
103         mTestSession.launchTestActivityOnDisplaySync(null, intent, DEFAULT_DISPLAY);
104         final TestActivity activity = mTestSession.getActivity();
105 
106         // To be kept in sync with res/layout/keep_clear_attr_activity
107         final Rect keepClearRect = new Rect(0, 0, 25, 25);
108         assertSameElementsEventually(Arrays.asList(keepClearRect),
109                 () -> getKeepClearRectsForActivity(activity));
110     }
111 
112     @Test
113     @ApiTest(apis = {"android.view.View#setPreferKeepClear"})
testSetPreferKeepClearSingleView()114     public void testSetPreferKeepClearSingleView() throws Exception {
115         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
116         final TestActivity activity = mTestSession.getActivity();
117 
118         final Rect keepClearRect = new Rect(0, 0, 25, 25);
119         final View v = createTestViewInActivity(activity, keepClearRect);
120         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true));
121 
122         assertSameElementsEventually(Arrays.asList(keepClearRect),
123                 () -> getKeepClearRectsForActivity(activity));
124     }
125 
126     @Test
127     @ApiTest(apis = {"android.view.View#setPreferKeepClear"})
testSetPreferKeepClearTwoViews()128     public void testSetPreferKeepClearTwoViews() throws Exception {
129         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
130         final TestActivity activity = mTestSession.getActivity();
131 
132         final Rect keepClearRect = new Rect(0, 0, 25, 25);
133         final View v = createTestViewInActivity(activity, keepClearRect);
134         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true));
135 
136         final List<Rect> expected = new ArrayList(Arrays.asList(keepClearRect));
137         assertSameElementsEventually(expected, () -> getKeepClearRectsForActivity(activity));
138 
139         final Rect keepClearRect2 = new Rect(25, 25, 50, 50);
140         final View v2 = createTestViewInActivity(activity, keepClearRect2);
141         mTestSession.runOnMainSyncAndWait(() -> v2.setPreferKeepClear(true));
142 
143         expected.addAll(Arrays.asList(keepClearRect2));
144         assertSameElementsEventually(expected, () -> getKeepClearRectsForActivity(activity));
145     }
146 
147     @Test
148     @ApiTest(apis = {"android.view.View#setPreferKeepClearRects"})
testSetMultipleKeepClearRectsSingleView()149     public void testSetMultipleKeepClearRectsSingleView() throws Exception {
150         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
151         final TestActivity activity = mTestSession.getActivity();
152 
153         final View v = createTestViewInActivity(activity);
154         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS));
155 
156         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS,
157                 () -> getKeepClearRectsForActivity(activity));
158     }
159 
160     @Test
161     @ApiTest(apis = {"android.view.View#setPreferKeepClearRects"})
testSetMultipleKeepClearRectsTwoViews()162     public void testSetMultipleKeepClearRectsTwoViews() throws Exception {
163         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
164         final TestActivity activity = mTestSession.getActivity();
165 
166         final View v1 = createTestViewInActivity(activity);
167         final View v2 = createTestViewInActivity(activity);
168         mTestSession.runOnMainSyncAndWait(() -> {
169             v1.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS);
170             v2.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS_2);
171         });
172 
173         final List<Rect> expected = new ArrayList(TEST_KEEP_CLEAR_RECTS);
174         expected.addAll(TEST_KEEP_CLEAR_RECTS_2);
175         assertSameElementsEventually(expected, () -> getKeepClearRectsForActivity(activity));
176     }
177 
178     @Test
179     @ApiTest(apis = {"android.view.View#setPreferKeepClear",
180                      "android.view.View#isPreferKeepClear"})
testIsPreferKeepClearSingleView()181     public void testIsPreferKeepClearSingleView() {
182         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
183         final TestActivity activity = mTestSession.getActivity();
184 
185         final Rect viewBounds = new Rect(0, 0, 60, 60);
186         final View v = createTestViewInActivity(activity, viewBounds);
187         assertFalse(v.isPreferKeepClear());
188         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true));
189         assertTrue(v.isPreferKeepClear());
190 
191         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(false));
192         assertFalse(v.isPreferKeepClear());
193     }
194 
195     @Test
196     @ApiTest(apis = {"android.view.View#getPreferKeepClearRects",
197                      "android.view.View#setPreferKeepClearRects"})
testGetPreferKeepClearRectsSingleView()198     public void testGetPreferKeepClearRectsSingleView() throws Exception {
199         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
200         final TestActivity activity = mTestSession.getActivity();
201 
202         final Rect viewBounds = new Rect(0, 0, 60, 60);
203         final View v = createTestViewInActivity(activity, viewBounds);
204         assertSameElementsEventually(EMPTY_LIST, () -> v.getPreferKeepClearRects());
205         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS));
206         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, () -> v.getPreferKeepClearRects());
207 
208         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS_2));
209         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS_2, () -> v.getPreferKeepClearRects());
210 
211         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(EMPTY_LIST));
212         assertSameElementsEventually(EMPTY_LIST, () -> v.getPreferKeepClearRects());
213     }
214 
215     @Test
216     @ApiTest(apis = {"android.view.View#setPreferKeepClearRects",
217                      "android.view.View#setPreferKeepClear",
218                      "android.view.View#getPreferKeepClearRects",
219                      "android.view.View#isPreferKeepClear"})
testGettersPreferKeepClearRectsTwoViews()220     public void testGettersPreferKeepClearRectsTwoViews() throws Exception {
221         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
222         final TestActivity activity = mTestSession.getActivity();
223 
224         final Rect viewBounds1 = new Rect(0, 0, 60, 60);
225         final Rect viewBounds2 = new Rect(0, 0, 90, 90);
226         final View v1 = createTestViewInActivity(activity, viewBounds1);
227         final View v2 = createTestViewInActivity(activity, viewBounds2);
228         mTestSession.runOnMainSyncAndWait(() -> {
229             v1.setPreferKeepClear(true);
230             v2.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS);
231         });
232 
233         assertTrue(v1.isPreferKeepClear());
234         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, () -> v2.getPreferKeepClearRects());
235 
236         mTestSession.runOnMainSyncAndWait(() -> v1.setPreferKeepClear(false));
237         assertFalse(v1.isPreferKeepClear());
238         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS, () -> v2.getPreferKeepClearRects());
239 
240         mTestSession.runOnMainSyncAndWait(() -> {
241             v1.setPreferKeepClear(true);
242             v2.setPreferKeepClearRects(EMPTY_LIST);
243         });
244         assertTrue(v1.isPreferKeepClear());
245         assertSameElementsEventually(EMPTY_LIST, () -> v2.getPreferKeepClearRects());
246     }
247 
248     @Test
249     @ApiTest(apis = {"android.view.View#setPreferKeepClearRects",
250                      "android.view.View#setPreferKeepClear"})
testSetPreferKeepClearCombinesWithMultipleRects()251     public void testSetPreferKeepClearCombinesWithMultipleRects() throws Exception {
252         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
253         final TestActivity activity = mTestSession.getActivity();
254 
255         final Rect viewBounds = new Rect(0, 0, 60, 60);
256         final View v = createTestViewInActivity(activity, viewBounds);
257         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS));
258         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS,
259                 () -> getKeepClearRectsForActivity(activity));
260 
261         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true));
262         final List<Rect> combinedRects = new ArrayList<>(TEST_KEEP_CLEAR_RECTS);
263         combinedRects.add(viewBounds);
264         assertSameElementsEventually(combinedRects, () -> getKeepClearRectsForActivity(activity));
265 
266         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(false));
267         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS,
268                 () -> getKeepClearRectsForActivity(activity));
269     }
270 
271     @Test
272     @ApiTest(apis = {"android.view.View#setPreferKeepClearRects",
273                      "android.view.View#setPreferKeepClear"})
testIgnoreKeepClearRectsFromGoneViews()274     public void testIgnoreKeepClearRectsFromGoneViews() throws Exception {
275         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
276         final TestActivity activity = mTestSession.getActivity();
277 
278         final Rect viewBounds = new Rect(0, 0, 60, 60);
279         final View v = createTestViewInActivity(activity, viewBounds);
280         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true));
281         assertSameElementsEventually(Arrays.asList(viewBounds),
282                 () -> getKeepClearRectsForActivity(activity));
283 
284         mTestSession.runOnMainSyncAndWait(() -> v.setVisibility(View.GONE));
285         assertSameElementsEventually(EMPTY_LIST, () -> getKeepClearRectsForActivity(activity));
286 
287         mTestSession.runOnMainSyncAndWait(() -> v.setVisibility(View.VISIBLE));
288         assertSameElementsEventually(Arrays.asList(viewBounds),
289                 () -> getKeepClearRectsForActivity(activity));
290 
291         mTestSession.runOnMainSyncAndWait(() -> {
292             v.setPreferKeepClear(false);
293             v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS);
294         });
295         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS,
296                 () -> getKeepClearRectsForActivity(activity));
297 
298         mTestSession.runOnMainSyncAndWait(() -> v.setVisibility(View.GONE));
299         assertSameElementsEventually(EMPTY_LIST, () -> getKeepClearRectsForActivity(activity));
300 
301         mTestSession.runOnMainSyncAndWait(() -> v.setVisibility(View.VISIBLE));
302         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS,
303                 () -> getKeepClearRectsForActivity(activity));
304 
305         final Rect viewBounds2 = new Rect(60, 60, 90, 90);
306         final View v2 = createTestViewInActivity(activity, viewBounds2);
307         mTestSession.runOnMainSyncAndWait(() -> v2.setPreferKeepClear(true));
308 
309         final List<Rect> expected = new ArrayList(TEST_KEEP_CLEAR_RECTS);
310         expected.add(viewBounds2);
311         assertSameElementsEventually(expected, () -> getKeepClearRectsForActivity(activity));
312 
313         mTestSession.runOnMainSyncAndWait(() -> v.setVisibility(View.GONE));
314         assertSameElementsEventually(Arrays.asList(viewBounds2),
315                 () -> getKeepClearRectsForActivity(activity));
316 
317         mTestSession.runOnMainSyncAndWait(() -> {
318             v.setVisibility(View.VISIBLE);
319             v2.setVisibility(View.GONE);
320         });
321         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS,
322                 () -> getKeepClearRectsForActivity(activity));
323 
324         mTestSession.runOnMainSyncAndWait(() -> {
325             v.setVisibility(View.VISIBLE);
326             v2.setVisibility(View.VISIBLE);
327         });
328         assertSameElementsEventually(expected, () -> getKeepClearRectsForActivity(activity));
329     }
330 
331     @Test
332     @ApiTest(apis = {"android.view.View#setPreferKeepClear"})
testIgnoreKeepClearRectsFromDetachedViews()333     public void testIgnoreKeepClearRectsFromDetachedViews() throws Exception {
334         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
335         final TestActivity activity = mTestSession.getActivity();
336 
337         final Rect viewBounds = new Rect(0, 0, 60, 60);
338         final View v = createTestViewInActivity(activity, viewBounds);
339         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true));
340         assertSameElementsEventually(Arrays.asList(viewBounds),
341                 () -> getKeepClearRectsForActivity(activity));
342 
343         mTestSession.runOnMainSyncAndWait(() -> ((ViewGroup) v.getParent()).removeView(v));
344         assertSameElementsEventually(EMPTY_LIST, () -> getKeepClearRectsForActivity(activity));
345     }
346 
347     @Test
testFocusedViewDeclaredAsKeepClearArea()348     public void testFocusedViewDeclaredAsKeepClearArea() throws Exception {
349         assumeTrue(ViewConfiguration.get(mContext).isPreferKeepClearForFocusEnabled());
350 
351         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
352         final TestActivity activity = mTestSession.getActivity();
353 
354         final Rect viewBounds = new Rect(0, 0, 60, 60);
355         final View v = createTestViewInActivity(activity, viewBounds);
356         assertSameElementsEventually(EMPTY_LIST, () -> getKeepClearRectsForActivity(activity));
357 
358         mTestSession.runOnMainSyncAndWait(() -> {
359             v.setFocusableInTouchMode(true);
360             v.setFocusable(true);
361             v.requestFocus();
362         });
363 
364         assertSameElementsEventually(Arrays.asList(viewBounds),
365                 () -> getKeepClearRectsForActivity(activity));
366 
367         mTestSession.runOnMainSyncAndWait(() -> v.setFocusable(false));
368         assertSameElementsEventually(EMPTY_LIST, () -> getKeepClearRectsForActivity(activity));
369     }
370 
371     @Test
372     @ApiTest(apis = {"android.view.View#setPreferKeepClearRects"})
testKeepClearRectsGetTranslatedToWindowSpace()373     public void testKeepClearRectsGetTranslatedToWindowSpace() throws Exception {
374         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
375         final TestActivity activity = mTestSession.getActivity();
376 
377         final Rect viewBounds = new Rect(30, 30, 60, 60);
378         final View v = createTestViewInActivity(activity, viewBounds);
379         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS));
380         final List<Rect> expected = new ArrayList();
381         for (Rect r : TEST_KEEP_CLEAR_RECTS) {
382             Rect newRect = new Rect(r);
383             newRect.offset(viewBounds.left, viewBounds.top);
384             expected.add(newRect);
385         }
386 
387         assertSameElementsEventually(expected, () -> getKeepClearRectsForActivity(activity));
388     }
389 
390     @Test
391     @ApiTest(apis = {"android.view.View#setPreferKeepClear",
392                      "android.view.View#setPreferKeepClearRects"})
testSetKeepClearRectsOnDisplaySingleWindow()393     public void testSetKeepClearRectsOnDisplaySingleWindow() throws Exception {
394         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
395         final TestActivity activity = mTestSession.getActivity();
396 
397         final Rect keepClearRect = new Rect(0, 0, 25, 25);
398         final View v = createTestViewInActivity(activity, keepClearRect);
399         final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay();
400         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClear(true));
401         assertSameElementsEventually(Arrays.asList(keepClearRect),
402                 () -> getKeepClearRectsForActivity(activity));
403 
404         mTestSession.runOnMainSyncAndWait(() -> {
405             v.setPreferKeepClear(false);
406             v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS);
407         });
408         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS,
409                 () -> getKeepClearRectsForActivity(activity));
410 
411         final List<Rect> expectedRectsOnDisplay = new ArrayList<Rect>();
412         expectedRectsOnDisplay.addAll(prevKeepClearRectsOnDisplay);
413         expectedRectsOnDisplay.addAll(
414                 getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS, activity.getComponentName()));
415         assertSameElementsEventually(expectedRectsOnDisplay,
416                 () -> getKeepClearRectsOnDefaultDisplay());
417     }
418 
419     @Test
420     @ApiTest(apis = {"android.view.View#setPreferKeepClearRects"})
testFinishingActivityRemovesItsKeepClearRects()421     public void testFinishingActivityRemovesItsKeepClearRects() throws Exception {
422         final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay();
423 
424         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
425         final TestActivity activity = mTestSession.getActivity();
426 
427         final Rect viewBounds = new Rect(0, 0, 60, 60);
428         final View v = createTestViewInActivity(activity, viewBounds);
429         mTestSession.runOnMainSyncAndWait(() -> v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS));
430         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS,
431                 () -> getKeepClearRectsForActivity(activity));
432 
433         activity.finishAndRemoveTask();
434         assertSameElementsEventually(prevKeepClearRectsOnDisplay,
435                 () -> getKeepClearRectsOnDefaultDisplay());
436     }
437 
438     @Test
439     @ApiTest(apis = {"android.view.View#setPreferKeepClear"})
testKeepClearRectsOnDisplayTwoWindows()440     public void testKeepClearRectsOnDisplayTwoWindows() throws Exception {
441         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
442         final TestActivity activity = mTestSession.getActivity();
443 
444         final Rect viewBounds = new Rect(0, 0, 25, 25);
445         final View v1 = createTestViewInActivity(activity, viewBounds);
446         mTestSession.runOnMainSyncAndWait(() -> v1.setPreferKeepClear(true));
447         assertSameElementsEventually(Arrays.asList(viewBounds),
448                 () -> getKeepClearRectsForActivity(activity));
449 
450         final String title = "KeepClearRectsTestWindow";
451         mTestSession.runOnMainSyncAndWait(() -> {
452             final View testView = new View(activity);
453             testView.setPreferKeepClear(true);
454             testView.setBackgroundColor(Color.argb(20, 255, 0, 0));
455             WindowManager.LayoutParams params = new WindowManager.LayoutParams();
456             params.gravity = Gravity.TOP | Gravity.START;
457             params.width = 50;
458             params.height = 50;
459             params.setTitle(title);
460             activity.getWindowManager().addView(testView, params);
461         });
462         mWmState.waitAndAssertWindowSurfaceShown(title, true);
463 
464         assertSameElementsEventually(Arrays.asList(viewBounds),
465                 () -> getKeepClearRectsForActivity(activity));
466     }
467 
468     @Test
469     @ApiTest(apis = {"android.view.View#setPreferKeepClear",
470                      "android.view.View#setPreferKeepClearRects"})
testKeepClearRectsOnDisplayTwoFullscreenActivities()471     public void testKeepClearRectsOnDisplayTwoFullscreenActivities() throws Exception {
472         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
473         final TestActivity activity1 = mTestSession.getActivity();
474 
475         final Rect viewBounds = new Rect(0, 0, 25, 25);
476         final View v1 = createTestViewInActivity(activity1, viewBounds);
477         final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay();
478         mTestSession.runOnMainSyncAndWait(() -> v1.setPreferKeepClear(true));
479         assertSameElementsEventually(Arrays.asList(viewBounds),
480                 () -> getKeepClearRectsForActivity(activity1));
481 
482         final TestActivitySession<TranslucentTestActivity> translucentTestSession =
483                 createManagedTestActivitySession();
484         translucentTestSession.launchTestActivityOnDisplaySync(
485                 TranslucentTestActivity.class, DEFAULT_DISPLAY);
486         final TestActivity activity2 = translucentTestSession.getActivity();
487 
488         final View v2 = createTestViewInActivity(activity2);
489         mTestSession.runOnMainSyncAndWait(() -> v2.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS));
490         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS,
491                 () -> getKeepClearRectsForActivity(activity2));
492 
493         mWmState.assertVisibility(activity1.getComponentName(), true);
494         mWmState.assertVisibility(activity2.getComponentName(), true);
495 
496         // Since both activities are fullscreen, WM only takes the keep clear areas from the top one
497         final List<Rect> expectedRectsOnDisplay = new ArrayList<Rect>();
498         expectedRectsOnDisplay.addAll(prevKeepClearRectsOnDisplay);
499         expectedRectsOnDisplay.addAll(getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS,
500                 activity2.getComponentName()));
501         assertSameElementsEventually(expectedRectsOnDisplay,
502                 () -> getKeepClearRectsOnDefaultDisplay());
503     }
504 
505     @Test
506     @ApiTest(apis = {"android.view.View#setPreferKeepClear",
507                      "android.view.View#setPreferKeepClearRects"})
testDisplayHasKeepClearRectsOnlyFromVisibleWindows()508     public void testDisplayHasKeepClearRectsOnlyFromVisibleWindows() throws Exception {
509         final TestActivitySession<TranslucentTestActivity> translucentTestSession =
510                 createManagedTestActivitySession();
511         translucentTestSession.launchTestActivityOnDisplaySync(
512                 TranslucentTestActivity.class, DEFAULT_DISPLAY);
513         final TestActivity activity1 = translucentTestSession.getActivity();
514         final Rect viewBounds = new Rect(0, 0, 25, 25);
515         final View v1 = createTestViewInActivity(activity1, viewBounds);
516         final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay();
517         translucentTestSession.runOnMainSyncAndWait(() -> v1.setPreferKeepClear(true));
518 
519         // Add keep-clear rects in the activity
520         final List<Rect> expectedRectsOnDisplay = new ArrayList<Rect>();
521         expectedRectsOnDisplay.addAll(prevKeepClearRectsOnDisplay);
522         expectedRectsOnDisplay.addAll(getRectsInScreenSpace(Arrays.asList(viewBounds),
523                 activity1.getComponentName()));
524         assertSameElementsEventually(expectedRectsOnDisplay,
525                 () -> getKeepClearRectsOnDefaultDisplay());
526 
527         // Start an opaque activity on top
528         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
529         final TestActivity activity2 = mTestSession.getActivity();
530 
531         // Add keep-clear rects in the opaque activity
532         final View v2 = createTestViewInActivity(activity2);
533         mTestSession.runOnMainSyncAndWait(() -> v2.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS));
534         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS,
535                 () -> getKeepClearRectsForActivity(activity2));
536 
537         mWmState.waitAndAssertVisibilityGone(activity1.getComponentName());
538         mWmState.assertVisibility(activity2.getComponentName(), true);
539 
540         // Only the opaque activity's keep-clear areas should be reported on the display
541         expectedRectsOnDisplay.clear();
542         expectedRectsOnDisplay.addAll(prevKeepClearRectsOnDisplay);
543         expectedRectsOnDisplay.addAll(getRectsInScreenSpace(
544                     TEST_KEEP_CLEAR_RECTS, activity2.getComponentName()));
545         assertSameElementsEventually(expectedRectsOnDisplay,
546                 () -> getKeepClearRectsOnDefaultDisplay());
547     }
548 
549     @Test
550     @ApiTest(apis = {"android.view.View#setPreferKeepClearRects"})
testDisplayHasKeepClearAreasFromTwoActivitiesInSplitscreen()551     public void testDisplayHasKeepClearAreasFromTwoActivitiesInSplitscreen() throws Exception {
552         assumeTrue("Skipping test: no split multi-window support",
553                 supportsSplitScreenMultiWindow());
554 
555         startKeepClearActivitiesInSplitscreen(KEEP_CLEAR_RECTS_ACTIVITY,
556                 KEEP_CLEAR_RECTS_ACTIVITY2, Collections.emptyList(), Collections.emptyList());
557         final List<Rect> prevKeepClearRectsOnDisplay = getKeepClearRectsOnDefaultDisplay();
558 
559         removeRootTask(mWmState.getTaskByActivity(KEEP_CLEAR_RECTS_ACTIVITY).getTaskId());
560         removeRootTask(mWmState.getTaskByActivity(KEEP_CLEAR_RECTS_ACTIVITY2).getTaskId());
561 
562         startKeepClearActivitiesInSplitscreen(KEEP_CLEAR_RECTS_ACTIVITY,
563                 KEEP_CLEAR_RECTS_ACTIVITY2, TEST_KEEP_CLEAR_RECTS, TEST_KEEP_CLEAR_RECTS_2);
564 
565         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS,
566                 () -> getKeepClearRectsForActivity(KEEP_CLEAR_RECTS_ACTIVITY));
567         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS_2,
568                 () -> getKeepClearRectsForActivity(KEEP_CLEAR_RECTS_ACTIVITY2));
569 
570         final List<Rect> expected = new ArrayList();
571         expected.addAll(prevKeepClearRectsOnDisplay);
572         expected.addAll(getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS, KEEP_CLEAR_RECTS_ACTIVITY));
573         expected.addAll(getRectsInScreenSpace(TEST_KEEP_CLEAR_RECTS_2, KEEP_CLEAR_RECTS_ACTIVITY2));
574         assertSameElementsEventually(expected, () -> getKeepClearRectsOnDefaultDisplay());
575     }
576 
startKeepClearActivitiesInSplitscreen(ComponentName activity1, ComponentName activity2, List<Rect> keepClearRects1, List<Rect> keepClearRects2)577     private void startKeepClearActivitiesInSplitscreen(ComponentName activity1,
578             ComponentName activity2, List<Rect> keepClearRects1, List<Rect> keepClearRects2) {
579         final LaunchActivityBuilder activityBuilder1 = getLaunchActivityBuilder()
580                 .setUseInstrumentation()
581                 .setTargetActivity(activity1)
582                 .setIntentExtra(extra -> {
583                     extra.putParcelableArrayList(EXTRA_KEEP_CLEAR_RECTS,
584                             new ArrayList(keepClearRects1));
585                 });
586 
587         final LaunchActivityBuilder activityBuilder2 = getLaunchActivityBuilder()
588                 .setUseInstrumentation()
589                 .setTargetActivity(activity2)
590                 .setIntentExtra(extra -> {
591                     extra.putParcelableArrayList(EXTRA_KEEP_CLEAR_RECTS,
592                             new ArrayList(keepClearRects2));
593                 });
594 
595         launchActivitiesInSplitScreen(activityBuilder1, activityBuilder2);
596 
597         waitAndAssertResumedActivity(activity1, activity1 + " must be resumed");
598         waitAndAssertResumedActivity(activity2, activity2 + " must be resumed");
599         mWmState.assertVisibility(activity1, true);
600         mWmState.assertVisibility(activity2, true);
601     }
602 
603     @Test
604     @ApiTest(apis = {"android.view.View#setUnrestrictedPreferKeepClearRects",
605                      "android.view.View#setPreferKeepClearRects"})
testUnrestrictedKeepClearRects()606     public void testUnrestrictedKeepClearRects() throws Exception {
607         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
608         final TestActivity activity = mTestSession.getActivity();
609 
610         final View v = createTestViewInActivity(activity);
611         mTestSession.runOnMainSyncAndWait(() -> {
612             v.setUnrestrictedPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS);
613         });
614 
615         assertSameElementsEventually(EMPTY_LIST, () -> getKeepClearRectsForActivity(activity));
616         assertSameElementsEventually(EMPTY_LIST,
617                 () -> getUnrestrictedKeepClearRectsForActivity(activity));
618 
619         mTestSession.runOnMainSyncAndWait(() -> {
620             v.setPreferKeepClearRects(TEST_KEEP_CLEAR_RECTS);
621         });
622 
623         assertSameElementsEventually(TEST_KEEP_CLEAR_RECTS,
624                 () -> getKeepClearRectsForActivity(activity));
625         assertSameElementsEventually(EMPTY_LIST,
626                 () -> getUnrestrictedKeepClearRectsForActivity(activity));
627     }
628 
629     @Test
testAccessibilityFocusCreatesKeepClearRect()630     public void testAccessibilityFocusCreatesKeepClearRect() throws Exception {
631         assumeTrue(ViewConfiguration.get(mContext).isPreferKeepClearForFocusEnabled());
632 
633         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
634         final TestActivity activity = mTestSession.getActivity();
635 
636         mWmState.setSuppressAccessibilityServices(false);
637         mAccessibilityServiceRule.enableService();
638 
639         final View v = createTestViewInActivity(activity, TEST_VIEW_BOUNDS);
640         v.setFocusable(false);
641 
642         mTestSession.runOnMainSyncAndWait(() -> {
643             v.performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
644         });
645 
646         assertSameElementsEventually(Collections.singletonList(TEST_VIEW_BOUNDS),
647                 () -> getKeepClearRectsForActivity(activity));
648     }
649 
650     @Test
testAccessibilityAndInputFocusCreateKeepClearRects()651     public void testAccessibilityAndInputFocusCreateKeepClearRects() throws Exception {
652         assumeTrue(ViewConfiguration.get(mContext).isPreferKeepClearForFocusEnabled());
653 
654         mTestSession.launchTestActivityOnDisplaySync(TestActivity.class, DEFAULT_DISPLAY);
655         final TestActivity activity = mTestSession.getActivity();
656 
657         mWmState.setSuppressAccessibilityServices(false);
658         mAccessibilityServiceRule.enableService();
659 
660         final Rect a11yFocusViewBounds = new Rect(30, 0, 50, 25);
661         final Rect inputFocusViewBounds = new Rect(0, 0, 25, 25);
662 
663         final View a11yFocusView = createTestViewInActivity(activity, a11yFocusViewBounds);
664         final View inputFocusView = createTestViewInActivity(activity, inputFocusViewBounds);
665 
666         mTestSession.runOnMainSyncAndWait(() -> {
667             a11yFocusView.performAccessibilityAction(
668                     AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
669 
670             inputFocusView.setFocusableInTouchMode(true);
671             inputFocusView.setFocusable(true);
672             inputFocusView.requestFocus();
673         });
674 
675         assertSameElementsEventually(Arrays.asList(a11yFocusViewBounds, inputFocusViewBounds),
676                 () -> getKeepClearRectsForActivity(activity));
677     }
678 
createTestViewInActivity(TestActivity activity)679     private View createTestViewInActivity(TestActivity activity) {
680         return createTestViewInActivity(activity, TEST_VIEW_BOUNDS);
681     }
682 
createTestViewInActivity(TestActivity activity, Rect viewBounds)683     private View createTestViewInActivity(TestActivity activity, Rect viewBounds) {
684         final View newView = new View(activity);
685         final LayoutParams params = new LayoutParams(viewBounds.width(), viewBounds.height());
686         params.leftMargin = viewBounds.left;
687         params.topMargin = viewBounds.top;
688         mTestSession.runOnMainSyncAndWait(() -> {
689             activity.addView(newView, params);
690         });
691         waitForIdle();
692         return newView;
693     }
694 
getKeepClearRectsForActivity(Activity activity)695     private List<Rect> getKeepClearRectsForActivity(Activity activity) {
696         return getKeepClearRectsForActivity(activity.getComponentName());
697     }
698 
getKeepClearRectsForActivity(ComponentName activityComponent)699     private List<Rect> getKeepClearRectsForActivity(ComponentName activityComponent) {
700         mWmState.computeState();
701         return mWmState.getWindowState(activityComponent).getKeepClearRects();
702     }
703 
getKeepClearRectsOnDefaultDisplay()704     private List<Rect> getKeepClearRectsOnDefaultDisplay() {
705         mWmState.computeState();
706         return mWmState.getDisplay(DEFAULT_DISPLAY).getKeepClearRects();
707     }
708 
getUnrestrictedKeepClearRectsForActivity(Activity activity)709     private List<Rect> getUnrestrictedKeepClearRectsForActivity(Activity activity) {
710         mWmState.computeState();
711         return mWmState.getWindowState(activity.getComponentName()).getUnrestrictedKeepClearRects();
712     }
713 
714     public static class TestActivity extends FocusableActivity {
715         private RelativeLayout mRootView;
716 
addView(View v, LayoutParams params)717         public void addView(View v, LayoutParams params) {
718             mRootView.addView(v, params);
719         }
720 
721         @Override
onCreate(Bundle savedInstanceState)722         protected void onCreate(Bundle savedInstanceState) {
723             super.onCreate(savedInstanceState);
724             if (getIntent().getBooleanExtra(USE_KEEP_CLEAR_ATTR_LAYOUT, false)) {
725                 setContentView(R.layout.keep_clear_attr_activity);
726             } else {
727                 setContentView(R.layout.keep_clear_rects_activity);
728             }
729             mRootView = findViewById(R.id.root);
730 
731             getWindow().setDecorFitsSystemWindows(false);
732         }
733     }
734 
735     public static class TranslucentTestActivity extends TestActivity {}
736 
getRectsInScreenSpace(List<Rect> rects, ComponentName componentName)737     private List<Rect> getRectsInScreenSpace(List<Rect> rects, ComponentName componentName) {
738         mWmState.computeState();
739         final WindowManagerState.WindowState windowState =
740                 mWmState.getWindowState(componentName);
741         final List<Rect> result = new ArrayList<>();
742         for (Rect r : rects) {
743             Rect rectInScreenSpace = new Rect(r);
744             rectInScreenSpace.offset(windowState.getFrame().left, windowState.getFrame().top);
745             result.add(rectInScreenSpace);
746         }
747         return result;
748     }
749 
assertSameElementsEventually(List<T> expected, Callable<List<T>> actual)750     private static <T> void assertSameElementsEventually(List<T> expected, Callable<List<T>> actual)
751             throws Exception {
752         PollingCheck.check("Lists do not have the same elements."
753                 + "Expected=" + expected + ", actual=" + actual.call(),
754                 SAME_ELEMENT_ASSERTION_TIMEOUT,
755                 () -> hasSameElements(expected, actual.call()));
756     }
757 
hasSameElements(List<T> fst, List<T> snd)758     private static <T> boolean hasSameElements(List<T> fst, List<T> snd) {
759         if (fst.size() != snd.size()) return false;
760 
761         for (T a : fst) {
762             if (!snd.contains(a)) {
763                 return false;
764             }
765         }
766         return true;
767     }
768 }
769