123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- // Copyright 2020 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/system/phonehub/phone_status_view.h"
- #include <string>
- #include "ash/constants/ash_features.h"
- #include "ash/public/cpp/network_icon_image_source.h"
- #include "ash/public/cpp/shelf_config.h"
- #include "ash/resources/vector_icons/vector_icons.h"
- #include "ash/root_window_controller.h"
- #include "ash/strings/grit/ash_strings.h"
- #include "ash/style/ash_color_id.h"
- #include "ash/style/ash_color_provider.h"
- #include "ash/style/icon_button.h"
- #include "ash/system/phonehub/phone_hub_tray.h"
- #include "ash/system/phonehub/phone_hub_view_ids.h"
- #include "ash/system/status_area_widget.h"
- #include "ash/system/tray/tray_constants.h"
- #include "ash/system/tray/tray_popup_utils.h"
- #include "base/bind.h"
- #include "base/i18n/number_formatting.h"
- #include "base/strings/strcat.h"
- #include "base/strings/string_number_conversions.h"
- #include "ui/base/l10n/l10n_util.h"
- #include "ui/color/color_id.h"
- #include "ui/compositor/layer.h"
- #include "ui/gfx/color_utils.h"
- #include "ui/gfx/geometry/insets.h"
- #include "ui/gfx/image/image_skia.h"
- #include "ui/gfx/paint_vector_icon.h"
- #include "ui/gfx/text_elider.h"
- #include "ui/views/border.h"
- #include "ui/views/controls/image_view.h"
- #include "ui/views/controls/label.h"
- #include "ui/views/controls/separator.h"
- #include "ui/views/layout/box_layout.h"
- namespace ash {
- namespace {
- using PhoneStatusModel = phonehub::PhoneStatusModel;
- // Appearance in Dip.
- constexpr int kTitleContainerSpacing = 16;
- constexpr int kStatusSpacing = 4;
- constexpr gfx::Size kStatusIconSize(kUnifiedTrayIconSize, kUnifiedTrayIconSize);
- constexpr gfx::Size kSignalIconSize(15, 15);
- constexpr int kSeparatorHeight = 18;
- constexpr int kPhoneNameLabelWidthMax = 160;
- constexpr auto kBorderInsets = gfx::Insets::VH(0, 16);
- constexpr auto kBatteryLabelBorderInsets = gfx::Insets::TLBR(0, 0, 0, 4);
- // Typograph in dip.
- constexpr int kBatteryLabelFontSize = 11;
- // Multiplied by the int returned by GetSignalStrengthAsInt() to obtain a
- // percentage for the signal strength displayed by the tooltip when hovering
- // over the signal strength icon, and verbalized by ChromeVox.
- constexpr int kSignalStrengthToPercentageMultiplier = 25;
- int GetSignalStrengthAsInt(PhoneStatusModel::SignalStrength signal_strength) {
- switch (signal_strength) {
- case PhoneStatusModel::SignalStrength::kZeroBars:
- return 0;
- case PhoneStatusModel::SignalStrength::kOneBar:
- return 1;
- case PhoneStatusModel::SignalStrength::kTwoBars:
- return 2;
- case PhoneStatusModel::SignalStrength::kThreeBars:
- return 3;
- case PhoneStatusModel::SignalStrength::kFourBars:
- return 4;
- }
- }
- bool IsBatterySaverModeOn(const PhoneStatusModel& phone_status) {
- return phone_status.battery_saver_state() ==
- PhoneStatusModel::BatterySaverState::kOn;
- }
- } // namespace
- PhoneStatusView::PhoneStatusView(phonehub::PhoneModel* phone_model,
- Delegate* delegate)
- : TriView(kTitleContainerSpacing),
- phone_model_(phone_model),
- phone_name_label_(new views::Label),
- signal_icon_(new views::ImageView),
- battery_icon_(new views::ImageView),
- battery_label_(new views::Label) {
- DCHECK(delegate);
- // In dark light mode, we switch TrayBubbleView to use a textured layer
- // instead of solid color layer, so no need to create an extra layer here.
- if (!features::IsDarkLightModeEnabled()) {
- SetPaintToLayer();
- layer()->SetFillsBoundsOpaquely(false);
- }
- SetID(PhoneHubViewID::kPhoneStatusView);
- SetBorder(views::CreateEmptyBorder(kBorderInsets));
- // Phone name is placed at START container, Settings icon is
- // placed at END container, other phone states, i.e. battery level,
- // and Separator are placed at CENTER container.
- ConfigureTriViewContainer(TriView::Container::START);
- ConfigureTriViewContainer(TriView::Container::CENTER);
- ConfigureTriViewContainer(TriView::Container::END);
- phone_model_->AddObserver(this);
- phone_name_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
- phone_name_label_->SetEnabledColor(
- AshColorProvider::Get()->GetContentLayerColor(
- AshColorProvider::ContentLayerType::kTextColorPrimary));
- TrayPopupUtils::SetLabelFontList(phone_name_label_,
- TrayPopupUtils::FontStyle::kSubHeader);
- phone_name_label_->SetElideBehavior(gfx::ElideBehavior::ELIDE_TAIL);
- AddView(TriView::Container::START, phone_name_label_);
- AddView(TriView::Container::CENTER, signal_icon_);
- if (features::IsDarkLightModeEnabled()) {
- // The battery icon requires its own layer to properly render the masked
- // outline of the badge within the battery icon.
- battery_icon_->SetPaintToLayer();
- battery_icon_->layer()->SetFillsBoundsOpaquely(false);
- }
- AddView(TriView::Container::CENTER, battery_icon_);
- battery_label_->SetAutoColorReadabilityEnabled(false);
- battery_label_->SetSubpixelRenderingEnabled(false);
- battery_label_->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
- AshColorProvider::ContentLayerType::kTextColorPrimary));
- auto default_font = battery_label_->font_list();
- battery_label_->SetFontList(default_font.DeriveWithSizeDelta(
- kBatteryLabelFontSize - default_font.GetFontSize()));
- battery_label_->SetBorder(
- views::CreateEmptyBorder(kBatteryLabelBorderInsets));
- AddView(TriView::Container::CENTER, battery_label_);
- separator_ = new views::Separator();
- separator_->SetColorId(ui::kColorAshSystemUIMenuSeparator);
- separator_->SetPreferredLength(kSeparatorHeight);
- AddView(TriView::Container::CENTER, separator_);
- settings_button_ = new IconButton(
- base::BindRepeating(&Delegate::OpenConnectedDevicesSettings,
- base::Unretained(delegate)),
- IconButton::Type::kSmall, &kSystemMenuSettingsIcon,
- IDS_ASH_PHONE_HUB_CONNECTED_DEVICE_SETTINGS_LABEL);
- AddView(TriView::Container::END, settings_button_);
- separator_->SetVisible(delegate->CanOpenConnectedDeviceSettings());
- settings_button_->SetVisible(delegate->CanOpenConnectedDeviceSettings());
- }
- PhoneStatusView::~PhoneStatusView() {
- phone_model_->RemoveObserver(this);
- }
- void PhoneStatusView::OnThemeChanged() {
- TriView::OnThemeChanged();
- Update();
- }
- void PhoneStatusView::OnModelChanged() {
- Update();
- }
- void PhoneStatusView::Update() {
- // Set phone name text and elide it if needed.
- phone_name_label_->SetText(
- gfx::ElideText(phone_model_->phone_name().value_or(std::u16string()),
- phone_name_label_->font_list(), kPhoneNameLabelWidthMax,
- gfx::ELIDE_TAIL));
- // Clear the phone status if the status model returns null when the phone is
- // disconnected.
- if (!phone_model_->phone_status_model()) {
- ClearExistingStatus();
- // Hide separator if there is no preceding content.
- separator_->SetVisible(false);
- return;
- }
- UpdateMobileStatus();
- UpdateBatteryStatus();
- }
- void PhoneStatusView::UpdateMobileStatus() {
- const PhoneStatusModel& phone_status =
- phone_model_->phone_status_model().value();
- const SkColor primary_color = AshColorProvider::Get()->GetContentLayerColor(
- AshColorProvider::ContentLayerType::kIconColorPrimary);
- gfx::ImageSkia signal_image;
- std::u16string tooltip_text;
- switch (phone_status.mobile_status()) {
- case PhoneStatusModel::MobileStatus::kNoSim:
- signal_image = CreateVectorIcon(kPhoneHubMobileNoSimIcon, primary_color);
- tooltip_text =
- l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_MOBILE_STATUS_NO_SIM);
- signal_icon_->SetImageSize(kStatusIconSize);
- break;
- case PhoneStatusModel::MobileStatus::kSimButNoReception:
- signal_image =
- CreateVectorIcon(kPhoneHubMobileNoConnectionIcon, primary_color);
- tooltip_text =
- l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_MOBILE_STATUS_NO_NETWORK);
- signal_icon_->SetImageSize(kStatusIconSize);
- break;
- case PhoneStatusModel::MobileStatus::kSimWithReception:
- const PhoneStatusModel::MobileConnectionMetadata& metadata =
- phone_status.mobile_connection_metadata().value();
- int signal_strength = GetSignalStrengthAsInt(metadata.signal_strength);
- signal_image = gfx::CanvasImageSource::MakeImageSkia<
- network_icon::SignalStrengthImageSource>(
- network_icon::ImageType::BARS, primary_color, kSignalIconSize,
- signal_strength);
- signal_icon_->SetImageSize(kSignalIconSize);
- tooltip_text = l10n_util::GetStringFUTF16(
- IDS_ASH_PHONE_HUB_MOBILE_STATUS_NETWORK_NAME_AND_STRENGTH,
- metadata.mobile_provider,
- base::NumberToString16(signal_strength *
- kSignalStrengthToPercentageMultiplier));
- break;
- }
- signal_icon_->SetImage(signal_image);
- signal_icon_->SetTooltipText(tooltip_text);
- }
- void PhoneStatusView::UpdateBatteryStatus() {
- const PhoneStatusModel& phone_status =
- phone_model_->phone_status_model().value();
- const PowerStatus::BatteryImageInfo& info = CalculateBatteryInfo();
- const SkColor icon_bg_color = color_utils::GetResultingPaintColor(
- ShelfConfig::Get()->GetShelfControlButtonColor(),
- GetColorProvider()->GetColor(kColorAshShieldAndBaseOpaque));
- const SkColor icon_fg_color = AshColorProvider::Get()->GetContentLayerColor(
- IsBatterySaverModeOn(phone_status)
- ? AshColorProvider::ContentLayerType::kIconColorWarning
- : AshColorProvider::ContentLayerType::kIconColorPrimary);
- battery_icon_->SetImage(PowerStatus::GetBatteryImage(
- info, kUnifiedTrayBatteryIconSize, icon_bg_color, icon_fg_color));
- SetBatteryTooltipText();
- battery_label_->SetText(
- base::FormatPercent(phone_status.battery_percentage()));
- battery_label_->SetAccessibleName(l10n_util::GetStringFUTF16(
- IDS_ASH_PHONE_HUB_BATTERY_PERCENTAGE_ACCESSIBLE_TEXT,
- base::NumberToString16(phone_status.battery_percentage())));
- }
- PowerStatus::BatteryImageInfo PhoneStatusView::CalculateBatteryInfo() {
- PowerStatus::BatteryImageInfo info;
- const PhoneStatusModel& phone_status =
- phone_model_->phone_status_model().value();
- info.charge_percent = phone_status.battery_percentage();
- if (IsBatterySaverModeOn(phone_status)) {
- info.icon_badge = &kPhoneHubBatterySaverIcon;
- if (features::IsDarkLightModeEnabled()) {
- info.badge_outline = &kPhoneHubBatterySaverOutlineMaskIcon;
- } else {
- info.badge_outline = &kPhoneHubBatterySaverOutlineIcon;
- }
- return info;
- }
- switch (phone_status.charging_state()) {
- case PhoneStatusModel::ChargingState::kNotCharging:
- info.alert_if_low = true;
- if (info.charge_percent < PowerStatus::kCriticalBatteryChargePercentage) {
- info.icon_badge = &kUnifiedMenuBatteryAlertIcon;
- if (features::IsDarkLightModeEnabled()) {
- info.badge_outline = &kUnifiedMenuBatteryAlertOutlineMaskIcon;
- } else {
- info.badge_outline = &kUnifiedMenuBatteryAlertOutlineIcon;
- }
- }
- break;
- case PhoneStatusModel::ChargingState::kChargingAc:
- info.icon_badge = &kUnifiedMenuBatteryBoltIcon;
- if (features::IsDarkLightModeEnabled()) {
- info.badge_outline = &kUnifiedMenuBatteryBoltOutlineMaskIcon;
- } else {
- info.badge_outline = &kUnifiedMenuBatteryBoltOutlineIcon;
- }
- break;
- case PhoneStatusModel::ChargingState::kChargingUsb:
- info.icon_badge = &kUnifiedMenuBatteryUnreliableIcon;
- if (features::IsDarkLightModeEnabled()) {
- info.badge_outline = &kUnifiedMenuBatteryUnreliableOutlineMaskIcon;
- } else {
- info.badge_outline = &kUnifiedMenuBatteryUnreliableOutlineIcon;
- }
- break;
- }
- return info;
- }
- void PhoneStatusView::SetBatteryTooltipText() {
- const PhoneStatusModel& phone_status =
- phone_model_->phone_status_model().value();
- int charging_tooltip_id;
- switch (phone_status.charging_state()) {
- case PhoneStatusModel::ChargingState::kNotCharging:
- charging_tooltip_id = IDS_ASH_PHONE_HUB_BATTERY_STATUS_NOT_CHARGING;
- break;
- case PhoneStatusModel::ChargingState::kChargingAc:
- charging_tooltip_id = IDS_ASH_PHONE_HUB_BATTERY_STATUS_CHARGING_AC;
- break;
- case PhoneStatusModel::ChargingState::kChargingUsb:
- charging_tooltip_id = IDS_ASH_PHONE_HUB_BATTERY_STATUS_CHARGING_USB;
- break;
- }
- std::u16string charging_tooltip =
- l10n_util::GetStringUTF16(charging_tooltip_id);
- bool battery_saver_on = phone_status.battery_saver_state() ==
- PhoneStatusModel::BatterySaverState::kOn;
- std::u16string batter_saver_tooltip =
- battery_saver_on
- ? l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_BATTERY_SAVER_ON)
- : l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_BATTERY_SAVER_OFF);
- battery_icon_->SetTooltipText(
- l10n_util::GetStringFUTF16(IDS_ASH_PHONE_HUB_BATTERY_TOOLTIP,
- charging_tooltip, batter_saver_tooltip));
- }
- void PhoneStatusView::ClearExistingStatus() {
- // Clear mobile status.
- signal_icon_->SetImage(gfx::ImageSkia());
- // Clear battery status.
- battery_icon_->SetImage(gfx::ImageSkia());
- battery_label_->SetText(std::u16string());
- }
- void PhoneStatusView::ConfigureTriViewContainer(TriView::Container container) {
- std::unique_ptr<views::BoxLayout> layout;
- switch (container) {
- case TriView::Container::START:
- SetFlexForContainer(TriView::Container::START, 1.f);
- layout = std::make_unique<views::BoxLayout>(
- views::BoxLayout::Orientation::kVertical);
- layout->set_main_axis_alignment(
- views::BoxLayout::MainAxisAlignment::kCenter);
- layout->set_cross_axis_alignment(
- views::BoxLayout::CrossAxisAlignment::kStretch);
- break;
- case TriView::Container::CENTER:
- layout = std::make_unique<views::BoxLayout>(
- views::BoxLayout::Orientation::kHorizontal, gfx::Insets(),
- kStatusSpacing);
- layout->set_main_axis_alignment(
- views::BoxLayout::MainAxisAlignment::kEnd);
- layout->set_cross_axis_alignment(
- views::BoxLayout::CrossAxisAlignment::kCenter);
- break;
- case TriView::Container::END:
- layout = std::make_unique<views::BoxLayout>(
- views::BoxLayout::Orientation::kHorizontal);
- layout->set_main_axis_alignment(
- views::BoxLayout::MainAxisAlignment::kCenter);
- layout->set_cross_axis_alignment(
- views::BoxLayout::CrossAxisAlignment::kCenter);
- break;
- }
- SetContainerLayout(container, std::move(layout));
- SetMinSize(container, gfx::Size(0, kUnifiedDetailedViewTitleRowHeight));
- }
- } // namespace ash
|