Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit be6a9a57 authored by Tiger Huang's avatar Tiger Huang
Browse files

Re-land: Make action mode view extend into system insets area

This CL makes the action mode view extend into system insets area if the
insets don't get consumed by its parent container. If the vertical
navigation bar is visible, the action mode view and the action bar will
fit navigation bar, which will align the width of the status bar color
view, and there won't be weird color blending in the corner.

Before this CL, when onConfigurationChanged of an ActionBarContextView
is called, it would read and set the wrong content height. This is
because ActionBarContextView extends AbsActionBarView, but didn't
override onConfigurationChanged which would read ActionBar_height
instead of ActionMode_height. This CL also fixes the issue.

Bug: 379783298
Fix: 397378527
Flag: com.android.window.flags.action_mode_edge_to_edge
Test: atest ActionBarContextViewTest
Change-Id: Iaa805482cf69fbf846b31a4626c01e9cbeafd12e
parent 8d989e6d
Loading
Loading
Loading
Loading
+97 −51
Original line number Diff line number Diff line
@@ -120,6 +120,7 @@ import com.android.internal.view.menu.MenuHelper;
import com.android.internal.widget.ActionBarContextView;
import com.android.internal.widget.BackgroundFallback;
import com.android.internal.widget.floatingtoolbar.FloatingToolbar;
import com.android.window.flags.Flags;

import java.util.List;
import java.util.concurrent.Executor;
@@ -201,6 +202,13 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
    ActionMode mPrimaryActionMode;
    private ActionMode mFloatingActionMode;
    private ActionBarContextView mPrimaryActionModeView;

    // Paddings loaded from R.attr.actionModeStyle.
    private int mActionModeViewInternalPaddingLeft;
    private int mActionModeViewInternalPaddingTop;
    private int mActionModeViewInternalPaddingRight;
    private int mActionModeViewInternalPaddingBottom;

    private PopupWindow mPrimaryActionModePopup;
    private Runnable mShowPrimaryActionModePopup;
    private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
@@ -1003,7 +1011,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
    public void onWindowSystemUiVisibilityChanged(int visible) {
        updateColorViews(null /* insets */, true /* animate */);

        if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
        if (!Flags.actionModeEdgeToEdge()
                && mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
            updateStatusGuardColor();
        }
    }
@@ -1040,7 +1049,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
        }
        mFrameOffsets.set(insets.getSystemWindowInsetsAsRect());
        insets = updateColorViews(insets, true /* animate */);
        insets = updateStatusGuard(insets);
        insets = updateActionModeInsets(insets);
        if (getForeground() != null) {
            drawableChanged();
        }
