• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.quickstep;
17 
18 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
19 
20 import static com.android.launcher3.Flags.enableHandleDelayedGestureCallbacks;
21 import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation;
22 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
23 import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
24 import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
25 import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
26 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
27 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
28 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
29 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
30 
31 import android.app.ActivityManager;
32 import android.app.ActivityOptions;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.os.SystemProperties;
36 import android.util.Log;
37 import android.view.RemoteAnimationTarget;
38 import android.window.TransitionInfo;
39 
40 import androidx.annotation.NonNull;
41 import androidx.annotation.Nullable;
42 import androidx.annotation.UiThread;
43 
44 import com.android.internal.util.ArrayUtils;
45 import com.android.launcher3.Utilities;
46 import com.android.launcher3.config.FeatureFlags;
47 import com.android.launcher3.taskbar.TaskbarUIController;
48 import com.android.launcher3.util.DisplayController;
49 import com.android.quickstep.fallback.window.RecentsDisplayModel;
50 import com.android.quickstep.fallback.window.RecentsWindowFlags;
51 import com.android.quickstep.fallback.window.RecentsWindowManager;
52 import com.android.quickstep.util.ActiveGestureProtoLogProxy;
53 import com.android.quickstep.util.SystemUiFlagUtils;
54 import com.android.quickstep.views.RecentsView;
55 import com.android.systemui.shared.recents.model.ThumbnailData;
56 import com.android.systemui.shared.system.QuickStepContract;
57 import com.android.systemui.shared.system.TaskStackChangeListener;
58 import com.android.systemui.shared.system.TaskStackChangeListeners;
59 
60 import java.io.PrintWriter;
61 import java.util.HashMap;
62 import java.util.Locale;
63 
64 public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
65     public static final boolean SHELL_TRANSITIONS_ROTATION =
66             SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
67     private final Context mCtx;
68     private RecentsAnimationController mController;
69     private RecentsAnimationCallbacks mCallbacks;
70     private RecentsAnimationTargets mTargets;
71     private TransitionInfo mTransitionInfo;
72     private RecentsAnimationDeviceState mDeviceState;
73 
74     // Temporary until we can hook into gesture state events
75     private GestureState mLastGestureState;
76     private RemoteAnimationTarget[] mLastAppearedTaskTargets;
77     private Runnable mLiveTileCleanUpHandler;
78 
79     private boolean mRecentsAnimationStartPending = false;
80     private boolean mShouldIgnoreMotionEvents = false;
81     private final int mDisplayId;
82 
83     private final TaskStackChangeListener mLiveTileRestartListener = new TaskStackChangeListener() {
84         @Override
85         public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
86                 boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
87             if (mLastGestureState == null) {
88                 TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
89                         mLiveTileRestartListener);
90                 return;
91             }
92             BaseContainerInterface containerInterface = mLastGestureState.getContainerInterface();
93             if (containerInterface.isInLiveTileMode()
94                     && containerInterface.getCreatedContainer() != null) {
95                 RecentsView recentsView = containerInterface.getCreatedContainer()
96                         .getOverviewPanel();
97                 if (recentsView != null) {
98                     recentsView.launchSideTaskInLiveTileModeForRestartedApp(task.taskId);
99                     TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
100                             mLiveTileRestartListener);
101                 }
102             }
103         }
104     };
105 
TaskAnimationManager(Context ctx, RecentsAnimationDeviceState deviceState, int displayId)106     public TaskAnimationManager(Context ctx, RecentsAnimationDeviceState deviceState,
107             int displayId) {
108         mCtx = ctx;
109         mDeviceState = deviceState;
110         mDisplayId = displayId;
111     }
112 
getSystemUiProxy()113     SystemUiProxy getSystemUiProxy() {
114         return SystemUiProxy.INSTANCE.get(mCtx);
115     }
116 
shouldIgnoreMotionEvents()117     boolean shouldIgnoreMotionEvents() {
118         return mShouldIgnoreMotionEvents;
119     }
120 
notifyNewGestureStart()121     void notifyNewGestureStart() {
122         // If mRecentsAnimationStartPending is true at the beginning of a gesture, block all motion
123         // events for this new gesture so that this new gesture does not interfere with the
124         // previously-requested recents animation. Otherwise, clean up mShouldIgnoreMotionEvents.
125         // NOTE: this can lead to misleading logs
126         mShouldIgnoreMotionEvents = mRecentsAnimationStartPending;
127     }
128 
129     /**
130      * Starts a new recents animation for the activity with the given {@param intent}.
131      */
132     @UiThread
startRecentsAnimation(@onNull GestureState gestureState, Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener)133     public RecentsAnimationCallbacks startRecentsAnimation(@NonNull GestureState gestureState,
134             Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
135         ActiveGestureProtoLogProxy.logStartRecentsAnimation();
136         // Check displayId
137         if (mDisplayId != gestureState.getDisplayId()) {
138             String msg = String.format(Locale.ENGLISH,
139                     "Constructor displayId %d does not equal gestureState display id %d",
140                     mDisplayId, gestureState.getDisplayId());
141             if (FeatureFlags.IS_STUDIO_BUILD) {
142                 throw new IllegalArgumentException(msg);
143             } else {
144                 Log.e("TaskAnimationManager", msg, new Exception());
145             }
146         }
147         // Notify if recents animation is still running
148         if (mController != null) {
149             String msg = "New recents animation started before old animation completed";
150             if (FeatureFlags.IS_STUDIO_BUILD) {
151                 throw new IllegalArgumentException(msg);
152             } else {
153                 Log.e("TaskAnimationManager", msg, new Exception());
154             }
155         }
156         // But force-finish it anyways
157         finishRunningRecentsAnimation(false /* toHome */, true /* forceFinish */,
158                 null /* forceFinishCb */);
159 
160         if (mCallbacks != null) {
161             // If mCallbacks still != null, that means we are getting this startRecentsAnimation()
162             // before the previous one got onRecentsAnimationStart(). In that case, cleanup the
163             // previous animation so it doesn't mess up/listen to state changes in this animation.
164             cleanUpRecentsAnimation(mCallbacks);
165         }
166 
167         final BaseContainerInterface containerInterface = gestureState.getContainerInterface();
168         mLastGestureState = gestureState;
169         RecentsAnimationCallbacks newCallbacks = new RecentsAnimationCallbacks(getSystemUiProxy());
170         mCallbacks = newCallbacks;
171         mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
172             @Override
173             public void onRecentsAnimationStart(RecentsAnimationController controller,
174                     RecentsAnimationTargets targets, @Nullable TransitionInfo transitionInfo) {
175                 if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) {
176                     ActiveGestureProtoLogProxy.logStartRecentsAnimationCallback(
177                             "onRecentsAnimationStart");
178                     mRecentsAnimationStartPending = false;
179                 }
180                 if (mCallbacks == null) {
181                     // It's possible for the recents animation to have finished and be cleaned up
182                     // by the time we process the start callback, and in that case, just we can skip
183                     // handling this call entirely
184                     return;
185                 }
186                 mController = controller;
187                 mTargets = targets;
188                 mTransitionInfo = transitionInfo;
189                 // TODO(b/236226779): We can probably get away w/ setting mLastAppearedTaskTargets
190                 //  to all appeared targets directly vs just looking at running ones
191                 int[] runningTaskIds = mLastGestureState.getRunningTaskIds(targets.apps.length > 1);
192                 mLastAppearedTaskTargets = new RemoteAnimationTarget[runningTaskIds.length];
193                 for (int i = 0; i < runningTaskIds.length; i++) {
194                     RemoteAnimationTarget task = mTargets.findTask(runningTaskIds[i]);
195                     mLastAppearedTaskTargets[i] = task;
196                 }
197                 mLastGestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets);
198 
199                 if (mTargets.hasRecents
200                         // The filtered (MODE_CLOSING) targets only contain 1 home activity.
201                         && mTargets.apps.length == 1
202                         && mTargets.apps[0].windowConfiguration.getActivityType()
203                         == ACTIVITY_TYPE_HOME) {
204                     // This is launching RecentsActivity on top of a 3p launcher. There are no
205                     // other apps need to keep visible so finish the animating state after the
206                     // enter animation of overview is done. Then 3p launcher can be stopped.
207                     mLastGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED, () -> {
208                         if (mLastGestureState != gestureState) return;
209                         // Only finish if the end target is RECENTS. Otherwise, if the target is
210                         // NEW_TASK, startActivityFromRecents will be skipped.
211                         if (mLastGestureState.getEndTarget() == RECENTS) {
212                             finishRunningRecentsAnimation(false /* toHome */);
213                         }
214                     });
215                 }
216             }
217 
218             @Override
219             public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) {
220                 if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) {
221                     ActiveGestureProtoLogProxy.logStartRecentsAnimationCallback(
222                             "onRecentsAnimationCanceled");
223                     mRecentsAnimationStartPending = false;
224                 }
225                 cleanUpRecentsAnimation(newCallbacks);
226             }
227 
228             @Override
229             public void onRecentsAnimationFinished(RecentsAnimationController controller) {
230                 if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) {
231                     ActiveGestureProtoLogProxy.logStartRecentsAnimationCallback(
232                             "onRecentsAnimationFinished");
233                     mRecentsAnimationStartPending = false;
234                 }
235                 cleanUpRecentsAnimation(newCallbacks);
236             }
237 
238             private boolean isNonRecentsStartedTasksAppeared(
239                     RemoteAnimationTarget[] appearedTaskTargets) {
240                 // For example, right after swiping from task X to task Y (e.g. from
241                 // AbsSwipeUpHandler#startNewTask), and then task Y starts X immediately
242                 // (e.g. in Y's onResume). The case will be: lastStartedTask=Y and appearedTask=X.
243                 return mLastGestureState.getEndTarget() == GestureState.GestureEndTarget.NEW_TASK
244                         && ArrayUtils.find(appearedTaskTargets,
245                                 mLastGestureState.mLastStartedTaskIdPredicate) == null;
246             }
247 
248             @Override
249             public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets,
250                     @Nullable TransitionInfo transitionInfo) {
251                 RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0];
252                 BaseContainerInterface containerInterface =
253                         mLastGestureState.getContainerInterface();
254                 for (RemoteAnimationTarget compat : appearedTaskTargets) {
255                     if (compat.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME
256                             && containerInterface.getCreatedContainer() instanceof RecentsActivity
257                             && DisplayController.INSTANCE.get(mCtx).getInfoForDisplay(
258                             mDisplayId).getNavigationMode() != NO_BUTTON) {
259                         // The only time we get onTasksAppeared() in button navigation with a
260                         // 3p launcher is if the user goes to overview first, and in this case we
261                         // can immediately finish the transition
262                         RecentsView recentsView =
263                                 containerInterface.getCreatedContainer().getOverviewPanel();
264                         if (recentsView != null) {
265                             recentsView.finishRecentsAnimation(true, null);
266                         }
267                         return;
268                     }
269                 }
270 
271                 RemoteAnimationTarget[] nonAppTargets = new RemoteAnimationTarget[0];
272                 if ((containerInterface.isInLiveTileMode()
273                             || mLastGestureState.getEndTarget() == RECENTS
274                             || isNonRecentsStartedTasksAppeared(appearedTaskTargets))
275                         && containerInterface.getCreatedContainer() != null) {
276                     RecentsView recentsView =
277                             containerInterface.getCreatedContainer().getOverviewPanel();
278                     if (recentsView != null) {
279                         ActiveGestureProtoLogProxy.logLaunchingSideTask(appearedTaskTarget.taskId);
280                         recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId,
281                                 appearedTaskTargets,
282                                 new RemoteAnimationTarget[0] /* wallpaper */,
283                                 nonAppTargets /* nonApps */,
284                                 transitionInfo);
285                         return;
286                     } else {
287                         ActiveGestureProtoLogProxy.logLaunchingSideTaskFailed();
288                     }
289                 } else if (nonAppTargets.length > 0) {
290                     TaskViewUtils.createSplitAuxiliarySurfacesAnimator(nonAppTargets /* nonApps */,
291                             true /*shown*/, null /* animatorHandler */);
292                 }
293                 if (mController != null) {
294                     mLastAppearedTaskTargets = appearedTaskTargets;
295                     mLastGestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets);
296                 }
297             }
298         });
299         final long eventTime = gestureState.getSwipeUpStartTimeMs();
300         mCallbacks.addListener(gestureState);
301         mCallbacks.addListener(listener);
302 
303         final ActivityOptions options = ActivityOptions.makeBasic();
304         options.setPendingIntentBackgroundActivityStartMode(
305                 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
306         options.setTransientLaunch();
307         options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime);
308 
309         // Notify taskbar that we should skip reacting to launcher visibility change to
310         // avoid a jumping taskbar.
311         TaskbarUIController taskbarUIController = containerInterface.getTaskbarController();
312         if (enableScalingRevealHomeAnimation() && taskbarUIController != null) {
313             taskbarUIController.setSkipLauncherVisibilityChange(true);
314 
315             mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
316                 @Override
317                 public void onRecentsAnimationCanceled(
318                         @NonNull HashMap<Integer, ThumbnailData> thumbnailDatas) {
319                     taskbarUIController.setSkipLauncherVisibilityChange(false);
320                 }
321 
322                 @Override
323                 public void onRecentsAnimationFinished(
324                         @NonNull RecentsAnimationController controller) {
325                     taskbarUIController.setSkipLauncherVisibilityChange(false);
326                 }
327             });
328         }
329 
330         if(containerInterface.getCreatedContainer() instanceof RecentsWindowManager
331                 && RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
332             mRecentsAnimationStartPending = getSystemUiProxy().startRecentsActivity(intent, options,
333                     mCallbacks, gestureState.useSyntheticRecentsTransition());
334             RecentsDisplayModel.getINSTANCE().get(mCtx)
335                     .getRecentsWindowManager(gestureState.getDisplayId())
336                     .startRecentsWindow(mCallbacks);
337         } else {
338             mRecentsAnimationStartPending = getSystemUiProxy().startRecentsActivity(intent,
339                     options, mCallbacks, false /* useSyntheticRecentsTransition */);
340         }
341 
342         if (enableHandleDelayedGestureCallbacks()) {
343             ActiveGestureProtoLogProxy.logSettingRecentsAnimationStartPending(
344                     mRecentsAnimationStartPending);
345         }
346         gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED);
347         return mCallbacks;
348     }
349 
350     /**
351      * Continues the existing running recents animation for a new gesture.
352      */
continueRecentsAnimation(GestureState gestureState)353     public RecentsAnimationCallbacks continueRecentsAnimation(GestureState gestureState) {
354         ActiveGestureProtoLogProxy.logContinueRecentsAnimation();
355         mCallbacks.removeListener(mLastGestureState);
356         mLastGestureState = gestureState;
357         mCallbacks.addListener(gestureState);
358         gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED
359                 | STATE_RECENTS_ANIMATION_STARTED);
360         gestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets);
361         return mCallbacks;
362     }
363 
onSystemUiFlagsChanged(@uickStepContract.SystemUiStateFlags long lastSysUIFlags, @QuickStepContract.SystemUiStateFlags long newSysUIFlags)364     public void onSystemUiFlagsChanged(@QuickStepContract.SystemUiStateFlags long lastSysUIFlags,
365             @QuickStepContract.SystemUiStateFlags long newSysUIFlags) {
366         long isShadeExpandedFlagMask =
367                 SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
368         boolean wasExpanded = hasAnyFlag(lastSysUIFlags, isShadeExpandedFlagMask);
369         boolean isExpanded = hasAnyFlag(newSysUIFlags, isShadeExpandedFlagMask);
370         if (wasExpanded != isExpanded && isExpanded) {
371             // End live tile when expanding the notification panel for the first time from
372             // overview.
373             if (endLiveTile()) {
374                 return;
375             }
376         }
377 
378         boolean wasLocked = SystemUiFlagUtils.isLocked(lastSysUIFlags);
379         boolean isLocked = SystemUiFlagUtils.isLocked(newSysUIFlags);
380         if (wasLocked != isLocked && isLocked) {
381             // Finish the running recents animation when locking the device.
382             finishRunningRecentsAnimation(
383                     mController != null && mController.getFinishTargetIsLauncher());
384         }
385     }
386 
hasAnyFlag(long flags, long flagMask)387     private boolean hasAnyFlag(long flags, long flagMask) {
388         return (flags & flagMask) != 0;
389     }
390 
391     /**
392      * Switches the {@link RecentsView} to screenshot if in live tile mode.
393      *
394      * @return true iff the {@link RecentsView} was in live tile mode and was switched to screenshot
395      */
endLiveTile()396     public boolean endLiveTile() {
397         if (mLastGestureState == null) {
398             return false;
399         }
400         BaseContainerInterface containerInterface = mLastGestureState.getContainerInterface();
401         if (!containerInterface.isInLiveTileMode()
402                 || containerInterface.getCreatedContainer() == null) {
403             return false;
404         }
405         RecentsView recentsView = containerInterface.getCreatedContainer().getOverviewPanel();
406         if (recentsView == null) {
407             return false;
408         }
409         recentsView.switchToScreenshot(null, () -> recentsView.finishRecentsAnimation(
410                 true /* toRecents */, false /* shouldPip */, null));
411         return true;
412     }
413 
setLiveTileCleanUpHandler(Runnable cleanUpHandler)414     public void setLiveTileCleanUpHandler(Runnable cleanUpHandler) {
415         mLiveTileCleanUpHandler = cleanUpHandler;
416     }
417 
enableLiveTileRestartListener()418     public void enableLiveTileRestartListener() {
419         TaskStackChangeListeners.getInstance().registerTaskStackListener(mLiveTileRestartListener);
420     }
421 
422     /**
423      * Finishes the running recents animation.
424      */
finishRunningRecentsAnimation(boolean toHome)425     public void finishRunningRecentsAnimation(boolean toHome) {
426         finishRunningRecentsAnimation(toHome, false /* forceFinish */, null /* forceFinishCb */);
427     }
finishRunningRecentsAnimation( boolean toHome, boolean forceFinish, Runnable forceFinishCb)428     public void finishRunningRecentsAnimation(
429             boolean toHome, boolean forceFinish, Runnable forceFinishCb) {
430         finishRunningRecentsAnimation(toHome, forceFinish, forceFinishCb, mController);
431     }
432 
433     /**
434      * Finishes the running recents animation.
435      * @param forceFinish will synchronously finish the controller
436      */
finishRunningRecentsAnimation( boolean toHome, boolean forceFinish, @Nullable Runnable forceFinishCb, @Nullable RecentsAnimationController controller)437     public void finishRunningRecentsAnimation(
438             boolean toHome,
439             boolean forceFinish,
440             @Nullable Runnable forceFinishCb,
441             @Nullable RecentsAnimationController controller) {
442         if (controller != null) {
443             ActiveGestureProtoLogProxy.logFinishRunningRecentsAnimation(toHome);
444             if (forceFinish) {
445                 controller.finishController(toHome, forceFinishCb, false /* sendUserLeaveHint */,
446                         true /* forceFinish */);
447             } else {
448                 Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
449                         ? controller::finishAnimationToHome
450                         : controller::finishAnimationToApp);
451             }
452         }
453     }
454 
455     /**
456      * Used to notify a listener of the current recents animation state (used if the listener was
457      * not yet added to the callbacks at the point that the listener callbacks would have been
458      * made).
459      */
notifyRecentsAnimationState( RecentsAnimationCallbacks.RecentsAnimationListener listener)460     public void notifyRecentsAnimationState(
461             RecentsAnimationCallbacks.RecentsAnimationListener listener) {
462         if (isRecentsAnimationRunning()) {
463             listener.onRecentsAnimationStart(mController, mTargets, mTransitionInfo);
464         }
465         // TODO: Do we actually need to report canceled/finished?
466     }
467 
468     /**
469      * @return whether there is a recents animation running.
470      */
isRecentsAnimationRunning()471     public boolean isRecentsAnimationRunning() {
472         return mController != null;
473     }
474 
onLauncherDestroyed()475     void onLauncherDestroyed() {
476         if (!mRecentsAnimationStartPending) {
477             return;
478         }
479         if (mCallbacks == null) {
480             return;
481         }
482         ActiveGestureProtoLogProxy.logQueuingForceFinishRecentsAnimation();
483         mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() {
484             @Override
485             public void onRecentsAnimationStart(
486                     RecentsAnimationController controller,
487                     RecentsAnimationTargets targets,
488                     @Nullable TransitionInfo transitionInfo) {
489                 finishRunningRecentsAnimation(
490                         /* toHome= */ false,
491                         /* forceFinish= */ true,
492                         /* forceFinishCb= */ null,
493                         controller);
494             }
495         });
496     }
497 
498     /**
499      * Cleans up the recents animation entirely.
500      */
cleanUpRecentsAnimation(RecentsAnimationCallbacks targetCallbacks)501     private void cleanUpRecentsAnimation(RecentsAnimationCallbacks targetCallbacks) {
502         if (mCallbacks != targetCallbacks) {
503             ActiveGestureProtoLogProxy.logCleanUpRecentsAnimationSkipped();
504             return;
505         }
506         ActiveGestureProtoLogProxy.logCleanUpRecentsAnimation();
507         if (mLiveTileCleanUpHandler != null) {
508             mLiveTileCleanUpHandler.run();
509             mLiveTileCleanUpHandler = null;
510         }
511         TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mLiveTileRestartListener);
512 
513         // Release all the target leashes
514         if (mTargets != null) {
515             mTargets.release();
516         }
517 
518         // Clean up all listeners to ensure we don't get subsequent callbacks
519         if (mCallbacks != null) {
520             mCallbacks.removeAllListeners();
521         }
522 
523         mController = null;
524         mCallbacks = null;
525         mTargets = null;
526         mTransitionInfo = null;
527         mLastGestureState = null;
528         mLastAppearedTaskTargets = null;
529     }
530 
531     @Nullable
getCurrentCallbacks()532     public RecentsAnimationCallbacks getCurrentCallbacks() {
533         return mCallbacks;
534     }
535 
dump(String prefix, PrintWriter pw)536     public void dump(String prefix, PrintWriter pw) {
537         pw.println(prefix + "TaskAnimationManager:");
538         pw.println(prefix + "\tmDisplayId=" + mDisplayId);
539 
540         if (enableHandleDelayedGestureCallbacks()) {
541             pw.println(prefix + "\tmRecentsAnimationStartPending=" + mRecentsAnimationStartPending);
542             pw.println(prefix + "\tmShouldIgnoreUpcomingGestures=" + mShouldIgnoreMotionEvents);
543         }
544         if (mController != null) {
545             mController.dump(prefix + '\t', pw);
546         }
547         if (mCallbacks != null) {
548             mCallbacks.dump(prefix + '\t', pw);
549         }
550         if (mTargets != null) {
551             mTargets.dump(prefix + '\t', pw);
552         }
553         if (mLastGestureState != null) {
554             mLastGestureState.dump(prefix + '\t', pw);
555         }
556     }
557 }
558