From f347ab5eddd63dbcbb4a70a94ec0916b3db46569 Mon Sep 17 00:00:00 2001 From: Chong Zhang Date: Mon, 18 Apr 2016 21:02:01 -0700 Subject: [PATCH] Fix IME adjust when stack focus changes while IME is visible - Use two values to animate divider width adjustment separately from stack position adjustment. For example IME is visible with focus on bottom stack, then user clicks top stack. In this case bottom stack position should go back to unadjusted, but divider should remain thinner. - If we need to start a new animation during an existing animation, start the motion from where the existing animation left off, so that it doesn't look discontinuous. - Do not adjust if IME is not actually focused on any stack. This could happen when swiping down the status bar. bug: 28175599 Change-Id: I802def5d1c13ebe11094eb28fc5a0b0c309e4d76 --- .../wm/DockedStackDividerController.java | 59 +++++++++++++++---- .../java/com/android/server/wm/TaskStack.java | 54 +++++++++-------- .../server/wm/WindowManagerService.java | 43 +++++++------- 3 files changed, 98 insertions(+), 58 deletions(-) diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index b90d0d191a784..8a003deb9db48 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -116,6 +116,11 @@ public class DockedStackDividerController implements DimLayerUser { private boolean mAnimatingForIme; private boolean mAdjustedForIme; private WindowState mDelayedImeWin; + private boolean mAdjustedForDivider; + private float mDividerAnimationStart; + private float mDividerAnimationTarget; + private float mLastAnimationProgress; + private float mLastDividerProgress; DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) { mService = service; @@ -208,16 +213,18 @@ public class DockedStackDividerController implements DimLayerUser { return mLastVisibility; } - void setAdjustedForIme(boolean adjusted, boolean animate, WindowState imeWin) { - if (mAdjustedForIme != adjusted) { - mAdjustedForIme = adjusted; + void setAdjustedForIme( + boolean adjustedForIme, boolean adjustedForDivider, + boolean animate, WindowState imeWin) { + if (mAdjustedForIme != adjustedForIme || mAdjustedForDivider != adjustedForDivider) { if (animate) { - startImeAdjustAnimation(adjusted, imeWin); + startImeAdjustAnimation(adjustedForIme, adjustedForDivider, imeWin); } else { - // Animation might be delayed, so only notify if we don't run an animation. - notifyAdjustedForImeChanged(adjusted, 0 /* duration */); + notifyAdjustedForImeChanged(adjustedForIme || adjustedForDivider, 0 /* duration */); } + mAdjustedForIme = adjustedForIme; + mAdjustedForDivider = adjustedForDivider; } } @@ -457,11 +464,25 @@ public class DockedStackDividerController implements DimLayerUser { mAnimationTarget = to; } - private void startImeAdjustAnimation(boolean adjusted, WindowState imeWin) { + private void startImeAdjustAnimation( + boolean adjustedForIme, boolean adjustedForDivider, WindowState imeWin) { mAnimatingForIme = true; mAnimationStarted = false; - mAnimationStart = adjusted ? 0 : 1; - mAnimationTarget = adjusted ? 1 : 0; + + // If we're not in an animation, the starting point depends on whether we're adjusted + // or not. If we're already in an animation, we start from where the current animation + // left off, so that the motion doesn't look discontinuous. + if (!mAnimatingForIme) { + mAnimationStart = mAdjustedForIme ? 1 : 0; + mDividerAnimationStart = mAdjustedForDivider ? 1 : 0; + mLastAnimationProgress = mAnimationStart; + mLastDividerProgress = mDividerAnimationStart; + } else { + mAnimationStart = mLastAnimationProgress; + mDividerAnimationStart = mLastDividerProgress; + } + mAnimationTarget = adjustedForIme ? 1 : 0; + mDividerAnimationTarget = adjustedForDivider ? 1 : 0; final ArrayList stacks = mDisplayContent.getStacks(); for (int i = stacks.size() - 1; i >= 0; --i) { @@ -492,10 +513,12 @@ public class DockedStackDividerController implements DimLayerUser { if (mDelayedImeWin != null) { mDelayedImeWin.mWinAnimator.endDelayingAnimationStart(); } - notifyAdjustedForImeChanged(adjusted, IME_ADJUST_ANIM_DURATION); + notifyAdjustedForImeChanged( + adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION); }; } else { - notifyAdjustedForImeChanged(adjusted, IME_ADJUST_ANIM_DURATION); + notifyAdjustedForImeChanged( + adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION); } } @@ -539,11 +562,15 @@ public class DockedStackDividerController implements DimLayerUser { for (int i = stacks.size() - 1; i >= 0; --i) { final TaskStack stack = stacks.get(i); if (stack != null && stack.isAdjustedForIme()) { - if (t >= 1f && mAnimationTarget == 0f) { + if (t >= 1f && mAnimationTarget == 0f && mDividerAnimationTarget == 0f) { stack.resetAdjustedForIme(true /* adjustBoundsNow */); updated = true; } else { - updated |= stack.updateAdjustForIme(getInterpolatedAnimationValue(t), + mLastAnimationProgress = getInterpolatedAnimationValue(t); + mLastDividerProgress = getInterpolatedDividerValue(t); + updated |= stack.updateAdjustForIme( + mLastAnimationProgress, + mLastDividerProgress, false /* force */); } if (t >= 1f) { @@ -555,6 +582,8 @@ public class DockedStackDividerController implements DimLayerUser { mService.mWindowPlacerLocked.performSurfacePlacement(); } if (t >= 1.0f) { + mLastAnimationProgress = mAnimationTarget; + mLastDividerProgress = mDividerAnimationTarget; mAnimatingForIme = false; return false; } else { @@ -596,6 +625,10 @@ public class DockedStackDividerController implements DimLayerUser { return t * mAnimationTarget + (1 - t) * mAnimationStart; } + private float getInterpolatedDividerValue(float t) { + return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart; + } + /** * Gets the amount how much to minimize a stack depending on the interpolated fraction t. */ diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 1fd2b1f2dfa3e..a289855586c46 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -126,6 +126,7 @@ public class TaskStack implements DimLayer.DimLayerUser, private WindowState mImeWin; private float mMinimizeAmount; private float mAdjustImeAmount; + private float mAdjustDividerAmount; private final int mDockedStackMinimizeThickness; // If this is true, we are in the bounds animating mode. @@ -853,7 +854,8 @@ public class TaskStack implements DimLayer.DimLayerUser, if (!mAdjustedForIme) { mAdjustedForIme = true; mAdjustImeAmount = 0f; - updateAdjustForIme(0f, true /* force */); + mAdjustDividerAmount = 0f; + updateAdjustForIme(0f, 0f, true /* force */); } } @@ -873,9 +875,11 @@ public class TaskStack implements DimLayer.DimLayerUser, * * @return true if a traversal should be performed after the adjustment. */ - boolean updateAdjustForIme(float adjustAmount, boolean force) { - if (adjustAmount != mAdjustImeAmount || force) { + boolean updateAdjustForIme(float adjustAmount, float adjustDividerAmount, boolean force) { + if (adjustAmount != mAdjustImeAmount + || adjustDividerAmount != mAdjustDividerAmount || force) { mAdjustImeAmount = adjustAmount; + mAdjustDividerAmount = adjustDividerAmount; updateAdjustedBounds(); return isVisibleForUserLocked(); } else { @@ -895,6 +899,7 @@ public class TaskStack implements DimLayer.DimLayerUser, mAdjustedForIme = false; mImeGoingAway = false; mAdjustImeAmount = 0f; + mAdjustDividerAmount = 0f; updateAdjustedBounds(); mService.setResizeDimLayer(false, mStackId, 1.0f); } else { @@ -992,25 +997,27 @@ public class TaskStack implements DimLayer.DimLayerUser, (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom); mFullyAdjustedImeBounds.set(mBounds); } else { - final int top; - final boolean isFocusedStack = mService.getFocusedStackLocked() == this; - if (isFocusedStack) { - // If this stack is docked on bottom and has focus, we shift it up so that it's not - // occluded by IME. We try to move it up by the height of the IME window, but only - // to the extent that leaves at least 30% of the top stack visible. - final int minTopStackBottom = - getMinTopStackBottom(displayContentRect, mBounds.top - dividerWidth); - top = Math.max( - mBounds.top - yOffset, minTopStackBottom + dividerWidthInactive); - } else { - // If this stack is docked on bottom but doesn't have focus, we don't need to adjust - // for IME, but still need to apply a small adjustment due to the thinner divider. - top = mBounds.top - dividerWidth + dividerWidthInactive; - } + // When the stack is on bottom and has no focus, it's only adjusted for divider width. + final int dividerWidthDelta = dividerWidthInactive - dividerWidth; + + // When the stack is on bottom and has focus, it needs to be moved up so as to + // not occluded by IME, and at the same time adjusted for divider width. + // We try to move it up by the height of the IME window, but only to the extent + // that leaves at least 30% of the top stack visible. + // 'top' is where the top of bottom stack will move to in this case. + final int topBeforeImeAdjust = mBounds.top - dividerWidth + dividerWidthInactive; + final int minTopStackBottom = + getMinTopStackBottom(displayContentRect, mBounds.top - dividerWidth); + final int top = Math.max( + mBounds.top - yOffset, minTopStackBottom + dividerWidthInactive); mTmpAdjustedBounds.set(mBounds); - mTmpAdjustedBounds.top = - (int) (mAdjustImeAmount * top + (1 - mAdjustImeAmount) * mBounds.top); + // Account for the adjustment for IME and divider width separately. + // (top - topBeforeImeAdjust) is the amount of movement due to IME only, + // and dividerWidthDelta is due to divider width change only. + mTmpAdjustedBounds.top = mBounds.top + + (int) (mAdjustImeAmount * (top - topBeforeImeAdjust) + + mAdjustDividerAmount * dividerWidthDelta); mFullyAdjustedImeBounds.set(mBounds); mFullyAdjustedImeBounds.top = top; mFullyAdjustedImeBounds.bottom = top + mBounds.height(); @@ -1082,9 +1089,10 @@ public class TaskStack implements DimLayer.DimLayerUser, } setAdjustedBounds(mTmpAdjustedBounds); - final boolean isFocusedStack = mService.getFocusedStackLocked() == this; - if (mAdjustedForIme && adjust && !isFocusedStack) { - final float alpha = mAdjustImeAmount * IME_ADJUST_DIM_AMOUNT; + final boolean isImeTarget = (mService.getImeTargetStackLocked() == this); + if (mAdjustedForIme && adjust && !isImeTarget) { + final float alpha = Math.max(mAdjustImeAmount, mAdjustDividerAmount) + * IME_ADJUST_DIM_AMOUNT; mService.setResizeDimLayer(true, mStackId, alpha); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 38f12a13e3794..9994af31c5207 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -7455,27 +7455,38 @@ public class WindowManagerService extends IWindowManager.Stub void adjustForImeIfNeeded(final DisplayContent displayContent) { final WindowState imeWin = mInputMethodWindow; - final TaskStack focusedStack = getFocusedStackLocked(); + final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw(); final boolean dockVisible = isStackVisibleLocked(DOCKED_STACK_ID); - if (imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw() - && dockVisible && focusedStack != null) { - final boolean isFocusOnBottom = focusedStack.getDockSide() == DOCKED_BOTTOM; + final TaskStack imeTargetStack = getImeTargetStackLocked(); + + // The divider could be adjusted for IME position, or be thinner than usual, + // or both. There are three possible cases: + // - If IME is visible, and focus is on top, divider is not moved for IME but thinner. + // - If IME is visible, and focus is on bottom, divider is moved for IME and thinner. + // - If IME is not visible, divider is not moved and is normal width. + + if (imeVisible && dockVisible && imeTargetStack != null) { + final boolean isFocusOnBottom = imeTargetStack.getDockSide() == DOCKED_BOTTOM; final ArrayList stacks = displayContent.getStacks(); for (int i = stacks.size() - 1; i >= 0; --i) { final TaskStack stack = stacks.get(i); final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM; if (stack.isVisibleLocked() && (isFocusOnBottom || isDockedOnBottom)) { stack.setAdjustedForIme(imeWin); + } else { + stack.resetAdjustedForIme(false); } } - displayContent.mDividerControllerLocked.setAdjustedForIme(true, true, imeWin); + displayContent.mDividerControllerLocked.setAdjustedForIme( + isFocusOnBottom /*ime*/, true /*divider*/, true /*animate*/, 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, imeWin); + displayContent.mDividerControllerLocked.setAdjustedForIme( + false /*ime*/, false /*divider*/, dockVisible /*animate*/, null); } } @@ -7608,8 +7619,10 @@ public class WindowManagerService extends IWindowManager.Stub return mCurrentFocus; } - TaskStack getFocusedStackLocked() { - return mCurrentFocus != null ? mCurrentFocus.getStack() : null; + TaskStack getImeTargetStackLocked() { + // Don't use WindowState.getStack() because it returns home stack for system windows. + Task imeTask = mInputMethodTarget != null ? mInputMethodTarget.getTask() : null; + return imeTask != null ? imeTask.mStack : null; } private void showAuditSafeModeNotification() { @@ -9354,7 +9367,6 @@ public class WindowManagerService extends IWindowManager.Stub WindowState newFocus = computeFocusedWindowLocked(); if (mCurrentFocus != newFocus) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus"); - TaskStack oldFocusedStack = getFocusedStackLocked(); // This check makes sure that we don't already have the focus // change message pending. mH.removeMessages(H.REPORT_FOCUS_CHANGE); @@ -9376,7 +9388,6 @@ public class WindowManagerService extends IWindowManager.Stub mLosingFocus.remove(newFocus); int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus); - TaskStack newFocusedStack = getFocusedStackLocked(); if (imWindowChanged && oldFocus != mInputMethodWindow) { // Focus of the input method window changed. Perform layout if needed. @@ -9406,18 +9417,6 @@ public class WindowManagerService extends IWindowManager.Stub mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows); } - // TODO: Reset and re-apply IME adjustment if needed when stack focus changed. - // This makes sure divider starts an animation from pre-adjust position to final - // position. Ideally we want to skip the reset and animation from current position - // directly to final position. - final WindowState imeWin = mInputMethodWindow; - if (oldFocusedStack != null) { - oldFocusedStack.resetAdjustedForIme(true); - } - if (newFocusedStack != null) { - newFocusedStack.resetAdjustedForIme(true); - } - displayContent.mDividerControllerLocked.setAdjustedForIme(false, false, imeWin); adjustForImeIfNeeded(displayContent); Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);