[7/n] Letterbox Education: add enter/exit animation to the dialog.
Bug: 207010227 Test: N/A Change-Id: Ie5f88b97c16a7bd043c6c63a8693415563b56a54
This commit is contained in:
@@ -22,6 +22,8 @@
|
||||
|
||||
<!-- The background of the top-level layout acts as the background dim. -->
|
||||
|
||||
<!-- Setting the alpha of the dialog container to 0, since it shouldn't be visible until the
|
||||
enter animation starts. -->
|
||||
<LinearLayout
|
||||
android:id="@+id/letterbox_education_dialog_container"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -30,7 +32,8 @@
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:background="@drawable/letterbox_education_dialog_background"
|
||||
android:padding="24dp">
|
||||
android:padding="24dp"
|
||||
android:alpha="0">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/letterbox_education_icon"
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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.letterboxedu;
|
||||
|
||||
import static com.android.internal.R.styleable.WindowAnimation_windowEnterAnimation;
|
||||
import static com.android.internal.R.styleable.WindowAnimation_windowExitAnimation;
|
||||
|
||||
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 animations of the letterbox education.
|
||||
*/
|
||||
// TODO(b/215316431): Add tests
|
||||
class LetterboxEduAnimationController {
|
||||
private static final String TAG = "LetterboxEduAnimation";
|
||||
|
||||
private final TransitionAnimation mTransitionAnimation;
|
||||
private final String mPackageName;
|
||||
@AnyRes
|
||||
private final int mAnimStyleResId;
|
||||
|
||||
@Nullable
|
||||
private Animation mDialogAnimation;
|
||||
@Nullable
|
||||
private Animator mBackgroundDimAnimator;
|
||||
|
||||
LetterboxEduAnimationController(Context context) {
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts both background dim fade-in animation and the dialog enter animation.
|
||||
*/
|
||||
void startEnterAnimation(@NonNull LetterboxEduDialogLayout layout, Runnable endCallback) {
|
||||
// Cancel any previous animation if it's still running.
|
||||
cancelAnimation();
|
||||
|
||||
final View dialogContainer = layout.getDialogContainer();
|
||||
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.getBackgroundDim(),
|
||||
/* endAlpha= */ LetterboxEduDialogLayout.BACKGROUND_DIM_ALPHA,
|
||||
mDialogAnimation.getDuration());
|
||||
mBackgroundDimAnimator.addListener(getDimAnimatorListener());
|
||||
|
||||
dialogContainer.startAnimation(mDialogAnimation);
|
||||
mBackgroundDimAnimator.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts both the background dim fade-out animation and the dialog exit animation.
|
||||
*/
|
||||
void startExitAnimation(@Nullable LetterboxEduDialogLayout layout, Runnable endCallback) {
|
||||
// Cancel any previous animation if it's still running.
|
||||
cancelAnimation();
|
||||
|
||||
if (layout == null) {
|
||||
endCallback.run();
|
||||
return;
|
||||
}
|
||||
|
||||
final View dialogContainer = layout.getDialogContainer();
|
||||
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.getBackgroundDim(), /* endAlpha= */ 0,
|
||||
mDialogAnimation.getDuration());
|
||||
mBackgroundDimAnimator.addListener(getDimAnimatorListener());
|
||||
|
||||
dialogContainer.startAnimation(mDialogAnimation);
|
||||
mBackgroundDimAnimator.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels all animations and resets the state of the controller.
|
||||
*/
|
||||
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(TAG, "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();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -17,7 +17,9 @@
|
||||
package com.android.wm.shell.compatui.letterboxedu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
@@ -33,9 +35,11 @@ class LetterboxEduDialogLayout extends FrameLayout {
|
||||
|
||||
// The alpha of a background is a number between 0 (fully transparent) to 255 (fully opaque).
|
||||
// 204 is simply 255 * 0.8.
|
||||
private static final int BACKGROUND_DIM_ALPHA = 204;
|
||||
static final int BACKGROUND_DIM_ALPHA = 204;
|
||||
|
||||
private LetterboxEduWindowManager mWindowManager;
|
||||
private View mDialogContainer;
|
||||
private Drawable mBackgroundDim;
|
||||
|
||||
public LetterboxEduDialogLayout(Context context) {
|
||||
this(context, null);
|
||||
@@ -58,6 +62,14 @@ class LetterboxEduDialogLayout extends FrameLayout {
|
||||
mWindowManager = windowManager;
|
||||
}
|
||||
|
||||
View getDialogContainer() {
|
||||
return mDialogContainer;
|
||||
}
|
||||
|
||||
Drawable getBackgroundDim() {
|
||||
return mBackgroundDim;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
@@ -77,12 +89,15 @@ class LetterboxEduDialogLayout extends FrameLayout {
|
||||
setOnClickListener(view -> callback.run());
|
||||
// We add a no-op on-click listener to the dialog container so that clicks on it won't
|
||||
// propagate to the listener of the layout (which represents the background dim).
|
||||
findViewById(R.id.letterbox_education_dialog_container).setOnClickListener(view -> {});
|
||||
mDialogContainer.setOnClickListener(view -> {});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
getBackground().mutate().setAlpha(BACKGROUND_DIM_ALPHA);
|
||||
mDialogContainer = findViewById(R.id.letterbox_education_dialog_container);
|
||||
mBackgroundDim = getBackground().mutate();
|
||||
// Set the alpha of the background dim to 0 for enter animation.
|
||||
mBackgroundDim.setAlpha(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,8 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
*/
|
||||
private final SharedPreferences mSharedPreferences;
|
||||
|
||||
private final LetterboxEduAnimationController mAnimationController;
|
||||
|
||||
// Remember the last reported state in case visibility changes due to keyguard or IME updates.
|
||||
private boolean mEligibleForLetterboxEducation;
|
||||
|
||||
@@ -69,6 +71,7 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
super(context, taskInfo, syncQueue, taskListener, displayLayout);
|
||||
mOnDismissCallback = onDismissCallback;
|
||||
mEligibleForLetterboxEducation = taskInfo.topActivityEligibleForLetterboxEducation;
|
||||
mAnimationController = new LetterboxEduAnimationController(context);
|
||||
mSharedPreferences = mContext.getSharedPreferences(HAS_SEEN_LETTERBOX_EDUCATION_PREF_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
}
|
||||
@@ -101,7 +104,9 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
setSeenLetterboxEducation();
|
||||
mLayout = inflateLayout();
|
||||
mLayout.inject(this);
|
||||
mLayout.setDismissOnClickListener(this::onDismiss);
|
||||
|
||||
mAnimationController.startEnterAnimation(mLayout, /* endCallback= */
|
||||
this::setDismissOnClickListener);
|
||||
|
||||
return mLayout;
|
||||
}
|
||||
@@ -111,9 +116,24 @@ public class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
|
||||
R.layout.letterbox_education_dialog_layout, null);
|
||||
}
|
||||
|
||||
private void setDismissOnClickListener() {
|
||||
if (mLayout == null) {
|
||||
return;
|
||||
}
|
||||
mLayout.setDismissOnClickListener(this::onDismiss);
|
||||
}
|
||||
|
||||
private void onDismiss() {
|
||||
release();
|
||||
mOnDismissCallback.run();
|
||||
mAnimationController.startExitAnimation(mLayout, () -> {
|
||||
release();
|
||||
mOnDismissCallback.run();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
mAnimationController.cancelAnimation();
|
||||
super.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user