mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-27 15:26:58 +00:00
Merge "Fix DWB banner not showing up for split tasks" into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
1d9f23b1ad
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user