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