• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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