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