#include "chrome/browser/ui/app_list/app_list_syncable_service.h"
#include "base/command_line.h"
+#include "chrome/browser/apps/drive/drive_app_provider.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/app_list/app_list_service.h"
#include "chrome/browser/ui/app_list/extension_app_item.h"
#include "chrome/browser/ui/app_list/extension_app_model_builder.h"
#include "chrome/browser/ui/host_desktop.h"
#include "chrome/common/chrome_switches.h"
+#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/grit/generated_resources.h"
#include "content/public/browser/notification_source.h"
#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/uninstall_reason.h"
+#include "extensions/common/constants.h"
#include "sync/api/sync_change_processor.h"
#include "sync/api/sync_data.h"
#include "sync/api/sync_merge_result.h"
#include "ui/app_list/app_list_folder_item.h"
#include "ui/app_list/app_list_item.h"
#include "ui/app_list/app_list_model.h"
+#include "ui/app_list/app_list_model_observer.h"
+#include "ui/app_list/app_list_switches.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if defined(OS_CHROMEOS)
+#include "chrome/browser/chromeos/file_manager/app_id.h"
+#include "chrome/browser/chromeos/genius_app/app_id.h"
+#endif
using syncer::SyncChange;
namespace {
-bool SyncAppListEnabled() {
- return CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableSyncAppList);
-}
+const char kOemFolderId[] = "ddb1da55-d478-4243-8642-56d3041f0263";
void UpdateSyncItemFromSync(const sync_pb::AppListSpecifics& specifics,
AppListSyncableService::SyncItem* item) {
AppListSyncableService::SyncItem* sync_item) {
DCHECK_EQ(sync_item->item_id, app_item->id());
bool changed = false;
- if (sync_item->item_name != app_item->title()) {
- sync_item->item_name = app_item->title();
+ if (app_list::switches::IsFolderUIEnabled() &&
+ sync_item->parent_id != app_item->folder_id()) {
+ sync_item->parent_id = app_item->folder_id();
+ changed = true;
+ }
+ if (sync_item->item_name != app_item->name()) {
+ sync_item->item_name = app_item->name();
changed = true;
}
if (!sync_item->item_ordinal.IsValid() ||
sync_item->item_ordinal = app_item->position();
changed = true;
}
- // TODO(stevenjb): Set parent_id and page_ordinal.
+ // TODO(stevenjb): Set page_ordinal.
return changed;
}
}
bool AppIsDefault(ExtensionService* service, const std::string& id) {
- return service && service->extension_prefs()->WasInstalledByDefault(id);
+ return service && extensions::ExtensionPrefs::Get(service->profile())
+ ->WasInstalledByDefault(id);
+}
+
+bool IsUnRemovableDefaultApp(const std::string& id) {
+ if (id == extension_misc::kChromeAppId ||
+ id == extensions::kWebStoreAppId)
+ return true;
+#if defined(OS_CHROMEOS)
+ if (id == file_manager::kFileManagerAppId || id == genius_app::kGeniusAppId)
+ return true;
+#endif
+ return false;
}
void UninstallExtension(ExtensionService* service, const std::string& id) {
- if (service && service->GetInstalledExtension(id))
- service->UninstallExtension(id, false, NULL);
+ if (service && service->GetInstalledExtension(id)) {
+ service->UninstallExtension(id,
+ extensions::UNINSTALL_REASON_SYNC,
+ base::Bind(&base::DoNothing),
+ NULL);
+ }
}
bool GetAppListItemType(AppListItem* item,
AppListSyncableService::SyncItem::~SyncItem() {
}
-// AppListSyncableService::ItemListObserver
+// AppListSyncableService::ModelObserver
-class AppListSyncableService::ItemListObserver
- : public AppListItemListObserver {
+class AppListSyncableService::ModelObserver : public AppListModelObserver {
public:
- explicit ItemListObserver(AppListSyncableService* owner) : owner_(owner) {
- owner_->model()->item_list()->AddObserver(this);
+ explicit ModelObserver(AppListSyncableService* owner)
+ : owner_(owner),
+ adding_item_(NULL) {
+ DVLOG(2) << owner_ << ": ModelObserver Added";
+ owner_->model()->AddObserver(this);
}
- virtual ~ItemListObserver() {
- owner_->model()->item_list()->RemoveObserver(this);
+ virtual ~ModelObserver() {
+ owner_->model()->RemoveObserver(this);
+ DVLOG(2) << owner_ << ": ModelObserver Removed";
}
private:
- // AppListItemListObserver
- virtual void OnListItemAdded(size_t index, AppListItem* item) OVERRIDE {
+ // AppListModelObserver
+ virtual void OnAppListItemAdded(AppListItem* item) OVERRIDE {
+ DCHECK(!adding_item_);
+ adding_item_ = item; // Ignore updates while adding an item.
+ VLOG(2) << owner_ << " OnAppListItemAdded: " << item->ToDebugString();
owner_->AddOrUpdateFromSyncItem(item);
+ adding_item_ = NULL;
}
- virtual void OnListItemRemoved(size_t index, AppListItem* item) OVERRIDE {
+ virtual void OnAppListItemWillBeDeleted(AppListItem* item) OVERRIDE {
+ DCHECK(!adding_item_);
+ VLOG(2) << owner_ << " OnAppListItemDeleted: " << item->ToDebugString();
+ // Don't sync folder removal in case the folder still exists on another
+ // device (e.g. with device specific items in it). Empty folders will be
+ // deleted when the last item is removed (in PruneEmptySyncFolders()).
+ if (item->GetItemType() == AppListFolderItem::kItemType)
+ return;
owner_->RemoveSyncItem(item->id());
}
- virtual void OnListItemMoved(size_t from_index,
- size_t to_index,
- AppListItem* item) OVERRIDE {
+ virtual void OnAppListItemUpdated(AppListItem* item) OVERRIDE {
+ if (adding_item_) {
+ // Adding an item may trigger update notifications which should be
+ // ignored.
+ DCHECK_EQ(adding_item_, item);
+ return;
+ }
+ VLOG(2) << owner_ << " OnAppListItemUpdated: " << item->ToDebugString();
owner_->UpdateSyncItem(item);
}
AppListSyncableService* owner_;
+ AppListItem* adding_item_; // Unowned pointer to item being added.
- DISALLOW_COPY_AND_ASSIGN(ItemListObserver);
+ DISALLOW_COPY_AND_ASSIGN(ModelObserver);
};
// AppListSyncableService
extensions::ExtensionSystem* extension_system)
: profile_(profile),
extension_system_(extension_system),
- model_(new AppListModel) {
- if (!extension_system || !extension_system->extension_service()) {
- LOG(WARNING) << "AppListSyncableService created with no ExtensionService";
+ model_(new AppListModel),
+ initial_sync_data_processed_(false),
+ first_app_list_sync_(true) {
+ if (!extension_system) {
+ LOG(ERROR) << "AppListSyncableService created with no ExtensionSystem";
return;
}
- if (SyncAppListEnabled())
- item_list_observer_.reset(new ItemListObserver(this));
+ oem_folder_name_ =
+ l10n_util::GetStringUTF8(IDS_APP_LIST_OEM_DEFAULT_FOLDER_NAME);
- if (extension_system->extension_service()->is_ready()) {
+ // Note: model_observer_ is constructed after the initial sync changes are
+ // received in MergeDataAndStartSyncing(). Changes to the model before that
+ // will be synced after the initial sync occurs.
+ if (extension_system->extension_service() &&
+ extension_system->extension_service()->is_ready()) {
BuildModel();
return;
}
// The extensions for this profile have not yet all been loaded.
- registrar_.Add(this, chrome::NOTIFICATION_EXTENSIONS_READY,
+ registrar_.Add(this,
+ extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
content::Source<Profile>(profile));
}
AppListSyncableService::~AppListSyncableService() {
// Remove observers.
- item_list_observer_.reset();
+ model_observer_.reset();
STLDeleteContainerPairSecondPointers(sync_items_.begin(), sync_items_.end());
}
controller = service->GetControllerDelegate();
apps_builder_.reset(new ExtensionAppModelBuilder(controller));
DCHECK(profile_);
- // TODO(stevenjb): Correctly handle OTR profiles for Guest mode.
- if (!profile_->IsOffTheRecord() && SyncAppListEnabled()) {
- DVLOG(1) << this << ": AppListSyncableService: InitializeWithService.";
+ if (app_list::switches::IsAppListSyncEnabled()) {
+ VLOG(1) << this << ": AppListSyncableService: InitializeWithService.";
SyncStarted();
apps_builder_->InitializeWithService(this);
} else {
- DVLOG(1) << this << ": AppListSyncableService: InitializeWithProfile.";
+ VLOG(1) << this << ": AppListSyncableService: InitializeWithProfile.";
apps_builder_->InitializeWithProfile(profile_, model_.get());
}
+
+ if (app_list::switches::IsDriveAppsInAppListEnabled())
+ drive_app_provider_.reset(new DriveAppProvider(profile_));
+}
+
+void AppListSyncableService::ResetDriveAppProviderForTest() {
+ drive_app_provider_.reset();
+}
+
+void AppListSyncableService::Shutdown() {
+ // DriveAppProvider touches other KeyedServices in its dtor and needs be
+ // released in shutdown stage.
+ drive_app_provider_.reset();
}
void AppListSyncableService::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
- DCHECK_EQ(chrome::NOTIFICATION_EXTENSIONS_READY, type);
+ DCHECK_EQ(extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED, type);
DCHECK_EQ(profile_, content::Source<Profile>(source).ptr());
registrar_.RemoveAll();
BuildModel();
return NULL;
}
-void AppListSyncableService::AddItem(AppListItem* app_item) {
- SyncItem* sync_item = AddOrUpdateSyncItem(app_item);
+void AppListSyncableService::SetOemFolderName(const std::string& name) {
+ oem_folder_name_ = name;
+ AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId);
+ if (oem_folder)
+ model_->SetItemName(oem_folder, oem_folder_name_);
+}
+
+void AppListSyncableService::AddItem(scoped_ptr<AppListItem> app_item) {
+ SyncItem* sync_item = FindOrAddSyncItem(app_item.get());
if (!sync_item)
return; // Item is not valid.
- DVLOG(1) << this << ": AddItem: " << sync_item->ToString();
-
- // Add the item to the model if necessary.
- if (!model_->item_list()->FindItem(app_item->id()))
- model_->item_list()->AddItem(app_item);
- else
- model_->item_list()->SetItemPosition(app_item, sync_item->item_ordinal);
+ std::string folder_id;
+ if (app_list::switches::IsFolderUIEnabled()) {
+ if (AppIsOem(app_item->id())) {
+ folder_id = FindOrCreateOemFolder();
+ VLOG_IF(2, !folder_id.empty())
+ << this << ": AddItem to OEM folder: " << sync_item->ToString();
+ } else {
+ folder_id = sync_item->parent_id;
+ }
+ }
+ VLOG(2) << this << ": AddItem: " << sync_item->ToString()
+ << " Folder: '" << folder_id << "'";
+ model_->AddItemToFolder(app_item.Pass(), folder_id);
}
-AppListSyncableService::SyncItem* AppListSyncableService::AddOrUpdateSyncItem(
+AppListSyncableService::SyncItem* AppListSyncableService::FindOrAddSyncItem(
AppListItem* app_item) {
const std::string& item_id = app_item->id();
if (item_id.empty()) {
}
SyncItem* sync_item = FindSyncItem(item_id);
if (sync_item) {
- // If there is an existing, non-REMOVE_DEFAULT entry, update it.
+ // If there is an existing, non-REMOVE_DEFAULT entry, return it.
if (sync_item->item_type !=
sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
DVLOG(2) << this << ": AddItem already exists: " << sync_item->ToString();
- UpdateSyncItem(app_item);
return sync_item;
}
sync_pb::AppListSpecifics::AppListItemType type;
if (!GetAppListItemType(app_item, &type))
return NULL;
+ VLOG(2) << this << " CreateSyncItemFromAppItem:" << app_item->ToDebugString();
SyncItem* sync_item = CreateSyncItem(app_item->id(), type);
UpdateSyncItemFromAppItem(app_item, sync_item);
SendSyncChange(sync_item, SyncChange::ACTION_ADD);
}
void AppListSyncableService::AddOrUpdateFromSyncItem(AppListItem* app_item) {
+ // Do not create a sync item for the OEM folder here, do that in
+ // ResolveFolderPositions once the position has been resolved.
+ if (app_item->id() == kOemFolderId)
+ return;
+
SyncItem* sync_item = FindSyncItem(app_item->id());
if (sync_item) {
UpdateAppItemFromSyncItem(sync_item, app_item);
// installed as a Default app, uninstall the app instead of adding it.
if (sync_item->item_type == sync_pb::AppListSpecifics::TYPE_APP &&
AppIsDefault(extension_system_->extension_service(), item->id())) {
- DVLOG(1) << this << ": HandleDefaultApp: Uninstall: "
- << sync_item->ToString();
+ VLOG(2) << this << ": HandleDefaultApp: Uninstall: "
+ << sync_item->ToString();
UninstallExtension(extension_system_->extension_service(), item->id());
return true;
}
void AppListSyncableService::DeleteSyncItem(SyncItem* sync_item) {
if (SyncStarted()) {
- DVLOG(2) << this << " -> SYNC DELETE: " << sync_item->ToString();
+ VLOG(2) << this << " -> SYNC DELETE: " << sync_item->ToString();
SyncChange sync_change(FROM_HERE, SyncChange::ACTION_DELETE,
GetSyncDataFromSyncItem(sync_item));
sync_processor_->ProcessSyncChanges(
void AppListSyncableService::RemoveItem(const std::string& id) {
RemoveSyncItem(id);
- model_->item_list()->DeleteItem(id);
+ model_->DeleteItem(id);
+ PruneEmptySyncFolders();
+}
+
+void AppListSyncableService::UpdateItem(AppListItem* app_item) {
+ // Check to see if the item needs to be moved to/from the OEM folder.
+ if (!app_list::switches::IsFolderUIEnabled())
+ return;
+ bool is_oem = AppIsOem(app_item->id());
+ if (!is_oem && app_item->folder_id() == kOemFolderId)
+ model_->MoveItemToFolder(app_item, "");
+ else if (is_oem && app_item->folder_id() != kOemFolderId)
+ model_->MoveItemToFolder(app_item, kOemFolderId);
}
void AppListSyncableService::RemoveSyncItem(const std::string& id) {
- DVLOG(2) << this << ": RemoveSyncItem: " << id.substr(0, 8);
+ VLOG(2) << this << ": RemoveSyncItem: " << id.substr(0, 8);
SyncItemMap::iterator iter = sync_items_.find(id);
if (iter == sync_items_.end()) {
DVLOG(2) << this << " : RemoveSyncItem: No Item.";
AppIsDefault(extension_system_->extension_service(), id)) {
// This is a Default app; update the entry to a REMOVE_DEFAULT entry. This
// will overwrite any existing entry for the item.
- DVLOG(2) << this << " -> SYNC UPDATE: REMOVE_DEFAULT: "
- << sync_item->item_id;
+ VLOG(2) << this << " -> SYNC UPDATE: REMOVE_DEFAULT: "
+ << sync_item->item_id;
sync_item->item_type = sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP;
SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
return;
DeleteSyncItem(sync_item);
}
+void AppListSyncableService::ResolveFolderPositions() {
+ if (!app_list::switches::IsFolderUIEnabled())
+ return;
+
+ VLOG(1) << "ResolveFolderPositions.";
+ for (SyncItemMap::iterator iter = sync_items_.begin();
+ iter != sync_items_.end(); ++iter) {
+ SyncItem* sync_item = iter->second;
+ if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
+ continue;
+ AppListItem* app_item = model_->FindItem(sync_item->item_id);
+ if (!app_item)
+ continue;
+ UpdateAppItemFromSyncItem(sync_item, app_item);
+ }
+
+ // Move the OEM folder if one exists and we have not synced its position.
+ AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId);
+ if (oem_folder && !FindSyncItem(kOemFolderId)) {
+ model_->SetItemPosition(oem_folder, GetOemFolderPos());
+ VLOG(1) << "Creating new OEM folder sync item: "
+ << oem_folder->position().ToDebugString();
+ CreateSyncItemFromAppItem(oem_folder);
+ }
+}
+
+void AppListSyncableService::PruneEmptySyncFolders() {
+ if (!app_list::switches::IsFolderUIEnabled())
+ return;
+
+ std::set<std::string> parent_ids;
+ for (SyncItemMap::iterator iter = sync_items_.begin();
+ iter != sync_items_.end(); ++iter) {
+ parent_ids.insert(iter->second->parent_id);
+ }
+ for (SyncItemMap::iterator iter = sync_items_.begin();
+ iter != sync_items_.end(); ) {
+ SyncItem* sync_item = (iter++)->second;
+ if (sync_item->item_type != sync_pb::AppListSpecifics::TYPE_FOLDER)
+ continue;
+ if (!ContainsKey(parent_ids, sync_item->item_id))
+ DeleteSyncItem(sync_item);
+ }
+}
+
// AppListSyncableService syncer::SyncableService
syncer::SyncMergeResult AppListSyncableService::MergeDataAndStartSyncing(
sync_processor_ = sync_processor.Pass();
sync_error_handler_ = error_handler.Pass();
+ if (switches::IsFolderUIEnabled())
+ model_->SetFoldersEnabled(true);
syncer::SyncMergeResult result = syncer::SyncMergeResult(type);
result.set_num_items_before_association(sync_items_.size());
- DVLOG(1) << this << ": MergeDataAndStartSyncing: "
- << initial_sync_data.size();
+ VLOG(1) << this << ": MergeDataAndStartSyncing: "
+ << initial_sync_data.size();
// Copy all sync items to |unsynced_items|.
std::set<std::string> unsynced_items;
for (syncer::SyncDataList::const_iterator iter = initial_sync_data.begin();
iter != initial_sync_data.end(); ++iter) {
const syncer::SyncData& data = *iter;
- DVLOG(2) << this << " Initial Sync Item: "
- << data.GetSpecifics().app_list().item_id()
- << " Type: " << data.GetSpecifics().app_list().item_type();
+ const std::string& item_id = data.GetSpecifics().app_list().item_id();
+ const sync_pb::AppListSpecifics& specifics = data.GetSpecifics().app_list();
+ DVLOG(2) << this << " Initial Sync Item: " << item_id
+ << " Type: " << specifics.item_type();
DCHECK_EQ(syncer::APP_LIST, data.GetDataType());
- if (ProcessSyncItemSpecifics(data.GetSpecifics().app_list()))
+ if (ProcessSyncItemSpecifics(specifics))
++new_items;
else
++updated_items;
- unsynced_items.erase(data.GetSpecifics().app_list().item_id());
+ if (specifics.item_type() != sync_pb::AppListSpecifics::TYPE_FOLDER &&
+ !IsUnRemovableDefaultApp(item_id) &&
+ !AppIsOem(item_id) &&
+ !AppIsDefault(extension_system_->extension_service(), item_id)) {
+ VLOG(2) << "Syncing non-default item: " << item_id;
+ first_app_list_sync_ = false;
+ }
+ unsynced_items.erase(item_id);
}
-
result.set_num_items_after_association(sync_items_.size());
result.set_num_items_added(new_items);
result.set_num_items_deleted(0);
result.set_num_items_modified(updated_items);
+ // Initial sync data has been processed, it is safe now to add new sync items.
+ initial_sync_data_processed_ = true;
+
// Send unsynced items. Does not affect |result|.
syncer::SyncChangeList change_list;
for (std::set<std::string>::iterator iter = unsynced_items.begin();
iter != unsynced_items.end(); ++iter) {
SyncItem* sync_item = FindSyncItem(*iter);
- DVLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
+ // Sync can cause an item to change folders, causing an unsynced folder
+ // item to be removed.
+ if (!sync_item)
+ continue;
+ VLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
change_list.push_back(SyncChange(FROM_HERE, SyncChange::ACTION_ADD,
GetSyncDataFromSyncItem(sync_item)));
}
sync_processor_->ProcessSyncChanges(FROM_HERE, change_list);
+ // Adding items may have created folders without setting their positions
+ // since we haven't started observing the item list yet. Resolve those.
+ ResolveFolderPositions();
+
+ // Start observing app list model changes.
+ model_observer_.reset(new ModelObserver(this));
+
return result;
}
sync_processor_.reset();
sync_error_handler_.reset();
+ model_->SetFoldersEnabled(false);
}
syncer::SyncDataList AppListSyncableService::GetAllSyncData(
syncer::ModelType type) const {
DCHECK_EQ(syncer::APP_LIST, type);
- DVLOG(1) << this << ": GetAllSyncData: " << sync_items_.size();
+ VLOG(1) << this << ": GetAllSyncData: " << sync_items_.size();
syncer::SyncDataList list;
for (SyncItemMap::const_iterator iter = sync_items_.begin();
iter != sync_items_.end(); ++iter) {
- DVLOG(2) << this << " -> SYNC: " << iter->second->ToString();
+ VLOG(2) << this << " -> SYNC: " << iter->second->ToString();
list.push_back(GetSyncDataFromSyncItem(iter->second));
}
return list;
syncer::APP_LIST);
}
- DVLOG(1) << this << ": ProcessSyncChanges: " << change_list.size();
+ // Don't observe the model while processing incoming sync changes.
+ model_observer_.reset();
+
+ VLOG(1) << this << ": ProcessSyncChanges: " << change_list.size();
for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
iter != change_list.end(); ++iter) {
const SyncChange& change = *iter;
- DVLOG(2) << this << " Change: "
- << change.sync_data().GetSpecifics().app_list().item_id()
- << " (" << change.change_type() << ")";
+ VLOG(2) << this << " Change: "
+ << change.sync_data().GetSpecifics().app_list().item_id()
+ << " (" << change.change_type() << ")";
if (change.change_type() == SyncChange::ACTION_ADD ||
change.change_type() == SyncChange::ACTION_UPDATE) {
ProcessSyncItemSpecifics(change.sync_data().GetSpecifics().app_list());
LOG(ERROR) << "Invalid sync change";
}
}
+
+ // Continue observing app list model changes.
+ model_observer_.reset(new ModelObserver(this));
+
return syncer::SyncError();
}
if (sync_item->item_type == specifics.item_type()) {
UpdateSyncItemFromSync(specifics, sync_item);
ProcessExistingSyncItem(sync_item);
- DVLOG(2) << this << " <- SYNC UPDATE: " << sync_item->ToString();
+ VLOG(2) << this << " <- SYNC UPDATE: " << sync_item->ToString();
return false;
}
// Otherwise, one of the entries should be TYPE_REMOVE_DEFAULT_APP.
LOG(ERROR) << "Synced item type: " << specifics.item_type()
<< " != existing sync item type: " << sync_item->item_type
<< " Deleting item from model!";
- model_->item_list()->DeleteItem(item_id);
+ model_->DeleteItem(item_id);
}
- DVLOG(2) << this << " - ProcessSyncItem: Delete existing entry: "
- << sync_item->ToString();
+ VLOG(2) << this << " - ProcessSyncItem: Delete existing entry: "
+ << sync_item->ToString();
delete sync_item;
sync_items_.erase(item_id);
}
sync_item = CreateSyncItem(item_id, specifics.item_type());
UpdateSyncItemFromSync(specifics, sync_item);
ProcessNewSyncItem(sync_item);
- DVLOG(2) << this << " <- SYNC ADD: " << sync_item->ToString();
+ VLOG(2) << this << " <- SYNC ADD: " << sync_item->ToString();
return true;
}
void AppListSyncableService::ProcessNewSyncItem(SyncItem* sync_item) {
- DVLOG(2) << "ProcessNewSyncItem: " << sync_item->ToString();
+ VLOG(2) << "ProcessNewSyncItem: " << sync_item->ToString();
switch (sync_item->item_type) {
case sync_pb::AppListSpecifics::TYPE_APP: {
// New apps are added through ExtensionAppModelBuilder.
return;
}
case sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP: {
- DVLOG(1) << this << ": Uninstall: " << sync_item->ToString();
+ VLOG(1) << this << ": Uninstall: " << sync_item->ToString();
UninstallExtension(extension_system_->extension_service(),
sync_item->item_id);
return;
}
case sync_pb::AppListSpecifics::TYPE_FOLDER: {
- // TODO(stevenjb): Implement
- LOG(WARNING) << "TYPE_FOLDER not supported";
+ AppListItem* app_item = model_->FindItem(sync_item->item_id);
+ if (!app_item)
+ return; // Don't create new folders here, the model will do that.
+ UpdateAppItemFromSyncItem(sync_item, app_item);
return;
}
case sync_pb::AppListSpecifics::TYPE_URL: {
return;
}
}
- NOTREACHED() << "Unrecoginized sync item type: " << sync_item->ToString();
+ NOTREACHED() << "Unrecognized sync item type: " << sync_item->ToString();
}
void AppListSyncableService::ProcessExistingSyncItem(SyncItem* sync_item) {
sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP) {
return;
}
- DVLOG(2) << "ProcessExistingSyncItem: " << sync_item->ToString();
- AppListItem* app_item = model_->item_list()->FindItem(sync_item->item_id);
+ VLOG(2) << "ProcessExistingSyncItem: " << sync_item->ToString();
+ AppListItem* app_item = model_->FindItem(sync_item->item_id);
+ DVLOG(2) << " AppItem: " << app_item->ToDebugString();
if (!app_item) {
LOG(ERROR) << "Item not found in model: " << sync_item->ToString();
return;
}
+ // This is the only place where sync can cause an item to change folders.
+ if (app_list::switches::IsFolderUIEnabled() &&
+ app_item->folder_id() != sync_item->parent_id &&
+ !AppIsOem(app_item->id())) {
+ VLOG(2) << " Moving Item To Folder: " << sync_item->parent_id;
+ model_->MoveItemToFolder(app_item, sync_item->parent_id);
+ }
UpdateAppItemFromSyncItem(sync_item, app_item);
}
void AppListSyncableService::UpdateAppItemFromSyncItem(
const AppListSyncableService::SyncItem* sync_item,
AppListItem* app_item) {
+ VLOG(2) << this << " UpdateAppItemFromSyncItem: " << sync_item->ToString();
if (!app_item->position().Equals(sync_item->item_ordinal))
- model_->item_list()->SetItemPosition(app_item, sync_item->item_ordinal);
+ model_->SetItemPosition(app_item, sync_item->item_ordinal);
+ // Only update the item name if it is a Folder or the name is empty.
+ if (sync_item->item_name != app_item->name() &&
+ sync_item->item_id != kOemFolderId &&
+ (app_item->GetItemType() == AppListFolderItem::kItemType ||
+ app_item->name().empty())) {
+ model_->SetItemName(app_item, sync_item->item_name);
+ }
}
bool AppListSyncableService::SyncStarted() {
if (sync_processor_.get())
return true;
if (flare_.is_null()) {
- DVLOG(2) << this << ": SyncStarted: Flare.";
+ VLOG(1) << this << ": SyncStarted: Flare.";
flare_ = sync_start_util::GetFlareForSyncableService(profile_->GetPath());
flare_.Run(syncer::APP_LIST);
}
<< sync_item->ToString();
return;
}
+ if (!initial_sync_data_processed_ &&
+ sync_change_type == SyncChange::ACTION_ADD) {
+ // This can occur if an initial item is created before its folder item.
+ // A sync item should already exist for the folder, so we do not want to
+ // send an ADD event, since that would trigger a CHECK in the sync code.
+ DCHECK(sync_item->item_type == sync_pb::AppListSpecifics::TYPE_FOLDER);
+ DVLOG(2) << this << " - SendSyncChange: ADD before initial data processed: "
+ << sync_item->ToString();
+ return;
+ }
if (sync_change_type == SyncChange::ACTION_ADD)
- DVLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
+ VLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
else
- DVLOG(2) << this << " -> SYNC UPDATE: " << sync_item->ToString();
+ VLOG(2) << this << " -> SYNC UPDATE: " << sync_item->ToString();
SyncChange sync_change(FROM_HERE, sync_change_type,
GetSyncDataFromSyncItem(sync_item));
sync_processor_->ProcessSyncChanges(
LOG(ERROR) << "Delete AppList item with empty ID";
return;
}
- DVLOG(2) << this << ": DeleteSyncItemSpecifics: " << item_id.substr(0, 8);
+ VLOG(2) << this << ": DeleteSyncItemSpecifics: " << item_id.substr(0, 8);
SyncItemMap::iterator iter = sync_items_.find(item_id);
if (iter == sync_items_.end())
return;
sync_pb::AppListSpecifics::AppListItemType item_type =
iter->second->item_type;
- DVLOG(2) << this << " <- SYNC DELETE: " << iter->second->ToString();
+ VLOG(2) << this << " <- SYNC DELETE: " << iter->second->ToString();
delete iter->second;
sync_items_.erase(iter);
- if (item_type != sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP)
- model_->item_list()->DeleteItem(item_id);
+ // Only delete apps from the model. Folders will be deleted when all
+ // children have been deleted.
+ if (item_type == sync_pb::AppListSpecifics::TYPE_APP)
+ model_->DeleteItem(item_id);
+}
+
+std::string AppListSyncableService::FindOrCreateOemFolder() {
+ AppListFolderItem* oem_folder = model_->FindFolderItem(kOemFolderId);
+ if (!oem_folder) {
+ scoped_ptr<AppListFolderItem> new_folder(new AppListFolderItem(
+ kOemFolderId, AppListFolderItem::FOLDER_TYPE_OEM));
+ oem_folder = static_cast<AppListFolderItem*>(
+ model_->AddItem(new_folder.PassAs<app_list::AppListItem>()));
+ SyncItem* oem_sync_item = FindSyncItem(kOemFolderId);
+ if (oem_sync_item) {
+ VLOG(1) << "Creating OEM folder from existing sync item: "
+ << oem_sync_item->item_ordinal.ToDebugString();
+ model_->SetItemPosition(oem_folder, oem_sync_item->item_ordinal);
+ } else {
+ model_->SetItemPosition(oem_folder, GetOemFolderPos());
+ // Do not create a sync item for the OEM folder here, do it in
+ // ResolveFolderPositions() when the item position is finalized.
+ }
+ }
+ model_->SetItemName(oem_folder, oem_folder_name_);
+ return oem_folder->id();
+}
+
+syncer::StringOrdinal AppListSyncableService::GetOemFolderPos() {
+ VLOG(1) << "GetOemFolderPos: " << first_app_list_sync_;
+ if (!first_app_list_sync_) {
+ VLOG(1) << "Sync items exist, placing OEM folder at end.";
+ syncer::StringOrdinal last;
+ for (SyncItemMap::iterator iter = sync_items_.begin();
+ iter != sync_items_.end(); ++iter) {
+ SyncItem* sync_item = iter->second;
+ if (!last.IsValid() || sync_item->item_ordinal.GreaterThan(last))
+ last = sync_item->item_ordinal;
+ }
+ return last.CreateAfter();
+ }
+
+ // Place the OEM folder just after the web store, which should always be
+ // followed by a pre-installed app (e.g. Search), so the poosition should be
+ // stable. TODO(stevenjb): consider explicitly setting the OEM folder location
+ // along with the name in ServicesCustomizationDocument::SetOemFolderName().
+ AppListItemList* item_list = model_->top_level_item_list();
+ if (item_list->item_count() == 0)
+ return syncer::StringOrdinal();
+
+ size_t oem_index = 0;
+ for (; oem_index < item_list->item_count() - 1; ++oem_index) {
+ AppListItem* cur_item = item_list->item_at(oem_index);
+ if (cur_item->id() == extensions::kWebStoreAppId)
+ break;
+ }
+ syncer::StringOrdinal oem_ordinal;
+ AppListItem* prev = item_list->item_at(oem_index);
+ if (oem_index + 1 < item_list->item_count()) {
+ AppListItem* next = item_list->item_at(oem_index + 1);
+ oem_ordinal = prev->position().CreateBetween(next->position());
+ } else {
+ oem_ordinal = prev->position().CreateAfter();
+ }
+ VLOG(1) << "Placing OEM Folder at: " << oem_index
+ << " position: " << oem_ordinal.ToDebugString();
+ return oem_ordinal;
+}
+
+bool AppListSyncableService::AppIsOem(const std::string& id) {
+ if (!extension_system_->extension_service())
+ return false;
+ const extensions::Extension* extension =
+ extension_system_->extension_service()->GetExtensionById(id, true);
+ return extension && extension->was_installed_by_oem();
}
std::string AppListSyncableService::SyncItem::ToString() const {
} else {
res += " { " + item_name + " }";
res += " [" + item_ordinal.ToDebugString() + "]";
+ if (!parent_id.empty())
+ res += " <" + parent_id.substr(0, 8) + ">";
}
return res;
}