DockedStackDividerController.java revision f0b76b071c8434fbf4a76798e9cdd56ab67e523d
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.content.res.Configuration.ORIENTATION_LANDSCAPE; 23import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 24import static android.view.Surface.ROTATION_270; 25import static android.view.Surface.ROTATION_90; 26import static android.view.WindowManager.DOCKED_BOTTOM; 27import static android.view.WindowManager.DOCKED_LEFT; 28import static android.view.WindowManager.DOCKED_RIGHT; 29import static android.view.WindowManager.DOCKED_TOP; 30import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION; 31import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR; 32import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 33import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 34import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED; 35 36import android.content.Context; 37import android.content.res.Configuration; 38import android.graphics.Rect; 39import android.os.RemoteCallbackList; 40import android.os.RemoteException; 41import android.util.Slog; 42import android.view.DisplayInfo; 43import android.view.IDockedStackListener; 44import android.view.SurfaceControl; 45import android.view.animation.AnimationUtils; 46import android.view.animation.Interpolator; 47import android.view.animation.PathInterpolator; 48 49import com.android.internal.policy.DividerSnapAlgorithm; 50import com.android.internal.policy.DockedDividerUtils; 51import com.android.server.wm.DimLayer.DimLayerUser; 52import com.android.server.wm.WindowManagerService.H; 53 54import java.io.PrintWriter; 55import java.util.ArrayList; 56 57/** 58 * Keeps information about the docked stack divider. 59 */ 60public class DockedStackDividerController implements DimLayerUser { 61 62 private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM; 63 64 /** 65 * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip 66 * revealing surface at the earliest. 67 */ 68 private static final float CLIP_REVEAL_MEET_EARLIEST = 0.6f; 69 70 /** 71 * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip 72 * revealing surface at the latest. 73 */ 74 private static final float CLIP_REVEAL_MEET_LAST = 1f; 75 76 /** 77 * If the app translates at least CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, we start 78 * meet somewhere between {@link #CLIP_REVEAL_MEET_LAST} and {@link #CLIP_REVEAL_MEET_EARLIEST}. 79 */ 80 private static final float CLIP_REVEAL_MEET_FRACTION_MIN = 0.4f; 81 82 /** 83 * If the app translates equals or more than CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, 84 * we meet at {@link #CLIP_REVEAL_MEET_EARLIEST}. 85 */ 86 private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f; 87 88 private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR = 89 new PathInterpolator(0.2f, 0f, 0.1f, 1f); 90 91 private static final long IME_ADJUST_ANIM_DURATION = 280; 92 93 private static final long IME_ADJUST_DRAWN_TIMEOUT = 200; 94 95 private static final int DIVIDER_WIDTH_INACTIVE_DP = 4; 96 97 private final WindowManagerService mService; 98 private final DisplayContent mDisplayContent; 99 private int mDividerWindowWidth; 100 private int mDividerWindowWidthInactive; 101 private int mDividerInsets; 102 private boolean mResizing; 103 private WindowState mWindow; 104 private final Rect mTmpRect = new Rect(); 105 private final Rect mTmpRect2 = new Rect(); 106 private final Rect mTmpRect3 = new Rect(); 107 private final Rect mLastRect = new Rect(); 108 private boolean mLastVisibility = false; 109 private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners 110 = new RemoteCallbackList<>(); 111 private final DimLayer mDimLayer; 112 113 private boolean mMinimizedDock; 114 private boolean mAnimatingForMinimizedDockedStack; 115 private boolean mAnimationStarted; 116 private long mAnimationStartTime; 117 private float mAnimationStart; 118 private float mAnimationTarget; 119 private long mAnimationDuration; 120 private boolean mAnimationStartDelayed; 121 private final Interpolator mMinimizedDockInterpolator; 122 private float mMaximizeMeetFraction; 123 private final Rect mTouchRegion = new Rect(); 124 private boolean mAnimatingForIme; 125 private boolean mAdjustedForIme; 126 private int mImeHeight; 127 private WindowState mDelayedImeWin; 128 private boolean mAdjustedForDivider; 129 private float mDividerAnimationStart; 130 private float mDividerAnimationTarget; 131 private float mLastAnimationProgress; 132 private float mLastDividerProgress; 133 private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4]; 134 135 DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) { 136 mService = service; 137 mDisplayContent = displayContent; 138 final Context context = service.mContext; 139 mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(), 140 "DockedStackDim"); 141 mMinimizedDockInterpolator = AnimationUtils.loadInterpolator( 142 context, android.R.interpolator.fast_out_slow_in); 143 loadDimens(); 144 } 145 146 int getSmallestWidthDpForBounds(Rect bounds) { 147 final DisplayInfo di = mDisplayContent.getDisplayInfo(); 148 149 // If the bounds are fullscreen, return the value of the fullscreen configuration 150 if (bounds == null || (bounds.left == 0 && bounds.top == 0 151 && bounds.right == di.logicalWidth && bounds.bottom == di.logicalHeight)) { 152 return mService.mCurConfiguration.smallestScreenWidthDp; 153 } 154 final int baseDisplayWidth = mDisplayContent.mBaseDisplayWidth; 155 final int baseDisplayHeight = mDisplayContent.mBaseDisplayHeight; 156 int minWidth = Integer.MAX_VALUE; 157 158 // Go through all screen orientations and find the orientation in which the task has the 159 // smallest width. 160 for (int rotation = 0; rotation < 4; rotation++) { 161 mTmpRect.set(bounds); 162 mDisplayContent.rotateBounds(di.rotation, rotation, mTmpRect); 163 final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); 164 mTmpRect2.set(0, 0, 165 rotated ? baseDisplayHeight : baseDisplayWidth, 166 rotated ? baseDisplayWidth : baseDisplayHeight); 167 final int orientation = mTmpRect2.width() <= mTmpRect2.height() 168 ? ORIENTATION_PORTRAIT 169 : ORIENTATION_LANDSCAPE; 170 final int dockSide = TaskStack.getDockSideUnchecked(mTmpRect, mTmpRect2, orientation); 171 final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide, 172 getContentWidth()); 173 174 // Since we only care about feasible states, snap to the closest snap target, like it 175 // would happen when actually rotating the screen. 176 final int snappedPosition = mSnapAlgorithmForRotation[rotation] 177 .calculateNonDismissingSnapTarget(position).position; 178 DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, mTmpRect, 179 mTmpRect2.width(), mTmpRect2.height(), getContentWidth()); 180 mService.mPolicy.getStableInsetsLw(rotation, mTmpRect2.width(), mTmpRect2.height(), 181 mTmpRect3); 182 mService.subtractInsets(mTmpRect2, mTmpRect3, mTmpRect); 183 minWidth = Math.min(mTmpRect.width(), minWidth); 184 } 185 return (int) (minWidth / mDisplayContent.getDisplayMetrics().density); 186 } 187 188 private void initSnapAlgorithmForRotations() { 189 final Configuration baseConfig = mService.mCurConfiguration; 190 191 // Initialize the snap algorithms for all 4 screen orientations. 192 final Configuration config = new Configuration(); 193 for (int rotation = 0; rotation < 4; rotation++) { 194 final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); 195 final int dw = rotated 196 ? mDisplayContent.mBaseDisplayHeight 197 : mDisplayContent.mBaseDisplayWidth; 198 final int dh = rotated 199 ? mDisplayContent.mBaseDisplayWidth 200 : mDisplayContent.mBaseDisplayHeight; 201 mService.mPolicy.getStableInsetsLw(rotation, dw, dh, mTmpRect); 202 config.setToDefaults(); 203 config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; 204 config.screenWidthDp = (int) 205 (mService.mPolicy.getConfigDisplayWidth(dw, dh, rotation, baseConfig.uiMode) / 206 mDisplayContent.getDisplayMetrics().density); 207 config.screenHeightDp = (int) 208 (mService.mPolicy.getConfigDisplayHeight(dw, dh, rotation, baseConfig.uiMode) / 209 mDisplayContent.getDisplayMetrics().density); 210 final Context rotationContext = mService.mContext.createConfigurationContext(config); 211 mSnapAlgorithmForRotation[rotation] = new DividerSnapAlgorithm( 212 rotationContext.getResources(), dw, dh, getContentWidth(), 213 config.orientation == ORIENTATION_PORTRAIT, mTmpRect); 214 } 215 } 216 217 private void loadDimens() { 218 final Context context = mService.mContext; 219 mDividerWindowWidth = context.getResources().getDimensionPixelSize( 220 com.android.internal.R.dimen.docked_stack_divider_thickness); 221 mDividerInsets = context.getResources().getDimensionPixelSize( 222 com.android.internal.R.dimen.docked_stack_divider_insets); 223 mDividerWindowWidthInactive = WindowManagerService.dipToPixel( 224 DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics()); 225 initSnapAlgorithmForRotations(); 226 } 227 228 void onConfigurationChanged() { 229 loadDimens(); 230 } 231 232 boolean isResizing() { 233 return mResizing; 234 } 235 236 int getContentWidth() { 237 return mDividerWindowWidth - 2 * mDividerInsets; 238 } 239 240 int getContentInsets() { 241 return mDividerInsets; 242 } 243 244 int getContentWidthInactive() { 245 return mDividerWindowWidthInactive; 246 } 247 248 void setResizing(boolean resizing) { 249 if (mResizing != resizing) { 250 mResizing = resizing; 251 resetDragResizingChangeReported(); 252 } 253 } 254 255 void setTouchRegion(Rect touchRegion) { 256 mTouchRegion.set(touchRegion); 257 } 258 259 void getTouchRegion(Rect outRegion) { 260 outRegion.set(mTouchRegion); 261 outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top); 262 } 263 264 private void resetDragResizingChangeReported() { 265 final WindowList windowList = mDisplayContent.getWindowList(); 266 for (int i = windowList.size() - 1; i >= 0; i--) { 267 windowList.get(i).resetDragResizingChangeReported(); 268 } 269 } 270 271 void setWindow(WindowState window) { 272 mWindow = window; 273 reevaluateVisibility(false); 274 } 275 276 void reevaluateVisibility(boolean force) { 277 if (mWindow == null) { 278 return; 279 } 280 TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID); 281 282 // If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide 283 final boolean visible = stack != null; 284 if (mLastVisibility == visible && !force) { 285 return; 286 } 287 mLastVisibility = visible; 288 notifyDockedDividerVisibilityChanged(visible); 289 if (!visible) { 290 setResizeDimLayer(false, INVALID_STACK_ID, 0f); 291 } 292 } 293 294 boolean wasVisible() { 295 return mLastVisibility; 296 } 297 298 void setAdjustedForIme( 299 boolean adjustedForIme, boolean adjustedForDivider, 300 boolean animate, WindowState imeWin, int imeHeight) { 301 if (mAdjustedForIme != adjustedForIme || (adjustedForIme && mImeHeight != imeHeight) 302 || mAdjustedForDivider != adjustedForDivider) { 303 if (animate) { 304 startImeAdjustAnimation(adjustedForIme, adjustedForDivider, imeWin); 305 } else { 306 // Animation might be delayed, so only notify if we don't run an animation. 307 notifyAdjustedForImeChanged(adjustedForIme || adjustedForDivider, 0 /* duration */); 308 } 309 mAdjustedForIme = adjustedForIme; 310 mImeHeight = imeHeight; 311 mAdjustedForDivider = adjustedForDivider; 312 } 313 } 314 315 int getImeHeightAdjustedFor() { 316 return mImeHeight; 317 } 318 319 void positionDockedStackedDivider(Rect frame) { 320 TaskStack stack = mDisplayContent.getDockedStackLocked(); 321 if (stack == null) { 322 // Unfortunately we might end up with still having a divider, even though the underlying 323 // stack was already removed. This is because we are on AM thread and the removal of the 324 // divider was deferred to WM thread and hasn't happened yet. In that case let's just 325 // keep putting it in the same place it was before the stack was removed to have 326 // continuity and prevent it from jumping to the center. It will get hidden soon. 327 frame.set(mLastRect); 328 return; 329 } else { 330 stack.getDimBounds(mTmpRect); 331 } 332 int side = stack.getDockSide(); 333 switch (side) { 334 case DOCKED_LEFT: 335 frame.set(mTmpRect.right - mDividerInsets, frame.top, 336 mTmpRect.right + frame.width() - mDividerInsets, frame.bottom); 337 break; 338 case DOCKED_TOP: 339 frame.set(frame.left, mTmpRect.bottom - mDividerInsets, 340 mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets); 341 break; 342 case DOCKED_RIGHT: 343 frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top, 344 mTmpRect.left + mDividerInsets, frame.bottom); 345 break; 346 case DOCKED_BOTTOM: 347 frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets, 348 frame.right, mTmpRect.top + mDividerInsets); 349 break; 350 } 351 mLastRect.set(frame); 352 } 353 354 void notifyDockedDividerVisibilityChanged(boolean visible) { 355 final int size = mDockedStackListeners.beginBroadcast(); 356 for (int i = 0; i < size; ++i) { 357 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 358 try { 359 listener.onDividerVisibilityChanged(visible); 360 } catch (RemoteException e) { 361 Slog.e(TAG_WM, "Error delivering divider visibility changed event.", e); 362 } 363 } 364 mDockedStackListeners.finishBroadcast(); 365 } 366 367 void notifyDockedStackExistsChanged(boolean exists) { 368 final int size = mDockedStackListeners.beginBroadcast(); 369 for (int i = 0; i < size; ++i) { 370 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 371 try { 372 listener.onDockedStackExistsChanged(exists); 373 } catch (RemoteException e) { 374 Slog.e(TAG_WM, "Error delivering docked stack exists changed event.", e); 375 } 376 } 377 mDockedStackListeners.finishBroadcast(); 378 if (!exists) { 379 setMinimizedDockedStack(false); 380 } 381 } 382 383 void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) { 384 mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED); 385 mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED, 386 minimizedDock ? 1 : 0, 0).sendToTarget(); 387 final int size = mDockedStackListeners.beginBroadcast(); 388 for (int i = 0; i < size; ++i) { 389 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 390 try { 391 listener.onDockedStackMinimizedChanged(minimizedDock, animDuration); 392 } catch (RemoteException e) { 393 Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e); 394 } 395 } 396 mDockedStackListeners.finishBroadcast(); 397 } 398 399 void notifyDockSideChanged(int newDockSide) { 400 final int size = mDockedStackListeners.beginBroadcast(); 401 for (int i = 0; i < size; ++i) { 402 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 403 try { 404 listener.onDockSideChanged(newDockSide); 405 } catch (RemoteException e) { 406 Slog.e(TAG_WM, "Error delivering dock side changed event.", e); 407 } 408 } 409 mDockedStackListeners.finishBroadcast(); 410 } 411 412 void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) { 413 final int size = mDockedStackListeners.beginBroadcast(); 414 for (int i = 0; i < size; ++i) { 415 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 416 try { 417 listener.onAdjustedForImeChanged(adjustedForIme, animDuration); 418 } catch (RemoteException e) { 419 Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e); 420 } 421 } 422 mDockedStackListeners.finishBroadcast(); 423 } 424 425 void registerDockedStackListener(IDockedStackListener listener) { 426 mDockedStackListeners.register(listener); 427 notifyDockedDividerVisibilityChanged(wasVisible()); 428 notifyDockedStackExistsChanged( 429 mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null); 430 notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */); 431 notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */); 432 433 } 434 435 void setResizeDimLayer(boolean visible, int targetStackId, float alpha) { 436 SurfaceControl.openTransaction(); 437 final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId); 438 final TaskStack dockedStack = mDisplayContent.getDockedStackLocked(); 439 boolean visibleAndValid = visible && stack != null && dockedStack != null; 440 if (visibleAndValid) { 441 stack.getDimBounds(mTmpRect); 442 if (mTmpRect.height() > 0 && mTmpRect.width() > 0) { 443 mDimLayer.setBounds(mTmpRect); 444 mDimLayer.show(mService.mLayersController.getResizeDimLayer(), 445 alpha, 0 /* duration */); 446 } else { 447 visibleAndValid = false; 448 } 449 } 450 if (!visibleAndValid) { 451 mDimLayer.hide(); 452 } 453 SurfaceControl.closeTransaction(); 454 } 455 456 /** 457 * Notifies the docked stack divider controller of a visibility change that happens without 458 * an animation. 459 */ 460 void notifyAppVisibilityChanged() { 461 checkMinimizeChanged(false /* animate */); 462 } 463 464 void notifyAppTransitionStarting() { 465 checkMinimizeChanged(true /* animate */); 466 } 467 468 boolean isMinimizedDock() { 469 return mMinimizedDock; 470 } 471 472 private void checkMinimizeChanged(boolean animate) { 473 if (mDisplayContent.getDockedStackVisibleForUserLocked() == null) { 474 return; 475 } 476 final TaskStack homeStack = mDisplayContent.getHomeStack(); 477 if (homeStack == null) { 478 return; 479 } 480 final Task homeTask = homeStack.findHomeTask(); 481 if (homeTask == null || !isWithinDisplay(homeTask)) { 482 return; 483 } 484 final TaskStack fullscreenStack 485 = mService.mStackIdToStack.get(FULLSCREEN_WORKSPACE_STACK_ID); 486 final ArrayList<Task> homeStackTasks = homeStack.getTasks(); 487 final Task topHomeStackTask = homeStackTasks.get(homeStackTasks.size() - 1); 488 final boolean homeVisible = homeTask.getTopVisibleAppToken() != null; 489 final boolean homeBehind = (fullscreenStack != null && fullscreenStack.isVisibleLocked()) 490 || (homeStackTasks.size() > 1 && topHomeStackTask != homeTask); 491 setMinimizedDockedStack(homeVisible && !homeBehind, animate); 492 } 493 494 private boolean isWithinDisplay(Task task) { 495 task.mStack.getBounds(mTmpRect); 496 mDisplayContent.getLogicalDisplayRect(mTmpRect2); 497 return mTmpRect.intersect(mTmpRect2); 498 } 499 500 /** 501 * Sets whether the docked stack is currently in a minimized state, i.e. all the tasks in the 502 * docked stack are heavily clipped so you can only see a minimal peek state. 503 * 504 * @param minimizedDock Whether the docked stack is currently minimized. 505 * @param animate Whether to animate the change. 506 */ 507 private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) { 508 final boolean wasMinimized = mMinimizedDock; 509 mMinimizedDock = minimizedDock; 510 if (minimizedDock == wasMinimized) { 511 return; 512 } 513 514 clearImeAdjustAnimation(); 515 if (minimizedDock) { 516 if (animate) { 517 startAdjustAnimation(0f, 1f); 518 } else { 519 setMinimizedDockedStack(true); 520 } 521 } else { 522 if (animate) { 523 startAdjustAnimation(1f, 0f); 524 } else { 525 setMinimizedDockedStack(false); 526 } 527 } 528 } 529 530 private void clearImeAdjustAnimation() { 531 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks(); 532 for (int i = stacks.size() - 1; i >= 0; --i) { 533 final TaskStack stack = stacks.get(i); 534 if (stack != null && stack.isAdjustedForIme()) { 535 stack.resetAdjustedForIme(true /* adjustBoundsNow */); 536 } 537 } 538 mAnimatingForIme = false; 539 } 540 541 private void startAdjustAnimation(float from, float to) { 542 mAnimatingForMinimizedDockedStack = true; 543 mAnimationStarted = false; 544 mAnimationStart = from; 545 mAnimationTarget = to; 546 } 547 548 private void startImeAdjustAnimation( 549 boolean adjustedForIme, boolean adjustedForDivider, WindowState imeWin) { 550 mAnimatingForIme = true; 551 mAnimationStarted = false; 552 553 // If we're not in an animation, the starting point depends on whether we're adjusted 554 // or not. If we're already in an animation, we start from where the current animation 555 // left off, so that the motion doesn't look discontinuous. 556 if (!mAnimatingForIme) { 557 mAnimationStart = mAdjustedForIme ? 1 : 0; 558 mDividerAnimationStart = mAdjustedForDivider ? 1 : 0; 559 mLastAnimationProgress = mAnimationStart; 560 mLastDividerProgress = mDividerAnimationStart; 561 } else { 562 mAnimationStart = mLastAnimationProgress; 563 mDividerAnimationStart = mLastDividerProgress; 564 } 565 mAnimationTarget = adjustedForIme ? 1 : 0; 566 mDividerAnimationTarget = adjustedForDivider ? 1 : 0; 567 568 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks(); 569 for (int i = stacks.size() - 1; i >= 0; --i) { 570 final TaskStack stack = stacks.get(i); 571 if (stack.isVisibleLocked() && stack.isAdjustedForIme()) { 572 stack.beginImeAdjustAnimation(); 573 } 574 } 575 576 // We put all tasks into drag resizing mode - wait until all of them have completed the 577 // drag resizing switch. 578 if (!mService.mWaitingForDrawn.isEmpty()) { 579 mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT); 580 mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, 581 IME_ADJUST_DRAWN_TIMEOUT); 582 mAnimationStartDelayed = true; 583 if (imeWin != null) { 584 585 // There might be an old window delaying the animation start - clear it. 586 if (mDelayedImeWin != null) { 587 mDelayedImeWin.mWinAnimator.endDelayingAnimationStart(); 588 } 589 mDelayedImeWin = imeWin; 590 imeWin.mWinAnimator.startDelayingAnimationStart(); 591 } 592 mService.mWaitingForDrawnCallback = () -> { 593 mAnimationStartDelayed = false; 594 if (mDelayedImeWin != null) { 595 mDelayedImeWin.mWinAnimator.endDelayingAnimationStart(); 596 } 597 notifyAdjustedForImeChanged( 598 adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION); 599 }; 600 } else { 601 notifyAdjustedForImeChanged( 602 adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION); 603 } 604 } 605 606 private void setMinimizedDockedStack(boolean minimized) { 607 final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked(); 608 notifyDockedStackMinimizedChanged(minimized, 0); 609 setMinimizeAmount(stack, minimized ? 1f : 0f); 610 } 611 612 private boolean isAnimationMaximizing() { 613 return mAnimationTarget == 0f; 614 } 615 616 public boolean animate(long now) { 617 if (mWindow == null) { 618 return false; 619 } 620 if (mAnimatingForMinimizedDockedStack) { 621 return animateForMinimizedDockedStack(now); 622 } else if (mAnimatingForIme) { 623 return animateForIme(now); 624 } else { 625 if (mDimLayer != null && mDimLayer.isDimming()) { 626 mDimLayer.setLayer(mService.mLayersController.getResizeDimLayer()); 627 } 628 return false; 629 } 630 } 631 632 private boolean animateForIme(long now) { 633 if (!mAnimationStarted || mAnimationStartDelayed) { 634 mAnimationStarted = true; 635 mAnimationStartTime = now; 636 mAnimationDuration = (long) 637 (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked()); 638 } 639 float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration); 640 t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR) 641 .getInterpolation(t); 642 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks(); 643 boolean updated = false; 644 for (int i = stacks.size() - 1; i >= 0; --i) { 645 final TaskStack stack = stacks.get(i); 646 if (stack != null && stack.isAdjustedForIme()) { 647 if (t >= 1f && mAnimationTarget == 0f && mDividerAnimationTarget == 0f) { 648 stack.resetAdjustedForIme(true /* adjustBoundsNow */); 649 updated = true; 650 } else { 651 mLastAnimationProgress = getInterpolatedAnimationValue(t); 652 mLastDividerProgress = getInterpolatedDividerValue(t); 653 updated |= stack.updateAdjustForIme( 654 mLastAnimationProgress, 655 mLastDividerProgress, 656 false /* force */); 657 } 658 if (t >= 1f) { 659 stack.endImeAdjustAnimation(); 660 } 661 } 662 } 663 if (updated) { 664 mService.mWindowPlacerLocked.performSurfacePlacement(); 665 } 666 if (t >= 1.0f) { 667 mLastAnimationProgress = mAnimationTarget; 668 mLastDividerProgress = mDividerAnimationTarget; 669 mAnimatingForIme = false; 670 return false; 671 } else { 672 return true; 673 } 674 } 675 676 private boolean animateForMinimizedDockedStack(long now) { 677 final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID); 678 if (!mAnimationStarted) { 679 mAnimationStarted = true; 680 mAnimationStartTime = now; 681 final long transitionDuration = isAnimationMaximizing() 682 ? mService.mAppTransition.getLastClipRevealTransitionDuration() 683 : DEFAULT_APP_TRANSITION_DURATION; 684 mAnimationDuration = (long) 685 (transitionDuration * mService.getTransitionAnimationScaleLocked()); 686 mMaximizeMeetFraction = getClipRevealMeetFraction(stack); 687 notifyDockedStackMinimizedChanged(mMinimizedDock, 688 (long) (mAnimationDuration * mMaximizeMeetFraction)); 689 } 690 float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration); 691 t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator) 692 .getInterpolation(t); 693 setMinimizeAmount(stack, getMinimizeAmount(stack, t)); 694 695 if (t >= 1.0f) { 696 mAnimatingForMinimizedDockedStack = false; 697 return false; 698 } else { 699 return true; 700 } 701 } 702 703 void setMinimizeAmount(TaskStack dockedStack, float minimizeAmount) { 704 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks(); 705 706 // If the docked stack is not visible, clear the complementary stack on all stacks. 707 if (dockedStack == null) { 708 for (int i = stacks.size() - 1; i >= 0; --i) { 709 final TaskStack stack = stacks.get(i); 710 stack.resetAdjustedForComplementDock(); 711 } 712 return; 713 } 714 715 // Otherwise if the docked stack minimize amount has changed, update the adjusted bounds 716 // on the other stack that's currently visible, so that the stack's getDimBounds() 717 // occupies what's left by the docked stack. This is needed so that stuff like wallpaper 718 // gets cropped properly to the area left by the dock. 719 if (dockedStack.setAdjustedForMinimizedDock(minimizeAmount)) { 720 final boolean adjusted = 721 dockedStack.isVisibleForUserLocked() && minimizeAmount != 0.0f; 722 dockedStack.getDimBounds(mTmpRect2); 723 int dockSide = dockedStack.getDockSide(); 724 for (int i = stacks.size() - 1; i >= 0; --i) { 725 final TaskStack stack = stacks.get(i); 726 if (stack == dockedStack) { 727 continue; 728 } 729 if (stack.isVisibleLocked() && adjusted) { 730 stack.setAdjustedForComplementDock(mTmpRect2, dockSide); 731 } else { 732 stack.resetAdjustedForComplementDock(); 733 } 734 } 735 mService.mWindowPlacerLocked.performSurfacePlacement(); 736 } 737 } 738 739 private float getInterpolatedAnimationValue(float t) { 740 return t * mAnimationTarget + (1 - t) * mAnimationStart; 741 } 742 743 private float getInterpolatedDividerValue(float t) { 744 return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart; 745 } 746 747 /** 748 * Gets the amount how much to minimize a stack depending on the interpolated fraction t. 749 */ 750 private float getMinimizeAmount(TaskStack stack, float t) { 751 final float naturalAmount = getInterpolatedAnimationValue(t); 752 if (isAnimationMaximizing()) { 753 return adjustMaximizeAmount(stack, t, naturalAmount); 754 } else { 755 return naturalAmount; 756 } 757 } 758 759 /** 760 * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount 761 * during the transition such that the edge of the clip reveal rect is met earlier in the 762 * transition so we don't create a visible "hole", but only if both the clip reveal and the 763 * docked stack divider start from about the same portion on the screen. 764 */ 765 private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) { 766 if (mMaximizeMeetFraction == 1f) { 767 return naturalAmount; 768 } 769 final int minimizeDistance = stack.getMinimizeDistance(); 770 float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation() 771 / (float) minimizeDistance; 772 final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime; 773 final float t2 = Math.min(t / mMaximizeMeetFraction, 1); 774 return amountPrime * t2 + naturalAmount * (1 - t2); 775 } 776 777 /** 778 * Retrieves the animation fraction at which the docked stack has to meet the clip reveal 779 * edge. See {@link #adjustMaximizeAmount}. 780 */ 781 private float getClipRevealMeetFraction(TaskStack stack) { 782 if (!isAnimationMaximizing() || stack == null || 783 !mService.mAppTransition.hadClipRevealAnimation()) { 784 return 1f; 785 } 786 final int minimizeDistance = stack.getMinimizeDistance(); 787 final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation()) 788 / (float) minimizeDistance; 789 final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN) 790 / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN))); 791 return CLIP_REVEAL_MEET_EARLIEST 792 + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST); 793 } 794 795 @Override 796 public boolean dimFullscreen() { 797 return false; 798 } 799 800 @Override 801 public DisplayInfo getDisplayInfo() { 802 return mDisplayContent.getDisplayInfo(); 803 } 804 805 @Override 806 public void getDimBounds(Rect outBounds) { 807 // This dim layer user doesn't need this. 808 } 809 810 @Override 811 public String toShortString() { 812 return TAG; 813 } 814 815 WindowState getWindow() { 816 return mWindow; 817 } 818 819 void dump(String prefix, PrintWriter pw) { 820 pw.println(prefix + "DockedStackDividerController"); 821 pw.println(prefix + " mLastVisibility=" + mLastVisibility); 822 pw.println(prefix + " mMinimizedDock=" + mMinimizedDock); 823 pw.println(prefix + " mAdjustedForIme=" + mAdjustedForIme); 824 pw.println(prefix + " mAdjustedForDivider=" + mAdjustedForDivider); 825 if (mDimLayer.isDimming()) { 826 pw.println(prefix + " Dim layer is dimming: "); 827 mDimLayer.printTo(prefix + " ", pw); 828 } 829 } 830} 831