• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 #define MLOG_TAG "MediaAlbumChangeRequestNapi"
17 
18 #include "media_album_change_request_napi.h"
19 
20 #include <unordered_map>
21 #include <unordered_set>
22 
23 #include "file_asset_napi.h"
24 #include "media_file_utils.h"
25 #include "medialibrary_client_errno.h"
26 #include "medialibrary_napi_log.h"
27 #include "medialibrary_tracer.h"
28 #include "photo_album_napi.h"
29 #include "photo_map_column.h"
30 #include "result_set_utils.h"
31 #include "userfile_client.h"
32 #include "vision_column.h"
33 #include "vision_face_tag_column.h"
34 #include "vision_photo_map_column.h"
35 
36 using namespace std;
37 
38 namespace OHOS::Media {
39 static const string MEDIA_ALBUM_CHANGE_REQUEST_CLASS = "MediaAlbumChangeRequest";
40 thread_local napi_ref MediaAlbumChangeRequestNapi::constructor_ = nullptr;
41 
Init(napi_env env,napi_value exports)42 napi_value MediaAlbumChangeRequestNapi::Init(napi_env env, napi_value exports)
43 {
44     NapiClassInfo info = { .name = MEDIA_ALBUM_CHANGE_REQUEST_CLASS,
45         .ref = &constructor_,
46         .constructor = Constructor,
47         .props = {
48             DECLARE_NAPI_STATIC_FUNCTION("createAlbumRequest", JSCreateAlbumRequest),
49             DECLARE_NAPI_STATIC_FUNCTION("deleteAlbums", JSDeleteAlbums),
50             DECLARE_NAPI_FUNCTION("getAlbum", JSGetAlbum),
51             DECLARE_NAPI_FUNCTION("addAssets", JSAddAssets),
52             DECLARE_NAPI_FUNCTION("removeAssets", JSRemoveAssets),
53             DECLARE_NAPI_FUNCTION("moveAssets", JSMoveAssets),
54             DECLARE_NAPI_FUNCTION("recoverAssets", JSRecoverAssets),
55             DECLARE_NAPI_FUNCTION("deleteAssets", JSDeleteAssets),
56             DECLARE_NAPI_FUNCTION("setAlbumName", JSSetAlbumName),
57             DECLARE_NAPI_FUNCTION("setCoverUri", JSSetCoverUri),
58             DECLARE_NAPI_FUNCTION("placeBefore", JSPlaceBefore),
59             DECLARE_NAPI_FUNCTION("setDisplayLevel", JSSetDisplayLevel),
60             DECLARE_NAPI_FUNCTION("mergeAlbum", JSMergeAlbum),
61             DECLARE_NAPI_FUNCTION("dismissAssets", JSDismissAssets),
62             DECLARE_NAPI_FUNCTION("setIsMe", JSSetIsMe),
63             DECLARE_NAPI_FUNCTION("dismiss", JSDismiss),
64         } };
65     MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
66     return exports;
67 }
68 
ParsePhotoAlbum(napi_env env,napi_value arg,shared_ptr<PhotoAlbum> & photoAlbum)69 static napi_value ParsePhotoAlbum(napi_env env, napi_value arg, shared_ptr<PhotoAlbum>& photoAlbum)
70 {
71     napi_valuetype valueType;
72     PhotoAlbumNapi* photoAlbumNapi;
73     CHECK_ARGS(env, napi_typeof(env, arg, &valueType), JS_INNER_FAIL);
74     CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
75     CHECK_ARGS(env, napi_unwrap(env, arg, reinterpret_cast<void**>(&photoAlbumNapi)), JS_INNER_FAIL);
76     CHECK_COND_WITH_MESSAGE(env, photoAlbumNapi != nullptr, "Failed to get PhotoAlbumNapi object");
77 
78     auto photoAlbumPtr = photoAlbumNapi->GetPhotoAlbumInstance();
79     CHECK_COND_WITH_MESSAGE(env, photoAlbumPtr != nullptr, "photoAlbum is null");
80     CHECK_COND_WITH_MESSAGE(env,
81         photoAlbumPtr->GetResultNapiType() == ResultNapiType::TYPE_PHOTOACCESS_HELPER &&
82             PhotoAlbum::CheckPhotoAlbumType(photoAlbumPtr->GetPhotoAlbumType()) &&
83             PhotoAlbum::CheckPhotoAlbumSubType(photoAlbumPtr->GetPhotoAlbumSubType()),
84         "Unsupported type of photoAlbum");
85     photoAlbum = photoAlbumPtr;
86     RETURN_NAPI_TRUE(env);
87 }
88 
Constructor(napi_env env,napi_callback_info info)89 napi_value MediaAlbumChangeRequestNapi::Constructor(napi_env env, napi_callback_info info)
90 {
91     napi_value newTarget = nullptr;
92     CHECK_ARGS(env, napi_get_new_target(env, info, &newTarget), JS_INNER_FAIL);
93     CHECK_COND_RET(newTarget != nullptr, nullptr, "Failed to check new.target");
94 
95     size_t argc = ARGS_ONE;
96     napi_value argv[ARGS_ONE] = { 0 };
97     napi_value thisVar = nullptr;
98     shared_ptr<PhotoAlbum> photoAlbum = nullptr;
99     CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr), JS_INNER_FAIL);
100     CHECK_COND_WITH_MESSAGE(env, argc == ARGS_ONE, "Number of args is invalid");
101     CHECK_COND_WITH_MESSAGE(env, ParsePhotoAlbum(env, argv[PARAM0], photoAlbum), "Failed to parse album");
102 
103     unique_ptr<MediaAlbumChangeRequestNapi> obj = make_unique<MediaAlbumChangeRequestNapi>();
104     CHECK_COND(env, obj != nullptr, JS_INNER_FAIL);
105     obj->photoAlbum_ = photoAlbum;
106     CHECK_ARGS(env,
107         napi_wrap(env, thisVar, reinterpret_cast<void*>(obj.get()), MediaAlbumChangeRequestNapi::Destructor, nullptr,
108             nullptr),
109         JS_INNER_FAIL);
110     obj.release();
111     return thisVar;
112 }
113 
Destructor(napi_env env,void * nativeObject,void * finalizeHint)114 void MediaAlbumChangeRequestNapi::Destructor(napi_env env, void* nativeObject, void* finalizeHint)
115 {
116     auto* albumChangeRequest = reinterpret_cast<MediaAlbumChangeRequestNapi*>(nativeObject);
117     if (albumChangeRequest != nullptr) {
118         delete albumChangeRequest;
119         albumChangeRequest = nullptr;
120     }
121 }
122 
GetPhotoAlbumInstance() const123 shared_ptr<PhotoAlbum> MediaAlbumChangeRequestNapi::GetPhotoAlbumInstance() const
124 {
125     return photoAlbum_;
126 }
127 
GetReferencePhotoAlbumInstance() const128 shared_ptr<PhotoAlbum> MediaAlbumChangeRequestNapi::GetReferencePhotoAlbumInstance() const
129 {
130     return referencePhotoAlbum_;
131 }
132 
GetTargetPhotoAlbumInstance() const133 shared_ptr<PhotoAlbum> MediaAlbumChangeRequestNapi::GetTargetPhotoAlbumInstance() const
134 {
135     return targetAlbum_;
136 }
137 
GetAddAssetArray() const138 vector<string> MediaAlbumChangeRequestNapi::GetAddAssetArray() const
139 {
140     return assetsToAdd_;
141 }
142 
GetRemoveAssetArray() const143 vector<string> MediaAlbumChangeRequestNapi::GetRemoveAssetArray() const
144 {
145     return assetsToRemove_;
146 }
147 
GetRecoverAssetArray() const148 vector<string> MediaAlbumChangeRequestNapi::GetRecoverAssetArray() const
149 {
150     return assetsToRecover_;
151 }
152 
GetDeleteAssetArray() const153 vector<string> MediaAlbumChangeRequestNapi::GetDeleteAssetArray() const
154 {
155     return assetsToDelete_;
156 }
157 
GetDismissAssetArray() const158 vector<string> MediaAlbumChangeRequestNapi::GetDismissAssetArray() const
159 {
160     return dismissAssets_;
161 }
162 
GetMoveMap() const163 map<shared_ptr<PhotoAlbum>, vector<string>, PhotoAlbumPtrCompare> MediaAlbumChangeRequestNapi::GetMoveMap() const
164 {
165     return moveMap_;
166 }
167 
RecordMoveAssets(vector<string> & assetArray,shared_ptr<PhotoAlbum> & targetAlbum)168 void MediaAlbumChangeRequestNapi::RecordMoveAssets(vector<string>& assetArray, shared_ptr<PhotoAlbum>& targetAlbum)
169 {
170     if (targetAlbum == nullptr || assetArray.empty()) {
171         return;
172     }
173 
174     auto iter = moveMap_.find(targetAlbum);
175     if (iter != moveMap_.end()) {
176         iter->second.insert(iter->second.end(), assetArray.begin(), assetArray.end());
177     } else {
178         moveMap_.insert(make_pair(targetAlbum, assetArray));
179     }
180 }
181 
ClearAddAssetArray()182 void MediaAlbumChangeRequestNapi::ClearAddAssetArray()
183 {
184     assetsToAdd_.clear();
185 }
186 
ClearRemoveAssetArray()187 void MediaAlbumChangeRequestNapi::ClearRemoveAssetArray()
188 {
189     assetsToRemove_.clear();
190 }
191 
ClearRecoverAssetArray()192 void MediaAlbumChangeRequestNapi::ClearRecoverAssetArray()
193 {
194     assetsToRecover_.clear();
195 }
196 
ClearDeleteAssetArray()197 void MediaAlbumChangeRequestNapi::ClearDeleteAssetArray()
198 {
199     assetsToDelete_.clear();
200 }
201 
ClearDismissAssetArray()202 void MediaAlbumChangeRequestNapi::ClearDismissAssetArray()
203 {
204     dismissAssets_.clear();
205 }
206 
ClearMoveMap()207 void MediaAlbumChangeRequestNapi::ClearMoveMap()
208 {
209     moveMap_.clear();
210 }
211 
CheckChangeOperations(napi_env env)212 bool MediaAlbumChangeRequestNapi::CheckChangeOperations(napi_env env)
213 {
214     if (albumChangeOperations_.empty()) {
215         NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "None request to apply");
216         return false;
217     }
218 
219     auto photoAlbum = GetPhotoAlbumInstance();
220     if (photoAlbum == nullptr) {
221         NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "photoAlbum is null");
222         return false;
223     }
224 
225     if (albumChangeOperations_.front() != AlbumChangeOperation::CREATE_ALBUM && photoAlbum->GetAlbumId() <= 0) {
226         NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Invalid album change request");
227         return false;
228     }
229 
230     return true;
231 }
232 
ParseAssetArray(napi_env env,napi_value arg,vector<string> & uriArray)233 static napi_value ParseAssetArray(napi_env env, napi_value arg, vector<string>& uriArray)
234 {
235     vector<napi_value> napiValues;
236     napi_valuetype valueType = napi_undefined;
237     CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetNapiValueArray(env, arg, napiValues));
238     CHECK_COND_WITH_MESSAGE(env, !napiValues.empty(), "array is empty");
239     CHECK_ARGS(env, napi_typeof(env, napiValues.front(), &valueType), JS_INNER_FAIL);
240     CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
241     CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetUriArrayFromAssets(env, napiValues, uriArray));
242     RETURN_NAPI_TRUE(env);
243 }
244 
CheckDuplicatedAssetArray(const vector<string> & arrayToCheck,const vector<string> & currentArray)245 static bool CheckDuplicatedAssetArray(const vector<string>& arrayToCheck, const vector<string>& currentArray)
246 {
247     if (currentArray.empty()) {
248         return true;
249     }
250 
251     for (const auto& element : arrayToCheck) {
252         if (std::find(currentArray.begin(), currentArray.end(), element) != currentArray.end()) {
253             return false;
254         }
255     }
256     return true;
257 }
258 
JSGetAlbum(napi_env env,napi_callback_info info)259 napi_value MediaAlbumChangeRequestNapi::JSGetAlbum(napi_env env, napi_callback_info info)
260 {
261     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
262     CHECK_ARGS_THROW_INVALID_PARAM(
263         env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_ZERO, ARGS_ZERO));
264 
265     auto changeRequest = asyncContext->objectInfo;
266     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
267     CHECK_COND(env, photoAlbum != nullptr, JS_INNER_FAIL);
268     if (photoAlbum->GetAlbumId() > 0) {
269         return PhotoAlbumNapi::CreatePhotoAlbumNapi(env, photoAlbum);
270     }
271 
272     // PhotoAlbum object has not been actually created, return null.
273     napi_value nullValue;
274     napi_get_null(env, &nullValue);
275     return nullValue;
276 }
277 
ParseArgsCreateAlbum(napi_env env,napi_callback_info info,unique_ptr<MediaAlbumChangeRequestAsyncContext> & context)278 static napi_value ParseArgsCreateAlbum(
279     napi_env env, napi_callback_info info, unique_ptr<MediaAlbumChangeRequestAsyncContext>& context)
280 {
281     if (!MediaLibraryNapiUtils::IsSystemApp()) {
282         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
283         return nullptr;
284     }
285 
286     CHECK_COND_WITH_MESSAGE(env,
287         MediaLibraryNapiUtils::AsyncContextGetArgs(env, info, context, ARGS_TWO, ARGS_TWO) == napi_ok,
288         "Failed to get args");
289     CHECK_COND(env, MediaAlbumChangeRequestNapi::InitUserFileClient(env, info), JS_INNER_FAIL);
290 
291     string albumName;
292     CHECK_COND_WITH_MESSAGE(env,
293         MediaLibraryNapiUtils::GetParamStringPathMax(env, context->argv[PARAM1], albumName) == napi_ok,
294         "Failed to get album name");
295     CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckAlbumName(albumName) == E_OK, "Invalid album name");
296     context->valuesBucket.Put(PhotoAlbumColumns::ALBUM_NAME, albumName);
297     RETURN_NAPI_TRUE(env);
298 }
299 
JSCreateAlbumRequest(napi_env env,napi_callback_info info)300 napi_value MediaAlbumChangeRequestNapi::JSCreateAlbumRequest(napi_env env, napi_callback_info info)
301 {
302     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
303     CHECK_COND_WITH_MESSAGE(env, ParseArgsCreateAlbum(env, info, asyncContext), "Failed to parse args");
304 
305     bool isValid = false;
306     string albumName = asyncContext->valuesBucket.Get(PhotoAlbumColumns::ALBUM_NAME, isValid);
307     auto photoAlbum = make_unique<PhotoAlbum>();
308     photoAlbum->SetAlbumName(albumName);
309     photoAlbum->SetPhotoAlbumType(USER);
310     photoAlbum->SetPhotoAlbumSubType(USER_GENERIC);
311     photoAlbum->SetResultNapiType(ResultNapiType::TYPE_PHOTOACCESS_HELPER);
312     napi_value photoAlbumNapi = PhotoAlbumNapi::CreatePhotoAlbumNapi(env, photoAlbum);
313     CHECK_COND(env, photoAlbumNapi != nullptr, JS_INNER_FAIL);
314 
315     napi_value constructor = nullptr;
316     napi_value instance = nullptr;
317     CHECK_ARGS(env, napi_get_reference_value(env, constructor_, &constructor), JS_INNER_FAIL);
318     CHECK_ARGS(env, napi_new_instance(env, constructor, 1, &photoAlbumNapi, &instance), JS_INNER_FAIL);
319     CHECK_COND(env, instance != nullptr, JS_INNER_FAIL);
320 
321     MediaAlbumChangeRequestNapi* changeRequest = nullptr;
322     CHECK_ARGS(env, napi_unwrap(env, instance, reinterpret_cast<void**>(&changeRequest)), JS_INNER_FAIL);
323     CHECK_COND(env, changeRequest != nullptr, JS_INNER_FAIL);
324     changeRequest->albumChangeOperations_.push_back(AlbumChangeOperation::CREATE_ALBUM);
325     return instance;
326 }
327 
ParseArgsDeleteAlbums(napi_env env,napi_callback_info info,unique_ptr<MediaAlbumChangeRequestAsyncContext> & context)328 static napi_value ParseArgsDeleteAlbums(
329     napi_env env, napi_callback_info info, unique_ptr<MediaAlbumChangeRequestAsyncContext>& context)
330 {
331     if (!MediaLibraryNapiUtils::IsSystemApp()) {
332         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
333         return nullptr;
334     }
335 
336     constexpr size_t minArgs = ARGS_TWO;
337     constexpr size_t maxArgs = ARGS_THREE;
338     CHECK_COND_WITH_MESSAGE(env,
339         MediaLibraryNapiUtils::AsyncContextGetArgs(env, info, context, minArgs, maxArgs) == napi_ok,
340         "Failed to get args");
341     CHECK_COND(env, MediaAlbumChangeRequestNapi::InitUserFileClient(env, info), JS_INNER_FAIL);
342 
343     vector<napi_value> napiValues;
344     napi_valuetype valueType = napi_undefined;
345     CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetNapiValueArray(env, context->argv[PARAM1], napiValues));
346     CHECK_COND_WITH_MESSAGE(env, !napiValues.empty(), "array is empty");
347     CHECK_ARGS(env, napi_typeof(env, napiValues.front(), &valueType), JS_INNER_FAIL);
348     CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
349 
350     vector<string> deleteIds;
351     for (const auto& napiValue : napiValues) {
352         PhotoAlbumNapi* obj = nullptr;
353         CHECK_ARGS(env, napi_unwrap(env, napiValue, reinterpret_cast<void**>(&obj)), JS_INNER_FAIL);
354         CHECK_COND_WITH_MESSAGE(env, obj != nullptr, "Failed to get album napi object");
355         CHECK_COND_WITH_MESSAGE(env,
356             PhotoAlbum::IsUserPhotoAlbum(obj->GetPhotoAlbumType(), obj->GetPhotoAlbumSubType()),
357             "Only user album can be deleted");
358         deleteIds.push_back(to_string(obj->GetAlbumId()));
359     }
360     context->predicates.In(PhotoAlbumColumns::ALBUM_ID, deleteIds);
361     RETURN_NAPI_TRUE(env);
362 }
363 
DeleteAlbumsExecute(napi_env env,void * data)364 static void DeleteAlbumsExecute(napi_env env, void* data)
365 {
366     MediaLibraryTracer tracer;
367     tracer.Start("DeleteAlbumsExecute");
368 
369     auto* context = static_cast<MediaAlbumChangeRequestAsyncContext*>(data);
370     Uri deleteAlbumUri(PAH_DELETE_PHOTO_ALBUM);
371     int ret = UserFileClient::Delete(deleteAlbumUri, context->predicates);
372     if (ret < 0) {
373         context->SaveError(ret);
374         NAPI_ERR_LOG("Failed to delete albums, err: %{public}d", ret);
375         return;
376     }
377     NAPI_INFO_LOG("Delete %{public}d album(s)", ret);
378 }
379 
DeleteAlbumsCompleteCallback(napi_env env,napi_status status,void * data)380 static void DeleteAlbumsCompleteCallback(napi_env env, napi_status status, void* data)
381 {
382     auto* context = static_cast<MediaAlbumChangeRequestAsyncContext*>(data);
383     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
384     auto jsContext = make_unique<JSAsyncContextOutput>();
385     jsContext->status = false;
386     napi_get_undefined(env, &jsContext->data);
387     napi_get_undefined(env, &jsContext->error);
388     if (context->error == ERR_DEFAULT) {
389         jsContext->status = true;
390     } else {
391         context->HandleError(env, jsContext->error);
392     }
393 
394     if (context->work != nullptr) {
395         MediaLibraryNapiUtils::InvokeJSAsyncMethod(
396             env, context->deferred, context->callbackRef, context->work, *jsContext);
397     }
398     delete context;
399 }
400 
JSDeleteAlbums(napi_env env,napi_callback_info info)401 napi_value MediaAlbumChangeRequestNapi::JSDeleteAlbums(napi_env env, napi_callback_info info)
402 {
403     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
404     CHECK_COND_WITH_MESSAGE(env, ParseArgsDeleteAlbums(env, info, asyncContext), "Failed to parse args");
405     return MediaLibraryNapiUtils::NapiCreateAsyncWork(
406         env, asyncContext, "ChangeRequestDeleteAlbums", DeleteAlbumsExecute, DeleteAlbumsCompleteCallback);
407 }
408 
JSAddAssets(napi_env env,napi_callback_info info)409 napi_value MediaAlbumChangeRequestNapi::JSAddAssets(napi_env env, napi_callback_info info)
410 {
411     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
412     CHECK_COND_WITH_MESSAGE(env,
413         MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok,
414         "Failed to get object info");
415 
416     auto changeRequest = asyncContext->objectInfo;
417     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
418     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
419     CHECK_COND_WITH_MESSAGE(env,
420         PhotoAlbum::IsUserPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
421         "Only user album can add assets");
422 
423     vector<string> assetUriArray;
424     CHECK_COND_WITH_MESSAGE(env, ParseAssetArray(env, asyncContext->argv[PARAM0], assetUriArray),
425         "Failed to parse assets");
426     if (!CheckDuplicatedAssetArray(assetUriArray, changeRequest->assetsToAdd_)) {
427         NapiError::ThrowError(env, JS_E_OPERATION_NOT_SUPPORT,
428             "The previous addAssets operation has contained the same asset");
429         return nullptr;
430     }
431     changeRequest->assetsToAdd_.insert(changeRequest->assetsToAdd_.end(), assetUriArray.begin(), assetUriArray.end());
432     changeRequest->albumChangeOperations_.push_back(AlbumChangeOperation::ADD_ASSETS);
433     RETURN_NAPI_UNDEFINED(env);
434 }
435 
JSRemoveAssets(napi_env env,napi_callback_info info)436 napi_value MediaAlbumChangeRequestNapi::JSRemoveAssets(napi_env env, napi_callback_info info)
437 {
438     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
439     CHECK_COND_WITH_MESSAGE(env,
440         MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok,
441         "Failed to get object info");
442 
443     auto changeRequest = asyncContext->objectInfo;
444     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
445     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
446     CHECK_COND_WITH_MESSAGE(env,
447         PhotoAlbum::IsUserPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
448         "Only user album can remove assets");
449 
450     vector<string> assetUriArray;
451     CHECK_COND_WITH_MESSAGE(env, ParseAssetArray(env, asyncContext->argv[PARAM0], assetUriArray),
452         "Failed to parse assets");
453     if (!CheckDuplicatedAssetArray(assetUriArray, changeRequest->assetsToRemove_)) {
454         NapiError::ThrowError(env, JS_E_OPERATION_NOT_SUPPORT,
455             "The previous removeAssets operation has contained the same asset");
456         return nullptr;
457     }
458     changeRequest->assetsToRemove_.insert(
459         changeRequest->assetsToRemove_.end(), assetUriArray.begin(), assetUriArray.end());
460     changeRequest->albumChangeOperations_.push_back(AlbumChangeOperation::REMOVE_ASSETS);
461     RETURN_NAPI_UNDEFINED(env);
462 }
463 
JSMoveAssets(napi_env env,napi_callback_info info)464 napi_value MediaAlbumChangeRequestNapi::JSMoveAssets(napi_env env, napi_callback_info info)
465 {
466     if (!MediaLibraryNapiUtils::IsSystemApp()) {
467         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
468         return nullptr;
469     }
470 
471     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
472     CHECK_COND_WITH_MESSAGE(env,
473         MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_TWO, ARGS_TWO) == napi_ok,
474         "Failed to get object info");
475 
476     auto changeRequest = asyncContext->objectInfo;
477     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
478     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
479 
480     shared_ptr<PhotoAlbum> targetAlbum = nullptr;
481     CHECK_COND_WITH_MESSAGE(
482         env, ParsePhotoAlbum(env, asyncContext->argv[PARAM1], targetAlbum), "Failed to parse targetAlbum");
483     CHECK_COND_WITH_MESSAGE(env, targetAlbum->GetAlbumId() != photoAlbum->GetAlbumId(), "targetAlbum cannot be self");
484 
485     vector<string> assetUriArray;
486     CHECK_COND_WITH_MESSAGE(env, ParseAssetArray(env, asyncContext->argv[PARAM0], assetUriArray),
487         "Failed to parse assets");
488     auto moveMap = changeRequest->GetMoveMap();
489     for (auto iter = moveMap.begin(); iter != moveMap.end(); iter++) {
490         if (!CheckDuplicatedAssetArray(assetUriArray, iter->second)) {
491             NapiError::ThrowError(env, JS_E_OPERATION_NOT_SUPPORT,
492                 "The previous moveAssets operation has contained the same asset");
493             return nullptr;
494         }
495     }
496     changeRequest->RecordMoveAssets(assetUriArray, targetAlbum);
497     changeRequest->albumChangeOperations_.push_back(AlbumChangeOperation::MOVE_ASSETS);
498     RETURN_NAPI_UNDEFINED(env);
499 }
500 
JSRecoverAssets(napi_env env,napi_callback_info info)501 napi_value MediaAlbumChangeRequestNapi::JSRecoverAssets(napi_env env, napi_callback_info info)
502 {
503     if (!MediaLibraryNapiUtils::IsSystemApp()) {
504         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
505         return nullptr;
506     }
507 
508     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
509     CHECK_COND_WITH_MESSAGE(env,
510         MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok,
511         "Failed to get object info");
512 
513     auto changeRequest = asyncContext->objectInfo;
514     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
515     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
516     CHECK_COND_WITH_MESSAGE(env,
517         PhotoAlbum::IsTrashAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
518         "Only trash album can recover assets");
519 
520     vector<string> assetUriArray;
521     CHECK_COND_WITH_MESSAGE(env, ParseAssetArray(env, asyncContext->argv[PARAM0], assetUriArray),
522         "Failed to parse assets");
523     if (!CheckDuplicatedAssetArray(assetUriArray, changeRequest->assetsToRecover_)) {
524         NapiError::ThrowError(env, JS_E_OPERATION_NOT_SUPPORT,
525             "The previous recoverAssets operation has contained the same asset");
526         return nullptr;
527     }
528     changeRequest->assetsToRecover_.insert(
529         changeRequest->assetsToRecover_.end(), assetUriArray.begin(), assetUriArray.end());
530     changeRequest->albumChangeOperations_.push_back(AlbumChangeOperation::RECOVER_ASSETS);
531     RETURN_NAPI_UNDEFINED(env);
532 }
533 
JSDeleteAssets(napi_env env,napi_callback_info info)534 napi_value MediaAlbumChangeRequestNapi::JSDeleteAssets(napi_env env, napi_callback_info info)
535 {
536     if (!MediaLibraryNapiUtils::IsSystemApp()) {
537         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
538         return nullptr;
539     }
540 
541     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
542     CHECK_COND_WITH_MESSAGE(env,
543         MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok,
544         "Failed to get object info");
545 
546     auto changeRequest = asyncContext->objectInfo;
547     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
548     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
549     CHECK_COND_WITH_MESSAGE(env,
550         PhotoAlbum::IsTrashAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
551         "Only trash album can delete assets permanently");
552 
553     vector<string> assetUriArray;
554     CHECK_COND_WITH_MESSAGE(env, ParseAssetArray(env, asyncContext->argv[PARAM0], assetUriArray),
555         "Failed to parse assets");
556     if (!CheckDuplicatedAssetArray(assetUriArray, changeRequest->assetsToDelete_)) {
557         NapiError::ThrowError(env, JS_E_OPERATION_NOT_SUPPORT,
558             "The previous deleteAssets operation has contained the same asset");
559         return nullptr;
560     }
561     changeRequest->assetsToDelete_.insert(
562         changeRequest->assetsToDelete_.end(), assetUriArray.begin(), assetUriArray.end());
563     changeRequest->albumChangeOperations_.push_back(AlbumChangeOperation::DELETE_ASSETS);
564     RETURN_NAPI_UNDEFINED(env);
565 }
566 
GetAssetsIdArray(napi_env env,napi_value arg,vector<string> & assetsArray)567 static napi_value GetAssetsIdArray(napi_env env, napi_value arg, vector<string> &assetsArray)
568 {
569     bool isArray = false;
570     CHECK_ARGS(env, napi_is_array(env, arg, &isArray), JS_INNER_FAIL);
571     if (!isArray) {
572         NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check array type");
573         return nullptr;
574     }
575 
576     uint32_t len = 0;
577     CHECK_ARGS(env, napi_get_array_length(env, arg, &len), JS_INNER_FAIL);
578     if (len <= 0) {
579         NAPI_ERR_LOG("Failed to check array length: %{public}u", len);
580         NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check array length");
581         return nullptr;
582     }
583 
584     for (uint32_t i = 0; i < len; i++) {
585         napi_value asset = nullptr;
586         CHECK_ARGS(env, napi_get_element(env, arg, i, &asset), JS_INNER_FAIL);
587         if (asset == nullptr) {
588             NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to get asset element");
589             return nullptr;
590         }
591 
592         FileAssetNapi *obj = nullptr;
593         CHECK_ARGS(env, napi_unwrap(env, asset, reinterpret_cast<void **>(&obj)), JS_INNER_FAIL);
594         if (obj == nullptr) {
595             NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to get asset napi object");
596             return nullptr;
597         }
598         if ((obj->GetMediaType() != MEDIA_TYPE_IMAGE && obj->GetMediaType() != MEDIA_TYPE_VIDEO)) {
599             NAPI_INFO_LOG("Skip invalid asset, mediaType: %{public}d", obj->GetMediaType());
600             continue;
601         }
602         assetsArray.push_back(obj->GetFileUri());
603     }
604 
605     napi_value result = nullptr;
606     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
607     return result;
608 }
609 
JSSetIsMe(napi_env env,napi_callback_info info)610 napi_value MediaAlbumChangeRequestNapi::JSSetIsMe(napi_env env, napi_callback_info info)
611 {
612     if (!MediaLibraryNapiUtils::IsSystemApp()) {
613         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
614         return nullptr;
615     }
616     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
617     CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(
618         env, info, asyncContext, ARGS_ZERO, ARGS_ZERO) == napi_ok, "Failed to get object info");
619 
620     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
621     CHECK_COND_WITH_MESSAGE(env,
622         PhotoAlbum::IsSmartPortraitPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
623         "Only portrait album can set is me");
624     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::SET_IS_ME);
625     napi_value result = nullptr;
626     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
627     return result;
628 }
629 
CheckDismissAssetVaild(std::vector<std::string> & dismissAssets,std::vector<std::string> & newAssetArray)630 bool MediaAlbumChangeRequestNapi::CheckDismissAssetVaild(std::vector<std::string> &dismissAssets,
631     std::vector<std::string> &newAssetArray)
632 {
633     if (newAssetArray.empty()) {
634         return false;
635     }
636     unordered_set<string> assetSet(dismissAssets.begin(), dismissAssets.end());
637     unordered_set<string> tempSet;
638     for (const auto& newAsset : newAssetArray) {
639         if (assetSet.find(newAsset) != assetSet.end()) {
640             return false;
641         }
642         tempSet.insert(newAsset);
643     }
644     for (const auto& tmp : tempSet) {
645         dismissAssets.push_back(tmp);
646     }
647     return true;
648 }
649 
JSDismissAssets(napi_env env,napi_callback_info info)650 napi_value MediaAlbumChangeRequestNapi::JSDismissAssets(napi_env env, napi_callback_info info)
651 {
652     if (!MediaLibraryNapiUtils::IsSystemApp()) {
653         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
654         return nullptr;
655     }
656     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
657     CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(
658         env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok, "Failed to get object info");
659 
660     vector<std::string> newAssetArray;
661     CHECK_NULLPTR_RET(GetAssetsIdArray(env, asyncContext->argv[PARAM0], newAssetArray));
662     if (!CheckDismissAssetVaild(asyncContext->objectInfo->dismissAssets_, newAssetArray)) {
663         NapiError::ThrowError(env, JS_E_OPERATION_NOT_SUPPORT, "This dismissAssets is not support");
664         return nullptr;
665     }
666     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
667     auto type = photoAlbum->GetPhotoAlbumType();
668     auto subtype = photoAlbum->GetPhotoAlbumSubType();
669     CHECK_COND_WITH_MESSAGE(env, PhotoAlbum::IsSmartPortraitPhotoAlbum(type, subtype) ||
670         PhotoAlbum::IsSmartGroupPhotoAlbum(type, subtype) || PhotoAlbum::IsSmartClassifyAlbum(type, subtype),
671         "Only portrait, group photo and classify album can dismiss asset");
672 
673     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::DISMISS_ASSET);
674     napi_value result = nullptr;
675     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
676     return result;
677 }
678 
JSMergeAlbum(napi_env env,napi_callback_info info)679 napi_value MediaAlbumChangeRequestNapi::JSMergeAlbum(napi_env env, napi_callback_info info)
680 {
681     if (!MediaLibraryNapiUtils::IsSystemApp()) {
682         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
683         return nullptr;
684     }
685     napi_valuetype valueType;
686     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
687 
688     CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(
689         env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok, "Failed to get object info");
690     CHECK_ARGS(env, napi_typeof(env, asyncContext->argv[PARAM0], &valueType), JS_INNER_FAIL);
691     CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
692     if (valueType == napi_object) {
693         PhotoAlbumNapi* photoAlbumNapi;
694         CHECK_ARGS(env, napi_unwrap(env, asyncContext->argv[PARAM0],
695             reinterpret_cast<void**>(&photoAlbumNapi)), JS_INNER_FAIL);
696         CHECK_COND_WITH_MESSAGE(env, photoAlbumNapi != nullptr, "Failed to get PhotoAlbumNapi object");
697         asyncContext->objectInfo->targetAlbum_ = photoAlbumNapi->GetPhotoAlbumInstance();
698     }
699     auto photoAlbum = asyncContext->objectInfo->photoAlbum_;
700     auto targetAlbum = asyncContext->objectInfo->targetAlbum_;
701     CHECK_COND_WITH_MESSAGE(env,
702         (photoAlbum != nullptr) && (targetAlbum != nullptr), "PhotoAlbum  Or TargetAlbum is nullptr");
703     CHECK_COND_WITH_MESSAGE(env,
704         (PhotoAlbum::IsSmartPortraitPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType())) &&
705         (PhotoAlbum::IsSmartPortraitPhotoAlbum(targetAlbum->GetPhotoAlbumType(), targetAlbum->GetPhotoAlbumSubType())),
706         "Only portrait album can merge");
707     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::MERGE_ALBUM);
708     napi_value result = nullptr;
709     CHECK_ARGS(env, napi_get_undefined(env, &result), JS_INNER_FAIL);
710     return result;
711 }
712 
JSSetDisplayLevel(napi_env env,napi_callback_info info)713 napi_value MediaAlbumChangeRequestNapi::JSSetDisplayLevel(napi_env env, napi_callback_info info)
714 {
715     if (!MediaLibraryNapiUtils::IsSystemApp()) {
716         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
717         return nullptr;
718     }
719     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
720     int32_t displayLevel;
721     CHECK_COND_WITH_MESSAGE(env,
722         MediaLibraryNapiUtils::ParseArgsNumberCallback(env, info, asyncContext, displayLevel) == napi_ok,
723         "Failed to parse args");
724     CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
725     CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckDisplayLevel(displayLevel), "Invalid display level");
726 
727     auto photoAlbum = asyncContext->objectInfo->photoAlbum_;
728     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "PhotoAlbum is nullptr");
729     CHECK_COND_WITH_MESSAGE(env,
730         PhotoAlbum::IsSmartPortraitPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
731         "Only portrait album can set album display level");
732     photoAlbum->SetDisplayLevel(displayLevel);
733     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::SET_DISPLAY_LEVEL);
734 
735     napi_value result = nullptr;
736     CHECK_ARGS(env, napi_get_undefined(env, &result), JS_INNER_FAIL);
737     return result;
738 }
739 
JSDismiss(napi_env env,napi_callback_info info)740 napi_value MediaAlbumChangeRequestNapi::JSDismiss(napi_env env, napi_callback_info info)
741 {
742     if (!MediaLibraryNapiUtils::IsSystemApp()) {
743         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
744         return nullptr;
745     }
746     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
747     CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(
748         env, info, asyncContext, ARGS_ZERO, ARGS_ZERO) == napi_ok, "Failed to get object info");
749 
750     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
751     CHECK_COND_WITH_MESSAGE(env,
752         PhotoAlbum::IsSmartGroupPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
753         "Only group photo can be dismissed");
754     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::DISMISS);
755     napi_value result = nullptr;
756     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
757     return result;
758 }
759 
JSSetAlbumName(napi_env env,napi_callback_info info)760 napi_value MediaAlbumChangeRequestNapi::JSSetAlbumName(napi_env env, napi_callback_info info)
761 {
762     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
763     string albumName;
764     CHECK_COND_WITH_MESSAGE(env,
765         MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, asyncContext, albumName) == napi_ok,
766         "Failed to parse args");
767     CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
768     CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckAlbumName(albumName) == E_OK, "Invalid album name");
769 
770     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
771     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
772     CHECK_COND_WITH_MESSAGE(env,
773         PhotoAlbum::IsUserPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()) ||
774         PhotoAlbum::IsSmartPortraitPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()) ||
775         PhotoAlbum::IsSmartGroupPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
776         "Only user album, smart portrait album and group photo can set album name");
777     photoAlbum->SetAlbumName(albumName);
778     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::SET_ALBUM_NAME);
779     RETURN_NAPI_UNDEFINED(env);
780 }
781 
JSSetCoverUri(napi_env env,napi_callback_info info)782 napi_value MediaAlbumChangeRequestNapi::JSSetCoverUri(napi_env env, napi_callback_info info)
783 {
784     if (!MediaLibraryNapiUtils::IsSystemApp()) {
785         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
786         return nullptr;
787     }
788 
789     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
790     string coverUri;
791     CHECK_COND_WITH_MESSAGE(env,
792         MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, asyncContext, coverUri) == napi_ok,
793         "Failed to parse args");
794     CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
795 
796     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
797     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
798     CHECK_COND_WITH_MESSAGE(env,
799         PhotoAlbum::IsUserPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()) ||
800         PhotoAlbum::IsSmartPortraitPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()) ||
801         PhotoAlbum::IsSmartGroupPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
802         "Only user album, smart portrait album and group photo can set album name");
803     photoAlbum->SetCoverUri(coverUri);
804     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::SET_COVER_URI);
805     RETURN_NAPI_UNDEFINED(env);
806 }
807 
JSPlaceBefore(napi_env env,napi_callback_info info)808 napi_value MediaAlbumChangeRequestNapi::JSPlaceBefore(napi_env env, napi_callback_info info)
809 {
810     if (!MediaLibraryNapiUtils::IsSystemApp()) {
811         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
812         return nullptr;
813     }
814     napi_valuetype valueType;
815     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
816 
817     CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(
818         env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok, "Failed to get object info");
819     CHECK_ARGS(env, napi_typeof(env, asyncContext->argv[PARAM0], &valueType), JS_INNER_FAIL);
820     CHECK_COND_WITH_MESSAGE(env, valueType == napi_object || valueType == napi_null, "Invalid argument type");
821     if (valueType == napi_object) {
822         PhotoAlbumNapi* photoAlbumNapi;
823         CHECK_ARGS(env, napi_unwrap(env, asyncContext->argv[PARAM0],
824             reinterpret_cast<void**>(&photoAlbumNapi)), JS_INNER_FAIL);
825         CHECK_COND_WITH_MESSAGE(env, photoAlbumNapi != nullptr, "Failed to get PhotoAlbumNapi object");
826         asyncContext->objectInfo->referencePhotoAlbum_ = photoAlbumNapi->GetPhotoAlbumInstance();
827     }
828     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::ORDER_ALBUM);
829     RETURN_NAPI_UNDEFINED(env);
830 }
831 
CreateAlbumExecute(MediaAlbumChangeRequestAsyncContext & context)832 static bool CreateAlbumExecute(MediaAlbumChangeRequestAsyncContext& context)
833 {
834     MediaLibraryTracer tracer;
835     tracer.Start("CreateAlbumExecute");
836 
837     auto changeRequest = context.objectInfo;
838     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
839 
840     Uri createAlbumUri(PAH_CREATE_PHOTO_ALBUM);
841     DataShare::DataShareValuesBucket valuesBucket;
842     valuesBucket.Put(PhotoAlbumColumns::ALBUM_NAME, photoAlbum->GetAlbumName());
843     int32_t ret = UserFileClient::Insert(createAlbumUri, valuesBucket);
844     if (ret == -1) {
845         context.SaveError(-EEXIST);
846         NAPI_ERR_LOG("Album exists");
847         return false;
848     }
849     if (ret < 0) {
850         context.SaveError(ret);
851         NAPI_ERR_LOG("Failed to create album, ret: %{public}d", ret);
852         return false;
853     }
854 
855     photoAlbum->SetAlbumId(ret);
856     photoAlbum->SetAlbumUri(PhotoAlbumColumns::ALBUM_URI_PREFIX + to_string(ret));
857     return true;
858 }
859 
FetchNewCount(MediaAlbumChangeRequestAsyncContext & context,shared_ptr<PhotoAlbum> & album)860 static bool FetchNewCount(MediaAlbumChangeRequestAsyncContext& context, shared_ptr<PhotoAlbum>& album)
861 {
862     if (album == nullptr) {
863         NAPI_ERR_LOG("Album is null");
864         context.SaveError(E_FAIL);
865         return false;
866     }
867 
868     Uri queryUri(PAH_QUERY_PHOTO_ALBUM);
869     DataShare::DataSharePredicates predicates;
870     predicates.EqualTo(PhotoAlbumColumns::ALBUM_ID, album->GetAlbumId());
871     vector<string> fetchColumns = { PhotoAlbumColumns::ALBUM_ID, PhotoAlbumColumns::ALBUM_COUNT,
872         PhotoAlbumColumns::ALBUM_IMAGE_COUNT, PhotoAlbumColumns::ALBUM_VIDEO_COUNT };
873     int errCode = 0;
874     auto resultSet = UserFileClient::Query(queryUri, predicates, fetchColumns, errCode);
875     if (resultSet == nullptr) {
876         NAPI_ERR_LOG("resultSet == nullptr, errCode is %{public}d", errCode);
877         context.SaveError(E_HAS_DB_ERROR);
878         return false;
879     }
880     if (resultSet->GoToFirstRow() != 0) {
881         NAPI_ERR_LOG("go to first row failed when fetch new count");
882         context.SaveError(E_HAS_DB_ERROR);
883         return false;
884     }
885 
886     bool hiddenOnly = album->GetHiddenOnly();
887     int imageCount = hiddenOnly ? -1 :
888         get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_IMAGE_COUNT, resultSet, TYPE_INT32));
889     int videoCount = hiddenOnly ? -1 :
890         get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_VIDEO_COUNT, resultSet, TYPE_INT32));
891     album->SetCount(
892         get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_COUNT, resultSet, TYPE_INT32)));
893     album->SetImageCount(imageCount);
894     album->SetVideoCount(videoCount);
895     return true;
896 }
897 
AddAssetsExecute(MediaAlbumChangeRequestAsyncContext & context)898 static bool AddAssetsExecute(MediaAlbumChangeRequestAsyncContext& context)
899 {
900     MediaLibraryTracer tracer;
901     tracer.Start("AddAssetsExecute");
902 
903     auto changeRequest = context.objectInfo;
904     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
905     int32_t albumId = photoAlbum->GetAlbumId();
906     vector<DataShare::DataShareValuesBucket> valuesBuckets;
907     for (const auto& asset : changeRequest->GetAddAssetArray()) {
908         DataShare::DataShareValuesBucket pair;
909         pair.Put(PhotoColumn::PHOTO_OWNER_ALBUM_ID, albumId);
910         pair.Put(PhotoColumn::MEDIA_ID, asset);
911         valuesBuckets.push_back(pair);
912     }
913 
914     Uri addAssetsUri(PAH_PHOTO_ALBUM_ADD_ASSET);
915     int ret = UserFileClient::BatchInsert(addAssetsUri, valuesBuckets);
916     changeRequest->ClearAddAssetArray();
917     if (ret < 0) {
918         context.SaveError(ret);
919         NAPI_ERR_LOG("Failed to add assets into album %{public}d, err: %{public}d", albumId, ret);
920         return false;
921     }
922 
923     NAPI_INFO_LOG("Add %{public}d asset(s) into album %{public}d", ret, albumId);
924     FetchNewCount(context, photoAlbum);
925     return true;
926 }
927 
RemoveAssetsExecute(MediaAlbumChangeRequestAsyncContext & context)928 static bool RemoveAssetsExecute(MediaAlbumChangeRequestAsyncContext& context)
929 {
930     MediaLibraryTracer tracer;
931     tracer.Start("RemoveAssetsExecute");
932 
933     auto changeRequest = context.objectInfo;
934     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
935     int32_t albumId = photoAlbum->GetAlbumId();
936     DataShare::DataSharePredicates predicates;
937     predicates.EqualTo(PhotoColumn::PHOTO_OWNER_ALBUM_ID, to_string(albumId));
938     predicates.And()->In(PhotoColumn::MEDIA_ID, changeRequest->GetRemoveAssetArray());
939 
940     Uri removeAssetsUri(PAH_PHOTO_ALBUM_REMOVE_ASSET);
941     int ret = UserFileClient::Delete(removeAssetsUri, predicates);
942     changeRequest->ClearRemoveAssetArray();
943     if (ret < 0) {
944         context.SaveError(ret);
945         NAPI_ERR_LOG("Failed to remove assets from album %{public}d, err: %{public}d", albumId, ret);
946         return false;
947     }
948 
949     NAPI_INFO_LOG("Remove %{public}d asset(s) from album %{public}d", ret, albumId);
950     FetchNewCount(context, photoAlbum);
951     return true;
952 }
953 
MoveAssetsExecute(MediaAlbumChangeRequestAsyncContext & context)954 static bool MoveAssetsExecute(MediaAlbumChangeRequestAsyncContext& context)
955 {
956     MediaLibraryTracer tracer;
957     tracer.Start("MoveAssetsExecute");
958 
959     auto changeRequest = context.objectInfo;
960     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
961     int32_t albumId = photoAlbum->GetAlbumId();
962     auto moveMap = changeRequest->GetMoveMap();
963     changeRequest->ClearMoveMap();
964 
965     for (auto iter = moveMap.begin(); iter != moveMap.end(); iter++) {
966         auto targetPhotoAlbum = iter->first;
967         int32_t targetAlbumId = targetPhotoAlbum->GetAlbumId();
968         vector<string> moveAssetArray = iter->second;
969         // Move into target album.
970         DataShare::DataSharePredicates predicates;
971         predicates.EqualTo(PhotoColumn::PHOTO_OWNER_ALBUM_ID, to_string(albumId));
972         predicates.And()->In(PhotoColumn::MEDIA_ID, moveAssetArray);
973 
974         DataShare::DataShareValuesBucket valuesBuckets;
975         valuesBuckets.Put(PhotoColumn::PHOTO_OWNER_ALBUM_ID, targetAlbumId);
976         string uri = PAH_BATCH_UPDATE_OWNER_ALBUM_ID;
977         MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
978         Uri moveAssetsUri(uri);
979         int ret = UserFileClient::Update(moveAssetsUri, predicates, valuesBuckets);
980         if (ret < 0) {
981             context.SaveError(ret);
982             NAPI_ERR_LOG("Failed to move assets into album %{public}d, err: %{public}d", targetAlbumId, ret);
983             return false;
984         }
985         NAPI_INFO_LOG("Move %{public}d asset(s) into album %{public}d", ret, targetAlbumId);
986         FetchNewCount(context, targetPhotoAlbum);
987     }
988     FetchNewCount(context, photoAlbum);
989     return true;
990 }
991 
RecoverAssetsExecute(MediaAlbumChangeRequestAsyncContext & context)992 static bool RecoverAssetsExecute(MediaAlbumChangeRequestAsyncContext& context)
993 {
994     MediaLibraryTracer tracer;
995     tracer.Start("RecoverAssetsExecute");
996 
997     DataShare::DataSharePredicates predicates;
998     DataShare::DataShareValuesBucket valuesBucket;
999     predicates.In(PhotoColumn::MEDIA_ID, context.objectInfo->GetRecoverAssetArray());
1000     valuesBucket.Put(PhotoColumn::MEDIA_DATE_TRASHED, 0);
1001 
1002     Uri recoverAssetsUri(PAH_RECOVER_PHOTOS);
1003     int ret = UserFileClient::Update(recoverAssetsUri, predicates, valuesBucket);
1004     context.objectInfo->ClearRecoverAssetArray();
1005     if (ret < 0) {
1006         context.SaveError(ret);
1007         NAPI_ERR_LOG("Failed to recover assets, err: %{public}d", ret);
1008         return false;
1009     }
1010 
1011     NAPI_INFO_LOG("Recover %{public}d assets from trash album", ret);
1012     auto photoAlbum = context.objectInfo->GetPhotoAlbumInstance();
1013     int32_t currentCount = photoAlbum->GetCount() - ret;
1014     photoAlbum->SetCount(currentCount > 0 ? currentCount : 0);
1015     return true;
1016 }
1017 
DeleteAssetsExecute(MediaAlbumChangeRequestAsyncContext & context)1018 static bool DeleteAssetsExecute(MediaAlbumChangeRequestAsyncContext& context)
1019 {
1020     MediaLibraryTracer tracer;
1021     tracer.Start("DeleteAssetsExecute");
1022 
1023     DataShare::DataSharePredicates predicates;
1024     predicates.In(PhotoColumn::MEDIA_ID, context.objectInfo->GetDeleteAssetArray());
1025     DataShare::DataShareValuesBucket valuesBucket;
1026     valuesBucket.Put(PhotoColumn::MEDIA_DATE_TRASHED, 0);
1027 
1028     Uri deleteAssetsUri(PAH_DELETE_PHOTOS);
1029     int ret = UserFileClient::Update(deleteAssetsUri, predicates, valuesBucket);
1030     context.objectInfo->ClearDeleteAssetArray();
1031     if (ret < 0) {
1032         context.SaveError(ret);
1033         NAPI_ERR_LOG("Failed to delete assets from trash album permanently, err: %{public}d", ret);
1034         return false;
1035     }
1036 
1037     NAPI_INFO_LOG("Delete %{public}d assets permanently from trash album", ret);
1038     auto photoAlbum = context.objectInfo->GetPhotoAlbumInstance();
1039     int32_t currentCount = photoAlbum->GetCount() - ret;
1040     photoAlbum->SetCount(currentCount > 0 ? currentCount : 0);
1041     return true;
1042 }
1043 
OrderAlbumExecute(MediaAlbumChangeRequestAsyncContext & context)1044 static bool OrderAlbumExecute(MediaAlbumChangeRequestAsyncContext& context)
1045 {
1046     MediaLibraryTracer tracer;
1047     tracer.Start("OrderAlbumExecute");
1048 
1049     DataShare::DataSharePredicates predicates;
1050     DataShare::DataShareValuesBucket valuesBucket;
1051     auto photoAlbum = context.objectInfo->GetPhotoAlbumInstance();
1052     auto referenceAlum = context.objectInfo->GetReferencePhotoAlbumInstance();
1053     Uri updateAlbumUri(PAH_ORDER_ALBUM);
1054     valuesBucket.Put(PhotoAlbumColumns::ALBUM_ID, photoAlbum->GetAlbumId());
1055     int32_t referenceAlbumId = -1;
1056     if (referenceAlum != nullptr) {
1057         referenceAlbumId = referenceAlum->GetAlbumId();
1058     }
1059     valuesBucket.Put(PhotoAlbumColumns::REFERENCE_ALBUM_ID, referenceAlbumId);
1060     valuesBucket.Put(PhotoAlbumColumns::ALBUM_TYPE, photoAlbum->GetPhotoAlbumType());
1061     valuesBucket.Put(PhotoAlbumColumns::ALBUM_SUBTYPE, photoAlbum->GetPhotoAlbumSubType());
1062     int32_t result = UserFileClient::Update(updateAlbumUri, predicates, valuesBucket);
1063     if (result < 0) {
1064         context.SaveError(result);
1065         NAPI_ERR_LOG("Failed to order albums err: %{public}d", result);
1066         return false;
1067     }
1068     return true;
1069 }
1070 
UpdateTabAnalysisImageFace(std::shared_ptr<PhotoAlbum> & photoAlbum,MediaAlbumChangeRequestAsyncContext & context)1071 static void UpdateTabAnalysisImageFace(std::shared_ptr<PhotoAlbum>& photoAlbum,
1072     MediaAlbumChangeRequestAsyncContext& context)
1073 {
1074     if (photoAlbum->GetPhotoAlbumSubType() != PhotoAlbumSubType::PORTRAIT) {
1075         return;
1076     }
1077 
1078     std::string updateUri = PAH_UPDATE_ANA_FACE;
1079     MediaLibraryNapiUtils::UriAppendKeyValue(updateUri, API_VERSION, std::to_string(MEDIA_API_VERSION_V10));
1080     MediaLibraryNapiUtils::UriAppendKeyValue(updateUri, MEDIA_OPERN_KEYWORD, UPDATE_DISMISS_ASSET);
1081     Uri updateFaceUri(updateUri);
1082 
1083     DataShare::DataShareValuesBucket updateValues;
1084     updateValues.Put(MediaAlbumChangeRequestNapi::TAG_ID,
1085         std::to_string(MediaAlbumChangeRequestNapi::PORTRAIT_REMOVED));
1086 
1087     DataShare::DataSharePredicates updatePredicates;
1088     std::vector<std::string> dismissAssetArray = context.objectInfo->GetDismissAssetArray();
1089     std::string selection = std::to_string(photoAlbum->GetAlbumId());
1090     for (size_t i = 0; i < dismissAssetArray.size(); ++i) {
1091         selection += "," + dismissAssetArray[i];
1092     }
1093     updatePredicates.SetWhereClause(selection);
1094     int updatedRows = UserFileClient::Update(updateFaceUri, updatePredicates, updateValues);
1095     if (updatedRows <= 0) {
1096         NAPI_WARN_LOG("Failed to update tab_analysis_image_face, err: %{public}d", updatedRows);
1097     }
1098 }
1099 
DismissAssetExecute(MediaAlbumChangeRequestAsyncContext & context)1100 static bool DismissAssetExecute(MediaAlbumChangeRequestAsyncContext& context)
1101 {
1102     MediaLibraryTracer tracer;
1103     tracer.Start("DismissAssetExecute");
1104 
1105     string disMissAssetAssetsUri = PAH_DISMISS_ASSET;
1106     Uri uri(disMissAssetAssetsUri);
1107 
1108     auto photoAlbum = context.objectInfo->GetPhotoAlbumInstance();
1109     DataShare::DataSharePredicates predicates;
1110     predicates.EqualTo(MAP_ALBUM, to_string(photoAlbum->GetAlbumId()));
1111     predicates.And()->In(MAP_ASSET, context.objectInfo->GetDismissAssetArray());
1112     predicates.And()->EqualTo(ALBUM_SUBTYPE, to_string(photoAlbum->GetPhotoAlbumSubType()));
1113 
1114     auto deletedRows = UserFileClient::Delete(uri, predicates);
1115     if (deletedRows < 0) {
1116         context.SaveError(deletedRows);
1117         NAPI_ERR_LOG("Failed to dismiss asset err: %{public}d", deletedRows);
1118         return false;
1119     }
1120 
1121     /* 只针对人像相册更新 tab_analysis_image_face 表 */
1122     if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::PORTRAIT) {
1123         UpdateTabAnalysisImageFace(photoAlbum, context);
1124     }
1125 
1126     context.objectInfo->ClearDismissAssetArray();
1127     return true;
1128 }
1129 
MergeAlbumExecute(MediaAlbumChangeRequestAsyncContext & context)1130 static bool MergeAlbumExecute(MediaAlbumChangeRequestAsyncContext& context)
1131 {
1132     MediaLibraryTracer tracer;
1133     tracer.Start("MergeAlbumExecute");
1134 
1135     DataShare::DataSharePredicates predicates;
1136     DataShare::DataShareValuesBucket valuesBucket;
1137     auto photoAlbum = context.objectInfo->GetPhotoAlbumInstance();
1138     auto targetAlum = context.objectInfo->GetTargetPhotoAlbumInstance();
1139     Uri updateAlbumUri(PAH_PORTRAIT_MERGE_ALBUM);
1140     valuesBucket.Put(ALBUM_ID, photoAlbum->GetAlbumId());
1141     int32_t targetAlbumId = -1;
1142     if (targetAlum != nullptr) {
1143         targetAlbumId = targetAlum->GetAlbumId();
1144     }
1145     valuesBucket.Put(TARGET_ALBUM_ID, targetAlbumId);
1146     int32_t result = UserFileClient::Update(updateAlbumUri, predicates, valuesBucket);
1147     if (result < 0) {
1148         context.SaveError(result);
1149         NAPI_ERR_LOG("Failed to merge albums err: %{public}d", result);
1150         return false;
1151     }
1152     return true;
1153 }
1154 
GetAlbumUpdateValue(shared_ptr<PhotoAlbum> & photoAlbum,const AlbumChangeOperation changeOperation,string & uri,DataShare::DataShareValuesBucket & valuesBucket,string & property)1155 static bool GetAlbumUpdateValue(shared_ptr<PhotoAlbum>& photoAlbum, const AlbumChangeOperation changeOperation,
1156     string& uri, DataShare::DataShareValuesBucket& valuesBucket, string& property)
1157 {
1158     if (photoAlbum == nullptr) {
1159         NAPI_ERR_LOG("photoAlbum is null");
1160         return false;
1161     }
1162 
1163     switch (changeOperation) {
1164         case AlbumChangeOperation::SET_ALBUM_NAME:
1165             if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::PORTRAIT) {
1166                 uri = PAH_PORTRAIT_ANAALBUM_ALBUM_NAME;
1167             } else if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::GROUP_PHOTO) {
1168                 uri = PAH_GROUP_ANAALBUM_ALBUM_NAME;
1169             } else {
1170                 uri = PAH_SET_PHOTO_ALBUM_NAME;
1171             }
1172             property = PhotoAlbumColumns::ALBUM_NAME;
1173             valuesBucket.Put(property, photoAlbum->GetAlbumName());
1174             break;
1175         case AlbumChangeOperation::SET_COVER_URI:
1176             if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::PORTRAIT) {
1177                 uri = PAH_PORTRAIT_ANAALBUM_COVER_URI;
1178             } else if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::GROUP_PHOTO) {
1179                 uri = PAH_GROUP_ANAALBUM_COVER_URI;
1180             } else {
1181                 uri = PAH_UPDATE_PHOTO_ALBUM;
1182             }
1183             property = PhotoAlbumColumns::ALBUM_COVER_URI;
1184             valuesBucket.Put(property, photoAlbum->GetCoverUri());
1185             break;
1186         case AlbumChangeOperation::SET_DISPLAY_LEVEL:
1187             uri = PAH_PORTRAIT_DISPLAY_LEVLE;
1188             property = USER_DISPLAY_LEVEL;
1189             valuesBucket.Put(property, photoAlbum->GetDisplayLevel());
1190             break;
1191         case AlbumChangeOperation::SET_IS_ME:
1192             uri = PAH_PORTRAIT_IS_ME;
1193             property = IS_ME;
1194             valuesBucket.Put(property, 1);
1195             break;
1196         case AlbumChangeOperation::DISMISS:
1197             uri = PAH_GROUP_ANAALBUM_DISMISS;
1198             property = IS_REMOVED;
1199             valuesBucket.Put(property, 1);
1200             break;
1201         default:
1202             return false;
1203     }
1204     return true;
1205 }
1206 
SetAlbumPropertyExecute(MediaAlbumChangeRequestAsyncContext & context,const AlbumChangeOperation changeOperation)1207 static bool SetAlbumPropertyExecute(
1208     MediaAlbumChangeRequestAsyncContext& context, const AlbumChangeOperation changeOperation)
1209 {
1210     MediaLibraryTracer tracer;
1211     tracer.Start("SetAlbumPropertyExecute");
1212 
1213     // In the scenario of creation, the new name will be applied when the album is created.
1214     if (changeOperation == AlbumChangeOperation::SET_ALBUM_NAME &&
1215         context.albumChangeOperations.front() == AlbumChangeOperation::CREATE_ALBUM) {
1216         return true;
1217     }
1218 
1219     DataShare::DataSharePredicates predicates;
1220     DataShare::DataShareValuesBucket valuesBucket;
1221     auto photoAlbum = context.objectInfo->GetPhotoAlbumInstance();
1222     predicates.EqualTo(PhotoAlbumColumns::ALBUM_ID, to_string(photoAlbum->GetAlbumId()));
1223     string uri;
1224     string property;
1225     if (!GetAlbumUpdateValue(photoAlbum, changeOperation, uri, valuesBucket, property)) {
1226         context.SaveError(E_FAIL);
1227         NAPI_ERR_LOG("Failed to parse album change operation: %{public}d", changeOperation);
1228         return false;
1229     }
1230     valuesBucket.Put(PhotoAlbumColumns::ALBUM_SUBTYPE, photoAlbum->GetPhotoAlbumSubType());
1231     MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
1232     Uri updateAlbumUri(uri);
1233     int32_t changedRows = UserFileClient::Update(updateAlbumUri, predicates, valuesBucket);
1234     if (changedRows < 0) {
1235         context.SaveError(changedRows);
1236         NAPI_ERR_LOG("Failed to set %{public}s, err: %{public}d", property.c_str(), changedRows);
1237         return false;
1238     }
1239     return true;
1240 }
1241 
1242 static const unordered_map<AlbumChangeOperation, bool (*)(MediaAlbumChangeRequestAsyncContext&)> EXECUTE_MAP = {
1243     { AlbumChangeOperation::CREATE_ALBUM, CreateAlbumExecute },
1244     { AlbumChangeOperation::ADD_ASSETS, AddAssetsExecute },
1245     { AlbumChangeOperation::REMOVE_ASSETS, RemoveAssetsExecute },
1246     { AlbumChangeOperation::MOVE_ASSETS, MoveAssetsExecute },
1247     { AlbumChangeOperation::RECOVER_ASSETS, RecoverAssetsExecute },
1248     { AlbumChangeOperation::DELETE_ASSETS, DeleteAssetsExecute },
1249     { AlbumChangeOperation::ORDER_ALBUM, OrderAlbumExecute },
1250     { AlbumChangeOperation::MERGE_ALBUM, MergeAlbumExecute },
1251     { AlbumChangeOperation::DISMISS_ASSET, DismissAssetExecute },
1252 };
1253 
ApplyAlbumChangeRequestExecute(napi_env env,void * data)1254 static void ApplyAlbumChangeRequestExecute(napi_env env, void* data)
1255 {
1256     MediaLibraryTracer tracer;
1257     tracer.Start("ApplyAlbumChangeRequestExecute");
1258 
1259     auto* context = static_cast<MediaAlbumChangeRequestAsyncContext*>(data);
1260     unordered_set<AlbumChangeOperation> appliedOperations;
1261     for (const auto& changeOperation : context->albumChangeOperations) {
1262         // Keep the final result(s) of each operation, and commit only once.
1263         if (appliedOperations.find(changeOperation) != appliedOperations.end()) {
1264             continue;
1265         }
1266 
1267         bool valid = false;
1268         auto iter = EXECUTE_MAP.find(changeOperation);
1269         if (iter != EXECUTE_MAP.end()) {
1270             valid = iter->second(*context);
1271         } else if (changeOperation == AlbumChangeOperation::SET_ALBUM_NAME ||
1272                    changeOperation == AlbumChangeOperation::SET_COVER_URI ||
1273                    changeOperation == AlbumChangeOperation::SET_IS_ME ||
1274                    changeOperation == AlbumChangeOperation::SET_DISPLAY_LEVEL ||
1275                    changeOperation == AlbumChangeOperation::DISMISS) {
1276             valid = SetAlbumPropertyExecute(*context, changeOperation);
1277         } else {
1278             NAPI_ERR_LOG("Invalid album change operation: %{public}d", changeOperation);
1279             context->error = OHOS_INVALID_PARAM_CODE;
1280             return;
1281         }
1282 
1283         if (!valid) {
1284             NAPI_ERR_LOG("Failed to apply album change request, operation: %{public}d", changeOperation);
1285             return;
1286         }
1287         appliedOperations.insert(changeOperation);
1288     }
1289 }
1290 
ApplyAlbumChangeRequestCompleteCallback(napi_env env,napi_status status,void * data)1291 static void ApplyAlbumChangeRequestCompleteCallback(napi_env env, napi_status status, void* data)
1292 {
1293     MediaLibraryTracer tracer;
1294     tracer.Start("ApplyAlbumChangeRequestCompleteCallback");
1295 
1296     auto* context = static_cast<MediaAlbumChangeRequestAsyncContext*>(data);
1297     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
1298     auto jsContext = make_unique<JSAsyncContextOutput>();
1299     jsContext->status = false;
1300     napi_get_undefined(env, &jsContext->data);
1301     napi_get_undefined(env, &jsContext->error);
1302     if (context->error == ERR_DEFAULT) {
1303         jsContext->status = true;
1304     } else {
1305         context->HandleError(env, jsContext->error);
1306     }
1307 
1308     if (context->work != nullptr) {
1309         MediaLibraryNapiUtils::InvokeJSAsyncMethod(
1310             env, context->deferred, context->callbackRef, context->work, *jsContext);
1311     }
1312     delete context;
1313 }
1314 
CheckPortraitMergeAlbum()1315 bool MediaAlbumChangeRequestNapi::CheckPortraitMergeAlbum()
1316 {
1317     bool hasMergeAlbum = false;
1318     bool hasAlbumName = false;
1319     for (auto operation : albumChangeOperations_) {
1320         if (operation == AlbumChangeOperation::MERGE_ALBUM) {
1321             hasMergeAlbum = true;
1322         }
1323         if (operation == AlbumChangeOperation::SET_ALBUM_NAME) {
1324             hasAlbumName = true;
1325         }
1326     }
1327     return (hasAlbumName && hasMergeAlbum) || (hasMergeAlbum == false);
1328 }
1329 
ApplyChanges(napi_env env,napi_callback_info info)1330 napi_value MediaAlbumChangeRequestNapi::ApplyChanges(napi_env env, napi_callback_info info)
1331 {
1332     constexpr size_t minArgs = ARGS_ONE;
1333     constexpr size_t maxArgs = ARGS_TWO;
1334     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
1335     CHECK_COND_WITH_MESSAGE(env,
1336         MediaLibraryNapiUtils::AsyncContextGetArgs(env, info, asyncContext, minArgs, maxArgs) == napi_ok,
1337         "Failed to get args");
1338     asyncContext->objectInfo = this;
1339     CHECK_COND_WITH_MESSAGE(env, CheckChangeOperations(env), "Failed to check album change request operations");
1340     asyncContext->albumChangeOperations = albumChangeOperations_;
1341     albumChangeOperations_.clear();
1342     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "ApplyMediaAlbumChangeRequest",
1343         ApplyAlbumChangeRequestExecute, ApplyAlbumChangeRequestCompleteCallback);
1344 }
1345 } // namespace OHOS::Media