From c5b519266acf33246fefefdafcc06dfae6672d91 Mon Sep 17 00:00:00 2001 From: markYao Date: Fri, 9 Aug 2024 17:02:26 +0800 Subject: [PATCH] =?UTF-8?q?cherry=20pick=2082665d5=20from=20https://gitee.?= =?UTF-8?q?com/mark-yao/notification=5Fdistributed=5Fnotification=5Fservic?= =?UTF-8?q?e/pulls/2006=20=E6=94=AF=E6=8C=81=E5=BA=94=E7=94=A8=E6=8B=89?= =?UTF-8?q?=E8=B5=B7=E9=80=9A=E7=9F=A5=E8=AE=BE=E7=BD=AE=E5=8D=8A=E6=A8=A1?= =?UTF-8?q?=E6=80=81=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: markYao --- frameworks/cj/ffi/include/inner_errors.h | 1 + .../core/common/include/ans_inner_errors.h | 1 + .../napi/include/manager/napi_open_settings.h | 69 +++ frameworks/js/napi/src/common_utils.cpp | 1 + frameworks/js/napi/src/manager/BUILD.gn | 1 + .../js/napi/src/manager/init_module.cpp | 2 + .../napi/src/manager/napi_open_settings.cpp | 403 ++++++++++++++++++ 7 files changed, 478 insertions(+) create mode 100644 frameworks/js/napi/include/manager/napi_open_settings.h create mode 100644 frameworks/js/napi/src/manager/napi_open_settings.cpp diff --git a/frameworks/cj/ffi/include/inner_errors.h b/frameworks/cj/ffi/include/inner_errors.h index bb9b164c9..053b5c2b9 100644 --- a/frameworks/cj/ffi/include/inner_errors.h +++ b/frameworks/cj/ffi/include/inner_errors.h @@ -132,6 +132,7 @@ const int32_t ERROR_DIALOG_IS_POPPING = 1600013; // A notification const int32_t ERROR_NO_RIGHT = 1600014; // No permission. const int32_t ERROR_REPEAT_SET = 1600015; // Repeat create or end. const int32_t ERROR_EXPIRED_NOTIFICATION = 1600016; // Low update version. +const int32_t ERROR_SETTING_WINDOW_EXIST = 1600018; // The notification settings window is already displayed. const int32_t ERROR_NETWORK_UNREACHABLE = 2300007; // Network unreachable. const int32_t ERROR_BUNDLE_NOT_FOUND = 17700001; // The specified bundle name was not found. diff --git a/frameworks/core/common/include/ans_inner_errors.h b/frameworks/core/common/include/ans_inner_errors.h index 49b6ebdd1..cf62079af 100644 --- a/frameworks/core/common/include/ans_inner_errors.h +++ b/frameworks/core/common/include/ans_inner_errors.h @@ -147,6 +147,7 @@ const int32_t ERROR_NO_RIGHT = 1600014; // No permission. const int32_t ERROR_REPEAT_SET = 1600015; // Repeat create or end. const int32_t ERROR_EXPIRED_NOTIFICATION = 1600016; // Low update version. const int32_t ERROR_NO_AGENT_SETTING = 1600017; // No corresponding agent relationship configuration. +const int32_t ERROR_SETTING_WINDOW_EXIST = 1600018; // The notification settings window is already displayed. const int32_t ERROR_NETWORK_UNREACHABLE = 2300007; // Network unreachable. const int32_t ERROR_BUNDLE_NOT_FOUND = 17700001; // The specified bundle name was not found. } // namespace Notification diff --git a/frameworks/js/napi/include/manager/napi_open_settings.h b/frameworks/js/napi/include/manager/napi_open_settings.h new file mode 100644 index 000000000..925e03954 --- /dev/null +++ b/frameworks/js/napi/include/manager/napi_open_settings.h @@ -0,0 +1,69 @@ +/* + * 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 BASE_NOTIFICATION_DISTRIBUTED_NOTIFICATION_SERVICE_FRAMEWORKS_JS_NAPI_OPEN_SETTINGS_H +#define BASE_NOTIFICATION_DISTRIBUTED_NOTIFICATION_SERVICE_FRAMEWORKS_JS_NAPI_OPEN_SETTINGS_H + +#include "common.h" +#include +#include "ui_content.h" +#include "ability.h" +#include "ability_context.h" + +namespace OHOS { +namespace NotificationNapi { +using namespace OHOS::Notification; +using JsAnsCallbackComplete = void(napi_env, void*); + +struct OpenSettingsParams { + std::shared_ptr context; +}; + +struct AsyncCallbackInfoOpenSettings { + napi_env env = nullptr; + napi_async_work asyncWork = nullptr; + OpenSettingsParams params; + CallbackPromiseInfo info; +}; + +void NapiAsyncCompleteCallbackOpenSettings(napi_env env, void *data); +napi_value NapiOpenNotificationSettings(napi_env env, napi_callback_info info); +napi_value ParseOpenSettingsParameters(const napi_env &env, const napi_callback_info &info, OpenSettingsParams ¶ms); +bool CreateSettingsUIExtension(std::shared_ptr context, std::string &bundleName); +bool Init(napi_env env, AsyncCallbackInfoOpenSettings* callbackInfo, JsAnsCallbackComplete complete); +void ProcessStatusChanged(int32_t code); + +class SettingsModalExtensionCallback { +public: + SettingsModalExtensionCallback(); + ~SettingsModalExtensionCallback(); + void OnRelease(int32_t releaseCode); + void OnResult(int32_t resultCode, const OHOS::AAFwk::Want& result); + void OnReceive(const OHOS::AAFwk::WantParams& request); + void OnError(int32_t code, const std::string& name, const std::string &message); + void OnRemoteReady(const std::shared_ptr &uiProxy); + void OnDestroy(); + void SetSessionId(int32_t sessionId); + void SetBundleName(std::string bundleName); + void SetAbilityContext(std::shared_ptr abilityContext); + void ReleaseOrErrorHandle(int32_t code); + +private: + int32_t sessionId_ = 0; + std::string bundleName_; + std::shared_ptr abilityContext_; +}; +} // namespace NotificationNapi +} // namespace OHOS +#endif // BASE_NOTIFICATION_DISTRIBUTED_NOTIFICATION_SERVICE_FRAMEWORKS_JS_NAPI_OPEN_SETTINGS_H \ No newline at end of file diff --git a/frameworks/js/napi/src/common_utils.cpp b/frameworks/js/napi/src/common_utils.cpp index 148fbd8ab..a37f02e07 100644 --- a/frameworks/js/napi/src/common_utils.cpp +++ b/frameworks/js/napi/src/common_utils.cpp @@ -50,6 +50,7 @@ static const std::unordered_map ERROR_CODE_MESSAGE { {ERROR_BUNDLE_NOT_FOUND, "The specified bundle name was not found"}, {ERROR_NO_AGENT_SETTING, "There is no corresponding agent relationship configuration"}, {ERROR_DIALOG_IS_POPPING, "Dialog is popping"}, + {ERROR_SETTING_WINDOW_EXIST, "The notification settings window is already displayed"}, }; } diff --git a/frameworks/js/napi/src/manager/BUILD.gn b/frameworks/js/napi/src/manager/BUILD.gn index 1c80f6904..3cfda893b 100644 --- a/frameworks/js/napi/src/manager/BUILD.gn +++ b/frameworks/js/napi/src/manager/BUILD.gn @@ -84,6 +84,7 @@ ohos_shared_library("notificationmanager") { "napi_enable_notification.cpp", "napi_get_active.cpp", "napi_local_live_view.cpp", + "napi_open_settings.cpp", "napi_publish.cpp", "napi_push.cpp", "napi_push_callback.cpp", diff --git a/frameworks/js/napi/src/manager/init_module.cpp b/frameworks/js/napi/src/manager/init_module.cpp index 5f6860ccd..0f2ffd95e 100644 --- a/frameworks/js/napi/src/manager/init_module.cpp +++ b/frameworks/js/napi/src/manager/init_module.cpp @@ -34,6 +34,7 @@ #include "napi_local_live_view.h" #include "napi_distributed_enable.h" #include "napi_sync_config.h" +#include "napi_open_settings.h" namespace OHOS { namespace NotificationNapi { @@ -120,6 +121,7 @@ napi_value NotificationManagerInit(napi_env env, napi_value exports) DECLARE_NAPI_FUNCTION("getSlotByBundle", NapiGetSlotByBundle), DECLARE_NAPI_FUNCTION("setAdditionalConfig", NapiSetAdditionConfig), DECLARE_NAPI_FUNCTION("isNotificationEnabledSync", NapiIsNotificationEnabledSync), + DECLARE_NAPI_FUNCTION("openNotificationSettings", NapiOpenNotificationSettings), }; NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); diff --git a/frameworks/js/napi/src/manager/napi_open_settings.cpp b/frameworks/js/napi/src/manager/napi_open_settings.cpp new file mode 100644 index 000000000..cda74b8d7 --- /dev/null +++ b/frameworks/js/napi/src/manager/napi_open_settings.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2024-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_open_settings.h" +#include +#include "napi_base_context.h" +#include "ans_inner_errors.h" + +namespace OHOS { +namespace NotificationNapi { +const int OPEN_NOTIFICATION_SETTINGS_MAX_PARA = 1; +static napi_env env_ = nullptr; +static AsyncCallbackInfoOpenSettings* callbackInfo_ = nullptr; +static JsAnsCallbackComplete* complete_ = nullptr; +static std::atomic isExist = false; + +void NapiAsyncCompleteCallbackOpenSettings(napi_env env, void *data) +{ + ANS_LOGD("enter NapiAsyncCompleteCallbackOpenSettings"); + if (data == nullptr) { + ANS_LOGE("Invalid async callback data."); + return; + } + auto* asynccallbackinfo = static_cast(data); + napi_value result = nullptr; + napi_get_undefined(env, &result); + int32_t errorCode = ERR_OK; + if (asynccallbackinfo->info.errorCode == ERROR_SETTING_WINDOW_EXIST) { + errorCode = ERROR_SETTING_WINDOW_EXIST; + } else { + errorCode = asynccallbackinfo->info.errorCode == + ERR_OK ? ERR_OK : Common::ErrorToExternal(asynccallbackinfo->info.errorCode); + } + if (asynccallbackinfo->info.isCallback) { + Common::SetCallback(env, asynccallbackinfo->info.callback, errorCode, result, true); + } else { + Common::SetPromise(env, asynccallbackinfo->info.deferred, errorCode, result, true); + } + if (asynccallbackinfo->info.callback != nullptr) { + napi_delete_reference(env, asynccallbackinfo->info.callback); + } + napi_delete_async_work(env, asynccallbackinfo->asyncWork); + delete asynccallbackinfo; +} + +napi_value NapiOpenNotificationSettings(napi_env env, napi_callback_info info) +{ + ANS_LOGD("NapiOpenNotificationSettings start"); + OpenSettingsParams params {}; + if (ParseOpenSettingsParameters(env, info, params) == nullptr) { + Common::NapiThrow(env, ERROR_PARAM_INVALID); + return Common::NapiGetUndefined(env); + } + + AsyncCallbackInfoOpenSettings *asynccallbackinfo = new (std::nothrow) AsyncCallbackInfoOpenSettings { + .env = env, .params = params}; + if (!asynccallbackinfo) { + return Common::JSParaError(env, nullptr); + } + napi_value promise = nullptr; + Common::PaddingCallbackPromiseInfo(env, nullptr, asynccallbackinfo->info, promise); + + napi_value resourceName = nullptr; + napi_create_string_latin1(env, "openNotificationSettings", NAPI_AUTO_LENGTH, &resourceName); + + auto createExtension = [](napi_env env, void* data) { + ANS_LOGD("enter"); + if (data == nullptr) { + ANS_LOGE("data is invalid"); + return; + } + auto* asynccallbackinfo = static_cast(data); + + if (asynccallbackinfo->params.context != nullptr) { + ANS_LOGD("stage mode"); + std::string bundleName {""}; + if (isExist.exchange(true)) { + ANS_LOGE("SettingsUIExtension existed"); + asynccallbackinfo->info.errorCode = ERROR_SETTING_WINDOW_EXIST; + return; + } + bool success = CreateSettingsUIExtension(asynccallbackinfo->params.context, bundleName); + if (success) { + asynccallbackinfo->info.errorCode = ERR_ANS_DIALOG_POP_SUCCEEDED; + } else { + asynccallbackinfo->info.errorCode = ERROR_INTERNAL_ERROR; + } + } else { + ANS_LOGD("un stage mode"); + } + ANS_LOGI("done, code is %{public}d.", asynccallbackinfo->info.errorCode); + }; + auto jsCb = [](napi_env env, napi_status status, void* data) { + ANS_LOGD("enter jsCb"); + auto* asynccallbackinfo = static_cast(data); + ErrCode errCode = asynccallbackinfo->info.errorCode; + if (errCode != ERR_ANS_DIALOG_POP_SUCCEEDED) { + ANS_LOGE("error, code is %{public}d.", errCode); + NapiAsyncCompleteCallbackOpenSettings(env, static_cast(asynccallbackinfo)); + isExist.store(false); + return; + } + if (!Init(env, asynccallbackinfo, NapiAsyncCompleteCallbackOpenSettings)) { + ANS_LOGE("error"); + asynccallbackinfo->info.errorCode = ERROR_INTERNAL_ERROR; + NapiAsyncCompleteCallbackOpenSettings(env, static_cast(asynccallbackinfo)); + return; + } + ANS_LOGD("jsCb end"); + }; + + // Asynchronous function call + napi_create_async_work(env, + nullptr, + resourceName, + createExtension, + jsCb, + static_cast(asynccallbackinfo), + &asynccallbackinfo->asyncWork); + + napi_queue_async_work_with_qos(env, asynccallbackinfo->asyncWork, napi_qos_user_initiated); + ANS_LOGD("NapiOpenNotificationSettings end"); + return promise; +} + +napi_value ParseOpenSettingsParameters(const napi_env &env, const napi_callback_info &info, OpenSettingsParams ¶ms) +{ + ANS_LOGD("enter"); + + size_t argc = OPEN_NOTIFICATION_SETTINGS_MAX_PARA; + napi_value argv[OPEN_NOTIFICATION_SETTINGS_MAX_PARA] = {nullptr}; + napi_value thisVar = nullptr; + NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL)); + + if (argc == 0) { + return Common::NapiGetNull(env); + } + + // argv[0]: context + napi_valuetype valuetype = napi_undefined; + NAPI_CALL(env, napi_typeof(env, argv[PARAM0], &valuetype)); + if ((valuetype != napi_object) && (valuetype != napi_function)) { + ANS_LOGW("Wrong argument type. Function or object expected. Excute promise."); + return Common::NapiGetNull(env); + } + if (valuetype == napi_object) { + bool stageMode = false; + napi_status status = OHOS::AbilityRuntime::IsStageContext(env, argv[PARAM0], stageMode); + if (status == napi_ok && stageMode) { + auto context = OHOS::AbilityRuntime::GetStageModeContext(env, argv[PARAM0]); + sptr callerToken = context->GetToken(); + params.context = context; + } else { + ANS_LOGE("Only support stage mode"); + std::string msg = "Incorrect parameter types.Only support stage mode."; + Common::NapiThrow(env, ERROR_PARAM_INVALID, msg); + return nullptr; + } + } + return Common::NapiGetNull(env); +} + +bool CreateSettingsUIExtension(std::shared_ptr context, std::string &bundleName) +{ + if (context == nullptr) { + ANS_LOGE("Get context failed"); + return false; + } + + std::shared_ptr abilityContext = + OHOS::AbilityRuntime::Context::ConvertTo(context); + if (abilityContext == nullptr) { + ANS_LOGE("abilityContext is null"); + return false; + } + auto uiContent = abilityContext->GetUIContent(); + if (uiContent == nullptr) { + ANS_LOGE("uiContent is null"); + return false; + } + + AAFwk::Want want; + std::string targetBundleName = "com.ohos.sceneboard"; + std::string targetAbilityName = "NotificationManangerUIExtensionAbility"; + want.SetElementName(targetBundleName, targetAbilityName); + + std::string typeKey = "ability.want.params.uiExtensionType"; + std::string typeValue = "sys/commonUI"; + want.SetParam(typeKey, typeValue); + + auto uiExtCallback = std::make_shared(); + uiExtCallback->SetAbilityContext(abilityContext); + uiExtCallback->SetBundleName(bundleName); + Ace::ModalUIExtensionCallbacks uiExtensionCallbacks = { + .onRelease = + std::bind(&SettingsModalExtensionCallback::OnRelease, uiExtCallback, std::placeholders::_1), + .onResult = std::bind(&SettingsModalExtensionCallback::OnResult, uiExtCallback, + std::placeholders::_1, std::placeholders::_2), + .onReceive = + std::bind(&SettingsModalExtensionCallback::OnReceive, uiExtCallback, std::placeholders::_1), + .onError = std::bind(&SettingsModalExtensionCallback::OnError, uiExtCallback, + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + .onRemoteReady = + std::bind(&SettingsModalExtensionCallback::OnRemoteReady, uiExtCallback, std::placeholders::_1), + .onDestroy = std::bind(&SettingsModalExtensionCallback::OnDestroy, uiExtCallback), + }; + + Ace::ModalUIExtensionConfig config; + config.isProhibitBack = true; + + int32_t sessionId = uiContent->CreateModalUIExtension(want, uiExtensionCallbacks, config); + ANS_LOGI("Create end, sessionId: %{public}d", sessionId); + if (sessionId == 0) { + ANS_LOGE("Create component failed, sessionId is 0"); + return false; + } + uiExtCallback->SetSessionId(sessionId); + return true; +} + +bool Init(napi_env env, AsyncCallbackInfoOpenSettings* callbackInfo, + JsAnsCallbackComplete complete) +{ + ANS_LOGD("enter JsAnsCallback::Init"); + if (env == nullptr || callbackInfo == nullptr || complete == nullptr) { + ANS_LOGE("invalid data"); + return false; + } + env_ = env; + callbackInfo_ = callbackInfo; + complete_ = complete; + return true; +} + +void ProcessStatusChanged(int32_t code) +{ + ANS_LOGD("enter"); + std::unique_ptr callbackInfo(callbackInfo_); + if (env_ == nullptr || callbackInfo == nullptr || complete_ == nullptr) { + ANS_LOGE("invalid data"); + return; + } + + callbackInfo->info.errorCode = code; + + uv_loop_s* loop = nullptr; + napi_get_uv_event_loop(env_, &loop); + if (loop == nullptr) { + ANS_LOGE("loop is nullptr"); + return; + } + + auto work = std::make_unique(); + struct WorkData { + decltype(env_) env = nullptr; + decltype(callbackInfo_) callbackInfo = nullptr; + decltype(complete_) complete = nullptr; + }; + auto workData = std::make_unique(); + workData->env = env_; + workData->callbackInfo = callbackInfo_; + workData->complete = complete_; + + work->data = static_cast(workData.get()); + auto jsCb = [](uv_work_t* work, int status) { + ANS_LOGD("enter ProcessStatusChanged jsCb"); + std::unique_ptr workSP(work); + if (work == nullptr || work->data == nullptr) { + ANS_LOGE("invalid data"); + return; + } + auto* data = static_cast(work->data); + std::unique_ptr dataSP(data); + std::unique_ptr callbackInfoSP(data->callbackInfo); + if (data->env == nullptr || + data->callbackInfo == nullptr || + data->complete == nullptr) { + return; + } + auto* callbackInfoPtr = callbackInfoSP.release(); + data->complete(data->env, static_cast(callbackInfoPtr)); + }; + + int ret = uv_queue_work_with_qos(loop, + work.get(), + [](uv_work_t *work) {}, + jsCb, + uv_qos_user_initiated); + if (ret != 0) { + ANS_LOGE("uv_queue_work failed"); + return; + } + callbackInfo.release(); + workData.release(); + work.release(); +} + +SettingsModalExtensionCallback::SettingsModalExtensionCallback() +{} + +SettingsModalExtensionCallback::~SettingsModalExtensionCallback() +{} + + +/* + * when UIExtensionAbility use terminateSelfWithResult + */ +void SettingsModalExtensionCallback::OnResult(int32_t resultCode, const AAFwk::Want& result) +{ + ANS_LOGD("OnResult"); +} + +/* + * when UIExtensionAbility send message to UIExtensionComponent + */ +void SettingsModalExtensionCallback::OnReceive(const AAFwk::WantParams& receive) +{ + ANS_LOGD("OnReceive"); +} + +/* + * when UIExtensionAbility disconnect or use terminate or process die + * releaseCode is 0 when process normal exit + */ +void SettingsModalExtensionCallback::OnRelease(int32_t releaseCode) +{ + ANS_LOGD("OnRelease"); + ReleaseOrErrorHandle(releaseCode); +} + +/* + * when UIExtensionComponent init or turn to background or destroy UIExtensionAbility occur error + */ +void SettingsModalExtensionCallback::OnError(int32_t code, const std::string& name, const std::string& message) +{ + ANS_LOGE("OnError, code = %{public}d,name = %{public}s, message = %{public}s", code, name.c_str(), message.c_str()); + ReleaseOrErrorHandle(code); + ProcessStatusChanged(code); +} + +/* + * when UIExtensionComponent connect to UIExtensionAbility, ModalUIExtensionProxy will init, + * UIExtensionComponent can send message to UIExtensionAbility by ModalUIExtensionProxy + */ +void SettingsModalExtensionCallback::OnRemoteReady(const std::shared_ptr& uiProxy) +{ + ANS_LOGI("OnRemoteReady"); + ProcessStatusChanged(0); +} + +/* + * when UIExtensionComponent destructed + */ +void SettingsModalExtensionCallback::OnDestroy() +{ + ANS_LOGI("OnDestroy"); + isExist.store(false); +} + + +void SettingsModalExtensionCallback::SetSessionId(int32_t sessionId) +{ + this->sessionId_ = sessionId; +} + +void SettingsModalExtensionCallback::SetBundleName(std::string bundleName) +{ + this->bundleName_ = bundleName; +} + +void SettingsModalExtensionCallback::SetAbilityContext( + std::shared_ptr abilityContext) +{ + this->abilityContext_ = abilityContext; +} + +void SettingsModalExtensionCallback::ReleaseOrErrorHandle(int32_t code) +{ + ANS_LOGD("ReleaseOrErrorHandle start"); + Ace::UIContent* uiContent = this->abilityContext_->GetUIContent(); + if (uiContent == nullptr) { + ANS_LOGE("uiContent is null"); + return; + } + uiContent->CloseModalUIExtension(this->sessionId_); + ANS_LOGD("ReleaseOrErrorHandle end"); + return; +} + +} // namespace NotificationNapi +} // namespace OHOS \ No newline at end of file -- Gitee