Support rotation on secondary displays (2/N)
Move partial operations of rotation/orientation from
PhoneWindowManager to DisplayRotation.
Bug: 111361251
Test: go/wm-smoke
Test: atest FrameworksServicesTests:PhoneWindowManagerLayoutTest
Test: atest FrameworksServicesTests:PhoneWindowManagerInsetsTest
Change-Id: I7507e25f14e7fd1733ffd2a71789673d6a7ee17d
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
new file mode 100644
index 0000000..2074ecf
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
+import android.view.Surface;
+
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.policy.WindowManagerPolicy.RotationSource;
+
+import java.io.PrintWriter;
+
+/**
+ * Defines the mapping between orientation and rotation of a display.
+ */
+public class DisplayRotation {
+ private final DisplayContent mDisplayContent;
+ private final WindowManagerPolicy mPolicy;
+ private final Context mContext;
+ private RotationSource mRotationSource;
+
+ int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ int mLandscapeRotation; // default landscape
+ int mSeascapeRotation; // "other" landscape, 180 degrees from mLandscapeRotation
+ int mPortraitRotation; // default portrait
+ int mUpsideDownRotation; // "other" portrait
+
+ DisplayRotation(DisplayContent displayContent, WindowManagerPolicy policy, Context context) {
+ mDisplayContent = displayContent;
+ mPolicy = policy;
+ mContext = context;
+ }
+
+ void configure() {
+ mRotationSource = mPolicy.getRotationSource(mDisplayContent.getDisplayId());
+
+ final int width = mDisplayContent.mBaseDisplayWidth;
+ final int height = mDisplayContent.mBaseDisplayHeight;
+ final Resources res = mContext.getResources();
+ if (width > height) {
+ mLandscapeRotation = Surface.ROTATION_0;
+ mSeascapeRotation = Surface.ROTATION_180;
+ if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
+ mPortraitRotation = Surface.ROTATION_90;
+ mUpsideDownRotation = Surface.ROTATION_270;
+ } else {
+ mPortraitRotation = Surface.ROTATION_270;
+ mUpsideDownRotation = Surface.ROTATION_90;
+ }
+ } else {
+ mPortraitRotation = Surface.ROTATION_0;
+ mUpsideDownRotation = Surface.ROTATION_180;
+ if (res.getBoolean(com.android.internal.R.bool.config_reverseDefaultRotation)) {
+ mLandscapeRotation = Surface.ROTATION_270;
+ mSeascapeRotation = Surface.ROTATION_90;
+ } else {
+ mLandscapeRotation = Surface.ROTATION_90;
+ mSeascapeRotation = Surface.ROTATION_270;
+ }
+ }
+
+ mPolicy.setInitialDisplaySize(this, width, height, mDisplayContent.mBaseDisplayDensity);
+ }
+
+ public int getLandscapeRotation() {
+ return mLandscapeRotation;
+ }
+
+ public int getSeascapeRotation() {
+ return mSeascapeRotation;
+ }
+
+ public int getPortraitRotation() {
+ return mPortraitRotation;
+ }
+
+ public int getUpsideDownRotation() {
+ return mUpsideDownRotation;
+ }
+
+ public int getCurrentAppOrientation() {
+ return mCurrentAppOrientation;
+ }
+
+ public int getSensorRotation() {
+ return mRotationSource != null ? mRotationSource.getProposedRotation() : -1;
+ }
+
+ public boolean isDefaultDisplay() {
+ return mDisplayContent.isDefaultDisplay;
+ }
+
+ void setRotation(int rotation) {
+ if (mRotationSource != null) {
+ mRotationSource.setCurrentRotation(rotation);
+ }
+ }
+
+ void setCurrentOrientation(int newOrientation) {
+ if (newOrientation != mCurrentAppOrientation) {
+ mCurrentAppOrientation = newOrientation;
+ // TODO(multi-display): Separate orientation listeners.
+ if (mDisplayContent.isDefaultDisplay) {
+ mPolicy.updateOrientationListener();
+ }
+ }
+ }
+
+ public int rotationForOrientation(int orientation, int lastRotation, int preferredRotation) {
+ switch (orientation) {
+ case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
+ // Return portrait unless overridden.
+ if (isAnyPortrait(preferredRotation)) {
+ return preferredRotation;
+ }
+ return mPortraitRotation;
+
+ case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
+ // Return landscape unless overridden.
+ if (isLandscapeOrSeascape(preferredRotation)) {
+ return preferredRotation;
+ }
+ return mLandscapeRotation;
+
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+ // Return reverse portrait unless overridden.
+ if (isAnyPortrait(preferredRotation)) {
+ return preferredRotation;
+ }
+ return mUpsideDownRotation;
+
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+ // Return seascape unless overridden.
+ if (isLandscapeOrSeascape(preferredRotation)) {
+ return preferredRotation;
+ }
+ return mSeascapeRotation;
+
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+ // Return either landscape rotation.
+ if (isLandscapeOrSeascape(preferredRotation)) {
+ return preferredRotation;
+ }
+ if (isLandscapeOrSeascape(lastRotation)) {
+ return lastRotation;
+ }
+ return mLandscapeRotation;
+
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+ case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+ // Return either portrait rotation.
+ if (isAnyPortrait(preferredRotation)) {
+ return preferredRotation;
+ }
+ if (isAnyPortrait(lastRotation)) {
+ return lastRotation;
+ }
+ return mPortraitRotation;
+
+ default:
+ // For USER, UNSPECIFIED, NOSENSOR, SENSOR and FULL_SENSOR,
+ // just return the preferred orientation we already calculated.
+ if (preferredRotation >= 0) {
+ return preferredRotation;
+ }
+ return Surface.ROTATION_0;
+ }
+ }
+
+ /**
+ * Given an orientation constant and a rotation, returns true if the rotation
+ * has compatible metrics to the requested orientation. For example, if
+ * the application requested landscape and got seascape, then the rotation
+ * has compatible metrics; if the application requested portrait and got landscape,
+ * then the rotation has incompatible metrics; if the application did not specify
+ * a preference, then anything goes.
+ *
+ * @param orientation An orientation constant, such as
+ * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
+ * @param rotation The rotation to check.
+ * @return True if the rotation is compatible with the requested orientation.
+ */
+ boolean rotationHasCompatibleMetrics(int orientation, int rotation) {
+ switch (orientation) {
+ case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
+ return isAnyPortrait(rotation);
+
+ case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+ case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
+ return isLandscapeOrSeascape(rotation);
+
+ default:
+ return true;
+ }
+ }
+
+ public boolean isValidRotationChoice(final int preferredRotation) {
+ // Determine if the given app orientation is compatible with the provided rotation choice.
+ switch (mCurrentAppOrientation) {
+ case ActivityInfo.SCREEN_ORIENTATION_FULL_USER:
+ // Works with any of the 4 rotations.
+ return preferredRotation >= 0;
+
+ case ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT:
+ // It's possible for the user pref to be set at 180 because of FULL_USER. This would
+ // make switching to USER_PORTRAIT appear at 180. Provide choice to back to portrait
+ // but never to go to 180.
+ return preferredRotation == mPortraitRotation;
+
+ case ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE:
+ // Works landscape or seascape.
+ return isLandscapeOrSeascape(preferredRotation);
+
+ case ActivityInfo.SCREEN_ORIENTATION_USER:
+ case ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED:
+ // Works with any rotation except upside down.
+ return (preferredRotation >= 0) && (preferredRotation != mUpsideDownRotation);
+ }
+
+ return false;
+ }
+
+ private boolean isLandscapeOrSeascape(int rotation) {
+ return rotation == mLandscapeRotation || rotation == mSeascapeRotation;
+ }
+
+ private boolean isAnyPortrait(int rotation) {
+ return rotation == mPortraitRotation || rotation == mUpsideDownRotation;
+ }
+
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "DisplayRotation");
+ pw.println(prefix + " mCurrentAppOrientation="
+ + ActivityInfo.screenOrientationToString(mCurrentAppOrientation));
+ pw.print(prefix + " mLandscapeRotation=" + Surface.rotationToString(mLandscapeRotation));
+ pw.println(" mSeascapeRotation=" + Surface.rotationToString(mSeascapeRotation));
+ pw.print(prefix + " mPortraitRotation=" + Surface.rotationToString(mPortraitRotation));
+ pw.println(" mUpsideDownRotation=" + Surface.rotationToString(mUpsideDownRotation));
+ }
+}