Animate launching desktop from overview
Create a new controller that handles launching desktop from recents. It
creates a remote transition runner. Then notifies wmshell to show
desktop apps and use this remote runner to run the animation.
TODO:
- there are flickers in the animation
Bug: 297590571
Test: open an app on desktop, swipe up to overview, click on desktop
tile
open multiple apps on desktop, swipe up to overview, click on
desktop tile
Change-Id: Ie8c9189bace77ba80e6be90222b3f786dfe5bb15
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
new file mode 100644
index 0000000..6fe007c
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.launcher3.desktop
+
+import android.app.IApplicationThread
+import android.os.IBinder
+import android.os.RemoteException
+import android.util.Log
+import android.view.SurfaceControl
+import android.window.IRemoteTransition
+import android.window.IRemoteTransitionFinishedCallback
+import android.window.RemoteTransition
+import android.window.TransitionInfo
+import com.android.launcher3.statehandlers.DepthController
+import com.android.launcher3.statemanager.StateManager
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
+import com.android.quickstep.SystemUiProxy
+import com.android.quickstep.TaskViewUtils
+import com.android.quickstep.views.DesktopTaskView
+import java.util.function.Consumer
+
+/** Manage recents related operations with desktop tasks */
+class DesktopRecentsTransitionController(
+ private val stateManager: StateManager<*>,
+ private val systemUiProxy: SystemUiProxy,
+ private val appThread: IApplicationThread,
+ private val depthController: DepthController?
+) {
+
+ /** Launch desktop tasks from recents view */
+ fun launchDesktopFromRecents(
+ desktopTaskView: DesktopTaskView,
+ callback: Consumer<Boolean>? = null
+ ) {
+ val animRunner =
+ RemoteDesktopLaunchTransitionRunner(
+ desktopTaskView,
+ stateManager,
+ depthController,
+ callback
+ )
+ val transition = RemoteTransition(animRunner, appThread, "RecentsToDesktop")
+ systemUiProxy.showDesktopApps(desktopTaskView.display.displayId, transition)
+ }
+
+ private class RemoteDesktopLaunchTransitionRunner(
+ private val desktopTaskView: DesktopTaskView,
+ private val stateManager: StateManager<*>,
+ private val depthController: DepthController?,
+ private val successCallback: Consumer<Boolean>?
+ ) : IRemoteTransition.Stub() {
+
+ override fun startAnimation(
+ token: IBinder,
+ info: TransitionInfo,
+ t: SurfaceControl.Transaction,
+ finishCallback: IRemoteTransitionFinishedCallback
+ ) {
+ val errorHandlingFinishCallback = Runnable {
+ try {
+ finishCallback.onTransitionFinished(null /* wct */, null /* sct */)
+ } catch (e: RemoteException) {
+ Log.e(TAG, "Failed to call finish callback for desktop recents animation", e)
+ }
+ }
+
+ MAIN_EXECUTOR.execute {
+ TaskViewUtils.composeRecentsDesktopLaunchAnimator(
+ desktopTaskView,
+ stateManager,
+ depthController,
+ info,
+ t
+ ) {
+ errorHandlingFinishCallback.run()
+ successCallback?.accept(true)
+ }
+ }
+ }
+
+ override fun mergeAnimation(
+ transition: IBinder,
+ info: TransitionInfo,
+ t: SurfaceControl.Transaction,
+ mergeTarget: IBinder,
+ finishCallback: IRemoteTransitionFinishedCallback
+ ) {}
+ }
+
+ companion object {
+ const val TAG = "DesktopRecentsTransitionController"
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 1756117..3a047c0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -106,6 +106,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.appprediction.PredictionRowView;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
@@ -225,6 +226,9 @@
*/
private PendingSplitSelectInfo mPendingSplitSelectInfo = null;
+ @Nullable
+ private DesktopRecentsTransitionController mDesktopRecentsTransitionController;
+
private SafeCloseable mViewCapture;
private boolean mEnableWidgetDepth;
@@ -235,12 +239,19 @@
mActionsView = findViewById(R.id.overview_actions_view);
RecentsView overviewPanel = getOverviewPanel();
+ SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this);
mSplitSelectStateController =
new SplitSelectStateController(this, mHandler, getStateManager(),
getDepthController(), getStatsLogManager(),
- SystemUiProxy.INSTANCE.get(this), RecentsModel.INSTANCE.get(this),
+ systemUiProxy, RecentsModel.INSTANCE.get(this),
() -> onStateBack());
- overviewPanel.init(mActionsView, mSplitSelectStateController);
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
+ getStateManager(), systemUiProxy, getIApplicationThread(),
+ getDepthController());
+ }
+ overviewPanel.init(mActionsView, mSplitSelectStateController,
+ mDesktopRecentsTransitionController);
mSplitWithKeyboardShortcutController = new SplitWithKeyboardShortcutController(this,
mSplitSelectStateController);
mSplitToWorkspaceController = new SplitToWorkspaceController(this,
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index d66421f..3c7fbf0 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -60,6 +60,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
+import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
@@ -82,6 +83,7 @@
import com.android.quickstep.util.RecentsAtomicAnimationFactory;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.TISBindHelper;
+import com.android.quickstep.views.DesktopTaskView;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -121,6 +123,8 @@
private final Handler mHandler = new Handler();
private final Runnable mAnimationStartTimeoutRunnable = this::onAnimationStartTimeout;
private SplitSelectStateController mSplitSelectStateController;
+ @Nullable
+ private DesktopRecentsTransitionController mDesktopRecentsTransitionController;
/**
* Init drag layer and overview panel views.
@@ -133,13 +137,21 @@
mFallbackRecentsView = findViewById(R.id.overview_panel);
mActionsView = findViewById(R.id.overview_actions_view);
getRootView().getSysUiScrim().getSysUIProgress().updateValue(0);
+ SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.get(this);
mSplitSelectStateController =
new SplitSelectStateController(this, mHandler, getStateManager(),
null /* depthController */, getStatsLogManager(),
- SystemUiProxy.INSTANCE.get(this), RecentsModel.INSTANCE.get(this),
+ systemUiProxy, RecentsModel.INSTANCE.get(this),
null /*activityBackCallback*/);
mDragLayer.recreateControllers();
- mFallbackRecentsView.init(mActionsView, mSplitSelectStateController);
+ if (DesktopTaskView.DESKTOP_MODE_SUPPORTED) {
+ mDesktopRecentsTransitionController = new DesktopRecentsTransitionController(
+ getStateManager(), systemUiProxy, getIApplicationThread(),
+ null /* depthController */
+ );
+ }
+ mFallbackRecentsView.init(mActionsView, mSplitSelectStateController,
+ mDesktopRecentsTransitionController);
mTISBindHelper = new TISBindHelper(this, this::onTISConnected);
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 5da41ae..cf6fc98 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -1264,10 +1264,10 @@
//
/** Call shell to show all apps active on the desktop */
- public void showDesktopApps(int displayId) {
+ public void showDesktopApps(int displayId, @Nullable RemoteTransition transition) {
if (mDesktopMode != null) {
try {
- mDesktopMode.showDesktopApps(displayId);
+ mDesktopMode.showDesktopApps(displayId, transition);
} catch (RemoteException e) {
Log.w(TAG, "Failed call showDesktopApps", e);
}
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 97e484a..484628b 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -612,6 +612,42 @@
animator.start();
}
+ /**
+ * Start recents to desktop animation
+ */
+ public static void composeRecentsDesktopLaunchAnimator(
+ @NonNull DesktopTaskView launchingTaskView,
+ @NonNull StateManager stateManager, @Nullable DepthController depthController,
+ @NonNull TransitionInfo transitionInfo,
+ SurfaceControl.Transaction t, @NonNull Runnable finishCallback) {
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ t.apply();
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finishCallback.run();
+ }
+ });
+
+ final RemoteAnimationTarget[] apps = RemoteAnimationTargetCompat.wrapApps(
+ transitionInfo, t, null /* leashMap */);
+ final RemoteAnimationTarget[] wallpaper = RemoteAnimationTargetCompat.wrapNonApps(
+ transitionInfo, true /* wallpapers */, t, null /* leashMap */);
+ final RemoteAnimationTarget[] nonApps = RemoteAnimationTargetCompat.wrapNonApps(
+ transitionInfo, false /* wallpapers */, t, null /* leashMap */);
+
+ composeRecentsLaunchAnimator(animatorSet, launchingTaskView, apps, wallpaper, nonApps,
+ true /* launcherClosing */, stateManager, launchingTaskView.getRecentsView(),
+ depthController);
+
+ animatorSet.start();
+ }
+
public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
@NonNull RemoteAnimationTarget[] appTargets,
@NonNull RemoteAnimationTarget[] wallpaperTargets,
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 95d88cd..059b0ce 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -36,6 +36,7 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.SplitConfigurationOptions;
@@ -73,8 +74,9 @@
}
@Override
- public void init(OverviewActionsView actionsView, SplitSelectStateController splitController) {
- super.init(actionsView, splitController);
+ public void init(OverviewActionsView actionsView, SplitSelectStateController splitController,
+ @Nullable DesktopRecentsTransitionController desktopRecentsTransitionController) {
+ super.init(actionsView, splitController, desktopRecentsTransitionController);
setOverviewStateEnabled(true);
setOverlayEnabled(true);
}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
index 5135345..32d6582 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.java
@@ -44,10 +44,10 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.util.RunnableList;
import com.android.quickstep.RecentsModel;
-import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.RecentsOrientedState;
@@ -331,17 +331,21 @@
return false;
}
- @Override
- public RunnableList launchTasks() {
- SystemUiProxy.INSTANCE.get(getContext()).showDesktopApps(mActivity.getDisplayId());
- Launcher.getLauncher(mActivity).getStateManager().goToState(NORMAL, false /* animated */);
- return null;
- }
-
@Nullable
@Override
public RunnableList launchTaskAnimated() {
- return launchTasks();
+ RunnableList endCallback = new RunnableList();
+ endCallback.add(() -> Launcher.getLauncher(mActivity).getStateManager().goToState(NORMAL));
+
+ DesktopRecentsTransitionController recentsController =
+ getRecentsView().getDesktopRecentsController();
+ if (recentsController != null) {
+ recentsController.launchDesktopFromRecents(this, success -> {
+ endCallback.executeAllAndDestroy();
+ });
+ }
+
+ return endCallback;
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 437009f..2cc4f40 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -38,6 +38,7 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.LauncherState;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
@@ -77,8 +78,9 @@
@Override
public void init(OverviewActionsView actionsView,
- SplitSelectStateController splitPlaceholderView) {
- super.init(actionsView, splitPlaceholderView);
+ SplitSelectStateController splitPlaceholderView,
+ @Nullable DesktopRecentsTransitionController desktopRecentsTransitionController) {
+ super.init(actionsView, splitPlaceholderView, desktopRecentsTransitionController);
setContentAlpha(0);
}
@@ -278,7 +280,8 @@
desktopVisibilityController.setRecentsGestureEnd(endTarget);
}
if (showDesktopApps) {
- SystemUiProxy.INSTANCE.get(mActivity).showDesktopApps(mActivity.getDisplayId());
+ SystemUiProxy.INSTANCE.get(mActivity).showDesktopApps(mActivity.getDisplayId(),
+ null /* transition */);
}
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 9b41ccd..8888c0d 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -143,6 +143,7 @@
import com.android.launcher3.anim.SpringProperty;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.desktop.DesktopRecentsTransitionController;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.statehandlers.DepthController;
@@ -722,6 +723,9 @@
private ObjectAnimator mActionsViewAlphaAnimator;
private float mActionsViewAlphaAnimatorFinalValue;
+ @Nullable
+ private DesktopRecentsTransitionController mDesktopRecentsTransitionController;
+
/**
* Keeps track of the desktop task. Optional and only present when the feature flag is enabled.
*/
@@ -1042,10 +1046,12 @@
updateTaskStackListenerState();
}
- public void init(OverviewActionsView actionsView, SplitSelectStateController splitController) {
+ public void init(OverviewActionsView actionsView, SplitSelectStateController splitController,
+ @Nullable DesktopRecentsTransitionController desktopRecentsTransitionController) {
mActionsView = actionsView;
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
mSplitSelectStateController = splitController;
+ mDesktopRecentsTransitionController = desktopRecentsTransitionController;
}
public SplitSelectStateController getSplitSelectController() {
@@ -5787,6 +5793,11 @@
return null;
}
+ @Nullable
+ protected DesktopRecentsTransitionController getDesktopRecentsController() {
+ return mDesktopRecentsTransitionController;
+ }
+
/** Enables or disables modal state for RecentsView */
public abstract void setModalStateEnabled(int taskId, boolean animate);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index f84b549..d5b43a8 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -107,7 +107,6 @@
import com.android.quickstep.util.SplitSelectStateController;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.quickstep.util.TaskRemovedDuringLaunchListener;
-import com.android.quickstep.util.TransformParams;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -975,19 +974,17 @@
if (remoteTargetHandles.length == 1) {
targets = remoteTargetHandles[0].getTransformParams().getTargetSet();
} else {
- TransformParams topLeftParams = remoteTargetHandles[0].getTransformParams();
- TransformParams rightBottomParams = remoteTargetHandles[1].getTransformParams();
- RemoteAnimationTarget[] apps = Stream.concat(
- Arrays.stream(topLeftParams.getTargetSet().apps),
- Arrays.stream(rightBottomParams.getTargetSet().apps))
+ RemoteAnimationTarget[] apps = Arrays.stream(remoteTargetHandles)
+ .flatMap(handle -> Stream.of(
+ handle.getTransformParams().getTargetSet().apps))
.toArray(RemoteAnimationTarget[]::new);
- RemoteAnimationTarget[] wallpapers = Stream.concat(
- Arrays.stream(topLeftParams.getTargetSet().wallpapers),
- Arrays.stream(rightBottomParams.getTargetSet().wallpapers))
+ RemoteAnimationTarget[] wallpapers = Arrays.stream(remoteTargetHandles)
+ .flatMap(handle -> Stream.of(
+ handle.getTransformParams().getTargetSet().wallpapers))
.toArray(RemoteAnimationTarget[]::new);
targets = new RemoteAnimationTargets(apps, wallpapers,
- topLeftParams.getTargetSet().nonApps,
- topLeftParams.getTargetSet().targetMode);
+ remoteTargetHandles[0].getTransformParams().getTargetSet().nonApps,
+ remoteTargetHandles[0].getTransformParams().getTargetSet().targetMode);
}
if (targets == null) {
// If the recents animation is cancelled somehow between the parent if block and