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