DockedStackDividerController.java revision 853304c0b11921db142a3945ab66fae5f0cc7d8a
1/* 2 * Copyright (C) 2012 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.server.wm; 18 19import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; 20import static android.app.ActivityManager.StackId.INVALID_STACK_ID; 21import static android.view.WindowManager.DOCKED_BOTTOM; 22import static android.view.WindowManager.DOCKED_LEFT; 23import static android.view.WindowManager.DOCKED_RIGHT; 24import static android.view.WindowManager.DOCKED_TOP; 25import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION; 26import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR; 27import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; 28import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 29import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 30import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED; 31 32import android.content.Context; 33import android.graphics.Rect; 34import android.os.RemoteCallbackList; 35import android.os.RemoteException; 36import android.util.ArraySet; 37import android.util.Slog; 38import android.view.DisplayInfo; 39import android.view.IDockedStackListener; 40import android.view.SurfaceControl; 41import android.view.animation.AnimationUtils; 42import android.view.animation.Interpolator; 43import android.view.animation.PathInterpolator; 44 45import com.android.server.wm.DimLayer.DimLayerUser; 46import com.android.server.wm.WindowManagerService.H; 47 48import java.util.ArrayList; 49 50/** 51 * Keeps information about the docked stack divider. 52 */ 53public class DockedStackDividerController implements DimLayerUser { 54 55 private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM; 56 57 /** 58 * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip 59 * revealing surface at the earliest. 60 */ 61 private static final float CLIP_REVEAL_MEET_EARLIEST = 0.6f; 62 63 /** 64 * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip 65 * revealing surface at the latest. 66 */ 67 private static final float CLIP_REVEAL_MEET_LAST = 1f; 68 69 /** 70 * If the app translates at least CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, we start 71 * meet somewhere between {@link #CLIP_REVEAL_MEET_LAST} and {@link #CLIP_REVEAL_MEET_EARLIEST}. 72 */ 73 private static final float CLIP_REVEAL_MEET_FRACTION_MIN = 0.4f; 74 75 /** 76 * If the app translates equals or more than CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, 77 * we meet at {@link #CLIP_REVEAL_MEET_EARLIEST}. 78 */ 79 private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f; 80 81 private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR = 82 new PathInterpolator(0.2f, 0f, 0.1f, 1f); 83 84 private static final long IME_ADJUST_ANIM_DURATION = 280; 85 86 private static final long IME_ADJUST_DRAWN_TIMEOUT = 200; 87 88 private final WindowManagerService mService; 89 private final DisplayContent mDisplayContent; 90 private int mDividerWindowWidth; 91 private int mDividerInsets; 92 private boolean mResizing; 93 private WindowState mWindow; 94 private final Rect mTmpRect = new Rect(); 95 private final Rect mTmpRect2 = new Rect(); 96 private final Rect mLastRect = new Rect(); 97 private boolean mLastVisibility = false; 98 private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners 99 = new RemoteCallbackList<>(); 100 private final DimLayer mDimLayer; 101 102 private boolean mMinimizedDock; 103 private boolean mAnimatingForMinimizedDockedStack; 104 private boolean mAnimationStarted; 105 private long mAnimationStartTime; 106 private float mAnimationStart; 107 private float mAnimationTarget; 108 private long mAnimationDuration; 109 private boolean mAnimationStartDelayed; 110 private final Interpolator mMinimizedDockInterpolator; 111 private float mMaximizeMeetFraction; 112 private final Rect mTouchRegion = new Rect(); 113 private boolean mAnimatingForIme; 114 private boolean mAdjustedForIme; 115 private WindowState mDelayedImeWin; 116 117 DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) { 118 mService = service; 119 mDisplayContent = displayContent; 120 final Context context = service.mContext; 121 mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(), 122 "DockedStackDim"); 123 mMinimizedDockInterpolator = AnimationUtils.loadInterpolator( 124 context, android.R.interpolator.fast_out_slow_in); 125 loadDimens(); 126 } 127 128 private void loadDimens() { 129 final Context context = mService.mContext; 130 mDividerWindowWidth = context.getResources().getDimensionPixelSize( 131 com.android.internal.R.dimen.docked_stack_divider_thickness); 132 mDividerInsets = context.getResources().getDimensionPixelSize( 133 com.android.internal.R.dimen.docked_stack_divider_insets); 134 } 135 136 void onConfigurationChanged() { 137 loadDimens(); 138 } 139 140 boolean isResizing() { 141 return mResizing; 142 } 143 144 int getContentWidth() { 145 return mDividerWindowWidth - 2 * mDividerInsets; 146 } 147 148 int getContentInsets() { 149 return mDividerInsets; 150 } 151 152 void setResizing(boolean resizing) { 153 if (mResizing != resizing) { 154 mResizing = resizing; 155 resetDragResizingChangeReported(); 156 } 157 } 158 159 void setTouchRegion(Rect touchRegion) { 160 mTouchRegion.set(touchRegion); 161 } 162 163 void getTouchRegion(Rect outRegion) { 164 outRegion.set(mTouchRegion); 165 outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top); 166 } 167 168 private void resetDragResizingChangeReported() { 169 final WindowList windowList = mDisplayContent.getWindowList(); 170 for (int i = windowList.size() - 1; i >= 0; i--) { 171 windowList.get(i).resetDragResizingChangeReported(); 172 } 173 } 174 175 void setWindow(WindowState window) { 176 mWindow = window; 177 reevaluateVisibility(false); 178 } 179 180 void reevaluateVisibility(boolean force) { 181 if (mWindow == null) { 182 return; 183 } 184 TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID); 185 186 // If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide 187 final boolean visible = stack != null; 188 if (mLastVisibility == visible && !force) { 189 return; 190 } 191 mLastVisibility = visible; 192 notifyDockedDividerVisibilityChanged(visible); 193 if (!visible) { 194 setResizeDimLayer(false, INVALID_STACK_ID, 0f); 195 } 196 } 197 198 boolean wasVisible() { 199 return mLastVisibility; 200 } 201 202 void setAdjustedForIme(boolean adjusted, boolean animate, WindowState imeWin) { 203 if (mAdjustedForIme != adjusted) { 204 mAdjustedForIme = adjusted; 205 if (animate) { 206 startImeAdjustAnimation(adjusted, imeWin); 207 } else { 208 209 // Animation might be delayed, so only notify if we don't run an animation. 210 notifyAdjustedForImeChanged(adjusted, 0 /* duration */); 211 } 212 } 213 } 214 215 void positionDockedStackedDivider(Rect frame) { 216 TaskStack stack = mDisplayContent.getDockedStackLocked(); 217 if (stack == null) { 218 // Unfortunately we might end up with still having a divider, even though the underlying 219 // stack was already removed. This is because we are on AM thread and the removal of the 220 // divider was deferred to WM thread and hasn't happened yet. In that case let's just 221 // keep putting it in the same place it was before the stack was removed to have 222 // continuity and prevent it from jumping to the center. It will get hidden soon. 223 frame.set(mLastRect); 224 return; 225 } else { 226 stack.getDimBounds(mTmpRect); 227 } 228 int side = stack.getDockSide(); 229 switch (side) { 230 case DOCKED_LEFT: 231 frame.set(mTmpRect.right - mDividerInsets, frame.top, 232 mTmpRect.right + frame.width() - mDividerInsets, frame.bottom); 233 break; 234 case DOCKED_TOP: 235 frame.set(frame.left, mTmpRect.bottom - mDividerInsets, 236 mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets); 237 break; 238 case DOCKED_RIGHT: 239 frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top, 240 mTmpRect.left + mDividerInsets, frame.bottom); 241 break; 242 case DOCKED_BOTTOM: 243 frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets, 244 frame.right, mTmpRect.top + mDividerInsets); 245 break; 246 } 247 mLastRect.set(frame); 248 } 249 250 void notifyDockedDividerVisibilityChanged(boolean visible) { 251 final int size = mDockedStackListeners.beginBroadcast(); 252 for (int i = 0; i < size; ++i) { 253 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 254 try { 255 listener.onDividerVisibilityChanged(visible); 256 } catch (RemoteException e) { 257 Slog.e(TAG_WM, "Error delivering divider visibility changed event.", e); 258 } 259 } 260 mDockedStackListeners.finishBroadcast(); 261 } 262 263 void notifyDockedStackExistsChanged(boolean exists) { 264 final int size = mDockedStackListeners.beginBroadcast(); 265 for (int i = 0; i < size; ++i) { 266 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 267 try { 268 listener.onDockedStackExistsChanged(exists); 269 } catch (RemoteException e) { 270 Slog.e(TAG_WM, "Error delivering docked stack exists changed event.", e); 271 } 272 } 273 mDockedStackListeners.finishBroadcast(); 274 if (!exists) { 275 setMinimizedDockedStack(false); 276 } 277 } 278 279 void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) { 280 mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED); 281 mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED, 282 minimizedDock ? 1 : 0, 0).sendToTarget(); 283 final int size = mDockedStackListeners.beginBroadcast(); 284 for (int i = 0; i < size; ++i) { 285 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 286 try { 287 listener.onDockedStackMinimizedChanged(minimizedDock, animDuration); 288 } catch (RemoteException e) { 289 Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e); 290 } 291 } 292 mDockedStackListeners.finishBroadcast(); 293 } 294 295 void notifyDockSideChanged(int newDockSide) { 296 final int size = mDockedStackListeners.beginBroadcast(); 297 for (int i = 0; i < size; ++i) { 298 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 299 try { 300 listener.onDockSideChanged(newDockSide); 301 } catch (RemoteException e) { 302 Slog.e(TAG_WM, "Error delivering dock side changed event.", e); 303 } 304 } 305 mDockedStackListeners.finishBroadcast(); 306 } 307 308 void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) { 309 final int size = mDockedStackListeners.beginBroadcast(); 310 for (int i = 0; i < size; ++i) { 311 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 312 try { 313 listener.onAdjustedForImeChanged(adjustedForIme, animDuration); 314 } catch (RemoteException e) { 315 Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e); 316 } 317 } 318 mDockedStackListeners.finishBroadcast(); 319 } 320 321 void registerDockedStackListener(IDockedStackListener listener) { 322 mDockedStackListeners.register(listener); 323 notifyDockedDividerVisibilityChanged(wasVisible()); 324 notifyDockedStackExistsChanged( 325 mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null); 326 notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */); 327 notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */); 328 329 } 330 331 void setResizeDimLayer(boolean visible, int targetStackId, float alpha) { 332 SurfaceControl.openTransaction(); 333 final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId); 334 final TaskStack dockedStack = mDisplayContent.getDockedStackLocked(); 335 boolean visibleAndValid = visible && stack != null && dockedStack != null; 336 if (visibleAndValid) { 337 stack.getDimBounds(mTmpRect); 338 if (mTmpRect.height() > 0 && mTmpRect.width() > 0) { 339 mDimLayer.setBounds(mTmpRect); 340 mDimLayer.show(mDisplayContent.mService.mLayersController.getResizeDimLayer(), 341 alpha, 0 /* duration */); 342 } else { 343 visibleAndValid = false; 344 } 345 } 346 if (!visibleAndValid) { 347 mDimLayer.hide(); 348 } 349 SurfaceControl.closeTransaction(); 350 } 351 352 /** 353 * Notifies the docked stack divider controller of a visibility change that happens without 354 * an animation. 355 */ 356 void notifyAppVisibilityChanged(AppWindowToken wtoken, boolean visible) { 357 final Task task = wtoken.mTask; 358 if (!task.isHomeTask() || !task.isVisibleForUser()) { 359 return; 360 } 361 362 // If the app that having visibility change is not the top visible one in the task, 363 // it does not affect whether the docked stack is minimized, ignore it. 364 if (task.getTopVisibleAppToken() == null || task.getTopVisibleAppToken() != wtoken) { 365 return; 366 } 367 368 // If the stack is completely offscreen, this might just be an intermediate state when 369 // docking a task/launching recents at the same time, but home doesn't actually get 370 // visible after the state settles in. 371 if (isWithinDisplay(task) 372 && mDisplayContent.getDockedStackVisibleForUserLocked() != null) { 373 setMinimizedDockedStack(visible, false /* animate */); 374 } 375 } 376 377 void notifyAppTransitionStarting(ArraySet<AppWindowToken> openingApps, 378 ArraySet<AppWindowToken> closingApps) { 379 if (containsHomeTaskWithinDisplay(openingApps)) { 380 setMinimizedDockedStack(true /* minimized */, true /* animate */); 381 } else if (containsHomeTaskWithinDisplay(closingApps)) { 382 setMinimizedDockedStack(false /* minimized */, true /* animate */); 383 } 384 } 385 386 private boolean containsHomeTaskWithinDisplay(ArraySet<AppWindowToken> apps) { 387 for (int i = apps.size() - 1; i >= 0; i--) { 388 final Task task = apps.valueAt(i).mTask; 389 if (task != null && task.isHomeTask()) { 390 return isWithinDisplay(task); 391 } 392 } 393 return false; 394 } 395 396 private boolean isWithinDisplay(Task task) { 397 task.mStack.getBounds(mTmpRect); 398 mDisplayContent.getLogicalDisplayRect(mTmpRect2); 399 return mTmpRect.intersect(mTmpRect2); 400 } 401 402 /** 403 * Sets whether the docked stack is currently in a minimized state, i.e. all the tasks in the 404 * docked stack are heavily clipped so you can only see a minimal peek state. 405 * 406 * @param minimizedDock Whether the docked stack is currently minimized. 407 * @param animate Whether to animate the change. 408 */ 409 private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) { 410 final boolean wasMinimized = mMinimizedDock; 411 mMinimizedDock = minimizedDock; 412 if (minimizedDock == wasMinimized 413 || mDisplayContent.getDockedStackVisibleForUserLocked() == null) { 414 return; 415 } 416 417 clearImeAdjustAnimation(); 418 if (minimizedDock) { 419 if (animate) { 420 startAdjustAnimation(0f, 1f); 421 } else { 422 setMinimizedDockedStack(true); 423 } 424 } else { 425 if (animate) { 426 startAdjustAnimation(1f, 0f); 427 } else { 428 setMinimizedDockedStack(false); 429 } 430 } 431 } 432 433 private void clearImeAdjustAnimation() { 434 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks(); 435 for (int i = stacks.size() - 1; i >= 0; --i) { 436 final TaskStack stack = stacks.get(i); 437 if (stack != null && stack.isAdjustedForIme()) { 438 stack.resetAdjustedForIme(true /* adjustBoundsNow */); 439 } 440 } 441 mAnimatingForIme = false; 442 } 443 444 private void startAdjustAnimation(float from, float to) { 445 mAnimatingForMinimizedDockedStack = true; 446 mAnimationStarted = false; 447 mAnimationStart = from; 448 mAnimationTarget = to; 449 } 450 451 private void startImeAdjustAnimation(boolean adjusted, WindowState imeWin) { 452 mAnimatingForIme = true; 453 mAnimationStarted = false; 454 mAnimationStart = adjusted ? 0 : 1; 455 mAnimationTarget = adjusted ? 1 : 0; 456 457 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks(); 458 for (int i = stacks.size() - 1; i >= 0; --i) { 459 final TaskStack stack = stacks.get(i); 460 if (stack.isVisibleLocked() && stack.isAdjustedForIme()) { 461 stack.beginImeAdjustAnimation(); 462 } 463 } 464 465 // We put all tasks into drag resizing mode - wait until all of them have completed the 466 // drag resizing switch. 467 if (!mService.mWaitingForDrawn.isEmpty()) { 468 mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT); 469 mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, 470 IME_ADJUST_DRAWN_TIMEOUT); 471 mAnimationStartDelayed = true; 472 if (imeWin != null) { 473 474 // There might be an old window delaying the animation start - clear it. 475 if (mDelayedImeWin != null) { 476 mDelayedImeWin.mWinAnimator.endDelayingAnimationStart(); 477 } 478 mDelayedImeWin = imeWin; 479 imeWin.mWinAnimator.startDelayingAnimationStart(); 480 } 481 mService.mWaitingForDrawnCallback = () -> { 482 mAnimationStartDelayed = false; 483 if (mDelayedImeWin != null) { 484 mDelayedImeWin.mWinAnimator.endDelayingAnimationStart(); 485 } 486 notifyAdjustedForImeChanged(adjusted, IME_ADJUST_ANIM_DURATION); 487 }; 488 } else { 489 notifyAdjustedForImeChanged(adjusted, IME_ADJUST_ANIM_DURATION); 490 } 491 } 492 493 private void setMinimizedDockedStack(boolean minimized) { 494 final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked(); 495 notifyDockedStackMinimizedChanged(minimized, 0); 496 if (stack == null) { 497 return; 498 } 499 if (stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f)) { 500 mService.mWindowPlacerLocked.performSurfacePlacement(); 501 } 502 } 503 504 private boolean isAnimationMaximizing() { 505 return mAnimationTarget == 0f; 506 } 507 508 public boolean animate(long now) { 509 if (mAnimatingForMinimizedDockedStack) { 510 return animateForMinimizedDockedStack(now); 511 } else if (mAnimatingForIme) { 512 return animateForIme(now); 513 } else { 514 return false; 515 } 516 } 517 518 private boolean animateForIme(long now) { 519 if (!mAnimationStarted || mAnimationStartDelayed) { 520 mAnimationStarted = true; 521 mAnimationStartTime = now; 522 mAnimationDuration = (long) 523 (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked()); 524 } 525 float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration); 526 t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR) 527 .getInterpolation(t); 528 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks(); 529 boolean updated = false; 530 for (int i = stacks.size() - 1; i >= 0; --i) { 531 final TaskStack stack = stacks.get(i); 532 if (stack != null && stack.isAdjustedForIme()) { 533 if (t >= 1f && mAnimationTarget == 0f) { 534 stack.resetAdjustedForIme(true /* adjustBoundsNow */); 535 updated = true; 536 } else { 537 updated |= stack.updateAdjustForIme(getInterpolatedAnimationValue(t), 538 false /* force */); 539 } 540 if (t >= 1f) { 541 stack.endImeAdjustAnimation(); 542 } 543 } 544 } 545 if (updated) { 546 mService.mWindowPlacerLocked.performSurfacePlacement(); 547 } 548 if (t >= 1.0f) { 549 mAnimatingForIme = false; 550 return false; 551 } else { 552 return true; 553 } 554 } 555 556 private boolean animateForMinimizedDockedStack(long now) { 557 final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked(); 558 if (!mAnimationStarted) { 559 mAnimationStarted = true; 560 mAnimationStartTime = now; 561 final long transitionDuration = isAnimationMaximizing() 562 ? mService.mAppTransition.getLastClipRevealTransitionDuration() 563 : DEFAULT_APP_TRANSITION_DURATION; 564 mAnimationDuration = (long) 565 (transitionDuration * mService.getTransitionAnimationScaleLocked()); 566 mMaximizeMeetFraction = getClipRevealMeetFraction(stack); 567 notifyDockedStackMinimizedChanged(mMinimizedDock, 568 (long) (mAnimationDuration * mMaximizeMeetFraction)); 569 } 570 float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration); 571 t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator) 572 .getInterpolation(t); 573 if (stack != null) { 574 if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) { 575 mService.mWindowPlacerLocked.performSurfacePlacement(); 576 } 577 } 578 if (t >= 1.0f) { 579 mAnimatingForMinimizedDockedStack = false; 580 return false; 581 } else { 582 return true; 583 } 584 } 585 586 private float getInterpolatedAnimationValue(float t) { 587 return t * mAnimationTarget + (1 - t) * mAnimationStart; 588 } 589 590 /** 591 * Gets the amount how much to minimize a stack depending on the interpolated fraction t. 592 */ 593 private float getMinimizeAmount(TaskStack stack, float t) { 594 final float naturalAmount = getInterpolatedAnimationValue(t); 595 if (isAnimationMaximizing()) { 596 return adjustMaximizeAmount(stack, t, naturalAmount); 597 } else { 598 return naturalAmount; 599 } 600 } 601 602 /** 603 * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount 604 * during the transition such that the edge of the clip reveal rect is met earlier in the 605 * transition so we don't create a visible "hole", but only if both the clip reveal and the 606 * docked stack divider start from about the same portion on the screen. 607 */ 608 private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) { 609 if (mMaximizeMeetFraction == 1f) { 610 return naturalAmount; 611 } 612 final int minimizeDistance = stack.getMinimizeDistance(); 613 float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation() 614 / (float) minimizeDistance; 615 final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime; 616 final float t2 = Math.min(t / mMaximizeMeetFraction, 1); 617 return amountPrime * t2 + naturalAmount * (1 - t2); 618 } 619 620 /** 621 * Retrieves the animation fraction at which the docked stack has to meet the clip reveal 622 * edge. See {@link #adjustMaximizeAmount}. 623 */ 624 private float getClipRevealMeetFraction(TaskStack stack) { 625 if (!isAnimationMaximizing() || stack == null || 626 !mService.mAppTransition.hadClipRevealAnimation()) { 627 return 1f; 628 } 629 final int minimizeDistance = stack.getMinimizeDistance(); 630 final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation()) 631 / (float) minimizeDistance; 632 final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN) 633 / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN))); 634 return CLIP_REVEAL_MEET_EARLIEST 635 + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST); 636 } 637 638 @Override 639 public boolean isFullscreen() { 640 return false; 641 } 642 643 @Override 644 public DisplayInfo getDisplayInfo() { 645 return mDisplayContent.getDisplayInfo(); 646 } 647 648 @Override 649 public void getDimBounds(Rect outBounds) { 650 // This dim layer user doesn't need this. 651 } 652 653 @Override 654 public String toShortString() { 655 return TAG; 656 } 657}