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;
import android.view.animation.PathInterpolator;
import com.android.server.wm.DimLayer.DimLayerUser;
+import com.android.server.wm.WindowManagerService.H;
import java.util.ArrayList;
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;
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;
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);
}
}
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<TaskStack> 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) {
}
private boolean animateForIme(long now) {
- if (!mAnimationStarted) {
+ if (!mAnimationStarted || mAnimationStartDelayed) {
mAnimationStarted = true;
mAnimationStartTime = now;
mAnimationDuration = (long)
stack.resetAdjustedForIme(true /* adjustBoundsNow */);
updated = true;
} else {
- updated |= stack.updateAdjustForIme(getInterpolatedAnimationValue(t));
+ updated |= stack.updateAdjustForIme(getInterpolatedAnimationValue(t),
+ false /* force */);
+ }
+ if (t >= 1f) {
+ stack.endImeAdjustAnimation();
}
}
}
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<WindowState> 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;
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;
}
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;
* @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() {
*
* @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();
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;
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();
return mDragResizing;
}
- private void setDragResizingLocked(boolean resizing) {
+ void setDragResizingLocked(boolean resizing) {
if (mDragResizing == resizing) {
return;
}
stack.setAdjustedForIme(imeWin);
}
}
- displayContent.mDividerControllerLocked.setAdjustedForIme(true, true);
+ displayContent.mDividerControllerLocked.setAdjustedForIme(true, true, 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);
+ displayContent.mDividerControllerLocked.setAdjustedForIme(false, dockVisible, imeWin);
}
}
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;
* window is first added or shown, cleared when the callback has been made. */
boolean mEnteringAnimation;
+ private boolean mAnimationStartDelayed;
+
boolean mKeyguardGoingAwayAnimation;
boolean mKeyguardGoingAwayWithWallpaper;
/** 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? */
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;
pw.print(" mDsDy="); pw.print(mDsDy);
pw.print(" mDtDy="); pw.println(mDtDy);
}
+ if (mAnimationStartDelayed) {
+ pw.print(prefix); pw.print("mAnimationStartDelayed="); pw.print(mAnimationStartDelayed);
+ }
}
@Override
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;
+ }
}