1 /* 2 * Copyright (C) 2015 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 17 package com.android.systemui.stackdivider; 18 19 import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW; 20 import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW; 21 22 import android.animation.Animator; 23 import android.animation.AnimatorListenerAdapter; 24 import android.animation.ValueAnimator; 25 import android.annotation.Nullable; 26 import android.app.ActivityManager.StackId; 27 import android.content.Context; 28 import android.content.res.Configuration; 29 import android.graphics.Rect; 30 import android.graphics.Region.Op; 31 import android.hardware.display.DisplayManager; 32 import android.os.Bundle; 33 import android.os.Handler; 34 import android.util.AttributeSet; 35 import android.view.Display; 36 import android.view.DisplayInfo; 37 import android.view.GestureDetector; 38 import android.view.GestureDetector.SimpleOnGestureListener; 39 import android.view.MotionEvent; 40 import android.view.PointerIcon; 41 import android.view.VelocityTracker; 42 import android.view.View; 43 import android.view.View.OnTouchListener; 44 import android.view.ViewConfiguration; 45 import android.view.ViewTreeObserver.InternalInsetsInfo; 46 import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; 47 import android.view.WindowInsets; 48 import android.view.WindowManager; 49 import android.view.accessibility.AccessibilityNodeInfo; 50 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; 51 import android.view.animation.Interpolator; 52 import android.view.animation.PathInterpolator; 53 import android.widget.FrameLayout; 54 55 import com.android.internal.logging.MetricsLogger; 56 import com.android.internal.logging.MetricsProto.MetricsEvent; 57 import com.android.internal.policy.DividerSnapAlgorithm; 58 import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; 59 import com.android.internal.policy.DockedDividerUtils; 60 import com.android.systemui.Interpolators; 61 import com.android.systemui.R; 62 import com.android.systemui.recents.Recents; 63 import com.android.systemui.recents.events.EventBus; 64 import com.android.systemui.recents.events.activity.DockedTopTaskEvent; 65 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; 66 import com.android.systemui.recents.events.activity.UndockingTaskEvent; 67 import com.android.systemui.recents.events.ui.RecentsDrawnEvent; 68 import com.android.systemui.recents.events.ui.RecentsGrowingEvent; 69 import com.android.systemui.recents.misc.SystemServicesProxy; 70 import com.android.systemui.stackdivider.events.StartedDragingEvent; 71 import com.android.systemui.stackdivider.events.StoppedDragingEvent; 72 import com.android.systemui.statusbar.FlingAnimationUtils; 73 import com.android.systemui.statusbar.phone.NavigationBarGestureHelper; 74 75 /** 76 * Docked stack divider. 77 */ 78 public class DividerView extends FrameLayout implements OnTouchListener, 79 OnComputeInternalInsetsListener { 80 81 static final long TOUCH_ANIMATION_DURATION = 150; 82 static final long TOUCH_RELEASE_ANIMATION_DURATION = 200; 83 84 public static final int INVALID_RECENTS_GROW_TARGET = -1; 85 86 private static final int LOG_VALUE_RESIZE_50_50 = 0; 87 private static final int LOG_VALUE_RESIZE_DOCKED_SMALLER = 1; 88 private static final int LOG_VALUE_RESIZE_DOCKED_LARGER = 2; 89 90 private static final int LOG_VALUE_UNDOCK_MAX_DOCKED = 0; 91 private static final int LOG_VALUE_UNDOCK_MAX_OTHER = 1; 92 93 private static final int TASK_POSITION_SAME = Integer.MAX_VALUE; 94 private static final boolean SWAPPING_ENABLED = false; 95 96 /** 97 * How much the background gets scaled when we are in the minimized dock state. 98 */ 99 private static final float MINIMIZE_DOCK_SCALE = 0f; 100 private static final float ADJUSTED_FOR_IME_SCALE = 0.5f; 101 102 private static final PathInterpolator SLOWDOWN_INTERPOLATOR = 103 new PathInterpolator(0.5f, 1f, 0.5f, 1f); 104 private static final PathInterpolator DIM_INTERPOLATOR = 105 new PathInterpolator(.23f, .87f, .52f, -0.11f); 106 private static final Interpolator IME_ADJUST_INTERPOLATOR = 107 new PathInterpolator(0.2f, 0f, 0.1f, 1f); 108 109 private DividerHandleView mHandle; 110 private View mBackground; 111 private MinimizedDockShadow mMinimizedShadow; 112 private int mStartX; 113 private int mStartY; 114 private int mStartPosition; 115 private int mDockSide; 116 private final int[] mTempInt2 = new int[2]; 117 private boolean mMoving; 118 private int mTouchSlop; 119 private boolean mBackgroundLifted; 120 121 private int mDividerInsets; 122 private int mDisplayWidth; 123 private int mDisplayHeight; 124 private int mDividerWindowWidth; 125 private int mDividerSize; 126 private int mTouchElevation; 127 private int mLongPressEntraceAnimDuration; 128 129 private final Rect mDockedRect = new Rect(); 130 private final Rect mDockedTaskRect = new Rect(); 131 private final Rect mOtherTaskRect = new Rect(); 132 private final Rect mOtherRect = new Rect(); 133 private final Rect mDockedInsetRect = new Rect(); 134 private final Rect mOtherInsetRect = new Rect(); 135 private final Rect mLastResizeRect = new Rect(); 136 private final Rect mDisplayRect = new Rect(); 137 private final WindowManagerProxy mWindowManagerProxy = WindowManagerProxy.getInstance(); 138 private DividerWindowManager mWindowManager; 139 private VelocityTracker mVelocityTracker; 140 private FlingAnimationUtils mFlingAnimationUtils; 141 private DividerSnapAlgorithm mSnapAlgorithm; 142 private final Rect mStableInsets = new Rect(); 143 144 private boolean mGrowRecents; 145 private ValueAnimator mCurrentAnimator; 146 private boolean mEntranceAnimationRunning; 147 private boolean mExitAnimationRunning; 148 private int mExitStartPosition; 149 private GestureDetector mGestureDetector; 150 private boolean mDockedStackMinimized; 151 private boolean mAdjustedForIme; 152 private DividerState mState; 153 private final Handler mHandler = new Handler(); 154 155 private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() { 156 @Override 157 public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { 158 super.onInitializeAccessibilityNodeInfo(host, info); 159 if (isHorizontalDivision()) { 160 info.addAction(new AccessibilityAction(R.id.action_move_tl_full, 161 mContext.getString(R.string.accessibility_action_divider_top_full))); 162 if (mSnapAlgorithm.isFirstSplitTargetAvailable()) { 163 info.addAction(new AccessibilityAction(R.id.action_move_tl_70, 164 mContext.getString(R.string.accessibility_action_divider_top_70))); 165 } 166 info.addAction(new AccessibilityAction(R.id.action_move_tl_50, 167 mContext.getString(R.string.accessibility_action_divider_top_50))); 168 if (mSnapAlgorithm.isLastSplitTargetAvailable()) { 169 info.addAction(new AccessibilityAction(R.id.action_move_tl_30, 170 mContext.getString(R.string.accessibility_action_divider_top_30))); 171 } 172 info.addAction(new AccessibilityAction(R.id.action_move_rb_full, 173 mContext.getString(R.string.accessibility_action_divider_bottom_full))); 174 } else { 175 info.addAction(new AccessibilityAction(R.id.action_move_tl_full, 176 mContext.getString(R.string.accessibility_action_divider_left_full))); 177 if (mSnapAlgorithm.isFirstSplitTargetAvailable()) { 178 info.addAction(new AccessibilityAction(R.id.action_move_tl_70, 179 mContext.getString(R.string.accessibility_action_divider_left_70))); 180 } 181 info.addAction(new AccessibilityAction(R.id.action_move_tl_50, 182 mContext.getString(R.string.accessibility_action_divider_left_50))); 183 if (mSnapAlgorithm.isLastSplitTargetAvailable()) { 184 info.addAction(new AccessibilityAction(R.id.action_move_tl_30, 185 mContext.getString(R.string.accessibility_action_divider_left_30))); 186 } 187 info.addAction(new AccessibilityAction(R.id.action_move_rb_full, 188 mContext.getString(R.string.accessibility_action_divider_right_full))); 189 } 190 } 191 192 @Override 193 public boolean performAccessibilityAction(View host, int action, Bundle args) { 194 int currentPosition = getCurrentPosition(); 195 SnapTarget nextTarget = null; 196 switch (action) { 197 case R.id.action_move_tl_full: 198 nextTarget = mSnapAlgorithm.getDismissEndTarget(); 199 break; 200 case R.id.action_move_tl_70: 201 nextTarget = mSnapAlgorithm.getLastSplitTarget(); 202 break; 203 case R.id.action_move_tl_50: 204 nextTarget = mSnapAlgorithm.getMiddleTarget(); 205 break; 206 case R.id.action_move_tl_30: 207 nextTarget = mSnapAlgorithm.getFirstSplitTarget(); 208 break; 209 case R.id.action_move_rb_full: 210 nextTarget = mSnapAlgorithm.getDismissStartTarget(); 211 break; 212 } 213 if (nextTarget != null) { 214 startDragging(true /* animate */, false /* touching */); 215 stopDragging(currentPosition, nextTarget, 250, Interpolators.FAST_OUT_SLOW_IN); 216 return true; 217 } 218 return super.performAccessibilityAction(host, action, args); 219 } 220 }; 221 222 private final Runnable mResetBackgroundRunnable = new Runnable() { 223 @Override 224 public void run() { 225 resetBackground(); 226 } 227 }; 228 DividerView(Context context)229 public DividerView(Context context) { 230 super(context); 231 } 232 DividerView(Context context, @Nullable AttributeSet attrs)233 public DividerView(Context context, @Nullable AttributeSet attrs) { 234 super(context, attrs); 235 } 236 DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr)237 public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 238 super(context, attrs, defStyleAttr); 239 } 240 DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)241 public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, 242 int defStyleRes) { 243 super(context, attrs, defStyleAttr, defStyleRes); 244 } 245 246 @Override onFinishInflate()247 protected void onFinishInflate() { 248 super.onFinishInflate(); 249 mHandle = (DividerHandleView) findViewById(R.id.docked_divider_handle); 250 mBackground = findViewById(R.id.docked_divider_background); 251 mMinimizedShadow = (MinimizedDockShadow) findViewById(R.id.minimized_dock_shadow); 252 mHandle.setOnTouchListener(this); 253 mDividerWindowWidth = getResources().getDimensionPixelSize( 254 com.android.internal.R.dimen.docked_stack_divider_thickness); 255 mDividerInsets = getResources().getDimensionPixelSize( 256 com.android.internal.R.dimen.docked_stack_divider_insets); 257 mDividerSize = mDividerWindowWidth - 2 * mDividerInsets; 258 mTouchElevation = getResources().getDimensionPixelSize( 259 R.dimen.docked_stack_divider_lift_elevation); 260 mLongPressEntraceAnimDuration = getResources().getInteger( 261 R.integer.long_press_dock_anim_duration); 262 mGrowRecents = getResources().getBoolean(R.bool.recents_grow_in_multiwindow); 263 mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); 264 mFlingAnimationUtils = new FlingAnimationUtils(getContext(), 0.3f); 265 updateDisplayInfo(); 266 boolean landscape = getResources().getConfiguration().orientation 267 == Configuration.ORIENTATION_LANDSCAPE; 268 mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(), 269 landscape ? TYPE_HORIZONTAL_DOUBLE_ARROW : TYPE_VERTICAL_DOUBLE_ARROW)); 270 getViewTreeObserver().addOnComputeInternalInsetsListener(this); 271 mHandle.setAccessibilityDelegate(mHandleDelegate); 272 mGestureDetector = new GestureDetector(mContext, new SimpleOnGestureListener() { 273 @Override 274 public boolean onSingleTapUp(MotionEvent e) { 275 if (SWAPPING_ENABLED) { 276 updateDockSide(); 277 SystemServicesProxy ssp = Recents.getSystemServices(); 278 if (mDockSide != WindowManager.DOCKED_INVALID 279 && !ssp.isRecentsActivityVisible()) { 280 mWindowManagerProxy.swapTasks(); 281 return true; 282 } 283 } 284 return false; 285 } 286 }); 287 } 288 289 @Override onAttachedToWindow()290 protected void onAttachedToWindow() { 291 super.onAttachedToWindow(); 292 EventBus.getDefault().register(this); 293 } 294 295 @Override onDetachedFromWindow()296 protected void onDetachedFromWindow() { 297 super.onDetachedFromWindow(); 298 EventBus.getDefault().unregister(this); 299 } 300 301 @Override onApplyWindowInsets(WindowInsets insets)302 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 303 if (mStableInsets.left != insets.getStableInsetLeft() 304 || mStableInsets.top != insets.getStableInsetTop() 305 || mStableInsets.right != insets.getStableInsetRight() 306 || mStableInsets.bottom != insets.getStableInsetBottom()) { 307 mStableInsets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(), 308 insets.getStableInsetRight(), insets.getStableInsetBottom()); 309 if (mSnapAlgorithm != null) { 310 mSnapAlgorithm = null; 311 initializeSnapAlgorithm(); 312 } 313 } 314 return super.onApplyWindowInsets(insets); 315 } 316 317 @Override onLayout(boolean changed, int left, int top, int right, int bottom)318 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 319 super.onLayout(changed, left, top, right, bottom); 320 int minimizeLeft = 0; 321 int minimizeTop = 0; 322 if (mDockSide == WindowManager.DOCKED_TOP) { 323 minimizeTop = mBackground.getTop(); 324 } else if (mDockSide == WindowManager.DOCKED_LEFT) { 325 minimizeLeft = mBackground.getLeft(); 326 } else if (mDockSide == WindowManager.DOCKED_RIGHT) { 327 minimizeLeft = mBackground.getRight() - mMinimizedShadow.getWidth(); 328 } 329 mMinimizedShadow.layout(minimizeLeft, minimizeTop, 330 minimizeLeft + mMinimizedShadow.getMeasuredWidth(), 331 minimizeTop + mMinimizedShadow.getMeasuredHeight()); 332 if (changed) { 333 mWindowManagerProxy.setTouchRegion(new Rect(mHandle.getLeft(), mHandle.getTop(), 334 mHandle.getRight(), mHandle.getBottom())); 335 } 336 } 337 injectDependencies(DividerWindowManager windowManager, DividerState dividerState)338 public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState) { 339 mWindowManager = windowManager; 340 mState = dividerState; 341 } 342 getWindowManagerProxy()343 public WindowManagerProxy getWindowManagerProxy() { 344 return mWindowManagerProxy; 345 } 346 startDragging(boolean animate, boolean touching)347 public boolean startDragging(boolean animate, boolean touching) { 348 cancelFlingAnimation(); 349 if (touching) { 350 mHandle.setTouching(true, animate); 351 } 352 mDockSide = mWindowManagerProxy.getDockSide(); 353 initializeSnapAlgorithm(); 354 mWindowManagerProxy.setResizing(true); 355 if (touching) { 356 mWindowManager.setSlippery(false); 357 liftBackground(); 358 } 359 EventBus.getDefault().send(new StartedDragingEvent()); 360 return mDockSide != WindowManager.DOCKED_INVALID; 361 } 362 stopDragging(int position, float velocity, boolean avoidDismissStart, boolean logMetrics)363 public void stopDragging(int position, float velocity, boolean avoidDismissStart, 364 boolean logMetrics) { 365 mHandle.setTouching(false, true /* animate */); 366 fling(position, velocity, avoidDismissStart, logMetrics); 367 mWindowManager.setSlippery(true); 368 releaseBackground(); 369 } 370 stopDragging(int position, SnapTarget target, long duration, Interpolator interpolator)371 public void stopDragging(int position, SnapTarget target, long duration, 372 Interpolator interpolator) { 373 stopDragging(position, target, duration, 0 /* startDelay*/, 0 /* endDelay */, interpolator); 374 } 375 stopDragging(int position, SnapTarget target, long duration, Interpolator interpolator, long endDelay)376 public void stopDragging(int position, SnapTarget target, long duration, 377 Interpolator interpolator, long endDelay) { 378 stopDragging(position, target, duration, 0 /* startDelay*/, endDelay, interpolator); 379 } 380 stopDragging(int position, SnapTarget target, long duration, long startDelay, long endDelay, Interpolator interpolator)381 public void stopDragging(int position, SnapTarget target, long duration, long startDelay, 382 long endDelay, Interpolator interpolator) { 383 mHandle.setTouching(false, true /* animate */); 384 flingTo(position, target, duration, startDelay, endDelay, interpolator); 385 mWindowManager.setSlippery(true); 386 releaseBackground(); 387 } 388 stopDragging()389 private void stopDragging() { 390 mHandle.setTouching(false, true /* animate */); 391 mWindowManager.setSlippery(true); 392 releaseBackground(); 393 } 394 updateDockSide()395 private void updateDockSide() { 396 mDockSide = mWindowManagerProxy.getDockSide(); 397 mMinimizedShadow.setDockSide(mDockSide); 398 } 399 initializeSnapAlgorithm()400 private void initializeSnapAlgorithm() { 401 if (mSnapAlgorithm == null) { 402 mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth, 403 mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets); 404 } 405 } 406 getSnapAlgorithm()407 public DividerSnapAlgorithm getSnapAlgorithm() { 408 initializeSnapAlgorithm(); 409 return mSnapAlgorithm; 410 } 411 getCurrentPosition()412 public int getCurrentPosition() { 413 getLocationOnScreen(mTempInt2); 414 if (isHorizontalDivision()) { 415 return mTempInt2[1] + mDividerInsets; 416 } else { 417 return mTempInt2[0] + mDividerInsets; 418 } 419 } 420 421 @Override onTouch(View v, MotionEvent event)422 public boolean onTouch(View v, MotionEvent event) { 423 convertToScreenCoordinates(event); 424 mGestureDetector.onTouchEvent(event); 425 final int action = event.getAction() & MotionEvent.ACTION_MASK; 426 switch (action) { 427 case MotionEvent.ACTION_DOWN: 428 mVelocityTracker = VelocityTracker.obtain(); 429 mVelocityTracker.addMovement(event); 430 mStartX = (int) event.getX(); 431 mStartY = (int) event.getY(); 432 boolean result = startDragging(true /* animate */, true /* touching */); 433 if (!result) { 434 435 // Weren't able to start dragging successfully, so cancel it again. 436 stopDragging(); 437 } 438 mStartPosition = getCurrentPosition(); 439 mMoving = false; 440 return result; 441 case MotionEvent.ACTION_MOVE: 442 mVelocityTracker.addMovement(event); 443 int x = (int) event.getX(); 444 int y = (int) event.getY(); 445 boolean exceededTouchSlop = 446 isHorizontalDivision() && Math.abs(y - mStartY) > mTouchSlop 447 || (!isHorizontalDivision() && Math.abs(x - mStartX) > mTouchSlop); 448 if (!mMoving && exceededTouchSlop) { 449 mStartX = x; 450 mStartY = y; 451 mMoving = true; 452 } 453 if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) { 454 SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget( 455 mStartPosition, 0 /* velocity */, false /* hardDismiss */); 456 resizeStack(calculatePosition(x, y), mStartPosition, snapTarget); 457 } 458 break; 459 case MotionEvent.ACTION_UP: 460 case MotionEvent.ACTION_CANCEL: 461 mVelocityTracker.addMovement(event); 462 463 x = (int) event.getRawX(); 464 y = (int) event.getRawY(); 465 466 mVelocityTracker.computeCurrentVelocity(1000); 467 int position = calculatePosition(x, y); 468 stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity() 469 : mVelocityTracker.getXVelocity(), false /* avoidDismissStart */, 470 true /* log */); 471 mMoving = false; 472 break; 473 } 474 return true; 475 } 476 logResizeEvent(SnapTarget snapTarget)477 private void logResizeEvent(SnapTarget snapTarget) { 478 if (snapTarget == mSnapAlgorithm.getDismissStartTarget()) { 479 MetricsLogger.action( 480 mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideTopLeft(mDockSide) 481 ? LOG_VALUE_UNDOCK_MAX_OTHER 482 : LOG_VALUE_UNDOCK_MAX_DOCKED); 483 } else if (snapTarget == mSnapAlgorithm.getDismissEndTarget()) { 484 MetricsLogger.action( 485 mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideBottomRight(mDockSide) 486 ? LOG_VALUE_UNDOCK_MAX_OTHER 487 : LOG_VALUE_UNDOCK_MAX_DOCKED); 488 } else if (snapTarget == mSnapAlgorithm.getMiddleTarget()) { 489 MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE, 490 LOG_VALUE_RESIZE_50_50); 491 } else if (snapTarget == mSnapAlgorithm.getFirstSplitTarget()) { 492 MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE, 493 dockSideTopLeft(mDockSide) 494 ? LOG_VALUE_RESIZE_DOCKED_SMALLER 495 : LOG_VALUE_RESIZE_DOCKED_LARGER); 496 } else if (snapTarget == mSnapAlgorithm.getLastSplitTarget()) { 497 MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE, 498 dockSideTopLeft(mDockSide) 499 ? LOG_VALUE_RESIZE_DOCKED_LARGER 500 : LOG_VALUE_RESIZE_DOCKED_SMALLER); 501 } 502 } 503 convertToScreenCoordinates(MotionEvent event)504 private void convertToScreenCoordinates(MotionEvent event) { 505 event.setLocation(event.getRawX(), event.getRawY()); 506 } 507 fling(int position, float velocity, boolean avoidDismissStart, boolean logMetrics)508 private void fling(int position, float velocity, boolean avoidDismissStart, 509 boolean logMetrics) { 510 SnapTarget snapTarget = mSnapAlgorithm.calculateSnapTarget(position, velocity); 511 if (avoidDismissStart && snapTarget == mSnapAlgorithm.getDismissStartTarget()) { 512 snapTarget = mSnapAlgorithm.getFirstSplitTarget(); 513 } 514 if (logMetrics) { 515 logResizeEvent(snapTarget); 516 } 517 ValueAnimator anim = getFlingAnimator(position, snapTarget, 0 /* endDelay */); 518 mFlingAnimationUtils.apply(anim, position, snapTarget.position, velocity); 519 anim.start(); 520 } 521 flingTo(int position, SnapTarget target, long duration, long startDelay, long endDelay, Interpolator interpolator)522 private void flingTo(int position, SnapTarget target, long duration, long startDelay, 523 long endDelay, Interpolator interpolator) { 524 ValueAnimator anim = getFlingAnimator(position, target, endDelay); 525 anim.setDuration(duration); 526 anim.setStartDelay(startDelay); 527 anim.setInterpolator(interpolator); 528 anim.start(); 529 } 530 getFlingAnimator(int position, final SnapTarget snapTarget, final long endDelay)531 private ValueAnimator getFlingAnimator(int position, final SnapTarget snapTarget, 532 final long endDelay) { 533 final boolean taskPositionSameAtEnd = snapTarget.flag == SnapTarget.FLAG_NONE; 534 ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position); 535 anim.addUpdateListener(animation -> resizeStack((Integer) animation.getAnimatedValue(), 536 taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f 537 ? TASK_POSITION_SAME 538 : snapTarget.taskPosition, snapTarget)); 539 Runnable endAction = () -> { 540 commitSnapFlags(snapTarget); 541 mWindowManagerProxy.setResizing(false); 542 mDockSide = WindowManager.DOCKED_INVALID; 543 mCurrentAnimator = null; 544 mEntranceAnimationRunning = false; 545 mExitAnimationRunning = false; 546 EventBus.getDefault().send(new StoppedDragingEvent()); 547 }; 548 anim.addListener(new AnimatorListenerAdapter() { 549 550 private boolean mCancelled; 551 552 @Override 553 public void onAnimationCancel(Animator animation) { 554 mCancelled = true; 555 } 556 557 @Override 558 public void onAnimationEnd(Animator animation) { 559 if (endDelay == 0 || mCancelled) { 560 endAction.run(); 561 } else { 562 mHandler.postDelayed(endAction, endDelay); 563 } 564 } 565 }); 566 mCurrentAnimator = anim; 567 return anim; 568 } 569 cancelFlingAnimation()570 private void cancelFlingAnimation() { 571 if (mCurrentAnimator != null) { 572 mCurrentAnimator.cancel(); 573 } 574 } 575 commitSnapFlags(SnapTarget target)576 private void commitSnapFlags(SnapTarget target) { 577 if (target.flag == SnapTarget.FLAG_NONE) { 578 return; 579 } 580 boolean dismissOrMaximize; 581 if (target.flag == SnapTarget.FLAG_DISMISS_START) { 582 dismissOrMaximize = mDockSide == WindowManager.DOCKED_LEFT 583 || mDockSide == WindowManager.DOCKED_TOP; 584 } else { 585 dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT 586 || mDockSide == WindowManager.DOCKED_BOTTOM; 587 } 588 if (dismissOrMaximize) { 589 mWindowManagerProxy.dismissDockedStack(); 590 } else { 591 mWindowManagerProxy.maximizeDockedStack(); 592 } 593 mWindowManagerProxy.setResizeDimLayer(false, -1, 0f); 594 } 595 liftBackground()596 private void liftBackground() { 597 if (mBackgroundLifted) { 598 return; 599 } 600 if (isHorizontalDivision()) { 601 mBackground.animate().scaleY(1.4f); 602 } else { 603 mBackground.animate().scaleX(1.4f); 604 } 605 mBackground.animate() 606 .setInterpolator(Interpolators.TOUCH_RESPONSE) 607 .setDuration(TOUCH_ANIMATION_DURATION) 608 .translationZ(mTouchElevation) 609 .start(); 610 611 // Lift handle as well so it doesn't get behind the background, even though it doesn't 612 // cast shadow. 613 mHandle.animate() 614 .setInterpolator(Interpolators.TOUCH_RESPONSE) 615 .setDuration(TOUCH_ANIMATION_DURATION) 616 .translationZ(mTouchElevation) 617 .start(); 618 mBackgroundLifted = true; 619 } 620 releaseBackground()621 private void releaseBackground() { 622 if (!mBackgroundLifted) { 623 return; 624 } 625 mBackground.animate() 626 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) 627 .setDuration(TOUCH_RELEASE_ANIMATION_DURATION) 628 .translationZ(0) 629 .scaleX(1f) 630 .scaleY(1f) 631 .start(); 632 mHandle.animate() 633 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) 634 .setDuration(TOUCH_RELEASE_ANIMATION_DURATION) 635 .translationZ(0) 636 .start(); 637 mBackgroundLifted = false; 638 } 639 640 setMinimizedDockStack(boolean minimized)641 public void setMinimizedDockStack(boolean minimized) { 642 updateDockSide(); 643 mHandle.setAlpha(minimized ? 0f : 1f); 644 if (!minimized) { 645 resetBackground(); 646 } else if (mDockSide == WindowManager.DOCKED_TOP) { 647 mBackground.setPivotY(0); 648 mBackground.setScaleY(MINIMIZE_DOCK_SCALE); 649 } else if (mDockSide == WindowManager.DOCKED_LEFT 650 || mDockSide == WindowManager.DOCKED_RIGHT) { 651 mBackground.setPivotX(mDockSide == WindowManager.DOCKED_LEFT 652 ? 0 653 : mBackground.getWidth()); 654 mBackground.setScaleX(MINIMIZE_DOCK_SCALE); 655 } 656 mMinimizedShadow.setAlpha(minimized ? 1f : 0f); 657 mDockedStackMinimized = minimized; 658 } 659 setMinimizedDockStack(boolean minimized, long animDuration)660 public void setMinimizedDockStack(boolean minimized, long animDuration) { 661 updateDockSide(); 662 mHandle.animate() 663 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) 664 .setDuration(animDuration) 665 .alpha(minimized ? 0f : 1f) 666 .start(); 667 if (mDockSide == WindowManager.DOCKED_TOP) { 668 mBackground.setPivotY(0); 669 mBackground.animate() 670 .scaleY(minimized ? MINIMIZE_DOCK_SCALE : 1f); 671 } else if (mDockSide == WindowManager.DOCKED_LEFT 672 || mDockSide == WindowManager.DOCKED_RIGHT) { 673 mBackground.setPivotX(mDockSide == WindowManager.DOCKED_LEFT 674 ? 0 675 : mBackground.getWidth()); 676 mBackground.animate() 677 .scaleX(minimized ? MINIMIZE_DOCK_SCALE : 1f); 678 } 679 if (!minimized) { 680 mBackground.animate().withEndAction(mResetBackgroundRunnable); 681 } 682 mMinimizedShadow.animate() 683 .alpha(minimized ? 1f : 0f) 684 .setInterpolator(Interpolators.ALPHA_IN) 685 .setDuration(animDuration) 686 .start(); 687 mBackground.animate() 688 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) 689 .setDuration(animDuration) 690 .start(); 691 mDockedStackMinimized = minimized; 692 } 693 setAdjustedForIme(boolean adjustedForIme)694 public void setAdjustedForIme(boolean adjustedForIme) { 695 updateDockSide(); 696 mHandle.setAlpha(adjustedForIme ? 0f : 1f); 697 if (!adjustedForIme) { 698 resetBackground(); 699 } else if (mDockSide == WindowManager.DOCKED_TOP) { 700 mBackground.setPivotY(0); 701 mBackground.setScaleY(ADJUSTED_FOR_IME_SCALE); 702 } 703 mAdjustedForIme = adjustedForIme; 704 } 705 setAdjustedForIme(boolean adjustedForIme, long animDuration)706 public void setAdjustedForIme(boolean adjustedForIme, long animDuration) { 707 updateDockSide(); 708 mHandle.animate() 709 .setInterpolator(IME_ADJUST_INTERPOLATOR) 710 .setDuration(animDuration) 711 .alpha(adjustedForIme ? 0f : 1f) 712 .start(); 713 if (mDockSide == WindowManager.DOCKED_TOP) { 714 mBackground.setPivotY(0); 715 mBackground.animate() 716 .scaleY(adjustedForIme ? ADJUSTED_FOR_IME_SCALE : 1f); 717 } 718 if (!adjustedForIme) { 719 mBackground.animate().withEndAction(mResetBackgroundRunnable); 720 } 721 mBackground.animate() 722 .setInterpolator(IME_ADJUST_INTERPOLATOR) 723 .setDuration(animDuration) 724 .start(); 725 mAdjustedForIme = adjustedForIme; 726 } 727 resetBackground()728 private void resetBackground() { 729 mBackground.setPivotX(mBackground.getWidth() / 2); 730 mBackground.setPivotY(mBackground.getHeight() / 2); 731 mBackground.setScaleX(1f); 732 mBackground.setScaleY(1f); 733 mMinimizedShadow.setAlpha(0f); 734 } 735 736 @Override onConfigurationChanged(Configuration newConfig)737 protected void onConfigurationChanged(Configuration newConfig) { 738 super.onConfigurationChanged(newConfig); 739 updateDisplayInfo(); 740 } 741 742 notifyDockSideChanged(int newDockSide)743 public void notifyDockSideChanged(int newDockSide) { 744 mDockSide = newDockSide; 745 mMinimizedShadow.setDockSide(mDockSide); 746 requestLayout(); 747 } 748 updateDisplayInfo()749 private void updateDisplayInfo() { 750 final DisplayManager displayManager = 751 (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); 752 Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY); 753 final DisplayInfo info = new DisplayInfo(); 754 display.getDisplayInfo(info); 755 mDisplayWidth = info.logicalWidth; 756 mDisplayHeight = info.logicalHeight; 757 mSnapAlgorithm = null; 758 initializeSnapAlgorithm(); 759 } 760 calculatePosition(int touchX, int touchY)761 private int calculatePosition(int touchX, int touchY) { 762 return isHorizontalDivision() ? calculateYPosition(touchY) : calculateXPosition(touchX); 763 } 764 isHorizontalDivision()765 public boolean isHorizontalDivision() { 766 return getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; 767 } 768 calculateXPosition(int touchX)769 private int calculateXPosition(int touchX) { 770 return mStartPosition + touchX - mStartX; 771 } 772 calculateYPosition(int touchY)773 private int calculateYPosition(int touchY) { 774 return mStartPosition + touchY - mStartY; 775 } 776 alignTopLeft(Rect containingRect, Rect rect)777 private void alignTopLeft(Rect containingRect, Rect rect) { 778 int width = rect.width(); 779 int height = rect.height(); 780 rect.set(containingRect.left, containingRect.top, 781 containingRect.left + width, containingRect.top + height); 782 } 783 alignBottomRight(Rect containingRect, Rect rect)784 private void alignBottomRight(Rect containingRect, Rect rect) { 785 int width = rect.width(); 786 int height = rect.height(); 787 rect.set(containingRect.right - width, containingRect.bottom - height, 788 containingRect.right, containingRect.bottom); 789 } 790 calculateBoundsForPosition(int position, int dockSide, Rect outRect)791 public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) { 792 DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, mDisplayWidth, 793 mDisplayHeight, mDividerSize); 794 } 795 resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget)796 public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) { 797 calculateBoundsForPosition(position, mDockSide, mDockedRect); 798 799 if (mDockedRect.equals(mLastResizeRect) && !mEntranceAnimationRunning) { 800 return; 801 } 802 803 // Make sure shadows are updated 804 if (mBackground.getZ() > 0f) { 805 mBackground.invalidate(); 806 } 807 808 mLastResizeRect.set(mDockedRect); 809 if (mEntranceAnimationRunning && taskPosition != TASK_POSITION_SAME) { 810 if (mCurrentAnimator != null) { 811 calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect); 812 } else { 813 calculateBoundsForPosition(isHorizontalDivision() ? mDisplayHeight : mDisplayWidth, 814 mDockSide, mDockedTaskRect); 815 } 816 calculateBoundsForPosition(taskPosition, DockedDividerUtils.invertDockSide(mDockSide), 817 mOtherTaskRect); 818 mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null, 819 mOtherTaskRect, null); 820 } else if (mExitAnimationRunning && taskPosition != TASK_POSITION_SAME) { 821 calculateBoundsForPosition(taskPosition, 822 mDockSide, mDockedTaskRect); 823 calculateBoundsForPosition(mExitStartPosition, 824 DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect); 825 mOtherInsetRect.set(mOtherTaskRect); 826 applyExitAnimationParallax(mOtherTaskRect, position); 827 mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null, 828 mOtherTaskRect, mOtherInsetRect); 829 } else if (taskPosition != TASK_POSITION_SAME) { 830 calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide), 831 mOtherRect); 832 int dockSideInverted = DockedDividerUtils.invertDockSide(mDockSide); 833 int taskPositionDocked = 834 restrictDismissingTaskPosition(taskPosition, mDockSide, taskSnapTarget); 835 int taskPositionOther = 836 restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget); 837 calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect); 838 calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect); 839 mDisplayRect.set(0, 0, mDisplayWidth, mDisplayHeight); 840 alignTopLeft(mDockedRect, mDockedTaskRect); 841 alignTopLeft(mOtherRect, mOtherTaskRect); 842 mDockedInsetRect.set(mDockedTaskRect); 843 mOtherInsetRect.set(mOtherTaskRect); 844 if (dockSideTopLeft(mDockSide)) { 845 alignTopLeft(mDisplayRect, mDockedInsetRect); 846 alignBottomRight(mDisplayRect, mOtherInsetRect); 847 } else { 848 alignBottomRight(mDisplayRect, mDockedInsetRect); 849 alignTopLeft(mDisplayRect, mOtherInsetRect); 850 } 851 applyDismissingParallax(mDockedTaskRect, mDockSide, taskSnapTarget, position, 852 taskPositionDocked); 853 applyDismissingParallax(mOtherTaskRect, dockSideInverted, taskSnapTarget, position, 854 taskPositionOther); 855 mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect, 856 mOtherTaskRect, mOtherInsetRect); 857 } else { 858 mWindowManagerProxy.resizeDockedStack(mDockedRect, null, null, null, null); 859 } 860 SnapTarget closestDismissTarget = mSnapAlgorithm.getClosestDismissTarget(position); 861 float dimFraction = getDimFraction(position, closestDismissTarget); 862 mWindowManagerProxy.setResizeDimLayer(dimFraction != 0f, 863 getStackIdForDismissTarget(closestDismissTarget), 864 dimFraction); 865 } 866 applyExitAnimationParallax(Rect taskRect, int position)867 private void applyExitAnimationParallax(Rect taskRect, int position) { 868 if (mDockSide == WindowManager.DOCKED_TOP) { 869 taskRect.offset(0, (int) ((position - mExitStartPosition) * 0.25f)); 870 } else if (mDockSide == WindowManager.DOCKED_LEFT) { 871 taskRect.offset((int) ((position - mExitStartPosition) * 0.25f), 0); 872 } else if (mDockSide == WindowManager.DOCKED_RIGHT) { 873 taskRect.offset((int) ((mExitStartPosition - position) * 0.25f), 0); 874 } 875 } 876 getDimFraction(int position, SnapTarget dismissTarget)877 private float getDimFraction(int position, SnapTarget dismissTarget) { 878 if (mEntranceAnimationRunning) { 879 return 0f; 880 } 881 float fraction = mSnapAlgorithm.calculateDismissingFraction(position); 882 fraction = Math.max(0, Math.min(fraction, 1f)); 883 fraction = DIM_INTERPOLATOR.getInterpolation(fraction); 884 if (hasInsetsAtDismissTarget(dismissTarget)) { 885 886 // Less darkening with system insets. 887 fraction *= 0.8f; 888 } 889 return fraction; 890 } 891 892 /** 893 * @return true if and only if there are system insets at the location of the dismiss target 894 */ hasInsetsAtDismissTarget(SnapTarget dismissTarget)895 private boolean hasInsetsAtDismissTarget(SnapTarget dismissTarget) { 896 if (isHorizontalDivision()) { 897 if (dismissTarget == mSnapAlgorithm.getDismissStartTarget()) { 898 return mStableInsets.top != 0; 899 } else { 900 return mStableInsets.bottom != 0; 901 } 902 } else { 903 if (dismissTarget == mSnapAlgorithm.getDismissStartTarget()) { 904 return mStableInsets.left != 0; 905 } else { 906 return mStableInsets.right != 0; 907 } 908 } 909 } 910 911 /** 912 * When the snap target is dismissing one side, make sure that the dismissing side doesn't get 913 * 0 size. 914 */ restrictDismissingTaskPosition(int taskPosition, int dockSide, SnapTarget snapTarget)915 private int restrictDismissingTaskPosition(int taskPosition, int dockSide, 916 SnapTarget snapTarget) { 917 if (snapTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(dockSide)) { 918 return Math.max(mSnapAlgorithm.getFirstSplitTarget().position, mStartPosition); 919 } else if (snapTarget.flag == SnapTarget.FLAG_DISMISS_END 920 && dockSideBottomRight(dockSide)) { 921 return Math.min(mSnapAlgorithm.getLastSplitTarget().position, mStartPosition); 922 } else { 923 return taskPosition; 924 } 925 } 926 927 /** 928 * Applies a parallax to the task when dismissing. 929 */ applyDismissingParallax(Rect taskRect, int dockSide, SnapTarget snapTarget, int position, int taskPosition)930 private void applyDismissingParallax(Rect taskRect, int dockSide, SnapTarget snapTarget, 931 int position, int taskPosition) { 932 float fraction = Math.min(1, Math.max(0, 933 mSnapAlgorithm.calculateDismissingFraction(position))); 934 SnapTarget dismissTarget = null; 935 SnapTarget splitTarget = null; 936 int start = 0; 937 if (position <= mSnapAlgorithm.getLastSplitTarget().position 938 && dockSideTopLeft(dockSide)) { 939 dismissTarget = mSnapAlgorithm.getDismissStartTarget(); 940 splitTarget = mSnapAlgorithm.getFirstSplitTarget(); 941 start = taskPosition; 942 } else if (position >= mSnapAlgorithm.getLastSplitTarget().position 943 && dockSideBottomRight(dockSide)) { 944 dismissTarget = mSnapAlgorithm.getDismissEndTarget(); 945 splitTarget = mSnapAlgorithm.getLastSplitTarget(); 946 start = splitTarget.position; 947 } 948 if (dismissTarget != null && fraction > 0f 949 && isDismissing(splitTarget, position, dockSide)) { 950 fraction = calculateParallaxDismissingFraction(fraction, dockSide); 951 int offsetPosition = (int) (start + 952 fraction * (dismissTarget.position - splitTarget.position)); 953 int width = taskRect.width(); 954 int height = taskRect.height(); 955 switch (dockSide) { 956 case WindowManager.DOCKED_LEFT: 957 taskRect.left = offsetPosition - width; 958 taskRect.right = offsetPosition; 959 break; 960 case WindowManager.DOCKED_RIGHT: 961 taskRect.left = offsetPosition + mDividerSize; 962 taskRect.right = offsetPosition + width + mDividerSize; 963 break; 964 case WindowManager.DOCKED_TOP: 965 taskRect.top = offsetPosition - height; 966 taskRect.bottom = offsetPosition; 967 break; 968 case WindowManager.DOCKED_BOTTOM: 969 taskRect.top = offsetPosition + mDividerSize; 970 taskRect.bottom = offsetPosition + height + mDividerSize; 971 break; 972 } 973 } 974 } 975 976 /** 977 * @return for a specified {@code fraction}, this returns an adjusted value that simulates a 978 * slowing down parallax effect 979 */ calculateParallaxDismissingFraction(float fraction, int dockSide)980 private static float calculateParallaxDismissingFraction(float fraction, int dockSide) { 981 float result = SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f; 982 983 // Less parallax at the top, just because. 984 if (dockSide == WindowManager.DOCKED_TOP) { 985 result /= 2f; 986 } 987 return result; 988 } 989 isDismissing(SnapTarget snapTarget, int position, int dockSide)990 private static boolean isDismissing(SnapTarget snapTarget, int position, int dockSide) { 991 if (dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT) { 992 return position < snapTarget.position; 993 } else { 994 return position > snapTarget.position; 995 } 996 } 997 getStackIdForDismissTarget(SnapTarget dismissTarget)998 private int getStackIdForDismissTarget(SnapTarget dismissTarget) { 999 if ((dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide)) 1000 || (dismissTarget.flag == SnapTarget.FLAG_DISMISS_END 1001 && dockSideBottomRight(mDockSide))) { 1002 return StackId.DOCKED_STACK_ID; 1003 } else { 1004 return StackId.HOME_STACK_ID; 1005 } 1006 } 1007 1008 /** 1009 * @return true if and only if {@code dockSide} is top or left 1010 */ dockSideTopLeft(int dockSide)1011 private static boolean dockSideTopLeft(int dockSide) { 1012 return dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT; 1013 } 1014 1015 /** 1016 * @return true if and only if {@code dockSide} is bottom or right 1017 */ dockSideBottomRight(int dockSide)1018 private static boolean dockSideBottomRight(int dockSide) { 1019 return dockSide == WindowManager.DOCKED_BOTTOM || dockSide == WindowManager.DOCKED_RIGHT; 1020 } 1021 1022 @Override onComputeInternalInsets(InternalInsetsInfo inoutInfo)1023 public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) { 1024 inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 1025 inoutInfo.touchableRegion.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), 1026 mHandle.getBottom()); 1027 inoutInfo.touchableRegion.op(mBackground.getLeft(), mBackground.getTop(), 1028 mBackground.getRight(), mBackground.getBottom(), Op.UNION); 1029 } 1030 1031 /** 1032 * Checks whether recents will grow when invoked. This happens in multi-window when recents is 1033 * very small. When invoking recents, we shrink the docked stack so recents has more space. 1034 * 1035 * @return the position of the divider when recents grows, or 1036 * {@link #INVALID_RECENTS_GROW_TARGET} if recents won't grow 1037 */ growsRecents()1038 public int growsRecents() { 1039 boolean result = mGrowRecents 1040 && mWindowManagerProxy.getDockSide() == WindowManager.DOCKED_TOP 1041 && getCurrentPosition() == getSnapAlgorithm().getLastSplitTarget().position; 1042 if (result) { 1043 return getSnapAlgorithm().getMiddleTarget().position; 1044 } else { 1045 return INVALID_RECENTS_GROW_TARGET; 1046 } 1047 } 1048 onBusEvent(RecentsActivityStartingEvent recentsActivityStartingEvent)1049 public final void onBusEvent(RecentsActivityStartingEvent recentsActivityStartingEvent) { 1050 if (mGrowRecents && getWindowManagerProxy().getDockSide() == WindowManager.DOCKED_TOP 1051 && getCurrentPosition() == getSnapAlgorithm().getLastSplitTarget().position) { 1052 mState.growAfterRecentsDrawn = true; 1053 startDragging(false /* animate */, false /* touching */); 1054 } 1055 } 1056 onBusEvent(DockedTopTaskEvent event)1057 public final void onBusEvent(DockedTopTaskEvent event) { 1058 if (event.dragMode == NavigationBarGestureHelper.DRAG_MODE_NONE) { 1059 mState.growAfterRecentsDrawn = false; 1060 mState.animateAfterRecentsDrawn = true; 1061 startDragging(false /* animate */, false /* touching */); 1062 } 1063 updateDockSide(); 1064 int position = DockedDividerUtils.calculatePositionForBounds(event.initialRect, 1065 mDockSide, mDividerSize); 1066 mEntranceAnimationRunning = true; 1067 1068 // Insets might not have been fetched yet, so fetch manually if needed. 1069 if (mStableInsets.isEmpty()) { 1070 SystemServicesProxy.getInstance(mContext).getStableInsets(mStableInsets); 1071 mSnapAlgorithm = null; 1072 initializeSnapAlgorithm(); 1073 } 1074 1075 resizeStack(position, mSnapAlgorithm.getMiddleTarget().position, 1076 mSnapAlgorithm.getMiddleTarget()); 1077 } 1078 onBusEvent(RecentsDrawnEvent drawnEvent)1079 public final void onBusEvent(RecentsDrawnEvent drawnEvent) { 1080 if (mState.animateAfterRecentsDrawn) { 1081 mState.animateAfterRecentsDrawn = false; 1082 updateDockSide(); 1083 1084 mHandler.post(() -> { 1085 // Delay switching resizing mode because this might cause jank in recents animation 1086 // that's longer than this animation. 1087 stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 1088 mLongPressEntraceAnimDuration, Interpolators.FAST_OUT_SLOW_IN, 1089 200 /* endDelay */); 1090 }); 1091 } 1092 if (mState.growAfterRecentsDrawn) { 1093 mState.growAfterRecentsDrawn = false; 1094 updateDockSide(); 1095 EventBus.getDefault().send(new RecentsGrowingEvent()); 1096 stopDragging(getCurrentPosition(), mSnapAlgorithm.getMiddleTarget(), 336, 1097 Interpolators.FAST_OUT_SLOW_IN); 1098 } 1099 } 1100 onBusEvent(UndockingTaskEvent undockingTaskEvent)1101 public final void onBusEvent(UndockingTaskEvent undockingTaskEvent) { 1102 int dockSide = mWindowManagerProxy.getDockSide(); 1103 if (dockSide != WindowManager.DOCKED_INVALID && !mDockedStackMinimized) { 1104 startDragging(false /* animate */, false /* touching */); 1105 SnapTarget target = dockSideTopLeft(dockSide) 1106 ? mSnapAlgorithm.getDismissEndTarget() 1107 : mSnapAlgorithm.getDismissStartTarget(); 1108 1109 // Don't start immediately - give a little bit time to settle the drag resize change. 1110 mExitAnimationRunning = true; 1111 mExitStartPosition = getCurrentPosition(); 1112 stopDragging(mExitStartPosition, target, 336 /* duration */, 100 /* startDelay */, 1113 0 /* endDelay */, Interpolators.FAST_OUT_SLOW_IN); 1114 } 1115 } 1116 } 1117