From ff71d20ff357a33c30e1e3b474e51ad47d5c5d3a Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Thu, 14 Apr 2016 13:12:36 -0700 Subject: [PATCH] Put windows into resizing during IME adjust animation Because the IME animates in with translucency there was a black hole visible at the bottom. This CL puts the window into resizing mode, waits until the change is commited, and then starts the animation Bug: 28175599 Change-Id: Ib31c1e765639e5490208bccba77b25318ec8dc71 --- .../wm/DockedStackDividerController.java | 66 ++++++++++++++++--- .../core/java/com/android/server/wm/Task.java | 26 ++++++++ .../java/com/android/server/wm/TaskStack.java | 38 +++++++++-- .../server/wm/WindowManagerService.java | 4 +- .../server/wm/WindowStateAnimator.java | 34 +++++++++- 5 files changed, 149 insertions(+), 19 deletions(-) diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 21c9ee67f86a0..3bd7a96182f30 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.DOCKED_RIGHT; import static android.view.WindowManager.DOCKED_TOP; import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION; import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR; +import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -41,6 +42,7 @@ import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; import com.android.server.wm.DimLayer.DimLayerUser; +import com.android.server.wm.WindowManagerService.H; import java.util.ArrayList; @@ -76,10 +78,12 @@ public class DockedStackDividerController implements DimLayerUser { private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f; private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR = - new PathInterpolator(0.1f, 0f, 0.1f, 1f); + new PathInterpolator(0.2f, 0f, 0.1f, 1f); private static final long IME_ADJUST_ANIM_DURATION = 280; + private static final long IME_ADJUST_DRAWN_TIMEOUT = 200; + private final WindowManagerService mService; private final DisplayContent mDisplayContent; private int mDividerWindowWidth; @@ -101,11 +105,13 @@ public class DockedStackDividerController implements DimLayerUser { private float mAnimationStart; private float mAnimationTarget; private long mAnimationDuration; + private boolean mAnimationStartDelayed; private final Interpolator mMinimizedDockInterpolator; private float mMaximizeMeetFraction; private final Rect mTouchRegion = new Rect(); private boolean mAnimatingForIme; private boolean mAdjustedForIme; + private WindowState mDelayedImeWin; DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) { mService = service; @@ -192,13 +198,16 @@ public class DockedStackDividerController implements DimLayerUser { return mLastVisibility; } - void setAdjustedForIme(boolean adjusted, boolean animate) { + void setAdjustedForIme(boolean adjusted, boolean animate, WindowState imeWin) { if (mAdjustedForIme != adjusted) { mAdjustedForIme = adjusted; if (animate) { - startImeAdjustAnimation(adjusted ? 0 : 1, adjusted ? 1 : 0); + startImeAdjustAnimation(adjusted, imeWin); + } else { + + // Animation might be delayed, so only notify if we don't run an animation. + notifyAdjustedForImeChanged(adjusted, 0 /* duration */); } - notifyAdjustedForImeChanged(adjusted, animate ? IME_ADJUST_ANIM_DURATION : 0); } } @@ -429,11 +438,46 @@ public class DockedStackDividerController implements DimLayerUser { mAnimationTarget = to; } - private void startImeAdjustAnimation(float from, float to) { + private void startImeAdjustAnimation(boolean adjusted, WindowState imeWin) { mAnimatingForIme = true; mAnimationStarted = false; - mAnimationStart = from; - mAnimationTarget = to; + mAnimationStart = adjusted ? 0 : 1; + mAnimationTarget = adjusted ? 1 : 0; + + final ArrayList stacks = mDisplayContent.getStacks(); + for (int i = stacks.size() - 1; i >= 0; --i) { + final TaskStack stack = stacks.get(i); + if (stack.isVisibleLocked() && stack.isAdjustedForIme()) { + stack.beginImeAdjustAnimation(); + } + } + + // We put all tasks into drag resizing mode - wait until all of them have completed the + // drag resizing switch. + if (!mService.mWaitingForDrawn.isEmpty()) { + mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT); + mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, + IME_ADJUST_DRAWN_TIMEOUT); + mAnimationStartDelayed = true; + if (imeWin != null) { + + // There might be an old window delaying the animation start - clear it. + if (mDelayedImeWin != null) { + mDelayedImeWin.mWinAnimator.endDelayingAnimationStart(); + } + mDelayedImeWin = imeWin; + imeWin.mWinAnimator.startDelayingAnimationStart(); + } + mService.mWaitingForDrawnCallback = () -> { + mAnimationStartDelayed = false; + if (mDelayedImeWin != null) { + mDelayedImeWin.mWinAnimator.endDelayingAnimationStart(); + } + notifyAdjustedForImeChanged(adjusted, IME_ADJUST_ANIM_DURATION); + }; + } else { + notifyAdjustedForImeChanged(adjusted, IME_ADJUST_ANIM_DURATION); + } } private void setMinimizedDockedStack(boolean minimized) { @@ -462,7 +506,7 @@ public class DockedStackDividerController implements DimLayerUser { } private boolean animateForIme(long now) { - if (!mAnimationStarted) { + if (!mAnimationStarted || mAnimationStartDelayed) { mAnimationStarted = true; mAnimationStartTime = now; mAnimationDuration = (long) @@ -480,7 +524,11 @@ public class DockedStackDividerController implements DimLayerUser { stack.resetAdjustedForIme(true /* adjustBoundsNow */); updated = true; } else { - updated |= stack.updateAdjustForIme(getInterpolatedAnimationValue(t)); + updated |= stack.updateAdjustForIme(getInterpolatedAnimationValue(t), + false /* force */); + } + if (t >= 1f) { + stack.endImeAdjustAnimation(); } } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 7b16dbec68a5f..13bfa5713a2fe 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -509,6 +509,22 @@ class Task implements DimLayer.DimLayerUser { return mDragResizeMode; } + /** + * Adds all of the tasks windows to {@link WindowManagerService#mWaitingForDrawn} if drag + * resizing state of the window has been changed. + */ + void addWindowsWaitingForDrawnIfResizingChanged() { + for (int activityNdx = mAppTokens.size() - 1; activityNdx >= 0; --activityNdx) { + final ArrayList windows = mAppTokens.get(activityNdx).allAppWindows; + for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) { + final WindowState win = windows.get(winNdx); + if (win.isDragResizeChanged()) { + mService.mWaitingForDrawn.add(win); + } + } + } + } + void updateDisplayInfo(final DisplayContent displayContent) { if (displayContent == null) { return; @@ -619,6 +635,16 @@ class Task implements DimLayer.DimLayerUser { return false; } + boolean isVisible() { + for (int i = mAppTokens.size() - 1; i >= 0; i--) { + final AppWindowToken appToken = mAppTokens.get(i); + if (appToken.isVisible()) { + return true; + } + } + return false; + } + boolean inHomeStack() { return mStack != null && mStack.mStackId == HOME_STACK_ID; } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 0a08a54e91894..c322cd829f1c1 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -26,6 +26,7 @@ import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_RIGHT; import static android.view.WindowManager.DOCKED_TOP; +import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK; @@ -833,10 +834,13 @@ public class TaskStack implements DimLayer.DimLayerUser, * @param imeWin The IME window. */ void setAdjustedForIme(WindowState imeWin) { - mAdjustedForIme = true; - mAdjustImeAmount = 0f; mImeWin = imeWin; mImeGoingAway = false; + if (!mAdjustedForIme) { + mAdjustedForIme = true; + mAdjustImeAmount = 0f; + updateAdjustForIme(0f, true /* force */); + } } boolean isAdjustedForIme() { @@ -855,8 +859,8 @@ public class TaskStack implements DimLayer.DimLayerUser, * * @return true if a traversal should be performed after the adjustment. */ - boolean updateAdjustForIme(float adjustAmount) { - if (adjustAmount != mAdjustImeAmount) { + boolean updateAdjustForIme(float adjustAmount, boolean force) { + if (adjustAmount != mAdjustImeAmount || force) { mAdjustImeAmount = adjustAmount; updateAdjustedBounds(); return isVisibleForUserLocked(); @@ -903,6 +907,29 @@ public class TaskStack implements DimLayer.DimLayerUser, return mMinimizeAmount != 0f; } + /** + * Puts all visible tasks that are adjusted for IME into resizing mode and adds the windows + * to the list of to be drawn windows the service is waiting for. + */ + void beginImeAdjustAnimation() { + for (int j = mTasks.size() - 1; j >= 0; j--) { + final Task task = mTasks.get(j); + if (task.isVisibleForUser()) { + task.setDragResizing(true, DRAG_RESIZE_MODE_DOCKED_DIVIDER); + task.addWindowsWaitingForDrawnIfResizingChanged(); + } + } + } + + /** + * Resets the resizing state of all windows. + */ + void endImeAdjustAnimation() { + for (int j = mTasks.size() - 1; j >= 0; j--) { + mTasks.get(j).setDragResizing(false, DRAG_RESIZE_MODE_DOCKED_DIVIDER); + } + } + private boolean adjustForIME(final WindowState imeWin) { final int dockedSide = getDockSide(); final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM; @@ -946,7 +973,6 @@ public class TaskStack implements DimLayer.DimLayerUser, mTmpAdjustedBounds.set(mBounds); mTmpAdjustedBounds.top = (int) (mAdjustImeAmount * top + (1 - mAdjustImeAmount) * mBounds.top); - mTmpAdjustedBounds.bottom = mTmpAdjustedBounds.top + mBounds.height(); mFullyAdjustedImeBounds.set(mBounds); mFullyAdjustedImeBounds.top = top; mFullyAdjustedImeBounds.bottom = top + mBounds.height(); @@ -1159,7 +1185,7 @@ public class TaskStack implements DimLayer.DimLayerUser, return mDragResizing; } - private void setDragResizingLocked(boolean resizing) { + void setDragResizingLocked(boolean resizing) { if (mDragResizing == resizing) { return; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index a4d5c9586ceb3..a4238c1a40175 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7459,14 +7459,14 @@ public class WindowManagerService extends IWindowManager.Stub stack.setAdjustedForIme(imeWin); } } - displayContent.mDividerControllerLocked.setAdjustedForIme(true, true); + displayContent.mDividerControllerLocked.setAdjustedForIme(true, true, imeWin); } else { final ArrayList stacks = displayContent.getStacks(); for (int i = stacks.size() - 1; i >= 0; --i) { final TaskStack stack = stacks.get(i); stack.resetAdjustedForIme(!dockVisible); } - displayContent.mDividerControllerLocked.setAdjustedForIme(false, dockVisible); + displayContent.mDividerControllerLocked.setAdjustedForIme(false, dockVisible, imeWin); } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index db8f9bd231a41..06c3a0244cffd 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -22,7 +22,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; import static android.view.WindowManager.LayoutParams.FLAG_SCALED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; -import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; +import static com.android.server.wm.AppWindowAnimator.sDummyAnimation; import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; @@ -183,6 +183,8 @@ class WindowStateAnimator { * window is first added or shown, cleared when the callback has been made. */ boolean mEnteringAnimation; + private boolean mAnimationStartDelayed; + boolean mKeyguardGoingAwayAnimation; boolean mKeyguardGoingAwayWithWallpaper; @@ -299,7 +301,7 @@ class WindowStateAnimator { /** Is the window animating the DummyAnimation? */ boolean isDummyAnimation() { return mAppAnimator != null - && mAppAnimator.animation == AppWindowAnimator.sDummyAnimation; + && mAppAnimator.animation == sDummyAnimation; } /** Is this window currently set to animate or currently animating? */ @@ -323,8 +325,12 @@ class WindowStateAnimator { if ((mAnimation == null) || !mLocalAnimating) { return false; } + currentTime = getAnimationFrameTime(mAnimation, currentTime); mTransformation.clear(); final boolean more = mAnimation.getTransformation(currentTime, mTransformation); + if (mAnimationStartDelayed && mAnimationIsEntrance) { + mTransformation.setAlpha(0f); + } if (false && DEBUG_ANIM) Slog.v(TAG, "Stepped animation in " + this + ": more=" + more + ", xform=" + mTransformation); return more; @@ -1841,6 +1847,9 @@ class WindowStateAnimator { pw.print(" mDsDy="); pw.print(mDsDy); pw.print(" mDtDy="); pw.println(mDtDy); } + if (mAnimationStartDelayed) { + pw.print(prefix); pw.print("mAnimationStartDelayed="); pw.print(mAnimationStartDelayed); + } } @Override @@ -1919,4 +1928,25 @@ class WindowStateAnimator { mDeferTransactionUntilFrame); } } + + /** + * Sometimes we need to synchronize the first frame of animation with some external event. + * To achieve this, we prolong the start of the animation and keep producing the first frame of + * the animation. + */ + private long getAnimationFrameTime(Animation animation, long currentTime) { + if (mAnimationStartDelayed) { + animation.setStartTime(currentTime); + return currentTime + 1; + } + return currentTime; + } + + void startDelayingAnimationStart() { + mAnimationStartDelayed = true; + } + + void endDelayingAnimationStart() { + mAnimationStartDelayed = false; + } }