1 /* 2 * Copyright (C) 2011 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.dpi.cts; 18 19 import static android.content.res.Configuration.SCREENLAYOUT_LONG_MASK; 20 import static android.content.res.Configuration.SCREENLAYOUT_LONG_NO; 21 import static android.content.res.Configuration.SCREENLAYOUT_LONG_YES; 22 import static android.content.res.Configuration.SCREENLAYOUT_SIZE_LARGE; 23 import static android.content.res.Configuration.SCREENLAYOUT_SIZE_MASK; 24 import static android.content.res.Configuration.SCREENLAYOUT_SIZE_NORMAL; 25 import static android.content.res.Configuration.SCREENLAYOUT_SIZE_XLARGE; 26 import static android.view.WindowInsets.Type.displayCutout; 27 import static android.view.WindowInsets.Type.systemBars; 28 29 import android.app.Activity; 30 import android.content.Intent; 31 import android.content.pm.ActivityInfo; 32 import android.content.pm.PackageManager; 33 import android.content.res.Configuration; 34 import android.graphics.Insets; 35 import android.graphics.Rect; 36 import android.server.wm.IgnoreOrientationRequestSession; 37 import android.server.wm.WindowManagerStateHelper; 38 import android.test.ActivityInstrumentationTestCase2; 39 import android.view.WindowInsets; 40 import android.view.WindowMetrics; 41 42 import com.android.window.flags.Flags; 43 44 public class ConfigurationScreenLayoutTest 45 extends ActivityInstrumentationTestCase2<OrientationActivity> { 46 47 private static final int[] ORIENTATIONS = new int[] { 48 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, 49 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, 50 ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, 51 ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE, 52 }; 53 54 private static final int BIGGEST_LAYOUT = SCREENLAYOUT_SIZE_XLARGE 55 | SCREENLAYOUT_LONG_YES; 56 ConfigurationScreenLayoutTest()57 public ConfigurationScreenLayoutTest() { 58 super(OrientationActivity.class); 59 } 60 testScreenLayout()61 public void testScreenLayout() throws Exception { 62 if (!supportsRotation()) { 63 // test has no effect if device does not support rotation 64 tearDown(); 65 return; 66 } 67 if (isPC()) { 68 // The test skips mainly for Chromebook clamshell mode. For Chromebook clamshell mode 69 // with non-rotated landscape physical screen, the portrait window/activity has special 70 // behavior with black background on both sides to make the window/activity look 71 // portrait, which returns smaller screen layout size. 72 tearDown(); 73 return; 74 } 75 // Disable IgnoreOrientationRequest feature because when it's enabled, the device would only 76 // follow physical rotations. 77 try (IgnoreOrientationRequestSession session = 78 new IgnoreOrientationRequestSession(false /* enable */)) { 79 80 // Check that all four orientations report the same configuration value. 81 for (int orientation : ORIENTATIONS) { 82 Activity activity = startOrientationActivity(orientation); 83 WindowManagerStateHelper wmState = new WindowManagerStateHelper(); 84 wmState.computeState(); 85 if (activity.isInMultiWindowMode() 86 || wmState.isTaskDisplayAreaIgnoringOrientationRequest( 87 activity.getComponentName())) { 88 // activity.setRequestedOrientation has no effect in multi-window mode. 89 // Besides, the test is not feasible when current display area is ignoring 90 // orientation since configuration will be updated, so it goes to quit this 91 // test as well. 92 tearDown(); 93 return; 94 } 95 final int expectedLayout = computeScreenLayout(activity); 96 final int expectedSize = expectedLayout & SCREENLAYOUT_SIZE_MASK; 97 final int expectedLong = expectedLayout & SCREENLAYOUT_LONG_MASK; 98 99 final Configuration config = activity.getResources().getConfiguration(); 100 final int actualSize = config.screenLayout & SCREENLAYOUT_SIZE_MASK; 101 final int actualLong = config.screenLayout & SCREENLAYOUT_LONG_MASK; 102 103 assertEquals("Expected screen size value of " + expectedSize + " but got " 104 + actualSize + " for orientation " 105 + orientation, expectedSize, actualSize); 106 assertEquals("Expected screen long value of " + expectedLong + " but got " 107 + actualLong + " for orientation " 108 + orientation, expectedLong, actualLong); 109 tearDown(); 110 } 111 } finally { 112 tearDown(); 113 } 114 } 115 hasDeviceFeature(final String requiredFeature)116 private boolean hasDeviceFeature(final String requiredFeature) { 117 return getInstrumentation().getContext() 118 .getPackageManager() 119 .hasSystemFeature(requiredFeature); 120 } 121 startOrientationActivity(int orientation)122 private Activity startOrientationActivity(int orientation) { 123 Intent intent = new Intent(); 124 intent.putExtra(OrientationActivity.EXTRA_ORIENTATION, orientation); 125 setActivityIntent(intent); 126 return getActivity(); 127 } 128 129 // Logic copied from Configuration#reduceScreenLayout(int, int, int) 130 /** 131 * Returns expected value of {@link Configuration#screenLayout} with the 132 * {@link Configuration#SCREENLAYOUT_LONG_MASK} and 133 * {@link Configuration#SCREENLAYOUT_SIZE_MASK} defined 134 */ computeScreenLayout(Activity activity)135 private int computeScreenLayout(Activity activity) { 136 final Insets insets; 137 if (!Flags.insetsDecoupledConfiguration()) { 138 final WindowInsets windowInsets = activity.getWindowManager().getCurrentWindowMetrics() 139 .getWindowInsets(); 140 insets = windowInsets.getInsets(systemBars() | displayCutout()); 141 } else { 142 insets = Insets.NONE; 143 } 144 return reduceScreenLayout(activity, insets, BIGGEST_LAYOUT); 145 } 146 reduceScreenLayout(Activity activity, Insets excludeInsets, int screenLayout)147 private int reduceScreenLayout(Activity activity, Insets excludeInsets, int screenLayout) { 148 int screenLayoutSize; 149 boolean screenLayoutLong; 150 151 final WindowMetrics windowMetrics = activity.getWindowManager().getCurrentWindowMetrics(); 152 final Rect bounds = new Rect(windowMetrics.getBounds()); 153 bounds.inset(excludeInsets); 154 155 final float density = activity.getResources().getDisplayMetrics().density; 156 final int widthDp = (int) (bounds.width() / density); 157 final int heightDp = (int) (bounds.height() / density); 158 final int longSize = Math.max(widthDp, heightDp); 159 final int shortSize = Math.min(widthDp, heightDp); 160 161 if (longSize < 470) { 162 screenLayoutSize = Configuration.SCREENLAYOUT_SIZE_SMALL; 163 screenLayoutLong = false; 164 } else { 165 if (longSize >= 960 && shortSize >= 720) { 166 screenLayoutSize = SCREENLAYOUT_SIZE_XLARGE; 167 } else if (longSize >= 640 && shortSize >= 480) { 168 screenLayoutSize = SCREENLAYOUT_SIZE_LARGE; 169 } else { 170 screenLayoutSize = SCREENLAYOUT_SIZE_NORMAL; 171 } 172 screenLayoutLong = ((longSize * 3) / 5) >= (shortSize - 1); 173 } 174 175 if (!screenLayoutLong) { 176 screenLayout = (screenLayout & ~SCREENLAYOUT_LONG_MASK) | SCREENLAYOUT_LONG_NO; 177 } 178 int curSize = screenLayout & SCREENLAYOUT_SIZE_MASK; 179 if (screenLayoutSize < curSize) { 180 screenLayout = (screenLayout & ~SCREENLAYOUT_SIZE_MASK) | screenLayoutSize; 181 } 182 return screenLayout; 183 } 184 185 /** 186 * Rotation support is indicated by explicitly having both landscape and portrait 187 * features or not listing either at all. 188 */ supportsRotation()189 private boolean supportsRotation() { 190 final boolean supportsLandscape = hasDeviceFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE); 191 final boolean supportsPortrait = hasDeviceFeature(PackageManager.FEATURE_SCREEN_PORTRAIT); 192 return (supportsLandscape && supportsPortrait) 193 || (!supportsLandscape && !supportsPortrait); 194 } 195 196 /** Checks if it is a PC device */ isPC()197 private boolean isPC() { 198 return hasDeviceFeature(PackageManager.FEATURE_PC); 199 } 200 } 201