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