Cancel the recents animation when keyguard is occluding.[1/2]
User can enter recents animation while a showWhenLocked activity on top of keyguard by touch down navigation bar. In legacy, Launcher start the recents animation by keep the original top activity on top. But for shell transition, Launcher will start home activity with transient launch flag. When core receive start activity request, unlike startRecentsAnimation, core will try to move the activity to top, which break the keyguard occluding state, so the keyguard will pop up on screen before dismiss recents animation, and there is not have enough information for shell know whether to hide the surface of the original top activity. To make the occluding state unchange during start transient launch, we do not resume the launcher activity, also we do not move it to top. So what we need to do is to collect the surfaces from launcher + top occluding task + wallpaper and send this transition to shell. Another change is Launcher will finish recents animation by calling the RecentsAnimationController directly without start home activity. By determine the top activity when set restoreTransientOrder, there will update the hierarchy, which may change the occluding state and decide whether to play unoccluding animation. Bug: 230582311 Test: monitor no strange animation when running atest KeyguardTests Test: enable shell transition. Start showWhenLocked activity while keyguard showing. Scroll up to dismiss the activity and verify the activity and home shall be stopped and only keyguard remains on the screen. Also verify entering recents works fine when device is unlocked. Change-Id: Ia16fa01fdc09547cfb3ae1370875ac70f07ede8d
This commit is contained in:
@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
|
||||
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
|
||||
import static android.view.WindowManager.TRANSIT_OPEN;
|
||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||
@@ -168,7 +169,8 @@ public class RemoteTransitionCompat implements Parcelable {
|
||||
}
|
||||
t.apply();
|
||||
mRecentsSession.setup(controller, info, finishedCallback, pausingTasks, pipTask,
|
||||
recentsTask, leashMap, mToken);
|
||||
recentsTask, leashMap, mToken,
|
||||
(info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0);
|
||||
recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0),
|
||||
new Rect());
|
||||
}
|
||||
@@ -223,12 +225,13 @@ public class RemoteTransitionCompat implements Parcelable {
|
||||
private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
|
||||
private PictureInPictureSurfaceTransaction mPipTransaction = null;
|
||||
private IBinder mTransition = null;
|
||||
private boolean mKeyguardLocked = false;
|
||||
|
||||
void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info,
|
||||
IRemoteTransitionFinishedCallback finishCB,
|
||||
ArrayList<WindowContainerToken> pausingTasks, WindowContainerToken pipTask,
|
||||
WindowContainerToken recentsTask, ArrayMap<SurfaceControl, SurfaceControl> leashMap,
|
||||
IBinder transition) {
|
||||
IBinder transition, boolean keyguardLocked) {
|
||||
if (mInfo != null) {
|
||||
throw new IllegalStateException("Trying to run a new recents animation while"
|
||||
+ " recents is already active.");
|
||||
@@ -241,6 +244,7 @@ public class RemoteTransitionCompat implements Parcelable {
|
||||
mRecentsTask = recentsTask;
|
||||
mLeashMap = leashMap;
|
||||
mTransition = transition;
|
||||
mKeyguardLocked = keyguardLocked;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@@ -375,6 +379,10 @@ public class RemoteTransitionCompat implements Parcelable {
|
||||
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
|
||||
if (mKeyguardLocked && mRecentsTask != null) {
|
||||
if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
|
||||
else wct.restoreTransientOrder(mRecentsTask);
|
||||
}
|
||||
if (!toHome && mPausingTasks != null && mOpeningLeashes == null) {
|
||||
// The gesture went back to opening the app rather than continuing with
|
||||
// recents, so end the transition by moving the app back to the top (and also
|
||||
@@ -384,7 +392,7 @@ public class RemoteTransitionCompat implements Parcelable {
|
||||
wct.reorder(mPausingTasks.get(i), true /* onTop */);
|
||||
t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash());
|
||||
}
|
||||
if (mRecentsTask != null) {
|
||||
if (!mKeyguardLocked && mRecentsTask != null) {
|
||||
wct.restoreTransientOrder(mRecentsTask);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -211,6 +211,7 @@ class ActivityStarter {
|
||||
// The task which was above the targetTask before starting this activity. null if the targetTask
|
||||
// was already on top or if the activity is in a new task.
|
||||
private Task mPriorAboveTask;
|
||||
private boolean mDisplayLockAndOccluded;
|
||||
|
||||
// We must track when we deliver the new intent since multiple code paths invoke
|
||||
// {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used
|
||||
@@ -1734,19 +1735,38 @@ class ActivityStarter {
|
||||
// Transition housekeeping.
|
||||
final TransitionController transitionController = started.mTransitionController;
|
||||
final boolean isStarted = result == START_SUCCESS || result == START_TASK_TO_FRONT;
|
||||
final boolean isTransientLaunch = options != null && options.getTransientLaunch();
|
||||
// Start transient launch while keyguard locked and occluded by other app, for this
|
||||
// condition we would like to play the remote transition without modify any visible state
|
||||
// for the hierarchy in core, so here will force execute this transition.
|
||||
final boolean forceTransientTransition = isTransientLaunch && mPriorAboveTask != null
|
||||
&& mDisplayLockAndOccluded;
|
||||
if (isStarted) {
|
||||
// The activity is started new rather than just brought forward, so record it as an
|
||||
// existence change.
|
||||
transitionController.collectExistenceChange(started);
|
||||
} else if (result == START_DELIVERED_TO_TOP && newTransition != null) {
|
||||
// We just delivered to top, so there isn't an actual transition here.
|
||||
newTransition.abort();
|
||||
newTransition = null;
|
||||
if (!forceTransientTransition) {
|
||||
newTransition.abort();
|
||||
newTransition = null;
|
||||
}
|
||||
}
|
||||
if (options != null && options.getTransientLaunch()) {
|
||||
if (isTransientLaunch) {
|
||||
if (forceTransientTransition && newTransition != null) {
|
||||
newTransition.collect(mLastStartActivityRecord);
|
||||
newTransition.collect(mPriorAboveTask);
|
||||
}
|
||||
// `started` isn't guaranteed to be the actual relevant activity, so we must wait
|
||||
// until after we launched to identify the relevant activity.
|
||||
transitionController.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask);
|
||||
if (forceTransientTransition && newTransition != null) {
|
||||
final DisplayContent dc = mLastStartActivityRecord.getDisplayContent();
|
||||
// update wallpaper target to TransientHide
|
||||
dc.mWallpaperController.adjustWallpaperWindows();
|
||||
// execute transition because there is no change
|
||||
newTransition.setReady(dc, true /* ready */);
|
||||
}
|
||||
}
|
||||
if (!userLeaving) {
|
||||
// no-user-leaving implies not entering PiP.
|
||||
@@ -2384,6 +2404,7 @@ class ActivityStarter {
|
||||
mAvoidMoveToFront = false;
|
||||
mFrozeTaskList = false;
|
||||
mTransientLaunch = false;
|
||||
mDisplayLockAndOccluded = false;
|
||||
|
||||
mVoiceSession = null;
|
||||
mVoiceInteractor = null;
|
||||
@@ -2492,6 +2513,16 @@ class ActivityStarter {
|
||||
mAvoidMoveToFront = true;
|
||||
}
|
||||
mTransientLaunch = mOptions.getTransientLaunch();
|
||||
final KeyguardController kc = mSupervisor.getKeyguardController();
|
||||
final int displayId = mPreferredTaskDisplayArea.getDisplayId();
|
||||
mDisplayLockAndOccluded = kc.isKeyguardLocked(displayId)
|
||||
&& kc.isDisplayOccluded(displayId);
|
||||
// Recents animation on lock screen, do not resume & move launcher to top.
|
||||
if (mTransientLaunch && mDisplayLockAndOccluded
|
||||
&& mService.getTransitionController().isShellTransitionsEnabled()) {
|
||||
mDoResume = false;
|
||||
mAvoidMoveToFront = true;
|
||||
}
|
||||
mTargetRootTask = Task.fromWindowContainerToken(mOptions.getLaunchRootTask());
|
||||
|
||||
if (inTaskFragment == null) {
|
||||
|
||||
@@ -1559,8 +1559,11 @@ class TaskFragment extends WindowContainer<WindowContainer> {
|
||||
|
||||
} else {
|
||||
prev.schedulePauseTimeout();
|
||||
// Unset readiness since we now need to wait until this pause is complete.
|
||||
mTransitionController.setReady(this, false /* ready */);
|
||||
// All activities will be stopped when sleeping, don't need to wait for pause.
|
||||
if (!uiSleeping) {
|
||||
// Unset readiness since we now need to wait until this pause is complete.
|
||||
mTransitionController.setReady(this, false /* ready */);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -214,10 +214,26 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
mTransientLaunches = new ArrayMap<>();
|
||||
}
|
||||
mTransientLaunches.put(activity, restoreBelow);
|
||||
setTransientLaunchToChanges(activity);
|
||||
|
||||
if (restoreBelow != null) {
|
||||
final ChangeInfo info = mChanges.get(restoreBelow);
|
||||
info.mFlags |= ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH;
|
||||
}
|
||||
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as "
|
||||
+ "transient-launch", mSyncId, activity);
|
||||
}
|
||||
|
||||
boolean isTransientHide(@NonNull Task task) {
|
||||
if (mTransientLaunches == null) return false;
|
||||
for (int i = 0; i < mTransientLaunches.size(); ++i) {
|
||||
if (mTransientLaunches.valueAt(i) == task) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isTransientLaunch(@NonNull ActivityRecord activity) {
|
||||
return mTransientLaunches != null && mTransientLaunches.containsKey(activity);
|
||||
}
|
||||
@@ -242,6 +258,20 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
info.mFlags = info.mFlags | ChangeInfo.FLAG_SEAMLESS_ROTATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only set flag to the parent tasks and activity itself.
|
||||
*/
|
||||
private void setTransientLaunchToChanges(@NonNull WindowContainer wc) {
|
||||
for (WindowContainer curr = wc; curr != null && mChanges.containsKey(curr);
|
||||
curr = curr.getParent()) {
|
||||
if (curr.asTask() == null && curr.asActivityRecord() == null) {
|
||||
return;
|
||||
}
|
||||
final ChangeInfo info = mChanges.get(curr);
|
||||
info.mFlags = info.mFlags | ChangeInfo.FLAG_TRANSIENT_LAUNCH;
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
int getSyncId() {
|
||||
return mSyncId;
|
||||
@@ -1546,10 +1576,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
* seamless rotation. This is currently only used by DisplayContent during fixed-rotation.
|
||||
*/
|
||||
private static final int FLAG_SEAMLESS_ROTATION = 1;
|
||||
private static final int FLAG_TRANSIENT_LAUNCH = 2;
|
||||
private static final int FLAG_ABOVE_TRANSIENT_LAUNCH = 4;
|
||||
|
||||
@IntDef(prefix = { "FLAG_" }, value = {
|
||||
FLAG_NONE,
|
||||
FLAG_SEAMLESS_ROTATION
|
||||
FLAG_SEAMLESS_ROTATION,
|
||||
FLAG_TRANSIENT_LAUNCH,
|
||||
FLAG_ABOVE_TRANSIENT_LAUNCH
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface Flag {}
|
||||
@@ -1586,6 +1620,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
}
|
||||
|
||||
boolean hasChanged(@NonNull WindowContainer newState) {
|
||||
// the task including transient launch must promote to root task
|
||||
if ((mFlags & ChangeInfo.FLAG_TRANSIENT_LAUNCH) != 0
|
||||
|| (mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0) {
|
||||
return true;
|
||||
}
|
||||
// If it's invisible and hasn't changed visibility, always return false since even if
|
||||
// something changed, it wouldn't be a visible change.
|
||||
final boolean currVisible = newState.isVisibleRequested();
|
||||
@@ -1601,6 +1640,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
|
||||
|
||||
@TransitionInfo.TransitionMode
|
||||
int getTransitMode(@NonNull WindowContainer wc) {
|
||||
if ((mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0) {
|
||||
return TRANSIT_CLOSE;
|
||||
}
|
||||
final boolean nowVisible = wc.isVisibleRequested();
|
||||
if (nowVisible == mVisible) {
|
||||
return TRANSIT_CHANGE;
|
||||
|
||||
@@ -282,6 +282,16 @@ class TransitionController {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isTransientHide(@NonNull Task task) {
|
||||
if (mCollectingTransition != null && mCollectingTransition.isTransientHide(task)) {
|
||||
return true;
|
||||
}
|
||||
for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) {
|
||||
if (mPlayingTransitions.get(i).isTransientHide(task)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if {@param ar} is part of a transient-launch activity in an active
|
||||
* transition.
|
||||
|
||||
@@ -188,7 +188,8 @@ class WallpaperController {
|
||||
&& animatingContainer.getAnimation().getShowWallpaper();
|
||||
final boolean hasWallpaper = w.hasWallpaper() || animationWallpaper;
|
||||
final boolean isRecentsTransitionTarget = (recentsAnimationController != null
|
||||
&& recentsAnimationController.isWallpaperVisible(w));
|
||||
&& recentsAnimationController.isWallpaperVisible(w))
|
||||
|| w.mTransitionController.isTransientHide(w.getTask());
|
||||
if (isRecentsTransitionTarget) {
|
||||
if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w);
|
||||
mFindResults.setWallpaperTarget(w);
|
||||
|
||||
Reference in New Issue
Block a user