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

Unified Diff: chrome/browser/ui/app_list/app_list_syncable_service.cc

Issue 2416133002: Implement local storage for App List in case app sync is off. (Closed)
Patch Set: method renamed Created 4 years, 2 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/app_list/app_list_syncable_service.cc
diff --git a/chrome/browser/ui/app_list/app_list_syncable_service.cc b/chrome/browser/ui/app_list/app_list_syncable_service.cc
index f7dd4d5f3854935f6c59c8c5ac09d99c802d0088..2972882914d8d62f858e6b10bbdcfdc9d9a67d4e 100644
--- a/chrome/browser/ui/app_list/app_list_syncable_service.cc
+++ b/chrome/browser/ui/app_list/app_list_syncable_service.cc
@@ -20,7 +20,9 @@
#include "chrome/browser/ui/app_list/extension_app_model_builder.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension_constants.h"
+#include "chrome/common/pref_names.h"
#include "chrome/grit/generated_resources.h"
+#include "components/pref_registry/pref_registry_syncable.h"
#include "components/sync/model/sync_change_processor.h"
#include "components/sync/model/sync_data.h"
#include "components/sync/model/sync_merge_result.h"
@@ -29,6 +31,7 @@
#include "extensions/browser/extension_system.h"
#include "extensions/browser/uninstall_reason.h"
#include "extensions/common/constants.h"
+#include "extensions/common/one_shot_event.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"
@@ -53,6 +56,12 @@ namespace {
const char kOemFolderId[] = "ddb1da55-d478-4243-8642-56d3041f0263";
+const char kNameKey[] = "name";
+const char kParentIdKey[] = "parent_id";
+const char kPositionKey[] = "position";
+const char kPinPositionKey[] = "pin_position";
+const char kTypeKey[] = "type";
+
// Prefix for a sync id of a Drive app. Drive app ids are in a different
// format and have to be used because a Drive app could have only an URL
// without a matching Chrome app. To differentiate the Drive app id from
@@ -173,6 +182,33 @@ std::string GetDriveAppIdFromSyncId(const std::string& sync_id) {
return sync_id.substr(strlen(kDriveAppSyncIdPrefix));
}
+void RemoveSyncItemFromLocalStorage(Profile* profile,
+ const std::string& item_id) {
+ DictionaryPrefUpdate(profile->GetPrefs(), prefs::kAppListLocalState)->
+ Remove(item_id, nullptr);
+}
+
+void UpdateSyncItemInLocalStorage(
+ Profile* profile,
+ const AppListSyncableService::SyncItem* sync_item) {
+ DictionaryPrefUpdate pref_update(profile->GetPrefs(),
+ prefs::kAppListLocalState);
+ base::DictionaryValue* dict_item = nullptr;
+ if (!pref_update->GetDictionaryWithoutPathExpansion(sync_item->item_id,
+ &dict_item)) {
+ dict_item = new base::DictionaryValue();
+ pref_update->SetWithoutPathExpansion(sync_item->item_id, dict_item);
+ }
+
+ dict_item->SetString(kNameKey, sync_item->item_name);
+ dict_item->SetString(kParentIdKey, sync_item->parent_id);
+ dict_item->SetString(kPositionKey,sync_item->item_ordinal.IsValid() ?
+ sync_item->item_ordinal.ToInternalValue() : std::string());
+ dict_item->SetString(kPinPositionKey, sync_item->item_pin_ordinal.IsValid() ?
+ sync_item->item_pin_ordinal.ToInternalValue() : std::string());
+ dict_item->SetInteger(kTypeKey, static_cast<int>(sync_item->item_type));
+}
+
} // namespace
// AppListSyncableService::SyncItem
@@ -254,6 +290,12 @@ class AppListSyncableService::ModelObserver : public AppListModelObserver {
// AppListSyncableService
+// static
+void AppListSyncableService::RegisterProfilePrefs(
+ user_prefs::PrefRegistrySyncable* registry) {
+ registry->RegisterDictionaryPref(prefs::kAppListLocalState);
+}
+
AppListSyncableService::AppListSyncableService(
Profile* profile,
extensions::ExtensionSystem* extension_system)
@@ -261,7 +303,8 @@ AppListSyncableService::AppListSyncableService(
extension_system_(extension_system),
model_(new AppListModel),
initial_sync_data_processed_(false),
- first_app_list_sync_(true) {
+ first_app_list_sync_(true),
+ weak_ptr_factory_(this) {
if (!extension_system) {
LOG(ERROR) << "AppListSyncableService created with no ExtensionSystem";
return;
@@ -269,6 +312,19 @@ AppListSyncableService::AppListSyncableService(
oem_folder_name_ =
l10n_util::GetStringUTF8(IDS_APP_LIST_OEM_DEFAULT_FOLDER_NAME);
+
+ // TODO(khmel): Now we support persistent state of this service. It is
+ // possible to remove folder UI enabled check.
+ if (switches::IsFolderUIEnabled())
+ model_->SetFoldersEnabled(true);
+
+ if (IsExtensionServiceReady()) {
+ BuildModel();
+ } else {
+ extension_system_->ready().Post(
+ FROM_HERE, base::Bind(&AppListSyncableService::BuildModel,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
}
AppListSyncableService::~AppListSyncableService() {
@@ -276,10 +332,62 @@ AppListSyncableService::~AppListSyncableService() {
model_observer_.reset();
}
+bool AppListSyncableService::IsExtensionServiceReady() const {
+ return extension_system_->extension_service() &&
+ extension_system_->extension_service()->is_ready();
+}
+
+void AppListSyncableService::InitFromLocalStorage() {
+ // This should happen before sync and model is built.
+ DCHECK(!sync_processor_.get());
+ DCHECK(!IsInitialized());
+
+ // Restore initial state from local storage.
+ const base::DictionaryValue* local_items = profile_->GetPrefs()->
+ GetDictionary(prefs::kAppListLocalState);
+ DCHECK(local_items);
+
+ for (base::DictionaryValue::Iterator item(*local_items); !item.IsAtEnd();
+ item.Advance()) {
+ const base::DictionaryValue* dict_item;
+ if (!item.value().GetAsDictionary(&dict_item)) {
+ LOG(ERROR) << "Dictionary not found for " << item.key() + ".";
+ continue;
+ }
+
+ int type;
+ if (!dict_item->GetInteger(kTypeKey, &type)) {
+ LOG(ERROR) << "Item type is not set in local storage for " << item.key()
+ << ".";
+ continue;
+ }
+
+ SyncItem* sync_item = CreateSyncItem(item.key(),
+ static_cast<sync_pb::AppListSpecifics::AppListItemType>(type));
+
+ dict_item->GetString(kNameKey, &sync_item->item_name);
+ dict_item->GetString(kParentIdKey, &sync_item->parent_id);
+ std::string position;
+ std::string pin_position;
+ dict_item->GetString(kPositionKey, &position);
+ dict_item->GetString(kPinPositionKey, &pin_position);
+ if (!position.empty())
+ sync_item->item_ordinal = syncer::StringOrdinal(position);
+ if (!pin_position.empty())
+ sync_item->item_pin_ordinal = syncer::StringOrdinal(pin_position);
+ ProcessNewSyncItem(sync_item);
+ }
+}
+
+bool AppListSyncableService::IsInitialized() const {
+ return apps_builder_.get();
+}
+
void AppListSyncableService::BuildModel() {
+ InitFromLocalStorage();
+
// TODO(calamity): make this a DCHECK after a dev channel release.
- CHECK(extension_system_->extension_service() &&
- extension_system_->extension_service()->is_ready());
+ CHECK(IsExtensionServiceReady());
AppListControllerDelegate* controller = NULL;
AppListService* service = AppListService::Get();
if (service)
@@ -309,6 +417,8 @@ void AppListSyncableService::BuildModel() {
if (app_list::switches::IsDriveAppsInAppListEnabled())
drive_app_provider_.reset(new DriveAppProvider(profile_, this));
+
+ HandleUpdateFinished();
}
void AppListSyncableService::AddObserverAndStart(Observer* observer) {
@@ -326,9 +436,7 @@ void AppListSyncableService::NotifyObserversSyncUpdated() {
}
size_t AppListSyncableService::GetNumSyncItemsForTest() {
- // If the model isn't built yet, there will be no sync items.
- GetModel();
-
+ DCHECK(IsInitialized());
return sync_items_.size();
}
@@ -385,12 +493,26 @@ void AppListSyncableService::SetOemFolderName(const std::string& name) {
}
AppListModel* AppListSyncableService::GetModel() {
- if (!apps_builder_)
- BuildModel();
-
+ DCHECK(IsInitialized());
return model_.get();
}
+void AppListSyncableService::HandleUpdateStarted() {
+ // Don't observe the model while processing update changes.
+ model_observer_.reset();
+}
+
+void AppListSyncableService::HandleUpdateFinished() {
+ // Processing an update may create folders without setting their positions.
+ // Resolve them now.
+ ResolveFolderPositions();
+
+ // Resume or start observing app list model changes.
+ model_observer_.reset(new ModelObserver(this));
+
+ NotifyObserversSyncUpdated();
+}
+
void AppListSyncableService::AddItem(std::unique_ptr<AppListItem> app_item) {
SyncItem* sync_item = FindOrAddSyncItem(app_item.get());
if (!sync_item)
@@ -445,6 +567,7 @@ AppListSyncableService::CreateSyncItemFromAppItem(AppListItem* app_item) {
VLOG(2) << this << " CreateSyncItemFromAppItem:" << app_item->ToDebugString();
SyncItem* sync_item = CreateSyncItem(app_item->id(), type);
UpdateSyncItemFromAppItem(app_item, sync_item);
+ UpdateSyncItemInLocalStorage(profile_, sync_item);
SendSyncChange(sync_item, SyncChange::ACTION_ADD);
return sync_item;
}
@@ -470,6 +593,7 @@ void AppListSyncableService::SetPinPosition(
}
sync_item->item_pin_ordinal = item_pin_ordinal;
+ UpdateSyncItemInLocalStorage(profile_, sync_item);
SendSyncChange(sync_item, sync_change_type);
}
@@ -522,6 +646,7 @@ void AppListSyncableService::DeleteSyncItem(const std::string& item_id) {
sync_processor_->ProcessSyncChanges(
FROM_HERE, syncer::SyncChangeList(1, sync_change));
}
+ RemoveSyncItemFromLocalStorage(profile_, item_id);
sync_items_.erase(item_id);
}
@@ -536,6 +661,7 @@ void AppListSyncableService::UpdateSyncItem(AppListItem* app_item) {
DVLOG(2) << this << " - Update: SYNC NO CHANGE: " << sync_item->ToString();
return;
}
+ UpdateSyncItemInLocalStorage(profile_, sync_item);
SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
}
@@ -586,6 +712,7 @@ void AppListSyncableService::RemoveSyncItem(const std::string& id) {
VLOG(2) << this << " -> SYNC UPDATE: REMOVE_DEFAULT: "
<< sync_item->item_id;
sync_item->item_type = sync_pb::AppListSpecifics::TYPE_REMOVE_DEFAULT_APP;
+ UpdateSyncItemInLocalStorage(profile_, sync_item);
SendSyncChange(sync_item, SyncChange::ACTION_UPDATE);
return;
}
@@ -646,13 +773,15 @@ syncer::SyncMergeResult AppListSyncableService::MergeDataAndStartSyncing(
DCHECK(sync_processor.get());
DCHECK(error_handler.get());
- // Ensure the model is built.
- GetModel();
+ HandleUpdateStarted();
+
+ // Reset local state and recreate from sync info.
+ DictionaryPrefUpdate pref_update(profile_->GetPrefs(),
+ prefs::kAppListLocalState);
+ pref_update->Clear();
sync_processor_ = std::move(sync_processor);
sync_error_handler_ = std::move(error_handler);
- if (switches::IsFolderUIEnabled())
- model_->SetFoldersEnabled(true);
syncer::SyncMergeResult result = syncer::SyncMergeResult(type);
result.set_num_items_before_association(sync_items_.size());
@@ -706,19 +835,13 @@ syncer::SyncMergeResult AppListSyncableService::MergeDataAndStartSyncing(
if (!sync_item)
continue;
VLOG(2) << this << " -> SYNC ADD: " << sync_item->ToString();
+ UpdateSyncItemInLocalStorage(profile_, sync_item);
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));
-
- NotifyObserversSyncUpdated();
+ HandleUpdateFinished();
return result;
}
@@ -728,7 +851,6 @@ void AppListSyncableService::StopSyncing(syncer::ModelType type) {
sync_processor_.reset();
sync_error_handler_.reset();
- model_->SetFoldersEnabled(false);
}
syncer::SyncDataList AppListSyncableService::GetAllSyncData(
@@ -754,8 +876,7 @@ syncer::SyncError AppListSyncableService::ProcessSyncChanges(
syncer::APP_LIST);
}
- // Don't observe the model while processing incoming sync changes.
- model_observer_.reset();
+ HandleUpdateStarted();
VLOG(1) << this << ": ProcessSyncChanges: " << change_list.size();
for (syncer::SyncChangeList::const_iterator iter = change_list.begin();
@@ -774,10 +895,7 @@ syncer::SyncError AppListSyncableService::ProcessSyncChanges(
}
}
- // Continue observing app list model changes.
- model_observer_.reset(new ModelObserver(this));
-
- NotifyObserversSyncUpdated();
+ HandleUpdateFinished();
return syncer::SyncError();
}
@@ -797,6 +915,7 @@ bool AppListSyncableService::ProcessSyncItemSpecifics(
if (sync_item->item_type == specifics.item_type()) {
UpdateSyncItemFromSync(specifics, sync_item);
ProcessExistingSyncItem(sync_item);
+ UpdateSyncItemInLocalStorage(profile_, sync_item);
VLOG(2) << this << " <- SYNC UPDATE: " << sync_item->ToString();
return false;
}
@@ -818,6 +937,7 @@ bool AppListSyncableService::ProcessSyncItemSpecifics(
sync_item = CreateSyncItem(item_id, specifics.item_type());
UpdateSyncItemFromSync(specifics, sync_item);
ProcessNewSyncItem(sync_item);
+ UpdateSyncItemInLocalStorage(profile_, sync_item);
VLOG(2) << this << " <- SYNC ADD: " << sync_item->ToString();
return true;
}
@@ -969,7 +1089,9 @@ void AppListSyncableService::DeleteSyncItemSpecifics(
sync_pb::AppListSpecifics::AppListItemType item_type =
iter->second->item_type;
VLOG(2) << this << " <- SYNC DELETE: " << iter->second->ToString();
+ RemoveSyncItemFromLocalStorage(profile_, item_id);
sync_items_.erase(iter);
+
// Only delete apps from the model. Folders will be deleted when all
// children have been deleted.
if (item_type == sync_pb::AppListSpecifics::TYPE_APP) {
« no previous file with comments | « chrome/browser/ui/app_list/app_list_syncable_service.h ('k') | chrome/browser/ui/ash/chrome_launcher_prefs.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698