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;
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;
}
}
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<TaskStack> stacks = mDisplayContent.getStacks();
for (int i = stacks.size() - 1; i >= 0; --i) {
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);
}
}
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) {
mService.mWindowPlacerLocked.performSurfacePlacement();
}
if (t >= 1.0f) {
+ mLastAnimationProgress = mAnimationTarget;
+ mLastDividerProgress = mDividerAnimationTarget;
mAnimatingForIme = false;
return false;
} else {
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.
*/
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.
if (!mAdjustedForIme) {
mAdjustedForIme = true;
mAdjustImeAmount = 0f;
- updateAdjustForIme(0f, true /* force */);
+ mAdjustDividerAmount = 0f;
+ updateAdjustForIme(0f, 0f, true /* force */);
}
}
*
* @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 {
mAdjustedForIme = false;
mImeGoingAway = false;
mAdjustImeAmount = 0f;
+ mAdjustDividerAmount = 0f;
updateAdjustedBounds();
mService.setResizeDimLayer(false, mStackId, 1.0f);
} else {
(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();
}
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);
}
}
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<TaskStack> 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<TaskStack> 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);
}
}
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() {
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);
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.
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);