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