1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
|
// Copyright 2020 The Chromium Authors
// 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 <algorithm>
#include "ash/constants/ash_features.h"
#include "ash/public/cpp/holding_space/holding_space_constants.h"
#include "ash/public/cpp/holding_space/holding_space_file.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 "ash/public/cpp/holding_space/holding_space_util.h"
#include "base/containers/contains.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().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.
ScopedListPrefUpdate 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.
ScopedListPrefUpdate update(profile()->GetPrefs(), kPersistencePath);
update->EraseIf([&items](const base::Value& persisted_item) {
const std::string& persisted_item_id =
HoldingSpaceItem::DeserializeId(persisted_item.GetDict());
return base::Contains(items, persisted_item_id, &HoldingSpaceItem::id);
});
}
void HoldingSpacePersistenceDelegate::OnHoldingSpaceItemUpdated(
const HoldingSpaceItem* item,
const HoldingSpaceItemUpdatedFields& 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.
ScopedListPrefUpdate update(profile()->GetPrefs(), kPersistencePath);
base::Value::List& list = update.Get();
auto item_it = std::ranges::find(
list, item->id(), [](const base::Value& persisted_item) {
return HoldingSpaceItem::DeserializeId(persisted_item.GetDict());
});
// If the finalized `item` already exists in persistent storage, update it.
if (item_it != list.end()) {
*item_it = base::Value(item->Serialize());
return;
}
// If the finalized `item` did not previously exist in persistent storage,
// insert it at the appropriate index.
item_it = list.begin();
for (const auto& candidate_item : model()->items()) {
if (candidate_item.get() == item) {
list.Insert(item_it, base::Value(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());
// Remove items from persistent storage that should not be restored to the
// in-memory holding space model.
MaybeRemoveItemsFromPersistence();
const base::Value::List& 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.
std::vector<std::unique_ptr<HoldingSpaceItem>> restored_items;
if (persisted_holding_space_items.empty()) {
std::move(persistence_restored_callback_).Run(std::move(restored_items));
return;
}
for (const auto& persisted_holding_space_item :
persisted_holding_space_items) {
std::unique_ptr<HoldingSpaceItem> holding_space_item =
HoldingSpaceItem::Deserialize(
persisted_holding_space_item.GetDict(),
base::BindOnce(&holding_space_util::ResolveImage,
base::Unretained(thumbnail_loader_)));
if (!ShouldIgnoreItem(profile(), holding_space_item.get())) {
restored_items.push_back(std::move(holding_space_item));
}
}
// Notify completion of persistence restoration.
std::move(persistence_restored_callback_).Run(std::move(restored_items));
}
void HoldingSpacePersistenceDelegate::MaybeRemoveItemsFromPersistence() {
CHECK(is_restoring_persistence());
const auto known_types = holding_space_util::GetAllItemTypes();
const bool remove_suggestion_items =
!features::IsHoldingSpaceSuggestionsEnabled();
ScopedListPrefUpdate update(profile()->GetPrefs(), kPersistencePath);
update->EraseIf([&](const base::Value& persisted_item) {
auto type = HoldingSpaceItem::DeserializeType(persisted_item.GetDict());
// Remove items associated with unknown `type`s.
if (!base::Contains(known_types, type)) {
return true;
}
// Remove items associated with disabled features.
if (remove_suggestion_items && HoldingSpaceItem::IsSuggestionType(type)) {
return true;
}
return false;
});
}
} // namespace ash
|