Merge "Invoke insets animation callbacks when resizing system bars" into sc-v2-dev
This commit is contained in:
@@ -20,7 +20,8 @@ import android.view.WindowInsets.Type.InsetsType;
|
||||
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
|
||||
*/
|
||||
public interface InsetsAnimationControlCallbacks {
|
||||
@@ -34,10 +35,9 @@ public interface InsetsAnimationControlCallbacks {
|
||||
* <li>Dispatch {@link WindowInsetsAnimationControlListener#onReady}</li>
|
||||
* </ul>
|
||||
*/
|
||||
void startAnimation(InsetsAnimationControlImpl controller,
|
||||
WindowInsetsAnimationControlListener listener, int types,
|
||||
WindowInsetsAnimation animation,
|
||||
Bounds bounds);
|
||||
<T extends InsetsAnimationControlRunner & WindowInsetsAnimationController>
|
||||
void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
|
||||
WindowInsetsAnimation animation, Bounds bounds);
|
||||
|
||||
/**
|
||||
* Schedule the apply by posting the animation callback.
|
||||
|
||||
@@ -105,7 +105,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
|
||||
private float mCurrentAlpha = 1.0f;
|
||||
private float mPendingAlpha = 1.0f;
|
||||
@VisibleForTesting(visibility = PACKAGE)
|
||||
public boolean mReadyDispatched;
|
||||
private boolean mReadyDispatched;
|
||||
private Boolean mPerceptible;
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -169,6 +169,11 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
|
||||
return mHasZeroInsetsIme;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadyDispatched(boolean dispatched) {
|
||||
mReadyDispatched = dispatched;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getHiddenStateInsets() {
|
||||
return mHiddenInsets;
|
||||
|
||||
@@ -54,8 +54,8 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro
|
||||
|
||||
@Override
|
||||
@UiThread
|
||||
public void startAnimation(InsetsAnimationControlImpl controller,
|
||||
WindowInsetsAnimationControlListener listener, int types,
|
||||
public <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController>
|
||||
void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
|
||||
WindowInsetsAnimation animation, Bounds bounds) {
|
||||
// Animation will be started in constructor already.
|
||||
}
|
||||
|
||||
@@ -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_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_DURATION_SYNC_IME_MS = 285;
|
||||
@@ -235,6 +238,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
private static final Interpolator FAST_OUT_LINEAR_IN_INTERPOLATOR =
|
||||
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. */
|
||||
private static final int FLOATING_IME_BOTTOM_INSET_DP = -80;
|
||||
|
||||
@@ -288,9 +294,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
@VisibleForTesting
|
||||
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)
|
||||
@IntDef(value = {ANIMATION_TYPE_NONE, ANIMATION_TYPE_SHOW, ANIMATION_TYPE_HIDE,
|
||||
ANIMATION_TYPE_USER})
|
||||
ANIMATION_TYPE_USER, ANIMATION_TYPE_RESIZE})
|
||||
@interface AnimationType {
|
||||
}
|
||||
|
||||
@@ -320,7 +330,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
private final boolean mDisable;
|
||||
private final int mFloatingImeBottomInset;
|
||||
|
||||
private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
|
||||
private final ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
|
||||
new ThreadLocal<AnimationHandler>() {
|
||||
@Override
|
||||
protected AnimationHandler initialValue() {
|
||||
@@ -550,7 +560,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
|
||||
private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>();
|
||||
private final ArrayList<RunningAnimation> mRunningAnimations = new ArrayList<>();
|
||||
private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>();
|
||||
private final ArraySet<InsetsSourceConsumer> mRequestedVisibilityChanged = new ArraySet<>();
|
||||
private WindowInsets mLastInsets;
|
||||
|
||||
@@ -570,7 +579,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
private int mCaptionInsetsHeight = 0;
|
||||
private boolean mAnimationsDisabled;
|
||||
|
||||
private Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
|
||||
private final Runnable mPendingControlTimeout = this::abortPendingImeControlRequest;
|
||||
private final ArrayList<OnControllableInsetsChangedListener> mControllableInsetsChangedListeners
|
||||
= new ArrayList<>();
|
||||
|
||||
@@ -580,7 +589,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
/** Set of inset types which cannot be controlled by the user animation */
|
||||
private @InsetsType int mDisabledUserAnimationInsetsTypes;
|
||||
|
||||
private Runnable mInvokeControllableInsetsChangedListeners =
|
||||
private final Runnable mInvokeControllableInsetsChangedListeners =
|
||||
this::invokeControllableInsetsChangedListeners;
|
||||
|
||||
public InsetsController(Host host) {
|
||||
@@ -608,23 +617,23 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
}
|
||||
|
||||
final List<WindowInsetsAnimation> runningAnimations = new ArrayList<>();
|
||||
final List<WindowInsetsAnimation> finishedAnimations = new ArrayList<>();
|
||||
final InsetsState state = new InsetsState(mState, true /* copySources */);
|
||||
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
|
||||
RunningAnimation runningAnimation = mRunningAnimations.get(i);
|
||||
if (DEBUG) Log.d(TAG, "Running animation type: " + runningAnimation.type);
|
||||
InsetsAnimationControlRunner runner = runningAnimation.runner;
|
||||
if (runner instanceof InsetsAnimationControlImpl) {
|
||||
InsetsAnimationControlImpl control = (InsetsAnimationControlImpl) runner;
|
||||
final InsetsAnimationControlRunner runner = runningAnimation.runner;
|
||||
if (runner instanceof WindowInsetsAnimationController) {
|
||||
|
||||
// Keep track of running animation to be dispatched. Aggregate it here such that
|
||||
// if it gets finished within applyChangeInsets we still dispatch it to
|
||||
// onProgress.
|
||||
if (runningAnimation.startDispatched) {
|
||||
runningAnimations.add(control.getAnimation());
|
||||
runningAnimations.add(runner.getAnimation());
|
||||
}
|
||||
|
||||
if (control.applyChangeInsets(state)) {
|
||||
mTmpFinishedControls.add(control);
|
||||
if (((WindowInsetsAnimationController) runner).applyChangeInsets(state)) {
|
||||
finishedAnimations.add(runner.getAnimation());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -642,10 +651,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) {
|
||||
dispatchAnimationEnd(mTmpFinishedControls.get(i).getAnimation());
|
||||
for (int i = finishedAnimations.size() - 1; i >= 0; i--) {
|
||||
dispatchAnimationEnd(finishedAnimations.get(i));
|
||||
}
|
||||
mTmpFinishedControls.clear();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -691,15 +699,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
true /* excludeInvisibleIme */)) {
|
||||
if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged");
|
||||
mHost.notifyInsetsChanged();
|
||||
startResizingAnimationIfNeeded(lastState);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateState(InsetsState newState) {
|
||||
mState.setDisplayFrame(newState.getDisplayFrame());
|
||||
mState.setDisplayCutout(newState.getDisplayCutout());
|
||||
mState.setRoundedCorners(newState.getRoundedCorners());
|
||||
mState.setPrivacyIndicatorBounds(newState.getPrivacyIndicatorBounds());
|
||||
mState.set(newState, 0 /* types */);
|
||||
@InsetsType int disabledUserAnimationTypes = 0;
|
||||
@InsetsType int[] cancelledUserAnimationTypes = {0};
|
||||
for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
|
||||
@@ -764,6 +770,39 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
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
|
||||
*/
|
||||
@@ -785,7 +824,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
/**
|
||||
* @see InsetsState#calculateVisibleInsets(Rect, int)
|
||||
*/
|
||||
public Rect calculateVisibleInsets(@SoftInputModeFlags int softInputMode) {
|
||||
public Insets calculateVisibleInsets(@SoftInputModeFlags int softInputMode) {
|
||||
return mState.calculateVisibleInsets(mFrame, softInputMode);
|
||||
}
|
||||
|
||||
@@ -1474,12 +1513,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
|
||||
@VisibleForTesting
|
||||
@Override
|
||||
public void startAnimation(InsetsAnimationControlImpl controller,
|
||||
WindowInsetsAnimationControlListener listener, int types,
|
||||
public <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController>
|
||||
void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
|
||||
WindowInsetsAnimation animation, Bounds bounds) {
|
||||
mHost.dispatchWindowInsetsAnimationPrepare(animation);
|
||||
mHost.addOnPreDrawRunnable(() -> {
|
||||
if (controller.isCancelled()) {
|
||||
if (runner.isCancelled()) {
|
||||
if (WARN) Log.w(TAG, "startAnimation canceled before preDraw");
|
||||
return;
|
||||
}
|
||||
@@ -1487,15 +1526,15 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
|
||||
"InsetsAnimation: " + WindowInsets.Type.toString(types), types);
|
||||
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
|
||||
RunningAnimation runningAnimation = mRunningAnimations.get(i);
|
||||
if (runningAnimation.runner == controller) {
|
||||
if (runningAnimation.runner == runner) {
|
||||
runningAnimation.startDispatched = true;
|
||||
}
|
||||
}
|
||||
Trace.asyncTraceEnd(TRACE_TAG_VIEW, "IC.pendingAnim", 0);
|
||||
mHost.dispatchWindowInsetsAnimationStart(animation, bounds);
|
||||
mStartingAnimation = true;
|
||||
controller.mReadyDispatched = true;
|
||||
listener.onReady(controller, types);
|
||||
runner.setReadyDispatched(true);
|
||||
listener.onReady(runner, types);
|
||||
mStartingAnimation = false;
|
||||
});
|
||||
}
|
||||
|
||||
235
core/java/android/view/InsetsResizeAnimationRunner.java
Normal file
235
core/java/android/view/InsetsResizeAnimationRunner.java
Normal file
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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) {
|
||||
}
|
||||
}
|
||||
@@ -301,7 +301,7 @@ public class InsetsState implements Parcelable {
|
||||
return mPrivacyIndicatorBounds.inset(insetLeft, insetTop, insetRight, insetBottom);
|
||||
}
|
||||
|
||||
public Rect calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility) {
|
||||
public Insets calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility) {
|
||||
Insets insets = Insets.NONE;
|
||||
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
|
||||
InsetsSource source = mSources[type];
|
||||
@@ -314,10 +314,10 @@ public class InsetsState implements Parcelable {
|
||||
}
|
||||
insets = Insets.max(source.calculateInsets(frame, ignoreVisibility), insets);
|
||||
}
|
||||
return insets.toRect();
|
||||
return insets;
|
||||
}
|
||||
|
||||
public Rect calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
|
||||
public Insets calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) {
|
||||
Insets insets = Insets.NONE;
|
||||
for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
|
||||
InsetsSource source = mSources[type];
|
||||
@@ -332,7 +332,7 @@ public class InsetsState implements Parcelable {
|
||||
}
|
||||
insets = Insets.max(source.calculateVisibleInsets(frame), insets);
|
||||
}
|
||||
return insets.toRect();
|
||||
return insets;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2467,7 +2467,7 @@ public final class ViewRootImpl implements ViewParent,
|
||||
mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect());
|
||||
mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect());
|
||||
mAttachInfo.mVisibleInsets.set(mInsetsController.calculateVisibleInsets(
|
||||
mWindowAttributes.softInputMode));
|
||||
mWindowAttributes.softInputMode).toRect());
|
||||
}
|
||||
return mLastWindowInsets;
|
||||
}
|
||||
|
||||
@@ -188,4 +188,20 @@ public interface WindowInsetsAnimationController {
|
||||
* fullscreen or non-overlapping).
|
||||
*/
|
||||
boolean hasZeroInsetsIme();
|
||||
|
||||
/**
|
||||
* Flags whether {@link WindowInsetsAnimationControlListener#onReady(
|
||||
* WindowInsetsAnimationController, int)} has been invoked.
|
||||
* @hide
|
||||
*/
|
||||
void setReadyDispatched(boolean dispatched);
|
||||
|
||||
/**
|
||||
* Returns the {@link InsetsState} based on the current animation progress.
|
||||
*
|
||||
* @param outState the insets state which matches the current animation progress.
|
||||
* @return {@code true} if the animation has been finished; {@code false} otherwise.
|
||||
* @hide
|
||||
*/
|
||||
boolean applyChangeInsets(InsetsState outState);
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ public class InsetsAnimationControlImplTest {
|
||||
new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
|
||||
mMockController, 10 /* durationMs */, new LinearInterpolator(),
|
||||
0 /* animationType */, 0 /* layoutInsetsDuringAnimation */, null /* translator */);
|
||||
mController.mReadyDispatched = true;
|
||||
mController.setReadyDispatched(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -197,7 +197,7 @@ public class InsetsAnimationControlImplTest {
|
||||
|
||||
@Test
|
||||
public void testCancelled_beforeReadyDispatched() {
|
||||
mController.mReadyDispatched = false;
|
||||
mController.setReadyDispatched(false);
|
||||
mController.cancel();
|
||||
assertFalse(mController.isReady());
|
||||
assertFalse(mController.isFinished());
|
||||
|
||||
@@ -19,13 +19,17 @@ package android.view;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
import static android.view.InsetsController.ANIMATION_TYPE_HIDE;
|
||||
import static android.view.InsetsController.ANIMATION_TYPE_NONE;
|
||||
import static android.view.InsetsController.ANIMATION_TYPE_RESIZE;
|
||||
import static android.view.InsetsController.ANIMATION_TYPE_SHOW;
|
||||
import static android.view.InsetsController.AnimationType;
|
||||
import static android.view.InsetsSourceConsumer.ShowResult.IME_SHOW_DELAYED;
|
||||
import static android.view.InsetsSourceConsumer.ShowResult.SHOW_IMMEDIATELY;
|
||||
import static android.view.InsetsState.FIRST_TYPE;
|
||||
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
|
||||
import static android.view.InsetsState.ITYPE_IME;
|
||||
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
|
||||
import static android.view.InsetsState.ITYPE_STATUS_BAR;
|
||||
import static android.view.InsetsState.LAST_TYPE;
|
||||
import static android.view.WindowInsets.Type.ime;
|
||||
import static android.view.WindowInsets.Type.navigationBars;
|
||||
import static android.view.WindowInsets.Type.statusBars;
|
||||
@@ -661,6 +665,97 @@ public class InsetsControllerTest {
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResizeAnimation_insetsTypes() {
|
||||
for (@InternalInsetsType int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
|
||||
final @AnimationType int expectedAnimationType =
|
||||
(InsetsState.toPublicType(type) & Type.systemBars()) != 0
|
||||
? ANIMATION_TYPE_RESIZE
|
||||
: ANIMATION_TYPE_NONE;
|
||||
doTestResizeAnimation_insetsTypes(type, expectedAnimationType);
|
||||
}
|
||||
}
|
||||
|
||||
private void doTestResizeAnimation_insetsTypes(@InternalInsetsType int type,
|
||||
@AnimationType int expectedAnimationType) {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
final InsetsState state1 = new InsetsState();
|
||||
state1.getSource(type).setVisible(true);
|
||||
state1.getSource(type).setFrame(0, 0, 500, 50);
|
||||
final InsetsState state2 = new InsetsState(state1, true /* copySources */);
|
||||
state2.getSource(type).setFrame(0, 0, 500, 60);
|
||||
final String message = "Animation type of " + InsetsState.typeToString(type) + ":";
|
||||
|
||||
// New insets source won't cause the resize animation.
|
||||
mController.onStateChanged(state1);
|
||||
assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
|
||||
|
||||
// Changing frame might cause the resize animation. This depends on the insets type.
|
||||
mController.onStateChanged(state2);
|
||||
assertEquals(message, expectedAnimationType, mController.getAnimationType(type));
|
||||
|
||||
// Cancel the existing animations for the next iteration.
|
||||
mController.cancelExistingAnimations();
|
||||
assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
|
||||
});
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResizeAnimation_displayFrame() {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
final @InternalInsetsType int type = ITYPE_STATUS_BAR;
|
||||
final InsetsState state1 = new InsetsState();
|
||||
state1.setDisplayFrame(new Rect(0, 0, 500, 1000));
|
||||
state1.getSource(type).setFrame(0, 0, 500, 50);
|
||||
final InsetsState state2 = new InsetsState(state1, true /* copySources */);
|
||||
state2.setDisplayFrame(new Rect(0, 0, 500, 1010));
|
||||
state2.getSource(type).setFrame(0, 0, 500, 60);
|
||||
final String message = "There must not be resize animation.";
|
||||
|
||||
// New insets source won't cause the resize animation.
|
||||
mController.onStateChanged(state1);
|
||||
assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
|
||||
|
||||
// Changing frame won't cause the resize animation if the display frame is also changed.
|
||||
mController.onStateChanged(state2);
|
||||
assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
|
||||
});
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResizeAnimation_visibility() {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
final @InternalInsetsType int type = ITYPE_STATUS_BAR;
|
||||
final InsetsState state1 = new InsetsState();
|
||||
state1.getSource(type).setVisible(true);
|
||||
state1.getSource(type).setFrame(0, 0, 500, 50);
|
||||
final InsetsState state2 = new InsetsState(state1, true /* copySources */);
|
||||
state2.getSource(type).setVisible(false);
|
||||
state2.getSource(type).setFrame(0, 0, 500, 60);
|
||||
final InsetsState state3 = new InsetsState(state2, true /* copySources */);
|
||||
state3.getSource(type).setVisible(true);
|
||||
state3.getSource(type).setFrame(0, 0, 500, 70);
|
||||
final String message = "There must not be resize animation.";
|
||||
|
||||
// New insets source won't cause the resize animation.
|
||||
mController.onStateChanged(state1);
|
||||
assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
|
||||
|
||||
// Changing source visibility (visible --> invisible) won't cause the resize animation.
|
||||
// The previous source and the current one must be both visible.
|
||||
mController.onStateChanged(state2);
|
||||
assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
|
||||
|
||||
// Changing source visibility (invisible --> visible) won't cause the resize animation.
|
||||
// The previous source and the current one must be both visible.
|
||||
mController.onStateChanged(state3);
|
||||
assertEquals(message, ANIMATION_TYPE_NONE, mController.getAnimationType(type));
|
||||
});
|
||||
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaptionInsetsStateAssemble() {
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
|
||||
|
||||
@@ -216,9 +216,9 @@ public class InsetsStateTest {
|
||||
mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300));
|
||||
mState.getSource(ITYPE_CAPTION_BAR).setVisible(true);
|
||||
|
||||
Rect visibleInsets = mState.calculateVisibleInsets(
|
||||
Insets visibleInsets = mState.calculateVisibleInsets(
|
||||
new Rect(0, 0, 100, 400), SOFT_INPUT_ADJUST_NOTHING);
|
||||
assertEquals(new Rect(0, 300, 0, 0), visibleInsets);
|
||||
assertEquals(Insets.of(0, 300, 0, 0), visibleInsets);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -226,9 +226,9 @@ public class InsetsStateTest {
|
||||
mState.getSource(ITYPE_CAPTION_BAR).setFrame(new Rect(0, 0, 100, 300));
|
||||
mState.getSource(ITYPE_CAPTION_BAR).setVisible(true);
|
||||
|
||||
Rect visibleInsets = mState.calculateVisibleInsets(
|
||||
Insets visibleInsets = mState.calculateVisibleInsets(
|
||||
new Rect(0, 0, 150, 400), SOFT_INPUT_ADJUST_NOTHING);
|
||||
assertEquals(new Rect(0, 300, 0, 0), visibleInsets);
|
||||
assertEquals(Insets.of(0, 300, 0, 0), visibleInsets);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -413,9 +413,9 @@ public class InsetsStateTest {
|
||||
// Make sure bottom gestures are ignored
|
||||
mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
|
||||
mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
|
||||
Rect visibleInsets = mState.calculateVisibleInsets(
|
||||
Insets visibleInsets = mState.calculateVisibleInsets(
|
||||
new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_PAN);
|
||||
assertEquals(new Rect(0, 100, 0, 100), visibleInsets);
|
||||
assertEquals(Insets.of(0, 100, 0, 100), visibleInsets);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -428,9 +428,9 @@ public class InsetsStateTest {
|
||||
// Make sure bottom gestures are ignored
|
||||
mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300));
|
||||
mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true);
|
||||
Rect visibleInsets = mState.calculateVisibleInsets(
|
||||
Insets visibleInsets = mState.calculateVisibleInsets(
|
||||
new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_NOTHING);
|
||||
assertEquals(new Rect(0, 100, 0, 0), visibleInsets);
|
||||
assertEquals(Insets.of(0, 100, 0, 0), visibleInsets);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -343,7 +343,7 @@ public class TaskSnapshotWindow {
|
||||
|
||||
static Rect getSystemBarInsets(Rect frame, InsetsState state) {
|
||||
return state.calculateInsets(frame, WindowInsets.Type.systemBars(),
|
||||
false /* ignoreVisibility */);
|
||||
false /* ignoreVisibility */).toRect();
|
||||
}
|
||||
|
||||
private void drawSnapshot() {
|
||||
|
||||
@@ -27,10 +27,12 @@ import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.InsetsController;
|
||||
import android.view.InsetsState;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
|
||||
import com.android.systemui.shared.recents.view.RecentsTransition;
|
||||
@@ -89,6 +91,9 @@ public class WindowManagerWrapper {
|
||||
public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT =
|
||||
InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
|
||||
|
||||
public static final int ANIMATION_DURATION_RESIZE = InsetsController.ANIMATION_DURATION_RESIZE;
|
||||
public static final Interpolator RESIZE_INTERPOLATOR = InsetsController.RESIZE_INTERPOLATOR;
|
||||
|
||||
private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();
|
||||
|
||||
public static WindowManagerWrapper getInstance() {
|
||||
|
||||
@@ -6963,7 +6963,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
Rect appRect;
|
||||
if (win != null) {
|
||||
insets = win.getInsetsStateWithVisibilityOverride().calculateInsets(
|
||||
win.getFrame(), Type.systemBars(), false /* ignoreVisibility */);
|
||||
win.getFrame(), Type.systemBars(), false /* ignoreVisibility */).toRect();
|
||||
appRect = new Rect(win.getFrame());
|
||||
appRect.inset(insets);
|
||||
} else {
|
||||
@@ -9231,7 +9231,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
|
||||
return null;
|
||||
}
|
||||
final Rect insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets(
|
||||
task.getBounds(), Type.systemBars(), false /* ignoreVisibility */);
|
||||
task.getBounds(), Type.systemBars(), false /* ignoreVisibility */).toRect();
|
||||
InsetUtils.addInsets(insets, getLetterboxInsets());
|
||||
|
||||
return new RemoteAnimationTarget(task.mTaskId, record.getMode(),
|
||||
|
||||
@@ -48,6 +48,7 @@ import android.view.SyncRtSurfaceTransactionApplier;
|
||||
import android.view.WindowInsetsAnimation;
|
||||
import android.view.WindowInsetsAnimation.Bounds;
|
||||
import android.view.WindowInsetsAnimationControlListener;
|
||||
import android.view.WindowInsetsAnimationController;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.internal.R;
|
||||
@@ -570,8 +571,8 @@ class InsetsPolicy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAnimation(InsetsAnimationControlImpl controller,
|
||||
WindowInsetsAnimationControlListener listener, int types,
|
||||
public <T extends InsetsAnimationControlRunner & WindowInsetsAnimationController>
|
||||
void startAnimation(T runner, WindowInsetsAnimationControlListener listener, int types,
|
||||
WindowInsetsAnimation animation,
|
||||
Bounds bounds) {
|
||||
}
|
||||
|
||||
@@ -581,7 +581,7 @@ public class RecentsAnimationController implements DeathRecipient {
|
||||
contentInsets = targetAppMainWindow
|
||||
.getInsetsStateWithVisibilityOverride()
|
||||
.calculateInsets(mTargetActivityRecord.getBounds(), Type.systemBars(),
|
||||
false /* ignoreVisibility */);
|
||||
false /* ignoreVisibility */).toRect();
|
||||
} else {
|
||||
// If the window for the activity had not yet been created, use the display insets.
|
||||
mService.getStableInsets(mDisplayId, mTmpRect);
|
||||
@@ -1209,7 +1209,7 @@ public class RecentsAnimationController implements DeathRecipient {
|
||||
return null;
|
||||
}
|
||||
final Rect insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets(
|
||||
mBounds, Type.systemBars(), false /* ignoreVisibility */);
|
||||
mBounds, Type.systemBars(), false /* ignoreVisibility */).toRect();
|
||||
InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets());
|
||||
final int mode = topApp.getActivityType() == mTargetActivityType
|
||||
? MODE_OPENING
|
||||
|
||||
@@ -683,7 +683,8 @@ class TaskSnapshotController {
|
||||
}
|
||||
|
||||
static Rect getSystemBarInsets(Rect frame, InsetsState state) {
|
||||
return state.calculateInsets(frame, Type.systemBars(), false /* ignoreVisibility */);
|
||||
return state.calculateInsets(
|
||||
frame, Type.systemBars(), false /* ignoreVisibility */).toRect();
|
||||
}
|
||||
|
||||
void dump(PrintWriter pw, String prefix) {
|
||||
|
||||
@@ -5959,9 +5959,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
|
||||
outSurfaceInsets.set(getAttrs().surfaceInsets);
|
||||
final InsetsState state = getInsetsStateWithVisibilityOverride();
|
||||
outInsets.set(state.calculateInsets(outFrame, systemBars(),
|
||||
false /* ignoreVisibility */));
|
||||
false /* ignoreVisibility */).toRect());
|
||||
outStableInsets.set(state.calculateInsets(outFrame, systemBars(),
|
||||
true /* ignoreVisibility */));
|
||||
true /* ignoreVisibility */).toRect());
|
||||
}
|
||||
|
||||
void setViewVisibility(int viewVisibility) {
|
||||
|
||||
Reference in New Issue
Block a user