1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "media_album_change_request_impl.h"
17
18 #include "result_set_utils.h"
19 #include "userfile_client.h"
20 #include "album_operation_uri.h"
21 namespace OHOS {
22 namespace Media {
MediaAlbumChangeRequestImpl(shared_ptr<PhotoAlbum> photoAlbumPtr)23 MediaAlbumChangeRequestImpl::MediaAlbumChangeRequestImpl(shared_ptr<PhotoAlbum> photoAlbumPtr)
24 {
25 photoAlbum_ = photoAlbumPtr;
26 }
27
GetPhotoAlbumInstance() const28 shared_ptr<PhotoAlbum> MediaAlbumChangeRequestImpl::GetPhotoAlbumInstance() const
29 {
30 return photoAlbum_;
31 }
32
GetAddAssetArray() const33 vector<string> MediaAlbumChangeRequestImpl::GetAddAssetArray() const
34 {
35 return assetsToAdd_;
36 }
37
GetRemoveAssetArray() const38 vector<string> MediaAlbumChangeRequestImpl::GetRemoveAssetArray() const
39 {
40 return assetsToRemove_;
41 }
42
GetAlbumChangeOperations() const43 std::vector<AlbumChangeOperation> MediaAlbumChangeRequestImpl::GetAlbumChangeOperations() const
44 {
45 return albumChangeOperations_;
46 }
47
ClearAddAssetArray()48 void MediaAlbumChangeRequestImpl::ClearAddAssetArray()
49 {
50 assetsToAdd_.clear();
51 }
52
ClearRemoveAssetArray()53 void MediaAlbumChangeRequestImpl::ClearRemoveAssetArray()
54 {
55 assetsToRemove_.clear();
56 }
57
CJGetAlbum(int32_t * errCode)58 int64_t MediaAlbumChangeRequestImpl::CJGetAlbum(int32_t* errCode)
59 {
60 if (photoAlbum_ == nullptr) {
61 *errCode = JS_INNER_FAIL;
62 return 0;
63 }
64 if (photoAlbum_->GetAlbumId() > 0) {
65 auto photoAlbumImpl = FFIData::Create<PhotoAlbumImpl>(photoAlbum_);
66 if (photoAlbumImpl == nullptr) {
67 *errCode = JS_INNER_FAIL;
68 return 0;
69 }
70 return photoAlbumImpl->GetID();
71 }
72 return 0;
73 }
74
CJSetAlbumName(std::string albumName)75 int32_t MediaAlbumChangeRequestImpl::CJSetAlbumName(std::string albumName)
76 {
77 if (MediaFileUtils::CheckAlbumName(albumName) != E_OK) {
78 LOGE("Invalid album name");
79 return OHOS_INVALID_PARAM_CODE;
80 }
81 if (photoAlbum_ == nullptr) {
82 LOGE("photoAlbum is null")
83 return OHOS_INVALID_PARAM_CODE;
84 }
85 bool isUserPhotoAlbum = PhotoAlbum::IsUserPhotoAlbum(photoAlbum_->GetPhotoAlbumType(),
86 photoAlbum_->GetPhotoAlbumSubType());
87 bool isSmartPortraitPhotoAlbum = PhotoAlbum::IsSmartPortraitPhotoAlbum(photoAlbum_->GetPhotoAlbumType(),
88 photoAlbum_->GetPhotoAlbumSubType());
89 bool isSmartGroupPhotoAlbum = PhotoAlbum::IsSmartGroupPhotoAlbum(photoAlbum_->GetPhotoAlbumType(),
90 photoAlbum_->GetPhotoAlbumSubType());
91 if (!(isUserPhotoAlbum || isSmartPortraitPhotoAlbum || isSmartGroupPhotoAlbum)) {
92 LOGE("Only user album, smart portrait album and group photo can set album name");
93 return OHOS_INVALID_PARAM_CODE;
94 }
95 photoAlbum_->SetAlbumName(albumName);
96 albumChangeOperations_.push_back(AlbumChangeOperation::SET_ALBUM_NAME);
97 return 0;
98 }
99
CheckDuplicatedAssetArray(const vector<string> & arrayToCheck,const vector<string> & currentArray)100 static bool CheckDuplicatedAssetArray(const vector<string>& arrayToCheck, const vector<string>& currentArray)
101 {
102 if (currentArray.empty()) {
103 return true;
104 }
105
106 for (const auto& element : arrayToCheck) {
107 if (std::find(currentArray.begin(), currentArray.end(), element) != currentArray.end()) {
108 return false;
109 }
110 }
111 return true;
112 }
113
CJAddAssets(std::vector<std::string> assetUriArray)114 int32_t MediaAlbumChangeRequestImpl::CJAddAssets(std::vector<std::string> assetUriArray)
115 {
116 if (photoAlbum_ == nullptr) {
117 LOGE("photoAlbum is null")
118 return OHOS_INVALID_PARAM_CODE;
119 }
120 if (!(PhotoAlbum::IsUserPhotoAlbum(photoAlbum_->GetPhotoAlbumType(), photoAlbum_->GetPhotoAlbumSubType()))) {
121 LOGE("Only user album can add assets");
122 return OHOS_INVALID_PARAM_CODE;
123 }
124 if (!CheckDuplicatedAssetArray(assetUriArray, assetsToAdd_)) {
125 LOGE("The previous addAssets operation has contained the same asset");
126 return JS_E_OPERATION_NOT_SUPPORT;
127 }
128 assetsToAdd_.insert(assetsToAdd_.end(), assetUriArray.begin(), assetUriArray.end());
129 albumChangeOperations_.push_back(AlbumChangeOperation::ADD_ASSETS);
130 return 0;
131 }
132
CJRemoveAssets(std::vector<std::string> assetUriArray)133 int32_t MediaAlbumChangeRequestImpl::CJRemoveAssets(std::vector<std::string> assetUriArray)
134 {
135 if (photoAlbum_ == nullptr) {
136 LOGE("photoAlbum is null")
137 return OHOS_INVALID_PARAM_CODE;
138 }
139 if (!(PhotoAlbum::IsUserPhotoAlbum(photoAlbum_->GetPhotoAlbumType(), photoAlbum_->GetPhotoAlbumSubType()))) {
140 LOGE("Only user album can add assets");
141 return OHOS_INVALID_PARAM_CODE;
142 }
143 if (!CheckDuplicatedAssetArray(assetUriArray, assetsToAdd_)) {
144 LOGE("The previous addAssets operation has contained the same asset");
145 return JS_E_OPERATION_NOT_SUPPORT;
146 }
147 assetsToRemove_.insert(assetsToRemove_.end(), assetUriArray.begin(), assetUriArray.end());
148 albumChangeOperations_.push_back(AlbumChangeOperation::REMOVE_ASSETS);
149 return 0;
150 }
151
CheckChangeOperations()152 bool MediaAlbumChangeRequestImpl::CheckChangeOperations()
153 {
154 if (albumChangeOperations_.empty()) {
155 LOGE("None request to apply");
156 return false;
157 }
158
159 auto photoAlbum = GetPhotoAlbumInstance();
160 if (photoAlbum == nullptr) {
161 LOGE("photoAlbum is null");
162 return false;
163 }
164
165 if (albumChangeOperations_.front() != AlbumChangeOperation::CREATE_ALBUM && photoAlbum->GetAlbumId() <= 0) {
166 LOGE("Invalid album change request");
167 return false;
168 }
169
170 return true;
171 }
172
FetchNewCount(shared_ptr<PhotoAlbum> & album)173 static bool FetchNewCount(shared_ptr<PhotoAlbum>& album)
174 {
175 if (album == nullptr) {
176 LOGE("Album is null");
177 return false;
178 }
179
180 Uri queryUri(PAH_QUERY_PHOTO_ALBUM);
181 DataShare::DataSharePredicates predicates;
182 predicates.EqualTo(PhotoAlbumColumns::ALBUM_ID, album->GetAlbumId());
183 vector<string> fetchColumns = { PhotoAlbumColumns::ALBUM_ID, PhotoAlbumColumns::ALBUM_COUNT,
184 PhotoAlbumColumns::ALBUM_IMAGE_COUNT, PhotoAlbumColumns::ALBUM_VIDEO_COUNT };
185 int errCode = 0;
186 auto resultSet = UserFileClient::Query(queryUri, predicates, fetchColumns, errCode);
187 if (resultSet == nullptr) {
188 NAPI_ERR_LOG("resultSet == nullptr, errCode is %{public}d", errCode);
189 return false;
190 }
191 if (resultSet->GoToFirstRow() != 0) {
192 NAPI_ERR_LOG("go to first row failed when fetch new count");
193 return false;
194 }
195
196 bool hiddenOnly = album->GetHiddenOnly();
197 int imageCount = hiddenOnly ? -1 : get<int32_t>(ResultSetUtils::GetValFromColumn(
198 PhotoAlbumColumns::ALBUM_IMAGE_COUNT, resultSet, TYPE_INT32));
199 int videoCount = hiddenOnly ? -1 : get<int32_t>(ResultSetUtils::GetValFromColumn(
200 PhotoAlbumColumns::ALBUM_VIDEO_COUNT, resultSet, TYPE_INT32));
201 album->SetCount(
202 get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_COUNT, resultSet, TYPE_INT32)));
203 album->SetImageCount(imageCount);
204 album->SetVideoCount(videoCount);
205 return true;
206 }
207
AddAssetsExecute(MediaAlbumChangeRequestImpl * changeRequest)208 static bool AddAssetsExecute(MediaAlbumChangeRequestImpl* changeRequest)
209 {
210 auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
211 int32_t albumId = photoAlbum->GetAlbumId();
212 vector<DataShare::DataShareValuesBucket> valuesBuckets;
213 for (const auto& asset : changeRequest->GetAddAssetArray()) {
214 DataShare::DataShareValuesBucket pair;
215 pair.Put(PhotoColumn::PHOTO_OWNER_ALBUM_ID, albumId);
216 pair.Put(PhotoColumn::MEDIA_ID, asset);
217 valuesBuckets.push_back(pair);
218 }
219
220 Uri addAssetsUri(PAH_PHOTO_ALBUM_ADD_ASSET);
221 int ret = UserFileClient::BatchInsert(addAssetsUri, valuesBuckets);
222 changeRequest->ClearAddAssetArray();
223 if (ret < 0) {
224 LOGE("Failed to add assets into album %{public}d, err: %{public}d", albumId, ret);
225 return false;
226 }
227
228 LOGE("Add %{public}d asset(s) into album %{public}d", ret, albumId);
229 FetchNewCount(photoAlbum);
230 return true;
231 }
232
RemoveAssetsExecute(MediaAlbumChangeRequestImpl * changeRequest)233 static bool RemoveAssetsExecute(MediaAlbumChangeRequestImpl* changeRequest)
234 {
235 auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
236 int32_t albumId = photoAlbum->GetAlbumId();
237 DataShare::DataSharePredicates predicates;
238 predicates.EqualTo(PhotoColumn::PHOTO_OWNER_ALBUM_ID, to_string(albumId));
239 predicates.And()->In(PhotoColumn::MEDIA_ID, changeRequest->GetRemoveAssetArray());
240
241 Uri removeAssetsUri(PAH_PHOTO_ALBUM_REMOVE_ASSET);
242 int ret = UserFileClient::Delete(removeAssetsUri, predicates);
243 changeRequest->ClearRemoveAssetArray();
244 if (ret < 0) {
245 LOGE("Failed to remove assets from album %{public}d, err: %{public}d", albumId, ret);
246 return false;
247 }
248
249 LOGE("Remove %{public}d asset(s) from album %{public}d", ret, albumId);
250 FetchNewCount(photoAlbum);
251 return true;
252 }
253
GetAlbumUpdateCoverUri(shared_ptr<PhotoAlbum> & photoAlbum,string & uri)254 static void GetAlbumUpdateCoverUri(shared_ptr<PhotoAlbum>& photoAlbum, string& uri)
255 {
256 if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::PORTRAIT) {
257 uri = PAH_PORTRAIT_ANAALBUM_COVER_URI;
258 } else if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::GROUP_PHOTO) {
259 uri = PAH_GROUP_ANAALBUM_COVER_URI;
260 } else if (PhotoAlbum::IsUserPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType())) {
261 uri = PAH_UPDATE_USER_ALBUM_COVER_URI;
262 } else if (PhotoAlbum::IsUserPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType())) {
263 uri = PAH_UPDATE_USER_ALBUM_COVER_URI;
264 } else if (PhotoAlbum::IsSourceAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType())) {
265 uri = PAH_UPDATE_SOURCE_ALBUM_COVER_URI;
266 } else {
267 uri = PAH_UPDATE_SYSTEM_ALBUM_COVER_URI;
268 }
269 }
270
GetAlbumUpdateValue(shared_ptr<PhotoAlbum> & photoAlbum,const AlbumChangeOperation changeOperation,string & uri,DataShare::DataShareValuesBucket & valuesBucket,string & property)271 static bool GetAlbumUpdateValue(shared_ptr<PhotoAlbum>& photoAlbum, const AlbumChangeOperation changeOperation,
272 string& uri, DataShare::DataShareValuesBucket& valuesBucket, string& property)
273 {
274 if (photoAlbum == nullptr) {
275 LOGE("photoAlbum is null");
276 return false;
277 }
278
279 switch (changeOperation) {
280 case AlbumChangeOperation::SET_ALBUM_NAME:
281 if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::PORTRAIT) {
282 uri = PAH_PORTRAIT_ANAALBUM_ALBUM_NAME;
283 } else if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::GROUP_PHOTO) {
284 uri = PAH_GROUP_ANAALBUM_ALBUM_NAME;
285 } else {
286 uri = PAH_SET_PHOTO_ALBUM_NAME;
287 }
288 property = PhotoAlbumColumns::ALBUM_NAME;
289 valuesBucket.Put(property, photoAlbum->GetAlbumName());
290 break;
291 case AlbumChangeOperation::SET_COVER_URI:
292 GetAlbumUpdateCoverUri(photoAlbum, uri);
293 property = PhotoAlbumColumns::ALBUM_COVER_URI;
294 valuesBucket.Put(property, photoAlbum->GetCoverUri());
295 break;
296 case AlbumChangeOperation::RESET_COVER_URI:
297 uri = PAH_RESET_ALBUM_COVER_URI;
298 break;
299 case AlbumChangeOperation::SET_DISPLAY_LEVEL:
300 uri = PAH_PORTRAIT_DISPLAY_LEVLE;
301 property = USER_DISPLAY_LEVEL;
302 valuesBucket.Put(property, photoAlbum->GetDisplayLevel());
303 break;
304 case AlbumChangeOperation::SET_IS_ME:
305 uri = PAH_PORTRAIT_IS_ME;
306 property = IS_ME;
307 valuesBucket.Put(property, 1);
308 break;
309 case AlbumChangeOperation::DISMISS:
310 uri = PAH_GROUP_ANAALBUM_DISMISS;
311 property = IS_REMOVED;
312 valuesBucket.Put(property, 1);
313 break;
314 default:
315 return false;
316 }
317 return true;
318 }
319
SetAlbumPropertyExecute(MediaAlbumChangeRequestImpl * changeRequest,const AlbumChangeOperation changeOperation)320 static bool SetAlbumPropertyExecute(
321 MediaAlbumChangeRequestImpl* changeRequest, const AlbumChangeOperation changeOperation)
322 {
323 if (changeOperation == AlbumChangeOperation::SET_ALBUM_NAME &&
324 changeRequest->GetAlbumChangeOperations().front() == AlbumChangeOperation::CREATE_ALBUM) {
325 return true;
326 }
327
328 DataShare::DataSharePredicates predicates;
329 DataShare::DataShareValuesBucket valuesBucket;
330 auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
331 predicates.EqualTo(PhotoAlbumColumns::ALBUM_ID, std::to_string(photoAlbum->GetAlbumId()));
332 string uri;
333 string property;
334 if (!GetAlbumUpdateValue(photoAlbum, changeOperation, uri, valuesBucket, property)) {
335 LOGE("Failed to parse album change operation: %{public}d", changeOperation);
336 return false;
337 }
338 valuesBucket.Put(PhotoAlbumColumns::ALBUM_SUBTYPE, photoAlbum->GetPhotoAlbumSubType());
339 MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
340 Uri updateAlbumUri(uri);
341 int32_t changedRows = UserFileClient::Update(updateAlbumUri, predicates, valuesBucket);
342 if (changedRows < 0) {
343 LOGE("Failed to set %{public}s, err: %{public}d", property.c_str(), changedRows);
344 return false;
345 }
346 return true;
347 }
348
349 static const unordered_map<AlbumChangeOperation, bool (*)(MediaAlbumChangeRequestImpl*)> EXECUTE_MAP = {
350 { AlbumChangeOperation::ADD_ASSETS, AddAssetsExecute },
351 { AlbumChangeOperation::REMOVE_ASSETS, RemoveAssetsExecute },
352 };
353
ApplyChanges()354 int32_t MediaAlbumChangeRequestImpl::ApplyChanges()
355 {
356 if (!CheckChangeOperations()) {
357 LOGE("Failed to check album change request operations");
358 return OHOS_INVALID_PARAM_CODE;
359 }
360 unordered_set<AlbumChangeOperation> appliedOperations;
361 for (const auto& changeOperation : albumChangeOperations_) {
362 // Keep the final result(s) of each operation, and commit only once.
363 if (appliedOperations.find(changeOperation) != appliedOperations.end()) {
364 continue;
365 }
366
367 bool valid = false;
368 auto iter = EXECUTE_MAP.find(changeOperation);
369 if (iter != EXECUTE_MAP.end()) {
370 valid = iter->second(this);
371 } else if (changeOperation == AlbumChangeOperation::SET_ALBUM_NAME ||
372 changeOperation == AlbumChangeOperation::SET_COVER_URI ||
373 changeOperation == AlbumChangeOperation::SET_IS_ME ||
374 changeOperation == AlbumChangeOperation::SET_DISPLAY_LEVEL ||
375 changeOperation == AlbumChangeOperation::DISMISS ||
376 changeOperation == AlbumChangeOperation::RESET_COVER_URI) {
377 valid = SetAlbumPropertyExecute(this, changeOperation);
378 } else {
379 LOGE("Invalid album change operation: %{public}d", changeOperation);
380 albumChangeOperations_.clear();
381 return OHOS_INVALID_PARAM_CODE;
382 }
383
384 if (!valid) {
385 LOGE("Failed to apply album change request, operation: %{public}d", changeOperation);
386 albumChangeOperations_.clear();
387 return 0;
388 }
389 appliedOperations.insert(changeOperation);
390 }
391 albumChangeOperations_.clear();
392 return 0;
393 }
394 } // namespace Media
395 } // namespace OHOS