Home | History | Annotate | Download | only in tv
      1 /*
      2  * Copyright (C) 2016 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 package com.android.systemui.recents.tv;
     17 
     18 import android.app.Activity;
     19 import android.app.ActivityOptions;
     20 import android.content.Intent;
     21 import android.graphics.Rect;
     22 import android.os.Bundle;
     23 import android.os.UserHandle;
     24 import android.util.Log;
     25 import android.view.KeyEvent;
     26 import android.view.View;
     27 import android.view.ViewTreeObserver.OnPreDrawListener;
     28 import android.view.WindowManager;
     29 import android.view.accessibility.AccessibilityEvent;
     30 import android.widget.FrameLayout.LayoutParams;
     31 
     32 import com.android.systemui.R;
     33 import com.android.systemui.recents.Recents;
     34 import com.android.systemui.recents.RecentsActivityLaunchState;
     35 import com.android.systemui.recents.RecentsConfiguration;
     36 import com.android.systemui.recents.RecentsImpl;
     37 import com.android.systemui.recents.events.EventBus;
     38 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
     39 import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
     40 import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
     41 import com.android.systemui.recents.events.activity.HideRecentsEvent;
     42 import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
     43 import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
     44 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
     45 import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
     46 import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
     47 import com.android.systemui.recents.events.ui.UserInteractionEvent;
     48 import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
     49 import com.android.systemui.recents.misc.SystemServicesProxy;
     50 import com.android.systemui.recents.model.RecentsPackageMonitor;
     51 import com.android.systemui.recents.model.RecentsTaskLoadPlan;
     52 import com.android.systemui.recents.model.RecentsTaskLoader;
     53 import com.android.systemui.recents.model.Task;
     54 import com.android.systemui.recents.model.TaskStack;
     55 import com.android.systemui.recents.tv.animations.HomeRecentsEnterExitAnimationHolder;
     56 import com.android.systemui.recents.tv.views.RecentsTvView;
     57 import com.android.systemui.recents.tv.views.TaskCardView;
     58 import com.android.systemui.recents.tv.views.TaskStackHorizontalGridView;
     59 import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
     60 import com.android.systemui.statusbar.BaseStatusBar;
     61 import com.android.systemui.tv.pip.PipManager;
     62 import com.android.systemui.tv.pip.PipRecentsOverlayManager;
     63 
     64 import java.util.ArrayList;
     65 import java.util.Collections;
     66 import java.util.List;
     67 
     68 /**
     69  * The main TV recents activity started by the RecentsImpl.
     70  */
     71 public class RecentsTvActivity extends Activity implements OnPreDrawListener {
     72     private final static String TAG = "RecentsTvActivity";
     73     private final static boolean DEBUG = false;
     74 
     75     public final static int EVENT_BUS_PRIORITY = Recents.EVENT_BUS_PRIORITY + 1;
     76     private final static String RECENTS_HOME_INTENT_EXTRA =
     77             "com.android.systemui.recents.tv.RecentsTvActivity.RECENTS_HOME_INTENT_EXTRA";
     78 
     79     private boolean mFinishedOnStartup;
     80     private RecentsPackageMonitor mPackageMonitor;
     81     private long mLastTabKeyEventTime;
     82     private boolean mIgnoreAltTabRelease;
     83     private boolean mLaunchedFromHome;
     84     private boolean mTalkBackEnabled;
     85 
     86     private RecentsTvView mRecentsView;
     87     private View mPipView;
     88     private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
     89     private TaskStackHorizontalGridView mTaskStackHorizontalGridView;
     90     private FinishRecentsRunnable mFinishLaunchHomeRunnable;
     91     private HomeRecentsEnterExitAnimationHolder mHomeRecentsEnterExitAnimationHolder;
     92 
     93     private final PipManager mPipManager = PipManager.getInstance();
     94     private final PipManager.Listener mPipListener = new PipManager.Listener() {
     95         @Override
     96         public void onPipEntered() {
     97             updatePipUI();
     98         }
     99 
    100         @Override
    101         public void onPipActivityClosed() {
    102             updatePipUI();
    103         }
    104 
    105         @Override
    106         public void onShowPipMenu() {
    107             updatePipUI();
    108         }
    109 
    110         @Override
    111         public void onMoveToFullscreen() {
    112             // Recents should be dismissed when PIP moves to fullscreen. If not, Recents will
    113             // be unnecessarily shown in the scenario: PIP->Fullscreen->PIP.
    114             // Do not show Recents close animation because PIP->Fullscreen animation will be shown
    115             // instead.
    116             dismissRecentsToLaunchTargetTaskOrHome(false);
    117         }
    118 
    119         @Override
    120         public void onPipResizeAboutToStart() { }
    121     };
    122     private PipRecentsOverlayManager mPipRecentsOverlayManager;
    123     private final PipRecentsOverlayManager.Callback mPipRecentsOverlayManagerCallback =
    124             new PipRecentsOverlayManager.Callback() {
    125                 @Override
    126                 public void onClosed() {
    127                     dismissRecentsToLaunchTargetTaskOrHome(true);
    128                 }
    129 
    130                 @Override
    131                 public void onBackPressed() {
    132                     RecentsTvActivity.this.onBackPressed();
    133                 }
    134 
    135                 @Override
    136                 public void onRecentsFocused() {
    137                     if (mTalkBackEnabled) {
    138                         mTaskStackHorizontalGridView.requestFocus();
    139                         mTaskStackHorizontalGridView.sendAccessibilityEvent(
    140                                 AccessibilityEvent.TYPE_VIEW_FOCUSED);
    141                     }
    142                     mTaskStackHorizontalGridView.startFocusGainAnimation();
    143                 }
    144             };
    145 
    146     private final View.OnFocusChangeListener mPipViewFocusChangeListener =
    147             new View.OnFocusChangeListener() {
    148                 @Override
    149                 public void onFocusChange(View v, boolean hasFocus) {
    150                     if (hasFocus) {
    151                         requestPipControlsFocus();
    152                     }
    153                 }
    154             };
    155 
    156     /**
    157      * A common Runnable to finish Recents by launching Home with an animation depending on the
    158      * last activity launch state.  Generally we always launch home when we exit Recents rather than
    159      * just finishing the activity since we don't know what is behind Recents in the task stack.
    160      */
    161     class FinishRecentsRunnable implements Runnable {
    162         Intent mLaunchIntent;
    163 
    164         /**
    165          * Creates a finish runnable that starts the specified intent.
    166          */
    167         public FinishRecentsRunnable(Intent launchIntent) {
    168             mLaunchIntent = launchIntent;
    169         }
    170 
    171         @Override
    172         public void run() {
    173             try {
    174                 ActivityOptions opts = ActivityOptions.makeCustomAnimation(RecentsTvActivity.this,
    175                         R.anim.recents_to_launcher_enter, R.anim.recents_to_launcher_exit);
    176                 startActivityAsUser(mLaunchIntent, opts.toBundle(), UserHandle.CURRENT);
    177             } catch (Exception e) {
    178                 Log.e(TAG, getString(R.string.recents_launch_error_message, "Home"), e);
    179             }
    180         }
    181     }
    182 
    183     private void updateRecentsTasks() {
    184         RecentsTaskLoader loader = Recents.getTaskLoader();
    185         RecentsTaskLoadPlan plan = RecentsImpl.consumeInstanceLoadPlan();
    186         if (plan == null) {
    187             plan = loader.createLoadPlan(this);
    188         }
    189 
    190         RecentsConfiguration config = Recents.getConfiguration();
    191         RecentsActivityLaunchState launchState = config.getLaunchState();
    192         if (!plan.hasTasks()) {
    193             loader.preloadTasks(plan, -1, !launchState.launchedFromHome);
    194         }
    195 
    196         int numVisibleTasks = TaskCardView.getNumberOfVisibleTasks(getApplicationContext());
    197         mLaunchedFromHome = launchState.launchedFromHome;
    198         TaskStack stack = plan.getTaskStack();
    199         RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
    200         loadOpts.runningTaskId = launchState.launchedToTaskId;
    201         loadOpts.numVisibleTasks = numVisibleTasks;
    202         loadOpts.numVisibleTaskThumbnails = numVisibleTasks;
    203         loader.loadTasks(this, plan, loadOpts);
    204 
    205         List stackTasks = stack.getStackTasks();
    206         Collections.reverse(stackTasks);
    207         if (mTaskStackViewAdapter == null) {
    208             mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stackTasks);
    209             mTaskStackHorizontalGridView = mRecentsView
    210                     .setTaskStackViewAdapter(mTaskStackViewAdapter);
    211             mHomeRecentsEnterExitAnimationHolder = new HomeRecentsEnterExitAnimationHolder(
    212                     getApplicationContext(), mTaskStackHorizontalGridView);
    213         } else {
    214             mTaskStackViewAdapter.setNewStackTasks(stackTasks);
    215         }
    216         mRecentsView.init(stack);
    217 
    218         if (launchState.launchedToTaskId != -1) {
    219             ArrayList<Task> tasks = stack.getStackTasks();
    220             int taskCount = tasks.size();
    221             for (int i = 0; i < taskCount; i++) {
    222                 Task t = tasks.get(i);
    223                 if (t.key.id == launchState.launchedToTaskId) {
    224                     t.isLaunchTarget = true;
    225                     break;
    226                 }
    227             }
    228         }
    229     }
    230 
    231     boolean dismissRecentsToLaunchTargetTaskOrHome(boolean animate) {
    232         SystemServicesProxy ssp = Recents.getSystemServices();
    233         if (ssp.isRecentsActivityVisible()) {
    234             // If we have a focused Task, launch that Task now
    235             if (mRecentsView.launchPreviousTask(animate)) {
    236               return true;
    237             }
    238             // If none of the other cases apply, then just go Home
    239             dismissRecentsToHome(animate /* animateTaskViews */);
    240         }
    241         return false;
    242     }
    243 
    244     boolean dismissRecentsToFocusedTaskOrHome() {
    245         SystemServicesProxy ssp = Recents.getSystemServices();
    246         if (ssp.isRecentsActivityVisible()) {
    247             // If we have a focused Task, launch that Task now
    248             if (mRecentsView.launchFocusedTask()) return true;
    249             // If none of the other cases apply, then just go Home
    250             dismissRecentsToHome(true /* animateTaskViews */);
    251             return true;
    252         }
    253         return false;
    254     }
    255 
    256     void dismissRecentsToHome(boolean animateTaskViews) {
    257         Runnable closeSystemWindows = new Runnable() {
    258             @Override
    259             public void run() {
    260                 Recents.getSystemServices().sendCloseSystemWindows(
    261                         BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
    262             }
    263         };
    264         DismissRecentsToHomeAnimationStarted dismissEvent =
    265                 new DismissRecentsToHomeAnimationStarted(animateTaskViews);
    266         dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
    267         dismissEvent.addPostAnimationCallback(closeSystemWindows);
    268 
    269         if (mTaskStackHorizontalGridView.getChildCount() > 0 && animateTaskViews) {
    270             mHomeRecentsEnterExitAnimationHolder.startExitAnimation(dismissEvent);
    271         } else {
    272             closeSystemWindows.run();
    273             mFinishLaunchHomeRunnable.run();
    274         }
    275     }
    276 
    277     boolean dismissRecentsToHomeIfVisible(boolean animated) {
    278         SystemServicesProxy ssp = Recents.getSystemServices();
    279         if (ssp.isRecentsActivityVisible()) {
    280             // Return to Home
    281             dismissRecentsToHome(animated);
    282             return true;
    283         }
    284         return false;
    285     }
    286 
    287     @Override
    288     public void onCreate(Bundle savedInstanceState) {
    289         super.onCreate(savedInstanceState);
    290         mFinishedOnStartup = false;
    291 
    292         // In the case that the activity starts up before the Recents component has initialized
    293         // (usually when debugging/pushing the SysUI apk), just finish this activity.
    294         SystemServicesProxy ssp = Recents.getSystemServices();
    295         if (ssp == null) {
    296             mFinishedOnStartup = true;
    297             finish();
    298             return;
    299         }
    300         mPipRecentsOverlayManager = PipManager.getInstance().getPipRecentsOverlayManager();
    301 
    302         // Register this activity with the event bus
    303         EventBus.getDefault().register(this, EVENT_BUS_PRIORITY);
    304 
    305         mPackageMonitor = new RecentsPackageMonitor();
    306         mPackageMonitor.register(this);
    307 
    308         // Set the Recents layout
    309         setContentView(R.layout.recents_on_tv);
    310 
    311         mRecentsView = (RecentsTvView) findViewById(R.id.recents_view);
    312         mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
    313                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
    314                 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
    315 
    316         mPipView = findViewById(R.id.pip);
    317         mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener);
    318         // Place mPipView at the PIP bounds for fine tuned focus handling.
    319         Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
    320         LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
    321         lp.width = pipBounds.width();
    322         lp.height = pipBounds.height();
    323         lp.leftMargin = pipBounds.left;
    324         lp.topMargin = pipBounds.top;
    325         mPipView.setLayoutParams(lp);
    326 
    327         mPipRecentsOverlayManager.setCallback(mPipRecentsOverlayManagerCallback);
    328 
    329         getWindow().getAttributes().privateFlags |=
    330                 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
    331 
    332         // Create the home intent runnable
    333         Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
    334         homeIntent.addCategory(Intent.CATEGORY_HOME);
    335         homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
    336                 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
    337         homeIntent.putExtra(RECENTS_HOME_INTENT_EXTRA, true);
    338         mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent);
    339 
    340         mPipManager.addListener(mPipListener);
    341     }
    342 
    343     @Override
    344     protected void onNewIntent(Intent intent) {
    345         super.onNewIntent(intent);
    346         setIntent(intent);
    347     }
    348 
    349     @Override
    350     public void onEnterAnimationComplete() {
    351         super.onEnterAnimationComplete();
    352         if(mLaunchedFromHome) {
    353             mHomeRecentsEnterExitAnimationHolder.startEnterAnimation(mPipManager.isPipShown());
    354         }
    355         EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
    356     }
    357 
    358     @Override
    359     public void onResume() {
    360         super.onResume();
    361         mPipRecentsOverlayManager.onRecentsResumed();
    362         // Update the recent tasks
    363         updateRecentsTasks();
    364 
    365         // If this is a new instance from a configuration change, then we have to manually trigger
    366         // the enter animation state, or if recents was relaunched by AM, without going through
    367         // the normal mechanisms
    368         RecentsConfiguration config = Recents.getConfiguration();
    369         RecentsActivityLaunchState launchState = config.getLaunchState();
    370         boolean wasLaunchedByAm = !launchState.launchedFromHome &&
    371                 !launchState.launchedFromApp;
    372         if (wasLaunchedByAm) {
    373             EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
    374         }
    375 
    376         // Notify that recents is now visible
    377         SystemServicesProxy ssp = Recents.getSystemServices();
    378         EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, true));
    379         if(mTaskStackHorizontalGridView.getStack().getTaskCount() > 1 && !mLaunchedFromHome) {
    380             // If there are 2 or more tasks, and we are not launching from home
    381             // set the selected position to the 2nd task to allow for faster app switching
    382             mTaskStackHorizontalGridView.setSelectedPosition(1);
    383         } else {
    384             mTaskStackHorizontalGridView.setSelectedPosition(0);
    385         }
    386         mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
    387 
    388         View dismissPlaceholder = findViewById(R.id.dismiss_placeholder);
    389         mTalkBackEnabled = ssp.isTouchExplorationEnabled();
    390         if (mTalkBackEnabled) {
    391             dismissPlaceholder.setAccessibilityTraversalBefore(R.id.task_list);
    392             dismissPlaceholder.setAccessibilityTraversalAfter(R.id.dismiss_placeholder);
    393             mTaskStackHorizontalGridView.setAccessibilityTraversalAfter(R.id.dismiss_placeholder);
    394             mTaskStackHorizontalGridView.setAccessibilityTraversalBefore(R.id.pip);
    395             dismissPlaceholder.setOnClickListener(new View.OnClickListener() {
    396                 @Override
    397                 public void onClick(View v) {
    398                     mTaskStackHorizontalGridView.requestFocus();
    399                     mTaskStackHorizontalGridView.
    400                             sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
    401                     Task focusedTask = mTaskStackHorizontalGridView.getFocusedTask();
    402                     if (focusedTask != null) {
    403                         mTaskStackViewAdapter.removeTask(focusedTask);
    404                         EventBus.getDefault().send(new DeleteTaskDataEvent(focusedTask));
    405                     }
    406                 }
    407             });
    408         }
    409 
    410         // Initialize PIP UI
    411         if (mPipManager.isPipShown()) {
    412             if (mTalkBackEnabled) {
    413                 // If talkback is on, use the mPipView to handle focus changes
    414                 // between recents row and PIP controls.
    415                 mPipView.setVisibility(View.VISIBLE);
    416             } else {
    417                 mPipView.setVisibility(View.GONE);
    418             }
    419             // When PIP view has focus, recents overlay view will takes the focus
    420             // as if it's the part of the Recents UI.
    421             mPipRecentsOverlayManager.requestFocus(mTaskStackViewAdapter.getItemCount() > 0);
    422         } else {
    423             mPipView.setVisibility(View.GONE);
    424             mPipRecentsOverlayManager.removePipRecentsOverlayView();
    425         }
    426     }
    427 
    428     @Override
    429     public void onPause() {
    430         super.onPause();
    431         mPipRecentsOverlayManager.onRecentsPaused();
    432     }
    433 
    434     @Override
    435     protected void onStop() {
    436         super.onStop();
    437 
    438         mIgnoreAltTabRelease = false;
    439         // Notify that recents is now hidden
    440         EventBus.getDefault().send(new RecentsVisibilityChangedEvent(this, false));
    441 
    442         // Workaround for b/22542869, if the RecentsActivity is started again, but without going
    443         // through SystemUI, we need to reset the config launch flags to ensure that we do not
    444         // wait on the system to send a signal that was never queued.
    445         RecentsConfiguration config = Recents.getConfiguration();
    446         RecentsActivityLaunchState launchState = config.getLaunchState();
    447         launchState.reset();
    448 
    449         // Workaround for b/28333917.
    450         finish();
    451     }
    452 
    453     @Override
    454     protected void onDestroy() {
    455         super.onDestroy();
    456 
    457         mPipManager.removeListener(mPipListener);
    458         // In the case that the activity finished on startup, just skip the unregistration below
    459         if (mFinishedOnStartup) {
    460             return;
    461         }
    462 
    463         // Unregister any broadcast receivers for the task loader
    464         mPackageMonitor.unregister();
    465 
    466         EventBus.getDefault().unregister(this);
    467     }
    468 
    469     @Override
    470     public void onTrimMemory(int level) {
    471         RecentsTaskLoader loader = Recents.getTaskLoader();
    472         if (loader != null) {
    473             loader.onTrimMemory(level);
    474         }
    475     }
    476 
    477     @Override
    478     public boolean onKeyDown(int keyCode, KeyEvent event) {
    479         switch (keyCode) {
    480             case KeyEvent.KEYCODE_DEL:
    481             case KeyEvent.KEYCODE_FORWARD_DEL: {
    482                 EventBus.getDefault().send(new DismissFocusedTaskViewEvent());
    483                 return true;
    484             }
    485             default:
    486                 break;
    487         }
    488         return super.onKeyDown(keyCode, event);
    489     }
    490 
    491     @Override
    492     public void onUserInteraction() {
    493         EventBus.getDefault().send(new UserInteractionEvent());
    494     }
    495 
    496     @Override
    497     public void onBackPressed() {
    498         // Back behaves like the recents button so just trigger a toggle event
    499         EventBus.getDefault().send(new ToggleRecentsEvent());
    500     }
    501 
    502     /**** EventBus events ****/
    503 
    504     public final void onBusEvent(ToggleRecentsEvent event) {
    505         RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
    506         if (launchState.launchedFromHome) {
    507             dismissRecentsToHome(true /* animateTaskViews */);
    508         } else {
    509             dismissRecentsToLaunchTargetTaskOrHome(true);
    510         }
    511     }
    512 
    513     public final void onBusEvent(HideRecentsEvent event) {
    514         if (event.triggeredFromAltTab) {
    515             // If we are hiding from releasing Alt-Tab, dismiss Recents to the focused app
    516             if (!mIgnoreAltTabRelease) {
    517                 dismissRecentsToFocusedTaskOrHome();
    518             }
    519         } else if (event.triggeredFromHomeKey) {
    520                 dismissRecentsToHome(true /* animateTaskViews */);
    521         } else {
    522             // Do nothing
    523         }
    524     }
    525 
    526     public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
    527         RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
    528         int launchToTaskId = launchState.launchedToTaskId;
    529         if (launchToTaskId != -1 &&
    530                 (event.launchTask == null || launchToTaskId != event.launchTask.key.id)) {
    531             SystemServicesProxy ssp = Recents.getSystemServices();
    532             ssp.cancelWindowTransition(launchState.launchedToTaskId);
    533             ssp.cancelThumbnailTransition(getTaskId());
    534         }
    535     }
    536 
    537     public final void onBusEvent(DeleteTaskDataEvent event) {
    538         // Remove any stored data from the loader
    539         RecentsTaskLoader loader = Recents.getTaskLoader();
    540         loader.deleteTaskData(event.task, false);
    541 
    542         // Remove the task from activity manager
    543         SystemServicesProxy ssp = Recents.getSystemServices();
    544         ssp.removeTask(event.task.key.id);
    545     }
    546 
    547     public final void onBusEvent(AllTaskViewsDismissedEvent event) {
    548         if (mPipManager.isPipShown()) {
    549             mRecentsView.showEmptyView();
    550             mPipRecentsOverlayManager.requestFocus(false);
    551         } else {
    552             dismissRecentsToHome(false);
    553         }
    554     }
    555 
    556     public final void onBusEvent(LaunchTaskFailedEvent event) {
    557         // Return to Home
    558         dismissRecentsToHome(true /* animateTaskViews */);
    559     }
    560 
    561     @Override
    562     public boolean onPreDraw() {
    563         mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
    564         // Sets the initial values for enter animation.
    565         // Animation will be started in {@link #onEnterAnimationComplete()}
    566         if (mLaunchedFromHome) {
    567             mHomeRecentsEnterExitAnimationHolder
    568                     .setEnterFromHomeStartingAnimationValues(mPipManager.isPipShown());
    569         } else {
    570             mHomeRecentsEnterExitAnimationHolder
    571                     .setEnterFromAppStartingAnimationValues(mPipManager.isPipShown());
    572         }
    573         // We post to make sure that this information is delivered after this traversals is
    574         // finished.
    575         mRecentsView.post(new Runnable() {
    576             @Override
    577             public void run() {
    578                 Recents.getSystemServices().endProlongedAnimations();
    579             }
    580         });
    581         return true;
    582     }
    583 
    584     private void updatePipUI() {
    585         if (!mPipManager.isPipShown()) {
    586             mPipRecentsOverlayManager.removePipRecentsOverlayView();
    587             mTaskStackHorizontalGridView.startFocusLossAnimation();
    588         } else {
    589             Log.w(TAG, "An activity entered PIP mode while Recents is shown");
    590         }
    591     }
    592 
    593     /**
    594      * Requests the focus to the PIP controls.
    595      * This starts the relevant recents row animation
    596      * and give focus to the recents overlay if needed.
    597      */
    598     public void requestPipControlsFocus() {
    599         if (!mPipManager.isPipShown()) {
    600             return;
    601         }
    602 
    603         mTaskStackHorizontalGridView.startFocusLossAnimation();
    604         mPipRecentsOverlayManager.requestFocus(mTaskStackViewAdapter.getItemCount() > 0);
    605     }
    606 }
    607