Show Digital Wellbeing Banners for split tasks

Fixes: 199936292
Change-Id: I38743d58f53a65ef717b1babc21ad1df2dc840a4
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
index dbdcf19..e5664c6 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -19,6 +19,7 @@
 import static android.provider.Settings.ACTION_APP_USAGE_SETTINGS;
 import static android.view.Gravity.BOTTOM;
 import static android.view.Gravity.CENTER_HORIZONTAL;
+import static android.view.Gravity.START;
 
 import static com.android.launcher3.Utilities.prefixTextWithIcon;
 import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
@@ -38,26 +39,51 @@
 import android.os.Build;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewOutlineProvider;
 import android.widget.FrameLayout;
 import android.widget.TextView;
 
+import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.SplitConfigurationOptions.StagedSplitBounds;
 import com.android.systemui.shared.recents.model.Task;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.time.Duration;
 import java.util.Locale;
 
 @TargetApi(Build.VERSION_CODES.Q)
 public final class DigitalWellBeingToast {
+
+    private static final float THRESHOLD_LEFT_ICON_ONLY = 0.4f;
+    private static final float THRESHOLD_RIGHT_ICON_ONLY = 0.6f;
+
+    /** Will span entire width of taskView with full text */
+    private static final int SPLIT_BANNER_FULLSCREEN = 0;
+    /** Used for grid task view, only showing icon and time */
+    private static final int SPLIT_GRID_BANNER_LARGE = 1;
+    /** Used for grid task view, only showing icon */
+    private static final int SPLIT_GRID_BANNER_SMALL = 2;
+    @IntDef(value = {
+            SPLIT_BANNER_FULLSCREEN,
+            SPLIT_GRID_BANNER_LARGE,
+            SPLIT_GRID_BANNER_SMALL,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface SPLIT_BANNER_CONFIG{}
+
     static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
     static final int MINUTE_MS = 60000;
 
@@ -74,7 +100,16 @@
     private View mBanner;
     private ViewOutlineProvider mOldBannerOutlineProvider;
     private float mBannerOffsetPercentage;
-    private float mVerticalOffset = 0f;
+    /**
+     * Clips rect provided by {@link #mOldBannerOutlineProvider} when in the model state to
+     * hide this banner as the taskView scales up and down
+     */
+    private float mModalOffset = 0f;
+    @Nullable
+    private StagedSplitBounds mStagedSplitBounds;
+    private int mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN;
+    private float mSplitOffsetTranslationY;
+    private float mSplitOffsetTranslationX;
 
     public DigitalWellBeingToast(BaseDraggingActivity activity, TaskView taskView) {
         mActivity = activity;
@@ -103,7 +138,7 @@
     }
 
     public String getText() {
-        return getText(mAppRemainingTimeMs);
+        return getText(mAppRemainingTimeMs, false /* forContentDesc */);
     }
 
     public boolean hasLimit() {
@@ -138,6 +173,31 @@
         });
     }
 
+    public void setSplitConfiguration(StagedSplitBounds stagedSplitBounds) {
+        mStagedSplitBounds = stagedSplitBounds;
+        if (mStagedSplitBounds == null ||
+                !mActivity.getDeviceProfile().overviewShowAsGrid ||
+                mTaskView.isFocusedTask()) {
+            mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN;
+            return;
+        }
+
+        // For portrait grid only height of task changes, not width. So we keep the text the same
+        if (!mActivity.getDeviceProfile().isLandscape) {
+            mSplitBannerConfig = SPLIT_GRID_BANNER_LARGE;
+            return;
+        }
+
+        // For landscape grid, for 30% width we only show icon, otherwise show icon and time
+        if (mTask.key.id == mStagedSplitBounds.leftTopTaskId) {
+            mSplitBannerConfig = mStagedSplitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY ?
+                    SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
+        } else {
+            mSplitBannerConfig = mStagedSplitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY ?
+                    SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
+        }
+    }
+
     private String getReadableDuration(
             Duration duration,
             FormatWidth formatWidthHourAndMinute,
@@ -181,30 +241,33 @@
                 .formatMeasures(new Measure(0, MeasureUnit.MINUTE));
     }
 
-    private String getReadableDuration(
-            Duration duration,
-            FormatWidth formatWidthHourAndMinute,
-            @StringRes int durationLessThanOneMinuteStringId) {
-        return getReadableDuration(
-                duration,
-                formatWidthHourAndMinute,
-                durationLessThanOneMinuteStringId,
-                /* forceFormatWidth= */ false);
-    }
-
-    private String getRoundedUpToMinuteReadableDuration(long remainingTime) {
+    /**
+     * Returns text to show for the banner depending on {@link #mSplitBannerConfig}
+     * If {@param forContentDesc} is {@code true}, this will always return the full
+     * string corresponding to {@link #SPLIT_BANNER_FULLSCREEN}
+     */
+    private String getText(long remainingTime, boolean forContentDesc) {
         final Duration duration = Duration.ofMillis(
                 remainingTime > MINUTE_MS ?
                         (remainingTime + MINUTE_MS - 1) / MINUTE_MS * MINUTE_MS :
                         remainingTime);
-        return getReadableDuration(
-                duration, FormatWidth.NARROW, R.string.shorter_duration_less_than_one_minute);
-    }
+        String readableDuration = getReadableDuration(duration,
+                FormatWidth.NARROW,
+                R.string.shorter_duration_less_than_one_minute,
+                false /* forceFormatWidth */);
+        if (forContentDesc || mSplitBannerConfig == SPLIT_BANNER_FULLSCREEN) {
+            return mActivity.getString(
+                    R.string.time_left_for_app,
+                    readableDuration);
+        }
 
-    private String getText(long remainingTime) {
-        return mActivity.getString(
-                R.string.time_left_for_app,
-                getRoundedUpToMinuteReadableDuration(remainingTime));
+        if (mSplitBannerConfig == SPLIT_GRID_BANNER_SMALL) {
+            // show no text
+            return "";
+        } else { // SPLIT_GRID_BANNER_LARGE
+            // only show time
+            return readableDuration;
+        }
     }
 
     public void openAppUsageSettings(View view) {
@@ -232,7 +295,7 @@
                 mActivity.getString(
                         R.string.task_contents_description_with_remaining_time,
                         task.titleDescription,
-                        getText(appRemainingTimeMs)) :
+                        getText(appRemainingTimeMs, true /* forContentDesc */)) :
                 task.titleDescription;
     }
 
@@ -261,10 +324,18 @@
     private void setupAndAddBanner() {
         FrameLayout.LayoutParams layoutParams =
                 (FrameLayout.LayoutParams) mBanner.getLayoutParams();
-        layoutParams.gravity = BOTTOM | CENTER_HORIZONTAL;
+        DeviceProfile deviceProfile = mActivity.getDeviceProfile();
         layoutParams.bottomMargin = ((ViewGroup.MarginLayoutParams)
                 mTaskView.getThumbnail().getLayoutParams()).bottomMargin;
-        mBanner.setTranslationY(mBannerOffsetPercentage * mBanner.getHeight());
+        PagedOrientationHandler orientationHandler = mTaskView.getPagedOrientationHandler();
+        Pair<Float, Float> translations = orientationHandler
+                .setDwbLayoutParamsAndGetTranslations(mTaskView.getMeasuredWidth(),
+                        mTaskView.getMeasuredHeight(), mStagedSplitBounds, deviceProfile,
+                        mTaskView.getThumbnails(), mTask.key.id, mBanner);
+        mSplitOffsetTranslationX = translations.first;
+        mSplitOffsetTranslationY = translations.second;
+        updateTranslationY();
+        updateTranslationX();
         mTaskView.addView(mBanner);
     }
 
@@ -274,7 +345,9 @@
             @Override
             public void getOutline(View view, Outline outline) {
                 mOldBannerOutlineProvider.getOutline(view, outline);
-                outline.offset(0, Math.round(-view.getTranslationY() + mVerticalOffset));
+                float verticalTranslation = -view.getTranslationY() + mModalOffset
+                        + mSplitOffsetTranslationY;
+                outline.offset(0, Math.round(verticalTranslation));
             }
         });
         mBanner.setClipToOutline(true);
