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