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