2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server.wm;
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.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
33 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
34 import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
36 import android.content.Context;
37 import android.content.res.Configuration;
38 import android.graphics.Rect;
39 import android.os.RemoteCallbackList;
40 import android.os.RemoteException;
41 import android.util.Slog;
42 import android.view.DisplayInfo;
43 import android.view.IDockedStackListener;
44 import android.view.SurfaceControl;
45 import android.view.animation.AnimationUtils;
46 import android.view.animation.Interpolator;
47 import android.view.animation.PathInterpolator;
48 import android.view.inputmethod.InputMethodManagerInternal;
50 import com.android.internal.policy.DividerSnapAlgorithm;
51 import com.android.internal.policy.DockedDividerUtils;
52 import com.android.server.LocalServices;
53 import com.android.server.wm.DimLayer.DimLayerUser;
54 import com.android.server.wm.WindowManagerService.H;
56 import java.io.PrintWriter;
57 import java.util.ArrayList;
60 * Keeps information about the docked stack divider.
62 public class DockedStackDividerController implements DimLayerUser {
64 private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
67 * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
68 * revealing surface at the earliest.
70 private static final float CLIP_REVEAL_MEET_EARLIEST = 0.6f;
73 * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
74 * revealing surface at the latest.
76 private static final float CLIP_REVEAL_MEET_LAST = 1f;
79 * If the app translates at least CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, we start
80 * meet somewhere between {@link #CLIP_REVEAL_MEET_LAST} and {@link #CLIP_REVEAL_MEET_EARLIEST}.
82 private static final float CLIP_REVEAL_MEET_FRACTION_MIN = 0.4f;
85 * If the app translates equals or more than CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance,
86 * we meet at {@link #CLIP_REVEAL_MEET_EARLIEST}.
88 private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f;
90 private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR =
91 new PathInterpolator(0.2f, 0f, 0.1f, 1f);
93 private static final long IME_ADJUST_ANIM_DURATION = 280;
95 private static final long IME_ADJUST_DRAWN_TIMEOUT = 200;
97 private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
99 private final WindowManagerService mService;
100 private final DisplayContent mDisplayContent;
101 private int mDividerWindowWidth;
102 private int mDividerWindowWidthInactive;
103 private int mDividerInsets;
104 private boolean mResizing;
105 private WindowState mWindow;
106 private final Rect mTmpRect = new Rect();
107 private final Rect mTmpRect2 = new Rect();
108 private final Rect mTmpRect3 = new Rect();
109 private final Rect mLastRect = new Rect();
110 private boolean mLastVisibility = false;
111 private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
112 = new RemoteCallbackList<>();
113 private final DimLayer mDimLayer;
115 private boolean mMinimizedDock;
116 private boolean mAnimatingForMinimizedDockedStack;
117 private boolean mAnimationStarted;
118 private long mAnimationStartTime;
119 private float mAnimationStart;
120 private float mAnimationTarget;
121 private long mAnimationDuration;
122 private boolean mAnimationStartDelayed;
123 private final Interpolator mMinimizedDockInterpolator;
124 private float mMaximizeMeetFraction;
125 private final Rect mTouchRegion = new Rect();
126 private boolean mAnimatingForIme;
127 private boolean mAdjustedForIme;
128 private int mImeHeight;
129 private WindowState mDelayedImeWin;
130 private boolean mAdjustedForDivider;
131 private float mDividerAnimationStart;
132 private float mDividerAnimationTarget;
133 private float mLastAnimationProgress;
134 private float mLastDividerProgress;
135 private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4];
136 private boolean mImeHideRequested;
138 DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
140 mDisplayContent = displayContent;
141 final Context context = service.mContext;
142 mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
144 mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
145 context, android.R.interpolator.fast_out_slow_in);
149 int getSmallestWidthDpForBounds(Rect bounds) {
150 final DisplayInfo di = mDisplayContent.getDisplayInfo();
152 // If the bounds are fullscreen, return the value of the fullscreen configuration
153 if (bounds == null || (bounds.left == 0 && bounds.top == 0
154 && bounds.right == di.logicalWidth && bounds.bottom == di.logicalHeight)) {
155 return mService.mCurConfiguration.smallestScreenWidthDp;
157 final int baseDisplayWidth = mDisplayContent.mBaseDisplayWidth;
158 final int baseDisplayHeight = mDisplayContent.mBaseDisplayHeight;
159 int minWidth = Integer.MAX_VALUE;
161 // Go through all screen orientations and find the orientation in which the task has the
163 for (int rotation = 0; rotation < 4; rotation++) {
164 mTmpRect.set(bounds);
165 mDisplayContent.rotateBounds(di.rotation, rotation, mTmpRect);
166 final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
168 rotated ? baseDisplayHeight : baseDisplayWidth,
169 rotated ? baseDisplayWidth : baseDisplayHeight);
170 final int orientation = mTmpRect2.width() <= mTmpRect2.height()
171 ? ORIENTATION_PORTRAIT
172 : ORIENTATION_LANDSCAPE;
173 final int dockSide = TaskStack.getDockSideUnchecked(mTmpRect, mTmpRect2, orientation);
174 final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide,
177 // Since we only care about feasible states, snap to the closest snap target, like it
178 // would happen when actually rotating the screen.
179 final int snappedPosition = mSnapAlgorithmForRotation[rotation]
180 .calculateNonDismissingSnapTarget(position).position;
181 DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, mTmpRect,
182 mTmpRect2.width(), mTmpRect2.height(), getContentWidth());
183 mService.mPolicy.getStableInsetsLw(rotation, mTmpRect2.width(), mTmpRect2.height(),
185 mService.subtractInsets(mTmpRect2, mTmpRect3, mTmpRect);
186 minWidth = Math.min(mTmpRect.width(), minWidth);
188 return (int) (minWidth / mDisplayContent.getDisplayMetrics().density);
191 private void initSnapAlgorithmForRotations() {
192 final Configuration baseConfig = mService.mCurConfiguration;
194 // Initialize the snap algorithms for all 4 screen orientations.
195 final Configuration config = new Configuration();
196 for (int rotation = 0; rotation < 4; rotation++) {
197 final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
198 final int dw = rotated
199 ? mDisplayContent.mBaseDisplayHeight
200 : mDisplayContent.mBaseDisplayWidth;
201 final int dh = rotated
202 ? mDisplayContent.mBaseDisplayWidth
203 : mDisplayContent.mBaseDisplayHeight;
204 mService.mPolicy.getStableInsetsLw(rotation, dw, dh, mTmpRect);
205 config.setToDefaults();
206 config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
207 config.screenWidthDp = (int)
208 (mService.mPolicy.getConfigDisplayWidth(dw, dh, rotation, baseConfig.uiMode) /
209 mDisplayContent.getDisplayMetrics().density);
210 config.screenHeightDp = (int)
211 (mService.mPolicy.getConfigDisplayHeight(dw, dh, rotation, baseConfig.uiMode) /
212 mDisplayContent.getDisplayMetrics().density);
213 final Context rotationContext = mService.mContext.createConfigurationContext(config);
214 mSnapAlgorithmForRotation[rotation] = new DividerSnapAlgorithm(
215 rotationContext.getResources(), dw, dh, getContentWidth(),
216 config.orientation == ORIENTATION_PORTRAIT, mTmpRect);
220 private void loadDimens() {
221 final Context context = mService.mContext;
222 mDividerWindowWidth = context.getResources().getDimensionPixelSize(
223 com.android.internal.R.dimen.docked_stack_divider_thickness);
224 mDividerInsets = context.getResources().getDimensionPixelSize(
225 com.android.internal.R.dimen.docked_stack_divider_insets);
226 mDividerWindowWidthInactive = WindowManagerService.dipToPixel(
227 DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics());
228 initSnapAlgorithmForRotations();
231 void onConfigurationChanged() {
235 boolean isResizing() {
239 int getContentWidth() {
240 return mDividerWindowWidth - 2 * mDividerInsets;
243 int getContentInsets() {
244 return mDividerInsets;
247 int getContentWidthInactive() {
248 return mDividerWindowWidthInactive;
251 void setResizing(boolean resizing) {
252 if (mResizing != resizing) {
253 mResizing = resizing;
254 resetDragResizingChangeReported();
258 void setTouchRegion(Rect touchRegion) {
259 mTouchRegion.set(touchRegion);
262 void getTouchRegion(Rect outRegion) {
263 outRegion.set(mTouchRegion);
264 outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
267 private void resetDragResizingChangeReported() {
268 final WindowList windowList = mDisplayContent.getWindowList();
269 for (int i = windowList.size() - 1; i >= 0; i--) {
270 windowList.get(i).resetDragResizingChangeReported();
274 void setWindow(WindowState window) {
276 reevaluateVisibility(false);
279 void reevaluateVisibility(boolean force) {
280 if (mWindow == null) {
283 TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID);
285 // If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide
286 final boolean visible = stack != null;
287 if (mLastVisibility == visible && !force) {
290 mLastVisibility = visible;
291 notifyDockedDividerVisibilityChanged(visible);
293 setResizeDimLayer(false, INVALID_STACK_ID, 0f);
297 boolean wasVisible() {
298 return mLastVisibility;
301 void setAdjustedForIme(
302 boolean adjustedForIme, boolean adjustedForDivider,
303 boolean animate, WindowState imeWin, int imeHeight) {
304 if (mAdjustedForIme != adjustedForIme || (adjustedForIme && mImeHeight != imeHeight)
305 || mAdjustedForDivider != adjustedForDivider) {
306 if (animate && !mAnimatingForMinimizedDockedStack) {
307 startImeAdjustAnimation(adjustedForIme, adjustedForDivider, imeWin);
309 // Animation might be delayed, so only notify if we don't run an animation.
310 notifyAdjustedForImeChanged(adjustedForIme || adjustedForDivider, 0 /* duration */);
312 mAdjustedForIme = adjustedForIme;
313 mImeHeight = imeHeight;
314 mAdjustedForDivider = adjustedForDivider;
318 int getImeHeightAdjustedFor() {
322 void positionDockedStackedDivider(Rect frame) {
323 TaskStack stack = mDisplayContent.getDockedStackLocked();
325 // Unfortunately we might end up with still having a divider, even though the underlying
326 // stack was already removed. This is because we are on AM thread and the removal of the
327 // divider was deferred to WM thread and hasn't happened yet. In that case let's just
328 // keep putting it in the same place it was before the stack was removed to have
329 // continuity and prevent it from jumping to the center. It will get hidden soon.
330 frame.set(mLastRect);
333 stack.getDimBounds(mTmpRect);
335 int side = stack.getDockSide();
338 frame.set(mTmpRect.right - mDividerInsets, frame.top,
339 mTmpRect.right + frame.width() - mDividerInsets, frame.bottom);
342 frame.set(frame.left, mTmpRect.bottom - mDividerInsets,
343 mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets);
346 frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top,
347 mTmpRect.left + mDividerInsets, frame.bottom);
350 frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets,
351 frame.right, mTmpRect.top + mDividerInsets);
354 mLastRect.set(frame);
357 void notifyDockedDividerVisibilityChanged(boolean visible) {
358 final int size = mDockedStackListeners.beginBroadcast();
359 for (int i = 0; i < size; ++i) {
360 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
362 listener.onDividerVisibilityChanged(visible);
363 } catch (RemoteException e) {
364 Slog.e(TAG_WM, "Error delivering divider visibility changed event.", e);
367 mDockedStackListeners.finishBroadcast();
370 void notifyDockedStackExistsChanged(boolean exists) {
371 final int size = mDockedStackListeners.beginBroadcast();
372 for (int i = 0; i < size; ++i) {
373 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
375 listener.onDockedStackExistsChanged(exists);
376 } catch (RemoteException e) {
377 Slog.e(TAG_WM, "Error delivering docked stack exists changed event.", e);
380 mDockedStackListeners.finishBroadcast();
382 InputMethodManagerInternal inputMethodManagerInternal =
383 LocalServices.getService(InputMethodManagerInternal.class);
384 if (inputMethodManagerInternal != null) {
386 // Hide the current IME to avoid problems with animations from IME adjustment when
387 // attaching the docked stack.
388 inputMethodManagerInternal.hideCurrentInputMethod();
389 mImeHideRequested = true;
392 setMinimizedDockedStack(false);
397 * Resets the state that IME hide has been requested. See {@link #isImeHideRequested}.
399 void resetImeHideRequested() {
400 mImeHideRequested = false;
404 * The docked stack divider controller makes sure the IME gets hidden when attaching the docked
405 * stack, to avoid animation problems. This flag indicates whether the request to hide the IME
406 * has been sent in an asynchronous manner, and the IME should be treated as hidden already.
408 * @return whether IME hide request has been sent
410 boolean isImeHideRequested() {
411 return mImeHideRequested;
414 void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
415 mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
416 mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
417 minimizedDock ? 1 : 0, 0).sendToTarget();
418 final int size = mDockedStackListeners.beginBroadcast();
419 for (int i = 0; i < size; ++i) {
420 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
422 listener.onDockedStackMinimizedChanged(minimizedDock, animDuration);
423 } catch (RemoteException e) {
424 Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e);
427 mDockedStackListeners.finishBroadcast();
430 void notifyDockSideChanged(int newDockSide) {
431 final int size = mDockedStackListeners.beginBroadcast();
432 for (int i = 0; i < size; ++i) {
433 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
435 listener.onDockSideChanged(newDockSide);
436 } catch (RemoteException e) {
437 Slog.e(TAG_WM, "Error delivering dock side changed event.", e);
440 mDockedStackListeners.finishBroadcast();
443 void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {
444 final int size = mDockedStackListeners.beginBroadcast();
445 for (int i = 0; i < size; ++i) {
446 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
448 listener.onAdjustedForImeChanged(adjustedForIme, animDuration);
449 } catch (RemoteException e) {
450 Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e);
453 mDockedStackListeners.finishBroadcast();
456 void registerDockedStackListener(IDockedStackListener listener) {
457 mDockedStackListeners.register(listener);
458 notifyDockedDividerVisibilityChanged(wasVisible());
459 notifyDockedStackExistsChanged(
460 mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null);
461 notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */);
462 notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
466 void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
467 SurfaceControl.openTransaction();
468 final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
469 final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
470 boolean visibleAndValid = visible && stack != null && dockedStack != null;
471 if (visibleAndValid) {
472 stack.getDimBounds(mTmpRect);
473 if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
474 mDimLayer.setBounds(mTmpRect);
475 mDimLayer.show(mService.mLayersController.getResizeDimLayer(),
476 alpha, 0 /* duration */);
478 visibleAndValid = false;
481 if (!visibleAndValid) {
484 SurfaceControl.closeTransaction();
488 * Notifies the docked stack divider controller of a visibility change that happens without
491 void notifyAppVisibilityChanged() {
492 checkMinimizeChanged(false /* animate */);
495 void notifyAppTransitionStarting() {
496 checkMinimizeChanged(true /* animate */);
499 boolean isMinimizedDock() {
500 return mMinimizedDock;
503 private void checkMinimizeChanged(boolean animate) {
504 if (mDisplayContent.getDockedStackVisibleForUserLocked() == null) {
507 final TaskStack homeStack = mDisplayContent.getHomeStack();
508 if (homeStack == null) {
511 final Task homeTask = homeStack.findHomeTask();
512 if (homeTask == null || !isWithinDisplay(homeTask)) {
515 final TaskStack fullscreenStack
516 = mService.mStackIdToStack.get(FULLSCREEN_WORKSPACE_STACK_ID);
517 final ArrayList<Task> homeStackTasks = homeStack.getTasks();
518 final Task topHomeStackTask = homeStackTasks.get(homeStackTasks.size() - 1);
519 final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
520 final boolean homeBehind = (fullscreenStack != null && fullscreenStack.isVisibleLocked())
521 || (homeStackTasks.size() > 1 && topHomeStackTask != homeTask);
522 setMinimizedDockedStack(homeVisible && !homeBehind, animate);
525 private boolean isWithinDisplay(Task task) {
526 task.mStack.getBounds(mTmpRect);
527 mDisplayContent.getLogicalDisplayRect(mTmpRect2);
528 return mTmpRect.intersect(mTmpRect2);
532 * Sets whether the docked stack is currently in a minimized state, i.e. all the tasks in the
533 * docked stack are heavily clipped so you can only see a minimal peek state.
535 * @param minimizedDock Whether the docked stack is currently minimized.
536 * @param animate Whether to animate the change.
538 private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
539 final boolean wasMinimized = mMinimizedDock;
540 mMinimizedDock = minimizedDock;
541 if (minimizedDock == wasMinimized) {
545 clearImeAdjustAnimation();
548 startAdjustAnimation(0f, 1f);
550 setMinimizedDockedStack(true);
554 startAdjustAnimation(1f, 0f);
556 setMinimizedDockedStack(false);
561 private void clearImeAdjustAnimation() {
562 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
563 for (int i = stacks.size() - 1; i >= 0; --i) {
564 final TaskStack stack = stacks.get(i);
565 if (stack != null && stack.isAdjustedForIme()) {
566 stack.resetAdjustedForIme(true /* adjustBoundsNow */);
569 mAnimatingForIme = false;
572 private void startAdjustAnimation(float from, float to) {
573 mAnimatingForMinimizedDockedStack = true;
574 mAnimationStarted = false;
575 mAnimationStart = from;
576 mAnimationTarget = to;
579 private void startImeAdjustAnimation(
580 boolean adjustedForIme, boolean adjustedForDivider, WindowState imeWin) {
582 // If we're not in an animation, the starting point depends on whether we're adjusted
583 // or not. If we're already in an animation, we start from where the current animation
584 // left off, so that the motion doesn't look discontinuous.
585 if (!mAnimatingForIme) {
586 mAnimationStart = mAdjustedForIme ? 1 : 0;
587 mDividerAnimationStart = mAdjustedForDivider ? 1 : 0;
588 mLastAnimationProgress = mAnimationStart;
589 mLastDividerProgress = mDividerAnimationStart;
591 mAnimationStart = mLastAnimationProgress;
592 mDividerAnimationStart = mLastDividerProgress;
594 mAnimatingForIme = true;
595 mAnimationStarted = false;
596 mAnimationTarget = adjustedForIme ? 1 : 0;
597 mDividerAnimationTarget = adjustedForDivider ? 1 : 0;
599 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
600 for (int i = stacks.size() - 1; i >= 0; --i) {
601 final TaskStack stack = stacks.get(i);
602 if (stack.isVisibleLocked() && stack.isAdjustedForIme()) {
603 stack.beginImeAdjustAnimation();
607 // We put all tasks into drag resizing mode - wait until all of them have completed the
608 // drag resizing switch.
609 if (!mService.mWaitingForDrawn.isEmpty()) {
610 mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
611 mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT,
612 IME_ADJUST_DRAWN_TIMEOUT);
613 mAnimationStartDelayed = true;
614 if (imeWin != null) {
616 // There might be an old window delaying the animation start - clear it.
617 if (mDelayedImeWin != null) {
618 mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
620 mDelayedImeWin = imeWin;
621 imeWin.mWinAnimator.startDelayingAnimationStart();
623 mService.mWaitingForDrawnCallback = () -> {
624 mAnimationStartDelayed = false;
625 if (mDelayedImeWin != null) {
626 mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
628 notifyAdjustedForImeChanged(
629 adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
632 notifyAdjustedForImeChanged(
633 adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
637 private void setMinimizedDockedStack(boolean minimized) {
638 final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
639 notifyDockedStackMinimizedChanged(minimized, 0);
643 if (stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f)) {
644 mService.mWindowPlacerLocked.performSurfacePlacement();
648 private boolean isAnimationMaximizing() {
649 return mAnimationTarget == 0f;
652 public boolean animate(long now) {
653 if (mWindow == null) {
656 if (mAnimatingForMinimizedDockedStack) {
657 return animateForMinimizedDockedStack(now);
658 } else if (mAnimatingForIme) {
659 return animateForIme(now);
661 if (mDimLayer != null && mDimLayer.isDimming()) {
662 mDimLayer.setLayer(mService.mLayersController.getResizeDimLayer());
668 private boolean animateForIme(long now) {
669 if (!mAnimationStarted || mAnimationStartDelayed) {
670 mAnimationStarted = true;
671 mAnimationStartTime = now;
672 mAnimationDuration = (long)
673 (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked());
675 float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
676 t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
677 .getInterpolation(t);
678 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
679 boolean updated = false;
680 for (int i = stacks.size() - 1; i >= 0; --i) {
681 final TaskStack stack = stacks.get(i);
682 if (stack != null && stack.isAdjustedForIme()) {
683 if (t >= 1f && mAnimationTarget == 0f && mDividerAnimationTarget == 0f) {
684 stack.resetAdjustedForIme(true /* adjustBoundsNow */);
687 mLastAnimationProgress = getInterpolatedAnimationValue(t);
688 mLastDividerProgress = getInterpolatedDividerValue(t);
689 updated |= stack.updateAdjustForIme(
690 mLastAnimationProgress,
691 mLastDividerProgress,
695 stack.endImeAdjustAnimation();
700 mService.mWindowPlacerLocked.performSurfacePlacement();
703 mLastAnimationProgress = mAnimationTarget;
704 mLastDividerProgress = mDividerAnimationTarget;
705 mAnimatingForIme = false;
712 private boolean animateForMinimizedDockedStack(long now) {
713 final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
714 if (!mAnimationStarted) {
715 mAnimationStarted = true;
716 mAnimationStartTime = now;
717 final long transitionDuration = isAnimationMaximizing()
718 ? mService.mAppTransition.getLastClipRevealTransitionDuration()
719 : DEFAULT_APP_TRANSITION_DURATION;
720 mAnimationDuration = (long)
721 (transitionDuration * mService.getTransitionAnimationScaleLocked());
722 mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
723 notifyDockedStackMinimizedChanged(mMinimizedDock,
724 (long) (mAnimationDuration * mMaximizeMeetFraction));
726 float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
727 t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
728 .getInterpolation(t);
730 if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) {
731 mService.mWindowPlacerLocked.performSurfacePlacement();
735 mAnimatingForMinimizedDockedStack = false;
742 private float getInterpolatedAnimationValue(float t) {
743 return t * mAnimationTarget + (1 - t) * mAnimationStart;
746 private float getInterpolatedDividerValue(float t) {
747 return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart;
751 * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
753 private float getMinimizeAmount(TaskStack stack, float t) {
754 final float naturalAmount = getInterpolatedAnimationValue(t);
755 if (isAnimationMaximizing()) {
756 return adjustMaximizeAmount(stack, t, naturalAmount);
758 return naturalAmount;
763 * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount
764 * during the transition such that the edge of the clip reveal rect is met earlier in the
765 * transition so we don't create a visible "hole", but only if both the clip reveal and the
766 * docked stack divider start from about the same portion on the screen.
768 private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) {
769 if (mMaximizeMeetFraction == 1f) {
770 return naturalAmount;
772 final int minimizeDistance = stack.getMinimizeDistance();
773 float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation()
774 / (float) minimizeDistance;
775 final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime;
776 final float t2 = Math.min(t / mMaximizeMeetFraction, 1);
777 return amountPrime * t2 + naturalAmount * (1 - t2);
781 * Retrieves the animation fraction at which the docked stack has to meet the clip reveal
782 * edge. See {@link #adjustMaximizeAmount}.
784 private float getClipRevealMeetFraction(TaskStack stack) {
785 if (!isAnimationMaximizing() || stack == null ||
786 !mService.mAppTransition.hadClipRevealAnimation()) {
789 final int minimizeDistance = stack.getMinimizeDistance();
790 final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation())
791 / (float) minimizeDistance;
792 final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN)
793 / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN)));
794 return CLIP_REVEAL_MEET_EARLIEST
795 + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
799 public boolean dimFullscreen() {
804 public DisplayInfo getDisplayInfo() {
805 return mDisplayContent.getDisplayInfo();
809 public void getDimBounds(Rect outBounds) {
810 // This dim layer user doesn't need this.
814 public String toShortString() {
818 WindowState getWindow() {
822 void dump(String prefix, PrintWriter pw) {
823 pw.println(prefix + "DockedStackDividerController");
824 pw.println(prefix + " mLastVisibility=" + mLastVisibility);
825 pw.println(prefix + " mMinimizedDock=" + mMinimizedDock);
826 pw.println(prefix + " mAdjustedForIme=" + mAdjustedForIme);
827 pw.println(prefix + " mAdjustedForDivider=" + mAdjustedForDivider);
828 if (mDimLayer.isDimming()) {
829 pw.println(prefix + " Dim layer is dimming: ");
830 mDimLayer.printTo(prefix + " ", pw);