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     NAPI_INFO_LOG("enter");
537     if (!MediaLibraryNapiUtils::IsSystemApp()) {
538         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
539         return nullptr;
540     }
541 
542     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
543     CHECK_COND_WITH_MESSAGE(env,
544         MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok,
545         "Failed to get object info");
546 
547     auto changeRequest = asyncContext->objectInfo;
548     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
549     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
550     CHECK_COND_WITH_MESSAGE(env,
551         PhotoAlbum::IsTrashAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
552         "Only trash album can delete assets permanently");
553 
554     vector<string> assetUriArray;
555     CHECK_COND_WITH_MESSAGE(env, ParseAssetArray(env, asyncContext->argv[PARAM0], assetUriArray),
556         "Failed to parse assets");
557     if (!CheckDuplicatedAssetArray(assetUriArray, changeRequest->assetsToDelete_)) {
558         NapiError::ThrowError(env, JS_E_OPERATION_NOT_SUPPORT,
559             "The previous deleteAssets operation has contained the same asset");
560         return nullptr;
561     }
562     changeRequest->assetsToDelete_.insert(
563         changeRequest->assetsToDelete_.end(), assetUriArray.begin(), assetUriArray.end());
564     changeRequest->albumChangeOperations_.push_back(AlbumChangeOperation::DELETE_ASSETS);
565     RETURN_NAPI_UNDEFINED(env);
566 }
567 
GetAssetsIdArray(napi_env env,napi_value arg,vector<string> & assetsArray)568 static napi_value GetAssetsIdArray(napi_env env, napi_value arg, vector<string> &assetsArray)
569 {
570     bool isArray = false;
571     CHECK_ARGS(env, napi_is_array(env, arg, &isArray), JS_INNER_FAIL);
572     if (!isArray) {
573         NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check array type");
574         return nullptr;
575     }
576 
577     uint32_t len = 0;
578     CHECK_ARGS(env, napi_get_array_length(env, arg, &len), JS_INNER_FAIL);
579     if (len <= 0) {
580         NAPI_ERR_LOG("Failed to check array length: %{public}u", len);
581         NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to check array length");
582         return nullptr;
583     }
584 
585     for (uint32_t i = 0; i < len; i++) {
586         napi_value asset = nullptr;
587         CHECK_ARGS(env, napi_get_element(env, arg, i, &asset), JS_INNER_FAIL);
588         if (asset == nullptr) {
589             NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to get asset element");
590             return nullptr;
591         }
592 
593         FileAssetNapi *obj = nullptr;
594         CHECK_ARGS(env, napi_unwrap(env, asset, reinterpret_cast<void **>(&obj)), JS_INNER_FAIL);
595         if (obj == nullptr) {
596             NapiError::ThrowError(env, OHOS_INVALID_PARAM_CODE, "Failed to get asset napi object");
597             return nullptr;
598         }
599         if ((obj->GetMediaType() != MEDIA_TYPE_IMAGE && obj->GetMediaType() != MEDIA_TYPE_VIDEO)) {
600             NAPI_INFO_LOG("Skip invalid asset, mediaType: %{public}d", obj->GetMediaType());
601             continue;
602         }
603         assetsArray.push_back(obj->GetFileUri());
604     }
605 
606     napi_value result = nullptr;
607     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
608     return result;
609 }
610 
JSSetIsMe(napi_env env,napi_callback_info info)611 napi_value MediaAlbumChangeRequestNapi::JSSetIsMe(napi_env env, napi_callback_info info)
612 {
613     if (!MediaLibraryNapiUtils::IsSystemApp()) {
614         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
615         return nullptr;
616     }
617     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
618     CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(
619         env, info, asyncContext, ARGS_ZERO, ARGS_ZERO) == napi_ok, "Failed to get object info");
620 
621     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
622     CHECK_COND_WITH_MESSAGE(env,
623         PhotoAlbum::IsSmartPortraitPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
624         "Only portrait album can set is me");
625     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::SET_IS_ME);
626     napi_value result = nullptr;
627     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
628     return result;
629 }
630 
CheckDismissAssetVaild(std::vector<std::string> & dismissAssets,std::vector<std::string> & newAssetArray)631 bool MediaAlbumChangeRequestNapi::CheckDismissAssetVaild(std::vector<std::string> &dismissAssets,
632     std::vector<std::string> &newAssetArray)
633 {
634     if (newAssetArray.empty()) {
635         return false;
636     }
637     unordered_set<string> assetSet(dismissAssets.begin(), dismissAssets.end());
638     unordered_set<string> tempSet;
639     for (const auto& newAsset : newAssetArray) {
640         if (assetSet.find(newAsset) != assetSet.end()) {
641             return false;
642         }
643         tempSet.insert(newAsset);
644     }
645     for (const auto& tmp : tempSet) {
646         dismissAssets.push_back(tmp);
647     }
648     return true;
649 }
650 
JSDismissAssets(napi_env env,napi_callback_info info)651 napi_value MediaAlbumChangeRequestNapi::JSDismissAssets(napi_env env, napi_callback_info info)
652 {
653     if (!MediaLibraryNapiUtils::IsSystemApp()) {
654         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
655         return nullptr;
656     }
657     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
658     CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(
659         env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok, "Failed to get object info");
660 
661     vector<std::string> newAssetArray;
662     CHECK_NULLPTR_RET(GetAssetsIdArray(env, asyncContext->argv[PARAM0], newAssetArray));
663     if (!CheckDismissAssetVaild(asyncContext->objectInfo->dismissAssets_, newAssetArray)) {
664         NapiError::ThrowError(env, JS_E_OPERATION_NOT_SUPPORT, "This dismissAssets is not support");
665         return nullptr;
666     }
667     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
668     auto type = photoAlbum->GetPhotoAlbumType();
669     auto subtype = photoAlbum->GetPhotoAlbumSubType();
670     CHECK_COND_WITH_MESSAGE(env, PhotoAlbum::IsSmartPortraitPhotoAlbum(type, subtype) ||
671         PhotoAlbum::IsSmartGroupPhotoAlbum(type, subtype) || PhotoAlbum::IsSmartClassifyAlbum(type, subtype),
672         "Only portrait, group photo and classify album can dismiss asset");
673 
674     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::DISMISS_ASSET);
675     napi_value result = nullptr;
676     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
677     return result;
678 }
679 
JSMergeAlbum(napi_env env,napi_callback_info info)680 napi_value MediaAlbumChangeRequestNapi::JSMergeAlbum(napi_env env, napi_callback_info info)
681 {
682     if (!MediaLibraryNapiUtils::IsSystemApp()) {
683         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
684         return nullptr;
685     }
686     napi_valuetype valueType;
687     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
688 
689     CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(
690         env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok, "Failed to get object info");
691     CHECK_ARGS(env, napi_typeof(env, asyncContext->argv[PARAM0], &valueType), JS_INNER_FAIL);
692     CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
693     if (valueType == napi_object) {
694         PhotoAlbumNapi* photoAlbumNapi;
695         CHECK_ARGS(env, napi_unwrap(env, asyncContext->argv[PARAM0],
696             reinterpret_cast<void**>(&photoAlbumNapi)), JS_INNER_FAIL);
697         CHECK_COND_WITH_MESSAGE(env, photoAlbumNapi != nullptr, "Failed to get PhotoAlbumNapi object");
698         asyncContext->objectInfo->targetAlbum_ = photoAlbumNapi->GetPhotoAlbumInstance();
699     }
700     auto photoAlbum = asyncContext->objectInfo->photoAlbum_;
701     auto targetAlbum = asyncContext->objectInfo->targetAlbum_;
702     CHECK_COND_WITH_MESSAGE(env,
703         (photoAlbum != nullptr) && (targetAlbum != nullptr), "PhotoAlbum  Or TargetAlbum is nullptr");
704     CHECK_COND_WITH_MESSAGE(env,
705         (PhotoAlbum::IsSmartPortraitPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType())) &&
706         (PhotoAlbum::IsSmartPortraitPhotoAlbum(targetAlbum->GetPhotoAlbumType(), targetAlbum->GetPhotoAlbumSubType())),
707         "Only portrait album can merge");
708     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::MERGE_ALBUM);
709     napi_value result = nullptr;
710     CHECK_ARGS(env, napi_get_undefined(env, &result), JS_INNER_FAIL);
711     return result;
712 }
713 
JSSetDisplayLevel(napi_env env,napi_callback_info info)714 napi_value MediaAlbumChangeRequestNapi::JSSetDisplayLevel(napi_env env, napi_callback_info info)
715 {
716     if (!MediaLibraryNapiUtils::IsSystemApp()) {
717         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
718         return nullptr;
719     }
720     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
721     int32_t displayLevel;
722     CHECK_COND_WITH_MESSAGE(env,
723         MediaLibraryNapiUtils::ParseArgsNumberCallback(env, info, asyncContext, displayLevel) == napi_ok,
724         "Failed to parse args");
725     CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
726     CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckDisplayLevel(displayLevel), "Invalid display level");
727 
728     auto photoAlbum = asyncContext->objectInfo->photoAlbum_;
729     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "PhotoAlbum is nullptr");
730     CHECK_COND_WITH_MESSAGE(env,
731         PhotoAlbum::IsSmartPortraitPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
732         "Only portrait album can set album display level");
733     photoAlbum->SetDisplayLevel(displayLevel);
734     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::SET_DISPLAY_LEVEL);
735 
736     napi_value result = nullptr;
737     CHECK_ARGS(env, napi_get_undefined(env, &result), JS_INNER_FAIL);
738     return result;
739 }
740 
JSDismiss(napi_env env,napi_callback_info info)741 napi_value MediaAlbumChangeRequestNapi::JSDismiss(napi_env env, napi_callback_info info)
742 {
743     if (!MediaLibraryNapiUtils::IsSystemApp()) {
744         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
745         return nullptr;
746     }
747     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
748     CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(
749         env, info, asyncContext, ARGS_ZERO, ARGS_ZERO) == napi_ok, "Failed to get object info");
750 
751     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
752     CHECK_COND_WITH_MESSAGE(env,
753         PhotoAlbum::IsSmartGroupPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
754         "Only group photo can be dismissed");
755     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::DISMISS);
756     napi_value result = nullptr;
757     CHECK_ARGS(env, napi_get_boolean(env, true, &result), JS_INNER_FAIL);
758     return result;
759 }
760 
JSSetAlbumName(napi_env env,napi_callback_info info)761 napi_value MediaAlbumChangeRequestNapi::JSSetAlbumName(napi_env env, napi_callback_info info)
762 {
763     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
764     string albumName;
765     CHECK_COND_WITH_MESSAGE(env,
766         MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, asyncContext, albumName) == napi_ok,
767         "Failed to parse args");
768     CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
769     CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckAlbumName(albumName) == E_OK, "Invalid album name");
770 
771     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
772     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
773     CHECK_COND_WITH_MESSAGE(env,
774         PhotoAlbum::IsUserPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()) ||
775         PhotoAlbum::IsSmartPortraitPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()) ||
776         PhotoAlbum::IsSmartGroupPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
777         "Only user album, smart portrait album and group photo can set album name");
778     photoAlbum->SetAlbumName(albumName);
779     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::SET_ALBUM_NAME);
780     RETURN_NAPI_UNDEFINED(env);
781 }
782 
JSSetCoverUri(napi_env env,napi_callback_info info)783 napi_value MediaAlbumChangeRequestNapi::JSSetCoverUri(napi_env env, napi_callback_info info)
784 {
785     if (!MediaLibraryNapiUtils::IsSystemApp()) {
786         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
787         return nullptr;
788     }
789 
790     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
791     string coverUri;
792     CHECK_COND_WITH_MESSAGE(env,
793         MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, asyncContext, coverUri) == napi_ok,
794         "Failed to parse args");
795     CHECK_COND_WITH_MESSAGE(env, asyncContext->argc == ARGS_ONE, "Number of args is invalid");
796 
797     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
798     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
799     CHECK_COND_WITH_MESSAGE(env,
800         PhotoAlbum::IsUserPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()) ||
801         PhotoAlbum::IsSmartPortraitPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()) ||
802         PhotoAlbum::IsSmartGroupPhotoAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
803         "Only user album, smart portrait album and group photo can set album name");
804     photoAlbum->SetCoverUri(coverUri);
805     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::SET_COVER_URI);
806     RETURN_NAPI_UNDEFINED(env);
807 }
808 
JSPlaceBefore(napi_env env,napi_callback_info info)809 napi_value MediaAlbumChangeRequestNapi::JSPlaceBefore(napi_env env, napi_callback_info info)
810 {
811     if (!MediaLibraryNapiUtils::IsSystemApp()) {
812         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
813         return nullptr;
814     }
815     napi_valuetype valueType;
816     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
817 
818     CHECK_COND_WITH_MESSAGE(env, MediaLibraryNapiUtils::AsyncContextSetObjectInfo(
819         env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok, "Failed to get object info");
820     CHECK_ARGS(env, napi_typeof(env, asyncContext->argv[PARAM0], &valueType), JS_INNER_FAIL);
821     CHECK_COND_WITH_MESSAGE(env, valueType == napi_object || valueType == napi_null, "Invalid argument type");
822     if (valueType == napi_object) {
823         PhotoAlbumNapi* photoAlbumNapi;
824         CHECK_ARGS(env, napi_unwrap(env, asyncContext->argv[PARAM0],
825             reinterpret_cast<void**>(&photoAlbumNapi)), JS_INNER_FAIL);
826         CHECK_COND_WITH_MESSAGE(env, photoAlbumNapi != nullptr, "Failed to get PhotoAlbumNapi object");
827         asyncContext->objectInfo->referencePhotoAlbum_ = photoAlbumNapi->GetPhotoAlbumInstance();
828     }
829     asyncContext->objectInfo->albumChangeOperations_.push_back(AlbumChangeOperation::ORDER_ALBUM);
830     RETURN_NAPI_UNDEFINED(env);
831 }
832 
CreateAlbumExecute(MediaAlbumChangeRequestAsyncContext & context)833 static bool CreateAlbumExecute(MediaAlbumChangeRequestAsyncContext& context)
834 {
835     MediaLibraryTracer tracer;
836     tracer.Start("CreateAlbumExecute");
837 
838     auto changeRequest = context.objectInfo;
839     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
840 
841     Uri createAlbumUri(PAH_CREATE_PHOTO_ALBUM);
842     DataShare::DataShareValuesBucket valuesBucket;
843     valuesBucket.Put(PhotoAlbumColumns::ALBUM_NAME, photoAlbum->GetAlbumName());
844     int32_t ret = UserFileClient::Insert(createAlbumUri, valuesBucket);
845     if (ret == -1) {
846         context.SaveError(-EEXIST);
847         NAPI_ERR_LOG("Album exists");
848         return false;
849     }
850     if (ret < 0) {
851         context.SaveError(ret);
852         NAPI_ERR_LOG("Failed to create album, ret: %{public}d", ret);
853         return false;
854     }
855 
856     photoAlbum->SetAlbumId(ret);
857     photoAlbum->SetAlbumUri(PhotoAlbumColumns::ALBUM_URI_PREFIX + to_string(ret));
858     return true;
859 }
860 
FetchNewCount(MediaAlbumChangeRequestAsyncContext & context,shared_ptr<PhotoAlbum> & album)861 static bool FetchNewCount(MediaAlbumChangeRequestAsyncContext& context, shared_ptr<PhotoAlbum>& album)
862 {
863     if (album == nullptr) {
864         NAPI_ERR_LOG("Album is null");
865         context.SaveError(E_FAIL);
866         return false;
867     }
868 
869     Uri queryUri(PAH_QUERY_PHOTO_ALBUM);
870     DataShare::DataSharePredicates predicates;
871     predicates.EqualTo(PhotoAlbumColumns::ALBUM_ID, album->GetAlbumId());
872     vector<string> fetchColumns = { PhotoAlbumColumns::ALBUM_ID, PhotoAlbumColumns::ALBUM_COUNT,
873         PhotoAlbumColumns::ALBUM_IMAGE_COUNT, PhotoAlbumColumns::ALBUM_VIDEO_COUNT };
874     int errCode = 0;
875     auto resultSet = UserFileClient::Query(queryUri, predicates, fetchColumns, errCode);
876     if (resultSet == nullptr) {
877         NAPI_ERR_LOG("resultSet == nullptr, errCode is %{public}d", errCode);
878         context.SaveError(E_HAS_DB_ERROR);
879         return false;
880     }
881     if (resultSet->GoToFirstRow() != 0) {
882         NAPI_ERR_LOG("go to first row failed when fetch new count");
883         context.SaveError(E_HAS_DB_ERROR);
884         return false;
885     }
886 
887     bool hiddenOnly = album->GetHiddenOnly();
888     int imageCount = hiddenOnly ? -1 :
889         get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_IMAGE_COUNT, resultSet, TYPE_INT32));
890     int videoCount = hiddenOnly ? -1 :
891         get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_VIDEO_COUNT, resultSet, TYPE_INT32));
892     album->SetCount(
893         get<int32_t>(ResultSetUtils::GetValFromColumn(PhotoAlbumColumns::ALBUM_COUNT, resultSet, TYPE_INT32)));
894     album->SetImageCount(imageCount);
895     album->SetVideoCount(videoCount);
896     return true;
897 }
898 
AddAssetsExecute(MediaAlbumChangeRequestAsyncContext & context)899 static bool AddAssetsExecute(MediaAlbumChangeRequestAsyncContext& context)
900 {
901     MediaLibraryTracer tracer;
902     tracer.Start("AddAssetsExecute");
903 
904     auto changeRequest = context.objectInfo;
905     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
906     int32_t albumId = photoAlbum->GetAlbumId();
907     vector<DataShare::DataShareValuesBucket> valuesBuckets;
908     for (const auto& asset : changeRequest->GetAddAssetArray()) {
909         DataShare::DataShareValuesBucket pair;
910         pair.Put(PhotoColumn::PHOTO_OWNER_ALBUM_ID, albumId);
911         pair.Put(PhotoColumn::MEDIA_ID, asset);
912         valuesBuckets.push_back(pair);
913     }
914 
915     Uri addAssetsUri(PAH_PHOTO_ALBUM_ADD_ASSET);
916     int ret = UserFileClient::BatchInsert(addAssetsUri, valuesBuckets);
917     changeRequest->ClearAddAssetArray();
918     if (ret < 0) {
919         context.SaveError(ret);
920         NAPI_ERR_LOG("Failed to add assets into album %{public}d, err: %{public}d", albumId, ret);
921         return false;
922     }
923 
924     NAPI_INFO_LOG("Add %{public}d asset(s) into album %{public}d", ret, albumId);
925     FetchNewCount(context, photoAlbum);
926     return true;
927 }
928 
RemoveAssetsExecute(MediaAlbumChangeRequestAsyncContext & context)929 static bool RemoveAssetsExecute(MediaAlbumChangeRequestAsyncContext& context)
930 {
931     MediaLibraryTracer tracer;
932     tracer.Start("RemoveAssetsExecute");
933 
934     auto changeRequest = context.objectInfo;
935     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
936     int32_t albumId = photoAlbum->GetAlbumId();
937     DataShare::DataSharePredicates predicates;
938     predicates.EqualTo(PhotoColumn::PHOTO_OWNER_ALBUM_ID, to_string(albumId));
939     predicates.And()->In(PhotoColumn::MEDIA_ID, changeRequest->GetRemoveAssetArray());
940 
941     Uri removeAssetsUri(PAH_PHOTO_ALBUM_REMOVE_ASSET);
942     int ret = UserFileClient::Delete(removeAssetsUri, predicates);
943     changeRequest->ClearRemoveAssetArray();
944     if (ret < 0) {
945         context.SaveError(ret);
946         NAPI_ERR_LOG("Failed to remove assets from album %{public}d, err: %{public}d", albumId, ret);
947         return false;
948     }
949 
950     NAPI_INFO_LOG("Remove %{public}d asset(s) from album %{public}d", ret, albumId);
951     FetchNewCount(context, photoAlbum);
952     return true;
953 }
954 
MoveAssetsExecute(MediaAlbumChangeRequestAsyncContext & context)955 static bool MoveAssetsExecute(MediaAlbumChangeRequestAsyncContext& context)
956 {
957     MediaLibraryTracer tracer;
958     tracer.Start("MoveAssetsExecute");
959 
960     auto changeRequest = context.objectInfo;
961     auto photoAlbum = changeRequest->GetPhotoAlbumInstance();
962     int32_t albumId = photoAlbum->GetAlbumId();
963     auto moveMap = changeRequest->GetMoveMap();
964     changeRequest->ClearMoveMap();
965 
966     for (auto iter = moveMap.begin(); iter != moveMap.end(); iter++) {
967         auto targetPhotoAlbum = iter->first;
968         int32_t targetAlbumId = targetPhotoAlbum->GetAlbumId();
969         vector<string> moveAssetArray = iter->second;
970         // Move into target album.
971         DataShare::DataSharePredicates predicates;
972         predicates.EqualTo(PhotoColumn::PHOTO_OWNER_ALBUM_ID, to_string(albumId));
973         predicates.And()->In(PhotoColumn::MEDIA_ID, moveAssetArray);
974 
975         DataShare::DataShareValuesBucket valuesBuckets;
976         valuesBuckets.Put(PhotoColumn::PHOTO_OWNER_ALBUM_ID, targetAlbumId);
977         string uri = PAH_BATCH_UPDATE_OWNER_ALBUM_ID;
978         MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
979         Uri moveAssetsUri(uri);
980         int ret = UserFileClient::Update(moveAssetsUri, predicates, valuesBuckets);
981         if (ret < 0) {
982             context.SaveError(ret);
983             NAPI_ERR_LOG("Failed to move assets into album %{public}d, err: %{public}d", targetAlbumId, ret);
984             return false;
985         }
986         NAPI_INFO_LOG("Move %{public}d asset(s) into album %{public}d", ret, targetAlbumId);
987         FetchNewCount(context, targetPhotoAlbum);
988     }
989     FetchNewCount(context, photoAlbum);
990     return true;
991 }
992 
RecoverAssetsExecute(MediaAlbumChangeRequestAsyncContext & context)993 static bool RecoverAssetsExecute(MediaAlbumChangeRequestAsyncContext& context)
994 {
995     MediaLibraryTracer tracer;
996     tracer.Start("RecoverAssetsExecute");
997 
998     DataShare::DataSharePredicates predicates;
999     DataShare::DataShareValuesBucket valuesBucket;
1000     predicates.In(PhotoColumn::MEDIA_ID, context.objectInfo->GetRecoverAssetArray());
1001     valuesBucket.Put(PhotoColumn::MEDIA_DATE_TRASHED, 0);
1002 
1003     Uri recoverAssetsUri(PAH_RECOVER_PHOTOS);
1004     int ret = UserFileClient::Update(recoverAssetsUri, predicates, valuesBucket);
1005     context.objectInfo->ClearRecoverAssetArray();
1006     if (ret < 0) {
1007         context.SaveError(ret);
1008         NAPI_ERR_LOG("Failed to recover assets, err: %{public}d", ret);
1009         return false;
1010     }
1011 
1012     NAPI_INFO_LOG("Recover %{public}d assets from trash album", ret);
1013     auto photoAlbum = context.objectInfo->GetPhotoAlbumInstance();
1014     int32_t currentCount = photoAlbum->GetCount() - ret;
1015     photoAlbum->SetCount(currentCount > 0 ? currentCount : 0);
1016     return true;
1017 }
1018 
DeleteAssetsExecute(MediaAlbumChangeRequestAsyncContext & context)1019 static bool DeleteAssetsExecute(MediaAlbumChangeRequestAsyncContext& context)
1020 {
1021     MediaLibraryTracer tracer;
1022     tracer.Start("DeleteAssetsExecute");
1023 
1024     DataShare::DataSharePredicates predicates;
1025     predicates.In(PhotoColumn::MEDIA_ID, context.objectInfo->GetDeleteAssetArray());
1026     DataShare::DataShareValuesBucket valuesBucket;
1027     valuesBucket.Put(PhotoColumn::MEDIA_DATE_TRASHED, 0);
1028 
1029     Uri deleteAssetsUri(PAH_DELETE_PHOTOS);
1030     int ret = UserFileClient::Update(deleteAssetsUri, predicates, valuesBucket);
1031     context.objectInfo->ClearDeleteAssetArray();
1032     if (ret < 0) {
1033         context.SaveError(ret);
1034         NAPI_ERR_LOG("Failed to delete assets from trash album permanently, err: %{public}d", ret);
1035         return false;
1036     }
1037 
1038     NAPI_INFO_LOG("Delete %{public}d assets permanently from trash album", ret);
1039     auto photoAlbum = context.objectInfo->GetPhotoAlbumInstance();
1040     int32_t currentCount = photoAlbum->GetCount() - ret;
1041     photoAlbum->SetCount(currentCount > 0 ? currentCount : 0);
1042     return true;
1043 }
1044 
OrderAlbumExecute(MediaAlbumChangeRequestAsyncContext & context)1045 static bool OrderAlbumExecute(MediaAlbumChangeRequestAsyncContext& context)
1046 {
1047     MediaLibraryTracer tracer;
1048     tracer.Start("OrderAlbumExecute");
1049 
1050     DataShare::DataSharePredicates predicates;
1051     DataShare::DataShareValuesBucket valuesBucket;
1052     auto photoAlbum = context.objectInfo->GetPhotoAlbumInstance();
1053     auto referenceAlum = context.objectInfo->GetReferencePhotoAlbumInstance();
1054     Uri updateAlbumUri(PAH_ORDER_ALBUM);
1055     valuesBucket.Put(PhotoAlbumColumns::ALBUM_ID, photoAlbum->GetAlbumId());
1056     int32_t referenceAlbumId = -1;
1057     if (referenceAlum != nullptr) {
1058         referenceAlbumId = referenceAlum->GetAlbumId();
1059     }
1060     valuesBucket.Put(PhotoAlbumColumns::REFERENCE_ALBUM_ID, referenceAlbumId);
1061     valuesBucket.Put(PhotoAlbumColumns::ALBUM_TYPE, photoAlbum->GetPhotoAlbumType());
1062     valuesBucket.Put(PhotoAlbumColumns::ALBUM_SUBTYPE, photoAlbum->GetPhotoAlbumSubType());
1063     int32_t result = UserFileClient::Update(updateAlbumUri, predicates, valuesBucket);
1064     if (result < 0) {
1065         context.SaveError(result);
1066         NAPI_ERR_LOG("Failed to order albums err: %{public}d", result);
1067         return false;
1068     }
1069     return true;
1070 }
1071 
UpdateTabAnalysisImageFace(std::shared_ptr<PhotoAlbum> & photoAlbum,MediaAlbumChangeRequestAsyncContext & context)1072 static void UpdateTabAnalysisImageFace(std::shared_ptr<PhotoAlbum>& photoAlbum,
1073     MediaAlbumChangeRequestAsyncContext& context)
1074 {
1075     if (photoAlbum->GetPhotoAlbumSubType() != PhotoAlbumSubType::PORTRAIT) {
1076         return;
1077     }
1078 
1079     std::string updateUri = PAH_UPDATE_ANA_FACE;
1080     MediaLibraryNapiUtils::UriAppendKeyValue(updateUri, API_VERSION, std::to_string(MEDIA_API_VERSION_V10));
1081     MediaLibraryNapiUtils::UriAppendKeyValue(updateUri, MEDIA_OPERN_KEYWORD, UPDATE_DISMISS_ASSET);
1082     Uri updateFaceUri(updateUri);
1083 
1084     DataShare::DataShareValuesBucket updateValues;
1085     updateValues.Put(MediaAlbumChangeRequestNapi::TAG_ID,
1086         std::to_string(MediaAlbumChangeRequestNapi::PORTRAIT_REMOVED));
1087 
1088     DataShare::DataSharePredicates updatePredicates;
1089     std::vector<std::string> dismissAssetArray = context.objectInfo->GetDismissAssetArray();
1090     std::string selection = std::to_string(photoAlbum->GetAlbumId());
1091     for (size_t i = 0; i < dismissAssetArray.size(); ++i) {
1092         selection += "," + dismissAssetArray[i];
1093     }
1094     updatePredicates.SetWhereClause(selection);
1095     int updatedRows = UserFileClient::Update(updateFaceUri, updatePredicates, updateValues);
1096     if (updatedRows <= 0) {
1097         NAPI_WARN_LOG("Failed to update tab_analysis_image_face, err: %{public}d", updatedRows);
1098     }
1099 }
1100 
DismissAssetExecute(MediaAlbumChangeRequestAsyncContext & context)1101 static bool DismissAssetExecute(MediaAlbumChangeRequestAsyncContext& context)
1102 {
1103     MediaLibraryTracer tracer;
1104     tracer.Start("DismissAssetExecute");
1105 
1106     string disMissAssetAssetsUri = PAH_DISMISS_ASSET;
1107     Uri uri(disMissAssetAssetsUri);
1108 
1109     auto photoAlbum = context.objectInfo->GetPhotoAlbumInstance();
1110     DataShare::DataSharePredicates predicates;
1111     predicates.EqualTo(MAP_ALBUM, to_string(photoAlbum->GetAlbumId()));
1112     predicates.And()->In(MAP_ASSET, context.objectInfo->GetDismissAssetArray());
1113     predicates.And()->EqualTo(ALBUM_SUBTYPE, to_string(photoAlbum->GetPhotoAlbumSubType()));
1114 
1115     auto deletedRows = UserFileClient::Delete(uri, predicates);
1116     if (deletedRows < 0) {
1117         context.SaveError(deletedRows);
1118         NAPI_ERR_LOG("Failed to dismiss asset err: %{public}d", deletedRows);
1119         return false;
1120     }
1121 
1122     /* 只针对人像相册更新 tab_analysis_image_face 表 */
1123     if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::PORTRAIT) {
1124         UpdateTabAnalysisImageFace(photoAlbum, context);
1125     }
1126 
1127     context.objectInfo->ClearDismissAssetArray();
1128     return true;
1129 }
1130 
MergeAlbumExecute(MediaAlbumChangeRequestAsyncContext & context)1131 static bool MergeAlbumExecute(MediaAlbumChangeRequestAsyncContext& context)
1132 {
1133     MediaLibraryTracer tracer;
1134     tracer.Start("MergeAlbumExecute");
1135 
1136     DataShare::DataSharePredicates predicates;
1137     DataShare::DataShareValuesBucket valuesBucket;
1138     auto photoAlbum = context.objectInfo->GetPhotoAlbumInstance();
1139     auto targetAlum = context.objectInfo->GetTargetPhotoAlbumInstance();
1140     Uri updateAlbumUri(PAH_PORTRAIT_MERGE_ALBUM);
1141     valuesBucket.Put(ALBUM_ID, photoAlbum->GetAlbumId());
1142     int32_t targetAlbumId = -1;
1143     if (targetAlum != nullptr) {
1144         targetAlbumId = targetAlum->GetAlbumId();
1145     }
1146     valuesBucket.Put(TARGET_ALBUM_ID, targetAlbumId);
1147     int32_t result = UserFileClient::Update(updateAlbumUri, predicates, valuesBucket);
1148     if (result < 0) {
1149         context.SaveError(result);
1150         NAPI_ERR_LOG("Failed to merge albums err: %{public}d", result);
1151         return false;
1152     }
1153     return true;
1154 }
1155 
GetAlbumUpdateValue(shared_ptr<PhotoAlbum> & photoAlbum,const AlbumChangeOperation changeOperation,string & uri,DataShare::DataShareValuesBucket & valuesBucket,string & property)1156 static bool GetAlbumUpdateValue(shared_ptr<PhotoAlbum>& photoAlbum, const AlbumChangeOperation changeOperation,
1157     string& uri, DataShare::DataShareValuesBucket& valuesBucket, string& property)
1158 {
1159     if (photoAlbum == nullptr) {
1160         NAPI_ERR_LOG("photoAlbum is null");
1161         return false;
1162     }
1163 
1164     switch (changeOperation) {
1165         case AlbumChangeOperation::SET_ALBUM_NAME:
1166             if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::PORTRAIT) {
1167                 uri = PAH_PORTRAIT_ANAALBUM_ALBUM_NAME;
1168             } else if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::GROUP_PHOTO) {
1169                 uri = PAH_GROUP_ANAALBUM_ALBUM_NAME;
1170             } else {
1171                 uri = PAH_SET_PHOTO_ALBUM_NAME;
1172             }
1173             property = PhotoAlbumColumns::ALBUM_NAME;
1174             valuesBucket.Put(property, photoAlbum->GetAlbumName());
1175             break;
1176         case AlbumChangeOperation::SET_COVER_URI:
1177             if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::PORTRAIT) {
1178                 uri = PAH_PORTRAIT_ANAALBUM_COVER_URI;
1179             } else if (photoAlbum->GetPhotoAlbumSubType() == PhotoAlbumSubType::GROUP_PHOTO) {
1180                 uri = PAH_GROUP_ANAALBUM_COVER_URI;
1181             } else {
1182                 uri = PAH_UPDATE_PHOTO_ALBUM;
1183             }
1184             property = PhotoAlbumColumns::ALBUM_COVER_URI;
1185             valuesBucket.Put(property, photoAlbum->GetCoverUri());
1186             break;
1187         case AlbumChangeOperation::SET_DISPLAY_LEVEL:
1188             uri = PAH_PORTRAIT_DISPLAY_LEVLE;
1189             property = USER_DISPLAY_LEVEL;
1190             valuesBucket.Put(property, photoAlbum->GetDisplayLevel());
1191             break;
1192         case AlbumChangeOperation::SET_IS_ME:
1193             uri = PAH_PORTRAIT_IS_ME;
1194             property = IS_ME;
1195             valuesBucket.Put(property, 1);
1196             break;
1197         case AlbumChangeOperation::DISMISS:
1198             uri = PAH_GROUP_ANAALBUM_DISMISS;
1199             property = IS_REMOVED;
1200             valuesBucket.Put(property, 1);
1201             break;
1202         default:
1203             return false;
1204     }
1205     return true;
1206 }
1207 
SetAlbumPropertyExecute(MediaAlbumChangeRequestAsyncContext & context,const AlbumChangeOperation changeOperation)1208 static bool SetAlbumPropertyExecute(
1209     MediaAlbumChangeRequestAsyncContext& context, const AlbumChangeOperation changeOperation)
1210 {
1211     MediaLibraryTracer tracer;
1212     tracer.Start("SetAlbumPropertyExecute");
1213 
1214     // In the scenario of creation, the new name will be applied when the album is created.
1215     if (changeOperation == AlbumChangeOperation::SET_ALBUM_NAME &&
1216         context.albumChangeOperations.front() == AlbumChangeOperation::CREATE_ALBUM) {
1217         return true;
1218     }
1219 
1220     DataShare::DataSharePredicates predicates;
1221     DataShare::DataShareValuesBucket valuesBucket;
1222     auto photoAlbum = context.objectInfo->GetPhotoAlbumInstance();
1223     predicates.EqualTo(PhotoAlbumColumns::ALBUM_ID, to_string(photoAlbum->GetAlbumId()));
1224     string uri;
1225     string property;
1226     if (!GetAlbumUpdateValue(photoAlbum, changeOperation, uri, valuesBucket, property)) {
1227         context.SaveError(E_FAIL);
1228         NAPI_ERR_LOG("Failed to parse album change operation: %{public}d", changeOperation);
1229         return false;
1230     }
1231     valuesBucket.Put(PhotoAlbumColumns::ALBUM_SUBTYPE, photoAlbum->GetPhotoAlbumSubType());
1232     MediaLibraryNapiUtils::UriAppendKeyValue(uri, API_VERSION, to_string(MEDIA_API_VERSION_V10));
1233     Uri updateAlbumUri(uri);
1234     int32_t changedRows = UserFileClient::Update(updateAlbumUri, predicates, valuesBucket);
1235     if (changedRows < 0) {
1236         context.SaveError(changedRows);
1237         NAPI_ERR_LOG("Failed to set %{public}s, err: %{public}d", property.c_str(), changedRows);
1238         return false;
1239     }
1240     return true;
1241 }
1242 
1243 static const unordered_map<AlbumChangeOperation, bool (*)(MediaAlbumChangeRequestAsyncContext&)> EXECUTE_MAP = {
1244     { AlbumChangeOperation::CREATE_ALBUM, CreateAlbumExecute },
1245     { AlbumChangeOperation::ADD_ASSETS, AddAssetsExecute },
1246     { AlbumChangeOperation::REMOVE_ASSETS, RemoveAssetsExecute },
1247     { AlbumChangeOperation::MOVE_ASSETS, MoveAssetsExecute },
1248     { AlbumChangeOperation::RECOVER_ASSETS, RecoverAssetsExecute },
1249     { AlbumChangeOperation::DELETE_ASSETS, DeleteAssetsExecute },
1250     { AlbumChangeOperation::ORDER_ALBUM, OrderAlbumExecute },
1251     { AlbumChangeOperation::MERGE_ALBUM, MergeAlbumExecute },
1252     { AlbumChangeOperation::DISMISS_ASSET, DismissAssetExecute },
1253 };
1254 
ApplyAlbumChangeRequestExecute(napi_env env,void * data)1255 static void ApplyAlbumChangeRequestExecute(napi_env env, void* data)
1256 {
1257     MediaLibraryTracer tracer;
1258     tracer.Start("ApplyAlbumChangeRequestExecute");
1259 
1260     auto* context = static_cast<MediaAlbumChangeRequestAsyncContext*>(data);
1261     unordered_set<AlbumChangeOperation> appliedOperations;
1262     for (const auto& changeOperation : context->albumChangeOperations) {
1263         // Keep the final result(s) of each operation, and commit only once.
1264         if (appliedOperations.find(changeOperation) != appliedOperations.end()) {
1265             continue;
1266         }
1267 
1268         bool valid = false;
1269         auto iter = EXECUTE_MAP.find(changeOperation);
1270         if (iter != EXECUTE_MAP.end()) {
1271             valid = iter->second(*context);
1272         } else if (changeOperation == AlbumChangeOperation::SET_ALBUM_NAME ||
1273                    changeOperation == AlbumChangeOperation::SET_COVER_URI ||
1274                    changeOperation == AlbumChangeOperation::SET_IS_ME ||
1275                    changeOperation == AlbumChangeOperation::SET_DISPLAY_LEVEL ||
1276                    changeOperation == AlbumChangeOperation::DISMISS) {
1277             valid = SetAlbumPropertyExecute(*context, changeOperation);
1278         } else {
1279             NAPI_ERR_LOG("Invalid album change operation: %{public}d", changeOperation);
1280             context->error = OHOS_INVALID_PARAM_CODE;
1281             return;
1282         }
1283 
1284         if (!valid) {
1285             NAPI_ERR_LOG("Failed to apply album change request, operation: %{public}d", changeOperation);
1286             return;
1287         }
1288         appliedOperations.insert(changeOperation);
1289     }
1290 }
1291 
ApplyAlbumChangeRequestCompleteCallback(napi_env env,napi_status status,void * data)1292 static void ApplyAlbumChangeRequestCompleteCallback(napi_env env, napi_status status, void* data)
1293 {
1294     MediaLibraryTracer tracer;
1295     tracer.Start("ApplyAlbumChangeRequestCompleteCallback");
1296 
1297     auto* context = static_cast<MediaAlbumChangeRequestAsyncContext*>(data);
1298     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
1299     auto jsContext = make_unique<JSAsyncContextOutput>();
1300     jsContext->status = false;
1301     napi_get_undefined(env, &jsContext->data);
1302     napi_get_undefined(env, &jsContext->error);
1303     if (context->error == ERR_DEFAULT) {
1304         jsContext->status = true;
1305     } else {
1306         context->HandleError(env, jsContext->error);
1307     }
1308 
1309     if (context->work != nullptr) {
1310         MediaLibraryNapiUtils::InvokeJSAsyncMethod(
1311             env, context->deferred, context->callbackRef, context->work, *jsContext);
1312     }
1313     delete context;
1314 }
1315 
CheckPortraitMergeAlbum()1316 bool MediaAlbumChangeRequestNapi::CheckPortraitMergeAlbum()
1317 {
1318     bool hasMergeAlbum = false;
1319     bool hasAlbumName = false;
1320     for (auto operation : albumChangeOperations_) {
1321         if (operation == AlbumChangeOperation::MERGE_ALBUM) {
1322             hasMergeAlbum = true;
1323         }
1324         if (operation == AlbumChangeOperation::SET_ALBUM_NAME) {
1325             hasAlbumName = true;
1326         }
1327     }
1328     return (hasAlbumName && hasMergeAlbum) || (hasMergeAlbum == false);
1329 }
1330 
ApplyChanges(napi_env env,napi_callback_info info)1331 napi_value MediaAlbumChangeRequestNapi::ApplyChanges(napi_env env, napi_callback_info info)
1332 {
1333     constexpr size_t minArgs = ARGS_ONE;
1334     constexpr size_t maxArgs = ARGS_TWO;
1335     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
1336     CHECK_COND_WITH_MESSAGE(env,
1337         MediaLibraryNapiUtils::AsyncContextGetArgs(env, info, asyncContext, minArgs, maxArgs) == napi_ok,
1338         "Failed to get args");
1339     asyncContext->objectInfo = this;
1340     CHECK_COND_WITH_MESSAGE(env, CheckChangeOperations(env), "Failed to check album change request operations");
1341     asyncContext->albumChangeOperations = albumChangeOperations_;
1342     albumChangeOperations_.clear();
1343     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "ApplyMediaAlbumChangeRequest",
1344         ApplyAlbumChangeRequestExecute, ApplyAlbumChangeRequestCompleteCallback);
1345 }
1346 } // namespace OHOS::Media