RecentsActivity.java revision 1e44934f0213caf5c434f63a69c9f05fa76e891d
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.Console; 39import com.android.systemui.recents.misc.DebugTrigger; 40import com.android.systemui.recents.misc.ReferenceCountedTrigger; 41import com.android.systemui.recents.misc.SystemServicesProxy; 42import com.android.systemui.recents.misc.Utilities; 43import com.android.systemui.recents.model.RecentsTaskLoader; 44import com.android.systemui.recents.model.SpaceNode; 45import com.android.systemui.recents.model.TaskStack; 46import com.android.systemui.recents.views.FullscreenTransitionOverlayView; 47import com.android.systemui.recents.views.RecentsView; 48import com.android.systemui.recents.views.SystemBarScrimViews; 49import com.android.systemui.recents.views.ViewAnimation; 50 51import java.lang.reflect.InvocationTargetException; 52import java.util.ArrayList; 53 54/* Activity */ 55public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks, 56 RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks, 57 FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks { 58 59 final static String EXTRA_TRIGGERED_FROM_ALT_TAB = "extra_triggered_from_alt_tab"; 60 final static String ACTION_START_ENTER_ANIMATION = "action_start_enter_animation"; 61 final static String ACTION_TOGGLE_RECENTS_ACTIVITY = "action_toggle_recents_activity"; 62 final static String ACTION_HIDE_RECENTS_ACTIVITY = "action_hide_recents_activity"; 63 64 RecentsView mRecentsView; 65 SystemBarScrimViews mScrimViews; 66 ViewStub mEmptyViewStub; 67 View mEmptyView; 68 ViewStub mFullscreenOverlayStub; 69 FullscreenTransitionOverlayView mFullScreenOverlayView; 70 71 RecentsConfiguration mConfig; 72 73 RecentsAppWidgetHost mAppWidgetHost; 74 AppWidgetProviderInfo mSearchAppWidgetInfo; 75 AppWidgetHostView mSearchAppWidgetHostView; 76 77 boolean mVisible; 78 79 // Runnables to finish the Recents activity 80 FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable(true); 81 FinishRecentsRunnable mFinishLaunchHomeRunnable; 82 83 /** 84 * A Runnable to finish Recents either with/without a transition, and either by calling finish() 85 * or just launching the specified intent. 86 */ 87 class FinishRecentsRunnable implements Runnable { 88 boolean mUseCustomFinishTransition; 89 Intent mLaunchIntent; 90 ActivityOptions mLaunchOpts; 91 92 public FinishRecentsRunnable(boolean withTransition) { 93 mUseCustomFinishTransition = withTransition; 94 } 95 96 /** 97 * Creates a finish runnable that starts the specified intent, using the given 98 * ActivityOptions. 99 */ 100 public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) { 101 mLaunchIntent = launchIntent; 102 mLaunchOpts = opts; 103 } 104 105 @Override 106 public void run() { 107 // Mark Recents as no longer visible 108 AlternateRecentsComponent.notifyVisibilityChanged(false); 109 mVisible = false; 110 // Finish Recents 111 if (mLaunchIntent != null) { 112 if (mLaunchOpts != null) { 113 startActivityAsUser(mLaunchIntent, new UserHandle(UserHandle.USER_CURRENT)); 114 } else { 115 startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), 116 new UserHandle(UserHandle.USER_CURRENT)); 117 } 118 } else { 119 finish(); 120 if (mUseCustomFinishTransition) { 121 overridePendingTransition(R.anim.recents_to_launcher_enter, 122 R.anim.recents_to_launcher_exit); 123 } 124 } 125 } 126 } 127 128 // Broadcast receiver to handle messages from our RecentsService 129 final BroadcastReceiver mServiceBroadcastReceiver = new BroadcastReceiver() { 130 @Override 131 public void onReceive(Context context, Intent intent) { 132 String action = intent.getAction(); 133 if (Console.Enabled) { 134 Console.log(Constants.Log.App.SystemUIHandshake, 135 "[RecentsActivity|serviceBroadcast]", action, Console.AnsiRed); 136 } 137 if (action.equals(ACTION_HIDE_RECENTS_ACTIVITY)) { 138 if (intent.getBooleanExtra(EXTRA_TRIGGERED_FROM_ALT_TAB, false)) { 139 // Dismiss recents, launching the focused task 140 dismissRecentsIfVisible(); 141 } else { 142 // If we are mid-animation into Recents, then reverse it and finish 143 if (mFullScreenOverlayView == null || 144 !mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) { 145 // Otherwise, either finish Recents, or launch Home directly 146 ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context, 147 null, mFinishLaunchHomeRunnable, null); 148 mRecentsView.startExitToHomeAnimation( 149 new ViewAnimation.TaskViewExitContext(exitTrigger)); 150 } 151 } 152 } else if (action.equals(ACTION_TOGGLE_RECENTS_ACTIVITY)) { 153 // Try and unfilter and filtered stacks 154 if (!mRecentsView.unfilterFilteredStacks()) { 155 // If there are no filtered stacks, dismiss recents and launch the first task 156 dismissRecentsIfVisible(); 157 } 158 } else if (action.equals(ACTION_START_ENTER_ANIMATION)) { 159 // Try and start the enter animation (or restart it on configuration changed) 160 ReferenceCountedTrigger t = new ReferenceCountedTrigger(context, null, null, null); 161 mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext( 162 mFullScreenOverlayView, t)); 163 // Call our callback 164 onEnterAnimationTriggered(); 165 } 166 } 167 }; 168 169 // Broadcast receiver to handle messages from the system 170 final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() { 171 @Override 172 public void onReceive(Context context, Intent intent) { 173 String action = intent.getAction(); 174 if (action.equals(Intent.ACTION_SCREEN_OFF) && mVisible) { 175 mFinishLaunchHomeRunnable.run(); 176 } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) { 177 // Refresh the search widget 178 refreshSearchWidget(); 179 } 180 } 181 }; 182 183 // Debug trigger 184 final DebugTrigger mDebugTrigger = new DebugTrigger(new Runnable() { 185 @Override 186 public void run() { 187 onDebugModeTriggered(); 188 } 189 }); 190 191 /** Updates the set of recent tasks */ 192 void updateRecentsTasks(Intent launchIntent) { 193 RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); 194 SpaceNode root = loader.reload(this, Constants.Values.RecentsTaskLoader.PreloadFirstTasksCount); 195 ArrayList<TaskStack> stacks = root.getStacks(); 196 if (!stacks.isEmpty()) { 197 mRecentsView.setBSP(root); 198 } 199 200 // Update the configuration based on the launch intent 201 mConfig.launchedFromHome = launchIntent.getBooleanExtra( 202 AlternateRecentsComponent.EXTRA_FROM_HOME, false); 203 mConfig.launchedFromAppWithThumbnail = launchIntent.getBooleanExtra( 204 AlternateRecentsComponent.EXTRA_FROM_APP_THUMBNAIL, false); 205 mConfig.launchedFromAppWithScreenshot = launchIntent.getBooleanExtra( 206 AlternateRecentsComponent.EXTRA_FROM_APP_FULL_SCREENSHOT, false); 207 mConfig.launchedWithAltTab = launchIntent.getBooleanExtra( 208 AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false); 209 mConfig.launchedWithNoRecentTasks = !root.hasTasks(); 210 mConfig.launchedToTaskId = launchIntent.getIntExtra( 211 AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_TASK_ID, -1); 212 213 // Add the default no-recents layout 214 if (mEmptyView == null) { 215 mEmptyView = mEmptyViewStub.inflate(); 216 } 217 if (mConfig.launchedWithNoRecentTasks) { 218 mEmptyView.setVisibility(View.VISIBLE); 219 mRecentsView.setSearchBarVisibility(View.GONE); 220 } else { 221 mEmptyView.setVisibility(View.GONE); 222 if (mRecentsView.hasSearchBar()) { 223 mRecentsView.setSearchBarVisibility(View.VISIBLE); 224 } else { 225 addSearchBarAppWidgetView(); 226 } 227 } 228 229 // Show the scrim if we animate into Recents without window transitions 230 mScrimViews.prepareEnterRecentsAnimation(); 231 } 232 233 /** Attempts to allocate and bind the search bar app widget */ 234 void bindSearchBarAppWidget() { 235 if (Constants.DebugFlags.App.EnableSearchLayout) { 236 SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); 237 238 // Reset the host view and widget info 239 mSearchAppWidgetHostView = null; 240 mSearchAppWidgetInfo = null; 241 242 // Try and load the app widget id from the settings 243 int appWidgetId = mConfig.searchBarAppWidgetId; 244 if (appWidgetId >= 0) { 245 mSearchAppWidgetInfo = ssp.getAppWidgetInfo(appWidgetId); 246 if (mSearchAppWidgetInfo == null) { 247 // If there is no actual widget associated with that id, then delete it and 248 // prepare to bind another app widget in its place 249 ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId); 250 appWidgetId = -1; 251 } 252 if (Console.Enabled) { 253 Console.log(Constants.Log.App.SystemUIHandshake, 254 "[RecentsActivity|onCreate|settings|appWidgetId]", 255 "Id: " + appWidgetId, 256 Console.AnsiBlue); 257 } 258 } 259 260 // If there is no id, then bind a new search app widget 261 if (appWidgetId < 0) { 262 Pair<Integer, AppWidgetProviderInfo> widgetInfo = 263 ssp.bindSearchAppWidget(mAppWidgetHost); 264 if (widgetInfo != null) { 265 if (Console.Enabled) { 266 Console.log(Constants.Log.App.SystemUIHandshake, 267 "[RecentsActivity|onCreate|searchWidget]", 268 "Id: " + widgetInfo.first + " Info: " + widgetInfo.second, 269 Console.AnsiBlue); 270 } 271 272 // Save the app widget id into the settings 273 mConfig.updateSearchBarAppWidgetId(this, widgetInfo.first); 274 mSearchAppWidgetInfo = widgetInfo.second; 275 } 276 } 277 } 278 } 279 280 /** Creates the search bar app widget view */ 281 void addSearchBarAppWidgetView() { 282 if (Constants.DebugFlags.App.EnableSearchLayout) { 283 int appWidgetId = mConfig.searchBarAppWidgetId; 284 if (appWidgetId >= 0) { 285 if (Console.Enabled) { 286 Console.log(Constants.Log.App.SystemUIHandshake, 287 "[RecentsActivity|onCreate|addSearchAppWidgetView]", 288 "Id: " + appWidgetId, 289 Console.AnsiBlue); 290 } 291 mSearchAppWidgetHostView = mAppWidgetHost.createView(this, appWidgetId, 292 mSearchAppWidgetInfo); 293 Bundle opts = new Bundle(); 294 opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, 295 AppWidgetProviderInfo.WIDGET_CATEGORY_RECENTS); 296 mSearchAppWidgetHostView.updateAppWidgetOptions(opts); 297 // Set the padding to 0 for this search widget 298 mSearchAppWidgetHostView.setPadding(0, 0, 0, 0); 299 mRecentsView.setSearchBar(mSearchAppWidgetHostView); 300 } else { 301 mRecentsView.setSearchBar(null); 302 } 303 } 304 } 305 306 /** Dismisses recents if we are already visible and the intent is to toggle the recents view */ 307 boolean dismissRecentsIfVisible() { 308 if (mVisible) { 309 // If we are mid-animation into Recents, then reverse it and finish 310 if (mFullScreenOverlayView == null || 311 !mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) { 312 // If we have a focused task, then launch that task 313 if (!mRecentsView.launchFocusedTask()) { 314 if (mConfig.launchedFromHome) { 315 // Just start the animation out of recents 316 ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, 317 null, mFinishLaunchHomeRunnable, null); 318 mRecentsView.startExitToHomeAnimation( 319 new ViewAnimation.TaskViewExitContext(exitTrigger)); 320 } else { 321 // Otherwise, try and launch the first task 322 if (!mRecentsView.launchFirstTask()) { 323 // If there are no tasks, then just finish recents 324 mFinishLaunchHomeRunnable.run(); 325 } 326 } 327 } 328 } 329 return true; 330 } 331 return false; 332 } 333 334 /** Called with the activity is first created. */ 335 @Override 336 public void onCreate(Bundle savedInstanceState) { 337 super.onCreate(savedInstanceState); 338 if (Console.Enabled) { 339 Console.logDivider(Constants.Log.App.SystemUIHandshake); 340 Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onCreate]", 341 getIntent().getAction() + " visible: " + mVisible, Console.AnsiRed); 342 Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, 343 Constants.Log.App.TimeRecentsStartupKey, "onCreate"); 344 } 345 346 // Initialize the loader and the configuration 347 RecentsTaskLoader.initialize(this); 348 mConfig = RecentsConfiguration.reinitialize(this); 349 350 // Create the home intent runnable 351 Intent homeIntent = new Intent(Intent.ACTION_MAIN, null); 352 homeIntent.addCategory(Intent.CATEGORY_HOME); 353 homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 354 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 355 mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent, 356 ActivityOptions.makeCustomAnimation(this, R.anim.recents_to_launcher_enter, 357 R.anim.recents_to_launcher_exit)); 358 359 // Initialize the widget host (the host id is static and does not change) 360 mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId); 361 362 // Set the Recents layout 363 setContentView(R.layout.recents); 364 mRecentsView = (RecentsView) findViewById(R.id.recents_view); 365 mRecentsView.setCallbacks(this); 366 mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | 367 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | 368 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); 369 mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub); 370 mFullscreenOverlayStub = (ViewStub) findViewById(R.id.fullscreen_overlay_stub); 371 mScrimViews = new SystemBarScrimViews(this, mConfig); 372 373 // Bind the search app widget when we first start up 374 bindSearchBarAppWidget(); 375 // Update the recent tasks 376 updateRecentsTasks(getIntent()); 377 378 // Register the broadcast receiver to handle messages when the screen is turned off 379 IntentFilter filter = new IntentFilter(); 380 filter.addAction(Intent.ACTION_SCREEN_OFF); 381 filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); 382 registerReceiver(mSystemBroadcastReceiver, filter); 383 384 // Register any broadcast receivers for the task loader 385 RecentsTaskLoader.getInstance().registerReceivers(this, mRecentsView); 386 387 // Private API calls to make the shadows look better 388 try { 389 Utilities.setShadowProperty("ambientShadowStrength", String.valueOf(35f)); 390 Utilities.setShadowProperty("ambientRatio", String.valueOf(0.5f)); 391 } catch (IllegalAccessException e) { 392 e.printStackTrace(); 393 } catch (InvocationTargetException e) { 394 e.printStackTrace(); 395 } 396 397 // Prepare the screenshot transition if necessary 398 if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { 399 mFullScreenOverlayView = (FullscreenTransitionOverlayView) mFullscreenOverlayStub.inflate(); 400 mFullScreenOverlayView.setCallbacks(this); 401 mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot()); 402 } 403 404 // Update if we are getting a configuration change 405 if (savedInstanceState != null) { 406 mConfig.updateOnConfigurationChange(); 407 onConfigurationChange(); 408 } 409 } 410 411 void onConfigurationChange() { 412 // Try and start the enter animation (or restart it on configuration changed) 413 ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null); 414 mRecentsView.startEnterRecentsAnimation(new ViewAnimation.TaskViewEnterContext( 415 mFullScreenOverlayView, t)); 416 // Call our callback 417 onEnterAnimationTriggered(); 418 } 419 420 @Override 421 protected void onNewIntent(Intent intent) { 422 super.onNewIntent(intent); 423 setIntent(intent); 424 425 if (Console.Enabled) { 426 Console.logDivider(Constants.Log.App.SystemUIHandshake); 427 Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onNewIntent]", 428 intent.getAction() + " visible: " + mVisible, Console.AnsiRed); 429 Console.logTraceTime(Constants.Log.App.TimeRecentsStartup, 430 Constants.Log.App.TimeRecentsStartupKey, "onNewIntent"); 431 } 432 433 // Initialize the loader and the configuration 434 RecentsTaskLoader.initialize(this); 435 mConfig = RecentsConfiguration.reinitialize(this); 436 437 // Update the recent tasks 438 updateRecentsTasks(intent); 439 440 // Prepare the screenshot transition if necessary 441 if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { 442 mFullScreenOverlayView.prepareAnimateOnEnterRecents(AlternateRecentsComponent.getLastScreenshot()); 443 } 444 } 445 446 @Override 447 protected void onStart() { 448 if (Console.Enabled) { 449 Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStart]", "", 450 Console.AnsiRed); 451 } 452 super.onStart(); 453 454 // Register the broadcast receiver to handle messages from our service 455 IntentFilter filter = new IntentFilter(); 456 filter.addAction(ACTION_HIDE_RECENTS_ACTIVITY); 457 filter.addAction(ACTION_TOGGLE_RECENTS_ACTIVITY); 458 filter.addAction(ACTION_START_ENTER_ANIMATION); 459 registerReceiver(mServiceBroadcastReceiver, filter); 460 461 // Start listening for widget package changes if there is one bound 462 if (mConfig.searchBarAppWidgetId >= 0) { 463 mAppWidgetHost.startListening(this); 464 } 465 466 mVisible = true; 467 } 468 469 @Override 470 protected void onResume() { 471 if (Console.Enabled) { 472 Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onResume]", "", 473 Console.AnsiRed); 474 } 475 super.onResume(); 476 } 477 478 @Override 479 public void onAttachedToWindow() { 480 if (Console.Enabled) { 481 Console.log(Constants.Log.App.SystemUIHandshake, 482 "[RecentsActivity|onAttachedToWindow]", "", 483 Console.AnsiRed); 484 } 485 super.onAttachedToWindow(); 486 } 487 488 @Override 489 public void onDetachedFromWindow() { 490 if (Console.Enabled) { 491 Console.log(Constants.Log.App.SystemUIHandshake, 492 "[RecentsActivity|onDetachedFromWindow]", "", 493 Console.AnsiRed); 494 } 495 super.onDetachedFromWindow(); 496 } 497 498 @Override 499 protected void onPause() { 500 if (Console.Enabled) { 501 Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onPause]", "", 502 Console.AnsiRed); 503 } 504 super.onPause(); 505 } 506 507 @Override 508 protected void onStop() { 509 if (Console.Enabled) { 510 Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onStop]", "", 511 Console.AnsiRed); 512 } 513 super.onStop(); 514 515 // Unregister the RecentsService receiver 516 unregisterReceiver(mServiceBroadcastReceiver); 517 518 // Stop listening for widget package changes if there was one bound 519 if (mConfig.searchBarAppWidgetId >= 0) { 520 mAppWidgetHost.stopListening(); 521 } 522 } 523 524 @Override 525 protected void onDestroy() { 526 if (Console.Enabled) { 527 Console.log(Constants.Log.App.SystemUIHandshake, "[RecentsActivity|onDestroy]", "", 528 Console.AnsiRed); 529 } 530 super.onDestroy(); 531 532 // Unregister the screen off receiver 533 unregisterReceiver(mSystemBroadcastReceiver); 534 RecentsTaskLoader.getInstance().unregisterReceivers(); 535 } 536 537 @Override 538 public void onTrimMemory(int level) { 539 RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); 540 if (loader != null) { 541 loader.onTrimMemory(level); 542 } 543 } 544 545 @Override 546 public boolean onKeyDown(int keyCode, KeyEvent event) { 547 if (keyCode == KeyEvent.KEYCODE_TAB) { 548 // Focus the next task in the stack 549 final boolean backward = event.isShiftPressed(); 550 mRecentsView.focusNextTask(!backward); 551 return true; 552 } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { 553 mRecentsView.focusNextTask(true); 554 return true; 555 } else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { 556 mRecentsView.focusNextTask(false); 557 return true; 558 } 559 // Pass through the debug trigger 560 mDebugTrigger.onKeyEvent(keyCode); 561 return super.onKeyDown(keyCode, event); 562 } 563 564 @Override 565 public void onUserInteraction() { 566 mRecentsView.onUserInteraction(); 567 } 568 569 @Override 570 public void onBackPressed() { 571 // Test mode where back does not do anything 572 if (mConfig.debugModeEnabled) return; 573 574 // If we are mid-animation into Recents, then reverse it and finish 575 if (mFullScreenOverlayView == null || 576 !mFullScreenOverlayView.cancelAnimateOnEnterRecents(mFinishRunnable)) { 577 // If we are currently filtering in any stacks, unfilter them first 578 if (!mRecentsView.unfilterFilteredStacks()) { 579 if (mConfig.launchedFromHome) { 580 // Just start the animation out of recents 581 ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this, 582 null, mFinishLaunchHomeRunnable, null); 583 mRecentsView.startExitToHomeAnimation( 584 new ViewAnimation.TaskViewExitContext(exitTrigger)); 585 } else { 586 // Otherwise, try and launch the first task 587 if (!mRecentsView.launchFirstTask()) { 588 // If there are no tasks, then just finish recents 589 mFinishLaunchHomeRunnable.run(); 590 } 591 } 592 } 593 } 594 } 595 596 /** Called when debug mode is triggered */ 597 public void onDebugModeTriggered() { 598 if (mConfig.developerOptionsEnabled) { 599 SharedPreferences settings = getSharedPreferences(getPackageName(), 0); 600 if (settings.getBoolean(Constants.Values.App.Key_DebugModeEnabled, false)) { 601 // Disable the debug mode 602 settings.edit().remove(Constants.Values.App.Key_DebugModeEnabled).apply(); 603 } else { 604 // Enable the debug mode 605 settings.edit().putBoolean(Constants.Values.App.Key_DebugModeEnabled, true).apply(); 606 } 607 Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + 608 ") toggled, please restart Recents now", Toast.LENGTH_SHORT).show(); 609 } 610 } 611 612 /** Called when the enter recents animation is triggered. */ 613 public void onEnterAnimationTriggered() { 614 // Animate the scrims in 615 mScrimViews.startEnterRecentsAnimation(); 616 } 617 618 /**** FullscreenTransitionOverlayView.FullScreenTransitionViewCallbacks Implementation ****/ 619 620 @Override 621 public void onEnterAnimationComplete() { 622 // Reset the full screenshot transition view 623 if (Constants.DebugFlags.App.EnableScreenshotAppTransition) { 624 mFullScreenOverlayView.reset(); 625 626 // Recycle the full screen screenshot 627 AlternateRecentsComponent.consumeLastScreenshot(); 628 } 629 } 630 631 /**** RecentsView.RecentsViewCallbacks Implementation ****/ 632 633 @Override 634 public void onExitToHomeAnimationTriggered() { 635 // Animate the scrims out 636 mScrimViews.startExitRecentsAnimation(); 637 } 638 639 @Override 640 public void onTaskViewClicked() { 641 // Mark recents as no longer visible 642 AlternateRecentsComponent.notifyVisibilityChanged(false); 643 mVisible = false; 644 } 645 646 @Override 647 public void onAllTaskViewsDismissed() { 648 mFinishLaunchHomeRunnable.run(); 649 } 650 651 /**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/ 652 653 @Override 654 public void refreshSearchWidget() { 655 // Load the Search widget again 656 bindSearchBarAppWidget(); 657 addSearchBarAppWidgetView(); 658 } 659} 660