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