• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "js_get_input_method_setting.h"
17 
18 #include "event_checker.h"
19 #include "input_client_info.h"
20 #include "input_method_controller.h"
21 #include "input_method_status.h"
22 #include "js_callback_handler.h"
23 #include "js_input_method.h"
24 #include "js_util.h"
25 #include "js_utils.h"
26 #include "keyevent_consumer_proxy.h"
27 #include "napi/native_api.h"
28 #include "napi/native_node_api.h"
29 #include "string_ex.h"
30 
31 namespace OHOS {
32 namespace MiscServices {
33 int32_t MAX_TYPE_NUM = 128;
34 constexpr size_t ARGC_ONE = 1;
35 constexpr size_t ARGC_TWO = 2;
36 constexpr size_t ARGC_MAX = 6;
37 thread_local napi_ref JsGetInputMethodSetting::IMSRef_ = nullptr;
38 const std::string JsGetInputMethodSetting::IMS_CLASS_NAME = "InputMethodSetting";
39 const std::map<InputWindowStatus, std::string> PANEL_STATUS{ { InputWindowStatus::SHOW, "imeShow" },
40     { InputWindowStatus::HIDE, "imeHide" } };
41 std::mutex JsGetInputMethodSetting::msMutex_;
42 std::shared_ptr<JsGetInputMethodSetting> JsGetInputMethodSetting::inputMethod_{ nullptr };
Init(napi_env env,napi_value exports)43 napi_value JsGetInputMethodSetting::Init(napi_env env, napi_value exports)
44 {
45     napi_value maxTypeNumber = nullptr;
46     napi_create_int32(env, MAX_TYPE_NUM, &maxTypeNumber);
47 
48     napi_property_descriptor descriptor[] = {
49         DECLARE_NAPI_FUNCTION("getInputMethodSetting", GetInputMethodSetting),
50         DECLARE_NAPI_FUNCTION("getSetting", GetSetting),
51         DECLARE_NAPI_PROPERTY("MAX_TYPE_NUM", maxTypeNumber),
52     };
53     NAPI_CALL(
54         env, napi_define_properties(env, exports, sizeof(descriptor) / sizeof(napi_property_descriptor), descriptor));
55 
56     napi_property_descriptor properties[] = {
57         DECLARE_NAPI_FUNCTION("listInputMethod", ListInputMethod),
58         DECLARE_NAPI_FUNCTION("listInputMethodSubtype", ListInputMethodSubtype),
59         DECLARE_NAPI_FUNCTION("listCurrentInputMethodSubtype", ListCurrentInputMethodSubtype),
60         DECLARE_NAPI_FUNCTION("getInputMethods", GetInputMethods),
61         DECLARE_NAPI_FUNCTION("getInputMethodsSync", GetInputMethodsSync),
62         DECLARE_NAPI_FUNCTION("getAllInputMethods", GetAllInputMethods),
63         DECLARE_NAPI_FUNCTION("getAllInputMethodsSync", GetAllInputMethodsSync),
64         DECLARE_NAPI_FUNCTION("displayOptionalInputMethod", DisplayOptionalInputMethod),
65         DECLARE_NAPI_FUNCTION("showOptionalInputMethods", ShowOptionalInputMethods),
66         DECLARE_NAPI_FUNCTION("isPanelShown", IsPanelShown),
67         DECLARE_NAPI_FUNCTION("on", Subscribe),
68         DECLARE_NAPI_FUNCTION("off", UnSubscribe),
69     };
70     napi_value cons = nullptr;
71     NAPI_CALL(env, napi_define_class(env, IMS_CLASS_NAME.c_str(), IMS_CLASS_NAME.size(), JsConstructor, nullptr,
72                        sizeof(properties) / sizeof(napi_property_descriptor), properties, &cons));
73     NAPI_CALL(env, napi_create_reference(env, cons, 1, &IMSRef_));
74     NAPI_CALL(env, napi_set_named_property(env, exports, IMS_CLASS_NAME.c_str(), cons));
75     return exports;
76 }
77 
JsConstructor(napi_env env,napi_callback_info cbinfo)78 napi_value JsGetInputMethodSetting::JsConstructor(napi_env env, napi_callback_info cbinfo)
79 {
80     napi_value thisVar = nullptr;
81     NAPI_CALL(env, napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr));
82 
83     auto delegate = GetInputMethodSettingInstance();
84     if (delegate == nullptr) {
85         IMSA_HILOGE("settingObject is nullptr");
86         napi_value result = nullptr;
87         napi_get_null(env, &result);
88         return result;
89     }
90     napi_status status = napi_wrap(
91         env, thisVar, delegate.get(), [](napi_env env, void *data, void *hint) {}, nullptr, nullptr);
92     if (status != napi_ok) {
93         IMSA_HILOGE("JsGetInputMethodSetting napi_wrap failed: %{public}d", status);
94         return nullptr;
95     }
96     if (delegate->loop_ == nullptr) {
97         napi_get_uv_event_loop(env, &delegate->loop_);
98     }
99     return thisVar;
100 }
101 
GetJsConstProperty(napi_env env,uint32_t num)102 napi_value JsGetInputMethodSetting::GetJsConstProperty(napi_env env, uint32_t num)
103 {
104     napi_value jsNumber = nullptr;
105     napi_create_int32(env, num, &jsNumber);
106     return jsNumber;
107 }
108 
GetInputMethodSettingInstance()109 std::shared_ptr<JsGetInputMethodSetting> JsGetInputMethodSetting::GetInputMethodSettingInstance()
110 {
111     if (inputMethod_ == nullptr) {
112         std::lock_guard<std::mutex> lock(msMutex_);
113         if (inputMethod_ == nullptr) {
114             auto engine = std::make_shared<JsGetInputMethodSetting>();
115             if (engine == nullptr) {
116                 IMSA_HILOGE("input method nullptr");
117                 return nullptr;
118             }
119             inputMethod_ = engine;
120         }
121     }
122     return inputMethod_;
123 }
124 
GetSetting(napi_env env,napi_callback_info info)125 napi_value JsGetInputMethodSetting::GetSetting(napi_env env, napi_callback_info info)
126 {
127     return GetIMSetting(env, info, true);
128 }
129 
GetInputMethodSetting(napi_env env,napi_callback_info info)130 napi_value JsGetInputMethodSetting::GetInputMethodSetting(napi_env env, napi_callback_info info)
131 {
132     return GetIMSetting(env, info, false);
133 }
134 
GetIMSetting(napi_env env,napi_callback_info info,bool needThrowException)135 napi_value JsGetInputMethodSetting::GetIMSetting(napi_env env, napi_callback_info info, bool needThrowException)
136 {
137     napi_value instance = nullptr;
138     napi_value cons = nullptr;
139     if (napi_get_reference_value(env, IMSRef_, &cons) != napi_ok) {
140         IMSA_HILOGE("GetSetting::napi_get_reference_value not ok");
141         if (needThrowException) {
142             JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_SETTINGS, "", TYPE_OBJECT);
143         }
144         return nullptr;
145     }
146     if (napi_new_instance(env, cons, 0, nullptr, &instance) != napi_ok) {
147         IMSA_HILOGE("GetSetting::napi_new_instance not ok");
148         if (needThrowException) {
149             JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_SETTINGS, "", TYPE_OBJECT);
150         }
151         return nullptr;
152     }
153     return instance;
154 }
155 
GetInputMethodProperty(napi_env env,napi_value argv,std::shared_ptr<ListInputContext> ctxt)156 napi_status JsGetInputMethodSetting::GetInputMethodProperty(
157     napi_env env, napi_value argv, std::shared_ptr<ListInputContext> ctxt)
158 {
159     napi_valuetype valueType = napi_undefined;
160     napi_typeof(env, argv, &valueType);
161     PARAM_CHECK_RETURN(env, valueType == napi_object, "Parameter error.", TYPE_OBJECT, napi_invalid_arg);
162     napi_value result = nullptr;
163     napi_get_named_property(env, argv, "name", &result);
164     JsUtils::GetValue(env, result, ctxt->property.name);
165 
166     result = nullptr;
167     napi_get_named_property(env, argv, "id", &result);
168     JsUtils::GetValue(env, result, ctxt->property.id);
169 
170     if (ctxt->property.name.empty() || ctxt->property.id.empty()) {
171         result = nullptr;
172         napi_get_named_property(env, argv, "packageName", &result);
173         JsUtils::GetValue(env, result, ctxt->property.name);
174 
175         result = nullptr;
176         napi_get_named_property(env, argv, "methodId", &result);
177         JsUtils::GetValue(env, result, ctxt->property.id);
178     }
179     PARAM_CHECK_RETURN(env, (!ctxt->property.name.empty() && !ctxt->property.id.empty()), "Parameter error.",
180         TYPE_NONE, napi_invalid_arg);
181     IMSA_HILOGD("methodId:%{public}s, packageName:%{public}s", ctxt->property.id.c_str(), ctxt->property.name.c_str());
182     return napi_ok;
183 }
184 
ListInputMethod(napi_env env,napi_callback_info info)185 napi_value JsGetInputMethodSetting::ListInputMethod(napi_env env, napi_callback_info info)
186 {
187     IMSA_HILOGD("run in ListInputMethod");
188     auto ctxt = std::make_shared<ListInputContext>();
189     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
190         ctxt->inputMethodStatus = InputMethodStatus::ALL;
191         return napi_ok;
192     };
193     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
194         *result = JsInputMethod::GetJSInputMethodProperties(env, ctxt->properties);
195         return napi_ok;
196     };
197     auto exec = [ctxt](AsyncCall::Context *ctx) {
198         int32_t errCode = InputMethodController::GetInstance()->ListInputMethod(ctxt->properties);
199         if (errCode == ErrorCode::NO_ERROR) {
200             IMSA_HILOGI("exec ---- ListInputMethod success");
201             ctxt->status = napi_ok;
202             ctxt->SetState(ctxt->status);
203             return;
204         }
205         ctxt->SetErrorCode(errCode);
206     };
207     ctxt->SetAction(std::move(input), std::move(output));
208     // 1 means JsAPI:listInputMethod has 1 params at most.
209     AsyncCall asyncCall(env, info, ctxt, 1);
210     return asyncCall.Call(env, exec, "listInputMethod");
211 }
212 
GetInputMethods(napi_env env,napi_callback_info info)213 napi_value JsGetInputMethodSetting::GetInputMethods(napi_env env, napi_callback_info info)
214 {
215     IMSA_HILOGD("run in GetInputMethods");
216     auto ctxt = std::make_shared<ListInputContext>();
217     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
218         PARAM_CHECK_RETURN(env, argc > 0, "should has one parameter.", TYPE_NONE, napi_invalid_arg);
219         bool enable = false;
220         // 0 means first param index
221         napi_status status = JsUtils::GetValue(env, argv[0], enable);
222         PARAM_CHECK_RETURN(env, status == napi_ok, "enable.", TYPE_NUMBER, napi_invalid_arg);
223         ctxt->inputMethodStatus = enable ? InputMethodStatus::ENABLE : InputMethodStatus::DISABLE;
224         return napi_ok;
225     };
226     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
227         *result = JsInputMethod::GetJSInputMethodProperties(env, ctxt->properties);
228         return napi_ok;
229     };
230     auto exec = [ctxt](AsyncCall::Context *ctx) {
231         int32_t errCode =
232             InputMethodController::GetInstance()->ListInputMethod(ctxt->inputMethodStatus == ENABLE, ctxt->properties);
233         if (errCode == ErrorCode::NO_ERROR) {
234             IMSA_HILOGI("exec ---- GetInputMethods success");
235             ctxt->status = napi_ok;
236             ctxt->SetState(ctxt->status);
237             return;
238         }
239         ctxt->SetErrorCode(errCode);
240     };
241     ctxt->SetAction(std::move(input), std::move(output));
242     // 2 means JsAPI:getInputMethods has 2 params at most.
243     AsyncCall asyncCall(env, info, ctxt, 2);
244     return asyncCall.Call(env, exec, "getInputMethods");
245 }
246 
GetInputMethodsSync(napi_env env,napi_callback_info info)247 napi_value JsGetInputMethodSetting::GetInputMethodsSync(napi_env env, napi_callback_info info)
248 {
249     IMSA_HILOGD("run in");
250     size_t argc = ARGC_MAX;
251     napi_value argv[ARGC_MAX] = { nullptr };
252     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
253 
254     bool enable = false;
255     // 0 means first param index
256     if (argc < 1 || JsUtils::GetValue(env, argv[0], enable) != napi_ok) {
257         JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_PARAMCHECK, "please check the params", TYPE_NONE);
258         return JsUtil::Const::Null(env);
259     }
260 
261     std::vector<Property> properties;
262     int32_t ret = InputMethodController::GetInstance()->ListInputMethod(enable, properties);
263     if (ret != ErrorCode::NO_ERROR) {
264         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get Inputmethods", TYPE_NONE);
265         return JsUtil::Const::Null(env);
266     }
267     return JsInputMethod::GetJSInputMethodProperties(env, properties);
268 }
269 
GetAllInputMethods(napi_env env,napi_callback_info info)270 napi_value JsGetInputMethodSetting::GetAllInputMethods(napi_env env, napi_callback_info info)
271 {
272     IMSA_HILOGD("run in GetAllInputMethods");
273     auto ctxt = std::make_shared<ListInputContext>();
274     auto input = [ctxt](
275                      napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { return napi_ok; };
276     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
277         *result = JsInputMethod::GetJSInputMethodProperties(env, ctxt->properties);
278         return napi_ok;
279     };
280     auto exec = [ctxt](AsyncCall::Context *ctx) {
281         int32_t errCode = InputMethodController::GetInstance()->ListInputMethod(ctxt->properties);
282         if (errCode == ErrorCode::NO_ERROR) {
283             IMSA_HILOGI("exec ---- GetInputMethods success");
284             ctxt->status = napi_ok;
285             ctxt->SetState(ctxt->status);
286             return;
287         }
288         ctxt->SetErrorCode(errCode);
289     };
290     ctxt->SetAction(std::move(input), std::move(output));
291     // 1 means JsAPI:getAllInputMethods has 1 params at most.
292     AsyncCall asyncCall(env, info, ctxt, 1);
293     return asyncCall.Call(env, exec, "getInputMethods");
294 }
295 
GetAllInputMethodsSync(napi_env env,napi_callback_info info)296 napi_value JsGetInputMethodSetting::GetAllInputMethodsSync(napi_env env, napi_callback_info info)
297 {
298     IMSA_HILOGD("run in");
299     std::vector<Property> properties;
300     int32_t ret = InputMethodController::GetInstance()->ListInputMethod(properties);
301     if (ret != ErrorCode::NO_ERROR) {
302         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get Inputmethods", TYPE_NONE);
303         return JsUtil::Const::Null(env);
304     }
305     return JsInputMethod::GetJSInputMethodProperties(env, properties);
306 }
307 
DisplayOptionalInputMethod(napi_env env,napi_callback_info info)308 napi_value JsGetInputMethodSetting::DisplayOptionalInputMethod(napi_env env, napi_callback_info info)
309 {
310     IMSA_HILOGD("JsGetInputMethodSetting run in");
311     auto ctxt = std::make_shared<DisplayOptionalInputMethodContext>();
312     auto input = [ctxt](
313                      napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { return napi_ok; };
314     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
315     auto exec = [ctxt](AsyncCall::Context *ctx) {
316         int32_t errCode = InputMethodController::GetInstance()->DisplayOptionalInputMethod();
317         if (errCode == ErrorCode::NO_ERROR) {
318             IMSA_HILOGI("exec ---- DisplayOptionalInputMethod success");
319             ctxt->status = napi_ok;
320             ctxt->SetState(ctxt->status);
321         }
322     };
323     ctxt->SetAction(std::move(input), std::move(output));
324     // 1 means JsAPI:displayOptionalInputMethod has 1 params at most.
325     AsyncCall asyncCall(env, info, ctxt, 1);
326     return asyncCall.Call(env, exec, "displayOptionalInputMethod");
327 }
328 
ShowOptionalInputMethods(napi_env env,napi_callback_info info)329 napi_value JsGetInputMethodSetting::ShowOptionalInputMethods(napi_env env, napi_callback_info info)
330 {
331     IMSA_HILOGD("JsGetInputMethodSetting run in");
332     auto ctxt = std::make_shared<DisplayOptionalInputMethodContext>();
333     auto input = [ctxt](
334                      napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { return napi_ok; };
335     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
336         napi_status status = napi_get_boolean(env, ctxt->isDisplayed, result);
337         IMSA_HILOGI("output napi_get_boolean != nullptr[%{public}d]", result != nullptr);
338         return status;
339     };
340     auto exec = [ctxt](AsyncCall::Context *ctx) {
341         int32_t errCode = InputMethodController::GetInstance()->DisplayOptionalInputMethod();
342         if (errCode == ErrorCode::NO_ERROR) {
343             IMSA_HILOGE("exec ---- DisplayOptionalInputMethod success");
344             ctxt->status = napi_ok;
345             ctxt->SetState(ctxt->status);
346             ctxt->isDisplayed = true;
347             return;
348         } else {
349             ctxt->SetErrorCode(errCode);
350         }
351     };
352     ctxt->SetAction(std::move(input), std::move(output));
353     // 1 means JsAPI:showOptionalInputMethods has 1 params at most.
354     AsyncCall asyncCall(env, info, ctxt, 1);
355     return asyncCall.Call(env, exec, "showOptionalInputMethods");
356 }
357 
ListInputMethodSubtype(napi_env env,napi_callback_info info)358 napi_value JsGetInputMethodSetting::ListInputMethodSubtype(napi_env env, napi_callback_info info)
359 {
360     IMSA_HILOGD("run in ListInputMethodSubtype");
361     auto ctxt = std::make_shared<ListInputContext>();
362     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
363         PARAM_CHECK_RETURN(env, argc > 0, "should has one parameter.", TYPE_NONE, napi_invalid_arg);
364         napi_valuetype valueType = napi_undefined;
365         napi_typeof(env, argv[0], &valueType);
366         PARAM_CHECK_RETURN(env, valueType == napi_object, "inputMethodProperty", TYPE_OBJECT, napi_invalid_arg);
367         napi_status status = JsGetInputMethodSetting::GetInputMethodProperty(env, argv[0], ctxt);
368         return status;
369     };
370     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
371         *result = JsInputMethod::GetJSInputMethodSubProperties(env, ctxt->subProperties);
372         return napi_ok;
373     };
374     auto exec = [ctxt](AsyncCall::Context *ctx) {
375         int32_t errCode =
376             InputMethodController::GetInstance()->ListInputMethodSubtype(ctxt->property, ctxt->subProperties);
377         if (errCode == ErrorCode::NO_ERROR) {
378             IMSA_HILOGI("exec ---- ListInputMethodSubtype success");
379             ctxt->status = napi_ok;
380             ctxt->SetState(ctxt->status);
381             return;
382         }
383         ctxt->SetErrorCode(errCode);
384     };
385     ctxt->SetAction(std::move(input), std::move(output));
386     // 2 means JsAPI:listInputMethodSubtype has 2 params at most.
387     AsyncCall asyncCall(env, info, ctxt, 2);
388     return asyncCall.Call(env, exec, "listInputMethodSubtype");
389 }
390 
ListCurrentInputMethodSubtype(napi_env env,napi_callback_info info)391 napi_value JsGetInputMethodSetting::ListCurrentInputMethodSubtype(napi_env env, napi_callback_info info)
392 {
393     IMSA_HILOGD("run in ListCurrentInputMethodSubtype");
394     auto ctxt = std::make_shared<ListInputContext>();
395     auto input = [ctxt](
396                      napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { return napi_ok; };
397     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
398         *result = JsInputMethod::GetJSInputMethodSubProperties(env, ctxt->subProperties);
399         return napi_ok;
400     };
401     auto exec = [ctxt](AsyncCall::Context *ctx) {
402         int32_t errCode = InputMethodController::GetInstance()->ListCurrentInputMethodSubtype(ctxt->subProperties);
403         if (errCode == ErrorCode::NO_ERROR) {
404             IMSA_HILOGI("exec ---- ListCurrentInputMethodSubtype success");
405             ctxt->status = napi_ok;
406             ctxt->SetState(ctxt->status);
407             return;
408         }
409         ctxt->SetErrorCode(errCode);
410     };
411     ctxt->SetAction(std::move(input), std::move(output));
412     // 1 means JsAPI:listCurrentInputMethodSubtype has 1 params at most.
413     AsyncCall asyncCall(env, info, ctxt, 1);
414     return asyncCall.Call(env, exec, "listCurrentInputMethodSubtype");
415 }
416 
IsPanelShown(napi_env env,napi_callback_info info)417 napi_value JsGetInputMethodSetting::IsPanelShown(napi_env env, napi_callback_info info)
418 {
419     IMSA_HILOGD("run in JsGetInputMethodSetting");
420     // 1 means required param num
421     size_t argc = 1;
422     napi_value argv[1] = { nullptr };
423     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
424     // 1 means least param num
425     PARAM_CHECK_RETURN(env, argc >= 1, "should has 1 parameters!", TYPE_NONE, JsUtil::Const::Null(env));
426     // 0 means parameter of info<PanelInfo>
427     napi_valuetype valueType = napi_undefined;
428     napi_typeof(env, argv[0], &valueType);
429     PARAM_CHECK_RETURN(env, valueType == napi_object, " panelInfo: ", TYPE_OBJECT, JsUtil::Const::Null(env));
430 
431     PanelInfo panelInfo;
432     napi_status status = JsUtils::GetValue(env, argv[0], panelInfo);
433     PARAM_CHECK_RETURN(env, status == napi_ok, "failed to get PanelInfo value", TYPE_NONE, JsUtil::Const::Null(env));
434 
435     bool isShown = false;
436     int32_t errorCode = InputMethodController::GetInstance()->IsPanelShown(panelInfo, isShown);
437     if (errorCode != ErrorCode::NO_ERROR) {
438         JsUtils::ThrowException(env, JsUtils::Convert(errorCode), "failed to query is panel shown", TYPE_NONE);
439         return JsUtil::Const::Null(env);
440     }
441     return JsUtil::GetValue(env, isShown);
442 }
443 
RegisterListener(napi_value callback,std::string type,std::shared_ptr<JSCallbackObject> callbackObj)444 int32_t JsGetInputMethodSetting::RegisterListener(
445     napi_value callback, std::string type, std::shared_ptr<JSCallbackObject> callbackObj)
446 {
447     IMSA_HILOGD("RegisterListener %{public}s", type.c_str());
448     std::lock_guard<std::recursive_mutex> lock(mutex_);
449     if (jsCbMap_.empty()) {
450         InputMethodController::GetInstance()->SetSettingListener(inputMethod_);
451     }
452     if (!jsCbMap_.empty() && jsCbMap_.find(type) == jsCbMap_.end()) {
453         IMSA_HILOGI("start type: %{public}s listening.", type.c_str());
454     }
455 
456     auto callbacks = jsCbMap_[type];
457     bool ret = std::any_of(callbacks.begin(), callbacks.end(), [&callback](std::shared_ptr<JSCallbackObject> cb) {
458         return JsUtils::Equals(cb->env_, callback, cb->callback_, cb->threadId_);
459     });
460     if (ret) {
461         IMSA_HILOGD("JsGetInputMethodSetting callback already registered!");
462         return ErrorCode::NO_ERROR;
463     }
464 
465     IMSA_HILOGI("Add %{public}s callbackObj into jsCbMap_", type.c_str());
466     jsCbMap_[type].push_back(std::move(callbackObj));
467     return ErrorCode::NO_ERROR;
468 }
469 
Subscribe(napi_env env,napi_callback_info info)470 napi_value JsGetInputMethodSetting::Subscribe(napi_env env, napi_callback_info info)
471 {
472     size_t argc = ARGC_TWO;
473     napi_value argv[ARGC_TWO] = { nullptr };
474     napi_value thisVar = nullptr;
475     void *data = nullptr;
476     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
477     std::string type;
478     // 2 means least param num.
479     if (argc < 2 || !JsUtil::GetValue(env, argv[0], type)
480         || !EventChecker::IsValidEventType(EventSubscribeModule::INPUT_METHOD_SETTING, type)
481         || JsUtil::GetType(env, argv[1]) != napi_function) {
482         IMSA_HILOGE("Subscribe failed, type:%{public}s", type.c_str());
483         return nullptr;
484     }
485     IMSA_HILOGD("Subscribe type:%{public}s.", type.c_str());
486     auto engine = reinterpret_cast<JsGetInputMethodSetting *>(JsUtils::GetNativeSelf(env, info));
487     if (engine == nullptr) {
488         return nullptr;
489     }
490     std::shared_ptr<JSCallbackObject> callback =
491         std::make_shared<JSCallbackObject>(env, argv[ARGC_ONE], std::this_thread::get_id());
492     auto ret = InputMethodController::GetInstance()->UpdateListenEventFlag(type, true);
493     if (ret == ErrorCode::NO_ERROR) {
494         engine->RegisterListener(argv[ARGC_ONE], type, callback);
495     } else {
496         auto errCode = JsUtils::Convert(ret);
497         if (errCode == EXCEPTION_SYSTEM_PERMISSION) {
498             IMSA_HILOGE("UpdateListenEventFlag failed, ret: %{public}d, type: %{public}s", ret, type.c_str());
499             JsUtils::ThrowException(env, errCode, "", TYPE_NONE);
500         }
501     }
502     napi_value result = nullptr;
503     napi_get_null(env, &result);
504     return result;
505 }
506 
UnRegisterListener(napi_value callback,std::string type,bool & isUpdateFlag)507 void JsGetInputMethodSetting::UnRegisterListener(napi_value callback, std::string type, bool &isUpdateFlag)
508 {
509     IMSA_HILOGI("UnRegisterListener %{public}s", type.c_str());
510     std::lock_guard<std::recursive_mutex> lock(mutex_);
511     if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
512         IMSA_HILOGE("methodName: %{public}s already unRegistered!", type.c_str());
513         return;
514     }
515 
516     if (callback == nullptr) {
517         jsCbMap_.erase(type);
518         IMSA_HILOGI("stop all type: %{public}s listening.", type.c_str());
519         isUpdateFlag = true;
520         return;
521     }
522 
523     for (auto item = jsCbMap_[type].begin(); item != jsCbMap_[type].end(); item++) {
524         if (JsUtils::Equals((*item)->env_, callback, (*item)->callback_, (*item)->threadId_)) {
525             jsCbMap_[type].erase(item);
526             break;
527         }
528     }
529 
530     if (jsCbMap_[type].empty()) {
531         IMSA_HILOGI("stop last type: %{public}s listening.", type.c_str());
532         jsCbMap_.erase(type);
533         isUpdateFlag = true;
534     }
535 }
536 
UnSubscribe(napi_env env,napi_callback_info info)537 napi_value JsGetInputMethodSetting::UnSubscribe(napi_env env, napi_callback_info info)
538 {
539     size_t argc = ARGC_TWO;
540     napi_value argv[ARGC_TWO] = { nullptr };
541     napi_value thisVar = nullptr;
542     void *data = nullptr;
543     NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
544     std::string type;
545     // 1 means least param num.
546     if (argc < 1 || !JsUtil::GetValue(env, argv[0], type)
547         || !EventChecker::IsValidEventType(EventSubscribeModule::INPUT_METHOD_SETTING, type)) {
548         IMSA_HILOGE("UnSubscribe failed, type:%{public}s", type.c_str());
549         return nullptr;
550     }
551 
552     // if the second param is not napi_function/napi_null/napi_undefined, return
553     auto paramType = JsUtil::GetType(env, argv[1]);
554     if (paramType != napi_function && paramType != napi_null && paramType != napi_undefined) {
555         return nullptr;
556     }
557     // if the second param is napi_function, delete it, else delete all
558     argv[1] = paramType == napi_function ? argv[1] : nullptr;
559 
560     IMSA_HILOGD("UnSubscribe type:%{public}s.", type.c_str());
561 
562     auto engine = reinterpret_cast<JsGetInputMethodSetting *>(JsUtils::GetNativeSelf(env, info));
563     if (engine == nullptr) {
564         return nullptr;
565     }
566     bool isUpdateFlag = false;
567     engine->UnRegisterListener(argv[ARGC_ONE], type, isUpdateFlag);
568     if (isUpdateFlag) {
569         auto ret = InputMethodController::GetInstance()->UpdateListenEventFlag(type, false);
570         IMSA_HILOGI("UpdateListenEventFlag, ret: %{public}d, type: %{public}s", ret, type.c_str());
571     }
572     napi_value result = nullptr;
573     napi_get_null(env, &result);
574     return result;
575 }
576 
OnImeChange(const Property & property,const SubProperty & subProperty)577 void JsGetInputMethodSetting::OnImeChange(const Property &property, const SubProperty &subProperty)
578 {
579     std::string type = "imeChange";
580     uv_work_t *work = GetUVwork(type, [&property, &subProperty](UvEntry &entry) {
581         entry.property = property;
582         entry.subProperty = subProperty;
583     });
584     if (work == nullptr) {
585         IMSA_HILOGD("failed to get uv entry");
586         return;
587     }
588     IMSA_HILOGI("run in");
589     auto ret = uv_queue_work_with_qos(
590         loop_, work, [](uv_work_t *work) {},
591         [](uv_work_t *work, int status) {
592             std::shared_ptr<UvEntry> entry(static_cast<UvEntry *>(work->data), [work](UvEntry *data) {
593                 delete data;
594                 delete work;
595             });
596             if (entry == nullptr) {
597                 IMSA_HILOGE("OnInputStart:: entryptr is null");
598                 return;
599             }
600             auto getImeChangeProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
601                 if (argc < 2) {
602                     return false;
603                 }
604                 napi_value subProperty = JsInputMethod::GetJsInputMethodSubProperty(env, entry->subProperty);
605                 napi_value property = JsInputMethod::GetJsInputMethodProperty(env, entry->property);
606                 if (subProperty == nullptr || property == nullptr) {
607                     IMSA_HILOGE("get KBCins or TICins failed:");
608                     return false;
609                 }
610                 // 0 means the first param of callback.
611                 args[0] = property;
612                 // 1 means the second param of callback.
613                 args[1] = subProperty;
614                 return true;
615             };
616             // 2 means callback has two params.
617             JsCallbackHandler::Traverse(entry->vecCopy, { 2, getImeChangeProperty });
618         },
619         uv_qos_user_initiated);
620     FreeWorkIfFail(ret, work);
621 }
622 
OnPanelStatusChange(const InputWindowStatus & status,const std::vector<InputWindowInfo> & windowInfo)623 void JsGetInputMethodSetting::OnPanelStatusChange(
624     const InputWindowStatus &status, const std::vector<InputWindowInfo> &windowInfo)
625 {
626     auto it = PANEL_STATUS.find(status);
627     if (it == PANEL_STATUS.end()) {
628         IMSA_HILOGE("invalid panel status");
629         return;
630     }
631     auto type = it->second;
632     uv_work_t *work = GetUVwork(type, [&windowInfo](UvEntry &entry) { entry.windowInfo = windowInfo; });
633     if (work == nullptr) {
634         IMSA_HILOGD("failed to get uv entry");
635         return;
636     }
637     IMSA_HILOGI("status: %{public}u", static_cast<uint32_t>(status));
638     auto ret = uv_queue_work_with_qos(
639         loop_, work, [](uv_work_t *work) {},
640         [](uv_work_t *work, int status) {
641             std::shared_ptr<UvEntry> entry(static_cast<UvEntry *>(work->data), [work](UvEntry *data) {
642                 delete data;
643                 delete work;
644             });
645             if (entry == nullptr) {
646                 IMSA_HILOGE("OnInputStart:: entry is nullptr");
647                 return;
648             }
649             auto getWindowInfo = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
650                 if (argc < 1) {
651                     return false;
652                 }
653                 auto windowInfo = JsUtils::GetValue(env, entry->windowInfo);
654                 if (windowInfo == nullptr) {
655                     IMSA_HILOGE("converse windowInfo failed");
656                     return false;
657                 }
658                 // 0 means the first param of callback.
659                 args[0] = windowInfo;
660                 return true;
661             };
662             // 1 means callback has one param.
663             JsCallbackHandler::Traverse(entry->vecCopy, { 1, getWindowInfo });
664         },
665         uv_qos_user_initiated);
666     FreeWorkIfFail(ret, work);
667 }
668 
GetUVwork(const std::string & type,EntrySetter entrySetter)669 uv_work_t *JsGetInputMethodSetting::GetUVwork(const std::string &type, EntrySetter entrySetter)
670 {
671     IMSA_HILOGD("run in, type: %{public}s", type.c_str());
672     UvEntry *entry = nullptr;
673     {
674         std::lock_guard<std::recursive_mutex> lock(mutex_);
675 
676         if (jsCbMap_[type].empty()) {
677             IMSA_HILOGD("%{public}s cb-vector is empty", type.c_str());
678             return nullptr;
679         }
680         entry = new (std::nothrow) UvEntry(jsCbMap_[type], type);
681         if (entry == nullptr) {
682             IMSA_HILOGE("entry ptr is nullptr!");
683             return nullptr;
684         }
685         if (entrySetter != nullptr) {
686             entrySetter(*entry);
687         }
688     }
689     uv_work_t *work = new (std::nothrow) uv_work_t;
690     if (work == nullptr) {
691         IMSA_HILOGE("entry ptr is nullptr!");
692         delete entry;
693         return nullptr;
694     }
695     work->data = entry;
696     return work;
697 }
FreeWorkIfFail(int ret,uv_work_t * work)698 void JsGetInputMethodSetting::FreeWorkIfFail(int ret, uv_work_t *work)
699 {
700     if (ret == 0 || work == nullptr) {
701         return;
702     }
703 
704     UvEntry *data = static_cast<UvEntry *>(work->data);
705     delete data;
706     delete work;
707     IMSA_HILOGE("uv_queue_work failed retCode:%{public}d", ret);
708 }
709 } // namespace MiscServices
710 } // namespace OHOS