Loading core/java/android/view/InsetsAnimationControlCallbacks.java +5 −5 Original line number Original line Diff line number Diff line Loading @@ -20,7 +20,8 @@ import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowInsetsAnimation.Bounds; /** /** * Provide an interface to let InsetsAnimationControlImpl call back into its owner. * Provide an interface to let InsetsAnimationControlImpl and InsetsResizeAnimationRunner call back * into its owner. * @hide * @hide */ */ public interface InsetsAnimationControlCallbacks { public interface InsetsAnimationControlCallbacks { Loading @@ -34,10 +35,9 @@ public interface InsetsAnimationControlCallbacks { * <li>Dispatch {@link WindowInsetsAnimationControlListener#onReady}</li> * <li>Dispatch {@link WindowInsetsAnimationControlListener#onReady}</li> * </ul> * </ul> */ */ void startAnimation(InsetsAnimationControlImpl controller, <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController> WindowInsetsAnimationControlListener listener, int types, void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, WindowInsetsAnimation animation, Bounds bounds); Bounds bounds); /** /** * Schedule the apply by posting the animation callback. * Schedule the apply by posting the animation callback. Loading core/java/android/view/InsetsAnimationControlImpl.java +6 −1 Original line number Original line Diff line number Diff line Loading @@ -105,7 +105,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll private float mCurrentAlpha = 1.0f; private float mCurrentAlpha = 1.0f; private float mPendingAlpha = 1.0f; private float mPendingAlpha = 1.0f; @VisibleForTesting(visibility = PACKAGE) @VisibleForTesting(visibility = PACKAGE) public boolean mReadyDispatched; private boolean mReadyDispatched; private Boolean mPerceptible; private Boolean mPerceptible; @VisibleForTesting @VisibleForTesting Loading Loading @@ -169,6 +169,11 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll return mHasZeroInsetsIme; return mHasZeroInsetsIme; } } @Override public void setReadyDispatched(boolean dispatched) { mReadyDispatched = dispatched; } @Override @Override public Insets getHiddenStateInsets() { public Insets getHiddenStateInsets() { return mHiddenInsets; return mHiddenInsets; Loading core/java/android/view/InsetsAnimationThreadControlRunner.java +2 −2 Original line number Original line Diff line number Diff line Loading @@ -54,8 +54,8 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro @Override @Override @UiThread @UiThread public void startAnimation(InsetsAnimationControlImpl controller, public <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController> WindowInsetsAnimationControlListener listener, int types, void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds) { WindowInsetsAnimation animation, Bounds bounds) { // Animation will be started in constructor already. // Animation will be started in constructor already. } } Loading core/java/android/view/InsetsController.java +64 −25 Original line number Original line Diff line number Diff line Loading @@ -205,6 +205,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private static final int ANIMATION_DURATION_FADE_IN_MS = 500; private static final int ANIMATION_DURATION_FADE_IN_MS = 500; private static final int ANIMATION_DURATION_FADE_OUT_MS = 1500; private static final int ANIMATION_DURATION_FADE_OUT_MS = 1500; /** Visible for WindowManagerWrapper */ public static final int ANIMATION_DURATION_RESIZE = 300; private static final int ANIMATION_DELAY_DIM_MS = 500; private static final int ANIMATION_DELAY_DIM_MS = 500; private static final int ANIMATION_DURATION_SYNC_IME_MS = 285; private static final int ANIMATION_DURATION_SYNC_IME_MS = 285; Loading Loading @@ -235,6 +238,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR = private static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR = new PathInterpolator(0.4f, 0f, 1f, 1f); new PathInterpolator(0.4f, 0f, 1f, 1f); /** Visible for WindowManagerWrapper */ public static final Interpolator RESIZE_INTERPOLATOR = new LinearInterpolator(); /** The amount IME will move up/down when animating in floating mode. */ /** The amount IME will move up/down when animating in floating mode. */ private static final int FLOATING_IME_BOTTOM_INSET_DP = -80; private static final int FLOATING_IME_BOTTOM_INSET_DP = -80; Loading Loading @@ -288,9 +294,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting @VisibleForTesting public static final int ANIMATION_TYPE_USER = 2; public static final int ANIMATION_TYPE_USER = 2; /** Running animation will resize insets */ @VisibleForTesting public static final int ANIMATION_TYPE_RESIZE = 3; @Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE) @IntDef(value = {ANIMATION_TYPE_NONE, ANIMATION_TYPE_SHOW, ANIMATION_TYPE_HIDE, @IntDef(value = {ANIMATION_TYPE_NONE, ANIMATION_TYPE_SHOW, ANIMATION_TYPE_HIDE, ANIMATION_TYPE_USER}) ANIMATION_TYPE_USER, ANIMATION_TYPE_RESIZE}) @interface AnimationType { @interface AnimationType { } } Loading Loading @@ -320,7 +330,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private final boolean mDisable; private final boolean mDisable; private final int mFloatingImeBottomInset; private final int mFloatingImeBottomInset; private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = private final ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = new ThreadLocal<AnimationHandler>() { new ThreadLocal<AnimationHandler>() { @Override @Override protected AnimationHandler initialValue() { protected AnimationHandler initialValue() { Loading Loading @@ -550,7 +560,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>(); private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>(); private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>(); private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>(); private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>(); private final ArraySet<InsetsSourceConsumer> mRequestedVisibilityChanged = new ArraySet<>(); private final ArraySet<InsetsSourceConsumer> mRequestedVisibilityChanged = new ArraySet<>(); private WindowInsets mLastInsets; private WindowInsets mLastInsets; Loading @@ -570,7 +579,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private int mCaptionInsetsHeight = 0; private int mCaptionInsetsHeight = 0; private boolean mAnimationsDisabled; private boolean mAnimationsDisabled; private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest; private final Runnable mPendingControlTimeout = this::abortPendingImeControlRequest; private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners = new ArrayList<>(); = new ArrayList<>(); Loading @@ -580,7 +589,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation /** Set of inset types which cannot be controlled by the user animation */ /** Set of inset types which cannot be controlled by the user animation */ private @InsetsType int mDisabledUserAnimationInsetsTypes; private @InsetsType int mDisabledUserAnimationInsetsTypes; private Runnable mInvokeControllableInsetsChangedListeners = private final Runnable mInvokeControllableInsetsChangedListeners = this::invokeControllableInsetsChangedListeners; this::invokeControllableInsetsChangedListeners; public InsetsController(Host host) { public InsetsController(Host host) { Loading Loading @@ -608,23 +617,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } final List<WindowInsetsAnimation> runningAnimations = new ArrayList<>(); final List<WindowInsetsAnimation> runningAnimations = new ArrayList<>(); final List<WindowInsetsAnimation> finishedAnimations = new ArrayList<>(); final InsetsState state = new InsetsState(mState, true /* copySources */); final InsetsState state = new InsetsState(mState, true /* copySources */); for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { RunningAnimation runningAnimation = mRunningAnimations.get(i); RunningAnimation runningAnimation = mRunningAnimations.get(i); if (DEBUG) Log.d(TAG, "Running animation type: " + runningAnimation.type); if (DEBUG) Log.d(TAG, "Running animation type: " + runningAnimation.type); InsetsAnimationControlRunner runner = runningAnimation.runner; final InsetsAnimationControlRunner runner = runningAnimation.runner; if (runner instanceof InsetsAnimationControlImpl) { if (runner instanceof WindowInsetsAnimationController) { InsetsAnimationControlImpl control = (InsetsAnimationControlImpl) runner; // Keep track of running animation to be dispatched. Aggregate it here such that // Keep track of running animation to be dispatched. Aggregate it here such that // if it gets finished within applyChangeInsets we still dispatch it to // if it gets finished within applyChangeInsets we still dispatch it to // onProgress. // onProgress. if (runningAnimation.startDispatched) { if (runningAnimation.startDispatched) { runningAnimations.add(control.getAnimation()); runningAnimations.add(runner.getAnimation()); } } if (control.applyChangeInsets(state)) { if (((WindowInsetsAnimationController) runner).applyChangeInsets(state)) { mTmpFinishedControls.add(control); finishedAnimations.add(runner.getAnimation()); } } } } } } Loading @@ -642,10 +651,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } } } for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) { for (int i = finishedAnimations.size() - 1; i >= 0; i--) { dispatchAnimationEnd(mTmpFinishedControls.get(i).getAnimation()); dispatchAnimationEnd(finishedAnimations.get(i)); } } mTmpFinishedControls.clear(); }; }; } } Loading Loading @@ -691,15 +699,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation true /* excludeInvisibleIme */)) { true /* excludeInvisibleIme */)) { if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); mHost.notifyInsetsChanged(); mHost.notifyInsetsChanged(); startResizingAnimationIfNeeded(lastState); } } return true; return true; } } private void updateState(InsetsState newState) { private void updateState(InsetsState newState) { mState.setDisplayFrame(newState.getDisplayFrame()); mState.set(newState, 0 /* types */); mState.setDisplayCutout(newState.getDisplayCutout()); mState.setRoundedCorners(newState.getRoundedCorners()); mState.setPrivacyIndicatorBounds(newState.getPrivacyIndicatorBounds()); @InsetsType int disabledUserAnimationTypes = 0; @InsetsType int disabledUserAnimationTypes = 0; @InsetsType int[] cancelledUserAnimationTypes = {0}; @InsetsType int[] cancelledUserAnimationTypes = {0}; for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) { for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) { Loading Loading @@ -764,6 +770,39 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return false; return false; } } private void startResizingAnimationIfNeeded(InsetsState fromState) { if (!fromState.getDisplayFrame().equals(mState.getDisplayFrame())) { return; } @InsetsType int types = 0; InsetsState toState = null; final ArraySet<Integer> internalTypes = InsetsState.toInternalType(Type.systemBars()); for (int i = internalTypes.size() - 1; i >= 0; i--) { final @InternalInsetsType int type = internalTypes.valueAt(i); final InsetsSource fromSource = fromState.peekSource(type); final InsetsSource toSource = mState.peekSource(type); if (fromSource != null && toSource != null && fromSource.isVisible() && toSource.isVisible() && !fromSource.getFrame().equals(toSource.getFrame()) && (Rect.intersects(mFrame, fromSource.getFrame()) || Rect.intersects(mFrame, toSource.getFrame()))) { types |= InsetsState.toPublicType(toSource.getType()); if (toState == null) { toState = new InsetsState(); } toState.addSource(new InsetsSource(toSource)); } } if (types == 0) { return; } cancelExistingControllers(types); final InsetsAnimationControlRunner runner = new InsetsResizeAnimationRunner( mFrame, fromState, toState, RESIZE_INTERPOLATOR, ANIMATION_DURATION_RESIZE, types, this); mRunningAnimations.add(new RunningAnimation(runner, runner.getAnimationType())); } /** /** * @see InsetsState#calculateInsets * @see InsetsState#calculateInsets */ */ Loading @@ -785,7 +824,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation /** /** * @see InsetsState#calculateVisibleInsets(Rect, int) * @see InsetsState#calculateVisibleInsets(Rect, int) */ */ public Rect calculateVisibleInsets(@SoftInputModeFlags int softInputMode) { public Insets calculateVisibleInsets(@SoftInputModeFlags int softInputMode) { return mState.calculateVisibleInsets(mFrame, softInputMode); return mState.calculateVisibleInsets(mFrame, softInputMode); } } Loading Loading @@ -1474,12 +1513,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting @VisibleForTesting @Override @Override public void startAnimation(InsetsAnimationControlImpl controller, public <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController> WindowInsetsAnimationControlListener listener, int types, void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds) { WindowInsetsAnimation animation, Bounds bounds) { mHost.dispatchWindowInsetsAnimationPrepare(animation); mHost.dispatchWindowInsetsAnimationPrepare(animation); mHost.addOnPreDrawRunnable(() -> { mHost.addOnPreDrawRunnable(() -> { if (controller.isCancelled()) { if (runner.isCancelled()) { if (WARN) Log.w(TAG, "startAnimation canceled before preDraw"); if (WARN) Log.w(TAG, "startAnimation canceled before preDraw"); return; return; } } Loading @@ -1487,15 +1526,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation "InsetsAnimation: " + WindowInsets.Type.toString(types), types); "InsetsAnimation: " + WindowInsets.Type.toString(types), types); for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { RunningAnimation runningAnimation = mRunningAnimations.get(i); RunningAnimation runningAnimation = mRunningAnimations.get(i); if (runningAnimation.runner == controller) { if (runningAnimation.runner == runner) { runningAnimation.startDispatched = true; runningAnimation.startDispatched = true; } } } } Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.pendingAnim", 0); Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.pendingAnim", 0); mHost.dispatchWindowInsetsAnimationStart(animation, bounds); mHost.dispatchWindowInsetsAnimationStart(animation, bounds); mStartingAnimation = true; mStartingAnimation = true; controller.mReadyDispatched = true; runner.setReadyDispatched(true); listener.onReady(controller, types); listener.onReady(runner, types); mStartingAnimation = false; mStartingAnimation = false; }); }); } } Loading core/java/android/view/InsetsResizeAnimationRunner.java 0 → 100644 +235 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import static android.view.InsetsAnimationControlImplProto.CURRENT_ALPHA; import static android.view.InsetsAnimationControlImplProto.IS_CANCELLED; import static android.view.InsetsAnimationControlImplProto.IS_FINISHED; import static android.view.InsetsAnimationControlImplProto.PENDING_ALPHA; import static android.view.InsetsAnimationControlImplProto.PENDING_FRACTION; import static android.view.InsetsAnimationControlImplProto.PENDING_INSETS; import static android.view.InsetsAnimationControlImplProto.SHOWN_ON_FINISH; import static android.view.InsetsAnimationControlImplProto.TMP_MATRIX; import static android.view.InsetsController.ANIMATION_TYPE_RESIZE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.graphics.Insets; import android.graphics.Rect; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation.Bounds; import android.view.animation.Interpolator; /** * Runs a fake animation of resizing insets to produce insets animation callbacks. * @hide */ public class InsetsResizeAnimationRunner implements InsetsAnimationControlRunner, WindowInsetsAnimationController, WindowInsetsAnimationControlListener { private final InsetsState mFromState; private final InsetsState mToState; private final @InsetsType int mTypes; private final WindowInsetsAnimation mAnimation; private final InsetsAnimationControlCallbacks mController; private ValueAnimator mAnimator; private boolean mCancelled; private boolean mFinished; public InsetsResizeAnimationRunner(Rect frame, InsetsState fromState, InsetsState toState, Interpolator interpolator, long duration, @InsetsType int types, InsetsAnimationControlCallbacks controller) { mFromState = fromState; mToState = toState; mTypes = types; mController = controller; mAnimation = new WindowInsetsAnimation(types, interpolator, duration); mAnimation.setAlpha(1f); final Insets fromInsets = fromState.calculateInsets( frame, types, false /* ignoreVisibility */); final Insets toInsets = toState.calculateInsets( frame, types, false /* ignoreVisibility */); controller.startAnimation(this, this, types, mAnimation, new Bounds(Insets.min(fromInsets, toInsets), Insets.max(fromInsets, toInsets))); } @Override public int getTypes() { return mTypes; } @Override public int getControllingTypes() { return mTypes; } @Override public WindowInsetsAnimation getAnimation() { return mAnimation; } @Override public int getAnimationType() { return ANIMATION_TYPE_RESIZE; } @Override public void cancel() { if (mCancelled || mFinished) { return; } mCancelled = true; if (mAnimator != null) { mAnimator.cancel(); } } @Override public boolean isCancelled() { return mCancelled; } @Override public void onReady(WindowInsetsAnimationController controller, int types) { if (mCancelled) { return; } mAnimator = ValueAnimator.ofFloat(0f, 1f); mAnimator.setDuration(mAnimation.getDurationMillis()); mAnimator.addUpdateListener(animation -> { mAnimation.setFraction(animation.getAnimatedFraction()); mController.scheduleApplyChangeInsets(InsetsResizeAnimationRunner.this); }); mAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mFinished = true; mController.scheduleApplyChangeInsets(InsetsResizeAnimationRunner.this); } }); mAnimator.start(); } @Override public boolean applyChangeInsets(InsetsState outState) { final float fraction = mAnimation.getInterpolatedFraction(); for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) { final InsetsSource fromSource = mFromState.peekSource(type); final InsetsSource toSource = mToState.peekSource(type); if (fromSource == null || toSource == null) { continue; } final Rect fromFrame = fromSource.getFrame(); final Rect toFrame = toSource.getFrame(); final Rect frame = new Rect( (int) (fromFrame.left + fraction * (toFrame.left - fromFrame.left)), (int) (fromFrame.top + fraction * (toFrame.top - fromFrame.top)), (int) (fromFrame.right + fraction * (toFrame.right - fromFrame.right)), (int) (fromFrame.bottom + fraction * (toFrame.bottom - fromFrame.bottom))); final InsetsSource source = new InsetsSource(type); source.setFrame(frame); source.setVisible(toSource.isVisible()); outState.addSource(source); } if (mFinished) { mController.notifyFinished(this, true /* shown */); } return mFinished; } @Override public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(IS_CANCELLED, mCancelled); proto.write(IS_FINISHED, mFinished); proto.write(TMP_MATRIX, "null"); proto.write(PENDING_INSETS, "null"); proto.write(PENDING_FRACTION, mAnimation.getInterpolatedFraction()); proto.write(SHOWN_ON_FINISH, true); proto.write(CURRENT_ALPHA, 1f); proto.write(PENDING_ALPHA, 1f); proto.end(token); } @Override public Insets getHiddenStateInsets() { return Insets.NONE; } @Override public Insets getShownStateInsets() { return Insets.NONE; } @Override public Insets getCurrentInsets() { return Insets.NONE; } @Override public float getCurrentFraction() { return 0; } @Override public float getCurrentAlpha() { return 0; } @Override public void setInsetsAndAlpha(Insets insets, float alpha, float fraction) { } @Override public void finish(boolean shown) { } @Override public boolean isFinished() { return false; } @Override public void notifyControlRevoked(int types) { } @Override public void updateSurfacePosition(SparseArray<InsetsSourceControl> controls) { } @Override public boolean hasZeroInsetsIme() { return false; } @Override public void setReadyDispatched(boolean dispatched) { } @Override public void onFinished(WindowInsetsAnimationController controller) { } @Override public void onCancelled(WindowInsetsAnimationController controller) { } } Loading
core/java/android/view/InsetsAnimationControlCallbacks.java +5 −5 Original line number Original line Diff line number Diff line Loading @@ -20,7 +20,8 @@ import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation.Bounds; import android.view.WindowInsetsAnimation.Bounds; /** /** * Provide an interface to let InsetsAnimationControlImpl call back into its owner. * Provide an interface to let InsetsAnimationControlImpl and InsetsResizeAnimationRunner call back * into its owner. * @hide * @hide */ */ public interface InsetsAnimationControlCallbacks { public interface InsetsAnimationControlCallbacks { Loading @@ -34,10 +35,9 @@ public interface InsetsAnimationControlCallbacks { * <li>Dispatch {@link WindowInsetsAnimationControlListener#onReady}</li> * <li>Dispatch {@link WindowInsetsAnimationControlListener#onReady}</li> * </ul> * </ul> */ */ void startAnimation(InsetsAnimationControlImpl controller, <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController> WindowInsetsAnimationControlListener listener, int types, void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, WindowInsetsAnimation animation, Bounds bounds); Bounds bounds); /** /** * Schedule the apply by posting the animation callback. * Schedule the apply by posting the animation callback. Loading
core/java/android/view/InsetsAnimationControlImpl.java +6 −1 Original line number Original line Diff line number Diff line Loading @@ -105,7 +105,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll private float mCurrentAlpha = 1.0f; private float mCurrentAlpha = 1.0f; private float mPendingAlpha = 1.0f; private float mPendingAlpha = 1.0f; @VisibleForTesting(visibility = PACKAGE) @VisibleForTesting(visibility = PACKAGE) public boolean mReadyDispatched; private boolean mReadyDispatched; private Boolean mPerceptible; private Boolean mPerceptible; @VisibleForTesting @VisibleForTesting Loading Loading @@ -169,6 +169,11 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll return mHasZeroInsetsIme; return mHasZeroInsetsIme; } } @Override public void setReadyDispatched(boolean dispatched) { mReadyDispatched = dispatched; } @Override @Override public Insets getHiddenStateInsets() { public Insets getHiddenStateInsets() { return mHiddenInsets; return mHiddenInsets; Loading
core/java/android/view/InsetsAnimationThreadControlRunner.java +2 −2 Original line number Original line Diff line number Diff line Loading @@ -54,8 +54,8 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro @Override @Override @UiThread @UiThread public void startAnimation(InsetsAnimationControlImpl controller, public <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController> WindowInsetsAnimationControlListener listener, int types, void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds) { WindowInsetsAnimation animation, Bounds bounds) { // Animation will be started in constructor already. // Animation will be started in constructor already. } } Loading
core/java/android/view/InsetsController.java +64 −25 Original line number Original line Diff line number Diff line Loading @@ -205,6 +205,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private static final int ANIMATION_DURATION_FADE_IN_MS = 500; private static final int ANIMATION_DURATION_FADE_IN_MS = 500; private static final int ANIMATION_DURATION_FADE_OUT_MS = 1500; private static final int ANIMATION_DURATION_FADE_OUT_MS = 1500; /** Visible for WindowManagerWrapper */ public static final int ANIMATION_DURATION_RESIZE = 300; private static final int ANIMATION_DELAY_DIM_MS = 500; private static final int ANIMATION_DELAY_DIM_MS = 500; private static final int ANIMATION_DURATION_SYNC_IME_MS = 285; private static final int ANIMATION_DURATION_SYNC_IME_MS = 285; Loading Loading @@ -235,6 +238,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR = private static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR = new PathInterpolator(0.4f, 0f, 1f, 1f); new PathInterpolator(0.4f, 0f, 1f, 1f); /** Visible for WindowManagerWrapper */ public static final Interpolator RESIZE_INTERPOLATOR = new LinearInterpolator(); /** The amount IME will move up/down when animating in floating mode. */ /** The amount IME will move up/down when animating in floating mode. */ private static final int FLOATING_IME_BOTTOM_INSET_DP = -80; private static final int FLOATING_IME_BOTTOM_INSET_DP = -80; Loading Loading @@ -288,9 +294,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting @VisibleForTesting public static final int ANIMATION_TYPE_USER = 2; public static final int ANIMATION_TYPE_USER = 2; /** Running animation will resize insets */ @VisibleForTesting public static final int ANIMATION_TYPE_RESIZE = 3; @Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE) @IntDef(value = {ANIMATION_TYPE_NONE, ANIMATION_TYPE_SHOW, ANIMATION_TYPE_HIDE, @IntDef(value = {ANIMATION_TYPE_NONE, ANIMATION_TYPE_SHOW, ANIMATION_TYPE_HIDE, ANIMATION_TYPE_USER}) ANIMATION_TYPE_USER, ANIMATION_TYPE_RESIZE}) @interface AnimationType { @interface AnimationType { } } Loading Loading @@ -320,7 +330,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private final boolean mDisable; private final boolean mDisable; private final int mFloatingImeBottomInset; private final int mFloatingImeBottomInset; private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = private final ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal = new ThreadLocal<AnimationHandler>() { new ThreadLocal<AnimationHandler>() { @Override @Override protected AnimationHandler initialValue() { protected AnimationHandler initialValue() { Loading Loading @@ -550,7 +560,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>(); private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>(); private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>(); private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>(); private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>(); private final ArraySet<InsetsSourceConsumer> mRequestedVisibilityChanged = new ArraySet<>(); private final ArraySet<InsetsSourceConsumer> mRequestedVisibilityChanged = new ArraySet<>(); private WindowInsets mLastInsets; private WindowInsets mLastInsets; Loading @@ -570,7 +579,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private int mCaptionInsetsHeight = 0; private int mCaptionInsetsHeight = 0; private boolean mAnimationsDisabled; private boolean mAnimationsDisabled; private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest; private final Runnable mPendingControlTimeout = this::abortPendingImeControlRequest; private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners = new ArrayList<>(); = new ArrayList<>(); Loading @@ -580,7 +589,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation /** Set of inset types which cannot be controlled by the user animation */ /** Set of inset types which cannot be controlled by the user animation */ private @InsetsType int mDisabledUserAnimationInsetsTypes; private @InsetsType int mDisabledUserAnimationInsetsTypes; private Runnable mInvokeControllableInsetsChangedListeners = private final Runnable mInvokeControllableInsetsChangedListeners = this::invokeControllableInsetsChangedListeners; this::invokeControllableInsetsChangedListeners; public InsetsController(Host host) { public InsetsController(Host host) { Loading Loading @@ -608,23 +617,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } final List<WindowInsetsAnimation> runningAnimations = new ArrayList<>(); final List<WindowInsetsAnimation> runningAnimations = new ArrayList<>(); final List<WindowInsetsAnimation> finishedAnimations = new ArrayList<>(); final InsetsState state = new InsetsState(mState, true /* copySources */); final InsetsState state = new InsetsState(mState, true /* copySources */); for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { RunningAnimation runningAnimation = mRunningAnimations.get(i); RunningAnimation runningAnimation = mRunningAnimations.get(i); if (DEBUG) Log.d(TAG, "Running animation type: " + runningAnimation.type); if (DEBUG) Log.d(TAG, "Running animation type: " + runningAnimation.type); InsetsAnimationControlRunner runner = runningAnimation.runner; final InsetsAnimationControlRunner runner = runningAnimation.runner; if (runner instanceof InsetsAnimationControlImpl) { if (runner instanceof WindowInsetsAnimationController) { InsetsAnimationControlImpl control = (InsetsAnimationControlImpl) runner; // Keep track of running animation to be dispatched. Aggregate it here such that // Keep track of running animation to be dispatched. Aggregate it here such that // if it gets finished within applyChangeInsets we still dispatch it to // if it gets finished within applyChangeInsets we still dispatch it to // onProgress. // onProgress. if (runningAnimation.startDispatched) { if (runningAnimation.startDispatched) { runningAnimations.add(control.getAnimation()); runningAnimations.add(runner.getAnimation()); } } if (control.applyChangeInsets(state)) { if (((WindowInsetsAnimationController) runner).applyChangeInsets(state)) { mTmpFinishedControls.add(control); finishedAnimations.add(runner.getAnimation()); } } } } } } Loading @@ -642,10 +651,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } } } } for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) { for (int i = finishedAnimations.size() - 1; i >= 0; i--) { dispatchAnimationEnd(mTmpFinishedControls.get(i).getAnimation()); dispatchAnimationEnd(finishedAnimations.get(i)); } } mTmpFinishedControls.clear(); }; }; } } Loading Loading @@ -691,15 +699,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation true /* excludeInvisibleIme */)) { true /* excludeInvisibleIme */)) { if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); mHost.notifyInsetsChanged(); mHost.notifyInsetsChanged(); startResizingAnimationIfNeeded(lastState); } } return true; return true; } } private void updateState(InsetsState newState) { private void updateState(InsetsState newState) { mState.setDisplayFrame(newState.getDisplayFrame()); mState.set(newState, 0 /* types */); mState.setDisplayCutout(newState.getDisplayCutout()); mState.setRoundedCorners(newState.getRoundedCorners()); mState.setPrivacyIndicatorBounds(newState.getPrivacyIndicatorBounds()); @InsetsType int disabledUserAnimationTypes = 0; @InsetsType int disabledUserAnimationTypes = 0; @InsetsType int[] cancelledUserAnimationTypes = {0}; @InsetsType int[] cancelledUserAnimationTypes = {0}; for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) { for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) { Loading Loading @@ -764,6 +770,39 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return false; return false; } } private void startResizingAnimationIfNeeded(InsetsState fromState) { if (!fromState.getDisplayFrame().equals(mState.getDisplayFrame())) { return; } @InsetsType int types = 0; InsetsState toState = null; final ArraySet<Integer> internalTypes = InsetsState.toInternalType(Type.systemBars()); for (int i = internalTypes.size() - 1; i >= 0; i--) { final @InternalInsetsType int type = internalTypes.valueAt(i); final InsetsSource fromSource = fromState.peekSource(type); final InsetsSource toSource = mState.peekSource(type); if (fromSource != null && toSource != null && fromSource.isVisible() && toSource.isVisible() && !fromSource.getFrame().equals(toSource.getFrame()) && (Rect.intersects(mFrame, fromSource.getFrame()) || Rect.intersects(mFrame, toSource.getFrame()))) { types |= InsetsState.toPublicType(toSource.getType()); if (toState == null) { toState = new InsetsState(); } toState.addSource(new InsetsSource(toSource)); } } if (types == 0) { return; } cancelExistingControllers(types); final InsetsAnimationControlRunner runner = new InsetsResizeAnimationRunner( mFrame, fromState, toState, RESIZE_INTERPOLATOR, ANIMATION_DURATION_RESIZE, types, this); mRunningAnimations.add(new RunningAnimation(runner, runner.getAnimationType())); } /** /** * @see InsetsState#calculateInsets * @see InsetsState#calculateInsets */ */ Loading @@ -785,7 +824,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation /** /** * @see InsetsState#calculateVisibleInsets(Rect, int) * @see InsetsState#calculateVisibleInsets(Rect, int) */ */ public Rect calculateVisibleInsets(@SoftInputModeFlags int softInputMode) { public Insets calculateVisibleInsets(@SoftInputModeFlags int softInputMode) { return mState.calculateVisibleInsets(mFrame, softInputMode); return mState.calculateVisibleInsets(mFrame, softInputMode); } } Loading Loading @@ -1474,12 +1513,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting @VisibleForTesting @Override @Override public void startAnimation(InsetsAnimationControlImpl controller, public <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController> WindowInsetsAnimationControlListener listener, int types, void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types, WindowInsetsAnimation animation, Bounds bounds) { WindowInsetsAnimation animation, Bounds bounds) { mHost.dispatchWindowInsetsAnimationPrepare(animation); mHost.dispatchWindowInsetsAnimationPrepare(animation); mHost.addOnPreDrawRunnable(() -> { mHost.addOnPreDrawRunnable(() -> { if (controller.isCancelled()) { if (runner.isCancelled()) { if (WARN) Log.w(TAG, "startAnimation canceled before preDraw"); if (WARN) Log.w(TAG, "startAnimation canceled before preDraw"); return; return; } } Loading @@ -1487,15 +1526,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation "InsetsAnimation: " + WindowInsets.Type.toString(types), types); "InsetsAnimation: " + WindowInsets.Type.toString(types), types); for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { for (int i = mRunningAnimations.size() - 1; i >= 0; i--) { RunningAnimation runningAnimation = mRunningAnimations.get(i); RunningAnimation runningAnimation = mRunningAnimations.get(i); if (runningAnimation.runner == controller) { if (runningAnimation.runner == runner) { runningAnimation.startDispatched = true; runningAnimation.startDispatched = true; } } } } Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.pendingAnim", 0); Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.pendingAnim", 0); mHost.dispatchWindowInsetsAnimationStart(animation, bounds); mHost.dispatchWindowInsetsAnimationStart(animation, bounds); mStartingAnimation = true; mStartingAnimation = true; controller.mReadyDispatched = true; runner.setReadyDispatched(true); listener.onReady(controller, types); listener.onReady(runner, types); mStartingAnimation = false; mStartingAnimation = false; }); }); } } Loading
core/java/android/view/InsetsResizeAnimationRunner.java 0 → 100644 +235 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.view; import static android.view.InsetsAnimationControlImplProto.CURRENT_ALPHA; import static android.view.InsetsAnimationControlImplProto.IS_CANCELLED; import static android.view.InsetsAnimationControlImplProto.IS_FINISHED; import static android.view.InsetsAnimationControlImplProto.PENDING_ALPHA; import static android.view.InsetsAnimationControlImplProto.PENDING_FRACTION; import static android.view.InsetsAnimationControlImplProto.PENDING_INSETS; import static android.view.InsetsAnimationControlImplProto.SHOWN_ON_FINISH; import static android.view.InsetsAnimationControlImplProto.TMP_MATRIX; import static android.view.InsetsController.ANIMATION_TYPE_RESIZE; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.graphics.Insets; import android.graphics.Rect; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowInsetsAnimation.Bounds; import android.view.animation.Interpolator; /** * Runs a fake animation of resizing insets to produce insets animation callbacks. * @hide */ public class InsetsResizeAnimationRunner implements InsetsAnimationControlRunner, WindowInsetsAnimationController, WindowInsetsAnimationControlListener { private final InsetsState mFromState; private final InsetsState mToState; private final @InsetsType int mTypes; private final WindowInsetsAnimation mAnimation; private final InsetsAnimationControlCallbacks mController; private ValueAnimator mAnimator; private boolean mCancelled; private boolean mFinished; public InsetsResizeAnimationRunner(Rect frame, InsetsState fromState, InsetsState toState, Interpolator interpolator, long duration, @InsetsType int types, InsetsAnimationControlCallbacks controller) { mFromState = fromState; mToState = toState; mTypes = types; mController = controller; mAnimation = new WindowInsetsAnimation(types, interpolator, duration); mAnimation.setAlpha(1f); final Insets fromInsets = fromState.calculateInsets( frame, types, false /* ignoreVisibility */); final Insets toInsets = toState.calculateInsets( frame, types, false /* ignoreVisibility */); controller.startAnimation(this, this, types, mAnimation, new Bounds(Insets.min(fromInsets, toInsets), Insets.max(fromInsets, toInsets))); } @Override public int getTypes() { return mTypes; } @Override public int getControllingTypes() { return mTypes; } @Override public WindowInsetsAnimation getAnimation() { return mAnimation; } @Override public int getAnimationType() { return ANIMATION_TYPE_RESIZE; } @Override public void cancel() { if (mCancelled || mFinished) { return; } mCancelled = true; if (mAnimator != null) { mAnimator.cancel(); } } @Override public boolean isCancelled() { return mCancelled; } @Override public void onReady(WindowInsetsAnimationController controller, int types) { if (mCancelled) { return; } mAnimator = ValueAnimator.ofFloat(0f, 1f); mAnimator.setDuration(mAnimation.getDurationMillis()); mAnimator.addUpdateListener(animation -> { mAnimation.setFraction(animation.getAnimatedFraction()); mController.scheduleApplyChangeInsets(InsetsResizeAnimationRunner.this); }); mAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mFinished = true; mController.scheduleApplyChangeInsets(InsetsResizeAnimationRunner.this); } }); mAnimator.start(); } @Override public boolean applyChangeInsets(InsetsState outState) { final float fraction = mAnimation.getInterpolatedFraction(); for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) { final InsetsSource fromSource = mFromState.peekSource(type); final InsetsSource toSource = mToState.peekSource(type); if (fromSource == null || toSource == null) { continue; } final Rect fromFrame = fromSource.getFrame(); final Rect toFrame = toSource.getFrame(); final Rect frame = new Rect( (int) (fromFrame.left + fraction * (toFrame.left - fromFrame.left)), (int) (fromFrame.top + fraction * (toFrame.top - fromFrame.top)), (int) (fromFrame.right + fraction * (toFrame.right - fromFrame.right)), (int) (fromFrame.bottom + fraction * (toFrame.bottom - fromFrame.bottom))); final InsetsSource source = new InsetsSource(type); source.setFrame(frame); source.setVisible(toSource.isVisible()); outState.addSource(source); } if (mFinished) { mController.notifyFinished(this, true /* shown */); } return mFinished; } @Override public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(IS_CANCELLED, mCancelled); proto.write(IS_FINISHED, mFinished); proto.write(TMP_MATRIX, "null"); proto.write(PENDING_INSETS, "null"); proto.write(PENDING_FRACTION, mAnimation.getInterpolatedFraction()); proto.write(SHOWN_ON_FINISH, true); proto.write(CURRENT_ALPHA, 1f); proto.write(PENDING_ALPHA, 1f); proto.end(token); } @Override public Insets getHiddenStateInsets() { return Insets.NONE; } @Override public Insets getShownStateInsets() { return Insets.NONE; } @Override public Insets getCurrentInsets() { return Insets.NONE; } @Override public float getCurrentFraction() { return 0; } @Override public float getCurrentAlpha() { return 0; } @Override public void setInsetsAndAlpha(Insets insets, float alpha, float fraction) { } @Override public void finish(boolean shown) { } @Override public boolean isFinished() { return false; } @Override public void notifyControlRevoked(int types) { } @Override public void updateSurfacePosition(SparseArray<InsetsSourceControl> controls) { } @Override public boolean hasZeroInsetsIme() { return false; } @Override public void setReadyDispatched(boolean dispatched) { } @Override public void onFinished(WindowInsetsAnimationController controller) { } @Override public void onCancelled(WindowInsetsAnimationController controller) { } }