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