Implementing available part of UX spec for DW toast

See https://docs.google.com/presentation/d/1AepsnLeKcRhjMW35SkB5yMKO3u6waigug8Tyfe0LO5o/edit#slide=id.g4c5ab81849_0_0

Also using time formatting code copy-pasted from Google DWB app.

Bug: 118319143
Tests: Manual
Change-Id: I84392d7655f402e38cf4c46ae530d06f755a7df8
This commit is contained in:
vadimt 2019-01-14 14:41:29 -08:00
parent ff9571b30f
commit 759db43f3b
5 changed files with 124 additions and 13 deletions

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2019 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="#E61A73E8" />
<corners android:radius="@dimen/task_corner_radius" />
</shape>

View File

@ -38,9 +38,11 @@
<com.android.quickstep.views.DigitalWellBeingToast
android:id="@+id/digital_well_being_toast"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_height="48dp"
android:importantForAccessibility="noHideDescendants"
android:background="#800000FF"
android:fontFamily="sans-serif"
android:textSize="14sp"
android:background="@drawable/bg_wellbeing_toast"
android:layout_gravity="bottom"
android:gravity="center"
android:textColor="@android:color/white"

View File

@ -16,7 +16,7 @@
* limitations under the License.
*/
-->
<resources>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- Application name -->
<string name="derived_app_name" translatable="false">Quickstep</string>
@ -46,4 +46,24 @@
<!-- Accessibility title for the list of recent apps [CHAR_LIMIT=none] -->
<string name="accessibility_recent_apps">Recent apps</string>
<!-- Accessibility title for an app card in Recents for apps that have time limit set
[CHAR_LIMIT=none] -->
<string name="task_contents_description_with_remaining_time"><xliff:g id="task_description" example="GMail">%1$s</xliff:g>, <xliff:g id="remaining_time" example="7 minutes left today">%2$s</xliff:g></string>
<!-- Text to show total app usage per day if it is less than 1 minute ("&lt;" is the
escaped form of '<'). [CHAR LIMIT=10] -->
<string name="shorter_duration_less_than_one_minute">&lt; 1 minute</string>
<!-- Annotation shown on an app card in Recents, telling that the app was switched to a
grayscale because it ran over its time limit [CHAR LIMIT=25] -->
<string name="app_in_grayscale">App in grayscale</string>
<!-- Annotation shown on an app card in Recents, telling that the app has a usage limit set by
the user, and a given time is left for it today [CHAR LIMIT=20] -->
<string name="time_left_for_app"><xliff:g id="time" example="7 minutes">%1$s</xliff:g> left today</string>
<!-- Annotation shown on an app card in Recents, telling that the app is in a group that has a
usage limit set by the user, and a given time is left for the group today [CHAR LIMIT=20] -->
<string name="time_left_for_group"><xliff:g id="time" example="1 hour">%1$s</xliff:g> left for group</string>
</resources>

View File

@ -20,17 +20,27 @@ import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.icu.text.MeasureFormat;
import android.icu.text.MeasureFormat.FormatWidth;
import android.icu.util.Measure;
import android.icu.util.MeasureUnit;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.StringRes;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.systemui.shared.recents.model.Task;
import java.time.Duration;
import java.util.Locale;
public final class DigitalWellBeingToast extends TextView {
public interface InitializeCallback {
@ -65,16 +75,78 @@ public final class DigitalWellBeingToast extends TextView {
callback.call(
appUsageLimitTimeMs >= 0 && appRemainingTimeMs < 0 ? 0 : 1,
getContentDescriptionForTask(task, appRemainingTimeMs, isGroupLimit));
getContentDescriptionForTask(
task, appUsageLimitTimeMs, appRemainingTimeMs, isGroupLimit));
});
});
}
public static String getText(long remainingTime, boolean isGroupLimit) {
return remainingTime < 0 ?
"Grayed" :
"Remaining time:" + (remainingTime + 59999) / 60000
+ " min " + (isGroupLimit ? "for group" : "for the app");
private String getReadableDuration(
Duration duration,
FormatWidth formatWidthHourAndMinute,
@StringRes int durationLessThanOneMinuteStringId,
boolean forceFormatWidth) {
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.
if (hours > 0 && minutes > 0) {
return MeasureFormat.getInstance(Locale.getDefault(), formatWidthHourAndMinute)
.formatMeasures(
new Measure(hours, MeasureUnit.HOUR),
new Measure(minutes, MeasureUnit.MINUTE));
}
// Apply formatWidthHourOrMinute 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));
}
// Apply formatWidthHourOrMinute 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));
}
// Use a specific string for usage less than one minute but non-zero.
if (duration.compareTo(Duration.ZERO) > 0) {
return getResources().getString(durationLessThanOneMinuteStringId);
}
// Otherwise, return 0-minute string.
return MeasureFormat.getInstance(
Locale.getDefault(), forceFormatWidth ? formatWidthHourAndMinute : FormatWidth.WIDE)
.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 getShorterReadableDuration(Duration duration) {
return getReadableDuration(
duration, FormatWidth.NARROW, R.string.shorter_duration_less_than_one_minute);
}
private String getText(long remainingTime, boolean isGroupLimit) {
final Resources resources = getResources();
return (remainingTime < 0) ?
resources.getString(R.string.app_in_grayscale) :
resources.getString(
isGroupLimit ? R.string.time_left_for_group : R.string.time_left_for_app,
getShorterReadableDuration(Duration.ofMillis(remainingTime)));
}
public void openAppUsageSettings() {
@ -97,8 +169,8 @@ public final class DigitalWellBeingToast extends TextView {
}
private String getContentDescriptionForTask(
Task task, long appRemainingTimeMs, boolean isGroupLimit) {
return appRemainingTimeMs > 0 ?
Task task, long appUsageLimitTimeMs, long appRemainingTimeMs, boolean isGroupLimit) {
return appUsageLimitTimeMs > 0 ?
getResources().getString(
R.string.task_contents_description_with_remaining_time,
task.titleDescription,

View File

@ -336,6 +336,4 @@
<!-- Failed action error message: e.g. Failed: Pause -->
<string name="remote_action_failed">Failed: <xliff:g id="what" example="Pause">%1$s</xliff:g></string>
<string name="task_contents_description_with_remaining_time" translatable="false"><xliff:g id="task_description" example="GMail">%1$s</xliff:g>, <xliff:g id="remaining_time" example="7 minutes">%2$s</xliff:g></string>
</resources>