1 /*
2 * Copyright (c) 2023 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 "imf_adapter_impl.h"
17
18 #include "hisysevent_adapter.h"
19 #include "nweb_log.h"
20 #include "ohos_adapter_helper.h"
21 #include "third_party/cJSON/cJSON.h"
22
23 namespace OHOS::NWeb {
24 constexpr char INPUT_METHOD[] = "INPUT_METHOD";
25 constexpr char ATTACH_CODE[] = "ATTACH_CODE";
26 constexpr char IS_SHOW_KEY_BOARD[] = "IS_SHOW_KEY_BOARD";
27 constexpr int32_t IMF_LISTENER_NULL_POINT = 1;
28 constexpr int32_t IMF_TEXT_CONFIG_NULL_POINT = 2;
29 const std::string AUTO_FILL_CANCEL_PRIVATE_COMMAND = "autofill.cancel";
30
IMFTextListenerAdapterImpl(const std::shared_ptr<IMFTextListenerAdapter>& listener)31 IMFTextListenerAdapterImpl::IMFTextListenerAdapterImpl(const std::shared_ptr<IMFTextListenerAdapter>& listener)
32 : listener_(listener) {};
33
34 IMFTextListenerAdapterImpl::~IMFTextListenerAdapterImpl() = default;
35
InsertText(const std::u16string& text)36 void IMFTextListenerAdapterImpl::InsertText(const std::u16string& text)
37 {
38 if (listener_) {
39 listener_->InsertText(text);
40 }
41 }
42
DeleteForward(int32_t length)43 void IMFTextListenerAdapterImpl::DeleteForward(int32_t length)
44 {
45 if (listener_) {
46 listener_->DeleteForward(length);
47 }
48 }
49
DeleteBackward(int32_t length)50 void IMFTextListenerAdapterImpl::DeleteBackward(int32_t length)
51 {
52 if (listener_) {
53 listener_->DeleteBackward(length);
54 }
55 }
56
SendKeyEventFromInputMethod(const MiscServices::KeyEvent& event)57 void IMFTextListenerAdapterImpl::SendKeyEventFromInputMethod(const MiscServices::KeyEvent& event)
58 {
59 (void)event;
60 if (listener_) {
61 listener_->SendKeyEventFromInputMethod();
62 }
63 }
64
SendKeyboardStatus(const MiscServices::KeyboardStatus& keyboardStatus)65 void IMFTextListenerAdapterImpl::SendKeyboardStatus(const MiscServices::KeyboardStatus& keyboardStatus)
66 {
67 if (listener_) {
68 auto status = IMFAdapterKeyboardStatus::NONE;
69 if (keyboardStatus == MiscServices::KeyboardStatus::SHOW) {
70 status = IMFAdapterKeyboardStatus::SHOW;
71 } else if (keyboardStatus == MiscServices::KeyboardStatus::HIDE) {
72 status = IMFAdapterKeyboardStatus::HIDE;
73 }
74 listener_->SendKeyboardStatus(status);
75 }
76 }
77
SendFunctionKey(const MiscServices::FunctionKey& functionKey)78 void IMFTextListenerAdapterImpl::SendFunctionKey(const MiscServices::FunctionKey& functionKey)
79 {
80 if (listener_) {
81 std::shared_ptr<IMFAdapterFunctionKeyAdapterImpl> adapterFunction =
82 std::make_shared<IMFAdapterFunctionKeyAdapterImpl>();
83 switch (functionKey.GetEnterKeyType()) {
84 case MiscServices::EnterKeyType::UNSPECIFIED:
85 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::UNSPECIFIED);
86 break;
87 case MiscServices::EnterKeyType::NONE:
88 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::NONE);
89 break;
90 case MiscServices::EnterKeyType::GO:
91 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::GO);
92 break;
93 case MiscServices::EnterKeyType::SEARCH:
94 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::SEARCH);
95 break;
96 case MiscServices::EnterKeyType::SEND:
97 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::SEND);
98 break;
99 case MiscServices::EnterKeyType::NEXT:
100 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::NEXT);
101 break;
102 case MiscServices::EnterKeyType::DONE:
103 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::DONE);
104 break;
105 case MiscServices::EnterKeyType::PREVIOUS:
106 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::PREVIOUS);
107 break;
108 case MiscServices::EnterKeyType::NEW_LINE:
109 adapterFunction->SetEnterKeyType(IMFAdapterEnterKeyType::NEW_LINE);
110 break;
111 default:
112 WVLOG_E("unknown functionKey");
113 break;
114 }
115 listener_->SendFunctionKey(adapterFunction);
116 }
117 }
118
SetKeyboardStatus(bool status)119 void IMFTextListenerAdapterImpl::SetKeyboardStatus(bool status)
120 {
121 if (listener_) {
122 listener_->SetKeyboardStatus(status);
123 }
124 }
125
MoveCursor(const MiscServices::Direction direction)126 void IMFTextListenerAdapterImpl::MoveCursor(const MiscServices::Direction direction)
127 {
128 if (listener_) {
129 IMFAdapterDirection adapterDirection;
130 switch (direction) {
131 case MiscServices::Direction::UP: {
132 adapterDirection = IMFAdapterDirection::UP;
133 break;
134 }
135 case MiscServices::Direction::DOWN: {
136 adapterDirection = IMFAdapterDirection::DOWN;
137 break;
138 }
139 case MiscServices::Direction::LEFT: {
140 adapterDirection = IMFAdapterDirection::LEFT;
141 break;
142 }
143 case MiscServices::Direction::RIGHT: {
144 adapterDirection = IMFAdapterDirection::RIGHT;
145 break;
146 }
147 default: {
148 adapterDirection = IMFAdapterDirection::NONE;
149 }
150 }
151 listener_->MoveCursor(adapterDirection);
152 }
153 }
154
HandleSetSelection(int32_t start, int32_t end)155 void IMFTextListenerAdapterImpl::HandleSetSelection(int32_t start, int32_t end)
156 {
157 if (listener_) {
158 listener_->HandleSetSelection(start, end);
159 }
160 }
161
HandleExtendAction(int32_t action)162 void IMFTextListenerAdapterImpl::HandleExtendAction(int32_t action)
163 {
164 if (listener_) {
165 listener_->HandleExtendAction(action);
166 }
167 }
168
HandleSelect(int32_t keyCode, int32_t cursorMoveSkip)169 void IMFTextListenerAdapterImpl::HandleSelect(int32_t keyCode, int32_t cursorMoveSkip)
170 {
171 if (listener_) {
172 listener_->HandleSelect(keyCode, cursorMoveSkip);
173 }
174 }
175
GetTextIndexAtCursor()176 int32_t IMFTextListenerAdapterImpl::GetTextIndexAtCursor()
177 {
178 if (listener_) {
179 return listener_->GetTextIndexAtCursor();
180 }
181 return -1;
182 }
183
GetLeftTextOfCursor(int32_t number)184 std::u16string IMFTextListenerAdapterImpl::GetLeftTextOfCursor(int32_t number)
185 {
186 if (listener_) {
187 return listener_->GetLeftTextOfCursor(number);
188 }
189 return u"";
190 }
191
GetRightTextOfCursor(int32_t number)192 std::u16string IMFTextListenerAdapterImpl::GetRightTextOfCursor(int32_t number)
193 {
194 if (listener_) {
195 return listener_->GetRightTextOfCursor(number);
196 }
197 return u"";
198 }
199
SetPreviewText(const std::u16string& text, const MiscServices::Range& range)200 int32_t IMFTextListenerAdapterImpl::SetPreviewText(const std::u16string& text, const MiscServices::Range& range)
201 {
202 if (listener_) {
203 return listener_->SetPreviewText(text, range.start, range.end);
204 }
205 return -1;
206 }
207
FinishTextPreview()208 void IMFTextListenerAdapterImpl::FinishTextPreview()
209 {
210 if (listener_) {
211 listener_->FinishTextPreview();
212 }
213 }
214
ReceivePrivateCommand( const std::unordered_map<std::string, MiscServices::PrivateDataValue>& privateCommand)215 int32_t IMFTextListenerAdapterImpl::ReceivePrivateCommand(
216 const std::unordered_map<std::string, MiscServices::PrivateDataValue>& privateCommand)
217 {
218 WVLOG_I("ReceivePrivateCommand");
219 auto item = privateCommand.find(PREVIEW_TEXT_STYLE_KEY);
220 if (item != privateCommand.end()) {
221 bool is_need_underline = false;
222 MiscServices::PrivateDataValue data = item->second;
223 std::string previewStyle = std::get<std::string>(data);
224 if (previewStyle == PREVIEW_TEXT_STYLE_UNDERLINE) {
225 is_need_underline = true;
226 }
227
228 if (listener_) {
229 listener_->SetNeedUnderLine(is_need_underline);
230 }
231 }
232
233 item = privateCommand.find(AUTO_FILL_PARAMS_USERNAME);
234 if (item != privateCommand.end()) {
235 if (listener_) {
236 std::string content = std::get<std::string>(item->second);
237 listener_->AutoFillWithIMFEvent(true, false, false, content);
238 }
239 }
240
241 item = privateCommand.find(AUTO_FILL_PARAMS_OTHERACCOUNT);
242 if (item != privateCommand.end()) {
243 if (listener_) {
244 std::string content = std::string("");
245 listener_->AutoFillWithIMFEvent(false, true, false, content);
246 }
247 }
248
249 return 0;
250 }
251
Attach(std::shared_ptr<IMFTextListenerAdapter> listener, bool isShowKeyboard)252 bool IMFAdapterImpl::Attach(std::shared_ptr<IMFTextListenerAdapter> listener, bool isShowKeyboard)
253 {
254 if (!listener) {
255 WVLOG_E("the listener is nullptr");
256 return false;
257 }
258 if (!textListener_) {
259 textListener_ = new (std::nothrow) IMFTextListenerAdapterImpl(listener);
260 if (!textListener_) {
261 WVLOG_E("new textListener failed");
262 return false;
263 }
264 }
265 int32_t ret = MiscServices::InputMethodController::GetInstance()->Attach(textListener_, isShowKeyboard);
266 if (ret != 0) {
267 WVLOG_E("inputmethod attach failed, errcode=%{public}d", ret);
268 return false;
269 }
270 return true;
271 }
272
ReportImfErrorEvent(int32_t ret, bool isShowKeyboard)273 void ReportImfErrorEvent(int32_t ret, bool isShowKeyboard)
274 {
275 std::string isShowKeyboardStr = isShowKeyboard ? "true" : "false";
276 OhosAdapterHelper::GetInstance().GetHiSysEventAdapterInstance().Write(INPUT_METHOD,
277 HiSysEventAdapter::EventType::FAULT,
278 { ATTACH_CODE, std::to_string(ret), IS_SHOW_KEY_BOARD, isShowKeyboardStr });
279 }
280
Attach(std::shared_ptr<IMFTextListenerAdapter> listener, bool isShowKeyboard, const std::shared_ptr<IMFTextConfigAdapter> config, bool isResetListener)281 bool IMFAdapterImpl::Attach(std::shared_ptr<IMFTextListenerAdapter> listener, bool isShowKeyboard,
282 const std::shared_ptr<IMFTextConfigAdapter> config, bool isResetListener)
283 {
284 if (!listener) {
285 WVLOG_E("the listener is nullptr");
286 ReportImfErrorEvent(IMF_LISTENER_NULL_POINT, isShowKeyboard);
287 return false;
288 }
289 if (!config || !(config->GetInputAttribute()) || !(config->GetCursorInfo())) {
290 WVLOG_E("the config is nullptr");
291 ReportImfErrorEvent(IMF_TEXT_CONFIG_NULL_POINT, isShowKeyboard);
292 return false;
293 }
294
295 if ((textListener_ != nullptr) && isResetListener) {
296 textListener_ = nullptr;
297 WVLOG_I("attach node is changed, need reset listener");
298 }
299
300 if (!textListener_) {
301 textListener_ = new (std::nothrow) IMFTextListenerAdapterImpl(listener);
302 if (!textListener_) {
303 WVLOG_E("new textListener failed");
304 ReportImfErrorEvent(IMF_LISTENER_NULL_POINT, isShowKeyboard);
305 return false;
306 }
307 }
308
309 MiscServices::InputAttribute inputAttribute = { .inputPattern = config->GetInputAttribute()->GetInputPattern(),
310 .enterKeyType = config->GetInputAttribute()->GetEnterKeyType(),
311 .isTextPreviewSupported = true };
312
313 MiscServices::CursorInfo imfInfo = { .left = config->GetCursorInfo()->GetLeft(),
314 .top = config->GetCursorInfo()->GetTop(),
315 .width = config->GetCursorInfo()->GetWidth(),
316 .height = config->GetCursorInfo()->GetHeight() };
317
318 MiscServices::TextConfig textConfig = { .inputAttribute = inputAttribute,
319 .cursorInfo = imfInfo,
320 .windowId = config->GetWindowId(),
321 .positionY = config->GetPositionY(),
322 .height = config->GetHeight() };
323 WVLOG_I("web inputmethod attach, isShowKeyboard=%{public}d, textConfig=%{public}s", isShowKeyboard,
324 textConfig.ToString().c_str());
325 int32_t ret = MiscServices::InputMethodController::GetInstance()->Attach(textListener_, isShowKeyboard, textConfig);
326 if (ret != 0) {
327 WVLOG_E("inputmethod attach failed, errcode=%{public}d", ret);
328 ReportImfErrorEvent(ret, isShowKeyboard);
329 return false;
330 }
331 return true;
332 }
333
ShowCurrentInput(const IMFAdapterTextInputType& inputType)334 void IMFAdapterImpl::ShowCurrentInput(const IMFAdapterTextInputType& inputType)
335 {
336 MiscServices::Configuration config;
337 if (inputType == IMFAdapterTextInputType::NUMBER) {
338 config.SetTextInputType(MiscServices::TextInputType::NUMBER);
339 } else {
340 config.SetTextInputType(MiscServices::TextInputType::TEXT);
341 }
342 MiscServices::InputMethodController::GetInstance()->OnConfigurationChange(config);
343 MiscServices::InputMethodController::GetInstance()->ShowCurrentInput();
344 }
345
HideTextInput()346 void IMFAdapterImpl::HideTextInput()
347 {
348 MiscServices::InputMethodController::GetInstance()->HideTextInput();
349 }
350
Close()351 void IMFAdapterImpl::Close()
352 {
353 MiscServices::InputMethodController::GetInstance()->Close();
354 }
355
OnCursorUpdate(const std::shared_ptr<IMFCursorInfoAdapter> cursorInfo)356 void IMFAdapterImpl::OnCursorUpdate(const std::shared_ptr<IMFCursorInfoAdapter> cursorInfo)
357 {
358 if (!cursorInfo) {
359 WVLOG_E("inputmethod OnCursorUpdate cursorInfo is null");
360 return;
361 }
362
363 MiscServices::CursorInfo imfInfo = { .left = cursorInfo->GetLeft(),
364 .top = cursorInfo->GetTop(),
365 .width = cursorInfo->GetWidth(),
366 .height = cursorInfo->GetHeight() };
367 WVLOG_D("imfInfo left = %{public}f, top = %{public}f, width = %{public}f, height = %{public}f", imfInfo.left,
368 imfInfo.top, imfInfo.width, imfInfo.height);
369 MiscServices::InputMethodController::GetInstance()->OnCursorUpdate(imfInfo);
370 }
371
OnSelectionChange(std::u16string text, int start, int end)372 void IMFAdapterImpl::OnSelectionChange(std::u16string text, int start, int end)
373 {
374 MiscServices::InputMethodController::GetInstance()->OnSelectionChange(text, start, end);
375 }
376
SendPrivateCommand(const std::string& commandKey, const std::string& commandValue)377 bool IMFAdapterImpl::SendPrivateCommand(const std::string& commandKey, const std::string& commandValue)
378 {
379 if (commandKey == AUTO_FILL_CANCEL_PRIVATE_COMMAND) {
380 std::unordered_map<std::string, MiscServices::PrivateDataValue> privateCommand;
381 ParseFillContentJsonValue(commandValue, privateCommand);
382 int32_t ret = MiscServices::InputMethodController::GetInstance()->SendPrivateCommand(privateCommand);
383 if (ret != 0) {
384 WVLOG_E("inputmethod SendPrivateCommand failed, errcode=%{public}d", ret);
385 return false;
386 }
387 WVLOG_I("inputmethod SendPrivateCommand success");
388 return true;
389 }
390 return false;
391 }
392
ParseFillContentJsonValue(const std::string& commandValue, std::unordered_map<std::string, std::variant<std::string, bool, int32_t>>& map)393 bool IMFAdapterImpl::ParseFillContentJsonValue(const std::string& commandValue,
394 std::unordered_map<std::string, std::variant<std::string, bool, int32_t>>& map)
395 {
396 cJSON* sourceJson = cJSON_Parse(commandValue.c_str());
397 if (sourceJson == nullptr || cJSON_IsNull(sourceJson)) {
398 cJSON_Delete(sourceJson);
399 return false;
400 }
401 if (cJSON_HasObjectItem(sourceJson, "userName")) {
402 cJSON* userName = cJSON_GetObjectItem(sourceJson, "userName");
403 if (userName != nullptr && cJSON_IsString(userName) && userName->valuestring != nullptr) {
404 map.insert(std::make_pair("userName", userName->valuestring));
405 }
406 }
407 if (cJSON_HasObjectItem(sourceJson, "hasAccount")) {
408 cJSON* hasAccount = cJSON_GetObjectItem(sourceJson, "hasAccount");
409 if (hasAccount != nullptr && cJSON_IsString(hasAccount) && hasAccount->valuestring != nullptr) {
410 map.insert(std::make_pair("hasAccount", hasAccount->valuestring));
411 }
412 }
413 cJSON_Delete(sourceJson);
414 return true;
415 }
416 } // namespace OHOS::NWeb
417