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