From 49ae8b0c3e04a58ccc6fd4ea1138b2935cf7ae64 Mon Sep 17 00:00:00 2001 From: Vali Calinescu Date: Fri, 22 Apr 2022 15:28:49 +0000 Subject: [PATCH] Allow letterbox to move up/down for landscape-only apps in portrait mode Fix: 226901995 Test: atest WmTests:SizeCompatTests Change-Id: Ibe60793b381e61d56f57f8d2309754eedc84bddf --- core/res/res/values/config.xml | 32 +- core/res/res/values/symbols.xml | 7 +- .../com/android/server/wm/ActivityRecord.java | 98 ++--- .../java/com/android/server/wm/Letterbox.java | 12 +- .../server/wm/LetterboxConfiguration.java | 383 ++++++++++++++---- .../server/wm/LetterboxUiController.java | 92 ++++- .../server/wm/WindowManagerShellCommand.java | 184 +++++++-- .../com/android/server/wm/LetterboxTest.java | 2 +- .../android/server/wm/SizeCompatTests.java | 129 +++++- .../android/server/wm/WindowTestsBase.java | 19 +- 10 files changed, 751 insertions(+), 207 deletions(-) diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index df14348747347..f53f2022d5d0a 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5133,16 +5133,24 @@ --> @android:color/system_neutral2_900 - + or > 1, it is ignored and central position is used (0.5). --> 0.5 - - false + + 0.5 - + false + + + false + + - 1 + 1 + + + 1 false diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 36e470dc91b7a..eb8f8a724fe5d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4384,8 +4384,11 @@ - - + + + + + diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 9932272f7473c..43e11f10a70a8 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -7727,7 +7727,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (isFixedOrientationLetterboxAllowed || mCompatDisplayInsets != null // In fullscreen, can be letterboxed for aspect ratio. || !inMultiWindowMode()) { - updateResolvedBoundsHorizontalPosition(newParentConfiguration); + updateResolvedBoundsPosition(newParentConfiguration); } if (mVisibleRequested) { @@ -7830,39 +7830,61 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** - * Adjusts horizontal position of resolved bounds if they doesn't fill the parent using gravity + * Adjusts position of resolved bounds if they doesn't fill the parent using gravity * requested in the config or via an ADB command. For more context see {@link - * LetterboxUiController#getHorizontalPositionMultiplier(Configuration)}. + * LetterboxUiController#getHorizontalPositionMultiplier(Configuration)} and + * {@link LetterboxUiController#getVerticalPositionMultiplier(Configuration)} */ - private void updateResolvedBoundsHorizontalPosition(Configuration newParentConfiguration) { + private void updateResolvedBoundsPosition(Configuration newParentConfiguration) { final Configuration resolvedConfig = getResolvedOverrideConfiguration(); final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds(); final Rect screenResolvedBounds = mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds; final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds(); final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds(); - if (resolvedBounds.isEmpty() || parentBounds.width() == screenResolvedBounds.width()) { + if (resolvedBounds.isEmpty()) { return; } - + // Horizontal position int offsetX = 0; - if (screenResolvedBounds.width() >= parentAppBounds.width()) { - // If resolved bounds overlap with insets, center within app bounds. - offsetX = getHorizontalCenterOffset( - parentAppBounds.width(), screenResolvedBounds.width()); - } else { - float positionMultiplier = - mLetterboxUiController.getHorizontalPositionMultiplier(newParentConfiguration); - offsetX = (int) Math.ceil((parentAppBounds.width() - screenResolvedBounds.width()) - * positionMultiplier); + if (parentBounds.width() != screenResolvedBounds.width()) { + if (screenResolvedBounds.width() >= parentAppBounds.width()) { + // If resolved bounds overlap with insets, center within app bounds. + offsetX = getCenterOffset( + parentAppBounds.width(), screenResolvedBounds.width()); + } else { + float positionMultiplier = + mLetterboxUiController.getHorizontalPositionMultiplier( + newParentConfiguration); + offsetX = (int) Math.ceil((parentAppBounds.width() - screenResolvedBounds.width()) + * positionMultiplier); + } + } + + // Vertical position + int offsetY = 0; + if (parentBounds.height() != screenResolvedBounds.height()) { + + if (screenResolvedBounds.height() >= parentAppBounds.height()) { + // If resolved bounds overlap with insets, center within app bounds. + offsetY = getCenterOffset( + parentAppBounds.height(), screenResolvedBounds.height()); + } else { + float positionMultiplier = + mLetterboxUiController.getVerticalPositionMultiplier( + newParentConfiguration); + offsetY = (int) Math.ceil((parentAppBounds.height() - screenResolvedBounds.height()) + * positionMultiplier); + } } if (mSizeCompatBounds != null) { - mSizeCompatBounds.offset(offsetX, 0 /* offsetY */); + mSizeCompatBounds.offset(offsetX , offsetY); + final int dy = mSizeCompatBounds.top - resolvedBounds.top; final int dx = mSizeCompatBounds.left - resolvedBounds.left; - offsetBounds(resolvedConfig, dx, 0 /* offsetY */); + offsetBounds(resolvedConfig, dx, dy); } else { - offsetBounds(resolvedConfig, offsetX, 0 /* offsetY */); + offsetBounds(resolvedConfig, offsetX, offsetY); } // Since bounds has changed, the configuration needs to be computed accordingly. @@ -8077,14 +8099,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingBoundsWithInsets, containingBounds, desiredAspectRatio, true); - // Vertically center if orientation is landscape. Center within parent bounds with insets - // to ensure that insets do not trim height. Bounds will later be horizontally centered in - // {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation. - if (forcedOrientation == ORIENTATION_LANDSCAPE) { - final int offsetY = parentBoundsWithInsets.centerY() - resolvedBounds.centerY(); - resolvedBounds.offset(0, offsetY); - } - if (mCompatDisplayInsets != null) { mCompatDisplayInsets.getBoundsByRotation( mTmpBounds, newParentConfig.windowConfiguration.getRotation()); @@ -8107,10 +8121,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** * Resolves aspect ratio restrictions for an activity. If the bounds are restricted by - * aspect ratio, the position will be adjusted later in {@link - * updateResolvedBoundsHorizontalPosition} within parent's app bounds to balance the visual - * appearance. The policy of aspect ratio has higher priority than the requested override - * bounds. + * aspect ratio, the position will be adjusted later in {@link #updateResolvedBoundsPosition + * within parent's app bounds to balance the visual appearance. The policy of aspect ratio has + * higher priority than the requested override bounds. */ private void resolveAspectRatioRestriction(Configuration newParentConfiguration) { final Configuration resolvedConfig = getResolvedOverrideConfiguration(); @@ -8122,7 +8135,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mTmpBounds.setEmpty(); mIsAspectRatioApplied = applyAspectRatio(mTmpBounds, parentAppBounds, parentBounds); // If the out bounds is not empty, it means the activity cannot fill parent's app bounds, - // then they should be aligned later in #updateResolvedBoundsHorizontalPosition(). + // then they should be aligned later in #updateResolvedBoundsPosition() if (!mTmpBounds.isEmpty()) { resolvedBounds.set(mTmpBounds); } @@ -8256,22 +8269,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */); } - // Vertically center within parent (bounds) - this is a UX choice and exclude the horizontal - // decor if needed. Horizontal position is adjusted in - // updateResolvedBoundsHorizontalPosition. + // The position will be later adjusted in updateResolvedBoundsPosition. // Above coordinates are in "@" space, now place "*" and "#" to screen space. final boolean fillContainer = resolvedBounds.equals(containingBounds); final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left; - // If the activity is not in size compat mode, calculate vertical centering - // from the container and resolved bounds. - // If the activity is in size compat mode, calculate vertical centering - // from the container and size compat bounds. - // The container bounds contain the parent bounds offset in the display, for - // example when an activity is in the lower split of split screen. - final int screenPosY = (mSizeCompatBounds == null - ? (containerBounds.height() - resolvedBounds.height()) / 2 - : (containerBounds.height() - mSizeCompatBounds.height()) / 2) - + containerBounds.top; + final int screenPosY = fillContainer ? containerBounds.top : containerAppBounds.top; if (screenPosX != 0 || screenPosY != 0) { if (mSizeCompatBounds != null) { @@ -8331,9 +8333,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return true; } - /** @return The horizontal offset of putting the content in the center of viewport. */ - private static int getHorizontalCenterOffset(int viewportW, int contentW) { - return (int) ((viewportW - contentW + 1) * 0.5f); + /** @return The horizontal / vertical offset of putting the content in the center of viewport.*/ + private static int getCenterOffset(int viewportDim, int contentDim) { + return (int) ((viewportDim - contentDim + 1) * 0.5f); } private static void offsetBounds(Configuration inOutConfig, int offsetX, int offsetY) { @@ -9595,7 +9597,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A outBounds.bottom = dH; outBounds.right = (int) ((float) dH * dH / dW); } - outBounds.offset(getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */); + outBounds.offset(getCenterOffset(mWidth, outBounds.width()), 0 /* dy */); } outAppBounds.set(outBounds); diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index 40df02c176e59..61c09cd604134 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -72,7 +72,8 @@ public class Letterbox { private final LetterboxSurface mFullWindowSurface = new LetterboxSurface("fullWindow"); private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom }; // Reachability gestures. - private final IntConsumer mDoubleTapCallback; + private final IntConsumer mDoubleTapCallbackX; + private final IntConsumer mDoubleTapCallbackY; /** * Constructs a Letterbox. @@ -86,7 +87,8 @@ public class Letterbox { Supplier hasWallpaperBackgroundSupplier, Supplier blurRadiusSupplier, Supplier darkScrimAlphaSupplier, - IntConsumer doubleTapCallback) { + IntConsumer doubleTapCallbackX, + IntConsumer doubleTapCallbackY) { mSurfaceControlFactory = surfaceControlFactory; mTransactionFactory = transactionFactory; mAreCornersRounded = areCornersRounded; @@ -94,7 +96,8 @@ public class Letterbox { mHasWallpaperBackgroundSupplier = hasWallpaperBackgroundSupplier; mBlurRadiusSupplier = blurRadiusSupplier; mDarkScrimAlphaSupplier = darkScrimAlphaSupplier; - mDoubleTapCallback = doubleTapCallback; + mDoubleTapCallbackX = doubleTapCallbackX; + mDoubleTapCallbackY = doubleTapCallbackY; } /** @@ -264,7 +267,8 @@ public class Letterbox { @Override public boolean onDoubleTapEvent(MotionEvent e) { if (e.getAction() == MotionEvent.ACTION_UP) { - mDoubleTapCallback.accept((int) e.getX()); + mDoubleTapCallbackX.accept((int) e.getX()); + mDoubleTapCallbackY.accept((int) e.getY()); return true; } return false; diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index ad2767c41e82e..bef8da8ff9173 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -55,25 +55,48 @@ final class LetterboxConfiguration { static final int LETTERBOX_BACKGROUND_WALLPAPER = 3; /** - * Enum for Letterbox reachability position types. + * Enum for Letterbox horizontal reachability position types. * *

Order from left to right is important since it's used in {@link * #movePositionForReachabilityToNextRightStop} and {@link * #movePositionForReachabilityToNextLeftStop}. */ @Retention(RetentionPolicy.SOURCE) - @IntDef({LETTERBOX_REACHABILITY_POSITION_LEFT, LETTERBOX_REACHABILITY_POSITION_CENTER, - LETTERBOX_REACHABILITY_POSITION_RIGHT}) - @interface LetterboxReachabilityPosition {}; + @IntDef({LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT, + LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER, + LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT}) + @interface LetterboxHorizontalReachabilityPosition {}; /** Letterboxed app window is aligned to the left side. */ - static final int LETTERBOX_REACHABILITY_POSITION_LEFT = 0; + static final int LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT = 0; /** Letterboxed app window is positioned in the horizontal center. */ - static final int LETTERBOX_REACHABILITY_POSITION_CENTER = 1; + static final int LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER = 1; /** Letterboxed app window is aligned to the right side. */ - static final int LETTERBOX_REACHABILITY_POSITION_RIGHT = 2; + static final int LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT = 2; + + /** + * Enum for Letterbox vertical reachability position types. + * + *

Order from top to bottom is important since it's used in {@link + * #movePositionForReachabilityToNextBottomStop} and {@link + * #movePositionForReachabilityToNextTopStop}. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP, + LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER, + LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM}) + @interface LetterboxVerticalReachabilityPosition {}; + + /** Letterboxed app window is aligned to the left side. */ + static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP = 0; + + /** Letterboxed app window is positioned in the vertical center. */ + static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER = 1; + + /** Letterboxed app window is aligned to the right side. */ + static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM = 2; final Context mContext; @@ -106,25 +129,50 @@ final class LetterboxConfiguration { // side of the screen and 1.0 to the right side. private float mLetterboxHorizontalPositionMultiplier; - // Default horizontal position the letterboxed app window when reachability is enabled and - // an app is fullscreen in landscape device orientatio. - // It is used as a starting point for mLetterboxPositionForReachability. - @LetterboxReachabilityPosition - private int mDefaultPositionForReachability; + // Vertical position of a center of the letterboxed app window. 0 corresponds to the top + // side of the screen and 1.0 to the bottom side. + private float mLetterboxVerticalPositionMultiplier; + + // Default horizontal position the letterboxed app window when horizontal reachability is + // enabled and an app is fullscreen in landscape device orientation. + // It is used as a starting point for mLetterboxPositionForHorizontalReachability. + @LetterboxHorizontalReachabilityPosition + private int mDefaultPositionForHorizontalReachability; + + // Default vertical position the letterboxed app window when vertical reachability is enabled + // and an app is fullscreen in portrait device orientation. + // It is used as a starting point for mLetterboxPositionForVerticalReachability. + @LetterboxVerticalReachabilityPosition + private int mDefaultPositionForVerticalReachability; + + // Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps in + // landscape device orientation. + private boolean mIsHorizontalReachabilityEnabled; + + // Whether vertical reachability repositioning is allowed for letterboxed fullscreen apps in + // portrait device orientation. + private boolean mIsVerticalReachabilityEnabled; - // Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape - // device orientation. - private boolean mIsReachabilityEnabled; // Horizontal position of a center of the letterboxed app window which is global to prevent // "jumps" when switching between letterboxed apps. It's updated to reposition the app window // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in // LetterboxUiController#getHorizontalPositionMultiplier which is called from - // ActivityRecord#updateResolvedBoundsHorizontalPosition. + // ActivityRecord#updateResolvedBoundsPosition. // TODO(b/199426138): Global reachability setting causes a jump when resuming an app from // Overview after changing position in another app. - @LetterboxReachabilityPosition - private volatile int mLetterboxPositionForReachability; + @LetterboxHorizontalReachabilityPosition + private volatile int mLetterboxPositionForHorizontalReachability; + + // Vertical position of a center of the letterboxed app window which is global to prevent + // "jumps" when switching between letterboxed apps. It's updated to reposition the app window + // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in + // LetterboxUiController#getVerticalPositionMultiplier which is called from + // ActivityRecord#updateResolvedBoundsPosition. + // TODO(b/199426138): Global reachability setting causes a jump when resuming an app from + // Overview after changing position in another app. + @LetterboxVerticalReachabilityPosition + private volatile int mLetterboxPositionForVerticalReachability; // Whether education is allowed for letterboxed fullscreen apps. private boolean mIsEducationEnabled; @@ -142,10 +190,16 @@ final class LetterboxConfiguration { R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha); mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat( R.dimen.config_letterboxHorizontalPositionMultiplier); - mIsReachabilityEnabled = mContext.getResources().getBoolean( - R.bool.config_letterboxIsReachabilityEnabled); - mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext); - mLetterboxPositionForReachability = mDefaultPositionForReachability; + mIsHorizontalReachabilityEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsHorizontalReachabilityEnabled); + mIsVerticalReachabilityEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsVerticalReachabilityEnabled); + mDefaultPositionForHorizontalReachability = + readLetterboxHorizontalReachabilityPositionFromConfig(mContext); + mDefaultPositionForVerticalReachability = + readLetterboxVerticalReachabilityPositionFromConfig(mContext); + mLetterboxPositionForHorizontalReachability = mDefaultPositionForHorizontalReachability; + mLetterboxPositionForVerticalReachability = mDefaultPositionForVerticalReachability; mIsEducationEnabled = mContext.getResources().getBoolean( R.bool.config_letterboxIsEducationEnabled); } @@ -375,6 +429,19 @@ final class LetterboxConfiguration { ? 0.5f : mLetterboxHorizontalPositionMultiplier; } + /* + * Gets vertical position of a center of the letterboxed app window specified + * in {@link com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier} + * or via an ADB command. 0 corresponds to the top side of the screen and 1 to the + * bottom side. + */ + float getLetterboxVerticalPositionMultiplier() { + return (mLetterboxVerticalPositionMultiplier < 0.0f + || mLetterboxVerticalPositionMultiplier > 1.0f) + // Default to central position if invalid value is provided. + ? 0.5f : mLetterboxVerticalPositionMultiplier; + } + /** * Overrides horizontal position of a center of the letterboxed app window. If given value < 0 * or > 1, then it and a value of {@link @@ -385,6 +452,16 @@ final class LetterboxConfiguration { mLetterboxHorizontalPositionMultiplier = multiplier; } + /** + * Overrides vertical position of a center of the letterboxed app window. If given value < 0 + * or > 1, then it and a value of {@link + * com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier} are ignored and + * central position (0.5) is used. + */ + void setLetterboxVerticalPositionMultiplier(float multiplier) { + mLetterboxVerticalPositionMultiplier = multiplier; + } + /** * Resets horizontal position of a center of the letterboxed app window to {@link * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}. @@ -394,65 +471,145 @@ final class LetterboxConfiguration { com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier); } - /* - * Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape - * device orientation. + /** + * Resets vertical position of a center of the letterboxed app window to {@link + * com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier}. */ - boolean getIsReachabilityEnabled() { - return mIsReachabilityEnabled; + void resetLetterboxVerticalPositionMultiplier() { + mLetterboxVerticalPositionMultiplier = mContext.getResources().getFloat( + com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier); } - /** - * Overrides whether reachability repositioning is allowed for letterboxed fullscreen apps in + /* + * Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps in * landscape device orientation. */ - void setIsReachabilityEnabled(boolean enabled) { - mIsReachabilityEnabled = enabled; - } - - /** - * Resets whether reachability repositioning is allowed for letterboxed fullscreen apps in - * landscape device orientation to {@link R.bool.config_letterboxIsReachabilityEnabled}. - */ - void resetIsReachabilityEnabled() { - mIsReachabilityEnabled = mContext.getResources().getBoolean( - R.bool.config_letterboxIsReachabilityEnabled); + boolean getIsHorizontalReachabilityEnabled() { + return mIsHorizontalReachabilityEnabled; } /* - * Gets default horizontal position of the letterboxed app window when reachability is enabled. - * Specified in {@link R.integer.config_letterboxDefaultPositionForReachability} or via an ADB - * command. + * Whether vertical reachability repositioning is allowed for letterboxed fullscreen apps in + * portrait device orientation. */ - @LetterboxReachabilityPosition - int getDefaultPositionForReachability() { - return mDefaultPositionForReachability; + boolean getIsVerticalReachabilityEnabled() { + return mIsVerticalReachabilityEnabled; } /** - * Overrides default horizonal position of the letterboxed app window when reachability + * Overrides whether horizontal reachability repositioning is allowed for letterboxed fullscreen + * apps in landscape device orientation. + */ + void setIsHorizontalReachabilityEnabled(boolean enabled) { + mIsHorizontalReachabilityEnabled = enabled; + } + + /** + * Overrides whether vertical reachability repositioning is allowed for letterboxed fullscreen + * apps in portrait device orientation. + */ + void setIsVerticalReachabilityEnabled(boolean enabled) { + mIsVerticalReachabilityEnabled = enabled; + } + + /** + * Resets whether horizontal reachability repositioning is allowed for letterboxed fullscreen + * apps in landscape device orientation to + * {@link R.bool.config_letterboxIsHorizontalReachabilityEnabled}. + */ + void resetIsHorizontalReachabilityEnabled() { + mIsHorizontalReachabilityEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsHorizontalReachabilityEnabled); + } + + /** + * Resets whether vertical reachability repositioning is allowed for letterboxed fullscreen apps + * in portrait device orientation to + * {@link R.bool.config_letterboxIsVerticalReachabilityEnabled}. + */ + void resetIsVerticalReachabilityEnabled() { + mIsVerticalReachabilityEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsVerticalReachabilityEnabled); + } + + /* + * Gets default horizontal position of the letterboxed app window when horizontal reachability * is enabled. + * + *

Specified in {@link R.integer.config_letterboxDefaultPositionForHorizontalReachability} + * or via an ADB command. */ - void setDefaultPositionForReachability(@LetterboxReachabilityPosition int position) { - mDefaultPositionForReachability = position; + @LetterboxHorizontalReachabilityPosition + int getDefaultPositionForHorizontalReachability() { + return mDefaultPositionForHorizontalReachability; + } + + /* + * Gets default vertical position of the letterboxed app window when vertical reachability is + * enabled. + * + *

Specified in {@link R.integer.config_letterboxDefaultPositionForVerticalReachability} or + * via an ADB command. + */ + @LetterboxVerticalReachabilityPosition + int getDefaultPositionForVerticalReachability() { + return mDefaultPositionForVerticalReachability; } /** - * Resets default horizontal position of the letterboxed app window when reachability is - * enabled to {@link R.integer.config_letterboxDefaultPositionForReachability}. + * Overrides default horizontal position of the letterboxed app window when horizontal + * reachability is enabled. */ - void resetDefaultPositionForReachability() { - mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext); + void setDefaultPositionForHorizontalReachability( + @LetterboxHorizontalReachabilityPosition int position) { + mDefaultPositionForHorizontalReachability = position; } - @LetterboxReachabilityPosition - private static int readLetterboxReachabilityPositionFromConfig(Context context) { + /** + * Overrides default vertical position of the letterboxed app window when vertical + * reachability is enabled. + */ + void setDefaultPositionForVerticalReachability( + @LetterboxVerticalReachabilityPosition int position) { + mDefaultPositionForVerticalReachability = position; + } + + /** + * Resets default horizontal position of the letterboxed app window when horizontal reachability + * is enabled to {@link R.integer.config_letterboxDefaultPositionForHorizontalReachability}. + */ + void resetDefaultPositionForHorizontalReachability() { + mDefaultPositionForHorizontalReachability = + readLetterboxHorizontalReachabilityPositionFromConfig(mContext); + } + + /** + * Resets default vertical position of the letterboxed app window when vertical reachability + * is enabled to {@link R.integer.config_letterboxDefaultPositionForVerticalReachability}. + */ + void resetDefaultPositionForVerticalReachability() { + mDefaultPositionForVerticalReachability = + readLetterboxVerticalReachabilityPositionFromConfig(mContext); + } + + @LetterboxHorizontalReachabilityPosition + private static int readLetterboxHorizontalReachabilityPositionFromConfig(Context context) { int position = context.getResources().getInteger( - R.integer.config_letterboxDefaultPositionForReachability); - return position == LETTERBOX_REACHABILITY_POSITION_LEFT - || position == LETTERBOX_REACHABILITY_POSITION_CENTER - || position == LETTERBOX_REACHABILITY_POSITION_RIGHT - ? position : LETTERBOX_REACHABILITY_POSITION_CENTER; + R.integer.config_letterboxDefaultPositionForHorizontalReachability); + return position == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT + || position == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER + || position == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT + ? position : LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER; + } + + @LetterboxVerticalReachabilityPosition + private static int readLetterboxVerticalReachabilityPositionFromConfig(Context context) { + int position = context.getResources().getInteger( + R.integer.config_letterboxDefaultPositionForVerticalReachability); + return position == LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP + || position == LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER + || position == LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM + ? position : LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER; } /* @@ -462,48 +619,108 @@ final class LetterboxConfiguration { *

The position multiplier is changed after each double tap in the letterbox area. */ float getHorizontalMultiplierForReachability() { - switch (mLetterboxPositionForReachability) { - case LETTERBOX_REACHABILITY_POSITION_LEFT: + switch (mLetterboxPositionForHorizontalReachability) { + case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT: return 0.0f; - case LETTERBOX_REACHABILITY_POSITION_CENTER: + case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER: return 0.5f; - case LETTERBOX_REACHABILITY_POSITION_RIGHT: + case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT: return 1.0f; default: throw new AssertionError( - "Unexpected letterbox position type: " + mLetterboxPositionForReachability); + "Unexpected letterbox position type: " + + mLetterboxPositionForHorizontalReachability); + } + } + /* + * Gets vertical position of a center of the letterboxed app window when reachability + * is enabled specified. 0 corresponds to the top side of the screen and 1 to the bottom side. + * + *

The position multiplier is changed after each double tap in the letterbox area. + */ + float getVerticalMultiplierForReachability() { + switch (mLetterboxPositionForVerticalReachability) { + case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP: + return 0.0f; + case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER: + return 0.5f; + case LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM: + return 1.0f; + default: + throw new AssertionError( + "Unexpected letterbox position type: " + + mLetterboxPositionForVerticalReachability); } } - /** Returns a string representing the given {@link LetterboxReachabilityPosition}. */ - static String letterboxReachabilityPositionToString( - @LetterboxReachabilityPosition int position) { + /** Returns a string representing the given {@link LetterboxHorizontalReachabilityPosition}. */ + static String letterboxHorizontalReachabilityPositionToString( + @LetterboxHorizontalReachabilityPosition int position) { switch (position) { - case LETTERBOX_REACHABILITY_POSITION_LEFT: - return "LETTERBOX_REACHABILITY_POSITION_LEFT"; - case LETTERBOX_REACHABILITY_POSITION_CENTER: - return "LETTERBOX_REACHABILITY_POSITION_CENTER"; - case LETTERBOX_REACHABILITY_POSITION_RIGHT: - return "LETTERBOX_REACHABILITY_POSITION_RIGHT"; + case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT: + return "LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT"; + case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER: + return "LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER"; + case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT: + return "LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT"; default: throw new AssertionError( "Unexpected letterbox position type: " + position); } } - /** - * Changes letterbox position for reachability to the next available one on the right side. - */ - void movePositionForReachabilityToNextRightStop() { - mLetterboxPositionForReachability = Math.min( - mLetterboxPositionForReachability + 1, LETTERBOX_REACHABILITY_POSITION_RIGHT); + /** Returns a string representing the given {@link LetterboxVerticalReachabilityPosition}. */ + static String letterboxVerticalReachabilityPositionToString( + @LetterboxVerticalReachabilityPosition int position) { + switch (position) { + case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP: + return "LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP"; + case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER: + return "LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER"; + case LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM: + return "LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM"; + default: + throw new AssertionError( + "Unexpected letterbox position type: " + position); + } } /** - * Changes letterbox position for reachability to the next available one on the left side. + * Changes letterbox position for horizontal reachability to the next available one on the + * right side. */ - void movePositionForReachabilityToNextLeftStop() { - mLetterboxPositionForReachability = Math.max(mLetterboxPositionForReachability - 1, 0); + void movePositionForHorizontalReachabilityToNextRightStop() { + mLetterboxPositionForHorizontalReachability = Math.min( + mLetterboxPositionForHorizontalReachability + 1, + LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT); + } + + /** + * Changes letterbox position for horizontal reachability to the next available one on the left + * side. + */ + void movePositionForHorizontalReachabilityToNextLeftStop() { + mLetterboxPositionForHorizontalReachability = + Math.max(mLetterboxPositionForHorizontalReachability - 1, 0); + } + + /** + * Changes letterbox position for vertical reachability to the next available one on the bottom + * side. + */ + void movePositionForVerticalReachabilityToNextBottomStop() { + mLetterboxPositionForVerticalReachability = Math.min( + mLetterboxPositionForVerticalReachability + 1, + LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM); + } + + /** + * Changes letterbox position for vertical reachability to the next available one on the top + * side. + */ + void movePositionForVerticalReachabilityToNextTopStop() { + mLetterboxPositionForVerticalReachability = + Math.max(mLetterboxPositionForVerticalReachability - 1, 0); } /** diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index bb15d76c3bac0..201520677b26d 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -163,7 +163,8 @@ final class LetterboxUiController { this::hasWallpaperBackgroudForLetterbox, this::getLetterboxWallpaperBlurRadius, this::getLetterboxWallpaperDarkScrimAlpha, - this::handleDoubleTap); + this::handleHorizontalDoubleTap, + this::handleVerticalDoubleTap); mLetterbox.attachInput(w); } mActivityRecord.getPosition(mTmpPoint); @@ -193,17 +194,28 @@ final class LetterboxUiController { float getHorizontalPositionMultiplier(Configuration parentConfiguration) { // Don't check resolved configuration because it may not be updated yet during // configuration change. - return isReachabilityEnabled(parentConfiguration) + return isHorizontalReachabilityEnabled(parentConfiguration) // Using the last global dynamic position to avoid "jumps" when moving // between apps or activities. ? mLetterboxConfiguration.getHorizontalMultiplierForReachability() : mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier(); } + float getVerticalPositionMultiplier(Configuration parentConfiguration) { + // Don't check resolved configuration because it may not be updated yet during + // configuration change. + return isVerticalReachabilityEnabled(parentConfiguration) + // Using the last global dynamic position to avoid "jumps" when moving + // between apps or activities. + ? mLetterboxConfiguration.getVerticalMultiplierForReachability() + : mLetterboxConfiguration.getLetterboxVerticalPositionMultiplier(); + } + float getFixedOrientationLetterboxAspectRatio(Configuration parentConfiguration) { // Don't check resolved windowing mode because it may not be updated yet during // configuration change. - if (!isReachabilityEnabled(parentConfiguration)) { + if (!isHorizontalReachabilityEnabled(parentConfiguration) + && !isVerticalReachabilityEnabled(parentConfiguration)) { return mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(); } @@ -225,8 +237,8 @@ final class LetterboxUiController { return mActivityRecord.mWmService.mContext.getResources(); } - private void handleDoubleTap(int x) { - if (!isReachabilityEnabled() || mActivityRecord.isInTransition()) { + private void handleHorizontalDoubleTap(int x) { + if (!isHorizontalReachabilityEnabled() || mActivityRecord.isInTransition()) { return; } @@ -237,10 +249,32 @@ final class LetterboxUiController { if (mLetterbox.getInnerFrame().left > x) { // Moving to the next stop on the left side of the app window: right > center > left. - mLetterboxConfiguration.movePositionForReachabilityToNextLeftStop(); + mLetterboxConfiguration.movePositionForHorizontalReachabilityToNextLeftStop(); } else if (mLetterbox.getInnerFrame().right < x) { // Moving to the next stop on the right side of the app window: left > center > right. - mLetterboxConfiguration.movePositionForReachabilityToNextRightStop(); + mLetterboxConfiguration.movePositionForHorizontalReachabilityToNextRightStop(); + } + + // TODO(197549949): Add animation for transition. + mActivityRecord.recomputeConfiguration(); + } + + private void handleVerticalDoubleTap(int y) { + if (!isVerticalReachabilityEnabled() || mActivityRecord.isInTransition()) { + return; + } + + if (mLetterbox.getInnerFrame().top <= y && mLetterbox.getInnerFrame().bottom >= y) { + // Only react to clicks at the top and bottom of the letterboxed app window. + return; + } + + if (mLetterbox.getInnerFrame().top > y) { + // Moving to the next stop on the top side of the app window: bottom > center > top. + mLetterboxConfiguration.movePositionForVerticalReachabilityToNextTopStop(); + } else if (mLetterbox.getInnerFrame().bottom < y) { + // Moving to the next stop on the bottom side of the app window: top > center > bottom. + mLetterboxConfiguration.movePositionForVerticalReachabilityToNextBottomStop(); } // TODO(197549949): Add animation for transition. @@ -248,25 +282,47 @@ final class LetterboxUiController { } /** - * Whether reachability is enabled for an activity in the curren configuration. + * Whether horizontal reachability is enabled for an activity in the current configuration. * *

Conditions that needs to be met: *

    *
  • Activity is portrait-only. *
  • Fullscreen window in landscape device orientation. - *
  • Reachability is enabled. + *
  • Horizontal Reachability is enabled. *
*/ - private boolean isReachabilityEnabled(Configuration parentConfiguration) { - return mLetterboxConfiguration.getIsReachabilityEnabled() + private boolean isHorizontalReachabilityEnabled(Configuration parentConfiguration) { + return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled() && parentConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FULLSCREEN - && parentConfiguration.orientation == ORIENTATION_LANDSCAPE - && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT; + && (parentConfiguration.orientation == ORIENTATION_LANDSCAPE + && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT); } - private boolean isReachabilityEnabled() { - return isReachabilityEnabled(mActivityRecord.getParent().getConfiguration()); + private boolean isHorizontalReachabilityEnabled() { + return isHorizontalReachabilityEnabled(mActivityRecord.getParent().getConfiguration()); + } + + /** + * Whether vertical reachability is enabled for an activity in the current configuration. + * + *

Conditions that needs to be met: + *

    + *
  • Activity is landscape-only. + *
  • Fullscreen window in portrait device orientation. + *
  • Vertical Reachability is enabled. + *
+ */ + private boolean isVerticalReachabilityEnabled(Configuration parentConfiguration) { + return mLetterboxConfiguration.getIsVerticalReachabilityEnabled() + && parentConfiguration.windowConfiguration.getWindowingMode() + == WINDOWING_MODE_FULLSCREEN + && (parentConfiguration.orientation == ORIENTATION_PORTRAIT + && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_LANDSCAPE); + } + + private boolean isVerticalReachabilityEnabled() { + return isVerticalReachabilityEnabled(mActivityRecord.getParent().getConfiguration()); } @VisibleForTesting @@ -474,9 +530,13 @@ final class LetterboxUiController { + getLetterboxWallpaperBlurRadius()); } - pw.println(prefix + " isReachabilityEnabled=" + isReachabilityEnabled()); + pw.println(prefix + " isHorizontalReachabilityEnabled=" + + isHorizontalReachabilityEnabled()); + pw.println(prefix + " isVerticalReachabilityEnabled=" + isVerticalReachabilityEnabled()); pw.println(prefix + " letterboxHorizontalPositionMultiplier=" + getHorizontalPositionMultiplier(mActivityRecord.getParent().getConfiguration())); + pw.println(prefix + " letterboxVerticalPositionMultiplier=" + + getVerticalPositionMultiplier(mActivityRecord.getParent().getConfiguration())); pw.println(prefix + " fixedOrientationLetterboxAspectRatio=" + getFixedOrientationLetterboxAspectRatio( mActivityRecord.getParent().getConfiguration())); diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 5a2f28f4a3658..1284bb3062759 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -23,9 +23,12 @@ import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_ import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR; import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_CENTER; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_LEFT; -import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_REACHABILITY_POSITION_RIGHT; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP; import android.content.res.Resources.NotFoundException; import android.graphics.Color; @@ -47,7 +50,8 @@ import com.android.internal.protolog.ProtoLogImpl; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType; -import com.android.server.wm.LetterboxConfiguration.LetterboxReachabilityPosition; +import com.android.server.wm.LetterboxConfiguration.LetterboxHorizontalReachabilityPosition; +import com.android.server.wm.LetterboxConfiguration.LetterboxVerticalReachabilityPosition; import java.io.IOException; import java.io.PrintWriter; @@ -773,7 +777,27 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } - private int runSetLetterboxIsReachabilityEnabled(PrintWriter pw) throws RemoteException { + private int runSetLetterboxVerticalPositionMultiplier(PrintWriter pw) throws RemoteException { + final float multiplier; + try { + String arg = getNextArgRequired(); + multiplier = Float.parseFloat(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad multiplier format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: multiplier should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(multiplier); + } + return 0; + } + + private int runSetLetterboxIsHorizontalReachabilityEnabled(PrintWriter pw) + throws RemoteException { String arg = getNextArg(); final boolean enabled; switch (arg) { @@ -791,25 +815,49 @@ public class WindowManagerShellCommand extends ShellCommand { } synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setIsReachabilityEnabled(enabled); + mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(enabled); } return 0; } - private int runSetLetterboxDefaultPositionForReachability(PrintWriter pw) + private int runSetLetterboxIsVerticalReachabilityEnabled(PrintWriter pw) throws RemoteException { - @LetterboxReachabilityPosition final int position; + String arg = getNextArg(); + final boolean enabled; + switch (arg) { + case "true": + case "1": + enabled = true; + break; + case "false": + case "0": + enabled = false; + break; + default: + getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg); + return -1; + } + + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setIsVerticalReachabilityEnabled(enabled); + } + return 0; + } + + private int runSetLetterboxDefaultPositionForHorizontalReachability(PrintWriter pw) + throws RemoteException { + @LetterboxHorizontalReachabilityPosition final int position; try { String arg = getNextArgRequired(); switch (arg) { case "left": - position = LETTERBOX_REACHABILITY_POSITION_LEFT; + position = LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT; break; case "center": - position = LETTERBOX_REACHABILITY_POSITION_CENTER; + position = LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER; break; case "right": - position = LETTERBOX_REACHABILITY_POSITION_RIGHT; + position = LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT; break; default: getErrPrintWriter().println( @@ -822,7 +870,38 @@ public class WindowManagerShellCommand extends ShellCommand { return -1; } synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setDefaultPositionForReachability(position); + mLetterboxConfiguration.setDefaultPositionForHorizontalReachability(position); + } + return 0; + } + + private int runSetLetterboxDefaultPositionForVerticalReachability(PrintWriter pw) + throws RemoteException { + @LetterboxVerticalReachabilityPosition final int position; + try { + String arg = getNextArgRequired(); + switch (arg) { + case "top": + position = LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP; + break; + case "center": + position = LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER; + break; + case "bottom": + position = LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM; + break; + default: + getErrPrintWriter().println( + "Error: 'top', 'center' or 'bottom' are expected as an argument"); + return -1; + } + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: 'top', 'center' or 'bottom' are expected as an argument" + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setDefaultPositionForVerticalReachability(position); } return 0; } @@ -881,11 +960,20 @@ public class WindowManagerShellCommand extends ShellCommand { case "--horizontalPositionMultiplier": runSetLetterboxHorizontalPositionMultiplier(pw); break; - case "--isReachabilityEnabled": - runSetLetterboxIsReachabilityEnabled(pw); + case "--verticalPositionMultiplier": + runSetLetterboxVerticalPositionMultiplier(pw); break; - case "--defaultPositionForReachability": - runSetLetterboxDefaultPositionForReachability(pw); + case "--isHorizontalReachabilityEnabled": + runSetLetterboxIsHorizontalReachabilityEnabled(pw); + break; + case "--isVerticalReachabilityEnabled": + runSetLetterboxIsVerticalReachabilityEnabled(pw); + break; + case "--defaultPositionForHorizontalReachability": + runSetLetterboxDefaultPositionForHorizontalReachability(pw); + break; + case "--defaultPositionForVerticalReachability": + runSetLetterboxDefaultPositionForVerticalReachability(pw); break; case "--isEducationEnabled": runSetLetterboxIsEducationEnabled(pw); @@ -928,11 +1016,20 @@ public class WindowManagerShellCommand extends ShellCommand { case "horizontalPositionMultiplier": mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); break; - case "isReachabilityEnabled": - mLetterboxConfiguration.getIsReachabilityEnabled(); + case "verticalPositionMultiplier": + mLetterboxConfiguration.resetLetterboxVerticalPositionMultiplier(); break; - case "defaultPositionForReachability": - mLetterboxConfiguration.getDefaultPositionForReachability(); + case "isHorizontalReachabilityEnabled": + mLetterboxConfiguration.getIsHorizontalReachabilityEnabled(); + break; + case "isVerticalReachabilityEnabled": + mLetterboxConfiguration.getIsVerticalReachabilityEnabled(); + break; + case "defaultPositionForHorizontalReachability": + mLetterboxConfiguration.getDefaultPositionForHorizontalReachability(); + break; + case "defaultPositionForVerticalReachability": + mLetterboxConfiguration.getDefaultPositionForVerticalReachability(); break; case "isEducationEnabled": mLetterboxConfiguration.getIsEducationEnabled(); @@ -1030,8 +1127,10 @@ public class WindowManagerShellCommand extends ShellCommand { mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); - mLetterboxConfiguration.resetIsReachabilityEnabled(); - mLetterboxConfiguration.resetDefaultPositionForReachability(); + mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled(); + mLetterboxConfiguration.resetIsVerticalReachabilityEnabled(); + mLetterboxConfiguration.resetDefaultPositionForHorizontalReachability(); + mLetterboxConfiguration.resetDefaultPositionForVerticalReachability(); mLetterboxConfiguration.resetIsEducationEnabled(); } } @@ -1044,11 +1143,16 @@ public class WindowManagerShellCommand extends ShellCommand { + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier()); pw.println("Aspect ratio: " + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio()); - pw.println("Is reachability enabled: " - + mLetterboxConfiguration.getIsReachabilityEnabled()); - pw.println("Default position for reachability: " - + LetterboxConfiguration.letterboxReachabilityPositionToString( - mLetterboxConfiguration.getDefaultPositionForReachability())); + pw.println("Is horizontal reachability enabled: " + + mLetterboxConfiguration.getIsHorizontalReachabilityEnabled()); + pw.println("Is vertical reachability enabled: " + + mLetterboxConfiguration.getIsVerticalReachabilityEnabled()); + pw.println("Default position for horizontal reachability: " + + LetterboxConfiguration.letterboxHorizontalReachabilityPositionToString( + mLetterboxConfiguration.getDefaultPositionForHorizontalReachability())); + pw.println("Default position for vertical reachability: " + + LetterboxConfiguration.letterboxVerticalReachabilityPositionToString( + mLetterboxConfiguration.getDefaultPositionForVerticalReachability())); pw.println("Is education enabled: " + mLetterboxConfiguration.getIsEducationEnabled()); @@ -1185,18 +1289,30 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,"); pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier"); pw.println(" are ignored and central position (0.5) is used."); - pw.println(" --isReachabilityEnabled [true|1|false|0]"); - pw.println(" Whether reachability repositioning is allowed for letterboxed"); - pw.println(" fullscreen apps in landscape device orientation."); - pw.println(" --defaultPositionForReachability [left|center|right]"); - pw.println(" Default horizontal position of app window when reachability is."); + pw.println(" --verticalPositionMultiplier multiplier"); + pw.println(" Vertical position of app window center. If multiplier < 0 or > 1,"); + pw.println(" both it and R.dimen.config_letterboxVerticalPositionMultiplier"); + pw.println(" are ignored and central position (0.5) is used."); + pw.println(" --isHorizontalReachabilityEnabled [true|1|false|0]"); + pw.println(" Whether horizontal reachability repositioning is allowed for "); + pw.println(" letterboxed fullscreen apps in landscape device orientation."); + pw.println(" --isVerticalReachabilityEnabled [true|1|false|0]"); + pw.println(" Whether vertical reachability repositioning is allowed for "); + pw.println(" letterboxed fullscreen apps in portrait device orientation."); + pw.println(" --defaultPositionForHorizontalReachability [left|center|right]"); + pw.println(" Default position of app window when horizontal reachability is."); + pw.println(" enabled."); + pw.println(" --defaultPositionForVerticalReachability [top|center|bottom]"); + pw.println(" Default position of app window when vertical reachability is."); pw.println(" enabled."); pw.println(" --isEducationEnabled [true|1|false|0]"); pw.println(" Whether education is allowed for letterboxed fullscreen apps."); pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType"); pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha"); - pw.println(" |horizontalPositionMultiplier|isReachabilityEnabled"); - pw.println(" isEducationEnabled||defaultPositionMultiplierForReachability]"); + pw.println(" |horizontalPositionMultiplier|verticalPositionMultiplier"); + pw.println(" |isHorizontalReachabilityEnabled|isVerticalReachabilityEnabled"); + pw.println(" isEducationEnabled||defaultPositionMultiplierForHorizontalReachability"); + pw.println(" ||defaultPositionMultiplierForVerticalReachability]"); pw.println(" Resets overrides to default values for specified properties separated"); pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'."); pw.println(" If no arguments provided, all values will be reset."); diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java index 1e86522a23073..e502f2fbd1734 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java @@ -63,7 +63,7 @@ public class LetterboxTest { mLetterbox = new Letterbox(mSurfaces, StubTransaction::new, () -> mAreCornersRounded, () -> Color.valueOf(mColor), () -> mHasWallpaperBackground, () -> mBlurRadius, () -> mDarkScrimAlpha, - /* doubleTapCallback= */ x -> {}); + /* doubleTapCallbackX= */ x -> {}, /* doubleTapCallbackY= */ y -> {}); mTransaction = spy(StubTransaction.class); } diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 76fb7ff2e6f86..aca2d5989f957 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -1928,7 +1928,7 @@ public class SizeCompatTests extends WindowTestsBase { prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED); // Bounds are letterboxed to respect the provided max aspect ratio. - assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 1100)); + assertEquals(mActivity.getBounds(), new Rect(0, 850, 1000, 1950)); // Move activity to split screen which has landscape size. mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents */ false, "test"); @@ -2045,19 +2045,14 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier( letterboxHorizontalPositionMultiplier); prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); - assertEquals(fixedOrientationLetterbox, mActivity.getBounds()); - // Rotate to put activity in size compat mode. rotateDisplay(mActivity.mDisplayContent, ROTATION_90); - assertTrue(mActivity.inSizeCompatMode()); // Activity is in size compat mode but not scaled. assertEquals(sizeCompatUnscaled, mActivity.getBounds()); - // Force activity to scaled down for size compat mode. resizeDisplay(mTask.mDisplayContent, 700, 1400); - assertTrue(mActivity.inSizeCompatMode()); assertScaled(); assertEquals(sizeCompatScaled, mActivity.getBounds()); @@ -2074,6 +2069,109 @@ public class SizeCompatTests extends WindowTestsBase { /* letterboxHorizontalPositionMultiplier */ 1.0f); } + @Test + public void testUpdateResolvedBoundsVerticalPosition_top() { + // Display configured as (1400, 2800). + assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( + /* letterboxVerticalPositionMultiplier */ 0.0f, + // At launch. + /* fixedOrientationLetterbox */ new Rect(0, 0, 1400, 700), + // After 90 degree rotation. + /* sizeCompatUnscaled */ new Rect(700, 0, 2100, 700), + // After the display is resized to (1400, 700). + /* sizeCompatScaled */ new Rect(0, 0, 700, 350)); + } + + @Test + public void testUpdateResolvedBoundsVerticalPosition_center() { + // Display configured as (1400, 2800). + assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( + /* letterboxVerticalPositionMultiplier */ 0.5f, + // At launch. + /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750), + // After 90 degree rotation. + /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050), + // After the display is resized to (1400, 700). + /* sizeCompatScaled */ new Rect(0, 525, 700, 875)); + } + + @Test + public void testUpdateResolvedBoundsVerticalPosition_invalidMultiplier_defaultToCenter() { + // Display configured as (1400, 2800). + + // Below 0.0. + assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( + /* letterboxVerticalPositionMultiplier */ -1.0f, + // At launch. + /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750), + // After 90 degree rotation. + /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050), + // After the display is resized to (1400, 700). + /* sizeCompatScaled */ new Rect(0, 525, 700, 875)); + + // Above 1.0 + assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( + /* letterboxVerticalPositionMultiplier */ 2.0f, + // At launch. + /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750), + // After 90 degree rotation. + /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050), + // After the display is resized to (1400, 700). + /* sizeCompatScaled */ new Rect(0, 525, 700, 875)); + } + + @Test + public void testUpdateResolvedBoundsVerticalPosition_bottom() { + // Display configured as (1400, 2800). + assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( + /* letterboxVerticalPositionMultiplier */ 1.0f, + // At launch. + /* fixedOrientationLetterbox */ new Rect(0, 2100, 1400, 2800), + // After 90 degree rotation. + /* sizeCompatUnscaled */ new Rect(700, 700, 2100, 1400), + // After the display is resized to (1400, 700). + /* sizeCompatScaled */ new Rect(0, 1050, 700, 1400)); + } + + private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( + float letterboxVerticalPositionMultiplier, Rect fixedOrientationLetterbox, + Rect sizeCompatUnscaled, Rect sizeCompatScaled) { + // Set up a display in portrait and ignoring orientation request. + setUpDisplaySizeWithApp(1400, 2800); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier( + letterboxVerticalPositionMultiplier); + prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE); + + assertEquals(fixedOrientationLetterbox, mActivity.getBounds()); + + // Rotate to put activity in size compat mode. + rotateDisplay(mActivity.mDisplayContent, ROTATION_90); + + assertTrue(mActivity.inSizeCompatMode()); + // Activity is in size compat mode but not scaled. + assertEquals(sizeCompatUnscaled, mActivity.getBounds()); + + // Force activity to scaled down for size compat mode. + resizeDisplay(mTask.mDisplayContent, 1400, 700); + + assertTrue(mActivity.inSizeCompatMode()); + assertScaled(); + assertEquals(sizeCompatScaled, mActivity.getBounds()); + } + + @Test + public void testUpdateResolvedBoundsVerticalPosition_activityFillParentHeight() { + // When activity height equals parent height, multiplier shouldn't have any effect. + assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity( + /* letterboxVerticalPositionMultiplier */ 0.0f); + assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity( + /* letterboxVerticalPositionMultiplier */ 0.5f); + assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity( + /* letterboxVerticalPositionMultiplier */ 1.0f); + } + @Test public void testAreBoundsLetterboxed_letterboxedForAspectRatio_returnsTrue() { setUpDisplaySizeWithApp(1000, 2500); @@ -2367,6 +2465,23 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier( letterboxHorizontalPositionMultiplier); prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE); + assertFitted(); + // Rotate to put activity in size compat mode. + rotateDisplay(mActivity.mDisplayContent, ROTATION_90); + assertTrue(mActivity.inSizeCompatMode()); + // Activity is in size compat mode but not scaled. + assertEquals(new Rect(0, 1050, 1400, 1750), mActivity.getBounds()); + } + + private void assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity( + float letterboxVerticalPositionMultiplier) { + // Set up a display in portrait and ignoring orientation request. + setUpDisplaySizeWithApp(1400, 2800); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier( + letterboxVerticalPositionMultiplier); + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); assertFitted(); @@ -2375,7 +2490,7 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(mActivity.inSizeCompatMode()); // Activity is in size compat mode but not scaled. - assertEquals(new Rect(0, 1050, 1400, 1750), mActivity.getBounds()); + assertEquals(new Rect(1050, 0, 1750, 1400), mActivity.getBounds()); } private static WindowState addWindowToActivity(ActivityRecord activity) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 9957d05d0a529..e138d52b8ef61 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -229,14 +229,22 @@ class WindowTestsBase extends SystemServiceTestsBase { // {@link com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}, is set // on some device form factors. mAtm.mWindowManager.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(0); - // Ensure letterbox position multiplier is not overridden on any device target. + // Ensure letterbox horizontal position multiplier is not overridden on any device target. // {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}, // may be set on some device form factors. mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(0.5f); - // Ensure letterbox reachability treatment isn't overridden on any device target. - // {@link com.android.internal.R.bool.config_letterboxIsReachabilityEnabled}, + // Ensure letterbox vertical position multiplier is not overridden on any device target. + // {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}, // may be set on some device form factors. - mAtm.mWindowManager.mLetterboxConfiguration.setIsReachabilityEnabled(false); + mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f); + // Ensure letterbox horizontal reachability treatment isn't overridden on any device target. + // {@link com.android.internal.R.bool.config_letterboxIsHorizontalReachabilityEnabled}, + // may be set on some device form factors. + mAtm.mWindowManager.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(false); + // Ensure letterbox vertical reachability treatment isn't overridden on any device target. + // {@link com.android.internal.R.bool.config_letterboxIsVerticalReachabilityEnabled}, + // may be set on some device form factors. + mAtm.mWindowManager.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(false); checkDeviceSpecificOverridesNotApplied(); } @@ -246,7 +254,8 @@ class WindowTestsBase extends SystemServiceTestsBase { // Revert back to device overrides. mAtm.mWindowManager.mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio(); mAtm.mWindowManager.mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); - mAtm.mWindowManager.mLetterboxConfiguration.resetIsReachabilityEnabled(); + mAtm.mWindowManager.mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled(); + mAtm.mWindowManager.mLetterboxConfiguration.resetIsVerticalReachabilityEnabled(); } /**