Chromium Code Reviews| Index: chrome/browser/ui/ash/chrome_launcher_prefs.cc |
| diff --git a/chrome/browser/ui/ash/chrome_launcher_prefs.cc b/chrome/browser/ui/ash/chrome_launcher_prefs.cc |
| index 80035443341bb1581e4bcb8b9537cb7969606033..3be18c13bd55702fd6bcc2838b4d8c44396ae54b 100644 |
| --- a/chrome/browser/ui/ash/chrome_launcher_prefs.cc |
| +++ b/chrome/browser/ui/ash/chrome_launcher_prefs.cc |
| @@ -14,6 +14,7 @@ |
| #include "chrome/browser/app_mode/app_mode_utils.h" |
| #include "chrome/browser/chromeos/arc/arc_auth_service.h" |
| #include "chrome/browser/chromeos/arc/arc_support_host.h" |
| +#include "chrome/browser/ui/app_list/app_list_syncable_service.h" |
| #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h" |
| #include "chrome/browser/ui/ash/launcher/launcher_controller_helper.h" |
| #include "chrome/common/extensions/extension_constants.h" |
| @@ -21,6 +22,7 @@ |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/prefs/scoped_user_pref_update.h" |
| +#include "sync/api/string_ordinal.h" |
| #include "ui/display/display.h" |
| #include "ui/display/screen.h" |
| @@ -191,6 +193,20 @@ std::vector<std::string> GetActivitiesForPackage( |
| return activities; |
| } |
| +struct PinInfo { |
| + PinInfo(const std::string& app_id, const syncer::StringOrdinal& item_ordinal) |
| + : app_id(app_id), item_ordinal(item_ordinal) {} |
| + |
| + std::string app_id; |
| + syncer::StringOrdinal item_ordinal; |
| +}; |
| + |
| +struct ComparePinInfo { |
| + bool operator()(const PinInfo& pin1, const PinInfo& pin2) { |
| + return pin1.item_ordinal.LessThan(pin2.item_ordinal); |
| + } |
| +}; |
| + |
| } // namespace |
| const char kPinnedAppsPrefAppIDPath[] = "id"; |
| @@ -292,29 +308,14 @@ void SetShelfAlignmentPref(PrefService* prefs, |
| } |
| } |
| -std::vector<std::string> GetPinnedAppsFromPrefs( |
| +std::vector<std::string> GetPolicyPinnedApps( |
| const PrefService* prefs, |
| const LauncherControllerHelper* helper) { |
| - // Adding the app list item to the list of items requires that the ID is not |
| - // a valid and known ID for the extension system. The ID was constructed that |
| - // way - but just to make sure... |
| - DCHECK(!helper->IsValidIDForCurrentUser(kPinnedAppsPlaceholder)); |
| - |
| - std::vector<std::string> apps; |
| - const auto* pinned = prefs->GetList(prefs::kPinnedLauncherApps); |
| const auto* policy = prefs->GetList(prefs::kPolicyPinnedLauncherApps); |
| - // Get the sanitized preference value for the index of the Chrome app icon. |
| - const size_t chrome_icon_index = std::max<size_t>( |
| - 0, std::min<size_t>(pinned->GetSize(), |
| - prefs->GetInteger(prefs::kShelfChromeIconIndex))); |
| - |
| - // Check if Chrome is in either of the the preferences lists. |
| - std::unique_ptr<base::Value> chrome_app( |
| - ash::CreateAppDict(extension_misc::kChromeAppId)); |
| - bool chrome_listed = |
| - (pinned->Find(*chrome_app.get()) != pinned->end() || |
| - (policy && policy->Find(*chrome_app.get()) != policy->end())); |
| + std::vector<std::string> apps; |
| + if (!policy) |
| + return apps; |
| // Obtain here all ids of ARC apps because it takes linear time, and getting |
| // them in the loop bellow would lead to quadratic complexity. |
| @@ -324,7 +325,7 @@ std::vector<std::string> GetPinnedAppsFromPrefs( |
| : std::vector<std::string>()); |
| std::string app_id; |
| - for (size_t i = 0; policy && (i < policy->GetSize()); ++i) { |
| + for (size_t i = 0; i < policy->GetSize(); ++i) { |
| const base::DictionaryValue* dictionary = nullptr; |
| if (policy->GetDictionary(i, &dictionary) && |
| dictionary->GetString(kPinnedAppsPrefAppIDPath, &app_id) && |
| @@ -349,6 +350,35 @@ std::vector<std::string> GetPinnedAppsFromPrefs( |
| } |
| } |
| + return apps; |
| +} |
| + |
| +std::vector<std::string> GetPinnedAppsFromPrefsDepricated( |
| + const PrefService* prefs, |
| + const LauncherControllerHelper* helper) { |
| + // Adding the app list item to the list of items requires that the ID is not |
| + // a valid and known ID for the extension system. The ID was constructed that |
| + // way - but just to make sure... |
| + DCHECK(!helper->IsValidIDForCurrentUser(kPinnedAppsPlaceholder)); |
| + |
| + const auto* pinned = prefs->GetList(prefs::kPinnedLauncherApps); |
| + const auto* policy = prefs->GetList(prefs::kPolicyPinnedLauncherApps); |
| + |
| + // Get the sanitized preference value for the index of the Chrome app icon. |
| + const size_t chrome_icon_index = std::max<size_t>( |
| + 0, std::min<size_t>(pinned->GetSize(), |
| + prefs->GetInteger(prefs::kShelfChromeIconIndex))); |
| + |
| + // Check if Chrome is in either of the the preferences lists. |
| + std::unique_ptr<base::Value> chrome_app( |
| + ash::CreateAppDict(extension_misc::kChromeAppId)); |
| + bool chrome_listed = |
| + (pinned->Find(*chrome_app.get()) != pinned->end() || |
| + (policy && policy->Find(*chrome_app.get()) != policy->end())); |
| + |
| + std::vector<std::string> apps = GetPolicyPinnedApps(prefs, helper); |
| + |
| + std::string app_id; |
| for (size_t i = 0; i < pinned->GetSize(); ++i) { |
| // We need to position the chrome icon relative to its place in the pinned |
| // preference list - even if an item of that list isn't shown yet. |
| @@ -369,11 +399,6 @@ std::vector<std::string> GetPinnedAppsFromPrefs( |
| } |
| } |
| - if (arc::ArcAuthService::IsAllowedForProfile(helper->profile()) && |
|
khmel
2016/06/09 21:40:21
This is related fix, we need Arc Support App unpin
|
| - helper->IsValidIDForCurrentUser(ArcSupportHost::kHostAppId)) { |
| - apps.push_back(ArcSupportHost::kHostAppId); |
| - } |
| - |
| // If not added yet, the chrome item will be the last item in the list. |
| if (!chrome_listed) |
| apps.push_back(extension_misc::kChromeAppId); |
| @@ -385,4 +410,213 @@ std::vector<std::string> GetPinnedAppsFromPrefs( |
| return apps; |
| } |
| +syncer::StringOrdinal CreateFirstPinPosition(Profile* profile) { |
| + syncer::StringOrdinal position; |
| + app_list::AppListSyncableService* app_service = |
| + app_list::AppListSyncableService::Get(profile); |
| + for (const auto& sync_peer : app_service->sync_items()) { |
| + if (!sync_peer.second->item_pin_ordinal.IsValid()) |
| + continue; |
| + if (!position.IsValid() || |
| + sync_peer.second->item_pin_ordinal.LessThan(position)) { |
| + position = sync_peer.second->item_pin_ordinal; |
| + } |
| + } |
| + |
| + return position.IsValid() ? position.CreateBefore() |
| + : syncer::StringOrdinal::CreateInitialOrdinal(); |
| +} |
| + |
| +std::vector<std::string> GetPinnedAppsFromPrefs( |
| + const PrefService* prefs, |
| + LauncherControllerHelper* helper) { |
| + app_list::AppListSyncableService* app_service = |
| + app_list::AppListSyncableService::Get(helper->profile()); |
| + // Some unit tests may not have it. |
| + if (!app_service) |
| + return std::vector<std::string>(); |
| + |
| + const std::vector<std::string> policy_apps = |
| + GetPolicyPinnedApps(prefs, helper); |
| + |
| + std::vector<PinInfo> pin_infos; |
| + bool first_run = true; |
| + for (const auto& sync_peer : app_service->sync_items()) { |
| + if (!sync_peer.second->item_pin_ordinal.IsValid()) |
| + continue; |
| + first_run = false; |
| + if (sync_peer.first != extension_misc::kChromeAppId && |
| + !helper->IsValidIDForCurrentUser(sync_peer.first)) { |
| + continue; |
| + } |
| + |
| + const bool pin_by_policy = std::find(policy_apps.begin(), policy_apps.end(), |
| + sync_peer.first) != policy_apps.end(); |
| + // Check if item was pinned by policy only and currently is not in policy. |
| + if (!pin_by_policy && sync_peer.second->item_pin_by_policy) |
| + continue; |
| + |
| + pin_infos.push_back( |
| + PinInfo(sync_peer.first, sync_peer.second->item_pin_ordinal)); |
| + } |
| + |
| + if (first_run) { |
| + // Empty pins indicates that sync based pin model is used for the first |
| + // time. We need to import legacy pins model and convert it to sync based |
| + // model. |
| + std::vector<std::string> legacy_pins = |
| + GetPinnedAppsFromPrefsDepricated(prefs, helper); |
| + DCHECK(!legacy_pins.empty()); |
| + |
| + legacy_pins.erase( |
| + std::remove_if(legacy_pins.begin(), legacy_pins.end(), |
| + [](const std::string& app_id) { |
| + return (app_id == kPinnedAppsPlaceholder); |
| + }), |
| + legacy_pins.end()); |
| + |
| + syncer::StringOrdinal last_position; |
| + for (const auto& app_id : legacy_pins) { |
| + last_position = last_position.IsValid() |
| + ? last_position.CreateAfter() |
| + : syncer::StringOrdinal::CreateInitialOrdinal(); |
| + last_position = last_position.CreateAfter(); |
| + const bool pin_by_policy = |
| + std::find(policy_apps.begin(), policy_apps.end(), app_id) != |
| + policy_apps.end(); |
| + app_service->SetPinPosition(app_id, last_position, pin_by_policy); |
| + } |
| + |
| + for (size_t i = 0; i < arraysize(kDefaultPinnedApps); ++i) { |
| + const std::string& app_id = kDefaultPinnedApps[i]; |
| + // Check if it is already imported. |
| + if (app_service->GetPinPosition(app_id).IsValid()) |
| + continue; |
| + // Check if it is present but not in legacy pin. |
| + if (helper->IsValidIDForCurrentUser(app_id)) |
| + continue; |
| + DCHECK(last_position.IsValid()); |
| + last_position = last_position.CreateAfter(); |
| + app_service->SetPinPosition(app_id, last_position, false); |
| + } |
| + |
| + return legacy_pins; |
| + } |
| + |
| + std::sort(pin_infos.begin(), pin_infos.end(), ComparePinInfo()); |
| + |
| + // Now insert Chrome browser app. |
| + syncer::StringOrdinal chrome_position = |
| + app_service->GetPinPosition(extension_misc::kChromeAppId); |
| + if (!chrome_position.IsValid()) { |
| + chrome_position = CreateFirstPinPosition(helper->profile()); |
| + pin_infos.insert(pin_infos.begin(), |
| + PinInfo(extension_misc::kChromeAppId, chrome_position)); |
| + app_service->SetPinPosition(extension_misc::kChromeAppId, chrome_position, |
| + false); |
| + } |
| + |
| + // Policy apps must preserve order of appearance and be first app in the list. |
| + // Validate that there have correct position and fix if needed. |
| + size_t shelf_index = 0; |
| + for (const auto& app_id : policy_apps) { |
| + if (app_id == kPinnedAppsPlaceholder) |
| + continue; |
| + // Ignore Chrome app, it has own rule. |
| + if (app_id == extension_misc::kChromeAppId) |
| + continue; |
| + |
| + // Chrome browser app has right to appear between pinned by policy apps. |
| + if (shelf_index < pin_infos.size() && |
| + pin_infos[shelf_index].app_id == extension_misc::kChromeAppId) { |
| + ++shelf_index; |
| + } |
| + |
| + const bool need_position_item = shelf_index >= pin_infos.size() || |
| + app_id != pin_infos[shelf_index].app_id; |
| + |
| + if (need_position_item) { |
| + // Remove existing app that breaks the order from the list. |
| + if (shelf_index < pin_infos.size()) { |
| + pin_infos.erase( |
| + std::remove_if(pin_infos.begin() + shelf_index + 1, pin_infos.end(), |
| + [app_id](const PinInfo& pin_info) { |
| + return (pin_info.app_id == app_id); |
| + }), |
| + pin_infos.end()); |
| + } |
| + |
| + syncer::StringOrdinal new_shelf_ordinal; |
| + if (shelf_index == 0) { |
| + // First app |
| + new_shelf_ordinal = pin_infos.empty() |
| + ? syncer::StringOrdinal::CreateInitialOrdinal() |
| + : pin_infos.front().item_ordinal.CreateBefore(); |
| + } else if (shelf_index == pin_infos.size()) { |
| + new_shelf_ordinal = pin_infos.back().item_ordinal.CreateAfter(); |
| + } else { |
| + new_shelf_ordinal = |
| + pin_infos[shelf_index - 1].item_ordinal.CreateBetween( |
| + pin_infos[shelf_index].item_ordinal); |
| + } |
| + pin_infos.insert(pin_infos.begin() + shelf_index, |
| + PinInfo(app_id, new_shelf_ordinal)); |
| + const bool pin_by_policy = |
| + !app_service->GetPinPosition(app_id).IsValid() || |
| + app_service->GetPinByPolicy(app_id); |
| + app_service->SetPinPosition(app_id, new_shelf_ordinal, pin_by_policy); |
| + } |
| + ++shelf_index; |
| + } |
| + |
| + std::vector<std::string> pins(pin_infos.size()); |
| + for (size_t i = 0; i < pin_infos.size(); ++i) |
| + pins[i] = pin_infos[i].app_id; |
| + |
| + return pins; |
| +} |
| + |
| +void RemovePinPosition(Profile* profile, const std::string& app_id) { |
| + DCHECK(profile); |
| + DCHECK(!app_id.empty()); |
| + app_list::AppListSyncableService* app_service = |
| + app_list::AppListSyncableService::Get(profile); |
| + app_service->SetPinPosition(app_id, syncer::StringOrdinal(), false); |
| +} |
| + |
| +void SetPinPosition(Profile* profile, |
| + const std::string& app_id, |
| + const std::string& app_id_before, |
| + const std::string& app_id_after) { |
| + DCHECK(profile); |
| + DCHECK(!app_id.empty()); |
| + DCHECK_NE(app_id, app_id_before); |
| + DCHECK_NE(app_id, app_id_after); |
| + DCHECK(app_id_before.empty() || app_id_before != app_id_after); |
| + |
| + app_list::AppListSyncableService* app_service = |
| + app_list::AppListSyncableService::Get(profile); |
| + // Some unit tests may not have this service. |
| + if (!app_service) |
| + return; |
| + |
| + syncer::StringOrdinal position_before = |
| + app_id_before.empty() ? syncer::StringOrdinal() |
| + : app_service->GetPinPosition(app_id_before); |
| + syncer::StringOrdinal position_after = |
| + app_id_after.empty() ? syncer::StringOrdinal() |
| + : app_service->GetPinPosition(app_id_after); |
| + |
| + syncer::StringOrdinal pin_position; |
| + if (position_before.IsValid() && position_after.IsValid()) |
| + pin_position = position_before.CreateBetween(position_after); |
| + else if (position_before.IsValid()) |
| + pin_position = position_before.CreateAfter(); |
| + else if (position_after.IsValid()) |
| + pin_position = position_after.CreateBefore(); |
| + else |
| + pin_position = syncer::StringOrdinal::CreateInitialOrdinal(); |
| + app_service->SetPinPosition(app_id, pin_position, false); |
| +} |
| + |
| } // namespace ash |