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