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