Merge "Differentiate seamless rotations in shell transitions" into sc-v2-dev
This commit is contained in:
@@ -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_DOWN;
|
||||||
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
|
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
|
||||||
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
|
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_CHANGE;
|
||||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||||
import static android.view.WindowManager.TRANSIT_NONE;
|
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. */
|
/** The container can show on top of lock screen. */
|
||||||
public static final int FLAG_OCCLUDES_KEYGUARD = 1 << 6;
|
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. */
|
/** 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 */
|
/** @hide */
|
||||||
@IntDef(prefix = { "FLAG_" }, value = {
|
@IntDef(prefix = { "FLAG_" }, value = {
|
||||||
@@ -107,6 +116,7 @@ public final class TransitionInfo implements Parcelable {
|
|||||||
FLAG_IS_VOICE_INTERACTION,
|
FLAG_IS_VOICE_INTERACTION,
|
||||||
FLAG_IS_DISPLAY,
|
FLAG_IS_DISPLAY,
|
||||||
FLAG_OCCLUDES_KEYGUARD,
|
FLAG_OCCLUDES_KEYGUARD,
|
||||||
|
FLAG_DISPLAY_HAS_ALERT_WINDOWS,
|
||||||
FLAG_FIRST_CUSTOM
|
FLAG_FIRST_CUSTOM
|
||||||
})
|
})
|
||||||
public @interface ChangeFlags {}
|
public @interface ChangeFlags {}
|
||||||
@@ -209,6 +219,10 @@ public final class TransitionInfo implements Parcelable {
|
|||||||
return mOptions;
|
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
|
@NonNull
|
||||||
public List<Change> getChanges() {
|
public List<Change> getChanges() {
|
||||||
return mChanges;
|
return mChanges;
|
||||||
@@ -290,6 +304,9 @@ public final class TransitionInfo implements Parcelable {
|
|||||||
if ((flags & FLAG_OCCLUDES_KEYGUARD) != 0) {
|
if ((flags & FLAG_OCCLUDES_KEYGUARD) != 0) {
|
||||||
sb.append((sb.length() == 0 ? "" : "|") + "OCCLUDES_KEYGUARD");
|
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) {
|
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
|
||||||
sb.append((sb.length() == 0 ? "" : "|") + "FIRST_CUSTOM");
|
sb.append((sb.length() == 0 ? "" : "|") + "FIRST_CUSTOM");
|
||||||
}
|
}
|
||||||
@@ -337,6 +354,7 @@ public final class TransitionInfo implements Parcelable {
|
|||||||
private ActivityManager.RunningTaskInfo mTaskInfo = null;
|
private ActivityManager.RunningTaskInfo mTaskInfo = null;
|
||||||
private int mStartRotation = ROTATION_UNDEFINED;
|
private int mStartRotation = ROTATION_UNDEFINED;
|
||||||
private int mEndRotation = ROTATION_UNDEFINED;
|
private int mEndRotation = ROTATION_UNDEFINED;
|
||||||
|
private int mRotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
|
||||||
|
|
||||||
public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) {
|
public Change(@Nullable WindowContainerToken container, @NonNull SurfaceControl leash) {
|
||||||
mContainer = container;
|
mContainer = container;
|
||||||
@@ -356,6 +374,7 @@ public final class TransitionInfo implements Parcelable {
|
|||||||
mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
|
mTaskInfo = in.readTypedObject(ActivityManager.RunningTaskInfo.CREATOR);
|
||||||
mStartRotation = in.readInt();
|
mStartRotation = in.readInt();
|
||||||
mEndRotation = in.readInt();
|
mEndRotation = in.readInt();
|
||||||
|
mRotationAnimation = in.readInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the parent of this change's container. The parent must be a participant or null. */
|
/** 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;
|
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) */
|
/** @return the container that is changing. May be null if non-remotable (eg. activity) */
|
||||||
@Nullable
|
@Nullable
|
||||||
public WindowContainerToken getContainer() {
|
public WindowContainerToken getContainer() {
|
||||||
@@ -473,6 +500,11 @@ public final class TransitionInfo implements Parcelable {
|
|||||||
return mEndRotation;
|
return mEndRotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return the rotation animation. */
|
||||||
|
public int getRotationAnimation() {
|
||||||
|
return mRotationAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
@Override
|
@Override
|
||||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||||
@@ -487,6 +519,7 @@ public final class TransitionInfo implements Parcelable {
|
|||||||
dest.writeTypedObject(mTaskInfo, flags);
|
dest.writeTypedObject(mTaskInfo, flags);
|
||||||
dest.writeInt(mStartRotation);
|
dest.writeInt(mStartRotation);
|
||||||
dest.writeInt(mEndRotation);
|
dest.writeInt(mEndRotation);
|
||||||
|
dest.writeInt(mRotationAnimation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -514,7 +547,7 @@ public final class TransitionInfo implements Parcelable {
|
|||||||
return "{" + mContainer + "(" + mParent + ") leash=" + mLeash
|
return "{" + mContainer + "(" + mParent + ") leash=" + mLeash
|
||||||
+ " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb="
|
+ " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb="
|
||||||
+ mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + " r="
|
+ mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + " r="
|
||||||
+ mStartRotation + "->" + mEndRotation + "}";
|
+ mStartRotation + "->" + mEndRotation + ":" + mRotationAnimation + "}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,9 @@ public class DisplayLayout {
|
|||||||
private boolean mHasNavigationBar = false;
|
private boolean mHasNavigationBar = false;
|
||||||
private boolean mHasStatusBar = false;
|
private boolean mHasStatusBar = false;
|
||||||
private int mNavBarFrameHeight = 0;
|
private int mNavBarFrameHeight = 0;
|
||||||
|
private boolean mAllowSeamlessRotationDespiteNavBarMoving = false;
|
||||||
|
private boolean mNavigationBarCanMove = false;
|
||||||
|
private boolean mReverseDefaultRotation = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
@@ -98,6 +101,10 @@ public class DisplayLayout {
|
|||||||
&& Objects.equals(mStableInsets, other.mStableInsets)
|
&& Objects.equals(mStableInsets, other.mStableInsets)
|
||||||
&& mHasNavigationBar == other.mHasNavigationBar
|
&& mHasNavigationBar == other.mHasNavigationBar
|
||||||
&& mHasStatusBar == other.mHasStatusBar
|
&& mHasStatusBar == other.mHasStatusBar
|
||||||
|
&& mAllowSeamlessRotationDespiteNavBarMoving
|
||||||
|
== other.mAllowSeamlessRotationDespiteNavBarMoving
|
||||||
|
&& mNavigationBarCanMove == other.mNavigationBarCanMove
|
||||||
|
&& mReverseDefaultRotation == other.mReverseDefaultRotation
|
||||||
&& mNavBarFrameHeight == other.mNavBarFrameHeight;
|
&& mNavBarFrameHeight == other.mNavBarFrameHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +112,8 @@ public class DisplayLayout {
|
|||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi,
|
return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi,
|
||||||
mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar,
|
mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar,
|
||||||
mNavBarFrameHeight);
|
mNavBarFrameHeight, mAllowSeamlessRotationDespiteNavBarMoving,
|
||||||
|
mNavigationBarCanMove, mReverseDefaultRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,6 +158,9 @@ public class DisplayLayout {
|
|||||||
mDensityDpi = dl.mDensityDpi;
|
mDensityDpi = dl.mDensityDpi;
|
||||||
mHasNavigationBar = dl.mHasNavigationBar;
|
mHasNavigationBar = dl.mHasNavigationBar;
|
||||||
mHasStatusBar = dl.mHasStatusBar;
|
mHasStatusBar = dl.mHasStatusBar;
|
||||||
|
mAllowSeamlessRotationDespiteNavBarMoving = dl.mAllowSeamlessRotationDespiteNavBarMoving;
|
||||||
|
mNavigationBarCanMove = dl.mNavigationBarCanMove;
|
||||||
|
mReverseDefaultRotation = dl.mReverseDefaultRotation;
|
||||||
mNavBarFrameHeight = dl.mNavBarFrameHeight;
|
mNavBarFrameHeight = dl.mNavBarFrameHeight;
|
||||||
mNonDecorInsets.set(dl.mNonDecorInsets);
|
mNonDecorInsets.set(dl.mNonDecorInsets);
|
||||||
mStableInsets.set(dl.mStableInsets);
|
mStableInsets.set(dl.mStableInsets);
|
||||||
@@ -165,6 +176,10 @@ public class DisplayLayout {
|
|||||||
mDensityDpi = info.logicalDensityDpi;
|
mDensityDpi = info.logicalDensityDpi;
|
||||||
mHasNavigationBar = hasNavigationBar;
|
mHasNavigationBar = hasNavigationBar;
|
||||||
mHasStatusBar = hasStatusBar;
|
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);
|
recalcInsets(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +264,28 @@ public class DisplayLayout {
|
|||||||
return mNavBarFrameHeight;
|
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 */
|
/** Gets the orientation of this layout */
|
||||||
public int getOrientation() {
|
public int getOrientation() {
|
||||||
return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
|
return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
|
||||||
|
|||||||
@@ -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_SCALE_UP;
|
||||||
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
|
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN;
|
||||||
import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP;
|
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_CHANGE;
|
||||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||||
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
|
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_RELAUNCH;
|
||||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
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_DISPLAY;
|
||||||
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
|
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_SHOW_WALLPAPER;
|
||||||
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
|
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
|
||||||
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
|
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
|
||||||
@@ -68,9 +74,12 @@ import android.window.TransitionRequestInfo;
|
|||||||
import android.window.WindowContainerTransaction;
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
import com.android.internal.R;
|
import com.android.internal.R;
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.policy.AttributeCache;
|
import com.android.internal.policy.AttributeCache;
|
||||||
import com.android.internal.policy.TransitionAnimation;
|
import com.android.internal.policy.TransitionAnimation;
|
||||||
import com.android.internal.protolog.common.ProtoLog;
|
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.ShellExecutor;
|
||||||
import com.android.wm.shell.common.TransactionPool;
|
import com.android.wm.shell.common.TransactionPool;
|
||||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
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);
|
SystemProperties.getBoolean(DISABLE_CUSTOM_TASK_ANIMATION_PROPERTY, true);
|
||||||
|
|
||||||
private final TransactionPool mTransactionPool;
|
private final TransactionPool mTransactionPool;
|
||||||
|
private final DisplayController mDisplayController;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final ShellExecutor mMainExecutor;
|
private final ShellExecutor mMainExecutor;
|
||||||
private final ShellExecutor mAnimExecutor;
|
private final ShellExecutor mAnimExecutor;
|
||||||
@@ -114,8 +124,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
|||||||
|
|
||||||
private ScreenRotationAnimation mRotationAnimation;
|
private ScreenRotationAnimation mRotationAnimation;
|
||||||
|
|
||||||
DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context,
|
DefaultTransitionHandler(@NonNull DisplayController displayController,
|
||||||
|
@NonNull TransactionPool transactionPool, Context context,
|
||||||
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
|
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
|
||||||
|
mDisplayController = displayController;
|
||||||
mTransactionPool = transactionPool;
|
mTransactionPool = transactionPool;
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mMainExecutor = mainExecutor;
|
mMainExecutor = mainExecutor;
|
||||||
@@ -126,6 +138,110 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
|||||||
AttributeCache.init(context);
|
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
|
@Override
|
||||||
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||||
@NonNull SurfaceControl.Transaction startTransaction,
|
@NonNull SurfaceControl.Transaction startTransaction,
|
||||||
@@ -168,11 +284,15 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
|||||||
if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE
|
if (info.getType() == TRANSIT_CHANGE && change.getMode() == TRANSIT_CHANGE
|
||||||
&& (change.getEndRotation() != change.getStartRotation())
|
&& (change.getEndRotation() != change.getStartRotation())
|
||||||
&& (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
|
&& (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
|
||||||
mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
|
boolean isSeamless = isRotationSeamless(info, mDisplayController);
|
||||||
mTransactionPool, startTransaction, change, info.getRootLeash());
|
final int anim = getRotationAnimation(info);
|
||||||
mRotationAnimation.startAnimation(animations, onAnimFinish,
|
if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
|
||||||
mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
|
mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
|
||||||
continue;
|
mTransactionPool, startTransaction, change, info.getRootLeash());
|
||||||
|
mRotationAnimation.startAnimation(animations, onAnimFinish,
|
||||||
|
mTransitionAnimationScaleSetting, mMainExecutor, mAnimExecutor);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (change.getMode() == TRANSIT_CHANGE) {
|
if (change.getMode() == TRANSIT_CHANGE) {
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ import com.android.internal.R;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.protolog.common.ProtoLog;
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
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.RemoteCallable;
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
import com.android.wm.shell.common.TransactionPool;
|
import com.android.wm.shell.common.TransactionPool;
|
||||||
@@ -113,15 +114,16 @@ public class Transitions implements RemoteCallable<Transitions> {
|
|||||||
private final ArrayList<ActiveTransition> mActiveTransitions = new ArrayList<>();
|
private final ArrayList<ActiveTransition> mActiveTransitions = new ArrayList<>();
|
||||||
|
|
||||||
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
|
public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
|
||||||
@NonNull Context context, @NonNull ShellExecutor mainExecutor,
|
@NonNull DisplayController displayController, @NonNull Context context,
|
||||||
@NonNull ShellExecutor animExecutor) {
|
@NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) {
|
||||||
mOrganizer = organizer;
|
mOrganizer = organizer;
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mMainExecutor = mainExecutor;
|
mMainExecutor = mainExecutor;
|
||||||
mAnimExecutor = animExecutor;
|
mAnimExecutor = animExecutor;
|
||||||
mPlayerImpl = new TransitionPlayerImpl();
|
mPlayerImpl = new TransitionPlayerImpl();
|
||||||
// The very last handler (0 in the list) should be the default one.
|
// 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.
|
// Next lowest priority is remote transitions.
|
||||||
mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor);
|
mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor);
|
||||||
mHandlers.add(mRemoteTransitionHandler);
|
mHandlers.add(mRemoteTransitionHandler);
|
||||||
|
|||||||
@@ -20,12 +20,18 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
|||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||||
|
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||||
|
import static android.view.Display.DEFAULT_DISPLAY;
|
||||||
|
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_CHANGE;
|
||||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||||
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
|
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
|
||||||
import static android.view.WindowManager.TRANSIT_OPEN;
|
import static android.view.WindowManager.TRANSIT_OPEN;
|
||||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
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_TRANSLUCENT;
|
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
|
||||||
|
|
||||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||||
@@ -49,6 +55,9 @@ import android.content.Context;
|
|||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
import android.view.IDisplayWindowListener;
|
||||||
|
import android.view.IWindowManager;
|
||||||
|
import android.view.Surface;
|
||||||
import android.view.SurfaceControl;
|
import android.view.SurfaceControl;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.window.IRemoteTransition;
|
import android.window.IRemoteTransition;
|
||||||
@@ -66,12 +75,15 @@ import androidx.test.filters.SmallTest;
|
|||||||
import androidx.test.platform.app.InstrumentationRegistry;
|
import androidx.test.platform.app.InstrumentationRegistry;
|
||||||
|
|
||||||
import com.android.wm.shell.TestShellExecutor;
|
import com.android.wm.shell.TestShellExecutor;
|
||||||
|
import com.android.wm.shell.common.DisplayController;
|
||||||
import com.android.wm.shell.common.ShellExecutor;
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
import com.android.wm.shell.common.TransactionPool;
|
import com.android.wm.shell.common.TransactionPool;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@@ -98,8 +110,7 @@ public class ShellTransitionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBasicTransitionFlow() {
|
public void testBasicTransitionFlow() {
|
||||||
Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
|
Transitions transitions = createTestTransitions();
|
||||||
mMainExecutor, mAnimExecutor);
|
|
||||||
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
||||||
|
|
||||||
IBinder transitToken = new Binder();
|
IBinder transitToken = new Binder();
|
||||||
@@ -118,8 +129,7 @@ public class ShellTransitionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNonDefaultHandler() {
|
public void testNonDefaultHandler() {
|
||||||
Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
|
Transitions transitions = createTestTransitions();
|
||||||
mMainExecutor, mAnimExecutor);
|
|
||||||
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
||||||
|
|
||||||
final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
|
final WindowContainerTransaction handlerWCT = new WindowContainerTransaction();
|
||||||
@@ -202,8 +212,7 @@ public class ShellTransitionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRequestRemoteTransition() {
|
public void testRequestRemoteTransition() {
|
||||||
Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
|
Transitions transitions = createTestTransitions();
|
||||||
mMainExecutor, mAnimExecutor);
|
|
||||||
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
||||||
|
|
||||||
final boolean[] remoteCalled = new boolean[]{false};
|
final boolean[] remoteCalled = new boolean[]{false};
|
||||||
@@ -297,8 +306,7 @@ public class ShellTransitionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRegisteredRemoteTransition() {
|
public void testRegisteredRemoteTransition() {
|
||||||
Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
|
Transitions transitions = createTestTransitions();
|
||||||
mMainExecutor, mAnimExecutor);
|
|
||||||
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
||||||
|
|
||||||
final boolean[] remoteCalled = new boolean[]{false};
|
final boolean[] remoteCalled = new boolean[]{false};
|
||||||
@@ -343,8 +351,7 @@ public class ShellTransitionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOneShotRemoteHandler() {
|
public void testOneShotRemoteHandler() {
|
||||||
Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
|
Transitions transitions = createTestTransitions();
|
||||||
mMainExecutor, mAnimExecutor);
|
|
||||||
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
||||||
|
|
||||||
final boolean[] remoteCalled = new boolean[]{false};
|
final boolean[] remoteCalled = new boolean[]{false};
|
||||||
@@ -390,8 +397,7 @@ public class ShellTransitionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransitionQueueing() {
|
public void testTransitionQueueing() {
|
||||||
Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
|
Transitions transitions = createTestTransitions();
|
||||||
mMainExecutor, mAnimExecutor);
|
|
||||||
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
||||||
|
|
||||||
IBinder transitToken1 = new Binder();
|
IBinder transitToken1 = new Binder();
|
||||||
@@ -431,8 +437,7 @@ public class ShellTransitionTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransitionMerging() {
|
public void testTransitionMerging() {
|
||||||
Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
|
Transitions transitions = createTestTransitions();
|
||||||
mMainExecutor, mAnimExecutor);
|
|
||||||
mDefaultHandler.setSimulateMerge(true);
|
mDefaultHandler.setSimulateMerge(true);
|
||||||
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
|
||||||
|
|
||||||
@@ -468,6 +473,63 @@ public class ShellTransitionTests {
|
|||||||
assertEquals(0, mDefaultHandler.activeCount());
|
assertEquals(0, mDefaultHandler.activeCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testShouldRotateSeamlessly() throws Exception {
|
||||||
|
final RunningTaskInfo taskInfo =
|
||||||
|
createTaskInfo(1, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
|
||||||
|
final RunningTaskInfo taskInfoPip =
|
||||||
|
createTaskInfo(1, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
|
||||||
|
|
||||||
|
final DisplayController displays = createTestDisplayController();
|
||||||
|
final @Surface.Rotation int upsideDown = displays
|
||||||
|
.getDisplayLayout(DEFAULT_DISPLAY).getUpsideDownRotation();
|
||||||
|
|
||||||
|
final TransitionInfo normalDispRotate = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
||||||
|
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY).setRotate()
|
||||||
|
.build())
|
||||||
|
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo).setRotate().build())
|
||||||
|
.build();
|
||||||
|
assertFalse(DefaultTransitionHandler.isRotationSeamless(normalDispRotate, displays));
|
||||||
|
|
||||||
|
// Seamless if all tasks are seamless
|
||||||
|
final TransitionInfo rotateSeamless = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
||||||
|
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY).setRotate()
|
||||||
|
.build())
|
||||||
|
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
|
||||||
|
.setRotate(ROTATION_ANIMATION_SEAMLESS).build())
|
||||||
|
.build();
|
||||||
|
assertTrue(DefaultTransitionHandler.isRotationSeamless(rotateSeamless, displays));
|
||||||
|
|
||||||
|
// Not seamless if there is PiP (or any other non-seamless task)
|
||||||
|
final TransitionInfo pipDispRotate = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
||||||
|
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY).setRotate()
|
||||||
|
.build())
|
||||||
|
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
|
||||||
|
.setRotate(ROTATION_ANIMATION_SEAMLESS).build())
|
||||||
|
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfoPip)
|
||||||
|
.setRotate().build())
|
||||||
|
.build();
|
||||||
|
assertFalse(DefaultTransitionHandler.isRotationSeamless(pipDispRotate, displays));
|
||||||
|
|
||||||
|
// Not seamless if one of rotations is upside-down
|
||||||
|
final TransitionInfo seamlessUpsideDown = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
||||||
|
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY)
|
||||||
|
.setRotate(upsideDown, ROTATION_ANIMATION_UNSPECIFIED).build())
|
||||||
|
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
|
||||||
|
.setRotate(upsideDown, ROTATION_ANIMATION_SEAMLESS).build())
|
||||||
|
.build();
|
||||||
|
assertFalse(DefaultTransitionHandler.isRotationSeamless(seamlessUpsideDown, displays));
|
||||||
|
|
||||||
|
// Not seamless if system alert windows
|
||||||
|
final TransitionInfo seamlessButAlert = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
||||||
|
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(
|
||||||
|
FLAG_IS_DISPLAY | FLAG_DISPLAY_HAS_ALERT_WINDOWS).setRotate().build())
|
||||||
|
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
|
||||||
|
.setRotate(ROTATION_ANIMATION_SEAMLESS).build())
|
||||||
|
.build();
|
||||||
|
assertFalse(DefaultTransitionHandler.isRotationSeamless(seamlessButAlert, displays));
|
||||||
|
}
|
||||||
|
|
||||||
class TransitionInfoBuilder {
|
class TransitionInfoBuilder {
|
||||||
final TransitionInfo mInfo;
|
final TransitionInfo mInfo;
|
||||||
|
|
||||||
@@ -490,11 +552,53 @@ public class ShellTransitionTests {
|
|||||||
return addChange(mode, null /* taskInfo */);
|
return addChange(mode, null /* taskInfo */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TransitionInfoBuilder addChange(TransitionInfo.Change change) {
|
||||||
|
mInfo.addChange(change);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
TransitionInfo build() {
|
TransitionInfo build() {
|
||||||
return mInfo;
|
return mInfo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ChangeBuilder {
|
||||||
|
final TransitionInfo.Change mChange;
|
||||||
|
|
||||||
|
ChangeBuilder(@WindowManager.TransitionType int mode) {
|
||||||
|
mChange = new TransitionInfo.Change(null /* token */, null /* leash */);
|
||||||
|
mChange.setMode(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeBuilder setFlags(@TransitionInfo.ChangeFlags int flags) {
|
||||||
|
mChange.setFlags(flags);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeBuilder setTask(RunningTaskInfo taskInfo) {
|
||||||
|
mChange.setTaskInfo(taskInfo);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeBuilder setRotate(int anim) {
|
||||||
|
return setRotate(Surface.ROTATION_90, anim);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeBuilder setRotate() {
|
||||||
|
return setRotate(ROTATION_ANIMATION_UNSPECIFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeBuilder setRotate(@Surface.Rotation int target, int anim) {
|
||||||
|
mChange.setRotation(Surface.ROTATION_0, target);
|
||||||
|
mChange.setRotationAnimation(anim);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransitionInfo.Change build() {
|
||||||
|
return mChange;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TestTransitionHandler implements Transitions.TransitionHandler {
|
class TestTransitionHandler implements Transitions.TransitionHandler {
|
||||||
ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>();
|
ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>();
|
||||||
final ArrayList<IBinder> mMerged = new ArrayList<>();
|
final ArrayList<IBinder> mMerged = new ArrayList<>();
|
||||||
@@ -566,4 +670,45 @@ public class ShellTransitionTests {
|
|||||||
return taskInfo;
|
return taskInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private DisplayController createTestDisplayController() {
|
||||||
|
IWindowManager mockWM = mock(IWindowManager.class);
|
||||||
|
final IDisplayWindowListener[] displayListener = new IDisplayWindowListener[1];
|
||||||
|
try {
|
||||||
|
doAnswer(new Answer() {
|
||||||
|
@Override
|
||||||
|
public Object answer(InvocationOnMock invocation) {
|
||||||
|
displayListener[0] = invocation.getArgument(0);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).when(mockWM).registerDisplayWindowListener(any());
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// No remote stuff happening, so this can't be hit
|
||||||
|
}
|
||||||
|
DisplayController out = new DisplayController(mContext, mockWM, mMainExecutor);
|
||||||
|
try {
|
||||||
|
displayListener[0].onDisplayAdded(DEFAULT_DISPLAY);
|
||||||
|
mMainExecutor.flushAll();
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// Again, no remote stuff
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Transitions createTestTransitions() {
|
||||||
|
return new Transitions(mOrganizer, mTransactionPool, createTestDisplayController(),
|
||||||
|
mContext, mMainExecutor, mAnimExecutor);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
// private class TestDisplayController extends DisplayController {
|
||||||
|
// private final DisplayLayout mTestDisplayLayout;
|
||||||
|
// TestDisplayController() {
|
||||||
|
// super(mContext, mock(IWindowManager.class), mMainExecutor);
|
||||||
|
// mTestDisplayLayout = new DisplayLayout();
|
||||||
|
// mTestDisplayLayout.
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// @Override
|
||||||
|
// DisplayLayout
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -328,9 +328,11 @@ public abstract class WMShellBaseModule {
|
|||||||
@WMSingleton
|
@WMSingleton
|
||||||
@Provides
|
@Provides
|
||||||
static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
|
static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool,
|
||||||
Context context, @ShellMainThread ShellExecutor mainExecutor,
|
DisplayController displayController, Context context,
|
||||||
|
@ShellMainThread ShellExecutor mainExecutor,
|
||||||
@ShellAnimationThread ShellExecutor animExecutor) {
|
@ShellAnimationThread ShellExecutor animExecutor) {
|
||||||
return new Transitions(organizer, pool, context, mainExecutor, animExecutor);
|
return new Transitions(organizer, pool, displayController, context, mainExecutor,
|
||||||
|
animExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
|||||||
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
|
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
|
||||||
import static android.view.Display.DEFAULT_DISPLAY;
|
import static android.view.Display.DEFAULT_DISPLAY;
|
||||||
import static android.view.Display.INVALID_DISPLAY;
|
import static android.view.Display.INVALID_DISPLAY;
|
||||||
|
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_CHANGE;
|
||||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||||
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
|
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
|
||||||
@@ -36,6 +38,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
|||||||
import static android.view.WindowManager.TransitionFlags;
|
import static android.view.WindowManager.TransitionFlags;
|
||||||
import static android.view.WindowManager.TransitionType;
|
import static android.view.WindowManager.TransitionType;
|
||||||
import static android.view.WindowManager.transitTypeToString;
|
import static android.view.WindowManager.transitTypeToString;
|
||||||
|
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_DISPLAY;
|
||||||
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
|
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
|
||||||
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
|
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
|
||||||
@@ -1066,6 +1069,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
|||||||
final ActivityManager.RunningTaskInfo tinfo = new ActivityManager.RunningTaskInfo();
|
final ActivityManager.RunningTaskInfo tinfo = new ActivityManager.RunningTaskInfo();
|
||||||
task.fillTaskInfo(tinfo);
|
task.fillTaskInfo(tinfo);
|
||||||
change.setTaskInfo(tinfo);
|
change.setTaskInfo(tinfo);
|
||||||
|
change.setRotationAnimation(getTaskRotationAnimation(task));
|
||||||
}
|
}
|
||||||
out.addChange(change);
|
out.addChange(change);
|
||||||
}
|
}
|
||||||
@@ -1073,6 +1077,23 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getTaskRotationAnimation(@NonNull Task task) {
|
||||||
|
final ActivityRecord top = task.getTopVisibleActivity();
|
||||||
|
if (top == null) return ROTATION_ANIMATION_UNSPECIFIED;
|
||||||
|
final WindowState mainWin = top.findMainWindow(false);
|
||||||
|
if (mainWin == null) return ROTATION_ANIMATION_UNSPECIFIED;
|
||||||
|
int anim = mainWin.getRotationAnimationHint();
|
||||||
|
if (anim >= 0) return anim;
|
||||||
|
anim = mainWin.getAttrs().rotationAnimation;
|
||||||
|
if (anim != ROTATION_ANIMATION_SEAMLESS) return anim;
|
||||||
|
if (mainWin != task.mDisplayContent.getDisplayPolicy().getTopFullscreenOpaqueWindow()
|
||||||
|
|| !top.matchParentBounds()) {
|
||||||
|
// At the moment, we only support seamless rotation if there is only one window showing.
|
||||||
|
return ROTATION_ANIMATION_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
return mainWin.getAttrs().rotationAnimation;
|
||||||
|
}
|
||||||
|
|
||||||
boolean getLegacyIsReady() {
|
boolean getLegacyIsReady() {
|
||||||
return mState == STATE_STARTED && mSyncId >= 0 && mSyncEngine.isReady(mSyncId);
|
return mState == STATE_STARTED && mSyncId >= 0 && mSyncEngine.isReady(mSyncId);
|
||||||
}
|
}
|
||||||
@@ -1166,6 +1187,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
|||||||
final DisplayContent dc = wc.asDisplayContent();
|
final DisplayContent dc = wc.asDisplayContent();
|
||||||
if (dc != null) {
|
if (dc != null) {
|
||||||
flags |= FLAG_IS_DISPLAY;
|
flags |= FLAG_IS_DISPLAY;
|
||||||
|
if (dc.hasAlertWindowSurfaces()) {
|
||||||
|
flags |= FLAG_DISPLAY_HAS_ALERT_WINDOWS;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isWallpaper(wc)) {
|
if (isWallpaper(wc)) {
|
||||||
flags |= FLAG_IS_WALLPAPER;
|
flags |= FLAG_IS_WALLPAPER;
|
||||||
|
|||||||
Reference in New Issue
Block a user