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

Commit 273c5220 authored by Hongwei Wang's avatar Hongwei Wang
Browse files

Use cached launcher shelf KCA before entering PiP

For entering PiP in button navigation mode, we would allow Launcher
shelf height change to update the destination bounds in the middle of
entering animation.

This works in most cases, but if the animation has progressed beyond a
certain point, such Launcher shelf height change would cause the
animation to suddenly change its moving direction, e.g. move up.

In this change, we used the cached launcher shelf height to calculate
the destination bounds, to make the animation independent from the
Launcher shelf height update timing.

Flag: EXEMPT bugfix
Video: http://recall/-/aaaaaabFQoRHlzixHdtY/fTeZwHHX3hGd6mRqSIjgp9
Bug: 326287104
Test: Enter PiP in button nav, both folded and unfolded
Test: atest --iteration 10  WMShellFlickerTestsPip1 -- --test-arg \
      com.android.tradefed.testtype.AndroidJUnitTest:instrumentation-arg\
      :filter-tests:="com.android.wm.shell.flicker.pip.\
      AutoEnterPipWithSourceRectHintTest#pipLayerMovesTowardsRightBottomCorner[ROTATION_0_3_BUTTON_NAV]"
Change-Id: I97f67824f9f153eba5bc34d090e7a12e4d634c03
parent 4a12a946
Loading
Loading
Loading
Loading
+61 −28
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.graphics.Rect;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Size;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
@@ -42,9 +43,7 @@ import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
@@ -69,26 +68,36 @@ public class PipBoundsState {
    @Retention(RetentionPolicy.SOURCE)
    public @interface StashType {}

    public static final int NAMED_KCA_LAUNCHER_SHELF = 0;
    public static final int NAMED_KCA_TABLETOP_MODE = 1;

    @IntDef(prefix = { "NAMED_KCA_" }, value = {
            NAMED_KCA_LAUNCHER_SHELF,
            NAMED_KCA_TABLETOP_MODE
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface NamedKca {}

    private static final String TAG = PipBoundsState.class.getSimpleName();

    private final @NonNull Rect mBounds = new Rect();
    private final @NonNull Rect mMovementBounds = new Rect();
    private final @NonNull Rect mNormalBounds = new Rect();
    private final @NonNull Rect mExpandedBounds = new Rect();
    private final @NonNull Rect mNormalMovementBounds = new Rect();
    private final @NonNull Rect mExpandedMovementBounds = new Rect();
    private final @NonNull PipDisplayLayoutState mPipDisplayLayoutState;
    @NonNull private final Rect mBounds = new Rect();
    @NonNull private final Rect mMovementBounds = new Rect();
    @NonNull private final Rect mNormalBounds = new Rect();
    @NonNull private final Rect mExpandedBounds = new Rect();
    @NonNull private final Rect mNormalMovementBounds = new Rect();
    @NonNull private final Rect mExpandedMovementBounds = new Rect();
    @NonNull private final PipDisplayLayoutState mPipDisplayLayoutState;
    private final Point mMaxSize = new Point();
    private final Point mMinSize = new Point();
    private final @NonNull Context mContext;
    @NonNull private final Context mContext;
    private float mAspectRatio;
    private int mStashedState = STASH_TYPE_NONE;
    private int mStashOffset;
    private @Nullable PipReentryState mPipReentryState;
    @Nullable private PipReentryState mPipReentryState;
    private final LauncherState mLauncherState = new LauncherState();
    private final @NonNull SizeSpecSource mSizeSpecSource;
    private @Nullable ComponentName mLastPipComponentName;
    private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState();
    @NonNull private final SizeSpecSource mSizeSpecSource;
    @Nullable private ComponentName mLastPipComponentName;
    @NonNull private final MotionBoundsState mMotionBoundsState = new MotionBoundsState();
    private boolean mIsImeShowing;
    private int mImeHeight;
    private boolean mIsShelfShowing;
@@ -120,12 +129,18 @@ public class PipBoundsState {
     * as unrestricted keep clear area. Values in this map would be appended to
     * {@link #getUnrestrictedKeepClearAreas()} and this is meant for internal usage only.
     */
    private final Map<String, Rect> mNamedUnrestrictedKeepClearAreas = new HashMap<>();
    private final SparseArray<Rect> mNamedUnrestrictedKeepClearAreas = new SparseArray<>();

    private @Nullable Runnable mOnMinimalSizeChangeCallback;
    private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
    private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
    private List<Consumer<Float>> mOnAspectRatioChangedCallbacks = new ArrayList<>();
    @Nullable private Runnable mOnMinimalSizeChangeCallback;
    @Nullable private TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback;
    private final List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>();
    private final List<Consumer<Float>> mOnAspectRatioChangedCallbacks = new ArrayList<>();

    /**
     * This is used to set the launcher shelf height ahead of non-auto-enter-pip animation,
     * to avoid the race condition. See also {@link #NAMED_KCA_LAUNCHER_SHELF}.
     */
    public final Rect mCachedLauncherShelfHeightKeepClearArea = new Rect();

    // the size of the current bounds relative to the max size spec
    private float mBoundsScale;
@@ -430,16 +445,31 @@ public class PipBoundsState {
        mUnrestrictedKeepClearAreas.addAll(unrestrictedAreas);
    }

    /** Add a named unrestricted keep clear area. */
    public void addNamedUnrestrictedKeepClearArea(@NonNull String name, Rect unrestrictedArea) {
        mNamedUnrestrictedKeepClearAreas.put(name, unrestrictedArea);
    /** Set a named unrestricted keep clear area. */
    public void setNamedUnrestrictedKeepClearArea(
            @NamedKca int tag, @Nullable Rect unrestrictedArea) {
        if (unrestrictedArea == null) {
            mNamedUnrestrictedKeepClearAreas.remove(tag);
        } else {
            mNamedUnrestrictedKeepClearAreas.put(tag, unrestrictedArea);
            if (tag == NAMED_KCA_LAUNCHER_SHELF) {
                mCachedLauncherShelfHeightKeepClearArea.set(unrestrictedArea);
            }
        }

    /** Remove a named unrestricted keep clear area. */
    public void removeNamedUnrestrictedKeepClearArea(@NonNull String name) {
        mNamedUnrestrictedKeepClearAreas.remove(name);
    }

    /**
     * Forcefully set the keep-clear-area for launcher shelf height if applicable.
     * This is used for entering PiP in button navigation mode to make sure the destination bounds
     * calculation includes the shelf height, to avoid race conditions that such callback is sent
     * from Launcher after the entering animation is started.
     */
    public void mayUseCachedLauncherShelfHeight() {
        if (!mCachedLauncherShelfHeightKeepClearArea.isEmpty()) {
            setNamedUnrestrictedKeepClearArea(
                    NAMED_KCA_LAUNCHER_SHELF, mCachedLauncherShelfHeightKeepClearArea);
        }
    }

    /**
     * @return restricted keep clear areas.
@@ -454,9 +484,12 @@ public class PipBoundsState {
     */
    @NonNull
    public Set<Rect> getUnrestrictedKeepClearAreas() {
        if (mNamedUnrestrictedKeepClearAreas.isEmpty()) return mUnrestrictedKeepClearAreas;
        if (mNamedUnrestrictedKeepClearAreas.size() == 0) return mUnrestrictedKeepClearAreas;
        final Set<Rect> unrestrictedAreas = new ArraySet<>(mUnrestrictedKeepClearAreas);
        unrestrictedAreas.addAll(mNamedUnrestrictedKeepClearAreas.values());
        for (int i = 0; i < mNamedUnrestrictedKeepClearAreas.size(); i++) {
            final int key = mNamedUnrestrictedKeepClearAreas.keyAt(i);
            unrestrictedAreas.add(mNamedUnrestrictedKeepClearAreas.get(key));
        }
        return unrestrictedAreas;
    }

+3 −0
Original line number Diff line number Diff line
@@ -1020,6 +1020,9 @@ public class PipTransition extends PipTransitionController {
            mPipMenuController.attach(leash);
        }

        // Make sure we have the launcher shelf into destination bounds calculation
        // before the animator starts.
        mPipBoundsState.mayUseCachedLauncherShelfHeight();
        final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
        final Rect currentBounds = pipChange.getStartAbsBounds();

+16 −12
Original line number Diff line number Diff line
@@ -645,9 +645,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb
                });

        mTabletopModeController.registerOnTabletopModeChangedListener((isInTabletopMode) -> {
            final String tag = "tabletop-mode";
            if (!isInTabletopMode) {
                mPipBoundsState.removeNamedUnrestrictedKeepClearArea(tag);
                mPipBoundsState.setNamedUnrestrictedKeepClearArea(
                        PipBoundsState.NAMED_KCA_TABLETOP_MODE, null);
                return;
            }

@@ -656,12 +656,14 @@ public class PipController implements PipTransitionController.PipTransitionCallb
            if (mTabletopModeController.getPreferredHalfInTabletopMode()
                    == TabletopModeController.PREFERRED_TABLETOP_HALF_TOP) {
                // Prefer top, avoid the bottom half of the display.
                mPipBoundsState.addNamedUnrestrictedKeepClearArea(tag, new Rect(
                mPipBoundsState.setNamedUnrestrictedKeepClearArea(
                        PipBoundsState.NAMED_KCA_TABLETOP_MODE, new Rect(
                                displayBounds.left, displayBounds.centerY(),
                                displayBounds.right, displayBounds.bottom));
            } else {
                // Prefer bottom, avoid the top half of the display.
                mPipBoundsState.addNamedUnrestrictedKeepClearArea(tag, new Rect(
                mPipBoundsState.setNamedUnrestrictedKeepClearArea(
                        PipBoundsState.NAMED_KCA_TABLETOP_MODE, new Rect(
                                displayBounds.left, displayBounds.top,
                                displayBounds.right, displayBounds.centerY()));
            }
@@ -915,10 +917,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb
                    0, mPipBoundsState.getDisplayBounds().bottom - height,
                    mPipBoundsState.getDisplayBounds().right,
                    mPipBoundsState.getDisplayBounds().bottom);
            mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG, rect);
            mPipBoundsState.setNamedUnrestrictedKeepClearArea(
                    PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, rect);
            updatePipPositionForKeepClearAreas();
        } else {
            mPipBoundsState.removeNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG);
            mPipBoundsState.setNamedUnrestrictedKeepClearArea(
                    PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, null);
            // postpone moving in response to hide of Launcher in case there's another change
            mMainExecutor.removeCallbacks(mMovePipInResponseToKeepClearAreasChangeCallback);
            mMainExecutor.executeDelayed(
@@ -967,8 +971,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb
            int launcherRotation, Rect hotseatKeepClearArea) {
        // preemptively add the keep clear area for Hotseat, so that it is taken into account
        // when calculating the entry destination bounds of PiP window
        mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG,
                hotseatKeepClearArea);
        mPipBoundsState.setNamedUnrestrictedKeepClearArea(
                PipBoundsState.NAMED_KCA_LAUNCHER_SHELF, hotseatKeepClearArea);
        onDisplayRotationChangedNotInPip(mContext, launcherRotation);
        // cache current min/max size
        Point minSize = mPipBoundsState.getMinSize();