RecentsActivity.java revision 5da4347b41b105dc6c6c01bf2810af3ce3013229
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.app.TaskStackBuilder;
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.pm.ActivityInfo;
30import android.net.Uri;
31import android.os.Bundle;
32import android.os.SystemClock;
33import android.os.UserHandle;
34import android.provider.Settings;
35import android.view.KeyEvent;
36import android.view.View;
37import android.view.ViewStub;
38import android.view.ViewTreeObserver;
39
40import com.android.internal.logging.MetricsLogger;
41import com.android.systemui.R;
42import com.android.systemui.recents.events.EventBus;
43import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
44import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
45import com.android.systemui.recents.events.activity.HideRecentsEvent;
46import com.android.systemui.recents.events.activity.IterateRecentsEvent;
47import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
48import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
49import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
50import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
51import com.android.systemui.recents.events.ui.DismissTaskViewEvent;
52import com.android.systemui.recents.events.ui.ResizeTaskEvent;
53import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
54import com.android.systemui.recents.events.ui.UserInteractionEvent;
55import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
56import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent;
57import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
58import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
59import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
60import com.android.systemui.recents.misc.Console;
61import com.android.systemui.recents.misc.DozeTrigger;
62import com.android.systemui.recents.misc.ReferenceCountedTrigger;
63import com.android.systemui.recents.misc.SystemServicesProxy;
64import com.android.systemui.recents.model.RecentsPackageMonitor;
65import com.android.systemui.recents.model.RecentsTaskLoadPlan;
66import com.android.systemui.recents.model.RecentsTaskLoader;
67import com.android.systemui.recents.model.Task;
68import com.android.systemui.recents.model.TaskStack;
69import com.android.systemui.recents.views.RecentsView;
70import com.android.systemui.recents.views.SystemBarScrimViews;
71import com.android.systemui.recents.views.ViewAnimation;
72
73import java.util.ArrayList;
74
75/**
76 * The main Recents activity that is started from AlternateRecentsComponent.
77 */
78public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
79        ViewTreeObserver.OnPreDrawListener {
80
81    private final static String TAG = "RecentsActivity";
82    private final static boolean DEBUG = false;
83
84    public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
85
86    RecentsPackageMonitor mPackageMonitor;
87    long mLastTabKeyEventTime;
88    boolean mFinishedOnStartup;
89
90    // Top level views
91    RecentsView mRecentsView;
92    SystemBarScrimViews mScrimViews;
93    ViewStub mEmptyViewStub;
94    View mEmptyView;
95
96    // Resize task debug
97    RecentsResizeTaskDialog mResizeTaskDebugDialog;
98
99    // Search AppWidget
100    AppWidgetProviderInfo mSearchWidgetInfo;
101    RecentsAppWidgetHost mAppWidgetHost;
102    RecentsAppWidgetHostView mSearchWidgetHostView;
103
104    // Runnables to finish the Recents activity
105    FinishRecentsRunnable mFinishLaunchHomeRunnable;
106
107    // Runnable to be executed after we paused ourselves
108    Runnable mAfterPauseRunnable;
109
110    // The trigger to automatically launch the current task
111    DozeTrigger mIterateTrigger = new DozeTrigger(500, new Runnable() {
112        @Override
113        public void run() {
114            boolean dismissed = dismissRecentsToFocusedTask(false);
115        }
116    });
117
118    /**
119     * A common Runnable to finish Recents either by calling finish() (with a custom animation) or
120     * launching Home with some ActivityOptions.  Generally we always launch home when we exit
121     * Recents rather than just finishing the activity since we don't know what is behind Recents in
122     * the task stack.  The only case where we finish() directly is when we are cancelling the full
123     * screen transition from the app.
124     */
125    class FinishRecentsRunnable implements Runnable {
126        Intent mLaunchIntent;
127        ActivityOptions mLaunchOpts;
128
129        /**
130         * Creates a finish runnable that starts the specified intent, using the given
131         * ActivityOptions.
132         */
133        public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) {
134            mLaunchIntent = launchIntent;
135            mLaunchOpts = opts;
136        }
137
138        @Override
139        public void run() {
140            try {
141                startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(), UserHandle.CURRENT);
142            } catch (Exception e) {
143                Console.logError(RecentsActivity.this,
144                        getString(R.string.recents_launch_error_message, "Home"));
145            }
146        }
147    }
148
149    /**
150     * Broadcast receiver to handle messages from the system
151     */
152    final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() {
153        @Override
154        public void onReceive(Context context, Intent intent) {
155            String action = intent.getAction();
156            if (action.equals(Intent.ACTION_SCREEN_OFF)) {
157                // When the screen turns off, dismiss Recents to Home
158                dismissRecentsToHomeIfVisible(false);
159            } else if (action.equals(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED)) {
160                // When the search activity changes, update the search widget view
161                SystemServicesProxy ssp = Recents.getSystemServices();
162                mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(context, mAppWidgetHost);
163                refreshSearchWidgetView();
164            }
165        }
166    };
167
168    /** Updates the set of recent tasks */
169    void updateRecentsTasks() {
170        // If AlternateRecentsComponent has preloaded a load plan, then use that to prevent
171        // reconstructing the task stack
172        RecentsTaskLoader loader = Recents.getTaskLoader();
173        RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
174        if (plan == null) {
175            plan = loader.createLoadPlan(this);
176        }
177
178        // Start loading tasks according to the load plan
179        RecentsConfiguration config = Recents.getConfiguration();
180        RecentsActivityLaunchState launchState = config.getLaunchState();
181        if (!plan.hasTasks()) {
182            loader.preloadTasks(plan, launchState.launchedFromHome);
183        }
184        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
185        loadOpts.runningTaskId = launchState.launchedToTaskId;
186        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
187        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
188        loader.loadTasks(this, plan, loadOpts);
189
190        TaskStack stack = plan.getTaskStack();
191        launchState.launchedWithNoRecentTasks = !plan.hasTasks();
192        if (!launchState.launchedWithNoRecentTasks) {
193            mRecentsView.setTaskStack(stack);
194        }
195
196        // Create the home intent runnable
197        Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
198        homeIntent.addCategory(Intent.CATEGORY_HOME);
199        homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
200                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
201        mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent,
202            ActivityOptions.makeCustomAnimation(this,
203                    launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter :
204                        R.anim.recents_to_launcher_enter,
205                    launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit :
206                        R.anim.recents_to_launcher_exit));
207
208        // Mark the task that is the launch target
209        int launchTaskIndexInStack = 0;
210        if (launchState.launchedToTaskId != -1) {
211            ArrayList<Task> tasks = stack.getTasks();
212            int taskCount = tasks.size();
213            for (int j = 0; j < taskCount; j++) {
214                Task t = tasks.get(j);
215                if (t.key.id == launchState.launchedToTaskId) {
216                    t.isLaunchTarget = true;
217                    launchTaskIndexInStack = tasks.size() - j - 1;
218                    break;
219                }
220            }
221        }
222
223        // Update the top level view's visibilities
224        if (launchState.launchedWithNoRecentTasks) {
225            if (mEmptyView == null) {
226                mEmptyView = mEmptyViewStub.inflate();
227            }
228            mEmptyView.setVisibility(View.VISIBLE);
229            if (!Constants.DebugFlags.App.DisableSearchBar) {
230                mRecentsView.setSearchBarVisibility(View.GONE);
231            }
232        } else {
233            if (mEmptyView != null) {
234                mEmptyView.setVisibility(View.GONE);
235            }
236            if (!Constants.DebugFlags.App.DisableSearchBar) {
237                if (mRecentsView.hasValidSearchBar()) {
238                    mRecentsView.setSearchBarVisibility(View.VISIBLE);
239                } else {
240                    refreshSearchWidgetView();
241                }
242            }
243        }
244
245        // Animate the SystemUI scrims into view
246        mScrimViews.prepareEnterRecentsAnimation();
247
248        // Keep track of whether we launched from the nav bar button or via alt-tab
249        if (launchState.launchedWithAltTab) {
250            MetricsLogger.count(this, "overview_trigger_alttab", 1);
251        } else {
252            MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
253        }
254        // Keep track of whether we launched from an app or from home
255        if (launchState.launchedFromAppWithThumbnail) {
256            MetricsLogger.count(this, "overview_source_app", 1);
257            // If from an app, track the stack index of the app in the stack (for affiliated tasks)
258            MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
259        } else {
260            MetricsLogger.count(this, "overview_source_home", 1);
261        }
262        // Keep track of the total stack task count
263        int taskCount = stack.getTaskCount();
264        MetricsLogger.histogram(this, "overview_task_count", taskCount);
265    }
266
267    /**
268     * Dismisses recents if we are already visible and the intent is to toggle the recents view.
269     */
270    boolean dismissRecentsToFocusedTask(boolean checkFilteredStackState) {
271        SystemServicesProxy ssp = Recents.getSystemServices();
272        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
273            // If we currently have filtered stacks, then unfilter those first
274            if (checkFilteredStackState &&
275                    mRecentsView.unfilterFilteredStacks()) return true;
276            // If we have a focused Task, launch that Task now
277            if (mRecentsView.launchFocusedTask()) return true;
278        }
279        return false;
280    }
281
282    /**
283     * Dismisses recents if we are already visible and the intent is to toggle the recents view.
284     */
285    boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) {
286        SystemServicesProxy ssp = Recents.getSystemServices();
287        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
288            // If we currently have filtered stacks, then unfilter those first
289            if (checkFilteredStackState &&
290                mRecentsView.unfilterFilteredStacks()) return true;
291            // If we have a focused Task, launch that Task now
292            if (mRecentsView.launchFocusedTask()) return true;
293            // If none of the other cases apply, then just go Home
294            dismissRecentsToHome(true);
295            return true;
296        }
297        return false;
298    }
299
300    /**
301     * Dismisses Recents directly to Home without checking whether it is currently visible.
302     */
303    void dismissRecentsToHome(boolean animated) {
304        if (animated) {
305            ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
306                    null, mFinishLaunchHomeRunnable, null);
307            mRecentsView.startExitToHomeAnimation(
308                    new ViewAnimation.TaskViewExitContext(exitTrigger));
309        } else {
310            mFinishLaunchHomeRunnable.run();
311        }
312    }
313
314    /** Dismisses Recents directly to Home without transition animation. */
315    void dismissRecentsToHomeWithoutTransitionAnimation() {
316        finish();
317        overridePendingTransition(0, 0);
318    }
319
320    /** Dismisses Recents directly to Home if we currently aren't transitioning. */
321    boolean dismissRecentsToHomeIfVisible(boolean animated) {
322        SystemServicesProxy ssp = Recents.getSystemServices();
323        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
324            // Return to Home
325            dismissRecentsToHome(animated);
326            return true;
327        }
328        return false;
329    }
330
331    /** Called with the activity is first created. */
332    @Override
333    public void onCreate(Bundle savedInstanceState) {
334        super.onCreate(savedInstanceState);
335        mFinishedOnStartup = false;
336
337        // In the case that the activity starts up before the Recents component has initialized
338        // (usually when debugging/pushing the SysUI apk), just finish this activity.
339        SystemServicesProxy ssp = Recents.getSystemServices();
340        if (ssp == null) {
341            mFinishedOnStartup = true;
342            finish();
343            return;
344        }
345
346        // Register this activity with the event bus
347        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
348
349        // Initialize the widget host (the host id is static and does not change)
350        if (!Constants.DebugFlags.App.DisableSearchBar) {
351            mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
352        }
353        mPackageMonitor = new RecentsPackageMonitor();
354        mPackageMonitor.register(this);
355
356        // Set the Recents layout
357        setContentView(R.layout.recents);
358        mRecentsView = (RecentsView) findViewById(R.id.recents_view);
359        mRecentsView.setCallbacks(this);
360        mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
361                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
362                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
363        mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub);
364        mScrimViews = new SystemBarScrimViews(this);
365
366        // Bind the search app widget when we first start up
367        if (!Constants.DebugFlags.App.DisableSearchBar) {
368            mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost);
369        }
370
371        // Register the broadcast receiver to handle messages when the screen is turned off
372        IntentFilter filter = new IntentFilter();
373        filter.addAction(Intent.ACTION_SCREEN_OFF);
374        if (!Constants.DebugFlags.App.DisableSearchBar) {
375            filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
376        }
377        registerReceiver(mSystemBroadcastReceiver, filter);
378    }
379
380    @Override
381    protected void onNewIntent(Intent intent) {
382        super.onNewIntent(intent);
383        setIntent(intent);
384    }
385
386    @Override
387    protected void onStart() {
388        super.onStart();
389
390        // Update the recent tasks
391        updateRecentsTasks();
392
393        // If this is a new instance from a configuration change, then we have to manually trigger
394        // the enter animation state, or if recents was relaunched by AM, without going through
395        // the normal mechanisms
396        RecentsConfiguration config = Recents.getConfiguration();
397        RecentsActivityLaunchState launchState = config.getLaunchState();
398        boolean wasLaunchedByAm = !launchState.launchedFromHome &&
399                !launchState.launchedFromAppWithThumbnail;
400        if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) {
401            EventBus.getDefault().send(new EnterRecentsWindowAnimationStartedEvent());
402        }
403
404        if (!launchState.launchedHasConfigurationChanged) {
405            mRecentsView.disableLayersForOneFrame();
406        }
407
408        // Notify that recents is now visible
409        SystemServicesProxy ssp = Recents.getSystemServices();
410        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, true));
411
412        MetricsLogger.visible(this, MetricsLogger.OVERVIEW_ACTIVITY);
413    }
414
415    @Override
416    protected void onResume() {
417        super.onResume();
418
419        final RecentsActivityLaunchState state = Recents.getConfiguration().getLaunchState();
420        if (state.startHidden) {
421            state.startHidden = false;
422            mRecentsView.setStackViewVisibility(View.INVISIBLE);
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        if (Constants.DebugFlags.App.EnableFastToggleRecents) {
435            // Stop the fast-toggle dozer
436            mIterateTrigger.stopDozing();
437        }
438    }
439
440    @Override
441    protected void onStop() {
442        super.onStop();
443
444        // Notify that recents is now hidden
445        SystemServicesProxy ssp = Recents.getSystemServices();
446        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
447
448        // Workaround for b/22542869, if the RecentsActivity is started again, but without going
449        // through SystemUI, we need to reset the config launch flags to ensure that we do not
450        // wait on the system to send a signal that was never queued.
451        RecentsConfiguration config = Recents.getConfiguration();
452        RecentsActivityLaunchState launchState = config.getLaunchState();
453        launchState.launchedFromHome = false;
454        launchState.launchedFromSearchHome = false;
455        launchState.launchedFromAppWithThumbnail = false;
456        launchState.launchedToTaskId = -1;
457        launchState.launchedWithAltTab = false;
458        launchState.launchedHasConfigurationChanged = false;
459
460        MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
461    }
462
463    @Override
464    protected void onDestroy() {
465        super.onDestroy();
466
467        // In the case that the activity finished on startup, just skip the unregistration below
468        if (mFinishedOnStartup) {
469            return;
470        }
471
472        // Unregister the system broadcast receivers
473        unregisterReceiver(mSystemBroadcastReceiver);
474
475        // Unregister any broadcast receivers for the task loader
476        mPackageMonitor.unregister();
477
478        // Stop listening for widget package changes if there was one bound
479        if (!Constants.DebugFlags.App.DisableSearchBar) {
480            mAppWidgetHost.stopListening();
481        }
482
483        EventBus.getDefault().unregister(this);
484    }
485
486    @Override
487    public void onAttachedToWindow() {
488        super.onAttachedToWindow();
489        EventBus.getDefault().register(mScrimViews, EVENT_BUS_PRIORITY);
490    }
491
492    @Override
493    public void onDetachedFromWindow() {
494        super.onDetachedFromWindow();
495        EventBus.getDefault().unregister(mScrimViews);
496    }
497
498    @Override
499    public void onTrimMemory(int level) {
500        RecentsTaskLoader loader = Recents.getTaskLoader();
501        if (loader != null) {
502            loader.onTrimMemory(level);
503        }
504    }
505
506    @Override
507    public boolean onKeyDown(int keyCode, KeyEvent event) {
508        switch (keyCode) {
509            case KeyEvent.KEYCODE_TAB: {
510                int altTabKeyDelay = getResources().getInteger(R.integer.recents_alt_tab_key_delay);
511                boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
512                        mLastTabKeyEventTime) > altTabKeyDelay;
513                if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
514                    // As we iterate to the next/previous task, cancel any current/lagging window
515                    // transition animations
516                    RecentsConfiguration config = Recents.getConfiguration();
517                    RecentsActivityLaunchState launchState = config.getLaunchState();
518                    if (launchState.launchedToTaskId != -1) {
519                        SystemServicesProxy ssp = Recents.getSystemServices();
520                        ssp.cancelWindowTransition(launchState.launchedToTaskId);
521                    }
522
523                    // Focus the next task in the stack
524                    final boolean backward = event.isShiftPressed();
525                    if (backward) {
526                        EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
527                    } else {
528                        EventBus.getDefault().send(new FocusNextTaskViewEvent());
529                    }
530                    mLastTabKeyEventTime = SystemClock.elapsedRealtime();
531                }
532                return true;
533            }
534            case KeyEvent.KEYCODE_DPAD_UP: {
535                EventBus.getDefault().send(new FocusNextTaskViewEvent());
536                return true;
537            }
538            case KeyEvent.KEYCODE_DPAD_DOWN: {
539                EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
540                return true;
541            }
542            case KeyEvent.KEYCODE_DEL:
543            case KeyEvent.KEYCODE_FORWARD_DEL: {
544                EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
545
546                // Keep track of deletions by keyboard
547                MetricsLogger.histogram(this, "overview_task_dismissed_source",
548                        Constants.Metrics.DismissSourceKeyboard);
549                return true;
550            }
551            default:
552                break;
553        }
554        return super.onKeyDown(keyCode, event);
555    }
556
557    @Override
558    public void onUserInteraction() {
559        EventBus.getDefault().send(new UserInteractionEvent());
560    }
561
562    @Override
563    public void onBackPressed() {
564        // Dismiss Recents to the focused Task or Home
565        dismissRecentsToFocusedTaskOrHome(true);
566    }
567
568    /**** RecentsResizeTaskDialog ****/
569
570    private RecentsResizeTaskDialog getResizeTaskDebugDialog() {
571        if (mResizeTaskDebugDialog == null) {
572            mResizeTaskDebugDialog = new RecentsResizeTaskDialog(getFragmentManager(), this);
573        }
574        return mResizeTaskDebugDialog;
575    }
576
577    /**** RecentsView.RecentsViewCallbacks Implementation ****/
578
579    @Override
580    public void onTaskViewClicked() {
581    }
582
583    @Override
584    public void onTaskLaunchFailed() {
585        // Return to Home
586        dismissRecentsToHome(true);
587    }
588
589    @Override
590    public void onAllTaskViewsDismissed() {
591        mFinishLaunchHomeRunnable.run();
592    }
593
594    @Override
595    public void runAfterPause(Runnable r) {
596        mAfterPauseRunnable = r;
597    }
598
599    /**** EventBus events ****/
600
601    public final void onBusEvent(ToggleRecentsEvent event) {
602        dismissRecentsToFocusedTaskOrHome(true /* checkFilteredStackState */);
603    }
604
605    public final void onBusEvent(IterateRecentsEvent event) {
606        // Focus the next task
607        EventBus.getDefault().send(new FocusNextTaskViewEvent());
608        mIterateTrigger.poke();
609    }
610
611    public final void onBusEvent(UserInteractionEvent event) {
612        mIterateTrigger.stopDozing();
613    }
614
615    public final void onBusEvent(HideRecentsEvent event) {
616        if (event.triggeredFromAltTab) {
617            // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
618            dismissRecentsToFocusedTaskOrHome(false /* checkFilteredStackState */);
619        } else if (event.triggeredFromHomeKey) {
620            // Otherwise, dismiss Recents to Home
621            dismissRecentsToHome(true /* checkFilteredStackState */);
622        } else {
623            // Do nothing
624        }
625    }
626
627    public final void onBusEvent(EnterRecentsWindowAnimationStartedEvent event) {
628        // Try and start the enter animation (or restart it on configuration changed)
629        ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
630        ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
631        ctx.postAnimationTrigger.increment();
632        if (mSearchWidgetInfo != null) {
633            if (!Constants.DebugFlags.App.DisableSearchBar) {
634                ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
635                    @Override
636                    public void run() {
637                        // Start listening for widget package changes if there is one bound
638                        if (mAppWidgetHost != null) {
639                            mAppWidgetHost.startListening();
640                        }
641                    }
642                });
643            }
644        }
645        ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
646            @Override
647            public void run() {
648                // If we are not launching with alt-tab and fast-toggle is enabled, then start
649                // the dozer now
650                RecentsConfiguration config = Recents.getConfiguration();
651                RecentsActivityLaunchState launchState = config.getLaunchState();
652                if (Constants.DebugFlags.App.EnableFastToggleRecents &&
653                        !launchState.launchedWithAltTab) {
654                    mIterateTrigger.startDozing();
655                }
656            }
657        });
658        mRecentsView.startEnterRecentsAnimation(ctx);
659        ctx.postAnimationTrigger.decrement();
660    }
661
662    public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
663        mRecentsView.setStackViewVisibility(View.VISIBLE);
664        mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
665    }
666
667    public final void onBusEvent(AppWidgetProviderChangedEvent event) {
668        refreshSearchWidgetView();
669    }
670
671    public final void onBusEvent(ShowApplicationInfoEvent event) {
672        // Create a new task stack with the application info details activity
673        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
674                Uri.fromParts("package", event.task.key.getComponent().getPackageName(), null));
675        intent.setComponent(intent.resolveActivity(getPackageManager()));
676        TaskStackBuilder.create(this)
677                .addNextIntentWithParentStack(intent).startActivities(null,
678                new UserHandle(event.task.key.userId));
679
680        // Keep track of app-info invocations
681        MetricsLogger.count(this, "overview_app_info", 1);
682    }
683
684    public final void onBusEvent(DismissTaskViewEvent event) {
685        // Remove any stored data from the loader
686        RecentsTaskLoader loader = Recents.getTaskLoader();
687        loader.deleteTaskData(event.task, false);
688
689        // Remove the task from activity manager
690        SystemServicesProxy ssp = Recents.getSystemServices();
691        ssp.removeTask(event.task.key.id);
692    }
693
694    public final void onBusEvent(ResizeTaskEvent event) {
695        getResizeTaskDebugDialog().showResizeTaskDialog(event.task, mRecentsView);
696    }
697
698    public final void onBusEvent(DragStartEvent event) {
699        // Lock the orientation while dragging
700        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
701
702        // TODO: docking requires custom accessibility actions
703    }
704
705    public final void onBusEvent(DragEndEvent event) {
706        // Unlock the orientation when dragging completes
707        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_BEHIND);
708    }
709
710    public final void onBusEvent(ScreenPinningRequestEvent event) {
711        MetricsLogger.count(this, "overview_screen_pinned", 1);
712    }
713
714    private void refreshSearchWidgetView() {
715        if (mSearchWidgetInfo != null) {
716            SystemServicesProxy ssp = Recents.getSystemServices();
717            int searchWidgetId = ssp.getSearchAppWidgetId(this);
718            mSearchWidgetHostView = (RecentsAppWidgetHostView) mAppWidgetHost.createView(
719                    this, searchWidgetId, mSearchWidgetInfo);
720            Bundle opts = new Bundle();
721            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
722                    AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
723            mSearchWidgetHostView.updateAppWidgetOptions(opts);
724            // Set the padding to 0 for this search widget
725            mSearchWidgetHostView.setPadding(0, 0, 0, 0);
726            mRecentsView.setSearchBar(mSearchWidgetHostView);
727        } else {
728            mRecentsView.setSearchBar(null);
729        }
730    }
731
732    @Override
733    public boolean onPreDraw() {
734        mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
735        // We post to make sure that this information is delivered after this traversals is
736        // finished.
737        mRecentsView.post(new Runnable() {
738            @Override
739            public void run() {
740                Recents.getSystemServices().endProlongedAnimations();
741            }
742        });
743        return true;
744    }
745}
746