From 1c5ebbedcdbc237a5317fb350004d65ed71b6d18 Mon Sep 17 00:00:00 2001 From: humeng Date: Sat, 19 Oct 2024 15:03:35 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E9=AB=98=E9=98=B6=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: humeng --- BUILD.gn | 9 +- atomicservicenavigation/interfaces/BUILD.gn | 59 + .../interfaces/atomicservicenavigation.cpp | 51 + .../interfaces/atomicservicenavigation.js | 181 +++ .../source/atomicservicenavigation.ets | 68 + atomicserviceweb/interfaces/BUILD.gn | 64 + .../interfaces/api_policy_adapter.cpp | 47 + .../interfaces/api_policy_adapter.h | 39 + .../interfaces/atomicserviceweb.cpp | 114 ++ .../interfaces/atomicserviceweb.js | 1348 ++++++++++++++++ atomicserviceweb/source/atomicserviceweb.ets | 1362 +++++++++++++++++ bundle.json | 9 +- .../interfaces/BUILD.gn | 61 + .../innerfullscreenlaunchcomponent.cpp | 52 + .../innerfullscreenlaunchcomponent.js | 214 +++ .../source/innerfullscreenlaunchcomponent.ets | 171 +++ interstitialdialogaction/interfaces/BUILD.gn | 59 + .../interfaces/interstitialdialogaction.cpp | 51 + .../interfaces/interstitialdialogaction.js | 267 ++++ .../source/interstitialdialogaction.ets | 296 ++++ navpushpathhelper/BUILD.gn | 76 + navpushpathhelper/include/hsp_silentinstall.h | 37 + .../include/hsp_silentinstall_napi.h | 59 + .../include/silent_install_callback.h | 170 ++ navpushpathhelper/navpushpathhelper.js | 146 ++ navpushpathhelper/src/hsp_silentinstall.cpp | 117 ++ .../src/hsp_silentinstall_napi.cpp | 273 ++++ navpushpathhelper/src/navpushpathhelper.cpp | 75 + 28 files changed, 5472 insertions(+), 3 deletions(-) create mode 100644 atomicservicenavigation/interfaces/BUILD.gn create mode 100644 atomicservicenavigation/interfaces/atomicservicenavigation.cpp create mode 100644 atomicservicenavigation/interfaces/atomicservicenavigation.js create mode 100644 atomicservicenavigation/source/atomicservicenavigation.ets create mode 100644 atomicserviceweb/interfaces/BUILD.gn create mode 100644 atomicserviceweb/interfaces/api_policy_adapter.cpp create mode 100644 atomicserviceweb/interfaces/api_policy_adapter.h create mode 100644 atomicserviceweb/interfaces/atomicserviceweb.cpp create mode 100644 atomicserviceweb/interfaces/atomicserviceweb.js create mode 100644 atomicserviceweb/source/atomicserviceweb.ets create mode 100644 innerfullscreenlaunchcomponent/interfaces/BUILD.gn create mode 100644 innerfullscreenlaunchcomponent/interfaces/innerfullscreenlaunchcomponent.cpp create mode 100644 innerfullscreenlaunchcomponent/interfaces/innerfullscreenlaunchcomponent.js create mode 100644 innerfullscreenlaunchcomponent/source/innerfullscreenlaunchcomponent.ets create mode 100644 interstitialdialogaction/interfaces/BUILD.gn create mode 100644 interstitialdialogaction/interfaces/interstitialdialogaction.cpp create mode 100644 interstitialdialogaction/interfaces/interstitialdialogaction.js create mode 100644 interstitialdialogaction/source/interstitialdialogaction.ets create mode 100644 navpushpathhelper/BUILD.gn create mode 100644 navpushpathhelper/include/hsp_silentinstall.h create mode 100644 navpushpathhelper/include/hsp_silentinstall_napi.h create mode 100644 navpushpathhelper/include/silent_install_callback.h create mode 100644 navpushpathhelper/navpushpathhelper.js create mode 100644 navpushpathhelper/src/hsp_silentinstall.cpp create mode 100644 navpushpathhelper/src/hsp_silentinstall_napi.cpp create mode 100644 navpushpathhelper/src/navpushpathhelper.cpp diff --git a/BUILD.gn b/BUILD.gn index f01339f..545b2db 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -12,5 +12,12 @@ # limitations under the License. group("as_advanced_ui_component") { - deps = [ "atomicservicetabs/interfaces:atomicservicetabs" ] + deps = [ + "atomicservicenavigation/interfaces:atomicservicenavigation", + "atomicservicetabs/interfaces:atomicservicetabs", + "atomicserviceweb/interfaces:atomicserviceweb", + "innerfullscreenlaunchcomponent/interfaces:innerfullscreenlaunchcomponent", + "interstitialdialogaction/interfaces:interstitialdialogaction", + "navpushpathhelper:navpushpathhelper", + ] } diff --git a/atomicservicenavigation/interfaces/BUILD.gn b/atomicservicenavigation/interfaces/BUILD.gn new file mode 100644 index 0000000..5808d6b --- /dev/null +++ b/atomicservicenavigation/interfaces/BUILD.gn @@ -0,0 +1,59 @@ +# 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. + +import("//build/config/components/ets_frontend/es2abc_config.gni") +import("//build/ohos.gni") +import("//foundation/arkui/ace_engine/ace_config.gni") +import("//foundation/arkui/ace_engine/adapter/preview/build/config.gni") +import("//foundation/arkui/ace_engine/build/ace_gen_obj.gni") + +es2abc_gen_abc("gen_atomicservicenavigation_abc") { + src_js = rebase_path("atomicservicenavigation.js") + dst_file = rebase_path(target_out_dir + "/atomicservicenavigation.abc") + in_puts = [ "atomicservicenavigation.js" ] + out_puts = [ target_out_dir + "/atomicservicenavigation.abc" ] + extra_args = [ "--module" ] +} + +gen_js_obj("atomicservicenavigation_abc") { + input = get_label_info(":gen_atomicservicenavigation_abc", "target_out_dir") + + "/atomicservicenavigation.abc" + output = target_out_dir + "/atomicservicenavigation_abc.o" + dep = ":gen_atomicservicenavigation_abc" +} + +gen_obj("atomicservicenavigation_abc_preview") { + input = get_label_info(":gen_atomicservicenavigation_abc", "target_out_dir") + + "/atomicservicenavigation.abc" + output = target_out_dir + "/atomicservicenavigation_abc.c" + snapshot_dep = [ ":gen_atomicservicenavigation_abc" ] +} + +ohos_shared_library("atomicservicenavigation") { + sources = [ "atomicservicenavigation.cpp" ] + + if (use_mingw_win || use_mac || use_linux) { + deps = [ ":gen_obj_src_atomicservicenavigation_abc_preview" ] + } else { + deps = [ ":atomicservicenavigation_abc" ] + } + + external_deps = [ + "hilog:libhilog", + "napi:ace_napi", + ] + + relative_install_dir = "module/atomicservice" + subsystem_name = "arkui" + part_name = "as_advanced_ui_component" +} diff --git a/atomicservicenavigation/interfaces/atomicservicenavigation.cpp b/atomicservicenavigation/interfaces/atomicservicenavigation.cpp new file mode 100644 index 0000000..8cdce4d --- /dev/null +++ b/atomicservicenavigation/interfaces/atomicservicenavigation.cpp @@ -0,0 +1,51 @@ +/* + * 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 "napi/native_node_api.h" + +extern const char _binary_atomicservicenavigation_abc_start[]; +extern const char _binary_atomicservicenavigation_abc_end[]; + +// Napi get abc code function +extern "C" __attribute__((visibility("default"))) +void NAPI_atomicservice_AtomicServiceNavigation_GetABCCode(const char **buf, int *buflen) +{ + if (buf != nullptr) { + *buf = _binary_atomicservicenavigation_abc_start; + } + if (buflen != nullptr) { + *buflen = _binary_atomicservicenavigation_abc_end - _binary_atomicservicenavigation_abc_start; + } +} + +/* + * Module define + */ +static napi_module AtomicServiceNavigationModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_modname = "atomicservice.AtomicServiceNavigation", + .nm_priv = ((void*)0), + .reserved = { 0 }, +}; + +/* + * Module registerfunction + */ +extern "C" __attribute__((constructor)) void AtomicServiceNavigationRegisterModule(void) +{ + napi_module_register(&AtomicServiceNavigationModule); +} \ No newline at end of file diff --git a/atomicservicenavigation/interfaces/atomicservicenavigation.js b/atomicservicenavigation/interfaces/atomicservicenavigation.js new file mode 100644 index 0000000..8d98ae4 --- /dev/null +++ b/atomicservicenavigation/interfaces/atomicservicenavigation.js @@ -0,0 +1,181 @@ +/* + * 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. + */ + +if (!("finalizeConstruction" in ViewPU.prototype)) { + Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { }); +} +export class AtomicServiceNavigation extends ViewPU { + constructor(w, x, y, z = -1, a1 = undefined, b1) { + super(w, y, z, b1); + if (typeof a1 === "function") { + this.paramsGenerator_ = a1; + } + this.__navPathStack = new ObservedPropertyObjectPU(new NavPathStack(), this, "navPathStack"); + this.navigationContent = undefined; + this.__title = new SynchedPropertyObjectOneWayPU(x.title, this, "title"); + this.__titleOptions = new SynchedPropertyObjectOneWayPU(x.titleOptions, this, "titleOptions"); + this.__hideTitleBar = new SynchedPropertySimpleOneWayPU(x.hideTitleBar, this, "hideTitleBar"); + this.__navBarWidth = new SynchedPropertyObjectOneWayPU(x.navBarWidth, this, "navBarWidth"); + this.__mode = new SynchedPropertySimpleOneWayPU(x.mode, this, "mode"); + this.navDestinationBuilder = this.defaultNavDestinationBuilder; + this.__navBarWidthRange = new SynchedPropertyObjectOneWayPU(x.navBarWidthRange, this, "navBarWidthRange"); + this.__minContentWidth = new SynchedPropertyObjectOneWayPU(x.minContentWidth, this, "minContentWidth"); + this.stateChangeCallback = undefined; + this.modeChangeCallback = undefined; + this.setInitiallyProvidedValue(x); + this.finalizeConstruction(); + } + setInitiallyProvidedValue(v) { + if (v.navPathStack !== undefined) { + this.navPathStack = v.navPathStack; + } + if (v.navigationContent !== undefined) { + this.navigationContent = v.navigationContent; + } + if (v.titleOptions === undefined) { + this.__titleOptions.set({ isBlurEnabled: true }); + } + if (v.navDestinationBuilder !== undefined) { + this.navDestinationBuilder = v.navDestinationBuilder; + } + if (v.stateChangeCallback !== undefined) { + this.stateChangeCallback = v.stateChangeCallback; + } + if (v.modeChangeCallback !== undefined) { + this.modeChangeCallback = v.modeChangeCallback; + } + } + updateStateVars(u) { + this.__title.reset(u.title); + this.__titleOptions.reset(u.titleOptions); + this.__hideTitleBar.reset(u.hideTitleBar); + this.__navBarWidth.reset(u.navBarWidth); + this.__mode.reset(u.mode); + this.__navBarWidthRange.reset(u.navBarWidthRange); + this.__minContentWidth.reset(u.minContentWidth); + } + purgeVariableDependenciesOnElmtId(t) { + this.__navPathStack.purgeDependencyOnElmtId(t); + this.__title.purgeDependencyOnElmtId(t); + this.__titleOptions.purgeDependencyOnElmtId(t); + this.__hideTitleBar.purgeDependencyOnElmtId(t); + this.__navBarWidth.purgeDependencyOnElmtId(t); + this.__mode.purgeDependencyOnElmtId(t); + this.__navBarWidthRange.purgeDependencyOnElmtId(t); + this.__minContentWidth.purgeDependencyOnElmtId(t); + } + aboutToBeDeleted() { + this.__navPathStack.aboutToBeDeleted(); + this.__title.aboutToBeDeleted(); + this.__titleOptions.aboutToBeDeleted(); + this.__hideTitleBar.aboutToBeDeleted(); + this.__navBarWidth.aboutToBeDeleted(); + this.__mode.aboutToBeDeleted(); + this.__navBarWidthRange.aboutToBeDeleted(); + this.__minContentWidth.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + get navPathStack() { + return this.__navPathStack.get(); + } + set navPathStack(s) { + this.__navPathStack.set(s); + } + get title() { + return this.__title.get(); + } + set title(r) { + this.__title.set(r); + } + get titleOptions() { + return this.__titleOptions.get(); + } + set titleOptions(q) { + this.__titleOptions.set(q); + } + get hideTitleBar() { + return this.__hideTitleBar.get(); + } + set hideTitleBar(p) { + this.__hideTitleBar.set(p); + } + get navBarWidth() { + return this.__navBarWidth.get(); + } + set navBarWidth(o) { + this.__navBarWidth.set(o); + } + get mode() { + return this.__mode.get(); + } + set mode(n) { + this.__mode.set(n); + } + get navBarWidthRange() { + return this.__navBarWidthRange.get(); + } + set navBarWidthRange(m) { + this.__navBarWidthRange.set(m); + } + get minContentWidth() { + return this.__minContentWidth.get(); + } + set minContentWidth(l) { + this.__minContentWidth.set(l); + } + defaultNavDestinationBuilder(i, j, k = null) { + } + initialRender() { + this.observeComponentCreation2((g, h) => { + Navigation.create(this.navPathStack); + Navigation.title(ObservedObject.GetRawObject(this.title), { + backgroundColor: this.titleOptions?.backgroundColor, + backgroundBlurStyle: this.titleOptions?.isBlurEnabled ? BlurStyle.COMPONENT_THICK : BlurStyle.NONE, + barStyle: this.titleOptions?.barStyle + }); + Navigation.titleMode(NavigationTitleMode.Mini); + Navigation.hideBackButton(true); + Navigation.hideTitleBar(this.hideTitleBar); + Navigation.navBarWidth(ObservedObject.GetRawObject(this.navBarWidth)); + Navigation.navBarPosition(NavBarPosition.Start); + Navigation.mode(this.mode); + Navigation.navDestination({ builder: this.navDestinationBuilder.bind(this) }); + Navigation.navBarWidthRange(ObservedObject.GetRawObject(this.navBarWidthRange)); + Navigation.minContentWidth(ObservedObject.GetRawObject(this.minContentWidth)); + Navigation.onNavBarStateChange(this.stateChangeCallback); + Navigation.onNavigationModeChange(this.modeChangeCallback); + }, Navigation); + this.observeComponentCreation2((c, d) => { + If.create(); + if (this.navigationContent) { + this.ifElseBranchUpdateFunction(0, () => { + this.navigationContent.bind(this)(this); + }); + } + else { + this.ifElseBranchUpdateFunction(1, () => { + }); + } + }, If); + If.pop(); + Navigation.pop(); + } + rerender() { + this.updateDirtyElements(); + } +} + +export default { AtomicServiceNavigation }; \ No newline at end of file diff --git a/atomicservicenavigation/source/atomicservicenavigation.ets b/atomicservicenavigation/source/atomicservicenavigation.ets new file mode 100644 index 0000000..13596b8 --- /dev/null +++ b/atomicservicenavigation/source/atomicservicenavigation.ets @@ -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. + */ + +import { Callback } from '@ohos.base'; + +@Component +export struct AtomicServiceNavigation { + @State navPathStack?: NavPathStack = new NavPathStack(); + @BuilderParam navigationContent?: Callback; + @Prop title?: ResourceStr; + @Prop titleOptions?: TitleOptions = { isBlurEnabled: true }; + @Prop hideTitleBar?: boolean; + @Prop navBarWidth?: Length; + @Prop mode?: NavigationMode; + @BuilderParam navDestinationBuilder?: NavDestinationBuilder = this.defaultNavDestinationBuilder; + @Prop navBarWidthRange?: [Dimension, Dimension]; + @Prop minContentWidth?: Dimension; + stateChangeCallback?: Callback; + modeChangeCallback?: Callback; + + @Builder + defaultNavDestinationBuilder(name: string, param?: Object) { + } + + build() { + Navigation(this.navPathStack) { + if (this.navigationContent) { + this.navigationContent() + } + } + .title(this.title, { + backgroundColor: this.titleOptions?.backgroundColor, + backgroundBlurStyle: this.titleOptions?.isBlurEnabled ? BlurStyle.COMPONENT_THICK : BlurStyle.NONE, + barStyle: this.titleOptions?.barStyle + }) + .titleMode(NavigationTitleMode.Mini) + .hideBackButton(true) + .hideTitleBar(this.hideTitleBar) + .navBarWidth(this.navBarWidth) + .navBarPosition(NavBarPosition.Start) + .mode(this.mode) + .navDestination(this.navDestinationBuilder) + .navBarWidthRange(this.navBarWidthRange) + .minContentWidth(this.minContentWidth) + .onNavBarStateChange(this.stateChangeCallback) + .onNavigationModeChange(this.modeChangeCallback) + } +} + +export interface TitleOptions { + backgroundColor?: ResourceColor, + isBlurEnabled?: boolean, + barStyle?: BarStyle +} + +export type NavDestinationBuilder = (name: string, param?: Object) => void; \ No newline at end of file diff --git a/atomicserviceweb/interfaces/BUILD.gn b/atomicserviceweb/interfaces/BUILD.gn new file mode 100644 index 0000000..a8deb63 --- /dev/null +++ b/atomicserviceweb/interfaces/BUILD.gn @@ -0,0 +1,64 @@ +# 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. + +import("//build/config/components/ets_frontend/es2abc_config.gni") +import("//build/ohos.gni") +import("//foundation/arkui/ace_engine/ace_config.gni") +import("//foundation/arkui/ace_engine/adapter/preview/build/config.gni") +import("//foundation/arkui/ace_engine/build/ace_gen_obj.gni") + +es2abc_gen_abc("gen_atomicserviceweb_abc") { + src_js = rebase_path("atomicserviceweb.js") + dst_file = rebase_path(target_out_dir + "/atomicserviceweb.abc") + in_puts = [ "atomicserviceweb.js" ] + out_puts = [ target_out_dir + "/atomicserviceweb.abc" ] + extra_args = [ "--module" ] +} + +gen_js_obj("atomicserviceweb_abc") { + input = get_label_info(":gen_atomicserviceweb_abc", "target_out_dir") + + "/atomicserviceweb.abc" + output = target_out_dir + "/atomicserviceweb_abc.o" + dep = ":gen_atomicserviceweb_abc" +} + +gen_obj("atomicserviceweb_abc_preview") { + input = get_label_info(":gen_atomicserviceweb_abc", "target_out_dir") + + "/atomicserviceweb.abc" + output = target_out_dir + "/atomicserviceweb_abc.c" + snapshot_dep = [ ":gen_atomicserviceweb_abc" ] +} + +ohos_shared_library("atomicserviceweb") { + include_dirs = [ "include" ] + + sources = [ + "api_policy_adapter.cpp", + "atomicserviceweb.cpp", + ] + + if (use_mingw_win || use_mac || use_linux) { + deps = [ ":gen_obj_src_atomicserviceweb_abc_preview" ] + } else { + deps = [ ":atomicserviceweb_abc" ] + } + + external_deps = [ + "hilog:libhilog", + "napi:ace_napi", + ] + + relative_install_dir = "module/atomicservice" + subsystem_name = "arkui" + part_name = "as_advanced_ui_component" +} diff --git a/atomicserviceweb/interfaces/api_policy_adapter.cpp b/atomicserviceweb/interfaces/api_policy_adapter.cpp new file mode 100644 index 0000000..05c6ea7 --- /dev/null +++ b/atomicserviceweb/interfaces/api_policy_adapter.cpp @@ -0,0 +1,47 @@ +/* + * 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 "api_policy_adapter.h" + +ApiPolicyAdapter::ApiPolicyAdapter() +{ +#ifndef __WIN32 + handle = dlopen("/system/lib64/platformsdk/libapipolicy_client.z.so", RTLD_NOW); + if (!handle) { + return; + } + func = reinterpret_cast(dlsym(handle, "CheckUrl")); +#endif +} + +ApiPolicyAdapter::~ApiPolicyAdapter() +{ +#ifndef __WIN32 + if (handle) { + dlclose(handle); + handle = nullptr; + } +#endif +} + +int32_t ApiPolicyAdapter::CheckUrl(const std::string& bundleName, const std::string& domainType, const std::string& url) +{ + int32_t res = 0; + if (func == nullptr) { + return res; + } + res = func(bundleName, domainType, url); + return res; +} diff --git a/atomicserviceweb/interfaces/api_policy_adapter.h b/atomicserviceweb/interfaces/api_policy_adapter.h new file mode 100644 index 0000000..03f009e --- /dev/null +++ b/atomicserviceweb/interfaces/api_policy_adapter.h @@ -0,0 +1,39 @@ +/* + * 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 API_POLICY_ADAPTER_H +#define API_POLICY_ADAPTER_H + +#include "string" +#ifndef __WIN32 +#include +#endif + +class ApiPolicyAdapter { +public: + ApiPolicyAdapter(); + ~ApiPolicyAdapter(); + + int32_t CheckUrl(const std::string& bundleName, const std::string& domainType, const std::string& url); + + using CheckUrlFunc = int32_t (*)(const std::string&, const std::string&, const std::string&); + void SetCheckUrlFunc(CheckUrlFunc& func); + +private: + void *handle = nullptr; + CheckUrlFunc func = nullptr; +}; + +#endif diff --git a/atomicserviceweb/interfaces/atomicserviceweb.cpp b/atomicserviceweb/interfaces/atomicserviceweb.cpp new file mode 100644 index 0000000..03a3ba8 --- /dev/null +++ b/atomicserviceweb/interfaces/atomicserviceweb.cpp @@ -0,0 +1,114 @@ +/* + * 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 + +#include "napi/native_node_api.h" +#include "api_policy_adapter.h" + +using namespace std; + +extern const char _binary_atomicserviceweb_abc_start[]; +extern const char _binary_atomicserviceweb_abc_end[]; + +namespace HMS::AtomicServiceWeb { + static napi_value CheckUrl(napi_env env, napi_callback_info info) + { + const int indexTwo = 2; + size_t requireArgc = 3; + size_t argc = 3; + napi_value args[3] = { nullptr }; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + + NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments"); + + napi_valuetype bundleNameType; + NAPI_CALL(env, napi_typeof(env, args[0], &bundleNameType)); + + napi_valuetype domainTypeType; + NAPI_CALL(env, napi_typeof(env, args[1], &domainTypeType)); + + napi_valuetype urlType; + NAPI_CALL(env, napi_typeof(env, args[indexTwo], &urlType)); + + NAPI_ASSERT(env, bundleNameType == napi_string && domainTypeType == napi_string && urlType == napi_string, + "Wrong argument type. String expected."); + + size_t maxValueLen = 1024; + char bundleNameValue[maxValueLen]; + size_t bundleNameLength = 0; + napi_get_value_string_utf8(env, args[0], bundleNameValue, maxValueLen, &bundleNameLength); + std::string bundleName = bundleNameValue; + + char domainTypeValue[maxValueLen]; + size_t domainTypeLength = 0; + napi_get_value_string_utf8(env, args[1], domainTypeValue, maxValueLen, &domainTypeLength); + std::string domainType = domainTypeValue; + + char urlValue[maxValueLen]; + size_t urlLength = 0; + napi_get_value_string_utf8(env, args[indexTwo], urlValue, maxValueLen, &urlLength); + std::string url = urlValue; + + std::shared_ptr apiPolicyAdapter = std::make_shared(); + int32_t res = apiPolicyAdapter->CheckUrl(bundleName, domainType, url); + + napi_value result; + NAPI_CALL(env, napi_create_double(env, res, &result)); + return result; + } + + static napi_value Init(napi_env env, napi_value exports) + { + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("checkUrl", CheckUrl), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + return exports; + } + + // Napi get abc code function + extern "C" __attribute__((visibility("default"))) + void NAPI_atomicservice_AtomicServiceWeb_GetABCCode(const char **buf, int *buflen) + { + if (buf != nullptr) { + *buf = _binary_atomicserviceweb_abc_start; + } + if (buflen != nullptr) { + *buflen = _binary_atomicserviceweb_abc_end - _binary_atomicserviceweb_abc_start; + } + } + + /* + * Module define + */ + static napi_module AtomicServiceWebModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "atomicservice.AtomicServiceWeb", + .nm_priv = ((void*)0), + .reserved = { 0 }, + }; + + /* + * Module registerfunction + */ + extern "C" __attribute__((constructor)) void AtomicServiceWebRegisterModule(void) + { + napi_module_register(&AtomicServiceWebModule); + } +} diff --git a/atomicserviceweb/interfaces/atomicserviceweb.js b/atomicserviceweb/interfaces/atomicserviceweb.js new file mode 100644 index 0000000..5cb35b2 --- /dev/null +++ b/atomicserviceweb/interfaces/atomicserviceweb.js @@ -0,0 +1,1348 @@ +/* + * 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. + */ + +var __decorate = (this && this.__decorate) || function (a12, b12, c12, d12) { + var e12 = arguments.length, + f12 = e12 < 3 ? b12 : d12 === null ? d12 = Object.getOwnPropertyDescriptor(b12, c12) : d12, g12; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") { + f12 = Reflect.decorate(a12, b12, c12, d12); + } else { + for (var h12 = a12.length - 1; h12 >= 0; h12--) { + if (g12 = a12[h12]) { + f12 = (e12 < 3 ? g12(f12) : e12 > 3 ? g12(b12, c12, f12) : g12(b12, c12)) || f12; + } + } + } + return e12 > 3 && f12 && Object.defineProperty(b12, c12, f12), f12; +}; + +if (!("finalizeConstruction" in ViewPU.prototype)) { + Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { + }); +} +const web_webview = requireNapi('web.webview'); +const router = requireNapi('router'); +const deviceInfo = requireNapi('deviceInfo'); +const geoLocationManager = requireNapi('geoLocationManager'); +const bundleManager = requireNapi('bundle.bundleManager'); +const abilityAccessCtrl = requireNapi('abilityAccessCtrl'); +const connection = requireNapi('net.connection'); +const request = requireNapi('request'); +const fs = requireNapi('file.fs'); +const util = requireNapi('util'); +const photoAccessHelper = requireNapi('file.photoAccessHelper'); +const filePreview = globalThis.requireNapi('filemanagement.filepreview', false, '', 'hms'); +const fileUri = requireNapi('file.fileuri'); +const picker = requireNapi('multimedia.cameraPicker'); +const filePicker = requireNapi('file.picker'); +const atomicServiceWebNapi = requireInternal('atomicservice.AtomicServiceWeb'); + +class AsError { + constructor(m11, n11) { + this.code = m11; + this.message = n11; + } +} + +class JsApiConfig { + constructor(i11, j11, k11, l11) { + this.apiName = i11; + this.minVersion = j11; + this.maxVersion = k11; + this.requiredFieldNames = l11; + } +} + +const LOG_ENABLE = true; +const LOG_PREFIX = '[AtomicServiceWebLog]'; +const UPLOAD_IMAGE_CACHE_DIR = '/cache/'; +const JAVA_SCRIPT_PROXY_OBJECT_NAME = 'atomicServiceProxy'; +const JAVA_SCRIPT_PROXY_API_NAME_LIST = ['invokeJsApi']; +const ATOMIC_SERVICE_JS_API_MAP = new Map(); +const registerJsApi = (u11, v11, w11, x11, y11) => { + ATOMIC_SERVICE_JS_API_MAP.set(u11, new JsApiConfig(v11, w11, x11, y11)); +}; +const MAX_VERSION = '99.99.99'; +const ATOMIC_SERVICE_JS_SDK_CURRENT_VERSION = '1.0.0'; +const PERMISSION_APPROXIMATELY_LOCATION = 'ohos.permission.APPROXIMATELY_LOCATION'; +const SYSTEM_INTERNAL_ERROR = new AsError(500, 'System internal error.'); +const JS_API_INVALID_INVOKE_ERROR = new AsError(200001, 'Invalid invoke.'); +const PARAM_REQUIRED_ERROR_CODE = 200002; +const PARAM_NUMBER_POSITIVE_ERROR_CODE = 200003; +const ROUTER_PARAM_MODE_INVALID_ERROR = new AsError(200004, 'Param mode is invalid.'); +const BACK_URL_NOT_EXIST_OR_OPENED_ERROR = new AsError(200005, 'Url is not exist or opened, can not be back.'); +const NAV_PATH_STACK_NOT_EXIST_ERROR_CODE = 200006; +const POP_PATH_NAME_NOT_EXIST_ERROR = new AsError(200007, 'Name is not exist or opened, can not be pop.'); +const POP_PATH_PARAM_INDEX_INVALID_ERROR = new AsError(200008, 'Param index is invalid.'); +const POP_PATH_INDEX_OUT_OF_RANGE_ERROR = new AsError(200009, 'The Index is out of range.'); +const UPLOAD_IMAGE_FILES_REQUIRED_ERROR = new AsError(200010, 'Param files is required.'); +const UPLOAD_IMAGE_FILE_NOT_EXIST_ERROR_CODE = 200011; +const UPLOAD_IMAGE_FILES_URI_REQUIRED_ERROR = new AsError(200012, 'Param uri of files is required.'); +const UPLOAD_FILE_ERROR = new AsError(200013, 'Upload file error.'); +const IMAGE_CAN_NOT_PREVIEW_ERROR = new AsError(200014, 'The filePath can not preview.'); +const NETWORK_NO_ACTIVE_ERROR = new AsError(200015, 'The network is not active.'); + +registerJsApi('router.pushUrl', 'pushUrl', '1.0.0', MAX_VERSION, ['url']); +registerJsApi('router.replaceUrl', 'replaceUrl', '1.0.0', MAX_VERSION, ['url']); +registerJsApi('router.back', 'backUrl', '1.0.0', MAX_VERSION, []); +registerJsApi('router.clear', 'clearUrl', '1.0.0', MAX_VERSION, []); +registerJsApi('navPathStack.pushPath', 'pushPath', '1.0.0', MAX_VERSION, ['name']); +registerJsApi('navPathStack.replacePath', 'replacePath', '1.0.0', MAX_VERSION, ['name']); +registerJsApi('navPathStack.pop', 'popPath', '1.0.0', MAX_VERSION, []); +registerJsApi('navPathStack.clear', 'clearPath', '1.0.0', MAX_VERSION, []); +registerJsApi('asWeb.postMessage', 'postMessage', '1.0.0', MAX_VERSION, ['data']); +registerJsApi('asWeb.getEnv', 'getEnv', '1.0.0', MAX_VERSION, []); +registerJsApi('asWeb.checkJsApi', 'checkJsApi', '1.0.0', MAX_VERSION, ['jsApiList']); +registerJsApi('cameraPicker.pick', 'pickCamera', '1.0.0', MAX_VERSION, ['mediaTypes', 'cameraPosition']); +registerJsApi('photoViewPicker.select', 'selectPhoto', '1.0.0', MAX_VERSION, []); +registerJsApi('filePreview.openPreview', 'openPreview', '1.0.0', MAX_VERSION, ['uri']); +registerJsApi('request.uploadFile', 'uploadFile', '1.0.0', MAX_VERSION, ['url', 'files']); +registerJsApi('request.downloadFile', 'downloadFile', '1.0.0', MAX_VERSION, ['url']); +registerJsApi('connection.getNetworkType', 'getNetworkType', '1.0.0', MAX_VERSION, []); +registerJsApi('location.getLocation', 'getLocation', '1.0.0', MAX_VERSION, []); + +export class AtomicServiceWeb extends ViewPU { + constructor(s10, t10, u10, v10 = -1, w10 = undefined, x10) { + super(s10, u10, v10, x10); + if (typeof w10 === "function") { + this.paramsGenerator_ = w10; + } + this.src = undefined; + this.navPathStack = undefined; + this.__mixedMode = new SynchedPropertySimpleOneWayPU(t10.mixedMode, this, "mixedMode"); + this.__darkMode = new SynchedPropertySimpleOneWayPU(t10.darkMode, this, "darkMode"); + this.__forceDarkAccess = new SynchedPropertySimpleOneWayPU(t10.forceDarkAccess, this, "forceDarkAccess"); + this.__controller = new SynchedPropertyNesedObjectPU(t10.controller, this, "controller"); + this.onMessage = () => { + }; + this.onErrorReceive = () => { + }; + this.onHttpErrorReceive = () => { + }; + this.onPageBegin = () => { + }; + this.onPageEnd = () => { + }; + this.onProgressChange = () => { + }; + this.onControllerAttached = undefined; + this.onLoadIntercept = undefined; + this.context = this.getUIContext().getHostContext(); + this.webViewController = new web_webview.WebviewController(); + this.schemeHandler = new web_webview.WebSchemeHandler(); + this.atomicService = undefined; + this.atomicServiceProxy = undefined; + this.setInitiallyProvidedValue(t10); + this.finalizeConstruction(); + } + + setInitiallyProvidedValue(r10) { + if (r10.src !== undefined) { + this.src = r10.src; + } + if (r10.navPathStack !== undefined) { + this.navPathStack = r10.navPathStack; + } + this.__controller.set(r10.controller); + if (r10.onMessage !== undefined) { + this.onMessage = r10.onMessage; + } + if (r10.onErrorReceive !== undefined) { + this.onErrorReceive = r10.onErrorReceive; + } + if (r10.onHttpErrorReceive !== undefined) { + this.onHttpErrorReceive = r10.onHttpErrorReceive; + } + if (r10.onPageBegin !== undefined) { + this.onPageBegin = r10.onPageBegin; + } + if (r10.onPageEnd !== undefined) { + this.onPageEnd = r10.onPageEnd; + } + if (r10.onProgressChange !== undefined) { + this.onProgressChange = r10.onProgressChange; + } + if (r10.onControllerAttached !== undefined) { + this.onControllerAttached = r10.onControllerAttached; + } + if (r10.onLoadIntercept !== undefined) { + this.onLoadIntercept = r10.onLoadIntercept; + } + if (r10.context !== undefined) { + this.context = r10.context; + } + if (r10.webViewController !== undefined) { + this.webViewController = r10.webViewController; + } + if (r10.schemeHandler !== undefined) { + this.schemeHandler = r10.schemeHandler; + } + if (r10.atomicService !== undefined) { + this.atomicService = r10.atomicService; + } + if (r10.atomicServiceProxy !== undefined) { + this.atomicServiceProxy = r10.atomicServiceProxy; + } + } + + updateStateVars(q10) { + this.__mixedMode.reset(q10.mixedMode); + this.__darkMode.reset(q10.darkMode); + this.__forceDarkAccess.reset(q10.forceDarkAccess); + this.__controller.set(q10.controller); + } + + purgeVariableDependenciesOnElmtId(p10) { + this.__mixedMode.purgeDependencyOnElmtId(p10); + this.__darkMode.purgeDependencyOnElmtId(p10); + this.__forceDarkAccess.purgeDependencyOnElmtId(p10); + this.__controller.purgeDependencyOnElmtId(p10); + } + + aboutToBeDeleted() { + this.__mixedMode.aboutToBeDeleted(); + this.__darkMode.aboutToBeDeleted(); + this.__forceDarkAccess.aboutToBeDeleted(); + this.__controller.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + + get mixedMode() { + return this.__mixedMode.get(); + } + + set mixedMode(t11) { + this.__mixedMode.set(t11); + } + + get darkMode() { + return this.__darkMode.get(); + } + + set darkMode(s11) { + this.__darkMode.set(s11); + } + + get forceDarkAccess() { + return this.__forceDarkAccess.get(); + } + + set forceDarkAccess(r11) { + this.__forceDarkAccess.set(r11); + } + + get controller() { + return this.__controller.get(); + } + + aboutToAppear() { + if (!this.atomicService) { + this.atomicService = new AtomicServiceApi(this.context, this.navPathStack, this.onMessage); + this.atomicServiceProxy = new AtomicServiceProxy(this.atomicService); + } + try { + let h2 = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); + if (h2?.appInfo?.appProvisionType === 'debug') { + console.log(`AtomicServiceWeb setWebDebuggingAccess`); + web_webview.WebviewController.setWebDebuggingAccess(true); + } + } catch (d2) { + console.error(`AtomicServiceWeb set Web Debug Mode failed, code is ${d2.code}, message is ${d2.message}`); + } + } + + aboutToDisappear() { + this.atomicService?.notifyMessage(); + } + + initialRender() { + this.observeComponentCreation2((g10, h10) => { + Web.create({ src: this.src, controller: this.webViewController }); + Web.zoomAccess(false); + Web.allowWindowOpenMethod(false); + Web.domStorageAccess(true); + Web.layoutMode(WebLayoutMode.NONE); + Web.mixedMode(this.mixedMode); + Web.darkMode(this.darkMode); + Web.forceDarkAccess(this.forceDarkAccess); + Web.onErrorReceive((q11) => this.onCommonCallBack('onErrorReceive', q11, this.onErrorReceive)); + Web.onHttpErrorReceive((p11) => this.onCommonCallBack('onHttpErrorReceive', p11, this.onHttpErrorReceive)); + Web.onPageBegin((l10) => this.onCommonCallBack('onPageBegin', l10, this.onPageBegin)); + Web.onPageEnd((b11) => this.onCommonCallBack('onPageEnd', b11, this.onPageEnd)); + Web.onProgressChange((a11) => this.onCommonCallBack('onProgressChange', a11, this.onProgressChange)); + Web.onControllerAttached(() => { + this.registerJavaScriptProxy(); + this.schemeHandler.onRequestStart((z10) => { + return !this.checkUrl(z10.getRequestUrl()); + }); + this.webViewController.setWebSchemeHandler('https', this.schemeHandler); + this.initAtomicServiceWebController(); + if (this.onControllerAttached) { + try { + this.onControllerAttached(); + } catch (y10) { + console.error(`AtomicServiceWeb onControllerAttached failed, code is ${y10.code}, message is ${y10.message}`); + } + } + }); + Web.onOverrideUrlLoading((i10) => { + return !this.checkUrl(i10.getRequestUrl()); + }); + Web.onLoadIntercept(m7 => { + let n7 = !this.checkUrl(m7.data.getRequestUrl()); + if (!n7 && this.onLoadIntercept) { + try { + return this.onLoadIntercept(m7); + } catch (u7) { + console.error(`AtomicServiceWeb onLoadIntercept failed, code is ${u7.code}, message is ${u7.message}`); + return true; + } + } + return n7; + }); + }, Web); + } + + onCommonCallBack(q5, e6, j6) { + try { + j6 && j6(e6); + } catch (k7) { + console.error(`AtomicServiceWeb ${q5} failed, code is ${k7.code}, message is ${k7.message}`); + } + } + + registerJavaScriptProxy() { + try { + this.webViewController.registerJavaScriptProxy(this.atomicServiceProxy, JAVA_SCRIPT_PROXY_OBJECT_NAME, + JAVA_SCRIPT_PROXY_API_NAME_LIST); + } catch (d10) { + let e10 = d10; + console.error(`AtomicServiceWeb registerJavaScriptProxy failed, code is ${e10.code}, message is ${e10.message}`); + } + } + + initAtomicServiceWebController() { + if (!this.controller) { + return; + } + this.controller.setWebviewController(this.webViewController); + } + + cutUrl(b10) { + if (b10) { + let c10 = b10.indexOf('?'); + if (c10 > -1) { + return b10.substring(0, c10); + } + } + return b10; + } + + checkUrl(q1) { + if (!q1) { + return false; + } + if (q1.startsWith('resource://rawfile')) { + return true; + } + try { + let w1 = this.context.abilityInfo.bundleName; + let t1 = 'webView'; + q1 = this.cutUrl(q1); + let res = atomicServiceWebNapi.checkUrl(w1, t1, q1); + return res === 0; + } catch (j2) { + let n2 = j2; + console.error(`AtomicServiceWeb checkUrl failed, code is ${n2.code}, message is ${n2.message}`); + return false; + } + } + + rerender() { + this.updateDirtyElements(); + } +} +let AtomicServiceWebController = class AtomicServiceWebController { + setWebviewController(l5) { + this.webViewController = l5; + } + + checkWebviewController() { + if (!this.webViewController) { + const d5 = { + name: '', + message: 'Init error. The AtomicServiceWebController must be associated with a AtomicServiceWeb component.', + code: 17100001, + }; + throw d5; + } + } + + getUserAgent() { + this.checkWebviewController(); + return this.webViewController?.getUserAgent(); + } + + getCustomUserAgent() { + this.checkWebviewController(); + return this.webViewController?.getCustomUserAgent(); + } + + setCustomUserAgent(z4) { + this.checkWebviewController(); + this.webViewController?.setCustomUserAgent(z4); + } + + accessForward() { + this.checkWebviewController(); + return this.webViewController?.accessForward(); + } + + accessBackward() { + this.checkWebviewController(); + return this.webViewController?.accessBackward(); + } + + accessStep(d4) { + this.checkWebviewController(); + return this.webViewController?.accessStep(d4); + } + + forward() { + this.checkWebviewController(); + this.webViewController?.forward(); + } + + backward() { + this.checkWebviewController(); + this.webViewController?.backward(); + } + + refresh() { + this.checkWebviewController(); + this.webViewController?.refresh(); + } + + loadUrl(t3, y3) { + this.checkWebviewController(); + if (y3) { + this.webViewController?.loadUrl(t3, y3); + } else { + this.webViewController?.loadUrl(t3); + } + } +}; +AtomicServiceWebController = __decorate([ + Observed +], AtomicServiceWebController); + +export { AtomicServiceWebController }; + +class AtomicServiceProxy { + constructor(z9) { + this.atomicService = z9; + } + + invokeJsApi(u9, v9) { + try { + v9 = v9 || {}; + if (!u9 || !ATOMIC_SERVICE_JS_API_MAP.has(u9)) { + this.atomicService.errorWithCodeAndMsg(JS_API_INVALID_INVOKE_ERROR, v9); + return; + } + let x9 = ATOMIC_SERVICE_JS_API_MAP.get(u9); + if (!this.atomicService.checkRequiredFieldInOptions(x9, v9)) { + return; + } + let y9 = this.atomicService; + y9[x9?.apiName](v9); + } catch (w9) { + this.atomicService.error(w9, v9); + } + } +} + +class AtomicService { + constructor(q9, r9, s9) { + this.messageDataList = []; + this.onMessage = () => { + }; + this.context = q9; + this.navPathStack = r9; + this.onMessage = s9 ? s9 : this.onMessage; + } + + success(o9, p9) { + try { + p9?.callback && p9?.callback(undefined, o9); + } catch (f3) { + this.consoleError(`callback error, code is ${f3.code}, message is ${f3.message}`); + } + } + + error(m9, n9) { + try { + n9?.callback && n9?.callback(new AsError(m9.code ? m9.code : SYSTEM_INTERNAL_ERROR.code, + m9.message ? m9.message : SYSTEM_INTERNAL_ERROR.message)); + } catch (a3) { + this.consoleError(`callback error, code is ${a3.code}, message is ${a3.message}`); + } + } + + errorWithCodeAndMsg(k9, l9) { + try { + l9?.callback && l9?.callback(k9); + } catch (u2) { + this.consoleError(`callback error, code is ${u2.code}, message is ${u2.message}`); + } + } + + consoleLog(j9) { + if (LOG_ENABLE) { + console.log(`${LOG_PREFIX} ${j9}`); + } + } + + consoleError(i9) { + if (LOG_ENABLE) { + console.error(`${LOG_PREFIX} ${i9}`); + } + } + + logOptions(g9, h9) { + this.consoleLog(`${g9} options=${JSON.stringify(h9)}`); + } + + checkParamRequired(d9, e9, f9) { + if (e9 === undefined || e9 === null || e9 === '') { + this.errorWithCodeAndMsg(new AsError(PARAM_REQUIRED_ERROR_CODE, `Param ${d9} is required.`), f9); + return false; + } + return true; + } + + checkNumberParamPositive(a9, b9, c9) { + if (b9 <= 0) { + this.errorWithCodeAndMsg(new AsError(PARAM_NUMBER_POSITIVE_ERROR_CODE, + `Param ${a9} must be a positive number.`), c9); + return false; + } + return true; + } + + checkRequiredFieldInOptions(v8, w8) { + if (!v8) { + return false; + } + if (!v8.requiredFieldNames) { + return true; + } + let x8 = w8; + for (let y8 = 0; y8 < v8.requiredFieldNames.length; y8++) { + let z8 = v8.requiredFieldNames[y8]; + if (!this.checkParamRequired(z8, x8[z8], w8)) { + return false; + } + } + return true; + } + + checkRouterMode(t8, u8) { + if (!t8 || t8 === 'Single' || t8 === 'Standard') { + return true; + } + this.errorWithCodeAndMsg(ROUTER_PARAM_MODE_INVALID_ERROR, u8); + return false; + } + + parseRouterMode(s8) { + return s8 === 'Single' ? router.RouterMode.Single : router.RouterMode.Standard; + } + + getRouterIndexByDelta(o8) { + let p8 = Number.parseInt(router.getLength()); + for (let q8 = p8; q8 > 0; q8--) { + let r8 = router.getStateByIndex(q8); + if (r8?.name && o8-- == 0) { + return q8; + } + } + return 1; + } + + checkBackUrlExists(i8, j8) { + let k8 = Number.parseInt(router.getLength()); + for (let l8 = k8; l8 > 0; l8--) { + let m8 = router.getStateByIndex(l8); + if (m8?.name) { + let n8 = m8?.path + m8?.name; + if (n8 === i8) { + return true; + } + } + } + this.errorWithCodeAndMsg(BACK_URL_NOT_EXIST_OR_OPENED_ERROR, j8); + return false; + } + + checkNavPathStack(g8, h8) { + if (!this.navPathStack) { + this.errorWithCodeAndMsg(new AsError(NAV_PATH_STACK_NOT_EXIST_ERROR_CODE, + `Current page is not NavDestination, not support ${g8}().`), h8); + return false; + } + return true; + } + + getNavPathIndexByDelta(e8) { + let f8 = this.navPathStack?.getAllPathName(); + if (!f8 || f8.length == 0) { + return -1; + } + return f8.length > e8 ? (f8.length - e8 - 1) : -1; + } + + onPopHandler(c8, d8) { + if (!c8?.info || !d8) { + return; + } + d8(new OnPopEvent(c8.info.name, c8.info.param, c8.result)); + } + + getCurrentNavPathInfo() { + let a8 = this.navPathStack?.getAllPathName(); + let b8 = (a8 && a8.length > 0) ? + new NavPathInfo(a8[a8.length - 1], a8.length - 1) : new NavPathInfo(undefined, -1); + if (b8.index >= 0) { + b8.param = this.navPathStack?.getParamByIndex(b8.index); + } + return b8; + } + + notifyMessage() { + if (this.messageDataList.length <= 0) { + return; + } + try { + this.onMessage(new OnMessageEvent(this.messageDataList)); + } catch (q2) { + this.consoleError(`onMessage failed, code is ${q2.code}, message is ${q2.message}`); + } + this.messageDataList = []; + } + + isJsApiEnable(z7) { + if (!z7) { + return false; + } + if (this.compareVersion(z7.minVersion, ATOMIC_SERVICE_JS_SDK_CURRENT_VERSION) && + this.compareVersion(ATOMIC_SERVICE_JS_SDK_CURRENT_VERSION, z7.maxVersion)) { + return true; + } + return false; + } + + compareVersion(p7, q7) { + if (!p7 || !q7) { + return false; + } + let r7 = p7.split('.').map(y7 => Number.parseInt(y7)); + let s7 = q7.split('.').map(x7 => Number.parseInt(x7)); + const t7 = Math.max(r7.length, s7.length); + for (let w7 = 0; w7 < t7; w7++) { + if (r7[w7] < s7[w7]) { + return true; + } else if (r7[w7] > s7[w7]) { + return false; + } + } + if (r7.length < s7.length) { + return true; + } + if (r7.length > s7.length) { + return false; + } + return true; + } + + getUri(o7) { + if (!o7 || o7.startsWith('file://')) { + return o7; + } + return fileUri.getUriFromPath(o7); + } + + async checkUploadFile(e7) { + if (!e7.files || e7.files.length <= 0) { + this.errorWithCodeAndMsg(UPLOAD_IMAGE_FILES_REQUIRED_ERROR, e7); + return new CheckUploadFileResult(false); + } + let f7 = new Map(); + for (let g7 = 0; g7 < e7.files?.length; g7++) { + let h7 = e7.files[g7]; + if (!h7.uri) { + this.errorWithCodeAndMsg(UPLOAD_IMAGE_FILES_URI_REQUIRED_ERROR, e7); + return new CheckUploadFileResult(false); + } + if (!h7.uri.startsWith('file://') && !fs.accessSync(h7.uri, fs.AccessModeType.EXIST)) { + this.errorWithCodeAndMsg(new AsError(UPLOAD_IMAGE_FILE_NOT_EXIST_ERROR_CODE, + `File uri ${h7.uri} is not exist.`), e7); + return new CheckUploadFileResult(false); + } + let i7 = h7.uri; + let j7 = h7.uri; + if (j7.indexOf(UPLOAD_IMAGE_CACHE_DIR) < 0) { + let l7 = j7.startsWith('file://') ? j7 : fileUri.getUriFromPath(h7.uri); + j7 = this.context.cacheDir + '/' + j7.substring(j7.lastIndexOf('/') + 1); + try { + await fs.copy(l7, fileUri.getUriFromPath(j7)); + } catch (x1) { + this.errorWithCodeAndMsg(UPLOAD_FILE_ERROR, e7); + return new CheckUploadFileResult(false); + } + } + h7.uri = 'internal://' + j7.substring(j7.indexOf(UPLOAD_IMAGE_CACHE_DIR) + 1); + f7.set(j7, i7); + } + return new CheckUploadFileResult(true, f7); + } + + convertToRequestData(a7) { + let b7 = []; + if (a7) { + a7.forEach(d7 => { + if (!d7.name || !d7.value) { + return; + } + b7.push({ name: d7.name, value: d7.value }); + }); + } + return b7; + } + + convertToFile(w6) { + let x6 = []; + if (w6) { + w6.forEach(z6 => { + x6.push({ + filename: z6.filename, + name: z6.name, + uri: z6.uri, + type: z6.type + }); + }); + } + return x6; + } + + handleUploadFileResult(p6, q6, r6) { + let s6 = []; + if (p6) { + p6.forEach(u6 => { + let v6 = u6.path ? q6.get(u6.path) : u6.path; + s6.push(new UploadFileTaskState(v6 ? v6 : u6.path, u6.responseCode, u6.message)); + }); + } + this.success(new UploadFileResult(s6), r6); + } + + parseFileNameFromUrl(m6) { + if (!m6) { + return ''; + } + let n6 = m6.split('?')[0]; + if (n6.indexOf('/') < 0) { + return ''; + } + let o6 = n6.lastIndexOf('/'); + if (o6 == (n6.length - 1)) { + return ''; + } + return n6.substring(o6 + 1); + } + + saveDownloadFile(z5, a6, b6, c6) { + let d6 = new filePicker.DocumentViewPicker(); + d6.save({ + newFileNames: [a6] + }).then(h6 => { + let i6 = h6[0]; + fs.copy(fileUri.getUriFromPath(z5), i6).then(() => { + c6 && c6(i6); + }).catch((l6) => { + this.error(l6, b6); + }); + }).catch((g6) => { + this.error(g6, b6); + }); + } + + checkAccessToken(v5) { + let w5 = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); + let x5 = w5.appInfo.accessTokenId; + let y5 = abilityAccessCtrl.createAtManager(); + return y5.checkAccessToken(x5, v5); + } + + checkPermissions(j5, k5) { + this.checkAccessToken(j5).then(o5 => { + if (o5 == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { + k5(undefined); + } else { + let p5 = abilityAccessCtrl.createAtManager(); + p5.requestPermissionsFromUser(this.context, [j5]).then(t5 => { + for (let u5 = 0; u5 < t5.authResults.length; u5++) { + if (t5.authResults[u5] != 0) { + return; + } + } + k5(undefined); + }).catch((s5) => { + k5(s5); + }); + } + }).catch((n5) => { + k5(n5); + }); + } +} + +class AtomicServiceApi extends AtomicService { + constructor(g5, h5, i5) { + super(g5, h5, i5); + } + + pushUrl(c5) { + if (!this.checkRouterMode(c5.mode, c5)) { + return; + } + router.pushUrl({ url: c5.url, params: c5.params }, this.parseRouterMode(c5.mode)).then(() => { + this.success(new PushUrlResult(), c5); + }).catch((f5) => { + this.error(f5, c5); + }); + } + + replaceUrl(y4) { + if (!this.checkRouterMode(y4.mode, y4)) { + return; + } + router.replaceUrl({ url: y4.url, params: y4.params }, this.parseRouterMode(y4.mode)).then(() => { + this.success(new ReplaceUrlResult(), y4); + }).catch((b5) => { + this.error(b5, y4); + }); + } + + backUrl(x4) { + if (x4.url) { + if (!this.checkBackUrlExists(x4.url, x4)) { + return; + } + router.back({ url: x4.url, params: x4.params }); + this.success(new BackUrlResult(), x4); + } else if (x4.index || x4.index === 0) { + if (!this.checkNumberParamPositive('index', x4.index, x4)) { + return; + } + router.back(x4.index, x4.params); + this.success(new BackUrlResult(), x4); + } else if (x4.delta || x4.delta === 0) { + if (!this.checkNumberParamPositive('delta', x4.delta, x4)) { + return; + } + router.back(this.getRouterIndexByDelta(x4.delta), x4.params); + this.success(new BackUrlResult(), x4); + } else { + router.back(); + this.success(new BackUrlResult(), x4); + } + } + + clearUrl(w4) { + router.clear(); + this.success(new ClearUrlResult(), w4); + } + + pushPath(u4) { + if (!this.checkNavPathStack('navPathStack.pushPath', u4)) { + return; + } + this.navPathStack?.pushPath({ + name: u4.name, + param: u4.param, + onPop: v4 => this.onPopHandler(v4, u4.onPop) + }, u4.animated); + this.success(new PushPathResult(), u4); + } + + replacePath(s4) { + if (!this.checkNavPathStack('navPathStack.replacePath', s4)) { + return; + } + this.navPathStack?.replacePath({ + name: s4.name, + param: s4.param, + onPop: t4 => this.onPopHandler(t4, s4.onPop) + }, s4.animated); + this.success(new ReplacePathResult(), s4); + } + + popPath(p4) { + if (!this.checkNavPathStack('navPathStack.pop', p4)) { + return; + } + if (p4.name) { + let r4 = this.navPathStack?.popToName(p4.name, p4.result, p4.animated); + if (r4 === undefined || r4 === -1) { + this.errorWithCodeAndMsg(POP_PATH_NAME_NOT_EXIST_ERROR, p4); + return; + } + } else if (p4.index || p4.index === 0) { + if (p4.index < -1) { + this.errorWithCodeAndMsg(POP_PATH_PARAM_INDEX_INVALID_ERROR, p4); + return; + } + if (p4.index > this.getCurrentNavPathInfo().index) { + this.errorWithCodeAndMsg(POP_PATH_INDEX_OUT_OF_RANGE_ERROR, p4); + return; + } + this.navPathStack?.popToIndex(p4.index, p4.result, p4.animated); + } else if (p4.delta || p4.delta === 0) { + if (!this.checkNumberParamPositive('delta', p4.delta, p4)) { + return; + } + this.navPathStack?.popToIndex(this.getNavPathIndexByDelta(p4.delta), p4.result, p4.animated); + } else { + this.navPathStack?.pop(p4.result, p4.animated); + } + let q4 = this.getCurrentNavPathInfo(); + this.success(new PopPathResult(q4.name, q4.index, q4.param), p4); + } + + clearPath(o4) { + if (!this.checkNavPathStack('navPathStack.clear', o4)) { + return; + } + this.navPathStack?.clear(o4.animated); + this.success(new ClearPathResult(), o4); + } + + postMessage(n4) { + n4.data && this.messageDataList.push(n4.data); + this.success(new PostMessageResult(), n4); + } + + getEnv(l4) { + let m4 = new GetEnvResult(); + m4.deviceType = deviceInfo.deviceType; + m4.brand = deviceInfo.brand; + m4.productModel = deviceInfo.productModel; + m4.osFullName = deviceInfo.osFullName; + this.success(m4, l4); + } + + checkJsApi(h4) { + let i4 = new Map(); + h4.jsApiList?.forEach(k4 => { + i4[k4] = this.isJsApiEnable(ATOMIC_SERVICE_JS_API_MAP.get(k4)); + }); + this.success(new CheckJsApiResult(i4), h4); + } + + pickCamera(c4) { + picker.pick(this.context, c4.mediaTypes, { + cameraPosition: c4.cameraPosition, + saveUri: c4.saveUri, + videoDuration: c4.videoDuration + }).then((g4) => { + this.success(new PickCameraResult(g4.resultCode, g4.resultUri, g4.mediaType), c4); + }).catch((f4) => { + this.error(f4, c4); + }); + } + + selectPhoto(w3) { + let x3 = new photoAccessHelper.PhotoViewPicker(); + x3.select({ + MIMEType: w3.mimeType, + maxSelectNumber: w3.maxSelectNumber, + isPhotoTakingSupported: w3.isPhotoTakingSupported, + isEditSupported: w3.isEditSupported, + isSearchSupported: w3.isSearchSupported, + recommendationOptions: { + recommendationType: w3.recommendationType + }, + preselectedUris: w3.preselectedUris + }).then((b4) => { + this.success(new SelectPhotoResult(b4.photoUris, b4.isOriginalPhoto), w3); + }).catch((a4) => { + this.error(a4, w3); + }); + } + + openPreview(n3) { + let o3 = this.getUri(n3.uri); + filePreview.canPreview(this.context, o3).then((s3) => { + if (!s3) { + this.errorWithCodeAndMsg(IMAGE_CAN_NOT_PREVIEW_ERROR, n3); + return; + } + filePreview.openPreview(this.context, { + uri: o3, + mimeType: n3.mimeType, + title: n3.title + }).then(() => { + this.success(new OpenPreviewResult(), n3); + }).catch((v3) => { + this.error(v3, n3); + }); + }).catch((r3) => { + this.error(r3, n3); + }); + } + + uploadFile(z2) { + this.checkUploadFile(z2).then(d3 => { + if (!d3.checkResult) { + return; + } + let e3 = { + url: z2.url, + header: z2.header, + method: z2.method, + files: this.convertToFile(z2.files), + data: this.convertToRequestData(z2.data) + }; + request.uploadFile(this.context, e3).then((i3) => { + i3.on('complete', (m3) => { + this.handleUploadFileResult(m3, d3.uriMap, z2); + }); + i3.on('fail', (l3) => { + this.handleUploadFileResult(l3, d3.uriMap, z2); + }); + }).catch((h3) => { + this.error(h3, z2); + }); + }).catch((c3) => { + this.error(c3, z2); + }); + } + + downloadFile(m2) { + let n2 = m2.fileName ? m2.fileName : this.parseFileNameFromUrl(m2.url); + let o2 = `${util.generateRandomUUID().replaceAll('-', '')}`; + let p2 = `${this.context.cacheDir}/${o2}`; + request.downloadFile(this.context, { + url: m2.url, + header: m2.header ? m2.header : new Object(), + filePath: p2, + enableMetered: m2.enableMetered, + enableRoaming: m2.enableRoaming, + networkType: m2.networkType, + background: false + }).then((t2) => { + t2.on('complete', () => { + this.saveDownloadFile(p2, n2, m2, y2 => { + this.success(new DownloadFileResult(y2), m2); + }); + }); + t2.on('fail', w2 => { + this.errorWithCodeAndMsg(new AsError(w2, 'File download fail.'), m2); + }); + }).catch((s2) => { + this.error(s2, m2); + }); + } + + getNetworkType(c2) { + connection.getDefaultNet().then(g2 => { + if (!g2 || g2.netId === 0) { + this.errorWithCodeAndMsg(NETWORK_NO_ACTIVE_ERROR, c2); + return; + } + connection.getNetCapabilities(g2).then(k2 => { + let l2 = new GetNetworkTypeResult(k2.bearerTypes, k2.networkCap, k2.linkUpBandwidthKbps, + k2.linkDownBandwidthKbps); + this.success(l2, c2); + }).catch((j2) => { + this.error(j2, c2); + }); + }).catch((f2) => { + this.error(f2, c2); + }); + } + + getLocation(u1) { + this.checkPermissions(PERMISSION_APPROXIMATELY_LOCATION, w1 => { + if (w1) { + this.error(w1, u1); + return; + } + geoLocationManager.getCurrentLocation({ + priority: u1.priority, + scenario: u1.scenario, + maxAccuracy: u1.maxAccuracy, + timeoutMs: u1.timeoutMs + }).then(a2 => { + let b2 = + new GetLocationResult(a2.latitude, a2.longitude, a2.altitude, a2.accuracy, a2.speed, a2.timeStamp, + a2.direction, a2.timeSinceBoot, a2.additions, a2.additionSize); + this.success(b2, u1); + }).catch((z1) => { + this.error(z1, u1); + }); + }); + } +} + +class NavPathInfo { + constructor(s1, t1) { + this.name = s1; + this.index = t1; + } +} + +class CheckUploadFileResult { + constructor(q1, r1) { + this.checkResult = q1; + this.uriMap = r1; + } +} + +class BaseOptions { +} + +class PushUrlOptions extends BaseOptions { +} + +class PushUrlResult { +} + +class ReplaceUrlOptions extends BaseOptions { +} + +class ReplaceUrlResult { +} + +class BackUrlOptions extends BaseOptions { +} + +class BackUrlResult { +} + +class ClearUrlOptions extends BaseOptions { +} + +class ClearUrlResult { +} + +class OnPopEvent { + constructor(n1, o1, p1) { + this.name = n1; + this.param = o1; + this.result = p1; + } +} + +class PushPathOptions extends BaseOptions { +} + +class PushPathResult { +} + +class ReplacePathOptions extends BaseOptions { +} + +class ReplacePathResult { +} + +class PopPathOptions extends BaseOptions { +} + +class PopPathResult { + constructor(k1, l1, m1) { + this.name = k1; + this.index = l1; + this.param = m1; + } +} + +class ClearPathOptions extends BaseOptions { +} + +class ClearPathResult { +} + +class PostMessageOptions extends BaseOptions { +} + +class PostMessageResult { +} + +export class OnMessageEvent { + constructor(j1) { + this.data = j1; + } +} + +export class OnErrorReceiveEvent { + constructor(h1, i1) { + this.request = h1; + this.error = i1; + } +} + +export class OnHttpErrorReceiveEvent { + constructor(f1, g1) { + this.request = f1; + this.response = g1; + } +} + +export class OnPageBeginEvent { + constructor(e1) { + this.url = e1; + } +} + +export class OnPageEndEvent { + constructor(d1) { + this.url = d1; + } +} + +export class WebHeader { + constructor(j3, p3) { + this.headerKey = j3; + this.headerValue = p3; + } +} + +class GetEnvOptions extends BaseOptions { +} + +class GetEnvResult { +} + +class CheckJsApiOptions extends BaseOptions { +} + +class CheckJsApiResult { + constructor(c1) { + this.checkResult = c1; + } +} + +class PickCameraOptions extends BaseOptions { +} + +class PickCameraResult { + constructor(z, a1, b1) { + this.resultCode = z; + this.resultUri = a1; + this.mediaType = b1; + } +} + +class SelectPhotoOptions extends BaseOptions { +} + +class SelectPhotoResult { + constructor(x, y) { + this.photoUris = x; + this.isOriginalPhoto = y; + } +} + +class OpenPreviewOptions extends BaseOptions { +} + +class OpenPreviewResult { +} + +class UploadFileOptions extends BaseOptions { +} + +class UploadFile { + constructor(t, u, v, w) { + this.filename = t; + this.name = u; + this.uri = v; + this.type = w; + } +} + +class UploadRequestData { +} + +class UploadFileResult { + constructor(s) { + this.taskStates = s; + } +} + +class UploadFileTaskState { + constructor(p, q, r) { + this.path = p; + this.responseCode = q; + this.message = r; + } +} + +class DownloadFileOptions extends BaseOptions { +} + +class DownloadFileResult { + constructor(o) { + this.uri = o; + } +} + +class GetNetworkTypeOptions extends BaseOptions { +} + +class GetNetworkTypeResult { + constructor(k, l, m, n) { + this.bearerTypes = k; + this.networkCap = l; + this.linkUpBandwidthKbps = m; + this.linkDownBandwidthKbps = n; + } +} + +class GetLocationOptions extends BaseOptions { +} + +class GetLocationResult { + constructor(a, b, c, d, e, f, g, h, i, j) { + this.latitude = a; + this.longitude = b; + this.altitude = c; + this.accuracy = d; + this.speed = e; + this.timeStamp = f; + this.direction = g; + this.timeSinceBoot = h; + this.additions = i; + this.additionSize = j; + } +} + +export default { + AtomicServiceWeb, + AtomicServiceWebController +} diff --git a/atomicserviceweb/source/atomicserviceweb.ets b/atomicserviceweb/source/atomicserviceweb.ets new file mode 100644 index 0000000..201d409 --- /dev/null +++ b/atomicserviceweb/source/atomicserviceweb.ets @@ -0,0 +1,1362 @@ +/* + * 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. + */ + +import web_webview from '@ohos.web.webview'; +import router from '@ohos.router'; +import deviceInfo from '@ohos.deviceInfo'; +import common from '@ohos.app.ability.common'; +import geoLocationManager from '@ohos.geoLocationManager'; +import bundleManager from '@ohos.bundle.bundleManager'; +import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl'; +import connection from '@ohos.net.connection'; +import request from '@ohos.request'; +import fs from '@ohos.file.fs'; +import util from '@ohos.util'; +import photoAccessHelper from '@ohos.file.photoAccessHelper'; +import { filePreview } from '@kit.PreviewKit'; +import fileUri from '@ohos.file.fileuri'; +import picker from '@ohos.multimedia.cameraPicker'; +import filePicker from '@ohos.file.picker'; +import { BusinessError } from '@ohos.base'; + +class AsError { + public code: number; + public message: string; + + constructor(code: number, message: string) { + this.code = code; + this.message = message; + } +} + +class JsApiConfig { + public apiName: string; + public minVersion: string; + public maxVersion: string; + public requiredFieldNames?: string[]; + + constructor(apiName: string, minVersion: string, maxVersion: string, requiredFieldNames?: string[]) { + this.apiName = apiName; + this.minVersion = minVersion; + this.maxVersion = maxVersion; + this.requiredFieldNames = requiredFieldNames; + } +} + +const LOG_ENABLE: boolean = true; +const LOG_PREFIX: string = '[AtomicServiceWebLog]'; +const UPLOAD_IMAGE_CACHE_DIR: string = '/cache/'; +const JAVA_SCRIPT_PROXY_OBJECT_NAME: string = 'atomicServiceProxy'; +const JAVA_SCRIPT_PROXY_API_NAME_LIST: string[] = ['invokeJsApi']; +const ATOMIC_SERVICE_JS_API_MAP = new Map(); +const registerJsApi = (apiNameAlias: string, apiName: string, minVersion: string, maxVersion: string, + requiredFieldNames: string[]): void => { + ATOMIC_SERVICE_JS_API_MAP.set(apiNameAlias, new JsApiConfig(apiName, minVersion, maxVersion, requiredFieldNames)); +}; +const MAX_VERSION = '99.99.99'; +const ATOMIC_SERVICE_JS_SDK_CURRENT_VERSION = '1.0.0'; +const PERMISSION_APPROXIMATELY_LOCATION: Permissions = 'ohos.permission.APPROXIMATELY_LOCATION'; + +const SYSTEM_INTERNAL_ERROR: AsError = new AsError(500, 'System internal error.'); +const JS_API_INVALID_INVOKE_ERROR: AsError = new AsError(200001, 'Invalid invoke.'); +const PARAM_REQUIRED_ERROR_CODE: number = 200002; +const PARAM_NUMBER_POSITIVE_ERROR_CODE: number = 200003; +const ROUTER_PARAM_MODE_INVALID_ERROR: AsError = new AsError(200004, 'Param mode is invalid.'); +const BACK_URL_NOT_EXIST_OR_OPENED_ERROR: AsError = new AsError(200005, 'Url is not exist or opened, can not be back.'); +const NAV_PATH_STACK_NOT_EXIST_ERROR_CODE: number = 200006; +const POP_PATH_NAME_NOT_EXIST_ERROR: AsError = new AsError(200007, 'Name is not exist or opened, can not be pop.'); +const POP_PATH_PARAM_INDEX_INVALID_ERROR: AsError = new AsError(200008, 'Param index is invalid.'); +const POP_PATH_INDEX_OUT_OF_RANGE_ERROR: AsError = new AsError(200009, 'The Index is out of range.'); +const UPLOAD_IMAGE_FILES_REQUIRED_ERROR: AsError = new AsError(200010, 'Param files is required.'); +const UPLOAD_IMAGE_FILE_NOT_EXIST_ERROR_CODE: number = 200011; +const UPLOAD_IMAGE_FILES_URI_REQUIRED_ERROR: AsError = new AsError(200012, 'Param uri of files is required.'); +const UPLOAD_FILE_ERROR: AsError = new AsError(200013, 'Upload file error.'); +const IMAGE_CAN_NOT_PREVIEW_ERROR: AsError = new AsError(200014, 'The filePath can not preview.'); +const NETWORK_NO_ACTIVE_ERROR: AsError = new AsError(200015, 'The network is not active.'); + +registerJsApi('router.pushUrl', 'pushUrl', '1.0.0', MAX_VERSION, ['url']); +registerJsApi('router.replaceUrl', 'replaceUrl', '1.0.0', MAX_VERSION, ['url']); +registerJsApi('router.back', 'backUrl', '1.0.0', MAX_VERSION, []); +registerJsApi('router.clear', 'clearUrl', '1.0.0', MAX_VERSION, []); +registerJsApi('navPathStack.pushPath', 'pushPath', '1.0.0', MAX_VERSION, ['name']); +registerJsApi('navPathStack.replacePath', 'replacePath', '1.0.0', MAX_VERSION, ['name']); +registerJsApi('navPathStack.pop', 'popPath', '1.0.0', MAX_VERSION, []); +registerJsApi('navPathStack.clear', 'clearPath', '1.0.0', MAX_VERSION, []); +registerJsApi('asWeb.postMessage', 'postMessage', '1.0.0', MAX_VERSION, ['data']); +registerJsApi('asWeb.getEnv', 'getEnv', '1.0.0', MAX_VERSION, []); +registerJsApi('asWeb.checkJsApi', 'checkJsApi', '1.0.0', MAX_VERSION, ['jsApiList']); +registerJsApi('cameraPicker.pick', 'pickCamera', '1.0.0', MAX_VERSION, ['mediaTypes', 'cameraPosition']); +registerJsApi('photoViewPicker.select', 'selectPhoto', '1.0.0', MAX_VERSION, []); +registerJsApi('filePreview.openPreview', 'openPreview', '1.0.0', MAX_VERSION, ['uri']); +registerJsApi('request.uploadFile', 'uploadFile', '1.0.0', MAX_VERSION, ['url', 'files']); +registerJsApi('request.downloadFile', 'downloadFile', '1.0.0', MAX_VERSION, ['url']); +registerJsApi('connection.getNetworkType', 'getNetworkType', '1.0.0', MAX_VERSION, []); +registerJsApi('location.getLocation', 'getLocation', '1.0.0', MAX_VERSION, []); + +@Component +export struct AtomicServiceWeb { + public src: ResourceStr | undefined = undefined; + public navPathStack?: NavPathStack; + @Prop mixedMode?: MixedMode; + @Prop darkMode?: WebDarkMode; + @Prop forceDarkAccess?: boolean; + @ObjectLink controller: AtomicServiceWebController; + public onMessage?: Callback = () => { + }; + public onErrorReceive?: Callback = () => { + }; + public onHttpErrorReceive?: Callback = () => { + }; + public onPageBegin?: Callback = () => { + }; + public onPageEnd?: Callback = () => { + }; + public onProgressChange?: Callback = () => { + }; + public onControllerAttached?: VoidCallback; + public onLoadIntercept?: Callback; + private context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext; + private webViewController: web_webview.WebviewController = new web_webview.WebviewController(); + private schemeHandler: web_webview.WebSchemeHandler = new web_webview.WebSchemeHandler(); + private atomicService?: AtomicService; + private atomicServiceProxy?: AtomicServiceProxy; + + aboutToAppear(): void { + if (!this.atomicService) { + this.atomicService = new AtomicServiceApi(this.context, this.navPathStack, this.onMessage); + this.atomicServiceProxy = new AtomicServiceProxy(this.atomicService); + } + + try { + let bundleInfo: bundleManager.BundleInfo = bundleManager.getBundleInfoForSelfSync( + bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); + if (bundleInfo?.appInfo?.appProvisionType === 'debug') { + console.log(`AtomicServiceWeb setWebDebuggingAccess`); + web_webview.WebviewController.setWebDebuggingAccess(true); + } + } catch (err) { + console.error(`AtomicServiceWeb set Web Debug Mode failed, code is ${err.code}, message is ${err.message}`); + } + } + + aboutToDisappear(): void { + this.atomicService?.notifyMessage(); + } + + build() { + Web({ src: this.src, controller: this.webViewController }) + .zoomAccess(false) + .allowWindowOpenMethod(false) + .domStorageAccess(true) + .layoutMode(WebLayoutMode.NONE) + .mixedMode(this.mixedMode) + .darkMode(this.darkMode) + .forceDarkAccess(this.forceDarkAccess) + .onErrorReceive((event: OnErrorReceiveEvent) => this.onCommonCallBack('onErrorReceive', event, + this.onErrorReceive)) + .onHttpErrorReceive((event: OnHttpErrorReceiveEvent) => this.onCommonCallBack('onHttpErrorReceive', event, + this.onHttpErrorReceive)) + .onPageBegin((event: OnPageBeginEvent) => this.onCommonCallBack('onPageBegin', event, this.onPageBegin)) + .onPageEnd((event: OnPageEndEvent) => this.onCommonCallBack('onPageEnd', event, this.onPageEnd)) + .onProgressChange((event: OnProgressChangeEvent) => this.onCommonCallBack('onProgressChange', event, + this.onProgressChange)) + .onControllerAttached(() => { + this.registerJavaScriptProxy(); + this.schemeHandler.onRequestStart((request: web_webview.WebSchemeHandlerRequest) => { + return !this.checkUrl(request.getRequestUrl()); + }); + this.webViewController.setWebSchemeHandler('https', this.schemeHandler); + this.initAtomicServiceWebController(); + if (this.onControllerAttached) { + try { + this.onControllerAttached(); + } catch (error) { + console.error(`AtomicServiceWeb onControllerAttached failed, code is ${error.code}, message is ${error.message}`); + } + } + }) + .onOverrideUrlLoading((webResourceRequest: WebResourceRequest) => { + return !this.checkUrl(webResourceRequest.getRequestUrl()); + }) + .onLoadIntercept(event => { + let checkResult = !this.checkUrl(event.data.getRequestUrl()); + if (!checkResult && this.onLoadIntercept) { + try { + return this.onLoadIntercept(event); + } catch (error) { + console.error(`AtomicServiceWeb onLoadIntercept failed, code is ${error.code}, message is ${error.message}`); + return true; + } + } + return checkResult + }) + } + + onCommonCallBack(method: string, event: T, callback?: (event: T) => void): void { + try { + callback && callback(event); + } catch (error) { + console.error(`AtomicServiceWeb ${method} failed, code is ${error.code}, message is ${error.message}`); + } + } + + registerJavaScriptProxy(): void { + try { + this.webViewController.registerJavaScriptProxy(this.atomicServiceProxy, JAVA_SCRIPT_PROXY_OBJECT_NAME, + JAVA_SCRIPT_PROXY_API_NAME_LIST); + } catch (error) { + let e: BusinessError = error as BusinessError; + console.error(`AtomicServiceWeb registerJavaScriptProxy failed, code is ${e.code}, message is ${e.message}`); + } + } + + initAtomicServiceWebController(): void { + if (!this.controller) { + return; + } + this.controller.setWebviewController(this.webViewController); + } + + cutUrl(url: string): string { + if (url) { + let index: number = url.indexOf('?'); + if (index > -1) { + return url.substring(0, index); + } + } + return url; + } + + checkUrl(url: string): boolean { + if (!url) { + return false; + } + if (url.startsWith('resource://rawfile')) { + return true; + } + url = this.cutUrl(url); + console.log(`AtomicServiceWebLog checkUrl url=${url}`); + return true; + } +} + +@Observed +export class AtomicServiceWebController { + private webViewController?: web_webview.WebviewController; + + setWebviewController(webViewController: web_webview.WebviewController): void { + this.webViewController = webViewController; + } + + checkWebviewController(): void { + if (!this.webViewController) { + const error: BusinessError = { + name: '', + message: 'Init error. The AtomicServiceWebController must be associated with a AtomicServiceWeb component.', + code: 17100001, + } + throw error as Error; + } + } + + getUserAgent(): string | undefined { + this.checkWebviewController(); + return this.webViewController?.getUserAgent(); + } + + getCustomUserAgent(): string | undefined { + this.checkWebviewController(); + return this.webViewController?.getCustomUserAgent(); + } + + setCustomUserAgent(userAgent: string): void { + this.checkWebviewController(); + this.webViewController?.setCustomUserAgent(userAgent); + } + + accessForward(): boolean | undefined { + this.checkWebviewController(); + return this.webViewController?.accessForward(); + } + + accessBackward(): boolean | undefined { + this.checkWebviewController(); + return this.webViewController?.accessBackward(); + } + + accessStep(step: number): boolean | undefined { + this.checkWebviewController(); + return this.webViewController?.accessStep(step); + } + + forward(): void { + this.checkWebviewController(); + this.webViewController?.forward(); + } + + backward(): void { + this.checkWebviewController(); + this.webViewController?.backward(); + } + + refresh(): void { + this.checkWebviewController(); + this.webViewController?.refresh(); + } + + loadUrl(url: string | Resource, headers?: Array): void { + this.checkWebviewController(); + if (headers) { + this.webViewController?.loadUrl(url, headers); + } else { + this.webViewController?.loadUrl(url); + } + } +} + +class AtomicServiceProxy { + private atomicService: AtomicService; + + constructor(atomicService: AtomicService) { + this.atomicService = atomicService; + } + + invokeJsApi(apiNameAlias: string, options: BaseOptions): void { + try { + options = options || {}; + if (!apiNameAlias || !ATOMIC_SERVICE_JS_API_MAP.has(apiNameAlias)) { + this.atomicService.errorWithCodeAndMsg(JS_API_INVALID_INVOKE_ERROR, options); + return; + } + let jsApiConfig: JsApiConfig | undefined = ATOMIC_SERVICE_JS_API_MAP.get(apiNameAlias); + if (!this.atomicService.checkRequiredFieldInOptions(jsApiConfig, options)) { + return; + } + let atomicService: object = this.atomicService; + atomicService[jsApiConfig?.apiName as string](options); + } catch (err) { + this.atomicService.error(err, options); + } + } +} + +class AtomicService { + protected context: common.UIAbilityContext; + protected navPathStack?: NavPathStack; + protected messageDataList: object[] = []; + protected onMessage: (event: OnMessageEvent) => void = () => { + }; + + constructor(context: common.UIAbilityContext, navPathStack?: NavPathStack, + onMessage?: (event: OnMessageEvent) => void) { + this.context = context; + this.navPathStack = navPathStack; + this.onMessage = onMessage ? onMessage : this.onMessage; + } + + success(res: T, options: BaseOptions): void { + try { + options?.callback && options?.callback(undefined, res); + } catch (err) { + this.consoleError(`callback error, code is ${err.code}, message is ${err.message}`); + } + } + + error(err: BusinessError, options: BaseOptions,): void { + try { + options?.callback && options?.callback(new AsError(err.code ? err.code : SYSTEM_INTERNAL_ERROR.code, + err.message ? err.message : SYSTEM_INTERNAL_ERROR.message)); + } catch (err) { + this.consoleError(`callback error, code is ${err.code}, message is ${err.message}`); + } + } + + errorWithCodeAndMsg(error: AsError, options: BaseOptions): void { + try { + options?.callback && options?.callback(error); + } catch (err) { + this.consoleError(`callback error, code is ${err.code}, message is ${err.message}`); + } + } + + consoleLog(msg: string): void { + if (LOG_ENABLE) { + console.log(`${LOG_PREFIX} ${msg}`); + } + } + + consoleError(msg: string): void { + if (LOG_ENABLE) { + console.error(`${LOG_PREFIX} ${msg}`); + } + } + + logOptions(name: string, options: BaseOptions): void { + this.consoleLog(`${name} options=${JSON.stringify(options)}`); + } + + checkParamRequired(paramKey: string, paramValue: V, options: BaseOptions): boolean { + if (paramValue === undefined || paramValue === null || paramValue === '') { + this.errorWithCodeAndMsg(new AsError(PARAM_REQUIRED_ERROR_CODE, `Param ${paramKey} is required.`), options); + return false; + } + return true; + } + + checkNumberParamPositive(paramKey: string, paramValue: number, options: BaseOptions): boolean { + if (paramValue <= 0) { + this.errorWithCodeAndMsg(new AsError(PARAM_NUMBER_POSITIVE_ERROR_CODE, + `Param ${paramKey} must be a positive number.`), options); + return false; + } + return true; + } + + checkRequiredFieldInOptions(jsApiConfig: JsApiConfig | undefined, options: BaseOptions): boolean { + if (!jsApiConfig) { + return false; + } + if (!jsApiConfig.requiredFieldNames) { + return true; + } + let obj: object = options; + for (let i = 0; i < jsApiConfig.requiredFieldNames.length; i++) { + let fieldName: string = jsApiConfig.requiredFieldNames[i]; + if (!this.checkParamRequired(fieldName, obj[fieldName], options)) { + return false; + } + } + return true; + } + + checkRouterMode(mode: string | undefined, options: BaseOptions): boolean { + if (!mode || mode === 'Single' || mode === 'Standard') { + return true; + } + this.errorWithCodeAndMsg(ROUTER_PARAM_MODE_INVALID_ERROR, options); + return false; + } + + parseRouterMode(routerMode?: string): router.RouterMode { + return routerMode === 'Single' ? router.RouterMode.Single : router.RouterMode.Standard; + } + + getRouterIndexByDelta(delta: number): number { + let length: number = Number.parseInt(router.getLength()); + for (let i = length; i > 0; i--) { + let state = router.getStateByIndex(i); + if (state?.name && delta-- == 0) { + return i; + } + } + return 1; + } + + checkBackUrlExists(url: string, options: BaseOptions): boolean { + let length: number = Number.parseInt(router.getLength()); + for (let i = length; i > 0; i--) { + let state = router.getStateByIndex(i); + if (state?.name) { + let stateUrl: string = state?.path + state?.name; + if (stateUrl === url) { + return true; + } + } + } + this.errorWithCodeAndMsg(BACK_URL_NOT_EXIST_OR_OPENED_ERROR, options); + return false; + } + + checkNavPathStack(apiName: string, options: BaseOptions): boolean { + if (!this.navPathStack) { + this.errorWithCodeAndMsg(new AsError(NAV_PATH_STACK_NOT_EXIST_ERROR_CODE, + `Current page is not NavDestination, not support ${apiName}().`), options); + return false; + } + return true; + } + + getNavPathIndexByDelta(delta: number): number { + let pathStack: string[] | undefined = this.navPathStack?.getAllPathName(); + if (!pathStack || pathStack.length == 0) { + return -1; + } + return pathStack.length > delta ? (pathStack.length - delta - 1) : -1; + } + + onPopHandler(popInfo: PopInfo, onPop?: (event: OnPopEvent) => void): void { + if (!popInfo?.info || !onPop) { + return; + } + onPop(new OnPopEvent(popInfo.info.name, popInfo.info.param as object, popInfo.result)); + } + + getCurrentNavPathInfo(): NavPathInfo { + let navPathStack: Array | undefined = this.navPathStack?.getAllPathName(); + let navPathInfo: NavPathInfo = (navPathStack && navPathStack.length > 0) ? + new NavPathInfo(navPathStack[navPathStack.length - 1], navPathStack.length - 1) : new NavPathInfo(undefined, -1); + if (navPathInfo.index >= 0) { + navPathInfo.param = this.navPathStack?.getParamByIndex(navPathInfo.index) as object; + } + return navPathInfo; + } + + notifyMessage(): void { + if (this.messageDataList.length <= 0) { + return; + } + try { + this.onMessage(new OnMessageEvent(this.messageDataList)); + } catch (err) { + this.consoleError(`onMessage failed, code is ${err.code}, message is ${err.message}`); + } + this.messageDataList = []; + } + + isJsApiEnable(jsApiConfig?: JsApiConfig): boolean { + if (!jsApiConfig) { + return false; + } + if (this.compareVersion(jsApiConfig.minVersion, ATOMIC_SERVICE_JS_SDK_CURRENT_VERSION) && + this.compareVersion(ATOMIC_SERVICE_JS_SDK_CURRENT_VERSION, jsApiConfig.maxVersion)) { + return true; + } + return false; + } + + compareVersion(lowVersion: string, highVersion: string): boolean { + if (!lowVersion || !highVersion) { + return false; + } + let v1 = lowVersion.split('.').map(m => Number.parseInt(m)); + let v2 = highVersion.split('.').map(m => Number.parseInt(m)); + const maxLength = Math.max(v1.length, v2.length); + for (let i = 0; i < maxLength; i++) { + if (v1[i] < v2[i]) { + return true; + } else if (v1[i] > v2[i]) { + return false; + } + } + if (v1.length < v2.length) { + return true; + } + if (v1.length > v2.length) { + return false; + } + return true; + } + + getUri(uriOrFilePath: string): string { + if (!uriOrFilePath || uriOrFilePath.startsWith('file://')) { + return uriOrFilePath; + } + return fileUri.getUriFromPath(uriOrFilePath); + } + + async checkUploadFile(options: UploadFileOptions): Promise { + if (!options.files || options.files.length <= 0) { + this.errorWithCodeAndMsg(UPLOAD_IMAGE_FILES_REQUIRED_ERROR, options); + return new CheckUploadFileResult(false); + } + let uriMap: Map = new Map(); + for (let i = 0; i < options.files?.length; i++) { + let file: UploadFile = options.files[i]; + if (!file.uri) { + this.errorWithCodeAndMsg(UPLOAD_IMAGE_FILES_URI_REQUIRED_ERROR, options); + return new CheckUploadFileResult(false); + } + if (!file.uri.startsWith('file://') && !fs.accessSync(file.uri, fs.AccessModeType.EXIST)) { + this.errorWithCodeAndMsg(new AsError(UPLOAD_IMAGE_FILE_NOT_EXIST_ERROR_CODE, + `File uri ${file.uri} is not exist.`), options); + return new CheckUploadFileResult(false); + } + let originUri: string = file.uri; + let uploadUri: string = file.uri; + if (uploadUri.indexOf(UPLOAD_IMAGE_CACHE_DIR) < 0) { + let srcUri: string = uploadUri.startsWith('file://') ? uploadUri : fileUri.getUriFromPath(file.uri); + uploadUri = this.context.cacheDir + '/' + uploadUri.substring(uploadUri.lastIndexOf('/') + 1); + try { + await fs.copy(srcUri, fileUri.getUriFromPath(uploadUri)) + } catch (err) { + this.errorWithCodeAndMsg(UPLOAD_FILE_ERROR, options); + return new CheckUploadFileResult(false); + } + } + file.uri = 'internal://' + uploadUri.substring(uploadUri.indexOf(UPLOAD_IMAGE_CACHE_DIR) + 1); + uriMap.set(uploadUri, originUri); + } + return new CheckUploadFileResult(true, uriMap); + } + + convertToRequestData(data?: UploadRequestData[]): request.RequestData[] { + let requestData: request.RequestData[] = []; + if (data) { + data.forEach(item => { + if (!item.name || !item.value) { + return; + } + requestData.push({ name: item.name, value: item.value }); + }); + } + return requestData; + } + + convertToFile(files?: UploadFile[]): request.File[] { + let requestFiles: request.File[] = []; + if (files) { + files.forEach(item => { + requestFiles.push({ + filename: item.filename, + name: item.name, + uri: item.uri, + type: item.type + }); + }); + } + return requestFiles; + } + + handleUploadFileResult(taskStateArray: Array, uriMap: Map, + options: UploadFileOptions): void { + let taskStates: UploadFileTaskState[] = []; + if (taskStateArray) { + taskStateArray.forEach(taskState => { + let path: (string | undefined) = taskState.path ? uriMap.get(taskState.path) : taskState.path; + taskStates.push(new UploadFileTaskState(path ? path : taskState.path, taskState.responseCode, + taskState.message)); + }); + } + this.success(new UploadFileResult(taskStates), options); + } + + parseFileNameFromUrl(url?: string): string { + if (!url) { + return ''; + } + let http: string = url.split('?')[0]; + if (http.indexOf('/') < 0) { + return ''; + } + let index: number = http.lastIndexOf('/'); + if (index == (http.length - 1)) { + return ''; + } + return http.substring(index + 1); + } + + saveDownloadFile(filePath: string, fileName: string, options: DownloadFileOptions, + callback: (uri: string) => void): void { + let documentPicker = new filePicker.DocumentViewPicker(); + documentPicker.save({ + newFileNames: [fileName] + }).then(res => { + let uri: string = res[0]; + fs.copy(fileUri.getUriFromPath(filePath), uri).then(() => { + callback && callback(uri); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + } + + checkAccessToken(permissionName: Permissions): Promise { + let bundleInfo: bundleManager.BundleInfo = bundleManager.getBundleInfoForSelfSync( + bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); + let tokenId: number = bundleInfo.appInfo.accessTokenId; + let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); + return atManager.checkAccessToken(tokenId, permissionName); + } + + checkPermissions(permissionName: Permissions, grantCallback: (err?: BusinessError) => void): void { + this.checkAccessToken(permissionName).then(grantStatus => { + if (grantStatus == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { + grantCallback(undefined); + } else { + let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); + atManager.requestPermissionsFromUser(this.context, [permissionName]).then(permissionRequestResult => { + for (let i = 0; i < permissionRequestResult.authResults.length; i++) { + if (permissionRequestResult.authResults[i] != 0) { + return; + } + } + grantCallback(undefined); + }).catch((err: BusinessError) => { + grantCallback(err); + }); + } + }).catch((err: BusinessError) => { + grantCallback(err); + }); + } +} + +class AtomicServiceApi extends AtomicService { + constructor(context: common.UIAbilityContext, navPathStack?: NavPathStack, + onMessage?: (event: OnMessageEvent) => void) { + super(context, navPathStack, onMessage); + } + + pushUrl(options: PushUrlOptions): void { + if (!this.checkRouterMode(options.mode, options)) { + return; + } + router.pushUrl({ url: options.url, params: options.params }, this.parseRouterMode(options.mode)).then(() => { + this.success(new PushUrlResult(), options); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + } + + replaceUrl(options: ReplaceUrlOptions): void { + if (!this.checkRouterMode(options.mode, options)) { + return; + } + router.replaceUrl({ url: options.url, params: options.params }, this.parseRouterMode(options.mode)).then(() => { + this.success(new ReplaceUrlResult(), options); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + } + + backUrl(options: BackUrlOptions): void { + if (options.url) { + if (!this.checkBackUrlExists(options.url, options)) { + return; + } + router.back({ url: options.url, params: options.params }); + this.success(new BackUrlResult(), options); + } else if (options.index || options.index === 0) { + if (!this.checkNumberParamPositive('index', options.index, options)) { + return; + } + router.back(options.index, options.params); + this.success(new BackUrlResult(), options); + } else if (options.delta || options.delta === 0) { + if (!this.checkNumberParamPositive('delta', options.delta, options)) { + return; + } + router.back(this.getRouterIndexByDelta(options.delta), options.params); + this.success(new BackUrlResult(), options); + } else { + router.back(); + this.success(new BackUrlResult(), options); + } + } + + clearUrl(options: ClearUrlOptions): void { + router.clear(); + this.success(new ClearUrlResult(), options); + } + + pushPath(options: PushPathOptions): void { + if (!this.checkNavPathStack('navPathStack.pushPath', options)) { + return; + } + this.navPathStack?.pushPath({ + name: options.name, + param: options.param, + onPop: popInfo => this.onPopHandler(popInfo, options.onPop) + }, options.animated); + this.success(new PushPathResult(), options); + } + + replacePath(options: ReplacePathOptions): void { + if (!this.checkNavPathStack('navPathStack.replacePath', options)) { + return; + } + this.navPathStack?.replacePath({ + name: options.name, + param: options.param, + onPop: popInfo => this.onPopHandler(popInfo, options.onPop) + }, options.animated); + this.success(new ReplacePathResult(), options); + } + + popPath(options: PopPathOptions): void { + if (!this.checkNavPathStack('navPathStack.pop', options)) { + return; + } + if (options.name) { + let index: number | undefined = this.navPathStack?.popToName(options.name, options.result, options.animated); + if (index === undefined || index === -1) { + this.errorWithCodeAndMsg(POP_PATH_NAME_NOT_EXIST_ERROR, options); + return; + } + } else if (options.index || options.index === 0) { + if (options.index < -1) { + this.errorWithCodeAndMsg(POP_PATH_PARAM_INDEX_INVALID_ERROR, options); + return; + } + if (options.index > this.getCurrentNavPathInfo().index) { + this.errorWithCodeAndMsg(POP_PATH_INDEX_OUT_OF_RANGE_ERROR, options); + return; + } + this.navPathStack?.popToIndex(options.index, options.result, options.animated); + } else if (options.delta || options.delta === 0) { + if (!this.checkNumberParamPositive('delta', options.delta, options)) { + return; + } + this.navPathStack?.popToIndex(this.getNavPathIndexByDelta(options.delta), options.result, options.animated); + } else { + this.navPathStack?.pop(options.result, options.animated); + } + let navPathInfo: NavPathInfo = this.getCurrentNavPathInfo(); + this.success(new PopPathResult(navPathInfo.name, navPathInfo.index, navPathInfo.param), options); + } + + clearPath(options: ClearPathOptions): void { + if (!this.checkNavPathStack('navPathStack.clear', options)) { + return; + } + this.navPathStack?.clear(options.animated); + this.success(new ClearPathResult(), options); + } + + postMessage(options: PostMessageOptions): void { + options.data && this.messageDataList.push(options.data); + this.success(new PostMessageResult(), options); + } + + getEnv(options: GetEnvOptions): void { + let res: GetEnvResult = new GetEnvResult(); + res.deviceType = deviceInfo.deviceType; + res.brand = deviceInfo.brand; + res.productModel = deviceInfo.productModel; + res.osFullName = deviceInfo.osFullName; + this.success(res, options); + } + + checkJsApi(options: CheckJsApiOptions): void { + let res: Map = new Map(); + options.jsApiList?.forEach(jsApi => { + res[jsApi] = this.isJsApiEnable(ATOMIC_SERVICE_JS_API_MAP.get(jsApi)); + }); + this.success(new CheckJsApiResult(res), options); + } + + pickCamera(options: PickCameraOptions): void { + picker.pick(this.context, options.mediaTypes as Array, { + cameraPosition: options.cameraPosition, + saveUri: options.saveUri, + videoDuration: options.videoDuration + }).then((pickerResult: picker.PickerResult) => { + this.success(new PickCameraResult(pickerResult.resultCode, pickerResult.resultUri, pickerResult.mediaType), + options); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + } + + selectPhoto(options: SelectPhotoOptions): void { + let photoViewPicker = new photoAccessHelper.PhotoViewPicker(); + photoViewPicker.select({ + MIMEType: options.mimeType as photoAccessHelper.PhotoViewMIMETypes, + maxSelectNumber: options.maxSelectNumber, + isPhotoTakingSupported: options.isPhotoTakingSupported, + isEditSupported: options.isEditSupported, + isSearchSupported: options.isSearchSupported, + recommendationOptions: { + recommendationType: options.recommendationType + }, + preselectedUris: options.preselectedUris + }).then((selectResult: photoAccessHelper.PhotoSelectResult) => { + this.success(new SelectPhotoResult(selectResult.photoUris, selectResult.isOriginalPhoto), options); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + } + + openPreview(options: OpenPreviewOptions): void { + let uri: string = this.getUri(options.uri as string); + filePreview.canPreview(this.context, uri).then((res: boolean) => { + if (!res) { + this.errorWithCodeAndMsg(IMAGE_CAN_NOT_PREVIEW_ERROR, options); + return; + } + filePreview.openPreview(this.context, { + uri: uri, + mimeType: options.mimeType as string, + title: options.title + }).then(() => { + this.success(new OpenPreviewResult(), options); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + } + + uploadFile(options: UploadFileOptions): void { + this.checkUploadFile(options).then(res => { + if (!res.checkResult) { + return; + } + let uploadConfig: request.UploadConfig = { + url: options.url as string, + header: options.header as object, + method: options.method as string, + files: this.convertToFile(options.files), + data: this.convertToRequestData(options.data) + }; + request.uploadFile(this.context, uploadConfig).then((uploadTask: request.UploadTask) => { + uploadTask.on('complete', (taskStateArray: Array) => { + this.handleUploadFileResult(taskStateArray, res.uriMap as Map, options); + }); + uploadTask.on('fail', (taskStateArray: Array) => { + this.handleUploadFileResult(taskStateArray, res.uriMap as Map, options); + }); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + } + + downloadFile(options: DownloadFileOptions): void { + let fileName: string = options.fileName ? options.fileName : this.parseFileNameFromUrl(options.url); + let cacheFileName: string = `${util.generateRandomUUID().replaceAll('-', '')}`; + let filePath: string = `${this.context.cacheDir}/${cacheFileName}`; + request.downloadFile(this.context, { + url: options.url, + header: options.header ? options.header : new Object(), + filePath: filePath, + enableMetered: options.enableMetered, + enableRoaming: options.enableRoaming, + networkType: options.networkType, + background: false + }).then((downloadTask: request.DownloadTask) => { + downloadTask.on('complete', () => { + this.saveDownloadFile(filePath, fileName, options, uri => { + this.success(new DownloadFileResult(uri), options); + }); + }); + downloadTask.on('fail', errCode => { + this.errorWithCodeAndMsg(new AsError(errCode, 'File download fail.'), options); + }); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + } + + getNetworkType(options: GetNetworkTypeOptions): void { + connection.getDefaultNet().then(netHandle => { + if (!netHandle || netHandle.netId === 0) { + this.errorWithCodeAndMsg(NETWORK_NO_ACTIVE_ERROR, options); + return; + } + connection.getNetCapabilities(netHandle).then(netCapabilities => { + let res: GetNetworkTypeResult = new GetNetworkTypeResult(netCapabilities.bearerTypes, + netCapabilities.networkCap, netCapabilities.linkUpBandwidthKbps, netCapabilities.linkDownBandwidthKbps); + this.success(res, options); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + } + + getLocation(options: GetLocationOptions): void { + this.checkPermissions(PERMISSION_APPROXIMATELY_LOCATION, err => { + if (err) { + this.error(err, options); + return; + } + geoLocationManager.getCurrentLocation({ + priority: options.priority, + scenario: options.scenario, + maxAccuracy: options.maxAccuracy, + timeoutMs: options.timeoutMs + }).then(location => { + let res: GetLocationResult = new GetLocationResult(location.latitude, location.longitude, location.altitude, + location.accuracy, location.speed, location.timeStamp, location.direction, location.timeSinceBoot, + location.additions, location.additionSize); + this.success(res, options); + }).catch((err: BusinessError) => { + this.error(err, options); + }); + }); + } +} + +class NavPathInfo { + public name: string | undefined; + public index: number; + public param?: object; + + constructor(name: string | undefined, index: number) { + this.name = name; + this.index = index; + } +} + +class CheckUploadFileResult { + public checkResult: boolean; + public uriMap?: Map; + + constructor(checkResult: boolean, uriMap?: Map) { + this.checkResult = checkResult; + this.uriMap = uriMap; + } +} + +class BaseOptions { + public callback?: (err: AsError | undefined, res?: T) => void; +} + +class PushUrlOptions extends BaseOptions { + public url?: string; + public params?: object; + public mode?: string; +} + +class PushUrlResult { +} + +class ReplaceUrlOptions extends BaseOptions { + public url?: string; + public params?: object; + public mode?: string; +} + +class ReplaceUrlResult { +} + +class BackUrlOptions extends BaseOptions { + public url?: string; + public index?: number; + public delta?: number; + public params?: object; +} + +class BackUrlResult { +} + +class ClearUrlOptions extends BaseOptions { +} + +class ClearUrlResult { +} + +class OnPopEvent { + public name?: string; + public param?: object; + public result?: object; + + constructor(name?: string, param?: object, result?: object) { + this.name = name; + this.param = param; + this.result = result; + } +} + +class PushPathOptions extends BaseOptions { + public name?: string; + public param?: object; + public animated?: boolean; + public onPop?: (event: OnPopEvent) => void; +} + +class PushPathResult { +} + +class ReplacePathOptions extends BaseOptions { + public name?: string; + public param?: object; + public animated?: boolean; + public onPop?: (event: OnPopEvent) => void; +} + +class ReplacePathResult { +} + +class PopPathOptions extends BaseOptions { + public name?: string; + public index?: number; + public delta?: number; + public result?: object; + public animated?: boolean; +} + +class PopPathResult { + public name: string | undefined; + public index: number; + public param?: object; + + constructor(name: string | undefined, index: number, param?: object) { + this.name = name; + this.index = index; + this.param = param; + } +} + +class ClearPathOptions extends BaseOptions { + public animated?: boolean; +} + +class ClearPathResult { +} + +class PostMessageOptions extends BaseOptions { + public data?: object; +} + +class PostMessageResult { +} + +export class OnMessageEvent { + public data: object[]; + + constructor(data: object[]) { + this.data = data; + } +} + +export class OnErrorReceiveEvent { + public request: WebResourceRequest; + public error: WebResourceError; + + constructor(request: WebResourceRequest, error: WebResourceError) { + this.request = request; + this.error = error; + } +} + +export class OnHttpErrorReceiveEvent { + public request: WebResourceRequest; + public response: WebResourceResponse; + + constructor(request: WebResourceRequest, response: WebResourceResponse) { + this.request = request; + this.response = response; + } +} + +export class OnPageBeginEvent { + public url: string; + + constructor(url: string) { + this.url = url; + } +} + +export class OnPageEndEvent { + public url: string; + + constructor(url: string) { + this.url = url; + } +} + +export class WebHeader { + public headerKey: string; + public headerValue: string; + + constructor(headerKey: string, headerValue: string) { + this.headerKey = headerKey; + this.headerValue = headerValue; + } +} + +class GetEnvOptions extends BaseOptions { +} + +class GetEnvResult { + public deviceType?: string; + public brand?: string; + public productModel?: string; + public osFullName?: string; +} + +class CheckJsApiOptions extends BaseOptions { + public jsApiList?: string[]; +} + +class CheckJsApiResult { + public checkResult?: Map; + + constructor(checkResult?: Map) { + this.checkResult = checkResult; + } +} + +class PickCameraOptions extends BaseOptions { + public mediaTypes?: string[]; + public cameraPosition?: number; + public saveUri?: string; + public videoDuration?: number; +} + +class PickCameraResult { + public resultCode?: number; + public resultUri?: string; + public mediaType?: string; + + constructor(resultCode?: number, resultUri?: string, mediaType?: string) { + this.resultCode = resultCode; + this.resultUri = resultUri; + this.mediaType = mediaType; + } +} + +class SelectPhotoOptions extends BaseOptions { + public mimeType?: string; + public maxSelectNumber?: number; + public isPhotoTakingSupported?: boolean; + public isEditSupported?: boolean; + public isSearchSupported?: boolean; + public recommendationType?: number; + public preselectedUris?: string[]; +} + +class SelectPhotoResult { + public photoUris?: string[]; + public isOriginalPhoto?: boolean; + + constructor(photoUris?: string[], isOriginalPhoto?: boolean) { + this.photoUris = photoUris; + this.isOriginalPhoto = isOriginalPhoto; + } +} + +class OpenPreviewOptions extends BaseOptions { + public title?: string; + public uri?: string; + public mimeType?: string; +} + +class OpenPreviewResult { +} + +class UploadFileOptions extends BaseOptions { + public url?: string; + public header?: object; + public method?: string; + public files?: UploadFile[]; + public data?: UploadRequestData[]; +} + +class UploadFile { + public filename: string; + public name: string; + public uri: string; + public type: string; + + constructor(filename: string, name: string, uri: string, type: string) { + this.filename = filename; + this.name = name; + this.uri = uri; + this.type = type; + } +} + +class UploadRequestData { + public name?: string; + public value?: string; +} + +class UploadFileResult { + public taskStates?: UploadFileTaskState[]; + + constructor(taskStates?: UploadFileTaskState[]) { + this.taskStates = taskStates; + } +} + +class UploadFileTaskState { + public path?: string; + public responseCode?: number; + public message?: string; + + constructor(path?: string, responseCode?: number, message?: string) { + this.path = path; + this.responseCode = responseCode; + this.message = message; + } +} + +class DownloadFileOptions extends BaseOptions { + public url?: string; + public header?: object; + public fileName?: string; + public enableMetered?: boolean; + public enableRoaming?: boolean; + public networkType?: number; +} + +class DownloadFileResult { + public uri?: string; + + constructor(uri?: string) { + this.uri = uri; + } +} + +class GetNetworkTypeOptions extends BaseOptions { +} + +class GetNetworkTypeResult { + public bearerTypes: number[]; + public networkCap?: number[]; + public linkUpBandwidthKbps?: number; + public linkDownBandwidthKbps?: number; + + constructor(bearerTypes: number[], networkCap?: number[], linkUpBandwidthKbps?: number, + linkDownBandwidthKbps?: number) { + this.bearerTypes = bearerTypes; + this.networkCap = networkCap; + this.linkUpBandwidthKbps = linkUpBandwidthKbps; + this.linkDownBandwidthKbps = linkDownBandwidthKbps; + } +} + +class GetLocationOptions extends BaseOptions { + public priority?: number; + public scenario?: number; + public maxAccuracy?: number; + public timeoutMs?: number; +} + +class GetLocationResult { + public latitude: number; + public longitude: number; + public altitude: number; + public accuracy: number; + public speed: number; + public timeStamp: number; + public direction: number; + public timeSinceBoot: number; + public additions?: string[] | undefined; + public additionSize?: number; + + constructor(latitude: number, longitude: number, altitude: number, accuracy: number, speed: number, + timeStamp: number, direction: number, timeSinceBoot: number, additions?: string[], additionSize?: number) { + this.latitude = latitude; + this.longitude = longitude; + this.altitude = altitude; + this.accuracy = accuracy; + this.speed = speed; + this.timeStamp = timeStamp; + this.direction = direction; + this.timeSinceBoot = timeSinceBoot; + this.additions = additions; + this.additionSize = additionSize; + } +} diff --git a/bundle.json b/bundle.json index b9a2d95..2f1102e 100644 --- a/bundle.json +++ b/bundle.json @@ -1,5 +1,5 @@ { - "name": "@ohos/advanced_ui_component", + "name": "@ohos/as_advanced_ui_component", "version": "1.0.0", "description": "as_advanced_ui_component", "publishAs": "code-segment", @@ -25,7 +25,12 @@ }, "build": { "sub_component": [ - "//foundation/arkui/arkui_advanced_ui_component/atomicservicetabs/interfaces:atomicservicetabs" + "//foundation/arkui/arkui_advanced_ui_component/atomicservicenavigation/interfaces:atomicservicenavigation", + "//foundation/arkui/arkui_advanced_ui_component/atomicservicetabs/interfaces:atomicservicetabs", + "//foundation/arkui/arkui_advanced_ui_component/atomicserviceweb/interfaces:atomicserviceweb", + "//foundation/arkui/arkui_advanced_ui_component/innerfullscreenlaunchcomponent/interfaces:innerfullscreenlaunchcomponent", + "//foundation/arkui/arkui_advanced_ui_component/interstitialdialogaction/interfaces:interstitialdialogaction", + "//foundation/arkui/arkui_advanced_ui_component/navpushpathhelper/interfaces:navpushpathhelper" ], "inner_kits": [], "test": [] diff --git a/innerfullscreenlaunchcomponent/interfaces/BUILD.gn b/innerfullscreenlaunchcomponent/interfaces/BUILD.gn new file mode 100644 index 0000000..4a79668 --- /dev/null +++ b/innerfullscreenlaunchcomponent/interfaces/BUILD.gn @@ -0,0 +1,61 @@ +# 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. + +import("//build/config/components/ets_frontend/es2abc_config.gni") +import("//build/ohos.gni") +import("//foundation/arkui/ace_engine/ace_config.gni") +import("//foundation/arkui/ace_engine/adapter/preview/build/config.gni") +import("//foundation/arkui/ace_engine/build/ace_gen_obj.gni") + +es2abc_gen_abc("gen_innerfullscreenlaunchcomponent_abc") { + src_js = rebase_path("innerfullscreenlaunchcomponent.js") + dst_file = rebase_path(target_out_dir + "/innerfullscreenlaunchcomponent.abc") + in_puts = [ "innerfullscreenlaunchcomponent.js" ] + out_puts = [ target_out_dir + "/innerfullscreenlaunchcomponent.abc" ] + extra_args = [ "--module" ] +} + +gen_js_obj("innerfullscreenlaunchcomponent_abc") { + input = + get_label_info(":gen_innerfullscreenlaunchcomponent_abc", + "target_out_dir") + "/innerfullscreenlaunchcomponent.abc" + output = target_out_dir + "/innerfullscreenlaunchcomponent_abc.o" + dep = ":gen_innerfullscreenlaunchcomponent_abc" +} + +gen_obj("innerfullscreenlaunchcomponent_abc_preview") { + input = + get_label_info(":gen_innerfullscreenlaunchcomponent_abc", + "target_out_dir") + "/innerfullscreenlaunchcomponent.abc" + output = target_out_dir + "/innerfullscreenlaunchcomponent_abc.c" + snapshot_dep = [ ":gen_innerfullscreenlaunchcomponent_abc" ] +} + +ohos_shared_library("innerfullscreenlaunchcomponent") { + sources = [ "innerfullscreenlaunchcomponent.cpp" ] + + if (use_mingw_win || use_mac || use_linux) { + deps = [ ":gen_obj_src_innerfullscreenlaunchcomponent_abc_preview" ] + } else { + deps = [ ":innerfullscreenlaunchcomponent_abc" ] + } + + external_deps = [ + "hilog:libhilog", + "napi:ace_napi", + ] + + relative_install_dir = "module/arkui/advanced" + subsystem_name = "arkui" + part_name = "as_advanced_ui_component" +} diff --git a/innerfullscreenlaunchcomponent/interfaces/innerfullscreenlaunchcomponent.cpp b/innerfullscreenlaunchcomponent/interfaces/innerfullscreenlaunchcomponent.cpp new file mode 100644 index 0000000..07cab76 --- /dev/null +++ b/innerfullscreenlaunchcomponent/interfaces/innerfullscreenlaunchcomponent.cpp @@ -0,0 +1,52 @@ +/* + * 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 "napi/native_node_api.h" + +extern const char _binary_innerfullscreenlaunchcomponent_abc_start[]; +extern const char _binary_innerfullscreenlaunchcomponent_abc_end[]; + +// Napi get abc code function +extern "C" __attribute__((visibility("default"))) void NAPI_arkui_advanced_InnerFullScreenLaunchComponent_GetABCCode( + const char** buf, int* buflen) +{ + if (buf != nullptr) { + *buf = _binary_innerfullscreenlaunchcomponent_abc_start; + } + if (buflen != nullptr) { + *buflen = _binary_innerfullscreenlaunchcomponent_abc_end - + _binary_innerfullscreenlaunchcomponent_abc_start; + } +} + +/* + * Module define + */ +static napi_module InnerFullScreenLaunchComponentModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_modname = "arkui.advanced.InnerFullScreenLaunchComponent", + .nm_priv = ((void*)0), + .reserved = { 0 }, +}; + +/* + * Module registerfunction + */ +extern "C" __attribute__((constructor)) void InnerFullScreenLaunchComponentRegisterModule(void) +{ + napi_module_register(&InnerFullScreenLaunchComponentModule); +} \ No newline at end of file diff --git a/innerfullscreenlaunchcomponent/interfaces/innerfullscreenlaunchcomponent.js b/innerfullscreenlaunchcomponent/interfaces/innerfullscreenlaunchcomponent.js new file mode 100644 index 0000000..107564d --- /dev/null +++ b/innerfullscreenlaunchcomponent/interfaces/innerfullscreenlaunchcomponent.js @@ -0,0 +1,214 @@ +/* + * 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. + */ + +if (!("finalizeConstruction" in ViewPU.prototype)) { + Reflect.set(ViewPU.prototype, "finalizeConstruction", () => { }); +} +const hilog = requireNapi('hilog'); +const abilityManager = requireNapi('app.ability.abilityManager'); +const commonEventManager = requireNapi('commonEventManager'); +export class LaunchController { + constructor() { + this.launchAtomicService = (n1, o1) => { }; + } +} + +const EMBEDDED_FULL_MODE = 1; +export class InnerFullScreenLaunchComponent extends ViewPU { + constructor(d1, e1, f1, g1 = -1, h1 = undefined, i1) { + super(d1, f1, g1, i1); + if (typeof h1 === "function") { + this.paramsGenerator_ = h1; + } + this.content = this.doNothingBuilder; + this.context = getContext(this); + this.controller = new LaunchController(); + this.appId = ''; + this.options = undefined; + this.__isShow = new ObservedPropertySimplePU(false, this, "isShow"); + this.subscriber = null; + this.launchAtomicService = (k1, l1) => { + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', 'launchAtomicService, appId: %{public}s.', k1); + this.appId = k1; + this.options = l1; + this.checkAbility(); + }; + this.setInitiallyProvidedValue(e1); + this.finalizeConstruction(); + } + setInitiallyProvidedValue(c1) { + if (c1.content !== undefined) { + this.content = c1.content; + } + if (c1.context !== undefined) { + this.context = c1.context; + } + if (c1.controller !== undefined) { + this.controller = c1.controller; + } + if (c1.appId !== undefined) { + this.appId = c1.appId; + } + if (c1.options !== undefined) { + this.options = c1.options; + } + if (c1.isShow !== undefined) { + this.isShow = c1.isShow; + } + if (c1.subscriber !== undefined) { + this.subscriber = c1.subscriber; + } + if (c1.launchAtomicService !== undefined) { + this.launchAtomicService = c1.launchAtomicService; + } + } + updateStateVars(b1) { + } + purgeVariableDependenciesOnElmtId(a1) { + this.__isShow.purgeDependencyOnElmtId(a1); + } + aboutToBeDeleted() { + this.__isShow.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + get isShow() { + return this.__isShow.get(); + } + set isShow(z) { + this.__isShow.set(z); + } + aboutToAppear() { + let s = { + events: [commonEventManager.Support.COMMON_EVENT_DISTRIBUTED_ACCOUNT_LOGOUT], + }; + commonEventManager.createSubscriber(s, (u, v) => { + if (u) { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', 'Failed to create subscriber, err: %{public}s.', u.message); + return; + } + if (v == null || v == undefined) { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', 'Failed to create subscriber, data is null.'); + return; + } + this.subscriber = v; + commonEventManager.subscribe(this.subscriber, (x, y) => { + if (x) { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', 'Failed to subscribe common event, err: %{public}s.', x.message); + return; + } + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', 'Received account logout event.'); + this.isShow = false; + }); + }); + this.controller.launchAtomicService = this.launchAtomicService; + } + aboutToDisappear() { + if (this.subscriber !== null) { + commonEventManager.unsubscribe(this.subscriber, (r) => { + if (r) { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', 'UnsubscribeCallBack, err: %{public}s.', r.message); + } + else { + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', 'Unsubscribe.'); + this.subscriber = null; + } + }); + } + } + doNothingBuilder(p = null) { + } + resetOptions() { + if (this.options?.parameters) { + this.options.parameters['ohos.extra.param.key.showMode'] = EMBEDDED_FULL_MODE; + this.options.parameters['ability.want.params.IsNotifyOccupiedAreaChange'] = true; + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', 'replaced options is %{public}s !', JSON.stringify(this.options)); + } + else { + this.options = { + parameters: { + 'ohos.extra.param.key.showMode': EMBEDDED_FULL_MODE, + 'ability.want.params.IsNotifyOccupiedAreaChange': true, + } + }; + } + } + async checkAbility() { + this.resetOptions(); + try { + const o = await abilityManager.isEmbeddedOpenAllowed(this.context, this.appId); + if (o) { + this.isShow = true; + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', ' EmbeddedOpen is Allowed!'); + } + else { + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', ' EmbeddedOpen is not Allowed!'); + this.popUp(); + } + } + catch (n) { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', 'isEmbeddedOpenAllowed called error!%{public}s', n.message); + } + } + async popUp() { + this.isShow = false; + try { + const m = await this.context.openAtomicService(this.appId, this.options); + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', '%{public}s open service success!', m.want); + } + catch (l) { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', '%{public}s open service error!', l.message); + } + } + initialRender() { + this.observeComponentCreation2((i, j) => { + Row.create(); + Row.justifyContent(FlexAlign.Center); + Row.bindContentCover({ value: this.isShow, changeEvent: k => { this.isShow = k; } }, { builder: () => { + this.uiExtensionBuilder.call(this); + } }, { modalTransition: ModalTransition.DEFAULT }); + }, Row); + this.content.bind(this)(this); + Row.pop(); + } + uiExtensionBuilder(a = null) { + this.observeComponentCreation2((c, d) => { + UIExtensionComponent.create({ + bundleName: `com.atomicservice.${this.appId}`, + flags: this.options?.flags, + parameters: this.options?.parameters + }); + UIExtensionComponent.height('100%'); + UIExtensionComponent.width('100%'); + UIExtensionComponent.onRelease(() => { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', 'onRelease'); + this.isShow = false; + }); + UIExtensionComponent.onError(g => { + this.isShow = false; + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', 'call up UIExtension error!%{public}s', g.message); + this.getUIContext().showAlertDialog({ + message: g.message + }); + }); + }, UIExtensionComponent); + } + rerender() { + this.updateDirtyElements(); + } +} + + +export default { InnerFullScreenLaunchComponent, LaunchController}; \ No newline at end of file diff --git a/innerfullscreenlaunchcomponent/source/innerfullscreenlaunchcomponent.ets b/innerfullscreenlaunchcomponent/source/innerfullscreenlaunchcomponent.ets new file mode 100644 index 0000000..ad3567d --- /dev/null +++ b/innerfullscreenlaunchcomponent/source/innerfullscreenlaunchcomponent.ets @@ -0,0 +1,171 @@ +/* + * 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. + */ +import hilog from '@ohos.hilog'; +import abilityManager from '@ohos.app.ability.abilityManager'; +import common from '@ohos.app.ability.common'; +import { Callback } from '@ohos.base'; +import AtomicServiceOptions from '@ohos.app.ability.AtomicServiceOptions'; +import commonEventManager from '@ohos.commonEventManager'; +import Base from '@ohos.base'; + +export class LaunchController { + public launchAtomicService = (appId: string, options?: AtomicServiceOptions) => {}; +} + +const EMBEDDED_FULL_MODE: number = 1; + +@Component +export struct InnerFullScreenLaunchComponent { + @BuilderParam content: Callback = this.doNothingBuilder; + private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; + controller: LaunchController = new LaunchController(); + private appId: string = ''; + private options?: AtomicServiceOptions; + @State private isShow: boolean = false; + private subscriber: commonEventManager.CommonEventSubscriber | null = null; + private launchAtomicService = (appId: string, options?: AtomicServiceOptions) => { + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', + 'launchAtomicService, appId: %{public}s.', appId); + this.appId = appId; + this.options = options; + this.checkAbility(); + } + + aboutToAppear() { + let subscribeInfo: commonEventManager.CommonEventSubscribeInfo = { + events: [commonEventManager.Support.COMMON_EVENT_DISTRIBUTED_ACCOUNT_LOGOUT], + }; + + commonEventManager.createSubscriber(subscribeInfo, + (err:Base.BusinessError, data: commonEventManager.CommonEventSubscriber) => { + if (err) { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', + 'Failed to create subscriber, err: %{public}s.', err.message); + return; + } + + if (data == null || data == undefined) { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', 'Failed to create subscriber, data is null.'); + return; + } + + this.subscriber = data; + commonEventManager.subscribe(this.subscriber, + (err: Base.BusinessError, data: commonEventManager.CommonEventData) => { + if (err) { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', + 'Failed to subscribe common event, err: %{public}s.', err.message); + return; + } + + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', 'Received account logout event.'); + this.isShow = false; + }) + }) + this.controller.launchAtomicService = this.launchAtomicService; + } + + aboutToDisappear() { + if (this.subscriber !== null) { + commonEventManager.unsubscribe(this.subscriber, (err) => { + if (err) { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', + 'UnsubscribeCallBack, err: %{public}s.', err.message); + } else { + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', 'Unsubscribe.'); + this.subscriber = null; + } + }) + } + } + + @Builder + doNothingBuilder() { + }; + + resetOptions() { + if (this.options?.parameters) { + this.options.parameters['ohos.extra.param.key.showMode'] = EMBEDDED_FULL_MODE; + this.options.parameters['ability.want.params.IsNotifyOccupiedAreaChange'] = true; + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', 'replaced options is %{public}s !', JSON.stringify(this.options)); + } else { + this.options = { + parameters: { + 'ohos.extra.param.key.showMode': EMBEDDED_FULL_MODE, + 'ability.want.params.IsNotifyOccupiedAreaChange': true, + } + }; + } + } + + async checkAbility() { + this.resetOptions(); + try { + const res: boolean = await abilityManager.isEmbeddedOpenAllowed(this.context, this.appId); + if (res) { + this.isShow = true; + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', ' EmbeddedOpen is Allowed!'); + } else { + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', ' EmbeddedOpen is not Allowed!'); + this.popUp(); + } + } catch (e) { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', 'isEmbeddedOpenAllowed called error!%{public}s', e.message); + } + } + + async popUp() { + this.isShow = false; + try { + const ability = await this.context.openAtomicService(this.appId, this.options); + hilog.info(0x3900, 'InnerFullScreenLaunchComponent', '%{public}s open service success!', ability.want); + } catch (e) { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', '%{public}s open service error!', e.message); + } + } + + build() { + Row() { + this.content(); + } + .justifyContent(FlexAlign.Center) + .bindContentCover($$this.isShow, this.uiExtensionBuilder()) + } + + @Builder + uiExtensionBuilder() { + UIExtensionComponent({ + bundleName: `com.atomicservice.${this.appId}`, + flags: this.options?.flags, + parameters: this.options?.parameters + }) + .height('100%') + .width('100%') + .onRelease( + () => { + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', 'onRelease'); + this.isShow = false; + } + ).onError( + err => { + this.isShow = false; + hilog.error(0x3900, 'InnerFullScreenLaunchComponent', 'call up UIExtension error! %{public}s', err.message); + this.getUIContext().showAlertDialog({ + message: err.message + }); + } + ) + } +} \ No newline at end of file diff --git a/interstitialdialogaction/interfaces/BUILD.gn b/interstitialdialogaction/interfaces/BUILD.gn new file mode 100644 index 0000000..10f84ab --- /dev/null +++ b/interstitialdialogaction/interfaces/BUILD.gn @@ -0,0 +1,59 @@ +# 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. + +import("//build/config/components/ets_frontend/es2abc_config.gni") +import("//build/ohos.gni") +import("//foundation/arkui/ace_engine/ace_config.gni") +import("//foundation/arkui/ace_engine/adapter/preview/build/config.gni") +import("//foundation/arkui/ace_engine/build/ace_gen_obj.gni") + +es2abc_gen_abc("gen_interstitialdialogaction_abc") { + src_js = rebase_path("interstitialdialogaction.js") + dst_file = rebase_path(target_out_dir + "/interstitialdialogaction.abc") + in_puts = [ "interstitialdialogaction.js" ] + out_puts = [ target_out_dir + "/interstitialdialogaction.abc" ] + extra_args = [ "--module" ] +} + +gen_js_obj("interstitialdialogaction_abc") { + input = get_label_info(":gen_interstitialdialogaction_abc", + "target_out_dir") + "/interstitialdialogaction.abc" + output = target_out_dir + "/interstitialdialogaction_abc.o" + dep = ":gen_interstitialdialogaction_abc" +} + +gen_obj("interstitialdialogaction_abc_preview") { + input = get_label_info(":gen_interstitialdialogaction_abc", + "target_out_dir") + "/interstitialdialogaction.abc" + output = target_out_dir + "/interstitialdialogaction_abc.c" + snapshot_dep = [ ":gen_interstitialdialogaction_abc" ] +} + +ohos_shared_library("interstitialdialogaction") { + sources = [ "interstitialdialogaction.cpp" ] + + if (use_mingw_win || use_mac || use_linux) { + deps = [ ":gen_obj_src_interstitialdialogaction_abc_preview" ] + } else { + deps = [ ":interstitialdialogaction_abc" ] + } + + external_deps = [ + "hilog:libhilog", + "napi:ace_napi", + ] + + relative_install_dir = "module/atomicservice" + subsystem_name = "arkui" + part_name = "as_advanced_ui_component" +} diff --git a/interstitialdialogaction/interfaces/interstitialdialogaction.cpp b/interstitialdialogaction/interfaces/interstitialdialogaction.cpp new file mode 100644 index 0000000..5b65be0 --- /dev/null +++ b/interstitialdialogaction/interfaces/interstitialdialogaction.cpp @@ -0,0 +1,51 @@ +/* + * 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 "napi/native_node_api.h" + +extern const char _binary_interstitialdialogaction_abc_start[]; +extern const char _binary_interstitialdialogaction_abc_end[]; + +// Napi get abc code function +extern "C" __attribute__((visibility("default"))) +void NAPI_atomicservice_InterstitialDialogAction_GetABCCode(const char **buf, int *buflen) +{ + if (buf != nullptr) { + *buf = _binary_interstitialdialogaction_abc_start; + } + if (buflen != nullptr) { + *buflen = _binary_interstitialdialogaction_abc_end - _binary_interstitialdialogaction_abc_start; + } +} + +/* + * Module define + */ +static napi_module InterstitialDialogActionModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_modname = "atomicservice.InterstitialDialogAction", + .nm_priv = ((void*)0), + .reserved = { 0 }, +}; + +/* + * Module registerfunction + */ +extern "C" __attribute__((constructor)) void InterstitialDialogActionRegisterModule(void) +{ + napi_module_register(&InterstitialDialogActionModule); +} \ No newline at end of file diff --git a/interstitialdialogaction/interfaces/interstitialdialogaction.js b/interstitialdialogaction/interfaces/interstitialdialogaction.js new file mode 100644 index 0000000..61bcff3 --- /dev/null +++ b/interstitialdialogaction/interfaces/interstitialdialogaction.js @@ -0,0 +1,267 @@ +/* + * 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. + */ +const ComponentContent = requireNapi("arkui.node").ComponentContent; +const curves = requireNativeModule('ohos.curves'); +const DIALOG_BORDER_RADIUS = { "id": -1, "type": 10002, params: ['sys.float.ohos_id_corner_radius_default_m'], "bundleName": "__harDefaultBundleName__", "moduleName": "__harDefaultModuleName__" }; +const DIALOG_INNER_PADDING_SIZE = 16; +const DIALOG_MAX_WIDTH = 480; +const DIALOG_OFFSET_X = 0; +const DIALOG_OFFSET_Y_FOR_BAR = -88; +const DIALOG_OFFSET_Y_FOR_NONE = -44; +const STANDARD_MIN_COMPONENT_HEIGHT = 82; +const STANDARD_MAX_COMPONENT_HEIGHT = 94; +const DIALOG_SHADOW_RADIUS = 16; +const DIALOG_SHADOW_OFFSET_Y = 10; +const DIALOG_SHADOW_COLOR = '#19000000'; +const TITLE_LINE_DISTANCE = 2; +const TITLE_MAX_LINE = 2; +const SUBTITLE_MAX_LINE = 1; +const TEXT_LEFT_MARGIN_SIZE = 16; +const SUBTITLE_DEFAULT_COLOR = { "id": -1, "type": 10001, params: ['sys.color.ohos_id_color_text_secondary_contrary'], "bundleName": "__harDefaultBundleName__", "moduleName": "__harDefaultModuleName__" }; +const TITLE_DEFAULT_COLOR = { "id": -1, "type": 10001, params: ['sys.color.ohos_id_color_text_primary_contrary'], "bundleName": "__harDefaultBundleName__", "moduleName": "__harDefaultModuleName__" }; +const OPERATE_AREA_AVOID_WIDTH = 28; +const CLOSE_ICON_DARK_RESOURCE = { "id": -1, "type": 10001, params: ['sys.color.ohos_id_color_tertiary'], "bundleName": "__harDefaultBundleName__", "moduleName": "__harDefaultModuleName__" }; +const CLOSE_ICON_LIGHT_RESOURCE = { "id": -1, "type": 10001, params: ['sys.color.ohos_id_color_primary_contrary'], "bundleName": "__harDefaultBundleName__", "moduleName": "__harDefaultModuleName__" }; +const CLOSE_BUTTON_BORDER_RADIUS = 8; +const CLOSE_BUTTON_ICON_SIZE = 16; +const CLOSE_BUTTON_HOT_SPOT_SIZE = 32; +const CLOSE_BUTTON_MARGIN = 8; +const CLOSE_BUTTON_ICON_OPACITY = 0.6; +const CLOSE_BUTTON_RESPONSE_REGION_OFFSET_X = -8; +const CLOSE_BUTTON_RESPONSE_REGION_OFFSET_Y = -8; +const CLOSE_BUTTON_OFFSET_X = 0; +const CLOSE_BUTTON_OFFSET_Y = -50; +const FOREGROUND_IMAGE_OFFSET_X = 4; +const FOREGROUND_IMAGE_OFFSET_Y = 0; +export var IconStyle; +(function (e2) { + e2[e2["DARK"] = 0] = "DARK"; + e2[e2["LIGHT"] = 1] = "LIGHT"; +})(IconStyle || (IconStyle = {})); +export var TitlePosition; +(function (d2) { + d2[d2["TOP"] = 0] = "TOP"; + d2[d2["BOTTOM"] = 1] = "BOTTOM"; +})(TitlePosition || (TitlePosition = {})); +export var BottomOffset; +(function (c2) { + c2[c2["OFFSET_FOR_BAR"] = 0] = "OFFSET_FOR_BAR"; + c2[c2["OFFSET_FOR_NONE"] = 1] = "OFFSET_FOR_NONE"; +})(BottomOffset || (BottomOffset = {})); +class DialogParams { + constructor(a2, b2) { + this.options = a2; + this.defaultCloseAction = b2; + } +} +function dialogBuilder(k, l = null) { + const m = k; + (l ? l : this).observeComponentCreation2((x1, y1, z1 = m) => { + Row.create(); + Row.backgroundColor('rgba(0,0,0,0)'); + Row.width('100%'); + Row.height(STANDARD_MAX_COMPONENT_HEIGHT); + Row.padding({ + left: DIALOG_INNER_PADDING_SIZE, + right: DIALOG_INNER_PADDING_SIZE + }); + Row.constraintSize({ + minHeight: STANDARD_MIN_COMPONENT_HEIGHT, + maxHeight: STANDARD_MAX_COMPONENT_HEIGHT, + maxWidth: DIALOG_MAX_WIDTH + }); + }, Row); + (l ? l : this).observeComponentCreation2((t1, u1, v1 = m) => { + Flex.create(); + Flex.backgroundColor(v1.options.backgroundImage === undefined ? '#EBEEF5' : 'rgba(0,0,0,0)'); + Flex.shadow({ + radius: DIALOG_SHADOW_RADIUS, + offsetX: 0, + offsetY: DIALOG_SHADOW_OFFSET_Y, + color: DIALOG_SHADOW_COLOR + }); + Flex.height(STANDARD_MIN_COMPONENT_HEIGHT); + Flex.width('100%'); + Flex.alignSelf(ItemAlign.End); + Flex.direction(Direction.Rtl); + Flex.zIndex(1); + Flex.borderRadius({ + topLeft: DIALOG_BORDER_RADIUS, + topRight: DIALOG_BORDER_RADIUS, + bottomLeft: DIALOG_BORDER_RADIUS, + bottomRight: DIALOG_BORDER_RADIUS + }); + Flex.onClick(() => { + if (v1.options.onDialogClick !== undefined) { + v1.options.onDialogClick(); + } + v1.defaultCloseAction(); + }); + }, Flex); + (l ? l : this).observeComponentCreation2((q1, r1, s1 = m) => { + Row.create(); + Row.padding({ left: OPERATE_AREA_AVOID_WIDTH }); + Row.direction(Direction.Rtl); + Row.defaultFocus(true); + Row.align(Alignment.End); + Row.alignSelf(ItemAlign.End); + Row.constraintSize({ + maxWidth: '50%', + minWidth: '40%' + }); + }, Row); + (l ? l : this).observeComponentCreation2((m1, n1, o1 = m) => { + SymbolGlyph.create({ "id": -1, "type": 40000, params: ['sys.symbol.xmark_circle_fill'], "bundleName": "__harDefaultBundleName__", "moduleName": "__harDefaultModuleName__" }); + SymbolGlyph.fontColor([o1.options.iconStyle === IconStyle.DARK ? + CLOSE_ICON_DARK_RESOURCE : CLOSE_ICON_LIGHT_RESOURCE]); + SymbolGlyph.borderRadius(CLOSE_BUTTON_BORDER_RADIUS); + SymbolGlyph.width(CLOSE_BUTTON_ICON_SIZE); + SymbolGlyph.height(CLOSE_BUTTON_ICON_SIZE); + SymbolGlyph.opacity(CLOSE_BUTTON_ICON_OPACITY); + SymbolGlyph.draggable(false); + SymbolGlyph.focusable(true); + SymbolGlyph.responseRegion({ + x: CLOSE_BUTTON_RESPONSE_REGION_OFFSET_X, + y: CLOSE_BUTTON_RESPONSE_REGION_OFFSET_Y, + width: CLOSE_BUTTON_HOT_SPOT_SIZE, + height: CLOSE_BUTTON_HOT_SPOT_SIZE + }); + SymbolGlyph.margin(CLOSE_BUTTON_MARGIN); + SymbolGlyph.alignSelf(ItemAlign.End); + SymbolGlyph.offset({ + x: CLOSE_BUTTON_OFFSET_X, + y: CLOSE_BUTTON_OFFSET_Y + }); + SymbolGlyph.onClick(() => { + if (o1.options.onDialogClose !== undefined) { + o1.options.onDialogClose(); + } + o1.defaultCloseAction(); + }); + }, SymbolGlyph); + (l ? l : this).observeComponentCreation2((j1, k1, l1 = m) => { + Image.create(l1.options.foregroundImage); + Image.height(STANDARD_MAX_COMPONENT_HEIGHT); + Image.objectFit(ImageFit.Contain); + Image.fitOriginalSize(true); + Image.offset({ + x: FOREGROUND_IMAGE_OFFSET_X, + y: FOREGROUND_IMAGE_OFFSET_Y + }); + Image.alignSelf(ItemAlign.End); + }, Image); + Row.pop(); + (l ? l : this).observeComponentCreation2((g1, h1, i1 = m) => { + Flex.create({ + direction: i1.options.titlePosition === TitlePosition.BOTTOM ? + FlexDirection.ColumnReverse : FlexDirection.Column, + justifyContent: FlexAlign.Center + }); + Flex.constraintSize({ + maxWidth: '60%', + minWidth: '50%' + }); + Flex.flexGrow(1); + Flex.margin({ left: TEXT_LEFT_MARGIN_SIZE }); + }, Flex); + (l ? l : this).observeComponentCreation2((d1, e1, f1 = m) => { + Text.create(f1.options.title); + Text.alignSelf(ItemAlign.Start); + Text.maxFontSize({ "id": -1, "type": 10002, params: ['sys.float.ohos_id_text_size_sub_title1'], "bundleName": "__harDefaultBundleName__", "moduleName": "__harDefaultModuleName__" }); + Text.minFontSize(16); + Text.fontColor(f1.options.titleColor !== undefined ? f1.options.titleColor : TITLE_DEFAULT_COLOR); + Text.fontWeight(FontWeight.Bold); + Text.margin(f1.options.titlePosition ? { top: TITLE_LINE_DISTANCE } : { bottom: TITLE_LINE_DISTANCE }); + Text.maxLines(TITLE_MAX_LINE); + Text.wordBreak(WordBreak.BREAK_WORD); + Text.textOverflow({ overflow: TextOverflow.Ellipsis }); + }, Text); + Text.pop(); + (l ? l : this).observeComponentCreation2((a1, b1, c1 = m) => { + Text.create(c1.options.subtitle); + Text.alignSelf(ItemAlign.Start); + Text.maxFontSize({ "id": -1, "type": 10002, params: ['sys.float.ohos_id_text_size_caption'], "bundleName": "__harDefaultBundleName__", "moduleName": "__harDefaultModuleName__" }); + Text.minFontSize(9); + Text.fontColor(c1.options.subtitleColor !== undefined ? c1.options.subtitleColor : SUBTITLE_DEFAULT_COLOR); + Text.maxLines(SUBTITLE_MAX_LINE); + Text.wordBreak(WordBreak.BREAK_WORD); + Text.textOverflow({ overflow: TextOverflow.Ellipsis }); + }, Text); + Text.pop(); + Flex.pop(); + Flex.pop(); + (l ? l : this).observeComponentCreation2((w, x, y = m) => { + Image.create(y.options.backgroundImage); + Image.width('100%'); + Image.height(STANDARD_MIN_COMPONENT_HEIGHT); + Image.offset({ x: '-100%', y: 0 }); + Image.borderRadius(DIALOG_BORDER_RADIUS); + Image.zIndex(0); + Image.alignSelf(ItemAlign.End); + Image.onClick(() => { + if (y.options.onDialogClose !== undefined) { + y.options.onDialogClose(); + } + y.defaultCloseAction(); + }); + }, Image); + Row.pop(); +} +export class InterstitialDialogAction { + constructor(i) { + this.uiContext = i.uiContext; + this.bottomOffsetType = i.bottomOffsetType; + this.dialogParam = new DialogParams(i, () => { + this.closeDialog(); + }); + this.contentNode = new ComponentContent(this.uiContext, wrapBuilder(dialogBuilder), this.dialogParam); + } + openDialog() { + if (this.contentNode !== null) { + this.uiContext.getPromptAction().openCustomDialog(this.contentNode, { + isModal: false, + autoCancel: false, + offset: { + dx: DIALOG_OFFSET_X, + dy: this.bottomOffsetType === BottomOffset.OFFSET_FOR_BAR ? + DIALOG_OFFSET_Y_FOR_BAR : DIALOG_OFFSET_Y_FOR_NONE + }, + alignment: DialogAlignment.Bottom, + transition: TransitionEffect.asymmetric(TransitionEffect.OPACITY.animation({ duration: 150, curve: Curve.Sharp }) + .combine(TransitionEffect.scale({ x: 0.85, y: 0.85, centerX: '50%', centerY: '85%' }) + .animation({ curve: curves.interpolatingSpring(0, 1, 228, 24) })), TransitionEffect.OPACITY.animation({ duration: 250, curve: Curve.Sharp }) + .combine(TransitionEffect.scale({ x: 0.85, y: 0.85, centerX: '50%', centerY: '85%' }) + .animation({ duration: 250, curve: Curve.Friction }))) + }) + .catch((f) => { + let g = f.message; + let h = f.code; + console.error(`${h}: ${g}`); + }); + } + } + closeDialog() { + if (this.contentNode !== null) { + this.uiContext.getPromptAction().closeCustomDialog(this.contentNode) + .catch((b) => { + let c = b.message; + let d = b.code; + console.error(`${d}: ${c}`); + }); + } + } +} + +export default { InterstitialDialogAction, IconStyle, TitlePosition, BottomOffset }; \ No newline at end of file diff --git a/interstitialdialogaction/source/interstitialdialogaction.ets b/interstitialdialogaction/source/interstitialdialogaction.ets new file mode 100644 index 0000000..427e504 --- /dev/null +++ b/interstitialdialogaction/source/interstitialdialogaction.ets @@ -0,0 +1,296 @@ +/* + * 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. + */ +import { UIContext } from '@ohos.arkui.UIContext'; +import { ComponentContent } from '@ohos.arkui.node'; +import { BusinessError } from '@ohos.base'; +import { curves } from '@kit.ArkUI'; + +const DIALOG_BORDER_RADIUS: Resource = $r('sys.float.ohos_id_corner_radius_default_m'); +const DIALOG_INNER_PADDING_SIZE: number = 16; +const DIALOG_MAX_WIDTH: number = 480; +const DIALOG_OFFSET_X: number = 0; +const DIALOG_OFFSET_Y_FOR_BAR: number = -88; +const DIALOG_OFFSET_Y_FOR_NONE: number = -44; + +const STANDARD_MIN_COMPONENT_HEIGHT: number = 82; +const STANDARD_MAX_COMPONENT_HEIGHT: number = 94; + +const DIALOG_SHADOW_RADIUS: number = 16; +const DIALOG_SHADOW_OFFSET_Y: number = 10; +const DIALOG_SHADOW_COLOR: ResourceStr = '#19000000'; + +const TITLE_LINE_DISTANCE: number = 2; +const TITLE_MAX_LINE: number = 2; +const SUBTITLE_MAX_LINE: number = 1; +const TEXT_LEFT_MARGIN_SIZE: number = 16; +const SUBTITLE_DEFAULT_COLOR: Resource = $r('sys.color.ohos_id_color_text_secondary_contrary'); +const TITLE_DEFAULT_COLOR: Resource = $r('sys.color.ohos_id_color_text_primary_contrary'); + +const OPERATE_AREA_AVOID_WIDTH: number = 28; + +const CLOSE_ICON_DARK_RESOURCE: Resource = $r('sys.color.ohos_id_color_tertiary'); +const CLOSE_ICON_LIGHT_RESOURCE: Resource = $r('sys.color.ohos_id_color_primary_contrary'); + +const CLOSE_BUTTON_BORDER_RADIUS: number = 8; +const CLOSE_BUTTON_ICON_SIZE: number = 16; +const CLOSE_BUTTON_HOT_SPOT_SIZE: number = 32; +const CLOSE_BUTTON_MARGIN: number = 8; +const CLOSE_BUTTON_ICON_OPACITY = 0.6; +const CLOSE_BUTTON_RESPONSE_REGION_OFFSET_X: number = -8; +const CLOSE_BUTTON_RESPONSE_REGION_OFFSET_Y: number = -8; +const CLOSE_BUTTON_OFFSET_X: number = 0; +const CLOSE_BUTTON_OFFSET_Y: number = -50; + +const FOREGROUND_IMAGE_OFFSET_X: number = 4; +const FOREGROUND_IMAGE_OFFSET_Y: number = 0; + +export enum IconStyle { + DARK = 0, + LIGHT = 1 +} + +export enum TitlePosition { + TOP = 0, + BOTTOM = 1 +} + +export enum BottomOffset { + OFFSET_FOR_BAR = 0, + OFFSET_FOR_NONE = 1 +} + +class DialogParams { + public options: DialogOptions; + public defaultCloseAction: Callback; + + constructor( + options: DialogOptions, + defaultCloseAction: Callback, + ) { + this.options = options; + this.defaultCloseAction = defaultCloseAction; + } +} + +@Builder +function dialogBuilder(params: DialogParams) { + Row() { + Flex() { + Row() { + SymbolGlyph($r('sys.symbol.xmark_circle_fill')) + .fontColor([params.options.iconStyle === IconStyle.DARK ? + CLOSE_ICON_DARK_RESOURCE : CLOSE_ICON_LIGHT_RESOURCE]) + .borderRadius(CLOSE_BUTTON_BORDER_RADIUS) + .width(CLOSE_BUTTON_ICON_SIZE) + .height(CLOSE_BUTTON_ICON_SIZE) + .opacity(CLOSE_BUTTON_ICON_OPACITY) + .draggable(false) + .focusable(true) + .responseRegion({ + x: CLOSE_BUTTON_RESPONSE_REGION_OFFSET_X, + y: CLOSE_BUTTON_RESPONSE_REGION_OFFSET_Y, + width: CLOSE_BUTTON_HOT_SPOT_SIZE, + height: CLOSE_BUTTON_HOT_SPOT_SIZE + }) + .margin(CLOSE_BUTTON_MARGIN) + .alignSelf(ItemAlign.End) + .offset({ + x: CLOSE_BUTTON_OFFSET_X, + y: CLOSE_BUTTON_OFFSET_Y + }) + .onClick(() => { + if (params.options.onDialogClose !== undefined) { + params.options.onDialogClose() + } + params.defaultCloseAction() + }) + + Image(params.options.foregroundImage) + .height(STANDARD_MAX_COMPONENT_HEIGHT) + .objectFit(ImageFit.Contain) + .fitOriginalSize(true) + .offset({ + x: FOREGROUND_IMAGE_OFFSET_X, + y: FOREGROUND_IMAGE_OFFSET_Y + }) + .alignSelf(ItemAlign.End) + } + .padding({ left: OPERATE_AREA_AVOID_WIDTH }) + .direction(Direction.Rtl) + .defaultFocus(true) + .align(Alignment.End) + .alignSelf(ItemAlign.End) + .constraintSize({ + maxWidth: '50%', + minWidth: '40%' + }) + + Flex({ + direction: params.options.titlePosition === TitlePosition.BOTTOM ? + FlexDirection.ColumnReverse : FlexDirection.Column, + justifyContent: FlexAlign.Center + }) { + Text(params.options.title) + .alignSelf(ItemAlign.Start) + .maxFontSize($r('sys.float.ohos_id_text_size_sub_title1')) + .minFontSize(16) + .fontColor(params.options.titleColor !== undefined ? params.options.titleColor : TITLE_DEFAULT_COLOR) + .fontWeight(FontWeight.Bold) + .margin(params.options.titlePosition ? { top: TITLE_LINE_DISTANCE } : { bottom: TITLE_LINE_DISTANCE }) + .maxLines(TITLE_MAX_LINE) + .wordBreak(WordBreak.BREAK_WORD) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + Text(params.options.subtitle) + .alignSelf(ItemAlign.Start) + .maxFontSize($r('sys.float.ohos_id_text_size_caption')) + .minFontSize(9) + .fontColor(params.options.subtitleColor !== undefined ? params.options.subtitleColor : SUBTITLE_DEFAULT_COLOR) + .maxLines(SUBTITLE_MAX_LINE) + .wordBreak(WordBreak.BREAK_WORD) + .textOverflow({ overflow: TextOverflow.Ellipsis }) + } + .constraintSize({ + maxWidth: '60%', + minWidth: '50%' + }) + .flexGrow(1) + .margin({ left: TEXT_LEFT_MARGIN_SIZE }) + } + .backgroundColor(params.options.backgroundImage === undefined ? '#EBEEF5' : 'rgba(0,0,0,0)') + .shadow({ + radius: DIALOG_SHADOW_RADIUS, + offsetX: 0, + offsetY: DIALOG_SHADOW_OFFSET_Y, + color: DIALOG_SHADOW_COLOR + }) + .height(STANDARD_MIN_COMPONENT_HEIGHT) + .width('100%') + .alignSelf(ItemAlign.End) + .direction(Direction.Rtl) + .zIndex(1) + .borderRadius({ + topLeft: DIALOG_BORDER_RADIUS, + topRight: DIALOG_BORDER_RADIUS, + bottomLeft: DIALOG_BORDER_RADIUS, + bottomRight: DIALOG_BORDER_RADIUS + }) + .onClick(() => { + if (params.options.onDialogClick !== undefined) { + params.options.onDialogClick() + } + params.defaultCloseAction() + }) + + Image(params.options.backgroundImage) + .width('100%') + .height(STANDARD_MIN_COMPONENT_HEIGHT) + .offset({ x: '-100%', y: 0 }) + .borderRadius(DIALOG_BORDER_RADIUS) + .zIndex(0) + .alignSelf(ItemAlign.End) + .onClick(() => { + if (params.options.onDialogClose !== undefined) { + params.options.onDialogClose() + } + params.defaultCloseAction() + }) + } + .backgroundColor('rgba(0,0,0,0)') + .width('100%') + .height(STANDARD_MAX_COMPONENT_HEIGHT) + .padding({ + left: DIALOG_INNER_PADDING_SIZE, + right: DIALOG_INNER_PADDING_SIZE + }) + .constraintSize({ + minHeight: STANDARD_MIN_COMPONENT_HEIGHT, + maxHeight: STANDARD_MAX_COMPONENT_HEIGHT, + maxWidth: DIALOG_MAX_WIDTH + }) +} + +declare interface DialogOptions { + uiContext: UIContext, + bottomOffsetType?: BottomOffset, + title?: ResourceStr, + subtitle?: ResourceStr, + titleColor?: ResourceStr | Color, + subtitleColor?: ResourceStr | Color, + backgroundImage?: Resource, + foregroundImage?: Resource, + iconStyle?: IconStyle, + titlePosition?: TitlePosition, + onDialogClick?: Callback, + onDialogClose?: Callback +} + +export class InterstitialDialogAction { + private uiContext: UIContext; + private contentNode: ComponentContent; + private dialogParam: DialogParams; + private bottomOffsetType?: BottomOffset; + + constructor(dialogOptions: DialogOptions) { + this.uiContext = dialogOptions.uiContext; + this.bottomOffsetType = dialogOptions.bottomOffsetType; + this.dialogParam = new DialogParams( + dialogOptions, + () => { + this.closeDialog() + } + ); + this.contentNode = new ComponentContent(this.uiContext, wrapBuilder(dialogBuilder), this.dialogParam) + } + + openDialog() { + if (this.contentNode !== null) { + this.uiContext.getPromptAction().openCustomDialog(this.contentNode, { + isModal: false, + autoCancel: false, + offset: { + dx: DIALOG_OFFSET_X, + dy: this.bottomOffsetType === BottomOffset.OFFSET_FOR_BAR ? + DIALOG_OFFSET_Y_FOR_BAR : DIALOG_OFFSET_Y_FOR_NONE + }, + alignment: DialogAlignment.Bottom, + transition: TransitionEffect.asymmetric( + TransitionEffect.OPACITY.animation({ duration: 150, curve: Curve.Sharp }) + .combine(TransitionEffect.scale({ x: 0.85, y: 0.85, centerX: '50%', centerY: '85%' }) + .animation({ curve: curves.interpolatingSpring(0, 1, 228, 24)})) + , + TransitionEffect.OPACITY.animation({ duration: 250, curve: Curve.Sharp }) + .combine(TransitionEffect.scale({ x: 0.85, y: 0.85, centerX: '50%', centerY: '85%' }) + .animation({ duration: 250, curve: Curve.Friction })) + ) + }) + .catch((error: BusinessError) => { + let message = (error as BusinessError).message + let code = (error as BusinessError).code + console.error(`${code}: ${message}`); + }) + } + } + + closeDialog() { + if (this.contentNode !== null) { + this.uiContext.getPromptAction().closeCustomDialog(this.contentNode) + .catch((error: BusinessError) => { + let message = (error as BusinessError).message + let code = (error as BusinessError).code + console.error(`${code}: ${message}`); + }) + } + } +} \ No newline at end of file diff --git a/navpushpathhelper/BUILD.gn b/navpushpathhelper/BUILD.gn new file mode 100644 index 0000000..c540e9d --- /dev/null +++ b/navpushpathhelper/BUILD.gn @@ -0,0 +1,76 @@ +# 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. + +import("//arkcompiler/ets_frontend/es2panda/es2abc_config.gni") +import("//build/ohos.gni") +import("//foundation/arkui/ace_engine/ace_config.gni") +import("//foundation/arkui/ace_engine/adapter/preview/build/config.gni") +import("//foundation/arkui/ace_engine/build/ace_gen_obj.gni") + +es2abc_gen_abc("gen_navpushpathhelper_abc") { + src_js = rebase_path("navpushpathhelper.js") + dst_file = rebase_path(target_out_dir + "/navpushpathhelper.abc") + in_puts = [ "navpushpathhelper.js" ] + out_puts = [ target_out_dir + "/navpushpathhelper.abc" ] + extra_args = [ "--module" ] +} + +gen_js_obj("navpushpathhelper_abc") { + input = get_label_info(":gen_navpushpathhelper_abc", "target_out_dir") + + "/navpushpathhelper.abc" + output = target_out_dir + "/navpushpathhelper_abc.o" + dep = ":gen_navpushpathhelper_abc" +} + +gen_obj("navpushpathhelper_abc_preview") { + input = get_label_info(":gen_navpushpathhelper_abc", "target_out_dir") + + "/navpushpathhelper.abc" + output = target_out_dir + "/navpushpathhelper_abc.c" + snapshot_dep = [ ":gen_navpushpathhelper_abc" ] +} + +additional_include_dirs = [ "${ace_root}" ] + +ohos_shared_library("navpushpathhelper") { + sources = [ + "src/hsp_silentinstall.cpp", + "src/hsp_silentinstall_napi.cpp", + "src/navpushpathhelper.cpp", + ] + + if (use_mingw_win || use_mac || use_linux) { + deps = [ ":gen_obj_src_navpushpathhelper_abc_preview" ] + } else { + deps = [ ":navpushpathhelper_abc" ] + } + + include_dirs = additional_include_dirs + include_dirs += [ "${ace_root}/frameworks" ] + deps += [ "$ace_root/build:libace_compatible" ] + + external_deps = [ + "ability_base:want", + "ability_runtime:abilitykit_native", + "bundle_framework:appexecfwk_base", + "bundle_framework:appexecfwk_core", + "c_utils:utils", + "hilog:libhilog", + "ipc:ipc_core", + "napi:ace_napi", + "samgr:samgr_proxy", + ] + + relative_install_dir = "module/atomicservice" + subsystem_name = "arkui" + part_name = "as_advanced_ui_component" +} diff --git a/navpushpathhelper/include/hsp_silentinstall.h b/navpushpathhelper/include/hsp_silentinstall.h new file mode 100644 index 0000000..ecc6fec --- /dev/null +++ b/navpushpathhelper/include/hsp_silentinstall.h @@ -0,0 +1,37 @@ +/* + * 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 ADVANCED_UI_COMPONENT_NAVPUSHPATHHELPER_INCLUDE_HSP_SILENTINSTALL_H +#define ADVANCED_UI_COMPONENT_NAVPUSHPATHHELPER_INCLUDE_HSP_SILENTINSTALL_H + +#include "bundlemgr/bundle_mgr_interface.h" +#include "interfaces/inner_api/ace/ui_content.h" +namespace OHOS::NavPushPathHelper { + +class HspSilentInstall { +public: + HspSilentInstall() = default; + ~HspSilentInstall() = default; + + static int32_t SilentInstall(const std::string& moduleName, const std::function& callback, + const std::function& silentInstallErrorCallBack); + static bool IsHspExist(const std::string& moduleName, const std::string& pathName); + static void InitRouteMap(); + +private: + static OHOS::sptr GetBundleManager(); +}; +} // namespace OHOS::NavPushPathHelper +#endif \ No newline at end of file diff --git a/navpushpathhelper/include/hsp_silentinstall_napi.h b/navpushpathhelper/include/hsp_silentinstall_napi.h new file mode 100644 index 0000000..e7cedac --- /dev/null +++ b/navpushpathhelper/include/hsp_silentinstall_napi.h @@ -0,0 +1,59 @@ +/* + * 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 ADVANCED_UI_COMPONENT_NAVPUSHPATHHELPER_INCLUDE_HSP_SILENT_INSTALL_NAPI_H +#define ADVANCED_UI_COMPONENT_NAVPUSHPATHHELPER_INCLUDE_HSP_SILENT_INSTALL_NAPI_H + +#include "native_engine/native_engine.h" + +#include "napi/native_api.h" +#include "napi/native_node_api.h" + +namespace OHOS::NavPushPathHelper { +class HspSilentInstallNapi { + public: + static napi_value SilentInstall(napi_env env, napi_callback_info info); + static napi_value IsHspExist(napi_env env, napi_callback_info info); + static napi_value InitRouteMap(napi_env env, napi_callback_info info); + + private: + struct CallbackData { + napi_env env = nullptr; + int32_t errCode = 0; + std::string errorMessage; + napi_ref successCallback = nullptr; + napi_ref failCallback = nullptr; + + ~CallbackData() + { + if (this->successCallback != nullptr) { + napi_delete_reference(this->env, this->successCallback); + this->successCallback = nullptr; + } + if (this->failCallback != nullptr) { + napi_delete_reference(this->env, this->failCallback); + this->failCallback = nullptr; + } + } + }; + + static void SendSuccessBackWork(uv_work_t *work, int statusIn); + static void SendFailBackWork(uv_work_t *work, int statusIn); + static napi_value CreateResultMessage(CallbackData *callbackData); + static napi_value getModuleName(napi_env env, napi_value args, std::string& moduleName); +}; +} + +#endif \ No newline at end of file diff --git a/navpushpathhelper/include/silent_install_callback.h b/navpushpathhelper/include/silent_install_callback.h new file mode 100644 index 0000000..d7fec29 --- /dev/null +++ b/navpushpathhelper/include/silent_install_callback.h @@ -0,0 +1,170 @@ +/* + * 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 ADVANCED_UI_COMPONENT_NAVPUSHPATHHELPER_INCLUDE_SILENT_INSTALL_CALLBACK_H +#define ADVANCED_UI_COMPONENT_NAVPUSHPATHHELPER_INCLUDE_SILENT_INSTALL_CALLBACK_H + +#include "atomic_service_status_callback.h" +#include "errors.h" +#include "iremote_broker.h" +#include "iremote_object.h" +#include "iremote_stub.h" +#include "base/log/log.h" +#include "want_params_wrapper.h" +#include "want.h" + +namespace OHOS::NavPushPathHelper { +constexpr int32_t SILENT_INSTALL_SUCCESS = 0; +constexpr int32_t SILENT_INSTALL_FAIL_CODE = 300001; +constexpr char SILENT_INSTALL_FAIL_MESSAGE[] = "hsp silent install fail"; + +/** + * @class IAtomicServiceStatusCallback + * IAtomicServiceStatusCallback is used to notify caller ability that free install is complete. + */ +class IAtomicServiceStatusCallback : public OHOS::IRemoteBroker { +public: + DECLARE_INTERFACE_DESCRIPTOR(u"ohos.IAtomicServiceStatusCallback"); + + /** + * @brief OnActionEvent. + */ + virtual int32_t OnActionEvent() = 0; + /** + * @brief OnError. + * @param code The code. + * @param msg The msg. + */ + virtual int32_t OnError(int32_t code, const std::string& msg) = 0; +}; + +/** + * @class AtomicServiceStatusCallbackStub + * AtomicServiceStatusCallbackStub. + */ +class AtomicServiceStatusCallbackStub : public OHOS::IRemoteStub { +public: + AtomicServiceStatusCallbackStub() + { + handleOnActionEventFunc_ = &AtomicServiceStatusCallbackStub::HandleOnActionEvent; + handleOnErrorFunc_ = &AtomicServiceStatusCallbackStub::HandleOnError; + } + ~AtomicServiceStatusCallbackStub() override + { + handleOnActionEventFunc_ = nullptr; + handleOnErrorFunc_ = nullptr; + } + + int32_t OnRemoteRequest(uint32_t code, OHOS::MessageParcel &data, OHOS::MessageParcel &reply, + OHOS::MessageOption &option) override + { + TAG_LOGD(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, + "AtomicServiceStatusCallbackStub::OnReceived, code = %{public}u, flags= %{public}d.", + code, option.GetFlags()); + std::u16string descriptor = AtomicServiceStatusCallbackStub::GetDescriptor(); + std::u16string remoteDescriptor = data.ReadInterfaceToken(); + if (descriptor != remoteDescriptor) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, + "%{public}s failed, local descriptor is not equal to remote", __func__); + return OHOS::ERR_INVALID_VALUE; + } + + auto resultCode = data.ReadInt32(); + if (resultCode == SILENT_INSTALL_SUCCESS) { + if (handleOnActionEventFunc_ != nullptr) { + return (this->*handleOnActionEventFunc_)(); + } + } + + if (resultCode < SILENT_INSTALL_SUCCESS) { + if (handleOnErrorFunc_ != nullptr) { + return (this->*handleOnErrorFunc_)(); + } + } + return IPCObjectStub::OnRemoteRequest(code, data, reply, option); + } + +private: + int32_t HandleOnActionEvent() + { + return OnActionEvent(); + } + int32_t HandleOnError() + { + return OnError(SILENT_INSTALL_FAIL_CODE, SILENT_INSTALL_FAIL_MESSAGE); + } + + using HandleOnActionEventFunc = int32_t (AtomicServiceStatusCallbackStub::*)(); + HandleOnActionEventFunc handleOnActionEventFunc_; + + using HandleOnErrorFunc = int32_t (AtomicServiceStatusCallbackStub::*)(); + HandleOnErrorFunc handleOnErrorFunc_; + + DISALLOW_COPY_AND_MOVE(AtomicServiceStatusCallbackStub); +}; + +/** + * @class AtomicServiceStatusCallback + * AtomicServiceStatusCallback. + */ +class AtomicServiceStatusCallback : public AtomicServiceStatusCallbackStub { +public: + AtomicServiceStatusCallback() = default; + ~AtomicServiceStatusCallback() override = default; + + /** + * @brief OnActionEvent. + */ + int32_t OnActionEvent() override + { + if (!actionEventHandler_) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "actionEventHandler_ is null."); + return OHOS::ERR_INVALID_VALUE; + } + actionEventHandler_(); + return OHOS::ERR_OK; + } + /** + * @brief OnError. + * @param code The code. + * @param msg The msg. + */ + int32_t OnError(int32_t code, const std::string& msg) override + { + TAG_LOGI(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "OnError code: %{public}d, msg: %{public}s", + code, msg.c_str()); + if (!errorEventHandler_) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "errorEventHandler_ is null"); + return OHOS::ERR_INVALID_VALUE; + } + + errorEventHandler_(code, msg); + return OHOS::ERR_OK; + } + + void SetActionEventHandler(const std::function& listener) + { + actionEventHandler_ = listener; + } + void SetErrorEventHandler(const std::function& listener) + { + errorEventHandler_ = listener; + } + +private: + std::function actionEventHandler_; + std::function errorEventHandler_; +}; +} +#endif \ No newline at end of file diff --git a/navpushpathhelper/navpushpathhelper.js b/navpushpathhelper/navpushpathhelper.js new file mode 100644 index 0000000..7d6fe2e --- /dev/null +++ b/navpushpathhelper/navpushpathhelper.js @@ -0,0 +1,146 @@ +/* + * 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. + */ + +const navPushPathHelperApi = requireInternal('atomicservice.NavPushPathHelper'); +const hilog = requireNapi('hilog'); + +const tag = 'NavPushPathHelper::JS::'; + +export class NavPushPathHelper { + static currentID = 0; + constructor(navPathStack) { + this.navPathStack_ = navPathStack; + this.currentHelperId_ = NavPushPathHelper.currentID; + NavPushPathHelper.currentID++; + } + + async pushPath(moduleName, info, optionParam) { + hilog.info(0x3900, tag, `pushPath -> currentID: ${this.currentHelperId_}`); + if (navPushPathHelperApi.isHspExist(moduleName, info.name)) { + this.navPathStack_?.pushPath(info, optionParam); + return; + } + return new Promise((resolve, reject) => { + navPushPathHelperApi.silentInstall(moduleName, () => { + navPushPathHelperApi.initRouteMap(); + this.navPathStack_?.pushPath(info, optionParam); + resolve(); + }, + (error) => { + const err = new Error(error.message); + err.code = error.code; + reject(err); + }); + }); + } + + async pushDestination(moduleName, info, optionParam) { + hilog.info(0x3900, tag, `pushDestination -> currentID: ${this.currentHelperId_}`); + if (navPushPathHelperApi.isHspExist(moduleName, info.name)) { + await this.navPathStack_?.pushDestination(info, optionParam); + return; + } + return new Promise((resolve, reject) => { + navPushPathHelperApi.silentInstall(moduleName, () => { + navPushPathHelperApi.initRouteMap(); + this.navPathStack_?.pushDestination(info, optionParam) + .then(resolve).catch(reject); + }, (error) => { + const err = new Error(error.message); + err.code = error.code; + reject(err); + }); + }); + } + + async pushPathByName(moduleName, name, param, onPop, optionParam) { + hilog.info(0x3900, tag, `pushPathByName -> currentID: ${this.currentHelperId_}`); + if (navPushPathHelperApi.isHspExist(moduleName, name)) { + this.navPathStack_?.pushPathByName(name, param, onPop, optionParam); + return; + } + return new Promise((resolve, reject) => { + navPushPathHelperApi.silentInstall(moduleName, () => { + navPushPathHelperApi.initRouteMap(); + this.navPathStack_?.pushPathByName(name, param, onPop, optionParam); + resolve(); + }, (error) => { + const err = new Error(error.message); + err.code = error.code; + reject(err); + }); + }); + } + + async pushDestinationByName(moduleName, name, param, onPop, optionParam) { + hilog.info(0x3900, tag, `pushDestinationByName -> currentID: ${this.currentHelperId_}`); + if (navPushPathHelperApi.isHspExist(moduleName, name)) { + await this.navPathStack_?.pushDestinationByName(name, param, onPop, optionParam); + return; + } + return new Promise((resolve, reject) => { + navPushPathHelperApi.silentInstall(moduleName, () => { + navPushPathHelperApi.initRouteMap(); + this.navPathStack_?.pushDestinationByName(name, param, onPop, optionParam) + .then(resolve).catch(reject); + }, (error) => { + const err = new Error(error.message); + err.code = error.code; + reject(err); + }); + }); + } + + async replacePath(moduleName, info, optionParam) { + hilog.info(0x3900, tag, `replacePath -> currentID: ${this.currentHelperId_}`); + if (navPushPathHelperApi.isHspExist(moduleName, info.name)) { + this.navPathStack_?.replacePath(info, optionParam); + return; + } + return new Promise((resolve, reject) => { + navPushPathHelperApi.silentInstall(moduleName, () => { + navPushPathHelperApi.initRouteMap(); + this.navPathStack_?.replacePath(info, optionParam); + resolve(); + }, (error) => { + const err = new Error(error.message); + err.code = error.code; + reject(err); + }); + }); + } + + async replacePathByName(moduleName, name, param, optionParam) { + hilog.info(0x3900, tag, `replacePathByName -> currentID: ${this.currentHelperId_}`); + if (navPushPathHelperApi.isHspExist(moduleName, name)) { + this.navPathStack_?.replacePathByName(name, param, optionParam); + return; + } + return new Promise((resolve, reject) => { + navPushPathHelperApi.silentInstall(moduleName, () => { + hilog.info(0x3900, tag, `silentInstall success`); + navPushPathHelperApi.initRouteMap(); + this.navPathStack_?.replacePathByName(name, param, optionParam); + resolve(); + }, (error) => { + const err = new Error(error.message); + err.code = error.code; + reject(err); + }); + }); + } +} + +export default { NavPushPathHelper }; \ No newline at end of file diff --git a/navpushpathhelper/src/hsp_silentinstall.cpp b/navpushpathhelper/src/hsp_silentinstall.cpp new file mode 100644 index 0000000..8bc7eca --- /dev/null +++ b/navpushpathhelper/src/hsp_silentinstall.cpp @@ -0,0 +1,117 @@ +/* + * 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 "advanced_ui_component/navpushpathhelper/include/hsp_silentinstall.h" +#include "advanced_ui_component/navpushpathhelper/include/silent_install_callback.h" + +#include "iservice_registry.h" +#include "system_ability_definition.h" +#include "ability_runtime/context/context.h" +#include "want.h" +#include "adapter/ohos/entrance/ace_container.h" +#include "core/pipeline_ng/pipeline_context.h" +#include "base/log/log.h" +#include "base/utils/utils.h" +#include "core/components_ng/manager/navigation/navigation_manager.h" +#include "core/components_ng/pattern/image/image_pattern.h" + +namespace OHOS::NavPushPathHelper { + +OHOS::sptr HspSilentInstall::GetBundleManager() +{ + auto systemAbilityMgr = OHOS::SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager(); + if (!systemAbilityMgr) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "get system ability failed"); + return nullptr; + } + auto bundleObj = systemAbilityMgr->GetSystemAbility(OHOS::BUNDLE_MGR_SERVICE_SYS_ABILITY_ID); + if (!bundleObj) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "get bundle service failed"); + return nullptr; + } + return OHOS::iface_cast(bundleObj); +} + +int32_t HspSilentInstall::SilentInstall(const std::string& moduleName, const std::function& callback, + const std::function& silentInstallErrorCallBack) +{ + auto pipeline = OHOS::Ace::NG::PipelineContext::GetCurrentContextSafely(); + CHECK_NULL_RETURN(pipeline, -1); + + auto runtimeContext = OHOS::Ace::Platform::AceContainer::GetRuntimeContext(pipeline->GetInstanceId()); + CHECK_NULL_RETURN(runtimeContext, -1); + + auto bundleName = runtimeContext->GetBundleName(); + if (bundleName.empty()) { + return -1; + } + + auto appInfo = runtimeContext->GetApplicationInfo(); + if (!appInfo) { + return -1; + } + auto bms = GetBundleManager(); + CHECK_NULL_RETURN(bms, -1); + + OHOS::AAFwk::Want want; + want.SetBundle(bundleName); + want.SetModuleName(moduleName); + OHOS::sptr routerCallback = new AtomicServiceStatusCallback(); + routerCallback->SetActionEventHandler(callback); + routerCallback->SetErrorEventHandler(silentInstallErrorCallBack); + if (bms->SilentInstall(want, appInfo->uid / OHOS::AppExecFwk::Constants::BASE_USER_RANGE, routerCallback)) { + TAG_LOGI(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "Begin to silent install"); + return 0; + } + return -1; +} + +bool HspSilentInstall::IsHspExist(const std::string &moduleName, const std::string &pathName) +{ + auto pipeline = OHOS::Ace::NG::PipelineContext::GetCurrentContextSafely(); + CHECK_NULL_RETURN(pipeline, false); + auto container = OHOS::Ace::Container::CurrentSafely(); + CHECK_NULL_RETURN(container, false); + auto navigationRoute = container->GetNavigationRoute(); + CHECK_NULL_RETURN(navigationRoute, false); + if (navigationRoute->IsNavigationItemExits(pathName)) { + return true; + } + + auto runtimeContext = OHOS::Ace::Platform::AceContainer::GetRuntimeContext(pipeline->GetInstanceId()); + CHECK_NULL_RETURN(runtimeContext, false); + + auto appInfo = runtimeContext->GetApplicationInfo(); + if (!appInfo) { + return false; + } + std::vector moduleList = appInfo->moduleInfos; + auto res = std::any_of(moduleList.begin(), moduleList.end(), [moduleName](const auto &module) { + return module.moduleName == moduleName; + }); + if (res) { + return true; + } + return false; +} + +void HspSilentInstall::InitRouteMap() +{ + auto container = OHOS::Ace::Container::CurrentSafely(); + CHECK_NULL_VOID(container); + auto navigationRoute = container->GetNavigationRoute(); + CHECK_NULL_VOID(navigationRoute); + navigationRoute->InitRouteMap(); +} +} // namespace OHOS::NavPushPathHelper \ No newline at end of file diff --git a/navpushpathhelper/src/hsp_silentinstall_napi.cpp b/navpushpathhelper/src/hsp_silentinstall_napi.cpp new file mode 100644 index 0000000..d6959c6 --- /dev/null +++ b/navpushpathhelper/src/hsp_silentinstall_napi.cpp @@ -0,0 +1,273 @@ +/* + * 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 "advanced_ui_component/navpushpathhelper/include/hsp_silentinstall_napi.h" +#include "advanced_ui_component/navpushpathhelper/include/hsp_silentinstall.h" +#include "base/log/log.h" + +using namespace std; + +namespace OHOS::NavPushPathHelper { + +napi_value HspSilentInstallNapi::IsHspExist(napi_env env, napi_callback_info info) +{ + size_t argc = 2; + size_t requireArgc = 2; + napi_value args[2] = { nullptr }; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments"); + + // get first parameter:moduleName + napi_valuetype moduleNameType; + NAPI_CALL(env, napi_typeof(env, args[0], &moduleNameType)); + NAPI_ASSERT(env, moduleNameType == napi_string, "Wrong argument type. String expected."); + + size_t maxValueLen = 1024; + char moduleNameValue[maxValueLen]; + size_t moduleNameLength = 0; + napi_get_value_string_utf8(env, args[0], moduleNameValue, maxValueLen, &moduleNameLength); + std::string moduleName = moduleNameValue; + + // get second parameter:pathName + napi_valuetype pathNameType; + NAPI_CALL(env, napi_typeof(env, args[1], &pathNameType)); + NAPI_ASSERT(env, pathNameType == napi_string, "Wrong argument type. String expected."); + + char pathNameValue[maxValueLen]; + size_t pathNameLength = 0; + napi_get_value_string_utf8(env, args[1], pathNameValue, maxValueLen, &pathNameLength); + std::string pathName = pathNameValue; + + bool isHspExits = HspSilentInstall::IsHspExist(moduleName, pathName); + napi_value jsResult; + NAPI_CALL(env, napi_get_boolean(env, isHspExits, &jsResult)); + return jsResult; +} + +napi_value HspSilentInstallNapi::InitRouteMap(napi_env env, napi_callback_info info) +{ + HspSilentInstall::InitRouteMap(); + return nullptr; +} + +napi_value HspSilentInstallNapi::SilentInstall(napi_env env, napi_callback_info info) +{ + napi_value result = nullptr; + size_t argc = 3; + size_t requireArgc = 3; + napi_value args[3] = { nullptr }; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, nullptr, nullptr)); + NAPI_ASSERT(env, argc >= requireArgc, "Wrong number of arguments"); + + // get first parameter:moduleName + std::string moduleName; + getModuleName(env, args[0], moduleName); + + auto callbackData = new (std::nothrow) CallbackData(); + if (callbackData == nullptr) { + return result; + } + uv_work_t *work = new (std::nothrow) uv_work_t; + if (work == nullptr) { + delete callbackData; + callbackData = nullptr; + return result; + } + int parameterNum = 1; + const int indexTwo = 2; + napi_create_reference(env, args[1], parameterNum, &(callbackData->successCallback)); + napi_create_reference(env, args[indexTwo], parameterNum, &(callbackData->failCallback)); + callbackData->env = env; + + auto successCallback = [callbackData, work]() { + uv_loop_s *loop = nullptr; + napi_get_uv_event_loop(callbackData->env, &loop); + work->data = reinterpret_cast(callbackData); + uv_queue_work(loop, work, [](uv_work_t *work) { (void)work; }, SendSuccessBackWork); + }; + + auto failCallback = [callbackData, work](int32_t errorCode, const std::string& errorMessage) { + callbackData->errCode = errorCode; + callbackData->errorMessage = errorMessage; + + uv_loop_s *loop = nullptr; + napi_get_uv_event_loop(callbackData->env, &loop); + work->data = reinterpret_cast(callbackData); + uv_queue_work(loop, work, [](uv_work_t *work) { (void)work; }, SendFailBackWork); + }; + + HspSilentInstall::SilentInstall(moduleName, successCallback, failCallback); + return result; +} + +napi_value HspSilentInstallNapi::getModuleName(napi_env env, napi_value args, std::string& moduleName) +{ + napi_valuetype moduleNameType; + NAPI_CALL(env, napi_typeof(env, args, &moduleNameType)); + NAPI_ASSERT(env, moduleNameType == napi_string, "Wrong argument type. String expected."); + + napi_status status; + size_t maxValueLen = 1024; + char moduleNameValue[maxValueLen]; + size_t moduleNameLength = 0; + status = napi_get_value_string_utf8(env, args, moduleNameValue, maxValueLen, &moduleNameLength); + NAPI_ASSERT(env, status == napi_ok, "Failed to napi_get_value_string_utf8"); + moduleName = moduleNameValue; + return nullptr; +} + +void HspSilentInstallNapi::SendSuccessBackWork(uv_work_t *work, int statusIn) +{ + if (work == nullptr) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "SendSuccessBackWork -> work is null"); + return; + } + (void)statusIn; + napi_status status; + napi_handle_scope scope = nullptr; + CallbackData *callbackData = reinterpret_cast(work->data); + if (callbackData == nullptr) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "SendSuccessBackWork -> callbackData is null"); + return; + } + + napi_open_handle_scope(callbackData->env, &scope); + if (scope == nullptr) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "SendSuccessBackWork -> scope is null"); + return; + } + + napi_value callback = nullptr; + status = napi_get_reference_value(callbackData->env, callbackData->successCallback, &callback); + if (status != napi_ok) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "SendSuccessBackWork -> napi_get_reference_value error"); + napi_close_handle_scope(callbackData->env, scope); + return; + } + + napi_value result; + status = napi_call_function(callbackData->env, nullptr, callback, 0, nullptr, &result); + if (status != napi_ok) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "SendSuccessBackWork -> napi_call_function error"); + napi_close_handle_scope(callbackData->env, scope); + return; + } + + napi_close_handle_scope(callbackData->env, scope); + + if (callbackData != nullptr) { + delete callbackData; + callbackData = nullptr; + } + + if (work != nullptr) { + delete work; + work = nullptr; + } +} + +void HspSilentInstallNapi::SendFailBackWork(uv_work_t *work, int statusIn) +{ + if (work == nullptr) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "SendSuccessBackWork -> work is null"); + return; + } + (void)statusIn; + napi_status status; + napi_handle_scope scope = nullptr; + CallbackData *callbackData = reinterpret_cast(work->data); + if (callbackData == nullptr) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "SendFailBackWork -> callbackData is null"); + return; + } + + napi_open_handle_scope(callbackData->env, &scope); + if (scope == nullptr) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "SendFailBackWork -> scope is null"); + return; + } + + napi_value callback = nullptr; + status = napi_get_reference_value(callbackData->env, callbackData->failCallback, &callback); + if (status != napi_ok) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "SendFailBackWork -> napi_get_reference_value error"); + napi_close_handle_scope(callbackData->env, scope); + return; + } + + size_t resultLength = 1; + napi_value resultMessage = CreateResultMessage(callbackData); + napi_value result[] = { resultMessage }; + status = napi_call_function(callbackData->env, nullptr, callback, resultLength, result, nullptr); + if (status != napi_ok) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "SendFailBackWork -> napi_call_function error"); + napi_close_handle_scope(callbackData->env, scope); + return; + } + + napi_close_handle_scope(callbackData->env, scope); + + if (callbackData != nullptr) { + delete callbackData; + callbackData = nullptr; + } + + if (work != nullptr) { + delete work; + work = nullptr; + } +} + +napi_value HspSilentInstallNapi::CreateResultMessage(CallbackData *callbackData) +{ + napi_status status; + napi_value result = nullptr; + napi_value code = nullptr; + + status = napi_create_int32(callbackData->env, callbackData->errCode, &code); + if (status != napi_ok) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "CreateResultMessage -> napi_create_int32 error"); + return result; + } + + napi_value message = nullptr; + status = napi_create_string_utf8(callbackData->env, callbackData->errorMessage.c_str(), + NAPI_AUTO_LENGTH, &message); + if (status != napi_ok) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "CreateResultMessage -> napi_create_string_utf8 error"); + return result; + } + + napi_value businessError = nullptr; + status = napi_create_object(callbackData->env, &businessError); + if (status != napi_ok) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "CreateResultMessage -> napi_create_object error"); + return result; + } + + status = napi_set_named_property(callbackData->env, businessError, "code", code); + if (status != napi_ok) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "CreateResultMessage -> napi_set_named_property error"); + return result; + } + + status = napi_set_named_property(callbackData->env, businessError, "message", message); + if (status != napi_ok) { + TAG_LOGE(OHOS::Ace::AceLogTag::ACE_DEFAULT_DOMAIN, "CreateResultMessage -> napi_set_named_property error"); + return result; + } + return businessError; +} +} \ No newline at end of file diff --git a/navpushpathhelper/src/navpushpathhelper.cpp b/navpushpathhelper/src/navpushpathhelper.cpp new file mode 100644 index 0000000..59496cb --- /dev/null +++ b/navpushpathhelper/src/navpushpathhelper.cpp @@ -0,0 +1,75 @@ +/* + * 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 "native_engine/native_engine.h" + +#include "napi/native_api.h" +#include "napi/native_node_api.h" +#include "advanced_ui_component/navpushpathhelper/include/hsp_silentinstall_napi.h" + +extern const char _binary_navpushpathhelper_abc_start[]; +extern const char _binary_navpushpathhelper_abc_end[]; + +namespace OHOS::NavPushPathHelper { + /* + * function for module exports + */ + static napi_value Init(napi_env env, napi_value exports) + { + /* + * Properties define + */ + napi_property_descriptor desc[] = { + DECLARE_NAPI_FUNCTION("silentInstall", HspSilentInstallNapi::SilentInstall), + DECLARE_NAPI_FUNCTION("isHspExist", HspSilentInstallNapi::IsHspExist), + DECLARE_NAPI_FUNCTION("initRouteMap", HspSilentInstallNapi::InitRouteMap), + }; + NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); + return exports; + } + + // Napi get abc code function + extern "C" __attribute__((visibility("default"))) + void NAPI_atomicservice_NavPushPathHelper_GetABCCode(const char **buf, int *buflen) + { + if (buf != nullptr) { + *buf = _binary_navpushpathhelper_abc_start; + } + if (buflen != nullptr) { + *buflen = _binary_navpushpathhelper_abc_end - _binary_navpushpathhelper_abc_start; + } + } + + /* + * Module define + */ + static napi_module NavPushPathHelperModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "atomicservice.NavPushPathHelper", + .nm_priv = ((void*)0), + .reserved = { 0 }, + }; + + /* + * Module registerfunction + */ + extern "C" __attribute__((constructor)) void NavPushPathHelperRegisterModule(void) + { + napi_module_register(&NavPushPathHelperModule); + } +} \ No newline at end of file -- Gitee