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