Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb88d83f authored by Jorim Jaggi's avatar Jorim Jaggi
Browse files

Add nice animation when adjusting for IME in multi-window

- Run a separate animation when we need to adjust for the IME. We
can't use the attached animation because the adjustment animation
needs to be longer than the IME animation.
- Also run an animation when IME is disappearing.
- Adjust IME exit animation to better match with adjustment exit
animation.
- Make sure to update adjust for IME when entry/exit animation of
IME is starting, to avoid flickers.
- Don't update the IME window in PhoneWindowManager for layout
until the animation has started. This lead to an issue where the
content inset was set too large too early.

Bug: 27154882
Bug: 28175599
Change-Id: I09a33413e307f84d6c3cf4ae928c280f7ad48348
parent be67c90f
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -17,10 +17,10 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
	android:shareInterpolator="false">
    <translate android:fromYDelta="0" android:toYDelta="10%"
			android:interpolator="@interpolator/accelerate_quint"
            android:duration="@android:integer/config_shortAnimTime"/>
    <translate android:fromYDelta="0" android:toYDelta="8%"
			android:interpolator="@interpolator/fast_out_linear_in"
            android:duration="150"/>
    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
			android:interpolator="@interpolator/accelerate_cubic"
            android:duration="@android:integer/config_shortAnimTime"/>
			android:interpolator="@interpolator/fast_out_linear_in"
            android:duration="150"/>
</set>
+1 −1
Original line number Diff line number Diff line
@@ -4754,7 +4754,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        // Dock windows carve out the bottom of the screen, so normal windows
        // can't appear underneath them.
        if (attrs.type == TYPE_INPUT_METHOD && win.isVisibleOrBehindKeyguardLw()
                && !win.getGivenInsetsPendingLw()) {
                && win.isDisplayedLw() && !win.getGivenInsetsPendingLw()) {
            setLastInputMethodWindowLw(null, null);
            offsetInputMethodWindowLw(win);
        }
+55 −20
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.view.IDockedStackListener;
import android.view.SurfaceControl;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;

import com.android.server.wm.DimLayer.DimLayerUser;

@@ -74,6 +75,11 @@ 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);

    private static final long IME_ADJUST_DURATION = 280;

    private final WindowManagerService mService;
    private final DisplayContent mDisplayContent;
    private int mDividerWindowWidth;
