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 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 
intentToSysuiVisibility(Intent intent)86         private static int intentToSysuiVisibility(Intent intent) {
87             int vis = 0;
88             vis |= intent.getBooleanExtra(ARG_LAYOUT_HIDE_NAV, false)
89                     ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0;
90             vis |= intent.getBooleanExtra(ARG_LAYOUT_FULLSCREEN, false)
91                     ? View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN : 0;
92             vis |= intent.getBooleanExtra(ARG_LAYOUT_STABLE, false)
93                     ? View.SYSTEM_UI_FLAG_LAYOUT_STABLE : 0;
94             return vis;
95         }
96     }
97 
assertContentViewLocationMatchesInsets()98     public void assertContentViewLocationMatchesInsets() {
99         TestActivity activity = mDecorActivity.getActivity();
100 
101         Insets insetsConsumedByDecor = Insets.subtract(
102                 systemWindowInsetsOrZero(activity.mLastDecorInsets),
103                 systemWindowInsetsOrZero(activity.mLastContentInsets));
104         Rect expectedContentRect = rectInWindow(activity.getWindow().getDecorView());
105         insetRect(expectedContentRect, insetsConsumedByDecor);
106 
107         Rect actualContentRect = rectInWindow(activity.findViewById(android.R.id.content));
108 
109         assertEquals("Decor consumed " + insetsConsumedByDecor + ", content rect:",
110                 expectedContentRect, actualContentRect);
111     }
112 
systemWindowInsetsOrZero(WindowInsets wi)113     public Insets systemWindowInsetsOrZero(WindowInsets wi) {
114         if (wi == null) {
115             return Insets.NONE;
116         }
117         return wi.getSystemWindowInsets();
118     }
119 
rectInWindow(View view)120     private Rect rectInWindow(View view) {
121         int[] loc = new int[2];
122         view.getLocationInWindow(loc);
123         return new Rect(loc[0], loc[1], loc[0] + view.getWidth(), loc[1] + view.getHeight());
124     }
125 
insetRect(Rect rect, Insets insets)126     private static void insetRect(Rect rect, Insets insets) {
127         rect.left += insets.left;
128         rect.top += insets.top;
129         rect.right -= insets.right;
130         rect.bottom -= insets.bottom;
131     }
132 }
133