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