1 /*
2  * Copyright (C) 2020 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;
18 
19 import static android.view.WindowInsets.Type.navigationBars;
20 import static android.view.WindowInsets.Type.statusBars;
21 import static android.view.WindowInsets.Type.systemBars;
22 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
23 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
24 
25 import static org.junit.Assert.assertFalse;
26 import static org.junit.Assert.assertNotNull;
27 import static org.junit.Assert.assertTrue;
28 import static org.junit.Assume.assumeFalse;
29 
30 import android.app.Activity;
31 import android.graphics.Insets;
32 import android.os.Bundle;
33 import android.view.View;
34 import android.view.Window;
35 import android.view.WindowInsets;
36 
37 import androidx.annotation.Nullable;
38 import androidx.test.platform.app.InstrumentationRegistry;
39 import androidx.test.rule.ActivityTestRule;
40 
41 import org.junit.Rule;
42 
43 import java.util.concurrent.CountDownLatch;
44 import java.util.concurrent.TimeUnit;
45 
46 public class ForceRelayoutTestBase {
47 
48     @Rule
49     public ActivityTestRule<TestActivity> mDecorActivity = new ActivityTestRule<>(
50             TestActivity.class);
51 
testRelayoutWhenInsetsChange(boolean expectRelayoutWhenInsetsChange, int softInputMode)52     void testRelayoutWhenInsetsChange(boolean expectRelayoutWhenInsetsChange, int softInputMode)
53             throws Throwable {
54         TestActivity activity = mDecorActivity.getActivity();
55         assertNotNull("test setup failed", activity.mLastContentInsets);
56         assumeFalse(Insets.NONE.equals(activity.mLastContentInsets.getInsetsIgnoringVisibility(
57                 statusBars() | navigationBars())));
58 
59         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
60             activity.mLayoutHappened = false;
61             activity.mMeasureHappened = false;
62             activity.getWindow().setSoftInputMode(softInputMode);
63             activity.getWindow().getInsetsController().hide(systemBars());
64         });
65         activity.mZeroInsets.await(180, TimeUnit.SECONDS);
66         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
67             if (expectRelayoutWhenInsetsChange) {
68                 assertTrue(activity.mLayoutHappened);
69                 assertTrue(activity.mMeasureHappened);
70             } else {
71                 assertFalse(activity.mLayoutHappened);
72                 assertFalse(activity.mMeasureHappened);
73             }
74         });
75     }
76 
77     public static class TestActivity extends Activity {
78         WindowInsets mLastContentInsets;
79         final CountDownLatch mZeroInsets = new CountDownLatch(1);
80 
81         volatile boolean mLayoutHappened;
82         volatile boolean mMeasureHappened;
83 
84         @Override
onCreate(@ullable Bundle savedInstanceState)85         protected void onCreate(@Nullable Bundle savedInstanceState) {
86             super.onCreate(savedInstanceState);
87             getWindow().requestFeature(Window.FEATURE_NO_TITLE);
88             getWindow().setDecorFitsSystemWindows(false);
89             getWindow().getAttributes().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
90             View view = new View(this) {
91                 @Override
92                 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
93                     super.onLayout(changed, left, top, right, bottom);
94                     mLayoutHappened = true;
95                 }
96 
97                 @Override
98                 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
99                     super.onMeasure(widthMeasureSpec, heightMeasureSpec);
100                     mMeasureHappened = true;
101 
102                 }
103             };
104             view.setOnApplyWindowInsetsListener((v, wi) -> {
105                 mLastContentInsets = wi;
106                 if (wi.getInsets(systemBars()).equals(Insets.NONE)) {
107 
108                     // Make sure full traversal happens before counting down.
109                     v.post(mZeroInsets::countDown);
110                 }
111                 return WindowInsets.CONSUMED;
112             });
113             setContentView(view);
114         }
115     }
116 }
117