diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index d42e0c367763b..fbb1b113e624d 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -284,6 +284,21 @@ public final class ViewRootImpl implements ViewParent, */ private static final int SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS = 2500; + /** + * If set to {@code true}, the new logic to layout system bars as normal window and to use + * layout result to get insets will be applied. Otherwise, the old hard-coded window logic will + * be applied. + */ + private static final String USE_FLEXIBLE_INSETS = "persist.debug.flexible_insets"; + + /** + * A flag to indicate to use the new generalized insets window logic, or the old hard-coded + * insets window layout logic. + * {@hide} + */ + public static final boolean INSETS_LAYOUT_GENERALIZATION = + SystemProperties.getBoolean(USE_FLEXIBLE_INSETS, false); + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) static final ThreadLocal sRunQueues = new ThreadLocal(); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index bad6ebb5a31f9..117971fa4e741 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -97,6 +97,7 @@ import android.content.ClipData; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; +import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; @@ -3467,6 +3468,22 @@ public interface WindowManager extends ViewManager { */ public @InsetsState.InternalInsetsType int[] providesInsetsTypes; + /** + * If specified, the insets provided by this window will be our window frame minus the + * insets specified by providedInternalInsets. + * + * @hide + */ + public Insets providedInternalInsets = Insets.NONE; + + /** + * {@link LayoutParams} to be applied to the window when layout with a assigned rotation. + * This will make layout during rotation change smoothly. + * + * @hide + */ + public LayoutParams[] paramsForRotation; + /** * Specifies types of insets that this window should avoid overlapping during layout. * @@ -3566,6 +3583,18 @@ public interface WindowManager extends ViewManager { return mFitInsetsIgnoringVisibility; } + private void checkNonRecursiveParams() { + if (paramsForRotation == null) { + return; + } + for (int i = paramsForRotation.length - 1; i >= 0; i--) { + if (paramsForRotation[i].paramsForRotation != null) { + throw new IllegalArgumentException( + "Params cannot contain params recursively."); + } + } + } + public LayoutParams() { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = TYPE_APPLICATION; @@ -3818,6 +3847,14 @@ public interface WindowManager extends ViewManager { } else { out.writeInt(0); } + providedInternalInsets.writeToParcel(out, 0 /* parcelableFlags */); + if (paramsForRotation != null) { + checkNonRecursiveParams(); + out.writeInt(paramsForRotation.length); + out.writeTypedArray(paramsForRotation, 0 /* parcelableFlags */); + } else { + out.writeInt(0); + } } public static final @android.annotation.NonNull Parcelable.Creator CREATOR @@ -3887,6 +3924,12 @@ public interface WindowManager extends ViewManager { providesInsetsTypes = new int[insetsTypesLength]; in.readIntArray(providesInsetsTypes); } + providedInternalInsets = Insets.CREATOR.createFromParcel(in); + int paramsForRotationLength = in.readInt(); + if (paramsForRotationLength > 0) { + paramsForRotation = new LayoutParams[paramsForRotationLength]; + in.readTypedArray(paramsForRotation, LayoutParams.CREATOR); + } } @SuppressWarnings({"PointlessBitwiseExpression"}) @@ -4169,6 +4212,17 @@ public interface WindowManager extends ViewManager { changes |= LAYOUT_CHANGED; } + if (!providedInternalInsets.equals(o.providedInternalInsets)) { + providedInternalInsets = o.providedInternalInsets; + changes |= LAYOUT_CHANGED; + } + + if (!Arrays.equals(paramsForRotation, o.paramsForRotation)) { + paramsForRotation = o.paramsForRotation; + checkNonRecursiveParams(); + changes |= LAYOUT_CHANGED; + } + return changes; } @@ -4356,6 +4410,18 @@ public interface WindowManager extends ViewManager { sb.append(InsetsState.typeToString(providesInsetsTypes[i])); } } + if (!providedInternalInsets.equals(Insets.NONE)) { + sb.append(" providedInternalInsets="); + sb.append(providedInternalInsets); + } + if (paramsForRotation != null && paramsForRotation.length != 0) { + sb.append(System.lineSeparator()); + sb.append(prefix).append(" paramsForRotation="); + for (int i = 0; i < paramsForRotation.length; ++i) { + if (i > 0) sb.append(' '); + sb.append(paramsForRotation[i].toString()); + } + } sb.append('}'); return sb.toString(); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 07a4988f47fd4..add4556d17ee2 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -23,10 +23,12 @@ import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; import static android.app.StatusBarManager.WindowType; import static android.app.StatusBarManager.WindowVisibleState; import static android.app.StatusBarManager.windowStateToString; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.containsType; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS; import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS; @@ -67,6 +69,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.database.ContentObserver; +import android.graphics.Insets; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; @@ -199,6 +202,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private Bundle mSavedState; private NavigationBarView mNavigationBarView; + private NavigationBarFrame mFrame; private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING; @@ -489,34 +493,17 @@ public class NavigationBar implements View.OnAttachStateChangeListener, } public View createView(Bundle savedState) { - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, - WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH - | WindowManager.LayoutParams.FLAG_SLIPPERY, - PixelFormat.TRANSLUCENT); - lp.token = new Binder(); - lp.accessibilityTitle = mContext.getString(R.string.nav_bar); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; - lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - lp.windowAnimations = 0; - lp.setTitle("NavigationBar" + mContext.getDisplayId()); - lp.setFitInsetsTypes(0 /* types */); - lp.setTrustedOverlay(); - - NavigationBarFrame frame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate( + mFrame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate( R.layout.navigation_bar_window, null); - View barView = LayoutInflater.from(frame.getContext()).inflate( - R.layout.navigation_bar, frame); + View barView = LayoutInflater.from(mFrame.getContext()).inflate( + R.layout.navigation_bar, mFrame); barView.addOnAttachStateChangeListener(this); mNavigationBarView = barView.findViewById(R.id.navigation_bar_view); if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView); - mContext.getSystemService(WindowManager.class).addView(frame, lp); + mContext.getSystemService(WindowManager.class).addView(mFrame, + getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration + .getRotation())); mDisplayId = mContext.getDisplayId(); mIsOnDefaultDisplay = mDisplayId == DEFAULT_DISPLAY; @@ -670,6 +657,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, mHandler.removeCallbacks(mAutoDim); mHandler.removeCallbacks(mOnVariableDurationHomeLongClick); mHandler.removeCallbacks(mEnableLayoutTransitions); + mFrame = null; mNavigationBarView = null; mOrientationHandle = null; } @@ -688,6 +676,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener, * Called when a non-reloading configuration change happens and we need to update. */ public void onConfigurationChanged(Configuration newConfig) { + final int rotation = newConfig.windowConfiguration.getRotation(); final Locale locale = mContext.getResources().getConfiguration().locale; final int ld = TextUtils.getLayoutDirectionFromLocale(locale); if (!locale.equals(mLocale) || ld != mLayoutDirection) { @@ -701,9 +690,8 @@ public class NavigationBar implements View.OnAttachStateChangeListener, refreshLayout(ld); } - repositionNavigationBar(); + repositionNavigationBar(rotation); if (canShowSecondaryHandle()) { - int rotation = newConfig.windowConfiguration.getRotation(); if (rotation != mCurrentRotation) { mCurrentRotation = rotation; orientSecondaryHomeHandle(); @@ -1068,13 +1056,12 @@ public class NavigationBar implements View.OnAttachStateChangeListener, || (mDisabledFlags1 & StatusBarManager.DISABLE_SEARCH) != 0; } - private void repositionNavigationBar() { - if (!mNavigationBarView.isAttachedToWindow()) return; + private void repositionNavigationBar(int rotation) { + if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return; prepareNavigationBarView(); - mWindowManager.updateViewLayout((View) mNavigationBarView.getParent(), - ((View) mNavigationBarView.getParent()).getLayoutParams()); + mWindowManager.updateViewLayout(mFrame, getBarLayoutParams(rotation)); } private void updateScreenPinningGestures() { @@ -1509,13 +1496,94 @@ public class NavigationBar implements View.OnAttachStateChangeListener, private final NavigationBarA11yHelper.NavA11yEventListener mAccessibilityListener = this::updateAccessibilityServicesState; + private WindowManager.LayoutParams getBarLayoutParams(int rotation) { + WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation); + lp.paramsForRotation = new WindowManager.LayoutParams[4]; + for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { + lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot); + } + return lp; + } + + private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) { + int width = WindowManager.LayoutParams.MATCH_PARENT; + int height = WindowManager.LayoutParams.MATCH_PARENT; + int insetsHeight = -1; + int gravity = Gravity.BOTTOM; + if (INSETS_LAYOUT_GENERALIZATION) { + boolean navBarCanMove = true; + if (mWindowManager != null && mWindowManager.getCurrentWindowMetrics() != null) { + Rect displaySize = mWindowManager.getCurrentWindowMetrics().getBounds(); + navBarCanMove = displaySize.width() != displaySize.height() + && mContext.getResources().getBoolean( + com.android.internal.R.bool.config_navBarCanMove); + } + if (!navBarCanMove) { + height = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_frame_height); + insetsHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_height); + } else { + switch (rotation) { + case ROTATION_UNDEFINED: + case Surface.ROTATION_0: + case Surface.ROTATION_180: + height = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_frame_height); + insetsHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_height); + break; + case Surface.ROTATION_90: + gravity = Gravity.RIGHT; + width = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_width); + break; + case Surface.ROTATION_270: + gravity = Gravity.LEFT; + width = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_width); + break; + } + } + } + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + width, + height, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, + WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_SLIPPERY, + PixelFormat.TRANSLUCENT); + if (INSETS_LAYOUT_GENERALIZATION) { + lp.gravity = gravity; + if (insetsHeight != -1) { + lp.providedInternalInsets = Insets.of(0, height - insetsHeight, 0, 0); + } else { + lp.providedInternalInsets = Insets.NONE; + } + } + lp.token = new Binder(); + lp.accessibilityTitle = mContext.getString(R.string.nav_bar); + lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; + lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + lp.windowAnimations = 0; + lp.setTitle("NavigationBar" + mContext.getDisplayId()); + lp.setFitInsetsTypes(0 /* types */); + lp.setTrustedOverlay(); + return lp; + } + private boolean canShowSecondaryHandle() { return mNavBarMode == NAV_BAR_MODE_GESTURAL && mOrientationHandle != null; } private final Consumer mRotationWatcher = rotation -> { - if (mNavigationBarView.needsReorient(rotation)) { - repositionNavigationBar(); + if (mNavigationBarView != null + && mNavigationBarView.needsReorient(rotation)) { + repositionNavigationBar(rotation); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index 9a25a70788592..3d3b58ae0c1f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR; @@ -34,6 +36,7 @@ import android.os.RemoteException; import android.util.Log; import android.view.Gravity; import android.view.IWindowManager; +import android.view.Surface; import android.view.ViewGroup; import android.view.WindowManager; @@ -118,21 +121,7 @@ public class StatusBarWindowController { // Now that the status bar window encompasses the sliding panel and its // translucent backdrop, the entire thing is made TRANSLUCENT and is // hardware-accelerated. - mLp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - mBarHeight, - WindowManager.LayoutParams.TYPE_STATUS_BAR, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH - | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, - PixelFormat.TRANSLUCENT); - mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; - mLp.token = new Binder(); - mLp.gravity = Gravity.TOP; - mLp.setFitInsetsTypes(0 /* types */); - mLp.setTitle("StatusBar"); - mLp.packageName = mContext.getPackageName(); - mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + mLp = getBarLayoutParams(mContext.getDisplay().getRotation()); mWindowManager.addView(mStatusBarView, mLp); mLpChanged.copyFrom(mLp); @@ -141,6 +130,63 @@ public class StatusBarWindowController { calculateStatusBarLocationsForAllRotations(); } + private WindowManager.LayoutParams getBarLayoutParams(int rotation) { + WindowManager.LayoutParams lp = getBarLayoutParamsForRotation(rotation); + lp.paramsForRotation = new WindowManager.LayoutParams[4]; + for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { + lp.paramsForRotation[rot] = getBarLayoutParamsForRotation(rot); + } + return lp; + } + + private WindowManager.LayoutParams getBarLayoutParamsForRotation(int rotation) { + int height = mBarHeight; + if (INSETS_LAYOUT_GENERALIZATION) { + Rect displayBounds = mWindowManager.getCurrentWindowMetrics().getBounds(); + int defaultAndUpsideDownHeight; + int theOtherHeight; + if (displayBounds.width() > displayBounds.height()) { + defaultAndUpsideDownHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height_landscape); + theOtherHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height_portrait); + } else { + defaultAndUpsideDownHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height_portrait); + theOtherHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height_landscape); + } + switch (rotation) { + case ROTATION_UNDEFINED: + case Surface.ROTATION_0: + case Surface.ROTATION_180: + height = defaultAndUpsideDownHeight; + break; + case Surface.ROTATION_90: + case Surface.ROTATION_270: + height = theOtherHeight; + break; + } + } + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + WindowManager.LayoutParams.MATCH_PARENT, + height, + WindowManager.LayoutParams.TYPE_STATUS_BAR, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, + PixelFormat.TRANSLUCENT); + lp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC; + lp.token = new Binder(); + lp.gravity = Gravity.TOP; + lp.setFitInsetsTypes(0 /* types */); + lp.setTitle("StatusBar"); + lp.packageName = mContext.getPackageName(); + lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + return lp; + + } + private void calculateStatusBarLocationsForAllRotations() { Rect[] bounds = new Rect[4]; bounds[0] = mContentInsetsProvider diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 7f7c095103e33..a3ed224073e19 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -39,6 +39,7 @@ import static android.view.InsetsState.ITYPE_RIGHT_GESTURES; import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.InsetsState.ITYPE_TOP_MANDATORY_GESTURES; import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.ViewRootImpl.computeWindowBounds; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; @@ -333,6 +334,7 @@ public class DisplayPolicy { private static final Rect sTmpRect = new Rect(); private static final Rect sTmpNavFrame = new Rect(); private static final Rect sTmpStatusFrame = new Rect(); + private static final Rect sTmpDecorFrame = new Rect(); private static final Rect sTmpScreenDecorFrame = new Rect(); private static final Rect sTmpLastParentFrame = new Rect(); private static final Rect sTmpDisplayFrameBounds = new Rect(); @@ -430,8 +432,10 @@ public class DisplayPolicy { final int displayId = displayContent.getDisplayId(); - mBarContentFrames.put(TYPE_STATUS_BAR, new Rect()); - mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect()); + if (!INSETS_LAYOUT_GENERALIZATION) { + mBarContentFrames.put(TYPE_STATUS_BAR, new Rect()); + mBarContentFrames.put(TYPE_NAVIGATION_BAR, new Rect()); + } final Resources r = mContext.getResources(); mCarDockEnablesAccelerometer = r.getBoolean(R.bool.config_carDockEnablesAccelerometer); @@ -1081,7 +1085,9 @@ public class DisplayPolicy { mStatusBar = win; final TriConsumer frameProvider = (displayFrames, windowState, rect) -> { - rect.bottom = rect.top + getStatusBarHeight(displayFrames); + if (!INSETS_LAYOUT_GENERALIZATION) { + rect.bottom = rect.top + getStatusBarHeight(displayFrames); + } }; mDisplayContent.setInsetProvider(ITYPE_STATUS_BAR, win, frameProvider); mDisplayContent.setInsetProvider(ITYPE_TOP_MANDATORY_GESTURES, win, frameProvider); @@ -1091,18 +1097,22 @@ public class DisplayPolicy { mNavigationBar = win; mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win, (displayFrames, windowState, inOutFrame) -> { - - // In Gesture Nav, navigation bar frame is larger than frame to - // calculate inset. - if (navigationBarPosition(displayFrames.mDisplayWidth, - displayFrames.mDisplayHeight, - displayFrames.mRotation) == NAV_BAR_BOTTOM - && !mNavButtonForcedVisible) { - sTmpRect.set(inOutFrame); - sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe); - inOutFrame.top = sTmpRect.bottom - - getNavigationBarHeight(displayFrames.mRotation, - mDisplayContent.getConfiguration().uiMode); + if (INSETS_LAYOUT_GENERALIZATION) { + inOutFrame.inset(windowState.getLayoutingAttrs( + displayFrames.mRotation).providedInternalInsets); + } else { + // In Gesture Nav, navigation bar frame is larger than frame to + // calculate inset. + if (navigationBarPosition(displayFrames.mDisplayWidth, + displayFrames.mDisplayHeight, + displayFrames.mRotation) == NAV_BAR_BOTTOM + && !mNavButtonForcedVisible) { + sTmpRect.set(inOutFrame); + sTmpRect.intersectUnchecked(displayFrames.mDisplayCutoutSafe); + inOutFrame.top = sTmpRect.bottom + - getNavigationBarHeight(displayFrames.mRotation, + mDisplayContent.getConfiguration().uiMode); + } } }, @@ -1163,7 +1173,14 @@ public class DisplayPolicy { mExtraNavBarAltPosition = getAltBarPosition(attrs); break; } - mDisplayContent.setInsetProvider(insetsType, win, null); + if (!INSETS_LAYOUT_GENERALIZATION) { + mDisplayContent.setInsetProvider(insetsType, win, null); + } else { + mDisplayContent.setInsetProvider(insetsType, win, (displayFrames, + windowState, inOutFrame) -> inOutFrame.inset( + windowState.getLayoutingAttrs(displayFrames.mRotation) + .providedInternalInsets)); + } } } break; @@ -1254,8 +1271,17 @@ public class DisplayPolicy { } private int getStatusBarHeight(DisplayFrames displayFrames) { - return Math.max(mStatusBarHeightForRotation[displayFrames.mRotation], - displayFrames.mDisplayCutoutSafe.top); + int statusBarHeight; + if (INSETS_LAYOUT_GENERALIZATION) { + if (mStatusBar != null) { + statusBarHeight = mStatusBar.getLayoutingAttrs(displayFrames.mRotation).height; + } else { + statusBarHeight = 0; + } + } else { + statusBarHeight = mStatusBarHeightForRotation[displayFrames.mRotation]; + } + return Math.max(statusBarHeight, displayFrames.mDisplayCutoutSafe.top); } WindowState getStatusBar() { @@ -1425,13 +1451,30 @@ public class DisplayPolicy { WindowFrames simulatedWindowFrames, SparseArray contentFrames, Consumer layout) { win.setSimulatedWindowFrames(simulatedWindowFrames); + final int requestedHeight = win.mRequestedHeight; + final int requestedWidth = win.mRequestedWidth; + if (INSETS_LAYOUT_GENERALIZATION) { + // Without a full layout process, in order to layout the system bars correctly, we need + // to set the requested size and the initial display frames to the window. + WindowManager.LayoutParams params = win.getLayoutingAttrs(displayFrames.mRotation); + win.setRequestedSize(params.width, params.height); + sTmpDecorFrame.set(0, 0, displayFrames.mDisplayWidth, displayFrames.mDisplayHeight); + simulatedWindowFrames.setFrames(sTmpDecorFrame /* parentFrame */, + sTmpDecorFrame /* displayFrame */); + simulatedWindowFrames.mIsSimulatingDecorWindow = true; + } final Rect contentFrame = new Rect(); try { layout.accept(contentFrame); } finally { win.setSimulatedWindowFrames(null); + if (INSETS_LAYOUT_GENERALIZATION) { + win.setRequestedSize(requestedWidth, requestedHeight); + } + } + if (!INSETS_LAYOUT_GENERALIZATION) { + contentFrames.put(win.mAttrs.type, contentFrame); } - contentFrames.put(win.mAttrs.type, contentFrame); mDisplayContent.getInsetsStateController().computeSimulatedState( win, displayFrames, simulatedWindowFrames); } @@ -1442,15 +1485,31 @@ public class DisplayPolicy { * some temporal states, but doesn't change the window frames used to show on screen. */ void simulateLayoutDisplay(DisplayFrames displayFrames, SparseArray barContentFrames) { - final WindowFrames simulatedWindowFrames = new WindowFrames(); if (mNavigationBar != null) { - simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames, - barContentFrames, contentFrame -> layoutNavigationBar(displayFrames, - contentFrame)); + final WindowFrames simulatedWindowFrames = new WindowFrames(); + if (INSETS_LAYOUT_GENERALIZATION) { + simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames, + barContentFrames, + contentFrame -> simulateLayoutForContentFrame(displayFrames, + mNavigationBar, contentFrame)); + } else { + simulateLayoutDecorWindow(mNavigationBar, displayFrames, simulatedWindowFrames, + barContentFrames, contentFrame -> layoutNavigationBar(displayFrames, + contentFrame)); + } } if (mStatusBar != null) { - simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames, - barContentFrames, contentFrame -> layoutStatusBar(displayFrames, contentFrame)); + final WindowFrames simulatedWindowFrames = new WindowFrames(); + if (INSETS_LAYOUT_GENERALIZATION) { + simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames, + barContentFrames, + contentFrame -> simulateLayoutForContentFrame(displayFrames, + mStatusBar, contentFrame)); + } else { + simulateLayoutDecorWindow(mStatusBar, displayFrames, simulatedWindowFrames, + barContentFrames, + contentFrame -> layoutStatusBar(displayFrames, contentFrame)); + } } } @@ -1469,7 +1528,7 @@ public class DisplayPolicy { windowFrames.setFrames(sTmpStatusFrame /* parentFrame */, sTmpStatusFrame /* displayFrame */); // Let the status bar determine its size. - mStatusBar.computeFrameAndUpdateSourceFrame(); + mStatusBar.computeFrameAndUpdateSourceFrame(displayFrames); // For layout, the status bar is always at the top with our fixed height. int statusBarBottom = displayFrames.mUnrestricted.top @@ -1520,18 +1579,18 @@ public class DisplayPolicy { } else if (navBarPosition == NAV_BAR_RIGHT) { // Landscape screen; nav bar goes to the right. navigationFrame.left = Math.min(cutoutSafeUnrestricted.right, navigationFrame.right) - - getNavigationBarWidth(rotation, uiMode); + - getNavigationBarWidth(rotation, uiMode, navBarPosition); } else if (navBarPosition == NAV_BAR_LEFT) { // Seascape screen; nav bar goes to the left. navigationFrame.right = Math.max(cutoutSafeUnrestricted.left, navigationFrame.left) - + getNavigationBarWidth(rotation, uiMode); + + getNavigationBarWidth(rotation, uiMode, navBarPosition); } // Compute the final frame. final WindowFrames windowFrames = mNavigationBar.getLayoutingWindowFrames(); windowFrames.setFrames(navigationFrame /* parentFrame */, navigationFrame /* displayFrame */); - mNavigationBar.computeFrameAndUpdateSourceFrame(); + mNavigationBar.computeFrameAndUpdateSourceFrame(displayFrames); sTmpRect.set(windowFrames.mFrame); sTmpRect.intersect(displayFrames.mDisplayCutoutSafe); contentFrame.set(sTmpRect); @@ -1540,6 +1599,16 @@ public class DisplayPolicy { return navBarPosition; } + private void simulateLayoutForContentFrame(DisplayFrames displayFrames, WindowState win, + Rect simulatedContentFrame) { + layoutWindowLw(win, null /* attached */, displayFrames); + final Rect contentFrame = sTmpRect; + contentFrame.set(win.getLayoutingWindowFrames().mFrame); + // Excluding the display cutout before set to the simulated content frame. + contentFrame.intersect(displayFrames.mDisplayCutoutSafe); + simulatedContentFrame.set(contentFrame); + } + private boolean canReceiveInput(WindowState win) { boolean notFocusable = (win.getAttrs().flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) != 0; @@ -1561,16 +1630,16 @@ public class DisplayPolicy { * @param displayFrames The display frames. */ public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) { - if (win == mNavigationBar) { + if (win == mNavigationBar && !INSETS_LAYOUT_GENERALIZATION) { mNavigationBarPosition = layoutNavigationBar(displayFrames, mBarContentFrames.get(TYPE_NAVIGATION_BAR)); return; } - if ((win == mStatusBar && !canReceiveInput(win))) { + if ((win == mStatusBar && !canReceiveInput(win)) && !INSETS_LAYOUT_GENERALIZATION) { layoutStatusBar(displayFrames, mBarContentFrames.get(TYPE_STATUS_BAR)); return; } - final WindowManager.LayoutParams attrs = win.getAttrs(); + final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation); final int type = attrs.type; final int fl = attrs.flags; @@ -1578,7 +1647,7 @@ public class DisplayPolicy { final int sim = attrs.softInputMode; displayFrames = win.getDisplayFrames(displayFrames); - final WindowFrames windowFrames = win.getWindowFrames(); + final WindowFrames windowFrames = win.getLayoutingWindowFrames(); sTmpLastParentFrame.set(windowFrames.mParentFrame); final Rect pf = windowFrames.mParentFrame; @@ -1589,7 +1658,13 @@ public class DisplayPolicy { final boolean layoutInsetDecor = (fl & FLAG_LAYOUT_INSET_DECOR) == FLAG_LAYOUT_INSET_DECOR; final InsetsState state = win.getInsetsState(); - computeWindowBounds(attrs, state, win.mToken.getBounds(), df); + if (windowFrames.mIsSimulatingDecorWindow && INSETS_LAYOUT_GENERALIZATION) { + // Override the bounds in window token has many side effects. Directly use the display + // frame set for the simulated layout for this case. + computeWindowBounds(attrs, state, df, df); + } else { + computeWindowBounds(attrs, state, win.mToken.getBounds(), df); + } if (attached == null) { pf.set(df); if ((pfl & PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME) != 0) { @@ -1689,7 +1764,17 @@ public class DisplayPolicy { windowFrames.setContentChanged(true); } - win.computeFrameAndUpdateSourceFrame(); + win.computeFrameAndUpdateSourceFrame(displayFrames); + if (INSETS_LAYOUT_GENERALIZATION && attrs.type == TYPE_STATUS_BAR) { + if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) { + // Make sure that the zone we're avoiding for the cutout is at least as tall as the + // status bar; otherwise fullscreen apps will end up cutting halfway into the status + // bar. + displayFrames.mDisplayCutoutSafe.top = Math.max( + displayFrames.mDisplayCutoutSafe.top, + windowFrames.mFrame.bottom); + } + } } WindowState getTopFullscreenOpaqueWindow() { @@ -2118,11 +2203,37 @@ public class DisplayPolicy { return mUiContext; } - private int getNavigationBarWidth(int rotation, int uiMode) { - if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { - return mNavigationBarWidthForRotationInCarMode[rotation]; + private int getNavigationBarWidth(int rotation, int uiMode, int position) { + if (INSETS_LAYOUT_GENERALIZATION) { + if (mNavigationBar == null) { + return 0; + } + LayoutParams lp = mNavigationBar.mAttrs; + if (lp.paramsForRotation != null + && lp.paramsForRotation.length == 4 + && lp.paramsForRotation[rotation] != null) { + lp = lp.paramsForRotation[rotation]; + } + if (position == NAV_BAR_LEFT) { + if (lp.width > lp.providedInternalInsets.right) { + return lp.width - lp.providedInternalInsets.right; + } else { + return 0; + } + } else if (position == NAV_BAR_RIGHT) { + if (lp.width > lp.providedInternalInsets.left) { + return lp.width - lp.providedInternalInsets.left; + } else { + return 0; + } + } + return lp.width; } else { - return mNavigationBarWidthForRotationDefault[rotation]; + if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { + return mNavigationBarWidthForRotationInCarMode[rotation]; + } else { + return mNavigationBarWidthForRotationDefault[rotation]; + } } } @@ -2149,7 +2260,7 @@ public class DisplayPolicy { if (hasNavigationBar()) { final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation); if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) { - width -= getNavigationBarWidth(rotation, uiMode); + width -= getNavigationBarWidth(rotation, uiMode, navBarPosition); } } if (displayCutout != null) { @@ -2159,10 +2270,21 @@ public class DisplayPolicy { } private int getNavigationBarHeight(int rotation, int uiMode) { - if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { - return mNavigationBarHeightForRotationInCarMode[rotation]; + if (INSETS_LAYOUT_GENERALIZATION) { + if (mNavigationBar == null) { + return 0; + } + LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation); + if (lp.height < lp.providedInternalInsets.top) { + return 0; + } + return lp.height - lp.providedInternalInsets.top; } else { - return mNavigationBarHeightForRotationDefault[rotation]; + if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { + return mNavigationBarHeightForRotationInCarMode[rotation]; + } else { + return mNavigationBarHeightForRotationDefault[rotation]; + } } } @@ -2179,10 +2301,17 @@ public class DisplayPolicy { * @return navigation bar frame height */ private int getNavigationBarFrameHeight(int rotation, int uiMode) { - if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { - return mNavigationBarHeightForRotationInCarMode[rotation]; + if (INSETS_LAYOUT_GENERALIZATION) { + if (mNavigationBar == null) { + return 0; + } + return mNavigationBar.mAttrs.height; } else { - return mNavigationBarFrameHeightForRotationDefault[rotation]; + if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) { + return mNavigationBarHeightForRotationInCarMode[rotation]; + } else { + return mNavigationBarFrameHeightForRotationDefault[rotation]; + } } } @@ -2303,9 +2432,9 @@ public class DisplayPolicy { if (position == NAV_BAR_BOTTOM) { outInsets.bottom = getNavigationBarHeight(displayRotation, uiMode); } else if (position == NAV_BAR_RIGHT) { - outInsets.right = getNavigationBarWidth(displayRotation, uiMode); + outInsets.right = getNavigationBarWidth(displayRotation, uiMode, position); } else if (position == NAV_BAR_LEFT) { - outInsets.left = getNavigationBarWidth(displayRotation, uiMode); + outInsets.left = getNavigationBarWidth(displayRotation, uiMode, position); } } @@ -2331,6 +2460,17 @@ public class DisplayPolicy { @NavigationBarPosition int navigationBarPosition(int displayWidth, int displayHeight, int displayRotation) { + if (INSETS_LAYOUT_GENERALIZATION && mNavigationBar != null) { + final int gravity = mNavigationBar.getLayoutingAttrs(displayRotation).gravity; + switch (gravity) { + case Gravity.LEFT: + return NAV_BAR_LEFT; + case Gravity.RIGHT: + return NAV_BAR_RIGHT; + default: + return NAV_BAR_BOTTOM; + } + } if (navigationBarCanMove() && displayWidth > displayHeight) { if (displayRotation == Surface.ROTATION_270) { return NAV_BAR_LEFT; @@ -2694,7 +2834,24 @@ public class DisplayPolicy { private Rect getBarContentFrameForWindow(WindowState win, int windowType) { final Rect rotatedBarFrame = win.mToken.getFixedRotationBarContentFrame(windowType); - return rotatedBarFrame != null ? rotatedBarFrame : mBarContentFrames.get(windowType); + if (rotatedBarFrame != null) { + return rotatedBarFrame; + } + if (!INSETS_LAYOUT_GENERALIZATION) { + return mBarContentFrames.get(windowType); + } + // We only need a window specific information for the fixed rotation, use raw insets state + // for all other cases. + InsetsState insetsState = mDisplayContent.getInsetsStateController().getRawInsetsState(); + final Rect tmpRect = new Rect(); + if (windowType == TYPE_NAVIGATION_BAR) { + tmpRect.set(insetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR).getFrame()); + } + if (windowType == TYPE_STATUS_BAR) { + tmpRect.set(insetsState.getSource(InsetsState.ITYPE_STATUS_BAR).getFrame()); + } + tmpRect.intersect(mDisplayContent.mDisplayFrames.mDisplayCutoutSafe); + return tmpRect; } /** diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java index ffd6d21c1026b..baea854395826 100644 --- a/services/core/java/com/android/server/wm/WindowFrames.java +++ b/services/core/java/com/android/server/wm/WindowFrames.java @@ -88,6 +88,11 @@ public class WindowFrames { // screen size compatibility mode. final Rect mCompatFrame = new Rect(); + /** + * {@code true} if the window frame is a simulated frame and attached to a decor window. + */ + boolean mIsSimulatingDecorWindow = false; + /** * Whether the parent frame would have been different if there was no display cutout. */ diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 339134486b0aa..a748b45ab07c1 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2515,7 +2515,8 @@ public class WindowManagerService extends IWindowManager.Stub Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } if (winAnimator.mSurfaceController != null) { - win.calculateSurfaceBounds(win.getAttrs(), mTmpRect); + win.calculateSurfaceBounds(win.getLayoutingAttrs( + win.getWindowConfiguration().getRotation()), mTmpRect); outSurfaceSize.set(mTmpRect.width(), mTmpRect.height()); } getInsetsSourceControls(win, outActiveControls); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 53271207bbe34..43879a01720ba 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -34,6 +34,7 @@ import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.SurfaceControl.Transaction; import static android.view.SurfaceControl.getGlobalTransaction; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; @@ -1259,8 +1260,8 @@ class WindowState extends WindowContainer implements WindowManagerP frame.inset(left, top, right, bottom); } - void computeFrameAndUpdateSourceFrame() { - computeFrame(); + void computeFrameAndUpdateSourceFrame(DisplayFrames displayFrames) { + computeFrame(displayFrames); // Update the source frame to provide insets to other windows during layout. If the // simulated frames exist, then this is not computing a stable result so just skip. if (mControllableInsetProvider != null && mSimulatedWindowFrames == null) { @@ -1271,7 +1272,7 @@ class WindowState extends WindowContainer implements WindowManagerP /** * Perform standard frame computation. The result can be obtained with getFrame() if so desired. */ - void computeFrame() { + void computeFrame(DisplayFrames displayFrames) { if (mWillReplaceWindow && (mAnimatingExit || !mReplacingRemoveRequested)) { // This window is being replaced and either already got information that it's being // removed or we are still waiting for some information. Because of this we don't @@ -1384,7 +1385,8 @@ class WindowState extends WindowContainer implements WindowManagerP final int fw = windowFrames.mFrame.width(); final int fh = windowFrames.mFrame.height(); - applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame); + applyGravityAndUpdateFrame(windowFrames, layoutContainingFrame, layoutDisplayFrame, + displayFrames); if (mAttrs.type == TYPE_DOCK_DIVIDER) { if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) { @@ -1477,6 +1479,18 @@ class WindowState extends WindowContainer implements WindowManagerP return mAttrs; } + WindowManager.LayoutParams getLayoutingAttrs(int rotation) { + if (!INSETS_LAYOUT_GENERALIZATION) { + return mAttrs; + } + final WindowManager.LayoutParams[] paramsForRotation = mAttrs.paramsForRotation; + if (paramsForRotation == null || paramsForRotation.length != 4 + || paramsForRotation[rotation] == null) { + return mAttrs; + } + return paramsForRotation[rotation]; + } + /** Retrieves the flags used to disable system UI functions. */ int getDisableFlags() { return mDisableFlags; @@ -4412,12 +4426,13 @@ class WindowState extends WindowContainer implements WindowManagerP } private void applyGravityAndUpdateFrame(WindowFrames windowFrames, Rect containingFrame, - Rect displayFrame) { + Rect displayFrame, DisplayFrames displayFrames) { final int pw = containingFrame.width(); final int ph = containingFrame.height(); final Task task = getTask(); final boolean inNonFullscreenContainer = !inAppWindowThatMatchesParentBounds(); - final boolean noLimits = (mAttrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0; + final WindowManager.LayoutParams attrs = getLayoutingAttrs(displayFrames.mRotation); + final boolean noLimits = (attrs.flags & FLAG_LAYOUT_NO_LIMITS) != 0; // We need to fit it to the display if either // a) The window is in a fullscreen container, or we don't have a task (we assume fullscreen @@ -4427,49 +4442,54 @@ class WindowState extends WindowContainer implements WindowManagerP // screen, but SurfaceViews want to be always at a specific location so we don't fit it to // the display. final boolean fitToDisplay = (task == null || !inNonFullscreenContainer) - || ((mAttrs.type != TYPE_BASE_APPLICATION) && !noLimits); + || ((attrs.type != TYPE_BASE_APPLICATION) && !noLimits); float x, y; int w,h; final boolean hasCompatScale = hasCompatScale(); - if ((mAttrs.flags & FLAG_SCALED) != 0) { - if (mAttrs.width < 0) { + if ((attrs.flags & FLAG_SCALED) != 0 || mAttrs != attrs) { + // For the window with different layout attrs for different rotations, we need to avoid + // using requested size. Otherwise, when finishing a simulated rotation, the information + // coming from WindowManagerServices to the ViewRootImpl may not contain the correct + // value for the new rotation, and there will be a quick flash of wrong layout when the + // simulated activity faded out. + if (attrs.width < 0) { w = pw; } else if (hasCompatScale) { - w = (int)(mAttrs.width * mGlobalScale + .5f); + w = (int) (attrs.width * mGlobalScale + .5f); } else { - w = mAttrs.width; + w = attrs.width; } - if (mAttrs.height < 0) { + if (attrs.height < 0) { h = ph; } else if (hasCompatScale) { - h = (int)(mAttrs.height * mGlobalScale + .5f); + h = (int) (attrs.height * mGlobalScale + .5f); } else { - h = mAttrs.height; + h = attrs.height; } } else { - if (mAttrs.width == MATCH_PARENT) { + if (attrs.width == MATCH_PARENT) { w = pw; } else if (hasCompatScale) { - w = (int)(mRequestedWidth * mGlobalScale + .5f); + w = (int) (mRequestedWidth * mGlobalScale + .5f); } else { w = mRequestedWidth; } - if (mAttrs.height == MATCH_PARENT) { + if (attrs.height == MATCH_PARENT) { h = ph; } else if (hasCompatScale) { - h = (int)(mRequestedHeight * mGlobalScale + .5f); + h = (int) (mRequestedHeight * mGlobalScale + .5f); } else { h = mRequestedHeight; } } if (hasCompatScale) { - x = mAttrs.x * mGlobalScale; - y = mAttrs.y * mGlobalScale; + x = attrs.x * mGlobalScale; + y = attrs.y * mGlobalScale; } else { - x = mAttrs.x; - y = mAttrs.y; + x = attrs.x; + y = attrs.y; } if (inNonFullscreenContainer && !layoutInParentFrame()) { @@ -4496,13 +4516,12 @@ class WindowState extends WindowContainer implements WindowManagerP } // Set mFrame - Gravity.apply(mAttrs.gravity, w, h, containingFrame, - (int) (x + mAttrs.horizontalMargin * pw), - (int) (y + mAttrs.verticalMargin * ph), windowFrames.mFrame); - + Gravity.apply(attrs.gravity, w, h, containingFrame, + (int) (x + attrs.horizontalMargin * pw), + (int) (y + attrs.verticalMargin * ph), windowFrames.mFrame); // Now make sure the window fits in the overall display frame. if (fitToDisplay) { - Gravity.applyDisplay(mAttrs.gravity, displayFrame, windowFrames.mFrame); + Gravity.applyDisplay(attrs.gravity, displayFrame, windowFrames.mFrame); } // We need to make sure we update the CompatFrame as it is used for diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index fbfa400ba8520..26241421cef92 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -16,9 +16,11 @@ package com.android.server.wm; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS; @@ -451,9 +453,24 @@ class WindowToken extends WindowContainer { } Rect getFixedRotationBarContentFrame(int windowType) { - return isFixedRotationTransforming() - ? mFixedRotationTransformState.mBarContentFrames.get(windowType) - : null; + if (!isFixedRotationTransforming()) { + return null; + } + if (!INSETS_LAYOUT_GENERALIZATION) { + return mFixedRotationTransformState.mBarContentFrames.get(windowType); + } + final DisplayFrames displayFrames = mFixedRotationTransformState.mDisplayFrames; + final Rect tmpRect = new Rect(); + if (windowType == TYPE_NAVIGATION_BAR) { + tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_NAVIGATION_BAR) + .getFrame()); + } + if (windowType == TYPE_STATUS_BAR) { + tmpRect.set(displayFrames.mInsetsState.getSource(InsetsState.ITYPE_STATUS_BAR) + .getFrame()); + } + tmpRect.intersect(displayFrames.mDisplayCutoutSafe); + return tmpRect; } InsetsState getFixedRotationTransformInsetsState() { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 03304bb9456a3..3741d499bc074 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -24,7 +24,7 @@ import static android.view.InsetsState.ITYPE_TOP_GESTURES; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; @@ -50,13 +50,13 @@ import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; import static org.testng.Assert.expectThrows; import android.graphics.Insets; -import android.graphics.PixelFormat; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.util.Pair; @@ -69,7 +69,6 @@ import android.view.PrivacyIndicatorBounds; import android.view.RoundedCorners; import android.view.WindowInsets.Side; import android.view.WindowInsets.Type; -import android.view.WindowManager; import androidx.test.filters.SmallTest; @@ -109,12 +108,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow = spy(createWindow(null, TYPE_APPLICATION, "window")); // We only test window frames set by DisplayPolicy, so here prevents computeFrameLw from // changing those frames. - doNothing().when(mWindow).computeFrame(); - - final WindowManager.LayoutParams attrs = mWindow.mAttrs; - attrs.width = MATCH_PARENT; - attrs.height = MATCH_PARENT; - attrs.format = PixelFormat.TRANSLUCENT; + doNothing().when(mWindow).computeFrame(any()); spyOn(mStatusBarWindow); spyOn(mNavBarWindow); @@ -219,7 +213,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { } @Test - public void addingWindow_ignoresInsetsTypes_InWindowTypeWithPredefinedInsets() { + public void addingWindow_InWindowTypeWithPredefinedInsets() { mDisplayPolicy.removeWindowLw(mStatusBarWindow); // Removes the existing one. WindowState win = createWindow(null, TYPE_STATUS_BAR, "StatusBar"); win.mAttrs.providesInsetsTypes = new int[]{ITYPE_STATUS_BAR}; @@ -230,7 +224,13 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { InsetsSourceProvider provider = mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR); - assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame()); + if (INSETS_LAYOUT_GENERALIZATION) { + // In the new flexible insets setup, the insets frame should always respect the window + // layout result. + assertEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame()); + } else { + assertNotEquals(new Rect(0, 0, 500, 100), provider.getSource().getFrame()); + } } @Test @@ -733,10 +733,12 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { @Test public void testFixedRotationInsetsSourceFrame() { + mDisplayContent.mBaseDisplayHeight = DISPLAY_HEIGHT; + mDisplayContent.mBaseDisplayWidth = DISPLAY_WIDTH; doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent) .rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord)); - mWindow.mAboveInsetsState.addSource(mDisplayContent.getInsetsStateController() - .getRawInsetsState().peekSource(ITYPE_STATUS_BAR)); + mWindow.mAboveInsetsState.set( + mDisplayContent.getInsetsStateController().getRawInsetsState()); final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow) .getSource(ITYPE_STATUS_BAR).getFrame(); mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index b793be74c033a..3f13394874dc9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -313,7 +313,9 @@ public class DisplayPolicyTests extends WindowTestsBase { displayInfo.logicalHeight = 2000; displayInfo.rotation = ROTATION_0; - displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs); + WindowManager.LayoutParams attrs = mNavBarWindow.mAttrs; + displayPolicy.addWindowLw(mNavBarWindow, attrs); + mNavBarWindow.setRequestedSize(attrs.width, attrs.height); mNavBarWindow.getControllableInsetProvider().setServerVisible(true); final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState(); mImeWindow.mAboveInsetsState.set(state); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 683ed889d2830..3982a83d7778c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -58,8 +58,6 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { static final int DISPLAY_HEIGHT = 1000; static final int DISPLAY_DENSITY = 320; - static final int STATUS_BAR_HEIGHT = 10; - static final int NAV_BAR_HEIGHT = 15; static final int DISPLAY_CUTOUT_HEIGHT = 8; static final int IME_HEIGHT = 415; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java index a1f89ec757840..efe65381b7dfd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -57,12 +57,14 @@ import org.mockito.Mockito; public class WindowFrameTests extends WindowTestsBase { private DisplayContent mTestDisplayContent; + private DisplayFrames mTestDisplayFrames; @Before public void setUp() throws Exception { DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo); testDisplayInfo.displayCutout = null; mTestDisplayContent = createNewDisplay(testDisplayInfo); + mTestDisplayFrames = mTestDisplayContent.mDisplayFrames; } // Do not use this function directly in the tests below. Instead, use more explicit function @@ -99,7 +101,7 @@ public class WindowFrameTests extends WindowTestsBase { // Here the window has FILL_PARENT, FILL_PARENT // so we expect it to fill the entire available frame. w.getWindowFrames().setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 0, 0, 1000, 1000); assertRelFrame(w, 0, 0, 1000, 1000); @@ -108,14 +110,14 @@ public class WindowFrameTests extends WindowTestsBase { // and we use mRequestedWidth/mRequestedHeight w.mAttrs.width = 300; w.mAttrs.height = 300; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); // Explicit width and height without requested width/height // gets us nothing. assertFrame(w, 0, 0, 0, 0); w.mRequestedWidth = 300; w.mRequestedHeight = 300; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); // With requestedWidth/Height we can freely choose our size within the // parent bounds. assertFrame(w, 0, 0, 300, 300); @@ -128,14 +130,14 @@ public class WindowFrameTests extends WindowTestsBase { w.mRequestedWidth = -1; w.mAttrs.width = 100; w.mAttrs.height = 100; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 0, 0, 100, 100); w.mAttrs.flags = 0; // But sizes too large will be clipped to the containing frame w.mRequestedWidth = 1200; w.mRequestedHeight = 1200; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 0, 0, 1000, 1000); // Before they are clipped though windows will be shifted @@ -143,7 +145,7 @@ public class WindowFrameTests extends WindowTestsBase { w.mAttrs.y = 300; w.mRequestedWidth = 1000; w.mRequestedHeight = 1000; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 0, 0, 1000, 1000); // If there is room to move around in the parent frame the window will be shifted according @@ -153,18 +155,18 @@ public class WindowFrameTests extends WindowTestsBase { w.mRequestedWidth = 300; w.mRequestedHeight = 300; w.mAttrs.gravity = Gravity.RIGHT | Gravity.TOP; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 700, 0, 1000, 300); assertRelFrame(w, 700, 0, 1000, 300); w.mAttrs.gravity = Gravity.RIGHT | Gravity.BOTTOM; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 700, 700, 1000, 1000); assertRelFrame(w, 700, 700, 1000, 1000); // Window specified x and y are interpreted as offsets in the opposite // direction of gravity w.mAttrs.x = 100; w.mAttrs.y = 100; - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, 600, 600, 900, 900); assertRelFrame(w, 600, 600, 900, 900); } @@ -191,7 +193,7 @@ public class WindowFrameTests extends WindowTestsBase { final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); final WindowFrames windowFrames = w.getWindowFrames(); windowFrames.setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); // For non fullscreen tasks the containing frame is based off the // task bounds not the parent frame. assertEquals(resolvedTaskBounds, w.getFrame()); @@ -204,7 +206,7 @@ public class WindowFrameTests extends WindowTestsBase { final int cfBottom = logicalHeight / 2; final Rect cf = new Rect(0, 0, cfRight, cfBottom); windowFrames.setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertEquals(resolvedTaskBounds, w.getFrame()); assertEquals(0, w.getRelativeFrame().left); assertEquals(0, w.getRelativeFrame().top); @@ -233,7 +235,7 @@ public class WindowFrameTests extends WindowTestsBase { final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); final WindowFrames windowFrames = w.getWindowFrames(); windowFrames.setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); // For non fullscreen tasks the containing frame is based off the // task bounds not the parent frame. assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); @@ -249,7 +251,7 @@ public class WindowFrameTests extends WindowTestsBase { task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); task.setBounds(null); windowFrames.setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, cf); } @@ -285,7 +287,7 @@ public class WindowFrameTests extends WindowTestsBase { final Rect winRect = new Rect(200, 200, 300, 500); task.setBounds(winRect); w.getWindowFrames().setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, winRect.left, imeFrame.top - winRect.height(), winRect.right, imeFrame.top); // Now check that it won't get moved beyond the top @@ -293,7 +295,7 @@ public class WindowFrameTests extends WindowTestsBase { task.setBounds(winRect); w.setBounds(winRect); w.getWindowFrames().setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, winRect.left, 0, winRect.right, winRect.height()); // Now we have status bar. Check that it won't go into the status bar area. @@ -301,14 +303,14 @@ public class WindowFrameTests extends WindowTestsBase { statusBarFrame.bottom = 60; state.getSource(ITYPE_STATUS_BAR).setFrame(statusBarFrame); w.getWindowFrames().setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertFrame(w, winRect.left, statusBarFrame.bottom, winRect.right, statusBarFrame.bottom + winRect.height()); // Check that it's moved back without ime insets state.removeSource(ITYPE_IME); w.getWindowFrames().setFrames(pf, pf); - w.computeFrame(); + w.computeFrame(mTestDisplayFrames); assertEquals(winRect, w.getFrame()); } 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 111449d2c1003..4ce674b21c6b7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; +import static android.app.WindowConfiguration.ROTATION_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; @@ -29,11 +30,13 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.os.Process.SYSTEM_UID; import static android.view.View.VISIBLE; +import static android.view.ViewRootImpl.INSETS_LAYOUT_GENERALIZATION; import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW; +import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; @@ -83,10 +86,12 @@ import android.service.voice.IVoiceInteractionSession; import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; +import android.view.Gravity; import android.view.IDisplayWindowInsetsController; import android.view.IWindow; import android.view.InsetsSourceControl; import android.view.InsetsState; +import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.View; @@ -132,6 +137,9 @@ class WindowTestsBase extends SystemServiceTestsBase { DisplayInfo mDisplayInfo = new DisplayInfo(); DisplayContent mDefaultDisplay; + static final int STATUS_BAR_HEIGHT = 10; + static final int NAV_BAR_HEIGHT = 15; + /** * It is {@link #mDefaultDisplay} by default. If the test class or method is annotated with * {@link UseTestDisplay}, it will be an additional display. @@ -268,6 +276,14 @@ class WindowTestsBase extends SystemServiceTestsBase { } if (addAll || ArrayUtils.contains(requestedWindows, W_STATUS_BAR)) { mStatusBarWindow = createCommonWindow(null, TYPE_STATUS_BAR, "mStatusBarWindow"); + if (INSETS_LAYOUT_GENERALIZATION) { + mStatusBarWindow.mAttrs.height = STATUS_BAR_HEIGHT; + mStatusBarWindow.mAttrs.gravity = Gravity.TOP; + mStatusBarWindow.mAttrs.layoutInDisplayCutoutMode = + LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + mStatusBarWindow.setRequestedSize(WindowManager.LayoutParams.MATCH_PARENT, + STATUS_BAR_HEIGHT); + } } if (addAll || ArrayUtils.contains(requestedWindows, W_NOTIFICATION_SHADE)) { mNotificationShadeWindow = createCommonWindow(null, TYPE_NOTIFICATION_SHADE, @@ -275,6 +291,15 @@ class WindowTestsBase extends SystemServiceTestsBase { } if (addAll || ArrayUtils.contains(requestedWindows, W_NAVIGATION_BAR)) { mNavBarWindow = createCommonWindow(null, TYPE_NAVIGATION_BAR, "mNavBarWindow"); + if (INSETS_LAYOUT_GENERALIZATION) { + mNavBarWindow.mAttrs.height = NAV_BAR_HEIGHT; + mNavBarWindow.mAttrs.gravity = Gravity.BOTTOM; + mNavBarWindow.mAttrs.paramsForRotation = new WindowManager.LayoutParams[4]; + for (int rot = Surface.ROTATION_0; rot <= Surface.ROTATION_270; rot++) { + mNavBarWindow.mAttrs.paramsForRotation[rot] = + getNavBarLayoutParamsForRotation(rot); + } + } } if (addAll || ArrayUtils.contains(requestedWindows, W_DOCK_DIVIDER)) { mDockedDividerWindow = createCommonWindow(null, TYPE_DOCK_DIVIDER, @@ -302,6 +327,37 @@ class WindowTestsBase extends SystemServiceTestsBase { waitUntilHandlersIdle(); } + private WindowManager.LayoutParams getNavBarLayoutParamsForRotation(int rotation) { + int width = WindowManager.LayoutParams.MATCH_PARENT; + int height = WindowManager.LayoutParams.MATCH_PARENT; + int gravity = Gravity.BOTTOM; + if (INSETS_LAYOUT_GENERALIZATION) { + switch (rotation) { + case ROTATION_UNDEFINED: + case Surface.ROTATION_0: + case Surface.ROTATION_180: + height = NAV_BAR_HEIGHT; + break; + case Surface.ROTATION_90: + gravity = Gravity.RIGHT; + width = NAV_BAR_HEIGHT; + break; + case Surface.ROTATION_270: + gravity = Gravity.LEFT; + width = NAV_BAR_HEIGHT; + break; + } + } + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR); + lp.width = width; + lp.height = height; + if (INSETS_LAYOUT_GENERALIZATION) { + lp.gravity = gravity; + } + return lp; + } + void beforeCreateTestDisplay() { // Called before display is created. }