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