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