RecentsActivity.java revision 36a5a2c7003ef8157f276b411c3fda47ad2f75e3
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.util.Log;
36import android.view.KeyEvent;
37import android.view.View;
38import android.view.ViewStub;
39import com.android.internal.logging.MetricsLogger;
40import com.android.systemui.R;
41import com.android.systemui.recents.events.EventBus;
42import com.android.systemui.recents.events.activity.AppWidgetProviderChangedEvent;
43import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationStartedEvent;
44import com.android.systemui.recents.events.activity.HideRecentsEvent;
45import com.android.systemui.recents.events.activity.IterateRecentsEvent;
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 onPause() {
423        super.onPause();
424        if (mAfterPauseRunnable != null) {
425            mRecentsView.post(mAfterPauseRunnable);
426            mAfterPauseRunnable = null;
427        }
428
429        if (Constants.DebugFlags.App.EnableFastToggleRecents) {
430            // Stop the fast-toggle dozer
431            mIterateTrigger.stopDozing();
432        }
433    }
434
435    @Override
436    protected void onStop() {
437        super.onStop();
438
439        // Notify that recents is now hidden
440        SystemServicesProxy ssp = Recents.getSystemServices();
441        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, ssp, false));
442
443        // Workaround for b/22542869, if the RecentsActivity is started again, but without going
444        // through SystemUI, we need to reset the config launch flags to ensure that we do not
445        // wait on the system to send a signal that was never queued.
446        RecentsConfiguration config = Recents.getConfiguration();
447        RecentsActivityLaunchState launchState = config.getLaunchState();
448        launchState.launchedFromHome = false;
449        launchState.launchedFromSearchHome = false;
450        launchState.launchedFromAppWithThumbnail = false;
451        launchState.launchedToTaskId = -1;
452        launchState.launchedWithAltTab = false;
453        launchState.launchedHasConfigurationChanged = false;
454
455        MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY);
456    }
457
458    @Override
459    protected void onDestroy() {
460        super.onDestroy();
461
462        // In the case that the activity finished on startup, just skip the unregistration below
463        if (mFinishedOnStartup) {
464            return;
465        }
466
467        // Unregister the system broadcast receivers
468        unregisterReceiver(mSystemBroadcastReceiver);
469
470        // Unregister any broadcast receivers for the task loader
471        mPackageMonitor.unregister();
472
473        // Stop listening for widget package changes if there was one bound
474        if (!Constants.DebugFlags.App.DisableSearchBar) {
475            mAppWidgetHost.stopListening();
476        }
477
478        EventBus.getDefault().unregister(this);
479    }
480
481    @Override
482    public void onAttachedToWindow() {
483        super.onAttachedToWindow();
484        EventBus.getDefault().register(mScrimViews, EVENT_BUS_PRIORITY);
485    }
486
487    @Override
488    public void onDetachedFromWindow() {
489        super.onDetachedFromWindow();
490        EventBus.getDefault().unregister(mScrimViews);
491    }
492
493    @Override
494    public void onTrimMemory(int level) {
495        RecentsTaskLoader loader = Recents.getTaskLoader();
496        if (loader != null) {
497            loader.onTrimMemory(level);
498        }
499    }
500
501    @Override
502    public boolean onKeyDown(int keyCode, KeyEvent event) {
503        switch (keyCode) {
504            case KeyEvent.KEYCODE_TAB: {
505                int altTabKeyDelay = getResources().getInteger(R.integer.recents_alt_tab_key_delay);
506                boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
507                        mLastTabKeyEventTime) > altTabKeyDelay;
508                if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
509                    // Focus the next task in the stack
510                    final boolean backward = event.isShiftPressed();
511                    if (backward) {
512                        EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
513                    } else {
514                        EventBus.getDefault().send(new FocusNextTaskViewEvent());
515                    }
516                    mLastTabKeyEventTime = SystemClock.elapsedRealtime();
517                }
518                return true;
519            }
520            case KeyEvent.KEYCODE_DPAD_UP: {
521                EventBus.getDefault().send(new FocusNextTaskViewEvent());
522                return true;
523            }
524            case KeyEvent.KEYCODE_DPAD_DOWN: {
525                EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
526                return true;
527            }
528            case KeyEvent.KEYCODE_DEL:
529            case KeyEvent.KEYCODE_FORWARD_DEL: {
530                EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
531
532                // Keep track of deletions by keyboard
533                MetricsLogger.histogram(this, "overview_task_dismissed_source",
534                        Constants.Metrics.DismissSourceKeyboard);
535                return true;
536            }
537            default:
538                break;
539        }
540        return super.onKeyDown(keyCode, event);
541    }
542
543    @Override
544    public void onUserInteraction() {
545        EventBus.getDefault().send(new UserInteractionEvent());
546    }
547
548    @Override
549    public void onBackPressed() {
550        // Dismiss Recents to the focused Task or Home
551        dismissRecentsToFocusedTaskOrHome(true);
552    }
553
554    /**** RecentsResizeTaskDialog ****/
555
556    private RecentsResizeTaskDialog getResizeTaskDebugDialog() {
557        if (mResizeTaskDebugDialog == null) {
558            mResizeTaskDebugDialog = new RecentsResizeTaskDialog(getFragmentManager(), this);
559        }
560        return mResizeTaskDebugDialog;
561    }
562
563    /**** RecentsView.RecentsViewCallbacks Implementation ****/
564
565    @Override
566    public void onTaskViewClicked() {
567    }
568
569    @Override
570    public void onTaskLaunchFailed() {
571        // Return to Home
572        dismissRecentsToHome(true);
573    }
574
575    @Override
576    public void onAllTaskViewsDismissed() {
577        mFinishLaunchHomeRunnable.run();
578    }
579
580    @Override
581    public void runAfterPause(Runnable r) {
582        mAfterPauseRunnable = r;
583    }
584
585    /**** EventBus events ****/
586
587    public final void onBusEvent(ToggleRecentsEvent event) {
588        dismissRecentsToFocusedTaskOrHome(true /* checkFilteredStackState */);
589    }
590
591    public final void onBusEvent(IterateRecentsEvent event) {
592        // Focus the next task
593        EventBus.getDefault().send(new FocusNextTaskViewEvent());
594        mIterateTrigger.poke();
595    }
596
597    public final void onBusEvent(UserInteractionEvent event) {
598        mIterateTrigger.stopDozing();
599    }
600
601    public final void onBusEvent(HideRecentsEvent event) {
602        if (event.triggeredFromAltTab) {
603            // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
604            dismissRecentsToFocusedTaskOrHome(false /* checkFilteredStackState */);
605        } else if (event.triggeredFromHomeKey) {
606            // Otherwise, dismiss Recents to Home
607            dismissRecentsToHome(true /* checkFilteredStackState */);
608        } else {
609            // Do nothing
610        }
611    }
612
613    public final void onBusEvent(EnterRecentsWindowAnimationStartedEvent event) {
614        // Try and start the enter animation (or restart it on configuration changed)
615        ReferenceCountedTrigger t = new ReferenceCountedTrigger(this, null, null, null);
616        ViewAnimation.TaskViewEnterContext ctx = new ViewAnimation.TaskViewEnterContext(t);
617        ctx.postAnimationTrigger.increment();
618        if (mSearchWidgetInfo != null) {
619            if (!Constants.DebugFlags.App.DisableSearchBar) {
620                ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
621                    @Override
622                    public void run() {
623                        // Start listening for widget package changes if there is one bound
624                        if (mAppWidgetHost != null) {
625                            mAppWidgetHost.startListening();
626                        }
627                    }
628                });
629            }
630        }
631        ctx.postAnimationTrigger.addLastDecrementRunnable(new Runnable() {
632            @Override
633            public void run() {
634                // If we are not launching with alt-tab and fast-toggle is enabled, then start
635                // the dozer now
636                RecentsConfiguration config = Recents.getConfiguration();
637                RecentsActivityLaunchState launchState = config.getLaunchState();
638                if (Constants.DebugFlags.App.EnableFastToggleRecents &&
639                        !launchState.launchedWithAltTab) {
640                    mIterateTrigger.startDozing();
641                }
642            }
643        });
644        mRecentsView.startEnterRecentsAnimation(ctx);
645        ctx.postAnimationTrigger.decrement();
646    }
647
648    public final void onBusEvent(AppWidgetProviderChangedEvent event) {
649        refreshSearchWidgetView();
650    }
651
652    public final void onBusEvent(ShowApplicationInfoEvent event) {
653        // Create a new task stack with the application info details activity
654        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
655                Uri.fromParts("package", event.task.key.getComponent().getPackageName(), null));
656        intent.setComponent(intent.resolveActivity(getPackageManager()));
657        TaskStackBuilder.create(this)
658                .addNextIntentWithParentStack(intent).startActivities(null,
659                new UserHandle(event.task.key.userId));
660
661        // Keep track of app-info invocations
662        MetricsLogger.count(this, "overview_app_info", 1);
663    }
664
665    public final void onBusEvent(DismissTaskViewEvent event) {
666        // Remove any stored data from the loader
667        RecentsTaskLoader loader = Recents.getTaskLoader();
668        loader.deleteTaskData(event.task, false);
669
670        // Remove the task from activity manager
671        SystemServicesProxy ssp = Recents.getSystemServices();
672        ssp.removeTask(event.task.key.id);
673    }
674
675    public final void onBusEvent(ResizeTaskEvent event) {
676        getResizeTaskDebugDialog().showResizeTaskDialog(event.task, mRecentsView);
677    }
678
679    public final void onBusEvent(DragStartEvent event) {
680        // Lock the orientation while dragging
681        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
682
683        // TODO: docking requires custom accessibility actions
684    }
685
686    public final void onBusEvent(DragEndEvent event) {
687        // Unlock the orientation when dragging completes
688        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_BEHIND);
689    }
690
691    public final void onBusEvent(ScreenPinningRequestEvent event) {
692        MetricsLogger.count(this, "overview_screen_pinned", 1);
693    }
694
695    private void refreshSearchWidgetView() {
696        if (mSearchWidgetInfo != null) {
697            SystemServicesProxy ssp = Recents.getSystemServices();
698            int searchWidgetId = ssp.getSearchAppWidgetId(this);
699            mSearchWidgetHostView = (RecentsAppWidgetHostView) mAppWidgetHost.createView(
700                    this, searchWidgetId, mSearchWidgetInfo);
701            Bundle opts = new Bundle();
702            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
703                    AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
704            mSearchWidgetHostView.updateAppWidgetOptions(opts);
705            // Set the padding to 0 for this search widget
706            mSearchWidgetHostView.setPadding(0, 0, 0, 0);
707            mRecentsView.setSearchBar(mSearchWidgetHostView);
708        } else {
709            mRecentsView.setSearchBar(null);
710        }
711    }
712}
713