Commit cc38b235 authored by David Black's avatar David Black Committed by Commit Bot

Update Assistant timer notification message for V2.

In v1, the timer notification message was the remaining time.
Example: 10:00.

In v2, the timer notification message is the total time and label.
Example: 10m timer · Eggs.

Note that the first example uses a NUMERIC format width while the latter
uses a NARROW format width for the time string.

Bug: b:156641714
Change-Id: Ie8102b5a687630f5bbcb5b5b2efa47bce1536a59
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2220687
Commit-Queue: David Black <dmblack@google.com>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#773205}
parent 2c039f4d
......@@ -2355,8 +2355,20 @@ This file contains the strings for ash.
<message name="IDS_ASSISTANT_TIMER_NOTIFICATION_TITLE" desc="Title for Assistant timer notification.">
Time's up
</message>
<message name="IDS_ASSISTANT_TIMER_NOTIFICATION_MESSAGE_EXPIRED" desc="Message for Assistant timer notification that has expired. Format: -hh:mm:ss.">
-<ph name="TIME_SINCE_EXPIRATION">{0}<ex>10:27</ex></ph>
<message name="IDS_ASSISTANT_TIMER_NOTIFICATION_MESSAGE" desc="Message for Assistant timer notification. Example: 10m timer.">
<ph name="TOTAL_TIME">{0}<ex>10m</ex></ph> timer
</message>
<message name="IDS_ASSISTANT_TIMER_NOTIFICATION_MESSAGE_WITH_LABEL" desc="Message for Assistant timer notification. Example: 10m timer · Eggs.">
<ph name="TOTAL_TIME">{0}<ex>10m</ex></ph> timer · <ph name="LABEL">{1}<ex>Eggs</ex></ph>
</message>
<message name="IDS_ASSISTANT_TIMER_NOTIFICATION_FORMATTED_TIME_NEGATE" desc="Negates a formatted time string. Example: -10:27.">
-<ph name="FORMATTED_TIME">{0}<ex>10:27</ex></ph>
</message>
<message name="IDS_ASSISTANT_TIMER_NOTIFICATION_FORMATTED_TIME_NARROW_FALLBACK" desc="Fallback for a narrow formatted time string. Example: 10h 27m 0s.">
<ph name="HOURS">{0}<ex>10</ex></ph>h <ph name="MINUTES">{1}<ex>27</ex></ph>m <ph name="SECONDS">{2}<ex>0</ex></ph>s
</message>
<message name="IDS_ASSISTANT_TIMER_NOTIFICATION_FORMATTED_TIME_NUMERIC_FALLBACK" desc="Fallback for a numeric formatted time string. Example: 10:27:00.">
<ph name="HOURS">{0, number,00}<ex>10</ex></ph>:<ph name="MINUTES">{1, number,00}<ex>27</ex></ph>:<ph name="SECONDS">{2, number,00}<ex>00</ex></ph>
</message>
<message name="IDS_ASSISTANT_TIMER_NOTIFICATION_ADD_1_MIN_BUTTON" desc="Label for button to add 1 minute to timer in Assistant timer notification.">
Add 1 min
......
......@@ -47,15 +47,17 @@ constexpr base::TimeDelta kTickInterval = base::TimeDelta::FromSeconds(1);
// Helpers ---------------------------------------------------------------------
// Returns a string representation of the remaining time for the given |timer|.
std::string ToRemainingTimeString(const AssistantTimer& timer) {
std::string ToFormattedTimeString(base::TimeDelta time,
UMeasureFormatWidth width) {
DCHECK(width == UMEASFMT_WIDTH_NARROW || width == UMEASFMT_WIDTH_NUMERIC);
// Method aliases to prevent line-wrapping below.
const auto createHour = icu::MeasureUnit::createHour;
const auto createMinute = icu::MeasureUnit::createMinute;
const auto createSecond = icu::MeasureUnit::createSecond;
// Calculate hours/minutes/seconds remaining.
const int64_t total_seconds = std::abs(timer.remaining_time.InSeconds());
// Calculate time in hours/minutes/seconds.
const int64_t total_seconds = std::abs(time.InSeconds());
const int32_t hours = total_seconds / 3600;
const int32_t minutes = (total_seconds - hours * 3600) / 60;
const int32_t seconds = total_seconds % 60;
......@@ -63,43 +65,61 @@ std::string ToRemainingTimeString(const AssistantTimer& timer) {
// Success of the ICU APIs is tracked by |status|.
UErrorCode status = U_ZERO_ERROR;
// Create our distinct |measures| to be formatted. We only show |hours| if
// necessary, otherwise they are omitted.
// Create our distinct |measures| to be formatted.
std::vector<icu::Measure> measures;
if (hours)
if (hours) {
// We only show |hours| if necessary.
measures.push_back(icu::Measure(hours, createHour(status), status));
measures.push_back(icu::Measure(minutes, createMinute(status), status));
}
if (minutes || width == UMEASFMT_WIDTH_NUMERIC) {
// We only show |minutes| if necessary or if using numeric format width.
measures.push_back(icu::Measure(minutes, createMinute(status), status));
}
measures.push_back(icu::Measure(seconds, createSecond(status), status));
// Format our |measures| into a |unicode_message|.
icu::UnicodeString unicode_message;
icu::FieldPosition field_position = icu::FieldPosition::DONT_CARE;
UMeasureFormatWidth width = UMEASFMT_WIDTH_NUMERIC;
icu::MeasureFormat measure_format(icu::Locale::getDefault(), width, status);
measure_format.formatMeasures(measures.data(), measures.size(),
unicode_message, field_position, status);
std::string message;
std::string formatted_time;
if (U_SUCCESS(status)) {
// If formatting was successful, convert our |unicode_message| into UTF-8.
unicode_message.toUTF8String(message);
unicode_message.toUTF8String(formatted_time);
} else {
// If something went wrong, we'll fall back to using "hh:mm:ss" instead.
LOG(ERROR) << "Error formatting timer notification message: " << status;
message = base::StringPrintf("%02d:%02d:%02d", hours, minutes, seconds);
// If something went wrong formatting w/ ICU, fall back to I18N messages.
LOG(ERROR) << "Error formatting time string: " << status;
formatted_time =
base::UTF16ToUTF8(base::i18n::MessageFormatter::FormatWithNumberedArgs(
l10n_util::GetStringUTF16(
width == UMEASFMT_WIDTH_NARROW
? IDS_ASSISTANT_TIMER_NOTIFICATION_FORMATTED_TIME_NARROW_FALLBACK
: IDS_ASSISTANT_TIMER_NOTIFICATION_FORMATTED_TIME_NUMERIC_FALLBACK),
hours, minutes, seconds));
}
// If time has elapsed since the |timer| has fired, we'll need to negate the
// amount of time remaining.
if (timer.remaining_time.InSeconds() < 0) {
const auto format = l10n_util::GetStringUTF16(
IDS_ASSISTANT_TIMER_NOTIFICATION_MESSAGE_EXPIRED);
return base::UTF16ToUTF8(
base::i18n::MessageFormatter::FormatWithNumberedArgs(format, message));
// If necessary, negate the amount of time remaining.
if (time.InSeconds() < 0) {
formatted_time =
base::UTF16ToUTF8(base::i18n::MessageFormatter::FormatWithNumberedArgs(
l10n_util::GetStringUTF16(
IDS_ASSISTANT_TIMER_NOTIFICATION_FORMATTED_TIME_NEGATE),
formatted_time));
}
// Otherwise, all necessary formatting has been performed.
return message;
return formatted_time;
}
// Returns a string representation of the original duration for a given |timer|.
std::string ToOriginalDurationString(const AssistantTimer& timer) {
return ToFormattedTimeString(timer.original_duration, UMEASFMT_WIDTH_NARROW);
}
// Returns a string representation of the remaining time for the given |timer|.
std::string ToRemainingTimeString(const AssistantTimer& timer) {
return ToFormattedTimeString(timer.remaining_time, UMEASFMT_WIDTH_NUMERIC);
}
// Creates a notification ID for the given |timer|. It is guaranteed that this
......@@ -117,6 +137,20 @@ std::string CreateTimerNotificationTitle(const AssistantTimer& timer) {
// Creates a notification message for the given |timer|.
std::string CreateTimerNotificationMessage(const AssistantTimer& timer) {
if (IsTimersV2Enabled()) {
if (timer.label.empty()) {
return base::UTF16ToUTF8(
base::i18n::MessageFormatter::FormatWithNumberedArgs(
l10n_util::GetStringUTF16(
IDS_ASSISTANT_TIMER_NOTIFICATION_MESSAGE),
ToOriginalDurationString(timer)));
}
return base::UTF16ToUTF8(
base::i18n::MessageFormatter::FormatWithNumberedArgs(
l10n_util::GetStringUTF16(
IDS_ASSISTANT_TIMER_NOTIFICATION_MESSAGE_WITH_LABEL),
ToOriginalDurationString(timer), timer.label));
}
return ToRemainingTimeString(timer);
}
......
......@@ -14,6 +14,13 @@ AssistantAlarmTimerController* g_instance = nullptr;
} // namespace
// AssistantTimer --------------------------------------------------------------
AssistantTimer::AssistantTimer() = default;
AssistantTimer::~AssistantTimer() = default;
// AssistantAlarmTimerController -----------------------------------------------
AssistantAlarmTimerController::AssistantAlarmTimerController() {
DCHECK_EQ(nullptr, g_instance);
g_instance = this;
......
......@@ -31,13 +31,20 @@ enum class AssistantTimerState {
};
// Models an Assistant timer.
struct AssistantTimer {
struct ASH_PUBLIC_EXPORT AssistantTimer {
AssistantTimer();
AssistantTimer(const AssistantTimer&) = delete;
AssistantTimer& operator=(const AssistantTimer&) = delete;
~AssistantTimer();
std::string id;
std::string label;
AssistantTimerState state{AssistantTimerState::kUnknown};
base::TimeDelta original_duration;
base::Time fire_time;
base::TimeDelta remaining_time;
};
using AssistantTimerPtr = std::unique_ptr<AssistantTimer>;
// Interface to the AssistantAlarmTimerController which is owned by the
......
......@@ -1254,6 +1254,8 @@ void AssistantManagerServiceImpl::OnAlarmTimerStateChanged() {
timer->id = event.timer_data.timer_id;
timer->label = event.timer_data.label;
timer->state = GetTimerState(event.timer_data.state);
timer->original_duration = base::TimeDelta::FromMilliseconds(
event.timer_data.original_duration_ms);
// LibAssistant provides |fire_time_ms| as an offset from unix epoch.
timer->fire_time =
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment