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; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 21 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 22 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 23 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; 24 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; 25 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; 26 import static android.server.wm.RoundedCornerTests.TestActivity.EXTRA_ORIENTATION; 27 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 28 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 29 30 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 31 32 import static org.junit.Assert.assertEquals; 33 import static org.junit.Assert.assertNotNull; 34 import static org.junit.Assert.assertTrue; 35 import static org.junit.Assume.assumeFalse; 36 37 import android.app.Activity; 38 import android.content.Intent; 39 import android.graphics.Rect; 40 import android.os.Bundle; 41 import android.platform.test.annotations.Presubmit; 42 import android.view.Gravity; 43 import android.view.View; 44 import android.view.Window; 45 import android.view.WindowInsets; 46 import android.view.WindowManager; 47 import android.view.WindowMetrics; 48 49 import androidx.test.rule.ActivityTestRule; 50 51 import com.android.compatibility.common.util.PollingCheck; 52 53 import org.junit.Rule; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.junit.runners.Parameterized; 57 58 @Presubmit 59 @RunWith(Parameterized.class) 60 public class PrivacyIndicatorBoundsTests extends ActivityManagerTestBase { 61 62 private static final String TAG = PrivacyIndicatorBoundsTests.class.getSimpleName(); 63 private static final long TIMEOUT_MS = 1000; 64 65 @Parameterized.Parameters(name= "{1}({0})") data()66 public static Object[][] data() { 67 return new Object[][]{ 68 {SCREEN_ORIENTATION_PORTRAIT, "SCREEN_ORIENTATION_PORTRAIT"}, 69 {SCREEN_ORIENTATION_LANDSCAPE, "SCREEN_ORIENTATION_LANDSCAPE"}, 70 {SCREEN_ORIENTATION_REVERSE_LANDSCAPE, "SCREEN_ORIENTATION_REVERSE_LANDSCAPE"}, 71 {SCREEN_ORIENTATION_REVERSE_PORTRAIT, "SCREEN_ORIENTATION_REVERSE_PORTRAIT"}, 72 }; 73 } 74 75 @Parameterized.Parameter(0) 76 public int orientation; 77 78 @Parameterized.Parameter(1) 79 public String orientationName; 80 81 @Rule 82 public final ActivityTestRule<TestActivity> mTestActivity = 83 new ActivityTestRule<>(TestActivity.class, false /* initialTouchMode */, 84 false /* launchActivity */); 85 86 87 @Test testStaticBoundsAreNotNull()88 public void testStaticBoundsAreNotNull() { 89 // TODO(b/187757919): Allow Automotive to skip this test until privacy chip is implemented 90 // in immersive mode 91 assumeFalse(isCar()); 92 assumeFalse(isWatch()); 93 94 final PrivacyIndicatorBoundsTests.TestActivity activity = mTestActivity.launchActivity( 95 new Intent().putExtra(EXTRA_ORIENTATION, orientation)); 96 getInstrumentation().runOnMainSync(() -> { 97 activity.addChildWindow(); 98 }); 99 100 final View childWindowRoot = activity.getChildWindowRoot(); 101 PollingCheck.waitFor(TIMEOUT_MS, () -> childWindowRoot.getWidth() > 0); 102 PollingCheck.waitFor(TIMEOUT_MS, () -> activity.getDispatchedInsets() != null); 103 mWmState.waitForValidState(mTestActivity.getActivity().getComponentName()); 104 WindowInsets insets = activity.getDispatchedInsets(); 105 assertNotNull(insets); 106 Rect screenBounds = activity.getScreenBounds(); 107 assertNotNull(screenBounds); 108 Rect bounds = insets.getPrivacyIndicatorBounds(); 109 assertNotNull(bounds); 110 final int windowingMode = mWmState 111 .getTaskDisplayArea(mTestActivity.getActivity().getComponentName()) 112 .getWindowingMode(); 113 final boolean inMultiWindowMode = windowingMode != WINDOWING_MODE_FULLSCREEN 114 && windowingMode != WINDOWING_MODE_UNDEFINED; 115 if (!inMultiWindowMode) { 116 // Multi-window environments may place the indicator bounds somewhere other than the 117 // top (e.g. desktops may decide that the bottom-right corner has the highest visual 118 // priority). Other windowing modes 119 assertEquals(bounds.top, 0); 120 } 121 // TODO 188788786: Figure out why the screen bounds are different in cuttlefish, 122 // causing failures 123 // assertTrue(bounds + " not contained in " + screenBounds, screenBounds.contains(bounds)); 124 assertTrue(bounds.left >= 0); 125 assertTrue(bounds.right >= 0); 126 } 127 128 public static class TestActivity extends Activity { 129 static final String EXTRA_ORIENTATION = "extra.orientation"; 130 131 private View mChildWindowRoot; 132 private WindowInsets mDispatchedInsets; 133 private Rect mScreenBounds = null; 134 135 @Override onCreate(Bundle savedInstanceState)136 protected void onCreate(Bundle savedInstanceState) { 137 super.onCreate(savedInstanceState); 138 getWindow().requestFeature(Window.FEATURE_NO_TITLE); 139 getWindow().getDecorView().getWindowInsetsController().hide( 140 android.view.WindowInsets.Type.statusBars()); 141 getWindow().getAttributes().layoutInDisplayCutoutMode = 142 LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 143 if (getIntent() != null) { 144 setRequestedOrientation(getIntent().getIntExtra( 145 EXTRA_ORIENTATION, SCREEN_ORIENTATION_UNSPECIFIED)); 146 } 147 148 } 149 addChildWindow()150 void addChildWindow() { 151 final WindowMetrics windowMetrics = getWindowManager().getMaximumWindowMetrics(); 152 mScreenBounds = windowMetrics.getBounds(); 153 final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(); 154 attrs.x = mScreenBounds.left; 155 attrs.y = mScreenBounds.top; 156 attrs.width = mScreenBounds.width(); 157 attrs.height = mScreenBounds.height(); 158 attrs.gravity = Gravity.LEFT | Gravity.TOP; 159 attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; 160 attrs.flags = FLAG_NOT_FOCUSABLE; 161 attrs.setFitInsetsTypes(0); 162 mChildWindowRoot = new View(this); 163 mChildWindowRoot.setOnApplyWindowInsetsListener( 164 (v, insets) -> { 165 mDispatchedInsets = insets; 166 return insets; 167 }); 168 getWindowManager().addView(mChildWindowRoot, attrs); 169 } 170 getChildWindowRoot()171 View getChildWindowRoot() { 172 return mChildWindowRoot; 173 } 174 getDispatchedInsets()175 WindowInsets getDispatchedInsets() { 176 return mDispatchedInsets; 177 } 178 getScreenBounds()179 Rect getScreenBounds() { 180 return mScreenBounds; 181 } 182 } 183 } 184