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 "photo_album.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 "story_album_column.h"
34 #include "story_cover_info_column.h"
35 #include "story_play_info_column.h"
36 #include "user_photography_info_column.h"
37 
38 using namespace std;
39 
40 namespace OHOS::Media {
41 static const string HIGHLIGHT_ALBUM_CLASS = "HighlightAlbum";
42 thread_local napi_ref HighlightAlbumNapi::constructor_ = nullptr;
43 
44 using CompleteCallback = napi_async_complete_callback;
45 
HighlightAlbumNapi()46 HighlightAlbumNapi::HighlightAlbumNapi() : highlightmEnv_(nullptr) {}
47 
48 HighlightAlbumNapi::~HighlightAlbumNapi() = default;
49 
Init(napi_env env,napi_value exports)50 napi_value HighlightAlbumNapi::Init(napi_env env, napi_value exports)
51 {
52     NapiClassInfo info = {
53         .name = HIGHLIGHT_ALBUM_CLASS,
54         .ref = &constructor_,
55         .constructor = Constructor,
56         .props = {
57             DECLARE_NAPI_FUNCTION("getHighlightAlbumInfo", JSGetHighlightAlbumInfo),
58             DECLARE_NAPI_FUNCTION("setHighlightUserActionData", JSSetHighlightUserActionData),
59             DECLARE_NAPI_FUNCTION("getHighlightResource", JSGetHighlightResource),
60         } };
61     MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
62     return exports;
63 }
64 
ParseHighlightAlbum(napi_env env,napi_value arg,shared_ptr<PhotoAlbum> & photoAlbum)65 static napi_value ParseHighlightAlbum(napi_env env, napi_value arg, shared_ptr<PhotoAlbum>& photoAlbum)
66 {
67     napi_valuetype valueType;
68     PhotoAlbumNapi* photoAlbumNapi;
69     CHECK_ARGS(env, napi_typeof(env, arg, &valueType), JS_INNER_FAIL);
70     CHECK_COND_WITH_MESSAGE(env, valueType == napi_object, "Invalid argument type");
71     CHECK_ARGS(env, napi_unwrap(env, arg, reinterpret_cast<void**>(&photoAlbumNapi)), JS_INNER_FAIL);
72     CHECK_COND_WITH_MESSAGE(env, photoAlbumNapi != nullptr, "Failed to get PhotoAlbumNapi object");
73 
74     auto photoAlbumPtr = photoAlbumNapi->GetPhotoAlbumInstance();
75     CHECK_COND_WITH_MESSAGE(env, photoAlbumPtr != nullptr, "photoAlbum is null");
76     CHECK_COND_WITH_MESSAGE(env,
77         photoAlbumPtr->GetResultNapiType() == ResultNapiType::TYPE_PHOTOACCESS_HELPER &&
78             PhotoAlbum::CheckPhotoAlbumType(photoAlbumPtr->GetPhotoAlbumType()) &&
79             PhotoAlbum::CheckPhotoAlbumSubType(photoAlbumPtr->GetPhotoAlbumSubType()),
80         "Unsupported type of photoAlbum");
81     photoAlbum = photoAlbumPtr;
82     RETURN_NAPI_TRUE(env);
83 }
84 
Constructor(napi_env env,napi_callback_info info)85 napi_value HighlightAlbumNapi::Constructor(napi_env env, napi_callback_info info)
86 {
87     napi_value newTarget = nullptr;
88     CHECK_ARGS(env, napi_get_new_target(env, info, &newTarget), JS_INNER_FAIL);
89     CHECK_COND_RET(newTarget != nullptr, nullptr, "Failed to check new.target");
90 
91     size_t argc = ARGS_ONE;
92     napi_value argv[ARGS_ONE] = { 0 };
93     napi_value thisVar = nullptr;
94     shared_ptr<PhotoAlbum> photoAlbum = nullptr;
95     CHECK_ARGS(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr), JS_INNER_FAIL);
96     CHECK_COND_WITH_MESSAGE(env, argc == ARGS_ONE, "Number of args is invalid");
97     CHECK_COND_WITH_MESSAGE(env, ParseHighlightAlbum(env, argv[PARAM0], photoAlbum), "Failed to parse album");
98 
99     unique_ptr<HighlightAlbumNapi> obj = make_unique<HighlightAlbumNapi>();
100     CHECK_COND(env, obj != nullptr, JS_INNER_FAIL);
101     obj->highlightAlbumPtr = photoAlbum;
102     obj->highlightmEnv_ = env;
103     CHECK_ARGS(env,
104         napi_wrap(env, thisVar, reinterpret_cast<void*>(obj.get()), HighlightAlbumNapi::Destructor, nullptr,
105             nullptr),
106         JS_INNER_FAIL);
107     obj.release();
108     return thisVar;
109 }
110 
Destructor(napi_env env,void * nativeObject,void * finalizeHint)111 void HighlightAlbumNapi::Destructor(napi_env env, void* nativeObject, void* finalizeHint)
112 {
113     auto* hightlihgtAlbum = reinterpret_cast<HighlightAlbumNapi*>(nativeObject);
114     if (hightlihgtAlbum != nullptr) {
115         delete hightlihgtAlbum;
116         hightlihgtAlbum = nullptr;
117     }
118 }
119 
120 static const map<int32_t, struct HighlightAlbumInfo> HIGHLIGHT_ALBUM_INFO_MAP = {
121     { COVER_INFO, { PAH_QUERY_HIGHLIGHT_COVER, { ID, HIGHLIGHT_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID,
122         AI_ALBUM_ID, SUB_TITLE, CLUSTER_TYPE, CLUSTER_SUB_TYPE,
123         CLUSTER_CONDITION, MIN_DATE_ADDED, MAX_DATE_ADDED, GENERATE_TIME, HIGHLIGHT_VERSION,
124         REMARKS, HIGHLIGHT_STATUS, RATIO, BACKGROUND, FOREGROUND, WORDART, IS_COVERED, COLOR,
125         RADIUS, SATURATION, BRIGHTNESS, BACKGROUND_COLOR_TYPE, SHADOW_LEVEL, TITLE_SCALE_X,
126         TITLE_SCALE_Y, TITLE_RECT_WIDTH, TITLE_RECT_HEIGHT, BACKGROUND_SCALE_X, BACKGROUND_SCALE_Y,
127         BACKGROUND_RECT_WIDTH, BACKGROUND_RECT_HEIGHT, LAYOUT_INDEX, COVER_ALGO_VERSION, COVER_KEY,
128         HIGHLIGHT_IS_MUTED, HIGHLIGHT_IS_FAVORITE, HIGHLIGHT_THEME } } },
129     { PLAY_INFO, { PAH_QUERY_HIGHLIGHT_PLAY, { ID, HIGHLIGHT_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID,
130         MUSIC, FILTER, HIGHLIGHT_PLAY_INFO, IS_CHOSEN, PLAY_INFO_VERSION, PLAY_INFO_ID } } },
131 };
132 
133 static const map<int32_t, std::string> HIGHLIGHT_USER_ACTION_MAP = {
134     { INSERTED_PIC_COUNT, HIGHLIGHT_INSERT_PIC_COUNT },
135     { REMOVED_PIC_COUNT, HIGHLIGHT_REMOVE_PIC_COUNT },
136     { SHARED_SCREENSHOT_COUNT, HIGHLIGHT_SHARE_SCREENSHOT_COUNT },
137     { SHARED_COVER_COUNT, HIGHLIGHT_SHARE_COVER_COUNT },
138     { RENAMED_COUNT, HIGHLIGHT_RENAME_COUNT },
139     { CHANGED_COVER_COUNT, HIGHLIGHT_CHANGE_COVER_COUNT },
140     { RENDER_VIEWED_TIMES, HIGHLIGHT_RENDER_VIEWED_TIMES },
141     { RENDER_VIEWED_DURATION, HIGHLIGHT_RENDER_VIEWED_DURATION },
142     { ART_LAYOUT_VIEWED_TIMES, HIGHLIGHT_ART_LAYOUT_VIEWED_TIMES },
143     { ART_LAYOUT_VIEWED_DURATION, HIGHLIGHT_ART_LAYOUT_VIEWED_DURATION },
144 };
145 
JSGetHighlightAlbumInfoExecute(napi_env env,void * data)146 static void JSGetHighlightAlbumInfoExecute(napi_env env, void *data)
147 {
148     MediaLibraryTracer tracer;
149     tracer.Start("JSGetHighlightAlbumInfoExecute");
150 
151     auto *context = static_cast<HighlightAlbumNapiAsyncContext*>(data);
152     string uriStr;
153     std::vector<std::string> fetchColumn;
154     DataShare::DataSharePredicates predicates;
155     if (HIGHLIGHT_ALBUM_INFO_MAP.find(context->highlightAlbumInfoType) != HIGHLIGHT_ALBUM_INFO_MAP.end()) {
156         uriStr = HIGHLIGHT_ALBUM_INFO_MAP.at(context->highlightAlbumInfoType).uriStr;
157         fetchColumn = HIGHLIGHT_ALBUM_INFO_MAP.at(context->highlightAlbumInfoType).fetchColumn;
158         string tabStr;
159         if (context->highlightAlbumInfoType == COVER_INFO) {
160             tabStr = HIGHLIGHT_COVER_INFO_TABLE;
161         } else {
162             tabStr = HIGHLIGHT_PLAY_INFO_TABLE;
163         }
164         vector<string> onClause = {
165             tabStr + "." + PhotoAlbumColumns::ALBUM_ID + " = " +
166             HIGHLIGHT_ALBUM_TABLE + "." + ID
167         };
168         predicates.InnerJoin(HIGHLIGHT_ALBUM_TABLE)->On(onClause);
169     } else {
170         NAPI_ERR_LOG("Invalid highlightAlbumInfoType");
171         return;
172     }
173     CHECK_NULL_PTR_RETURN_VOID(context->objectInfo, "objectInfo is null");
174     auto photoAlbum = context->objectInfo->GetPhotoAlbumInstance();
175     CHECK_NULL_PTR_RETURN_VOID(photoAlbum, "photoAlbum is null");
176     int albumId = photoAlbum->GetAlbumId();
177     int subType = photoAlbum->GetPhotoAlbumSubType();
178     Uri uri (uriStr);
179     if (subType == PhotoAlbumSubType::HIGHLIGHT) {
180         predicates.EqualTo(HIGHLIGHT_ALBUM_TABLE + "." + PhotoAlbumColumns::ALBUM_ID, to_string(albumId));
181     } else if (subType == PhotoAlbumSubType::HIGHLIGHT_SUGGESTIONS) {
182         predicates.EqualTo(HIGHLIGHT_ALBUM_TABLE + "." + AI_ALBUM_ID, to_string(albumId));
183     } else {
184         NAPI_ERR_LOG("Invalid highlight album subType");
185         return;
186     }
187     int errCode = 0;
188     auto resultSet = UserFileClient::Query(uri, predicates, fetchColumn, errCode);
189     if (resultSet != nullptr) {
190         context->highlightAlbumInfo = MediaLibraryNapiUtils::ParseResultSet2JsonStr(resultSet, fetchColumn);
191     }
192 }
193 
JSGetHighlightAlbumInfoCompleteCallback(napi_env env,napi_status status,void * data)194 static void JSGetHighlightAlbumInfoCompleteCallback(napi_env env, napi_status status, void *data)
195 {
196     MediaLibraryTracer tracer;
197     tracer.Start("JSGetHighlightAlbumInfoCompleteCallback");
198 
199     auto *context = static_cast<HighlightAlbumNapiAsyncContext *>(data);
200     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
201 
202     unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
203     jsContext->status = false;
204 
205     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
206     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
207     if (context->error == ERR_DEFAULT) {
208         CHECK_ARGS_RET_VOID(env, napi_create_string_utf8(env, context->highlightAlbumInfo.c_str(),
209             NAPI_AUTO_LENGTH, &jsContext->data), JS_INNER_FAIL);
210         jsContext->status = true;
211     } else {
212         context->HandleError(env, jsContext->error);
213     }
214 
215     tracer.Finish();
216     if (context->work != nullptr) {
217         MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
218             context->work, *jsContext);
219     }
220     delete context;
221 }
222 
JSSetHighlightUserActionDataExecute(napi_env env,void * data)223 static void JSSetHighlightUserActionDataExecute(napi_env env, void *data)
224 {
225     MediaLibraryTracer tracer;
226     tracer.Start("JSSetHighlightUserActionDataExecute");
227 
228     auto *context = static_cast<HighlightAlbumNapiAsyncContext*>(data);
229     string userActionType;
230     if (HIGHLIGHT_USER_ACTION_MAP.find(context->highlightUserActionType) != HIGHLIGHT_USER_ACTION_MAP.end()) {
231         userActionType = HIGHLIGHT_USER_ACTION_MAP.at(context->highlightUserActionType);
232         context->fetchColumn.push_back(userActionType);
233     } else {
234         NAPI_ERR_LOG("Invalid highlightUserActionType");
235         return;
236     }
237     int albumId = context->objectInfo->GetPhotoAlbumInstance()->GetAlbumId();
238     Uri uri(URI_HIGHLIGHT_ALBUM);
239     context->predicates.EqualTo(PhotoAlbumColumns::ALBUM_ID, to_string(albumId));
240     int errCode = 0;
241     auto resultSet = UserFileClient::Query(uri, context->predicates, context->fetchColumn, errCode);
242     if (resultSet != nullptr) {
243         auto count = 0;
244         auto ret = resultSet->GetRowCount(count);
245         if (ret != NativeRdb::E_OK || count == 0 || resultSet->GoToFirstRow() != NativeRdb::E_OK) {
246             NAPI_ERR_LOG("highlight user action data get rdbstore failed");
247             context->error = JS_INNER_FAIL;
248             return;
249         }
250         int64_t userActionDataCount = get<int64_t>(ResultSetUtils::GetValFromColumn(userActionType,
251             resultSet, TYPE_INT64));
252         context->valuesBucket.Put(userActionType, to_string(userActionDataCount + context->actionData));
253         int changedRows = UserFileClient::Update(uri, context->predicates, context->valuesBucket);
254         context->SaveError(changedRows);
255         context->changedRows = changedRows;
256     } else {
257         NAPI_ERR_LOG("highlight user action data resultSet is null");
258         context->error = JS_INNER_FAIL;
259         return;
260     }
261 }
262 
JSSetHighlightUserActionDataCompleteCallback(napi_env env,napi_status status,void * data)263 static void JSSetHighlightUserActionDataCompleteCallback(napi_env env, napi_status status, void *data)
264 {
265     MediaLibraryTracer tracer;
266     tracer.Start("JSSetHighlightUserActionDataCompleteCallback");
267 
268     auto *context = static_cast<HighlightAlbumNapiAsyncContext*>(data);
269     auto jsContext = make_unique<JSAsyncContextOutput>();
270     jsContext->status = false;
271     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
272     if (context->error == ERR_DEFAULT) {
273         CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
274         jsContext->status = true;
275     } else {
276         context->HandleError(env, jsContext->error);
277     }
278 
279     tracer.Finish();
280     if (context->work != nullptr) {
281         MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
282                                                    context->work, *jsContext);
283     }
284     delete context;
285 }
286 
GetFdForArrayBuffer(std::string uriStr)287 static int32_t GetFdForArrayBuffer(std::string uriStr)
288 {
289     int32_t fd = 0;
290     Uri uri(uriStr);
291     fd = UserFileClient::OpenFile(uri, MEDIA_FILEMODE_READONLY);
292     if (fd == E_ERR) {
293         NAPI_ERR_LOG("Open highlight cover file failed, error: %{public}d", errno);
294         return E_HAS_FS_ERROR;
295     } else if (fd < 0) {
296         NAPI_ERR_LOG("Open highlight cover file failed due to OpenFile failure");
297         return fd;
298     }
299     return fd;
300 }
301 
JSGetHighlightResourceExecute(napi_env env,void * data)302 static void JSGetHighlightResourceExecute(napi_env env, void *data)
303 {
304     MediaLibraryTracer tracer;
305     tracer.Start("JSGetHighlightResourceExecute");
306 
307     auto *context = static_cast<HighlightAlbumNapiAsyncContext*>(data);
308     if (context->resourceUri.find(MEDIA_DATA_DB_HIGHLIGHT) == string::npos) {
309         NAPI_ERR_LOG("Invalid highlight resource uri");
310         return;
311     }
312 
313     int32_t fd = GetFdForArrayBuffer(context->resourceUri);
314     if (fd < 0) {
315         return;
316     }
317     UniqueFd uniqueFd(fd);
318     off_t fileLen = lseek(uniqueFd.Get(), 0, SEEK_END);
319     if (fileLen < 0) {
320         NAPI_ERR_LOG("Failed to get highlight cover file length, error: %{public}d", errno);
321         return;
322     }
323     off_t ret = lseek(uniqueFd.Get(), 0, SEEK_SET);
324     if (ret < 0) {
325         NAPI_ERR_LOG("Failed to reset highlight cover file offset, error: %{public}d", errno);
326         return;
327     }
328     void* arrayBufferData = nullptr;
329     napi_value arrayBuffer;
330     if (napi_create_arraybuffer(env, fileLen, &arrayBufferData, &arrayBuffer) != napi_ok) {
331         NAPI_ERR_LOG("failed to create napi arraybuffer");
332         return;
333     }
334 
335     ssize_t readBytes = read(uniqueFd.Get(), arrayBufferData, fileLen);
336     if (readBytes != fileLen) {
337         NAPI_ERR_LOG("read file failed, read bytes is %{public}zu, actual length is %{public}jd, "
338             "error: %{public}d", readBytes, fileLen, errno);
339         return;
340     }
341     context->napiArrayBuffer = arrayBuffer;
342 }
343 
JSGetHighlightResourceCompleteCallback(napi_env env,napi_status status,void * data)344 static void JSGetHighlightResourceCompleteCallback(napi_env env, napi_status status, void *data)
345 {
346     MediaLibraryTracer tracer;
347     tracer.Start("JSGetHighlightResourceCompleteCallback");
348 
349     auto *context = static_cast<HighlightAlbumNapiAsyncContext *>(data);
350     CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
351 
352     unique_ptr<JSAsyncContextOutput> jsContext = make_unique<JSAsyncContextOutput>();
353     jsContext->status = false;
354 
355     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->data), JS_INNER_FAIL);
356     CHECK_ARGS_RET_VOID(env, napi_get_undefined(env, &jsContext->error), JS_INNER_FAIL);
357     if (context->error == ERR_DEFAULT) {
358         jsContext->data = context->napiArrayBuffer;
359         jsContext->status = true;
360     } else {
361         context->HandleError(env, jsContext->error);
362     }
363 
364     tracer.Finish();
365     if (context->work != nullptr) {
366         MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
367                                                    context->work, *jsContext);
368     }
369     delete context;
370 }
371 
JSGetHighlightAlbumInfo(napi_env env,napi_callback_info info)372 napi_value HighlightAlbumNapi::JSGetHighlightAlbumInfo(napi_env env, napi_callback_info info)
373 {
374     MediaLibraryTracer tracer;
375     tracer.Start("JSGetHighlightAlbumInfo");
376 
377     napi_value result = nullptr;
378     NAPI_CALL(env, napi_get_undefined(env, &result));
379     unique_ptr<HighlightAlbumNapiAsyncContext> asyncContext = make_unique<HighlightAlbumNapiAsyncContext>();
380     CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "asyncContext context is null");
381     CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsNumberCallback(env, info, asyncContext,
382         asyncContext->highlightAlbumInfoType), JS_ERR_PARAMETER_INVALID);
383 
384     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
385     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
386     CHECK_COND_WITH_MESSAGE(env,
387         PhotoAlbum::IsHighlightAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
388         "Only and smart highlight album can get highlight album info");
389 
390     asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
391     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetHighlightAlbumInfo",
392         JSGetHighlightAlbumInfoExecute, JSGetHighlightAlbumInfoCompleteCallback);
393 }
394 
JSSetHighlightUserActionData(napi_env env,napi_callback_info info)395 napi_value HighlightAlbumNapi::JSSetHighlightUserActionData(napi_env env, napi_callback_info info)
396 {
397     MediaLibraryTracer tracer;
398     tracer.Start("JSSetHighlightUserActionData");
399 
400     napi_value result = nullptr;
401     NAPI_CALL(env, napi_get_undefined(env, &result));
402     unique_ptr<HighlightAlbumNapiAsyncContext> asyncContext = make_unique<HighlightAlbumNapiAsyncContext>();
403     CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "asyncContext context is null");
404 
405     CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsNumberCallback(env, info, asyncContext,
406         asyncContext->highlightUserActionType), JS_ERR_PARAMETER_INVALID);
407     CHECK_NULLPTR_RET(MediaLibraryNapiUtils::GetInt32Arg(env, asyncContext->argv[1], asyncContext->actionData));
408 
409     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
410     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
411     CHECK_COND_WITH_MESSAGE(env,
412         PhotoAlbum::IsHighlightAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
413         "Only and smart highlight album can set user action info");
414 
415     asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
416     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSSetHighlightUserActionData",
417         JSSetHighlightUserActionDataExecute, JSSetHighlightUserActionDataCompleteCallback);
418 }
419 
JSGetHighlightResource(napi_env env,napi_callback_info info)420 napi_value HighlightAlbumNapi::JSGetHighlightResource(napi_env env, napi_callback_info info)
421 {
422     MediaLibraryTracer tracer;
423     tracer.Start("JSGetHighlightResource");
424 
425     napi_value result = nullptr;
426     NAPI_CALL(env, napi_get_undefined(env, &result));
427     unique_ptr<HighlightAlbumNapiAsyncContext> asyncContext = make_unique<HighlightAlbumNapiAsyncContext>();
428     CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "asyncContext context is null");
429 
430     CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsStringCallback(env, info, asyncContext, asyncContext->resourceUri),
431         JS_ERR_PARAMETER_INVALID);
432 
433     auto photoAlbum = asyncContext->objectInfo->GetPhotoAlbumInstance();
434     CHECK_COND_WITH_MESSAGE(env, photoAlbum != nullptr, "photoAlbum is null");
435     CHECK_COND_WITH_MESSAGE(env,
436         PhotoAlbum::IsHighlightAlbum(photoAlbum->GetPhotoAlbumType(), photoAlbum->GetPhotoAlbumSubType()),
437         "Only and smart highlight album can set user action info");
438 
439     asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
440     return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetHighlightResource",
441         JSGetHighlightResourceExecute, JSGetHighlightResourceCompleteCallback);
442 }
443 
GetPhotoAlbumInstance() const444 shared_ptr<PhotoAlbum> HighlightAlbumNapi::GetPhotoAlbumInstance() const
445 {
446     return highlightAlbumPtr;
447 }
448 } // namespace OHOS::Media
449