blob: 589a88853901c0f441dd13ee70d2564384ae564d [file] [log] [blame]
// Copyright 2020 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/holding_space/holding_space_persistence_delegate.h"
#include "ash/public/cpp/holding_space/holding_space_constants.h"
#include "ash/public/cpp/holding_space/holding_space_image.h"
#include "ash/public/cpp/holding_space/holding_space_item.h"
#include "ash/public/cpp/holding_space/holding_space_progress.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/scoped_user_pref_update.h"
namespace ash {
namespace {
// Returns whether the item should be ignored by the holding space model. This
// returns true if the item is not supported in the current context, but may
// be otherwise supported. For example, returns true for ARC file system
// backed items in a secondary user profile.
bool ShouldIgnoreItem(Profile* profile, const HoldingSpaceItem* item) {
return file_manager::util::GetAndroidFilesPath().IsParent(
item->file_path()) &&
!ProfileHelper::IsPrimaryProfile(profile);
}
} // namespace
// static
constexpr char HoldingSpacePersistenceDelegate::kPersistencePath[];
HoldingSpacePersistenceDelegate::HoldingSpacePersistenceDelegate(
HoldingSpaceKeyedService* service,
HoldingSpaceModel* model,
ThumbnailLoader* thumbnail_loader,
PersistenceRestoredCallback persistence_restored_callback)
: HoldingSpaceKeyedServiceDelegate(service, model),
thumbnail_loader_(thumbnail_loader),
persistence_restored_callback_(std::move(persistence_restored_callback)) {
}
HoldingSpacePersistenceDelegate::~HoldingSpacePersistenceDelegate() = default;
// static
void HoldingSpacePersistenceDelegate::RegisterProfilePrefs(
user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterListPref(kPersistencePath);
}
void HoldingSpacePersistenceDelegate::Init() {
// We expect that the associated profile is already ready when we are being
// initialized. That being the case, we can immediately proceed to restore
// the holding space model from persistence storage.
RestoreModelFromPersistence();
}
void HoldingSpacePersistenceDelegate::OnHoldingSpaceItemsAdded(
const std::vector<const HoldingSpaceItem*>& items) {
if (is_restoring_persistence())
return;
// Write the new finalized `items` to persistent storage.
ListPrefUpdate update(profile()->GetPrefs(), kPersistencePath);
for (const HoldingSpaceItem* item : items) {
if (item->progress().IsComplete())
update->Append(item->Serialize());
}
}
void HoldingSpacePersistenceDelegate::OnHoldingSpaceItemsRemoved(
const std::vector<const HoldingSpaceItem*>& items) {
if (is_restoring_persistence())
return;
// Remove the `items` from persistent storage.
ListPrefUpdate update(profile()->GetPrefs(), kPersistencePath);
update->EraseListValueIf([&items](const base::Value& persisted_item) {
const std::string& persisted_item_id = HoldingSpaceItem::DeserializeId(
base::Value::AsDictionaryValue(persisted_item));
return std::any_of(items.begin(), items.end(),
[&persisted_item_id](const HoldingSpaceItem* item) {
return persisted_item_id == item->id();
});
});
}
void HoldingSpacePersistenceDelegate::OnHoldingSpaceItemUpdated(
const HoldingSpaceItem* item,
uint32_t updated_fields) {
if (is_restoring_persistence())
return;
// Only finalized items are persisted.
if (!item->progress().IsComplete())
return;
// Attempt to find the finalized `item` in persistent storage.
ListPrefUpdate update(profile()->GetPrefs(), kPersistencePath);
auto item_it = std::find_if(
update->GetList().begin(), update->GetList().end(),
[&item](const base::Value& persisted_item) {
return HoldingSpaceItem::DeserializeId(base::Value::AsDictionaryValue(
persisted_item)) == item->id();
});
// If the finalized `item` already exists in persistent storage, update it.
if (item_it != update->GetList().end()) {
*item_it = item->Serialize();
return;
}
// If the finalized `item` did not previously exist in persistent storage,
// insert it at the appropriate index.
item_it = update->GetList().begin();
for (const auto& candidate_item : model()->items()) {
if (candidate_item.get() == item) {
update->Insert(item_it, item->Serialize());
return;
}
if (candidate_item->progress().IsComplete())
++item_it;
}
// The finalized `item` should exist in the model and be handled above.
NOTREACHED();
}
void HoldingSpacePersistenceDelegate::RestoreModelFromPersistence() {
DCHECK(model()->items().empty());
const auto* persisted_holding_space_items =
profile()->GetPrefs()->GetList(kPersistencePath);
// If persistent storage is empty we can immediately notify the callback of
// persistence restoration completion and quit early.
if (persisted_holding_space_items->GetList().empty()) {
std::move(persistence_restored_callback_).Run();
return;
}
for (const auto& persisted_holding_space_item :
persisted_holding_space_items->GetList()) {
std::unique_ptr<HoldingSpaceItem> holding_space_item =
HoldingSpaceItem::Deserialize(
base::Value::AsDictionaryValue(persisted_holding_space_item),
base::BindOnce(&holding_space_util::ResolveImage,
base::Unretained(thumbnail_loader_)));
if (!ShouldIgnoreItem(profile(), holding_space_item.get()))
service()->AddItem(std::move(holding_space_item));
}
// Notify completion of persistence restoration.
std::move(persistence_restored_callback_).Run();
}
} // namespace ash