RecentsActivity.java revision 619e40cd56266a362ab7da80cb9e4eba6c33b204
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.TaskStackBuilder;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.res.Configuration;
27import android.net.Uri;
28import android.os.Bundle;
29import android.os.SystemClock;
30import android.os.UserHandle;
31import android.provider.Settings;
32import android.util.Log;
33import android.view.KeyEvent;
34import android.view.View;
35import android.view.ViewTreeObserver;
36import android.view.WindowManager;
37import android.view.WindowManager.LayoutParams;
38
39import com.android.internal.logging.MetricsLogger;
40import com.android.internal.logging.MetricsProto.MetricsEvent;
41import com.android.systemui.R;
42import com.android.systemui.recents.events.EventBus;
43import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
44import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
45import com.android.systemui.recents.events.activity.DebugFlagsChangedEvent;
46import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
47import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent;
48import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
49import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
50import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
51import com.android.systemui.recents.events.activity.HideRecentsEvent;
52import com.android.systemui.recents.events.activity.IterateRecentsEvent;
53import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
54import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
55import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
56import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
57import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
58import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
59import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
60import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
61import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
62import com.android.systemui.recents.events.ui.ShowApplicationInfoEvent;
63import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
64import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
65import com.android.systemui.recents.events.ui.UserInteractionEvent;
66import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
67import com.android.systemui.recents.events.ui.focus.FocusNextTaskViewEvent;
68import com.android.systemui.recents.events.ui.focus.FocusPreviousTaskViewEvent;
69import com.android.systemui.recents.misc.DozeTrigger;
70import com.android.systemui.recents.misc.SystemServicesProxy;
71import com.android.systemui.recents.model.RecentsPackageMonitor;
72import com.android.systemui.recents.model.RecentsTaskLoadPlan;
73import com.android.systemui.recents.model.RecentsTaskLoader;
74import com.android.systemui.recents.model.Task;
75import com.android.systemui.recents.model.TaskStack;
76import com.android.systemui.recents.views.AnimationProps;
77import com.android.systemui.recents.views.RecentsView;
78import com.android.systemui.recents.views.SystemBarScrimViews;
79import com.android.systemui.statusbar.BaseStatusBar;
80
81/**
82 * The main Recents activity that is started from AlternateRecentsComponent.
83 */
84public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreDrawListener {
85
86    private final static String TAG = "RecentsActivity";
87    private final static boolean DEBUG = false;
88
89    public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
90
91    private RecentsPackageMonitor mPackageMonitor;
92    private long mLastTabKeyEventTime;
93    private boolean mFinishedOnStartup;
94    private boolean mIgnoreAltTabRelease;
95    private boolean mIsVisible;
96
97    // Top level views
98    private RecentsView mRecentsView;
99    private SystemBarScrimViews mScrimViews;
100
101    // Runnables to finish the Recents activity
102    private Intent mHomeIntent;
103
104    // The trigger to automatically launch the current task
105    private int mFocusTimerDuration;
106    private DozeTrigger mIterateTrigger;
107    private final UserInteractionEvent mUserInteractionEvent = new UserInteractionEvent();
108
109    /**
110     * A common Runnable to finish Recents by launching Home with an animation depending on the
111     * last activity launch state. Generally we always launch home when we exit Recents rather than
112     * just finishing the activity since we don't know what is behind Recents in the task stack.
113     */
114    class LaunchHomeRunnable implements Runnable {
115
116        Intent mLaunchIntent;
117        ActivityOptions mOpts;
118
119        /**
120         * Creates a finish runnable that starts the specified intent.
121         */
122        public LaunchHomeRunnable(Intent launchIntent, ActivityOptions opts) {
123            mLaunchIntent = launchIntent;
124            mOpts = opts;
125        }
126
127        @Override
128        public void run() {
129            try {
130                ActivityOptions opts = mOpts;
131                if (opts == null) {
132                    opts = ActivityOptions.makeCustomAnimation(RecentsActivity.this,
133                            R.anim.recents_to_launcher_enter, R.anim.recents_to_launcher_exit);
134                }
135                startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
136            } catch (Exception e) {
137                Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
138            }
139        }
140    }
141
142    /**
143     * Broadcast receiver to handle messages from the system
144     */
145    final BroadcastReceiver mSystemBroadcastReceiver = new BroadcastReceiver() {
146        @Override
147        public void onReceive(Context context, Intent intent) {
148            String action = intent.getAction();
149            if (action.equals(Intent.ACTION_SCREEN_OFF)) {
150                // When the screen turns off, dismiss Recents to Home
151                dismissRecentsToHomeIfVisible(false);
152            }
153        }
154    };
155
156    /**
157     * Dismisses recents if we are already visible and the intent is to toggle the recents view.
158     */
159    boolean dismissRecentsToFocusedTask(int logCategory) {
160        SystemServicesProxy ssp = Recents.getSystemServices();
161        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
162            // If we have a focused Task, launch that Task now
163            if (mRecentsView.launchFocusedTask(logCategory)) return true;
164        }
165        return false;
166    }
167
168    /**
169     * Dismisses recents back to the launch target task.
170     */
171    boolean dismissRecentsToLaunchTargetTaskOrHome() {
172        SystemServicesProxy ssp = Recents.getSystemServices();
173        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
174            // If we have a focused Task, launch that Task now
175            if (mRecentsView.launchPreviousTask()) return true;
176            // If none of the other cases apply, then just go Home
177            dismissRecentsToHome(true /* animateTaskViews */);
178        }
179        return false;
180    }
181
182    /**
183     * Dismisses recents if we are already visible and the intent is to toggle the recents view.
184     */
185    boolean dismissRecentsToFocusedTaskOrHome() {
186        SystemServicesProxy ssp = Recents.getSystemServices();
187        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
188            // If we have a focused Task, launch that Task now
189            if (mRecentsView.launchFocusedTask(0 /* logCategory */)) return true;
190            // If none of the other cases apply, then just go Home
191            dismissRecentsToHome(true /* animateTaskViews */);
192            return true;
193        }
194        return false;
195    }
196
197    /**
198     * Dismisses Recents directly to Home without checking whether it is currently visible.
199     */
200    void dismissRecentsToHome(boolean animateTaskViews) {
201        dismissRecentsToHome(animateTaskViews, null);
202    }
203
204    /**
205     * Dismisses Recents directly to Home without checking whether it is currently visible.
206     *
207     * @param overrideAnimation If not null, will override the default animation that is based on
208     *                          how Recents was launched.
209     */
210    void dismissRecentsToHome(boolean animateTaskViews, ActivityOptions overrideAnimation) {
211        DismissRecentsToHomeAnimationStarted dismissEvent =
212                new DismissRecentsToHomeAnimationStarted(animateTaskViews);
213        dismissEvent.addPostAnimationCallback(new LaunchHomeRunnable(mHomeIntent,
214                overrideAnimation));
215        dismissEvent.addPostAnimationCallback(new Runnable() {
216            @Override
217            public void run() {
218                Recents.getSystemServices().sendCloseSystemWindows(
219                        BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
220            }
221        });
222        EventBus.getDefault().send(dismissEvent);
223    }
224
225    /** Dismisses Recents directly to Home if we currently aren't transitioning. */
226    boolean dismissRecentsToHomeIfVisible(boolean animated) {
227        SystemServicesProxy ssp = Recents.getSystemServices();
228        if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) {
229            // Return to Home
230            dismissRecentsToHome(animated);
231            return true;
232        }
233        return false;
234    }
235
236    /** Called with the activity is first created. */
237    @Override
238    public void onCreate(Bundle savedInstanceState) {
239        super.onCreate(savedInstanceState);
240        mFinishedOnStartup = false;
241
242        // In the case that the activity starts up before the Recents component has initialized
243        // (usually when debugging/pushing the SysUI apk), just finish this activity.
244        SystemServicesProxy ssp = Recents.getSystemServices();
245        if (ssp == null) {
246            mFinishedOnStartup = true;
247            finish();
248            return;
249        }
250
251        // Register this activity with the event bus
252        EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
253
254        // Initialize the package monitor
255        mPackageMonitor = new RecentsPackageMonitor();
256        mPackageMonitor.register(this);
257
258        // Set the Recents layout
259        setContentView(R.layout.recents);
260        takeKeyEvents(true);
261        mRecentsView = (RecentsView) findViewById(R.id.recents_view);
262        mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
263                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
264                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
265        mScrimViews = new SystemBarScrimViews(this);
266        getWindow().getAttributes().privateFlags |=
267                WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
268
269        mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
270        mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
271            @Override
272            public void run() {
273                dismissRecentsToFocusedTask(MetricsEvent.OVERVIEW_SELECT_TIMEOUT);
274            }
275        });
276
277        // Create the home intent runnable
278        mHomeIntent = new Intent(Intent.ACTION_MAIN, null);
279        mHomeIntent.addCategory(Intent.CATEGORY_HOME);
280        mHomeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
281                Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
282
283        // Register the broadcast receiver to handle messages when the screen is turned off
284        IntentFilter filter = new IntentFilter();
285        filter.addAction(Intent.ACTION_SCREEN_OFF);
286        registerReceiver(mSystemBroadcastReceiver, filter);
287
288        getWindow().addPrivateFlags(LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION);
289
290        // Reload the stack view
291        reloadStackView();
292    }
293
294    @Override
295    protected void onStart() {
296        super.onStart();
297
298        // Notify that recents is now visible
299        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
300        MetricsLogger.visible(this, MetricsEvent.OVERVIEW_ACTIVITY);
301    }
302
303    @Override
304    protected void onNewIntent(Intent intent) {
305        super.onNewIntent(intent);
306
307        // Reload the stack view
308        reloadStackView();
309    }
310
311    /**
312     * Reloads the stack views upon launching Recents.
313     */
314    private void reloadStackView() {
315        // If the Recents component has preloaded a load plan, then use that to prevent
316        // reconstructing the task stack
317        RecentsTaskLoader loader = Recents.getTaskLoader();
318        RecentsTaskLoadPlan loadPlan = RecentsImpl.consumeInstanceLoadPlan();
319        if (loadPlan == null) {
320            loadPlan = loader.createLoadPlan(this);
321        }
322
323        // Start loading tasks according to the load plan
324        RecentsConfiguration config = Recents.getConfiguration();
325        RecentsActivityLaunchState launchState = config.getLaunchState();
326        if (!loadPlan.hasTasks()) {
327            loader.preloadTasks(loadPlan, launchState.launchedToTaskId,
328                    launchState.launchedFromHome);
329        }
330
331        RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
332        loadOpts.runningTaskId = launchState.launchedToTaskId;
333        loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
334        loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
335        loader.loadTasks(this, loadPlan, loadOpts);
336        TaskStack stack = loadPlan.getTaskStack();
337        mRecentsView.onReload(mIsVisible, stack.getTaskCount() == 0);
338        mRecentsView.updateStack(stack);
339
340        // Update the nav bar scrim, but defer the animation until the enter-window event
341        boolean animateNavBarScrim = !launchState.launchedWhileDocking;
342        updateNavBarScrim(animateNavBarScrim, null);
343
344        // If this is a new instance relaunched by AM, without going through the normal mechanisms,
345        // then we have to manually trigger the enter animation state
346        boolean wasLaunchedByAm = !launchState.launchedFromHome &&
347                !launchState.launchedFromApp;
348        if (wasLaunchedByAm) {
349            EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
350        }
351
352        // Keep track of whether we launched from the nav bar button or via alt-tab
353        if (launchState.launchedWithAltTab) {
354            MetricsLogger.count(this, "overview_trigger_alttab", 1);
355        } else {
356            MetricsLogger.count(this, "overview_trigger_nav_btn", 1);
357        }
358
359        // Keep track of whether we launched from an app or from home
360        if (launchState.launchedFromApp) {
361            Task launchTarget = stack.getLaunchTarget();
362            int launchTaskIndexInStack = launchTarget != null
363                    ? stack.indexOfStackTask(launchTarget)
364                    : 0;
365            MetricsLogger.count(this, "overview_source_app", 1);
366            // If from an app, track the stack index of the app in the stack (for affiliated tasks)
367            MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack);
368        } else {
369            MetricsLogger.count(this, "overview_source_home", 1);
370        }
371
372        // Keep track of the total stack task count
373        int taskCount = mRecentsView.getStack().getTaskCount();
374        MetricsLogger.histogram(this, "overview_task_count", taskCount);
375
376        // After we have resumed, set the visible state until the next onStop() call
377        mIsVisible = true;
378    }
379
380    @Override
381    public void onEnterAnimationComplete() {
382        super.onEnterAnimationComplete();
383        EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
384    }
385
386    @Override
387    protected void onResume() {
388        super.onResume();
389
390        // Notify of the next draw
391        mRecentsView.getViewTreeObserver().addOnPreDrawListener(
392                new ViewTreeObserver.OnPreDrawListener() {
393
394                    @Override
395                    public boolean onPreDraw() {
396                        mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
397                        EventBus.getDefault().post(new RecentsDrawnEvent());
398                        return true;
399                    }
400                });
401    }
402
403    @Override
404    protected void onPause() {
405        super.onPause();
406
407        mIgnoreAltTabRelease = false;
408        mIterateTrigger.stopDozing();
409    }
410
411    @Override
412    public void onConfigurationChanged(Configuration newConfig) {
413        super.onConfigurationChanged(newConfig);
414
415        // Update the nav bar for the current orientation
416        updateNavBarScrim(false /* animateNavBarScrim */, AnimationProps.IMMEDIATE);
417
418        EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */));
419    }
420
421    @Override
422    public void onMultiWindowChanged(boolean inMultiWindow) {
423        super.onMultiWindowChanged(inMultiWindow);
424        EventBus.getDefault().send(new ConfigurationChangedEvent(true /* fromMultiWindow */));
425
426        if (mRecentsView != null) {
427            // Reload the task stack completely
428            RecentsConfiguration config = Recents.getConfiguration();
429            RecentsActivityLaunchState launchState = config.getLaunchState();
430            RecentsTaskLoader loader = Recents.getTaskLoader();
431            RecentsTaskLoadPlan loadPlan = loader.createLoadPlan(this);
432            loader.preloadTasks(loadPlan, -1 /* topTaskId */, false /* isTopTaskHome */);
433
434            RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
435            loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks;
436            loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails;
437            loader.loadTasks(this, loadPlan, loadOpts);
438
439            mRecentsView.updateStack(loadPlan.getTaskStack());
440        }
441
442        EventBus.getDefault().send(new MultiWindowStateChangedEvent(inMultiWindow));
443    }
444
445    @Override
446    protected void onStop() {
447        super.onStop();
448
449        // Notify that recents is now hidden
450        mIsVisible = false;
451        EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
452        MetricsLogger.hidden(this, MetricsEvent.OVERVIEW_ACTIVITY);
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.reset();
460    }
461
462    @Override
463    protected void onDestroy() {
464        super.onDestroy();
465
466        // In the case that the activity finished on startup, just skip the unregistration below
467        if (mFinishedOnStartup) {
468            return;
469        }
470
471        // Unregister the system broadcast receivers
472        unregisterReceiver(mSystemBroadcastReceiver);
473
474        // Unregister any broadcast receivers for the task loader
475        mPackageMonitor.unregister();
476
477        EventBus.getDefault().unregister(this);
478    }
479
480    @Override
481    public void onAttachedToWindow() {
482        super.onAttachedToWindow();
483        EventBus.getDefault().register(mScrimViews, EVENT_BUS_PRIORITY);
484    }
485
486    @Override
487    public void onDetachedFromWindow() {
488        super.onDetachedFromWindow();
489        EventBus.getDefault().unregister(mScrimViews);
490    }
491
492    @Override
493    public void onTrimMemory(int level) {
494        RecentsTaskLoader loader = Recents.getTaskLoader();
495        if (loader != null) {
496            loader.onTrimMemory(level);
497        }
498    }
499
500    @Override
501    public boolean onKeyDown(int keyCode, KeyEvent event) {
502        switch (keyCode) {
503            case KeyEvent.KEYCODE_TAB: {
504                int altTabKeyDelay = getResources().getInteger(R.integer.recents_alt_tab_key_delay);
505                boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() -
506                        mLastTabKeyEventTime) > altTabKeyDelay;
507                if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) {
508                    // Focus the next task in the stack
509                    final boolean backward = event.isShiftPressed();
510                    if (backward) {
511                        EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
512                    } else {
513                        EventBus.getDefault().send(
514                                new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */));
515                    }
516                    mLastTabKeyEventTime = SystemClock.elapsedRealtime();
517
518                    // In the case of another ALT event, don't ignore the next release
519                    if (event.isAltPressed()) {
520                        mIgnoreAltTabRelease = false;
521                    }
522                }
523                return true;
524            }
525            case KeyEvent.KEYCODE_DPAD_UP: {
526                EventBus.getDefault().send(
527                        new FocusNextTaskViewEvent(0 /* timerIndicatorDuration */));
528                return true;
529            }
530            case KeyEvent.KEYCODE_DPAD_DOWN: {
531                EventBus.getDefault().send(new FocusPreviousTaskViewEvent());
532                return true;
533            }
534            case KeyEvent.KEYCODE_DEL:
535            case KeyEvent.KEYCODE_FORWARD_DEL: {
536                if (event.getRepeatCount() <= 0) {
537                    EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
538
539                    // Keep track of deletions by keyboard
540                    MetricsLogger.histogram(this, "overview_task_dismissed_source",
541                            Constants.Metrics.DismissSourceKeyboard);
542                    return true;
543                }
544            }
545            default:
546                break;
547        }
548        return super.onKeyDown(keyCode, event);
549    }
550
551    @Override
552    public void onUserInteraction() {
553        EventBus.getDefault().send(mUserInteractionEvent);
554    }
555
556    @Override
557    public void onBackPressed() {
558        // Back behaves like the recents button so just trigger a toggle event
559        EventBus.getDefault().send(new ToggleRecentsEvent());
560    }
561
562    /**** EventBus events ****/
563
564    public final void onBusEvent(ToggleRecentsEvent event) {
565        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
566        if (launchState.launchedFromHome) {
567            dismissRecentsToHome(true /* animateTaskViews */);
568        } else {
569            dismissRecentsToLaunchTargetTaskOrHome();
570        }
571    }
572
573    public final void onBusEvent(IterateRecentsEvent event) {
574        final RecentsDebugFlags debugFlags = Recents.getDebugFlags();
575
576        // Start dozing after the recents button is clicked
577        int timerIndicatorDuration = 0;
578        if (debugFlags.isFastToggleRecentsEnabled()) {
579            timerIndicatorDuration = getResources().getInteger(
580                    R.integer.recents_subsequent_auto_advance_duration);
581
582            mIterateTrigger.setDozeDuration(timerIndicatorDuration);
583            if (!mIterateTrigger.isDozing()) {
584                mIterateTrigger.startDozing();
585            } else {
586                mIterateTrigger.poke();
587            }
588        }
589
590        // Focus the next task
591        EventBus.getDefault().send(new FocusNextTaskViewEvent(timerIndicatorDuration));
592
593        MetricsLogger.action(this, MetricsEvent.ACTION_OVERVIEW_PAGE);
594    }
595
596    public final void onBusEvent(UserInteractionEvent event) {
597        // Stop the fast-toggle dozer
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            if (!mIgnoreAltTabRelease) {
605                dismissRecentsToFocusedTaskOrHome();
606            }
607        } else if (event.triggeredFromHomeKey) {
608            dismissRecentsToHome(true /* animateTaskViews */);
609
610            // Cancel any pending dozes
611            EventBus.getDefault().send(mUserInteractionEvent);
612        } else {
613            // Do nothing
614        }
615    }
616
617    public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
618        EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(true));
619        mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
620        mRecentsView.invalidate();
621    }
622
623    public final void onBusEvent(ExitRecentsWindowFirstAnimationFrameEvent event) {
624        if (mRecentsView.isLastTaskLaunchedFreeform()) {
625            EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(false));
626        }
627        mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
628        mRecentsView.invalidate();
629    }
630
631    public final void onBusEvent(DockedFirstAnimationFrameEvent event) {
632        mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
633        mRecentsView.invalidate();
634    }
635
636    public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
637        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
638        int launchToTaskId = launchState.launchedToTaskId;
639        if (launchToTaskId != -1 &&
640                (event.launchTask == null || launchToTaskId != event.launchTask.key.id)) {
641            SystemServicesProxy ssp = Recents.getSystemServices();
642            ssp.cancelWindowTransition(launchState.launchedToTaskId);
643            ssp.cancelThumbnailTransition(getTaskId());
644        }
645    }
646
647    public final void onBusEvent(ShowApplicationInfoEvent event) {
648        // Create a new task stack with the application info details activity
649        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
650                Uri.fromParts("package", event.task.key.getComponent().getPackageName(), null));
651        intent.setComponent(intent.resolveActivity(getPackageManager()));
652        TaskStackBuilder.create(this)
653                .addNextIntentWithParentStack(intent).startActivities(null,
654                        new UserHandle(event.task.key.userId));
655
656        // Keep track of app-info invocations
657        MetricsLogger.count(this, "overview_app_info", 1);
658    }
659
660    public final void onBusEvent(DeleteTaskDataEvent event) {
661        // Remove any stored data from the loader
662        RecentsTaskLoader loader = Recents.getTaskLoader();
663        loader.deleteTaskData(event.task, false);
664
665        // Remove the task from activity manager
666        SystemServicesProxy ssp = Recents.getSystemServices();
667        ssp.removeTask(event.task.key.id);
668    }
669
670    public final void onBusEvent(AllTaskViewsDismissedEvent event) {
671        SystemServicesProxy ssp = Recents.getSystemServices();
672        if (ssp.hasDockedTask()) {
673            mRecentsView.showEmptyView(event.msgResId);
674        } else {
675            // Just go straight home (no animation necessary because there are no more task views)
676            dismissRecentsToHome(false /* animateTaskViews */);
677        }
678
679        // Keep track of all-deletions
680        MetricsLogger.count(this, "overview_task_all_dismissed", 1);
681    }
682
683    public final void onBusEvent(LaunchTaskSucceededEvent event) {
684        MetricsLogger.histogram(this, "overview_task_launch_index", event.taskIndexFromStackFront);
685    }
686
687    public final void onBusEvent(LaunchTaskFailedEvent event) {
688        // Return to Home
689        dismissRecentsToHome(true /* animateTaskViews */);
690
691        MetricsLogger.count(this, "overview_task_launch_failed", 1);
692    }
693
694    public final void onBusEvent(ScreenPinningRequestEvent event) {
695        MetricsLogger.count(this, "overview_screen_pinned", 1);
696    }
697
698    public final void onBusEvent(DebugFlagsChangedEvent event) {
699        // Just finish recents so that we can reload the flags anew on the next instantiation
700        finish();
701    }
702
703    public final void onBusEvent(StackViewScrolledEvent event) {
704        // Once the user has scrolled while holding alt-tab, then we should ignore the release of
705        // the key
706        mIgnoreAltTabRelease = true;
707    }
708
709    @Override
710    public boolean onPreDraw() {
711        mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
712        // We post to make sure that this information is delivered after this traversals is
713        // finished.
714        mRecentsView.post(new Runnable() {
715            @Override
716            public void run() {
717                Recents.getSystemServices().endProlongedAnimations();
718            }
719        });
720        return true;
721    }
722
723    /**
724     * Updates the nav bar scrim.
725     */
726    private void updateNavBarScrim(boolean animateNavBarScrim, AnimationProps animation) {
727        // Animate the SystemUI scrims into view
728        SystemServicesProxy ssp = Recents.getSystemServices();
729        int taskCount = mRecentsView.getStack().getTaskCount();
730        boolean hasNavBarScrim = (taskCount > 0) && !ssp.hasTransposedNavBar();
731        mScrimViews.prepareEnterRecentsAnimation(hasNavBarScrim, animateNavBarScrim);
732        if (animateNavBarScrim && animation != null) {
733            mScrimViews.animateNavBarScrimVisibility(true, animation);
734        }
735    }
736}
737