@@ -282,13 +355,33 @@
 
     void updateBannerOffset(float offsetPercentage, float verticalOffset) {
         if (mBanner != null && mBannerOffsetPercentage != offsetPercentage) {
-            mVerticalOffset = verticalOffset;
+            mModalOffset = verticalOffset;
             mBannerOffsetPercentage = offsetPercentage;
-            mBanner.setTranslationY(offsetPercentage * mBanner.getHeight() + mVerticalOffset);
+            updateTranslationY();
             mBanner.invalidateOutline();
         }
     }
 
+    private void updateTranslationY() {
+        if (mBanner == null) {
+            return;
+        }
+
+        mBanner.setTranslationY(
+                (mBannerOffsetPercentage * mBanner.getHeight()) +
+                        mModalOffset +
+                        mSplitOffsetTranslationY
+        );
+    }
+
+    private void updateTranslationX() {
+        if (mBanner == null) {
+            return;
+        }
+
+        mBanner.setTranslationX(mSplitOffsetTranslationX);
+    }
+
     void setBannerColorTint(int color, float amount) {
         if (mBanner == null) {
             return;
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index b215ef1..7e4f9d0 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -50,17 +50,20 @@
     private final float[] mIcon2CenterCoords = new float[2];
     private TransformingTouchDelegate mIcon2TouchDelegate;
     @Nullable private StagedSplitBounds mSplitBoundsConfig;
+    private final DigitalWellBeingToast mDigitalWellBeingToast2;
+
 
     public GroupedTaskView(Context context) {
-        super(context);
+        this(context, null);
     }
 
     public GroupedTaskView(Context context, AttributeSet attrs) {
-        super(context, attrs);
+        this(context, attrs, 0);
     }
 
     public GroupedTaskView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
+        mDigitalWellBeingToast2 = new DigitalWellBeingToast(mActivity, this);
     }
 
     @Override
@@ -102,7 +105,9 @@
                 mIconLoadRequest2 = iconCache.updateIconInBackground(mSecondaryTask,
                         (task) -> {
                             setIcon(mIconView2, task.icon);
-                            // TODO(199936292) Digital Wellbeing for individual tasks?
+                            mDigitalWellBeingToast2.initialize(mSecondaryTask);
+                            mDigitalWellBeingToast2.setSplitConfiguration(mSplitBoundsConfig);
+                            mDigitalWellBeingToast.setSplitConfiguration(mSplitBoundsConfig);
                         });
             }
         } else {
@@ -262,6 +267,19 @@
     @Override
     protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
         super.setIconAndDimTransitionProgress(progress, invert);
-        mIconView2.setAlpha(mIconView.getAlpha());
+        // Value set by super call
+        float scale = mIconView.getAlpha();
+        mIconView2.setAlpha(scale);
+        mDigitalWellBeingToast2.updateBannerOffset(1f - scale,
+                mCurrentFullscreenParams.mCurrentDrawnInsets.top
+                        + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom);
+    }
+
+    @Override
+    public void setColorTint(float amount, int tintColor) {
+        super.setColorTint(amount, tintColor);
+        mIconView2.setIconColorTint(tintColor, amount);
+        mSnapshotView2.setDimAlpha(amount);
+        mDigitalWellBeingToast2.setBannerColorTint(tintColor, amount);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index e33d650..a71e558 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -366,7 +366,7 @@
     protected Task mTask;
     protected TaskThumbnailView mSnapshotView;
     protected IconView mIconView;
-    private final DigitalWellBeingToast mDigitalWellBeingToast;
+    protected final DigitalWellBeingToast mDigitalWellBeingToast;
     private float mFullscreenProgress;
     private float mGridProgress;
     private float mNonGridScale = 1;
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 9a2d6d8..a94ad7c 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -309,6 +309,46 @@
         return new PointF(margin, 0);
     }
 