@@ -1592,7 +1601,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
        }
    }

    private WindowInsets updateStatusGuard(WindowInsets insets) {
    private WindowInsets updateActionModeInsets(WindowInsets insets) {
        boolean showStatusGuard = false;
        // Show the status guard when the non-overlay contextual action bar is showing
        if (mPrimaryActionModeView != null) {
@@ -1608,15 +1617,39 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
                    final Rect rect = mTempRect;

                    // Apply the insets that have not been applied by the contentParent yet.
                    WindowInsets innerInsets =
                    final WindowInsets innerInsets =
                            mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
                    final boolean consumesSystemWindowInsetsTop;
                    if (Flags.actionModeEdgeToEdge()) {
                        final Insets systemWindowInsets = innerInsets.getSystemWindowInsets();
                        final Insets newMargin = innerInsets.getInsets(
                                WindowInsets.Type.navigationBars());

                        // Don't extend into navigation bar area so the width can align with status
                        // bar color view.
                        if (mlp.leftMargin != newMargin.left
                                || mlp.rightMargin != newMargin.right) {
                            mlpChanged = true;
                            mlp.leftMargin = newMargin.left;
                            mlp.rightMargin = newMargin.right;
                        }

                        mPrimaryActionModeView.setPadding(
                                mActionModeViewInternalPaddingLeft + systemWindowInsets.left
                                        - newMargin.left,
                                mActionModeViewInternalPaddingTop + systemWindowInsets.top,
                                mActionModeViewInternalPaddingRight + systemWindowInsets.right
                                        - newMargin.right,
                                mActionModeViewInternalPaddingBottom);
                        consumesSystemWindowInsetsTop = systemWindowInsets.top > 0;
                    } else {
                        int newTopMargin = innerInsets.getSystemWindowInsetTop();
                        int newLeftMargin = innerInsets.getSystemWindowInsetLeft();
                        int newRightMargin = innerInsets.getSystemWindowInsetRight();

                    // Must use root window insets for the guard, because the color views consume
                    // the navigation bar inset if the window does not request LAYOUT_HIDE_NAV - but
                    // the status guard is attached at the root.
                        // Must use root window insets for the guard, because the color views
                        // consume the navigation bar inset if the window does not request
                        // LAYOUT_HIDE_NAV - but the status guard is attached at the root.
                        WindowInsets rootInsets = getRootWindowInsets();
                        int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft();
                        int newGuardRightMargin = rootInsets.getSystemWindowInsetRight();
@@ -1657,6 +1690,8 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
                            // If it wasn't previously shown, the color may be stale
                            updateStatusGuardColor();
                        }
                        consumesSystemWindowInsetsTop = showStatusGuard;
                    }

                    // We only need to consume the insets if the action
                    // mode is overlaid on the app content (e.g. it's
@@ -1664,22 +1699,24 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
                    // screen_simple_overlay_action_mode.xml).
                    final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
                            & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
                    if (nonOverlay && showStatusGuard) {
                    if (nonOverlay && consumesSystemWindowInsetsTop) {
                        insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0);
                    }
                } else {
                    if (!Flags.actionModeEdgeToEdge()) {
                        // reset top margin
                        if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) {
                            mlpChanged = true;
                            mlp.topMargin = 0;
                        }
                    }
                }
                if (mlpChanged) {
                    mPrimaryActionModeView.setLayoutParams(mlp);
                }
            }
        }
        if (mStatusGuard != null) {
        if (!Flags.actionModeEdgeToEdge() && mStatusGuard != null) {
            mStatusGuard.setVisibility(showStatusGuard ? VISIBLE : GONE);
        }
        return insets;
@@ -1987,6 +2024,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
                }

                mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
                initializeActionModeViewInternalPadding();
                mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
                        R.attr.actionModePopupWindowStyle);
                mPrimaryActionModePopup.setWindowLayoutType(
@@ -2033,6 +2071,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
                ViewStub stub = findViewById(R.id.action_mode_bar_stub);
                if (stub != null) {
                    mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
                    initializeActionModeViewInternalPadding();
                    mPrimaryActionModePopup = null;
                }
            }
