• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #define MLOG_TAG "HighlightAlbumNapi"
17 
18 #include <unordered_map>
19 #include <unordered_set>
20 
21 #include "highlight_album_napi.h"
22 #include "file_asset_napi.h"
23 #include "media_file_utils.h"
24 #include "medialibrary_client_errno.h"
25 #include "medialibrary_napi_log.h"
26 #include "medialibrary_tracer.h"
27 #include "media_album_change_request_napi.h"
28 #include "photo_album.h"
29 #include "photo_album_napi.h"
30 #include "photo_map_column.h"
31 #include "result_set_utils.h"
32 #include "userfile_client.h"
33 #include "vision_column.h"
34 #include "story_album_column.h"
35 #include "story_cover_info_column.h"
36 #include "story_play_info_column.h"
37 #include "user_photography_info_column.h"
38 #include "vision_photo_map_column.h"
39 #include "highlight_column.h"
40 #include "album_operation_uri.h"
41 #include "user_define_ipc_client.h"
42 #include "medialibrary_business_code.h"
43 #include "delete_highlight_albums_vo.h"
44 #include "set_subtitle_vo.h"
45 #include "set_highlight_user_action_data_vo.h"
46 #include "get_order_position_vo.h"
47 #include "get_highlight_album_info_vo.h"
48 #include "query_result_vo.h"
49 
50 using namespace std;
51 
52 namespace OHOS::Media {
53 static const string HIGHLIGHT_ALBUM_CLASS = "HighlightAlbum";
54 static const string ANALYSIS_ALBUM_CLASS = "AnalysisAlbum";
55 thread_local napi_ref HighlightAlbumNapi::constructor_ = nullptr;
56 thread_local napi_ref HighlightAlbumNapi::analysisAlbumConstructor_ = nullptr;
57 
58 using CompleteCallback = napi_async_complete_callback;
59 
HighlightAlbumNapi()60 HighlightAlbumNapi::HighlightAlbumNapi() : highlightmEnv_(nullptr) {}
61 
62 HighlightAlbumNapi::~HighlightAlbumNapi() = default;
63 
Init(napi_env env,napi_value exports)64 napi_value HighlightAlbumNapi::Init(napi_env env, napi_value exports)
65 {
66     NapiClassInfo info = {
67         .name = HIGHLIGHT_ALBUM_CLASS,
68         .ref = &constructor_,
69         .constructor = Constructor,
70         .props = {
71             DECLARE_NAPI_FUNCTION("getHighlightAlbumInfo", JSGetHighlightAlbumInfo),
72             DECLARE_NAPI_FUNCTION("setHighlightUserActionData", JSSetHighlightUserActionData),
73             DECLARE_NAPI_FUNCTION("getHighlightResource", JSGetHighlightResource),
74             DECLARE_NAPI_FUNCTION("getOrderPosition", JSGetOrderPosition),
75             DECLARE_NAPI_FUNCTION("setSubTitle", JSSetHighlightSubtitle),
76             DECLARE_NAPI_STATIC_FUNCTION("deleteHighlightAlbums", JSDeleteHighlightAlbums),
77         } };
78     MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
79     return exports;
80 }
81 
AnalysisAlbumInit(napi_env env,napi_value exports)82 napi_value HighlightAlbumNapi::AnalysisAlbumInit(napi_env env, napi_value exports)
83 {
84     NapiClassInfo info = {
85         .name = ANALYSIS_ALBUM_CLASS,
86         .ref = &analysisAlbumConstructor_,
87         .constructor = Constructor,
88         .props = {
89             DECLARE_NAPI_FUNCTION("getOrderPosition", JSGetOrderPosition),
90         } };
91     MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
92     return exports;
93 }
94 
ParseHighlightAlbum(napi_env env,napi_value arg,shared_ptr<PhotoAlbum> & photoAlbum)95 static napi_value ParseHighlightAlbum(napi_env env, napi_value arg, shared_ptr<PhotoAlbum>& photoAlbum)
96 {
97     napi_valuetype valueType;
98     PhotoAlbumNapi* photoAlbumNapi;
99     CHECK_ARGS(env, napi_typeof(env, arg, &valueType), JS_INNER_FAIL);
100     CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
101     CHECK_ARGS(env, napi_unwrap(env, arg, reinterpret_cast<void**>(&photoAlbumNapi)), JS_INNER_FAIL);
102     CHECK_COND_WITH_MESSAGE(env, photoAlbumNapi != nullptr, "Failed to get PhotoAlbumNapi object");
103 
104     auto photoAlbumPtr = photoAlbumNapi->GetPhotoAlbumInstance();
105     CHECK_COND_WITH_MESSAGE(env, photoAlbumPtr != nullptr, "photoAlbum is null");
106     CHECK_COND_WITH_MESSAGE(env,
107         photoAlbumPtr->GetResultNapiType() == ResultNapiType::TYPE_PHOTOACCESS_HELPER &&
108             PhotoAlbum::CheckPhotoAlbumType(photoAlbumPtr->GetPhotoAlbumType()) &&
109             PhotoAlbum::CheckPhotoAlbumSubType(photoAlbumPtr->GetPhotoAlbumSubType()),
110         "Unsupported type of photoAlbum");
111     photoAlbum = photoAlbumPtr;
112     RETURN_NAPI_TRUE(env);
113 }
114 
Constructor(napi_env env,napi_callback_info info)115 napi_value HighlightAlbumNapi::Constructor(napi_env env, napi_callback_info info)
116 {
117     napi_value newTarget = nullptr;
118     CHECK_ARGS(env, napi_get_new_target(env, info, &newTarget), JS_INNER_FAIL);
119     CHECK_COND_RET(newTarget != nullptr, nullptr, "Failed to check new.target");
120 
121     size_t argc = ARGS_ONE;
122     napi_value argv[ARGS_ONE] = { 0 };
123     napi_value thisVar = nullptr;
124     shared_ptr<PhotoAlbum> photoAlbum = nullptr;
125     CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr), JS_INNER_FAIL);
126     CHECK_COND_WITH_MESSAGE(env, argc == ARGS_ONE, "Number of args is invalid");
127     CHECK_COND_WITH_MESSAGE(env, ParseHighlightAlbum(env, argv[PARAM0], photoAlbum), "Failed to parse album");
128 
129     unique_ptr<HighlightAlbumNapi> obj = make_unique<HighlightAlbumNapi>();
130     CHECK_COND(env, obj != nullptr, JS_INNER_FAIL);
131     obj->highlightAlbumPtr = photoAlbum;
132     obj->highlightmEnv_ = env;
133     CHECK_ARGS(env,
134         napi_wrap(env, thisVar, reinterpret_cast<void*>(obj.get()), HighlightAlbumNapi::Destructor, nullptr,
135             nullptr),
136         JS_INNER_FAIL);
137     obj.release();
138     return thisVar;
139 }
140 
Destructor(napi_env env,void * nativeObject,void * finalizeHint)141 void HighlightAlbumNapi::Destructor(napi_env env, void* nativeObject, void* finalizeHint)
142 {
143     auto* hightlihgtAlbum = reinterpret_cast<HighlightAlbumNapi*>(nativeObject);
144     if (hightlihgtAlbum != nullptr) {
145         delete hightlihgtAlbum;
146         hightlihgtAlbum = nullptr;
147     }
148 }
149 
150 static const map<int32_t, struct HighlightAlbumInfo> HIGHLIGHT_ALBUM_INFO_MAP = {
151     { COVER_INFO, { PAH_QUERY_HIGHLIGHT_COVER, { ID, HIGHLIGHT_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID,
152         AI_ALBUM_ID, SUB_TITLE, CLUSTER_TYPE, CLUSTER_SUB_TYPE,
153         CLUSTER_CONDITION, MIN_DATE_ADDED, MAX_DATE_ADDED, GENERATE_TIME, HIGHLIGHT_VERSION,
154         REMARKS, HIGHLIGHT_STATUS, RATIO, BACKGROUND, FOREGROUND, WORDART, IS_COVERED, COLOR,
155         RADIUS, SATURATION, BRIGHTNESS, BACKGROUND_COLOR_TYPE, SHADOW_LEVEL, TITLE_SCALE_X,
156         TITLE_SCALE_Y, TITLE_RECT_WIDTH, TITLE_RECT_HEIGHT, BACKGROUND_SCALE_X, BACKGROUND_SCALE_Y,
157         BACKGROUND_RECT_WIDTH, BACKGROUND_RECT_HEIGHT, LAYOUT_INDEX, COVER_ALGO_VERSION, COVER_KEY, COVER_STATUS,
158         HIGHLIGHT_IS_MUTED, HIGHLIGHT_IS_FAVORITE, HIGHLIGHT_THEME, HIGHLIGHT_PIN_TIME, HIGHLIGHT_USE_SUBTITLE } } },
159     { PLAY_INFO, { PAH_QUERY_HIGHLIGHT_PLAY, { ID, HIGHLIGHT_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID,
160         MUSIC, FILTER, HIGHLIGHT_PLAY_INFO, IS_CHOSEN, PLAY_INFO_VERSION, PLAY_INFO_ID } } },
161 };
162 
163 static const map<int32_t, std::string> HIGHLIGHT_USER_ACTION_MAP = {
164     { INSERTED_PIC_COUNT, HIGHLIGHT_INSERT_PIC_COUNT },
165     { REMOVED_PIC_COUNT, HIGHLIGHT_REMOVE_PIC_COUNT },
166     { SHARED_SCREENSHOT_COUNT, HIGHLIGHT_SHARE_SCREENSHOT_COUNT },
167     { SHARED_COVER_COUNT, HIGHLIGHT_SHARE_COVER_COUNT },
168     { RENAMED_COUNT, HIGHLIGHT_RENAME_COUNT },
169     { CHANGED_COVER_COUNT, HIGHLIGHT_CHANGE_COVER_COUNT },
170     { RENDER_VIEWED_TIMES, HIGHLIGHT_RENDER_VIEWED_TIMES },
171     { RENDER_VIEWED_DURATION, HIGHLIGHT_RENDER_VIEWED_DURATION },
172     { ART_LAYOUT_VIEWED_TIMES, HIGHLIGHT_ART_LAYOUT_VIEWED_TIMES },
173     { ART_LAYOUT_VIEWED_DURATION, HIGHLIGHT_ART_LAYOUT_VIEWED_DURATION },
174 };
175 
JSGetHighlightAlbumInfoExecute(napi_env env,void * data)176 static void JSGetHighlightAlbumInfoExecute(napi_env env, void *data)
177 {
178     MediaLibraryTracer tracer;
179     tracer.Start("JSGetHighlightAlbumInfoExecute");
180 
181     auto *context = static_cast<HighlightAlbumNapiAsyncContext*>(data);
182     std::vector<std::string> fetchColumn;
183     DataShare::DataSharePredicates predicates;
184     if (HIGHLIGHT_ALBUM_INFO_MAP.find(context->highlightAlbumInfoType) != HIGHLIGHT_ALBUM_INFO_MAP.end()) {
185         fetchColumn = HIGHLIGHT_ALBUM_INFO_MAP.at(context->highlightAlbumInfoType).fetchColumn;
186     } else {
187         NAPI_ERR_LOG("Invalid highlightAlbumInfoType");
188         return;
189     }
190     int errCode = 0;
191     GetHighlightAlbumReqBody reqBody;
192     reqBody.highlightAlbumInfoType = context->highlightAlbumInfoType;
193     reqBody.albumId = context->albumId;
194     reqBody.subType = static_cast<int32_t>(context->subType);
195     QueryResultRespBody respBody;
196     errCode = IPC::UserDefineIPCClient().Call(
197         static_cast<uint32_t>(MediaLibraryBusinessCode::GET_HIGHLIGHT_ALBUM_INFO), reqBody, respBody);
198     shared_ptr<DataShare::DataShareResultSet> resultSet = respBody.resultSet;
199     if (resultSet != nullptr) {
200         context->highlightAlbumInfo = MediaLibraryNapiUtils::ParseResultSet2JsonStr(resultSet, fetchColumn);
201     }
202 }
203 
JSGetHighlightAlbumInfoCompleteCallback(napi_env env,napi_status status,void * data)204 static void JSGetHighlightAlbumInfoCompleteCallback(napi_env env, napi_status status, void *data)
205 {
206     MediaLibraryTracer tracer;
207     tracer.Start("JSGetHighlightAlbumInfoCompleteCallback");
208 
209     auto *context = static_cast<HighlightAlbumNapiAsyncContext *>(data);
210     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
211 
212     unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
213     jsContext->status = false;
214 
215     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
216     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
217     if (context->error == ERR_DEFAULT) {
218         CHECK_ARGS_RET_VOID(env, napi_create_string_utf8(env, context->highlightAlbumInfo.c_str(),
219             NAPI_AUTO_LENGTH, &jsContext->data), JS_INNER_FAIL);
220         jsContext->status = true;
221     } else {
222         context->HandleError(env, jsContext->error);
223     }
224 
225     tracer.Finish();
226     if (context->work != nullptr) {
227         MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
228             context->work, *jsContext);
229     }
230     delete context;
231 }
232 
JSSetHighlightUserActionDataExecute(napi_env env,void * data)233 static void JSSetHighlightUserActionDataExecute(napi_env env, void *data)
234 {
235     MediaLibraryTracer tracer;
236     tracer.Start("JSSetHighlightUserActionDataExecute");
237 
238     auto *context = static_cast<HighlightAlbumNapiAsyncContext*>(data);
239     int32_t albumId = context->objectInfo->GetPhotoAlbumInstance()->GetAlbumId();
240     SetHighlightUserActionDataReqBody reqBody;
241     reqBody.albumId = to_string(albumId);
242     reqBody.userActionType = context->highlightUserActionType;
243     auto photoAlbum = context->objectInfo->GetPhotoAlbumInstance();
244     reqBody.albumType = static_cast<int32_t>(photoAlbum->GetPhotoAlbumType());
245     reqBody.albumSubType = static_cast<int32_t>(photoAlbum->GetPhotoAlbumSubType());
246     uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::SET_HIGH_LIGHT_USER_ACTION_DATA);
247     int32_t ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
248     if (ret < 0) {
249         context->SaveError(ret);
250         NAPI_ERR_LOG("Submit cloud enhancement tasks failed, err: %{public}d", ret);
251         return;
252     }
253 }
254 
JSSetHighlightUserActionDataCompleteCallback(napi_env env,napi_status status,void * data)255 static void JSSetHighlightUserActionDataCompleteCallback(napi_env env, napi_status status, void *data)
256 {
257     MediaLibraryTracer tracer;
258     tracer.Start("JSSetHighlightUserActionDataCompleteCallback");
259 
260     auto *context = static_cast<HighlightAlbumNapiAsyncContext*>(data);
261     auto jsContext = make_unique<JSAsyncContextOutput>();
262     jsContext->status = false;
263     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
264     if (context->error == ERR_DEFAULT) {
265         CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
266         jsContext->status = true;
267     } else {
268         context->HandleError(env, jsContext->error);
269     }
270 
271     tracer.Finish();
272     if (context->work != nullptr) {
273         MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
274                                                    context->work, *jsContext);
275     }
276     delete context;
277 }
278 
JSSetHighlightSubtitleExecute(napi_env env,void * data)279 static void JSSetHighlightSubtitleExecute(napi_env env, void *data)
280 {
281     MediaLibraryTracer tracer;
282     tracer.Start("JSSetHighlightSubtitleExecute");
283 
284     auto *context = static_cast<HighlightAlbumNapiAsyncContext*>(data);
285     int albumId = context->objectInfo->GetPhotoAlbumInstance()->GetAlbumId();
286     auto photoAlbum = context->objectInfo->GetPhotoAlbumInstance();
287     SetSubtitleReqBody reqBody;
288     reqBody.albumId = to_string(albumId);
289     reqBody.subtitle = context->subtitle;
290     reqBody.albumType = static_cast<int32_t>(photoAlbum->GetPhotoAlbumType());
291     reqBody.albumSubType = static_cast<int32_t>(photoAlbum->GetPhotoAlbumSubType());
292     uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::SET_SUBTITLE);
293     int32_t ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
294     if (ret < 0) {
295         context->SaveError(ret);
296         NAPI_ERR_LOG("Failed to set highlight subtitle, err: %{public}d", ret);
297         return;
298     }
299 }
300 
JSSetHighlightSubtitleCompleteCallback(napi_env env,napi_status status,void * data)301 static void JSSetHighlightSubtitleCompleteCallback(napi_env env, napi_status status, void *data)
302 {
303     MediaLibraryTracer tracer;
304     tracer.Start("JSSetHighlightSubtitleCompleteCallback");
305 
306     auto *context = static_cast<HighlightAlbumNapiAsyncContext*>(data);
307     auto jsContext = make_unique<JSAsyncContextOutput>();
308     jsContext->status = false;
309     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
310     if (context->error == ERR_DEFAULT) {
311         CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
312         jsContext->status = true;
313     } else {
314         context->HandleError(env, jsContext->error);
315     }
316 
317     tracer.Finish();
318     if (context->work != nullptr) {
319         MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
320                                                    context->work, *jsContext);
321     }
322     delete context;
323 }
324 
GetFdForArrayBuffer(std::string uriStr)325 static int32_t GetFdForArrayBuffer(std::string uriStr)
326 {
327     int32_t fd = 0;
328     Uri uri(uriStr);
329     fd = UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
330     if (fd == E_ERR) {
331         NAPI_ERR_LOG("Open highlight cover file failed, error: %{public}d", errno);
332         return E_HAS_FS_ERROR;
333     } else if (fd < 0) {
334         NAPI_ERR_LOG("Open highlight cover file failed due to OpenFile failure");
335         return fd;
336     }
337     return fd;
338 }
339 
JSGetHighlightResourceExecute(napi_env env,void * data)340 static void JSGetHighlightResourceExecute(napi_env env, void *data)
341 {
342     MediaLibraryTracer tracer;
343     tracer.Start("JSGetHighlightResourceExecute");
344 
345     auto *context = static_cast<HighlightAlbumNapiAsyncContext*>(data);
346     if (context->resourceUri.find(MEDIA_DATA_DB_HIGHLIGHT) == string::npos) {
347         NAPI_ERR_LOG("Invalid highlight resource uri");
348         return;
349     }
350 
351     int32_t fd = GetFdForArrayBuffer(context->resourceUri);
352     if (fd < 0) {
353         return;
354     }
355     UniqueFd uniqueFd(fd);
356     off_t fileLen = lseek(uniqueFd.Get(), 0, SEEK_END);
357     if (fileLen < 0) {
358         NAPI_ERR_LOG("Failed to get highlight cover file length, error: %{public}d", errno);
359         return;
360     }
361     off_t ret = lseek(uniqueFd.Get(), 0, SEEK_SET);
362     if (ret < 0) {
363         NAPI_ERR_LOG("Failed to reset highlight cover file offset, error: %{public}d", errno);
364         return;
365     }
366     void* arrayBufferData = nullptr;
367     napi_value arrayBuffer;
368     if (napi_create_arraybuffer(env, fileLen, &arrayBufferData, &arrayBuffer) != napi_ok) {
369         NAPI_ERR_LOG("failed to create napi arraybuffer");
370         return;
371     }
372 
373     ssize_t readBytes = read(uniqueFd.Get(), arrayBufferData, fileLen);
374     if (readBytes != fileLen) {
375         NAPI_ERR_LOG("read file failed, read bytes is %{public}zu,""actual length is %{public}" PRId64
376             ",error: %{public}d", readBytes, fileLen, errno);
377         return;
378     }
379     context->napiArrayBuffer = arrayBuffer;
380 }
381 
JSGetHighlightResourceCompleteCallback(napi_env env,napi_status status,void * data)382 static void JSGetHighlightResourceCompleteCallback(napi_env env, napi_status status, void *data)
383 {
384     MediaLibraryTracer tracer;
385     tracer.Start("JSGetHighlightResourceCompleteCallback");
386 
387     auto *context = static_cast<HighlightAlbumNapiAsyncContext *>(data);
388     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
389 
390     unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
391     jsContext->status = false;
392 
393     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
394     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
395     if (context->error == ERR_DEFAULT) {
396         jsContext->data = context->napiArrayBuffer;
397         jsContext->status = true;
398     } else {
399         context->HandleError(env, jsContext->error);
400     }
401 
402     tracer.Finish();
403     if (context->work != nullptr) {
404         MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
405                                                    context->work, *jsContext);
406     }
407     delete context;
408 }
409 
JSGetOrderPositionExecute(napi_env env,void * data)410 static void JSGetOrderPositionExecute(napi_env env, void *data)
411 {
412     MediaLibraryTracer tracer;
413     tracer.Start("JSGetOrderPositionExecute");
414 
415     auto *context = static_cast<HighlightAlbumNapiAsyncContext *>(data);
416     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
417 
418     auto photoAlbum = context->objectInfo->GetPhotoAlbumInstance();
419 
420     GetOrderPositionRespBody respBody;
421     GetOrderPositionReqBody reqBody;
422     reqBody.albumId = photoAlbum->GetAlbumId();
423     reqBody.albumType = photoAlbum->GetPhotoAlbumType();
424     reqBody.albumSubType = photoAlbum->GetPhotoAlbumSubType();
425     reqBody.assetIdArray = context->assetIdArray;
426     uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::PAH_GET_ORDER_POSITION);
427 
428     int32_t ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody, respBody);
429     if (ret == E_OK) {
430         context->orderPositionArray.clear();
431         context->orderPositionArray = respBody.orderPositionArray;
432     }
433     NAPI_INFO_LOG("GetOrderPosition: orderPositionArray size: %{public}d",
434                   static_cast<int>(context->orderPositionArray.size()));
435 }
436 
JSGetOrderPositionCompleteCallback(napi_env env,napi_status status,void * data)437 static void JSGetOrderPositionCompleteCallback(napi_env env, napi_status status, void *data)
438 {
439     MediaLibraryTracer tracer;
440     tracer.Start("JSGetOrderPositionCompleteCallback");
441 
442     auto *context = static_cast<HighlightAlbumNapiAsyncContext *>(data);
443     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
444 
445     unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
446     jsContext->status = false;
447 
448     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
449     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
450 
451     size_t positionSize = context->orderPositionArray.size();
452     size_t assetSize = context->assetIdArray.size();
453     if (positionSize != assetSize) {
454         NAPI_ERR_LOG("GetOrderPosition failed, position size: %{public}d, asset size: %{public}d",
455                      static_cast<int>(positionSize), static_cast<int>(assetSize));
456         context->HandleError(env, jsContext->error);
457         napi_get_undefined(env, &jsContext->data);
458     } else {
459         napi_value jsArray = nullptr;
460         napi_create_array_with_length(env, positionSize, &jsArray);
461         for (size_t i = 0; i < positionSize; i++) {
462             napi_value element;
463             napi_create_int32(env, context->orderPositionArray[i], &element);
464             napi_set_element(env, jsArray, i, element);
465         }
466         jsContext->data = jsArray;
467         napi_get_undefined(env, &jsContext->error);
468         jsContext->status = true;
469     }
470 
471     if (context->work != nullptr) {
472         MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred,
473                                                    context->callbackRef, context->work, *jsContext);
474     }
475     delete context;
476 }
477 
JSGetOrderPosition(napi_env env,napi_callback_info info)478 napi_value HighlightAlbumNapi::JSGetOrderPosition(napi_env env, napi_callback_info info)
479 {
480     MediaLibraryTracer tracer;
481     tracer.Start("JSGetOrderPosition");
482 
483     // make undefined
484     napi_value undefinedObject = nullptr;
485     NAPI_CALL(env, napi_get_undefined(env, &undefinedObject));
486 
487     // make async context, if error then return undefined
488     unique_ptr<HighlightAlbumNapiAsyncContext> asyncContext = make_unique<HighlightAlbumNapiAsyncContext>();
489     CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, undefinedObject, "asyncContext context is null");
490     CHECK_COND_WITH_MESSAGE(env,
491         MediaLibraryNapiUtils::AsyncContextSetObjectInfo(env, info, asyncContext, ARGS_ONE, ARGS_ONE) == napi_ok,
492         "Failed to get object info");
493 
494     // get this album, check it is an analysis album
495     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
496     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "Failed to get photo album instance");
497     CHECK_COND_WITH_MESSAGE(env,
498         PhotoAlbum::IsAnalysisAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
499         "Only analysis album can get asset order positions");
500 
501     // get assets, check duplicated
502     vector<string> assetIdArray;
503     CHECK_COND_WITH_MESSAGE(env,
504         MediaLibraryNapiUtils::ParseAssetIdArray(env, asyncContext->argv[PARAM0], assetIdArray),
505         "Failed to parse assets");
506     NAPI_INFO_LOG("GetOrderPosition: get assets id size: %{public}d", static_cast<int>(assetIdArray.size()));
507     CHECK_COND_WITH_MESSAGE(
508         env, assetIdArray.size() > 0, "The getOrderPosition operation needs at least one asset id");
509     std::set<std::string> idSet(assetIdArray.begin(), assetIdArray.end());
510     CHECK_COND_WITH_MESSAGE(
511         env, assetIdArray.size() == idSet.size(), "The getOrderPosition operation has same assets");
512     asyncContext->assetIdArray = std::move(assetIdArray);
513 
514     // make async task
515     asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
516     return MediaLibraryNapiUtils::NapiCreateAsyncWork(
517         env, asyncContext, "JSGetOrderPosition", JSGetOrderPositionExecute, JSGetOrderPositionCompleteCallback);
518 }
519 
JSGetHighlightAlbumInfo(napi_env env,napi_callback_info info)520 napi_value HighlightAlbumNapi::JSGetHighlightAlbumInfo(napi_env env, napi_callback_info info)
521 {
522     MediaLibraryTracer tracer;
523     tracer.Start("JSGetHighlightAlbumInfo");
524 
525     napi_value result = nullptr;
526     NAPI_CALL(env, napi_get_undefined(env, &result));
527     unique_ptr<HighlightAlbumNapiAsyncContext> asyncContext = make_unique<HighlightAlbumNapiAsyncContext>();
528     CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "asyncContext context is null");
529     CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsNumberCallback(env, info, asyncContext,
530         asyncContext->highlightAlbumInfoType), JS_ERR_PARAMETER_INVALID);
531 
532     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
533     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
534     CHECK_COND_WITH_MESSAGE(env,
535         PhotoAlbum::IsHighlightAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
536         "Only and smart highlight album can get highlight album info");
537 
538     asyncContext->albumId = photoAlbum->GetAlbumId();
539     asyncContext->subType = photoAlbum->GetPhotoAlbumSubType();
540     asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
541     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetHighlightAlbumInfo",
542         JSGetHighlightAlbumInfoExecute, JSGetHighlightAlbumInfoCompleteCallback);
543 }
544 
JSSetHighlightUserActionData(napi_env env,napi_callback_info info)545 napi_value HighlightAlbumNapi::JSSetHighlightUserActionData(napi_env env, napi_callback_info info)
546 {
547     MediaLibraryTracer tracer;
548     tracer.Start("JSSetHighlightUserActionData");
549 
550     napi_value result = nullptr;
551     NAPI_CALL(env, napi_get_undefined(env, &result));
552     unique_ptr<HighlightAlbumNapiAsyncContext> asyncContext = make_unique<HighlightAlbumNapiAsyncContext>();
553     CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "asyncContext context is null");
554 
555     CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsNumberCallback(env, info, asyncContext,
556         asyncContext->highlightUserActionType), JS_ERR_PARAMETER_INVALID);
557     CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetInt32Arg(env, asyncContext->argv[1], asyncContext->actionData));
558 
559     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
560     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
561     CHECK_COND_WITH_MESSAGE(env,
562         PhotoAlbum::IsHighlightAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
563         "Only and smart highlight album can set user action info");
564 
565     asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
566     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSSetHighlightUserActionData",
567         JSSetHighlightUserActionDataExecute, JSSetHighlightUserActionDataCompleteCallback);
568 }
569 
JSGetHighlightResource(napi_env env,napi_callback_info info)570 napi_value HighlightAlbumNapi::JSGetHighlightResource(napi_env env, napi_callback_info info)
571 {
572     MediaLibraryTracer tracer;
573     tracer.Start("JSGetHighlightResource");
574 
575     napi_value result = nullptr;
576     NAPI_CALL(env, napi_get_undefined(env, &result));
577     unique_ptr<HighlightAlbumNapiAsyncContext> asyncContext = make_unique<HighlightAlbumNapiAsyncContext>();
578     CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "asyncContext context is null");
579 
580     CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, asyncContext, asyncContext->resourceUri),
581         JS_ERR_PARAMETER_INVALID);
582 
583     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
584     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
585     CHECK_COND_WITH_MESSAGE(env,
586         PhotoAlbum::IsHighlightAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
587         "Only and smart highlight album can set user action info");
588 
589     asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
590     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetHighlightResource",
591         JSGetHighlightResourceExecute, JSGetHighlightResourceCompleteCallback);
592 }
593 
JSSetHighlightSubtitle(napi_env env,napi_callback_info info)594 napi_value HighlightAlbumNapi::JSSetHighlightSubtitle(napi_env env, napi_callback_info info)
595 {
596     MediaLibraryTracer tracer;
597     tracer.Start("JSSetHighlightSubtitle");
598 
599     napi_value result = nullptr;
600     NAPI_CALL(env, napi_get_undefined(env, &result));
601     unique_ptr<HighlightAlbumNapiAsyncContext> asyncContext = make_unique<HighlightAlbumNapiAsyncContext>();
602     CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "asyncContext context is null");
603     CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, asyncContext, asyncContext->subtitle),
604         OHOS_INVALID_PARAM_CODE);
605 
606     CHECK_COND_WITH_MESSAGE(env, MediaFileUtils::CheckHighlightSubtitle(asyncContext->subtitle) == E_OK,
607         "Invalid highlight subtitle");
608     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
609     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
610     CHECK_COND_WITH_MESSAGE(env,
611         PhotoAlbum::IsHighlightAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
612         "Only highlight album can set highlight sub title");
613 
614     asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
615     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSSetHighlightSubtitle",
616         JSSetHighlightSubtitleExecute, JSSetHighlightSubtitleCompleteCallback);
617 }
618 
DeleteHighlightAlbumsCompleteCallback(napi_env env,napi_status status,void * data)619 static void DeleteHighlightAlbumsCompleteCallback(napi_env env, napi_status status, void* data)
620 {
621     MediaLibraryTracer tracer;
622     tracer.Start("JSDeleteHighlightAlbumsCompleteCallback");
623     int32_t deleteAlbumFailed = 1;
624     auto* context = static_cast<MediaAlbumChangeRequestAsyncContext*>(data);
625     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
626     auto jsContext = make_unique<JSAsyncContextOutput>();
627     jsContext->status = false;
628     napi_get_undefined(env, &jsContext->data);
629     napi_get_undefined(env, &jsContext->error);
630     if (context->error == ERR_DEFAULT) {
631         napi_create_int32(env, E_SUCCESS, &jsContext->data);
632         jsContext->status = true;
633     } else {
634         napi_create_int32(env, deleteAlbumFailed, &jsContext->data);
635         context->HandleError(env, jsContext->error);
636     }
637 
638     if (context->work != nullptr) {
639         MediaLibraryNapiUtils::InvokeJSAsyncMethod(
640             env, context->deferred, context->callbackRef, context->work, *jsContext);
641     }
642     delete context;
643 }
644 
DeleteHighlightAlbumsExecute(napi_env env,void * data)645 static void DeleteHighlightAlbumsExecute(napi_env env, void* data)
646 {
647     MediaLibraryTracer tracer;
648     tracer.Start("JSDeleteHighlightAlbumsExecute");
649     NAPI_INFO_LOG("Start delete highlight album(s)");
650 
651     auto* context = static_cast<MediaAlbumChangeRequestAsyncContext*>(data);
652     DeleteHighLightAlbumsReqBody reqBody;
653     reqBody.albumIds = context->deleteIds;
654     reqBody.photoAlbumTypes = context->photoAlbumTypes;
655     reqBody.photoAlbumSubtypes = context->photoAlbumSubtypes;
656     uint32_t businessCode = static_cast<uint32_t>(MediaLibraryBusinessCode::DELETE_HIGH_LIGHT_ALBUMS);
657     int32_t ret = IPC::UserDefineIPCClient().Call(businessCode, reqBody);
658     if (ret < 0) {
659         context->SaveError(ret);
660         NAPI_ERR_LOG("Failed to delete highlight albums, err: %{public}d", ret);
661         return;
662     }
663     NAPI_INFO_LOG("Delete highlight album(s): %{public}d", ret);
664 }
665 
ParseArgsDeleteHighlightAlbums(napi_env env,napi_callback_info info,unique_ptr<MediaAlbumChangeRequestAsyncContext> & context)666 static napi_value ParseArgsDeleteHighlightAlbums(
667     napi_env env, napi_callback_info info, unique_ptr<MediaAlbumChangeRequestAsyncContext>& context)
668 {
669     if (!MediaLibraryNapiUtils::IsSystemApp()) {
670         NapiError::ThrowError(env, E_CHECK_SYSTEMAPP_FAIL, "This interface can be called only by system apps");
671         return nullptr;
672     }
673 
674     constexpr size_t minArgs = ARGS_TWO;
675     constexpr size_t maxArgs = ARGS_THREE;
676     CHECK_COND_WITH_MESSAGE(env,
677         MediaLibraryNapiUtils::AsyncContextGetArgs(env, info, context, minArgs, maxArgs) == napi_ok,
678         "Failed to get args");
679     CHECK_COND(env, MediaAlbumChangeRequestNapi::InitUserFileClient(env, info), JS_INNER_FAIL);
680 
681     vector<napi_value> napiValues;
682     napi_valuetype valueType = napi_undefined;
683     CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetNapiValueArray(env, context->argv[PARAM1], napiValues));
684     CHECK_COND_WITH_MESSAGE(env, !napiValues.empty(), "array is empty");
685     CHECK_ARGS(env, napi_typeof(env, napiValues.front(), &valueType), JS_INNER_FAIL);
686     CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
687 
688     for (const auto& napiValue : napiValues) {
689         PhotoAlbumNapi* obj = nullptr;
690         CHECK_ARGS(env, napi_unwrap(env, napiValue, reinterpret_cast<void**>(&obj)), JS_INNER_FAIL);
691         CHECK_COND_WITH_MESSAGE(env, obj != nullptr, "Failed to get album napi object");
692         CHECK_COND_WITH_MESSAGE(env,
693             PhotoAlbum::IsHighlightAlbum(obj->GetPhotoAlbumType(), obj->GetPhotoAlbumSubType()),
694             "Only highlight album can be deleted");
695         context->deleteIds.push_back(to_string(obj->GetAlbumId()));
696         context->photoAlbumTypes.push_back(static_cast<int32_t>(obj->GetPhotoAlbumType()));
697         context->photoAlbumSubtypes.push_back(static_cast<int32_t>(obj->GetPhotoAlbumSubType()));
698     }
699     RETURN_NAPI_TRUE(env);
700 }
701 
JSDeleteHighlightAlbums(napi_env env,napi_callback_info info)702 napi_value HighlightAlbumNapi::JSDeleteHighlightAlbums(napi_env env, napi_callback_info info)
703 {
704     auto asyncContext = make_unique<MediaAlbumChangeRequestAsyncContext>();
705     CHECK_COND_WITH_MESSAGE(env, ParseArgsDeleteHighlightAlbums(env, info, asyncContext),
706         "Failed to parse highlight args");
707     return MediaLibraryNapiUtils::NapiCreateAsyncWork(
708         env, asyncContext, "ChangeRequestDeleteHighlightAlbums",
709         DeleteHighlightAlbumsExecute, DeleteHighlightAlbumsCompleteCallback);
710 }
711 
GetPhotoAlbumInstance() const712 shared_ptr<PhotoAlbum> HighlightAlbumNapi::GetPhotoAlbumInstance() const
713 {
714     return highlightAlbumPtr;
715 }
716 } // namespace OHOS::Media