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

Commit e5e52341 authored by Evan Rosky's avatar Evan Rosky Committed by Automerger Merge Worker
Browse files

Merge "Differentiate seamless rotations in shell transitions" into sc-v2-dev am: 9751bc2f

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15388785

Change-Id: Idea3cb030118f0346113cea0dd8cf22d2ac19ef6
parents 089a67d0 9751bc2f
Loading
Loading
Loading
Loading
+35 −2
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_NONE;
@@ -94,8 +95,16 @@ public final class TransitionInfo implements Parcelable {
    /** The container can show on top of lock screen. */
    public static final int FLAG_OCCLUDES_KEYGUARD = 1 << 6;

    /**
     * Only for IS_DISPLAY containers. Is set if the display has system alert windows. This is
     * used to prevent seamless rotation.
     * TODO(b/194540864): Once we can include all windows in transition, then replace this with
     *         something like FLAG_IS_SYSTEM_ALERT instead. Then we can do mixed rotations.
     */
    public static final int FLAG_DISPLAY_HAS_ALERT_WINDOWS = 1 << 7;

    /** The first unused bit. This can be used by remotes to attach custom flags to this change. */
    public static final int FLAG_FIRST_CUSTOM = 1 << 7;
    public static final int FLAG_FIRST_CUSTOM = 1 << 8;

    /** @hide */
    @IntDef(prefix = { "FLAG_" }, value = {
@@ -107,6 +116,7 @@ public final class TransitionInfo implements Parcelable {
            FLAG_IS_VOICE_INTERACTION,
            FLAG_IS_DISPLAY,
            FLAG_OCCLUDES_KEYGUARD,
            FLAG_DISPLAY_HAS_ALERT_WINDOWS,
            FLAG_FIRST_CUSTOM
    })
    public @interface ChangeFlags {}
@@ -209,6 +219,10 @@ public final class TransitionInfo implements Parcelable {
        return mOptions;
    }

    /**
     * @return the list of {@link Change}s in this transition. The list is sorted top-to-bottom
     *         in Z (meaning index 0 is the top-most container).
     */
    @NonNull
    public List<Change> getChanges() {
        return mChanges;
@@ -290,6 +304,9 @@ public final class TransitionInfo implements Parcelable {
        if ((flags & FLAG_OCCLUDES_KEYGUARD) != 0) {
            sb.append((sb.length() == 0 ? "" : "|") + "OCCLUDES_KEYGUARD");
        }
        if ((flags & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) {
            sb.append((sb.length() == 0 ? "" : "|") + "DISPLAY_HAS_ALERT_WINDOWS");
        }
        if ((flags & FLAG_FIRST_CUSTOM) != 0) {
            sb.append((sb.length() == 0 ? "" : "|") + "FIRST_CUSTOM");
        }
@@ -337,6 +354,7 @@ public final class TransitionInfo implements Parcelable {
        private ActivityManager.RunningTaskInfo mTaskInfo = null;
        private int mStartRotation = ROTATION_UNDEFINED;
        private int mEndRotation = ROTATION_UNDEFINED;
        private int mRotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;

        public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) {
            mContainer = container;
@@ -356,6 +374,7 @@ public final class TransitionInfo implements Parcelable {
            mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
            mStartRotation = in.readInt();
            mEndRotation = in.readInt();
            mRotationAnimation = in.readInt();
        }

        /** Sets the parent of this change's container. The parent must be a participant or null. */
@@ -402,6 +421,14 @@ public final class TransitionInfo implements Parcelable {
            mEndRotation = end;
        }

        /**
         * Sets the app-requested animation type for rotation. Will be one of the
         * ROTATION_ANIMATION_ values in {@link android.view.WindowManager.LayoutParams};
         */
        public void setRotationAnimation(int anim) {
            mRotationAnimation = anim;
        }

        /** @return the container that is changing. May be null if non-remotable (eg. activity) */
        @Nullable
        public WindowContainerToken getContainer() {
@@ -473,6 +500,11 @@ public final class TransitionInfo implements Parcelable {
            return mEndRotation;
        }

        /** @return the rotation animation. */
        public int getRotationAnimation() {
            return mRotationAnimation;
        }

        /** @hide */
        @Override
        public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -487,6 +519,7 @@ public final class TransitionInfo implements Parcelable {
            dest.writeTypedObject(mTaskInfo, flags);
            dest.writeInt(mStartRotation);
            dest.writeInt(mEndRotation);
            dest.writeInt(mRotationAnimation);
        }

        @NonNull
@@ -514,7 +547,7 @@ public final class TransitionInfo implements Parcelable {
            return "{" + mContainer + "(" + mParent + ") leash=" + mLeash
                    + " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb="
                    + mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + " r="
                    + mStartRotation + "->" + mEndRotation + "}";
                    + mStartRotation + "->" + mEndRotation + ":" + mRotationAnimation + "}";
        }
    }

+38 −1
Original line number Diff line number Diff line
@@ -82,6 +82,9 @@ public class DisplayLayout {
    private boolean mHasNavigationBar = false;
    private boolean mHasStatusBar = false;
    private int mNavBarFrameHeight = 0;
    private boolean mAllowSeamlessRotationDespiteNavBarMoving = false;
    private boolean mNavigationBarCanMove = false;
    private boolean mReverseDefaultRotation = false;

    @Override
    public boolean equals(Object o) {
@@ -98,6 +101,10 @@ public class DisplayLayout {
                && Objects.equals(mStableInsets, other.mStableInsets)
                && mHasNavigationBar == other.mHasNavigationBar
                && mHasStatusBar == other.mHasStatusBar
                && mAllowSeamlessRotationDespiteNavBarMoving
                        == other.mAllowSeamlessRotationDespiteNavBarMoving
                && mNavigationBarCanMove == other.mNavigationBarCanMove
                && mReverseDefaultRotation == other.mReverseDefaultRotation
                && mNavBarFrameHeight == other.mNavBarFrameHeight;
    }

@@ -105,7 +112,8 @@ public class DisplayLayout {
    public int hashCode() {
        return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi,
                mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar,
                mNavBarFrameHeight);
                mNavBarFrameHeight, mAllowSeamlessRotationDespiteNavBarMoving,
                mNavigationBarCanMove, mReverseDefaultRotation);
    }

    /**
@@ -150,6 +158,9 @@ public class DisplayLayout {
        mDensityDpi = dl.mDensityDpi;
        mHasNavigationBar = dl.mHasNavigationBar;
        mHasStatusBar = dl.mHasStatusBar;
        mAllowSeamlessRotationDespiteNavBarMoving = dl.mAllowSeamlessRotationDespiteNavBarMoving;
        mNavigationBarCanMove = dl.mNavigationBarCanMove;
        mReverseDefaultRotation = dl.mReverseDefaultRotation;
        mNavBarFrameHeight = dl.mNavBarFrameHeight;
        mNonDecorInsets.set(dl.mNonDecorInsets);
        mStableInsets.set(dl.mStableInsets);
@@ -165,6 +176,10 @@ public class DisplayLayout {
        mDensityDpi = info.logicalDensityDpi;
        mHasNavigationBar = hasNavigationBar;
        mHasStatusBar = hasStatusBar;
        mAllowSeamlessRotationDespiteNavBarMoving = res.getBoolean(
            R.bool.config_allowSeamlessRotationDespiteNavBarMoving);
        mNavigationBarCanMove = res.getBoolean(R.bool.config_navBarCanMove);
        mReverseDefaultRotation = res.getBoolean(R.bool.config_reverseDefaultRotation);
        recalcInsets(res);
    }

@@ -249,6 +264,28 @@ public class DisplayLayout {
        return mNavBarFrameHeight;
    }

    /** @return whether we can seamlessly rotate even if nav-bar can change sides. */
    public boolean allowSeamlessRotationDespiteNavBarMoving() {
        return mAllowSeamlessRotationDespiteNavBarMoving;
    }

    /** @return whether the navigation bar will change sides during rotation. */
    public boolean navigationBarCanMove() {
        return mNavigationBarCanMove;
    }

    /** @return the rotation that would make the physical display "upside down". */
    public int getUpsideDownRotation() {
        boolean displayHardwareIsLandscape = mWidth > mHeight;
        if ((mRotation % 2) != 0) {
            displayHardwareIsLandscape = !displayHardwareIsLandscape;
        }
        if (displayHardwareIsLandscape) {
            return mReverseDefaultRotation ? Surface.ROTATION_270 : Surface.ROTATION_90;
        }
        return Surface.ROTATION_180;
    }

    /** Gets the orientation of this layout */
    public int getOrientation() {
        return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
+126 −6
Original line number Diff line number Diff line
@@ -23,6 +23,10 @@ import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS;
import static android.app.ActivityOptions.ANIM_SCALE_UP;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
@@ -31,8 +35,10 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
@@ -68,9 +74,12 @@ import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.AttributeCache;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -97,6 +106,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
            SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);

    private final TransactionPool mTransactionPool;
    private final DisplayController mDisplayController;
    private final Context mContext;
    private final ShellExecutor mMainExecutor;
    private final ShellExecutor mAnimExecutor;
@@ -114,8 +124,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {

    private ScreenRotationAnimation mRotationAnimation;

    DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context,
    DefaultTransitionHandler(@NonNull DisplayController displayController,
            @NonNull TransactionPool transactionPool, Context context,
            @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
        mDisplayController = displayController;
        mTransactionPool = transactionPool;
        mContext = context;
        mMainExecutor = mainExecutor;
@@ -126,6 +138,110 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
        AttributeCache.init(context);
    }

    @VisibleForTesting
    static boolean isRotationSeamless(@NonNull TransitionInfo info,
            DisplayController displayController) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                "Display is rotating, check if it should be seamless.");
        boolean checkedDisplayLayout = false;
        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
            final TransitionInfo.Change change = info.getChanges().get(i);

            // Only look at changing things. showing/hiding don't need to rotate.
            if (change.getMode() != TRANSIT_CHANGE) continue;

            // This container isn't rotating, so we can ignore it.
            if (change.getEndRotation() == change.getStartRotation()) continue;

            if ((change.getFlags() & FLAG_IS_DISPLAY) != 0) {
                // In the presence of System Alert windows we can not seamlessly rotate.
                if ((change.getFlags() & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) {
                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                            "  display has system alert windows, so not seamless.");
                    return false;
                }
            } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
                if (change.getRotationAnimation() != ROTATION_ANIMATION_SEAMLESS) {
                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                            "  wallpaper is participating but isn't seamless.");
                    return false;
                }
            } else if (change.getTaskInfo() != null) {
                // We only enable seamless rotation if all the visible task windows requested it.
                if (change.getRotationAnimation() != ROTATION_ANIMATION_SEAMLESS) {
                    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                            "  task %s isn't requesting seamless, so not seamless.",
                            change.getTaskInfo().taskId);
                    return false;
                }

                // This is the only way to get display-id currently, so we will check display
                // capabilities here
                if (!checkedDisplayLayout) {
                    // only need to check display once.
                    checkedDisplayLayout = true;
                    final DisplayLayout displayLayout = displayController.getDisplayLayout(
                            change.getTaskInfo().displayId);
                    // For the upside down rotation we don't rotate seamlessly as the navigation
                    // bar moves position. Note most apps (using orientation:sensor or user as
                    // opposed to fullSensor) will not enter the reverse portrait orientation, so
                    // actually the orientation won't change at all.
                    int upsideDownRotation = displayLayout.getUpsideDownRotation();
                    if (change.getStartRotation() == upsideDownRotation
                            || change.getEndRotation() == upsideDownRotation) {
                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                                "  rotation involves upside-down portrait, so not seamless.");
                        return false;
                    }

                    // If the navigation bar can't change sides, then it will jump when we change
                    // orientations and we don't rotate seamlessly - unless that is allowed, eg.
                    // with gesture navigation where the navbar is low-profile enough that this
                    // isn't very noticeable.
                    if (!displayLayout.allowSeamlessRotationDespiteNavBarMoving()
                            && (!(displayLayout.navigationBarCanMove()
                                    && (change.getStartAbsBounds().width()
                                            != change.getStartAbsBounds().height())))) {
                        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
                                "  nav bar changes sides, so not seamless.");
                        return false;
                    }
                }
            }
        }

        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "  Rotation IS seamless.");
        return true;
    }

    /**
     * Gets the rotation animation for the topmost task. Assumes that seamless is checked
     * elsewhere, so it will default SEAMLESS to ROTATE.
     */
    private int getRotationAnimation(@NonNull TransitionInfo info) {
        // Traverse in top-to-bottom order so that the first task is top-most
        for (int i = 0; i < info.getChanges().size(); ++i) {
            final TransitionInfo.Change change = info.getChanges().get(i);

            // Only look at changing things. showing/hiding don't need to rotate.
            if (change.getMode() != TRANSIT_CHANGE) continue;

            // This container isn't rotating, so we can ignore it.
            if (change.getEndRotation() == change.getStartRotation()) continue;

            if (change.getTaskInfo() != null) {
                final int anim = change.getRotationAnimation();
                if (anim == ROTATION_ANIMATION_UNSPECIFIED
                        // Fallback animation for seamless should also be default.
                        || anim == ROTATION_ANIMATION_SEAMLESS) {
                    return ROTATION_ANIMATION_ROTATE;
                }
                return anim;
            }
        }
        return ROTATION_ANIMATION_ROTATE;
    }

    @Override
    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction startTransaction,
