RecentsView.java revision b34b7deb1e5dacab2b3dd54dfd63918ce139083f
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.views; 18 19import static android.app.ActivityManager.StackId.INVALID_STACK_ID; 20 21import android.animation.Animator; 22import android.animation.ObjectAnimator; 23import android.app.ActivityOptions.OnAnimationStartedListener; 24import android.content.Context; 25import android.graphics.Canvas; 26import android.graphics.Color; 27import android.graphics.Outline; 28import android.graphics.Rect; 29import android.graphics.drawable.ColorDrawable; 30import android.graphics.drawable.Drawable; 31import android.util.ArraySet; 32import android.util.AttributeSet; 33import android.view.AppTransitionAnimationSpec; 34import android.view.IAppTransitionAnimationSpecsFuture; 35import android.view.LayoutInflater; 36import android.view.MotionEvent; 37import android.view.View; 38import android.view.ViewDebug; 39import android.view.ViewOutlineProvider; 40import android.view.ViewPropertyAnimator; 41import android.view.WindowInsets; 42import android.widget.FrameLayout; 43import android.widget.TextView; 44 45import com.android.internal.logging.MetricsLogger; 46import com.android.internal.logging.MetricsProto.MetricsEvent; 47import com.android.systemui.Interpolators; 48import com.android.systemui.R; 49import com.android.systemui.recents.Recents; 50import com.android.systemui.recents.RecentsActivity; 51import com.android.systemui.recents.RecentsActivityLaunchState; 52import com.android.systemui.recents.RecentsConfiguration; 53import com.android.systemui.recents.RecentsDebugFlags; 54import com.android.systemui.recents.events.EventBus; 55import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted; 56import com.android.systemui.recents.events.activity.DockedFirstAnimationFrameEvent; 57import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent; 58import com.android.systemui.recents.events.activity.HideStackActionButtonEvent; 59import com.android.systemui.recents.events.activity.LaunchTaskEvent; 60import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent; 61import com.android.systemui.recents.events.activity.ShowStackActionButtonEvent; 62import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent; 63import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent; 64import com.android.systemui.recents.events.ui.DraggingInRecentsEndedEvent; 65import com.android.systemui.recents.events.ui.DraggingInRecentsEvent; 66import com.android.systemui.recents.events.ui.dragndrop.DragDropTargetChangedEvent; 67import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent; 68import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent; 69import com.android.systemui.recents.events.ui.dragndrop.DragStartEvent; 70import com.android.systemui.recents.misc.ReferenceCountedTrigger; 71import com.android.systemui.recents.misc.SystemServicesProxy; 72import com.android.systemui.recents.misc.Utilities; 73import com.android.systemui.recents.model.Task; 74import com.android.systemui.recents.model.TaskStack; 75import com.android.systemui.recents.views.RecentsTransitionHelper.AnimationSpecComposer; 76import com.android.systemui.stackdivider.WindowManagerProxy; 77import com.android.systemui.statusbar.FlingAnimationUtils; 78 79import java.io.FileDescriptor; 80import java.io.PrintWriter; 81import java.util.ArrayList; 82import java.util.List; 83 84/** 85 * This view is the the top level layout that contains TaskStacks (which are laid out according 86 * to their SpaceNode bounds. 87 */ 88public class RecentsView extends FrameLayout { 89 90 private static final String TAG = "RecentsView"; 91 92 private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200; 93 private static final float DEFAULT_SCRIM_ALPHA = 0.33f; 94 95 private static final int SHOW_STACK_ACTION_BUTTON_DURATION = 134; 96 private static final int HIDE_STACK_ACTION_BUTTON_DURATION = 100; 97 98 private TaskStack mStack; 99 private TaskStackView mTaskStackView; 100 private TextView mStackActionButton; 101 private TextView mEmptyView; 102 103 private boolean mAwaitingFirstLayout = true; 104 private boolean mLastTaskLaunchedWasFreeform; 105 106 @ViewDebug.ExportedProperty(category="recents") 107 private Rect mSystemInsets = new Rect(); 108 private int mDividerSize; 109 110 private Drawable mBackgroundScrim = new ColorDrawable( 111 Color.argb((int) (DEFAULT_SCRIM_ALPHA * 255), 0, 0, 0)).mutate(); 112 private Animator mBackgroundScrimAnimator; 113 114 private RecentsTransitionHelper mTransitionHelper; 115 @ViewDebug.ExportedProperty(deepExport=true, prefix="touch_") 116 private RecentsViewTouchHandler mTouchHandler; 117 private final FlingAnimationUtils mFlingAnimationUtils; 118 119 public RecentsView(Context context) { 120 this(context, null); 121 } 122 123 public RecentsView(Context context, AttributeSet attrs) { 124 this(context, attrs, 0); 125 } 126 127 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) { 128 this(context, attrs, defStyleAttr, 0); 129 } 130 131 public RecentsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 132 super(context, attrs, defStyleAttr, defStyleRes); 133 setWillNotDraw(false); 134 135 SystemServicesProxy ssp = Recents.getSystemServices(); 136 mTransitionHelper = new RecentsTransitionHelper(getContext()); 137 mDividerSize = ssp.getDockedDividerSize(context); 138 mTouchHandler = new RecentsViewTouchHandler(this); 139 mFlingAnimationUtils = new FlingAnimationUtils(context, 0.3f); 140 141 LayoutInflater inflater = LayoutInflater.from(context); 142 if (RecentsDebugFlags.Static.EnableStackActionButton) { 143 mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button, 144 this, false); 145 mStackActionButton.setOnClickListener(new View.OnClickListener() { 146 @Override 147 public void onClick(View v) { 148 EventBus.getDefault().send(new DismissAllTaskViewsEvent()); 149 } 150 }); 151 addView(mStackActionButton); 152 } 153 mEmptyView = (TextView) inflater.inflate(R.layout.recents_empty, this, false); 154 addView(mEmptyView); 155 } 156 157 /** 158 * Called from RecentsActivity when it is relaunched. 159 */ 160 public void onReload(boolean isResumingFromVisible, boolean isTaskStackEmpty) { 161 RecentsConfiguration config = Recents.getConfiguration(); 162 RecentsActivityLaunchState launchState = config.getLaunchState(); 163 164 if (mTaskStackView == null) { 165 isResumingFromVisible = false; 166 mTaskStackView = new TaskStackView(getContext()); 167 mTaskStackView.setSystemInsets(mSystemInsets); 168 addView(mTaskStackView); 169 } 170 171 // Reset the state 172 mAwaitingFirstLayout = !isResumingFromVisible; 173 mLastTaskLaunchedWasFreeform = false; 174 175 // Update the stack 176 mTaskStackView.onReload(isResumingFromVisible); 177 178 if (isResumingFromVisible) { 179 // If we are already visible, then restore the background scrim 180 animateBackgroundScrim(1f, DEFAULT_UPDATE_SCRIM_DURATION); 181 } else { 182 // If we are already occluded by the app, then set the final background scrim alpha now. 183 // Otherwise, defer until the enter animation completes to animate the scrim alpha with 184 // the tasks for the home animation. 185 if (launchState.launchedViaDockGesture || launchState.launchedFromApp 186 || isTaskStackEmpty) { 187 mBackgroundScrim.setAlpha(255); 188 } else { 189 mBackgroundScrim.setAlpha(0); 190 } 191 } 192 } 193 194 /** 195 * Called from RecentsActivity when the task stack is updated. 196 */ 197 public void updateStack(TaskStack stack, boolean setStackViewTasks) { 198 mStack = stack; 199 if (setStackViewTasks) { 200 mTaskStackView.setTasks(stack, true /* allowNotifyStackChanges */); 201 } 202 203 // Update the top level view's visibilities 204 if (stack.getTaskCount() > 0) { 205 hideEmptyView(); 206 } else { 207 showEmptyView(R.string.recents_empty_message); 208 } 209 } 210 211 /** 212 * Returns the current TaskStack. 213 */ 214 public TaskStack getStack() { 215 return mStack; 216 } 217 218 /* 219 * Returns the window background scrim. 220 */ 221 public Drawable getBackgroundScrim() { 222 return mBackgroundScrim; 223 } 224 225 /** 226 * Returns whether the last task launched was in the freeform stack or not. 227 */ 228 public boolean isLastTaskLaunchedFreeform() { 229 return mLastTaskLaunchedWasFreeform; 230 } 231 232 /** Launches the focused task from the first stack if possible */ 233 public boolean launchFocusedTask(int logEvent) { 234 if (mTaskStackView != null) { 235 Task task = mTaskStackView.getFocusedTask(); 236 if (task != null) { 237 TaskView taskView = mTaskStackView.getChildViewForTask(task); 238 EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, 239 INVALID_STACK_ID, false)); 240 241 if (logEvent != 0) { 242 MetricsLogger.action(getContext(), logEvent, 243 task.key.getComponent().toString()); 244 } 245 return true; 246 } 247 } 248 return false; 249 } 250 251 /** Launches the task that recents was launched from if possible */ 252 public boolean launchPreviousTask() { 253 if (mTaskStackView != null) { 254 TaskStack stack = mTaskStackView.getStack(); 255 Task task = stack.getLaunchTarget(); 256 if (task != null) { 257 TaskView taskView = mTaskStackView.getChildViewForTask(task); 258 EventBus.getDefault().send(new LaunchTaskEvent(taskView, task, null, 259 INVALID_STACK_ID, false)); 260 return true; 261 } 262 } 263 return false; 264 } 265 266 /** Launches a given task. */ 267 public boolean launchTask(Task task, Rect taskBounds, int destinationStack) { 268 if (mTaskStackView != null) { 269 // Iterate the stack views and try and find the given task. 270 List<TaskView> taskViews = mTaskStackView.getTaskViews(); 271 int taskViewCount = taskViews.size(); 272 for (int j = 0; j < taskViewCount; j++) { 273 TaskView tv = taskViews.get(j); 274 if (tv.getTask() == task) { 275 EventBus.getDefault().send(new LaunchTaskEvent(tv, task, taskBounds, 276 destinationStack, false)); 277 return true; 278 } 279 } 280 } 281 return false; 282 } 283 284 /** 285 * Hides the task stack and shows the empty view. 286 */ 287 public void showEmptyView(int msgResId) { 288 mTaskStackView.setVisibility(View.INVISIBLE); 289 mEmptyView.setText(msgResId); 290 mEmptyView.setVisibility(View.VISIBLE); 291 mEmptyView.bringToFront(); 292 if (RecentsDebugFlags.Static.EnableStackActionButton) { 293 mStackActionButton.bringToFront(); 294 } 295 } 296 297 /** 298 * Shows the task stack and hides the empty view. 299 */ 300 public void hideEmptyView() { 301 mEmptyView.setVisibility(View.INVISIBLE); 302 mTaskStackView.setVisibility(View.VISIBLE); 303 mTaskStackView.bringToFront(); 304 if (RecentsDebugFlags.Static.EnableStackActionButton) { 305 mStackActionButton.bringToFront(); 306 } 307 } 308 309 @Override 310 protected void onAttachedToWindow() { 311 EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1); 312 EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 2); 313 super.onAttachedToWindow(); 314 } 315 316 @Override 317 protected void onDetachedFromWindow() { 318 super.onDetachedFromWindow(); 319 EventBus.getDefault().unregister(this); 320 EventBus.getDefault().unregister(mTouchHandler); 321 } 322 323 /** 324 * This is called with the full size of the window since we are handling our own insets. 325 */ 326 @Override 327 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 328 int width = MeasureSpec.getSize(widthMeasureSpec); 329 int height = MeasureSpec.getSize(heightMeasureSpec); 330 331 if (mTaskStackView.getVisibility() != GONE) { 332 mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec); 333 } 334 335 // Measure the empty view to the full size of the screen 336 if (mEmptyView.getVisibility() != GONE) { 337 measureChild(mEmptyView, MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), 338 MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST)); 339 } 340 341 if (RecentsDebugFlags.Static.EnableStackActionButton) { 342 // Measure the stack action button within the constraints of the space above the stack 343 Rect buttonBounds = mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect; 344 measureChild(mStackActionButton, 345 MeasureSpec.makeMeasureSpec(buttonBounds.width(), MeasureSpec.AT_MOST), 346 MeasureSpec.makeMeasureSpec(buttonBounds.height(), MeasureSpec.AT_MOST)); 347 } 348 349 setMeasuredDimension(width, height); 350 } 351 352 /** 353 * This is called with the full size of the window since we are handling our own insets. 354 */ 355 @Override 356 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 357 if (mTaskStackView.getVisibility() != GONE) { 358 mTaskStackView.layout(left, top, left + getMeasuredWidth(), top + getMeasuredHeight()); 359 } 360 361 // Layout the empty view 362 if (mEmptyView.getVisibility() != GONE) { 363 int leftRightInsets = mSystemInsets.left + mSystemInsets.right; 364 int topBottomInsets = mSystemInsets.top + mSystemInsets.bottom; 365 int childWidth = mEmptyView.getMeasuredWidth(); 366 int childHeight = mEmptyView.getMeasuredHeight(); 367 int childLeft = left + mSystemInsets.left + 368 Math.max(0, (right - left - leftRightInsets - childWidth)) / 2; 369 int childTop = top + mSystemInsets.top + 370 Math.max(0, (bottom - top - topBottomInsets - childHeight)) / 2; 371 mEmptyView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); 372 } 373 374 if (RecentsDebugFlags.Static.EnableStackActionButton) { 375 // Layout the stack action button such that its drawable is start-aligned with the 376 // stack, vertically centered in the available space above the stack 377 Rect buttonBounds = getStackActionButtonBoundsFromStackLayout(); 378 mStackActionButton.layout(buttonBounds.left, buttonBounds.top, buttonBounds.right, 379 buttonBounds.bottom); 380 } 381 382 if (mAwaitingFirstLayout) { 383 mAwaitingFirstLayout = false; 384 385 // If launched via dragging from the nav bar, then we should translate the whole view 386 // down offscreen 387 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); 388 if (launchState.launchedViaDragGesture) { 389 setTranslationY(getMeasuredHeight()); 390 } else { 391 setTranslationY(0f); 392 } 393 } 394 } 395 396 @Override 397 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 398 mSystemInsets.set(insets.getSystemWindowInsets()); 399 mTaskStackView.setSystemInsets(mSystemInsets); 400 requestLayout(); 401 return insets; 402 } 403 404 @Override 405 public boolean onInterceptTouchEvent(MotionEvent ev) { 406 return mTouchHandler.onInterceptTouchEvent(ev); 407 } 408 409 @Override 410 public boolean onTouchEvent(MotionEvent ev) { 411 return mTouchHandler.onTouchEvent(ev); 412 } 413 414 @Override 415 public void onDrawForeground(Canvas canvas) { 416 super.onDrawForeground(canvas); 417 418 ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); 419 for (int i = visDockStates.size() - 1; i >= 0; i--) { 420 visDockStates.get(i).viewState.draw(canvas); 421 } 422 } 423 424 @Override 425 protected boolean verifyDrawable(Drawable who) { 426 ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); 427 for (int i = visDockStates.size() - 1; i >= 0; i--) { 428 Drawable d = visDockStates.get(i).viewState.dockAreaOverlay; 429 if (d == who) { 430 return true; 431 } 432 } 433 return super.verifyDrawable(who); 434 } 435 436 /**** EventBus Events ****/ 437 438 public final void onBusEvent(LaunchTaskEvent event) { 439 mLastTaskLaunchedWasFreeform = event.task.isFreeformTask(); 440 mTransitionHelper.launchTaskFromRecents(mStack, event.task, mTaskStackView, event.taskView, 441 event.screenPinningRequested, event.targetTaskBounds, event.targetTaskStack); 442 } 443 444 public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) { 445 int taskViewExitToHomeDuration = TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION; 446 if (RecentsDebugFlags.Static.EnableStackActionButton) { 447 // Hide the stack action button 448 hideStackActionButton(taskViewExitToHomeDuration, false /* translate */); 449 } 450 animateBackgroundScrim(0f, taskViewExitToHomeDuration); 451 } 452 453 public final void onBusEvent(DragStartEvent event) { 454 updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(), 455 true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, 456 TaskStack.DockState.NONE.viewState.hintTextAlpha, 457 true /* animateAlpha */, false /* animateBounds */); 458 459 // Temporarily hide the stack action button without changing visibility 460 if (mStackActionButton != null) { 461 mStackActionButton.animate() 462 .alpha(0f) 463 .setDuration(HIDE_STACK_ACTION_BUTTON_DURATION) 464 .setInterpolator(Interpolators.ALPHA_OUT) 465 .start(); 466 } 467 } 468 469 public final void onBusEvent(DragDropTargetChangedEvent event) { 470 if (event.dropTarget == null || !(event.dropTarget instanceof TaskStack.DockState)) { 471 updateVisibleDockRegions(mTouchHandler.getDockStatesForCurrentOrientation(), 472 true /* isDefaultDockState */, TaskStack.DockState.NONE.viewState.dockAreaAlpha, 473 TaskStack.DockState.NONE.viewState.hintTextAlpha, 474 true /* animateAlpha */, true /* animateBounds */); 475 } else { 476 final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; 477 updateVisibleDockRegions(new TaskStack.DockState[] {dockState}, 478 false /* isDefaultDockState */, -1, -1, true /* animateAlpha */, 479 true /* animateBounds */); 480 } 481 if (mStackActionButton != null) { 482 event.addPostAnimationCallback(new Runnable() { 483 @Override 484 public void run() { 485 // Move the clear all button to its new position 486 Rect buttonBounds = getStackActionButtonBoundsFromStackLayout(); 487 mStackActionButton.setLeftTopRightBottom(buttonBounds.left, buttonBounds.top, 488 buttonBounds.right, buttonBounds.bottom); 489 } 490 }); 491 } 492 } 493 494 public final void onBusEvent(final DragEndEvent event) { 495 // Handle the case where we drop onto a dock region 496 if (event.dropTarget instanceof TaskStack.DockState) { 497 final TaskStack.DockState dockState = (TaskStack.DockState) event.dropTarget; 498 499 // Hide the dock region 500 updateVisibleDockRegions(null, false /* isDefaultDockState */, -1, -1, 501 false /* animateAlpha */, false /* animateBounds */); 502 503 // We translated the view but we need to animate it back from the current layout-space 504 // rect to its final layout-space rect 505 Utilities.setViewFrameFromTranslation(event.taskView); 506 507 // Dock the task and launch it 508 SystemServicesProxy ssp = Recents.getSystemServices(); 509 if (ssp.startTaskInDockedMode(event.task.key.id, dockState.createMode)) { 510 final OnAnimationStartedListener startedListener = 511 new OnAnimationStartedListener() { 512 @Override 513 public void onAnimationStarted() { 514 EventBus.getDefault().send(new DockedFirstAnimationFrameEvent()); 515 // Remove the task and don't bother relaying out, as all the tasks will be 516 // relaid out when the stack changes on the multiwindow change event 517 mTaskStackView.getStack().removeTask(event.task, null, 518 true /* fromDockGesture */); 519 } 520 }; 521 522 final Rect taskRect = getTaskRect(event.taskView); 523 IAppTransitionAnimationSpecsFuture future = 524 mTransitionHelper.getAppTransitionFuture( 525 new AnimationSpecComposer() { 526 @Override 527 public List<AppTransitionAnimationSpec> composeSpecs() { 528 return mTransitionHelper.composeDockAnimationSpec( 529 event.taskView, taskRect); 530 } 531 }); 532 ssp.overridePendingAppTransitionMultiThumbFuture(future, 533 mTransitionHelper.wrapStartedListener(startedListener), 534 true /* scaleUp */); 535 536 MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_DRAG_DROP, 537 event.task.getTopComponent().flattenToShortString()); 538 } else { 539 EventBus.getDefault().send(new DragEndCancelledEvent(mStack, event.task, 540 event.taskView)); 541 } 542 } else { 543 // Animate the overlay alpha back to 0 544 updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1, 545 true /* animateAlpha */, false /* animateBounds */); 546 } 547 548 // Show the stack action button again without changing visibility 549 if (mStackActionButton != null) { 550 mStackActionButton.animate() 551 .alpha(1f) 552 .setDuration(SHOW_STACK_ACTION_BUTTON_DURATION) 553 .setInterpolator(Interpolators.ALPHA_IN) 554 .start(); 555 } 556 } 557 558 public final void onBusEvent(final DragEndCancelledEvent event) { 559 // Animate the overlay alpha back to 0 560 updateVisibleDockRegions(null, true /* isDefaultDockState */, -1, -1, 561 true /* animateAlpha */, false /* animateBounds */); 562 } 563 564 private Rect getTaskRect(TaskView taskView) { 565 int[] location = taskView.getLocationOnScreen(); 566 int viewX = location[0]; 567 int viewY = location[1]; 568 return new Rect(viewX, viewY, 569 (int) (viewX + taskView.getWidth() * taskView.getScaleX()), 570 (int) (viewY + taskView.getHeight() * taskView.getScaleY())); 571 } 572 573 public final void onBusEvent(DraggingInRecentsEvent event) { 574 if (mTaskStackView.getTaskViews().size() > 0) { 575 setTranslationY(event.distanceFromTop - mTaskStackView.getTaskViews().get(0).getY()); 576 } 577 } 578 579 public final void onBusEvent(DraggingInRecentsEndedEvent event) { 580 ViewPropertyAnimator animator = animate(); 581 if (event.velocity > mFlingAnimationUtils.getMinVelocityPxPerSecond()) { 582 animator.translationY(getHeight()); 583 animator.withEndAction(new Runnable() { 584 @Override 585 public void run() { 586 WindowManagerProxy.getInstance().maximizeDockedStack(); 587 } 588 }); 589 mFlingAnimationUtils.apply(animator, getTranslationY(), getHeight(), event.velocity); 590 } else { 591 animator.translationY(0f); 592 animator.setListener(null); 593 mFlingAnimationUtils.apply(animator, getTranslationY(), 0, event.velocity); 594 } 595 animator.start(); 596 } 597 598 public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) { 599 RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState(); 600 if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp 601 && mStack.getTaskCount() > 0) { 602 animateBackgroundScrim(1f, 603 TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION); 604 } 605 } 606 607 public final void onBusEvent(AllTaskViewsDismissedEvent event) { 608 hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */); 609 } 610 611 public final void onBusEvent(DismissAllTaskViewsEvent event) { 612 SystemServicesProxy ssp = Recents.getSystemServices(); 613 if (!ssp.hasDockedTask()) { 614 // Animate the background away only if we are dismissing Recents to home 615 animateBackgroundScrim(0f, DEFAULT_UPDATE_SCRIM_DURATION); 616 } 617 } 618 619 public final void onBusEvent(ShowStackActionButtonEvent event) { 620 if (!RecentsDebugFlags.Static.EnableStackActionButton) { 621 return; 622 } 623 624 showStackActionButton(SHOW_STACK_ACTION_BUTTON_DURATION, event.translate); 625 } 626 627 public final void onBusEvent(HideStackActionButtonEvent event) { 628 if (!RecentsDebugFlags.Static.EnableStackActionButton) { 629 return; 630 } 631 632 hideStackActionButton(HIDE_STACK_ACTION_BUTTON_DURATION, true /* translate */); 633 } 634 635 public final void onBusEvent(MultiWindowStateChangedEvent event) { 636 updateStack(event.stack, false /* setStackViewTasks */); 637 } 638 639 /** 640 * Shows the stack action button. 641 */ 642 private void showStackActionButton(final int duration, final boolean translate) { 643 if (!RecentsDebugFlags.Static.EnableStackActionButton) { 644 return; 645 } 646 647 final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(); 648 if (mStackActionButton.getVisibility() == View.INVISIBLE) { 649 mStackActionButton.setVisibility(View.VISIBLE); 650 mStackActionButton.setAlpha(0f); 651 if (translate) { 652 mStackActionButton.setTranslationY(-mStackActionButton.getMeasuredHeight() * 0.25f); 653 } else { 654 mStackActionButton.setTranslationY(0f); 655 } 656 postAnimationTrigger.addLastDecrementRunnable(new Runnable() { 657 @Override 658 public void run() { 659 if (translate) { 660 mStackActionButton.animate() 661 .translationY(0f); 662 } 663 mStackActionButton.animate() 664 .alpha(1f) 665 .setDuration(duration) 666 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) 667 .start(); 668 } 669 }); 670 } 671 postAnimationTrigger.flushLastDecrementRunnables(); 672 } 673 674 /** 675 * Hides the stack action button. 676 */ 677 private void hideStackActionButton(int duration, boolean translate) { 678 if (!RecentsDebugFlags.Static.EnableStackActionButton) { 679 return; 680 } 681 682 final ReferenceCountedTrigger postAnimationTrigger = new ReferenceCountedTrigger(); 683 hideStackActionButton(duration, translate, postAnimationTrigger); 684 postAnimationTrigger.flushLastDecrementRunnables(); 685 } 686 687 /** 688 * Hides the stack action button. 689 */ 690 private void hideStackActionButton(int duration, boolean translate, 691 final ReferenceCountedTrigger postAnimationTrigger) { 692 if (!RecentsDebugFlags.Static.EnableStackActionButton) { 693 return; 694 } 695 696 if (mStackActionButton.getVisibility() == View.VISIBLE) { 697 if (translate) { 698 mStackActionButton.animate() 699 .translationY(-mStackActionButton.getMeasuredHeight() * 0.25f); 700 } 701 mStackActionButton.animate() 702 .alpha(0f) 703 .setDuration(duration) 704 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) 705 .withEndAction(new Runnable() { 706 @Override 707 public void run() { 708 mStackActionButton.setVisibility(View.INVISIBLE); 709 postAnimationTrigger.decrement(); 710 } 711 }) 712 .start(); 713 postAnimationTrigger.increment(); 714 } 715 } 716 717 /** 718 * Updates the dock region to match the specified dock state. 719 */ 720 private void updateVisibleDockRegions(TaskStack.DockState[] newDockStates, 721 boolean isDefaultDockState, int overrideAreaAlpha, int overrideHintAlpha, 722 boolean animateAlpha, boolean animateBounds) { 723 ArraySet<TaskStack.DockState> newDockStatesSet = Utilities.arrayToSet(newDockStates, 724 new ArraySet<TaskStack.DockState>()); 725 ArrayList<TaskStack.DockState> visDockStates = mTouchHandler.getVisibleDockStates(); 726 for (int i = visDockStates.size() - 1; i >= 0; i--) { 727 TaskStack.DockState dockState = visDockStates.get(i); 728 TaskStack.DockState.ViewState viewState = dockState.viewState; 729 if (newDockStates == null || !newDockStatesSet.contains(dockState)) { 730 // This is no longer visible, so hide it 731 viewState.startAnimation(null, 0, 0, TaskStackView.SLOW_SYNC_STACK_DURATION, 732 Interpolators.FAST_OUT_SLOW_IN, animateAlpha, animateBounds); 733 } else { 734 // This state is now visible, update the bounds and show it 735 int areaAlpha = overrideAreaAlpha != -1 736 ? overrideAreaAlpha 737 : viewState.dockAreaAlpha; 738 int hintAlpha = overrideHintAlpha != -1 739 ? overrideHintAlpha 740 : viewState.hintTextAlpha; 741 Rect bounds = isDefaultDockState 742 ? dockState.getPreDockedBounds(getMeasuredWidth(), getMeasuredHeight()) 743 : dockState.getDockedBounds(getMeasuredWidth(), getMeasuredHeight(), 744 mDividerSize, mSystemInsets, getResources()); 745 if (viewState.dockAreaOverlay.getCallback() != this) { 746 viewState.dockAreaOverlay.setCallback(this); 747 viewState.dockAreaOverlay.setBounds(bounds); 748 } 749 viewState.startAnimation(bounds, areaAlpha, hintAlpha, 750 TaskStackView.SLOW_SYNC_STACK_DURATION, Interpolators.FAST_OUT_SLOW_IN, 751 animateAlpha, animateBounds); 752 } 753 } 754 } 755 756 /** 757 * Animates the background scrim to the given {@param alpha}. 758 */ 759 private void animateBackgroundScrim(float alpha, int duration) { 760 Utilities.cancelAnimationWithoutCallbacks(mBackgroundScrimAnimator); 761 // Calculate the absolute alpha to animate from 762 int fromAlpha = (int) ((mBackgroundScrim.getAlpha() / (DEFAULT_SCRIM_ALPHA * 255)) * 255); 763 int toAlpha = (int) (alpha * 255); 764 mBackgroundScrimAnimator = ObjectAnimator.ofInt(mBackgroundScrim, Utilities.DRAWABLE_ALPHA, 765 fromAlpha, toAlpha); 766 mBackgroundScrimAnimator.setDuration(duration); 767 mBackgroundScrimAnimator.setInterpolator(toAlpha > fromAlpha 768 ? Interpolators.ALPHA_IN 769 : Interpolators.ALPHA_OUT); 770 mBackgroundScrimAnimator.start(); 771 } 772 773 /** 774 * @return the bounds of the stack action button. 775 */ 776 private Rect getStackActionButtonBoundsFromStackLayout() { 777 Rect actionButtonRect = new Rect(mTaskStackView.mLayoutAlgorithm.mStackActionButtonRect); 778 int left = isLayoutRtl() 779 ? actionButtonRect.left - mStackActionButton.getPaddingLeft() 780 : actionButtonRect.right + mStackActionButton.getPaddingRight() 781 - mStackActionButton.getMeasuredWidth(); 782 int top = actionButtonRect.top + 783 (actionButtonRect.height() - mStackActionButton.getMeasuredHeight()) / 2; 784 actionButtonRect.set(left, top, left + mStackActionButton.getMeasuredWidth(), 785 top + mStackActionButton.getMeasuredHeight()); 786 return actionButtonRect; 787 } 788 789 public void dump(String prefix, PrintWriter writer) { 790 String innerPrefix = prefix + " "; 791 String id = Integer.toHexString(System.identityHashCode(this)); 792 793 writer.print(prefix); writer.print(TAG); 794 writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N"); 795 writer.print(" insets="); writer.print(Utilities.dumpRect(mSystemInsets)); 796 writer.print(" [0x"); writer.print(id); writer.print("]"); 797 writer.println(); 798 799 if (mStack != null) { 800 mStack.dump(innerPrefix, writer); 801 } 802 if (mTaskStackView != null) { 803 mTaskStackView.dump(innerPrefix, writer); 804 } 805 } 806} 807