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.AppWidgetManager;
23import android.appwidget.AppWidgetProviderInfo;
24import android.content.BroadcastReceiver;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.os.Bundle;
29import android.os.SystemClock;
30import android.os.UserHandle;
31import android.view.KeyEvent;
32import android.view.View;
33import android.view.ViewStub;
34import android.widget.Toast;
35
36import com.android.internal.logging.MetricsConstants;
37import com.android.internal.logging.MetricsLogger;
38import com.android.systemui.Prefs;
39import com.android.systemui.R;
40import com.android.systemui.recents.misc.Console;
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.model.RecentsTaskLoadPlan;
45import com.android.systemui.recents.model.RecentsTaskLoader;
46import com.android.systemui.recents.model.Task;
47import com.android.systemui.recents.model.TaskStack;
48import com.android.systemui.recents.views.DebugOverlayView;
49import com.android.systemui.recents.views.RecentsView;
50import com.android.systemui.recents.views.SystemBarScrimViews;
51import com.android.systemui.recents.views.ViewAnimation;
52
53import java.lang.ref.WeakReference;
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    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