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