• 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_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