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