1 // Copyright 2020 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/ui/webui/settings/chromeos/apps_section.h"
6
7 #include "base/feature_list.h"
8 #include "base/no_destructor.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/chromeos/arc/arc_util.h"
11 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_features.h"
12 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h"
13 #include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
14 #include "chrome/browser/ui/app_list/arc/arc_app_utils.h"
15 #include "chrome/browser/ui/webui/app_management/app_management_page_handler.h"
16 #include "chrome/browser/ui/webui/settings/chromeos/android_apps_handler.h"
17 #include "chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h"
18 #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h"
19 #include "chrome/browser/ui/webui/webui_util.h"
20 #include "chrome/common/url_constants.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "chrome/grit/os_settings_resources.h"
23 #include "chromeos/constants/chromeos_features.h"
24 #include "components/arc/arc_prefs.h"
25 #include "components/prefs/pref_service.h"
26 #include "content/public/browser/web_ui_data_source.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/base/webui/web_ui_util.h"
29 #include "ui/chromeos/devicetype_utils.h"
30
31 namespace chromeos {
32 namespace settings {
33 namespace {
34
GetAppsSearchConcepts()35 const std::vector<SearchConcept>& GetAppsSearchConcepts() {
36 static const base::NoDestructor<std::vector<SearchConcept>> tags({
37 {IDS_OS_SETTINGS_TAG_APPS,
38 mojom::kAppsSectionPath,
39 mojom::SearchResultIcon::kAppsGrid,
40 mojom::SearchResultDefaultRank::kMedium,
41 mojom::SearchResultType::kSection,
42 {.section = mojom::Section::kApps}},
43 {IDS_OS_SETTINGS_TAG_APPS_MANAGEMENT,
44 mojom::kAppManagementSubpagePath,
45 mojom::SearchResultIcon::kAppsGrid,
46 mojom::SearchResultDefaultRank::kMedium,
47 mojom::SearchResultType::kSubpage,
48 {.subpage = mojom::Subpage::kAppManagement},
49 {IDS_OS_SETTINGS_TAG_APPS_MANAGEMENT_ALT1, SearchConcept::kAltTagEnd}},
50 });
51 return *tags;
52 }
53
GetAndroidPlayStoreSearchConcepts()54 const std::vector<SearchConcept>& GetAndroidPlayStoreSearchConcepts() {
55 static const base::NoDestructor<std::vector<SearchConcept>> tags({
56 {IDS_OS_SETTINGS_TAG_PLAY_STORE,
57 mojom::kGooglePlayStoreSubpagePath,
58 mojom::SearchResultIcon::kGooglePlay,
59 mojom::SearchResultDefaultRank::kMedium,
60 mojom::SearchResultType::kSubpage,
61 {.subpage = mojom::Subpage::kGooglePlayStore}},
62 {IDS_OS_SETTINGS_TAG_REMOVE_PLAY_STORE,
63 mojom::kGooglePlayStoreSubpagePath,
64 mojom::SearchResultIcon::kGooglePlay,
65 mojom::SearchResultDefaultRank::kMedium,
66 mojom::SearchResultType::kSetting,
67 {.setting = mojom::Setting::kRemovePlayStore},
68 {IDS_OS_SETTINGS_TAG_REMOVE_PLAY_STORE_ALT1, SearchConcept::kAltTagEnd}},
69 });
70 return *tags;
71 }
72
GetAndroidSettingsSearchConcepts()73 const std::vector<SearchConcept>& GetAndroidSettingsSearchConcepts() {
74 static const base::NoDestructor<std::vector<SearchConcept>> tags({
75 {IDS_OS_SETTINGS_TAG_ANDROID_SETTINGS_WITH_PLAY_STORE,
76 mojom::kGooglePlayStoreSubpagePath,
77 mojom::SearchResultIcon::kGooglePlay,
78 mojom::SearchResultDefaultRank::kMedium,
79 mojom::SearchResultType::kSetting,
80 {.setting = mojom::Setting::kManageAndroidPreferences},
81 {IDS_OS_SETTINGS_TAG_ANDROID_SETTINGS_WITH_PLAY_STORE_ALT1,
82 SearchConcept::kAltTagEnd}},
83 });
84 return *tags;
85 }
86
GetAndroidNoPlayStoreSearchConcepts()87 const std::vector<SearchConcept>& GetAndroidNoPlayStoreSearchConcepts() {
88 static const base::NoDestructor<std::vector<SearchConcept>> tags({
89 {IDS_OS_SETTINGS_TAG_ANDROID_SETTINGS,
90 mojom::kAppsSectionPath,
91 mojom::SearchResultIcon::kAndroid,
92 mojom::SearchResultDefaultRank::kMedium,
93 mojom::SearchResultType::kSetting,
94 {.setting = mojom::Setting::kManageAndroidPreferences},
95 {IDS_OS_SETTINGS_TAG_ANDROID_SETTINGS_ALT1, SearchConcept::kAltTagEnd}},
96 });
97 return *tags;
98 }
99
GetAndroidPlayStoreDisabledSearchConcepts()100 const std::vector<SearchConcept>& GetAndroidPlayStoreDisabledSearchConcepts() {
101 static const base::NoDestructor<std::vector<SearchConcept>> tags({
102 {IDS_OS_SETTINGS_TAG_ANDROID_TURN_ON_PLAY_STORE,
103 mojom::kAppsSectionPath,
104 mojom::SearchResultIcon::kAndroid,
105 mojom::SearchResultDefaultRank::kMedium,
106 mojom::SearchResultType::kSetting,
107 {.setting = mojom::Setting::kTurnOnPlayStore},
108 {IDS_OS_SETTINGS_TAG_ANDROID_TURN_ON_PLAY_STORE_ALT1,
109 SearchConcept::kAltTagEnd}},
110 });
111 return *tags;
112 }
113
AddAppManagementStrings(content::WebUIDataSource * html_source)114 void AddAppManagementStrings(content::WebUIDataSource* html_source) {
115 static constexpr webui::LocalizedString kLocalizedStrings[] = {
116 {"appManagementAppInstalledByPolicyLabel",
117 IDS_APP_MANAGEMENT_POLICY_APP_POLICY_STRING},
118 {"appManagementCameraPermissionLabel", IDS_APP_MANAGEMENT_CAMERA},
119 {"appManagementContactsPermissionLabel", IDS_APP_MANAGEMENT_CONTACTS},
120 {"appManagementLocationPermissionLabel", IDS_APP_MANAGEMENT_LOCATION},
121 {"appManagementMicrophonePermissionLabel", IDS_APP_MANAGEMENT_MICROPHONE},
122 {"appManagementMoreSettingsLabel", IDS_APP_MANAGEMENT_MORE_SETTINGS},
123 {"appManagementNoAppsFound", IDS_APP_MANAGEMENT_NO_APPS_FOUND},
124 {"appManagementNoPermissions",
125 IDS_APPLICATION_INFO_APP_NO_PERMISSIONS_TEXT},
126 {"appManagementNotificationsLabel", IDS_APP_MANAGEMENT_NOTIFICATIONS},
127 {"appManagementPermissionsLabel", IDS_APP_MANAGEMENT_PERMISSIONS},
128 {"appManagementPinToShelfLabel", IDS_APP_MANAGEMENT_PIN_TO_SHELF},
129 {"appManagementPrintingPermissionLabel", IDS_APP_MANAGEMENT_PRINTING},
130 {"appManagementSearchPrompt", IDS_APP_MANAGEMENT_SEARCH_PROMPT},
131 {"appManagementStoragePermissionLabel", IDS_APP_MANAGEMENT_STORAGE},
132 {"appManagementUninstallLabel", IDS_APP_MANAGEMENT_UNINSTALL_APP},
133 };
134 AddLocalizedStringsBulk(html_source, kLocalizedStrings);
135 }
136
ShowPluginVm(const Profile * profile,const PrefService & pref_service)137 bool ShowPluginVm(const Profile* profile, const PrefService& pref_service) {
138 // Even if not allowed, we still want to show Plugin VM if the VM image is on
139 // disk, so that users are still able to delete the image at will.
140 return plugin_vm::PluginVmFeatures::Get()->IsAllowed(profile) ||
141 pref_service.GetBoolean(plugin_vm::prefs::kPluginVmImageExists);
142 }
143
144 } // namespace
145
AppsSection(Profile * profile,SearchTagRegistry * search_tag_registry,PrefService * pref_service,ArcAppListPrefs * arc_app_list_prefs)146 AppsSection::AppsSection(Profile* profile,
147 SearchTagRegistry* search_tag_registry,
148 PrefService* pref_service,
149 ArcAppListPrefs* arc_app_list_prefs)
150 : OsSettingsSection(profile, search_tag_registry),
151 pref_service_(pref_service),
152 arc_app_list_prefs_(arc_app_list_prefs) {
153 SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
154 updater.AddSearchTags(GetAppsSearchConcepts());
155
156 if (arc::IsArcAllowedForProfile(profile)) {
157 pref_change_registrar_.Init(pref_service_);
158 pref_change_registrar_.Add(
159 arc::prefs::kArcEnabled,
160 base::BindRepeating(&AppsSection::UpdateAndroidSearchTags,
161 base::Unretained(this)));
162
163 if (arc_app_list_prefs_)
164 arc_app_list_prefs_->AddObserver(this);
165
166 UpdateAndroidSearchTags();
167 }
168 }
169
~AppsSection()170 AppsSection::~AppsSection() {
171 if (arc::IsArcAllowedForProfile(profile())) {
172 if (arc_app_list_prefs_)
173 arc_app_list_prefs_->RemoveObserver(this);
174 }
175 }
176
AddLoadTimeData(content::WebUIDataSource * html_source)177 void AppsSection::AddLoadTimeData(content::WebUIDataSource* html_source) {
178 static constexpr webui::LocalizedString kLocalizedStrings[] = {
179 {"appsPageTitle", IDS_SETTINGS_APPS_TITLE},
180 {"appManagementTitle", IDS_SETTINGS_APPS_LINK_TEXT},
181 };
182 AddLocalizedStringsBulk(html_source, kLocalizedStrings);
183
184 // We have 2 variants of Android apps settings. Default case, when the Play
185 // Store app exists we show expandable section that allows as to
186 // enable/disable the Play Store and link to Android settings which is
187 // available once settings app is registered in the system.
188 // For AOSP images we don't have the Play Store app. In last case we Android
189 // apps settings consists only from root link to Android settings and only
190 // visible once settings app is registered.
191 html_source->AddBoolean("androidAppsVisible",
192 arc::IsArcAllowedForProfile(profile()));
193 html_source->AddBoolean("havePlayStoreApp", arc::IsPlayStoreAvailable());
194 html_source->AddBoolean(
195 "isSupportedArcVersion",
196 AppManagementPageHandler::IsCurrentArcVersionSupported(profile()));
197
198 AddAppManagementStrings(html_source);
199 AddAndroidAppStrings(html_source);
200 AddPluginVmLoadTimeData(html_source);
201 }
202
AddHandlers(content::WebUI * web_ui)203 void AppsSection::AddHandlers(content::WebUI* web_ui) {
204 web_ui->AddMessageHandler(
205 std::make_unique<chromeos::settings::AndroidAppsHandler>(profile()));
206
207 if (ShowPluginVm(profile(), *pref_service_)) {
208 web_ui->AddMessageHandler(
209 std::make_unique<chromeos::settings::PluginVmHandler>(profile()));
210 }
211 }
212
GetSectionNameMessageId() const213 int AppsSection::GetSectionNameMessageId() const {
214 return IDS_SETTINGS_APPS_TITLE;
215 }
216
GetSection() const217 mojom::Section AppsSection::GetSection() const {
218 return mojom::Section::kApps;
219 }
220
GetSectionIcon() const221 mojom::SearchResultIcon AppsSection::GetSectionIcon() const {
222 return mojom::SearchResultIcon::kAppsGrid;
223 }
224
GetSectionPath() const225 std::string AppsSection::GetSectionPath() const {
226 return mojom::kAppsSectionPath;
227 }
228
LogMetric(mojom::Setting setting,base::Value & value) const229 bool AppsSection::LogMetric(mojom::Setting setting, base::Value& value) const {
230 // Unimplemented.
231 return false;
232 }
233
RegisterHierarchy(HierarchyGenerator * generator) const234 void AppsSection::RegisterHierarchy(HierarchyGenerator* generator) const {
235 generator->RegisterTopLevelSetting(mojom::Setting::kTurnOnPlayStore);
236
237 // Manage apps.
238 generator->RegisterTopLevelSubpage(IDS_SETTINGS_APPS_LINK_TEXT,
239 mojom::Subpage::kAppManagement,
240 mojom::SearchResultIcon::kAppsGrid,
241 mojom::SearchResultDefaultRank::kMedium,
242 mojom::kAppManagementSubpagePath);
243 // Note: The subpage name in the UI is updated dynamically based on the app
244 // being shown, but we use a generic "App details" string here.
245 generator->RegisterNestedSubpage(
246 IDS_SETTINGS_APP_DETAILS_TITLE, mojom::Subpage::kAppDetails,
247 mojom::Subpage::kAppManagement, mojom::SearchResultIcon::kAppsGrid,
248 mojom::SearchResultDefaultRank::kMedium, mojom::kAppDetailsSubpagePath);
249 generator->RegisterNestedSubpage(IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS,
250 mojom::Subpage::kPluginVmSharedPaths,
251 mojom::Subpage::kAppManagement,
252 mojom::SearchResultIcon::kAppsGrid,
253 mojom::SearchResultDefaultRank::kMedium,
254 mojom::kPluginVmSharedPathsSubpagePath);
255 generator->RegisterNestedSubpage(
256 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_LABEL,
257 mojom::Subpage::kPluginVmUsbPreferences, mojom::Subpage::kAppManagement,
258 mojom::SearchResultIcon::kAppsGrid,
259 mojom::SearchResultDefaultRank::kMedium,
260 mojom::kPluginVmUsbPreferencesSubpagePath);
261
262 // Google Play Store.
263 generator->RegisterTopLevelSubpage(IDS_SETTINGS_ANDROID_APPS_LABEL,
264 mojom::Subpage::kGooglePlayStore,
265 mojom::SearchResultIcon::kGooglePlay,
266 mojom::SearchResultDefaultRank::kMedium,
267 mojom::kGooglePlayStoreSubpagePath);
268 static constexpr mojom::Setting kGooglePlayStoreSettings[] = {
269 mojom::Setting::kManageAndroidPreferences,
270 mojom::Setting::kRemovePlayStore,
271 };
272 RegisterNestedSettingBulk(mojom::Subpage::kGooglePlayStore,
273 kGooglePlayStoreSettings, generator);
274 generator->RegisterTopLevelAltSetting(
275 mojom::Setting::kManageAndroidPreferences);
276 }
277
OnAppRegistered(const std::string & app_id,const ArcAppListPrefs::AppInfo & app_info)278 void AppsSection::OnAppRegistered(const std::string& app_id,
279 const ArcAppListPrefs::AppInfo& app_info) {
280 UpdateAndroidSearchTags();
281 }
282
AddAndroidAppStrings(content::WebUIDataSource * html_source)283 void AppsSection::AddAndroidAppStrings(content::WebUIDataSource* html_source) {
284 static constexpr webui::LocalizedString kLocalizedStrings[] = {
285 {"androidAppsPageLabel", IDS_SETTINGS_ANDROID_APPS_LABEL},
286 {"androidAppsEnable", IDS_SETTINGS_TURN_ON},
287 {"androidAppsManageApps", IDS_SETTINGS_ANDROID_APPS_MANAGE_APPS},
288 {"androidAppsRemove", IDS_SETTINGS_ANDROID_APPS_REMOVE},
289 {"androidAppsRemoveButton", IDS_SETTINGS_ANDROID_APPS_REMOVE_BUTTON},
290 {"androidAppsDisableDialogTitle",
291 IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_TITLE},
292 {"androidAppsDisableDialogMessage",
293 IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_MESSAGE},
294 {"androidAppsDisableDialogRemove",
295 IDS_SETTINGS_ANDROID_APPS_DISABLE_DIALOG_REMOVE},
296 {"androidAppsManageAppLinks", IDS_SETTINGS_ANDROID_APPS_MANAGE_APP_LINKS},
297 };
298 AddLocalizedStringsBulk(html_source, kLocalizedStrings);
299 html_source->AddLocalizedString("androidAppsPageTitle",
300 arc::IsPlayStoreAvailable()
301 ? IDS_SETTINGS_ANDROID_APPS_TITLE
302 : IDS_SETTINGS_ANDROID_SETTINGS_TITLE);
303 html_source->AddString(
304 "androidAppsSubtext",
305 l10n_util::GetStringFUTF16(
306 IDS_SETTINGS_ANDROID_APPS_SUBTEXT, ui::GetChromeOSDeviceName(),
307 GetHelpUrlWithBoard(chrome::kAndroidAppsLearnMoreURL)));
308 }
309
AddPluginVmLoadTimeData(content::WebUIDataSource * html_source)310 void AppsSection::AddPluginVmLoadTimeData(
311 content::WebUIDataSource* html_source) {
312 static constexpr webui::LocalizedString kLocalizedStrings[] = {
313 {"pluginVmSharedPaths", IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS},
314 {"pluginVmSharedPathsListHeading",
315 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_HEADING},
316 {"pluginVmSharedPathsInstructionsAdd",
317 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_ADD},
318 {"pluginVmSharedPathsInstructionsRemove",
319 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_REMOVE},
320 {"pluginVmSharedPathsRemoveSharing",
321 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING},
322 {"pluginVmSharedPathsRemoveFailureDialogMessage",
323 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE},
324 {"pluginVmSharedPathsRemoveFailureDialogTitle",
325 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE},
326 {"pluginVmSharedPathsRemoveFailureTryAgain",
327 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN},
328 {"pluginVmSharedPathsListEmptyMessage",
329 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_EMPTY_MESSAGE},
330 {"pluginVmSharedUsbDevicesLabel",
331 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_LABEL},
332 {"pluginVmSharedUsbDevicesDescription",
333 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_DESCRIPTION},
334 {"pluginVmSharedUsbDevicesExtraDescription",
335 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_EXTRA_DESCRIPTION},
336 {"pluginVmSharedUsbDevicesListEmptyMessage",
337 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_LIST_EMPTY_MESSAGE},
338 {"pluginVmSharedUsbDevicesInUse",
339 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_IN_USE},
340 {"pluginVmSharedUsbDevicesReassign",
341 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_USB_DEVICES_REASSIGN},
342 {"pluginVmPermissionDialogCameraLabel",
343 IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_CAMERA_LABEL},
344 {"pluginVmPermissionDialogMicrophoneLabel",
345 IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_MICROPHONE_LABEL},
346 {"pluginVmPermissionDialogRelaunchButton",
347 IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_RELAUNCH_BUTTON},
348 };
349 AddLocalizedStringsBulk(html_source, kLocalizedStrings);
350
351 html_source->AddBoolean("showPluginVm",
352 ShowPluginVm(profile(), *pref_service_));
353 html_source->AddString(
354 "pluginVmSharedPathsInstructionsLocate",
355 l10n_util::GetStringFUTF16(
356 IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_LOCATE,
357 base::UTF8ToUTF16(plugin_vm::kChromeOSBaseDirectoryDisplayText)));
358 html_source->AddBoolean(
359 "showPluginVmCameraPermissions",
360 base::FeatureList::IsEnabled(
361 chromeos::features::kPluginVmShowCameraPermissions));
362 html_source->AddBoolean(
363 "showPluginVmMicrophonePermissions",
364 base::FeatureList::IsEnabled(
365 chromeos::features::kPluginVmShowMicrophonePermissions));
366 }
367
UpdateAndroidSearchTags()368 void AppsSection::UpdateAndroidSearchTags() {
369 SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate();
370
371 updater.RemoveSearchTags(GetAndroidNoPlayStoreSearchConcepts());
372 updater.RemoveSearchTags(GetAndroidPlayStoreDisabledSearchConcepts());
373 updater.RemoveSearchTags(GetAndroidPlayStoreSearchConcepts());
374 updater.RemoveSearchTags(GetAndroidSettingsSearchConcepts());
375
376 if (!arc::IsPlayStoreAvailable()) {
377 updater.AddSearchTags(GetAndroidNoPlayStoreSearchConcepts());
378 return;
379 }
380
381 if (!arc::IsArcPlayStoreEnabledForProfile(profile())) {
382 updater.AddSearchTags(GetAndroidPlayStoreDisabledSearchConcepts());
383 return;
384 }
385
386 updater.AddSearchTags(GetAndroidPlayStoreSearchConcepts());
387
388 if (arc_app_list_prefs_ &&
389 arc_app_list_prefs_->IsRegistered(arc::kSettingsAppId)) {
390 updater.AddSearchTags(GetAndroidSettingsSearchConcepts());
391 }
392 }
393
394 } // namespace settings
395 } // namespace chromeos
396