Merge "Use seamless rotation if it is requested by display" into tm-qpr-dev
This commit is contained in:
@@ -64,6 +64,7 @@ import android.animation.ValueAnimator;
|
||||
import android.annotation.ColorInt;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityThread;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
@@ -203,14 +204,24 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static boolean isRotationSeamless(@NonNull TransitionInfo info,
|
||||
DisplayController displayController) {
|
||||
static int getRotationAnimationHint(@NonNull TransitionInfo.Change displayChange,
|
||||
@NonNull TransitionInfo info, @NonNull DisplayController displayController) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
|
||||
"Display is changing, check if it should be seamless.");
|
||||
boolean checkedDisplayLayout = false;
|
||||
boolean hasTask = false;
|
||||
boolean displayExplicitSeamless = false;
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
"Display is changing, resolve the animation hint.");
|
||||
// The explicit request of display has the highest priority.
|
||||
if (displayChange.getRotationAnimation() == ROTATION_ANIMATION_SEAMLESS) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
|
||||
" display requests explicit seamless");
|
||||
return ROTATION_ANIMATION_SEAMLESS;
|
||||
}
|
||||
|
||||
boolean allTasksSeamless = false;
|
||||
boolean rejectSeamless = false;
|
||||
ActivityManager.RunningTaskInfo topTaskInfo = null;
|
||||
int animationHint = ROTATION_ANIMATION_ROTATE;
|
||||
// Traverse in top-to-bottom order so that the first task is top-most.
|
||||
final int size = info.getChanges().size();
|
||||
for (int i = 0; i < size; ++i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
|
||||
// Only look at changing things. showing/hiding don't need to rotate.
|
||||
@@ -223,95 +234,69 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
||||
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;
|
||||
rejectSeamless = true;
|
||||
}
|
||||
displayExplicitSeamless =
|
||||
change.getRotationAnimation() == ROTATION_ANIMATION_SEAMLESS;
|
||||
} 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;
|
||||
rejectSeamless = true;
|
||||
}
|
||||
} else if (change.getTaskInfo() != null) {
|
||||
hasTask = true;
|
||||
final int anim = change.getRotationAnimation();
|
||||
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
|
||||
final boolean isTopTask = topTaskInfo == null;
|
||||
if (isTopTask) {
|
||||
topTaskInfo = taskInfo;
|
||||
if (anim != ROTATION_ANIMATION_UNSPECIFIED
|
||||
&& anim != ROTATION_ANIMATION_SEAMLESS) {
|
||||
animationHint = anim;
|
||||
}
|
||||
}
|
||||
// We only enable seamless rotation if all the visible task windows requested it.
|
||||
if (change.getRotationAnimation() != ROTATION_ANIMATION_SEAMLESS) {
|
||||
if (anim != 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;
|
||||
}
|
||||
taskInfo.taskId);
|
||||
allTasksSeamless = false;
|
||||
} else if (isTopTask) {
|
||||
allTasksSeamless = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ROTATION_ANIMATION_SEAMLESS can only be requested by task or display.
|
||||
if (hasTask || displayExplicitSeamless) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Rotation IS seamless.");
|
||||
return true;
|
||||
if (!allTasksSeamless || rejectSeamless) {
|
||||
return animationHint;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
// This is the only way to get display-id currently, so check display capabilities here.
|
||||
final DisplayLayout displayLayout = displayController.getDisplayLayout(
|
||||
topTaskInfo.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.
|
||||
final int upsideDownRotation = displayLayout.getUpsideDownRotation();
|
||||
if (displayChange.getStartRotation() == upsideDownRotation
|
||||
|| displayChange.getEndRotation() == upsideDownRotation) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
|
||||
" rotation involves upside-down portrait, so not seamless.");
|
||||
return animationHint;
|
||||
}
|
||||
return ROTATION_ANIMATION_ROTATE;
|
||||
|
||||
// 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, e.g. with gesture navigation
|
||||
// where the navbar is low-profile enough that this isn't very noticeable.
|
||||
if (!displayLayout.allowSeamlessRotationDespiteNavBarMoving()
|
||||
&& (!(displayLayout.navigationBarCanMove()
|
||||
&& (displayChange.getStartAbsBounds().width()
|
||||
!= displayChange.getStartAbsBounds().height())))) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
|
||||
" nav bar changes sides, so not seamless.");
|
||||
return animationHint;
|
||||
}
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Rotation IS seamless.");
|
||||
return ROTATION_ANIMATION_SEAMLESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -354,8 +339,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
|
||||
|
||||
if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
|
||||
if (info.getType() == TRANSIT_CHANGE) {
|
||||
isSeamlessDisplayChange = isRotationSeamless(info, mDisplayController);
|
||||
final int anim = getRotationAnimation(info);
|
||||
final int anim = getRotationAnimationHint(change, info, mDisplayController);
|
||||
isSeamlessDisplayChange = anim == ROTATION_ANIMATION_SEAMLESS;
|
||||
if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
|
||||
startRotationAnimation(startTransaction, change, info, anim, animations,
|
||||
onAnimFinish);
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
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_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;
|
||||
@@ -553,64 +554,77 @@ public class ShellTransitionTests extends ShellTestCase {
|
||||
final @Surface.Rotation int upsideDown = displays
|
||||
.getDisplayLayout(DEFAULT_DISPLAY).getUpsideDownRotation();
|
||||
|
||||
TransitionInfo.Change displayChange = new ChangeBuilder(TRANSIT_CHANGE)
|
||||
.setFlags(FLAG_IS_DISPLAY).setRotate().build();
|
||||
// Set non-square display so nav bar won't be allowed to move.
|
||||
displayChange.getStartAbsBounds().set(0, 0, 1000, 2000);
|
||||
final TransitionInfo normalDispRotate = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
||||
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY).setRotate()
|
||||
.build())
|
||||
.addChange(displayChange)
|
||||
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo).setRotate().build())
|
||||
.build();
|
||||
assertFalse(DefaultTransitionHandler.isRotationSeamless(normalDispRotate, displays));
|
||||
assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint(
|
||||
displayChange, 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(displayChange)
|
||||
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
|
||||
.setRotate(ROTATION_ANIMATION_SEAMLESS).build())
|
||||
.build();
|
||||
assertTrue(DefaultTransitionHandler.isRotationSeamless(rotateSeamless, displays));
|
||||
assertEquals(ROTATION_ANIMATION_SEAMLESS, DefaultTransitionHandler.getRotationAnimationHint(
|
||||
displayChange, 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(displayChange)
|
||||
.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));
|
||||
assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint(
|
||||
displayChange, pipDispRotate, displays));
|
||||
|
||||
// Not seamless if there is no changed task.
|
||||
final TransitionInfo noTask = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
||||
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY)
|
||||
.setRotate().build())
|
||||
.addChange(displayChange)
|
||||
.build();
|
||||
assertFalse(DefaultTransitionHandler.isRotationSeamless(noTask, displays));
|
||||
assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint(
|
||||
displayChange, noTask, displays));
|
||||
|
||||
// Seamless if display is explicitly seamless.
|
||||
final TransitionInfo seamlessDisplay = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
||||
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY)
|
||||
// Not seamless if one of rotations is upside-down
|
||||
displayChange = new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY)
|
||||
.setRotate(upsideDown, ROTATION_ANIMATION_UNSPECIFIED).build();
|
||||
final TransitionInfo seamlessUpsideDown = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
||||
.addChange(displayChange)
|
||||
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
|
||||
.setRotate(upsideDown, ROTATION_ANIMATION_SEAMLESS).build())
|
||||
.build();
|
||||
assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint(
|
||||
displayChange, seamlessUpsideDown, displays));
|
||||
|
||||
// Not seamless if system alert windows
|
||||
displayChange = new ChangeBuilder(TRANSIT_CHANGE)
|
||||
.setFlags(FLAG_IS_DISPLAY | FLAG_DISPLAY_HAS_ALERT_WINDOWS).setRotate().build();
|
||||
final TransitionInfo seamlessButAlert = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
||||
.addChange(displayChange)
|
||||
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
|
||||
.setRotate(ROTATION_ANIMATION_SEAMLESS).build())
|
||||
.build();
|
||||
assertTrue(DefaultTransitionHandler.isRotationSeamless(seamlessDisplay, displays));
|
||||
assertEquals(ROTATION_ANIMATION_ROTATE, DefaultTransitionHandler.getRotationAnimationHint(
|
||||
displayChange, seamlessButAlert, displays));
|
||||
|
||||
// Seamless if display is explicitly seamless.
|
||||
displayChange = new ChangeBuilder(TRANSIT_CHANGE).setFlags(FLAG_IS_DISPLAY)
|
||||
.setRotate(ROTATION_ANIMATION_SEAMLESS).build();
|
||||
final TransitionInfo seamlessDisplay = new TransitionInfoBuilder(TRANSIT_CHANGE)
|
||||
.addChange(displayChange)
|
||||
// The animation hint of task will be ignored.
|
||||
.addChange(new ChangeBuilder(TRANSIT_CHANGE).setTask(taskInfo)
|
||||
.setRotate(ROTATION_ANIMATION_ROTATE).build())
|
||||
.build();
|
||||
assertEquals(ROTATION_ANIMATION_SEAMLESS, DefaultTransitionHandler.getRotationAnimationHint(
|
||||
displayChange, seamlessDisplay, displays));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user