| // Copyright 2016 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/chromeos/extensions/gfx_utils.h" |
| |
| #include "base/lazy_instance.h" |
| #include "base/stl_util.h" |
| #include "chrome/browser/chromeos/profiles/profile_helper.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" |
| #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" |
| #include "chrome/common/extensions/extension_constants.h" |
| #include "chrome/common/pref_names.h" |
| #include "components/prefs/pref_service.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/common/constants.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/chromeos/resources/grit/ui_chromeos_resources.h" |
| #include "ui/gfx/image/image_skia.h" |
| #include "ui/gfx/image/image_skia_operations.h" |
| |
| namespace extensions { |
| |
| namespace { |
| |
| // The badge map between |arc_package_name| and |extension_id|. Note the mapping |
| // from |extension_id| to |arc_package_name| is unique, but the mapping from |
| // |arc_package_name| to |extension_id| is not. |
| const struct { |
| const char* arc_package_name; |
| const char* extension_id; |
| } kDualBadgeMap[] = { |
| // Google Keep |
| {"com.google.android.keep", "hmjkmjkepdijhoojdojkdfohbdgmmhki"}, |
| {"com.google.android.keep", "dondgdlndnpianbklfnehgdhkickdjck"}, |
| // GMail |
| {"com.google.android.gm", extension_misc::kGmailAppId}, |
| {"com.google.android.gm", "bjdhhokmhgelphffoafoejjmlfblpdha"}, |
| // Google Drive |
| {"com.google.android.apps.docs", extension_misc::kDriveHostedAppId}, |
| {"com.google.android.apps.docs", "mdhnphfgagkpdhndljccoackjjhghlif"}, |
| // Google Maps |
| {"com.google.android.apps.maps", "lneaknkopdijkpnocmklfnjbeapigfbh"}, |
| // Calculator |
| {"com.google.android.calculator", "joodangkbfjnajiiifokapkpmhfnpleo"}, |
| // Chrome Remote Desktop |
| {"com.google.chromeremotedesktop", "gbchcmhmhahfdphkhkmpfmihenigjmpp"}, |
| {"com.google.chromeremotedesktop", "cdjikkcakjcdjemakobkmijmikhkegcj"}, |
| // Google Calender |
| {"com.google.android.calendar", "ejjicmeblgpmajnghnpcppodonldlgfn"}, |
| {"com.google.android.calendar", "fpgfohogebplgnamlafljlcidjedbdeb"}, |
| // Google Docs |
| {"com.google.android.apps.docs.editors.docs", |
| extension_misc::kGoogleDocAppId}, |
| {"com.google.android.apps.docs.editors.docs", |
| "npnjdccdffhdndcbeappiamcehbhjibf"}, |
| // Google Slides |
| {"com.google.android.apps.docs.editors.slides", |
| extension_misc::kGoogleSlidesAppId}, |
| {"com.google.android.apps.docs.editors.slides", |
| "hdmobeajeoanbanmdlabnbnlopepchip"}, |
| // Google Sheets |
| {"com.google.android.apps.docs.editors.sheets", |
| extension_misc::kGoogleSheetsAppId}, |
| {"com.google.android.apps.docs.editors.sheets", |
| "nifkmgcdokhkjghdlgflonppnefddien"}, |
| // YouTube |
| {"com.google.android.youtube", extension_misc::kYoutubeAppId}, |
| {"com.google.android.youtube", "pbdihpaifchmclcmkfdgffnnpfbobefh"}, |
| // Google Play Books |
| {"com.google.android.apps.books", extension_misc::kGooglePlayBooksAppId}, |
| // Google+ |
| {"com.google.android.apps.plus", "dlppkpafhbajpcmmoheippocdidnckmm"}, |
| {"com.google.android.apps.plus", "fgjnkhlabjcaajddbaenilcmpcidahll"}, |
| // Google Play Movies & TV |
| {"com.google.android.videos", extension_misc::kGooglePlayMoviesAppId}, |
| {"com.google.android.videos", "amfoiggnkefambnaaphodjdmdooiinna"}, |
| // Google Play Music |
| {"com.google.android.music", extension_misc::kGooglePlayMusicAppId}, |
| {"com.google.android.music", "onbhgdmifjebcabplolilidlpgeknifi"}, |
| // Google Now |
| {"com.google.android.launcher", "mnfadmojomeniojkkikjpgjaegolkbpb"}, |
| // Google Photos |
| {"com.google.android.apps.photos", "hcglmfcclpfgljeaiahehebeoaiicbko"}, |
| {"com.google.android.apps.photos", "efjnaogkjbogokcnohkmnjdojkikgobo"}, |
| {"com.google.android.apps.photos", "ifpkhncdnjfipfjlhfidljjffdgklanh"}, |
| // Google Classroom |
| {"com.google.android.apps.classroom", "mfhehppjhmmnlfbbopchdfldgimhfhfk"}, |
| // Google Hangouts |
| {"com.google.android.talk", "knipolnnllmklapflnccelgolnpehhpl"}, |
| {"com.google.android.talk", "cgmlfbhkckbedohgdepgbkflommbfkep"}, |
| {"com.google.android.talk", "gldgpnmcpaogjlojhhpebkbbanacoglc"}, |
| // Google Play Music |
| {"com.google.android.music", "fahmaaghhglfmonjliepjlchgpgfmobi"}, |
| // Google News |
| {"com.google.android.apps.genie.geniewidget", |
| "dllkocilcinkggkchnjgegijklcililc"}, |
| // Used in unit tests. |
| {"fake.package.name1", "emfkafnhnpcmabnnkckkchdilgeoekbo"}, |
| }; |
| |
| // This class maintains the maps between the extension id and its equivalent |
| // ARC package name. |
| class AppDualBadgeMap { |
| public: |
| using ArcAppToExtensionsMap = |
| std::unordered_map<std::string, std::vector<std::string>>; |
| using ExtensionToArcAppMap = std::unordered_map<std::string, std::string>; |
| |
| AppDualBadgeMap() { |
| for (size_t i = 0; i < base::size(kDualBadgeMap); ++i) { |
| arc_app_to_extensions_map_[kDualBadgeMap[i].arc_package_name].push_back( |
| kDualBadgeMap[i].extension_id); |
| extension_to_arc_app_map_[kDualBadgeMap[i].extension_id] = |
| kDualBadgeMap[i].arc_package_name; |
| } |
| } |
| |
| std::vector<std::string> GetExtensionIdsForArcPackageName( |
| std::string arc_package_name) { |
| const auto iter = arc_app_to_extensions_map_.find(arc_package_name); |
| if (iter == arc_app_to_extensions_map_.end()) |
| return std::vector<std::string>(); |
| return iter->second; |
| } |
| |
| std::string GetArcPackageNameFromExtensionId(std::string extension_id) { |
| const auto iter = extension_to_arc_app_map_.find(extension_id); |
| if (iter == extension_to_arc_app_map_.end()) |
| return std::string(); |
| return iter->second; |
| } |
| |
| private: |
| ArcAppToExtensionsMap arc_app_to_extensions_map_; |
| ExtensionToArcAppMap extension_to_arc_app_map_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AppDualBadgeMap); |
| }; |
| |
| base::LazyInstance<AppDualBadgeMap>::DestructorAtExit g_dual_badge_map = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| } // namespace |
| |
| namespace util { |
| |
| bool HasEquivalentInstalledArcApp(content::BrowserContext* context, |
| const std::string& extension_id) { |
| std::unordered_set<std::string> arc_apps; |
| return GetEquivalentInstalledArcApps(context, extension_id, &arc_apps); |
| } |
| |
| bool GetEquivalentInstalledArcApps(content::BrowserContext* context, |
| const std::string& extension_id, |
| std::unordered_set<std::string>* arc_apps) { |
| const std::string arc_package_name = |
| g_dual_badge_map.Get().GetArcPackageNameFromExtensionId(extension_id); |
| if (arc_package_name.empty()) |
| return false; |
| |
| const ArcAppListPrefs* const prefs = ArcAppListPrefs::Get(context); |
| if (!prefs) |
| return false; |
| |
| // TODO(hidehiko): The icon is per launcher, so we should have more precise |
| // check here. |
| DCHECK(arc_apps); |
| prefs->GetAppsForPackage(arc_package_name).swap(*arc_apps); |
| return !arc_apps->empty(); |
| } |
| |
| const std::vector<std::string> GetEquivalentInstalledExtensions( |
| content::BrowserContext* context, |
| const std::string& arc_package_name) { |
| const ExtensionRegistry* registry = ExtensionRegistry::Get(context); |
| if (!registry) |
| return std::vector<std::string>(); |
| |
| std::vector<std::string> extension_ids = |
| g_dual_badge_map.Get().GetExtensionIdsForArcPackageName(arc_package_name); |
| if (extension_ids.empty()) |
| return std::vector<std::string>(); |
| |
| base::EraseIf(extension_ids, [registry](std::string extension_id) { |
| return !registry->GetInstalledExtension(extension_id); |
| }); |
| return extension_ids; |
| } |
| |
| bool ShouldApplyChromeBadge(content::BrowserContext* context, |
| const std::string& extension_id) { |
| DCHECK(context); |
| |
| Profile* profile = Profile::FromBrowserContext(context); |
| // Only apply Chrome badge for the primary profile. |
| if (!chromeos::ProfileHelper::IsPrimaryProfile(profile) || |
| !multi_user_util::IsProfileFromActiveUser(profile)) { |
| return false; |
| } |
| |
| const ExtensionRegistry* registry = ExtensionRegistry::Get(context); |
| if (!registry || !registry->GetInstalledExtension(extension_id)) |
| return false; |
| |
| if (!HasEquivalentInstalledArcApp(context, extension_id)) |
| return false; |
| |
| return true; |
| } |
| |
| void ApplyChromeBadge(gfx::ImageSkia* icon_out) { |
| DCHECK(icon_out); |
| |
| const gfx::ImageSkia* badge_image = |
| ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( |
| IDR_ARC_DUAL_ICON_BADGE); |
| DCHECK(badge_image); |
| |
| gfx::ImageSkia resized_badge_image = *badge_image; |
| if (badge_image->size() != icon_out->size()) { |
| resized_badge_image = gfx::ImageSkiaOperations::CreateResizedImage( |
| *badge_image, skia::ImageOperations::RESIZE_BEST, icon_out->size()); |
| } |
| *icon_out = gfx::ImageSkiaOperations::CreateSuperimposedImage( |
| *icon_out, resized_badge_image); |
| } |
| |
| } // namespace util |
| } // namespace extensions |