+    @Override
+    public Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth,
+            int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+            View[] thumbnailViews, int desiredTaskId, View banner) {
+        float translationX = 0;
+        float translationY = 0;
+        FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
+        banner.setPivotX(0);
+        banner.setPivotY(0);
+        banner.setRotation(getDegreesRotated());
+        translationX = banner.getHeight();
+        FrameLayout.LayoutParams snapshotParams =
+                (FrameLayout.LayoutParams) thumbnailViews[0]
+                        .getLayoutParams();
+        bannerParams.gravity = TOP | START;
+        if (splitBounds == null) {
+            // Single, fullscreen case
+            bannerParams.width = taskViewHeight - snapshotParams.topMargin;
+            return new Pair<>(translationX, Integer.valueOf(snapshotParams.topMargin).floatValue());
+        }
+
+        // Set correct width
+        if (desiredTaskId == splitBounds.leftTopTaskId) {
+            bannerParams.width = thumbnailViews[0].getMeasuredHeight();
+        } else {
+            bannerParams.width = thumbnailViews[1].getMeasuredHeight();
+        }
+
+        // Set translations
+        if (desiredTaskId == splitBounds.rightBottomTaskId) {
+            translationY = (snapshotParams.topMargin + taskViewHeight)
+                    * (splitBounds.leftTaskPercent) +
+                    (taskViewHeight * splitBounds.dividerWidthPercent);
+        }
+        if (desiredTaskId == splitBounds.leftTopTaskId) {
+            translationY = snapshotParams.topMargin;
+        }
+        return new Pair<>(translationX, translationY);
+    }
+
     /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
 
     @Override
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 95168fb..19c4639 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -191,6 +191,10 @@
      */
     PointF getAdditionalInsetForTaskMenu(float margin);
 
