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