Merge "Show Digital Wellbeing Banners for split tasks" into sc-v2-dev
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;
}