Merge "Use separate thread if app doesn't listen to animations" into rvc-dev am: 92fd77548a am: 9d0fc001c3

Change-Id: I33678818ad20fcc2324485779e8b3763ad67810b
This commit is contained in:
Automerger Merge Worker
2020-03-12 21:08:02 +00:00
15 changed files with 462 additions and 111 deletions

View File

@@ -18,6 +18,7 @@ package android.animation;
import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -268,6 +269,11 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
*/
private float mDurationScale = -1f;
/**
* Animation handler used to schedule updates for this animation.
*/
private AnimationHandler mAnimationHandler;
/**
* Public constants
*/
@@ -1684,6 +1690,15 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
* @hide
*/
public AnimationHandler getAnimationHandler() {
return AnimationHandler.getInstance();
return mAnimationHandler != null ? mAnimationHandler : AnimationHandler.getInstance();
}
/**
* Sets the animation handler used to schedule updates for this animator or {@code null} to use
* the default handler.
* @hide
*/
public void setAnimationHandler(@Nullable AnimationHandler animationHandler) {
mAnimationHandler = animationHandler;
}
}

View File

@@ -16,7 +16,6 @@
package android.view;
import android.view.InsetsController.LayoutInsetsDuringAnimation;
import android.view.WindowInsetsAnimation.Bounds;
/**
@@ -37,7 +36,7 @@ public interface InsetsAnimationControlCallbacks {
void startAnimation(InsetsAnimationControlImpl controller,
WindowInsetsAnimationControlListener listener, int types,
WindowInsetsAnimation animation,
Bounds bounds, @LayoutInsetsDuringAnimation int layoutDuringAnimation);
Bounds bounds);
/**
* Schedule the apply by posting the animation callback.
@@ -46,10 +45,10 @@ public interface InsetsAnimationControlCallbacks {
/**
* Finish the final steps after the animation.
* @param controller The controller used to control the animation.
* @param runner The runner used to run the animation.
* @param shown {@code true} if the insets are shown.
*/
void notifyFinished(InsetsAnimationControlImpl controller, boolean shown);
void notifyFinished(InsetsAnimationControlRunner runner, boolean shown);
/**
* Apply the new params to the surface.

View File

@@ -31,9 +31,7 @@ import android.util.ArraySet;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.SparseSetArray;
import android.view.InsetsController.LayoutInsetsDuringAnimation;
import android.view.InsetsState.InternalInsetsSide;
import android.view.InsetsState.InternalInsetsType;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation.Bounds;
@@ -49,7 +47,8 @@ import java.util.ArrayList;
* @hide
*/
@VisibleForTesting
public class InsetsAnimationControlImpl implements WindowInsetsAnimationController {
public class InsetsAnimationControlImpl implements WindowInsetsAnimationController,
InsetsAnimationControlRunner {
private final Rect mTmpFrame = new Rect();
@@ -84,8 +83,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
InsetsState state, WindowInsetsAnimationControlListener listener,
@InsetsType int types,
InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator,
boolean fade, @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
@AnimationType int animationType) {
boolean fade, @AnimationType int animationType) {
mControls = controls;
mListener = listener;
mTypes = types;
@@ -105,7 +103,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
mAnimation.setAlpha(getCurrentAlpha());
mAnimationType = animationType;
mController.startAnimation(this, listener, types, mAnimation,
new Bounds(mHiddenInsets, mShownInsets), layoutInsetsDuringAnimation);
new Bounds(mHiddenInsets, mShownInsets));
}
@Override
@@ -133,11 +131,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
return mTypes;
}
boolean controlsInternalType(@InternalInsetsType int type) {
return InsetsState.toInternalType(mTypes).contains(type);
}
@AnimationType int getAnimationType() {
@Override
public @AnimationType int getAnimationType() {
return mAnimationType;
}
@@ -205,7 +200,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
return mAnimation.getFraction();
}
public void onCancelled() {
@Override
public void cancel() {
if (mFinished) {
return;
}
@@ -217,7 +213,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
return mCancelled;
}
WindowInsetsAnimation getAnimation() {
@Override
public WindowInsetsAnimation getAnimation() {
return mAnimation;
}
@@ -225,6 +222,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
return mListener;
}
SparseArray<InsetsSourceControl> getControls() {
return mControls;
}
private Insets calculateInsets(InsetsState state, Rect frame,
SparseArray<InsetsSourceControl> controls, boolean shown,
@Nullable @InternalInsetsSide SparseIntArray typeSideMap) {

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2020 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 android.view.InsetsController.AnimationType;
import android.view.InsetsState.InternalInsetsType;
import android.view.WindowInsets.Type.InsetsType;
/**
* Interface representing a runner for an insets animation.
*
* @hide
*/
public interface InsetsAnimationControlRunner {
/**
* @return The {@link InsetsType} the animation of this runner is controlling.
*/
@InsetsType int getTypes();
/**
* Cancels the animation.
*/
void cancel();
/**
* @return The animation this runner is running.
*/
WindowInsetsAnimation getAnimation();
/**
* @return Whether {@link #getTypes()} maps to a specific {@link InternalInsetsType}.
*/
default boolean controlsInternalType(@InternalInsetsType int type) {
return InsetsState.toInternalType(getTypes()).contains(type);
}
/**
* @return The animation type this runner is running.
*/
@AnimationType int getAnimationType();
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2020 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 android.os.Handler;
import android.os.HandlerThread;
import android.os.Trace;
/**
* Thread to be used for inset animations to be running off the main thread.
* @hide
*/
public class InsetsAnimationThread extends HandlerThread {
private static InsetsAnimationThread sInstance;
private static Handler sHandler;
private InsetsAnimationThread() {
// TODO: Should this use higher priority?
super("InsetsAnimations");
}
private static void ensureThreadLocked() {
if (sInstance == null) {
sInstance = new InsetsAnimationThread();
sInstance.start();
sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_VIEW);
sHandler = new Handler(sInstance.getLooper());
}
}
public static void release() {
synchronized (InsetsAnimationThread.class) {
if (sInstance == null) {
return;
}
sInstance.getLooper().quitSafely();
sInstance = null;
sHandler = null;
}
}
public static InsetsAnimationThread get() {
synchronized (InsetsAnimationThread.class) {
ensureThreadLocked();
return sInstance;
}
}
public static Handler getHandler() {
synchronized (InsetsAnimationThread.class) {
ensureThreadLocked();
return sHandler;
}
}
}

View File

@@ -0,0 +1,130 @@
/*
* Copyright (C) 2020 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.SyncRtSurfaceTransactionApplier.applyParams;
import android.annotation.UiThread;
import android.graphics.Rect;
import android.os.Handler;
import android.util.SparseArray;
import android.view.InsetsController.AnimationType;
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.animation.Interpolator;
/**
* Insets animation runner that uses {@link InsetsAnimationThread} to run the animation off from the
* main thread.
*
* @hide
*/
public class InsetsAnimationThreadControlRunner implements InsetsAnimationControlRunner {
private final InsetsAnimationControlImpl mControl;
private final InsetsAnimationControlCallbacks mOuterCallbacks;
private final Handler mMainThreadHandler;
private final InsetsState mState = new InsetsState();
private final InsetsAnimationControlCallbacks mCallbacks =
new InsetsAnimationControlCallbacks() {
private final float[] mTmpFloat9 = new float[9];
@Override
@UiThread
public void startAnimation(InsetsAnimationControlImpl controller,
WindowInsetsAnimationControlListener listener, int types,
WindowInsetsAnimation animation, Bounds bounds) {
// Animation will be started in constructor already.
}
@Override
public void scheduleApplyChangeInsets() {
mControl.applyChangeInsets(mState);
}
@Override
public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
releaseControls(mControl.getControls());
mMainThreadHandler.post(() ->
mOuterCallbacks.notifyFinished(InsetsAnimationThreadControlRunner.this, shown));
}
@Override
public void applySurfaceParams(SurfaceParams... params) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
for (int i = params.length - 1; i >= 0; i--) {
SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
applyParams(t, surfaceParams, mTmpFloat9);
}
t.apply();
t.close();
}
};
@UiThread
public InsetsAnimationThreadControlRunner(SparseArray<InsetsSourceControl> controls, Rect frame,
InsetsState state, WindowInsetsAnimationControlListener listener,
@InsetsType int types,
InsetsAnimationControlCallbacks controller, long durationMs, Interpolator interpolator,
boolean fade, @AnimationType int animationType, Handler mainThreadHandler) {
mMainThreadHandler = mainThreadHandler;
mOuterCallbacks = controller;
mControl = new InsetsAnimationControlImpl(copyControls(controls), frame, state, listener,
types, mCallbacks, durationMs, interpolator, fade, animationType);
InsetsAnimationThread.getHandler().post(() -> listener.onReady(mControl, types));
}
private void releaseControls(SparseArray<InsetsSourceControl> controls) {
for (int i = controls.size() - 1; i >= 0; i--) {
controls.valueAt(i).release(SurfaceControl::release);
}
}
private SparseArray<InsetsSourceControl> copyControls(
SparseArray<InsetsSourceControl> controls) {
SparseArray<InsetsSourceControl> copy = new SparseArray<>(controls.size());
for (int i = 0; i < controls.size(); i++) {
copy.append(controls.keyAt(i), new InsetsSourceControl(controls.valueAt(i)));
}
return copy;
}
@Override
@UiThread
public int getTypes() {
return mControl.getTypes();
}
@Override
@UiThread
public void cancel() {
InsetsAnimationThread.getHandler().post(mControl::cancel);
}
@Override
@UiThread
public WindowInsetsAnimation getAnimation() {
return mControl.getAnimation();
}
@Override
public int getAnimationType() {
return mControl.getAnimationType();
}
}

