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