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