Merge "Fix DWB banner not showing up for split tasks" into main

This commit is contained in:
Treehugger Robot
2024-05-30 11:04:37 +00:00
committed by Android (Google) Code Review
3 changed files with 79 additions and 65 deletions

View File

@@ -74,7 +74,7 @@ public final class DigitalWellBeingToast {
SPLIT_GRID_BANNER_SMALL,
})
@Retention(RetentionPolicy.SOURCE)
@interface SPLIT_BANNER_CONFIG{}
@interface SplitBannerConfig{}
static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
static final int MINUTE_MS = 60000;
@@ -88,7 +88,6 @@ public final class DigitalWellBeingToast {
private Task mTask;
private boolean mHasLimit;
private long mAppUsageLimitTimeMs;
private long mAppRemainingTimeMs;
@Nullable
private View mBanner;
@@ -96,10 +95,11 @@ public final class DigitalWellBeingToast {
private float mBannerOffsetPercentage;
@Nullable
private SplitBounds mSplitBounds;
private int mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN;
private float mSplitOffsetTranslationY;
private float mSplitOffsetTranslationX;
private boolean mIsDestroyed = false;
public DigitalWellBeingToast(RecentsViewContainer container, TaskView taskView) {
mContainer = container;
mTaskView = taskView;
@@ -110,12 +110,10 @@ public final class DigitalWellBeingToast {
mHasLimit = false;
mTaskView.setContentDescription(mTask.titleDescription);
replaceBanner(null);
mAppUsageLimitTimeMs = -1;
mAppRemainingTimeMs = -1;
}
private void setLimit(long appUsageLimitTimeMs, long appRemainingTimeMs) {
mAppUsageLimitTimeMs = appUsageLimitTimeMs;
mAppRemainingTimeMs = appRemainingTimeMs;
mHasLimit = true;
TextView toast = mContainer.getViewCache().getView(R.layout.digital_wellbeing_toast,
@@ -138,89 +136,95 @@ public final class DigitalWellBeingToast {
}
public void initialize(Task task) {
mAppUsageLimitTimeMs = mAppRemainingTimeMs = -1;
if (mIsDestroyed) {
throw new IllegalStateException("Cannot re-initialize a destroyed toast");
}
mTask = task;
ORDERED_BG_EXECUTOR.execute(() -> {
AppUsageLimit usageLimit = null;
try {
usageLimit = mLauncherApps.getAppUsageLimit(
mTask.getTopComponent().getPackageName(),
UserHandle.of(mTask.key.userId));
} catch (Exception e) {
Log.e(TAG, "Error initializing digital well being toast", e);
}
final long appUsageLimitTimeMs =
usageLimit != null ? usageLimit.getTotalUsageLimit() : -1;
final long appRemainingTimeMs =
usageLimit != null ? usageLimit.getUsageRemaining() : -1;
mTaskView.post(() -> {
if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) {
setNoLimit();
} else {
setLimit(appUsageLimitTimeMs, appRemainingTimeMs);
}
});
AppUsageLimit usageLimit = null;
try {
usageLimit = mLauncherApps.getAppUsageLimit(
mTask.getTopComponent().getPackageName(),
UserHandle.of(mTask.key.userId));
} catch (Exception e) {
Log.e(TAG, "Error initializing digital well being toast", e);
}
final long appUsageLimitTimeMs =
usageLimit != null ? usageLimit.getTotalUsageLimit() : -1;
final long appRemainingTimeMs =
usageLimit != null ? usageLimit.getUsageRemaining() : -1;
mTaskView.post(() -> {
if (mIsDestroyed) {
return;
}
);
if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) {
setNoLimit();
} else {
setLimit(appUsageLimitTimeMs, appRemainingTimeMs);
}
});
});
}
public void setSplitConfiguration(SplitBounds splitBounds) {
/**
* Mark the DWB toast as destroyed and remove banner from TaskView.
*/
public void destroy() {
mIsDestroyed = true;
mTaskView.post(() -> replaceBanner(null));
}
public void setSplitBounds(@Nullable SplitBounds splitBounds) {
mSplitBounds = splitBounds;
}
private @SplitBannerConfig int getSplitBannerConfig() {
if (mSplitBounds == null
|| !mContainer.getDeviceProfile().isTablet
|| mTaskView.isFocusedTask()) {
mSplitBannerConfig = SPLIT_BANNER_FULLSCREEN;
return;
return SPLIT_BANNER_FULLSCREEN;
}
// For portrait grid only height of task changes, not width. So we keep the text the same
if (!mContainer.getDeviceProfile().isLeftRightSplit) {
mSplitBannerConfig = SPLIT_GRID_BANNER_LARGE;
return;
return SPLIT_GRID_BANNER_LARGE;
}
// For landscape grid, for 30% width we only show icon, otherwise show icon and time
if (mTask.key.id == mSplitBounds.leftTopTaskId) {
mSplitBannerConfig = mSplitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY ?
SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
return mSplitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY
? SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
} else {
mSplitBannerConfig = mSplitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY ?
SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
return mSplitBounds.leftTaskPercent > THRESHOLD_RIGHT_ICON_ONLY
? SPLIT_GRID_BANNER_SMALL : SPLIT_GRID_BANNER_LARGE;
}
}
private String getReadableDuration(
Duration duration,
FormatWidth formatWidthHourAndMinute,
@StringRes int durationLessThanOneMinuteStringId,
boolean forceFormatWidth) {
@StringRes int durationLessThanOneMinuteStringId) {
int hours = Math.toIntExact(duration.toHours());
int minutes = Math.toIntExact(duration.minusHours(hours).toMinutes());
// Apply formatWidthHourAndMinute if both the hour part and the minute part are non-zero.
// Apply FormatWidth.WIDE if both the hour part and the minute part are non-zero.
if (hours > 0 && minutes > 0) {
return MeasureFormat.getInstance(Locale.getDefault(), formatWidthHourAndMinute)
return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.NARROW)
.formatMeasures(
new Measure(hours, MeasureUnit.HOUR),
new Measure(minutes, MeasureUnit.MINUTE));
}
// Apply formatWidthHourOrMinute if only the hour part is non-zero (unless forced).
// Apply FormatWidth.WIDE if only the hour part is non-zero (unless forced).
if (hours > 0) {
return MeasureFormat.getInstance(
Locale.getDefault(),
forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
.formatMeasures(new Measure(hours, MeasureUnit.HOUR));
return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.WIDE).formatMeasures(
new Measure(hours, MeasureUnit.HOUR));
}
// Apply formatWidthHourOrMinute if only the minute part is non-zero (unless forced).
// Apply FormatWidth.WIDE if only the minute part is non-zero (unless forced).
if (minutes > 0) {
return MeasureFormat.getInstance(
Locale.getDefault()
, forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
.formatMeasures(new Measure(minutes, MeasureUnit.MINUTE));
return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.WIDE).formatMeasures(
new Measure(minutes, MeasureUnit.MINUTE));
}
// Use a specific string for usage less than one minute but non-zero.
@@ -229,13 +233,12 @@ public final class DigitalWellBeingToast {
}
// Otherwise, return 0-minute string.
return MeasureFormat.getInstance(
Locale.getDefault(), forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
.formatMeasures(new Measure(0, MeasureUnit.MINUTE));
return MeasureFormat.getInstance(Locale.getDefault(), FormatWidth.WIDE).formatMeasures(
new Measure(0, MeasureUnit.MINUTE));
}
/**
* Returns text to show for the banner depending on {@link #mSplitBannerConfig}
* Returns text to show for the banner depending on {@link #getSplitBannerConfig()}
* If {@param forContentDesc} is {@code true}, this will always return the full
* string corresponding to {@link #SPLIT_BANNER_FULLSCREEN}
*/
@@ -245,16 +248,16 @@ public final class DigitalWellBeingToast {
(remainingTime + MINUTE_MS - 1) / MINUTE_MS * MINUTE_MS :
remainingTime);
String readableDuration = getReadableDuration(duration,
FormatWidth.NARROW,
R.string.shorter_duration_less_than_one_minute,
false /* forceFormatWidth */);
if (forContentDesc || mSplitBannerConfig == SPLIT_BANNER_FULLSCREEN) {
R.string.shorter_duration_less_than_one_minute
/* forceFormatWidth */);
@SplitBannerConfig int splitBannerConfig = getSplitBannerConfig();
if (forContentDesc || splitBannerConfig == SPLIT_BANNER_FULLSCREEN) {
return mContainer.asContext().getString(
R.string.time_left_for_app,
readableDuration);
}
if (mSplitBannerConfig == SPLIT_GRID_BANNER_SMALL) {
if (splitBannerConfig == SPLIT_GRID_BANNER_SMALL) {
// show no text
return "";
} else { // SPLIT_GRID_BANNER_LARGE
@@ -309,7 +312,7 @@ public final class DigitalWellBeingToast {
private void setBanner(@Nullable View view) {
mBanner = view;
if (view != null && mTaskView.getRecentsView() != null) {
if (mBanner != null && mTaskView.getRecentsView() != null) {
setupAndAddBanner();
setBannerOutline();
}

View File

@@ -162,6 +162,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
PreviewPositionHelper.STAGE_POSITION_BOTTOM_OR_RIGHT
)
}
taskContainers.forEach { it.digitalWellBeingToast?.setSplitBounds(splitBoundsConfig) }
setOrientationState(orientedState)
}
@@ -240,6 +241,10 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
fun updateSplitBoundsConfig(splitBounds: SplitConfigurationOptions.SplitBounds?) {
splitBoundsConfig = splitBounds
taskContainers.forEach {
it.digitalWellBeingToast?.setSplitBounds(splitBoundsConfig)
it.digitalWellBeingToast?.initialize(it.task)
}
invalidate()
}

View File

@@ -512,6 +512,7 @@ constructor(
onTaskListVisibilityChanged(false)
borderEnabled = false
taskViewId = UNBOUND_TASK_VIEW_ID
taskContainers.forEach { it.destroy() }
}
// TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
@@ -801,12 +802,12 @@ constructor(
taskContainers.forEach {
if (visible) {
recentsModel.iconCache
.updateIconInBackground(it.task) { thumbnailData ->
setIcon(it.iconView, thumbnailData.icon)
.updateIconInBackground(it.task) { task ->
setIcon(it.iconView, task.icon)
if (enableOverviewIconMenu()) {
setText(it.iconView, thumbnailData.title)
setText(it.iconView, task.title)
}
it.digitalWellBeingToast?.initialize(thumbnailData)
it.digitalWellBeingToast?.initialize(task)
}
?.also { request -> pendingIconLoadRequests.add(request) }
} else {
@@ -1586,6 +1587,11 @@ constructor(
val taskView: TaskView
get() = this@TaskView
fun destroy() {
digitalWellBeingToast?.destroy()
thumbnailView?.let { taskView.removeView(it) }
}
// TODO(b/335649589): TaskView's VM will already have access to TaskThumbnailView's VM
// so there will be no need to access TaskThumbnailView's VM through the TaskThumbnailView
fun bindThumbnailView() {