RecentsActivity.java revision d2a030613154e2007d9816e090c39e9726e7adba
1/* 2 * Copyright (C) 2014 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 17package com.android.systemui.recents; 18 19import android.app.Activity; 20import android.app.ActivityOptions; 21import android.app.TaskStackBuilder; 22import android.content.BroadcastReceiver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.content.res.Configuration; 27import android.net.Uri; 28import android.os.Bundle; 29import android.os.SystemClock; 30import android.os.UserHandle; 31import android.provider.Settings; 32import android.util.Log; 33import android.view.KeyEvent; 34import android.view.View; 35import android.view.ViewTreeObserver; 36import android.view.WindowManager; 37import android.view.WindowManager.LayoutParams; 38 39import com.android.internal.logging.MetricsLogger; 40import com.android.internal.logging.MetricsProto.MetricsEvent; 41import com.android.systemui.Interpolators; 42import com.android.systemui.R; 43import com.android.systemui.recents.events.EventBus; 44import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent; 45import com.android.systemui.recents.events.activity.ConfigurationChangedEvent; 46import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent; 47import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; 48import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; 49import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent; 50import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent; 51import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent; 52import com.android.systemui.recents.events.activity.HideRecentsEvent; 53import com.android.systemui.recents.events.activity.IterateRecentsEvent; 54import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent; 55import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent; 56import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent; 57import com.android.systemui.recents.events.activity.ToggleRecentsEvent; 58import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; 59import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; 60import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; 61import com.android.systemui.recents.events.ui.DeleteTaskDataEvent; 62import com.android.systemui.recents.events.ui.HideIncompatibleAppOverlayEvent; 63import com.android.systemui.recents.events.ui.RecentsDrawnEvent; 64import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent; 65import com.android.systemui.recents.events.ui.ShowIncompatibleAppOverlayEvent; 66import com.android.systemui.recents.events.ui.StackViewScrolledEvent; 67import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent; 68import com.android.systemui.recents.events.ui.UserInteractionEvent; 69import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; 70import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent; 71import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent; 72import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent; 73import com.android.systemui.recents.misc.DozeTrigger; 74import com.android.systemui.recents.misc.SystemServicesProxy; 75import com.android.systemui.recents.misc.Utilities; 76import com.android.systemui.recents.model.RecentsPackageMonitor; 77import com.android.systemui.recents.model.RecentsTaskLoadPlan; 78import com.android.systemui.recents.model.RecentsTaskLoader; 79import com.android.systemui.recents.model.Task; 80import com.android.systemui.recents.model.TaskStack; 81import com.android.systemui.recents.views.RecentsView; 82import com.android.systemui.recents.views.SystemBarScrimViews; 83import com.android.systemui.statusbar.BaseStatusBar; 84 85import java.io.FileDescriptor; 86import java.io.PrintWriter; 87 88/** 89 * The main Recents activity that is started from AlternateRecentsComponent. 90 */ 91public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreDrawListener { 92 93 private final static String TAG = "RecentsActivity"; 94 private final static boolean DEBUG = false; 95 96 public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1; 97 public final static int INCOMPATIBLE_APP_ALPHA_DURATION = 150; 98 99 private RecentsPackageMonitor mPackageMonitor; 100 private long mLastTabKeyEventTime; 101 private int mLastDeviceOrientation = Configuration.ORIENTATION_UNDEFINED; 102 private boolean mFinishedOnStartup; 103 private boolean mIgnoreAltTabRelease; 104 private boolean mIsVisible; 105 106 // Top level views 107 private RecentsView mRecentsView; 108 private SystemBarScrimViews mScrimViews; 109 private View mIncompatibleAppOverlay; 110 111 // Runnables to finish the Recents activity 112 private Intent mHomeIntent; 113 114 // The trigger to automatically launch the current task 115 private int mFocusTimerDuration; 116 private DozeTrigger mIterateTrigger; 117 private final UserInteractionEvent mUserInteractionEvent = new UserInteractionEvent(); 118 119 /** 120 * A common Runnable to finish Recents by launching Home with an animation depending on the 121 * last activity launch state. Generally we always launch home when we exit Recents rather than 122 * just finishing the activity since we don't know what is behind Recents in the task stack. 123 */ 124 class LaunchHomeRunnable implements Runnable { 125 126 Intent mLaunchIntent; 127 ActivityOptions mOpts; 128 129 /** 130 * Creates a finish runnable that starts the specified intent. 131 */ 132 public LaunchHomeRunnable(Intent launchIntent, ActivityOptions opts) { 133 mLaunchIntent = launchIntent; 134 mOpts = opts; 135 } 136 137 @Override 138 public void run() { 139 try { 140 ActivityOptions opts = mOpts; 141 if (opts == null) { 142 opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this, 143 R.anim.recents_to_launcher_enter, R.anim.recents_to_launcher_exit); 144 } 145 startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT); 146 } catch (Exception e) { 147 Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e); 148 } 149 } 150 } 151 152 /** 153 * Broadcast receiver to handle messages from the system 154 */ 155 final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() { 156 @Override 157 public void onReceive(Context context, Intent intent) { 158 String action = intent.getAction(); 159 if (action.equals(Intent.ACTION_SCREEN_OFF)) { 160 // When the screen turns off, dismiss Recents to Home 161 dismissRecentsToHomeIfVisible(false); 162 } 163 } 164 }; 165 166 /** 167 * Dismisses recents if we are already visible and the intent is to toggle the recents view. 168 */ 169 boolean dismissRecentsToFocusedTask(int logCategory) { 170 SystemServicesProxy ssp = Recents.getSystemServices(); 171 if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) { 172 // If we have a focused Task, launch that Task now 173 if (mRecentsView.launchFocusedTask(logCategory)) return true; 174 } 175 return false; 176 } 177 178 /** 179 * Dismisses recents back to the launch target task. 180 */ 181 boolean dismissRecentsToLaunchTargetTaskOrHome() { 182 SystemServicesProxy ssp = Recents.getSystemServices(); 183 if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) { 184 // If we have a focused Task, launch that Task now 185 if (mRecentsView.launchPreviousTask()) return true; 186 // If none of the other cases apply, then just go Home 187 dismissRecentsToHome(true /* animateTaskViews */); 188 } 189 return false; 190 } 191 192 /** 193 * Dismisses recents if we are already visible and the intent is to toggle the recents view. 194 */ 195 boolean dismissRecentsToFocusedTaskOrHome() { 196 SystemServicesProxy ssp = Recents.getSystemServices(); 197 if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) { 198 // If we have a focused Task, launch that Task now 199 if (mRecentsView.launchFocusedTask(0 /* logCategory */)) return true; 200 // If none of the other cases apply, then just go Home 201 dismissRecentsToHome(true /* animateTaskViews */); 202 return true; 203 } 204 return false; 205 } 206 207 /** 208 * Dismisses Recents directly to Home without checking whether it is currently visible. 209 */ 210 void dismissRecentsToHome(boolean animateTaskViews) { 211 dismissRecentsToHome(animateTaskViews, null); 212 } 213 214 /** 215 * Dismisses Recents directly to Home without checking whether it is currently visible. 216 * 217 * @param overrideAnimation If not null, will override the default animation that is based on 218 * how Recents was launched. 219 */ 220 void dismissRecentsToHome(boolean animateTaskViews, ActivityOptions overrideAnimation) { 221 DismissRecentsToHomeAnimationStarted dismissEvent = 222 new DismissRecentsToHomeAnimationStarted(animateTaskViews); 223 dismissEvent.addPostAnimationCallback(new LaunchHomeRunnable(mHomeIntent, 224 overrideAnimation)); 225 dismissEvent.addPostAnimationCallback(new Runnable() { 226 @Override 227 public void run() { 228 Recents.getSystemServices().sendCloseSystemWindows( 229 BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY); 230 } 231 }); 232 EventBus.getDefault().send(dismissEvent); 233 } 234 235 /** Dismisses Recents directly to Home if we currently aren't transitioning. */ 236 boolean dismissRecentsToHomeIfVisible(boolean animated) { 237 SystemServicesProxy ssp = Recents.getSystemServices(); 238 if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) { 239 // Return to Home 240 dismissRecentsToHome(animated); 241 return true; 242 } 243 return false; 244 } 245 246 /** Called with the activity is first created. */ 247 @Override 248 public void onCreate(Bundle savedInstanceState) { 249 super.onCreate(savedInstanceState); 250 mFinishedOnStartup = false; 251 252 // In the case that the activity starts up before the Recents component has initialized 253 // (usually when debugging/pushing the SysUI apk), just finish this activity. 254 SystemServicesProxy ssp = Recents.getSystemServices(); 255 if (ssp == null) { 256 mFinishedOnStartup = true; 257 finish(); 258 return; 259 } 260 261 // Register this activity with the event bus 262 EventBus.getDefault().register(this, EVENT_BUS_PRIORITY); 263 264 // Initialize the package monitor 265 mPackageMonitor = new RecentsPackageMonitor(); 266 mPackageMonitor.register(this); 267 268 // Set the Recents layout 269 setContentView(R.layout.recents); 270 takeKeyEvents(true); 271 mRecentsView = (RecentsView) findViewById(R.id.recents_view); 272 mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | 273 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | 274 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); 275 mScrimViews = new SystemBarScrimViews(this); 276 getWindow().getAttributes().privateFlags |= 277 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY; 278 279 mLastDeviceOrientation = Utilities.getAppConfiguration(this).orientation; 280 mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration); 281 mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() { 282 @Override 283 public void run() { 284 dismissRecentsToFocusedTask(MetricsEvent.OVERVIEW_SELECT_TIMEOUT); 285 } 286 }); 287 288 // Set the window background 289 getWindow().setBackgroundDrawable(mRecentsView.getBackgroundScrim()); 290 291 // Create the home intent runnable 292 mHomeIntent = new Intent(Intent.ACTION_MAIN, null); 293 mHomeIntent.addCategory(Intent.CATEGORY_HOME); 294 mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 295 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 296 297 // Register the broadcast receiver to handle messages when the screen is turned off 298 IntentFilter filter = new IntentFilter(); 299 filter.addAction(Intent.ACTION_SCREEN_OFF); 300 registerReceiver(mSystemBroadcastReceiver, filter); 301 302 getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION); 303 304 // Reload the stack view 305 reloadStackView(); 306 } 307 308 @Override 309 protected void onStart() { 310 super.onStart(); 311 312 // Notify that recents is now visible 313 EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true)); 314 MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY); 315 } 316 317 @Override 318 protected void onNewIntent(Intent intent) { 319 super.onNewIntent(intent); 320 321 // Reload the stack view 322 reloadStackView(); 323 } 324 325 /** 326 * Reloads the stack views upon launching Recents. 327 */ 328 private void reloadStackView() { 329 // If the Recents component has preloaded a load plan, then use that to prevent 330 // reconstructing the task stack 331 RecentsTaskLoader loader = Recents.getTaskLoader(); 332 RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan(); 333 if (loadPlan == null) { 334 loadPlan = loader.createLoadPlan(this); 335 } 336 337 // Start loading tasks according to the load plan 338 RecentsConfiguration config = Recents.getConfiguration(); 339 RecentsActivityLaunchState launchState = config.getLaunchState(); 340 if (!loadPlan.hasTasks()) { 341 loader.preloadTasks(loadPlan, launchState.launchedToTaskId, 342 launchState.launchedFromHome); 343 } 344 345 RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); 346 loadOpts.runningTaskId = launchState.launchedToTaskId; 347 loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks; 348 loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails; 349 loader.loadTasks(this, loadPlan, loadOpts); 350 TaskStack stack = loadPlan.getTaskStack(); 351 mRecentsView.onReload(mIsVisible, stack.getTaskCount() == 0); 352 mRecentsView.updateStack(stack, true /* setStackViewTasks */); 353 354 // Update the nav bar scrim, but defer the animation until the enter-window event 355 boolean animateNavBarScrim = !launchState.launchedViaDockGesture; 356 mScrimViews.updateNavBarScrim(animateNavBarScrim, stack.getTaskCount() > 0, null); 357 358 // If this is a new instance relaunched by AM, without going through the normal mechanisms, 359 // then we have to manually trigger the enter animation state 360 boolean wasLaunchedByAm = !launchState.launchedFromHome && 361 !launchState.launchedFromApp; 362 if (wasLaunchedByAm) { 363 EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent()); 364 } 365 366 // Keep track of whether we launched from the nav bar button or via alt-tab 367 if (launchState.launchedWithAltTab) { 368 MetricsLogger.count(this, "overview_trigger_alttab", 1); 369 } else { 370 MetricsLogger.count(this, "overview_trigger_nav_btn", 1); 371 } 372 373 // Keep track of whether we launched from an app or from home 374 if (launchState.launchedFromApp) { 375 Task launchTarget = stack.getLaunchTarget(); 376 int launchTaskIndexInStack = launchTarget != null 377 ? stack.indexOfStackTask(launchTarget) 378 : 0; 379 MetricsLogger.count(this, "overview_source_app", 1); 380 // If from an app, track the stack index of the app in the stack (for affiliated tasks) 381 MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack); 382 } else { 383 MetricsLogger.count(this, "overview_source_home", 1); 384 } 385 386 // Keep track of the total stack task count 387 int taskCount = mRecentsView.getStack().getTaskCount(); 388 MetricsLogger.histogram(this, "overview_task_count", taskCount); 389 390 // After we have resumed, set the visible state until the next onStop() call 391 mIsVisible = true; 392 } 393 394 @Override 395 public void onEnterAnimationComplete() { 396 super.onEnterAnimationComplete(); 397 EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent()); 398 } 399 400 @Override 401 protected void onResume() { 402 super.onResume(); 403 404 // Notify of the next draw 405 mRecentsView.getViewTreeObserver().addOnPreDrawListener( 406 new ViewTreeObserver.OnPreDrawListener() { 407 408 @Override 409 public boolean onPreDraw() { 410 mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this); 411 EventBus.getDefault().post(new RecentsDrawnEvent()); 412 return true; 413 } 414 }); 415 } 416 417 @Override 418 protected void onPause() { 419 super.onPause(); 420 421 mIgnoreAltTabRelease = false; 422 mIterateTrigger.stopDozing(); 423 } 424 425 @Override 426 public void onConfigurationChanged(Configuration newConfig) { 427 super.onConfigurationChanged(newConfig); 428 429 // Notify of the config change 430 int newDeviceOrientation = Utilities.getAppConfiguration(this).orientation; 431 int numStackTasks = mRecentsView.getStack().getStackTaskCount(); 432 EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */, 433 (mLastDeviceOrientation != newDeviceOrientation), numStackTasks > 0)); 434 mLastDeviceOrientation = newDeviceOrientation; 435 } 436 437 @Override 438 public void onMultiWindowModeChanged(boolean isInMultiWindowMode) { 439 super.onMultiWindowModeChanged(isInMultiWindowMode); 440 441 // Reload the task stack completely 442 RecentsConfiguration config = Recents.getConfiguration(); 443 RecentsActivityLaunchState launchState = config.getLaunchState(); 444 RecentsTaskLoader loader = Recents.getTaskLoader(); 445 RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this); 446 loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */); 447 448 RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); 449 loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks; 450 loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails; 451 loader.loadTasks(this, loadPlan, loadOpts); 452 453 TaskStack stack = loadPlan.getTaskStack(); 454 int numStackTasks = stack.getStackTaskCount(); 455 456 EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */, 457 false /* fromDeviceOrientationChange */, numStackTasks > 0)); 458 EventBus.getDefault().send(new MultiWindowStateChangedEvent(isInMultiWindowMode, stack)); 459 } 460 461 @Override 462 protected void onStop() { 463 super.onStop(); 464 465 // Notify that recents is now hidden 466 mIsVisible = false; 467 EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false)); 468 MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY); 469 470 // Workaround for b/22542869, if the RecentsActivity is started again, but without going 471 // through SystemUI, we need to reset the config launch flags to ensure that we do not 472 // wait on the system to send a signal that was never queued. 473 RecentsConfiguration config = Recents.getConfiguration(); 474 RecentsActivityLaunchState launchState = config.getLaunchState(); 475 launchState.reset(); 476 } 477 478 @Override 479 protected void onDestroy() { 480 super.onDestroy(); 481 482 // In the case that the activity finished on startup, just skip the unregistration below 483 if (mFinishedOnStartup) { 484 return; 485 } 486 487 // Unregister the system broadcast receivers 488 unregisterReceiver(mSystemBroadcastReceiver); 489 490 // Unregister any broadcast receivers for the task loader 491 mPackageMonitor.unregister(); 492 493 EventBus.getDefault().unregister(this); 494 } 495 496 @Override 497 public void onAttachedToWindow() { 498 super.onAttachedToWindow(); 499 EventBus.getDefault().register(mScrimViews, EVENT_BUS_PRIORITY); 500 } 501 502 @Override 503 public void onDetachedFromWindow() { 504 super.onDetachedFromWindow(); 505 EventBus.getDefault().unregister(mScrimViews); 506 } 507 508 @Override 509 public void onTrimMemory(int level) { 510 RecentsTaskLoader loader = Recents.getTaskLoader(); 511 if (loader != null) { 512 loader.onTrimMemory(level); 513 } 514 } 515 516 @Override 517 public boolean onKeyDown(int keyCode, KeyEvent event) { 518 switch (keyCode) { 519 case KeyEvent.KEYCODE_TAB: { 520 int altTabKeyDelay = getResources().getInteger(R.integer.recents_alt_tab_key_delay); 521 boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() - 522 mLastTabKeyEventTime) > altTabKeyDelay; 523 if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) { 524 // Focus the next task in the stack 525 final boolean backward = event.isShiftPressed(); 526 if (backward) { 527 EventBus.getDefault().send(new FocusPreviousTaskViewEvent()); 528 } else { 529 EventBus.getDefault().send( 530 new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */)); 531 } 532 mLastTabKeyEventTime = SystemClock.elapsedRealtime(); 533 534 // In the case of another ALT event, don't ignore the next release 535 if (event.isAltPressed()) { 536 mIgnoreAltTabRelease = false; 537 } 538 } 539 return true; 540 } 541 case KeyEvent.KEYCODE_DPAD_UP: { 542 EventBus.getDefault().send( 543 new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */)); 544 return true; 545 } 546 case KeyEvent.KEYCODE_DPAD_DOWN: { 547 EventBus.getDefault().send(new FocusPreviousTaskViewEvent()); 548 return true; 549 } 550 case KeyEvent.KEYCODE_DEL: 551 case KeyEvent.KEYCODE_FORWARD_DEL: { 552 if (event.getRepeatCount() <= 0) { 553 EventBus.getDefault().send(new DismissFocusedTaskViewEvent()); 554 555 // Keep track of deletions by keyboard 556 MetricsLogger.histogram(this, "overview_task_dismissed_source", 557 Constants.Metrics.DismissSourceKeyboard); 558 return true; 559 } 560 } 561 default: 562 break; 563 } 564 return super.onKeyDown(keyCode, event); 565 } 566 567 @Override 568 public void onUserInteraction() { 569 EventBus.getDefault().send(mUserInteractionEvent); 570 } 571 572 @Override 573 public void onBackPressed() { 574 // Back behaves like the recents button so just trigger a toggle event 575 EventBus.getDefault().send(new ToggleRecentsEvent()); 576 } 577 578 /**** EventBus events ****/ 579 580 public final void onBusEvent(ToggleRecentsEvent event) { 581 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); 582 if (launchState.launchedFromHome) { 583 dismissRecentsToHome(true /* animateTaskViews */); 584 } else { 585 dismissRecentsToLaunchTargetTaskOrHome(); 586 } 587 } 588 589 public final void onBusEvent(IterateRecentsEvent event) { 590 final RecentsDebugFlags debugFlags = Recents.getDebugFlags(); 591 592 // Start dozing after the recents button is clicked 593 int timerIndicatorDuration = 0; 594 if (debugFlags.isFastToggleRecentsEnabled()) { 595 timerIndicatorDuration = getResources().getInteger( 596 R.integer.recents_subsequent_auto_advance_duration); 597 598 mIterateTrigger.setDozeDuration(timerIndicatorDuration); 599 if (!mIterateTrigger.isDozing()) { 600 mIterateTrigger.startDozing(); 601 } else { 602 mIterateTrigger.poke(); 603 } 604 } 605 606 // Focus the next task 607 EventBus.getDefault().send(new FocusNextTaskViewEvent(timerIndicatorDuration)); 608 609 MetricsLogger.action(this, MetricsEvent.ACTION_OVERVIEW_PAGE); 610 } 611 612 public final void onBusEvent(UserInteractionEvent event) { 613 // Stop the fast-toggle dozer 614 mIterateTrigger.stopDozing(); 615 } 616 617 public final void onBusEvent(HideRecentsEvent event) { 618 if (event.triggeredFromAltTab) { 619 // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app 620 if (!mIgnoreAltTabRelease) { 621 dismissRecentsToFocusedTaskOrHome(); 622 } 623 } else if (event.triggeredFromHomeKey) { 624 dismissRecentsToHome(true /* animateTaskViews */); 625 626 // Cancel any pending dozes 627 EventBus.getDefault().send(mUserInteractionEvent); 628 } else { 629 // Do nothing 630 } 631 } 632 633 public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) { 634 EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(true)); 635 mRecentsView.getViewTreeObserver().addOnPreDrawListener(this); 636 mRecentsView.invalidate(); 637 } 638 639 public final void onBusEvent(ExitRecentsWindowFirstAnimationFrameEvent event) { 640 if (mRecentsView.isLastTaskLaunchedFreeform()) { 641 EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(false)); 642 } 643 mRecentsView.getViewTreeObserver().addOnPreDrawListener(this); 644 mRecentsView.invalidate(); 645 } 646 647 public final void onBusEvent(DockedFirstAnimationFrameEvent event) { 648 mRecentsView.getViewTreeObserver().addOnPreDrawListener(this); 649 mRecentsView.invalidate(); 650 } 651 652 public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) { 653 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); 654 int launchToTaskId = launchState.launchedToTaskId; 655 if (launchToTaskId != -1 && 656 (event.launchTask == null || launchToTaskId != event.launchTask.key.id)) { 657 SystemServicesProxy ssp = Recents.getSystemServices(); 658 ssp.cancelWindowTransition(launchState.launchedToTaskId); 659 ssp.cancelThumbnailTransition(getTaskId()); 660 } 661 } 662 663 public final void onBusEvent(ShowApplicationInfoEvent event) { 664 // Create a new task stack with the application info details activity 665 Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, 666 Uri.fromParts("package", event.task.key.getComponent().getPackageName(), null)); 667 intent.setComponent(intent.resolveActivity(getPackageManager())); 668 TaskStackBuilder.create(this) 669 .addNextIntentWithParentStack(intent).startActivities(null, 670 new UserHandle(event.task.key.userId)); 671 672 // Keep track of app-info invocations 673 MetricsLogger.count(this, "overview_app_info", 1); 674 } 675 676 public final void onBusEvent(ShowIncompatibleAppOverlayEvent event) { 677 if (mIncompatibleAppOverlay == null) { 678 mIncompatibleAppOverlay = Utilities.findViewStubById(this, 679 R.id.incompatible_app_overlay_stub).inflate(); 680 mIncompatibleAppOverlay.setWillNotDraw(false); 681 mIncompatibleAppOverlay.setVisibility(View.VISIBLE); 682 } 683 mIncompatibleAppOverlay.animate() 684 .alpha(1f) 685 .setDuration(INCOMPATIBLE_APP_ALPHA_DURATION) 686 .setInterpolator(Interpolators.ALPHA_IN) 687 .start(); 688 } 689 690 public final void onBusEvent(HideIncompatibleAppOverlayEvent event) { 691 if (mIncompatibleAppOverlay != null) { 692 mIncompatibleAppOverlay.animate() 693 .alpha(0f) 694 .setDuration(INCOMPATIBLE_APP_ALPHA_DURATION) 695 .setInterpolator(Interpolators.ALPHA_OUT) 696 .start(); 697 } 698 } 699 700 public final void onBusEvent(DeleteTaskDataEvent event) { 701 // Remove any stored data from the loader 702 RecentsTaskLoader loader = Recents.getTaskLoader(); 703 loader.deleteTaskData(event.task, false); 704 705 // Remove the task from activity manager 706 SystemServicesProxy ssp = Recents.getSystemServices(); 707 ssp.removeTask(event.task.key.id); 708 } 709 710 public final void onBusEvent(AllTaskViewsDismissedEvent event) { 711 SystemServicesProxy ssp = Recents.getSystemServices(); 712 if (ssp.hasDockedTask()) { 713 mRecentsView.showEmptyView(event.msgResId); 714 } else { 715 // Just go straight home (no animation necessary because there are no more task views) 716 dismissRecentsToHome(false /* animateTaskViews */); 717 } 718 719 // Keep track of all-deletions 720 MetricsLogger.count(this, "overview_task_all_dismissed", 1); 721 } 722 723 public final void onBusEvent(LaunchTaskSucceededEvent event) { 724 MetricsLogger.histogram(this, "overview_task_launch_index", event.taskIndexFromStackFront); 725 } 726 727 public final void onBusEvent(LaunchTaskFailedEvent event) { 728 // Return to Home 729 dismissRecentsToHome(true /* animateTaskViews */); 730 731 MetricsLogger.count(this, "overview_task_launch_failed", 1); 732 } 733 734 public final void onBusEvent(ScreenPinningRequestEvent event) { 735 MetricsLogger.count(this, "overview_screen_pinned", 1); 736 } 737 738 public final void onBusEvent(DebugFlagsChangedEvent event) { 739 // Just finish recents so that we can reload the flags anew on the next instantiation 740 finish(); 741 } 742 743 public final void onBusEvent(StackViewScrolledEvent event) { 744 // Once the user has scrolled while holding alt-tab, then we should ignore the release of 745 // the key 746 mIgnoreAltTabRelease = true; 747 } 748 749 public final void onBusEvent(final DragEndEvent event) { 750 // Handle the case where we drop onto a dock region 751 if (event.dropTarget instanceof TaskStack.DockState) { 752 mScrimViews.animateScrimToCurrentNavBarState(false /* hasStackTasks */); 753 } 754 } 755 756 @Override 757 public boolean onPreDraw() { 758 mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this); 759 // We post to make sure that this information is delivered after this traversals is 760 // finished. 761 mRecentsView.post(new Runnable() { 762 @Override 763 public void run() { 764 Recents.getSystemServices().endProlongedAnimations(); 765 } 766 }); 767 return true; 768 } 769 770 @Override 771 public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 772 super.dump(prefix, fd, writer, args); 773 EventBus.getDefault().dump(prefix, writer); 774 775 String id = Integer.toHexString(System.identityHashCode(this)); 776 777 writer.print(prefix); writer.print(TAG); 778 writer.print(" visible="); writer.print(mIsVisible ? "Y" : "N"); 779 writer.print(" [0x"); writer.print(id); writer.print("]"); 780 writer.println(); 781 782 if (mRecentsView != null) { 783 mRecentsView.dump(prefix, writer); 784 } 785 } 786} 787