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. ...@@ -2355,8 +2355,20 @@ This file contains the strings for ash.
<message name="IDS_ASSISTANT_TIMER_NOTIFICATION_TITLE" desc="Title for Assistant timer notification."> <message name="IDS_ASSISTANT_TIMER_NOTIFICATION_TITLE" desc="Title for Assistant timer notification.">
Time's up Time's up
</message> </message>
<message name="IDS_ASSISTANT_TIMER_NOTIFICATION_MESSAGE_EXPIRED" desc="Message for Assistant timer notification that has expired. Format: -hh:mm:ss."> <message name="IDS_ASSISTANT_TIMER_NOTIFICATION_MESSAGE" desc="Message for Assistant timer notification. Example: 10m timer.">
-<ph name="TIME_SINCE_EXPIRATION">{0}<ex>10:27</ex></ph> <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>
<message name="IDS_ASSISTANT_TIMER_NOTIFICATION_ADD_1_MIN_BUTTON" desc="Label for button to add 1 minute to timer in Assistant timer notification."> <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 Add 1 min
......
...@@ -47,15 +47,17 @@ constexpr base::TimeDelta kTickInterval = base::TimeDelta::FromSeconds(1); ...@@ -47,15 +47,17 @@ constexpr base::TimeDelta kTickInterval = base::TimeDelta::FromSeconds(1);
// Helpers --------------------------------------------------------------------- // Helpers ---------------------------------------------------------------------
// Returns a string representation of the remaining time for the given |timer|. std::string ToFormattedTimeString(base::TimeDelta time,
std::string ToRemainingTimeString(const AssistantTimer& timer) { UMeasureFormatWidth width) {
DCHECK(width == UMEASFMT_WIDTH_NARROW || width == UMEASFMT_WIDTH_NUMERIC);
// Method aliases to prevent line-wrapping below. // Method aliases to prevent line-wrapping below.
const auto createHour = icu::MeasureUnit::createHour; const auto createHour = icu::MeasureUnit::createHour;
const auto createMinute = icu::MeasureUnit::createMinute; const auto createMinute = icu::MeasureUnit::createMinute;
const auto createSecond = icu::MeasureUnit::createSecond; const auto createSecond = icu::MeasureUnit::createSecond;
// Calculate hours/minutes/seconds remaining. // Calculate time in hours/minutes/seconds.
const int64_t total_seconds = std::abs(timer.remaining_time.InSeconds()); const int64_t total_seconds = std::abs(time.InSeconds());
const int32_t hours = total_seconds / 3600; const int32_t hours = total_seconds / 3600;
const int32_t minutes = (total_seconds - hours * 3600) / 60; const int32_t minutes = (total_seconds - hours * 3600) / 60;
const int32_t seconds = total_seconds % 60; const int32_t seconds = total_seconds % 60;
...@@ -63,43 +65,61 @@ std::string ToRemainingTimeString(const AssistantTimer& timer) { ...@@ -63,43 +65,61 @@ std::string ToRemainingTimeString(const AssistantTimer& timer) {
// Success of the ICU APIs is tracked by |status|. // Success of the ICU APIs is tracked by |status|.
UErrorCode status = U_ZERO_ERROR; UErrorCode status = U_ZERO_ERROR;
// Create our distinct |measures| to be formatted. We only show |hours| if // Create our distinct |measures| to be formatted.
// necessary, otherwise they are omitted.
std::vector<icu::Measure> measures; 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(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)); measures.push_back(icu::Measure(seconds, createSecond(status), status));
// Format our |measures| into a |unicode_message|. // Format our |measures| into a |unicode_message|.
icu::UnicodeString unicode_message; icu::UnicodeString unicode_message;
icu::FieldPosition field_position = icu::FieldPosition::DONT_CARE; icu::FieldPosition field_position = icu::FieldPosition::DONT_CARE;
UMeasureFormatWidth width = UMEASFMT_WIDTH_NUMERIC;
icu::MeasureFormat measure_format(icu::Locale::getDefault(), width, status); icu::MeasureFormat measure_format(icu::Locale::getDefault(), width, status);
measure_format.formatMeasures(measures.data(), measures.size(), measure_format.formatMeasures(measures.data(), measures.size(),
unicode_message, field_position, status); unicode_message, field_position, status);
std::string message; std::string formatted_time;
if (U_SUCCESS(status)) { if (U_SUCCESS(status)) {
// If formatting was successful, convert our |unicode_message| into UTF-8. // If formatting was successful, convert our |unicode_message| into UTF-8.
unicode_message.toUTF8String(message); unicode_message.toUTF8String(formatted_time);
} else { } else {
// If something went wrong, we'll fall back to using "hh:mm:ss" instead. // If something went wrong formatting w/ ICU, fall back to I18N messages.
LOG(ERROR) << "Error formatting timer notification message: " << status; LOG(ERROR) << "Error formatting time string: " << status;
message = base::StringPrintf("%02d:%02d:%02d", hours, minutes, seconds); 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 // If necessary, negate the amount of time remaining.
// amount of time remaining. if (time.InSeconds() < 0) {
if (timer.remaining_time.InSeconds() < 0) { formatted_time =
const auto format = l10n_util::GetStringUTF16( base::UTF16ToUTF8(base::i18n::MessageFormatter::FormatWithNumberedArgs(
IDS_ASSISTANT_TIMER_NOTIFICATION_MESSAGE_EXPIRED); l10n_util::GetStringUTF16(
return base::UTF16ToUTF8( IDS_ASSISTANT_TIMER_NOTIFICATION_FORMATTED_TIME_NEGATE),
base::i18n::MessageFormatter::FormatWithNumberedArgs(format, message)); formatted_time));
} }
// Otherwise, all necessary formatting has been performed. return formatted_time;
return message; }
// 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 // Creates a notification ID for the given |timer|. It is guaranteed that this
...@@ -117,6 +137,20 @@ std::string CreateTimerNotificationTitle(const AssistantTimer& timer) { ...@@ -117,6 +137,20 @@ std::string CreateTimerNotificationTitle(const AssistantTimer& timer) {
// Creates a notification message for the given |timer|. // Creates a notification message for the given |timer|.
std::string CreateTimerNotificationMessage(const AssistantTimer& 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); return ToRemainingTimeString(timer);
} }
......
...@@ -14,6 +14,13 @@ AssistantAlarmTimerController* g_instance = nullptr; ...@@ -14,6 +14,13 @@ AssistantAlarmTimerController* g_instance = nullptr;
} // namespace } // namespace
// AssistantTimer --------------------------------------------------------------
AssistantTimer::AssistantTimer() = default;
AssistantTimer::~AssistantTimer() = default;
// AssistantAlarmTimerController -----------------------------------------------
AssistantAlarmTimerController::AssistantAlarmTimerController() { AssistantAlarmTimerController::AssistantAlarmTimerController() {
DCHECK_EQ(nullptr, g_instance); DCHECK_EQ(nullptr, g_instance);
g_instance = this; g_instance = this;
......
...@@ -31,13 +31,20 @@ enum class AssistantTimerState { ...@@ -31,13 +31,20 @@ enum class AssistantTimerState {
}; };
// Models an Assistant timer. // 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 id;
std::string label; std::string label;
AssistantTimerState state{AssistantTimerState::kUnknown}; AssistantTimerState state{AssistantTimerState::kUnknown};
base::TimeDelta original_duration;
base::Time fire_time; base::Time fire_time;
base::TimeDelta remaining_time; base::TimeDelta remaining_time;
}; };
using AssistantTimerPtr = std::unique_ptr<AssistantTimer>; using AssistantTimerPtr = std::unique_ptr<AssistantTimer>;
// Interface to the AssistantAlarmTimerController which is owned by the // Interface to the AssistantAlarmTimerController which is owned by the
......
...@@ -1254,6 +1254,8 @@ void AssistantManagerServiceImpl::OnAlarmTimerStateChanged() { ...@@ -1254,6 +1254,8 @@ void AssistantManagerServiceImpl::OnAlarmTimerStateChanged() {
timer->id = event.timer_data.timer_id; timer->id = event.timer_data.timer_id;
timer->label = event.timer_data.label; timer->label = event.timer_data.label;
timer->state = GetTimerState(event.timer_data.state); 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. // LibAssistant provides |fire_time_ms| as an offset from unix epoch.
timer->fire_time = 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