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