Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(263)

Side by Side Diff: chrome/browser/ui/ash/chrome_launcher_prefs.cc

Issue 2055553004: arc: Support pinned apps across Arc-enabled and Arc-disabled platforms. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h" 5 #include "chrome/browser/ui/ash/chrome_launcher_prefs.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <memory> 9 #include <memory>
10 10
11 #include "base/macros.h" 11 #include "base/macros.h"
12 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_number_conversions.h"
13 #include "base/values.h" 13 #include "base/values.h"
14 #include "chrome/browser/app_mode/app_mode_utils.h" 14 #include "chrome/browser/app_mode/app_mode_utils.h"
15 #include "chrome/browser/chromeos/arc/arc_auth_service.h" 15 #include "chrome/browser/chromeos/arc/arc_auth_service.h"
16 #include "chrome/browser/chromeos/arc/arc_support_host.h" 16 #include "chrome/browser/chromeos/arc/arc_support_host.h"
17 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
17 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" 18 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
18 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h" 19 #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h"
19 #include "chrome/common/extensions/extension_constants.h" 20 #include "chrome/common/extensions/extension_constants.h"
20 #include "chrome/common/pref_names.h" 21 #include "chrome/common/pref_names.h"
21 #include "components/pref_registry/pref_registry_syncable.h" 22 #include "components/pref_registry/pref_registry_syncable.h"
22 #include "components/prefs/pref_service.h" 23 #include "components/prefs/pref_service.h"
23 #include "components/prefs/scoped_user_pref_update.h" 24 #include "components/prefs/scoped_user_pref_update.h"
25 #include "sync/api/string_ordinal.h"
24 #include "ui/display/display.h" 26 #include "ui/display/display.h"
25 #include "ui/display/screen.h" 27 #include "ui/display/screen.h"
26 28
27 namespace ash { 29 namespace ash {
28 30
29 namespace { 31 namespace {
30 32
31 // App ID of default pinned apps. 33 // App ID of default pinned apps.
32 const char* kDefaultPinnedApps[] = { 34 const char* kDefaultPinnedApps[] = {
33 extension_misc::kGmailAppId, 35 extension_misc::kGmailAppId,
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after
184 for (const std::string& app_id : all_arc_app_ids) { 186 for (const std::string& app_id : all_arc_app_ids) {
185 const std::unique_ptr<ArcAppListPrefs::AppInfo> app_info = 187 const std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
186 app_list_pref.GetApp(app_id); 188 app_list_pref.GetApp(app_id);
187 if (app_info->package_name == package) { 189 if (app_info->package_name == package) {
188 activities.push_back(app_info->activity); 190 activities.push_back(app_info->activity);
189 } 191 }
190 } 192 }
191 return activities; 193 return activities;
192 } 194 }
193 195
196 struct PinInfo {
197 PinInfo(const std::string& app_id, const syncer::StringOrdinal& item_ordinal)
198 : app_id(app_id), item_ordinal(item_ordinal) {}
199
200 std::string app_id;
201 syncer::StringOrdinal item_ordinal;
202 };
203
204 struct ComparePinInfo {
205 bool operator()(const PinInfo& pin1, const PinInfo& pin2) {
206 return pin1.item_ordinal.LessThan(pin2.item_ordinal);
207 }
208 };
209
194 } // namespace 210 } // namespace
195 211
196 const char kPinnedAppsPrefAppIDPath[] = "id"; 212 const char kPinnedAppsPrefAppIDPath[] = "id";
197 const char kPinnedAppsPrefPinnedByPolicy[] = "pinned_by_policy"; 213 const char kPinnedAppsPrefPinnedByPolicy[] = "pinned_by_policy";
198 const char kPinnedAppsPlaceholder[] = "AppShelfIDPlaceholder--------"; 214 const char kPinnedAppsPlaceholder[] = "AppShelfIDPlaceholder--------";
199 215
200 const char kShelfAutoHideBehaviorAlways[] = "Always"; 216 const char kShelfAutoHideBehaviorAlways[] = "Always";
201 const char kShelfAutoHideBehaviorNever[] = "Never"; 217 const char kShelfAutoHideBehaviorNever[] = "Never";
202 218
203 const char kShelfAlignmentBottom[] = "Bottom"; 219 const char kShelfAlignmentBottom[] = "Bottom";
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
285 return; 301 return;
286 302
287 SetPerDisplayPref(prefs, display_id, prefs::kShelfAlignment, value); 303 SetPerDisplayPref(prefs, display_id, prefs::kShelfAlignment, value);
288 if (display_id == display::Screen::GetScreen()->GetPrimaryDisplay().id()) { 304 if (display_id == display::Screen::GetScreen()->GetPrimaryDisplay().id()) {
289 // See comment in |kShelfAlignment| as to why we consider two prefs. 305 // See comment in |kShelfAlignment| as to why we consider two prefs.
290 prefs->SetString(prefs::kShelfAlignmentLocal, value); 306 prefs->SetString(prefs::kShelfAlignmentLocal, value);
291 prefs->SetString(prefs::kShelfAlignment, value); 307 prefs->SetString(prefs::kShelfAlignment, value);
292 } 308 }
293 } 309 }
294 310
295 std::vector<std::string> GetPinnedAppsFromPrefs( 311 std::vector<std::string> GetPolicyPinnedApps(
296 const PrefService* prefs, 312 const PrefService* prefs,
297 const LauncherControllerHelper* helper) { 313 const LauncherControllerHelper* helper) {
298 // Adding the app list item to the list of items requires that the ID is not 314 const auto* policy = prefs->GetList(prefs::kPolicyPinnedLauncherApps);
299 // a valid and known ID for the extension system. The ID was constructed that
300 // way - but just to make sure...
301 DCHECK(!helper->IsValidIDForCurrentUser(kPinnedAppsPlaceholder));
302 315
303 std::vector<std::string> apps; 316 std::vector<std::string> apps;
304 const auto* pinned = prefs->GetList(prefs::kPinnedLauncherApps); 317 if (!policy)
305 const auto* policy = prefs->GetList(prefs::kPolicyPinnedLauncherApps); 318 return apps;
306
307 // Get the sanitized preference value for the index of the Chrome app icon.
308 const size_t chrome_icon_index = std::max<size_t>(
309 0, std::min<size_t>(pinned->GetSize(),
310 prefs->GetInteger(prefs::kShelfChromeIconIndex)));
311
312 // Check if Chrome is in either of the the preferences lists.
313 std::unique_ptr<base::Value> chrome_app(
314 ash::CreateAppDict(extension_misc::kChromeAppId));
315 bool chrome_listed =
316 (pinned->Find(*chrome_app.get()) != pinned->end() ||
317 (policy && policy->Find(*chrome_app.get()) != policy->end()));
318 319
319 // Obtain here all ids of ARC apps because it takes linear time, and getting 320 // Obtain here all ids of ARC apps because it takes linear time, and getting
320 // them in the loop bellow would lead to quadratic complexity. 321 // them in the loop bellow would lead to quadratic complexity.
321 const ArcAppListPrefs* const arc_app_list_pref = helper->GetArcAppListPrefs(); 322 const ArcAppListPrefs* const arc_app_list_pref = helper->GetArcAppListPrefs();
322 const std::vector<std::string> all_arc_app_ids( 323 const std::vector<std::string> all_arc_app_ids(
323 arc_app_list_pref ? arc_app_list_pref->GetAppIds() 324 arc_app_list_pref ? arc_app_list_pref->GetAppIds()
324 : std::vector<std::string>()); 325 : std::vector<std::string>());
325 326
326 std::string app_id; 327 std::string app_id;
327 for (size_t i = 0; policy && (i < policy->GetSize()); ++i) { 328 for (size_t i = 0; i < policy->GetSize(); ++i) {
328 const base::DictionaryValue* dictionary = nullptr; 329 const base::DictionaryValue* dictionary = nullptr;
329 if (policy->GetDictionary(i, &dictionary) && 330 if (policy->GetDictionary(i, &dictionary) &&
330 dictionary->GetString(kPinnedAppsPrefAppIDPath, &app_id) && 331 dictionary->GetString(kPinnedAppsPrefAppIDPath, &app_id) &&
331 std::find(apps.begin(), apps.end(), app_id) == apps.end()) { 332 std::find(apps.begin(), apps.end(), app_id) == apps.end()) {
332 if (IsAppIdArcPackage(app_id)) { 333 if (IsAppIdArcPackage(app_id)) {
333 if (!arc_app_list_pref) 334 if (!arc_app_list_pref)
334 continue; 335 continue;
335 336
336 // We are dealing with package name, not with 32 characters ID. 337 // We are dealing with package name, not with 32 characters ID.
337 const std::string& arc_package = app_id; 338 const std::string& arc_package = app_id;
338 const std::vector<std::string> activities = GetActivitiesForPackage( 339 const std::vector<std::string> activities = GetActivitiesForPackage(
339 arc_package, all_arc_app_ids, *arc_app_list_pref); 340 arc_package, all_arc_app_ids, *arc_app_list_pref);
340 for (const auto& activity : activities) { 341 for (const auto& activity : activities) {
341 const std::string arc_app_id = 342 const std::string arc_app_id =
342 ArcAppListPrefs::GetAppId(arc_package, activity); 343 ArcAppListPrefs::GetAppId(arc_package, activity);
343 if (helper->IsValidIDForCurrentUser(arc_app_id)) 344 if (helper->IsValidIDForCurrentUser(arc_app_id))
344 apps.push_back(arc_app_id); 345 apps.push_back(arc_app_id);
345 } 346 }
346 } else if (helper->IsValidIDForCurrentUser(app_id)) { 347 } else if (helper->IsValidIDForCurrentUser(app_id)) {
347 apps.push_back(app_id); 348 apps.push_back(app_id);
348 } 349 }
349 } 350 }
350 } 351 }
351 352
353 return apps;
354 }
355
356 std::vector<std::string> GetPinnedAppsFromPrefsDepricated(
357 const PrefService* prefs,
358 const LauncherControllerHelper* helper) {
359 // Adding the app list item to the list of items requires that the ID is not
360 // a valid and known ID for the extension system. The ID was constructed that
361 // way - but just to make sure...
362 DCHECK(!helper->IsValidIDForCurrentUser(kPinnedAppsPlaceholder));
363
364 const auto* pinned = prefs->GetList(prefs::kPinnedLauncherApps);
365 const auto* policy = prefs->GetList(prefs::kPolicyPinnedLauncherApps);
366
367 // Get the sanitized preference value for the index of the Chrome app icon.
368 const size_t chrome_icon_index = std::max<size_t>(
369 0, std::min<size_t>(pinned->GetSize(),
370 prefs->GetInteger(prefs::kShelfChromeIconIndex)));
371
372 // Check if Chrome is in either of the the preferences lists.
373 std::unique_ptr<base::Value> chrome_app(
374 ash::CreateAppDict(extension_misc::kChromeAppId));
375 bool chrome_listed =
376 (pinned->Find(*chrome_app.get()) != pinned->end() ||
377 (policy && policy->Find(*chrome_app.get()) != policy->end()));
378
379 std::vector<std::string> apps = GetPolicyPinnedApps(prefs, helper);
380
381 std::string app_id;
352 for (size_t i = 0; i < pinned->GetSize(); ++i) { 382 for (size_t i = 0; i < pinned->GetSize(); ++i) {
353 // We need to position the chrome icon relative to its place in the pinned 383 // We need to position the chrome icon relative to its place in the pinned
354 // preference list - even if an item of that list isn't shown yet. 384 // preference list - even if an item of that list isn't shown yet.
355 if (i == chrome_icon_index && !chrome_listed) { 385 if (i == chrome_icon_index && !chrome_listed) {
356 apps.push_back(extension_misc::kChromeAppId); 386 apps.push_back(extension_misc::kChromeAppId);
357 chrome_listed = true; 387 chrome_listed = true;
358 } 388 }
359 bool pinned_by_policy = false; 389 bool pinned_by_policy = false;
360 const base::DictionaryValue* dictionary = nullptr; 390 const base::DictionaryValue* dictionary = nullptr;
361 if (pinned->GetDictionary(i, &dictionary) && 391 if (pinned->GetDictionary(i, &dictionary) &&
362 dictionary->GetString(kPinnedAppsPrefAppIDPath, &app_id) && 392 dictionary->GetString(kPinnedAppsPrefAppIDPath, &app_id) &&
363 helper->IsValidIDForCurrentUser(app_id) && 393 helper->IsValidIDForCurrentUser(app_id) &&
364 std::find(apps.begin(), apps.end(), app_id) == apps.end() && 394 std::find(apps.begin(), apps.end(), app_id) == apps.end() &&
365 (!dictionary->GetBoolean(kPinnedAppsPrefPinnedByPolicy, 395 (!dictionary->GetBoolean(kPinnedAppsPrefPinnedByPolicy,
366 &pinned_by_policy) || 396 &pinned_by_policy) ||
367 !pinned_by_policy)) { 397 !pinned_by_policy)) {
368 apps.push_back(app_id); 398 apps.push_back(app_id);
369 } 399 }
370 } 400 }
371 401
372 if (arc::ArcAuthService::IsAllowedForProfile(helper->profile()) &&
khmel 2016/06/09 21:40:21 This is related fix, we need Arc Support App unpin
373 helper->IsValidIDForCurrentUser(ArcSupportHost::kHostAppId)) {
374 apps.push_back(ArcSupportHost::kHostAppId);
375 }
376
377 // If not added yet, the chrome item will be the last item in the list. 402 // If not added yet, the chrome item will be the last item in the list.
378 if (!chrome_listed) 403 if (!chrome_listed)
379 apps.push_back(extension_misc::kChromeAppId); 404 apps.push_back(extension_misc::kChromeAppId);
380 405
381 // If not added yet, place the app list item at the beginning of the list. 406 // If not added yet, place the app list item at the beginning of the list.
382 if (std::find(apps.begin(), apps.end(), kPinnedAppsPlaceholder) == apps.end()) 407 if (std::find(apps.begin(), apps.end(), kPinnedAppsPlaceholder) == apps.end())
383 apps.insert(apps.begin(), kPinnedAppsPlaceholder); 408 apps.insert(apps.begin(), kPinnedAppsPlaceholder);
384 409
385 return apps; 410 return apps;
386 } 411 }
387 412
413 syncer::StringOrdinal CreateFirstPinPosition(Profile* profile) {
414 syncer::StringOrdinal position;
415 app_list::AppListSyncableService* app_service =
416 app_list::AppListSyncableService::Get(profile);
417 for (const auto& sync_peer : app_service->sync_items()) {
418 if (!sync_peer.second->item_pin_ordinal.IsValid())
419 continue;
420 if (!position.IsValid() ||
421 sync_peer.second->item_pin_ordinal.LessThan(position)) {
422 position = sync_peer.second->item_pin_ordinal;
423 }
424 }
425
426 return position.IsValid() ? position.CreateBefore()
427 : syncer::StringOrdinal::CreateInitialOrdinal();
428 }
429
430 std::vector<std::string> GetPinnedAppsFromPrefs(
431 const PrefService* prefs,
432 LauncherControllerHelper* helper) {
433 app_list::AppListSyncableService* app_service =
434 app_list::AppListSyncableService::Get(helper->profile());
435 // Some unit tests may not have it.
436 if (!app_service)
437 return std::vector<std::string>();
438
439 const std::vector<std::string> policy_apps =
440 GetPolicyPinnedApps(prefs, helper);
441
442 std::vector<PinInfo> pin_infos;
443 bool first_run = true;
444 for (const auto& sync_peer : app_service->sync_items()) {
445 if (!sync_peer.second->item_pin_ordinal.IsValid())
446 continue;
447 first_run = false;
448 if (sync_peer.first != extension_misc::kChromeAppId &&
449 !helper->IsValidIDForCurrentUser(sync_peer.first)) {
450 continue;
451 }
452
453 const bool pin_by_policy = std::find(policy_apps.begin(), policy_apps.end(),
454 sync_peer.first) != policy_apps.end();
455 // Check if item was pinned by policy only and currently is not in policy.
456 if (!pin_by_policy && sync_peer.second->item_pin_by_policy)
457 continue;
458
459 pin_infos.push_back(
460 PinInfo(sync_peer.first, sync_peer.second->item_pin_ordinal));
461 }
462
463 if (first_run) {
464 // Empty pins indicates that sync based pin model is used for the first
465 // time. We need to import legacy pins model and convert it to sync based
466 // model.
467 std::vector<std::string> legacy_pins =
468 GetPinnedAppsFromPrefsDepricated(prefs, helper);
469 DCHECK(!legacy_pins.empty());
470
471 legacy_pins.erase(
472 std::remove_if(legacy_pins.begin(), legacy_pins.end(),
473 [](const std::string& app_id) {
474 return (app_id == kPinnedAppsPlaceholder);
475 }),
476 legacy_pins.end());
477
478 syncer::StringOrdinal last_position;
479 for (const auto& app_id : legacy_pins) {
480 last_position = last_position.IsValid()
481 ? last_position.CreateAfter()
482 : syncer::StringOrdinal::CreateInitialOrdinal();
483 last_position = last_position.CreateAfter();
484 const bool pin_by_policy =
485 std::find(policy_apps.begin(), policy_apps.end(), app_id) !=
486 policy_apps.end();
487 app_service->SetPinPosition(app_id, last_position, pin_by_policy);
488 }
489
490 for (size_t i = 0; i < arraysize(kDefaultPinnedApps); ++i) {
491 const std::string& app_id = kDefaultPinnedApps[i];
492 // Check if it is already imported.
493 if (app_service->GetPinPosition(app_id).IsValid())
494 continue;
495 // Check if it is present but not in legacy pin.
496 if (helper->IsValidIDForCurrentUser(app_id))
497 continue;
498 DCHECK(last_position.IsValid());
499 last_position = last_position.CreateAfter();
500 app_service->SetPinPosition(app_id, last_position, false);
501 }
502
503 return legacy_pins;
504 }
505
506 std::sort(pin_infos.begin(), pin_infos.end(), ComparePinInfo());
507
508 // Now insert Chrome browser app.
509 syncer::StringOrdinal chrome_position =
510 app_service->GetPinPosition(extension_misc::kChromeAppId);
511 if (!chrome_position.IsValid()) {
512 chrome_position = CreateFirstPinPosition(helper->profile());
513 pin_infos.insert(pin_infos.begin(),
514 PinInfo(extension_misc::kChromeAppId, chrome_position));
515 app_service->SetPinPosition(extension_misc::kChromeAppId, chrome_position,
516 false);
517 }
518
519 // Policy apps must preserve order of appearance and be first app in the list.
520 // Validate that there have correct position and fix if needed.
521 size_t shelf_index = 0;
522 for (const auto& app_id : policy_apps) {
523 if (app_id == kPinnedAppsPlaceholder)
524 continue;
525 // Ignore Chrome app, it has own rule.
526 if (app_id == extension_misc::kChromeAppId)
527 continue;
528
529 // Chrome browser app has right to appear between pinned by policy apps.
530 if (shelf_index < pin_infos.size() &&
531 pin_infos[shelf_index].app_id == extension_misc::kChromeAppId) {
532 ++shelf_index;
533 }
534
535 const bool need_position_item = shelf_index >= pin_infos.size() ||
536 app_id != pin_infos[shelf_index].app_id;
537
538 if (need_position_item) {
539 // Remove existing app that breaks the order from the list.
540 if (shelf_index < pin_infos.size()) {
541 pin_infos.erase(
542 std::remove_if(pin_infos.begin() + shelf_index + 1, pin_infos.end(),
543 [app_id](const PinInfo& pin_info) {
544 return (pin_info.app_id == app_id);
545 }),
546 pin_infos.end());
547 }
548
549 syncer::StringOrdinal new_shelf_ordinal;
550 if (shelf_index == 0) {
551 // First app
552 new_shelf_ordinal = pin_infos.empty()
553 ? syncer::StringOrdinal::CreateInitialOrdinal()
554 : pin_infos.front().item_ordinal.CreateBefore();
555 } else if (shelf_index == pin_infos.size()) {
556 new_shelf_ordinal = pin_infos.back().item_ordinal.CreateAfter();
557 } else {
558 new_shelf_ordinal =
559 pin_infos[shelf_index - 1].item_ordinal.CreateBetween(
560 pin_infos[shelf_index].item_ordinal);
561 }
562 pin_infos.insert(pin_infos.begin() + shelf_index,
563 PinInfo(app_id, new_shelf_ordinal));
564 const bool pin_by_policy =
565 !app_service->GetPinPosition(app_id).IsValid() ||
566 app_service->GetPinByPolicy(app_id);
567 app_service->SetPinPosition(app_id, new_shelf_ordinal, pin_by_policy);
568 }
569 ++shelf_index;
570 }
571
572 std::vector<std::string> pins(pin_infos.size());
573 for (size_t i = 0; i < pin_infos.size(); ++i)
574 pins[i] = pin_infos[i].app_id;
575
576 return pins;
577 }
578
579 void RemovePinPosition(Profile* profile, const std::string& app_id) {
580 DCHECK(profile);
581 DCHECK(!app_id.empty());
582 app_list::AppListSyncableService* app_service =
583 app_list::AppListSyncableService::Get(profile);
584 app_service->SetPinPosition(app_id, syncer::StringOrdinal(), false);
585 }
586
587 void SetPinPosition(Profile* profile,
588 const std::string& app_id,
589 const std::string& app_id_before,
590 const std::string& app_id_after) {
591 DCHECK(profile);
592 DCHECK(!app_id.empty());
593 DCHECK_NE(app_id, app_id_before);
594 DCHECK_NE(app_id, app_id_after);
595 DCHECK(app_id_before.empty() || app_id_before != app_id_after);
596
597 app_list::AppListSyncableService* app_service =
598 app_list::AppListSyncableService::Get(profile);
599 // Some unit tests may not have this service.
600 if (!app_service)
601 return;
602
603 syncer::StringOrdinal position_before =
604 app_id_before.empty() ? syncer::StringOrdinal()
605 : app_service->GetPinPosition(app_id_before);
606 syncer::StringOrdinal position_after =
607 app_id_after.empty() ? syncer::StringOrdinal()
608 : app_service->GetPinPosition(app_id_after);
609
610 syncer::StringOrdinal pin_position;
611 if (position_before.IsValid() && position_after.IsValid())
612 pin_position = position_before.CreateBetween(position_after);
613 else if (position_before.IsValid())
614 pin_position = position_before.CreateAfter();
615 else if (position_after.IsValid())
616 pin_position = position_after.CreateBefore();
617 else
618 pin_position = syncer::StringOrdinal::CreateInitialOrdinal();
619 app_service->SetPinPosition(app_id, pin_position, false);
620 }
621
388 } // namespace ash 622 } // namespace ash
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698