@@ -168,12 +284,16 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
            if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE
                    && (change.getEndRotation() != change.getStartRotation())
                    && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
                boolean isSeamless = isRotationSeamless(info, mDisplayController);
                final int anim = getRotationAnimation(info);
                if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
                    mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
                            mTransactionPool, startTransaction, change, info.getRootLeash());
                    mRotationAnimation.startAnimation(animations, onAnimFinish,
                            mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
                    continue;
                }
            }

            if (change.getMode() == TRANSIT_CHANGE) {
                // No default animation for this, so just update bounds/position.
+5 −3
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TransactionPool;
@@ -113,15 +114,16 @@ public class Transitions implements RemoteCallable<Transitions> {
    private final ArrayList<ActiveTransition> mActiveTransitions = new ArrayList<>();

    public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
            @NonNull Context context, @NonNull ShellExecutor mainExecutor,
            @NonNull ShellExecutor animExecutor) {
            @NonNull DisplayController displayController, @NonNull Context context,
            @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
        mOrganizer = organizer;
        mContext = context;
        mMainExecutor = mainExecutor;
        mAnimExecutor = animExecutor;
        mPlayerImpl = new TransitionPlayerImpl();
        // The very last handler (0 in the list) should be the default one.
        mHandlers.add(new DefaultTransitionHandler(pool, context, mainExecutor, animExecutor));
        mHandlers.add(new DefaultTransitionHandler(displayController, pool, context, mainExecutor,
                animExecutor));
        // Next lowest priority is remote transitions.
        mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor);
        mHandlers.add(mRemoteTransitionHandler);
+159 −14

File changed.

Preview size limit exceeded, changes collapsed.

Loading