1/*
2 * Copyright (C) 2021-2022 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#define MLOG_TAG "AlbumNapi"
16
17#include "album_napi.h"
18
19#include "media_file_asset_columns.h"
20#include "media_file_utils.h"
21#include "media_library_napi.h"
22#include "medialibrary_client_errno.h"
23#include "medialibrary_napi_log.h"
24#include "medialibrary_tracer.h"
25#include "userfile_client.h"
26#include "userfile_manager_types.h"
27
28using OHOS::HiviewDFX::HiLog;
29using OHOS::HiviewDFX::HiLogLabel;
30
31namespace OHOS {
32namespace Media {
33using namespace std;
34using namespace OHOS::DataShare;
35thread_local napi_ref AlbumNapi::sConstructor_ = nullptr;
36thread_local AlbumAsset *AlbumNapi::sAlbumData_ = nullptr;
37using CompleteCallback = napi_async_complete_callback;
38
39thread_local napi_ref AlbumNapi::userFileMgrConstructor_ = nullptr;
40thread_local napi_ref AlbumNapi::photoAccessHelperConstructor_ = nullptr;
41
42AlbumNapi::AlbumNapi()
43    : env_(nullptr) {}
44
45AlbumNapi::~AlbumNapi() = default;
46
47void AlbumNapi::AlbumNapiDestructor(napi_env env, void *nativeObject, void *finalize_hint)
48{
49    AlbumNapi *album = reinterpret_cast<AlbumNapi*>(nativeObject);
50    if (album != nullptr) {
51        delete album;
52        album = nullptr;
53    }
54}
55
56napi_value AlbumNapi::Init(napi_env env, napi_value exports)
57{
58    napi_status status;
59    napi_value ctorObj;
60    int32_t refCount = 1;
61
62    napi_property_descriptor album_props[] = {
63        DECLARE_NAPI_GETTER("albumId", JSGetAlbumId),
64        DECLARE_NAPI_GETTER_SETTER("albumName", JSGetAlbumName, JSAlbumNameSetter),
65        DECLARE_NAPI_GETTER("albumUri", JSGetAlbumUri),
66        DECLARE_NAPI_GETTER("dateModified", JSGetAlbumDateModified),
67        DECLARE_NAPI_GETTER("count", JSGetCount),
68        DECLARE_NAPI_GETTER("relativePath", JSGetAlbumRelativePath),
69        DECLARE_NAPI_GETTER("coverUri", JSGetCoverUri),
70        DECLARE_NAPI_FUNCTION("commitModify", JSCommitModify),
71        DECLARE_NAPI_GETTER_SETTER("path", JSGetAlbumPath, JSSetAlbumPath),
72        DECLARE_NAPI_GETTER("virtual", JSGetAlbumVirtual),
73        DECLARE_NAPI_FUNCTION("getFileAssets", JSGetAlbumFileAssets)
74    };
75
76    status = napi_define_class(env, ALBUM_NAPI_CLASS_NAME.c_str(), NAPI_AUTO_LENGTH,
77                               AlbumNapiConstructor, nullptr,
78                               sizeof(album_props) / sizeof(album_props[PARAM0]),
79                               album_props, &ctorObj);
80    if (status == napi_ok) {
81        status = napi_create_reference(env, ctorObj, refCount, &sConstructor_);
82        if (status == napi_ok) {
83            status = napi_set_named_property(env, exports, ALBUM_NAPI_CLASS_NAME.c_str(), ctorObj);
84            if (status == napi_ok) {
85                return exports;
86            }
87        }
88    }
89
90    return nullptr;
91}
92
93napi_value AlbumNapi::UserFileMgrInit(napi_env env, napi_value exports)
94{
95    NapiClassInfo info = {
96        .name = USERFILEMGR_ALBUM_NAPI_CLASS_NAME,
97        .ref = &userFileMgrConstructor_,
98        .constructor = AlbumNapiConstructor,
99        .props = {
100            DECLARE_NAPI_FUNCTION("getPhotoAssets", UserFileMgrGetAssets),
101            DECLARE_NAPI_FUNCTION("commitModify", UserFileMgrCommitModify),
102            DECLARE_NAPI_GETTER_SETTER("albumName", JSGetAlbumName, JSAlbumNameSetter),
103            DECLARE_NAPI_GETTER("albumUri", JSGetAlbumUri),
104            DECLARE_NAPI_GETTER("dateModified", JSGetAlbumDateModified),
105            DECLARE_NAPI_GETTER("count", JSGetCount),
106            DECLARE_NAPI_GETTER("relativePath", JSGetAlbumRelativePath),
107            DECLARE_NAPI_GETTER("coverUri", JSGetCoverUri)
108        }
109    };
110
111    MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
112    return exports;
113}
114
115napi_value AlbumNapi::PhotoAccessHelperInit(napi_env env, napi_value exports)
116{
117    NapiClassInfo info = {
118        .name = PHOTOACCESSHELPER_ALBUM_NAPI_CLASS_NAME,
119        .ref = &photoAccessHelperConstructor_,
120        .constructor = AlbumNapiConstructor,
121        .props = {
122            DECLARE_NAPI_FUNCTION("getAssets", PhotoAccessHelperGetAssets),
123            DECLARE_NAPI_FUNCTION("commitModify", PhotoAccessHelperCommitModify),
124            DECLARE_NAPI_GETTER_SETTER("albumName", JSGetAlbumName, JSAlbumNameSetter),
125            DECLARE_NAPI_GETTER("albumUri", JSGetAlbumUri),
126            DECLARE_NAPI_GETTER("count", JSGetCount),
127            DECLARE_NAPI_GETTER("coverUri", JSGetCoverUri)
128        }
129    };
130
131    MediaLibraryNapiUtils::NapiDefineClass(env, exports, info);
132    return exports;
133}
134
135void AlbumNapi::SetAlbumNapiProperties()
136{
137    albumAssetPtr = std::shared_ptr<AlbumAsset>(sAlbumData_);
138}
139
140// Constructor callback
141napi_value AlbumNapi::AlbumNapiConstructor(napi_env env, napi_callback_info info)
142{
143    napi_status status;
144    napi_value result = nullptr;
145    napi_value thisVar = nullptr;
146
147    napi_get_undefined(env, &result);
148    GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
149    if (status == napi_ok && thisVar != nullptr) {
150        std::unique_ptr<AlbumNapi> obj = std::make_unique<AlbumNapi>();
151        if (obj != nullptr) {
152            obj->env_ = env;
153            if (sAlbumData_ != nullptr) {
154                obj->SetAlbumNapiProperties();
155            }
156
157            status = napi_wrap(env, thisVar, reinterpret_cast<void *>(obj.get()),
158                               AlbumNapi::AlbumNapiDestructor, nullptr, nullptr);
159            if (status == napi_ok) {
160                obj.release();
161                return thisVar;
162            } else {
163                NAPI_ERR_LOG("Failure wrapping js to native napi. status: %{public}d", status);
164            }
165        }
166    }
167
168    return result;
169}
170
171napi_value AlbumNapi::CreateAlbumNapi(napi_env env, unique_ptr<AlbumAsset> &albumData)
172{
173    if (albumData == nullptr) {
174        return nullptr;
175    }
176
177    napi_value constructor;
178    napi_ref constructorRef;
179    if (albumData->GetResultNapiType() == ResultNapiType::TYPE_USERFILE_MGR) {
180        constructorRef = userFileMgrConstructor_;
181    } else if (albumData->GetResultNapiType() == ResultNapiType::TYPE_PHOTOACCESS_HELPER) {
182        constructorRef = photoAccessHelperConstructor_;
183    } else {
184        constructorRef = sConstructor_;
185    }
186    NAPI_CALL(env, napi_get_reference_value(env, constructorRef, &constructor));
187
188    napi_value result = nullptr;
189    sAlbumData_ = albumData.release();
190    NAPI_CALL(env, napi_new_instance(env, constructor, 0, nullptr, &result));
191    sAlbumData_ = nullptr;
192    return result;
193}
194
195std::string AlbumNapi::GetAlbumName() const
196{
197    return albumAssetPtr->GetAlbumName();
198}
199
200std::string AlbumNapi::GetAlbumPath() const
201{
202    return albumAssetPtr->GetAlbumPath();
203}
204
205int32_t AlbumNapi::GetAlbumId() const
206{
207    return albumAssetPtr->GetAlbumId();
208}
209
210std::string AlbumNapi::GetAlbumUri() const
211{
212    return albumAssetPtr->GetAlbumUri();
213}
214
215std::string AlbumNapi::GetNetworkId() const
216{
217    return MediaFileUtils::GetNetworkIdFromUri(GetAlbumUri());
218}
219
220#ifdef MEDIALIBRARY_COMPATIBILITY
221PhotoAlbumType AlbumNapi::GetAlbumType() const
222{
223    return albumAssetPtr->GetAlbumType();
224}
225PhotoAlbumSubType AlbumNapi::GetAlbumSubType() const
226{
227    return albumAssetPtr->GetAlbumSubType();
228}
229#endif
230
231napi_value AlbumNapi::JSGetAlbumId(napi_env env, napi_callback_info info)
232{
233    napi_status status;
234    napi_value jsResult = nullptr;
235    napi_value undefinedResult = nullptr;
236    AlbumNapi* obj = nullptr;
237    int32_t id;
238    napi_value thisVar = nullptr;
239
240    napi_get_undefined(env, &undefinedResult);
241    GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
242    if (status != napi_ok || thisVar == nullptr) {
243        NAPI_ERR_LOG("Invalid arguments! status: %{public}d", status);
244        return undefinedResult;
245    }
246
247    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
248    if (status == napi_ok && obj != nullptr) {
249        id = obj->GetAlbumId();
250        status = napi_create_int32(env, id, &jsResult);
251        if (status == napi_ok) {
252            return jsResult;
253        }
254    }
255
256    return undefinedResult;
257}
258
259napi_value AlbumNapi::JSGetAlbumName(napi_env env, napi_callback_info info)
260{
261    napi_status status;
262    napi_value jsResult = nullptr;
263    napi_value undefinedResult = nullptr;
264    AlbumNapi* obj = nullptr;
265    std::string name = "";
266    napi_value thisVar = nullptr;
267    napi_get_undefined(env, &undefinedResult);
268    GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
269    if (status != napi_ok || thisVar == nullptr) {
270        NAPI_ERR_LOG("Invalid arguments! status: %{public}d", status);
271        return undefinedResult;
272    }
273
274    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
275    if (status == napi_ok && obj != nullptr) {
276        name = obj->GetAlbumName();
277        status = napi_create_string_utf8(env, name.c_str(), NAPI_AUTO_LENGTH, &jsResult);
278        if (status == napi_ok) {
279            return jsResult;
280        }
281    }
282
283    return undefinedResult;
284}
285
286napi_value AlbumNapi::JSAlbumNameSetter(napi_env env, napi_callback_info info)
287{
288    napi_status status;
289    napi_value jsResult = nullptr;
290    size_t argc = ARGS_ONE;
291    napi_value argv[ARGS_ONE] = {0};
292    size_t res = 0;
293    char buffer[FILENAME_MAX];
294    AlbumNapi* obj = nullptr;
295    napi_value thisVar = nullptr;
296    napi_valuetype valueType = napi_undefined;
297
298    napi_get_undefined(env, &jsResult);
299    GET_JS_ARGS(env, info, argc, argv, thisVar);
300    NAPI_ASSERT(env, argc == ARGS_ONE, "requires 1 parameter");
301    if (thisVar == nullptr || napi_typeof(env, argv[PARAM0], &valueType) != napi_ok
302        || valueType != napi_string) {
303        NAPI_ERR_LOG("Invalid arguments type! valueType: %{public}d", valueType);
304        return jsResult;
305    }
306
307    napi_get_value_string_utf8(env, argv[PARAM0], buffer, FILENAME_MAX, &res);
308
309    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
310    if (status == napi_ok && obj != nullptr) {
311        obj->albumAssetPtr->SetAlbumName(std::string(buffer));
312    } else {
313        NAPI_ERR_LOG("status = %{public}d", status);
314    }
315    return jsResult;
316}
317napi_value AlbumNapi::JSGetAlbumUri(napi_env env, napi_callback_info info)
318{
319    napi_status status;
320    napi_value jsResult = nullptr;
321    napi_value undefinedResult = nullptr;
322    AlbumNapi* obj = nullptr;
323    std::string uri = "";
324    napi_value thisVar = nullptr;
325
326    napi_get_undefined(env, &undefinedResult);
327    GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
328    if (status != napi_ok || thisVar == nullptr) {
329        NAPI_ERR_LOG("Invalid arguments! status: %{public}d", status);
330        return undefinedResult;
331    }
332
333    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
334    if (status == napi_ok && obj != nullptr) {
335        uri = obj->GetAlbumUri();
336        status = napi_create_string_utf8(env, uri.c_str(), NAPI_AUTO_LENGTH, &jsResult);
337        if (status == napi_ok) {
338            return jsResult;
339        }
340    }
341
342    return undefinedResult;
343}
344napi_value AlbumNapi::JSGetAlbumDateModified(napi_env env, napi_callback_info info)
345{
346    napi_status status;
347    napi_value jsResult = nullptr;
348    napi_value undefinedResult = nullptr;
349    AlbumNapi* obj = nullptr;
350    int64_t dateModified;
351    napi_value thisVar = nullptr;
352
353    napi_get_undefined(env, &undefinedResult);
354    GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
355    if (status != napi_ok || thisVar == nullptr) {
356        NAPI_ERR_LOG("Invalid arguments! status: %{public}d", status);
357        return undefinedResult;
358    }
359
360    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
361    if (status == napi_ok && obj != nullptr) {
362        dateModified = obj->albumAssetPtr->GetAlbumDateModified() / MSEC_TO_SEC;
363        status = napi_create_int64(env, dateModified, &jsResult);
364        if (status == napi_ok) {
365            return jsResult;
366        }
367    }
368
369    return undefinedResult;
370}
371napi_value AlbumNapi::JSGetCount(napi_env env, napi_callback_info info)
372{
373    napi_status status;
374    napi_value jsResult = nullptr;
375    napi_value undefinedResult = nullptr;
376    AlbumNapi *obj = nullptr;
377    int32_t count;
378    napi_value thisVar = nullptr;
379
380    napi_get_undefined(env, &undefinedResult);
381    GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
382    if (status != napi_ok || thisVar == nullptr) {
383        NAPI_ERR_LOG("Invalid arguments! status: %{public}d", status);
384        return undefinedResult;
385    }
386    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
387    if (status == napi_ok && obj != nullptr) {
388        count = obj->albumAssetPtr->GetCount();
389        status = napi_create_int32(env, count, &jsResult);
390        if (status == napi_ok) {
391            return jsResult;
392        }
393    }
394    return undefinedResult;
395}
396napi_value AlbumNapi::JSGetAlbumRelativePath(napi_env env, napi_callback_info info)
397{
398    napi_status status;
399    napi_value jsResult = nullptr;
400    napi_value undefinedResult = nullptr;
401    AlbumNapi* obj = nullptr;
402    std::string relativePath = "";
403    napi_value thisVar = nullptr;
404
405    napi_get_undefined(env, &undefinedResult);
406    GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
407    if (status != napi_ok || thisVar == nullptr) {
408        NAPI_ERR_LOG("Invalid arguments! status: %{public}d", status);
409        return undefinedResult;
410    }
411
412    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
413    if (status == napi_ok && obj != nullptr) {
414        relativePath = obj->albumAssetPtr->GetAlbumRelativePath();
415        status = napi_create_string_utf8(env, relativePath.c_str(), NAPI_AUTO_LENGTH, &jsResult);
416        if (status == napi_ok) {
417            return jsResult;
418        }
419    }
420
421    return undefinedResult;
422}
423napi_value AlbumNapi::JSGetCoverUri(napi_env env, napi_callback_info info)
424{
425    napi_status status;
426    napi_value jsResult = nullptr;
427    napi_value undefinedResult = nullptr;
428    AlbumNapi* obj = nullptr;
429    std::string coverUri = "";
430    napi_value thisVar = nullptr;
431
432    napi_get_undefined(env, &undefinedResult);
433    GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
434    if (status != napi_ok || thisVar == nullptr) {
435        NAPI_ERR_LOG("Invalid arguments! status: %{public}d", status);
436        return undefinedResult;
437    }
438
439    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
440    if (status == napi_ok && obj != nullptr) {
441        coverUri = obj->albumAssetPtr->GetCoverUri();
442        status = napi_create_string_utf8(env, coverUri.c_str(), NAPI_AUTO_LENGTH, &jsResult);
443        if (status == napi_ok) {
444            return jsResult;
445        }
446    }
447
448    return undefinedResult;
449}
450
451napi_value AlbumNapi::JSSetAlbumPath(napi_env env, napi_callback_info info)
452{
453    napi_status status;
454    napi_value jsResult = nullptr;
455    size_t argc = ARGS_ONE;
456    napi_value argv[ARGS_ONE] = {0};
457    size_t res = 0;
458    char buffer[PATH_MAX];
459    AlbumNapi* obj = nullptr;
460    napi_value thisVar = nullptr;
461    napi_valuetype valueType = napi_undefined;
462
463    napi_get_undefined(env, &jsResult);
464    GET_JS_ARGS(env, info, argc, argv, thisVar);
465    NAPI_ASSERT(env, argc == ARGS_ONE, "requires 1 parameter");
466
467    if (thisVar == nullptr || napi_typeof(env, argv[PARAM0], &valueType) != napi_ok
468        || valueType != napi_string) {
469        NAPI_ERR_LOG("Invalid arguments type! type: %{public}d", valueType);
470        return jsResult;
471    }
472
473    napi_get_value_string_utf8(env, argv[PARAM0], buffer, PATH_MAX, &res);
474
475    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
476    if (status == napi_ok && obj != nullptr) {
477        obj->albumAssetPtr->SetAlbumPath(std::string(buffer));
478    }
479
480    return jsResult;
481}
482
483napi_value AlbumNapi::JSGetAlbumPath(napi_env env, napi_callback_info info)
484{
485    napi_status status;
486    napi_value jsResult = nullptr;
487    napi_value undefinedResult = nullptr;
488    AlbumNapi* obj = nullptr;
489    std::string path = "";
490    napi_value thisVar = nullptr;
491
492    napi_get_undefined(env, &undefinedResult);
493    GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
494    if (status != napi_ok || thisVar == nullptr) {
495        NAPI_ERR_LOG("Invalid arguments! status: %{public}d", status);
496        return undefinedResult;
497    }
498
499    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
500    if (status == napi_ok && obj != nullptr) {
501        path = obj->GetAlbumPath();
502        status = napi_create_string_utf8(env, path.c_str(), NAPI_AUTO_LENGTH, &jsResult);
503        if (status == napi_ok) {
504            return jsResult;
505        }
506    }
507
508    return undefinedResult;
509}
510
511napi_value AlbumNapi::JSGetAlbumVirtual(napi_env env, napi_callback_info info)
512{
513    napi_status status;
514    napi_value jsResult = nullptr;
515    napi_value undefinedResult = nullptr;
516    AlbumNapi* obj = nullptr;
517    bool virtualAlbum = false;
518    napi_value thisVar = nullptr;
519
520    napi_get_undefined(env, &undefinedResult);
521    GET_JS_OBJ_WITH_ZERO_ARGS(env, info, status, thisVar);
522    if (status != napi_ok || thisVar == nullptr) {
523        NAPI_ERR_LOG("Invalid arguments! status: %{public}d", status);
524        return undefinedResult;
525    }
526
527    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&obj));
528    if (status == napi_ok && obj != nullptr) {
529        virtualAlbum = obj->albumAssetPtr->GetAlbumVirtual();
530        status = napi_get_boolean(env, virtualAlbum, &jsResult);
531        if (status == napi_ok) {
532            return jsResult;
533        }
534    }
535
536    return undefinedResult;
537}
538
539static void GetFetchOptionsParam(napi_env env, napi_value arg, const AlbumNapiAsyncContext &context, bool &err)
540{
541    AlbumNapiAsyncContext *asyncContext = const_cast<AlbumNapiAsyncContext *>(&context);
542    CHECK_NULL_PTR_RETURN_VOID(asyncContext, "Async context is null");
543    char buffer[PATH_MAX];
544    size_t res;
545    uint32_t len = 0;
546    napi_value property = nullptr;
547    napi_value stringItem = nullptr;
548    bool present = false;
549    bool boolResult = false;
550
551    string propertyName = "selections";
552    string tmp = MediaLibraryNapiUtils::GetStringFetchProperty(env, arg, err, present, propertyName);
553    if (!tmp.empty()) {
554        asyncContext->selection = tmp;
555    }
556
557    propertyName = "order";
558    tmp = MediaLibraryNapiUtils::GetStringFetchProperty(env, arg, err, present, propertyName);
559    if (!tmp.empty()) {
560        asyncContext->order = tmp;
561    }
562
563    napi_has_named_property(env, arg, "selectionArgs", &present);
564    if (present && napi_get_named_property(env, arg, "selectionArgs", &property) == napi_ok &&
565        napi_is_array(env, property, &boolResult) == napi_ok && boolResult) {
566        napi_get_array_length(env, property, &len);
567        for (size_t i = 0; i < len; i++) {
568            napi_get_element(env, property, i, &stringItem);
569            napi_get_value_string_utf8(env, stringItem, buffer, PATH_MAX, &res);
570            asyncContext->selectionArgs.push_back(std::string(buffer));
571            CHECK_IF_EQUAL(memset_s(buffer, PATH_MAX, 0, sizeof(buffer)) == 0, "Memset for buffer failed");
572        }
573    } else {
574        NAPI_ERR_LOG("Could not get the string argument!");
575        err = true;
576    }
577}
578
579static napi_value ConvertJSArgsToNative(napi_env env, size_t argc, const napi_value argv[],
580    AlbumNapiAsyncContext &asyncContext)
581{
582    string str = "";
583    std::vector<string> strArr;
584    string order = "";
585    bool err = false;
586    const int32_t refCount = 1;
587    napi_value result;
588    auto context = &asyncContext;
589    CHECK_NULL_PTR_RETURN_UNDEFINED(env, context, result, "Async context is null");
590    NAPI_ASSERT(env, argv != nullptr, "Argument list is empty");
591    if (argc == ARGS_ONE) {
592        napi_valuetype valueType = napi_undefined;
593        if (napi_typeof(env, argv[PARAM0], &valueType) == napi_ok &&
594            (valueType == napi_undefined || valueType == napi_null)) {
595            argc -= 1;
596        }
597    }
598
599    for (size_t i = PARAM0; i < argc; i++) {
600        napi_valuetype valueType = napi_undefined;
601        napi_typeof(env, argv[i], &valueType);
602
603        if (i == PARAM0 && valueType == napi_object) {
604            GetFetchOptionsParam(env, argv[PARAM0], asyncContext, err);
605            if (err) {
606                NAPI_ASSERT(env, false, "type mismatch");
607            }
608        } else if (i == PARAM0 && valueType == napi_function) {
609            napi_create_reference(env, argv[i], refCount, &context->callbackRef);
610            break;
611        } else if (i == PARAM1 && valueType == napi_function) {
612            napi_create_reference(env, argv[i], refCount, &context->callbackRef);
613            break;
614        } else {
615            NAPI_ASSERT(env, false, "type mismatch");
616        }
617    }
618
619    // Return true napi_value if params are successfully obtained
620    napi_get_boolean(env, true, &result);
621    return result;
622}
623static napi_value ConvertCommitJSArgsToNative(napi_env env, size_t argc, const napi_value argv[],
624    AlbumNapiAsyncContext &asyncContext)
625{
626    string str = "";
627    vector<string> strArr;
628    string order = "";
629    bool err = false;
630    const int32_t refCount = 1;
631    napi_value result;
632    auto context = &asyncContext;
633    CHECK_NULL_PTR_RETURN_UNDEFINED(env, context, result, "Async context is null");
634    NAPI_ASSERT(env, argv != nullptr, "Argument list is empty");
635
636    for (size_t i = PARAM0; i < argc; i++) {
637        napi_valuetype valueType = napi_undefined;
638        napi_typeof(env, argv[i], &valueType);
639
640        if (i == PARAM0 && valueType == napi_object) {
641            GetFetchOptionsParam(env, argv[PARAM0], asyncContext, err);
642            if (err) {
643                NAPI_ERR_LOG("fetch options retrieval failed. err %{public}d", err);
644                NAPI_ASSERT(env, false, "type mismatch");
645            }
646        } else if (i == PARAM0 && valueType == napi_function) {
647            napi_create_reference(env, argv[i], refCount, &context->callbackRef);
648            break;
649        } else if (i == PARAM1 && valueType == napi_function) {
650            napi_create_reference(env, argv[i], refCount, &context->callbackRef);
651            break;
652        } else {
653            NAPI_ASSERT(env, false, "type mismatch");
654        }
655    }
656
657    // Return true napi_value if params are successfully obtained
658    napi_get_boolean(env, true, &result);
659    return result;
660}
661
662#ifdef MEDIALIBRARY_COMPATIBILITY
663static void UpdateCompatAlbumSelection(AlbumNapiAsyncContext *context)
664{
665    PhotoAlbumSubType subType = context->objectPtr->GetAlbumSubType();
666    string filterClause;
667    switch (subType) {
668        case PhotoAlbumSubType::CAMERA: {
669            static const string CAMERA_FILTER = PhotoColumn::PHOTO_SUBTYPE + "=" +
670                to_string(static_cast<int32_t>(PhotoSubType::CAMERA)) + " AND " + MediaColumn::ASSETS_QUERY_FILTER;
671            filterClause = CAMERA_FILTER;
672            break;
673        }
674        case PhotoAlbumSubType::SCREENSHOT: {
675            static const string SCREENSHOT_FILTER = PhotoColumn::PHOTO_SUBTYPE + "=" +
676                to_string(static_cast<int32_t>(PhotoSubType::SCREENSHOT)) + " AND " + MediaColumn::ASSETS_QUERY_FILTER;
677            filterClause = SCREENSHOT_FILTER;
678            break;
679        }
680        case PhotoAlbumSubType::FAVORITE: {
681            static const string FAVORITE_FILTER = PhotoColumn::MEDIA_IS_FAV + " = 1" + " AND " +
682                MediaColumn::ASSETS_QUERY_FILTER;
683            filterClause = FAVORITE_FILTER;
684            break;
685        }
686        case PhotoAlbumSubType::TRASH: {
687            static const string TRASH_FILTER =
688                PhotoColumn::MEDIA_DATE_TRASHED + " > 0 AND " + MEDIA_DATA_DB_IS_TRASH + " <> " +
689                to_string(static_cast<int32_t>(TRASHED_DIR_CHILD));
690            filterClause = TRASH_FILTER;
691            break;
692        }
693        default: {
694            NAPI_ERR_LOG("Album subtype not support for compatibility: %{public}d", subType);
695            context->SaveError(-EINVAL);
696            return;
697        }
698    }
699    if (!context->selection.empty()) {
700        context->selection = filterClause + " AND (" + context->selection + ")";
701    } else {
702        context->selection = filterClause;
703    }
704    MediaLibraryNapi::ReplaceSelection(context->selection, context->selectionArgs,
705        MEDIA_DATA_DB_RELATIVE_PATH, MEDIA_DATA_DB_RELATIVE_PATH);
706}
707#endif
708
709static void UpdateSelection(AlbumNapiAsyncContext *context)
710{
711    if (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR ||
712        context->resultNapiType == ResultNapiType::TYPE_PHOTOACCESS_HELPER) {
713        context->predicates.EqualTo(MEDIA_DATA_DB_DATE_TRASHED, 0);
714        context->predicates.NotEqualTo(MEDIA_DATA_DB_MEDIA_TYPE, MEDIA_TYPE_ALBUM);
715        context->predicates.EqualTo(MEDIA_DATA_DB_BUCKET_ID, context->objectPtr->GetAlbumId());
716        context->predicates.EqualTo(MediaColumn::MEDIA_TIME_PENDING, to_string(0));
717        context->predicates.EqualTo(PhotoColumn::PHOTO_IS_TEMP, to_string(0));
718        context->predicates.EqualTo(PhotoColumn::PHOTO_BURST_COVER_LEVEL,
719            to_string(static_cast<int32_t>(BurstCoverLevelType::COVER)));
720        MediaLibraryNapiUtils::UpdateMediaTypeSelections(context);
721    } else {
722#ifdef MEDIALIBRARY_COMPATIBILITY
723        UpdateCompatAlbumSelection(context);
724#else
725        string trashPrefix = MEDIA_DATA_DB_DATE_TRASHED + " = ? ";
726        MediaLibraryNapiUtils::AppendFetchOptionSelection(context->selection, trashPrefix);
727        context->selectionArgs.emplace_back("0");
728
729        string prefix = MEDIA_DATA_DB_MEDIA_TYPE + " <> ? ";
730        MediaLibraryNapiUtils::AppendFetchOptionSelection(context->selection, prefix);
731        context->selectionArgs.emplace_back(to_string(MEDIA_TYPE_ALBUM));
732
733        string idPrefix = MEDIA_DATA_DB_BUCKET_ID + " = ? ";
734        MediaLibraryNapiUtils::AppendFetchOptionSelection(context->selection, idPrefix);
735        context->selectionArgs.emplace_back(std::to_string(context->objectPtr->GetAlbumId()));
736#endif
737    }
738}
739
740static void GetFileAssetsNative(napi_env env, void *data)
741{
742    MediaLibraryTracer tracer;
743    tracer.Start("GetFileAssetsNative");
744
745    AlbumNapiAsyncContext *context = static_cast<AlbumNapiAsyncContext*>(data);
746
747    UpdateSelection(context);
748    MediaLibraryNapiUtils::FixSpecialDateType(context->selection);
749    context->predicates.SetWhereClause(context->selection);
750    context->predicates.SetWhereArgs(context->selectionArgs);
751    context->predicates.SetOrder(context->order);
752
753    if (context->resultNapiType == ResultNapiType::TYPE_MEDIALIBRARY) {
754        context->fetchColumn = FILE_ASSET_COLUMNS;
755    } else {
756        context->fetchColumn.push_back(MEDIA_DATA_DB_ID);
757        context->fetchColumn.push_back(MEDIA_DATA_DB_NAME);
758        context->fetchColumn.push_back(MEDIA_DATA_DB_MEDIA_TYPE);
759    }
760
761    string queryUri = MEDIALIBRARY_DATA_ABILITY_PREFIX +
762        (MediaFileUtils::GetNetworkIdFromUri(context->objectPtr->GetAlbumUri())) + MEDIALIBRARY_DATA_URI_IDENTIFIER;
763    NAPI_DEBUG_LOG("queryUri is = %{private}s", queryUri.c_str());
764    Uri uri(queryUri);
765    int errCode = 0;
766    std::shared_ptr<OHOS::DataShare::DataShareResultSet> resultSet =
767        UserFileClient::Query(uri, context->predicates, context->fetchColumn, errCode);
768    if (resultSet == nullptr) {
769        NAPI_ERR_LOG("GetFileAssetsNative called, UserFileClient::Query errorCode is = %{public}d", errCode);
770        context->SaveError(errCode);
771        return;
772    }
773    context->fetchResult = std::make_unique<FetchResult<FileAsset>>(move(resultSet));
774    context->fetchResult->SetNetworkId(MediaFileUtils::GetNetworkIdFromUri(context->objectPtr->GetAlbumUri()));
775    if (context->resultNapiType == ResultNapiType::TYPE_USERFILE_MGR ||
776        context->resultNapiType == ResultNapiType::TYPE_PHOTOACCESS_HELPER) {
777        context->fetchResult->SetResultNapiType(context->resultNapiType);
778    }
779}
780
781static void JSGetFileAssetsCompleteCallback(napi_env env, napi_status status, void *data)
782{
783    MediaLibraryTracer tracer;
784    tracer.Start("JSGetFileAssetsCompleteCallback");
785
786    AlbumNapiAsyncContext *context = static_cast<AlbumNapiAsyncContext*>(data);
787    CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
788
789    std::unique_ptr<JSAsyncContextOutput> jsContext = std::make_unique<JSAsyncContextOutput>();
790    jsContext->status = false;
791    if (context->fetchResult != nullptr) {
792        if (context->fetchResult->GetCount() < 0) {
793            napi_get_undefined(env, &jsContext->data);
794            MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_MEM_ALLOCATION,
795                                                         "find no data by options");
796        } else {
797            napi_value fetchRes = FetchFileResultNapi::CreateFetchFileResult(env, move(context->fetchResult));
798            if (fetchRes == nullptr) {
799                NAPI_ERR_LOG("Failed to get file asset napi object");
800                napi_get_undefined(env, &jsContext->data);
801                MediaLibraryNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_MEM_ALLOCATION,
802                    "Failed to create js object for FetchFileResult");
803            } else {
804                jsContext->data = fetchRes;
805                napi_get_undefined(env, &jsContext->error);
806                jsContext->status = true;
807            }
808        }
809    } else {
810        NAPI_ERR_LOG("No fetch file result found!");
811        context->HandleError(env, jsContext->error);
812        napi_get_undefined(env, &jsContext->data);
813    }
814
815    tracer.Finish();
816    if (context->work != nullptr) {
817        MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
818                                                   context->work, *jsContext);
819    }
820    delete context;
821}
822
823static void CommitModifyNative(napi_env env, void *data)
824{
825    MediaLibraryTracer tracer;
826    tracer.Start("CommitModifyNative");
827
828    auto *context = static_cast<AlbumNapiAsyncContext*>(data);
829    auto objectPtr = context->objectPtr;
830    if (MediaFileUtils::CheckAlbumName(objectPtr->GetAlbumName()) < 0) {
831        context->error = JS_E_DISPLAYNAME;
832        NAPI_ERR_LOG("album name invalid = %{private}s", objectPtr->GetAlbumName().c_str());
833        return;
834    }
835#ifdef MEDIALIBRARY_COMPATIBILITY
836    context->changedRows = 0;
837#else
838    DataSharePredicates predicates;
839    DataShareValuesBucket valuesBucket;
840    valuesBucket.Put(MEDIA_DATA_DB_TITLE, objectPtr->GetAlbumName());
841    predicates.SetWhereClause(MEDIA_DATA_DB_ID + " = ? ");
842    predicates.SetWhereArgs({ std::to_string(objectPtr->GetAlbumId()) });
843
844    string updateUri = MEDIALIBRARY_DATA_URI + "/" +
845        MEDIA_ALBUMOPRN + "/" + MEDIA_ALBUMOPRN_MODIFYALBUM + "/" + std::to_string(objectPtr->GetAlbumId());
846    Uri uri(updateUri);
847    int changedRows = UserFileClient::Update(uri, predicates, valuesBucket);
848    if (changedRows > 0) {
849        DataSharePredicates filePredicates;
850        DataShareValuesBucket fileValuesBucket;
851        fileValuesBucket.Put(MEDIA_DATA_DB_BUCKET_NAME, objectPtr->GetAlbumName());
852        filePredicates.SetWhereClause(MEDIA_DATA_DB_BUCKET_ID + " = ? ");
853        filePredicates.SetWhereArgs({ std::to_string(objectPtr->GetAlbumId()) });
854
855        string fileUriStr = MEDIALIBRARY_DATA_URI;
856        Uri fileUri(fileUriStr);
857        changedRows = UserFileClient::Update(fileUri, filePredicates, fileValuesBucket);
858    }
859    context->SaveError(changedRows);
860    context->changedRows = changedRows;
861#endif
862}
863
864static void JSCommitModifyCompleteCallback(napi_env env, napi_status status, void *data)
865{
866    MediaLibraryTracer tracer;
867    tracer.Start("JSCommitModifyCompleteCallback");
868
869    AlbumNapiAsyncContext *context = static_cast<AlbumNapiAsyncContext*>(data);
870    CHECK_NULL_PTR_RETURN_VOID(context, "Async context is null");
871    std::unique_ptr<JSAsyncContextOutput> jsContext = std::make_unique<JSAsyncContextOutput>();
872    jsContext->status = false;
873    if (context->error == ERR_DEFAULT) {
874        napi_create_int32(env, context->changedRows, &jsContext->data);
875        napi_get_undefined(env, &jsContext->error);
876        jsContext->status = true;
877        auto contextUri = make_unique<Uri>(MEDIALIBRARY_ALBUM_URI);
878        UserFileClient::NotifyChange(*contextUri);
879    } else {
880        napi_get_undefined(env, &jsContext->data);
881        context->HandleError(env, jsContext->error);
882    }
883
884    tracer.Finish();
885    if (context->work != nullptr) {
886        MediaLibraryNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef,
887                                                   context->work, *jsContext);
888    }
889
890    delete context;
891}
892napi_value AlbumNapi::JSGetAlbumFileAssets(napi_env env, napi_callback_info info)
893{
894    napi_status status;
895    napi_value result = nullptr;
896    size_t argc = ARGS_TWO;
897    napi_value argv[ARGS_TWO] = {0};
898    napi_value thisVar = nullptr;
899
900    MediaLibraryTracer tracer;
901    tracer.Start("JSGetAlbumFileAssets");
902
903    GET_JS_ARGS(env, info, argc, argv, thisVar);
904    NAPI_ASSERT(env, ((argc == ARGS_ZERO) || (argc == ARGS_ONE) || (argc == ARGS_TWO)),
905                "requires 2 parameter maximum");
906    napi_get_undefined(env, &result);
907
908    std::unique_ptr<AlbumNapiAsyncContext> asyncContext = std::make_unique<AlbumNapiAsyncContext>();
909    asyncContext->resultNapiType = ResultNapiType::TYPE_MEDIALIBRARY;
910    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
911    if (status == napi_ok && asyncContext->objectInfo != nullptr) {
912        result = ConvertJSArgsToNative(env, argc, argv, *asyncContext);
913        CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "Failed to obtain arguments");
914        asyncContext->objectPtr = asyncContext->objectInfo->albumAssetPtr;
915        CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext->objectPtr, result, "AlbumAsset is nullptr");
916
917        result = MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSGetAlbumFileAssets",
918            GetFileAssetsNative, JSGetFileAssetsCompleteCallback);
919    }
920
921    return result;
922}
923napi_value AlbumNapi::JSCommitModify(napi_env env, napi_callback_info info)
924{
925    napi_status status;
926    napi_value result = nullptr;
927    size_t argc = ARGS_ONE;
928    napi_value argv[ARGS_ONE] = {0};
929    napi_value thisVar = nullptr;
930
931    MediaLibraryTracer tracer;
932    tracer.Start("JSCommitModify");
933
934    GET_JS_ARGS(env, info, argc, argv, thisVar);
935    NAPI_ASSERT(env, (argc == ARGS_ZERO || argc == ARGS_ONE), "requires 1 parameter maximum");
936    napi_get_undefined(env, &result);
937
938    std::unique_ptr<AlbumNapiAsyncContext> asyncContext = std::make_unique<AlbumNapiAsyncContext>();
939    CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, result, "asyncContext context is null");
940    asyncContext->resultNapiType = ResultNapiType::TYPE_MEDIALIBRARY;
941    status = napi_unwrap(env, thisVar, reinterpret_cast<void **>(&asyncContext->objectInfo));
942    if (status == napi_ok && asyncContext->objectInfo != nullptr) {
943        result = ConvertCommitJSArgsToNative(env, argc, argv, *asyncContext);
944        CHECK_NULL_PTR_RETURN_UNDEFINED(env, result, result, "JSCommitModify fail ");
945        asyncContext->objectPtr = asyncContext->objectInfo->albumAssetPtr;
946        CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext->objectPtr, result, "AlbumAsset is nullptr");
947
948        result = MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "JSCommitModify", CommitModifyNative,
949            JSCommitModifyCompleteCallback);
950    }
951
952    return result;
953}
954
955napi_value AlbumNapi::UserFileMgrGetAssets(napi_env env, napi_callback_info info)
956{
957    napi_value ret = nullptr;
958    unique_ptr<AlbumNapiAsyncContext> asyncContext = make_unique<AlbumNapiAsyncContext>();
959    CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, ret, "asyncContext context is null");
960
961    asyncContext->mediaTypes.push_back(MEDIA_TYPE_IMAGE);
962    asyncContext->mediaTypes.push_back(MEDIA_TYPE_VIDEO);
963    CHECK_ARGS(env, MediaLibraryNapiUtils::ParseAssetFetchOptCallback(env, info, asyncContext),
964        JS_ERR_PARAMETER_INVALID);
965    asyncContext->resultNapiType = ResultNapiType::TYPE_USERFILE_MGR;
966    asyncContext->objectPtr = asyncContext->objectInfo->albumAssetPtr;
967    CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext->objectPtr, ret, "AlbumAsset is nullptr");
968
969    return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "UserFileMgrGetAssets", GetFileAssetsNative,
970        JSGetFileAssetsCompleteCallback);
971}
972
973napi_value AlbumNapi::UserFileMgrCommitModify(napi_env env, napi_callback_info info)
974{
975    MediaLibraryTracer tracer;
976    tracer.Start("UserFileMgrCommitModify");
977
978    napi_value ret = nullptr;
979    unique_ptr<AlbumNapiAsyncContext> asyncContext = make_unique<AlbumNapiAsyncContext>();
980    CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, ret, "asyncContext context is null");
981    asyncContext->resultNapiType = ResultNapiType::TYPE_USERFILE_MGR;
982    CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsOnlyCallBack(env, info, asyncContext), JS_ERR_PARAMETER_INVALID);
983    asyncContext->objectPtr = asyncContext->objectInfo->albumAssetPtr;
984    CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext->objectPtr, ret, "AlbumAsset is nullptr");
985
986    return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "UserFileMgrCommitModify", CommitModifyNative,
987        JSCommitModifyCompleteCallback);
988}
989
990napi_value AlbumNapi::PhotoAccessHelperGetAssets(napi_env env, napi_callback_info info)
991{
992    napi_value ret = nullptr;
993    unique_ptr<AlbumNapiAsyncContext> asyncContext = make_unique<AlbumNapiAsyncContext>();
994    CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, ret, "asyncContext context is null");
995
996    asyncContext->mediaTypes.push_back(MEDIA_TYPE_IMAGE);
997    asyncContext->mediaTypes.push_back(MEDIA_TYPE_VIDEO);
998    CHECK_ARGS(env, MediaLibraryNapiUtils::ParseAssetFetchOptCallback(env, info, asyncContext),
999        JS_ERR_PARAMETER_INVALID);
1000    asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
1001    asyncContext->objectPtr = asyncContext->objectInfo->albumAssetPtr;
1002    CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext->objectPtr, ret, "AlbumAsset is nullptr");
1003
1004    return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "UserFileMgrGetAssets", GetFileAssetsNative,
1005        JSGetFileAssetsCompleteCallback);
1006}
1007
1008napi_value AlbumNapi::PhotoAccessHelperCommitModify(napi_env env, napi_callback_info info)
1009{
1010    MediaLibraryTracer tracer;
1011    tracer.Start("UserFileMgrCommitModify");
1012
1013    napi_value ret = nullptr;
1014    unique_ptr<AlbumNapiAsyncContext> asyncContext = make_unique<AlbumNapiAsyncContext>();
1015    CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext, ret, "asyncContext context is null");
1016    asyncContext->resultNapiType = ResultNapiType::TYPE_PHOTOACCESS_HELPER;
1017    CHECK_ARGS(env, MediaLibraryNapiUtils::ParseArgsOnlyCallBack(env, info, asyncContext), JS_ERR_PARAMETER_INVALID);
1018    asyncContext->objectPtr = asyncContext->objectInfo->albumAssetPtr;
1019    CHECK_NULL_PTR_RETURN_UNDEFINED(env, asyncContext->objectPtr, ret, "AlbumAsset is nullptr");
1020
1021    return MediaLibraryNapiUtils::NapiCreateAsyncWork(env, asyncContext, "UserFileMgrCommitModify", CommitModifyNative,
1022        JSCommitModifyCompleteCallback);
1023}
1024} // namespace Media
1025} // namespace OHOS
1026