RecentsActivity.java revision 147ecaf3ba5d72872e8ff324aa32c0d228ada7f5
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.content.res.Configuration;
29import android.os.Bundle;
30import android.os.SystemClock;
31import android.os.UserHandle;
32import android.view.KeyEvent;
33import android.view.View;
34import android.view.ViewStub;
35import android.widget.Toast;
36
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        TaskStack stack = plan.getTaskStack();
210        mConfig.launchedWithNoRecentTasks = !plan.hasTasks();
211        if (!mConfig.launchedWithNoRecentTasks) {
212            mRecentsView.setTaskStack(stack);
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 launchTaskIndexInStack = 0;
229        if (mConfig.launchedToTaskId != -1) {
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                    launchTaskIndexInStack = tasks.size() - j - 1;
237                    break;
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.hasValidSearchBar()) {
254                mRecentsView.setSearchBarVisibility(View.VISIBLE);
255            } else {
256                refreshSearchWidgetView();
257            }
258        }
259
260        // Animate the SystemUI scrims into view
261        mScrimViews.prepareEnterRecentsAnimation();
262
263        // Keep track of whether we launched from the nav bar button or via alt-tab
264        if (mConfig.launchedWithAltTab) {
265            MetricsLogger.count(this, "overview_trigger_alttab", 1);
266        } else {
267            MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
268        }
269        // Keep track of whether we launched from an app or from home
270        if (mConfig.launchedFromAppWithThumbnail) {
271            MetricsLogger.count(this, "overview_source_app", 1);
272            // If from an app, track the stack index of the app in the stack (for affiliated tasks)
273            MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
274        } else {
275            MetricsLogger.count(this, "overview_source_home", 1);
276        }
277        // Keep track of the total stack task count
278        int taskCount = stack.getTaskCount();
279        MetricsLogger.histogram(this, "overview_task_count", taskCount);
280    }
281
282    /** Dismisses recents if we are already visible and the intent is to toggle the recents view */
283    boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
284        SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
285        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
286            // If we currently have filtered stacks, then unfilter those first
287            if (checkFilteredStackState &&
288                mRecentsView.unfilterFilteredStacks()) return true;
289            // If we have a focused Task, launch that Task now
290            if (mRecentsView.launchFocusedTask()) return true;
291            // If we launched from Home, then return to Home
292            if (mConfig.launchedFromHome) {
293                dismissRecentsToHomeRaw(true);
294                return true;
295            }
296            // Otherwise, try and return to the Task that Recents was launched from
297            if (mRecentsView.launchPreviousTask()) return true;
298            // If none of the other cases apply, then just go Home
299            dismissRecentsToHomeRaw(true);
300            return true;
301        }
302        return false;
303    }
304
305    /** Dismisses Recents directly to Home. */
306    void dismissRecentsToHomeRaw(boolean animated) {
307        if (animated) {
308            ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
309                    null, mFinishLaunchHomeRunnable, null);
310            mRecentsView.startExitToHomeAnimation(
311                    new ViewAnimation.TaskViewExitContext(exitTrigger));
312        } else {
313            mFinishLaunchHomeRunnable.run();
314        }
315    }
316
317    /** Dismisses Recents directly to Home without transition animation. */
318    void dismissRecentsToHomeWithoutTransitionAnimation() {
319        finish();
320        overridePendingTransition(0, 0);
321    }
322
323    /** Dismisses Recents directly to Home if we currently aren't transitioning. */
324    boolean dismissRecentsToHome(boolean animated) {
325        SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
326        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
327            // Return to Home
328            dismissRecentsToHomeRaw(animated);
329            return true;
330        }
331        return false;
332    }
333
334    /** Called with the activity is first created. */
335    @Override
336    public void onCreate(Bundle savedInstanceState) {
337        super.onCreate(savedInstanceState);
338        // For the non-primary user, ensure that the SystemServicesProxy and configuration is
339        // initialized
340        RecentsTaskLoader.initialize(this);
341        SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
342        mConfig = RecentsConfiguration.reinitialize(this, ssp);
343
344        // Initialize the widget host (the host id is static and does not change)
345        mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
346
347        // Set the Recents layout
348        setContentView(R.layout.recents);
349        mRecentsView = (RecentsView) findViewById(R.id.recents_view);
350        mRecentsView.setCallbacks(this);
351        mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
352                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
353                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
354        mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
355        mDebugOverlayStub = (ViewStub) findViewById(R.id.debug_overlay_stub);
356        mScrimViews = new SystemBarScrimViews(this, mConfig);
357        inflateDebugOverlay();
358
359        // Bind the search app widget when we first start up
360        mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
361
362        // Register the broadcast receiver to handle messages when the screen is turned off
363        IntentFilter filter = new IntentFilter();
364        filter.addAction(Intent.ACTION_SCREEN_OFF);
365        filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
366        registerReceiver(mSystemBroadcastReceiver, filter);
367    }
368
369    /** Inflates the debug overlay if debug mode is enabled. */
370    void inflateDebugOverlay() {
371        if (!Constants.DebugFlags.App.EnableDebugMode) return;
372
373        if (mConfig.debugModeEnabled && mDebugOverlay == null) {
374            // Inflate the overlay and seek bars
375            mDebugOverlay = (DebugOverlayView) mDebugOverlayStub.inflate();
376            mDebugOverlay.setCallbacks(this);
377            mRecentsView.setDebugOverlay(mDebugOverlay);
378        }
379    }
380
381    @Override
382    protected void onNewIntent(Intent intent) {
383        super.onNewIntent(intent);
384        setIntent(intent);
385
386        // Clear any debug rects
387        if (mDebugOverlay != null) {
388            mDebugOverlay.clear();
389        }
390    }
391
392    @Override
393    protected void onStart() {
394        super.onStart();
395        MetricsLogger.visible(this, MetricsLogger.OVERVIEW_ACTIVITY);
396        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
397        SystemServicesProxy ssp = loader.getSystemServicesProxy();
398        Recents.notifyVisibilityChanged(this, ssp, true);
399
400        // Register the broadcast receiver to handle messages from our service
401        IntentFilter filter = new IntentFilter();
402        filter.addAction(Recents.ACTION_HIDE_RECENTS_ACTIVITY);
403        filter.addAction(Recents.ACTION_TOGGLE_RECENTS_ACTIVITY);
404        filter.addAction(Recents.ACTION_START_ENTER_ANIMATION);
405        registerReceiver(mServiceBroadcastReceiver, filter);
406
407        // Register any broadcast receivers for the task loader
408        loader.registerReceivers(this, mRecentsView);
409
410        // Update the recent tasks
411        updateRecentsTasks();
412
413        // If this is a new instance from a configuration change, then we have to manually trigger
414        // the enter animation state, or if recents was relaunched by AM, without going through
415        // the normal mechanisms
416        boolean wasLaunchedByAm = !mConfig.launchedFromHome && !mConfig.launchedFromAppWithThumbnail;
417        if (mConfig.launchedHasConfigurationChanged || wasLaunchedByAm) {
418            onEnterAnimationTriggered();
419        }
420
421        if (!mConfig.launchedHasConfigurationChanged) {
422            mRecentsView.disableLayersForOneFrame();
423        }
424    }
425
426    @Override
427    protected void onPause() {
428        super.onPause();
429        if (mAfterPauseRunnable != null) {
430            mRecentsView.post(mAfterPauseRunnable);
431            mAfterPauseRunnable = null;
432        }
433    }
434
435    @Override
436    protected void onStop() {
437        super.onStop();
438        MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
439        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
440        SystemServicesProxy ssp = loader.getSystemServicesProxy();
441        Recents.notifyVisibilityChanged(this, ssp, false);
442
443        // Notify the views that we are no longer visible
444        mRecentsView.onRecentsHidden();
445
446        // Unregister the RecentsService receiver
447        unregisterReceiver(mServiceBroadcastReceiver);
448
449        // Unregister any broadcast receivers for the task loader
450        loader.unregisterReceivers();
451
452        // Workaround for b/22542869, if the RecentsActivity is started again, but without going
453        // through SystemUI, we need to reset the config launch flags to ensure that we do not
454        // wait on the system to send a signal that was never queued.
455        mConfig.launchedFromHome = false;
456        mConfig.launchedFromSearchHome = false;
457        mConfig.launchedFromAppWithThumbnail = false;
458        mConfig.launchedToTaskId = -1;
459        mConfig.launchedWithAltTab = false;
460        mConfig.launchedHasConfigurationChanged = false;
461    }
462
463    @Override
464    protected void onDestroy() {
465        super.onDestroy();
466
467        // Unregister the system broadcast receivers
468        unregisterReceiver(mSystemBroadcastReceiver);
469
470        // Stop listening for widget package changes if there was one bound
471        mAppWidgetHost.stopListening();
472    }
473
474    public void onEnterAnimationTriggered() {
475        // Try and start the enter animation (or restart it on configuration changed)
476        ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
477        ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
478        mRecentsView.startEnterRecentsAnimation(ctx);
479
480        if (mSearchWidgetInfo != null) {
481            final WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks> cbRef =
482                    new WeakReference<RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks>(
483                            RecentsActivity.this);
484            ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
485                @Override
486                public void run() {
487                    // Start listening for widget package changes if there is one bound
488                    RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks cb = cbRef.get();
489                    if (cb != null) {
490                        mAppWidgetHost.startListening(cb);
491                    }
492                }
493            });
494        }
495
496        // Animate the SystemUI scrim views
497        mScrimViews.startEnterRecentsAnimation();
498    }
499
500    @Override
501    public void onTrimMemory(int level) {
502        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
503        if (loader != null) {
504            loader.onTrimMemory(level);
505        }
506    }
507
508    @Override
509    public boolean onKeyDown(int keyCode, KeyEvent event) {
510        switch (keyCode) {
511            case KeyEvent.KEYCODE_TAB: {
512                boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
513                        mLastTabKeyEventTime) > mConfig.altTabKeyDelay;
514                if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
515                    // Focus the next task in the stack
516                    final boolean backward = event.isShiftPressed();
517                    mRecentsView.focusNextTask(!backward);
518                    mLastTabKeyEventTime = SystemClock.elapsedRealtime();
519                }
520                return true;
521            }
522            case KeyEvent.KEYCODE_DPAD_UP: {
523                mRecentsView.focusNextTask(true);
524                return true;
525            }
526            case KeyEvent.KEYCODE_DPAD_DOWN: {
527                mRecentsView.focusNextTask(false);
528                return true;
529            }
530            case KeyEvent.KEYCODE_DEL:
531            case KeyEvent.KEYCODE_FORWARD_DEL: {
532                mRecentsView.dismissFocusedTask();
533                // Keep track of deletions by keyboard
534                MetricsLogger.histogram(this, "overview_task_dismissed_source",
535                        Constants.Metrics.DismissSourceKeyboard);
536                return true;
537            }
538            default:
539                break;
540        }
541        // Pass through the debug trigger
542        mDebugTrigger.onKeyEvent(keyCode);
543        return super.onKeyDown(keyCode, event);
544    }
545
546    @Override
547    public void onUserInteraction() {
548        mRecentsView.onUserInteraction();
549    }
550
551    @Override
552    public void onBackPressed() {
553        // Test mode where back does not do anything
554        if (mConfig.debugModeEnabled) return;
555
556        // Dismiss Recents to the focused Task or Home
557        dismissRecentsToFocusedTaskOrHome(true);
558    }
559
560    /** Called when debug mode is triggered */
561    public void onDebugModeTriggered() {
562        if (mConfig.developerOptionsEnabled) {
563            if (Prefs.getBoolean(this, Prefs.Key.DEBUG_MODE_ENABLED, false /* boolean */)) {
564                // Disable the debug mode
565                Prefs.remove(this, Prefs.Key.DEBUG_MODE_ENABLED);
566                mConfig.debugModeEnabled = false;
567                inflateDebugOverlay();
568                if (mDebugOverlay != null) {
569                    mDebugOverlay.disable();
570                }
571            } else {
572                // Enable the debug mode
573                Prefs.putBoolean(this, Prefs.Key.DEBUG_MODE_ENABLED, true);
574                mConfig.debugModeEnabled = true;
575                inflateDebugOverlay();
576                if (mDebugOverlay != null) {
577                    mDebugOverlay.enable();
578                }
579            }
580            Toast.makeText(this, "Debug mode (" + Constants.Values.App.DebugModeVersion + ") " +
581                (mConfig.debugModeEnabled ? "Enabled" : "Disabled") + ", please restart Recents now",
582                Toast.LENGTH_SHORT).show();
583        }
584    }
585
586
587    /**** RecentsResizeTaskDialog ****/
588
589    private RecentsResizeTaskDialog getResizeTaskDebugDialog() {
590        if (mResizeTaskDebugDialog == null) {
591            mResizeTaskDebugDialog = new RecentsResizeTaskDialog(getFragmentManager(), this);
592        }
593        return mResizeTaskDebugDialog;
594    }
595
596    @Override
597    public void onTaskResize(Task t) {
598        getResizeTaskDebugDialog().showResizeTaskDialog(t, mRecentsView);
599    }
600
601    /**** RecentsView.RecentsViewCallbacks Implementation ****/
602
603    @Override
604    public void onExitToHomeAnimationTriggered() {
605        // Animate the SystemUI scrim views out
606        mScrimViews.startExitRecentsAnimation();
607    }
608
609    @Override
610    public void onTaskViewClicked() {
611    }
612
613    @Override
614    public void onTaskLaunchFailed() {
615        // Return to Home
616        dismissRecentsToHomeRaw(true);
617    }
618
619    @Override
620    public void onAllTaskViewsDismissed() {
621        mFinishLaunchHomeRunnable.run();
622    }
623
624    @Override
625    public void onScreenPinningRequest() {
626        RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
627        SystemServicesProxy ssp = loader.getSystemServicesProxy();
628        Recents.startScreenPinning(this, ssp);
629
630        MetricsLogger.count(this, "overview_screen_pinned", 1);
631    }
632
633    @Override
634    public void runAfterPause(Runnable r) {
635        mAfterPauseRunnable = r;
636    }
637
638    /**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/
639
640    @Override
641    public void refreshSearchWidgetView() {
642        if (mSearchWidgetInfo != null) {
643            SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
644            int searchWidgetId = ssp.getSearchAppWidgetId(this);
645            mSearchWidgetHostView = (RecentsAppWidgetHostView) mAppWidgetHost.createView(
646                    this, searchWidgetId, mSearchWidgetInfo);
647            Bundle opts = new Bundle();
648            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
649                    AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
650            mSearchWidgetHostView.updateAppWidgetOptions(opts);
651            // Set the padding to 0 for this search widget
652            mSearchWidgetHostView.setPadding(0, 0, 0, 0);
653            mRecentsView.setSearchBar(mSearchWidgetHostView);
654        } else {
655            mRecentsView.setSearchBar(null);
656        }
657    }
658
659    /**** DebugOverlayView.DebugOverlayViewCallbacks ****/
660
661    @Override
662    public void onPrimarySeekBarChanged(float progress) {
663        // Do nothing
664    }
665
666    @Override
667    public void onSecondarySeekBarChanged(float progress) {
668        // Do nothing
669    }
670}
671