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.insets;
18 
19 import static org.junit.Assert.assertEquals;
20 
21 import android.app.Activity;
22 import android.content.Intent;
23 import android.graphics.Insets;
24 import android.graphics.Rect;
25 import android.os.Bundle;
26 import android.view.View;
27 import android.view.ViewTreeObserver;
28 import android.view.Window;
29 import android.view.WindowInsets;
30 
31 import androidx.annotation.Nullable;
32 import androidx.test.rule.ActivityTestRule;
33 
34 import org.junit.Rule;
35 
36 import java.util.concurrent.CountDownLatch;
37 
38 public class DecorInsetTestsBase {
39 
40     public static final String ARG_DECOR_FITS_SYSTEM_WINDOWS = "decorFitsSystemWindows";
41     public static final String ARG_LAYOUT_STABLE = "flagLayoutStable";
42     public static final String ARG_LAYOUT_FULLSCREEN = "flagLayoutFullscreen";
43     public static final String ARG_LAYOUT_HIDE_NAV = "flagLayoutHideNav";
44 
45     @Rule
46     public ActivityTestRule<TestActivity> mDecorActivity = new ActivityTestRule<>(
47             TestActivity.class, false /* initialTouchMode */,
48             false /* launchActivity */);
49 
50     public static class TestActivity extends Activity {
51         WindowInsets mLastContentInsets;
52         WindowInsets mLastDecorInsets;
53         final CountDownLatch mLaidOut = new CountDownLatch(1);
54 
55         @Override
onCreate(@ullable Bundle savedInstanceState)56         protected void onCreate(@Nullable Bundle savedInstanceState) {
57             super.onCreate(savedInstanceState);
58             getWindow().requestFeature(Window.FEATURE_NO_TITLE);
59 
60             getWindow().setDecorFitsSystemWindows(
61                     getIntent().getBooleanExtra(ARG_DECOR_FITS_SYSTEM_WINDOWS, false));
62 
63             View view = new View(this);
64             view.setSystemUiVisibility(intentToSysuiVisibility(getIntent()));
65 
66             view.setOnApplyWindowInsetsListener((v, wi) -> {
67                 mLastContentInsets = wi;
68                 return WindowInsets.CONSUMED;
69             });
70             setContentView(view);
71             getWindow().getDecorView().setOnApplyWindowInsetsListener((v, wi) -> {
72                 mLastDecorInsets = wi;
73                 return v.onApplyWindowInsets(wi);
74             });
75 
76             view.getViewTreeObserver().addOnGlobalLayoutListener(
77                     new ViewTreeObserver.OnGlobalLayoutListener() {
78                 @Override
79                 public void onGlobalLayout() {
80                     view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
81                     mLaidOut.countDown();
82                 }
83             });
84         }
85 
getLastContentInsets()86         public WindowInsets getLastContentInsets() {
87             return mLastContentInsets;
88         }
89 
getLastDecorInsets()90         public WindowInsets getLastDecorInsets() {
91             return mLastDecorInsets;
92         }
93 
getLaidOut()94         public CountDownLatch getLaidOut() {
95             return mLaidOut;
96         }
97 
intentToSysuiVisibility(Intent intent)98         private static int intentToSysuiVisibility(Intent intent) {
99             int vis = 0;
100             vis |= intent.getBooleanExtra(ARG_LAYOUT_HIDE_NAV, false)
101                     ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0;
102             vis |= intent.getBooleanExtra(ARG_LAYOUT_FULLSCREEN, false)
103                     ? View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN : 0;
104             vis |= intent.getBooleanExtra(ARG_LAYOUT_STABLE, false)
105                     ? View.SYSTEM_UI_FLAG_LAYOUT_STABLE : 0;
106             return vis;
107         }
108     }
109 
assertContentViewLocationMatchesInsets()110     public void assertContentViewLocationMatchesInsets() {
111         TestActivity activity = mDecorActivity.getActivity();
112 
113         Insets insetsConsumedByDecor = Insets.subtract(
114                 systemWindowInsetsOrZero(activity.mLastDecorInsets),
115                 systemWindowInsetsOrZero(activity.mLastContentInsets));
116         Rect expectedContentRect = rectInWindow(activity.getWindow().getDecorView());
117         insetRect(expectedContentRect, insetsConsumedByDecor);
118 
119         Rect actualContentRect = rectInWindow(activity.findViewById(android.R.id.content));
120 
121         assertEquals("Decor consumed " + insetsConsumedByDecor + ", content rect:",
122                 expectedContentRect, actualContentRect);
123     }
124 
systemWindowInsetsOrZero(WindowInsets wi)125     public Insets systemWindowInsetsOrZero(WindowInsets wi) {
126         if (wi == null) {
127             return Insets.NONE;
128         }
129         return wi.getSystemWindowInsets();
130     }
131 
rectInWindow(View view)132     private Rect rectInWindow(View view) {
133         int[] loc = new int[2];
134         view.getLocationInWindow(loc);
135         return new Rect(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight());
136     }
137 
insetRect(Rect rect, Insets insets)138     private static void insetRect(Rect rect, Insets insets) {
139         rect.left += insets.left;
140         rect.top += insets.top;
141         rect.right -= insets.right;
142         rect.bottom -= insets.bottom;
143     }
144 }
145