Loading libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml +4 −1 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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" Loading libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java 0 → 100644 +197 −0 Original line number Diff line number Diff line /* * 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(); } }; } libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java +18 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading @@ -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); Loading @@ -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); } } libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java +23 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); } Loading Loading @@ -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; } Loading @@ -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() { mAnimationController.startExitAnimation(mLayout, () -> { release(); mOnDismissCallback.run(); }); } @Override public void release() { mAnimationController.cancelAnimation(); super.release(); } @Override Loading Loading
libs/WindowManager/Shell/res/layout/letterbox_education_dialog_layout.xml +4 −1 Original line number Diff line number Diff line Loading @@ -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" Loading @@ -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" Loading
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduAnimationController.java 0 → 100644 +197 −0 Original line number Diff line number Diff line /* * 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(); } }; }
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduDialogLayout.java +18 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); Loading @@ -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); Loading @@ -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); } }
libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduWindowManager.java +23 −3 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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); } Loading Loading @@ -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; } Loading @@ -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() { mAnimationController.startExitAnimation(mLayout, () -> { release(); mOnDismissCallback.run(); }); } @Override public void release() { mAnimationController.cancelAnimation(); super.release(); } @Override Loading