diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java index 95bde801bf..6630aede3d 100644 --- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java +++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java @@ -17,6 +17,8 @@ package com.android.quickstep.views; import static android.provider.Settings.ACTION_APP_USAGE_SETTINGS; +import static android.view.Gravity.BOTTOM; +import static android.view.Gravity.CENTER_HORIZONTAL; import static com.android.launcher3.Utilities.prefixTextWithIcon; import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR; @@ -27,6 +29,7 @@ import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.pm.LauncherApps; import android.content.pm.LauncherApps.AppUsageLimit; +import android.graphics.Outline; import android.icu.text.MeasureFormat; import android.icu.text.MeasureFormat.FormatWidth; import android.icu.util.Measure; @@ -35,6 +38,9 @@ import android.os.Build; import android.os.UserHandle; import android.util.Log; import android.view.View; +import android.view.ViewGroup; +import android.view.ViewOutlineProvider; +import android.widget.FrameLayout; import android.widget.TextView; import androidx.annotation.StringRes; @@ -61,6 +67,10 @@ public final class DigitalWellBeingToast { private Task mTask; private boolean mHasLimit; private long mAppRemainingTimeMs; + private View mBanner; + private ViewOutlineProvider mOldBannerOutlineProvider; + private float mBannerOffsetPercentage; + private float mBannerAlpha = 1f; public DigitalWellBeingToast(BaseDraggingActivity activity, TaskView taskView) { mActivity = activity; @@ -68,18 +78,10 @@ public final class DigitalWellBeingToast { mLauncherApps = activity.getSystemService(LauncherApps.class); } - private void setTaskFooter(View view) { - View oldFooter = mTaskView.setFooter(TaskView.INDEX_DIGITAL_WELLBEING_TOAST, view); - if (oldFooter != null) { - oldFooter.setOnClickListener(null); - mActivity.getViewCache().recycleView(R.layout.digital_wellbeing_toast, oldFooter); - } - } - private void setNoLimit() { mHasLimit = false; mTaskView.setContentDescription(mTask.titleDescription); - setTaskFooter(null); + replaceBanner(null); mAppRemainingTimeMs = 0; } @@ -90,7 +92,7 @@ public final class DigitalWellBeingToast { mActivity, mTaskView); toast.setText(prefixTextWithIcon(mActivity, R.drawable.ic_hourglass_top, getText())); toast.setOnClickListener(this::openAppUsageSettings); - setTaskFooter(toast); + replaceBanner(toast); mTaskView.setContentDescription( getContentDescriptionForTask(mTask, appUsageLimitTimeMs, appRemainingTimeMs)); @@ -233,4 +235,64 @@ public final class DigitalWellBeingToast { getText(appRemainingTimeMs)) : task.titleDescription; } + + private void replaceBanner(View view) { + resetOldBanner(); + setBanner(view); + } + + private void resetOldBanner() { + if (mBanner != null) { + mBanner.setOutlineProvider(mOldBannerOutlineProvider); + mTaskView.removeView(mBanner); + mBanner.setOnClickListener(null); + mActivity.getViewCache().recycleView(R.layout.digital_wellbeing_toast, mBanner); + } + } + + private void setBanner(View view) { + mBanner = view; + if (view != null) { + setupAndAddBanner(); + setBannerOutline(); + } + } + + private void setupAndAddBanner() { + FrameLayout.LayoutParams layoutParams = + (FrameLayout.LayoutParams) mBanner.getLayoutParams(); + layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL; + layoutParams.bottomMargin = ((ViewGroup.MarginLayoutParams) + mTaskView.getThumbnail().getLayoutParams()).bottomMargin; + mBanner.setTranslationY(mBannerOffsetPercentage * mBanner.getHeight()); + mBanner.setAlpha(mBannerAlpha); + mTaskView.addView(mBanner); + } + + private void setBannerOutline() { + mOldBannerOutlineProvider = mBanner.getOutlineProvider(); + mBanner.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + mOldBannerOutlineProvider.getOutline(view, outline); + outline.offset(0, -Math.round(view.getTranslationY())); + } + }); + mBanner.setClipToOutline(true); + } + + void updateBannerOffset(float offsetPercentage) { + if (mBanner != null && mBannerOffsetPercentage != offsetPercentage) { + mBannerOffsetPercentage = offsetPercentage; + mBanner.setTranslationY(offsetPercentage * mBanner.getHeight()); + mBanner.invalidateOutline(); + } + } + + void updateBannerAlpha(float alpha) { + if (mBanner != null && mBannerAlpha != alpha) { + mBannerAlpha = alpha; + mBanner.setAlpha(alpha); + } + } } diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 5154018f6c..23da7835c8 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -42,7 +42,6 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; -import android.animation.ValueAnimator; import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; @@ -194,12 +193,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { private boolean mEndQuickswitchCuj; - // Order in which the footers appear. Lower order appear below higher order. - public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0; - private final FooterWrapper[] mFooters = new FooterWrapper[2]; - private float mFooterVerticalOffset = 0; - private float mFooterAlpha = 1; - private int mStackHeight; private View mContextualChipWrapper; private View mContextualChip; private final float[] mIconCenterCoords = new float[2]; @@ -343,7 +336,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { if (mContextualChipWrapper != null) { mContextualChipWrapper.setAlpha(comp(modalness)); } - updateFooterVerticalOffset(mFooterVerticalOffset); + mDigitalWellBeingToast.updateBannerOffset(modalness); } public TaskMenuView getMenuView() { @@ -567,7 +560,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mContextualChip.setScaleX(scale); mContextualChip.setScaleY(scale); } - updateFooterVerticalOffset(1.0f - scale); + mDigitalWellBeingToast.updateBannerOffset(1f - scale); } public void setIconScaleAnimStartProgress(float startProgress) { @@ -639,12 +632,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA); setCurveScale(curveScaleForCurveInterpolation); - mFooterAlpha = Utilities.boundToRange(1.0f - 2 * scrollState.linearInterpolation, 0f, 1f); - for (FooterWrapper footer : mFooters) { - if (footer != null) { - footer.mView.setAlpha(mFooterAlpha); - } - } + float dwbBannerAlpha = Utilities.boundToRange(1.0f - 2 * scrollState.linearInterpolation, + 0f, 1f); + mDigitalWellBeingToast.updateBannerAlpha(dwbBannerAlpha); if (mMenuView != null) { PagedOrientationHandler pagedOrientationHandler = getPagedOrientationHandler(); @@ -656,57 +646,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } } - /** - * Sets the footer at the specific index and returns the previously set footer. - */ - public View setFooter(int index, View view) { - View oldFooter = null; - - // If the footer are is already collapsed, do not animate entry - boolean shouldAnimateEntry = mFooterVerticalOffset <= 0; - - if (mFooters[index] != null) { - oldFooter = mFooters[index].mView; - mFooters[index].release(); - removeView(oldFooter); - - // If we are replacing an existing footer, do not animate entry - shouldAnimateEntry = false; - } - if (view != null) { - int indexToAdd = getChildCount(); - for (int i = index - 1; i >= 0; i--) { - if (mFooters[i] != null) { - indexToAdd = indexOfChild(mFooters[i].mView); - break; - } - } - - addView(view, indexToAdd); - LayoutParams layoutParams = (LayoutParams) view.getLayoutParams(); - layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL; - layoutParams.bottomMargin = - ((MarginLayoutParams) mSnapshotView.getLayoutParams()).bottomMargin; - view.setAlpha(mFooterAlpha); - mFooters[index] = new FooterWrapper(view); - if (shouldAnimateEntry) { - mFooters[index].animateEntry(); - } - } else { - mFooters[index] = null; - } - - mStackHeight = 0; - for (FooterWrapper footer : mFooters) { - if (footer != null) { - footer.setVerticalShift(mStackHeight); - mStackHeight += footer.mExpectedHeight; - } - } - - return oldFooter; - } - /** * Sets the contextual chip. * @@ -780,24 +719,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(0, 0, getWidth(), getHeight()); setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT); } - - mStackHeight = 0; - for (FooterWrapper footer : mFooters) { - if (footer != null) { - mStackHeight += footer.mView.getHeight(); - } - } - updateFooterVerticalOffset(0); - } - - private void updateFooterVerticalOffset(float offset) { - mFooterVerticalOffset = offset; - - for (FooterWrapper footer : mFooters) { - if (footer != null) { - footer.updateFooterOffset(); - } - } } public static float getCurveScaleForInterpolation(float linearInterpolation) { @@ -860,71 +781,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable { } } - private class FooterWrapper extends ViewOutlineProvider { - - final View mView; - final ViewOutlineProvider mOldOutlineProvider; - final ViewOutlineProvider mDelegate; - - final int mExpectedHeight; - final int mOldPaddingBottom; - - int mAnimationOffset = 0; - int mEntryAnimationOffset = 0; - - public FooterWrapper(View view) { - mView = view; - mOldOutlineProvider = view.getOutlineProvider(); - mDelegate = mOldOutlineProvider == null - ? ViewOutlineProvider.BACKGROUND : mOldOutlineProvider; - - mExpectedHeight = getExpectedViewHeight(view); - mOldPaddingBottom = view.getPaddingBottom(); - - if (mOldOutlineProvider != null) { - view.setOutlineProvider(this); - view.setClipToOutline(true); - } - } - - public void setVerticalShift(int shift) { - mView.setPadding(mView.getPaddingLeft(), mView.getPaddingTop(), - mView.getPaddingRight(), mOldPaddingBottom + shift); - } - - @Override - public void getOutline(View view, Outline outline) { - mDelegate.getOutline(view, outline); - outline.offset(0, -mAnimationOffset - mEntryAnimationOffset); - } - - void updateFooterOffset() { - float offset = Utilities.or(mFooterVerticalOffset, mModalness); - mAnimationOffset = Math.round(mStackHeight * offset); - mView.setTranslationY(mAnimationOffset + mEntryAnimationOffset - + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom - + mCurrentFullscreenParams.mCurrentDrawnInsets.top); - mView.invalidateOutline(); - } - - void release() { - mView.setOutlineProvider(mOldOutlineProvider); - setVerticalShift(0); - } - - void animateEntry() { - ValueAnimator animator = ValueAnimator.ofFloat(0, 1); - animator.addUpdateListener(anim -> { - float factor = 1 - anim.getAnimatedFraction(); - int totalShift = mExpectedHeight + mView.getPaddingBottom() - mOldPaddingBottom; - mEntryAnimationOffset = Math.round(factor * totalShift); - updateFooterOffset(); - }); - animator.setDuration(100); - animator.start(); - } - } - private int getExpectedViewHeight(View view) { int expectedHeight; int h = view.getLayoutParams().height;