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