// 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.
#include "ash/system/hotspot/hotspot_feature_pod_controller.h"
#include "ash/ash_element_identifiers.h"
#include "ash/constants/ash_features.h"
#include "ash/public/cpp/hotspot_config_service.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/hotspot/hotspot_icon.h"
#include "ash/system/hotspot/hotspot_icon_animation.h"
#include "ash/system/hotspot/hotspot_info_cache.h"
#include "ash/system/unified/feature_tile.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/view_class_properties.h"
namespace ash {
using hotspot_config::mojom::HotspotAllowStatus;
using hotspot_config::mojom::HotspotControlResult;
using hotspot_config::mojom::HotspotInfo;
using hotspot_config::mojom::HotspotInfoPtr;
using hotspot_config::mojom::HotspotState;
HotspotFeaturePodController::HotspotFeaturePodController(
UnifiedSystemTrayController* tray_controller)
: hotspot_info_(Shell::Get()->hotspot_info_cache()->GetHotspotInfo()),
tray_controller_(tray_controller) {
GetHotspotConfigService(
remote_cros_hotspot_config_.BindNewPipeAndPassReceiver());
remote_cros_hotspot_config_->AddObserver(
hotspot_config_observer_receiver_.BindNewPipeAndPassRemote());
}
HotspotFeaturePodController::~HotspotFeaturePodController() {
Shell::Get()->hotspot_icon_animation()->RemoveObserver(this);
}
std::unique_ptr<FeatureTile> HotspotFeaturePodController::CreateTile(
bool compact) {
auto tile = std::make_unique<FeatureTile>(
base::BindRepeating(&HotspotFeaturePodController::OnLabelPressed,
weak_ptr_factory_.GetWeakPtr()));
tile_ = tile.get();
tile_->SetIconClickable(true);
tile_->SetIconClickCallback(
base::BindRepeating(&HotspotFeaturePodController::OnIconPressed,
weak_ptr_factory_.GetWeakPtr()));
tile_->SetLabel(l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_HOTSPOT));
tile_->CreateDecorativeDrillInArrow();
tile_->drill_in_arrow()->SetProperty(
views::kElementIdentifierKey, kHotspotFeatureTileDrillInArrowElementId);
// Default the visibility to false and update it in `UpdateTileState()` since
// it should only be shown if user has used the Hotspot from Settings before.
tile_->SetVisible(false);
UpdateTileState();
return tile;
}
QsFeatureCatalogName HotspotFeaturePodController::GetCatalogName() {
return QsFeatureCatalogName::kHotspot;
}
void HotspotFeaturePodController::OnIconPressed() {
if (!tile_->GetEnabled()) {
return;
}
if (hotspot_info_->state == HotspotState::kEnabled) {
remote_cros_hotspot_config_->DisableHotspot(base::BindOnce(
&HotspotFeaturePodController::TrackToggleHotspotUMA,
weak_ptr_factory_.GetWeakPtr(), /*target_toggle_state=*/false));
return;
}
EnableHotspotIfAllowedAndDiveIn();
}
void HotspotFeaturePodController::OnLabelPressed() {
if (!tile_->GetEnabled()) {
return;
}
TrackDiveInUMA();
tray_controller_->ShowHotspotDetailedView();
}
void HotspotFeaturePodController::OnHotspotInfoChanged() {
remote_cros_hotspot_config_->GetHotspotInfo(
base::BindOnce(&HotspotFeaturePodController::OnGetHotspotInfo,
weak_ptr_factory_.GetWeakPtr()));
}
void HotspotFeaturePodController::OnGetHotspotInfo(
HotspotInfoPtr hotspot_info) {
if (hotspot_info->state == HotspotState::kEnabling) {
Shell::Get()->hotspot_icon_animation()->AddObserver(this);
} else if (hotspot_info_ && hotspot_info_->state == HotspotState::kEnabling) {
Shell::Get()->hotspot_icon_animation()->RemoveObserver(this);
}
hotspot_info_ = std::move(hotspot_info);
UpdateTileState();
}
void HotspotFeaturePodController::UpdateTileState() {
if (!tile_) {
return;
}
if (!Shell::Get()->hotspot_info_cache()->HasHotspotUsedBefore()) {
return;
}
// If the tile's visibility changes from invisible to visible, log its
// visibility.
if (!tile_->GetVisible()) {
TrackVisibilityUMA();
}
tile_->SetVisible(true);
tile_->SetEnabled(true);
tile_->SetToggled(hotspot_info_->state != HotspotState::kDisabled);
tile_->SetSubLabel(ComputeSublabel());
tile_->SetIconButtonTooltipText(ComputeIconTooltip());
tile_->SetTooltipText(ComputeTileTooltip());
tile_->SetVectorIcon(hotspot_icon::GetIconForHotspot(hotspot_info_->state));
}
void HotspotFeaturePodController::HotspotIconChanged() {
tile_->SetVectorIcon(hotspot_icon::GetIconForHotspot(hotspot_info_->state));
}
void HotspotFeaturePodController::EnableHotspotIfAllowedAndDiveIn() {
if (hotspot_info_->state == HotspotState::kDisabled &&
hotspot_info_->allow_status == HotspotAllowStatus::kAllowed) {
remote_cros_hotspot_config_->EnableHotspot(base::BindOnce(
&HotspotFeaturePodController::TrackToggleHotspotUMA,
weak_ptr_factory_.GetWeakPtr(), /*target_toggle_state=*/true));
}
TrackDiveInUMA();
tray_controller_->ShowHotspotDetailedView();
}
void HotspotFeaturePodController::TrackToggleHotspotUMA(
bool target_toggle_state,
HotspotControlResult operation_result) {
TrackToggleUMA(/*target_toggle_state=*/target_toggle_state);
}
const gfx::VectorIcon& HotspotFeaturePodController::ComputeIcon() const {
return tile_->IsToggled() ? kHotspotOnIcon : kHotspotOffIcon;
}
std::u16string HotspotFeaturePodController::ComputeSublabel() const {
using l10n_util::GetStringUTF16;
switch (hotspot_info_->state) {
case HotspotState::kEnabled:
return GetStringUTF16(IDS_ASH_STATUS_TRAY_HOTSPOT_STATUS_ON);
case HotspotState::kEnabling:
return GetStringUTF16(IDS_ASH_STATUS_TRAY_HOTSPOT_STATUS_ENABLING);
case HotspotState::kDisabling:
return GetStringUTF16(IDS_ASH_STATUS_TRAY_HOTSPOT_STATUS_DISABLING);
case HotspotState::kDisabled:
return GetStringUTF16(IDS_ASH_STATUS_TRAY_HOTSPOT_STATUS_OFF);
}
}
std::u16string HotspotFeaturePodController::ComputeIconTooltip() const {
switch (hotspot_info_->state) {
case HotspotState::kEnabled: {
// Toggle hotspot. Hotspot is on, # devices connected.
uint32_t client_count = hotspot_info_->client_count;
if (client_count == 0) {
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_TOOLTIP_STATUS_ON_NO_DEVICE_CONNECTED);
}
if (client_count == 1) {
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_TOOLTIP_STATUS_ON_ONE_DEVICE_CONNECTED);
}
return l10n_util::GetStringFUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_TOOLTIP_STATUS_ON_MULTIPLE_DEVICES_CONNECTED,
base::NumberToString16(client_count));
}
case HotspotState::kEnabling:
// Show hotspot details. Hotspot is enabling.
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_TOOLTIP_STATUS_ENABLING);
case HotspotState::kDisabling:
// Show hotspot details. Hotspot is disabling.
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_TOOLTIP_STATUS_DISABLING);
case HotspotState::kDisabled:
// Show hotspot details. Connect to mobile data to use hotspot.
if (hotspot_info_->allow_status ==
HotspotAllowStatus::kDisallowedNoMobileData) {
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_TOOLTIP_NO_MOBILE_DATA);
}
// Show hotspot details. Your mobile data doesn't support hotspot.
if (hotspot_info_->allow_status ==
HotspotAllowStatus::kDisallowedReadinessCheckFail) {
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_TOOLTIP_MOBILE_DATA_NOT_SUPPORTED);
}
// Show hotspot details. Hotspot is blocked by your administrator.
if (hotspot_info_->allow_status ==
HotspotAllowStatus::kDisallowedByPolicy) {
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_TOOLTIP_PROHIBITED_BY_POLICY);
}
// Toggle hotspot. Hotspot is off.
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_TOOLTIP_STATUS_OFF);
}
}
std::u16string HotspotFeaturePodController::ComputeTileTooltip() const {
switch (hotspot_info_->state) {
case HotspotState::kEnabled:
// Show hotspot details. Hotspot is on.
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_DRILL_IN_TOOLTIP_STATUS_ON);
case HotspotState::kEnabling:
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_TOOLTIP_STATUS_ENABLING);
case HotspotState::kDisabling:
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_TOOLTIP_STATUS_DISABLING);
case HotspotState::kDisabled:
if (hotspot_info_->allow_status == HotspotAllowStatus::kAllowed) {
// Toggle hotspot. Hotspot is off.
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_TOOLTIP_STATUS_OFF);
}
// Show hotspot details. Hotspot is off.
return l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_HOTSPOT_FEATURE_TILE_DRILL_IN_TOOLTIP_STATUS_OFF);
}
}
} // namespace ash