+    Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth,
+            int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+            View[] thumbnailViews, int desiredTaskId, View banner);
+
     // The following are only used by TaskViewTouchHandler.
     /** @return Either VERTICAL or HORIZONTAL. */
     SingleAxisSwipeDetector.Direction getUpDownSwipeDirection();
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 7d70f77..ad9f95c 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -16,9 +16,11 @@
 
 package com.android.launcher3.touch;
 
+import static android.view.Gravity.BOTTOM;
 import static android.view.Gravity.CENTER_HORIZONTAL;
 import static android.view.Gravity.START;
 import static android.view.Gravity.TOP;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
@@ -321,6 +323,50 @@
         return new PointF(0, 0);
     }
 
+    @Override
+    public Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth,
+            int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+            View[] thumbnailViews, int desiredTaskId, View banner) {
+        float translationX = 0;
+        float translationY = 0;
+        FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
+        banner.setPivotX(0);
+        banner.setPivotY(0);
+        banner.setRotation(getDegreesRotated());
+        if (splitBounds == null) {
+            // Single, fullscreen case
+            bannerParams.width = MATCH_PARENT;
+            bannerParams.gravity = BOTTOM | CENTER_HORIZONTAL;
+            return new Pair<>(translationX, translationY);
+        }
+
+        bannerParams.gravity = BOTTOM | ((deviceProfile.isLandscape) ? START : CENTER_HORIZONTAL);
+
+        // Set correct width
+        if (desiredTaskId == splitBounds.leftTopTaskId) {
+            bannerParams.width = thumbnailViews[0].getMeasuredWidth();
+        } else {
+            bannerParams.width = thumbnailViews[1].getMeasuredWidth();
+        }
+
+        // Set translations
+        if (deviceProfile.isLandscape) {
+            if (desiredTaskId == splitBounds.rightBottomTaskId) {
+                translationX = ((taskViewWidth * splitBounds.leftTaskPercent)
+                                + (taskViewWidth * splitBounds.dividerWidthPercent));
+            }
+        } else {
+            if (desiredTaskId == splitBounds.leftTopTaskId) {
+                FrameLayout.LayoutParams snapshotParams =
+                        (FrameLayout.LayoutParams) thumbnailViews[0]
+                                .getLayoutParams();
+                translationY = -((taskViewHeight - snapshotParams.topMargin)
+                        * (1f - splitBounds.topTaskPercent));
+            }
+        }
+        return new Pair<>(translationX, translationY);
+    }
+
     /* ---------- The following are only used by TaskViewTouchHandler. ---------- */
 
     @Override
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index a921f94..de5f99c 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.touch;
 
+import static android.view.Gravity.BOTTOM;
 import static android.view.Gravity.CENTER_VERTICAL;
 import static android.view.Gravity.END;
 import static android.view.Gravity.START;
@@ -29,6 +30,7 @@
 import android.content.res.Resources;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.util.Pair;
 import android.view.Surface;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -106,6 +108,47 @@
     }
 
     @Override
+    public Pair<Float, Float> setDwbLayoutParamsAndGetTranslations(int taskViewWidth,
+            int taskViewHeight, StagedSplitBounds splitBounds, DeviceProfile deviceProfile,
+            View[] thumbnailViews, int desiredTaskId, View banner) {
+        float translationX = 0;
+        float translationY = 0;
+        FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
+        banner.setPivotX(0);
+        banner.setPivotY(0);
+        banner.setRotation(getDegreesRotated());
+        FrameLayout.LayoutParams snapshotParams =
+                (FrameLayout.LayoutParams) thumbnailViews[0]
+                        .getLayoutParams();
+        bannerParams.gravity = BOTTOM | END;
+        translationX = taskViewWidth - banner.getHeight();
+        if (splitBounds == null) {
+            // Single, fullscreen case
+            bannerParams.width = taskViewHeight - snapshotParams.topMargin;
+            translationY = banner.getHeight();
+            return new Pair<>(translationX, translationY);
+        }
+
+        // Set correct width
+        if (desiredTaskId == splitBounds.leftTopTaskId) {
+            bannerParams.width = thumbnailViews[1].getMeasuredHeight();
+        } else {
+            bannerParams.width = thumbnailViews[0].getMeasuredHeight();
+        }
+
+        // Set translations
+        if (desiredTaskId == splitBounds.rightBottomTaskId) {
+            translationY = -(taskViewHeight - snapshotParams.topMargin)
+                    * (1f - splitBounds.leftTaskPercent)
+                    + banner.getHeight();
+        }
+        if (desiredTaskId == splitBounds.leftTopTaskId) {
+            translationY = banner.getHeight();
+        }
+        return new Pair<>(translationX, translationY);
+    }
+
+    @Override
     public int getDistanceToBottomOfRect(DeviceProfile dp, Rect rect) {
         return dp.widthPx - rect.right;
     }