| // Copyright 2018 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 "chrome/browser/ui/app_list/search/internal_app_result.h" |
| |
| #include <utility> |
| |
| #include "ash/public/cpp/app_list/app_list_config.h" |
| #include "ash/public/cpp/app_list/internal_app_id_constants.h" |
| #include "base/bind.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "chrome/browser/favicon/large_icon_service_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/app_list/app_list_controller_delegate.h" |
| #include "chrome/browser/ui/app_list/internal_app/internal_app_context_menu.h" |
| #include "chrome/browser/ui/app_list/internal_app/internal_app_metadata.h" |
| #include "components/favicon/core/favicon_server_fetcher_params.h" |
| #include "components/favicon/core/large_icon_service.h" |
| #include "components/favicon_base/fallback_icon_style.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/gfx/image/image_skia_operations.h" |
| |
| namespace app_list { |
| |
| // TODO(crbug.com/826982): move UMA_HISTOGRAM_ENUMERATION code to |
| // built_in_chromeos_apps.cc when the AppService feature is enabled by default. |
| |
| // static |
| void InternalAppResult::RecordShowHistogram(const std::string& app_id) { |
| InternalAppName name = GetInternalAppNameByAppId(app_id); |
| UMA_HISTOGRAM_ENUMERATION("Apps.AppListSearchResultInternalApp.Show", name); |
| } |
| |
| // static |
| void InternalAppResult::RecordOpenHistogram(const std::string& app_id) { |
| InternalAppName name = GetInternalAppNameByAppId(app_id); |
| UMA_HISTOGRAM_ENUMERATION("Apps.AppListSearchResultInternalApp.Open", name); |
| } |
| |
| InternalAppResult::InternalAppResult(Profile* profile, |
| const std::string& app_id, |
| AppListControllerDelegate* controller, |
| bool is_recommendation) |
| : AppResult(profile, app_id, controller, is_recommendation), |
| weak_factory_(this) { |
| set_id(app_id); |
| SetResultType(ResultType::kInternalApp); |
| SetIcon(GetIconForResourceId( |
| GetIconResourceIdByAppId(app_id), |
| AppListConfig::instance().search_tile_icon_dimension())); |
| if (display_type() == ash::SearchResultDisplayType::kRecommendation) { |
| SetChipIcon(GetIconForResourceId( |
| GetIconResourceIdByAppId(app_id), |
| AppListConfig::instance().suggestion_chip_icon_dimension())); |
| } |
| |
| if (id() == kInternalAppIdContinueReading) { |
| large_icon_service_ = |
| LargeIconServiceFactory::GetForBrowserContext(profile); |
| UpdateContinueReadingFavicon(/*continue_to_google_server=*/true); |
| } |
| |
| RecordShowHistogram(app_id); |
| } |
| |
| InternalAppResult::~InternalAppResult() = default; |
| |
| void InternalAppResult::ExecuteLaunchCommand(int event_flags) { |
| Open(event_flags); |
| } |
| |
| void InternalAppResult::Open(int event_flags) { |
| RecordOpenHistogram(id()); |
| |
| if (id() == kInternalAppIdContinueReading && |
| url_for_continuous_reading_.is_valid()) { |
| controller()->OpenURL(profile(), url_for_continuous_reading_, |
| ui::PAGE_TRANSITION_GENERATED, |
| ui::DispositionFromEventFlags(event_flags)); |
| return; |
| } |
| |
| OpenInternalApp(id(), profile(), event_flags); |
| } |
| |
| void InternalAppResult::UpdateContinueReadingFavicon( |
| bool continue_to_google_server) { |
| base::string16 title; |
| GURL url; |
| if (HasRecommendableForeignTab(profile(), &title, &url, |
| /*test_delegate=*/nullptr)) { |
| url_for_continuous_reading_ = url; |
| |
| // Foreign tab could be updated since the title was set the last time. |
| // Update the title every time. |
| // TODO(wutao): If |title| is empty, use default title string. |
| if (!title.empty()) |
| SetTitle(title); |
| |
| // Desired size of the icon. If not available, a smaller one will be used. |
| constexpr int min_source_size_in_pixel = 16; |
| constexpr int desired_size_in_pixel = 32; |
| large_icon_service_->GetLargeIconImageOrFallbackStyleForPageUrl( |
| url_for_continuous_reading_, min_source_size_in_pixel, |
| desired_size_in_pixel, |
| base::BindRepeating(&InternalAppResult::OnGetFaviconFromCacheFinished, |
| weak_factory_.GetWeakPtr(), |
| continue_to_google_server), |
| &task_tracker_); |
| } |
| } |
| |
| void InternalAppResult::OnGetFaviconFromCacheFinished( |
| bool continue_to_google_server, |
| const favicon_base::LargeIconImageResult& image_result) { |
| if (!image_result.image.IsEmpty()) { |
| // Continue Reading app will only be shown in suggestion chip. |
| SetChipIcon(*image_result.image.ToImageSkia()); |
| // Update the time when the icon was last requested to postpone the |
| // automatic eviction of the favicon from the favicon database. |
| large_icon_service_->TouchIconFromGoogleServer(image_result.icon_url); |
| return; |
| } |
| |
| if (!continue_to_google_server) |
| return; |
| |
| // Try to fetch the favicon from a Google favicon server. |
| net::NetworkTrafficAnnotationTag traffic_annotation = |
| net::DefineNetworkTrafficAnnotation("app_suggestion_get_favicon", R"( |
| semantics { |
| sender: "App Suggestion" |
| description: |
| "Sends a request to a Google server to retrieve the favicon bitmap " |
| "for an article suggestion on the Launcher (URLs are public and " |
| "provided by Google)." |
| trigger: |
| "A request can be sent if Chrome does not have a favicon for a " |
| "particular page." |
| data: "Page URL and desired icon size." |
| destination: GOOGLE_OWNED_SERVICE |
| } |
| policy { |
| cookies_allowed: NO |
| setting: "This feature cannot be disabled by settings." |
| policy_exception_justification: "Not implemented." |
| })"); |
| large_icon_service_ |
| ->GetLargeIconOrFallbackStyleFromGoogleServerSkippingLocalCache( |
| favicon::FaviconServerFetcherParams::CreateForDesktop( |
| url_for_continuous_reading_), |
| /*may_page_url_be_private=*/false, traffic_annotation, |
| base::BindRepeating( |
| &InternalAppResult::OnGetFaviconFromGoogleServerFinished, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void InternalAppResult::OnGetFaviconFromGoogleServerFinished( |
| favicon_base::GoogleFaviconServerRequestStatus status) { |
| if (status != favicon_base::GoogleFaviconServerRequestStatus::SUCCESS) |
| return; |
| |
| UpdateContinueReadingFavicon(/*continue_to_google_server=*/false); |
| } |
| |
| void InternalAppResult::GetContextMenuModel(GetMenuModelCallback callback) { |
| const auto* internal_app = app_list::FindInternalApp(id()); |
| DCHECK(internal_app); |
| if (!internal_app->show_in_launcher) { |
| std::move(callback).Run(nullptr); |
| return; |
| } |
| |
| if (!context_menu_) { |
| context_menu_ = |
| std::make_unique<InternalAppContextMenu>(profile(), id(), controller()); |
| } |
| context_menu_->GetMenuModel(std::move(callback)); |
| } |
| |
| SearchResultType InternalAppResult::GetSearchResultType() const { |
| return INTERNAL_APP; |
| } |
| |
| AppContextMenu* InternalAppResult::GetAppContextMenu() { |
| return context_menu_.get(); |
| } |
| |
| } // namespace app_list |