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