Merge "Move recentsAnimationController to shell" into udc-dev
This commit is contained in:
@@ -82,6 +82,7 @@ import com.android.wm.shell.pip.PipUiEventLogger;
|
||||
import com.android.wm.shell.pip.phone.PipTouchHandler;
|
||||
import com.android.wm.shell.recents.RecentTasks;
|
||||
import com.android.wm.shell.recents.RecentTasksController;
|
||||
import com.android.wm.shell.recents.RecentsTransitionHandler;
|
||||
import com.android.wm.shell.splitscreen.SplitScreen;
|
||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||
import com.android.wm.shell.startingsurface.StartingSurface;
|
||||
@@ -520,6 +521,9 @@ public abstract class WMShellBaseModule {
|
||||
desktopModeTaskRepository, mainExecutor));
|
||||
}
|
||||
|
||||
@BindsOptionalOf
|
||||
abstract RecentsTransitionHandler optionalRecentsTransitionHandler();
|
||||
|
||||
//
|
||||
// Shell transitions
|
||||
//
|
||||
@@ -803,6 +807,7 @@ public abstract class WMShellBaseModule {
|
||||
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
|
||||
Optional<FreeformComponents> freeformComponents,
|
||||
Optional<RecentTasksController> recentTasksOptional,
|
||||
Optional<RecentsTransitionHandler> recentsTransitionHandlerOptional,
|
||||
Optional<OneHandedController> oneHandedControllerOptional,
|
||||
Optional<HideDisplayCutoutController> hideDisplayCutoutControllerOptional,
|
||||
Optional<ActivityEmbeddingController> activityEmbeddingOptional,
|
||||
|
||||
@@ -83,6 +83,7 @@ import com.android.wm.shell.pip.phone.PipMotionHelper;
|
||||
import com.android.wm.shell.pip.phone.PipSizeSpecHandler;
|
||||
import com.android.wm.shell.pip.phone.PipTouchHandler;
|
||||
import com.android.wm.shell.recents.RecentTasksController;
|
||||
import com.android.wm.shell.recents.RecentsTransitionHandler;
|
||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||
import com.android.wm.shell.sysui.ShellCommandHandler;
|
||||
import com.android.wm.shell.sysui.ShellController;
|
||||
@@ -528,9 +529,20 @@ public abstract class WMShellModule {
|
||||
ShellInit shellInit,
|
||||
Optional<SplitScreenController> splitScreenOptional,
|
||||
Optional<PipTouchHandler> pipTouchHandlerOptional,
|
||||
Optional<RecentsTransitionHandler> recentsTransitionHandler,
|
||||
Transitions transitions) {
|
||||
return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional,
|
||||
pipTouchHandlerOptional);
|
||||
pipTouchHandlerOptional, recentsTransitionHandler);
|
||||
}
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static RecentsTransitionHandler provideRecentsTransitionHandler(
|
||||
ShellInit shellInit,
|
||||
Transitions transitions,
|
||||
Optional<RecentTasksController> recentTasksController) {
|
||||
return new RecentsTransitionHandler(shellInit, transitions,
|
||||
recentTasksController.orElse(null));
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
package com.android.wm.shell.recents;
|
||||
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.IRecentsAnimationRunner;
|
||||
|
||||
import com.android.wm.shell.recents.IRecentTasksListener;
|
||||
import com.android.wm.shell.util.GroupedRecentTaskInfo;
|
||||
@@ -45,4 +50,10 @@ interface IRecentTasks {
|
||||
* Gets the set of running tasks.
|
||||
*/
|
||||
RunningTaskInfo[] getRunningTasks(int maxNum) = 4;
|
||||
|
||||
/**
|
||||
* Starts a recents transition.
|
||||
*/
|
||||
oneway void startRecentsTransition(in PendingIntent intent, in Intent fillIn, in Bundle options,
|
||||
IApplicationThread appThread, IRecentsAnimationRunner listener) = 5;
|
||||
}
|
||||
|
||||
@@ -24,13 +24,18 @@ import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RE
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.TaskInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.IRecentsAnimationRunner;
|
||||
|
||||
import androidx.annotation.BinderThread;
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -79,6 +84,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
|
||||
private final TaskStackListenerImpl mTaskStackListener;
|
||||
private final RecentTasksImpl mImpl = new RecentTasksImpl();
|
||||
private final ActivityTaskManager mActivityTaskManager;
|
||||
private RecentsTransitionHandler mTransitionHandler = null;
|
||||
private IRecentTasksListener mListener;
|
||||
private final boolean mIsDesktopMode;
|
||||
|
||||
@@ -150,6 +156,10 @@ public class RecentTasksController implements TaskStackListenerCallback,
|
||||
mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTaskListener(this));
|
||||
}
|
||||
|
||||
void setTransitionHandler(RecentsTransitionHandler handler) {
|
||||
mTransitionHandler = handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a split pair. This call does not validate the taskIds, only that they are not the same.
|
||||
*/
|
||||
@@ -492,5 +502,18 @@ public class RecentTasksController implements TaskStackListenerCallback,
|
||||
true /* blocking */);
|
||||
return tasks[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
|
||||
IApplicationThread appThread, IRecentsAnimationRunner listener) {
|
||||
if (mController.mTransitionHandler == null) {
|
||||
Slog.e(TAG, "Used shell-transitions startRecentsTransition without"
|
||||
+ " shell-transitions");
|
||||
return;
|
||||
}
|
||||
executeRemoteCallWithTaskPermission(mController, "startRecentsTransition",
|
||||
(controller) -> controller.mTransitionHandler.startRecentsTransition(
|
||||
intent, fillIn, options, appThread, listener));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,760 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.wm.shell.recents;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;
|
||||
import static android.view.WindowManager.TRANSIT_SLEEP;
|
||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.app.IApplicationThread;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
import android.view.IRecentsAnimationController;
|
||||
import android.view.IRecentsAnimationRunner;
|
||||
import android.view.RemoteAnimationTarget;
|
||||
import android.view.SurfaceControl;
|
||||
import android.window.PictureInPictureSurfaceTransaction;
|
||||
import android.window.TaskSnapshot;
|
||||
import android.window.TransitionInfo;
|
||||
import android.window.TransitionRequestInfo;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import com.android.wm.shell.common.ShellExecutor;
|
||||
import com.android.wm.shell.sysui.ShellInit;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
import com.android.wm.shell.util.TransitionUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Handles the Recents (overview) animation. Only one of these can run at a time. A recents
|
||||
* transition must be created via {@link #startRecentsTransition}. Anything else will be ignored.
|
||||
*/
|
||||
public class RecentsTransitionHandler implements Transitions.TransitionHandler {
|
||||
private static final String TAG = "RecentsTransitionHandler";
|
||||
|
||||
private final Transitions mTransitions;
|
||||
private final ShellExecutor mExecutor;
|
||||
private IApplicationThread mAnimApp = null;
|
||||
private final ArrayList<RecentsController> mControllers = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* List of other handlers which might need to mix recents with other things. These are checked
|
||||
* in the order they are added. Ideally there should only be one.
|
||||
*/
|
||||
private final ArrayList<RecentsMixedHandler> mMixers = new ArrayList<>();
|
||||
|
||||
public RecentsTransitionHandler(ShellInit shellInit, Transitions transitions,
|
||||
@Nullable RecentTasksController recentTasksController) {
|
||||
mTransitions = transitions;
|
||||
mExecutor = transitions.getMainExecutor();
|
||||
if (!Transitions.ENABLE_SHELL_TRANSITIONS) return;
|
||||
if (recentTasksController == null) return;
|
||||
shellInit.addInitCallback(() -> {
|
||||
recentTasksController.setTransitionHandler(this);
|
||||
transitions.addHandler(this);
|
||||
}, this);
|
||||
}
|
||||
|
||||
/** Register a mixer handler. {@see RecentsMixedHandler}*/
|
||||
public void addMixer(RecentsMixedHandler mixer) {
|
||||
mMixers.add(mixer);
|
||||
}
|
||||
|
||||
/** Unregister a Mixed Handler */
|
||||
public void removeMixer(RecentsMixedHandler mixer) {
|
||||
mMixers.remove(mixer);
|
||||
}
|
||||
|
||||
void startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
|
||||
IApplicationThread appThread, IRecentsAnimationRunner listener) {
|
||||
// only care about latest one.
|
||||
mAnimApp = appThread;
|
||||
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
wct.sendPendingIntent(intent, fillIn, options);
|
||||
final RecentsController controller = new RecentsController(listener);
|
||||
RecentsMixedHandler mixer = null;
|
||||
Transitions.TransitionHandler mixedHandler = null;
|
||||
for (int i = 0; i < mMixers.size(); ++i) {
|
||||
mixedHandler = mMixers.get(i).handleRecentsRequest(wct);
|
||||
if (mixedHandler != null) {
|
||||
mixer = mMixers.get(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
final IBinder transition = mTransitions.startTransition(TRANSIT_TO_FRONT, wct,
|
||||
mixedHandler == null ? this : mixedHandler);
|
||||
if (mixer != null) {
|
||||
mixer.setRecentsTransition(transition);
|
||||
}
|
||||
if (transition == null) {
|
||||
controller.cancel();
|
||||
return;
|
||||
}
|
||||
controller.setTransition(transition);
|
||||
mControllers.add(controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowContainerTransaction handleRequest(IBinder transition,
|
||||
TransitionRequestInfo request) {
|
||||
// do not directly handle requests. Only entry point should be via startRecentsTransition
|
||||
return null;
|
||||
}
|
||||
|
||||
private int findController(IBinder transition) {
|
||||
for (int i = mControllers.size() - 1; i >= 0; --i) {
|
||||
if (mControllers.get(i).mTransition == transition) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startAnimation(IBinder transition, TransitionInfo info,
|
||||
SurfaceControl.Transaction startTransaction,
|
||||
SurfaceControl.Transaction finishTransaction,
|
||||
Transitions.TransitionFinishCallback finishCallback) {
|
||||
final int controllerIdx = findController(transition);
|
||||
if (controllerIdx < 0) return false;
|
||||
final RecentsController controller = mControllers.get(controllerIdx);
|
||||
Transitions.setRunningRemoteTransitionDelegate(mAnimApp);
|
||||
mAnimApp = null;
|
||||
if (!controller.start(info, startTransaction, finishTransaction, finishCallback)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mergeAnimation(IBinder transition, TransitionInfo info,
|
||||
SurfaceControl.Transaction t, IBinder mergeTarget,
|
||||
Transitions.TransitionFinishCallback finishCallback) {
|
||||
final int targetIdx = findController(mergeTarget);
|
||||
if (targetIdx < 0) return;
|
||||
final RecentsController controller = mControllers.get(targetIdx);
|
||||
controller.merge(info, t, finishCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionConsumed(IBinder transition, boolean aborted,
|
||||
SurfaceControl.Transaction finishTransaction) {
|
||||
final int idx = findController(transition);
|
||||
if (idx < 0) return;
|
||||
mControllers.get(idx).cancel();
|
||||
}
|
||||
|
||||
/** There is only one of these and it gets reset on finish. */
|
||||
private class RecentsController extends IRecentsAnimationController.Stub {
|
||||
private IRecentsAnimationRunner mListener;
|
||||
private IBinder.DeathRecipient mDeathHandler;
|
||||
private Transitions.TransitionFinishCallback mFinishCB = null;
|
||||
private SurfaceControl.Transaction mFinishTransaction = null;
|
||||
|
||||
/**
|
||||
* List of tasks that we are switching away from via this transition. Upon finish, these
|
||||
* pausing tasks will become invisible.
|
||||
* These need to be ordered since the order must be restored if there is no task-switch.
|
||||
*/
|
||||
private ArrayList<TaskState> mPausingTasks = null;
|
||||
|
||||
/**
|
||||
* List of tasks that we are switching to. Upon finish, these will remain visible and
|
||||
* on top.
|
||||
*/
|
||||
private ArrayList<TaskState> mOpeningTasks = null;
|
||||
|
||||
private WindowContainerToken mPipTask = null;
|
||||
private WindowContainerToken mRecentsTask = null;
|
||||
private int mRecentsTaskId = -1;
|
||||
private TransitionInfo mInfo = null;
|
||||
private boolean mOpeningSeparateHome = false;
|
||||
private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null;
|
||||
private PictureInPictureSurfaceTransaction mPipTransaction = null;
|
||||
private IBinder mTransition = null;
|
||||
private boolean mKeyguardLocked = false;
|
||||
private boolean mWillFinishToHome = false;
|
||||
|
||||
/** The animation is idle, waiting for the user to choose a task to switch to. */
|
||||
private static final int STATE_NORMAL = 0;
|
||||
|
||||
/** The user chose a new task to switch to and the animation is animating to it. */
|
||||
private static final int STATE_NEW_TASK = 1;
|
||||
|
||||
/** The latest state that the recents animation is operating in. */
|
||||
private int mState = STATE_NORMAL;
|
||||
|
||||
RecentsController(IRecentsAnimationRunner listener) {
|
||||
mListener = listener;
|
||||
mDeathHandler = () -> mExecutor.execute(() -> {
|
||||
if (mListener == null) return;
|
||||
if (mFinishCB != null) {
|
||||
finish(mWillFinishToHome, false /* leaveHint */);
|
||||
}
|
||||
});
|
||||
try {
|
||||
mListener.asBinder().linkToDeath(mDeathHandler, 0 /* flags */);
|
||||
} catch (RemoteException e) {
|
||||
mListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
void setTransition(IBinder transition) {
|
||||
mTransition = transition;
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
// restoring (to-home = false) involves submitting more WM changes, so by default, use
|
||||
// toHome = true when canceling.
|
||||
cancel(true /* toHome */);
|
||||
}
|
||||
|
||||
void cancel(boolean toHome) {
|
||||
if (mListener != null) {
|
||||
try {
|
||||
mListener.onAnimationCanceled(null, null);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Error canceling recents animation", e);
|
||||
}
|
||||
}
|
||||
if (mFinishCB != null) {
|
||||
finish(toHome, false /* userLeave */);
|
||||
} else {
|
||||
cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a cancel message to the recents animation with snapshots. Used to trigger a
|
||||
* "replace-with-screenshot" like behavior.
|
||||
*/
|
||||
private boolean sendCancelWithSnapshots() {
|
||||
int[] taskIds = null;
|
||||
TaskSnapshot[] snapshots = null;
|
||||
if (mPausingTasks.size() > 0) {
|
||||
taskIds = new int[mPausingTasks.size()];
|
||||
snapshots = new TaskSnapshot[mPausingTasks.size()];
|
||||
try {
|
||||
for (int i = 0; i < mPausingTasks.size(); ++i) {
|
||||
snapshots[i] = ActivityTaskManager.getService().takeTaskSnapshot(
|
||||
mPausingTasks.get(0).mTaskInfo.taskId);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
taskIds = null;
|
||||
snapshots = null;
|
||||
}
|
||||
}
|
||||
try {
|
||||
mListener.onAnimationCanceled(taskIds, snapshots);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Error canceling recents animation", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void cleanUp() {
|
||||
if (mListener != null && mDeathHandler != null) {
|
||||
mListener.asBinder().unlinkToDeath(mDeathHandler, 0 /* flags */);
|
||||
mDeathHandler = null;
|
||||
}
|
||||
mListener = null;
|
||||
mFinishCB = null;
|
||||
// clean-up leash surfacecontrols and anything that might reference them.
|
||||
if (mLeashMap != null) {
|
||||
for (int i = 0; i < mLeashMap.size(); ++i) {
|
||||
mLeashMap.valueAt(i).release();
|
||||
}
|
||||
mLeashMap = null;
|
||||
}
|
||||
mFinishTransaction = null;
|
||||
mPausingTasks = null;
|
||||
mOpeningTasks = null;
|
||||
mInfo = null;
|
||||
mTransition = null;
|
||||
mControllers.remove(this);
|
||||
}
|
||||
|
||||
boolean start(TransitionInfo info, SurfaceControl.Transaction t,
|
||||
SurfaceControl.Transaction finishT, Transitions.TransitionFinishCallback finishCB) {
|
||||
if (mListener == null || mTransition == null) {
|
||||
cleanUp();
|
||||
return false;
|
||||
}
|
||||
// First see if this is a valid recents transition.
|
||||
boolean hasPausingTasks = false;
|
||||
for (int i = 0; i < info.getChanges().size(); ++i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
if (TransitionUtil.isWallpaper(change)) continue;
|
||||
if (TransitionUtil.isClosingType(change.getMode())) {
|
||||
hasPausingTasks = true;
|
||||
continue;
|
||||
}
|
||||
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
|
||||
if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) {
|
||||
mRecentsTask = taskInfo.token;
|
||||
mRecentsTaskId = taskInfo.taskId;
|
||||
} else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
|
||||
mRecentsTask = taskInfo.token;
|
||||
mRecentsTaskId = taskInfo.taskId;
|
||||
}
|
||||
}
|
||||
if (mRecentsTask == null && !hasPausingTasks) {
|
||||
// Recents is already running apparently, so this is a no-op.
|
||||
Slog.e(TAG, "Tried to start recents while it is already running.");
|
||||
cleanUp();
|
||||
return false;
|
||||
}
|
||||
|
||||
mInfo = info;
|
||||
mFinishCB = finishCB;
|
||||
mFinishTransaction = finishT;
|
||||
mPausingTasks = new ArrayList<>();
|
||||
mOpeningTasks = new ArrayList<>();
|
||||
mLeashMap = new ArrayMap<>();
|
||||
mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0;
|
||||
|
||||
final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>();
|
||||
final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>();
|
||||
TransitionUtil.LeafTaskFilter leafTaskFilter = new TransitionUtil.LeafTaskFilter();
|
||||
// About layering: we divide up the "layer space" into 3 regions (each the size of
|
||||
// the change count). This lets us categorize things into above/below/between
|
||||
// while maintaining their relative ordering.
|
||||
for (int i = 0; i < info.getChanges().size(); ++i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
|
||||
if (TransitionUtil.isWallpaper(change)) {
|
||||
final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
|
||||
// wallpapers go into the "below" layer space
|
||||
info.getChanges().size() - i, info, t, mLeashMap);
|
||||
wallpapers.add(target);
|
||||
// Make all the wallpapers opaque since we want them visible from the start
|
||||
t.setAlpha(target.leash, 1);
|
||||
} else if (leafTaskFilter.test(change)) {
|
||||
// start by putting everything into the "below" layer space.
|
||||
final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
|
||||
info.getChanges().size() - i, info, t, mLeashMap);
|
||||
apps.add(target);
|
||||
if (TransitionUtil.isClosingType(change.getMode())) {
|
||||
// raise closing (pausing) task to "above" layer so it isn't covered
|
||||
t.setLayer(target.leash, info.getChanges().size() * 3 - i);
|
||||
mPausingTasks.add(new TaskState(change, target.leash));
|
||||
if (taskInfo.pictureInPictureParams != null
|
||||
&& taskInfo.pictureInPictureParams.isAutoEnterEnabled()) {
|
||||
mPipTask = taskInfo.token;
|
||||
}
|
||||
} else if (taskInfo != null
|
||||
&& taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) {
|
||||
// There's a 3p launcher, so make sure recents goes above that.
|
||||
t.setLayer(target.leash, info.getChanges().size() * 3 - i);
|
||||
} else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
|
||||
// do nothing
|
||||
} else if (TransitionUtil.isOpeningType(change.getMode())) {
|
||||
mOpeningTasks.add(new TaskState(change, target.leash));
|
||||
}
|
||||
}
|
||||
}
|
||||
t.apply();
|
||||
try {
|
||||
mListener.onAnimationStart(this,
|
||||
apps.toArray(new RemoteAnimationTarget[apps.size()]),
|
||||
wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]),
|
||||
new Rect(0, 0, 0, 0), new Rect());
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Error starting recents animation", e);
|
||||
cancel();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
void merge(TransitionInfo info, SurfaceControl.Transaction t,
|
||||
Transitions.TransitionFinishCallback finishCallback) {
|
||||
if (mFinishCB == null) {
|
||||
// This was no-op'd (likely a repeated start) and we've already sent finish.
|
||||
return;
|
||||
}
|
||||
if (info.getType() == TRANSIT_SLEEP) {
|
||||
// A sleep event means we need to stop animations immediately, so cancel here.
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
ArrayList<TransitionInfo.Change> openingTasks = null;
|
||||
ArrayList<TransitionInfo.Change> closingTasks = null;
|
||||
mOpeningSeparateHome = false;
|
||||
TransitionInfo.Change recentsOpening = null;
|
||||
boolean foundRecentsClosing = false;
|
||||
boolean hasChangingApp = false;
|
||||
final TransitionUtil.LeafTaskFilter leafTaskFilter =
|
||||
new TransitionUtil.LeafTaskFilter();
|
||||
for (int i = 0; i < info.getChanges().size(); ++i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
|
||||
final boolean isLeafTask = leafTaskFilter.test(change);
|
||||
if (TransitionUtil.isOpeningType(change.getMode())) {
|
||||
if (mRecentsTask != null && mRecentsTask.equals(change.getContainer())) {
|
||||
recentsOpening = change;
|
||||
} else if (isLeafTask) {
|
||||
if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
|
||||
// This is usually a 3p launcher
|
||||
mOpeningSeparateHome = true;
|
||||
}
|
||||
if (openingTasks == null) {
|
||||
openingTasks = new ArrayList<>();
|
||||
}
|
||||
openingTasks.add(change);
|
||||
}
|
||||
} else if (TransitionUtil.isClosingType(change.getMode())) {
|
||||
if (mRecentsTask != null && mRecentsTask.equals(change.getContainer())) {
|
||||
foundRecentsClosing = true;
|
||||
} else if (isLeafTask) {
|
||||
if (closingTasks == null) {
|
||||
closingTasks = new ArrayList<>();
|
||||
}
|
||||
closingTasks.add(change);
|
||||
}
|
||||
} else if (change.getMode() == TRANSIT_CHANGE) {
|
||||
// Finish recents animation if the display is changed, so the default
|
||||
// transition handler can play the animation such as rotation effect.
|
||||
if (change.hasFlags(TransitionInfo.FLAG_IS_DISPLAY)) {
|
||||
cancel(mWillFinishToHome);
|
||||
return;
|
||||
}
|
||||
hasChangingApp = true;
|
||||
}
|
||||
}
|
||||
if (hasChangingApp && foundRecentsClosing) {
|
||||
// This happens when a visible app is expanding (usually PiP). In this case,
|
||||
// that transition probably has a special-purpose animation, so finish recents
|
||||
// now and let it do its animation (since recents is going to be occluded).
|
||||
sendCancelWithSnapshots();
|
||||
mExecutor.executeDelayed(
|
||||
() -> finishInner(true /* toHome */, false /* userLeaveHint */), 0);
|
||||
return;
|
||||
}
|
||||
if (recentsOpening != null) {
|
||||
// the recents task re-appeared. This happens if the user gestures before the
|
||||
// task-switch (NEW_TASK) animation finishes.
|
||||
if (mState == STATE_NORMAL) {
|
||||
Slog.e(TAG, "Returning to recents while recents is already idle.");
|
||||
}
|
||||
if (closingTasks == null || closingTasks.size() == 0) {
|
||||
Slog.e(TAG, "Returning to recents without closing any opening tasks.");
|
||||
}
|
||||
// Setup may hide it initially since it doesn't know that overview was still active.
|
||||
t.show(recentsOpening.getLeash());
|
||||
t.setAlpha(recentsOpening.getLeash(), 1.f);
|
||||
mState = STATE_NORMAL;
|
||||
}
|
||||
boolean didMergeThings = false;
|
||||
if (closingTasks != null) {
|
||||
// Cancelling a task-switch. Move the tasks back to mPausing from mOpening
|
||||
for (int i = 0; i < closingTasks.size(); ++i) {
|
||||
final TransitionInfo.Change change = closingTasks.get(i);
|
||||
int openingIdx = TaskState.indexOf(mOpeningTasks, change);
|
||||
if (openingIdx < 0) {
|
||||
Slog.e(TAG, "Back to existing recents animation from an unrecognized "
|
||||
+ "task: " + change.getTaskInfo().taskId);
|
||||
continue;
|
||||
}
|
||||
mPausingTasks.add(mOpeningTasks.remove(openingIdx));
|
||||
didMergeThings = true;
|
||||
}
|
||||
}
|
||||
RemoteAnimationTarget[] appearedTargets = null;
|
||||
if (openingTasks != null && openingTasks.size() > 0) {
|
||||
// Switching to some new tasks, add to mOpening and remove from mPausing. Also,
|
||||
// enter NEW_TASK state since this will start the switch-to animation.
|
||||
final int layer = mInfo.getChanges().size() * 3;
|
||||
appearedTargets = new RemoteAnimationTarget[openingTasks.size()];
|
||||
for (int i = 0; i < openingTasks.size(); ++i) {
|
||||
final TransitionInfo.Change change = openingTasks.get(i);
|
||||
int pausingIdx = TaskState.indexOf(mPausingTasks, change);
|
||||
if (pausingIdx >= 0) {
|
||||
// Something is showing/opening a previously-pausing app.
|
||||
appearedTargets[i] = TransitionUtil.newTarget(
|
||||
change, layer, mPausingTasks.get(pausingIdx).mLeash);
|
||||
mOpeningTasks.add(mPausingTasks.remove(pausingIdx));
|
||||
// Setup hides opening tasks initially, so make it visible again (since we
|
||||
// are already showing it).
|
||||
t.show(change.getLeash());
|
||||
t.setAlpha(change.getLeash(), 1.f);
|
||||
} else {
|
||||
// We are receiving new opening tasks, so convert to onTasksAppeared.
|
||||
appearedTargets[i] = TransitionUtil.newTarget(
|
||||
change, layer, info, t, mLeashMap);
|
||||
// reparent into the original `mInfo` since that's where we are animating.
|
||||
final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo);
|
||||
t.reparent(appearedTargets[i].leash, mInfo.getRoot(rootIdx).getLeash());
|
||||
t.setLayer(appearedTargets[i].leash, layer);
|
||||
mOpeningTasks.add(new TaskState(change, appearedTargets[i].leash));
|
||||
}
|
||||
}
|
||||
didMergeThings = true;
|
||||
mState = STATE_NEW_TASK;
|
||||
}
|
||||
if (!didMergeThings) {
|
||||
// Didn't recognize anything in incoming transition so don't merge it.
|
||||
Slog.w(TAG, "Don't know how to merge this transition.");
|
||||
return;
|
||||
}
|
||||
// At this point, we are accepting the merge.
|
||||
t.apply();
|
||||
// not using the incoming anim-only surfaces
|
||||
info.releaseAnimSurfaces();
|
||||
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
|
||||
if (appearedTargets == null) return;
|
||||
try {
|
||||
mListener.onTasksAppeared(appearedTargets);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Error sending appeared tasks to recents animation", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaskSnapshot screenshotTask(int taskId) {
|
||||
try {
|
||||
return ActivityTaskManager.getService().takeTaskSnapshot(taskId);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to screenshot task", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInputConsumerEnabled(boolean enabled) {
|
||||
mExecutor.execute(() -> {
|
||||
if (mFinishCB == null || !enabled) return;
|
||||
// transient launches don't receive focus automatically. Since we are taking over
|
||||
// the gesture now, take focus explicitly.
|
||||
// This also moves recents back to top if the user gestured before a switch
|
||||
// animation finished.
|
||||
try {
|
||||
ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to set focused task", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFinishTaskTransaction(int taskId,
|
||||
PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) {
|
||||
mExecutor.execute(() -> {
|
||||
if (mFinishCB == null) return;
|
||||
mPipTransaction = finishTransaction;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressLint("NewApi")
|
||||
public void finish(boolean toHome, boolean sendUserLeaveHint) {
|
||||
mExecutor.execute(() -> finishInner(toHome, sendUserLeaveHint));
|
||||
}
|
||||
|
||||
private void finishInner(boolean toHome, boolean sendUserLeaveHint) {
|
||||
if (mFinishCB == null) {
|
||||
Slog.e(TAG, "Duplicate call to finish");
|
||||
return;
|
||||
}
|
||||
final Transitions.TransitionFinishCallback finishCB = mFinishCB;
|
||||
mFinishCB = null;
|
||||
|
||||
final SurfaceControl.Transaction t = mFinishTransaction;
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
|
||||
if (mKeyguardLocked && mRecentsTask != null) {
|
||||
if (toHome) wct.reorder(mRecentsTask, true /* toTop */);
|
||||
else wct.restoreTransientOrder(mRecentsTask);
|
||||
}
|
||||
if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) {
|
||||
// The gesture is returning to the pausing-task(s) rather than continuing with
|
||||
// recents, so end the transition by moving the app back to the top (and also
|
||||
// re-showing it's task).
|
||||
for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
|
||||
// reverse order so that index 0 ends up on top
|
||||
wct.reorder(mPausingTasks.get(i).mToken, true /* onTop */);
|
||||
t.show(mPausingTasks.get(i).mTaskSurface);
|
||||
}
|
||||
if (!mKeyguardLocked && mRecentsTask != null) {
|
||||
wct.restoreTransientOrder(mRecentsTask);
|
||||
}
|
||||
} else if (toHome && mOpeningSeparateHome && mPausingTasks != null) {
|
||||
// Special situation where 3p launcher was changed during recents (this happens
|
||||
// during tapltests...). Here we get both "return to home" AND "home opening".
|
||||
// This is basically going home, but we have to restore the recents and home order.
|
||||
for (int i = 0; i < mOpeningTasks.size(); ++i) {
|
||||
final TaskState state = mOpeningTasks.get(i);
|
||||
if (state.mTaskInfo.topActivityType == ACTIVITY_TYPE_HOME) {
|
||||
// Make sure it is on top.
|
||||
wct.reorder(state.mToken, true /* onTop */);
|
||||
}
|
||||
t.show(state.mTaskSurface);
|
||||
}
|
||||
for (int i = mPausingTasks.size() - 1; i >= 0; --i) {
|
||||
t.hide(mPausingTasks.get(i).mTaskSurface);
|
||||
}
|
||||
if (!mKeyguardLocked && mRecentsTask != null) {
|
||||
wct.restoreTransientOrder(mRecentsTask);
|
||||
}
|
||||
} else {
|
||||
// The general case: committing to recents, going home, or switching tasks.
|
||||
for (int i = 0; i < mOpeningTasks.size(); ++i) {
|
||||
t.show(mOpeningTasks.get(i).mTaskSurface);
|
||||
}
|
||||
for (int i = 0; i < mPausingTasks.size(); ++i) {
|
||||
if (!sendUserLeaveHint) {
|
||||
// This means recents is not *actually* finishing, so of course we gotta
|
||||
// do special stuff in WMCore to accommodate.
|
||||
wct.setDoNotPip(mPausingTasks.get(i).mToken);
|
||||
}
|
||||
// Since we will reparent out of the leashes, pre-emptively hide the child
|
||||
// surface to match the leash. Otherwise, there will be a flicker before the
|
||||
// visibility gets committed in Core when using split-screen (in splitscreen,
|
||||
// the leaf-tasks are not "independent" so aren't hidden by normal setup).
|
||||
t.hide(mPausingTasks.get(i).mTaskSurface);
|
||||
}
|
||||
if (mPipTask != null && mPipTransaction != null && sendUserLeaveHint) {
|
||||
t.show(mInfo.getChange(mPipTask).getLeash());
|
||||
PictureInPictureSurfaceTransaction.apply(mPipTransaction,
|
||||
mInfo.getChange(mPipTask).getLeash(), t);
|
||||
mPipTask = null;
|
||||
mPipTransaction = null;
|
||||
}
|
||||
}
|
||||
cleanUp();
|
||||
finishCB.onTransitionFinished(wct.isEmpty() ? null : wct, null /* wctCB */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanupScreenshot() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWillFinishToHome(boolean willFinishToHome) {
|
||||
mExecutor.execute(() -> {
|
||||
mWillFinishToHome = willFinishToHome;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IRecentsAnimationController#removeTask
|
||||
*/
|
||||
@Override
|
||||
public boolean removeTask(int taskId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IRecentsAnimationController#detachNavigationBarFromApp
|
||||
*/
|
||||
@Override
|
||||
public void detachNavigationBarFromApp(boolean moveHomeToTop) {
|
||||
mExecutor.execute(() -> {
|
||||
if (mTransition == null) return;
|
||||
try {
|
||||
ActivityTaskManager.getService().detachNavigationBarFromApp(mTransition);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Failed to detach the navigation bar from app", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IRecentsAnimationController#animateNavigationBarToApp(long)
|
||||
*/
|
||||
@Override
|
||||
public void animateNavigationBarToApp(long duration) {
|
||||
}
|
||||
};
|
||||
|
||||
/** Utility class to track the state of a task as-seen by recents. */
|
||||
private static class TaskState {
|
||||
WindowContainerToken mToken;
|
||||
ActivityManager.RunningTaskInfo mTaskInfo;
|
||||
|
||||
/** The surface/leash of the task provided by Core. */
|
||||
SurfaceControl mTaskSurface;
|
||||
|
||||
/** The (local) animation-leash created for this task. */
|
||||
SurfaceControl mLeash;
|
||||
|
||||
TaskState(TransitionInfo.Change change, SurfaceControl leash) {
|
||||
mToken = change.getContainer();
|
||||
mTaskInfo = change.getTaskInfo();
|
||||
mTaskSurface = change.getLeash();
|
||||
mLeash = leash;
|
||||
}
|
||||
|
||||
static int indexOf(ArrayList<TaskState> list, TransitionInfo.Change change) {
|
||||
for (int i = list.size() - 1; i >= 0; --i) {
|
||||
if (list.get(i).mToken.equals(change.getContainer())) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "" + mToken + " : " + mLeash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for a mixed handler to receive information about recents requests (since these
|
||||
* come into this handler directly vs from WMCore request).
|
||||
*/
|
||||
public interface RecentsMixedHandler {
|
||||
/**
|
||||
* Called when a recents request comes in. The handler can add operations to outWCT. If
|
||||
* the handler wants to "accept" the transition, it should return itself; otherwise, it
|
||||
* should return `null`.
|
||||
*
|
||||
* If a mixed-handler accepts this recents, it will be the de-facto handler for this
|
||||
* transition and is required to call the associated {@link #startAnimation},
|
||||
* {@link #mergeAnimation}, and {@link #onTransitionConsumed} methods.
|
||||
*/
|
||||
Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT);
|
||||
|
||||
/**
|
||||
* Reports the transition token associated with the accepted recents request. If there was
|
||||
* a problem starting the request, this will be called with `null`.
|
||||
*/
|
||||
void setRecentsTransition(@Nullable IBinder transition);
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.pip.PipTransitionController;
|
||||
import com.android.wm.shell.pip.phone.PipTouchHandler;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
import com.android.wm.shell.recents.RecentsTransitionHandler;
|
||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||
import com.android.wm.shell.splitscreen.StageCoordinator;
|
||||
import com.android.wm.shell.sysui.ShellInit;
|
||||
@@ -55,10 +56,12 @@ import java.util.Optional;
|
||||
* A handler for dealing with transitions involving multiple other handlers. For example: an
|
||||
* activity in split-screen going into PiP.
|
||||
*/
|
||||
public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||
public class DefaultMixedHandler implements Transitions.TransitionHandler,
|
||||
RecentsTransitionHandler.RecentsMixedHandler {
|
||||
|
||||
private final Transitions mPlayer;
|
||||
private PipTransitionController mPipHandler;
|
||||
private RecentsTransitionHandler mRecentsHandler;
|
||||
private StageCoordinator mSplitHandler;
|
||||
|
||||
private static class MixedTransition {
|
||||
@@ -122,7 +125,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||
|
||||
public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player,
|
||||
Optional<SplitScreenController> splitScreenControllerOptional,
|
||||
Optional<PipTouchHandler> pipTouchHandlerOptional) {
|
||||
Optional<PipTouchHandler> pipTouchHandlerOptional,
|
||||
Optional<RecentsTransitionHandler> recentsHandlerOptional) {
|
||||
mPlayer = player;
|
||||
if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent()
|
||||
&& splitScreenControllerOptional.isPresent()) {
|
||||
@@ -134,6 +138,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||
if (mSplitHandler != null) {
|
||||
mSplitHandler.setMixedHandler(this);
|
||||
}
|
||||
mRecentsHandler = recentsHandlerOptional.orElse(null);
|
||||
if (mRecentsHandler != null) {
|
||||
mRecentsHandler.addMixer(this);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
@@ -200,6 +208,29 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Transitions.TransitionHandler handleRecentsRequest(WindowContainerTransaction outWCT) {
|
||||
if (mRecentsHandler != null && mSplitHandler.isSplitActive()) {
|
||||
return this;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecentsTransition(IBinder transition) {
|
||||
if (mSplitHandler.isSplitActive()) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a recents request while "
|
||||
+ "Split-Screen is active, so treat it as Mixed.");
|
||||
final MixedTransition mixed = new MixedTransition(
|
||||
MixedTransition.TYPE_RECENTS_DURING_SPLIT, transition);
|
||||
mixed.mLeftoversHandler = mRecentsHandler;
|
||||
mActiveTransitions.add(mixed);
|
||||
} else {
|
||||
throw new IllegalStateException("Accepted a recents transition but don't know how to"
|
||||
+ " handle it");
|
||||
}
|
||||
}
|
||||
|
||||
private TransitionInfo subCopy(@NonNull TransitionInfo info,
|
||||
@WindowManager.TransitionType int newType, boolean withChanges) {
|
||||
final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0);
|
||||
@@ -561,6 +592,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
|
||||
mPipHandler.onTransitionConsumed(transition, aborted, finishT);
|
||||
} else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
|
||||
mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
|
||||
} else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) {
|
||||
mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,8 +50,6 @@ import android.provider.Settings;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
import android.view.RemoteAnimationAdapter;
|
||||
import android.view.WindowManager;
|
||||
import android.window.RemoteTransition;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
@@ -562,9 +560,8 @@ public class ActivityStartController {
|
||||
final Task rootTask = mService.mRootWindowContainer.getDefaultTaskDisplayArea()
|
||||
.getRootTask(WINDOWING_MODE_UNDEFINED, activityType);
|
||||
if (rootTask == null) return false;
|
||||
final RemoteTransition remote = options.getRemoteTransition();
|
||||
final ActivityRecord r = rootTask.topRunningActivity();
|
||||
if (r == null || r.isVisibleRequested() || !r.attachedToProcess() || remote == null
|
||||
if (r == null || r.isVisibleRequested() || !r.attachedToProcess()
|
||||
|| !r.mActivityComponent.equals(intent.getComponent())
|
||||
// Recents keeps invisible while device is locked.
|
||||
|| r.mDisplayContent.isKeyguardLocked()) {
|
||||
@@ -573,47 +570,13 @@ public class ActivityStartController {
|
||||
mService.mRootWindowContainer.startPowerModeLaunchIfNeeded(true /* forceSend */, r);
|
||||
final ActivityMetricsLogger.LaunchingState launchingState =
|
||||
mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent);
|
||||
final Transition transition = new Transition(WindowManager.TRANSIT_TO_FRONT,
|
||||
0 /* flags */, r.mTransitionController, mService.mWindowManager.mSyncEngine);
|
||||
if (r.mTransitionController.isCollecting()) {
|
||||
// Special case: we are entering recents while an existing transition is running. In
|
||||
// this case, we know it's safe to "defer" the activity launch, so lets do so now so
|
||||
// that it can get its own transition and thus update launcher correctly.
|
||||
mService.mWindowManager.mSyncEngine.queueSyncSet(
|
||||
() -> {
|
||||
if (r.isAttached()) {
|
||||
r.mTransitionController.moveToCollecting(transition);
|
||||
}
|
||||
},
|
||||
() -> {
|
||||
if (r.isAttached() && transition.isCollecting()) {
|
||||
startExistingRecentsIfPossibleInner(options, r, rootTask,
|
||||
launchingState, remote, transition);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
r.mTransitionController.moveToCollecting(transition);
|
||||
startExistingRecentsIfPossibleInner(options, r, rootTask, launchingState, remote,
|
||||
transition);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void startExistingRecentsIfPossibleInner(ActivityOptions options, ActivityRecord r,
|
||||
Task rootTask, ActivityMetricsLogger.LaunchingState launchingState,
|
||||
RemoteTransition remoteTransition, Transition transition) {
|
||||
final Task task = r.getTask();
|
||||
mService.deferWindowLayout();
|
||||
try {
|
||||
final TransitionController controller = r.mTransitionController;
|
||||
if (controller.getTransitionPlayer() != null) {
|
||||
controller.requestStartTransition(transition, task, remoteTransition,
|
||||
null /* displayChange */);
|
||||
controller.collect(task);
|
||||
controller.setTransientLaunch(r, TaskDisplayArea.getRootTaskAbove(rootTask));
|
||||
} else {
|
||||
// The transition player might be died when executing the queued transition.
|
||||
transition.abort();
|
||||
}
|
||||
task.moveToFront("startExistingRecents");
|
||||
task.mInResumeTopActivity = true;
|
||||
@@ -624,6 +587,7 @@ public class ActivityStartController {
|
||||
task.mInResumeTopActivity = false;
|
||||
mService.continueWindowLayout();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void registerRemoteAnimationForNextActivityStart(String packageName,
|
||||
|
||||
@@ -1582,19 +1582,19 @@ class ActivityStarter {
|
||||
}
|
||||
}
|
||||
if (isTransientLaunch) {
|
||||
if (forceTransientTransition && newTransition != null) {
|
||||
newTransition.collect(mLastStartActivityRecord);
|
||||
newTransition.collect(mPriorAboveTask);
|
||||
if (forceTransientTransition) {
|
||||
transitionController.collect(mLastStartActivityRecord);
|
||||
transitionController.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) {
|
||||
if (forceTransientTransition) {
|
||||
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 */);
|
||||
transitionController.setReady(dc, true /* ready */);
|
||||
}
|
||||
}
|
||||
if (!userLeaving) {
|
||||
|
||||
@@ -1240,25 +1240,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
|
||||
ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
|
||||
|
||||
final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions);
|
||||
// A quick path (skip general intent/task resolving) to start recents animation if the
|
||||
// recents (or home) activity is available in background.
|
||||
if (opts != null && opts.getOriginalOptions().getTransientLaunch()
|
||||
&& isCallerRecents(Binder.getCallingUid())) {
|
||||
final long origId = Binder.clearCallingIdentity();
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "startExistingRecents");
|
||||
if (mActivityStartController.startExistingRecentsIfPossible(
|
||||
intent, opts.getOriginalOptions())) {
|
||||
return ActivityManager.START_TASK_TO_FRONT;
|
||||
}
|
||||
// Else follow the standard launch procedure.
|
||||
}
|
||||
} finally {
|
||||
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
||||
Binder.restoreCallingIdentity(origId);
|
||||
}
|
||||
}
|
||||
|
||||
assertPackageMatchesCallingUid(callingPackage);
|
||||
enforceNotIsolatedCaller("startActivityAsUser");
|
||||
@@ -5718,6 +5699,23 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
|
||||
boolean validateIncomingUser, PendingIntentRecord originatingPendingIntent,
|
||||
BackgroundStartPrivileges backgroundStartPrivileges) {
|
||||
assertPackageMatchesCallingUid(callingPackage);
|
||||
// A quick path (skip general intent/task resolving) to start recents animation if the
|
||||
// recents (or home) activity is available in background.
|
||||
if (options != null && options.getOriginalOptions() != null
|
||||
&& options.getOriginalOptions().getTransientLaunch() && isCallerRecents(uid)) {
|
||||
try {
|
||||
synchronized (mGlobalLock) {
|
||||
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "startExistingRecents");
|
||||
if (mActivityStartController.startExistingRecentsIfPossible(
|
||||
intent, options.getOriginalOptions())) {
|
||||
return ActivityManager.START_TASK_TO_FRONT;
|
||||
}
|
||||
// Else follow the standard launch procedure.
|
||||
}
|
||||
} finally {
|
||||
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
|
||||
}
|
||||
}
|
||||
return getActivityStartController().startActivityInPackage(uid, realCallingPid,
|
||||
realCallingUid, callingPackage, callingFeatureId, intent, resolvedType,
|
||||
resultTo, resultWho, requestCode, startFlags, options, userId, inTask,
|
||||
|
||||
Reference in New Issue
Block a user