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