View File

@@ -23,6 +23,7 @@ import static android.view.WindowInsets.Type.ime;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APPEARANCE_CONTROLLED;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_BEHAVIOR_CONTROLLED;
import android.animation.AnimationHandler;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -53,6 +54,7 @@ import android.view.animation.PathInterpolator;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -167,10 +169,22 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private WindowInsetsAnimationController mController;
private ObjectAnimator mAnimator;
protected boolean mShow;
private final boolean mShow;
private final boolean mUseSfVsync;
public InternalAnimationControlListener(boolean show) {
private ThreadLocal<AnimationHandler> mSfAnimationHandlerThreadLocal =
new ThreadLocal<AnimationHandler>() {
@Override
protected AnimationHandler initialValue() {
AnimationHandler handler = new AnimationHandler();
handler.setProvider(new SfVsyncFrameCallbackProvider());
return handler;
}
};
public InternalAnimationControlListener(boolean show, boolean useSfVsync) {
mShow = show;
mUseSfVsync = useSfVsync;
}
@Override
@@ -193,6 +207,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
onAnimationFinish();
}
});
if (mUseSfVsync) {
mAnimator.setAnimationHandler(mSfAnimationHandlerThreadLocal.get());
}
mAnimator.start();
}
@@ -228,12 +245,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
*/
private static class RunningAnimation {
RunningAnimation(InsetsAnimationControlImpl control, int type) {
this.control = control;
RunningAnimation(InsetsAnimationControlRunner runner, int type) {
this.runner = runner;
this.type = type;
}
final InsetsAnimationControlImpl control;
final InsetsAnimationControlRunner runner;
final @AnimationType int type;
/**
@@ -252,7 +269,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
PendingControlRequest(@InsetsType int types, WindowInsetsAnimationControlListener listener,
long durationMs, Interpolator interpolator, @AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
CancellationSignal cancellationSignal) {
CancellationSignal cancellationSignal, boolean useInsetsAnimationThread) {
this.types = types;
this.listener = listener;
this.durationMs = durationMs;
@@ -260,6 +277,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
this.animationType = animationType;
this.layoutInsetsDuringAnimation = layoutInsetsDuringAnimation;
this.cancellationSignal = cancellationSignal;
this.useInsetsAnimationThread = useInsetsAnimationThread;
}
final @InsetsType int types;
@@ -269,6 +287,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
final @AnimationType int animationType;
final @LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation;
final CancellationSignal cancellationSignal;
final boolean useInsetsAnimationThread;
}
private final String TAG = "InsetsControllerImpl";
@@ -347,15 +366,20 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
InsetsState state = new InsetsState(mState, true /* copySources */);
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
RunningAnimation runningAnimation = mRunningAnimations.get(i);
InsetsAnimationControlImpl control = runningAnimation.control;
InsetsAnimationControlRunner runner = runningAnimation.runner;
if (runner instanceof InsetsAnimationControlImpl) {
InsetsAnimationControlImpl control = (InsetsAnimationControlImpl) runner;
// 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) {
mTmpRunningAnims.add(control.getAnimation());
}
if (control.applyChangeInsets(state)) {
mTmpFinishedControls.add(control);
// 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) {
mTmpRunningAnims.add(control.getAnimation());
}
if (control.applyChangeInsets(state)) {
mTmpFinishedControls.add(control);
}
}
}
@@ -499,7 +523,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
pendingRequest.listener, mFrame,
true /* fromIme */, pendingRequest.durationMs, pendingRequest.interpolator,
false /* fade */, pendingRequest.animationType,
pendingRequest.layoutInsetsDuringAnimation);
pendingRequest.layoutInsetsDuringAnimation,
pendingRequest.useInsetsAnimationThread);
pendingRequest.cancellationSignal.setOnCancelListener(cancellationSignal::cancel);
return;
}
@@ -563,7 +588,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return cancellationSignal;
}
return controlAnimationUnchecked(types, listener, mFrame, fromIme, durationMs, interpolator,
false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types));
false /* fade */, animationType, getLayoutInsetsDuringAnimationMode(types),
false /* useInsetsAnimationThread */);
}
private boolean checkDisplayFramesForControlling() {
@@ -577,7 +603,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
WindowInsetsAnimationControlListener listener, Rect frame, boolean fromIme,
long durationMs, Interpolator interpolator, boolean fade,
@AnimationType int animationType,
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation) {
@LayoutInsetsDuringAnimation int layoutInsetsDuringAnimation,
boolean useInsetsAnimationThread) {
CancellationSignal cancellationSignal = new CancellationSignal();
if (types == 0) {
// nothing to animate.
@@ -600,7 +627,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
abortPendingImeControlRequest();
final PendingControlRequest request = new PendingControlRequest(types,
listener, durationMs,
interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal);
interpolator, animationType, layoutInsetsDuringAnimation, cancellationSignal,
useInsetsAnimationThread);
mPendingImeControlRequest = request;
mHandler.postDelayed(mPendingControlTimeout, PENDING_CONTROL_TIMEOUT_MS);
cancellationSignal.setOnCancelListener(() -> {
@@ -617,11 +645,21 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return cancellationSignal;
}
final InsetsAnimationControlImpl controller = new InsetsAnimationControlImpl(controls,
frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
layoutInsetsDuringAnimation, animationType);
mRunningAnimations.add(new RunningAnimation(controller, animationType));
cancellationSignal.setOnCancelListener(controller::onCancelled);
final InsetsAnimationControlRunner runner = useInsetsAnimationThread
? new InsetsAnimationThreadControlRunner(controls,
frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
animationType, mViewRoot.mHandler)
: new InsetsAnimationControlImpl(controls,
frame, mState, listener, typesReady, this, durationMs, interpolator, fade,
animationType);
mRunningAnimations.add(new RunningAnimation(runner, animationType));
cancellationSignal.setOnCancelListener(runner::cancel);
if (layoutInsetsDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
showDirectly(types);
} else {
hideDirectly(types, false /* animationFinished */, animationType);
}
return cancellationSignal;
}
@@ -705,7 +743,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private void cancelExistingControllers(@InsetsType int types) {
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
if ((control.getTypes() & types) != 0) {
cancelAnimation(control, true /* invokeCallback */);
}
@@ -725,13 +763,13 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@VisibleForTesting
@Override
public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
cancelAnimation(controller, false /* invokeCallback */);
public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
cancelAnimation(runner, false /* invokeCallback */);
if (shown) {
showDirectly(controller.getTypes());
showDirectly(runner.getTypes());
} else {
hideDirectly(controller.getTypes(), true /* animationFinished */,
controller.getAnimationType());
hideDirectly(runner.getTypes(), true /* animationFinished */,
runner.getAnimationType());
}
}
@@ -756,7 +794,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
void notifyControlRevoked(InsetsSourceConsumer consumer) {
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
if ((control.getTypes() & toPublicType(consumer.getType())) != 0) {
cancelAnimation(control, true /* invokeCallback */);
}
@@ -766,12 +804,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
}
private void cancelAnimation(InsetsAnimationControlImpl control, boolean invokeCallback) {
private void cancelAnimation(InsetsAnimationControlRunner control, boolean invokeCallback) {
if (invokeCallback) {
control.onCancelled();
control.cancel();
}
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
if (mRunningAnimations.get(i).control == control) {
if (mRunningAnimations.get(i).runner == control) {
mRunningAnimations.remove(i);
break;
}
@@ -844,7 +882,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@VisibleForTesting
public @AnimationType int getAnimationType(@InternalInsetsType int type) {
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
InsetsAnimationControlImpl control = mRunningAnimations.get(i).control;
InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
if (control.controlsInternalType(type)) {
return mRunningAnimations.get(i).type;
}
@@ -878,15 +916,24 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
return;
}
boolean useInsetsAnimationThread = canUseInsetsAnimationThread();
final InternalAnimationControlListener listener =
new InternalAnimationControlListener(show);
new InternalAnimationControlListener(show, useInsetsAnimationThread);
// Show/hide animations always need to be relative to the display frame, in order that shown
// and hidden state insets are correct.
controlAnimationUnchecked(
types, listener, mState.getDisplayFrame(), fromIme, listener.getDurationMs(),
INTERPOLATOR, true /* fade */, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
: LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
: LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
useInsetsAnimationThread);
}
private boolean canUseInsetsAnimationThread() {
if (mViewRoot.mView == null) {
return true;
}
return !mViewRoot.mView.hasWindowInsetsAnimationCallback();
}
private void hideDirectly(
@@ -921,13 +968,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@Override
public void startAnimation(InsetsAnimationControlImpl controller,
WindowInsetsAnimationControlListener listener, int types,
WindowInsetsAnimation animation, Bounds bounds, int layoutDuringAnimation) {
if (layoutDuringAnimation == LAYOUT_INSETS_DURING_ANIMATION_SHOWN) {
showDirectly(types);
} else {
hideDirectly(types, false /* animationFinished */, controller.getAnimationType());
}
WindowInsetsAnimation animation, Bounds bounds) {
if (mViewRoot.mView == null) {
return;
}
@@ -941,7 +982,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
}
for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
RunningAnimation runningAnimation = mRunningAnimations.get(i);
if (runningAnimation.control == controller) {
if (runningAnimation.runner == controller) {
runningAnimation.startDispatched = true;
}
}

View File

@@ -117,7 +117,7 @@ public class InsetsSourceConsumer {
}
}
if (lastControl != null) {
lastControl.release(mController);
lastControl.release(mController::releaseSurfaceControlFromRt);
}
}

View File

@@ -22,6 +22,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.view.InsetsState.InternalInsetsType;
import java.util.function.Consumer;
/**
* Represents a parcelable object to allow controlling a single {@link InsetsSource}.
* @hide
@@ -94,9 +96,9 @@ public class InsetsSourceControl implements Parcelable {
dest.writeParcelable(mSurfacePosition, 0 /* flags*/);
}
public void release(InsetsController controller) {
public void release(Consumer<SurfaceControl> surfaceReleaseConsumer) {
if (mLeash != null) {
controller.releaseSurfaceControlFromRt(mLeash);
surfaceReleaseConsumer.accept(mLeash);
}
}

View File

@@ -11226,6 +11226,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
getListenerInfo().mWindowInsetsAnimationCallback = callback;
}
/**
* @return {@code true} if any {@link WindowInsetsAnimation.Callback} is registered on the view
* or view tree of the sub-hierarchy {@code false} otherwise.
* @hide
*/
public boolean hasWindowInsetsAnimationCallback() {
return getListenerInfo().mWindowInsetsAnimationCallback != null;
}
/**
* Dispatches {@link WindowInsetsAnimation.Callback#onPrepare(WindowInsetsAnimation)}
* when Window Insets animation is being prepared.

View File

@@ -7258,6 +7258,34 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
: DISPATCH_MODE_CONTINUE_ON_SUBTREE;
}
/**
* @hide
*/
@Override
public boolean hasWindowInsetsAnimationCallback() {
if (super.hasWindowInsetsAnimationCallback()) {
return true;
}
// If we are root-level content view that fits insets, we imitate consuming behavior, so
// no child will retrieve window insets animation callback.
// See dispatchWindowInsetsAnimationPrepare.
boolean isOptionalFitSystemWindows = (mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
|| isFrameworkOptionalFitsSystemWindows();
if (isOptionalFitSystemWindows && mAttachInfo != null
&& mAttachInfo.mContentOnApplyWindowInsetsListener != null) {
return false;
}
final int count = getChildCount();
for (int i = 0; i < count; i++) {
if (getChildAt(i).hasWindowInsetsAnimationCallback()) {
return true;
}
}
return false;
}
@Override
public void dispatchWindowInsetsAnimationPrepare(
@NonNull WindowInsetsAnimation animation) {

View File

@@ -504,6 +504,7 @@ public final class WindowManagerGlobal {
}
void doRemoveView(ViewRootImpl root) {
boolean allViewsRemoved;
synchronized (mLock) {
final int index = mRoots.indexOf(root);
if (index >= 0) {
@@ -512,10 +513,17 @@ public final class WindowManagerGlobal {
final View view = mViews.remove(index);
mDyingViews.remove(view);
}
allViewsRemoved = mRoots.isEmpty();
}
if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
doTrimForeground();
}
// If we don't have any views anymore in our process, we no longer need the
// InsetsAnimationThread to save some resources.
if (allViewsRemoved) {
InsetsAnimationThread.release();
}
}
private int findViewLocked(View view, boolean required) {

View File

@@ -123,7 +123,7 @@ public class InsetsAnimationControlImplTest {
mController = new InsetsAnimationControlImpl(controls,
new Rect(0, 0, 500, 500), mInsetsState, mMockListener, systemBars(),
mMockController, 10 /* durationMs */, new LinearInterpolator(),
false /* fade */, LAYOUT_INSETS_DURING_ANIMATION_SHOWN, 0 /* animationType */);
false /* fade */, 0 /* animationType */);
}
@Test
@@ -182,7 +182,7 @@ public class InsetsAnimationControlImplTest {
@Test
public void testCancelled() {
mController.onCancelled();
mController.cancel();
try {
mController.setInsetsAndAlpha(Insets.NONE, 1f /*alpha */, 0f /* fraction */);
fail("Expected exception to be thrown");

View File

@@ -31,6 +31,7 @@ import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -47,6 +48,7 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.os.CancellationSignal;
import android.platform.test.annotations.Presubmit;
import android.view.InsetsState.InternalInsetsType;
import android.view.SurfaceControl.Transaction;
import android.view.WindowInsets.Type;
import android.view.WindowInsetsController.OnControllableInsetsChangedListener;
@@ -169,11 +171,8 @@ public class InsetsControllerTest {
@Test
public void testControlsChanged() {
InsetsSourceControl control =
new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
mController.onControlsChanged(new InsetsSourceControl[] { control });
assertEquals(mLeash,
mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl().getLeash());
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
assertNotNull(mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl().getLeash());
mController.addOnControllableInsetsChangedListener(
((controller, typeMask) -> assertEquals(statusBars(), typeMask)));
}
@@ -183,9 +182,7 @@ public class InsetsControllerTest {
OnControllableInsetsChangedListener listener
= mock(OnControllableInsetsChangedListener.class);
mController.addOnControllableInsetsChangedListener(listener);
InsetsSourceControl control =
new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
mController.onControlsChanged(new InsetsSourceControl[] { control });
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
mController.onControlsChanged(new InsetsSourceControl[0]);
assertNull(mController.getSourceConsumer(ITYPE_STATUS_BAR).getControl());
InOrder inOrder = Mockito.inOrder(listener);
@@ -197,9 +194,7 @@ public class InsetsControllerTest {
@Test
public void testControlsRevoked_duringAnim() {
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
InsetsSourceControl control =
new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
mController.onControlsChanged(new InsetsSourceControl[] { control });
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
WindowInsetsAnimationControlListener mockListener =
mock(WindowInsetsAnimationControlListener.class);
@@ -262,11 +257,8 @@ public class InsetsControllerTest {
@Test
public void testApplyImeVisibility() {
final InsetsSourceControl ime = new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
InsetsSourceControl[] controls = new InsetsSourceControl[3];
controls[0] = ime;
mController.onControlsChanged(controls);
InsetsSourceControl ime = createControl(ITYPE_IME);
mController.onControlsChanged(new InsetsSourceControl[] { ime });
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.getSourceConsumer(ITYPE_IME).onWindowFocusGained();
mController.applyImeVisibility(true);
@@ -429,9 +421,7 @@ public class InsetsControllerTest {
@Test
public void testRestoreStartsAnimation() {
InsetsSourceControl control =
new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
mController.onControlsChanged(new InsetsSourceControl[]{control});
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.hide(Type.statusBars());
@@ -448,7 +438,7 @@ public class InsetsControllerTest {
assertTrue(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
// Gaining control
mController.onControlsChanged(new InsetsSourceControl[]{control});
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
mController.cancelExistingAnimation();
assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
@@ -459,8 +449,6 @@ public class InsetsControllerTest {
@Test
public void testStartImeAnimationAfterGettingControl() {
InsetsSourceControl control =
new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
@@ -471,7 +459,7 @@ public class InsetsControllerTest {
mController.show(ime(), true /* fromIme */);
// Gaining control shortly after
mController.onControlsChanged(new InsetsSourceControl[]{control});
mController.onControlsChanged(createSingletonControl(ITYPE_IME));
assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
mController.cancelExistingAnimation();
@@ -483,16 +471,13 @@ public class InsetsControllerTest {
@Test
public void testStartImeAnimationAfterGettingControl_imeLater() {
InsetsSourceControl control =
new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
mController.show(ime());
assertFalse(mController.getState().getSource(ITYPE_IME).isVisible());
// Gaining control shortly after
mController.onControlsChanged(new InsetsSourceControl[]{control});
mController.onControlsChanged(createSingletonControl(ITYPE_IME));
// Pretend IME is calling
mController.show(ime(), true /* fromIme */);
@@ -507,9 +492,7 @@ public class InsetsControllerTest {
@Test
public void testAnimationEndState_controller() throws Exception {
InsetsSourceControl control =
new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
mController.onControlsChanged(new InsetsSourceControl[] { control });
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener mockListener =
@@ -535,9 +518,7 @@ public class InsetsControllerTest {
@Test
public void testCancellation_afterGainingControl() throws Exception {
InsetsSourceControl control =
new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
mController.onControlsChanged(new InsetsSourceControl[] { control });
mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR));
InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
WindowInsetsAnimationControlListener mockListener =
@@ -655,12 +636,23 @@ public class InsetsControllerTest {
latch.await();
}
private InsetsSourceControl createControl(@InternalInsetsType int type) {
// Simulate binder behavior by copying SurfaceControl. Otherwise, InsetsController will
// attempt to release mLeash directly.
SurfaceControl copy = new SurfaceControl();
copy.copyFrom(mLeash);
return new InsetsSourceControl(type, copy, new Point());
}
private InsetsSourceControl[] createSingletonControl(@InternalInsetsType int type) {
return new InsetsSourceControl[] { createControl(type) };
}
private InsetsSourceControl[] prepareControls() {
final InsetsSourceControl navBar = new InsetsSourceControl(ITYPE_NAVIGATION_BAR, mLeash,
new Point());
final InsetsSourceControl statusBar = new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash,
new Point());
final InsetsSourceControl ime = new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
final InsetsSourceControl navBar = createControl(ITYPE_NAVIGATION_BAR);
final InsetsSourceControl statusBar = createControl(ITYPE_STATUS_BAR);
final InsetsSourceControl ime = createControl(ITYPE_IME);
InsetsSourceControl[] controls = new InsetsSourceControl[3];
controls[0] = navBar;

View File

@@ -36,6 +36,7 @@ import android.util.IntArray;
import android.util.SparseArray;
import android.view.InsetsAnimationControlCallbacks;
import android.view.InsetsAnimationControlImpl;
import android.view.InsetsAnimationControlRunner;
import android.view.InsetsController;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -44,6 +45,7 @@ import android.view.SurfaceControl;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.ViewRootImpl;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimationControlListener;
import com.android.internal.annotations.VisibleForTesting;
@@ -327,7 +329,7 @@ class InsetsPolicy {
InsetsPolicyAnimationControlCallbacks mControlCallbacks;
InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback) {
super(show);
super(show, true /* useSfVsync */);
mFinishCallback = finishCallback;
mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
}
@@ -360,8 +362,6 @@ class InsetsPolicy {
mFocusedWin.getDisplayContent().getBounds(), getState(),
mListener, typesReady, this, mListener.getDurationMs(),
InsetsController.INTERPOLATOR, true,
show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
: LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE);
SurfaceAnimationThread.getHandler().post(
() -> mListener.onReady(mAnimationControl, typesReady));
@@ -377,7 +377,7 @@ class InsetsPolicy {
}
@Override
public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
public void notifyFinished(InsetsAnimationControlRunner runner, boolean shown) {
// Nothing's needed here. Finish steps is handled in the listener
// onAnimationFinished callback.
}
@@ -406,14 +406,14 @@ class InsetsPolicy {
applyParams(t, surfaceParams, mTmpFloat9);
}
t.apply();
t.close();
}
@Override
public void startAnimation(InsetsAnimationControlImpl controller,
WindowInsetsAnimationControlListener listener, int types,
WindowInsetsAnimation animation,
WindowInsetsAnimation.Bounds bounds,
int layoutDuringAnimation) {
Bounds bounds) {
}
}
}