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