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