Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e081914f authored by Alex Chau's avatar Alex Chau
Browse files

Fix DWB banner not showing up for split tasks

- Remove DWB banner and thumbnailView from TaskView when being recycled to avoid multiple banners from staying
- Extracted DWB toastg splitbounds update to not be called whenever task icon is loaded
- Refactor DWB toast to remove unused variable, and extract mSplitBannerConfig to be a getter

Fix: 343212887
Test: OverviewImageTest.digitalWellbeingToast
Flag: EXEMPT bugfix
Change-Id: I2dbdc483a7b99de009c9e2a946e1b026c5e4817f
parent 3661e3f6
Loading
Loading
Loading
Loading
+65 −62
Original line number Diff line number Diff line
@@ -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,7 +136,9 @@ 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;
@@ -155,72 +155,76 @@ public final class DigitalWellBeingToast {
                    usageLimit != null ? usageLimit.getUsageRemaining() : -1;

            mTaskView.post(() -> {
                if (mIsDestroyed) {
                    return;
                }
                if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) {
                    setNoLimit();
                } else {
                    setLimit(appUsageLimitTimeMs, appRemainingTimeMs);
                }
            });

        });
    }
        );

    /**
     * Mark the DWB toast as destroyed and remove banner from TaskView.
     */
    public void destroy() {
        mIsDestroyed = true;
        mTaskView.post(() -> replaceBanner(null));
    }

    public void setSplitConfiguration(SplitBounds splitBounds) {
    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();
        }
+5 −0
Original line number Diff line number Diff line
@@ -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()
    }

+10 −4
Original line number Diff line number Diff line
@@ -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() {