@@ -2047,6 +2086,13 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
        return null;
    }

    private void initializeActionModeViewInternalPadding() {
        mActionModeViewInternalPaddingLeft = mPrimaryActionModeView.getPaddingLeft();
        mActionModeViewInternalPaddingTop = mPrimaryActionModeView.getPaddingTop();
        mActionModeViewInternalPaddingRight = mPrimaryActionModeView.getPaddingRight();
        mActionModeViewInternalPaddingBottom = mPrimaryActionModeView.getPaddingBottom();
    }

    private void endOnGoingFadeAnimation() {
        if (mFadeAnim != null) {
            mFadeAnim.end();
@@ -2183,7 +2229,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind
        for (int i = getChildCount() - 1; i >= 0; i--) {
            View v = getChildAt(i);
            if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
                    && v != mStatusGuard) {
                    && (Flags.actionModeEdgeToEdge() || v != mStatusGuard)) {
                removeViewAt(i);
            }
        }
+28 −6
Original line number Diff line number Diff line
@@ -15,8 +15,10 @@
 */
package com.android.internal.widget;

import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build;
@@ -34,6 +36,7 @@ import android.widget.TextView;

import com.android.internal.R;
import com.android.internal.view.menu.MenuBuilder;
import com.android.window.flags.Flags;

/**
 * @hide
@@ -54,6 +57,7 @@ public class ActionBarContextView extends AbsActionBarView {
    private Drawable mSplitBackground;
    private boolean mTitleOptional;
    private int mCloseItemLayout;
    private final int mInternalVerticalPadding;

    public ActionBarContextView(Context context) {
        this(context, null);
@@ -92,6 +96,8 @@ public class ActionBarContextView extends AbsActionBarView {
                R.layout.action_mode_close_item);

        a.recycle();

        mInternalVerticalPadding = getPaddingTop() + getPaddingBottom();
    }

    @Override
@@ -103,6 +109,19 @@ public class ActionBarContextView extends AbsActionBarView {
        }
    }

    @SuppressLint("CustomViewStyleable")
    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        // Action mode can change size on configuration changes.
        // Reread the desired height from the theme-specified style.
        TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.ActionMode,
                R.attr.actionModeStyle, 0);
        setContentHeight(a.getLayoutDimension(R.styleable.ActionMode_height, 0));
        a.recycle();
    }

    @Override
    public void setSplitToolbar(boolean split) {
        if (mSplitActionBar != split) {
@@ -314,12 +333,15 @@ public class ActionBarContextView extends AbsActionBarView {
        }

        final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);

        int maxHeight = mContentHeight > 0 ?
                mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
        int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();

        final int verticalPadding = getPaddingTop() + getPaddingBottom();
        int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
        final int externalVerticalPadding = Math.max(0, verticalPadding - mInternalVerticalPadding);
        final int maxHeight = mContentHeight > 0
                ? Flags.actionModeEdgeToEdge()
                        ? mContentHeight + externalVerticalPadding
                        : mContentHeight
                : MeasureSpec.getSize(heightMeasureSpec);
        final int height = maxHeight - verticalPadding;
        final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);

+39 −46
Original line number Diff line number Diff line
@@ -294,54 +294,24 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar
        }
    }

    private boolean applyInsets(View view, Rect insets, boolean toPadding,
            boolean left, boolean top, boolean right, boolean bottom) {
        boolean changed;
        if (toPadding) {
            changed = setMargin(view, EMPTY_RECT, left, top, right, bottom);
            changed |= setPadding(view, insets, left, top, right, bottom);
        } else {
            changed = setPadding(view, EMPTY_RECT, left, top, right, bottom);
            changed |= setMargin(view, insets, left, top, right, bottom);
        }
        return changed;
    }

    private boolean setPadding(View view, Rect insets,
            boolean left, boolean top, boolean right, boolean bottom) {
        if ((left && view.getPaddingLeft() != insets.left)
                || (top && view.getPaddingTop() != insets.top)
                || (right && view.getPaddingRight() != insets.right)
                || (bottom && view.getPaddingBottom() != insets.bottom)) {
            view.setPadding(
                    left ? insets.left : view.getPaddingLeft(),
                    top ? insets.top : view.getPaddingTop(),
                    right ? insets.right : view.getPaddingRight(),
                    bottom ? insets.bottom : view.getPaddingBottom());
            return true;
        }
        return false;
    }

    private boolean setMargin(View view,  Rect insets,
            boolean left, boolean top, boolean right, boolean bottom) {
    private boolean setMargin(View view, int left, int top, int right, int bottom) {
        final LayoutParams lp = (LayoutParams) view.getLayoutParams();
        boolean changed = false;
        if (left && lp.leftMargin != insets.left) {
        if (lp.leftMargin != left) {
            changed = true;
            lp.leftMargin = insets.left;
            lp.leftMargin = left;
        }
        if (top && lp.topMargin != insets.top) {
        if (lp.topMargin != top) {
            changed = true;
            lp.topMargin = insets.top;
            lp.topMargin = top;
        }
        if (right && lp.rightMargin != insets.right) {
        if (lp.rightMargin != right) {
            changed = true;
            lp.rightMargin = insets.right;
            lp.rightMargin = right;
        }
        if (bottom && lp.bottomMargin != insets.bottom) {
        if (lp.bottomMargin != bottom) {
            changed = true;
            lp.bottomMargin = insets.bottom;
            lp.bottomMargin = bottom;
        }
        return changed;
    }
@@ -367,12 +337,30 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar
        final Insets sysInsets = insets.getSystemWindowInsets();
        mSystemInsets.set(sysInsets.left, sysInsets.top, sysInsets.right, sysInsets.bottom);

        // The top and bottom action bars are always within the content area.
        boolean changed = applyInsets(mActionBarTop, mSystemInsets,
                mActionBarExtendsIntoSystemInsets, true, true, true, false);
        boolean changed = false;
        if (mActionBarExtendsIntoSystemInsets) {
            // Don't extend into navigation bar area so the width can align with status bar
            // color view.
            final Insets navBarInsets = insets.getInsets(WindowInsets.Type.navigationBars());
            final int paddingLeft = sysInsets.left - navBarInsets.left;
            final int paddingRight = sysInsets.right - navBarInsets.right;
            mActionBarTop.setPadding(paddingLeft, sysInsets.top, paddingRight, 0);
            changed |= setMargin(
                    mActionBarTop, navBarInsets.left, 0, navBarInsets.right, 0);
            if (mActionBarBottom != null) {
            changed |= applyInsets(mActionBarBottom, mSystemInsets,
                    mActionBarExtendsIntoSystemInsets, true, false, true, true);
                mActionBarBottom.setPadding(paddingLeft, 0, paddingRight, sysInsets.bottom);
                changed |= setMargin(
                        mActionBarBottom, navBarInsets.left, 0, navBarInsets.right, 0);
            }
        } else {
            mActionBarTop.setPadding(0, 0, 0, 0);
            changed |= setMargin(
                    mActionBarTop, sysInsets.left, sysInsets.top, sysInsets.right, 0);
            if (mActionBarBottom != null) {
                mActionBarBottom.setPadding(0, 0, 0, 0);
                changed |= setMargin(
                        mActionBarBottom, sysInsets.left, 0, sysInsets.right, sysInsets.bottom);
            }
        }

        // Cannot use the result of computeSystemWindowInsets, because that consumes the
@@ -521,7 +509,12 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar
                );
            }
        }
        setMargin(mContent, mContentInsets, true, true, true, true);
        setMargin(
                mContent,
                mContentInsets.left,
                mContentInsets.top,
                mContentInsets.right,
                mContentInsets.bottom);

        if (!mLastInnerInsets.equals(mInnerInsets)) {
            // If the inner insets have changed, we need to dispatch this down to
+10 −0
Original line number Diff line number Diff line
@@ -1419,6 +1419,16 @@
                       android:resource="@xml/accessibility_shortcut_test_activity"/>
        </activity>

        <activity android:name="com.android.internal.widget.ActionBarContextViewActivity"
                  android:label="ActionBarContextViewActivity"
                  android:exported="true"
                  android:theme="@style/ActionBarContextViewTheme">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
            </intent-filter>
        </activity>

        <!-- Activity-level metadata -->
        <meta-data android:name="com.android.frameworks.coretests.isApp" android:value="true" />
        <meta-data android:name="com.android.frameworks.coretests.string" android:value="foo" />
+10 −0
Original line number Diff line number Diff line
@@ -84,5 +84,15 @@
        <item name="android:fontFeatureSettings">\"smcp\"</item>
        <item name="android:fontVariationSettings">\'wdth\' 150</item>
    </style>
    <style name="ActionBarContextViewTheme" parent="android:Theme.NoTitleBar">
        <item name="android:actionModeStyle">@style/ActionBarContextViewActionModeTheme</item>
    </style>
    <style name="ActionBarContextViewActionModeTheme">
        <item name="android:paddingLeft">10px</item>
        <item name="android:paddingTop">20px</item>
        <item name="android:paddingRight">30px</item>
        <item name="android:paddingBottom">40px</item>
        <item name="android:height">200px</item>
    </style>

</resources>
Loading