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