Loading core/java/android/window/TransitionInfo.java +35 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 = { Loading @@ -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 {} Loading Loading @@ -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; Loading Loading @@ -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"); } Loading Loading @@ -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; Loading @@ -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. */ Loading Loading @@ -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() { Loading Loading @@ -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) { Loading @@ -487,6 +519,7 @@ public final class TransitionInfo implements Parcelable { dest.writeTypedObject(mTaskInfo, flags); dest.writeInt(mStartRotation); dest.writeInt(mEndRotation); dest.writeInt(mRotationAnimation); } @NonNull Loading Loading @@ -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 + "}"; } } Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java +38 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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; } Loading @@ -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); } /** Loading Loading @@ -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); Loading @@ -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); } Loading Loading @@ -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; Loading libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +126 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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, Loading Loading @@ -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. Loading libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +5 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +159 −14 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/window/TransitionInfo.java +35 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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 = { Loading @@ -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 {} Loading Loading @@ -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; Loading Loading @@ -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"); } Loading Loading @@ -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; Loading @@ -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. */ Loading Loading @@ -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() { Loading Loading @@ -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) { Loading @@ -487,6 +519,7 @@ public final class TransitionInfo implements Parcelable { dest.writeTypedObject(mTaskInfo, flags); dest.writeInt(mStartRotation); dest.writeInt(mEndRotation); dest.writeInt(mRotationAnimation); } @NonNull Loading Loading @@ -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 + "}"; } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java +38 −1 Original line number Diff line number Diff line Loading @@ -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) { Loading @@ -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; } Loading @@ -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); } /** Loading Loading @@ -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); Loading @@ -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); } Loading Loading @@ -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; Loading
libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +126 −6 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading @@ -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, Loading Loading @@ -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. Loading
libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +5 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +159 −14 File changed.Preview size limit exceeded, changes collapsed. Show changes