| // 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/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); |
| SetAccessibleName( |
| 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 |