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 "ime_event_monitor_manager_impl.h"
20 #include "input_client_info.h"
21 #include "input_method_controller.h"
22 #include "input_method_status.h"
23 #include "js_callback_handler.h"
24 #include "js_input_method.h"
25 #include "js_util.h"
26 #include "js_utils.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::unordered_map<std::string, uint32_t> EVENT_TYPE{ { "imeChange", EVENT_IME_CHANGE_MASK },
40 { "imeShow", EVENT_IME_SHOW_MASK }, { "imeHide", EVENT_IME_HIDE_MASK } };
41 std::mutex JsGetInputMethodSetting::msMutex_;
42 std::shared_ptr<JsGetInputMethodSetting> JsGetInputMethodSetting::inputMethod_{ nullptr };
43 std::mutex JsGetInputMethodSetting::eventHandlerMutex_;
44 std::shared_ptr<AppExecFwk::EventHandler> JsGetInputMethodSetting::handler_{ nullptr };
Init(napi_env env,napi_value exports)45 napi_value JsGetInputMethodSetting::Init(napi_env env, napi_value exports)
46 {
47 napi_value maxTypeNumber = nullptr;
48 NAPI_CALL(env, napi_create_int32(env, MAX_TYPE_NUM, &maxTypeNumber));
49
50 napi_property_descriptor descriptor[] = {
51 DECLARE_NAPI_FUNCTION("getInputMethodSetting", GetInputMethodSetting),
52 DECLARE_NAPI_FUNCTION("getSetting", GetSetting),
53 DECLARE_NAPI_PROPERTY("MAX_TYPE_NUM", maxTypeNumber),
54 };
55 NAPI_CALL(env,
56 napi_define_properties(env, exports, sizeof(descriptor) / sizeof(napi_property_descriptor), descriptor));
57
58 napi_property_descriptor properties[] = {
59 DECLARE_NAPI_FUNCTION("listInputMethod", ListInputMethod),
60 DECLARE_NAPI_FUNCTION("listInputMethodSubtype", ListInputMethodSubtype),
61 DECLARE_NAPI_FUNCTION("listCurrentInputMethodSubtype", ListCurrentInputMethodSubtype),
62 DECLARE_NAPI_FUNCTION("getInputMethods", GetInputMethods),
63 DECLARE_NAPI_FUNCTION("getInputMethodsSync", GetInputMethodsSync),
64 DECLARE_NAPI_FUNCTION("getAllInputMethods", GetAllInputMethods),
65 DECLARE_NAPI_FUNCTION("getAllInputMethodsSync", GetAllInputMethodsSync),
66 DECLARE_NAPI_FUNCTION("displayOptionalInputMethod", DisplayOptionalInputMethod),
67 DECLARE_NAPI_FUNCTION("showOptionalInputMethods", ShowOptionalInputMethods),
68 DECLARE_NAPI_FUNCTION("isPanelShown", IsPanelShown),
69 DECLARE_NAPI_FUNCTION("enableInputMethod", EnableInputMethod),
70 DECLARE_NAPI_FUNCTION("getInputMethodState", GetInputMethodState),
71 DECLARE_NAPI_FUNCTION("on", Subscribe),
72 DECLARE_NAPI_FUNCTION("off", UnSubscribe),
73 };
74 napi_value cons = nullptr;
75 NAPI_CALL(env, napi_define_class(env, IMS_CLASS_NAME.c_str(), IMS_CLASS_NAME.size(), JsConstructor, nullptr,
76 sizeof(properties) / sizeof(napi_property_descriptor), properties, &cons));
77 NAPI_CALL(env, napi_create_reference(env, cons, 1, &IMSRef_));
78 NAPI_CALL(env, napi_set_named_property(env, exports, IMS_CLASS_NAME.c_str(), cons));
79 return exports;
80 }
81
JsConstructor(napi_env env,napi_callback_info cbinfo)82 napi_value JsGetInputMethodSetting::JsConstructor(napi_env env, napi_callback_info cbinfo)
83 {
84 {
85 std::lock_guard<std::mutex> lock(eventHandlerMutex_);
86 handler_ = AppExecFwk::EventHandler::Current();
87 }
88 napi_value thisVar = nullptr;
89 NAPI_CALL(env, napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr));
90
91 auto delegate = GetInputMethodSettingInstance();
92 if (delegate == nullptr) {
93 IMSA_HILOGE("delegate is nullptr!");
94 napi_value result = nullptr;
95 napi_get_null(env, &result);
96 return result;
97 }
98 napi_status status = napi_wrap(
99 env, thisVar, delegate.get(), [](napi_env env, void *data, void *hint) {}, nullptr, nullptr);
100 if (status != napi_ok) {
101 IMSA_HILOGE("failed to wrap: %{public}d", status);
102 return nullptr;
103 }
104 return thisVar;
105 }
106
GetJsConstProperty(napi_env env,uint32_t num)107 napi_value JsGetInputMethodSetting::GetJsConstProperty(napi_env env, uint32_t num)
108 {
109 napi_value jsNumber = nullptr;
110 napi_create_int32(env, num, &jsNumber);
111 return jsNumber;
112 }
113
GetInputMethodSettingInstance()114 std::shared_ptr<JsGetInputMethodSetting> JsGetInputMethodSetting::GetInputMethodSettingInstance()
115 {
116 if (inputMethod_ == nullptr) {
117 std::lock_guard<std::mutex> lock(msMutex_);
118 if (inputMethod_ == nullptr) {
119 auto engine = std::make_shared<JsGetInputMethodSetting>();
120 if (engine == nullptr) {
121 IMSA_HILOGE("engine is nullptr!");
122 return nullptr;
123 }
124 inputMethod_ = engine;
125 }
126 }
127 return inputMethod_;
128 }
129
GetSetting(napi_env env,napi_callback_info info)130 napi_value JsGetInputMethodSetting::GetSetting(napi_env env, napi_callback_info info)
131 {
132 return GetIMSetting(env, info, true);
133 }
134
GetInputMethodSetting(napi_env env,napi_callback_info info)135 napi_value JsGetInputMethodSetting::GetInputMethodSetting(napi_env env, napi_callback_info info)
136 {
137 return GetIMSetting(env, info, false);
138 }
139
GetIMSetting(napi_env env,napi_callback_info info,bool needThrowException)140 napi_value JsGetInputMethodSetting::GetIMSetting(napi_env env, napi_callback_info info, bool needThrowException)
141 {
142 napi_value instance = nullptr;
143 napi_value cons = nullptr;
144 if (napi_get_reference_value(env, IMSRef_, &cons) != napi_ok) {
145 IMSA_HILOGE("failed to get reference value!");
146 if (needThrowException) {
147 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_SETTINGS, "", TYPE_OBJECT);
148 }
149 return nullptr;
150 }
151 if (napi_new_instance(env, cons, 0, nullptr, &instance) != napi_ok) {
152 IMSA_HILOGE("failed to new instance!");
153 if (needThrowException) {
154 JsUtils::ThrowException(env, IMFErrorCode::EXCEPTION_SETTINGS, "", TYPE_OBJECT);
155 }
156 return nullptr;
157 }
158 return instance;
159 }
160
GetInputMethodProperty(napi_env env,napi_value argv,std::shared_ptr<ListInputContext> ctxt)161 napi_status JsGetInputMethodSetting::GetInputMethodProperty(napi_env env, napi_value argv,
162 std::shared_ptr<ListInputContext> ctxt)
163 {
164 napi_valuetype valueType = napi_undefined;
165 napi_typeof(env, argv, &valueType);
166 PARAM_CHECK_RETURN(env, valueType == napi_object, "inputMethodProperty type must be InputMethodProperty",
167 TYPE_NONE, napi_invalid_arg);
168 napi_value result = nullptr;
169 napi_get_named_property(env, argv, "name", &result);
170 JsUtils::GetValue(env, result, ctxt->property.name);
171
172 result = nullptr;
173 napi_get_named_property(env, argv, "id", &result);
174 JsUtils::GetValue(env, result, ctxt->property.id);
175
176 if (ctxt->property.name.empty() || ctxt->property.id.empty()) {
177 result = nullptr;
178 napi_get_named_property(env, argv, "packageName", &result);
179 JsUtils::GetValue(env, result, ctxt->property.name);
180
181 result = nullptr;
182 napi_get_named_property(env, argv, "methodId", &result);
183 JsUtils::GetValue(env, result, ctxt->property.id);
184 }
185 PARAM_CHECK_RETURN(env, (!ctxt->property.name.empty() && !ctxt->property.id.empty()),
186 "name and id must be string and cannot empty", TYPE_NONE, napi_invalid_arg);
187 IMSA_HILOGD("methodId: %{public}s, packageName: %{public}s.", ctxt->property.id.c_str(),
188 ctxt->property.name.c_str());
189 return napi_ok;
190 }
191
ListInputMethod(napi_env env,napi_callback_info info)192 napi_value JsGetInputMethodSetting::ListInputMethod(napi_env env, napi_callback_info info)
193 {
194 IMSA_HILOGD("start ListInputMethod");
195 auto ctxt = std::make_shared<ListInputContext>();
196 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
197 ctxt->inputMethodStatus = InputMethodStatus::ALL;
198 return napi_ok;
199 };
200 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
201 *result = JsInputMethod::GetJSInputMethodProperties(env, ctxt->properties);
202 return napi_ok;
203 };
204 auto exec = [ctxt](AsyncCall::Context *ctx) {
205 int32_t errCode = ErrorCode::ERROR_EX_NULL_POINTER;
206 auto instance = InputMethodController::GetInstance();
207 if (instance != nullptr) {
208 errCode = instance->ListInputMethod(ctxt->properties);
209 }
210 if (errCode == ErrorCode::NO_ERROR) {
211 IMSA_HILOGI("exec ListInputMethod success");
212 ctxt->status = napi_ok;
213 ctxt->SetState(ctxt->status);
214 return;
215 }
216 ctxt->SetErrorCode(errCode);
217 };
218 ctxt->SetAction(std::move(input), std::move(output));
219 // 1 means JsAPI:listInputMethod has 1 param at most.
220 AsyncCall asyncCall(env, info, ctxt, 1);
221 return asyncCall.Call(env, exec, "listInputMethod");
222 }
223
GetInputMethods(napi_env env,napi_callback_info info)224 napi_value JsGetInputMethodSetting::GetInputMethods(napi_env env, napi_callback_info info)
225 {
226 IMSA_HILOGD("run in GetInputMethods");
227 auto ctxt = std::make_shared<ListInputContext>();
228 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
229 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_invalid_arg);
230 bool enable = false;
231 // 0 means first param index
232 napi_status status = JsUtils::GetValue(env, argv[0], enable);
233 PARAM_CHECK_RETURN(env, status == napi_ok, "enable type must be boolean!", TYPE_NONE, napi_invalid_arg);
234 ctxt->inputMethodStatus = enable ? InputMethodStatus::ENABLE : InputMethodStatus::DISABLE;
235 return napi_ok;
236 };
237 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
238 *result = JsInputMethod::GetJSInputMethodProperties(env, ctxt->properties);
239 return napi_ok;
240 };
241 auto exec = [ctxt](AsyncCall::Context *ctx) {
242 int32_t errCode = ErrorCode::ERROR_EX_NULL_POINTER;
243 auto instance = InputMethodController::GetInstance();
244 if (instance != nullptr) {
245 errCode = instance->ListInputMethod(ctxt->inputMethodStatus == ENABLE, ctxt->properties);
246 }
247 if (errCode == ErrorCode::NO_ERROR) {
248 IMSA_HILOGI("exec GetInputMethods success.");
249 ctxt->status = napi_ok;
250 ctxt->SetState(ctxt->status);
251 return;
252 }
253 ctxt->SetErrorCode(errCode);
254 };
255 ctxt->SetAction(std::move(input), std::move(output));
256 // 2 means JsAPI:getInputMethods has 2 params at most.
257 AsyncCall asyncCall(env, info, ctxt, 2);
258 return asyncCall.Call(env, exec, "getInputMethods");
259 }
260
GetInputMethodsSync(napi_env env,napi_callback_info info)261 napi_value JsGetInputMethodSetting::GetInputMethodsSync(napi_env env, napi_callback_info info)
262 {
263 IMSA_HILOGD("run in");
264 size_t argc = ARGC_MAX;
265 napi_value argv[ARGC_MAX] = { nullptr };
266 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
267
268 bool enable = false;
269 // 0 means first param index
270 PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, JsUtil::Const::Null(env));
271 PARAM_CHECK_RETURN(env, JsUtils::GetValue(env, argv[0], enable) == napi_ok, "enable type must be boolean!",
272 TYPE_NONE, JsUtil::Const::Null(env));
273
274 std::vector<Property> properties;
275 int32_t ret = ErrorCode::ERROR_EX_NULL_POINTER;
276 auto instance = InputMethodController::GetInstance();
277 if (instance != nullptr) {
278 ret = instance->ListInputMethod(enable, properties);
279 }
280 if (ret != ErrorCode::NO_ERROR) {
281 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get input methods!", TYPE_NONE);
282 return JsUtil::Const::Null(env);
283 }
284 return JsInputMethod::GetJSInputMethodProperties(env, properties);
285 }
286
GetAllInputMethods(napi_env env,napi_callback_info info)287 napi_value JsGetInputMethodSetting::GetAllInputMethods(napi_env env, napi_callback_info info)
288 {
289 IMSA_HILOGD("start GetAllInputMethods.");
290 auto ctxt = std::make_shared<ListInputContext>();
291 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
292 return napi_ok;
293 };
294 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
295 *result = JsInputMethod::GetJSInputMethodProperties(env, ctxt->properties);
296 return napi_ok;
297 };
298 auto exec = [ctxt](AsyncCall::Context *ctx) {
299 int32_t errCode = ErrorCode::ERROR_EX_NULL_POINTER;
300 auto instance = InputMethodController::GetInstance();
301 if (instance != nullptr) {
302 errCode = instance->ListInputMethod(ctxt->properties);
303 }
304 if (errCode == ErrorCode::NO_ERROR) {
305 IMSA_HILOGI("exec GetInputMethods success.");
306 ctxt->status = napi_ok;
307 ctxt->SetState(ctxt->status);
308 return;
309 }
310 ctxt->SetErrorCode(errCode);
311 };
312 ctxt->SetAction(std::move(input), std::move(output));
313 // 1 means JsAPI:getAllInputMethods has 1 param at most.
314 AsyncCall asyncCall(env, info, ctxt, 1);
315 return asyncCall.Call(env, exec, "getInputMethods");
316 }
317
GetAllInputMethodsSync(napi_env env,napi_callback_info info)318 napi_value JsGetInputMethodSetting::GetAllInputMethodsSync(napi_env env, napi_callback_info info)
319 {
320 IMSA_HILOGD("run in");
321 std::vector<Property> properties;
322 int32_t ret = ErrorCode::ERROR_EX_NULL_POINTER;
323 auto instance = InputMethodController::GetInstance();
324 if (instance != nullptr) {
325 ret = instance->ListInputMethod(properties);
326 }
327 if (ret != ErrorCode::NO_ERROR) {
328 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get input methods", TYPE_NONE);
329 return JsUtil::Const::Null(env);
330 }
331 return JsInputMethod::GetJSInputMethodProperties(env, properties);
332 }
333
DisplayOptionalInputMethod(napi_env env,napi_callback_info info)334 napi_value JsGetInputMethodSetting::DisplayOptionalInputMethod(napi_env env, napi_callback_info info)
335 {
336 IMSA_HILOGD("start JsGetInputMethodSetting.");
337 auto ctxt = std::make_shared<DisplayOptionalInputMethodContext>();
338 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
339 return napi_ok;
340 };
341 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
342 auto exec = [ctxt](AsyncCall::Context *ctx) {
343 int32_t errCode = ErrorCode::ERROR_EX_NULL_POINTER;
344 auto instance = InputMethodController::GetInstance();
345 if (instance != nullptr) {
346 errCode = instance->DisplayOptionalInputMethod();
347 }
348 if (errCode == ErrorCode::NO_ERROR) {
349 IMSA_HILOGI("exec DisplayOptionalInputMethod success.");
350 ctxt->status = napi_ok;
351 ctxt->SetState(ctxt->status);
352 }
353 };
354 ctxt->SetAction(std::move(input), std::move(output));
355 // 1 means JsAPI:displayOptionalInputMethod has 1 param at most.
356 AsyncCall asyncCall(env, info, ctxt, 1);
357 return asyncCall.Call(env, exec, "displayOptionalInputMethod");
358 }
359
ShowOptionalInputMethods(napi_env env,napi_callback_info info)360 napi_value JsGetInputMethodSetting::ShowOptionalInputMethods(napi_env env, napi_callback_info info)
361 {
362 IMSA_HILOGD("start JsGetInputMethodSetting.");
363 auto ctxt = std::make_shared<DisplayOptionalInputMethodContext>();
364 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
365 return napi_ok;
366 };
367 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
368 napi_status status = napi_get_boolean(env, ctxt->isDisplayed, result);
369 IMSA_HILOGI("output get boolean != nullptr[%{public}d].", result != nullptr);
370 return status;
371 };
372 auto exec = [ctxt](AsyncCall::Context *ctx) {
373 int32_t errCode = ErrorCode::ERROR_EX_NULL_POINTER;
374 auto instance = InputMethodController::GetInstance();
375 if (instance != nullptr) {
376 errCode = instance->DisplayOptionalInputMethod();
377 }
378 if (errCode == ErrorCode::NO_ERROR) {
379 IMSA_HILOGI("exec DisplayOptionalInputMethod success");
380 ctxt->status = napi_ok;
381 ctxt->SetState(ctxt->status);
382 ctxt->isDisplayed = true;
383 return;
384 } else {
385 ctxt->SetErrorCode(errCode);
386 }
387 };
388 ctxt->SetAction(std::move(input), std::move(output));
389 // 1 means JsAPI:showOptionalInputMethods has 1 param at most.
390 AsyncCall asyncCall(env, info, ctxt, 1);
391 return asyncCall.Call(env, exec, "showOptionalInputMethods");
392 }
393
ListInputMethodSubtype(napi_env env,napi_callback_info info)394 napi_value JsGetInputMethodSetting::ListInputMethodSubtype(napi_env env, napi_callback_info info)
395 {
396 IMSA_HILOGD("run in ListInputMethodSubtype");
397 auto ctxt = std::make_shared<ListInputContext>();
398 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
399 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_invalid_arg);
400 napi_valuetype valueType = napi_undefined;
401 napi_typeof(env, argv[0], &valueType);
402 PARAM_CHECK_RETURN(env, valueType == napi_object, "inputMethodProperty type must be InputMethodProperty!",
403 TYPE_NONE, napi_invalid_arg);
404 napi_status status = JsGetInputMethodSetting::GetInputMethodProperty(env, argv[0], ctxt);
405 return status;
406 };
407 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
408 *result = JsInputMethod::GetJSInputMethodSubProperties(env, ctxt->subProperties);
409 return napi_ok;
410 };
411 auto exec = [ctxt](AsyncCall::Context *ctx) {
412 int32_t errCode = ErrorCode::ERROR_EX_NULL_POINTER;
413 auto instance = InputMethodController::GetInstance();
414 if (instance != nullptr) {
415 errCode = instance->ListInputMethodSubtype(ctxt->property, ctxt->subProperties);
416 }
417 if (errCode == ErrorCode::NO_ERROR) {
418 IMSA_HILOGI("exec ListInputMethodSubtype success");
419 ctxt->status = napi_ok;
420 ctxt->SetState(ctxt->status);
421 return;
422 }
423 ctxt->SetErrorCode(errCode);
424 };
425 ctxt->SetAction(std::move(input), std::move(output));
426 // 2 means JsAPI:listInputMethodSubtype has 2 params at most.
427 AsyncCall asyncCall(env, info, ctxt, 2);
428 return asyncCall.Call(env, exec, "listInputMethodSubtype");
429 }
430
ListCurrentInputMethodSubtype(napi_env env,napi_callback_info info)431 napi_value JsGetInputMethodSetting::ListCurrentInputMethodSubtype(napi_env env, napi_callback_info info)
432 {
433 IMSA_HILOGD("run in ListCurrentInputMethodSubtype");
434 auto ctxt = std::make_shared<ListInputContext>();
435 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
436 return napi_ok;
437 };
438 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
439 *result = JsInputMethod::GetJSInputMethodSubProperties(env, ctxt->subProperties);
440 return napi_ok;
441 };
442 auto exec = [ctxt](AsyncCall::Context *ctx) {
443 int32_t errCode = ErrorCode::ERROR_EX_NULL_POINTER;
444 auto instance = InputMethodController::GetInstance();
445 if (instance != nullptr) {
446 errCode = instance->ListCurrentInputMethodSubtype(ctxt->subProperties);
447 }
448 if (errCode == ErrorCode::NO_ERROR) {
449 IMSA_HILOGI("exec ListCurrentInputMethodSubtype success.");
450 ctxt->status = napi_ok;
451 ctxt->SetState(ctxt->status);
452 return;
453 }
454 ctxt->SetErrorCode(errCode);
455 };
456 ctxt->SetAction(std::move(input), std::move(output));
457 // 1 means JsAPI:listCurrentInputMethodSubtype has 1 param at most.
458 AsyncCall asyncCall(env, info, ctxt, 1);
459 return asyncCall.Call(env, exec, "listCurrentInputMethodSubtype");
460 }
461
IsPanelShown(napi_env env,napi_callback_info info)462 napi_value JsGetInputMethodSetting::IsPanelShown(napi_env env, napi_callback_info info)
463 {
464 IMSA_HILOGD("start JsGetInputMethodSetting");
465 // 1 means required param num
466 size_t argc = 1;
467 napi_value argv[1] = { nullptr };
468 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
469 // 1 means least param num
470 PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, JsUtil::Const::Null(env));
471 // 0 means parameter of info<PanelInfo>
472 napi_valuetype valueType = napi_undefined;
473 napi_typeof(env, argv[0], &valueType);
474 PARAM_CHECK_RETURN(env, valueType == napi_object, "panelInfo type must be PanelInfo!", TYPE_NONE,
475 JsUtil::Const::Null(env));
476
477 PanelInfo panelInfo;
478 napi_status status = JsUtils::GetValue(env, argv[0], panelInfo);
479 PARAM_CHECK_RETURN(env, status == napi_ok, "panelInfo covert failed!", TYPE_NONE, JsUtil::Const::Null(env));
480
481 bool isShown = false;
482 int32_t errCode = ErrorCode::ERROR_EX_NULL_POINTER;
483 auto instance = InputMethodController::GetInstance();
484 if (instance != nullptr) {
485 errCode = instance->IsPanelShown(panelInfo, isShown);
486 }
487 if (errCode != ErrorCode::NO_ERROR) {
488 JsUtils::ThrowException(env, JsUtils::Convert(errCode), "failed to query is panel shown!", TYPE_NONE);
489 return JsUtil::Const::Null(env);
490 }
491 return JsUtil::GetValue(env, isShown);
492 }
493
EnableInputMethod(napi_env env,napi_callback_info info)494 napi_value JsGetInputMethodSetting::EnableInputMethod(napi_env env, napi_callback_info info)
495 {
496 IMSA_HILOGD("run in");
497 auto ctxt = std::make_shared<EnableInputContext>();
498 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
499 PARAM_CHECK_RETURN(env, argc > 2, "at least three parameters is required!", TYPE_NONE, napi_invalid_arg);
500 PARAM_CHECK_RETURN(env,
501 JsUtil::GetType(env, argv[0]) == napi_string && JsUtil::GetValue(env, argv[0], ctxt->bundleName),
502 "bundleName type must be string!", TYPE_NONE, napi_invalid_arg);
503 PARAM_CHECK_RETURN(env,
504 JsUtil::GetType(env, argv[1]) == napi_string && JsUtil::GetValue(env, argv[1], ctxt->extName),
505 "extensionName type must be string!", TYPE_NONE, napi_invalid_arg);
506 int32_t status = 0;
507 PARAM_CHECK_RETURN(env,
508 JsUtil::GetType(env, argv[2]) == napi_number && JsUtil::GetValue(env, argv[2], status)
509 && (status >= static_cast<int32_t>(EnabledStatus::DISABLED)
510 && status <= static_cast<int32_t>(EnabledStatus::FULL_EXPERIENCE_MODE)),
511 "enabledState type must be EnabledState!", TYPE_NONE, napi_invalid_arg);
512 ctxt->enabledStatus = static_cast<EnabledStatus>(status);
513 return napi_ok;
514 };
515 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
516 auto exec = [ctxt](AsyncCall::Context *ctx) {
517 if (ctxt->bundleName.empty() || ctxt->extName.empty()) {
518 ctxt->SetErrorCode(ErrorCode::ERROR_IME_NOT_FOUND);
519 return;
520 }
521 int32_t errCode =
522 InputMethodController::GetInstance()->EnableIme(ctxt->bundleName, ctxt->extName, ctxt->enabledStatus);
523 if (errCode == ErrorCode::NO_ERROR) {
524 ctxt->status = napi_ok;
525 ctxt->SetState(ctxt->status);
526 return;
527 }
528 ctxt->SetErrorCode(errCode);
529 };
530 ctxt->SetAction(std::move(input), std::move(output));
531 // 3 means JsAPI:enableInputMethod has 3 params at most.
532 AsyncCall asyncCall(env, info, ctxt, 3);
533 return asyncCall.Call(env, exec, "EnableInputMethod");
534 }
535
GetInputMethodState(napi_env env,napi_callback_info info)536 napi_value JsGetInputMethodSetting::GetInputMethodState(napi_env env, napi_callback_info info)
537 {
538 auto ctxt = std::make_shared<GetInputMethodStateContext>();
539 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
540 int32_t enableStatus = static_cast<int32_t>(ctxt->enableStatus);
541 *result = JsUtil::GetValue(env, enableStatus);
542 return napi_ok;
543 };
544 auto exec = [ctxt](AsyncCall::Context *ctx) {
545 int32_t errCode = ErrorCode::ERROR_EX_NULL_POINTER;
546 auto instance = InputMethodController::GetInstance();
547 if (instance != nullptr) {
548 errCode = instance->GetInputMethodState(ctxt->enableStatus);
549 }
550 if (errCode == ErrorCode::NO_ERROR) {
551 ctxt->status = napi_ok;
552 ctxt->SetState(ctxt->status);
553 return;
554 }
555 ctxt->SetErrorCode(errCode);
556 };
557 ctxt->SetAction(nullptr, std::move(output));
558 // 0 means JsAPI:GetInputMethodState has no param.
559 AsyncCall asyncCall(env, info, ctxt, 0);
560 return asyncCall.Call(env, exec, "GetInputMethodState");
561 }
562
RegisterListener(napi_value callback,std::string type,std::shared_ptr<JSCallbackObject> callbackObj)563 int32_t JsGetInputMethodSetting::RegisterListener(napi_value callback, std::string type,
564 std::shared_ptr<JSCallbackObject> callbackObj)
565 {
566 IMSA_HILOGD("register listener: %{public}s", type.c_str());
567 std::lock_guard<std::recursive_mutex> lock(mutex_);
568 if (!jsCbMap_.empty() && jsCbMap_.find(type) == jsCbMap_.end()) {
569 IMSA_HILOGI("start type: %{public}s listening.", type.c_str());
570 }
571
572 auto callbacks = jsCbMap_[type];
573 bool ret = std::any_of(callbacks.begin(), callbacks.end(), [&callback](std::shared_ptr<JSCallbackObject> cb) {
574 if (cb == nullptr) {
575 return false;
576 }
577 return JsUtils::Equals(cb->env_, callback, cb->callback_, cb->threadId_);
578 });
579 if (ret) {
580 IMSA_HILOGD("callback already registered!");
581 return ErrorCode::NO_ERROR;
582 }
583
584 IMSA_HILOGI("add %{public}s callbackObj into jsCbMap_.", type.c_str());
585 jsCbMap_[type].push_back(std::move(callbackObj));
586 return ErrorCode::NO_ERROR;
587 }
588
Subscribe(napi_env env,napi_callback_info info)589 napi_value JsGetInputMethodSetting::Subscribe(napi_env env, napi_callback_info info)
590 {
591 size_t argc = ARGC_TWO;
592 napi_value argv[ARGC_TWO] = { nullptr };
593 napi_value thisVar = nullptr;
594 void *data = nullptr;
595 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
596 std::string type;
597 // 2 means least param num.
598 if (argc < 2 || !JsUtil::GetValue(env, argv[0], type) ||
599 !EventChecker::IsValidEventType(EventSubscribeModule::INPUT_METHOD_SETTING, type) ||
600 JsUtil::GetType(env, argv[1]) != napi_function) {
601 IMSA_HILOGE("subscribe failed, type:%{public}s", type.c_str());
602 return nullptr;
603 }
604 IMSA_HILOGD("subscribe type: %{public}s.", type.c_str());
605 auto engine = reinterpret_cast<JsGetInputMethodSetting *>(JsUtils::GetNativeSelf(env, info));
606 if (engine == nullptr) {
607 return nullptr;
608 }
609 auto iter = EVENT_TYPE.find(type);
610 if (iter == EVENT_TYPE.end()) {
611 return nullptr;
612 }
613 std::shared_ptr<JSCallbackObject> callback =
614 std::make_shared<JSCallbackObject>(env, argv[ARGC_ONE], std::this_thread::get_id(),
615 AppExecFwk::EventHandler::Current());
616 auto ret = ImeEventMonitorManagerImpl::GetInstance().RegisterImeEventListener(iter->second, inputMethod_);
617 if (ret == ErrorCode::NO_ERROR) {
618 engine->RegisterListener(argv[ARGC_ONE], type, callback);
619 } else {
620 auto errCode = JsUtils::Convert(ret);
621 if (errCode == EXCEPTION_SYSTEM_PERMISSION) {
622 IMSA_HILOGE("failed to UpdateListenEventFlag , ret: %{public}d, type: %{public}s!", ret, type.c_str());
623 JsUtils::ThrowException(env, errCode, "", TYPE_NONE);
624 }
625 }
626 napi_value result = nullptr;
627 napi_get_null(env, &result);
628 return result;
629 }
630
UnRegisterListener(napi_value callback,std::string type,bool & isUpdateFlag)631 void JsGetInputMethodSetting::UnRegisterListener(napi_value callback, std::string type, bool &isUpdateFlag)
632 {
633 IMSA_HILOGI("unregister listener: %{public}s!", type.c_str());
634 std::lock_guard<std::recursive_mutex> lock(mutex_);
635 if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
636 IMSA_HILOGE("methodName: %{public}s already unRegistered!", type.c_str());
637 return;
638 }
639
640 if (callback == nullptr) {
641 jsCbMap_.erase(type);
642 IMSA_HILOGI("stop all type: %{public}s listening.", type.c_str());
643 isUpdateFlag = true;
644 return;
645 }
646
647 for (auto item = jsCbMap_[type].begin(); item != jsCbMap_[type].end(); item++) {
648 if (JsUtils::Equals((*item)->env_, callback, (*item)->callback_, (*item)->threadId_)) {
649 jsCbMap_[type].erase(item);
650 break;
651 }
652 }
653
654 if (jsCbMap_[type].empty()) {
655 IMSA_HILOGI("stop last type: %{public}s listening.", type.c_str());
656 jsCbMap_.erase(type);
657 isUpdateFlag = true;
658 }
659 }
660
UnSubscribe(napi_env env,napi_callback_info info)661 napi_value JsGetInputMethodSetting::UnSubscribe(napi_env env, napi_callback_info info)
662 {
663 size_t argc = ARGC_TWO;
664 napi_value argv[ARGC_TWO] = { nullptr };
665 napi_value thisVar = nullptr;
666 void *data = nullptr;
667 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
668 std::string type;
669 // 1 means least param num.
670 if (argc < 1 || !JsUtil::GetValue(env, argv[0], type) ||
671 !EventChecker::IsValidEventType(EventSubscribeModule::INPUT_METHOD_SETTING, type)) {
672 IMSA_HILOGE("unsubscribe failed, type: %{public}s!", type.c_str());
673 return nullptr;
674 }
675
676 // if the second param is not napi_function/napi_null/napi_undefined, return
677 auto paramType = JsUtil::GetType(env, argv[1]);
678 if (paramType != napi_function && paramType != napi_null && paramType != napi_undefined) {
679 return nullptr;
680 }
681 // if the second param is napi_function, delete it, else delete all
682 argv[1] = paramType == napi_function ? argv[1] : nullptr;
683
684 IMSA_HILOGD("unsubscribe type: %{public}s.", type.c_str());
685
686 auto engine = reinterpret_cast<JsGetInputMethodSetting *>(JsUtils::GetNativeSelf(env, info));
687 if (engine == nullptr) {
688 return nullptr;
689 }
690 bool isUpdateFlag = false;
691 engine->UnRegisterListener(argv[ARGC_ONE], type, isUpdateFlag);
692 auto iter = EVENT_TYPE.find(type);
693 if (iter == EVENT_TYPE.end()) {
694 return nullptr;
695 }
696 if (isUpdateFlag) {
697 auto ret = ImeEventMonitorManagerImpl::GetInstance().UnRegisterImeEventListener(iter->second, inputMethod_);
698 IMSA_HILOGI("UpdateListenEventFlag, ret: %{public}d, type: %{public}s.", ret, type.c_str());
699 }
700 napi_value result = nullptr;
701 napi_get_null(env, &result);
702 return result;
703 }
704
OnImeChange(const Property & property,const SubProperty & subProperty)705 void JsGetInputMethodSetting::OnImeChange(const Property &property, const SubProperty &subProperty)
706 {
707 std::string type = "imeChange";
708 auto entry = GetEntry(type, [&property, &subProperty](UvEntry &entry) {
709 entry.property = property;
710 entry.subProperty = subProperty;
711 });
712 if (entry == nullptr) {
713 IMSA_HILOGD("failed to get uv entry.");
714 return;
715 }
716 auto eventHandler = GetEventHandler();
717 if (eventHandler == nullptr) {
718 IMSA_HILOGE("eventHandler is nullptr!");
719 return;
720 }
721 IMSA_HILOGI("start");
722 auto task = [entry]() {
723 auto getImeChangeProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
724 if (argc < 2) {
725 return false;
726 }
727 napi_value subProperty = JsInputMethod::GetJsInputMethodSubProperty(env, entry->subProperty);
728 napi_value property = JsInputMethod::GetJsInputMethodProperty(env, entry->property);
729 if (subProperty == nullptr || property == nullptr) {
730 IMSA_HILOGE("get KBCins or TICins failed!");
731 return false;
732 }
733 // 0 means the first param of callback.
734 args[0] = property;
735 // 1 means the second param of callback.
736 args[1] = subProperty;
737 return true;
738 };
739 // 2 means callback has two params.
740 JsCallbackHandler::Traverse(entry->vecCopy, { 2, getImeChangeProperty });
741 };
742 eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
743 }
744
GetSoftKbShowingFlag()745 PanelFlag JsGetInputMethodSetting::GetSoftKbShowingFlag()
746 {
747 return softKbShowingFlag_;
748 }
SetSoftKbShowingFlag(PanelFlag flag)749 void JsGetInputMethodSetting::SetSoftKbShowingFlag(PanelFlag flag)
750 {
751 softKbShowingFlag_ = flag;
752 }
753
OnImeShow(const ImeWindowInfo & info)754 void JsGetInputMethodSetting::OnImeShow(const ImeWindowInfo &info)
755 {
756 if (info.panelInfo.panelType != PanelType::SOFT_KEYBOARD ||
757 (info.panelInfo.panelFlag != FLG_FLOATING && info.panelInfo.panelFlag != FLG_FIXED)) {
758 return;
759 }
760 auto showingFlag = GetSoftKbShowingFlag();
761 // FLG_FIXED->FLG_FLOATING in show
762 if (info.panelInfo.panelFlag == FLG_FLOATING && showingFlag == FLG_FIXED) {
763 InputWindowInfo windowInfo;
764 windowInfo.name = info.windowInfo.name;
765 windowInfo.left = 0;
766 windowInfo.top = 0;
767 windowInfo.width = 0;
768 windowInfo.height = 0;
769 OnPanelStatusChange("imeHide", windowInfo);
770 }
771 // FLG_FLOATING->FLG_FIXED in show/show FLG_FIXED/ rotating(resize) in FLG_FIXED show
772 if ((info.panelInfo.panelFlag == FLG_FIXED && showingFlag == FLG_FLOATING) ||
773 (info.panelInfo.panelFlag == FLG_FIXED && showingFlag == FLG_CANDIDATE_COLUMN) ||
774 (info.panelInfo.panelFlag == FLG_FIXED && showingFlag == FLG_FIXED)) {
775 OnPanelStatusChange("imeShow", info.windowInfo);
776 }
777 SetSoftKbShowingFlag(info.panelInfo.panelFlag);
778 }
779
OnImeHide(const ImeWindowInfo & info)780 void JsGetInputMethodSetting::OnImeHide(const ImeWindowInfo &info)
781 {
782 SetSoftKbShowingFlag(FLG_CANDIDATE_COLUMN);
783 if (info.panelInfo.panelType != PanelType::SOFT_KEYBOARD || info.panelInfo.panelFlag != PanelFlag::FLG_FIXED) {
784 return;
785 }
786 OnPanelStatusChange("imeHide", info.windowInfo);
787 }
788
OnPanelStatusChange(const std::string & type,const InputWindowInfo & info)789 void JsGetInputMethodSetting::OnPanelStatusChange(const std::string &type, const InputWindowInfo &info)
790 {
791 IMSA_HILOGD("type: %{public}s, rect[%{public}d, %{public}d, %{public}u, %{public}u].", type.c_str(), info.left,
792 info.top, info.width, info.height);
793 auto entry = GetEntry(type, [&info](UvEntry &entry) { entry.windowInfo = { info }; });
794 if (entry == nullptr) {
795 IMSA_HILOGD("failed to get uv entry.");
796 return;
797 }
798 auto eventHandler = GetEventHandler();
799 if (eventHandler == nullptr) {
800 IMSA_HILOGE("eventHandler is nullptr!");
801 return;
802 }
803 IMSA_HILOGD("type: %{public}s", type.c_str());
804 auto task = [entry]() {
805 auto getWindowInfo = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
806 if (argc < 1) {
807 return false;
808 }
809 auto windowInfo = JsUtils::GetValue(env, entry->windowInfo);
810 if (windowInfo == nullptr) {
811 IMSA_HILOGE("failed to converse windowInfo!");
812 return false;
813 }
814 // 0 means the first param of callback.
815 args[0] = windowInfo;
816 return true;
817 };
818 // 1 means callback has one param.
819 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getWindowInfo });
820 };
821 eventHandler->PostTask(task, type, 0, AppExecFwk::EventQueue::Priority::VIP);
822 }
823
GetEventHandler()824 std::shared_ptr<AppExecFwk::EventHandler> JsGetInputMethodSetting::GetEventHandler()
825 {
826 std::lock_guard<std::mutex> lock(eventHandlerMutex_);
827 return handler_;
828 }
829
GetEntry(const std::string & type,EntrySetter entrySetter)830 std::shared_ptr<JsGetInputMethodSetting::UvEntry> JsGetInputMethodSetting::GetEntry(const std::string &type,
831 EntrySetter entrySetter)
832 {
833 IMSA_HILOGD("start, type: %{public}s.", type.c_str());
834 std::shared_ptr<UvEntry> entry = nullptr;
835 {
836 std::lock_guard<std::recursive_mutex> lock(mutex_);
837 if (jsCbMap_[type].empty()) {
838 IMSA_HILOGD("%{public}s cb-vector is empty.", type.c_str());
839 return nullptr;
840 }
841 entry = std::make_shared<UvEntry>(jsCbMap_[type], type);
842 }
843 if (entrySetter != nullptr) {
844 entrySetter(*entry);
845 }
846 return entry;
847 }
848 } // namespace MiscServices
849 } // namespace OHOS
850