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