diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java index 27b799bc02a36..333fa3ca0a2d0 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java @@ -19,6 +19,9 @@ package com.android.systemui.stackdivider; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.view.Display.DEFAULT_DISPLAY; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ValueAnimator; import android.app.ActivityTaskManager; import android.content.Context; import android.content.res.Configuration; @@ -35,6 +38,8 @@ import android.view.SurfaceSession; import android.view.View; import android.view.WindowContainerTransaction; +import androidx.annotation.Nullable; + import com.android.internal.policy.DividerSnapAlgorithm; import com.android.systemui.R; import com.android.systemui.SystemUI; @@ -69,6 +74,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, static final boolean DEBUG = true; static final int DEFAULT_APP_TRANSITION_DURATION = 336; + static final float ADJUSTED_NONFOCUS_DIM = 0.3f; private final Optional> mRecentsOptionalLazy; @@ -121,42 +127,92 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, } }; - private IWindowContainer mLastImeTarget = null; - private boolean mShouldAdjustForIme = false; - private DisplayImeController.ImePositionProcessor mImePositionProcessor = new DisplayImeController.ImePositionProcessor() { - private int mStartTop = 0; - private int mFinalTop = 0; + /** + * These are the y positions of the top of the IME surface when it is hidden and + * when it is shown respectively. These are NOT necessarily the top of the visible + * IME itself. + */ + private int mHiddenTop = 0; + private int mShownTop = 0; + + // The following are target states (what we are curretly animating towards). + /** + * {@code true} if, at the end of the animation, the split task positions should be + * adjusted by height of the IME. This happens when the secondary split is the IME + * target. + */ + private boolean mTargetAdjusted = false; + /** + * {@code true} if, at the end of the animation, the IME should be shown/visible + * regardless of what has focus. + */ + private boolean mTargetShown = false; + + // The following are the current (most recent) states set during animation + /** + * {@code true} if the secondary split has IME focus. + */ + private boolean mSecondaryHasFocus = false; + /** The dimming currently applied to the primary/secondary splits. */ + private float mLastPrimaryDim = 0.f; + private float mLastSecondaryDim = 0.f; + /** The most recent y position of the top of the IME surface */ + private int mLastAdjustTop = -1; + + // The following are states reached last time an animation fully completed. + /** {@code true} if the IME was shown/visible by the last-completed animation. */ + private boolean mImeWasShown = false; + /** + * {@code true} if the split positions were adjusted by the last-completed + * animation. + */ + private boolean mAdjusted = false; + + /** + * When some aspect of split-screen needs to animate independent from the IME, + * this will be non-null and control split animation. + */ + @Nullable + private ValueAnimator mAnimation = null; + + private boolean getSecondaryHasFocus(int displayId) { + try { + IWindowContainer imeSplit = ActivityTaskManager.getTaskOrganizerController() + .getImeTarget(displayId); + return imeSplit != null + && (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder()); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to get IME target", e); + } + return false; + } + @Override - public void onImeStartPositioning(int displayId, int imeTop, int finalImeTop, - boolean showing, SurfaceControl.Transaction t) { - mStartTop = imeTop; - mFinalTop = finalImeTop; - if (showing) { - try { - mLastImeTarget = ActivityTaskManager.getTaskOrganizerController() - .getImeTarget(displayId); - mShouldAdjustForIme = mLastImeTarget != null - && !mSplitLayout.mDisplayLayout.isLandscape() - && (mLastImeTarget.asBinder() - == mSplits.mSecondary.token.asBinder()); - } catch (RemoteException e) { - Slog.w(TAG, "Failed to get IME target", e); - } + public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop, + boolean imeShouldShow, SurfaceControl.Transaction t) { + mSecondaryHasFocus = getSecondaryHasFocus(displayId); + mTargetAdjusted = imeShouldShow && mSecondaryHasFocus + && !mSplitLayout.mDisplayLayout.isLandscape(); + mHiddenTop = hiddenTop; + mShownTop = shownTop; + mTargetShown = imeShouldShow; + if (mLastAdjustTop < 0) { + mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop; } - if (!mShouldAdjustForIme) { - setAdjustedForIme(false); - return; + if (mAnimation != null || (mImeWasShown && imeShouldShow + && mTargetAdjusted != mAdjusted)) { + // We need to animate adjustment independently of the IME position, so + // start our own animation to drive adjustment. This happens when a + // different split's editor has gained focus while the IME is still visible. + startAsyncAnimation(); } - mView.setAdjustedForIme(showing, showing - ? DisplayImeController.ANIMATION_DURATION_SHOW_MS - : DisplayImeController.ANIMATION_DURATION_HIDE_MS); // Reposition the server's secondary split position so that it evaluates // insets properly. WindowContainerTransaction wct = new WindowContainerTransaction(); - if (showing) { - mSplitLayout.updateAdjustedBounds(finalImeTop, imeTop, finalImeTop); + if (mTargetAdjusted) { + mSplitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop); wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mAdjustedSecondary); } else { wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mSecondary); @@ -166,34 +222,106 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks, .applyContainerTransaction(wct, null /* organizer */); } catch (RemoteException e) { } - setAdjustedForIme(showing); + + // Update all the adjusted-for-ime states + mView.setAdjustedForIme(mTargetShown, mTargetShown + ? DisplayImeController.ANIMATION_DURATION_SHOW_MS + : DisplayImeController.ANIMATION_DURATION_HIDE_MS); + setAdjustedForIme(mTargetShown); } @Override public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) { - if (!mShouldAdjustForIme) { + if (mAnimation != null) { + // Not synchronized with IME anymore, so return. return; } - mSplitLayout.updateAdjustedBounds(imeTop, mStartTop, mFinalTop); - mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary, - mSplitLayout.mAdjustedSecondary); - final boolean showing = mFinalTop < mStartTop; - final float progress = ((float) (imeTop - mStartTop)) / (mFinalTop - mStartTop); - final float fraction = showing ? progress : 1.f - progress; - mView.setResizeDimLayer(t, true /* primary */, fraction * 0.3f); + final float fraction = ((float) imeTop - mHiddenTop) / (mShownTop - mHiddenTop); + final float progress = mTargetShown ? fraction : 1.f - fraction; + onProgress(progress, t); } @Override - public void onImeEndPositioning(int displayId, int imeTop, - boolean showing, SurfaceControl.Transaction t) { - if (!mShouldAdjustForIme) { + public void onImeEndPositioning(int displayId, boolean cancelled, + SurfaceControl.Transaction t) { + if (mAnimation != null) { + // Not synchronized with IME anymore, so return. return; } - mSplitLayout.updateAdjustedBounds(imeTop, mStartTop, mFinalTop); - mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary, - mSplitLayout.mAdjustedSecondary); - mView.setResizeDimLayer(t, true /* primary */, showing ? 0.3f : 0.f); + onEnd(cancelled, t); + } + + private void onProgress(float progress, SurfaceControl.Transaction t) { + if (mTargetAdjusted != mAdjusted) { + final float fraction = mTargetAdjusted ? progress : 1.f - progress; + mLastAdjustTop = + (int) (fraction * mShownTop + (1.f - fraction) * mHiddenTop); + mSplitLayout.updateAdjustedBounds(mLastAdjustTop, mHiddenTop, mShownTop); + mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary, + mSplitLayout.mAdjustedSecondary); + } + final float invProg = 1.f - progress; + final float targetPrimaryDim = (mSecondaryHasFocus && mTargetShown) + ? ADJUSTED_NONFOCUS_DIM : 0.f; + final float targetSecondaryDim = (!mSecondaryHasFocus && mTargetShown) + ? ADJUSTED_NONFOCUS_DIM : 0.f; + mView.setResizeDimLayer(t, true /* primary */, + mLastPrimaryDim * invProg + progress * targetPrimaryDim); + mView.setResizeDimLayer(t, false /* primary */, + mLastSecondaryDim * invProg + progress * targetSecondaryDim); + } + + private void onEnd(boolean cancelled, SurfaceControl.Transaction t) { + if (!cancelled) { + onProgress(1.f, t); + mAdjusted = mTargetAdjusted; + mImeWasShown = mTargetShown; + mLastAdjustTop = mAdjusted ? mShownTop : mHiddenTop; + mLastPrimaryDim = + (mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f; + mLastSecondaryDim = + (!mSecondaryHasFocus && mTargetShown) ? ADJUSTED_NONFOCUS_DIM : 0.f; + } + } + + private void startAsyncAnimation() { + if (mAnimation != null) { + mAnimation.cancel(); + } + mAnimation = ValueAnimator.ofFloat(0.f, 1.f); + mAnimation.setDuration(DisplayImeController.ANIMATION_DURATION_SHOW_MS); + if (mTargetAdjusted != mAdjusted) { + final float fraction = + ((float) mLastAdjustTop - mHiddenTop) / (mShownTop - mHiddenTop); + final float progress = mTargetAdjusted ? fraction : 1.f - fraction; + mAnimation.setCurrentFraction(progress); + } + + mAnimation.addUpdateListener(animation -> { + SurfaceControl.Transaction t = mTransactionPool.acquire(); + float value = (float) animation.getAnimatedValue(); + onProgress(value, t); + t.apply(); + mTransactionPool.release(t); + }); + mAnimation.setInterpolator(DisplayImeController.INTERPOLATOR); + mAnimation.addListener(new AnimatorListenerAdapter() { + private boolean mCancel = false; + @Override + public void onAnimationCancel(Animator animation) { + mCancel = true; + } + @Override + public void onAnimationEnd(Animator animation) { + SurfaceControl.Transaction t = mTransactionPool.acquire(); + onEnd(mCancel, t); + t.apply(); + mTransactionPool.release(t); + mAnimation = null; + } + }); + mAnimation.start(); } }; diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java index be9fcbf19f120..477cbb7c7ad07 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java @@ -935,6 +935,9 @@ public class DividerView extends FrameLayout implements OnTouchListener, } public void setAdjustedForIme(boolean adjustedForIme, long animDuration) { + if (mAdjustedForIme == adjustedForIme) { + return; + } updateDockSide(); mHandle.animate() .setInterpolator(IME_ADJUST_INTERPOLATOR) diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java index b19f560f2f50a..271faed54bcab 100644 --- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java +++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java @@ -171,22 +171,13 @@ public class SplitDisplayLayout { /** * Updates the adjustment depending on it's current state. */ - void updateAdjustedBounds(int currImeTop, int startTop, int finalTop) { - updateAdjustedBounds(mDisplayLayout, currImeTop, startTop, finalTop, mDividerSize, + void updateAdjustedBounds(int currImeTop, int hiddenTop, int shownTop) { + adjustForIME(mDisplayLayout, currImeTop, hiddenTop, shownTop, mDividerSize, mDividerSizeInactive, mPrimary, mSecondary); } - /** - * Updates the adjustment depending on it's current state. - */ - private void updateAdjustedBounds(DisplayLayout dl, int currImeTop, int startTop, int finalTop, - int dividerWidth, int dividerWidthInactive, Rect primaryBounds, Rect secondaryBounds) { - adjustForIME(dl, currImeTop, startTop, finalTop, dividerWidth, dividerWidthInactive, - primaryBounds, secondaryBounds); - } - /** Assumes top/bottom split. Splits are not adjusted for left/right splits. */ - private void adjustForIME(DisplayLayout dl, int currImeTop, int startTop, int finalTop, + private void adjustForIME(DisplayLayout dl, int currImeTop, int hiddenTop, int shownTop, int dividerWidth, int dividerWidthInactive, Rect primaryBounds, Rect secondaryBounds) { if (mAdjustedPrimary == null) { mAdjustedPrimary = new Rect(); @@ -196,11 +187,9 @@ public class SplitDisplayLayout { final Rect displayStableRect = new Rect(); dl.getStableBounds(displayStableRect); - final boolean showing = finalTop < startTop; - final float progress = ((float) (currImeTop - startTop)) / (finalTop - startTop); - final float dividerSquish = showing ? progress : 1.f - progress; + final float shownFraction = ((float) (currImeTop - hiddenTop)) / (shownTop - hiddenTop); final int currDividerWidth = - (int) (dividerWidthInactive * dividerSquish + dividerWidth * (1.f - dividerSquish)); + (int) (dividerWidthInactive * shownFraction + dividerWidth * (1.f - shownFraction)); final int minTopStackBottom = displayStableRect.top + (int) ((mPrimary.bottom - displayStableRect.top) * ADJUSTED_STACK_FRACTION_MIN); diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java index 1b62cbfabe397..2f1b160ffd4ba 100644 --- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java +++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java @@ -51,7 +51,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged public static final int ANIMATION_DURATION_SHOW_MS = 275; public static final int ANIMATION_DURATION_HIDE_MS = 340; - static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f); + public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f); private static final int DIRECTION_NONE = 0; private static final int DIRECTION_SHOW = 1; private static final int DIRECTION_HIDE = 2; @@ -127,20 +127,20 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } } - private void dispatchStartPositioning(int displayId, int imeTop, int finalImeTop, + private void dispatchStartPositioning(int displayId, int hiddenTop, int shownTop, boolean show, SurfaceControl.Transaction t) { synchronized (mPositionProcessors) { for (ImePositionProcessor pp : mPositionProcessors) { - pp.onImeStartPositioning(displayId, imeTop, finalImeTop, show, t); + pp.onImeStartPositioning(displayId, hiddenTop, shownTop, show, t); } } } - private void dispatchEndPositioning(int displayId, int imeTop, boolean show, + private void dispatchEndPositioning(int displayId, boolean cancel, SurfaceControl.Transaction t) { synchronized (mPositionProcessors) { for (ImePositionProcessor pp : mPositionProcessors) { - pp.onImeEndPositioning(displayId, imeTop, show, t); + pp.onImeEndPositioning(displayId, cancel, t); } } } @@ -173,6 +173,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged int mAnimationDirection = DIRECTION_NONE; ValueAnimator mAnimation = null; int mRotation = Surface.ROTATION_0; + boolean mImeShowing = false; PerDisplay(int displayId, int initialRotation) { mDisplayId = displayId; @@ -239,23 +240,39 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if (imeSource == null || mImeSourceControl == null) { return; } - if ((mAnimationDirection == DIRECTION_SHOW && show) - || (mAnimationDirection == DIRECTION_HIDE && !show)) { - return; - } - if (mAnimationDirection != DIRECTION_NONE) { - mAnimation.end(); - mAnimationDirection = DIRECTION_NONE; - } - mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE; mHandler.post(() -> { + if ((mAnimationDirection == DIRECTION_SHOW && show) + || (mAnimationDirection == DIRECTION_HIDE && !show)) { + return; + } + boolean seek = false; + float seekValue = 0; + if (mAnimation != null) { + if (mAnimation.isRunning()) { + seekValue = (float) mAnimation.getAnimatedValue(); + seek = true; + } + mAnimation.cancel(); + } + mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE; final float defaultY = mImeSourceControl.getSurfacePosition().y; final float x = mImeSourceControl.getSurfacePosition().x; - final float startY = show ? defaultY + imeSource.getFrame().height() : defaultY; - final float endY = show ? defaultY : defaultY + imeSource.getFrame().height(); + final float hiddenY = defaultY + imeSource.getFrame().height(); + final float shownY = defaultY; + final float startY = show ? hiddenY : shownY; + final float endY = show ? shownY : hiddenY; + if (mImeShowing && show) { + // IME is already showing, so set seek to end + seekValue = shownY; + seek = true; + } + mImeShowing = show; mAnimation = ValueAnimator.ofFloat(startY, endY); mAnimation.setDuration( show ? ANIMATION_DURATION_SHOW_MS : ANIMATION_DURATION_HIDE_MS); + if (seek) { + mAnimation.setCurrentFraction((seekValue - startY) / (endY - startY)); + } mAnimation.addUpdateListener(animation -> { SurfaceControl.Transaction t = mTransactionPool.acquire(); @@ -267,12 +284,13 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged }); mAnimation.setInterpolator(INTERPOLATOR); mAnimation.addListener(new AnimatorListenerAdapter() { + private boolean mCancelled = false; @Override public void onAnimationStart(Animator animation) { SurfaceControl.Transaction t = mTransactionPool.acquire(); t.setPosition(mImeSourceControl.getLeash(), x, startY); - dispatchStartPositioning(mDisplayId, imeTop(imeSource, startY), - imeTop(imeSource, endY), mAnimationDirection == DIRECTION_SHOW, + dispatchStartPositioning(mDisplayId, imeTop(imeSource, hiddenY), + imeTop(imeSource, shownY), mAnimationDirection == DIRECTION_SHOW, t); if (mAnimationDirection == DIRECTION_SHOW) { t.show(mImeSourceControl.getLeash()); @@ -281,12 +299,17 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged mTransactionPool.release(t); } @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + @Override public void onAnimationEnd(Animator animation) { SurfaceControl.Transaction t = mTransactionPool.acquire(); - t.setPosition(mImeSourceControl.getLeash(), x, endY); - dispatchEndPositioning(mDisplayId, imeTop(imeSource, endY), - mAnimationDirection == DIRECTION_SHOW, t); - if (mAnimationDirection == DIRECTION_HIDE) { + if (!mCancelled) { + t.setPosition(mImeSourceControl.getLeash(), x, endY); + } + dispatchEndPositioning(mDisplayId, mCancelled, t); + if (mAnimationDirection == DIRECTION_HIDE && !mCancelled) { t.hide(mImeSourceControl.getLeash()); } t.apply(); @@ -317,21 +340,30 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged public interface ImePositionProcessor { /** * Called when the IME position is starting to animate. + * + * @param hiddenTop The y position of the top of the IME surface when it is hidden. + * @param shownTop The y position of the top of the IME surface when it is shown. + * @param showing {@code true} when we are animating from hidden to shown, {@code false} + * when animating from shown to hidden. */ - default void onImeStartPositioning(int displayId, int imeTop, int finalImeTop, + default void onImeStartPositioning(int displayId, int hiddenTop, int shownTop, boolean showing, SurfaceControl.Transaction t) {} /** * Called when the ime position changed. This is expected to be a synchronous call on the * animation thread. Operations can be added to the transaction to be applied in sync. + * + * @param imeTop The current y position of the top of the IME surface. */ default void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) {} /** * Called when the IME position is done animating. + * + * @param cancel {@code true} if this was cancelled. This implies another start is coming. */ - default void onImeEndPositioning(int displayId, int imeTop, boolean showing, + default void onImeEndPositioning(int displayId, boolean cancel, SurfaceControl.Transaction t) {} } } diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 725596819b79a..7491376cd1527 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -64,13 +64,14 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner"); // Target should still be the same. if (isImeTargetFromDisplayContentAndImeSame()) { + final InsetsControlTarget target = mDisplayContent.mInputMethodControlTarget; ProtoLog.d(WM_DEBUG_IME, "call showInsets(ime) on %s", - mDisplayContent.mInputMethodControlTarget.getWindow().getName()); - mDisplayContent.mInputMethodControlTarget.showInsets( - WindowInsets.Type.ime(), true /* fromIme */); + target.getWindow() != null ? target.getWindow().getName() : ""); + target.showInsets(WindowInsets.Type.ime(), true /* fromIme */); } abortShowImePostLayout(); }; + mDisplayContent.mWmService.requestTraversal(); } void checkShowImePostLayout() {