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_input_method.h"
17 
18 #include "event_handler.h"
19 #include "event_runner.h"
20 #include "inputmethod_trace.h"
21 #include "input_method_controller.h"
22 #include "input_method_property.h"
23 #include "napi/native_api.h"
24 #include "js_util.h"
25 #include "napi/native_node_api.h"
26 #include "string_ex.h"
27 
28 namespace OHOS {
29 namespace MiscServices {
Init(napi_env env,napi_value exports)30 napi_value JsInputMethod::Init(napi_env env, napi_value exports)
31 {
32     napi_property_descriptor descriptor[] = {
33         DECLARE_NAPI_FUNCTION("switchInputMethod", SwitchInputMethod),
34         DECLARE_NAPI_FUNCTION("getCurrentInputMethod", GetCurrentInputMethod),
35         DECLARE_NAPI_FUNCTION("getCurrentInputMethodSubtype", GetCurrentInputMethodSubtype),
36         DECLARE_NAPI_FUNCTION("getDefaultInputMethod", GetDefaultInputMethod),
37         DECLARE_NAPI_FUNCTION("getSystemInputMethodConfigAbility", GetSystemInputMethodConfigAbility),
38         DECLARE_NAPI_FUNCTION("switchCurrentInputMethodSubtype", SwitchCurrentInputMethodSubtype),
39         DECLARE_NAPI_FUNCTION("switchCurrentInputMethodAndSubtype", SwitchCurrentInputMethodAndSubtype),
40     };
41     NAPI_CALL(
42         env, napi_define_properties(env, exports, sizeof(descriptor) / sizeof(napi_property_descriptor), descriptor));
43     return exports;
44 };
45 
GetInputMethodProperty(napi_env env,napi_value argv,std::shared_ptr<SwitchInputMethodContext> ctxt)46 napi_status JsInputMethod::GetInputMethodProperty(
47     napi_env env, napi_value argv, std::shared_ptr<SwitchInputMethodContext> ctxt)
48 {
49     napi_valuetype valueType = napi_undefined;
50     napi_status status = napi_generic_failure;
51     napi_typeof(env, argv, &valueType);
52     if (valueType != napi_object) {
53         IMSA_HILOGE("type is not object!");
54         return status;
55     }
56     napi_value result = nullptr;
57     napi_get_named_property(env, argv, "name", &result);
58     status = JsUtils::GetValue(env, result, ctxt->packageName);
59     CHECK_RETURN(status == napi_ok, "get name failed!", status);
60     result = nullptr;
61     napi_get_named_property(env, argv, "id", &result);
62     status = JsUtils::GetValue(env, result, ctxt->methodId);
63     CHECK_RETURN(status == napi_ok, "get id failed!", status);
64     if (ctxt->packageName.empty() || ctxt->methodId.empty()) {
65         result = nullptr;
66         napi_get_named_property(env, argv, "packageName", &result);
67         status = JsUtils::GetValue(env, result, ctxt->packageName);
68         CHECK_RETURN(status == napi_ok, "get packageName failed!", status);
69 
70         result = nullptr;
71         napi_get_named_property(env, argv, "methodId", &result);
72         status = JsUtils::GetValue(env, result, ctxt->methodId);
73         CHECK_RETURN(status == napi_ok, "get methodId failed!", status);
74     }
75     PARAM_CHECK_RETURN(env, (!ctxt->packageName.empty() && !ctxt->methodId.empty()),
76         "packageName and methodId is empty", TYPE_NONE, napi_invalid_arg);
77     IMSA_HILOGD("methodId: %{public}s, packageName: %{public}s.", ctxt->methodId.c_str(), ctxt->packageName.c_str());
78     return napi_ok;
79 }
80 
GetInputMethodSubProperty(napi_env env,napi_value argv,std::shared_ptr<SwitchInputMethodContext> ctxt)81 napi_status JsInputMethod::GetInputMethodSubProperty(
82     napi_env env, napi_value argv, std::shared_ptr<SwitchInputMethodContext> ctxt)
83 {
84     napi_valuetype valueType = napi_undefined;
85     napi_status status = napi_generic_failure;
86     status = napi_typeof(env, argv, &valueType);
87     if (valueType == napi_object) {
88         napi_value result = nullptr;
89         status = napi_get_named_property(env, argv, "name", &result);
90         PARAM_CHECK_RETURN(env, status == napi_ok, " name ", TYPE_STRING, status);
91         status = JsUtils::GetValue(env, result, ctxt->name);
92         CHECK_RETURN(status == napi_ok, "get name failed!", status);
93         result = nullptr;
94         status = napi_get_named_property(env, argv, "id", &result);
95         PARAM_CHECK_RETURN(env, status == napi_ok, " id ", TYPE_STRING, status);
96         status = JsUtils::GetValue(env, result, ctxt->id);
97         CHECK_RETURN(status == napi_ok, "get id failed!", status);
98         IMSA_HILOGD("name: %{public}s and id: %{public}s.", ctxt->name.c_str(), ctxt->id.c_str());
99     }
100     return status;
101 }
102 
GetJsInputMethodProperty(napi_env env,const Property & property)103 napi_value JsInputMethod::GetJsInputMethodProperty(napi_env env, const Property &property)
104 {
105     napi_value prop = nullptr;
106     napi_create_object(env, &prop);
107 
108     napi_value packageName = nullptr;
109     napi_create_string_utf8(env, property.name.c_str(), NAPI_AUTO_LENGTH, &packageName);
110     napi_set_named_property(env, prop, "packageName", packageName);
111     napi_set_named_property(env, prop, "name", packageName);
112 
113     napi_value methodId = nullptr;
114     napi_create_string_utf8(env, property.id.c_str(), NAPI_AUTO_LENGTH, &methodId);
115     napi_set_named_property(env, prop, "methodId", methodId);
116     napi_set_named_property(env, prop, "id", methodId);
117 
118     napi_value icon = nullptr;
119     napi_create_string_utf8(env, property.icon.c_str(), NAPI_AUTO_LENGTH, &icon);
120     napi_set_named_property(env, prop, "icon", icon);
121 
122     napi_value iconId = nullptr;
123     napi_create_uint32(env, property.iconId, &iconId);
124     napi_set_named_property(env, prop, "iconId", iconId);
125 
126     napi_value label = nullptr;
127     napi_create_string_utf8(env, property.label.c_str(), NAPI_AUTO_LENGTH, &label);
128     napi_set_named_property(env, prop, "label", label);
129 
130     napi_value labelId = nullptr;
131     napi_create_uint32(env, property.labelId, &labelId);
132     napi_set_named_property(env, prop, "labelId", labelId);
133     return prop;
134 }
135 
GetJsInputMethodSubProperty(napi_env env,const SubProperty & subProperty)136 napi_value JsInputMethod::GetJsInputMethodSubProperty(napi_env env, const SubProperty &subProperty)
137 {
138     napi_value prop = nullptr;
139     napi_create_object(env, &prop);
140 
141     napi_value id = nullptr;
142     napi_create_string_utf8(env, subProperty.id.c_str(), NAPI_AUTO_LENGTH, &id);
143     napi_set_named_property(env, prop, "id", id);
144 
145     napi_value label = nullptr;
146     napi_create_string_utf8(env, subProperty.label.c_str(), NAPI_AUTO_LENGTH, &label);
147     napi_set_named_property(env, prop, "label", label);
148 
149     napi_value labelId = nullptr;
150     napi_create_uint32(env, subProperty.labelId, &labelId);
151     napi_set_named_property(env, prop, "labelId", labelId);
152 
153     napi_value name = nullptr;
154     napi_create_string_utf8(env, subProperty.name.c_str(), NAPI_AUTO_LENGTH, &name);
155     napi_set_named_property(env, prop, "name", name);
156 
157     napi_value mode = nullptr;
158     napi_create_string_utf8(env, subProperty.mode.c_str(), NAPI_AUTO_LENGTH, &mode);
159     napi_set_named_property(env, prop, "mode", mode);
160 
161     napi_value locale = nullptr;
162     napi_create_string_utf8(env, subProperty.locale.c_str(), NAPI_AUTO_LENGTH, &locale);
163     napi_set_named_property(env, prop, "locale", locale);
164 
165     napi_value language = nullptr;
166     napi_create_string_utf8(env, subProperty.language.c_str(), NAPI_AUTO_LENGTH, &language);
167     napi_set_named_property(env, prop, "language", language);
168 
169     napi_value icon = nullptr;
170     napi_create_string_utf8(env, subProperty.icon.c_str(), NAPI_AUTO_LENGTH, &icon);
171     napi_set_named_property(env, prop, "icon", icon);
172 
173     napi_value iconId = nullptr;
174     napi_create_uint32(env, subProperty.iconId, &iconId);
175     napi_set_named_property(env, prop, "iconId", iconId);
176     return prop;
177 }
178 
GetJsInputConfigElement(napi_env env,const OHOS::AppExecFwk::ElementName & elementName)179 napi_value JsInputMethod::GetJsInputConfigElement(napi_env env, const OHOS::AppExecFwk::ElementName &elementName)
180 {
181     napi_value element = nullptr;
182     napi_create_object(env, &element);
183 
184     napi_value bundleName = nullptr;
185     napi_create_string_utf8(env, elementName.GetBundleName().c_str(), NAPI_AUTO_LENGTH, &bundleName);
186     napi_set_named_property(env, element, "bundleName", bundleName);
187 
188     napi_value moduleName = nullptr;
189     napi_create_string_utf8(env, elementName.GetModuleName().c_str(), NAPI_AUTO_LENGTH, &moduleName);
190     napi_set_named_property(env, element, "moduleName", moduleName);
191 
192     napi_value abilityName = nullptr;
193     napi_create_string_utf8(env, elementName.GetAbilityName().c_str(), NAPI_AUTO_LENGTH, &abilityName);
194     napi_set_named_property(env, element, "abilityName", abilityName);
195 
196     return element;
197 }
198 
GetJSInputMethodSubProperties(napi_env env,const std::vector<SubProperty> & subProperties)199 napi_value JsInputMethod::GetJSInputMethodSubProperties(napi_env env, const std::vector<SubProperty> &subProperties)
200 {
201     uint32_t index = 0;
202     napi_value prop = nullptr;
203     napi_create_array(env, &prop);
204     if (prop == nullptr) {
205         IMSA_HILOGE("create array failed!");
206         return prop;
207     }
208     for (const auto &subproperty : subProperties) {
209         napi_value pro = GetJsInputMethodSubProperty(env, subproperty);
210         napi_set_element(env, prop, index, pro);
211         index++;
212     }
213     return prop;
214 }
215 
GetJSInputMethodProperties(napi_env env,const std::vector<Property> & properties)216 napi_value JsInputMethod::GetJSInputMethodProperties(napi_env env, const std::vector<Property> &properties)
217 {
218     uint32_t index = 0;
219     napi_value prop = nullptr;
220     napi_create_array(env, &prop);
221     if (prop == nullptr) {
222         IMSA_HILOGE("create array failed!");
223         return prop;
224     }
225     for (const auto &property : properties) {
226         napi_value pro = GetJsInputMethodProperty(env, property);
227         napi_set_element(env, prop, index, pro);
228         index++;
229     }
230     return prop;
231 }
232 
SwitchInputMethod(napi_env env,napi_callback_info info)233 napi_value JsInputMethod::SwitchInputMethod(napi_env env, napi_callback_info info)
234 {
235     InputMethodSyncTrace tracer("JsInputMethod_SwitchInputMethod");
236     auto ctxt = std::make_shared<SwitchInputMethodContext>();
237     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
238         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_invalid_arg);
239         napi_valuetype valueType = napi_undefined;
240         napi_typeof(env, argv[0], &valueType);
241         PARAM_CHECK_RETURN(env, valueType == napi_object || valueType == napi_string,
242             "target/bundleName type must be InputMethodProperty/string!", TYPE_NONE, napi_invalid_arg);
243         napi_status status = napi_generic_failure;
244         if (valueType == napi_object) {
245             ctxt->trigger = SwitchTrigger::CURRENT_IME;
246             status = GetInputMethodProperty(env, argv[0], ctxt);
247         } else {
248             status = JsUtils::GetValue(env, argv[0], ctxt->packageName);
249             ctxt->trigger = SwitchTrigger::SYSTEM_APP;
250             napi_valuetype type = napi_undefined;
251             napi_typeof(env, argv[1], &type);
252             if (argc > 1 && type == napi_string) {
253                 JsUtils::GetValue(env, argv[1], ctxt->id);
254             }
255         }
256         return status;
257     };
258     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
259         napi_status status = napi_get_boolean(env, ctxt->isSwitchInput, result);
260         return status;
261     };
262     auto exec = [ctxt](AsyncCall::Context *ctx) {
263         int32_t errCode =
264             InputMethodController::GetInstance()->SwitchInputMethod(ctxt->trigger, ctxt->packageName, ctxt->id);
265         if (errCode == ErrorCode::NO_ERROR) {
266             ctxt->status = napi_ok;
267             ctxt->SetState(ctxt->status);
268             ctxt->isSwitchInput = true;
269         } else {
270             IMSA_HILOGE("exec SwitchInputMethod failed ret: %{public}d!", errCode);
271             ctxt->SetErrorCode(errCode);
272         }
273     };
274     ctxt->SetAction(std::move(input), std::move(output));
275     // 2 means JsAPI:switchInputMethod has 2 params at most.
276     AsyncCall asyncCall(env, info, ctxt, 2);
277     return asyncCall.Call(env, exec, "switchInputMethod");
278 }
279 
GetCurrentInputMethod(napi_env env,napi_callback_info info)280 napi_value JsInputMethod::GetCurrentInputMethod(napi_env env, napi_callback_info info)
281 {
282     std::shared_ptr<Property> property = InputMethodController::GetInstance()->GetCurrentInputMethod();
283     if (property == nullptr) {
284         IMSA_HILOGE("current input method is nullptr!");
285         napi_value result = nullptr;
286         napi_get_null(env, &result);
287         return result;
288     }
289     return GetJsInputMethodProperty(env, *property);
290 }
291 
GetCurrentInputMethodSubtype(napi_env env,napi_callback_info info)292 napi_value JsInputMethod::GetCurrentInputMethodSubtype(napi_env env, napi_callback_info info)
293 {
294     std::shared_ptr<SubProperty> subProperty = InputMethodController::GetInstance()->GetCurrentInputMethodSubtype();
295     if (subProperty == nullptr) {
296         IMSA_HILOGE("current input method subtype is nullptr!");
297         napi_value result = nullptr;
298         napi_get_null(env, &result);
299         return result;
300     }
301     return GetJsInputMethodSubProperty(env, *subProperty);
302 }
303 
GetDefaultInputMethod(napi_env env,napi_callback_info info)304 napi_value JsInputMethod::GetDefaultInputMethod(napi_env env, napi_callback_info info)
305 {
306     std::shared_ptr<Property> property;
307     int32_t ret = InputMethodController::GetInstance()->GetDefaultInputMethod(property);
308     if (property == nullptr) {
309         IMSA_HILOGE("default input method is nullptr!");
310         napi_value result = nullptr;
311         napi_get_null(env, &result);
312         return result;
313     }
314     if (ret != ErrorCode::NO_ERROR) {
315         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get default input method!", TYPE_NONE);
316         return JsUtil::Const::Null(env);
317     }
318     return GetJsInputMethodProperty(env, *property);
319 }
320 
GetSystemInputMethodConfigAbility(napi_env env,napi_callback_info info)321 napi_value JsInputMethod::GetSystemInputMethodConfigAbility(napi_env env, napi_callback_info info)
322 {
323     OHOS::AppExecFwk::ElementName inputMethodConfig;
324     int32_t ret = InputMethodController::GetInstance()->GetInputMethodConfig(inputMethodConfig);
325     if (ret != ErrorCode::NO_ERROR) {
326         JsUtils::ThrowException(env, JsUtils::Convert(ret), "failed to get input method config", TYPE_NONE);
327         return JsUtil::Const::Null(env);
328     }
329     return GetJsInputConfigElement(env, inputMethodConfig);
330 }
331 
SwitchCurrentInputMethodSubtype(napi_env env,napi_callback_info info)332 napi_value JsInputMethod::SwitchCurrentInputMethodSubtype(napi_env env, napi_callback_info info)
333 {
334     InputMethodSyncTrace tracer("JsInputMethod_SwitchCurrentInputMethodSubtype");
335     auto ctxt = std::make_shared<SwitchInputMethodContext>();
336     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
337         PARAM_CHECK_RETURN(env, argc > 0, "at least one parameter is required!", TYPE_NONE, napi_invalid_arg);
338         napi_valuetype valueType = napi_undefined;
339         napi_typeof(env, argv[0], &valueType);
340         PARAM_CHECK_RETURN(env, valueType == napi_object, "target type must be InputMethodSubtype!", TYPE_NONE,
341             napi_invalid_arg);
342         napi_status status = GetInputMethodSubProperty(env, argv[0], ctxt);
343         return status;
344     };
345     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
346         napi_status status = napi_get_boolean(env, ctxt->isSwitchInput, result);
347         IMSA_HILOGE("output get boolean != nullptr[%{public}d]!", result != nullptr);
348         return status;
349     };
350     auto exec = [ctxt](AsyncCall::Context *ctx) {
351         int32_t errCode =
352             InputMethodController::GetInstance()->SwitchInputMethod(SwitchTrigger::CURRENT_IME, ctxt->name, ctxt->id);
353         if (errCode == ErrorCode::NO_ERROR) {
354             IMSA_HILOGI("exec SwitchInputMethod success.");
355             ctxt->status = napi_ok;
356             ctxt->SetState(ctxt->status);
357             ctxt->isSwitchInput = true;
358         } else {
359             ctxt->SetErrorCode(errCode);
360         }
361     };
362     ctxt->SetAction(std::move(input), std::move(output));
363     // 2 means JsAPI:switchCurrentInputMethodSubtype has 2 params at most.
364     AsyncCall asyncCall(env, info, ctxt, 2);
365     return asyncCall.Call(env, exec, "switchCurrentInputMethodSubtype");
366 }
367 
SwitchCurrentInputMethodAndSubtype(napi_env env,napi_callback_info info)368 napi_value JsInputMethod::SwitchCurrentInputMethodAndSubtype(napi_env env, napi_callback_info info)
369 {
370     InputMethodSyncTrace tracer("JsInputMethod_SwitchCurrentInputMethodAndSubtype");
371     auto ctxt = std::make_shared<SwitchInputMethodContext>();
372     auto input = [ctxt](napi_env env, size_t argc, napi_value *argv, napi_value self) -> napi_status {
373         PARAM_CHECK_RETURN(env, argc > 1, "at least two parameters is required!", TYPE_NONE, napi_invalid_arg);
374         napi_valuetype valueType = napi_undefined;
375         napi_typeof(env, argv[0], &valueType);
376         PARAM_CHECK_RETURN(env, valueType == napi_object, "inputMethodProperty type must be InputMethodProperty!",
377             TYPE_NONE, napi_invalid_arg);
378         napi_typeof(env, argv[1], &valueType);
379         PARAM_CHECK_RETURN(env, valueType == napi_object, "inputMethodSubtype type must be InputMethodSubtype!",
380             TYPE_NONE, napi_invalid_arg);
381         napi_status status = GetInputMethodSubProperty(env, argv[1], ctxt);
382         return status;
383     };
384     auto output = [ctxt](napi_env env, napi_value *result) -> napi_status {
385         napi_status status = napi_get_boolean(env, ctxt->isSwitchInput, result);
386         IMSA_HILOGE("output get boolean != nullptr[%{public}d]!", result != nullptr);
387         return status;
388     };
389     auto exec = [ctxt](AsyncCall::Context *ctx) {
390         int32_t errCode =
391             InputMethodController::GetInstance()->SwitchInputMethod(SwitchTrigger::CURRENT_IME, ctxt->name, ctxt->id);
392         if (errCode == ErrorCode::NO_ERROR) {
393             IMSA_HILOGI("exec SwitchInputMethod success.");
394             ctxt->status = napi_ok;
395             ctxt->SetState(ctxt->status);
396             ctxt->isSwitchInput = true;
397         } else {
398             ctxt->SetErrorCode(errCode);
399         }
400     };
401     ctxt->SetAction(std::move(input), std::move(output));
402     // 3 means JsAPI:switchCurrentInputMethodAndSubtype has 3 params at most.
403     AsyncCall asyncCall(env, info, ctxt, 3);
404     return asyncCall.Call(env, exec, "switchCurrentInputMethodAndSubtype");
405 }
406 } // namespace MiscServices
407 } // namespace OHOS