RecentsActivity.java revision a91c293be26b2deb5434eb827a800fa0c80dc92c
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.appwidget.AppWidgetHostView; 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.SharedPreferences; 30import android.os.Bundle; 31import android.os.UserHandle; 32import android.util.Pair; 33import android.view.KeyEvent; 34import android.view.View; 35import android.view.ViewStub; 36import android.widget.Toast; 37import com.android.systemui.R; 38import com.android.systemui.recents.misc.DebugTrigger; 39import com.android.systemui.recents.misc.ReferenceCountedTrigger; 40import com.android.systemui.recents.misc.SystemServicesProxy; 41import com.android.systemui.recents.misc.Utilities; 42import com.android.systemui.recents.model.RecentsTaskLoadPlan; 43import com.android.systemui.recents.model.RecentsTaskLoader; 44import com.android.systemui.recents.model.SpaceNode; 45import com.android.systemui.recents.model.Task; 46import com.android.systemui.recents.model.TaskStack; 47import com.android.systemui.recents.views.DebugOverlayView; 48import com.android.systemui.recents.views.RecentsView; 49import com.android.systemui.recents.views.SystemBarScrimViews; 50import com.android.systemui.recents.views.ViewAnimation; 51 52import java.lang.ref.WeakReference; 53import java.lang.reflect.InvocationTargetException; 54import java.util.ArrayList; 55 56/** 57 * The main Recents activity that is started from AlternateRecentsComponent. 58 */ 59public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks, 60 RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks, 61 DebugOverlayView.DebugOverlayViewCallbacks { 62 63 RecentsConfiguration mConfig; 64 boolean mVisible; 65 long mLastTabKeyEventTime; 66 67 // Top level views 68 RecentsView mRecentsView; 69 SystemBarScrimViews mScrimViews; 70 ViewStub mEmptyViewStub; 71 ViewStub mDebugOverlayStub; 72 View mEmptyView; 73 DebugOverlayView mDebugOverlay; 74 75 // Search AppWidget 76 RecentsAppWidgetHost mAppWidgetHost; 77 AppWidgetProviderInfo mSearchAppWidgetInfo; 78 AppWidgetHostView mSearchAppWidgetHostView; 79 80 // Runnables to finish the Recents activity 81 FinishRecentsRunnable mFinishLaunchHomeRunnable; 82 83 /** 84 * A common Runnable to finish Recents either by calling finish() (with a custom animation) or 85 * launching Home with some ActivityOptions. Generally we always launch home when we exit 86 * Recents rather than just finishing the activity since we don't know what is behind Recents in 87 * the task stack. The only case where we finish() directly is when we are cancelling the full 88 * screen transition from the app. 89 */ 90 class FinishRecentsRunnable implements Runnable { 91 Intent mLaunchIntent; 92 ActivityOptions mLaunchOpts; 93 94 /** 95 * Creates a finish runnable that starts the specified intent, using the given 96 * ActivityOptions. 97 */ 98 public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) { 99 mLaunchIntent = launchIntent; 100 mLaunchOpts = opts; 101 } 102 103 @Override 104 public void run() { 105 // Mark Recents as no longer visible 106 onRecentsActivityVisibilityChanged(false); 107 // Finish Recents 108 if (mLaunchIntent != null) { 109 if (mLaunchOpts != null) { 110 startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT); 111 } else { 112 startActivityAsUser(mLaunchIntent, UserHandle.CURRENT); 113 } 114 } else { 115 finish(); 116 overridePendingTransition(R.anim.recents_to_launcher_enter, 117 R.anim.recents_to_launcher_exit); 118 } 119 } 120 } 121 122 /** 123 * Broadcast receiver to handle messages from AlternateRecentsComponent. 124 */ 125 final BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() { 126 @Override 127 public void onReceive(Context context, Intent intent) { 128 String action = intent.getAction(); 129 if (action.equals(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY)) { 130 // Mark Recents as no longer visible 131 AlternateRecentsComponent.notifyVisibilityChanged(false); 132 if (intent.getBooleanExtra(AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false)) { 133 // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app 134 dismissRecentsToFocusedTaskOrHome(false); 135 } else if (intent.getBooleanExtra(AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_HOME_KEY, false)) { 136 // Otherwise, dismiss Recents to Home 137 dismissRecentsToHome(true); 138 } else { 139 // Do nothing, another activity is being launched on top of Recents 140 } 141 } else if (action.equals(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY)) { 142 // If we are toggling Recents, then first unfilter any filtered stacks first 143 dismissRecentsToFocusedTaskOrHome(true); 144 } else if (action.equals(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION)) { 145 // Trigger the enter animation 146 onEnterAnimationTriggered(); 147 // Notify the fallback receiver that we have successfully got the broadcast 148 // See AlternateRecentsComponent.onAnimationStarted() 149 setResultCode(Activity.RESULT_OK); 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 dismissRecentsToHome(false); 164 // Preload the metadata for all tasks in the background 165 RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); 166 RecentsTaskLoadPlan plan = loader.createLoadPlan(context); 167 loader.preloadTasks(plan, true /* isTopTaskHome */); 168 } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) { 169 // When the search activity changes, update the Search widget 170 refreshSearchWidget(); 171 } 172 } 173 }; 174 175 /** 176 * A custom debug trigger to listen for a debug key chord. 177 */ 178 final DebugTrigger mDebugTrigger = new DebugTrigger(new Runnable() { 179 @Override 180 public void run() { 181 onDebugModeTriggered(); 182 } 183 }); 184 185 /** Updates the set of recent tasks */ 186 void updateRecentsTasks(Intent launchIntent) { 187 // Update the configuration based on the launch intent 188 boolean fromSearchHome = launchIntent.getBooleanExtra( 189 AlternateRecentsComponent.EXTRA_FROM_SEARCH_HOME, false); 190 int numVisibleTasks = launchIntent.getIntExtra( 191 AlternateRecentsComponent.EXTRA_NUM_VISIBLE_TASKS, 0); 192 int numVisibleThumbnails = launchIntent.getIntExtra( 193 AlternateRecentsComponent.EXTRA_NUM_VISIBLE_THUMBNAILS, 0); 194 mConfig.launchedFromHome = fromSearchHome || launchIntent.getBooleanExtra( 195 AlternateRecentsComponent.EXTRA_FROM_HOME, false); 196 mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra( 197 AlternateRecentsComponent.EXTRA_FROM_APP_THUMBNAIL, false); 198 mConfig.launchedToTaskId = launchIntent.getIntExtra( 199 AlternateRecentsComponent.EXTRA_FROM_TASK_ID, -1); 200 mConfig.launchedWithAltTab = launchIntent.getBooleanExtra( 201 AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false); 202 mConfig.launchedReuseTaskStackViews = launchIntent.getBooleanExtra( 203 AlternateRecentsComponent.EXTRA_REUSE_TASK_STACK_VIEWS, false); 204 205 // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent 206 // reconstructing the task stack 207 RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); 208 RecentsTaskLoadPlan plan = AlternateRecentsComponent.consumeInstanceLoadPlan(); 209 if (plan == null) { 210 plan = loader.createLoadPlan(this); 211 loader.preloadTasks(plan, mConfig.launchedFromHome); 212 } 213 214 // Start loading tasks according to the load plan 215 RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); 216 loadOpts.runningTaskId = mConfig.launchedToTaskId; 217 loadOpts.numVisibleTasks = numVisibleTasks; 218 loadOpts.numVisibleTaskThumbnails = numVisibleThumbnails; 219 loader.loadTasks(this, plan, loadOpts); 220 221 SpaceNode root = plan.getSpaceNode(); 222 ArrayList<TaskStack> stacks = root.getStacks(); 223 boolean hasTasks = root.hasTasks(); 224 if (hasTasks) { 225 mRecentsView.setTaskStacks(stacks); 226 } 227 mConfig.launchedWithNoRecentTasks = !hasTasks; 228 229 // Create the home intent runnable 230 Intent homeIntent = new Intent(Intent.ACTION_MAIN, null); 231 homeIntent.addCategory(Intent.CATEGORY_HOME); 232 homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 233 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 234 mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent, 235 ActivityOptions.makeCustomAnimation(this, 236 fromSearchHome ? R.anim.recents_to_search_launcher_enter : 237 R.anim.recents_to_launcher_enter, 238 fromSearchHome ? R.anim.recents_to_search_launcher_exit : 239 R.anim.recents_to_launcher_exit)); 240 241 // Mark the task that is the launch target 242 int taskStackCount = stacks.size(); 243 if (mConfig.launchedToTaskId != -1) { 244 for (int i = 0; i < taskStackCount; i++) { 245 TaskStack stack = stacks.get(i); 246 ArrayList<Task> tasks = stack.getTasks(); 247 int taskCount = tasks.size(); 248 for (int j = 0; j < taskCount; j++) { 249 Task t = tasks.get(j); 250 if (t.key.id == mConfig.launchedToTaskId) { 251 t.isLaunchTarget = true; 252 break; 253 } 254 } 255 } 256 } 257 258 // Update the top level view's visibilities 259 if (mConfig.launchedWithNoRecentTasks) { 260 if (mEmptyView == null) { 261 mEmptyView = mEmptyViewStub.inflate(); 262 } 263 mEmptyView.setVisibility(View.VISIBLE); 264 mRecentsView.setSearchBarVisibility(View.GONE); 265 } else { 266 if (mEmptyView != null) { 267 mEmptyView.setVisibility(View.GONE); 268 } 269 if (mRecentsView.hasSearchBar()) { 270 mRecentsView.setSearchBarVisibility(View.VISIBLE); 271 } else { 272 addSearchBarAppWidgetView(); 273 } 274 } 275 276 // Animate the SystemUI scrims into view 277 mScrimViews.prepareEnterRecentsAnimation(); 278 } 279 280 /** Attempts to allocate and bind the search bar app widget */ 281 void bindSearchBarAppWidget() { 282 if (Constants.DebugFlags.App.EnableSearchLayout) { 283 SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); 284 285 // Reset the host view and widget info 286 mSearchAppWidgetHostView = null; 287 mSearchAppWidgetInfo = null; 288 289 // Try and load the app widget id from the settings 290 int appWidgetId = mConfig.searchBarAppWidgetId; 291 if (appWidgetId >= 0) { 292 mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId); 293 if (mSearchAppWidgetInfo == null) { 294 // If there is no actual widget associated with that id, then delete it and 295 // prepare to bind another app widget in its place 296 ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId); 297 appWidgetId = -1; 298 } 299 } 300 301 // If there is no id, then bind a new search app widget 302 if (appWidgetId < 0) { 303 Pair<Integer, AppWidgetProviderInfo> widgetInfo = 304 ssp.bindSearchAppWidget(mAppWidgetHost); 305 if (widgetInfo != null) { 306 // Save the app widget id into the settings 307 mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first); 308 mSearchAppWidgetInfo = widgetInfo.second; 309 } 310 } 311 } 312 } 313 314 /** Creates the search bar app widget view */ 315 void addSearchBarAppWidgetView() { 316 if (Constants.DebugFlags.App.EnableSearchLayout) { 317 int appWidgetId = mConfig.searchBarAppWidgetId; 318 if (appWidgetId >= 0) { 319 mSearchAppWidgetHostView = mAppWidgetHost.createView(this, appWidgetId, 320 mSearchAppWidgetInfo); 321 Bundle opts = new Bundle(); 322 opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, 323 AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX); 324 mSearchAppWidgetHostView.updateAppWidgetOptions(opts); 325 // Set the padding to 0 for this search widget 326 mSearchAppWidgetHostView.setPadding(0, 0, 0, 0); 327 mRecentsView.setSearchBar(mSearchAppWidgetHostView); 328 } else { 329 mRecentsView.setSearchBar(null); 330 } 331 } 332 } 333 334 /** Dismisses recents if we are already visible and the intent is to toggle the recents view */ 335 boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) { 336 if (mVisible) { 337 // If we currently have filtered stacks, then unfilter those first 338 if (checkFilteredStackState && 339 mRecentsView.unfilterFilteredStacks()) return true; 340 // If we have a focused Task, launch that Task now 341 if (mRecentsView.launchFocusedTask()) return true; 342 // If we launched from Home, then return to Home 343 if (mConfig.launchedFromHome) { 344 dismissRecentsToHomeRaw(true); 345 return true; 346 } 347 // Otherwise, try and return to the Task that Recents was launched from 348 if (mRecentsView.launchPreviousTask()) return true; 349 // If none of the other cases apply, then just go Home 350 dismissRecentsToHomeRaw(true); 351 return true; 352 } 353 return false; 354 } 355 356 /** Dismisses Recents directly to Home. */ 357 void dismissRecentsToHomeRaw(boolean animated) { 358 if (animated) { 359 ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, 360 null, mFinishLaunchHomeRunnable, null); 361 mRecentsView.startExitToHomeAnimation( 362 new ViewAnimation.TaskViewExitContext(exitTrigger)); 363 } else { 364 mFinishLaunchHomeRunnable.run(); 365 } 366 } 367 368 /** Dismisses Recents directly to Home if we currently aren't transitioning. */ 369 boolean dismissRecentsToHome(boolean animated) { 370 if (mVisible) { 371 // Return to Home 372 dismissRecentsToHomeRaw(animated); 373 return true; 374 } 375 return false; 376 } 377 378 /** Called with the activity is first created. */ 379 @Override 380 public void onCreate(Bundle savedInstanceState) { 381 super.onCreate(savedInstanceState); 382 // For the non-primary user, ensure that the SystemSericesProxy is initialized 383 RecentsTaskLoader.initialize(this); 384 385 // Initialize the loader and the configuration 386 mConfig = RecentsConfiguration.reinitialize(this, 387 RecentsTaskLoader.getInstance().getSystemServicesProxy()); 388 389 // Initialize the widget host (the host id is static and does not change) 390 mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId); 391 392 // Set the Recents layout 393 setContentView(R.layout.recents); 394 mRecentsView = (RecentsView) findViewById(R.id.recents_view); 395 mRecentsView.setCallbacks(this); 396 mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | 397 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | 398 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); 399 mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub); 400 mDebugOverlayStub = (ViewStub) findViewById(R.id.debug_overlay_stub); 401 mScrimViews = new SystemBarScrimViews(this, mConfig); 402 inflateDebugOverlay(); 403 404 // Bind the search app widget when we first start up 405 bindSearchBarAppWidget(); 406 // Update the recent tasks 407 updateRecentsTasks(getIntent()); 408 409 // Register the broadcast receiver to handle messages when the screen is turned off 410 IntentFilter filter = new IntentFilter(); 411 filter.addAction(Intent.ACTION_SCREEN_OFF); 412 filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); 413 registerReceiver(mSystemBroadcastReceiver, filter); 414 415 // Private API calls to make the shadows look better 416 try { 417 Utilities.setShadowProperty("ambientRatio", String.valueOf(1.5f)); 418 } catch (IllegalAccessException e) { 419 e.printStackTrace(); 420 } catch (InvocationTargetException e) { 421 e.printStackTrace(); 422 } 423 424 // Update if we are getting a configuration change 425 if (savedInstanceState != null) { 426 // Update RecentsConfiguration 427 mConfig = RecentsConfiguration.reinitialize(this, 428 RecentsTaskLoader.getInstance().getSystemServicesProxy()); 429 mConfig.updateOnConfigurationChange(); 430 // Trigger the enter animation 431 onEnterAnimationTriggered(); 432 } 433 434 // Start listening for widget package changes if there is one bound, post it since we don't 435 // want it stalling the startup 436 if (mConfig.searchBarAppWidgetId >= 0) { 437 final WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks> callback = 438 new WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks>(this); 439 mRecentsView.post(new Runnable() { 440 @Override 441 public void run() { 442 RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks cb = callback.get(); 443 if (cb != null) { 444 mAppWidgetHost.startListening(cb); 445 } 446 } 447 }); 448 } 449 } 450 451 /** Inflates the debug overlay if debug mode is enabled. */ 452 void inflateDebugOverlay() { 453 if (!Constants.DebugFlags.App.EnableDebugMode) return; 454 455 if (mConfig.debugModeEnabled && mDebugOverlay == null) { 456 // Inflate the overlay and seek bars 457 mDebugOverlay = (DebugOverlayView) mDebugOverlayStub.inflate(); 458 mDebugOverlay.setCallbacks(this); 459 mRecentsView.setDebugOverlay(mDebugOverlay); 460 } 461 } 462 463 /** Handles changes to the activity visibility. */ 464 void onRecentsActivityVisibilityChanged(boolean visible) { 465 if (!visible) { 466 AlternateRecentsComponent.notifyVisibilityChanged(visible); 467 } 468 mVisible = visible; 469 } 470 471 @Override 472 protected void onNewIntent(Intent intent) { 473 super.onNewIntent(intent); 474 setIntent(intent); 475 476 // Reinitialize the configuration 477 RecentsConfiguration.reinitialize(this, RecentsTaskLoader.getInstance().getSystemServicesProxy()); 478 479 // Clear any debug rects 480 if (mDebugOverlay != null) { 481 mDebugOverlay.clear(); 482 } 483 484 // Update the recent tasks 485 updateRecentsTasks(intent); 486 } 487 488 @Override 489 protected void onStart() { 490 super.onStart(); 491 492 // Register the broadcast receiver to handle messages from our service 493 IntentFilter filter = new IntentFilter(); 494 filter.addAction(AlternateRecentsComponent.ACTION_HIDE_RECENTS_ACTIVITY); 495 filter.addAction(AlternateRecentsComponent.ACTION_TOGGLE_RECENTS_ACTIVITY); 496 filter.addAction(AlternateRecentsComponent.ACTION_START_ENTER_ANIMATION); 497 registerReceiver(mServiceBroadcastReceiver, filter); 498 499 // Register any broadcast receivers for the task loader 500 RecentsTaskLoader.getInstance().registerReceivers(this, mRecentsView); 501 } 502 503 @Override 504 protected void onResume() { 505 super.onResume(); 506 507 // Mark Recents as visible 508 onRecentsActivityVisibilityChanged(true); 509 } 510 511 @Override 512 protected void onStop() { 513 super.onStop(); 514 515 // Notify the views that we are no longer visible 516 mRecentsView.onRecentsHidden(); 517 518 // Unregister the RecentsService receiver 519 unregisterReceiver(mServiceBroadcastReceiver); 520 521 // Unregister any broadcast receivers for the task loader 522 RecentsTaskLoader.getInstance().unregisterReceivers(); 523 } 524 525 @Override 526 protected void onDestroy() { 527 super.onDestroy(); 528 529 // Unregister the system broadcast receivers 530 unregisterReceiver(mSystemBroadcastReceiver); 531 532 // Stop listening for widget package changes if there was one bound 533 if (mAppWidgetHost.isListening()) { 534 mAppWidgetHost.stopListening(); 535 } 536 } 537 538 public void onEnterAnimationTriggered() { 539 // Try and start the enter animation (or restart it on configuration changed) 540 ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null); 541 mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext(t)); 542 543 // Animate the SystemUI scrim views 544 mScrimViews.startEnterRecentsAnimation(); 545 } 546 547 @Override 548 public void onTrimMemory(int level) { 549 RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); 550 if (loader != null) { 551 loader.onTrimMemory(level); 552 } 553 } 554 555 @Override 556 public boolean onKeyDown(int keyCode, KeyEvent event) { 557 switch (keyCode) { 558 case KeyEvent.KEYCODE_TAB: { 559 boolean hasRepKeyTimeElapsed = (System.currentTimeMillis() - 560 mLastTabKeyEventTime) > mConfig.altTabKeyDelay; 561 if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) { 562 // Focus the next task in the stack 563 final boolean backward = event.isShiftPressed(); 564 mRecentsView.focusNextTask(!backward); 565 mLastTabKeyEventTime = System.currentTimeMillis(); 566 } 567 return true; 568 } 569 case KeyEvent.KEYCODE_DPAD_UP: { 570 mRecentsView.focusNextTask(true); 571 return true; 572 } 573 case KeyEvent.KEYCODE_DPAD_DOWN: { 574 mRecentsView.focusNextTask(false); 575 return true; 576 } 577 case KeyEvent.KEYCODE_DEL: 578 case KeyEvent.KEYCODE_FORWARD_DEL: { 579 mRecentsView.dismissFocusedTask(); 580 return true; 581 } 582 default: 583 break; 584 } 585 // Pass through the debug trigger 586 mDebugTrigger.onKeyEvent(keyCode); 587 return super.onKeyDown(keyCode, event); 588 } 589 590 @Override 591 public void onUserInteraction() { 592 mRecentsView.onUserInteraction(); 593 } 594 595 @Override 596 public void onBackPressed() { 597 // Test mode where back does not do anything 598 if (mConfig.debugModeEnabled) return; 599 600 // Dismiss Recents to the focused Task or Home 601 dismissRecentsToFocusedTaskOrHome(true); 602 } 603 604 /** Called when debug mode is triggered */ 605 public void onDebugModeTriggered() { 606 if (mConfig.developerOptionsEnabled) { 607 SharedPreferences settings = getSharedPreferences(getPackageName(), 0); 608 if (settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false)) { 609 // Disable the debug mode 610 settings.edit().remove(Constants.Values.App.Key_DebugModeEnabled).apply(); 611 mConfig.debugModeEnabled = false; 612 inflateDebugOverlay(); 613 if (mDebugOverlay != null) { 614 mDebugOverlay.disable(); 615 } 616 } else { 617 // Enable the debug mode 618 settings.edit().putBoolean(Constants.Values.App.Key_DebugModeEnabled, true).apply(); 619 mConfig.debugModeEnabled = true; 620 inflateDebugOverlay(); 621 if (mDebugOverlay != null) { 622 mDebugOverlay.enable(); 623 } 624 } 625 Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + ") " + 626 (mConfig.debugModeEnabled ? "Enabled" : "Disabled") + ", please restart Recents now", 627 Toast.LENGTH_SHORT).show(); 628 } 629 } 630 631 /**** RecentsView.RecentsViewCallbacks Implementation ****/ 632 633 @Override 634 public void onExitToHomeAnimationTriggered() { 635 // Animate the SystemUI scrim views out 636 mScrimViews.startExitRecentsAnimation(); 637 } 638 639 @Override 640 public void onTaskViewClicked() { 641 // Mark recents as no longer visible 642 onRecentsActivityVisibilityChanged(false); 643 } 644 645 @Override 646 public void onTaskLaunchFailed() { 647 // Return to Home 648 dismissRecentsToHomeRaw(true); 649 } 650 651 @Override 652 public void onAllTaskViewsDismissed() { 653 mFinishLaunchHomeRunnable.run(); 654 } 655 656 /**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/ 657 658 @Override 659 public void refreshSearchWidget() { 660 bindSearchBarAppWidget(); 661 addSearchBarAppWidgetView(); 662 } 663 664 /**** DebugOverlayView.DebugOverlayViewCallbacks ****/ 665 666 @Override 667 public void onPrimarySeekBarChanged(float progress) { 668 // Do nothing 669 } 670 671 @Override 672 public void onSecondarySeekBarChanged(float progress) { 673 // Do nothing 674 } 675} 676