207 lines
7.5 KiB
Java
207 lines
7.5 KiB
Java
/*
|
|
* Copyright (C) 2022 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.wm.shell.compatui;
|
|
|
|
import static com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
|
|
import static com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
|
|
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
|
|
|
|
import android.animation.Animator;
|
|
import android.animation.AnimatorListenerAdapter;
|
|
import android.animation.ObjectAnimator;
|
|
import android.annotation.AnyRes;
|
|
import android.annotation.NonNull;
|
|
import android.annotation.Nullable;
|
|
import android.content.Context;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.util.IntProperty;
|
|
import android.util.Log;
|
|
import android.util.Property;
|
|
import android.view.ContextThemeWrapper;
|
|
import android.view.View;
|
|
import android.view.animation.Animation;
|
|
|
|
import com.android.internal.policy.TransitionAnimation;
|
|
|
|
/**
|
|
* Controls the enter/exit a dialog.
|
|
*
|
|
* @param <T> The {@link DialogContainerSupplier} to use
|
|
*/
|
|
public class DialogAnimationController<T extends DialogContainerSupplier> {
|
|
|
|
// The alpha of a background is a number between 0 (fully transparent) to 255 (fully opaque).
|
|
// 204 is simply 255 * 0.8.
|
|
static final int BACKGROUND_DIM_ALPHA = 204;
|
|
|
|
// If shell transitions are enabled, startEnterAnimation will be called after all transitions
|
|
// have finished, and therefore the start delay should be shorter.
|
|
private static final int ENTER_ANIM_START_DELAY_MILLIS = ENABLE_SHELL_TRANSITIONS ? 300 : 500;
|
|
|
|
private final TransitionAnimation mTransitionAnimation;
|
|
private final String mPackageName;
|
|
private final String mTag;
|
|
@AnyRes
|
|
private final int mAnimStyleResId;
|
|
|
|
@Nullable
|
|
private Animation mDialogAnimation;
|
|
@Nullable
|
|
private Animator mBackgroundDimAnimator;
|
|
|
|
public DialogAnimationController(Context context, String tag) {
|
|
mTransitionAnimation = new TransitionAnimation(context, /* debug= */ false, tag);
|
|
mAnimStyleResId = (new ContextThemeWrapper(context,
|
|
android.R.style.ThemeOverlay_Material_Dialog).getTheme()).obtainStyledAttributes(
|
|
com.android.internal.R.styleable.Window).getResourceId(
|
|
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
|
|
mPackageName = context.getPackageName();
|
|
mTag = tag;
|
|
}
|
|
|
|
/**
|
|
* Starts both background dim fade-in animation and the dialog enter animation.
|
|
*/
|
|
public void startEnterAnimation(@NonNull T layout, Runnable endCallback) {
|
|
// Cancel any previous animation if it's still running.
|
|
cancelAnimation();
|
|
|
|
final View dialogContainer = layout.getDialogContainerView();
|
|
mDialogAnimation = loadAnimation(WindowAnimation_windowEnterAnimation);
|
|
if (mDialogAnimation == null) {
|
|
endCallback.run();
|
|
return;
|
|
}
|
|
mDialogAnimation.setAnimationListener(getAnimationListener(
|
|
/* startCallback= */ () -> dialogContainer.setAlpha(1),
|
|
/* endCallback= */ () -> {
|
|
mDialogAnimation = null;
|
|
endCallback.run();
|
|
}));
|
|
|
|
mBackgroundDimAnimator = getAlphaAnimator(layout.getBackgroundDimDrawable(),
|
|
/* endAlpha= */ BACKGROUND_DIM_ALPHA,
|
|
mDialogAnimation.getDuration());
|
|
mBackgroundDimAnimator.addListener(getDimAnimatorListener());
|
|
|
|
mDialogAnimation.setStartOffset(ENTER_ANIM_START_DELAY_MILLIS);
|
|
mBackgroundDimAnimator.setStartDelay(ENTER_ANIM_START_DELAY_MILLIS);
|
|
|
|
dialogContainer.startAnimation(mDialogAnimation);
|
|
mBackgroundDimAnimator.start();
|
|
}
|
|
|
|
/**
|
|
* Starts both the background dim fade-out animation and the dialog exit animation.
|
|
*/
|
|
public void startExitAnimation(@NonNull T layout, Runnable endCallback) {
|
|
// Cancel any previous animation if it's still running.
|
|
cancelAnimation();
|
|
|
|
final View dialogContainer = layout.getDialogContainerView();
|
|
mDialogAnimation = loadAnimation(WindowAnimation_windowExitAnimation);
|
|
if (mDialogAnimation == null) {
|
|
endCallback.run();
|
|
return;
|
|
}
|
|
mDialogAnimation.setAnimationListener(getAnimationListener(
|
|
/* startCallback= */ () -> {},
|
|
/* endCallback= */ () -> {
|
|
dialogContainer.setAlpha(0);
|
|
mDialogAnimation = null;
|
|
endCallback.run();
|
|
}));
|
|
|
|
mBackgroundDimAnimator = getAlphaAnimator(layout.getBackgroundDimDrawable(),
|
|
/* endAlpha= */ 0, mDialogAnimation.getDuration());
|
|
mBackgroundDimAnimator.addListener(getDimAnimatorListener());
|
|
|
|
dialogContainer.startAnimation(mDialogAnimation);
|
|
mBackgroundDimAnimator.start();
|
|
}
|
|
|
|
/**
|
|
* Cancels all animations and resets the state of the controller.
|
|
*/
|
|
public void cancelAnimation() {
|
|
if (mDialogAnimation != null) {
|
|
mDialogAnimation.cancel();
|
|
mDialogAnimation = null;
|
|
}
|
|
if (mBackgroundDimAnimator != null) {
|
|
mBackgroundDimAnimator.cancel();
|
|
mBackgroundDimAnimator = null;
|
|
}
|
|
}
|
|
|
|
private Animation loadAnimation(int animAttr) {
|
|
Animation animation = mTransitionAnimation.loadAnimationAttr(mPackageName, mAnimStyleResId,
|
|
animAttr, /* translucent= */ false);
|
|
if (animation == null) {
|
|
Log.e(mTag, "Failed to load animation " + animAttr);
|
|
}
|
|
return animation;
|
|
}
|
|
|
|
private Animation.AnimationListener getAnimationListener(Runnable startCallback,
|
|
Runnable endCallback) {
|
|
return new Animation.AnimationListener() {
|
|
@Override
|
|
public void onAnimationStart(Animation animation) {
|
|
startCallback.run();
|
|
}
|
|
|
|
@Override
|
|
public void onAnimationEnd(Animation animation) {
|
|
endCallback.run();
|
|
}
|
|
|
|
@Override
|
|
public void onAnimationRepeat(Animation animation) {}
|
|
};
|
|
}
|
|
|
|
private AnimatorListenerAdapter getDimAnimatorListener() {
|
|
return new AnimatorListenerAdapter() {
|
|
@Override
|
|
public void onAnimationEnd(Animator animation) {
|
|
mBackgroundDimAnimator = null;
|
|
}
|
|
};
|
|
}
|
|
|
|
private static Animator getAlphaAnimator(
|
|
Drawable drawable, int endAlpha, long duration) {
|
|
Animator animator = ObjectAnimator.ofInt(drawable, DRAWABLE_ALPHA, endAlpha);
|
|
animator.setDuration(duration);
|
|
return animator;
|
|
}
|
|
|
|
private static final Property<Drawable, Integer> DRAWABLE_ALPHA = new IntProperty<Drawable>(
|
|
"alpha") {
|
|
@Override
|
|
public void setValue(Drawable object, int value) {
|
|
object.setAlpha(value);
|
|
}
|
|
|
|
@Override
|
|
public Integer get(Drawable object) {
|
|
return object.getAlpha();
|
|
}
|
|
};
|
|
}
|