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