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