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