#include "base/command_line.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/common/chrome_switches.h"
#include "content/public/browser/notification_source.h"
#include "extensions/browser/extension_prefs.h"
+#include "extensions/browser/extension_system.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"
using syncer::SyncChange;
bool SyncAppListEnabled() {
return CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableSyncAppList);
+ ::switches::kEnableSyncAppList);
}
void UpdateSyncItemFromSync(const sync_pb::AppListSpecifics& specifics,
AppListSyncableService::SyncItem* sync_item) {
DCHECK_EQ(sync_item->item_id, app_item->id());
bool changed = false;
+ 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->title()) {
sync_item->item_name = app_item->title();
changed = true;
sync_item->item_ordinal = app_item->position();
changed = true;
}
- // TODO(stevenjb): Set parent_id and page_ordinal.
+ // TODO(stevenjb): Set page_ordinal.
return changed;
}
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) {
+ 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 {
+ DVLOG(2) << owner_ << " OnAppListItemAdded: " << item->ToDebugString();
owner_->AddOrUpdateFromSyncItem(item);
}
- virtual void OnListItemRemoved(size_t index, AppListItem* item) OVERRIDE {
+ virtual void OnAppListItemWillBeDeleted(AppListItem* item) OVERRIDE {
+ DVLOG(2) << owner_ << " OnAppListItemDeleted: " << item->ToDebugString();
owner_->RemoveSyncItem(item->id());
}
- virtual void OnListItemMoved(size_t from_index,
- size_t to_index,
- AppListItem* item) OVERRIDE {
+ virtual void OnAppListItemUpdated(AppListItem* item) OVERRIDE {
+ DVLOG(2) << owner_ << " OnAppListItemUpdated: " << item->ToDebugString();
owner_->UpdateSyncItem(item);
}
AppListSyncableService* owner_;
- DISALLOW_COPY_AND_ASSIGN(ItemListObserver);
+ DISALLOW_COPY_AND_ASSIGN(ModelObserver);
};
// AppListSyncableService
: profile_(profile),
extension_system_(extension_system),
model_(new AppListModel) {
- if (!extension_system || !extension_system->extension_service()) {
- LOG(WARNING) << "AppListSyncableService created with no ExtensionService";
+ if (!extension_system) {
+ LOG(ERROR) << "AppListSyncableService created with no ExtensionSystem";
return;
}
- if (SyncAppListEnabled())
- item_list_observer_.reset(new ItemListObserver(this));
-
- 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;
}
AppListSyncableService::~AppListSyncableService() {
// Remove observers.
- item_list_observer_.reset();
+ model_observer_.reset();
STLDeleteContainerPairSecondPointers(sync_items_.begin(), sync_items_.end());
}
return NULL;
}
-void AppListSyncableService::AddItem(AppListItem* app_item) {
- SyncItem* sync_item = AddOrUpdateSyncItem(app_item);
+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 = sync_item->parent_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;
}
void AppListSyncableService::RemoveItem(const std::string& id) {
RemoveSyncItem(id);
- model_->item_list()->DeleteItem(id);
+ model_->DeleteItem(id);
+ PruneEmptySyncFolders();
}
void AppListSyncableService::RemoveSyncItem(const std::string& id) {
DeleteSyncItem(sync_item);
}
+void AppListSyncableService::ResolveFolderPositions() {
+ if (!app_list::switches::IsFolderUIEnabled())
+ return;
+
+ 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);
+ }
+}
+
+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_->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;
}
syncer::APP_LIST);
}
+ // Don't observe the model while processing incoming sync changes.
+ model_observer_.reset();
+
DVLOG(1) << this << ": ProcessSyncChanges: " << change_list.size();
for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
iter != change_list.end(); ++iter) {
LOG(ERROR) << "Invalid sync change";
}
}
+
+ // Continue observing app list model changes.
+ model_observer_.reset(new ModelObserver(this));
+
return syncer::SyncError();
}
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();
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) {
return;
}
DVLOG(2) << "ProcessExistingSyncItem: " << sync_item->ToString();
- AppListItem* app_item = model_->item_list()->FindItem(sync_item->item_id);
+ 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) {
+ DVLOG(2) << " Moving Item To Folder: " << sync_item->parent_id;
+ model_->MoveItemToFolder(app_item, sync_item->parent_id);
+ }
UpdateAppItemFromSyncItem(sync_item, app_item);
}
const AppListSyncableService::SyncItem* sync_item,
AppListItem* app_item) {
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);
}
bool AppListSyncableService::SyncStarted() {
DVLOG(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::SyncItem::ToString() const {
} else {
res += " { " + item_name + " }";
res += " [" + item_ordinal.ToDebugString() + "]";
+ if (!parent_id.empty())
+ res += " <" + parent_id.substr(0, 8) + ">";
}
return res;
}