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