// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "ash/system/focus_mode/focus_mode_util.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/system_textfield.h"
#include "ash/system/focus_mode/focus_mode_controller.h"
#include "ash/system/model/clock_model.h"
#include "ash/system/model/system_tray_model.h"
#include "base/check_op.h"
#include "base/i18n/time_formatting.h"
#include "base/i18n/unicodestring.h"
#include "base/strings/string_number_conversions.h"
#include "third_party/icu/source/i18n/unicode/measfmt.h"
#include "third_party/icu/source/i18n/unicode/measunit.h"
#include "third_party/icu/source/i18n/unicode/measure.h"
#include "ui/base/l10n/l10n_util.h"
namespace ash::focus_mode_util {
namespace {
constexpr std::pair<int, std::u16string>
congratulatory_pair[kCongratulatoryTitleNum] = {
std::make_pair(IDS_ASH_STATUS_TRAY_FOCUS_MODE_ENDING_MOMENT_TITLE,
u"🎉"),
std::make_pair(IDS_ASH_STATUS_TRAY_FOCUS_MODE_ENDING_MOMENT_TITLE_1,
u"⭐"),
std::make_pair(IDS_ASH_STATUS_TRAY_FOCUS_MODE_ENDING_MOMENT_TITLE_2,
u"🎉"),
std::make_pair(IDS_ASH_STATUS_TRAY_FOCUS_MODE_ENDING_MOMENT_TITLE_3,
u"⚡"),
std::make_pair(IDS_ASH_STATUS_TRAY_FOCUS_MODE_ENDING_MOMENT_TITLE_4,
u"🎈"),
std::make_pair(IDS_ASH_STATUS_TRAY_FOCUS_MODE_ENDING_MOMENT_TITLE_5,
u"☑"),
};
}
SelectedPlaylist::SelectedPlaylist() = default;
SelectedPlaylist::SelectedPlaylist(const SelectedPlaylist&) = default;
SelectedPlaylist& SelectedPlaylist::operator=(const SelectedPlaylist& other) =
default;
SelectedPlaylist::~SelectedPlaylist() = default;
std::u16string GetDurationString(base::TimeDelta duration_to_format,
bool digital_format) {
UErrorCode status = U_ZERO_ERROR;
const int64_t total_seconds =
base::ClampRound<int64_t>(duration_to_format.InSecondsF());
const int64_t hours =
digital_format || FocusModeController::Get()->in_focus_session()
? total_seconds / base::Time::kSecondsPerHour
: 0;
icu::MeasureFormat measure_format(
icu::Locale::getDefault(),
digital_format ? UMeasureFormatWidth::UMEASFMT_WIDTH_NUMERIC
: UMeasureFormatWidth::UMEASFMT_WIDTH_SHORT,
status);
icu::UnicodeString formatted;
icu::FieldPosition ignore(icu::FieldPosition::DONT_CARE);
std::vector<icu::Measure> measures;
if (hours != 0) {
measures.emplace_back(hours, icu::MeasureUnit::createHour(status), status);
}
if (digital_format || total_seconds >= base::Time::kSecondsPerMinute) {
const int64_t minutes =
(total_seconds - hours * base::Time::kSecondsPerHour) /
base::Time::kSecondsPerMinute;
measures.emplace_back(minutes, icu::MeasureUnit::createMinute(status),
status);
}
if (digital_format || total_seconds < base::Time::kSecondsPerMinute) {
const int64_t seconds = total_seconds % base::Time::kSecondsPerMinute;
measures.emplace_back(seconds, icu::MeasureUnit::createSecond(status),
status);
}
measure_format.formatMeasures(&measures[0], measures.size(), formatted,
ignore, status);
if (U_SUCCESS(status)) {
return base::i18n::UnicodeStringToString16(formatted);
}
return base::NumberToString16(std::ceil(duration_to_format.InSecondsF()));
}
std::u16string GetFormattedClockString(const base::Time end_time) {
return base::TimeFormatTimeOfDayWithHourClockType(
end_time, Shell::Get()->system_tray_model()->clock()->hour_clock_type(),
base::kKeepAmPm);
}
std::u16string GetNotificationDescriptionForFocusSession(
const base::Time end_time) {
return l10n_util::GetStringFUTF16(
IDS_ASH_DO_NOT_DISTURB_NOTIFICATION_IN_FOCUS_MODE_DESCRIPTION,
GetFormattedClockString(end_time));
}
int GetTimerTextfieldInputInMinutes(SystemTextfield* timer_textfield) {
// If the user is trying to adjust the session duration while the textfield is
// empty, we default to the last session duration that was set on the focus
// mode controller.
int duration_minutes;
if (!base::StringToInt(timer_textfield->GetText(), &duration_minutes)) {
duration_minutes =
FocusModeController::Get()->session_duration().InMinutes();
}
return duration_minutes;
}
std::u16string GetFormattedEndTimeString(const base::Time end_time) {
return l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_FOCUS_MODE_TOGGLE_TIME_SUBLABEL,
focus_mode_util::GetFormattedClockString(end_time));
}
std::string GetSourceTitleForMediaControls(const SelectedPlaylist& playlist) {
if (playlist.empty() || playlist.type == SoundType::kNone) {
return "";
}
std::string playlist_type = l10n_util::GetStringUTF8(
playlist.type == SoundType::kYouTubeMusic
? IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_YOUTUBE_MUSIC_BUTTON
: IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_SOUNDSCAPE_BUTTON);
if (playlist.title.empty()) {
return playlist_type;
}
return l10n_util::GetStringFUTF8(
IDS_ASH_STATUS_TRAY_FOCUS_MODE_SOUNDS_MEDIA_CONTROLS_SOURCE_TITLE,
base::UTF8ToUTF16(playlist_type), base::UTF8ToUTF16(playlist.title));
}
std::u16string GetCongratulatoryText(const size_t index) {
CHECK_LT(index, kCongratulatoryTitleNum);
return l10n_util::GetStringUTF16(congratulatory_pair[index].first);
}
std::u16string GetCongratulatoryEmoji(const size_t index) {
CHECK_LT(index, kCongratulatoryTitleNum);
return congratulatory_pair[index].second;
}
std::u16string GetCongratulatoryTextAndEmoji(const size_t index) {
CHECK_LT(index, kCongratulatoryTitleNum);
return base::JoinString(
{GetCongratulatoryText(index), GetCongratulatoryEmoji(index)}, u" ");
}
int GetNextProgressStep(double current_progress) {
CHECK_GE(current_progress, 0.0);
CHECK_LE(current_progress, 1.0);
return std::min(
(int)(std::floor(current_progress * kProgressIndicatorSteps) + 1),
kProgressIndicatorSteps);
}
} // namespace ash::focus_mode_util