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_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(
56 env, 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(
164 napi_env env, napi_value argv, 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](
283 napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { return napi_ok; };
284 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
285 *result = JsInputMethod::GetJSInputMethodProperties(env, ctxt->properties);
286 return napi_ok;
287 };
288 auto exec = [ctxt](AsyncCall::Context *ctx) {
289 int32_t errCode = InputMethodController::GetInstance()->ListInputMethod(ctxt->properties);
290 if (errCode == ErrorCode::NO_ERROR) {
291 IMSA_HILOGI("exec GetInputMethods success.");
292 ctxt->status = napi_ok;
293 ctxt->SetState(ctxt->status);
294 return;
295 }
296 ctxt->SetErrorCode(errCode);
297 };
298 ctxt->SetAction(std::move(input), std::move(output));
299 // 1 means JsAPI:getAllInputMethods has 1 param at most.
300 AsyncCall asyncCall(env, info, ctxt, 1);
301 return asyncCall.Call(env, exec, "getInputMethods");
302 }
303
GetAllInputMethodsSync(napi_env env,napi_callback_info info)304 napi_value JsGetInputMethodSetting::GetAllInputMethodsSync(napi_env env, napi_callback_info info)
305 {
306 IMSA_HILOGD("run in");
307 std::vector<Property> properties;
308 int32_t ret = InputMethodController::GetInstance()->ListInputMethod(properties);
309 if (ret != ErrorCode::NO_ERROR) {
310 JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get input methods", TYPE_NONE);
311 return JsUtil::Const::Null(env);
312 }
313 return JsInputMethod::GetJSInputMethodProperties(env, properties);
314 }
315
DisplayOptionalInputMethod(napi_env env,napi_callback_info info)316 napi_value JsGetInputMethodSetting::DisplayOptionalInputMethod(napi_env env, napi_callback_info info)
317 {
318 IMSA_HILOGD("start JsGetInputMethodSetting.");
319 auto ctxt = std::make_shared<DisplayOptionalInputMethodContext>();
320 auto input = [ctxt](
321 napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { return napi_ok; };
322 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status { return napi_ok; };
323 auto exec = [ctxt](AsyncCall::Context *ctx) {
324 int32_t errCode = InputMethodController::GetInstance()->DisplayOptionalInputMethod();
325 if (errCode == ErrorCode::NO_ERROR) {
326 IMSA_HILOGI("exec DisplayOptionalInputMethod success.");
327 ctxt->status = napi_ok;
328 ctxt->SetState(ctxt->status);
329 }
330 };
331 ctxt->SetAction(std::move(input), std::move(output));
332 // 1 means JsAPI:displayOptionalInputMethod has 1 param at most.
333 AsyncCall asyncCall(env, info, ctxt, 1);
334 return asyncCall.Call(env, exec, "displayOptionalInputMethod");
335 }
336
ShowOptionalInputMethods(napi_env env,napi_callback_info info)337 napi_value JsGetInputMethodSetting::ShowOptionalInputMethods(napi_env env, napi_callback_info info)
338 {
339 IMSA_HILOGD("start JsGetInputMethodSetting.");
340 auto ctxt = std::make_shared<DisplayOptionalInputMethodContext>();
341 auto input = [ctxt](
342 napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { return napi_ok; };
343 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
344 napi_status status = napi_get_boolean(env, ctxt->isDisplayed, result);
345 IMSA_HILOGI("output get boolean != nullptr[%{public}d].", result != nullptr);
346 return status;
347 };
348 auto exec = [ctxt](AsyncCall::Context *ctx) {
349 int32_t errCode = InputMethodController::GetInstance()->DisplayOptionalInputMethod();
350 if (errCode == ErrorCode::NO_ERROR) {
351 IMSA_HILOGI("exec DisplayOptionalInputMethod success");
352 ctxt->status = napi_ok;
353 ctxt->SetState(ctxt->status);
354 ctxt->isDisplayed = true;
355 return;
356 } else {
357 ctxt->SetErrorCode(errCode);
358 }
359 };
360 ctxt->SetAction(std::move(input), std::move(output));
361 // 1 means JsAPI:showOptionalInputMethods has 1 param at most.
362 AsyncCall asyncCall(env, info, ctxt, 1);
363 return asyncCall.Call(env, exec, "showOptionalInputMethods");
364 }
365
ListInputMethodSubtype(napi_env env,napi_callback_info info)366 napi_value JsGetInputMethodSetting::ListInputMethodSubtype(napi_env env, napi_callback_info info)
367 {
368 IMSA_HILOGD("run in ListInputMethodSubtype");
369 auto ctxt = std::make_shared<ListInputContext>();
370 auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
371 PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_invalid_arg);
372 napi_valuetype valueType = napi_undefined;
373 napi_typeof(env, argv[0], &valueType);
374 PARAM_CHECK_RETURN(env, valueType == napi_object, "inputMethodProperty type must be InputMethodProperty!",
375 TYPE_NONE, napi_invalid_arg);
376 napi_status status = JsGetInputMethodSetting::GetInputMethodProperty(env, argv[0], ctxt);
377 return status;
378 };
379 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
380 *result = JsInputMethod::GetJSInputMethodSubProperties(env, ctxt->subProperties);
381 return napi_ok;
382 };
383 auto exec = [ctxt](AsyncCall::Context *ctx) {
384 int32_t errCode =
385 InputMethodController::GetInstance()->ListInputMethodSubtype(ctxt->property, ctxt->subProperties);
386 if (errCode == ErrorCode::NO_ERROR) {
387 IMSA_HILOGI("exec ListInputMethodSubtype success");
388 ctxt->status = napi_ok;
389 ctxt->SetState(ctxt->status);
390 return;
391 }
392 ctxt->SetErrorCode(errCode);
393 };
394 ctxt->SetAction(std::move(input), std::move(output));
395 // 2 means JsAPI:listInputMethodSubtype has 2 params at most.
396 AsyncCall asyncCall(env, info, ctxt, 2);
397 return asyncCall.Call(env, exec, "listInputMethodSubtype");
398 }
399
ListCurrentInputMethodSubtype(napi_env env,napi_callback_info info)400 napi_value JsGetInputMethodSetting::ListCurrentInputMethodSubtype(napi_env env, napi_callback_info info)
401 {
402 IMSA_HILOGD("run in ListCurrentInputMethodSubtype");
403 auto ctxt = std::make_shared<ListInputContext>();
404 auto input = [ctxt](
405 napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status { return napi_ok; };
406 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
407 *result = JsInputMethod::GetJSInputMethodSubProperties(env, ctxt->subProperties);
408 return napi_ok;
409 };
410 auto exec = [ctxt](AsyncCall::Context *ctx) {
411 int32_t errCode = InputMethodController::GetInstance()->ListCurrentInputMethodSubtype(ctxt->subProperties);
412 if (errCode == ErrorCode::NO_ERROR) {
413 IMSA_HILOGI("exec ListCurrentInputMethodSubtype success.");
414 ctxt->status = napi_ok;
415 ctxt->SetState(ctxt->status);
416 return;
417 }
418 ctxt->SetErrorCode(errCode);
419 };
420 ctxt->SetAction(std::move(input), std::move(output));
421 // 1 means JsAPI:listCurrentInputMethodSubtype has 1 param at most.
422 AsyncCall asyncCall(env, info, ctxt, 1);
423 return asyncCall.Call(env, exec, "listCurrentInputMethodSubtype");
424 }
425
IsPanelShown(napi_env env,napi_callback_info info)426 napi_value JsGetInputMethodSetting::IsPanelShown(napi_env env, napi_callback_info info)
427 {
428 IMSA_HILOGD("start JsGetInputMethodSetting");
429 // 1 means required param num
430 size_t argc = 1;
431 napi_value argv[1] = { nullptr };
432 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
433 // 1 means least param num
434 PARAM_CHECK_RETURN(env, argc >= 1, "at least one parameter is required!", TYPE_NONE, JsUtil::Const::Null(env));
435 // 0 means parameter of info<PanelInfo>
436 napi_valuetype valueType = napi_undefined;
437 napi_typeof(env, argv[0], &valueType);
438 PARAM_CHECK_RETURN(env, valueType == napi_object, "panelInfo type must be PanelInfo!", TYPE_NONE,
439 JsUtil::Const::Null(env));
440
441 PanelInfo panelInfo;
442 napi_status status = JsUtils::GetValue(env, argv[0], panelInfo);
443 PARAM_CHECK_RETURN(env, status == napi_ok, "panelInfo covert failed!", TYPE_NONE, JsUtil::Const::Null(env));
444
445 bool isShown = false;
446 int32_t errorCode = InputMethodController::GetInstance()->IsPanelShown(panelInfo, isShown);
447 if (errorCode != ErrorCode::NO_ERROR) {
448 JsUtils::ThrowException(env, JsUtils::Convert(errorCode), "failed to query is panel shown!", TYPE_NONE);
449 return JsUtil::Const::Null(env);
450 }
451 return JsUtil::GetValue(env, isShown);
452 }
453
RegisterListener(napi_value callback,std::string type,std::shared_ptr<JSCallbackObject> callbackObj)454 int32_t JsGetInputMethodSetting::RegisterListener(
455 napi_value callback, std::string type, std::shared_ptr<JSCallbackObject> callbackObj)
456 {
457 IMSA_HILOGD("register listener: %{public}s", type.c_str());
458 std::lock_guard<std::recursive_mutex> lock(mutex_);
459 if (!jsCbMap_.empty() && jsCbMap_.find(type) == jsCbMap_.end()) {
460 IMSA_HILOGI("start type: %{public}s listening.", type.c_str());
461 }
462
463 auto callbacks = jsCbMap_[type];
464 bool ret = std::any_of(callbacks.begin(), callbacks.end(), [&callback](std::shared_ptr<JSCallbackObject> cb) {
465 return JsUtils::Equals(cb->env_, callback, cb->callback_, cb->threadId_);
466 });
467 if (ret) {
468 IMSA_HILOGD("callback already registered!");
469 return ErrorCode::NO_ERROR;
470 }
471
472 IMSA_HILOGI("add %{public}s callbackObj into jsCbMap_.", type.c_str());
473 jsCbMap_[type].push_back(std::move(callbackObj));
474 return ErrorCode::NO_ERROR;
475 }
476
Subscribe(napi_env env,napi_callback_info info)477 napi_value JsGetInputMethodSetting::Subscribe(napi_env env, napi_callback_info info)
478 {
479 size_t argc = ARGC_TWO;
480 napi_value argv[ARGC_TWO] = { nullptr };
481 napi_value thisVar = nullptr;
482 void *data = nullptr;
483 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
484 std::string type;
485 // 2 means least param num.
486 if (argc < 2 || !JsUtil::GetValue(env, argv[0], type) ||
487 !EventChecker::IsValidEventType(EventSubscribeModule::INPUT_METHOD_SETTING, type) ||
488 JsUtil::GetType(env, argv[1]) != napi_function) {
489 IMSA_HILOGE("subscribe failed, type:%{public}s", type.c_str());
490 return nullptr;
491 }
492 IMSA_HILOGD("subscribe type: %{public}s.", type.c_str());
493 auto engine = reinterpret_cast<JsGetInputMethodSetting *>(JsUtils::GetNativeSelf(env, info));
494 if (engine == nullptr) {
495 return nullptr;
496 }
497 auto iter = EVENT_TYPE.find(type);
498 if (iter == EVENT_TYPE.end()) {
499 return nullptr;
500 }
501 std::shared_ptr<JSCallbackObject> callback =
502 std::make_shared<JSCallbackObject>(env, argv[ARGC_ONE], std::this_thread::get_id());
503 auto ret = ImeEventMonitorManagerImpl::GetInstance().RegisterImeEventListener(iter->second, inputMethod_);
504 if (ret == ErrorCode::NO_ERROR) {
505 engine->RegisterListener(argv[ARGC_ONE], type, callback);
506 } else {
507 auto errCode = JsUtils::Convert(ret);
508 if (errCode == EXCEPTION_SYSTEM_PERMISSION) {
509 IMSA_HILOGE("failed to UpdateListenEventFlag , ret: %{public}d, type: %{public}s!", ret, type.c_str());
510 JsUtils::ThrowException(env, errCode, "", TYPE_NONE);
511 }
512 }
513 napi_value result = nullptr;
514 napi_get_null(env, &result);
515 return result;
516 }
517
UnRegisterListener(napi_value callback,std::string type,bool & isUpdateFlag)518 void JsGetInputMethodSetting::UnRegisterListener(napi_value callback, std::string type, bool &isUpdateFlag)
519 {
520 IMSA_HILOGI("unregister listener: %{public}s!", type.c_str());
521 std::lock_guard<std::recursive_mutex> lock(mutex_);
522 if (jsCbMap_.empty() || jsCbMap_.find(type) == jsCbMap_.end()) {
523 IMSA_HILOGE("methodName: %{public}s already unRegistered!", type.c_str());
524 return;
525 }
526
527 if (callback == nullptr) {
528 jsCbMap_.erase(type);
529 IMSA_HILOGI("stop all type: %{public}s listening.", type.c_str());
530 isUpdateFlag = true;
531 return;
532 }
533
534 for (auto item = jsCbMap_[type].begin(); item != jsCbMap_[type].end(); item++) {
535 if (JsUtils::Equals((*item)->env_, callback, (*item)->callback_, (*item)->threadId_)) {
536 jsCbMap_[type].erase(item);
537 break;
538 }
539 }
540
541 if (jsCbMap_[type].empty()) {
542 IMSA_HILOGI("stop last type: %{public}s listening.", type.c_str());
543 jsCbMap_.erase(type);
544 isUpdateFlag = true;
545 }
546 }
547
UnSubscribe(napi_env env,napi_callback_info info)548 napi_value JsGetInputMethodSetting::UnSubscribe(napi_env env, napi_callback_info info)
549 {
550 size_t argc = ARGC_TWO;
551 napi_value argv[ARGC_TWO] = { nullptr };
552 napi_value thisVar = nullptr;
553 void *data = nullptr;
554 NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, &data));
555 std::string type;
556 // 1 means least param num.
557 if (argc < 1 || !JsUtil::GetValue(env, argv[0], type) ||
558 !EventChecker::IsValidEventType(EventSubscribeModule::INPUT_METHOD_SETTING, type)) {
559 IMSA_HILOGE("unsubscribe failed, type: %{public}s!", type.c_str());
560 return nullptr;
561 }
562
563 // if the second param is not napi_function/napi_null/napi_undefined, return
564 auto paramType = JsUtil::GetType(env, argv[1]);
565 if (paramType != napi_function && paramType != napi_null && paramType != napi_undefined) {
566 return nullptr;
567 }
568 // if the second param is napi_function, delete it, else delete all
569 argv[1] = paramType == napi_function ? argv[1] : nullptr;
570
571 IMSA_HILOGD("unsubscribe type: %{public}s.", type.c_str());
572
573 auto engine = reinterpret_cast<JsGetInputMethodSetting *>(JsUtils::GetNativeSelf(env, info));
574 if (engine == nullptr) {
575 return nullptr;
576 }
577 bool isUpdateFlag = false;
578 engine->UnRegisterListener(argv[ARGC_ONE], type, isUpdateFlag);
579 auto iter = EVENT_TYPE.find(type);
580 if (iter == EVENT_TYPE.end()) {
581 return nullptr;
582 }
583 if (isUpdateFlag) {
584 auto ret = ImeEventMonitorManagerImpl::GetInstance().UnRegisterImeEventListener(iter->second, inputMethod_);
585 IMSA_HILOGI("UpdateListenEventFlag, ret: %{public}d, type: %{public}s.", ret, type.c_str());
586 }
587 napi_value result = nullptr;
588 napi_get_null(env, &result);
589 return result;
590 }
591
OnImeChange(const Property & property,const SubProperty & subProperty)592 void JsGetInputMethodSetting::OnImeChange(const Property &property, const SubProperty &subProperty)
593 {
594 std::string type = "imeChange";
595 auto entry = GetEntry(type, [&property, &subProperty](UvEntry &entry) {
596 entry.property = property;
597 entry.subProperty = subProperty;
598 });
599 if (entry == nullptr) {
600 IMSA_HILOGD("failed to get uv entry.");
601 return;
602 }
603 auto eventHandler = GetEventHandler();
604 if (eventHandler == nullptr) {
605 IMSA_HILOGE("eventHandler is nullptr!");
606 return;
607 }
608 IMSA_HILOGI("start");
609 auto task = [entry]() {
610 auto getImeChangeProperty = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
611 if (argc < 2) {
612 return false;
613 }
614 napi_value subProperty = JsInputMethod::GetJsInputMethodSubProperty(env, entry->subProperty);
615 napi_value property = JsInputMethod::GetJsInputMethodProperty(env, entry->property);
616 if (subProperty == nullptr || property == nullptr) {
617 IMSA_HILOGE("get KBCins or TICins failed!");
618 return false;
619 }
620 // 0 means the first param of callback.
621 args[0] = property;
622 // 1 means the second param of callback.
623 args[1] = subProperty;
624 return true;
625 };
626 // 2 means callback has two params.
627 JsCallbackHandler::Traverse(entry->vecCopy, { 2, getImeChangeProperty });
628 };
629 eventHandler->PostTask(task, type);
630 }
631
GetSoftKbShowingFlag()632 PanelFlag JsGetInputMethodSetting::GetSoftKbShowingFlag()
633 {
634 return softKbShowingFlag_;
635 }
SetSoftKbShowingFlag(PanelFlag flag)636 void JsGetInputMethodSetting::SetSoftKbShowingFlag(PanelFlag flag)
637 {
638 softKbShowingFlag_ = flag;
639 }
640
OnImeShow(const ImeWindowInfo & info)641 void JsGetInputMethodSetting::OnImeShow(const ImeWindowInfo &info)
642 {
643 if (info.panelInfo.panelType != PanelType::SOFT_KEYBOARD
644 || (info.panelInfo.panelFlag != FLG_FLOATING && info.panelInfo.panelFlag != FLG_FIXED)) {
645 return;
646 }
647 auto showingFlag = GetSoftKbShowingFlag();
648 // FLG_FIXED->FLG_FLOATING in show
649 if (info.panelInfo.panelFlag == FLG_FLOATING && showingFlag == FLG_FIXED) {
650 InputWindowInfo windowInfo{ info.windowInfo.name, 0, 0, 0, 0 };
651 OnPanelStatusChange("imeHide", windowInfo);
652 }
653 // FLG_FLOATING->FLG_FIXED in show/show FLG_FIXED/ rotating(resize) in FLG_FIXED show
654 if ((info.panelInfo.panelFlag == FLG_FIXED && showingFlag == FLG_FLOATING)
655 || (info.panelInfo.panelFlag == FLG_FIXED && showingFlag == FLG_CANDIDATE_COLUMN)
656 || (info.panelInfo.panelFlag == FLG_FIXED && showingFlag == FLG_FIXED)) {
657 OnPanelStatusChange("imeShow", info.windowInfo);
658 }
659 SetSoftKbShowingFlag(info.panelInfo.panelFlag);
660 }
661
OnImeHide(const ImeWindowInfo & info)662 void JsGetInputMethodSetting::OnImeHide(const ImeWindowInfo &info)
663 {
664 SetSoftKbShowingFlag(FLG_CANDIDATE_COLUMN);
665 if (info.panelInfo.panelType != PanelType::SOFT_KEYBOARD || info.panelInfo.panelFlag != PanelFlag::FLG_FIXED) {
666 return;
667 }
668 OnPanelStatusChange("imeHide", info.windowInfo);
669 }
670
OnPanelStatusChange(const std::string & type,const InputWindowInfo & info)671 void JsGetInputMethodSetting::OnPanelStatusChange(const std::string &type, const InputWindowInfo &info)
672 {
673 IMSA_HILOGI("type: %{public}s, rect[%{public}d, %{public}d, %{public}u, %{public}u].", type.c_str(), info.left,
674 info.top, info.width, info.height);
675 auto entry = GetEntry(type, [&info](UvEntry &entry) { entry.windowInfo = { info }; });
676 if (entry == nullptr) {
677 IMSA_HILOGD("failed to get uv entry.");
678 return;
679 }
680 auto eventHandler = GetEventHandler();
681 if (eventHandler == nullptr) {
682 IMSA_HILOGE("eventHandler is nullptr!");
683 return;
684 }
685 IMSA_HILOGI("type: %{public}s", type.c_str());
686 auto task = [entry]() {
687 auto getWindowInfo = [entry](napi_env env, napi_value *args, uint8_t argc) -> bool {
688 if (argc < 1) {
689 return false;
690 }
691 auto windowInfo = JsUtils::GetValue(env, entry->windowInfo);
692 if (windowInfo == nullptr) {
693 IMSA_HILOGE("failed to converse windowInfo!");
694 return false;
695 }
696 // 0 means the first param of callback.
697 args[0] = windowInfo;
698 return true;
699 };
700 // 1 means callback has one param.
701 JsCallbackHandler::Traverse(entry->vecCopy, { 1, getWindowInfo });
702 };
703 eventHandler->PostTask(task, type);
704 }
705
GetEventHandler()706 std::shared_ptr<AppExecFwk::EventHandler> JsGetInputMethodSetting::GetEventHandler()
707 {
708 std::lock_guard<std::mutex> lock(eventHandlerMutex_);
709 return handler_;
710 }
711
GetEntry(const std::string & type,EntrySetter entrySetter)712 std::shared_ptr<JsGetInputMethodSetting::UvEntry> JsGetInputMethodSetting::GetEntry(
713 const std::string &type, EntrySetter entrySetter)
714 {
715 IMSA_HILOGD("start, type: %{public}s.", type.c_str());
716 std::shared_ptr<UvEntry> entry = nullptr;
717 {
718 std::lock_guard<std::recursive_mutex> lock(mutex_);
719 if (jsCbMap_[type].empty()) {
720 IMSA_HILOGD("%{public}s cb-vector is empty.", type.c_str());
721 return nullptr;
722 }
723 entry = std::make_shared<UvEntry>(jsCbMap_[type], type);
724 }
725 if (entrySetter != nullptr) {
726 entrySetter(*entry);
727 }
728 return entry;
729 }
730
GetInputMethodState(napi_env env,napi_callback_info info)731 napi_value JsGetInputMethodSetting::GetInputMethodState(napi_env env, napi_callback_info info)
732 {
733 auto ctxt = std::make_shared<GetInputMethodStateContext>();
734 auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
735 int32_t enableStatus = static_cast<int32_t>(ctxt->enableStatus);
736 *result = JsUtil::GetValue(env, enableStatus);
737 return napi_ok;
738 };
739 auto exec = [ctxt](AsyncCall::Context *ctx) {
740 int32_t errCode = InputMethodController::GetInstance()->GetInputMethodState(ctxt->enableStatus);
741 if (errCode == ErrorCode::NO_ERROR) {
742 ctxt->status = napi_ok;
743 ctxt->SetState(ctxt->status);
744 return;
745 }
746 ctxt->SetErrorCode(errCode);
747 };
748 ctxt->SetAction(nullptr, std::move(output));
749 // 0 means JsAPI:GetInputMethodState has no param.
750 AsyncCall asyncCall(env, info, ctxt, 0);
751 return asyncCall.Call(env, exec, "GetInputMethodState");
752 }
753 } // namespace MiscServices
754 } // namespace OHOS