1 /*
2  * Copyright (c) 2022 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 "custom_input_method.h"
17 
18 #include <string>
19 
20 #include "common/input_method_manager.h"
21 #include "components/root_view.h"
22 #include "components/ui_checkbox.h"
23 #include "components/ui_edit_text.h"
24 #include "components/ui_label.h"
25 #include "font/ui_font.h"
26 #include "ui_test.h"
27 #include "graphic_timer.h"
28 
29 namespace OHOS {
30 namespace {
31 struct KeyInfo {
32     uint8_t keyCount;
33     uint8_t row1Count;
34     uint8_t row2Count;
35     uint8_t row3Count;
36     uint8_t row4Count;
37     const char* keyName[32]; // 32: max count of keys
38 };
39 
40 const KeyInfo LOW_CASE_KEY_IOFO = {
41     32, 10, 19, 28, 32,
42     {
43         "q",     "w",     "e", "r",     "t", "y", "u", "i", "o",   "p", // 10
44         "a",     "s",     "d", "f",     "g", "h", "j", "k", "l",        // 9
45         "shift", "z",     "x", "c",     "v", "b", "n", "m", "del",      // 9
46         "123",   "space", ".", "return"                                 // 4
47     }
48 };
49 
50 const KeyInfo UPPER_CASE_KEY_IOFO = {
51     32, 10, 19, 28, 32,
52     {
53         "Q",     "W",     "E", "R",     "T", "Y", "U", "I", "O",   "P", // 10
54         "A",     "S",     "D", "F",     "G", "H", "J", "K", "L",        // 9
55         "shift", "Z",     "X", "C",     "V", "B", "N", "M", "del",      // 9
56         "123",   "space", ".", "return"                                 // 4
57     }
58 };
59 
60 const KeyInfo NUM_KEY_IOFO = {
61     31, 10, 20, 27, 31,
62     {
63         "1",   "2",     "3", "4",     "5", "6", "7",   "8", "9", "0",  // 10
64         "-",   "/",     ":", ";",     "(", ")", "$",   "&", "@", "\"", // 10
65         "#+=", ".",     ",", "?",     "!", "'", "del",                 // 7
66         "ABC", "space", ".", "return"                                  // 4
67     }
68 };
69 
70 const KeyInfo SYMBOL_KEY_IOFO = {
71     31, 10, 20, 27, 31,
72     {
73         "[",   "]",     "{", "}",     "#", "%", "^",   "*", "+", "=", // 10
74         "_",   "\\",    "|", "~",     "<", ">", "¥",  "€", "£", "•", // 10
75         "123", ".",     ",", "?",     "!", "'", "del",                // 7
76         "ABC", "space", ".", "return"                                 // 4
77     }
78 };
79 
80 const int16_t KEYBOARD_WIDTH = 580;
81 const int16_t KEYBOARD_HEIGHT = 320;
82 const int16_t KEY_WIDTH = 50;
83 const int16_t KEY_HEIGHT = 65;
84 const int16_t FUNC_KEY_WIDTH = 65;
85 const int16_t SPACE_KEY_WIDTH = 260;
86 const int16_t RETURN_KEY_WIDTH = 120;
87 const int16_t KEY_BORDER_RADIUS = 10;
88 const int16_t KEY_MARGIN_LEFT = 7;
89 
GetKeyInfo(KeyboardType type)90 KeyInfo GetKeyInfo(KeyboardType type)
91 {
92     KeyInfo keyInfo;
93     switch (type) {
94         case KeyboardType::LOW_CASE:
95             keyInfo = LOW_CASE_KEY_IOFO;
96             break;
97         case KeyboardType::UPPER_CASE:
98             keyInfo = UPPER_CASE_KEY_IOFO;
99             break;
100         case KeyboardType::NUMBER:
101             keyInfo = NUM_KEY_IOFO;
102             break;
103         case KeyboardType::SYMBOL:
104             keyInfo = SYMBOL_KEY_IOFO;
105             break;
106         default:
107             keyInfo = LOW_CASE_KEY_IOFO;
108             break;
109     }
110 
111     return keyInfo;
112 }
113 } // namespace
114 
OnShow(InputMethodManager::InputMethodParam & param)115 void CustomInputMethod::OnShow(InputMethodManager::InputMethodParam& param)
116 {
117     bool needFocus = editView_ == nullptr ? true : false;
118     SetupView(keyboardType_);
119 
120     // update value
121     editView_->SetText(param.text.c_str());
122     editView_->SetInputType(param.inputType);
123     if (param.view != nullptr) {
124         UIEditText* paramView = reinterpret_cast<UIEditText*>(param.view);
125         editView_->SetPlaceholder(paramView->GetPlaceholder());
126         editView_->SetPlaceholderColor(paramView->GetPlaceholderColor());
127         editView_->SetCursorColor(paramView->GetCursorColor());
128         editView_->SetCursorIndex(paramView->GetCursorIndex());
129         editView_->SetTextColor(paramView->GetTextColor());
130         editView_->SetMaxLength(paramView->GetMaxLength());
131     }
132     if (needFocus) {
133         editView_->RequestFocus();
134     }
135 
136     container_->Invalidate();
137 
138     // keyboard show callback
139     InputMethodManager::GetInstance().OnKeyboardShow();
140 }
141 
OnHide()142 void CustomInputMethod::OnHide()
143 {
144     editView_->Blur();
145     TearDownView();
146 
147     // keyboard show callback
148     InputMethodManager::GetInstance().OnKeyboardHide();
149 }
150 
SetupView(KeyboardType type)151 void CustomInputMethod::SetupView(KeyboardType type)
152 {
153     if (container_ == nullptr) {
154         container_ = new UIViewGroup();
155         container_->Resize(KEYBOARD_WIDTH, 650); // 650: height
156         container_->SetPosition(350, 100);       // 350: position x, 100: position y
157         container_->SetStyle(STYLE_BORDER_COLOR, Color::White().full);
158         // add on rootview
159         RootView::GetInstance()->Add(container_);
160     }
161 
162     if (editView_ != nullptr) {
163         container_->Remove(editView_);
164         delete editView_;
165     }
166     editView_ = new UIEditTextEx();
167     container_->Add(editView_);
168     editView_->Resize(250, 40); // 250: width, 40: height
169     editView_->SetPosition(0, 0);
170     editView_->SetViewId("Input_edit_text_view");
171 
172     if (inputTypeBtn_ == nullptr) {
173         inputTypeBtn_ = new UILabelButton();
174         container_->Add(inputTypeBtn_);
175         inputTypeBtn_->Resize(100, 40); // 100: width, 40: height
176         inputTypeBtn_->SetText("toggle");
177         inputTypeBtn_->SetViewId(UI_TEST_KEY_INPUT);
178         inputTypeBtn_->LayoutRightToSibling("Input_edit_text_view", 10); // 10: offset
179         inputTypeBtn_->SetOnClickListener(this);
180         inputTypeBtn_->SetStyleForState(STYLE_BACKGROUND_COLOR, BUTTON_STYLE_BACKGROUND_COLOR_VALUE,
181                                         UIButton::RELEASED);
182         inputTypeBtn_->SetStyleForState(STYLE_BACKGROUND_COLOR, BUTTON_STYLE_BACKGROUND_COLOR_VALUE, UIButton::PRESSED);
183     }
184 
185     SetupKeyboard(type);
186 }
187 
SetupKeyboard(KeyboardType type)188 void CustomInputMethod::SetupKeyboard(KeyboardType type)
189 {
190     UIViewGroup* keyboard = new UIViewGroup();
191     int16_t rowHeight = KEYBOARD_HEIGHT / 4; // 4: row number
192     keyboard->Resize(KEYBOARD_WIDTH, KEYBOARD_HEIGHT);
193     keyboard->SetPosition(0, 45); // 45: position y
194     keyboard->SetStyle(STYLE_BACKGROUND_COLOR, Color::Gray().full);
195     keyboard->SetViewId("keyboard");
196     container_->Add(keyboard);
197     FlexLayout* row1 = SetupKeyRow("row1", KEYBOARD_WIDTH, rowHeight);
198     row1->SetPosition(0, 0);
199     keyboard->Add(row1);
200 
201     FlexLayout* row2 = SetupKeyRow("row2", KEYBOARD_WIDTH, rowHeight);
202     keyboard->Add(row2);
203     row2->LayoutBottomToSibling("row1");
204 
205     FlexLayout* row3 = SetupKeyRow("row3", KEYBOARD_WIDTH, rowHeight);
206     keyboard->Add(row3);
207     row3->LayoutBottomToSibling("row2");
208 
209     FlexLayout* row4 = SetupKeyRow("row4", KEYBOARD_WIDTH, rowHeight);
210     keyboard->Add(row4);
211     row4->LayoutBottomToSibling("row3");
212 
213     KeyInfo keyInfo = GetKeyInfo(type);
214     for (int16_t i = 0; i < keyInfo.keyCount; i++) {
215         UILabelButton* key = SetupButton(keyInfo.keyName[i]);
216         if (i < keyInfo.row1Count) {
217             row1->Add(key);
218         } else if (i < keyInfo.row2Count) {
219             row2->Add(key);
220         } else if (i < keyInfo.row3Count) {
221             row3->Add(key);
222         } else if (i < keyInfo.row4Count) {
223             row4->Add(key);
224         }
225     }
226 
227     row1->LayoutChildren();
228     row2->LayoutChildren();
229     row3->LayoutChildren();
230     row4->LayoutChildren();
231     keyboard->Invalidate();
232 }
233 
SetupKeyRow(const char * name,int16_t width,int16_t height)234 FlexLayout* CustomInputMethod::SetupKeyRow(const char* name, int16_t width, int16_t height)
235 {
236     FlexLayout* row = new FlexLayout();
237     row->Resize(width, height);
238     row->SetViewId(name);
239     row->SetMajorAxisAlign(ALIGN_CENTER);
240     row->SetStyle(STYLE_BACKGROUND_OPA, OPA_TRANSPARENT);
241     return row;
242 }
243 
SetupButton(const char * title)244 UILabelButton* CustomInputMethod::SetupButton(const char* title)
245 {
246     UILabelButton* keyBtn = new UILabelButton();
247     keyBtn->SetText(title);
248     keyBtn->SetViewId(title);
249     keyBtn->SetFont(DEFAULT_VECTOR_FONT_FILENAME, BUTTON_LABEL_SIZE);
250     keyBtn->SetStyle(STYLE_MARGIN_LEFT, KEY_MARGIN_LEFT);
251     keyBtn->SetOnClickListener(this);
252     int16_t radius = KEY_BORDER_RADIUS;
253     keyBtn->SetStyleForState(STYLE_BORDER_RADIUS, radius, UIButton::RELEASED);
254     keyBtn->SetStyleForState(STYLE_BORDER_RADIUS, radius, UIButton::PRESSED);
255     keyBtn->SetStyleForState(STYLE_BORDER_RADIUS, radius, UIButton::INACTIVE);
256     keyBtn->SetStyleForState(STYLE_BACKGROUND_COLOR, BUTTON_STYLE_BACKGROUND_COLOR_VALUE, UIButton::RELEASED);
257     keyBtn->SetStyleForState(STYLE_BACKGROUND_COLOR, BUTTON_STYLE_BACKGROUND_COLOR_VALUE, UIButton::PRESSED);
258     keyBtn->SetStyleForState(STYLE_BACKGROUND_COLOR, BUTTON_STYLE_BACKGROUND_COLOR_VALUE, UIButton::INACTIVE);
259     keyBtn->Resize(KEY_WIDTH, KEY_HEIGHT);
260 
261     keyBtn->SetOnLongPressListener(this);
262     keyBtn->SetOnTouchListener(this);
263 
264     if ((strcmp(title, "shift") == 0) || (strcmp(title, "del") == 0) || (strcmp(title, "123") == 0) ||
265         (strcmp(title, "ABC") == 0) || (strcmp(title, "#+=") == 0)) {
266         keyBtn->Resize(FUNC_KEY_WIDTH, KEY_HEIGHT);
267     } else if (strcmp(title, "space") == 0) {
268         keyBtn->Resize(SPACE_KEY_WIDTH, KEY_HEIGHT);
269     } else if (strcmp(title, "return") == 0) {
270         keyBtn->Resize(RETURN_KEY_WIDTH, KEY_HEIGHT);
271     }
272     return keyBtn;
273 }
274 
TearDownView()275 void CustomInputMethod::TearDownView()
276 {
277     UITest::DeleteChildren(container_);
278     container_ = nullptr;
279     editView_ = nullptr;
280 }
281 
OnClick(UIView & view,const ClickEvent & event)282 bool CustomInputMethod::OnClick(UIView& view, const ClickEvent& event)
283 {
284     if (inputTypeBtn_ == &view) {
285         InputType type = editView_->GetInputType();
286         if (type == InputType::TEXT_TYPE) {
287             editView_->SetInputType(InputType::PASSWORD_TYPE);
288             InputMethodManager::GetInstance().SetInputType(InputType::PASSWORD_TYPE);
289         } else {
290             editView_->SetInputType(InputType::TEXT_TYPE);
291             InputMethodManager::GetInstance().SetInputType(InputType::TEXT_TYPE);
292         }
293     } else {
294         DealKeyEvent(view);
295     }
296 
297     return true;
298 }
299 
DealKeyEvent(UIView & view)300 void CustomInputMethod::DealKeyEvent(UIView& view)
301 {
302     const char* key = reinterpret_cast<UILabelButton*>(&view)->GetText();
303     if (key == nullptr) {
304         return;
305     }
306 
307     if (editView_ == nullptr) {
308         return;
309     }
310 
311     if (editView_->GetIsFocus()) {
312         InputMethodManager::GetInstance().SetCursorIndex(editView_->GetCursorIndex());
313     } else {
314         editView_->SetCursorIndex(InputMethodManager::GetInstance().GetCursorIndex());
315     }
316 
317     if (strcmp(key, "shift") == 0) {
318         if (keyboardType_ == KeyboardType::LOW_CASE) {
319             keyboardType_ = KeyboardType::UPPER_CASE;
320         } else {
321             keyboardType_ = KeyboardType::LOW_CASE;
322         }
323         ChangeKeyboard(keyboardType_);
324     } else if (strcmp(key, "123") == 0) {
325         keyboardType_ = KeyboardType::NUMBER;
326         ChangeKeyboard(keyboardType_);
327     } else if (strcmp(key, "ABC") == 0) {
328         keyboardType_ = KeyboardType::LOW_CASE;
329         ChangeKeyboard(keyboardType_);
330     } else if (strcmp(key, "#+=") == 0) {
331         keyboardType_ = KeyboardType::SYMBOL;
332         ChangeKeyboard(keyboardType_);
333     } else if (strcmp(key, "del") == 0) {
334         InputMethodManager::GetInstance().DeleteBackward(1);
335         editView_->DeleteBackward(1);
336     } else if (strcmp(key, "space") == 0) {
337         InputMethodManager::GetInstance().InsertText(" ");
338         editView_->InsertText(" ");
339     } else if (strcmp(key, "return") == 0) {
340         // do nothing
341     } else {
342         InputMethodManager::GetInstance().InsertText(key);
343         editView_->InsertText(key);
344     }
345 }
346 
OnLongPress(UIView & view,const LongPressEvent & event)347 bool CustomInputMethod::OnLongPress(UIView &view, const LongPressEvent &event)
348 {
349     longPressed_ = true;
350     if (inputTypeBtn_ == &view) {
351         InputType type = editView_->GetInputType();
352         if (type == InputType::TEXT_TYPE) {
353             editView_->SetInputType(InputType::PASSWORD_TYPE);
354             InputMethodManager::GetInstance().SetInputType(InputType::PASSWORD_TYPE);
355         } else {
356             editView_->SetInputType(InputType::TEXT_TYPE);
357             InputMethodManager::GetInstance().SetInputType(InputType::TEXT_TYPE);
358         }
359     } else {
360         key_ = reinterpret_cast<UILabelButton*>(&view)->GetText();
361         timer_.Start();
362     }
363     return true;
364 }
365 
OnRelease(UIView & view,const ReleaseEvent & event)366 bool CustomInputMethod::OnRelease(UIView& view, const ReleaseEvent& event)
367 {
368     if (longPressed_) {
369         longPressed_ = false;
370         timer_.Stop();
371     }
372     return true;
373 }
374 
DealLongPressKeyEvent()375 void CustomInputMethod::DealLongPressKeyEvent()
376 {
377     if (key_ == nullptr) {
378         return;
379     }
380 
381     if (strcmp(key_, "del") == 0) {
382         InputMethodManager::GetInstance().DeleteBackward(1);
383         editView_->DeleteBackward(1);
384     } else if (strcmp(key_, "space") == 0) {
385         InputMethodManager::GetInstance().InsertText(" ");
386         editView_->InsertText(" ");
387     } else if (strcmp(key_, "return") == 0 || strcmp(key_, "123") == 0 || strcmp(key_, "ABC") == 0
388                || strcmp(key_, "#+=") == 0 || strcmp(key_, "shift") == 0) {
389         // do nothing
390     } else {
391         InputMethodManager::GetInstance().InsertText(key_);
392         editView_->InsertText(key_);
393     }
394 
395     if (!longPressed_) {
396         timer_.Stop();
397     } else {
398         timer_.Start();
399     }
400 }
401 
ChangeKeyboard(KeyboardType type)402 void CustomInputMethod::ChangeKeyboard(KeyboardType type)
403 {
404     UIView* keyboardTmp = container_->GetChildById("keyboard");
405     SetupKeyboard(type);
406     container_->Remove(keyboardTmp);
407     UITest::DeleteChildren(keyboardTmp);
408 }
409 } // namespace OHOS
410