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.util.Executors.MAIN_EXECUTOR; 22 import static com.android.launcher3.util.Executors.UI_HELPER_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.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION; 29 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; 30 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED; 31 32 import android.app.ActivityManager; 33 import android.app.ActivityOptions; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.os.SystemProperties; 37 import android.util.Log; 38 import android.view.RemoteAnimationTarget; 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.util.DisplayController; 48 import com.android.quickstep.util.ActiveGestureLog; 49 import com.android.quickstep.util.SystemUiFlagUtils; 50 import com.android.quickstep.views.RecentsView; 51 import com.android.systemui.shared.recents.model.ThumbnailData; 52 import com.android.systemui.shared.system.ActivityManagerWrapper; 53 import com.android.systemui.shared.system.QuickStepContract; 54 import com.android.systemui.shared.system.TaskStackChangeListener; 55 import com.android.systemui.shared.system.TaskStackChangeListeners; 56 57 import java.io.PrintWriter; 58 import java.util.HashMap; 59 60 public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener { 61 public static final boolean ENABLE_SHELL_TRANSITIONS = true; 62 public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS 63 && SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false); 64 65 private final Context mCtx; 66 private RecentsAnimationController mController; 67 private RecentsAnimationCallbacks mCallbacks; 68 private RecentsAnimationTargets mTargets; 69 // Temporary until we can hook into gesture state events 70 private GestureState mLastGestureState; 71 private RemoteAnimationTarget[] mLastAppearedTaskTargets; 72 private Runnable mLiveTileCleanUpHandler; 73 74 private boolean mRecentsAnimationStartPending = false; 75 private boolean mShouldIgnoreMotionEvents = false; 76 77 private final TaskStackChangeListener mLiveTileRestartListener = new TaskStackChangeListener() { 78 @Override 79 public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, 80 boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { 81 if (mLastGestureState == null) { 82 TaskStackChangeListeners.getInstance().unregisterTaskStackListener( 83 mLiveTileRestartListener); 84 return; 85 } 86 BaseContainerInterface containerInterface = mLastGestureState.getContainerInterface(); 87 if (containerInterface.isInLiveTileMode() 88 && containerInterface.getCreatedContainer() != null) { 89 RecentsView recentsView = containerInterface.getCreatedContainer() 90 .getOverviewPanel(); 91 if (recentsView != null) { 92 recentsView.launchSideTaskInLiveTileModeForRestartedApp(task.taskId); 93 TaskStackChangeListeners.getInstance().unregisterTaskStackListener( 94 mLiveTileRestartListener); 95 } 96 } 97 } 98 }; 99 TaskAnimationManager(Context ctx)100 TaskAnimationManager(Context ctx) { 101 mCtx = ctx; 102 } 103 getSystemUiProxy()104 SystemUiProxy getSystemUiProxy() { 105 return SystemUiProxy.INSTANCE.get(mCtx); 106 } 107 108 /** 109 * Preloads the recents animation. 110 */ preloadRecentsAnimation(Intent intent)111 public void preloadRecentsAnimation(Intent intent) { 112 // Pass null animation handler to indicate this start is for preloading 113 UI_HELPER_EXECUTOR.execute(() -> ActivityManagerWrapper.getInstance() 114 .startRecentsActivity(intent, 0, null, null, null)); 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 ActiveGestureLog.INSTANCE.addLog( 136 /* event= */ "startRecentsAnimation", 137 /* gestureEvent= */ START_RECENTS_ANIMATION); 138 // Notify if recents animation is still running 139 if (mController != null) { 140 String msg = "New recents animation started before old animation completed"; 141 if (FeatureFlags.IS_STUDIO_BUILD) { 142 throw new IllegalArgumentException(msg); 143 } else { 144 Log.e("TaskAnimationManager", msg, new Exception()); 145 } 146 } 147 // But force-finish it anyways 148 finishRunningRecentsAnimation(false /* toHome */, true /* forceFinish */, 149 null /* forceFinishCb */); 150 151 if (mCallbacks != null) { 152 // If mCallbacks still != null, that means we are getting this startRecentsAnimation() 153 // before the previous one got onRecentsAnimationStart(). In that case, cleanup the 154 // previous animation so it doesn't mess up/listen to state changes in this animation. 155 cleanUpRecentsAnimation(mCallbacks); 156 } 157 158 final BaseContainerInterface containerInterface = gestureState.getContainerInterface(); 159 mLastGestureState = gestureState; 160 RecentsAnimationCallbacks newCallbacks = new RecentsAnimationCallbacks( 161 getSystemUiProxy(), containerInterface.allowMinimizeSplitScreen()); 162 mCallbacks = newCallbacks; 163 mCallbacks.addListener(new RecentsAnimationCallbacks.RecentsAnimationListener() { 164 @Override 165 public void onRecentsAnimationStart(RecentsAnimationController controller, 166 RecentsAnimationTargets targets) { 167 if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) { 168 ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString( 169 "TaskAnimationManager.startRecentsAnimation(onRecentsAnimationStart): ") 170 .append("Setting mRecentsAnimationStartPending = false")); 171 mRecentsAnimationStartPending = false; 172 } 173 if (mCallbacks == null) { 174 // It's possible for the recents animation to have finished and be cleaned up 175 // by the time we process the start callback, and in that case, just we can skip 176 // handling this call entirely 177 return; 178 } 179 mController = controller; 180 mTargets = targets; 181 // TODO(b/236226779): We can probably get away w/ setting mLastAppearedTaskTargets 182 // to all appeared targets directly vs just looking at running ones 183 int[] runningTaskIds = mLastGestureState.getRunningTaskIds(targets.apps.length > 1); 184 mLastAppearedTaskTargets = new RemoteAnimationTarget[runningTaskIds.length]; 185 for (int i = 0; i < runningTaskIds.length; i++) { 186 RemoteAnimationTarget task = mTargets.findTask(runningTaskIds[i]); 187 mLastAppearedTaskTargets[i] = task; 188 } 189 mLastGestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets); 190 191 if (ENABLE_SHELL_TRANSITIONS && mTargets.hasRecents 192 // The filtered (MODE_CLOSING) targets only contain 1 home activity. 193 && mTargets.apps.length == 1 194 && mTargets.apps[0].windowConfiguration.getActivityType() 195 == ACTIVITY_TYPE_HOME) { 196 // This is launching RecentsActivity on top of a 3p launcher. There are no 197 // other apps need to keep visible so finish the animating state after the 198 // enter animation of overview is done. Then 3p launcher can be stopped. 199 mLastGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED, () -> { 200 if (mLastGestureState != gestureState) return; 201 // Only finish if the end target is RECENTS. Otherwise, if the target is 202 // NEW_TASK, startActivityFromRecents will be skipped. 203 if (mLastGestureState.getEndTarget() == RECENTS) { 204 finishRunningRecentsAnimation(false /* toHome */, 205 true /* forceFinish */, null /* forceFinishCb */); 206 } 207 }); 208 } 209 } 210 211 @Override 212 public void onRecentsAnimationCanceled(HashMap<Integer, ThumbnailData> thumbnailDatas) { 213 if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) { 214 ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString( 215 "TaskAnimationManager.startRecentsAnimation") 216 .append("(onRecentsAnimationCanceled): ") 217 .append("Setting mRecentsAnimationStartPending = false")); 218 mRecentsAnimationStartPending = false; 219 } 220 cleanUpRecentsAnimation(newCallbacks); 221 } 222 223 @Override 224 public void onRecentsAnimationFinished(RecentsAnimationController controller) { 225 if (enableHandleDelayedGestureCallbacks() && mRecentsAnimationStartPending) { 226 ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString( 227 "TaskAnimationManager.startRecentsAnimation") 228 .append("(onRecentsAnimationFinished): ") 229 .append("Setting mRecentsAnimationStartPending = false")); 230 mRecentsAnimationStartPending = false; 231 } 232 cleanUpRecentsAnimation(newCallbacks); 233 } 234 235 private boolean isNonRecentsStartedTasksAppeared( 236 RemoteAnimationTarget[] appearedTaskTargets) { 237 // For example, right after swiping from task X to task Y (e.g. from 238 // AbsSwipeUpHandler#startNewTask), and then task Y starts X immediately 239 // (e.g. in Y's onResume). The case will be: lastStartedTask=Y and appearedTask=X. 240 return mLastGestureState.getEndTarget() == GestureState.GestureEndTarget.NEW_TASK 241 && ArrayUtils.find(appearedTaskTargets, 242 mLastGestureState.mLastStartedTaskIdPredicate) == null; 243 } 244 245 @Override 246 public void onTasksAppeared(RemoteAnimationTarget[] appearedTaskTargets) { 247 RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0]; 248 BaseContainerInterface containerInterface = 249 mLastGestureState.getContainerInterface(); 250 251 for (RemoteAnimationTarget compat : appearedTaskTargets) { 252 if (compat.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME 253 && containerInterface.getCreatedContainer() instanceof RecentsActivity 254 && DisplayController.getNavigationMode(mCtx) != NO_BUTTON) { 255 // The only time we get onTasksAppeared() in button navigation with a 256 // 3p launcher is if the user goes to overview first, and in this case we 257 // can immediately finish the transition 258 RecentsView recentsView = 259 containerInterface.getCreatedContainer().getOverviewPanel(); 260 if (recentsView != null) { 261 recentsView.finishRecentsAnimation(true, null); 262 } 263 return; 264 } 265 } 266 267 RemoteAnimationTarget[] nonAppTargets = ENABLE_SHELL_TRANSITIONS 268 ? null : getSystemUiProxy().onStartingSplitLegacy( 269 appearedTaskTargets); 270 if (nonAppTargets == null) { 271 nonAppTargets = new RemoteAnimationTarget[0]; 272 } 273 if ((containerInterface.isInLiveTileMode() 274 || mLastGestureState.getEndTarget() == RECENTS 275 || isNonRecentsStartedTasksAppeared(appearedTaskTargets)) 276 && containerInterface.getCreatedContainer() != null) { 277 RecentsView recentsView = 278 containerInterface.getCreatedContainer().getOverviewPanel(); 279 if (recentsView != null) { 280 ActiveGestureLog.INSTANCE.addLog( 281 new ActiveGestureLog.CompoundString("Launching side task id=") 282 .append(appearedTaskTarget.taskId)); 283 recentsView.launchSideTaskInLiveTileMode(appearedTaskTarget.taskId, 284 appearedTaskTargets, 285 new RemoteAnimationTarget[0] /* wallpaper */, 286 nonAppTargets /* nonApps */); 287 return; 288 } else { 289 ActiveGestureLog.INSTANCE.addLog("Unable to launch side task (no recents)"); 290 } 291 } else if (nonAppTargets.length > 0) { 292 TaskViewUtils.createSplitAuxiliarySurfacesAnimator(nonAppTargets /* nonApps */, 293 true /*shown*/, null /* animatorHandler */); 294 } 295 if (mController != null) { 296 if (mLastAppearedTaskTargets != null) { 297 for (RemoteAnimationTarget lastTarget : mLastAppearedTaskTargets) { 298 for (RemoteAnimationTarget appearedTarget : appearedTaskTargets) { 299 if (lastTarget != null && 300 appearedTarget.taskId != lastTarget.taskId) { 301 mController.removeTaskTarget(lastTarget.taskId); 302 } 303 } 304 } 305 } 306 mLastAppearedTaskTargets = appearedTaskTargets; 307 mLastGestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets); 308 } 309 } 310 311 @Override 312 public boolean onSwitchToScreenshot(Runnable onFinished) { 313 if (!containerInterface.isInLiveTileMode() 314 || containerInterface.getCreatedContainer() == null) { 315 // No need to switch since tile is already a screenshot. 316 onFinished.run(); 317 } else { 318 final RecentsView recentsView = 319 containerInterface.getCreatedContainer().getOverviewPanel(); 320 if (recentsView != null) { 321 recentsView.switchToScreenshot(onFinished); 322 } else { 323 onFinished.run(); 324 } 325 } 326 return true; 327 } 328 }); 329 final long eventTime = gestureState.getSwipeUpStartTimeMs(); 330 mCallbacks.addListener(gestureState); 331 mCallbacks.addListener(listener); 332 333 if (ENABLE_SHELL_TRANSITIONS) { 334 final ActivityOptions options = ActivityOptions.makeBasic(); 335 options.setPendingIntentBackgroundActivityLaunchAllowedByPermission(true); 336 // Use regular (non-transient) launch for all apps page to control IME. 337 if (!containerInterface.allowAllAppsFromOverview()) { 338 options.setTransientLaunch(); 339 } 340 options.setSourceInfo(ActivityOptions.SourceInfo.TYPE_RECENTS_ANIMATION, eventTime); 341 mRecentsAnimationStartPending = getSystemUiProxy() 342 .startRecentsActivity(intent, options, mCallbacks); 343 if (enableHandleDelayedGestureCallbacks()) { 344 ActiveGestureLog.INSTANCE.addLog(new ActiveGestureLog.CompoundString( 345 "TaskAnimationManager.startRecentsAnimation(shell transition path): ") 346 .append("Setting mRecentsAnimationStartPending = ") 347 .append(mRecentsAnimationStartPending)); 348 } 349 } else { 350 UI_HELPER_EXECUTOR.execute( 351 () -> ActivityManagerWrapper.getInstance().startRecentsActivity( 352 intent, 353 eventTime, 354 mCallbacks, 355 result -> { 356 if (enableHandleDelayedGestureCallbacks()) { 357 ActiveGestureLog.INSTANCE.addLog( 358 new ActiveGestureLog.CompoundString( 359 "TaskAnimationManager.startRecentsAnimation") 360 .append("(legacy path): Setting ") 361 .append("mRecentsAnimationStartPending = ") 362 .append(result)); 363 } 364 mRecentsAnimationStartPending = result; 365 }, 366 MAIN_EXECUTOR.getHandler())); 367 } 368 gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED); 369 return mCallbacks; 370 } 371 372 /** 373 * Continues the existing running recents animation for a new gesture. 374 */ continueRecentsAnimation(GestureState gestureState)375 public RecentsAnimationCallbacks continueRecentsAnimation(GestureState gestureState) { 376 ActiveGestureLog.INSTANCE.addLog(/* event= */ "continueRecentsAnimation"); 377 mCallbacks.removeListener(mLastGestureState); 378 mLastGestureState = gestureState; 379 mCallbacks.addListener(gestureState); 380 gestureState.setState(STATE_RECENTS_ANIMATION_INITIALIZED 381 | STATE_RECENTS_ANIMATION_STARTED); 382 gestureState.updateLastAppearedTaskTargets(mLastAppearedTaskTargets); 383 return mCallbacks; 384 } 385 onSystemUiFlagsChanged(@uickStepContract.SystemUiStateFlags long lastSysUIFlags, @QuickStepContract.SystemUiStateFlags long newSysUIFlags)386 public void onSystemUiFlagsChanged(@QuickStepContract.SystemUiStateFlags long lastSysUIFlags, 387 @QuickStepContract.SystemUiStateFlags long newSysUIFlags) { 388 long isShadeExpandedFlagMask = 389 SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; 390 boolean wasExpanded = hasAnyFlag(lastSysUIFlags, isShadeExpandedFlagMask); 391 boolean isExpanded = hasAnyFlag(newSysUIFlags, isShadeExpandedFlagMask); 392 if (wasExpanded != isExpanded && isExpanded) { 393 // End live tile when expanding the notification panel for the first time from 394 // overview. 395 if (endLiveTile()) { 396 return; 397 } 398 } 399 400 boolean wasLocked = SystemUiFlagUtils.isLocked(lastSysUIFlags); 401 boolean isLocked = SystemUiFlagUtils.isLocked(newSysUIFlags); 402 if (wasLocked != isLocked && isLocked) { 403 // Finish the running recents animation when locking the device. 404 finishRunningRecentsAnimation( 405 mController != null && mController.getFinishTargetIsLauncher()); 406 } 407 } 408 hasAnyFlag(long flags, long flagMask)409 private boolean hasAnyFlag(long flags, long flagMask) { 410 return (flags & flagMask) != 0; 411 } 412 413 /** 414 * Switches the {@link RecentsView} to screenshot if in live tile mode. 415 * 416 * @return true iff the {@link RecentsView} was in live tile mode and was switched to screenshot 417 */ endLiveTile()418 public boolean endLiveTile() { 419 if (mLastGestureState == null) { 420 return false; 421 } 422 BaseContainerInterface containerInterface = mLastGestureState.getContainerInterface(); 423 if (!containerInterface.isInLiveTileMode() 424 || containerInterface.getCreatedContainer() == null) { 425 return false; 426 } 427 RecentsView recentsView = containerInterface.getCreatedContainer().getOverviewPanel(); 428 if (recentsView == null) { 429 return false; 430 } 431 recentsView.switchToScreenshot(null, () -> recentsView.finishRecentsAnimation( 432 true /* toRecents */, false /* shouldPip */, null)); 433 return true; 434 } 435 setLiveTileCleanUpHandler(Runnable cleanUpHandler)436 public void setLiveTileCleanUpHandler(Runnable cleanUpHandler) { 437 mLiveTileCleanUpHandler = cleanUpHandler; 438 } 439 enableLiveTileRestartListener()440 public void enableLiveTileRestartListener() { 441 TaskStackChangeListeners.getInstance().registerTaskStackListener(mLiveTileRestartListener); 442 } 443 444 /** 445 * Finishes the running recents animation. 446 */ finishRunningRecentsAnimation(boolean toHome)447 public void finishRunningRecentsAnimation(boolean toHome) { 448 finishRunningRecentsAnimation(toHome, false /* forceFinish */, null /* forceFinishCb */); 449 } 450 451 /** 452 * Finishes the running recents animation. 453 * @param forceFinish will synchronously finish the controller 454 */ finishRunningRecentsAnimation(boolean toHome, boolean forceFinish, Runnable forceFinishCb)455 public void finishRunningRecentsAnimation(boolean toHome, boolean forceFinish, 456 Runnable forceFinishCb) { 457 if (mController != null) { 458 ActiveGestureLog.INSTANCE.addLog( 459 /* event= */ "finishRunningRecentsAnimation", toHome); 460 if (forceFinish) { 461 mController.finishController(toHome, forceFinishCb, false /* sendUserLeaveHint */, 462 true /* forceFinish */); 463 } else { 464 Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome 465 ? mController::finishAnimationToHome 466 : mController::finishAnimationToApp); 467 } 468 } 469 } 470 471 /** 472 * Used to notify a listener of the current recents animation state (used if the listener was 473 * not yet added to the callbacks at the point that the listener callbacks would have been 474 * made). 475 */ notifyRecentsAnimationState( RecentsAnimationCallbacks.RecentsAnimationListener listener)476 public void notifyRecentsAnimationState( 477 RecentsAnimationCallbacks.RecentsAnimationListener listener) { 478 if (isRecentsAnimationRunning()) { 479 listener.onRecentsAnimationStart(mController, mTargets); 480 } 481 // TODO: Do we actually need to report canceled/finished? 482 } 483 484 /** 485 * @return whether there is a recents animation running. 486 */ isRecentsAnimationRunning()487 public boolean isRecentsAnimationRunning() { 488 return mController != null; 489 } 490 491 /** 492 * Cleans up the recents animation entirely. 493 */ cleanUpRecentsAnimation(RecentsAnimationCallbacks targetCallbacks)494 private void cleanUpRecentsAnimation(RecentsAnimationCallbacks targetCallbacks) { 495 if (mCallbacks != targetCallbacks) { 496 ActiveGestureLog.INSTANCE.addLog( 497 /* event= */ "cleanUpRecentsAnimation skipped due to wrong callbacks"); 498 return; 499 } 500 ActiveGestureLog.INSTANCE.addLog(/* event= */ "cleanUpRecentsAnimation"); 501 if (mLiveTileCleanUpHandler != null) { 502 mLiveTileCleanUpHandler.run(); 503 mLiveTileCleanUpHandler = null; 504 } 505 TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mLiveTileRestartListener); 506 507 // Release all the target leashes 508 if (mTargets != null) { 509 mTargets.release(); 510 } 511 512 // Clean up all listeners to ensure we don't get subsequent callbacks 513 if (mCallbacks != null) { 514 mCallbacks.removeAllListeners(); 515 } 516 517 mController = null; 518 mCallbacks = null; 519 mTargets = null; 520 mLastGestureState = null; 521 mLastAppearedTaskTargets = null; 522 } 523 524 @Nullable getCurrentCallbacks()525 public RecentsAnimationCallbacks getCurrentCallbacks() { 526 return mCallbacks; 527 } 528 dump(String prefix, PrintWriter pw)529 public void dump(String prefix, PrintWriter pw) { 530 pw.println(prefix + "TaskAnimationManager:"); 531 532 if (enableHandleDelayedGestureCallbacks()) { 533 pw.println(prefix + "\tmRecentsAnimationStartPending=" + mRecentsAnimationStartPending); 534 pw.println(prefix + "\tmShouldIgnoreUpcomingGestures=" + mShouldIgnoreMotionEvents); 535 } 536 if (mController != null) { 537 mController.dump(prefix + '\t', pw); 538 } 539 if (mCallbacks != null) { 540 mCallbacks.dump(prefix + '\t', pw); 541 } 542 if (mTargets != null) { 543 mTargets.dump(prefix + '\t', pw); 544 } 545 if (mLastGestureState != null) { 546 mLastGestureState.dump(prefix + '\t', pw); 547 } 548 } 549 } 550