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