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

Unified 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: cleanup and update 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 side-by-side diff with in-line comments
Download patch
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..cd36eb6087dd45068980f76d0fdf172e9e74ae1b 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(
+// Helper that extracts app list from policy preferences.
+std::vector<std::string> GetAppsPinnedByPolicy(
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()));
+ 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(
stevenjb 2016/06/10 18:00:41 s/Depricated/Deprecated/, but also maybe use Legac
khmel 2016/06/10 22:08:10 Legacy sounds better, thanks
+ 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 = GetAppsPinnedByPolicy(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()) &&
- 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,253 @@ std::vector<std::string> GetPinnedAppsFromPrefs(
return apps;
}
+// Helper to creates pin position that stays before any synced app, even if
stevenjb 2016/06/10 18:00:40 s/creates/create/
khmel 2016/06/10 22:08:10 Done.
+// app is not currently visbile on a device.
+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();
+}
+
+// Helper to creates pin position that stays before any synced app, even if
+// app is not currently visbile on a device.
+syncer::StringOrdinal CreateLastPinPosition(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.GreaterThan(position)) {
+ position = sync_peer.second->item_pin_ordinal;
+ }
+ }
+
+ return position.IsValid() ? position.CreateAfter()
+ : 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 =
+ GetAppsPinnedByPolicy(prefs, helper);
+
+ std::vector<PinInfo> pin_infos;
+
+ // Empty pins indicates that sync based pin model is used for the first
+ // time. In normal workflow we have at least Chrom browser pin info.
stevenjb 2016/06/10 18:00:41 Chrome
khmel 2016/06/10 22:08:10 Done.
+ 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;
+ // Don't include apps that are currently do not exist on device.
stevenjb 2016/06/10 18:00:40 s/are//
khmel 2016/06/10 22:08:10 Done.
+ 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();
stevenjb 2016/06/10 18:00:40 If the app list gets large this could be expensive
+ // 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) {
stevenjb 2016/06/10 18:00:41 We should wrap this into a separate function, e.g.
khmel 2016/06/10 22:08:10 Done.
+ // 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());
stevenjb 2016/06/10 18:00:41 We are already iterating over legacy_pins below, w
khmel 2016/06/10 22:08:10 Done. Refactored GetPinnedAppsFromPrefsLegacy not
+ DCHECK(!legacy_pins.empty());
+
+ syncer::StringOrdinal last_position;
+ // Convert to sync item record.
+ for (const auto& app_id : legacy_pins) {
+ last_position = last_position.IsValid()
+ ? last_position.CreateAfter()
+ : syncer::StringOrdinal::CreateInitialOrdinal();
stevenjb 2016/06/10 18:00:40 nit: Just initialize last_position to syncer::Stri
khmel 2016/06/10 22:08:11 That is nicer, thanks
+ const bool pin_by_policy =
+ std::find(policy_apps.begin(), policy_apps.end(), app_id) !=
+ policy_apps.end();
stevenjb 2016/06/10 18:00:41 This will also avoid an N^2 operation if policy_ap
khmel 2016/06/10 22:08:10 Done.
+ app_service->SetPinPosition(app_id, last_position, pin_by_policy);
+ }
+
+ // Now process default apps.
+ 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());
stevenjb 2016/06/10 18:00:41 Just check this once above.
khmel 2016/06/10 22:08:10 Done.
+ last_position = last_position.CreateAfter();
+ app_service->SetPinPosition(app_id, last_position, false);
+ }
+
+ return legacy_pins;
+ }
+
+ // Sort pins according its ordinals.
stevenjb 2016/06/10 18:00:40 s/its/their/
khmel 2016/06/10 22:08:10 Done.
+ std::sort(pin_infos.begin(), pin_infos.end(), ComparePinInfo());
+
+ // Now insert Chrome browser app if needed.
+ 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.
stevenjb 2016/06/10 18:00:40 s/there have correct/they have a correct/
khmel 2016/06/10 22:08:10 Done.
khmel 2016/06/10 22:08:11 Done.
+ size_t shelf_index = 0;
+ for (const auto& app_id : policy_apps) {
+ if (app_id == kPinnedAppsPlaceholder)
+ continue;
+ 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;
+ }
+
+ if (arc::ArcAuthService::IsAllowedForProfile(helper->profile()) &&
+ helper->IsValidIDForCurrentUser(ArcSupportHost::kHostAppId)) {
+ if (!app_service->GetSyncItem(ArcSupportHost::kHostAppId)) {
+ const syncer::StringOrdinal arc_host_position =
+ CreateLastPinPosition(helper->profile());
+ pin_infos.insert(pin_infos.begin(),
+ PinInfo(ArcSupportHost::kHostAppId, arc_host_position));
+ app_service->SetPinPosition(ArcSupportHost::kHostAppId, arc_host_position,
+ false);
+ }
+ }
+
+ // Convert to string array.
+ 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

Powered by Google App Engine
This is Rietveld 408576698