diff --git a/bundle.json b/bundle.json index e9e6c6357bf752e588e69c8755a7e8e88cc4d9ff..450283ee515b878c4801d9dd09996e8da56de84c 100644 --- a/bundle.json +++ b/bundle.json @@ -25,6 +25,7 @@ "components": [ "ability_base", "ability_runtime", + "ace_engine", "app_file_service", "ipc", "init", diff --git a/interfaces/kits/picker/BUILD.gn b/interfaces/kits/picker/BUILD.gn index fa0434b01d784a9016dc81d44083902caa41aa73..664cb8ae269e0e68dabe540f310b93718de0e079 100644 --- a/interfaces/kits/picker/BUILD.gn +++ b/interfaces/kits/picker/BUILD.gn @@ -12,6 +12,7 @@ # limitations under the License. import("//build/ohos.gni") +import("//build/ohos/ace/ace.gni") import("../../../../../../arkcompiler/ets_frontend/es2panda/es2abc_config.gni") import("../../../filemanagement_aafwk.gni") @@ -54,7 +55,15 @@ gen_js_obj("picker_abc") { ohos_shared_library("picker") { branch_protector_ret = "pac_ret" ldflags = [ "-Wl" ] - sources = [ "native_module_ohos_picker.cpp" ] + + include_dirs = [ "include" ] + + sources = [ + "native_module_ohos_picker.cpp", + "src/modal_ui_callback.cpp", + "src/picker_n_exporter.cpp", + "src/picker_napi_utils.cpp", + ] deps = [ ":picker_abc", @@ -62,7 +71,22 @@ ohos_shared_library("picker") { ] external_deps = [ + "ability_base:want", + "ability_base:zuri", + "ability_runtime:ability_context_native", + "ability_runtime:ability_manager", + "ability_runtime:abilitykit_native", "ability_runtime:abilitykit_native", + "ability_runtime:app_context", + "ability_runtime:dataobs_manager", + "ability_runtime:napi_base_context", + "ability_runtime:runtime", + "ability_runtime:service_extension", + "ability_runtime:ui_extension", + "ace_engine:ace_uicontent", + "file_api:filemgmt_libhilog", + "file_api:filemgmt_libn", + "hilog:libhilog", "napi:ace_napi", ] diff --git a/interfaces/kits/picker/include/modal_ui_callback.h b/interfaces/kits/picker/include/modal_ui_callback.h new file mode 100644 index 0000000000000000000000000000000000000000..eef34d8ccb14bb73b701865b01f3e6f909445fb1 --- /dev/null +++ b/interfaces/kits/picker/include/modal_ui_callback.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MODAL_UI_CALLBACK_H +#define MODAL_UI_CALLBACK_H + +#include "ability_context.h" +#include "want.h" +#include "want_params.h" +#include +#include "picker_n_exporter.h" +#include "ui_content.h" + +namespace OHOS { +namespace Picker { + +class ModalUICallback { +public: + explicit ModalUICallback(Ace::UIContent* uiContent, PickerCallBack* pickerCallBack); + void OnRelease(int32_t releaseCode); + void OnResultForModal(int32_t resultCode, const OHOS::AAFwk::Want& result); + void OnReceive(const OHOS::AAFwk::WantParams& request); + void OnError(int32_t code, const std::string& name, const std::string& message); + void OnDestroy(); + void SetSessionId(int32_t sessionId); + +private: + int32_t sessionId_ = 0; + Ace::UIContent* uiContent; + PickerCallBack* pickerCallBack_; +}; +} // namespace Picker +} // namespace OHOS + +#endif \ No newline at end of file diff --git a/interfaces/kits/picker/include/picker_n_exporter.h b/interfaces/kits/picker/include/picker_n_exporter.h new file mode 100644 index 0000000000000000000000000000000000000000..09f58890a6fcdf529772da2818a4df9d3a1a18d5 --- /dev/null +++ b/interfaces/kits/picker/include/picker_n_exporter.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef INTERFACES_KITS_NATIVE_PICKER_N_EXPOTER_H +#define INTERFACES_KITS_NATIVE_PICKER_N_EXPOTER_H + +#include +#include +#include "data_ability_helper.h" +#include "data_ability_observer_stub.h" +#include "data_ability_predicates.h" +#include "filemgmt_libn.h" +#include "picker_napi_utils.h" +#include "napi_base_context.h" +#include "napi_common_want.h" + + +namespace OHOS { +namespace Picker { +using namespace FileManagement::LibN; +using namespace std; + +struct NameListArg { + struct dirent** namelist = { nullptr }; + int direntNum = 0; +}; + +struct PickerCallBack { + bool ready = false; + int32_t resultCode; + string uri; +}; + +struct PickerAsyncContext { + napi_async_work work; + napi_deferred deferred; + napi_ref callbackRef; + size_t argc; + napi_value argv[NAPI_ARGC_MAX]; + std::shared_ptr pickerCallBack; +}; + +class PickerNExporter final : public FileManagement::LibN::NExporter { +public: + inline static const std::string className_ = "Picker"; + static napi_value StartModalPicker(napi_env env, napi_callback_info info); + bool Export() override; + std::string GetClassName() override; + PickerNExporter(napi_env env, napi_value exports); + ~PickerNExporter() override; +}; +} // namespace FileAccessFwk +} // namespace OHOS +#endif // INTERFACES_KITS_NATIVE_PICKER_N_EXPOTER_H diff --git a/interfaces/kits/picker/include/picker_napi_utils.h b/interfaces/kits/picker/include/picker_napi_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..474181568f6c2d251a3abd1ad5d2161d4557fa75 --- /dev/null +++ b/interfaces/kits/picker/include/picker_napi_utils.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INTERFACES_KITS_JS_PICKER_INCLUDE_PICKER_NAPI_UTILS_H +#define INTERFACES_KITS_JS_PICKER_INCLUDE_PICKER_NAPI_UTILS_H + +#include +#include +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "hilog_wrapper.h" + +namespace OHOS { +namespace Picker { +/* Constants for array index */ +const int32_t PARAM0 = 0; +const int32_t PARAM1 = 1; +const int32_t PARAM2 = 2; +const int32_t PARAM3 = 3; +const int32_t PARAM4 = 4; + +/* Constants for array size */ +const int32_t ARGS_ZERO = 0; +const int32_t ARGS_ONE = 1; +const int32_t ARGS_TWO = 2; +const int32_t ARGS_THREE = 3; +const int32_t ARGS_FOUR = 4; +const int32_t ARGS_FIVE = 5; +const int32_t ARG_BUF_SIZE = 384; // 256 for display name and 128 for relative path +constexpr uint32_t NAPI_INIT_REF_COUNT = 1; + +constexpr size_t NAPI_ARGC_MAX = 5; + +// Error codes +const int32_t ERR_DEFAULT = 0; +const int32_t ERR_MEM_ALLOCATION = 2; +const int32_t ERR_INVALID_OUTPUT = 3; + +struct JSAsyncContextOutput { + napi_value error; + napi_value data; + bool status; +}; + +struct NapiClassInfo { + std::string name; + napi_ref *ref; + napi_value (*constructor)(napi_env, napi_callback_info); + std::vector props; +}; + +/* Util class used by napi asynchronous methods for making call to js callback function */ +class PickerNapiUtils { +public: + static void CreateNapiErrorObject(napi_env env, napi_value &errorObj, const int32_t errCode, + const std::string errMsg); + static void InvokeJSAsyncMethod(napi_env env, napi_deferred deferred, napi_ref callbackRef, napi_async_work work, + const JSAsyncContextOutput &asyncContext); + template + static napi_value NapiCreateAsyncWork(napi_env env, std::unique_ptr &asyncContext, + const std::string &resourceName, void (*execute)(napi_env, void *), + void (*complete)(napi_env, napi_status, void *)); +}; + +} // namespace Picker +} // namespace OHOS +#endif // INTERFACES_KITS_JS_PICKER_INCLUDE_PICKER_NAPI_UTILS_H diff --git a/interfaces/kits/picker/native_module_ohos_picker.cpp b/interfaces/kits/picker/native_module_ohos_picker.cpp index ba51922bbbba812fd6ade489d746efc5e0599185..f0041fd9a6058de6795926bc70a19a8d7381617b 100644 --- a/interfaces/kits/picker/native_module_ohos_picker.cpp +++ b/interfaces/kits/picker/native_module_ohos_picker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 Huawei Device Co., Ltd. + * Copyright (C) 2023-2024 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -15,6 +15,7 @@ #include "napi/native_api.h" #include "napi/native_node_api.h" +#include "picker_n_exporter.h" extern const char _binary_picker_js_start[]; extern const char _binary_picker_js_end[]; @@ -23,13 +24,27 @@ extern const char _binary_picker_abc_end[]; namespace OHOS { namespace FileAccessFwk { -/* - * Function registering all props and functions of ohos.medialibrary module - */ +using namespace Picker; +using namespace FileManagement::LibN; + +EXTERN_C_START static napi_value Export(napi_env env, napi_value exports) { + std::vector> products; + products.emplace_back(make_unique(env, exports)); + + for (auto &&product : products) { + std::string nExporterName = product->GetClassName(); + if (!product->Export()) { + HILOG_ERROR("INNER BUG. Failed to export class %{public}s for module trash", nExporterName.c_str()); + return nullptr; + } else { + HILOG_INFO("Class %{public}s for module trash has been exported", nExporterName.c_str()); + } + } return exports; } +EXTERN_C_END extern "C" __attribute__((visibility("default"))) void NAPI_file_picker_GetJSCode(const char** buf, int* bufLen) diff --git a/interfaces/kits/picker/picker.js b/interfaces/kits/picker/picker.js index d5b451d586a72219b81f81455a2f1ccc01f03fbf..85681b7f2f081638581067eb14eb949ece004fb4 100644 --- a/interfaces/kits/picker/picker.js +++ b/interfaces/kits/picker/picker.js @@ -12,6 +12,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +const pickerHelper = requireInternal('file.picker'); + +let gContext = undefined; const PhotoViewMIMETypes = { IMAGE_TYPE: 'image/*', @@ -26,6 +29,19 @@ const DocumentSelectMode = { MIXED: 2, }; +const DocumentPickerMode = { + DEFAULT: 0, + DOWNLOAD: 1, +}; + +const ExtTypes = { + DOWNLOAD_TYPE: 'download', +}; + +const PickerDetailType = { + FILE_MGR_AUTH: 'downloadAuth', +}; + const ErrCode = { INVALID_ARGS: 13900020, RESULT_ERROR: 13900042, @@ -51,6 +67,7 @@ const ACTION = { SELECT_ACTION_MODAL: 'ohos.want.action.OPEN_FILE_SERVICE', SAVE_ACTION: 'ohos.want.action.CREATE_FILE', SAVE_ACTION_MODAL: 'ohos.want.action.CREATE_FILE_SERVICE', + SAVE_ACTION_DOWNLOAD: 'ohos.want.action.DOWNLOAD', } const CREATE_FILE_NAME_LENGTH_LIMIT = 256; @@ -327,6 +344,7 @@ function parseDocumentPickerSaveOption(args, action) { action: action, parameters: { startMode: 'save', + pickerMode: DocumentPickerMode.DEFAULT, } }; @@ -343,12 +361,32 @@ function parseDocumentPickerSaveOption(args, action) { if ((option.fileSuffixChoices !== undefined) && option.fileSuffixChoices.length > 0) { config.parameters.key_file_suffix_choices = option.fileSuffixChoices; } + if (option.pickerMode === DocumentPickerMode.DOWNLOAD) { + config.parameters.pickerMode = option.pickerMode; + config.action = ACTION.SAVE_ACTION_DOWNLOAD; + config.parameters.extType = ExtTypes.DOWNLOAD_TYPE; + config.parameters.pickerType = PickerDetailType.FILE_MGR_AUTH; + } } console.log('[picker] document save config: ' + JSON.stringify(config)); return config; } +function getModalPickerResult(args) { + let saveResult = { + error: undefined, + data: undefined + } + if (args) { + var dataArr = []; + dataArr.push(args.uri); + saveResult.data = dataArr; + } + console.log('modal picker: download saveResult: ' + JSON.stringify(saveResult)); + return saveResult; +} + function getDocumentPickerSaveResult(args) { let saveResult = { error: undefined, @@ -379,6 +417,37 @@ function getDocumentPickerSaveResult(args) { return saveResult; } +function startModalPicker(context, config) { + if (context === undefined) { + console.log('modal picker: startModalPicker context undefined.'); + throw Error('modal picker: startModalPicker context undefined.'); + } + if (config === undefined) { + console.log('modal picker: startModalPicker config undefined.'); + throw Error('modal picker: startModalPicker config undefined.'); + } + gContext = context; + if (pickerHelper === undefined) { + console.log('modal picker: pickerHelper undefined.') + } + let helper = pickerHelper.startModalPicker(gContext, config); + if (helper === undefined) { + console.log('modal picker: startModalPicker helper undefined.'); + } + return helper; +} + +async function modalPicker(args, context, config) { + try { + console.log('modal picker: config: ' + JSON.stringify(config)); + let modalSaveResult = await startModalPicker(context, config); + const saveResult = getModalPickerResult(modalSaveResult); + return saveResult; + } catch (resultError) { + console.error('modal picker: Result error: ' + resultError); + } +} + async function documentPickerSave(...args) { let checkDocumentSaveArgsResult = checkArguments(args); if (checkDocumentSaveArgsResult !== undefined) { @@ -389,6 +458,7 @@ async function documentPickerSave(...args) { let documentSaveContext = undefined; let documentSaveConfig = undefined; let documentSaveResult = undefined; + let saveResult = undefined; try { documentSaveContext = getContext(this); @@ -396,25 +466,32 @@ async function documentPickerSave(...args) { console.error('[picker] getContext error: ' + getContextError); throw getErr(ErrCode.CONTEXT_NO_EXIST); } - try { - if (documentSaveContext === undefined) { - throw getErr(ErrCode.CONTEXT_NO_EXIST); - } - documentSaveConfig = parseDocumentPickerSaveOption(args, ACTION.SAVE_ACTION_MODAL); - documentSaveResult = await documentSaveContext.requestDialogService(documentSaveConfig); - } catch (paramError) { - console.error('[picker] paramError: ' + JSON.stringify(paramError)); + + documentSaveConfig = parseDocumentPickerSaveOption(args, ACTION.SAVE_ACTION_MODAL); + if (documentSaveConfig.parameters.pickerMode === DocumentPickerMode.DOWNLOAD) { + console.log('modal picker: will start modal picker process. (DOWNLOAD)'); + saveResult = await modalPicker(args, documentSaveContext, documentSaveConfig); + } else { try { - documentSaveConfig = parseDocumentPickerSaveOption(args, ACTION.SAVE_ACTION); - documentSaveResult = await documentSaveContext.startAbilityForResult(documentSaveConfig, {windowMode: 0}); - } catch (error) { - console.error('[picker] document save error: ' + error); - return undefined; + if (documentSaveContext === undefined) { + throw getErr(ErrCode.CONTEXT_NO_EXIST); + } + documentSaveConfig = parseDocumentPickerSaveOption(args, ACTION.SAVE_ACTION_MODAL); + documentSaveResult = await documentSaveContext.requestDialogService(documentSaveConfig); + } catch (paramError) { + console.error('[picker] paramError: ' + JSON.stringify(paramError)); + try { + documentSaveConfig = parseDocumentPickerSaveOption(args, ACTION.SAVE_ACTION); + documentSaveResult = await documentSaveContext.startAbilityForResult(documentSaveConfig, {windowMode: 0}); + } catch (error) { + console.error('[picker] document save error: ' + error); + return undefined; + } } + console.log('[picker] document save result: ' + JSON.stringify(documentSaveResult)); + saveResult = getDocumentPickerSaveResult(documentSaveResult); } - console.log('[picker] document save result: ' + JSON.stringify(documentSaveResult)); try { - const saveResult = getDocumentPickerSaveResult(documentSaveResult); if (args.length === ARGS_TWO && typeof args[ARGS_ONE] === 'function') { return args[ARGS_ONE](saveResult.error, saveResult.data); } else if (args.length === ARGS_ONE && typeof args[ARGS_ZERO] === 'function') { @@ -501,6 +578,7 @@ function DocumentSaveOptions() { this.newFileNames = undefined; this.defaultFilePathUri = undefined; this.fileSuffixChoices = undefined; + this.pickerMode = DocumentPickerMode.DEFAULT; } function AudioSelectOptions() {} @@ -525,11 +603,15 @@ function AudioViewPicker() { } export default { + startModalPicker, + ExtTypes : ExtTypes, + PickerDetailType: PickerDetailType, PhotoViewMIMETypes : PhotoViewMIMETypes, PhotoSelectOptions : PhotoSelectOptions, PhotoSelectResult : PhotoSelectResult, PhotoSaveOptions : PhotoSaveOptions, DocumentSelectMode : DocumentSelectMode, + DocumentPickerMode : DocumentPickerMode, DocumentSelectOptions : DocumentSelectOptions, DocumentSaveOptions : DocumentSaveOptions, AudioSelectOptions : AudioSelectOptions, diff --git a/interfaces/kits/picker/src/modal_ui_callback.cpp b/interfaces/kits/picker/src/modal_ui_callback.cpp new file mode 100644 index 0000000000000000000000000000000000000000..97140deccb802c1cdb8707e0d55612c8a786675b --- /dev/null +++ b/interfaces/kits/picker/src/modal_ui_callback.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "modal_ui_callback.h" +#include "picker_n_exporter.h" + +namespace OHOS { +namespace Picker { +using namespace OHOS::Ace; +using namespace std; +ModalUICallback::ModalUICallback(Ace::UIContent* uiContent, PickerCallBack* pickerCallBack) +{ + this->uiContent = uiContent; + this->pickerCallBack_ = pickerCallBack; +} + +void ModalUICallback::SetSessionId(int32_t sessionId) +{ + this->sessionId_=sessionId; +} + +void ModalUICallback::OnRelease(int32_t releaseCode) +{ + HILOG_INFO("modal picker: OnRelease enter. release code is %{public}d", releaseCode); + this->uiContent->CloseModalUIExtension(this->sessionId_); + pickerCallBack_->ready = true; +} + +void ModalUICallback::OnError(int32_t code, const std::string& name, const std::string& message) +{ + HILOG_ERROR("modal picker: OnError enter. errorCode=%{public}d, name=%{public}s, message=%{public}s", + code, name.c_str(), message.c_str()); +} + +void ModalUICallback::OnResultForModal(int32_t resultCode, const OHOS::AAFwk::Want &result) +{ + HILOG_INFO("modal picker: OnResultForModal enter. resultCode is %{public}d", resultCode); + if (result.GetParams().HasParam("downloadNewUri")) { + HILOG_INFO("modal picker: downloadNewUri exit."); + pickerCallBack_->uri = result.GetStringParam("downloadNewUri"); + } + pickerCallBack_->resultCode = resultCode; + pickerCallBack_->ready = true; +} + +void ModalUICallback::OnReceive(const OHOS::AAFwk::WantParams &request) +{ + HILOG_INFO("modal picker: OnReceive enter."); +} + +void ModalUICallback::OnDestroy() +{ + HILOG_INFO("modal picker: OnDestroy enter."); +} +} // namespace Picker +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/kits/picker/src/picker_n_exporter.cpp b/interfaces/kits/picker/src/picker_n_exporter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..740f4fc9d904dbc107df728a72fb20484a1ccc40 --- /dev/null +++ b/interfaces/kits/picker/src/picker_n_exporter.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define ABILITY_WANT_PARAMS_UIEXTENSIONTARGETTYPE "ability.want.params.uiExtensionTargetType" +#include "picker_n_exporter.h" +#include "modal_ui_callback.h" +#include "modal_ui_extension_config.h" +#include "ability_context.h" +#include "context.h" +#include "ability.h" +#ifdef HAS_ACE_ENGINE_PART +#include "ui_content.h" +#endif +#include "ui_extension_context.h" +#include "want.h" + +namespace OHOS { +namespace Picker { +using namespace std; +using namespace FileManagement; +using namespace FileManagement::LibN; +using namespace AppExecFwk; +#define WAIT_TIME_MS 100 + +bool PickerNExporter::Export() +{ + return exports_.AddProp({ + NVal::DeclareNapiFunction("startModalPicker", StartModalPicker) + }); +} + +string PickerNExporter::GetClassName() +{ + return PickerNExporter::className_; +} + +static void StartModalPickerExecute(napi_env env, void *data) +{ + HILOG_INFO("modal picker: StartModalPickerExecute begin"); + auto *context = static_cast(data); + while (!context->pickerCallBack->ready) { + std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_MS)); + } + HILOG_INFO("modal picker: StartModalPickerExecute is ready."); +} + +static void StartModalPickerAsyncCallbackComplete(napi_env env, napi_status status, void *data) +{ + HILOG_INFO("modal picker: StartModalPickerAsyncCallbackComplete begin."); + auto *context = static_cast(data); + if (context == nullptr) { + HILOG_ERROR("Async context is null"); + return; + } + + auto jsContext = make_unique(); + jsContext->status = false; + + status = napi_get_undefined(env, &jsContext->data); + if (status != napi_ok) { + HILOG_ERROR("modal picker: napi_get_undefined jsContext->data failed"); + } + status = napi_get_undefined(env, &jsContext->error); + if (status != napi_ok) { + HILOG_ERROR("modal picker: napi_get_undefined jsContext->error failed"); + } + const string uri = context->pickerCallBack->uri; + HILOG_DEBUG("modal picker: uri is %{public}s.", uri.c_str()); + napi_value jsUri = nullptr; + status = napi_create_string_utf8(env, uri.c_str(), NAPI_AUTO_LENGTH, &jsUri); + + if (jsUri == nullptr) { + HILOG_ERROR("jsUri is nullptr."); + } + napi_value result = nullptr; + napi_create_object(env, &result); + status = napi_set_named_property(env, result, "uri", jsUri); + if (status != napi_ok) { + HILOG_ERROR("modal picker: napi_set_named_property uri failed"); + } + if (result != nullptr) { + jsContext->data = result; + jsContext->status = true; + } else { + PickerNapiUtils::CreateNapiErrorObject(env, jsContext->error, ERR_MEM_ALLOCATION, + "failed to create js object"); + } + if (context->work != nullptr) { + PickerNapiUtils::InvokeJSAsyncMethod(env, context->deferred, context->callbackRef, + context->work, *jsContext); + } + delete context; +} + +Ace::UIContent *GetUIContent(napi_env env, napi_callback_info info, + unique_ptr &AsyncContext) +{ + bool isStageMode = false; + napi_status status = AbilityRuntime::IsStageContext(env, AsyncContext->argv[ARGS_ZERO], isStageMode); + if (status != napi_ok || !isStageMode) { + HILOG_ERROR("modal picker: is not StageMode context"); + return nullptr; + } + auto context = AbilityRuntime::GetStageModeContext(env, AsyncContext->argv[ARGS_ZERO]); + if (context == nullptr) { + HILOG_ERROR("modal picker: Failed to get native stage context instance"); + return nullptr; + } + auto abilityContext = AbilityRuntime::Context::ConvertTo(context); + if (abilityContext == nullptr) { + auto uiExtensionContext = AbilityRuntime::Context::ConvertTo(context); + if (uiExtensionContext == nullptr) { + HILOG_ERROR("modal picker: Fail to convert to abilityContext or uiExtensionContext"); + return nullptr; + } + return uiExtensionContext->GetUIContent(); + } + return abilityContext->GetUIContent(); +} + +static napi_value StartPickerExtension(napi_env env, napi_callback_info info, + unique_ptr &AsyncContext) +{ + HILOG_INFO("modal picker: StartPickerExtension begin."); + Ace::UIContent *uiContent = GetUIContent(env, info, AsyncContext); + if (uiContent == nullptr) { + HILOG_ERROR("modal picker: get uiContent failed"); + return nullptr; + } + AAFwk::Want request; + AppExecFwk::UnwrapWant(env, AsyncContext->argv[ARGS_ONE], request); + + std::string targetType = request.GetStringParam("extType"); + std::string pickerType; + if (request.GetParams().HasParam("pickerType")) { + pickerType = request.GetStringParam("pickerType"); + } + request.SetParam(ABILITY_WANT_PARAMS_UIEXTENSIONTARGETTYPE, targetType); + AsyncContext->pickerCallBack = make_shared(); + auto callback = std::make_shared(uiContent, AsyncContext->pickerCallBack.get()); + Ace::ModalUIExtensionCallbacks extensionCallback = { + .onRelease = std::bind(&ModalUICallback::OnRelease, callback, std::placeholders::_1), + .onResult = std::bind(&ModalUICallback::OnResultForModal, callback, std::placeholders::_1, + std::placeholders::_2), + .onReceive = std::bind(&ModalUICallback::OnReceive, callback, std::placeholders::_1), + .onError = std::bind(&ModalUICallback::OnError, callback, std::placeholders::_1, std::placeholders::_2, + std::placeholders::_3), + .onDestroy = std::bind(&ModalUICallback::OnDestroy, callback), + }; + Ace::ModalUIExtensionConfig config; + HILOG_INFO("modal picker: will CreateModalUIExtension by extType: %{public}s, pickerType: %{public}s", + targetType.c_str(), pickerType.c_str()); + int sessionId = uiContent->CreateModalUIExtension(request, extensionCallback, config); + if (sessionId == 0) { + HILOG_ERROR("modal picker: create modalUIExtension failed"); + return nullptr; + } + callback->SetSessionId(sessionId); + napi_value result = nullptr; + napi_get_boolean(env, true, &result); + return result; +} + +template +static napi_status AsyncContextSetStaticObjectInfo(napi_env env, napi_callback_info info, + AsyncContext &asyncContext, const size_t minArgs, const size_t maxArgs) +{ + HILOG_INFO("modal picker: AsyncContextSetStaticObjectInfo begin."); + napi_value thisVar = nullptr; + asyncContext->argc = maxArgs; + napi_status ret = napi_get_cb_info(env, info, &asyncContext->argc, &(asyncContext->argv[ARGS_ZERO]), + &thisVar, nullptr); + if (ret != napi_ok) { + HILOG_ERROR("modal picker: Failed to get cb info"); + return ret; + } + if (!((asyncContext->argc >= minArgs) && (asyncContext->argc <= maxArgs))) { + HILOG_ERROR("modal picker: Number of args is invalid"); + return napi_invalid_arg; + } + if (minArgs > 0) { + if (asyncContext->argv[ARGS_ZERO] == nullptr) { + HILOG_ERROR("modal picker: Argument list is empty"); + return napi_invalid_arg; + } + } + return napi_ok; +} + +static napi_value ParseArgsStartModalPicker(napi_env env, napi_callback_info info, + unique_ptr &context) +{ + HILOG_INFO("modal picker: ParseArgsStartModalPicker begin."); + constexpr size_t minArgs = ARGS_TWO; + constexpr size_t maxArgs = ARGS_THREE; + napi_status status = AsyncContextSetStaticObjectInfo(env, info, context, minArgs, maxArgs); + if (status != napi_ok) { + HILOG_ERROR("modal picker: AsyncContextSetStaticObjectInfo faild"); + } + napi_value result = nullptr; + napi_value ret = StartPickerExtension(env, info, context); + if (ret == nullptr) { + return nullptr; + } + napi_get_boolean(env, true, &result); + return result; +} + +napi_value PickerNExporter::StartModalPicker(napi_env env, napi_callback_info info) +{ + HILOG_INFO("modal picker: StartModalPicker begin."); + unique_ptr asyncContext = make_unique(); + napi_value ret = ParseArgsStartModalPicker(env, info, asyncContext); + if (ret == nullptr) { + return nullptr; + } + return PickerNapiUtils::NapiCreateAsyncWork(env, asyncContext, "StrartModalPicker", + StartModalPickerExecute, StartModalPickerAsyncCallbackComplete); +} + +PickerNExporter::PickerNExporter(napi_env env, napi_value exports) : NExporter(env, exports) +{ + HILOG_INFO("PickerNExporter is called."); +} + +PickerNExporter::~PickerNExporter() {} +} // namespace Picker +} // namespace OHOS diff --git a/interfaces/kits/picker/src/picker_napi_utils.cpp b/interfaces/kits/picker/src/picker_napi_utils.cpp new file mode 100644 index 0000000000000000000000000000000000000000..32d251d966cef0b910111d11b5545f2b4ff7c0a8 --- /dev/null +++ b/interfaces/kits/picker/src/picker_napi_utils.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define MLOG_TAG "PickerNapiUtils" + +#include "picker_napi_utils.h" +#include "ipc_skeleton.h" +#include "picker_n_exporter.h" + +using namespace std; + +namespace OHOS { +namespace Picker { +static const string EMPTY_STRING = ""; + +void PickerNapiUtils::CreateNapiErrorObject(napi_env env, napi_value &errorObj, const int32_t errCode, + const string errMsg) +{ + napi_status statusError; + napi_value napiErrorCode = nullptr; + napi_value napiErrorMsg = nullptr; + statusError = napi_create_string_utf8(env, to_string(errCode).c_str(), NAPI_AUTO_LENGTH, &napiErrorCode); + if (statusError == napi_ok) { + statusError = napi_create_string_utf8(env, errMsg.c_str(), NAPI_AUTO_LENGTH, &napiErrorMsg); + if (statusError == napi_ok) { + statusError = napi_create_error(env, napiErrorCode, napiErrorMsg, &errorObj); + if (statusError == napi_ok) { + HILOG_DEBUG("napi_create_error success"); + } + } + } +} + +void PickerNapiUtils::InvokeJSAsyncMethod(napi_env env, napi_deferred deferred, napi_ref callbackRef, + napi_async_work work, const JSAsyncContextOutput &asyncContext) +{ + HILOG_INFO("modal picker: InvokeJSAsyncMethod begin."); + if (asyncContext.status) { + napi_resolve_deferred(env, deferred, asyncContext.data); + } else { + napi_reject_deferred(env, deferred, asyncContext.error); + } + napi_delete_async_work(env, work); +} + +template +napi_value PickerNapiUtils::NapiCreateAsyncWork(napi_env env, unique_ptr &asyncContext, + const string &resourceName, void (*execute)(napi_env, void *), void (*complete)(napi_env, napi_status, void *)) +{ + napi_value result = nullptr; + napi_value resource = nullptr; + napi_create_promise(env, &(asyncContext->deferred), &(result)); + napi_create_string_utf8(env, resourceName.c_str(), NAPI_AUTO_LENGTH, &(resource)); + NAPI_CALL(env, napi_create_async_work(env, nullptr, resource, execute, complete, + static_cast(asyncContext.get()), &asyncContext->work)); + NAPI_CALL(env, napi_queue_async_work(env, asyncContext->work)); + asyncContext.release(); + + return result; +} + +template napi_value PickerNapiUtils::NapiCreateAsyncWork(napi_env env, + unique_ptr &asyncContext, const string &resourceName, + void (*execute)(napi_env, void *), void (*complete)(napi_env, napi_status, void *)); +} // namespace Picker +} // namespace OHOS