blob: e91857f1da82ab027bfffe61ddd4b9e176399942 [file] [log] [blame]
/*
* Copyright (C) 2023 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 com.android.server.wm;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DIMMER;
import static com.android.server.wm.AlphaAnimationSpecProto.DURATION_MS;
import static com.android.server.wm.AlphaAnimationSpecProto.FROM;
import static com.android.server.wm.AlphaAnimationSpecProto.TO;
import static com.android.server.wm.AnimationSpecProto.ALPHA;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.util.Log;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import com.android.internal.protolog.common.ProtoLog;
import java.io.PrintWriter;
/**
* Contains the information relative to the changes to apply to the dim layer
*/
public class DimmerAnimationHelper {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DimmerAnimationHelper" : TAG_WM;
private static final int DEFAULT_DIM_ANIM_DURATION_MS = 200;
/**
* Contains the requested changes
*/
static class Change {
private float mAlpha = -1f;
private int mBlurRadius = -1;
private WindowContainer mDimmingContainer = null;
private int mRelativeLayer = -1;
private static final float EPSILON = 0.0001f;
Change() {}
Change(Change other) {
mAlpha = other.mAlpha;
mBlurRadius = other.mBlurRadius;
mDimmingContainer = other.mDimmingContainer;
mRelativeLayer = other.mRelativeLayer;
}
// Same alpha and blur
boolean hasSameVisualProperties(Change other) {
return Math.abs(mAlpha - other.mAlpha) < EPSILON && mBlurRadius == other.mBlurRadius;
}
boolean hasSameDimmingContainer(Change other) {
return mDimmingContainer != null && mDimmingContainer == other.mDimmingContainer;
}
void inheritPropertiesFromAnimation(AnimationSpec anim) {
mAlpha = anim.mCurrentAlpha;
mBlurRadius = anim.mCurrentBlur;
}
@Override
public String toString() {
return "Dim state: alpha=" + mAlpha + ", blur=" + mBlurRadius + ", container="
+ mDimmingContainer + ", relativePosition=" + mRelativeLayer;
}
}
private Change mCurrentProperties = new Change();
private Change mRequestedProperties = new Change();
private AnimationSpec mAlphaAnimationSpec;
private final AnimationAdapterFactory mAnimationAdapterFactory;
private AnimationAdapter mLocalAnimationAdapter;
DimmerAnimationHelper(AnimationAdapterFactory animationFactory) {
mAnimationAdapterFactory = animationFactory;
}
void setExitParameters() {
setRequestedRelativeParent(mRequestedProperties.mDimmingContainer, -1 /* relativeLayer */);
setRequestedAppearance(0f /* alpha */, 0 /* blur */);
}
// Sets a requested change without applying it immediately
void setRequestedRelativeParent(WindowContainer relativeParent, int relativeLayer) {
mRequestedProperties.mDimmingContainer = relativeParent;
mRequestedProperties.mRelativeLayer = relativeLayer;
}
// Sets a requested change without applying it immediately
void setRequestedAppearance(float alpha, int blurRadius) {
mRequestedProperties.mAlpha = alpha;
mRequestedProperties.mBlurRadius = blurRadius;
}
/**
* Commit the last changes we received. Called after
* {@link Change#setExitParameters()},
* {@link Change#setRequestedRelativeParent(WindowContainer, int)}, or
* {@link Change#setRequestedAppearance(float, int)}
*/
void applyChanges(SurfaceControl.Transaction t, SmoothDimmer.DimState dim) {
if (mRequestedProperties.mDimmingContainer == null) {
Log.e(TAG, this + " does not have a dimming container. Have you forgotten to "
+ "call adjustRelativeLayer?");
return;
}
if (mRequestedProperties.mDimmingContainer.mSurfaceControl == null) {
Log.w(TAG, "container " + mRequestedProperties.mDimmingContainer
+ "does not have a surface");
dim.remove(t);
return;
}
dim.ensureVisible(t);
relativeReparent(dim.mDimSurface,
mRequestedProperties.mDimmingContainer.getSurfaceControl(),
mRequestedProperties.mRelativeLayer, t);
if (!mCurrentProperties.hasSameVisualProperties(mRequestedProperties)) {
stopCurrentAnimation(dim.mDimSurface);
if (dim.mSkipAnimation
// If the container doesn't change but requests a dim change, then it is
// directly providing us the animated values
|| (mRequestedProperties.hasSameDimmingContainer(mCurrentProperties)
&& dim.isDimming())) {
ProtoLog.d(WM_DEBUG_DIMMER,
"%s skipping animation and directly setting alpha=%f, blur=%d",
dim, mRequestedProperties.mAlpha,
mRequestedProperties.mBlurRadius);
setAlphaBlur(dim.mDimSurface, mRequestedProperties.mAlpha,
mRequestedProperties.mBlurRadius, t);
dim.mSkipAnimation = false;
} else {
startAnimation(t, dim);
}
} else if (!dim.isDimming()) {
// We are not dimming, so we tried the exit animation but the alpha is already 0,
// therefore, let's just remove this surface
dim.remove(t);
}
mCurrentProperties = new Change(mRequestedProperties);
}
private void startAnimation(
SurfaceControl.Transaction t, SmoothDimmer.DimState dim) {
ProtoLog.v(WM_DEBUG_DIMMER, "Starting animation on %s", dim);
mAlphaAnimationSpec = getRequestedAnimationSpec();
mLocalAnimationAdapter = mAnimationAdapterFactory.get(mAlphaAnimationSpec,
dim.mHostContainer.mWmService.mSurfaceAnimationRunner);
float targetAlpha = mRequestedProperties.mAlpha;
int targetBlur = mRequestedProperties.mBlurRadius;
mLocalAnimationAdapter.startAnimation(dim.mDimSurface, t,
ANIMATION_TYPE_DIMMER, /* finishCallback */ (type, animator) -> {
setAlphaBlur(dim.mDimSurface, targetAlpha, targetBlur, t);
if (targetAlpha == 0f && !dim.isDimming()) {
dim.remove(t);
}
mLocalAnimationAdapter = null;
mAlphaAnimationSpec = null;
});
}
private boolean isAnimating() {
return mAlphaAnimationSpec != null;
}
void stopCurrentAnimation(SurfaceControl surface) {
if (mLocalAnimationAdapter != null && isAnimating()) {
// Save the current animation progress and cancel the animation
mCurrentProperties.inheritPropertiesFromAnimation(mAlphaAnimationSpec);
mLocalAnimationAdapter.onAnimationCancelled(surface);
mLocalAnimationAdapter = null;
mAlphaAnimationSpec = null;
}
}
private AnimationSpec getRequestedAnimationSpec() {
final float startAlpha = Math.max(mCurrentProperties.mAlpha, 0f);
final int startBlur = Math.max(mCurrentProperties.mBlurRadius, 0);
long duration = (long) (getDimDuration(mRequestedProperties.mDimmingContainer)
* Math.abs(mRequestedProperties.mAlpha - startAlpha));
final AnimationSpec spec = new AnimationSpec(
new AnimationSpec.AnimationExtremes<>(startAlpha, mRequestedProperties.mAlpha),
new AnimationSpec.AnimationExtremes<>(startBlur, mRequestedProperties.mBlurRadius),
duration
);
ProtoLog.v(WM_DEBUG_DIMMER, "Dim animation requested: %s", spec);
return spec;
}
/**
* Change the relative parent of this dim layer
*/
void relativeReparent(SurfaceControl dimLayer, SurfaceControl relativeParent,
int relativePosition, SurfaceControl.Transaction t) {
try {
t.setRelativeLayer(dimLayer, relativeParent, relativePosition);
} catch (NullPointerException e) {
Log.w(TAG, "Tried to change parent of dim " + dimLayer + " after remove", e);
}
}
void setAlphaBlur(SurfaceControl sc, float alpha, int blur, SurfaceControl.Transaction t) {
try {
t.setAlpha(sc, alpha);
t.setBackgroundBlurRadius(sc, blur);
} catch (NullPointerException e) {
Log.w(TAG , "Tried to change look of dim " + sc + " after remove", e);
}
}
private long getDimDuration(WindowContainer container) {
// Use the same duration as the animation on the WindowContainer
AnimationAdapter animationAdapter = container.mSurfaceAnimator.getAnimation();
final float durationScale = container.mWmService.getTransitionAnimationScaleLocked();
return animationAdapter == null ? (long) (DEFAULT_DIM_ANIM_DURATION_MS * durationScale)
: animationAdapter.getDurationHint();
}
/**
* Collects the animation specifics
*/
static class AnimationSpec implements LocalAnimationAdapter.AnimationSpec {
private static final String TAG = TAG_WITH_CLASS_NAME ? "DimmerAnimationSpec" : TAG_WM;
static class AnimationExtremes<T> {
final T mStartValue;
final T mFinishValue;
AnimationExtremes(T fromValue, T toValue) {
mStartValue = fromValue;
mFinishValue = toValue;
}
@Override
public String toString() {
return "[" + mStartValue + "->" + mFinishValue + "]";
}
}
private final long mDuration;
private final AnimationSpec.AnimationExtremes<Float> mAlpha;
private final AnimationSpec.AnimationExtremes<Integer> mBlur;
float mCurrentAlpha = 0;
int mCurrentBlur = 0;
boolean mStarted = false;
AnimationSpec(AnimationSpec.AnimationExtremes<Float> alpha,
AnimationSpec.AnimationExtremes<Integer> blur, long duration) {
mAlpha = alpha;
mBlur = blur;
mDuration = duration;
}
@Override
public long getDuration() {
return mDuration;
}
@Override
public void apply(SurfaceControl.Transaction t, SurfaceControl sc, long currentPlayTime) {
if (!mStarted) {
// The first frame would end up in the sync transaction, and since this could be
// applied after the animation transaction, we avoid putting visible changes here.
// The initial state of the animation matches the current state of the dim anyway.
mStarted = true;
return;
}
final float fraction = getFraction(currentPlayTime);
mCurrentAlpha =
fraction * (mAlpha.mFinishValue - mAlpha.mStartValue) + mAlpha.mStartValue;
mCurrentBlur =
(int) fraction * (mBlur.mFinishValue - mBlur.mStartValue) + mBlur.mStartValue;
if (sc.isValid()) {
t.setAlpha(sc, mCurrentAlpha);
t.setBackgroundBlurRadius(sc, mCurrentBlur);
} else {
Log.w(TAG, "Dimmer#AnimationSpec tried to access " + sc + " after release");
}
}
@Override
public String toString() {
return "Animation spec: alpha=" + mAlpha + ", blur=" + mBlur;
}
@Override
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("from_alpha="); pw.print(mAlpha.mStartValue);
pw.print(" to_alpha="); pw.print(mAlpha.mFinishValue);
pw.print(prefix); pw.print("from_blur="); pw.print(mBlur.mStartValue);
pw.print(" to_blur="); pw.print(mBlur.mFinishValue);
pw.print(" duration="); pw.println(mDuration);
}
@Override
public void dumpDebugInner(ProtoOutputStream proto) {
final long token = proto.start(ALPHA);
proto.write(FROM, mAlpha.mStartValue);
proto.write(TO, mAlpha.mFinishValue);
proto.write(DURATION_MS, mDuration);
proto.end(token);
}
}
static class AnimationAdapterFactory {
public AnimationAdapter get(LocalAnimationAdapter.AnimationSpec alphaAnimationSpec,
SurfaceAnimationRunner runner) {
return new LocalAnimationAdapter(alphaAnimationSpec, runner);
}
}
}