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.server.wm.ActivityManagerTestBase.isTablet; 27 import static android.view.WindowInsets.Type.displayCutout; 28 import static android.view.WindowInsets.Type.systemBars; 29 30 import android.app.Activity; 31 import android.content.Intent; 32 import android.content.pm.ActivityInfo; 33 import android.content.pm.PackageManager; 34 import android.content.res.Configuration; 35 import android.graphics.Insets; 36 import android.graphics.Rect; 37 import android.server.wm.IgnoreOrientationRequestSession; 38 import android.server.wm.WindowManagerStateHelper; 39 import android.test.ActivityInstrumentationTestCase2; 40 import android.view.WindowInsets; 41 import android.view.WindowMetrics; 42 43 public class ConfigurationScreenLayoutTest 44 extends ActivityInstrumentationTestCase2<OrientationActivity> { 45 46 private static final int[] ORIENTATIONS = new int[] { 47 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT, 48 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, 49 ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT, 50 ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE, 51 }; 52 53 private static final int BIGGEST_LAYOUT = SCREENLAYOUT_SIZE_XLARGE 54 | SCREENLAYOUT_LONG_YES; 55 ConfigurationScreenLayoutTest()56 public ConfigurationScreenLayoutTest() { 57 super(OrientationActivity.class); 58 } 59 testScreenLayout()60 public void testScreenLayout() throws Exception { 61 if (!supportsRotation()) { 62 // test has no effect if device does not support rotation 63 tearDown(); 64 return; 65 } 66 if (isPC()) { 67 // The test skips mainly for Chromebook clamshell mode. For Chromebook clamshell mode 68 // with non-rotated landscape physical screen, the portrait window/activity has special 69 // behavior with black background on both sides to make the window/activity look 70 // portrait, which returns smaller screen layout size. 71 tearDown(); 72 return; 73 } 74 if (isTablet()) { 75 // TODO (b/228380863): re-enable it once the configuration calculation issue is resolved 76 // on taskbar devices. 77 tearDown(); 78 return; 79 } 80 // Disable IgnoreOrientationRequest feature because when it's enabled, the device would only 81 // follow physical rotations. 82 try (IgnoreOrientationRequestSession session = 83 new IgnoreOrientationRequestSession(false /* enable */)) { 84 85 // Check that all four orientations report the same configuration value. 86 for (int orientation : ORIENTATIONS) { 87 Activity activity = startOrientationActivity(orientation); 88 WindowManagerStateHelper wmState = new WindowManagerStateHelper(); 89 wmState.computeState(); 90 if (activity.isInMultiWindowMode() 91 || wmState.isTaskDisplayAreaIgnoringOrientationRequest( 92 activity.getComponentName())) { 93 // activity.setRequestedOrientation has no effect in multi-window mode. 94 // Besides, the test is not feasible when current display area is ignoring 95 // orientation since configuration will be updated, so it goes to quit this 96 // test as well. 97 tearDown(); 98 return; 99 } 100 final int expectedLayout = computeScreenLayout(activity); 101 final int expectedSize = expectedLayout & SCREENLAYOUT_SIZE_MASK; 102 final int expectedLong = expectedLayout & SCREENLAYOUT_LONG_MASK; 103 104 final Configuration config = activity.getResources().getConfiguration(); 105 final int actualSize = config.screenLayout & SCREENLAYOUT_SIZE_MASK; 106 final int actualLong = config.screenLayout & SCREENLAYOUT_LONG_MASK; 107 108 assertEquals("Expected screen size value of " + expectedSize + " but got " 109 + actualSize + " for orientation " 110 + orientation, expectedSize, actualSize); 111 assertEquals("Expected screen long value of " + expectedLong + " but got " 112 + actualLong + " for orientation " 113 + orientation, expectedLong, actualLong); 114 tearDown(); 115 } 116 } finally { 117 tearDown(); 118 } 119 } 120 hasDeviceFeature(final String requiredFeature)121 private boolean hasDeviceFeature(final String requiredFeature) { 122 return getInstrumentation().getContext() 123 .getPackageManager() 124 .hasSystemFeature(requiredFeature); 125 } 126 startOrientationActivity(int orientation)127 private Activity startOrientationActivity(int orientation) { 128 Intent intent = new Intent(); 129 intent.putExtra(OrientationActivity.EXTRA_ORIENTATION, orientation); 130 setActivityIntent(intent); 131 return getActivity(); 132 } 133 134 // Logic copied from Configuration#reduceScreenLayout(int, int, int) 135 /** 136 * Returns expected value of {@link Configuration#screenLayout} with the 137 * {@link Configuration#SCREENLAYOUT_LONG_MASK} and 138 * {@link Configuration#SCREENLAYOUT_SIZE_MASK} defined 139 */ computeScreenLayout(Activity activity)140 private int computeScreenLayout(Activity activity) { 141 final WindowInsets windowInsets = activity.getWindowManager().getCurrentWindowMetrics() 142 .getWindowInsets(); 143 final Insets insets = windowInsets.getInsets(systemBars() | displayCutout()); 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