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          */
FinishRecentsRunnable(Intent launchIntent)167         public FinishRecentsRunnable(Intent launchIntent) {
168             mLaunchIntent = launchIntent;
169         }
170 
171         @Override
run()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 
updateRecentsTasks()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 
dismissRecentsToLaunchTargetTaskOrHome(boolean animate)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 
dismissRecentsToFocusedTaskOrHome()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 
dismissRecentsToHome(boolean animateTaskViews)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 
dismissRecentsToHomeIfVisible(boolean animated)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
onCreate(Bundle savedInstanceState)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
onNewIntent(Intent intent)344     protected void onNewIntent(Intent intent) {
345         super.onNewIntent(intent);
346         setIntent(intent);
347     }
348 
349     @Override
onEnterAnimationComplete()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
onResume()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
onPause()429     public void onPause() {
430         super.onPause();
431         mPipRecentsOverlayManager.onRecentsPaused();
432     }
433 
434     @Override
onStop()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
onDestroy()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
onTrimMemory(int level)470     public void onTrimMemory(int level) {
471         RecentsTaskLoader loader = Recents.getTaskLoader();
472         if (loader != null) {
473             loader.onTrimMemory(level);
474         }
475     }
476 
477     @Override
onKeyDown(int keyCode, KeyEvent event)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
onUserInteraction()492     public void onUserInteraction() {
493         EventBus.getDefault().send(new UserInteractionEvent());
494     }
495 
496     @Override
onBackPressed()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 
onBusEvent(ToggleRecentsEvent event)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 
onBusEvent(HideRecentsEvent event)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 
onBusEvent(CancelEnterRecentsWindowAnimationEvent event)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 
onBusEvent(DeleteTaskDataEvent event)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 
onBusEvent(AllTaskViewsDismissedEvent event)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 
onBusEvent(LaunchTaskFailedEvent event)556     public final void onBusEvent(LaunchTaskFailedEvent event) {
557         // Return to Home
558         dismissRecentsToHome(true /* animateTaskViews */);
559     }
560 
561     @Override
onPreDraw()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 
updatePipUI()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      */
requestPipControlsFocus()598     public void requestPipControlsFocus() {
599         if (!mPipManager.isPipShown()) {
600             return;
601         }
602 
603         mTaskStackHorizontalGridView.startFocusLossAnimation();
604         mPipRecentsOverlayManager.requestFocus(mTaskStackViewAdapter.getItemCount() > 0);
605     }
606 }
607