Home | History | Annotate | Download | only in recents
      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