chromium/chrome/browser/ash/app_list/search/help_app_zero_state_provider.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/app_list/search/help_app_zero_state_provider.h"

#include <memory>

#include "ash/public/cpp/app_list/app_list_config.h"
#include "ash/public/cpp/app_list/vector_icons/vector_icons.h"
#include "ash/public/cpp/style/color_provider.h"
#include "base/metrics/user_metrics.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/ash/app_list/search/common/icon_constants.h"
#include "chrome/browser/ash/app_list/search/types.h"
#include "chrome/browser/ash/release_notes/release_notes_storage.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h"
#include "chrome/browser/web_applications/web_app_id_constants.h"
#include "chrome/common/pref_names.h"
#include "chromeos/strings/grit/chromeos_strings.h"
#include "components/prefs/pref_service.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
#include "components/services/app_service/public/cpp/app_registry_cache.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/paint_vector_icon.h"
#include "url/gurl.h"

namespace app_list {
namespace {

constexpr char kHelpAppUpdatesResult[] = "help-app://updates";

}  // namespace

HelpAppZeroStateResult::HelpAppZeroStateResult(Profile* profile,
                                               const std::string& id,
                                               DisplayType display_type,
                                               const std::u16string& title,
                                               const std::u16string& details,
                                               const gfx::ImageSkia& icon)
    : profile_(profile) {
  DCHECK(profile_);
  set_id(id);
  SetCategory(Category::kHelp);
  SetTitle(title);
  if (!details.empty())
    SetDetails(details);
  SetResultType(ResultType::kZeroStateHelpApp);
  SetDisplayType(display_type);
  // Some chips have different metrics types.
  if (id == kHelpAppUpdatesResult) {
    SetMetricsType(ash::HELP_APP_UPDATES);
  } else {
    SetMetricsType(ash::HELP_APP_DEFAULT);
  }
  SetChipIcon(icon);
}

HelpAppZeroStateResult::~HelpAppZeroStateResult() = default;

void HelpAppZeroStateResult::Open(int event_flags) {
  // Note: event_flags is ignored, LaunchSWA doesn't need it.
  if (id() == kHelpAppUpdatesResult) {
    // Launch release notes suggestion chip.
    base::RecordAction(
        base::UserMetricsAction("ReleaseNotes.SuggestionChipLaunched"));

    ash::ReleaseNotesStorage(profile_).StopShowingSuggestionChip();

    ash::SystemAppLaunchParams params;
    params.url = GURL("chrome://help-app/updates");
    params.launch_source = apps::LaunchSource::kFromAppListRecommendation;
    ash::LaunchSystemWebAppAsync(
        profile_, ash::SystemWebAppType::HELP, params,
        std::make_unique<apps::WindowInfo>(display::kDefaultDisplayId));
    // NOTE: Launching the result may dismiss the app list, which may delete
    // this result.
  }
}

HelpAppZeroStateProvider::HelpAppZeroStateProvider(
    Profile* profile,
    ash::AppListNotifier* notifier)
    : SearchProvider(SearchCategory::kHelp), profile_(profile) {
  DCHECK(profile_);

  app_registry_cache_observer_.Observe(
      &apps::AppServiceProxyFactory::GetForProfile(profile)
           ->AppRegistryCache());
  LoadIcon();

  if (notifier) {
    notifier_observer_.Observe(notifier);
  }
}

HelpAppZeroStateProvider::~HelpAppZeroStateProvider() = default;

void HelpAppZeroStateProvider::StartZeroState() {
  SearchProvider::Results search_results;

  if (ash::ReleaseNotesStorage(profile_).ShouldShowSuggestionChip()) {
    // Release notes are shown in the Continue section.
    auto* color_provider = ash::ColorProvider::Get();
    // NOTE: Color provider may not be set in unit tests.
    SkColor icon_color =
        color_provider
            ? color_provider->GetContentLayerColor(
                  ash::ColorProvider::ContentLayerType::kButtonIconColorPrimary)
            : gfx::kGoogleGrey900;
    gfx::ImageSkia icon = gfx::CreateVectorIcon(
        ash::kReleaseNotesChipIcon, app_list::kSystemIconDimension, icon_color);
    search_results.emplace_back(std::make_unique<HelpAppZeroStateResult>(
        profile_, kHelpAppUpdatesResult, DisplayType::kContinue,
        l10n_util::GetStringUTF16(IDS_HELP_APP_WHATS_NEW_CONTINUE_TASK_TITLE),
        l10n_util::GetStringUTF16(IDS_HELP_APP_WHATS_NEW_CONTINUE_TASK_DETAILS),
        icon));
  }

  SwapResults(&search_results);
}

ash::AppListSearchResultType HelpAppZeroStateProvider::ResultType() const {
  return ash::AppListSearchResultType::kZeroStateHelpApp;
}

void HelpAppZeroStateProvider::OnAppUpdate(const apps::AppUpdate& update) {
  if (update.AppId() == web_app::kHelpAppId && update.ReadinessChanged() &&
      update.Readiness() == apps::Readiness::kReady) {
    LoadIcon();
  }
}

void HelpAppZeroStateProvider::OnAppRegistryCacheWillBeDestroyed(
    apps::AppRegistryCache* cache) {
  app_registry_cache_observer_.Reset();
}

void HelpAppZeroStateProvider::OnImpression(
    ash::AppListNotifier::Location location,
    const std::vector<ash::AppListNotifier::Result>& results,
    const std::u16string& query) {
  if (location != ash::AppListNotifier::Location::kContinue) {
    return;
  }

  for (const auto& result : results) {
    if (result.id == kHelpAppUpdatesResult) {
      ash::ReleaseNotesStorage(profile_)
          .DecreaseTimesLeftToShowSuggestionChip();
    }
  }
}

void HelpAppZeroStateProvider::OnLoadIcon(apps::IconValuePtr icon_value) {
  if (icon_value && icon_value->icon_type == apps::IconType::kStandard) {
    icon_ = icon_value->uncompressed;
  }
}

void HelpAppZeroStateProvider::LoadIcon() {
  auto* proxy = apps::AppServiceProxyFactory::GetForProfile(profile_);
  proxy->LoadIcon(
      web_app::kHelpAppId, apps::IconType::kStandard,
      ash::SharedAppListConfig::instance().suggestion_chip_icon_dimension(),
      /*allow_placeholder_icon=*/false,
      base::BindOnce(&HelpAppZeroStateProvider::OnLoadIcon,
                     weak_factory_.GetWeakPtr()));
}

}  // namespace app_list