@@ -188,8 +194,10 @@ public class DockedStackDividerController implements DimLayerUser {

    void setAdjustedForIme(boolean adjusted, boolean animate) {
        if (mAdjustedForIme != adjusted) {
            mAnimatingForIme = animate;
            mAdjustedForIme = adjusted;
            if (animate) {
                startImeAdjustAnimation(adjusted ? 0 : 1, adjusted ? 1 : 0);
            }
        }
    }

@@ -371,7 +379,7 @@ public class DockedStackDividerController implements DimLayerUser {
            return;
        }

        mAnimatingForIme = false;
        clearImeAdjustAnimation();
        if (minimizedDock) {
            if (animate) {
                startAdjustAnimation(0f, 1f);
@@ -387,6 +395,17 @@ public class DockedStackDividerController implements DimLayerUser {
        }
    }

    private void clearImeAdjustAnimation() {
        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
        for (int i = stacks.size() - 1; i >= 0; --i) {
            final TaskStack stack = stacks.get(i);
            if (stack != null && stack.isAdjustedForIme()) {
                stack.resetAdjustedForIme(true /* adjustBoundsNow */);
            }
        }
        mAnimatingForIme = false;
    }

    private void startAdjustAnimation(float from, float to) {
        mAnimatingForMinimizedDockedStack = true;
        mAnimationStarted = false;
@@ -394,6 +413,13 @@ public class DockedStackDividerController implements DimLayerUser {
        mAnimationTarget = to;
    }

    private void startImeAdjustAnimation(float from, float to) {
        mAnimatingForIme = true;
        mAnimationStarted = false;
        mAnimationStart = from;
        mAnimationTarget = to;
    }

    private void setMinimizedDockedStack(boolean minimized) {
        final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
        notifyDockedStackMinimizedChanged(minimized, 0);
@@ -413,39 +439,44 @@ public class DockedStackDividerController implements DimLayerUser {
        if (mAnimatingForMinimizedDockedStack) {
            return animateForMinimizedDockedStack(now);
        } else if (mAnimatingForIme) {
            return animateForIme();
            return animateForIme(now);
        } else {
            return false;
        }
    }

    private boolean animateForIme() {
        boolean updated = false;
        boolean animating = false;

    private boolean animateForIme(long now) {
        if (!mAnimationStarted) {
            mAnimationStarted = true;
            mAnimationStartTime = now;
            mAnimationDuration = (long)
                    (IME_ADJUST_DURATION * mService.getWindowAnimationScaleLocked());
        }
        float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
        t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
                .getInterpolation(t);
        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
        boolean updated = false;
        for (int i = stacks.size() - 1; i >= 0; --i) {
            final TaskStack stack = stacks.get(i);
            if (stack != null && stack.isAdjustedForIme()) {
                updated |= stack.updateAdjustForIme();
                animating |= stack.isAnimatingForIme();
                if (t >= 1f && mAnimationTarget == 0f) {
                    stack.resetAdjustedForIme(true /* adjustBoundsNow */);
                    updated = true;
                } else {
                    updated |= stack.updateAdjustForIme(getInterpolatedAnimationValue(t));
                }
            }
        }

        if (updated) {
            mService.mWindowPlacerLocked.performSurfacePlacement();
        }

        if (!animating) {
        if (t >= 1.0f) {
            mAnimatingForIme = false;
            for (int i = stacks.size() - 1; i >= 0; --i) {
                final TaskStack stack = stacks.get(i);
                if (stack != null) {
                    stack.clearImeGoingAway();
                }
            }
            return false;
        } else {
            return true;
        }
        return animating;
    }

    private boolean animateForMinimizedDockedStack(long now) {
@@ -478,11 +509,15 @@ public class DockedStackDividerController implements DimLayerUser {
        }
    }

    private float getInterpolatedAnimationValue(float t) {
        return t * mAnimationTarget + (1 - t) * mAnimationStart;
    }

    /**
     * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
     */
    private float getMinimizeAmount(TaskStack stack, float t) {
        final float naturalAmount = t * mAnimationTarget + (1 - t) * mAnimationStart;
        final float naturalAmount = getInterpolatedAnimationValue(t);
        if (isAnimationMaximizing()) {
            return adjustMaximizeAmount(stack, t, naturalAmount);
        } else {
+50 −49
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.animation.PathInterpolator;

import com.android.internal.policy.DividerSnapAlgorithm;
import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget;
@@ -52,14 +53,6 @@ import java.util.ArrayList;
public class TaskStack implements DimLayer.DimLayerUser,
        BoundsAnimationController.AnimateBoundsUser {

    // If the stack should be resized to fullscreen.
    private static final boolean FULLSCREEN = true;

    // When we have a top-bottom split screen, we shift the bottom stack up to accommodate
    // the IME window. The static flag below controls whether to run animation when the
    // IME window goes away.
    private static final boolean ANIMATE_IME_GOING_AWAY = false;

    /** Unique identifier */
    final int mStackId;

@@ -83,6 +76,12 @@ public class TaskStack implements DimLayer.DimLayerUser,
    /** Stack bounds adjusted to screen content area (taking into account IM windows, etc.) */
    private final Rect mAdjustedBounds = new Rect();

    /**
     * Fully adjusted IME bounds. These are different from {@link #mAdjustedBounds} because they
     * represent the state when the animation has ended.
     */
    private final Rect mFullyAdjustedImeBounds = new Rect();

    /** Whether mBounds is fullscreen */
    private boolean mFullscreen = true;

@@ -118,6 +117,7 @@ public class TaskStack implements DimLayer.DimLayerUser,
    private boolean mImeGoingAway;
    private WindowState mImeWin;
    private float mMinimizeAmount;
    private float mAdjustImeAmount;
    private final int mDockedStackMinimizeThickness;

    // If this is true, the task will be down or upscaled
@@ -211,21 +211,26 @@ public class TaskStack implements DimLayer.DimLayerUser,
     * the normal task bounds.
     *
     * @param bounds The adjusted bounds.
     * @param keepInsets Whether to keep the insets from the original bounds or to calculate new
     *                   ones depending on the adjusted bounds.
     * @return true if the adjusted bounds has changed.
     */
    private boolean setAdjustedBounds(Rect bounds, boolean keepInsets) {
        if (mAdjustedBounds.equals(bounds)) {
            return false;
    private void setAdjustedBounds(Rect bounds) {
        if (mAdjustedBounds.equals(bounds) && !isAnimatingForIme()) {
            return;
        }

        mAdjustedBounds.set(bounds);
        final boolean adjusted = !mAdjustedBounds.isEmpty();
        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds,
                adjusted && keepInsets ? mBounds : null);
        Rect insetBounds = null;
        if (adjusted && isAdjustedForMinimizedDock()) {
            insetBounds = mBounds;
        } else if (adjusted && isAdjustedForIme()) {
            if (mImeGoingAway) {
                insetBounds = mBounds;
            } else {
                insetBounds = mFullyAdjustedImeBounds;
            }
        }
        alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds);
        mDisplayContent.layoutNeeded = true;
        return true;
    }

    private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
@@ -829,6 +834,7 @@ public class TaskStack implements DimLayer.DimLayerUser,
     */
    void setAdjustedForIme(WindowState imeWin) {
        mAdjustedForIme = true;
        mAdjustImeAmount = 0f;
        mImeWin = imeWin;
        mImeGoingAway = false;
    }
@@ -836,9 +842,6 @@ public class TaskStack implements DimLayer.DimLayerUser,
    boolean isAdjustedForIme() {
        return mAdjustedForIme || mImeGoingAway;
    }
    void clearImeGoingAway() {
        mImeGoingAway = false;
    }

    boolean isAnimatingForIme() {
        return mImeWin != null && mImeWin.isAnimatingLw();
@@ -852,16 +855,14 @@ public class TaskStack implements DimLayer.DimLayerUser,
     *
     * @return true if a traversal should be performed after the adjustment.
     */
    boolean updateAdjustForIme() {
        boolean stopped = false;
        if (mImeGoingAway && (!ANIMATE_IME_GOING_AWAY || !isAnimatingForIme())) {
            mImeWin = null;
            mAdjustedForIme = false;
            stopped = true;
    boolean updateAdjustForIme(float adjustAmount) {
        if (adjustAmount != mAdjustImeAmount) {
            mAdjustImeAmount = adjustAmount;
            updateAdjustedBounds();
            return isVisibleForUserLocked();
        } else {
            return false;
        }
        // Make sure to run a traversal when the animation stops so that the stack
        // is moved to its final position.
        return updateAdjustedBounds() || stopped;
    }

    /**
@@ -875,6 +876,7 @@ public class TaskStack implements DimLayer.DimLayerUser,
            mImeWin = null;
            mAdjustedForIme = false;
            mImeGoingAway = false;
            mAdjustImeAmount = 0f;
            updateAdjustedBounds();
        } else {
            mImeGoingAway |= mAdjustedForIme;
@@ -904,7 +906,6 @@ public class TaskStack implements DimLayer.DimLayerUser,
    private boolean adjustForIME(final WindowState imeWin) {
        final int dockedSide = getDockSide();
        final boolean dockedTopOrBottom = dockedSide == DOCKED_TOP || dockedSide == DOCKED_BOTTOM;
        final Rect adjustedBounds = mTmpAdjustedBounds;
        if (imeWin == null || !dockedTopOrBottom) {
            return false;
        }
@@ -917,41 +918,38 @@ public class TaskStack implements DimLayer.DimLayerUser,
        contentBounds.set(displayContentRect);
        int imeTop = Math.max(imeWin.getFrameLw().top, contentBounds.top);

        // if IME window is animating, get its actual vertical shown position (but no smaller than
        // the final target vertical position)
        if (imeWin.isAnimatingLw()) {
            imeTop = Math.max(imeTop, imeWin.getShownPositionLw().y);
        }
        imeTop += imeWin.getGivenContentInsetsLw().top;
        if (contentBounds.bottom > imeTop) {
            contentBounds.bottom = imeTop;
        }

        // If content bounds not changing, nothing to do.
        if (mLastContentBounds.equals(contentBounds)) {
            return true;
        }

        // Content bounds changed, need to apply adjustments depending on dock sides.
        mLastContentBounds.set(contentBounds);
        adjustedBounds.set(mBounds);
        final int yOffset = displayContentRect.bottom - contentBounds.bottom;

        if (dockedSide == DOCKED_TOP) {
            // If this stack is docked on top, we make it smaller so the bottom stack is not
            // occluded by IME. We shift its bottom up by the height of the IME (capped by
            // the display content rect). Note that we don't change the task bounds.
            adjustedBounds.bottom = Math.max(
                    adjustedBounds.bottom - yOffset, displayContentRect.top);
            int bottom = Math.max(
                    mBounds.bottom - yOffset, displayContentRect.top);
            mTmpAdjustedBounds.set(mBounds);
            mTmpAdjustedBounds.bottom =
                    (int) (mAdjustImeAmount * bottom + (1 - mAdjustImeAmount) * mBounds.bottom);
            mFullyAdjustedImeBounds.set(mBounds);
        } else {
            // If this stack is docked on bottom, 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 (although the best
            // we could do is to make the top stack fully collapsed).
            final int dividerWidth = getDisplayContent().mDividerControllerLocked
                    .getContentWidth();
            adjustedBounds.top = Math.max(
                    adjustedBounds.top - yOffset, displayContentRect.top + dividerWidth);
            adjustedBounds.bottom = adjustedBounds.top + mBounds.height();
            int top = Math.max(mBounds.top - yOffset, displayContentRect.top + dividerWidth);
            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 true;
    }
@@ -1007,7 +1005,7 @@ public class TaskStack implements DimLayer.DimLayerUser,
    /**
     * Updates the adjustment depending on it's current state.
     */
    boolean updateAdjustedBounds() {
    void updateAdjustedBounds() {
        boolean adjust = false;
        if (mMinimizeAmount != 0f) {
            adjust = adjustForMinimizedDockedStack(mMinimizeAmount);
@@ -1018,7 +1016,7 @@ public class TaskStack implements DimLayer.DimLayerUser,
            mTmpAdjustedBounds.setEmpty();
            mLastContentBounds.setEmpty();
        }
        return setAdjustedBounds(mTmpAdjustedBounds, isAdjustedForMinimizedDockedStack());
        setAdjustedBounds(mTmpAdjustedBounds);
    }

    boolean isAdjustedForMinimizedDockedStack() {
@@ -1030,6 +1028,9 @@ public class TaskStack implements DimLayer.DimLayerUser,
        pw.println(prefix + "mDeferDetach=" + mDeferDetach);
        pw.println(prefix + "mFullscreen=" + mFullscreen);
        pw.println(prefix + "mBounds=" + mBounds.toShortString());
        if (!mAdjustedBounds.isEmpty()) {
            pw.println(prefix + "mAdjustedBounds=" + mAdjustedBounds.toShortString());
        }
        for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; taskNdx--) {
            mTasks.get(taskNdx).dump(prefix + "  ", pw);
        }
+1 −1
Original line number Diff line number Diff line
@@ -7443,7 +7443,7 @@ public class WindowManagerService extends IWindowManager.Stub
        }
    }

    private void adjustForImeIfNeeded(final DisplayContent displayContent) {
    void adjustForImeIfNeeded(final DisplayContent displayContent) {
        final WindowState imeWin = mInputMethodWindow;
        final TaskStack focusedStack =
                mCurrentFocus != null ? mCurrentFocus.getStack() : null;
Loading