// 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/phonehub/phone_hub_more_apps_button.h"
#include <memory>
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/phonehub/phone_hub_app_count_icon.h"
#include "ash/system/phonehub/phone_hub_app_icon.h"
#include "ash/system/phonehub/phone_hub_app_loading_icon.h"
#include "ash/system/phonehub/phone_hub_metrics.h"
#include "base/metrics/histogram_functions.h"
#include "chromeos/ash/components/phonehub/app_stream_launcher_data_model.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/size.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/background.h"
#include "ui/views/layout/table_layout.h"
namespace ash {
// Appearance constants in DIPs
constexpr int kMoreAppsButtonRowPadding = 20;
constexpr int kMoreAppsButtonColumnPadding = 2;
constexpr int kMoreAppsButtonBackgroundRadius = 18;
// The app icons in the LoadingView stagger the start of the loading animation
// to make the appearance of a ripple.
constexpr int kAnimationLoadingIconStaggerDelayInMs = 100;
class MoreAppsButtonBackground : public views::Background {
public:
MoreAppsButtonBackground() = default;
MoreAppsButtonBackground(const MoreAppsButtonBackground&) = delete;
MoreAppsButtonBackground& operator=(const MoreAppsButtonBackground&) = delete;
void Paint(gfx::Canvas* canvas, views::View* view) const override {
cc::PaintFlags flags;
flags.setStyle(cc::PaintFlags::kFill_Style);
flags.setAntiAlias(true);
flags.setColor(AshColorProvider::Get()->GetControlsLayerColor(
AshColorProvider::ControlsLayerType::kControlBackgroundColorInactive));
canvas->DrawCircle(view->GetContentsBounds().CenterPoint(),
kMoreAppsButtonBackgroundRadius, flags);
}
};
PhoneHubMoreAppsButton::PhoneHubMoreAppsButton() {
InitLayout();
}
PhoneHubMoreAppsButton::PhoneHubMoreAppsButton(
phonehub::AppStreamLauncherDataModel* app_stream_launcher_data_model,
views::Button::PressedCallback callback)
: views::Button(std::move(callback)),
app_stream_launcher_data_model_(app_stream_launcher_data_model) {
CHECK(app_stream_launcher_data_model_);
SetFocusBehavior(FocusBehavior::ALWAYS);
GetViewAccessibility().SetName(
l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_FULL_APPS_LIST_BUTTON_TITLE));
SetTooltipText(
l10n_util::GetStringUTF16(IDS_ASH_PHONE_HUB_FULL_APPS_LIST_BUTTON_TITLE));
InitLayout();
app_stream_launcher_data_model_->AddObserver(this);
}
PhoneHubMoreAppsButton::~PhoneHubMoreAppsButton() {
if (app_stream_launcher_data_model_) {
app_stream_launcher_data_model_->RemoveObserver(this);
}
}
void PhoneHubMoreAppsButton::InitLayout() {
table_layout_ = SetLayoutManager(std::make_unique<views::TableLayout>());
table_layout_->AddPaddingColumn(/*horizontal_resize=*/3, /*width=*/0);
table_layout_->AddColumn(views::LayoutAlignment::kStretch,
views::LayoutAlignment::kStretch,
views::TableLayout::kFixedSize,
views::TableLayout::ColumnSize::kUsePreferred,
/*fixed_width=*/0, /*min_width=*/0);
table_layout_->AddPaddingColumn(views::TableLayout::kFixedSize,
kMoreAppsButtonColumnPadding);
table_layout_->AddColumn(views::LayoutAlignment::kStretch,
views::LayoutAlignment::kStretch,
views::TableLayout::kFixedSize,
views::TableLayout::ColumnSize::kUsePreferred,
/*fixed_width=*/0, /*min_width=*/0);
table_layout_->AddPaddingColumn(/*horizontal_resize=*/3, /*width=*/0);
table_layout_->AddRows(/*n=*/1, views::TableLayout::kFixedSize,
kMoreAppsButtonRowPadding);
table_layout_->AddPaddingRow(views::TableLayout::kFixedSize,
kMoreAppsButtonColumnPadding);
table_layout_->AddRows(/*n=*/1, views::TableLayout::kFixedSize,
kMoreAppsButtonRowPadding);
SetEnabled(false);
SetBackground(std::make_unique<MoreAppsButtonBackground>());
if (!app_stream_launcher_data_model_) {
return;
}
if (app_stream_launcher_data_model_->GetAppsListSortedByName()->empty()) {
load_app_list_latency_ = base::TimeTicks::Now();
StartLoadingAnimation(/*initial_delay=*/std::nullopt);
SetEnabled(false);
phone_hub_metrics::LogMoreAppsButtonAnimationOnShow(
phone_hub_metrics::MoreAppsButtonLoadingState::kAnimationShown);
} else {
LoadAppList();
SetEnabled(true);
// Reset the latency variable to a default state when there is already an
// apps list from the app stream launcher data model. This ensures we don't
// log the latency metric since the loaded list should already be shown.
load_app_list_latency_ = base::TimeTicks();
phone_hub_metrics::LogMoreAppsButtonAnimationOnShow(
phone_hub_metrics::MoreAppsButtonLoadingState::kMoreAppsButtonLoaded);
}
}
void PhoneHubMoreAppsButton::StartLoadingAnimation(
std::optional<base::TimeDelta> initial_delay) {
app_loading_icons_.clear();
RemoveAllChildViews();
for (size_t i = 0; i < 4; i++) {
AppLoadingIcon* app_loading_icon =
AddChildView(new AppLoadingIcon(AppIcon::kSizeSmall));
app_loading_icons_.push_back(app_loading_icon);
size_t x = i % 2;
size_t y = i / 2;
base::TimeDelta stagger_delay =
(x + y) * base::Milliseconds(kAnimationLoadingIconStaggerDelayInMs);
if (initial_delay) {
stagger_delay += *initial_delay;
}
app_loading_icon->StartLoadingAnimation(stagger_delay);
}
}
void PhoneHubMoreAppsButton::StopLoadingAnimation() {
for (AppLoadingIcon* app_loading_icon : app_loading_icons_) {
app_loading_icon->StopLoadingAnimation();
}
}
void PhoneHubMoreAppsButton::OnShouldShowMiniLauncherChanged() {}
void PhoneHubMoreAppsButton::OnAppListChanged() {
LoadAppList();
if (load_app_list_latency_ != base::TimeTicks()) {
phone_hub_metrics::LogMoreAppsButtonFullAppsLatency(base::TimeTicks::Now() -
load_app_list_latency_);
load_app_list_latency_ = base::TimeTicks();
}
}
void PhoneHubMoreAppsButton::LoadAppList() {
CHECK(app_stream_launcher_data_model_);
app_loading_icons_.clear();
RemoveAllChildViews();
const std::vector<phonehub::Notification::AppMetadata>* app_list =
app_stream_launcher_data_model_->GetAppsListSortedByName();
if (!app_list->empty()) {
auto app_count = std::min(app_list->size(), size_t{3});
for (size_t i = 0; i < app_count; i++) {
AddChildView(std::make_unique<AppIcon>(app_list->at(i).color_icon,
AppIcon::kSizeSmall));
}
}
AddChildView(std::make_unique<AppCountIcon>(app_list->size()));
SetEnabled(true);
}
BEGIN_METADATA(PhoneHubMoreAppsButton)
END_METADATA
} // namespace ash