blob: 5d60fb7aa3136b1f18c025e3072d3574fc18c01a [file] [log] [blame]
// Copyright (c) 2012 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/ui/ash/chrome_launcher_prefs.h"
#include <stddef.h>
#include <set>
#include "ash/public/cpp/app_launch_id.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/values.h"
#include "chrome/browser/app_mode/app_mode_utils.h"
#include "chrome/browser/chromeos/arc/arc_support_host.h"
#include "chrome/browser/prefs/pref_service_syncable_util.h"
#include "chrome/browser/ui/app_list/app_list_syncable_service.h"
#include "chrome/browser/ui/app_list/app_list_syncable_service_factory.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"
#include "chrome/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/sync/model/string_ordinal.h"
#include "components/sync_preferences/pref_service_syncable.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/display/types/display_constants.h"
namespace ash {
namespace launcher {
namespace {
std::unique_ptr<base::DictionaryValue> CreateAppDict(
const std::string& app_id) {
auto app_value = base::MakeUnique<base::DictionaryValue>();
app_value->SetString(kPinnedAppsPrefAppIDPath, app_id);
return app_value;
}
// App ID of default pinned apps.
const char* kDefaultPinnedApps[] = {
extension_misc::kGmailAppId, extension_misc::kGoogleDocAppId,
extension_misc::kYoutubeAppId, ArcSupportHost::kHostAppId};
std::unique_ptr<base::ListValue> CreateDefaultPinnedAppsList() {
std::unique_ptr<base::ListValue> apps(new base::ListValue);
for (size_t i = 0; i < arraysize(kDefaultPinnedApps); ++i)
apps->Append(CreateAppDict(kDefaultPinnedApps[i]));
return apps;
}
// Returns the preference value for the display with the given |display_id|.
// The pref value is stored in |local_path| and |path|, but the pref service may
// have per-display preferences and the value can be specified by policy.
// Here is the priority:
// * A value managed by policy. This is a single value that applies to all
// displays.
// * A user-set value for the specified display.
// * A user-set value in |local_path| or |path|, if no per-display settings are
// ever specified (see http://crbug.com/173719 for why). |local_path| is
// preferred. See comment in |kShelfAlignment| as to why we consider two
// prefs and why |local_path| is preferred.
// * A value recommended by policy. This is a single value that applies to all
// root windows.
// * The default value for |local_path| if the value is not recommended by
// policy.
std::string GetPerDisplayPref(PrefService* prefs,
int64_t display_id,
const char* local_path,
const char* path) {
const PrefService::Preference* local_pref = prefs->FindPreference(local_path);
const std::string value(prefs->GetString(local_path));
if (local_pref->IsManaged())
return value;
std::string pref_key = base::Int64ToString(display_id);
bool has_per_display_prefs = false;
if (!pref_key.empty()) {
const base::DictionaryValue* shelf_prefs =
prefs->GetDictionary(prefs::kShelfPreferences);
const base::DictionaryValue* display_pref = nullptr;
std::string per_display_value;
if (shelf_prefs->GetDictionary(pref_key, &display_pref) &&
display_pref->GetString(path, &per_display_value))
return per_display_value;
// If the pref for the specified display is not found, scan the whole prefs
// and check if the prefs for other display is already specified.
std::string unused_value;
for (base::DictionaryValue::Iterator iter(*shelf_prefs); !iter.IsAtEnd();
iter.Advance()) {
const base::DictionaryValue* display_pref = nullptr;
if (iter.value().GetAsDictionary(&display_pref) &&
display_pref->GetString(path, &unused_value)) {
has_per_display_prefs = true;
break;
}
}
}
if (local_pref->IsRecommended() || !has_per_display_prefs)
return value;
const base::Value* default_value = prefs->GetDefaultPrefValue(local_path);
std::string default_string;
default_value->GetAsString(&default_string);
return default_string;
}
// Sets the preference value for the display with the given |display_id|.
void SetPerDisplayPref(PrefService* prefs,
int64_t display_id,
const char* pref_key,
const std::string& value) {
if (display_id < 0)
return;
// Avoid DictionaryPrefUpdate's notifications for read but unmodified prefs.
const base::DictionaryValue* current_shelf_prefs =
prefs->GetDictionary(prefs::kShelfPreferences);
DCHECK(current_shelf_prefs);
std::string display_key = base::Int64ToString(display_id);
const base::DictionaryValue* current_display_prefs = nullptr;
std::string current_value;
if (current_shelf_prefs->GetDictionary(display_key, &current_display_prefs) &&
current_display_prefs->GetString(pref_key, &current_value) &&
current_value == value) {
return;
}
DictionaryPrefUpdate update(prefs, prefs::kShelfPreferences);
base::DictionaryValue* shelf_prefs = update.Get();
base::DictionaryValue* display_prefs = nullptr;
if (!shelf_prefs->GetDictionary(display_key, &display_prefs)) {
display_prefs = new base::DictionaryValue();
shelf_prefs->Set(display_key, display_prefs);
}
display_prefs->SetStringWithoutPathExpansion(pref_key, value);
}
ShelfAlignment AlignmentFromPref(const std::string& value) {
if (value == kShelfAlignmentLeft)
return SHELF_ALIGNMENT_LEFT;
else if (value == kShelfAlignmentRight)
return SHELF_ALIGNMENT_RIGHT;
// Default to bottom.
return SHELF_ALIGNMENT_BOTTOM;
}
const char* AlignmentToPref(ShelfAlignment alignment) {
switch (alignment) {
case SHELF_ALIGNMENT_BOTTOM:
return kShelfAlignmentBottom;
case SHELF_ALIGNMENT_LEFT:
return kShelfAlignmentLeft;
case SHELF_ALIGNMENT_RIGHT:
return kShelfAlignmentRight;
case SHELF_ALIGNMENT_BOTTOM_LOCKED:
// This should not be a valid preference option for now. We only want to
// lock the shelf during login or when adding a user.
return nullptr;
}
NOTREACHED();
return nullptr;
}
ShelfAutoHideBehavior AutoHideBehaviorFromPref(const std::string& value) {
// Note: To maintain sync compatibility with old images of chrome/chromeos
// the set of values that may be encountered includes the now-extinct
// "Default" as well as "Never" and "Always", "Default" should now
// be treated as "Never" (http://crbug.com/146773).
if (value == kShelfAutoHideBehaviorAlways)
return SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS;
return SHELF_AUTO_HIDE_BEHAVIOR_NEVER;
}
const char* AutoHideBehaviorToPref(ShelfAutoHideBehavior behavior) {
switch (behavior) {
case SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS:
return kShelfAutoHideBehaviorAlways;
case SHELF_AUTO_HIDE_BEHAVIOR_NEVER:
return kShelfAutoHideBehaviorNever;
case SHELF_AUTO_HIDE_ALWAYS_HIDDEN:
// This should not be a valid preference option for now. We only want to
// completely hide it when we run in app mode - or while we temporarily
// hide the shelf as part of an animation (e.g. the multi user change).
return nullptr;
}
NOTREACHED();
return nullptr;
}
bool IsAppIdArcPackage(const std::string& app_id) {
return app_id.find('.') != app_id.npos;
}
std::vector<std::string> GetActivitiesForPackage(
const std::string& package,
const std::vector<std::string>& all_arc_app_ids,
const ArcAppListPrefs& app_list_pref) {
std::vector<std::string> activities;
for (const std::string& app_id : all_arc_app_ids) {
const std::unique_ptr<ArcAppListPrefs::AppInfo> app_info =
app_list_pref.GetApp(app_id);
if (app_info->package_name == package) {
activities.push_back(app_info->activity);
}
}
return activities;
}
// If no user-set value exists at |local_path|, the value from |synced_path| is
// copied to |local_path|.
void PropagatePrefToLocalIfNotSet(
sync_preferences::PrefServiceSyncable* pref_service,
const char* local_path,
const char* synced_path) {
if (!pref_service->FindPreference(local_path)->HasUserSetting())
pref_service->SetString(local_path, pref_service->GetString(synced_path));
}
std::vector<AppLaunchId> AppIdsToAppLaunchIds(
const std::vector<std::string> app_ids) {
std::vector<AppLaunchId> app_launch_ids(app_ids.size(), AppLaunchId());
for (size_t i = 0; i < app_ids.size(); ++i)
app_launch_ids[i] = AppLaunchId(app_ids[i]);
return app_launch_ids;
}
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);
}
};
// Helper class to keep apps in order of appearance and to provide fast way
// to check if app exists in the list.
class AppTracker {
public:
bool HasApp(const std::string& app_id) const {
return app_set_.find(app_id) != app_set_.end();
}
void AddApp(const std::string& app_id) {
if (HasApp(app_id))
return;
app_list_.push_back(app_id);
app_set_.insert(app_id);
}
void MaybeAddApp(const std::string& app_id,
const LauncherControllerHelper* helper,
bool check_for_valid_app) {
DCHECK_NE(kPinnedAppsPlaceholder, app_id);
if (check_for_valid_app && !helper->IsValidIDForCurrentUser(app_id)) {
return;
}
AddApp(app_id);
}
void MaybeAddAppFromPref(const base::DictionaryValue* app_pref,
const LauncherControllerHelper* helper,
bool check_for_valid_app) {
std::string app_id;
if (!app_pref->GetString(kPinnedAppsPrefAppIDPath, &app_id)) {
LOG(ERROR) << "Cannot get app id from app pref entry.";
return;
}
if (app_id == kPinnedAppsPlaceholder)
return;
bool pinned_by_policy = false;
if (app_pref->GetBoolean(kPinnedAppsPrefPinnedByPolicy,
&pinned_by_policy) &&
pinned_by_policy) {
return;
}
MaybeAddApp(app_id, helper, check_for_valid_app);
}
const std::vector<std::string>& app_list() const { return app_list_; }
private:
std::vector<std::string> app_list_;
std::set<std::string> app_set_;
};
} // namespace
const char kPinnedAppsPrefAppIDPath[] = "id";
const char kPinnedAppsPrefPinnedByPolicy[] = "pinned_by_policy";
const char kPinnedAppsPlaceholder[] = "AppShelfIDPlaceholder--------";
const char kShelfAutoHideBehaviorAlways[] = "Always";
const char kShelfAutoHideBehaviorNever[] = "Never";
const char kShelfAlignmentBottom[] = "Bottom";
const char kShelfAlignmentLeft[] = "Left";
const char kShelfAlignmentRight[] = "Right";
void RegisterChromeLauncherUserPrefs(
user_prefs::PrefRegistrySyncable* registry) {
// TODO: If we want to support multiple profiles this will likely need to be
// pushed to local state and we'll need to track profile per item.
registry->RegisterIntegerPref(
prefs::kShelfChromeIconIndex,
0,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
registry->RegisterListPref(prefs::kPinnedLauncherApps,
CreateDefaultPinnedAppsList(),
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
registry->RegisterListPref(prefs::kPolicyPinnedLauncherApps);
registry->RegisterStringPref(prefs::kShelfAutoHideBehavior,
kShelfAutoHideBehaviorNever,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
registry->RegisterStringPref(prefs::kShelfAutoHideBehaviorLocal,
std::string());
registry->RegisterStringPref(prefs::kShelfAlignment,
kShelfAlignmentBottom,
user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
registry->RegisterStringPref(prefs::kShelfAlignmentLocal, std::string());
registry->RegisterDictionaryPref(prefs::kShelfPreferences);
registry->RegisterIntegerPref(prefs::kLogoutDialogDurationMs, 20000);
registry->RegisterBooleanPref(prefs::kShowLogoutButtonInTray, false);
}
ShelfAutoHideBehavior GetShelfAutoHideBehaviorPref(PrefService* prefs,
int64_t display_id) {
DCHECK_NE(display_id, display::kInvalidDisplayId);
// Don't show the shelf in app mode.
if (chrome::IsRunningInAppMode())
return SHELF_AUTO_HIDE_ALWAYS_HIDDEN;
// See comment in |kShelfAlignment| as to why we consider two prefs.
return AutoHideBehaviorFromPref(
GetPerDisplayPref(prefs, display_id, prefs::kShelfAutoHideBehaviorLocal,
prefs::kShelfAutoHideBehavior));
}
void SetShelfAutoHideBehaviorPref(PrefService* prefs,
int64_t display_id,
ShelfAutoHideBehavior behavior) {
DCHECK_NE(display_id, display::kInvalidDisplayId);
const char* value = AutoHideBehaviorToPref(behavior);
if (!value)
return;
SetPerDisplayPref(prefs, display_id, prefs::kShelfAutoHideBehavior, value);
if (display_id == display::Screen::GetScreen()->GetPrimaryDisplay().id()) {
// See comment in |kShelfAlignment| about why we have two prefs here.
prefs->SetString(prefs::kShelfAutoHideBehaviorLocal, value);
prefs->SetString(prefs::kShelfAutoHideBehavior, value);
}
}
ShelfAlignment GetShelfAlignmentPref(PrefService* prefs, int64_t display_id) {
DCHECK_NE(display_id, display::kInvalidDisplayId);
// See comment in |kShelfAlignment| as to why we consider two prefs.
return AlignmentFromPref(GetPerDisplayPref(
prefs, display_id, prefs::kShelfAlignmentLocal, prefs::kShelfAlignment));
}
void SetShelfAlignmentPref(PrefService* prefs,
int64_t display_id,
ShelfAlignment alignment) {
DCHECK_NE(display_id, display::kInvalidDisplayId);
const char* value = AlignmentToPref(alignment);
if (!value)
return;
SetPerDisplayPref(prefs, display_id, prefs::kShelfAlignment, value);
if (display_id == display::Screen::GetScreen()->GetPrimaryDisplay().id()) {
// See comment in |kShelfAlignment| as to why we consider two prefs.
prefs->SetString(prefs::kShelfAlignmentLocal, value);
prefs->SetString(prefs::kShelfAlignment, value);
}
}
// Helper that extracts app list from policy preferences.
void GetAppsPinnedByPolicy(const PrefService* prefs,
const LauncherControllerHelper* helper,
bool check_for_valid_app,
AppTracker* apps) {
DCHECK(apps);
DCHECK(apps->app_list().empty());
const auto* policy_apps = prefs->GetList(prefs::kPolicyPinnedLauncherApps);
if (!policy_apps)
return;
// Obtain here all ids of ARC apps because it takes linear time, and getting
// them in the loop bellow would lead to quadratic complexity.
const ArcAppListPrefs* const arc_app_list_pref = helper->GetArcAppListPrefs();
const std::vector<std::string> all_arc_app_ids(
arc_app_list_pref ? arc_app_list_pref->GetAppIds()
: std::vector<std::string>());
std::string app_id;
for (size_t i = 0; i < policy_apps->GetSize(); ++i) {
const base::DictionaryValue* dictionary = nullptr;
if (!policy_apps->GetDictionary(i, &dictionary) ||
!dictionary->GetString(kPinnedAppsPrefAppIDPath, &app_id)) {
LOG(ERROR) << "Cannot extract policy app info from prefs.";
continue;
}
if (IsAppIdArcPackage(app_id)) {
if (!arc_app_list_pref)
continue;
// We are dealing with package name, not with 32 characters ID.
const std::string& arc_package = app_id;
const std::vector<std::string> activities = GetActivitiesForPackage(
arc_package, all_arc_app_ids, *arc_app_list_pref);
for (const auto& activity : activities) {
const std::string arc_app_id =
ArcAppListPrefs::GetAppId(arc_package, activity);
apps->MaybeAddApp(arc_app_id, helper, check_for_valid_app);
}
} else {
apps->MaybeAddApp(app_id, helper, check_for_valid_app);
}
}
}
std::vector<std::string> GetPinnedAppsFromPrefsLegacy(
const PrefService* prefs,
const LauncherControllerHelper* helper,
bool check_for_valid_app) {
// 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_apps = prefs->GetList(prefs::kPinnedLauncherApps);
// 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_apps->GetSize(),
prefs->GetInteger(prefs::kShelfChromeIconIndex)));
// Check if Chrome is in either of the the preferences lists.
std::unique_ptr<base::Value> chrome_app(
CreateAppDict(extension_misc::kChromeAppId));
AppTracker apps;
GetAppsPinnedByPolicy(prefs, helper, check_for_valid_app, &apps);
std::string app_id;
for (size_t i = 0; i < pinned_apps->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.
if (i == chrome_icon_index)
apps.AddApp(extension_misc::kChromeAppId);
const base::DictionaryValue* app_pref = nullptr;
if (!pinned_apps->GetDictionary(i, &app_pref)) {
LOG(ERROR) << "There is no dictionary for app entry.";
continue;
}
apps.MaybeAddAppFromPref(app_pref, helper, check_for_valid_app);
}
// If not added yet, the chrome item will be the last item in the list.
apps.AddApp(extension_misc::kChromeAppId);
return apps.app_list();
}
// static
std::unique_ptr<ChromeLauncherPrefsObserver>
ChromeLauncherPrefsObserver::CreateIfNecessary(Profile* profile) {
sync_preferences::PrefServiceSyncable* prefs =
PrefServiceSyncableFromProfile(profile);
if (!prefs->FindPreference(prefs::kShelfAlignmentLocal)->HasUserSetting() ||
!prefs->FindPreference(prefs::kShelfAutoHideBehaviorLocal)
->HasUserSetting()) {
return base::WrapUnique(new ChromeLauncherPrefsObserver(prefs));
}
return nullptr;
}
ChromeLauncherPrefsObserver::~ChromeLauncherPrefsObserver() {
prefs_->RemoveObserver(this);
}
ChromeLauncherPrefsObserver::ChromeLauncherPrefsObserver(
sync_preferences::PrefServiceSyncable* prefs)
: prefs_(prefs) {
// This causes OnIsSyncingChanged to be called when the value of
// PrefService::IsSyncing() changes.
prefs_->AddObserver(this);
}
void ChromeLauncherPrefsObserver::OnIsSyncingChanged() {
// If prefs have synced, copy the values from |synced_path| to |local_path|
// if the local values haven't already been set.
if (!prefs_->IsSyncing())
return;
PropagatePrefToLocalIfNotSet(prefs_, prefs::kShelfAlignmentLocal,
prefs::kShelfAlignment);
PropagatePrefToLocalIfNotSet(prefs_, prefs::kShelfAutoHideBehaviorLocal,
prefs::kShelfAutoHideBehavior);
prefs_->RemoveObserver(this);
}
// Helper to create pin position that stays before any synced app, even if
// app is not currently visible on a device.
syncer::StringOrdinal GetFirstPinPosition(Profile* profile) {
syncer::StringOrdinal position;
app_list::AppListSyncableService* app_service =
app_list::AppListSyncableServiceFactory::GetForProfile(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 visible on a device.
syncer::StringOrdinal GetLastPinPosition(Profile* profile) {
syncer::StringOrdinal position;
app_list::AppListSyncableService* app_service =
app_list::AppListSyncableServiceFactory::GetForProfile(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> ImportLegacyPinnedApps(
const PrefService* prefs,
LauncherControllerHelper* helper) {
const std::vector<std::string> legacy_pins_all =
GetPinnedAppsFromPrefsLegacy(prefs, helper, false);
DCHECK(!legacy_pins_all.empty());
app_list::AppListSyncableService* app_service =
app_list::AppListSyncableServiceFactory::GetForProfile(helper->profile());
std::vector<std::string> legacy_pins_valid;
syncer::StringOrdinal last_position =
syncer::StringOrdinal::CreateInitialOrdinal();
// Convert to sync item record.
for (const auto& app_id : legacy_pins_all) {
DCHECK_NE(kPinnedAppsPlaceholder, app_id);
app_service->SetPinPosition(app_id, last_position);
last_position = last_position.CreateAfter();
if (helper->IsValidIDForCurrentUser(app_id))
legacy_pins_valid.push_back(app_id);
}
// 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;
app_service->SetPinPosition(app_id, last_position);
last_position = last_position.CreateAfter();
}
return legacy_pins_valid;
}
std::vector<AppLaunchId> GetPinnedAppsFromPrefs(
const PrefService* prefs,
LauncherControllerHelper* helper) {
app_list::AppListSyncableService* app_service =
app_list::AppListSyncableServiceFactory::GetForProfile(helper->profile());
// Some unit tests may not have it or service may not be initialized.
if (!app_service || !app_service->IsInitialized())
return std::vector<AppLaunchId>();
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 Chrome browser pin info.
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 currently do not exist on device.
if (sync_peer.first != extension_misc::kChromeAppId &&
!helper->IsValidIDForCurrentUser(sync_peer.first)) {
continue;
}
pin_infos.push_back(
PinInfo(sync_peer.first, sync_peer.second->item_pin_ordinal));
}
if (first_run) {
// Return default apps in case profile is not synced yet.
sync_preferences::PrefServiceSyncable* const pref_service_syncable =
PrefServiceSyncableFromProfile(helper->profile());
if (!pref_service_syncable->IsSyncing())
return AppIdsToAppLaunchIds(
GetPinnedAppsFromPrefsLegacy(prefs, helper, true));
// We need to import legacy pins model and convert it to sync based
// model.
return AppIdsToAppLaunchIds(ImportLegacyPinnedApps(prefs, helper));
}
// Sort pins according their ordinals.
std::sort(pin_infos.begin(), pin_infos.end(), ComparePinInfo());
AppTracker policy_apps;
GetAppsPinnedByPolicy(prefs, helper, true, &policy_apps);
// Pinned by policy apps appear first, if they were not shown before.
syncer::StringOrdinal front_position = GetFirstPinPosition(helper->profile());
std::vector<std::string>::const_reverse_iterator it;
for (it = policy_apps.app_list().rbegin();
it != policy_apps.app_list().rend(); ++it) {
const std::string& app_id = *it;
if (app_id == kPinnedAppsPlaceholder)
continue;
// Check if we already processed current app.
if (app_service->GetPinPosition(app_id).IsValid())
continue;
// Now move it to the front.
pin_infos.insert(pin_infos.begin(), PinInfo(*it, front_position));
app_service->SetPinPosition(app_id, front_position);
front_position = front_position.CreateBefore();
}
// Now insert Chrome browser app if needed.
if (!app_service->GetPinPosition(extension_misc::kChromeAppId).IsValid()) {
pin_infos.insert(pin_infos.begin(),
PinInfo(extension_misc::kChromeAppId, front_position));
app_service->SetPinPosition(extension_misc::kChromeAppId, front_position);
}
if (helper->IsValidIDForCurrentUser(ArcSupportHost::kHostAppId)) {
if (!app_service->GetSyncItem(ArcSupportHost::kHostAppId)) {
const syncer::StringOrdinal arc_host_position =
GetLastPinPosition(helper->profile());
pin_infos.insert(pin_infos.begin(),
PinInfo(ArcSupportHost::kHostAppId, arc_host_position));
app_service->SetPinPosition(ArcSupportHost::kHostAppId,
arc_host_position);
}
}
// Convert to AppLaunchId 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 AppIdsToAppLaunchIds(pins);
}
void RemovePinPosition(Profile* profile, const AppLaunchId& app_launch_id) {
DCHECK(profile);
const std::string& app_id = app_launch_id.app_id();
if (!app_launch_id.launch_id().empty()) {
VLOG(2) << "Syncing remove pin for '" << app_id
<< "' with non-empty launch id '" << app_launch_id.launch_id()
<< "' is not supported.";
return;
}
DCHECK(!app_id.empty());
app_list::AppListSyncableService* app_service =
app_list::AppListSyncableServiceFactory::GetForProfile(profile);
app_service->SetPinPosition(app_id, syncer::StringOrdinal());
}
void SetPinPosition(Profile* profile,
const AppLaunchId& app_launch_id,
const AppLaunchId& app_launch_id_before,
const std::vector<AppLaunchId>& app_launch_ids_after) {
DCHECK(profile);
const std::string& app_id = app_launch_id.app_id();
if (!app_launch_id.launch_id().empty()) {
VLOG(2) << "Syncing set pin for '" << app_id
<< "' with non-empty launch id '" << app_launch_id.launch_id()
<< "' is not supported.";
return;
}
const std::string& app_id_before = app_launch_id_before.app_id();
DCHECK(!app_id.empty());
DCHECK_NE(app_id, app_id_before);
app_list::AppListSyncableService* app_service =
app_list::AppListSyncableServiceFactory::GetForProfile(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;
for (const auto& app_launch_id_after : app_launch_ids_after) {
const std::string& app_id_after = app_launch_id_after.app_id();
DCHECK_NE(app_id_after, app_id);
DCHECK_NE(app_id_after, app_id_before);
syncer::StringOrdinal position = app_service->GetPinPosition(app_id_after);
DCHECK(position.IsValid());
if (!position.IsValid()) {
LOG(ERROR) << "Sync pin position was not found for " << app_id_after;
continue;
}
if (!position_before.IsValid() || !position.Equals(position_before)) {
position_after = position;
break;
}
}
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);
}
} // namespace launcher
} // namespace ash