1 /*
2 * Copyright (c) 2022-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 "core/components_ng/pattern/text_field/text_field_manager.h"
17
18 #include "base/geometry/dimension.h"
19 #include "base/memory/ace_type.h"
20 #include "base/utils/utils.h"
21 #include "core/common/ime/text_input_type.h"
22 #include "core/components_ng/event/focus_hub.h"
23 #include "core/components_ng/pattern/navigation/navigation_pattern.h"
24 #include "core/components_ng/pattern/scrollable/scrollable_pattern.h"
25 #include "core/components_ng/pattern/text/text_base.h"
26 #include "core/components_ng/pattern/text_field/text_field_pattern.h"
27
28 namespace OHOS::Ace::NG {
29 namespace {
30 constexpr Dimension RESERVE_BOTTOM_HEIGHT = 24.0_vp;
31 constexpr int32_t MAX_FILL_CONTENT_SIZE = 5;
32 } // namespace
33
ClearOnFocusTextField()34 void TextFieldManagerNG::ClearOnFocusTextField()
35 {
36 onFocusTextField_ = nullptr;
37 }
38
ClearOnFocusTextField(int32_t id)39 void TextFieldManagerNG::ClearOnFocusTextField(int32_t id)
40 {
41 if (onFocusTextFieldId_ == id) {
42 onFocusTextField_ = nullptr;
43 focusFieldIsInline_ = false;
44 optionalPosition_ = std::nullopt;
45 usingCustomKeyboardAvoid_ = false;
46 isScrollableChild_ = false;
47 }
48 }
49
OnBackPressed()50 bool TextFieldManagerNG::OnBackPressed()
51 {
52 auto pattern = onFocusTextField_.Upgrade();
53 CHECK_NULL_RETURN(pattern, false);
54 auto textBasePattern = AceType::DynamicCast<TextBase>(pattern);
55 CHECK_NULL_RETURN(textBasePattern, false);
56 return textBasePattern->OnBackPressed();
57 }
58
SetClickPosition(const Offset & position)59 void TextFieldManagerNG::SetClickPosition(const Offset& position)
60 {
61 auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
62 CHECK_NULL_VOID(pipeline);
63 auto rootHeight = pipeline->GetRootHeight();
64 if (GreatOrEqual(position.GetY(), rootHeight) || LessOrEqual(position.GetY(), 0.0f)) {
65 auto pattern = onFocusTextField_.Upgrade();
66 CHECK_NULL_VOID(pattern);
67 auto host = pattern->GetHost();
68 CHECK_NULL_VOID(host);
69 auto parent = host->GetAncestorNodeOfFrame(true);
70 while (parent) {
71 // when Panel and SheetPage is out of screen, no need to update position_ for keyboard avoidance
72 if (parent->GetTag() == V2::PANEL_ETS_TAG || parent->GetTag() == V2::SHEET_PAGE_TAG) {
73 return;
74 }
75 parent = parent->GetAncestorNodeOfFrame(true);
76 }
77 }
78 auto rootWidth = pipeline->GetRootWidth();
79 if (GreatOrEqual(position.GetX(), rootWidth) || LessNotEqual(position.GetX(), 0.0f)) {
80 return;
81 }
82 TAG_LOGD(AceLogTag::ACE_KEYBOARD, "SetClickPosition from %{public}s to %{public}s",
83 position_.ToString().c_str(), position.ToString().c_str());
84 position_ = position;
85 optionalPosition_ = position;
86 }
87
FindScrollableOfFocusedTextField(const RefPtr<FrameNode> & textField)88 RefPtr<FrameNode> TextFieldManagerNG::FindScrollableOfFocusedTextField(const RefPtr<FrameNode>& textField)
89 {
90 CHECK_NULL_RETURN(textField, {});
91 auto parent = textField->GetAncestorNodeOfFrame(true);
92 while (parent) {
93 auto pattern = parent->GetPattern<ScrollablePattern>();
94 if (pattern) {
95 return parent;
96 }
97 parent = parent->GetAncestorNodeOfFrame(true);
98 }
99 return {};
100 }
101
GetFocusedNodeCaretRect()102 RectF TextFieldManagerNG::GetFocusedNodeCaretRect()
103 {
104 auto node = onFocusTextField_.Upgrade();
105 CHECK_NULL_RETURN(node, RectF());
106 auto frameNode = node->GetHost();
107 CHECK_NULL_RETURN(frameNode, RectF());
108 auto textBase = DynamicCast<TextBase>(node);
109 CHECK_NULL_RETURN(textBase, RectF());
110 auto caretRect = textBase->GetCaretRect() + frameNode->GetTransformRectRelativeToWindow();
111 return caretRect;
112 }
113
TriggerCustomKeyboardAvoid()114 void TextFieldManagerNG::TriggerCustomKeyboardAvoid()
115 {
116 CHECK_NULL_VOID(UsingCustomKeyboardAvoid());
117 auto pattern = onFocusTextField_.Upgrade();
118 CHECK_NULL_VOID(pattern);
119 auto curPattern = DynamicCast<TextFieldPattern>(pattern);
120 CHECK_NULL_VOID(curPattern);
121 if (!curPattern->GetIsCustomKeyboardAttached()) {
122 return;
123 }
124 auto caretHeight = curPattern->GetCaretRect().Height();
125 auto safeHeight = caretHeight + curPattern->GetCaretRect().GetY();
126 if (curPattern->GetCaretRect().GetY() > caretHeight) {
127 safeHeight = caretHeight;
128 }
129 auto keyboardOverLay = curPattern->GetKeyboardOverLay();
130 CHECK_NULL_VOID(keyboardOverLay);
131 auto host = curPattern->GetHost();
132 CHECK_NULL_VOID(host);
133 auto nodeId = host->GetId();
134 keyboardOverLay->TriggerCustomKeyboardAvoid(nodeId, safeHeight);
135 }
136
TriggerAvoidOnCaretChange()137 void TextFieldManagerNG::TriggerAvoidOnCaretChange()
138 {
139 auto pattern = onFocusTextField_.Upgrade();
140 CHECK_NULL_VOID(pattern);
141 auto host = pattern->GetHost();
142 CHECK_NULL_VOID(host);
143 auto pipeline = host->GetContext();
144 CHECK_NULL_VOID(pipeline);
145 auto safeAreaManager = pipeline->GetSafeAreaManager();
146 CHECK_NULL_VOID(safeAreaManager);
147 if (!pipeline->UsingCaretAvoidMode() || NearEqual(safeAreaManager->GetKeyboardInset().Length(), 0)) {
148 return;
149 }
150 ScrollTextFieldToSafeArea();
151 if (UsingCustomKeyboardAvoid()) {
152 TriggerCustomKeyboardAvoid();
153 } else {
154 auto keyboardInset = safeAreaManager->GetKeyboardInset();
155 lastKeyboardOffset_ = safeAreaManager->GetKeyboardOffset(true);
156 Rect keyboardRect;
157 keyboardRect.SetRect(0, 0, 0, keyboardInset.Length());
158 pipeline->OnVirtualKeyboardAreaChange(keyboardRect,
159 GetFocusedNodeCaretRect().Top(), GetHeight());
160 }
161 auto currentKeyboardOffset = safeAreaManager->GetKeyboardOffset(true);
162 if (currentKeyboardOffset != lastKeyboardOffset_) {
163 AvoidKeyboardInSheet(host);
164 }
165 }
166
GetOnFocusTextFieldInfo(const WeakPtr<Pattern> & onFocusTextField)167 void TextFieldManagerNG::GetOnFocusTextFieldInfo(const WeakPtr<Pattern>& onFocusTextField)
168 {
169 auto node = onFocusTextField.Upgrade();
170 CHECK_NULL_VOID(node);
171 auto frameNode = node->GetHost();
172 CHECK_NULL_VOID(frameNode);
173 auto scrollableNode = FindScrollableOfFocusedTextField(frameNode);
174 CHECK_NULL_VOID(scrollableNode);
175 auto scrollPattern = scrollableNode->GetPattern<ScrollablePattern>();
176 CHECK_NULL_VOID(scrollPattern);
177 isScrollableChild_ = scrollPattern->IsScrollToSafeAreaHelper();
178 TAG_LOGI(ACE_KEYBOARD, "isScrollableChild_: %{public}d", isScrollableChild_);
179 }
180
FindCorrectScrollNode(const SafeAreaInsets::Inset & bottomInset,bool isShowKeyboard)181 RefPtr<FrameNode> TextFieldManagerNG::FindCorrectScrollNode(const SafeAreaInsets::Inset& bottomInset,
182 bool isShowKeyboard)
183 {
184 auto node = onFocusTextField_.Upgrade();
185 CHECK_NULL_RETURN(node, nullptr);
186 auto frameNode = node->GetHost();
187 CHECK_NULL_RETURN(frameNode, nullptr);
188 auto parent = frameNode->GetAncestorNodeOfFrame(true);
189 while (parent) {
190 auto pattern = parent->GetPattern<ScrollablePattern>();
191 if (!pattern) {
192 parent = parent->GetAncestorNodeOfFrame(true);
193 continue;
194 }
195 if (!pattern->IsScrollToSafeAreaHelper() || pattern->GetAxis() == Axis::HORIZONTAL) {
196 return nullptr;
197 }
198 auto scrollableRect = parent->GetTransformRectRelativeToWindow();
199 if (!isShowKeyboard || LessNotEqual(scrollableRect.Top(), bottomInset.start)) {
200 return parent;
201 }
202 parent = parent->GetAncestorNodeOfFrame(true);
203 }
204 return nullptr;
205 }
206
ScrollToSafeAreaHelper(const SafeAreaInsets::Inset & bottomInset,bool isShowKeyboard)207 bool TextFieldManagerNG::ScrollToSafeAreaHelper(
208 const SafeAreaInsets::Inset& bottomInset, bool isShowKeyboard)
209 {
210 auto node = onFocusTextField_.Upgrade();
211 CHECK_NULL_RETURN(node, false);
212 auto frameNode = node->GetHost();
213 CHECK_NULL_RETURN(frameNode, false);
214 auto textBase = DynamicCast<TextBase>(node);
215 CHECK_NULL_RETURN(textBase, false);
216 textBase->OnVirtualKeyboardAreaChanged();
217
218 auto scrollableNode = FindCorrectScrollNode(bottomInset, isShowKeyboard);
219 CHECK_NULL_RETURN(scrollableNode, false);
220 auto scrollPattern = scrollableNode->GetPattern<ScrollablePattern>();
221 CHECK_NULL_RETURN(scrollPattern && scrollPattern->IsScrollToSafeAreaHelper(), false);
222 CHECK_NULL_RETURN(scrollPattern->GetAxis() != Axis::HORIZONTAL, false);
223
224 auto scrollableRect = scrollableNode->GetTransformRectRelativeToWindow();
225 if (isShowKeyboard) {
226 CHECK_NULL_RETURN(LessNotEqual(scrollableRect.Top(), bottomInset.start), false);
227 }
228
229 auto caretRect = textBase->GetCaretRect() + frameNode->GetPositionToWindowWithTransform();
230 auto diffTop = caretRect.Top() - scrollableRect.Top();
231 // caret height larger scroll's content region
232 if (isShowKeyboard && LessOrEqual(diffTop, 0) && LessNotEqual(bottomInset.start,
233 (caretRect.Bottom() + RESERVE_BOTTOM_HEIGHT.ConvertToPx()))) {
234 return false;
235 }
236
237 // caret above scroll's content region
238 if (LessNotEqual(diffTop, 0)) {
239 TAG_LOGI(ACE_KEYBOARD, "scrollRect:%{public}s caretRect:%{public}s totalOffset()=%{public}f diffTop=%{public}f",
240 scrollableRect.ToString().c_str(), caretRect.ToString().c_str(), scrollPattern->GetTotalOffset(), diffTop);
241 scrollPattern->ScrollTo(scrollPattern->GetTotalOffset() + diffTop);
242 return true;
243 }
244
245 // caret inner scroll's content region
246 if (isShowKeyboard && LessNotEqual((caretRect.Bottom() + RESERVE_BOTTOM_HEIGHT.ConvertToPx()), bottomInset.start)) {
247 return false;
248 }
249
250 // caret below safeArea
251 float diffBot = 0.0f;
252
253 auto scrollBottom = isShowKeyboard && GreatOrEqual(scrollableRect.Bottom(), bottomInset.start) ?
254 bottomInset.start : scrollableRect.Bottom();
255 diffBot = scrollBottom - caretRect.Bottom() - RESERVE_BOTTOM_HEIGHT.ConvertToPx();
256 CHECK_NULL_RETURN(diffBot < 0, false);
257 TAG_LOGI(ACE_KEYBOARD, "scrollRect:%{public}s caretRect:%{public}s totalOffset()=%{public}f diffBot=%{public}f",
258 scrollableRect.ToString().c_str(), caretRect.ToString().c_str(), scrollPattern->GetTotalOffset(), diffBot);
259 scrollPattern->ScrollTo(scrollPattern->GetTotalOffset() - diffBot);
260 return true;
261 }
262
ScrollTextFieldToSafeArea()263 bool TextFieldManagerNG::ScrollTextFieldToSafeArea()
264 {
265 auto pipeline = PipelineContext::GetCurrentContext();
266 CHECK_NULL_RETURN(pipeline, false);
267 auto manager = pipeline->GetSafeAreaManager();
268 CHECK_NULL_RETURN(manager, false);
269 auto systemSafeArea = manager->GetSystemSafeArea();
270 uint32_t bottom = systemSafeArea.bottom_.IsValid()? systemSafeArea.bottom_.start : pipeline->GetCurrentRootHeight();
271 auto keyboardHeight = manager->GetRawKeyboardHeight();
272 SafeAreaInsets::Inset keyboardInset = { .start = bottom - keyboardHeight, .end = bottom };
273 bool isShowKeyboard = manager->GetKeyboardInset().IsValid();
274 int32_t keyboardOrientation = manager->GetKeyboardOrientation();
275 auto container = Container::Current();
276 if (keyboardOrientation != -1 && container && container->GetDisplayInfo()) {
277 auto nowOrientation = static_cast<int32_t>(container->GetDisplayInfo()->GetRotation());
278 if (nowOrientation != keyboardOrientation) {
279 // When rotating the screen, sometimes we might get a keyboard height that in wrong
280 // orientation due to timeing issue. In this case, we assume there is no keyboard.
281 TAG_LOGI(ACE_KEYBOARD, "Current Orientation can't match keyboard orientation");
282 keyboardInset = { .start = bottom, .end = bottom };
283 }
284 }
285 if (isShowKeyboard) {
286 auto bottomInset = pipeline->GetSafeArea().bottom_.Combine(keyboardInset);
287 CHECK_NULL_RETURN(bottomInset.IsValid(), false);
288 return ScrollToSafeAreaHelper(bottomInset, isShowKeyboard);
289 } else if (manager->KeyboardSafeAreaEnabled()) {
290 // hide keyboard only scroll when keyboard avoid mode is resize
291 return ScrollToSafeAreaHelper({0, 0}, isShowKeyboard);
292 }
293 return false;
294 }
295
SetHeight(float height)296 void TextFieldManagerNG::SetHeight(float height)
297 {
298 height_ = height + RESERVE_BOTTOM_HEIGHT.ConvertToPx();
299 }
300
UpdateScrollableParentViewPort(const RefPtr<FrameNode> & node)301 void TextFieldManagerNG::UpdateScrollableParentViewPort(const RefPtr<FrameNode>& node)
302 {
303 CHECK_NULL_VOID(node);
304 auto scrollableNode = FindScrollableOfFocusedTextField(node);
305 CHECK_NULL_VOID(scrollableNode);
306 auto scrollPattern = scrollableNode->GetPattern<ScrollablePattern>();
307 CHECK_NULL_VOID(scrollPattern);
308 if (scrollPattern->GetAxis() == Axis::HORIZONTAL) {
309 return;
310 }
311 auto scrollableRect = scrollableNode->GetTransformRectRelativeToWindow();
312 scrollableNode->SetViewPort(scrollableRect);
313 }
314
AvoidKeyBoardInNavigation()315 void TextFieldManagerNG::AvoidKeyBoardInNavigation()
316 {
317 auto node = onFocusTextField_.Upgrade();
318 auto pipeline = PipelineContext::GetCurrentContext();
319 CHECK_NULL_VOID(pipeline);
320 auto manager = pipeline->GetSafeAreaManager();
321 auto avoidKeyboardOffset = manager ? manager->GetKeyboardOffset() : 0.0f;
322 if (!node) {
323 auto navNode = weakNavNode_.Upgrade();
324 CHECK_NULL_VOID(navNode);
325 SetNavContentAvoidKeyboardOffset(navNode, avoidKeyboardOffset);
326 return;
327 }
328 auto frameNode = node->GetHost();
329 CHECK_NULL_VOID(frameNode);
330 auto preNavNode = weakNavNode_.Upgrade();
331 if (preNavNode) {
332 SetNavContentAvoidKeyboardOffset(preNavNode, 0.0f);
333 }
334 auto navNode = FindNavNode(frameNode);
335 CHECK_NULL_VOID(navNode);
336 weakNavNode_ = navNode;
337 SetNavContentAvoidKeyboardOffset(navNode, avoidKeyboardOffset);
338 }
339
AvoidKeyboardInSheet(const RefPtr<FrameNode> & textField)340 void TextFieldManagerNG::AvoidKeyboardInSheet(const RefPtr<FrameNode>& textField)
341 {
342 CHECK_NULL_VOID(textField);
343 auto parent = textField->GetAncestorNodeOfFrame(true);
344 while (parent) {
345 if (parent->GetHostTag() == V2::SHEET_PAGE_TAG) {
346 break;
347 }
348 parent = parent->GetAncestorNodeOfFrame(true);
349 }
350 CHECK_NULL_VOID(parent);
351 auto sheetNodePattern = parent->GetPattern<SheetPresentationPattern>();
352 CHECK_NULL_VOID(sheetNodePattern);
353 TAG_LOGI(ACE_KEYBOARD, "Force AvoidKeyboard in sheet");
354 sheetNodePattern->AvoidSafeArea(true);
355 }
356
FindNavNode(const RefPtr<FrameNode> & textField)357 RefPtr<FrameNode> TextFieldManagerNG::FindNavNode(const RefPtr<FrameNode>& textField)
358 {
359 CHECK_NULL_RETURN(textField, nullptr);
360 auto parent = textField->GetAncestorNodeOfFrame(true);
361 RefPtr<FrameNode> ret = nullptr;
362 while (parent) {
363 // when the sheet showed in navdestination, sheet replaced navdestination to do avoid keyboard.
364 if (parent->GetHostTag() == V2::SHEET_WRAPPER_TAG) {
365 auto sheetNode = parent->GetChildAtIndex(0);
366 CHECK_NULL_RETURN(sheetNode, nullptr);
367 return AceType::DynamicCast<FrameNode>(sheetNode);
368 }
369 if (parent->GetHostTag() == V2::DIALOG_ETS_TAG) {
370 return AceType::DynamicCast<FrameNode>(parent);
371 }
372 if (parent->GetHostTag() == V2::NAVDESTINATION_VIEW_ETS_TAG ||
373 parent->GetHostTag() == V2::NAVBAR_ETS_TAG) {
374 ret = parent;
375 break;
376 }
377 parent = parent->GetAncestorNodeOfFrame(true);
378 }
379 CHECK_NULL_RETURN(ret, nullptr);
380
381 // return navdestination or navBar if the closest ancestor navigation can expandKeyboard
382 // if can't, recursively find the ancestor navigation can expandKeyboard.
383 auto navigationNode = ret->GetAncestorNodeOfFrame(true);
384 while (navigationNode) {
385 if (navigationNode->GetHostTag() == V2::NAVIGATION_VIEW_ETS_TAG) {
386 break;
387 }
388 navigationNode = navigationNode->GetAncestorNodeOfFrame(true);
389 }
390 CHECK_NULL_RETURN(navigationNode, nullptr);
391 auto layoutProperty = navigationNode->GetLayoutProperty<NavigationLayoutProperty>();
392 CHECK_NULL_RETURN(layoutProperty, nullptr);
393 auto& opts = layoutProperty->GetSafeAreaExpandOpts();
394
395 // if the extended keyboard area is set for the navigation, top navdestination or navbar need to avoid keyboard,
396 // otherwise don't aovid, following parent navigation.
397 bool isExpandKeyboard = opts && (opts->type & SAFE_AREA_TYPE_KEYBOARD) && (opts->edges & SAFE_AREA_EDGE_BOTTOM);
398 if (isExpandKeyboard) {
399 return ret;
400 }
401 auto mayAvoidNavContentNode = FindNavNode(navigationNode);
402 if (mayAvoidNavContentNode) {
403 return mayAvoidNavContentNode;
404 }
405 SetNavContentAvoidKeyboardOffset(ret, 0.0f);
406 return nullptr;
407 }
408
SetNavContentAvoidKeyboardOffset(const RefPtr<FrameNode> & navNode,float avoidKeyboardOffset)409 void TextFieldManagerNG::SetNavContentAvoidKeyboardOffset(const RefPtr<FrameNode>& navNode, float avoidKeyboardOffset)
410 {
411 auto navDestinationNode = AceType::DynamicCast<NavDestinationGroupNode>(navNode);
412 if (navDestinationNode) {
413 TAG_LOGI(ACE_KEYBOARD, "navNode id:%{public}d, avoidKeyboardOffset:%{public}f", navNode->GetId(),
414 avoidKeyboardOffset);
415 auto pattern = navDestinationNode->GetPattern<NavDestinationPattern>();
416 if (pattern) {
417 avoidKeyboardOffset = pattern->NeedIgnoreKeyboard() ? 0.0f : avoidKeyboardOffset;
418 pattern->SetAvoidKeyboardOffset(avoidKeyboardOffset);
419 }
420 }
421 auto navBarNode = AceType::DynamicCast<NavBarNode>(navNode);
422 if (navBarNode) {
423 auto pattern = navBarNode->GetPattern<NavBarPattern>();
424 if (pattern) {
425 pattern->SetAvoidKeyboardOffset(avoidKeyboardOffset);
426 }
427 }
428 navNode->MarkDirtyNode(PROPERTY_UPDATE_LAYOUT);
429 }
430
AddTextFieldInfo(const TextFieldInfo & textFieldInfo)431 void TextFieldManagerNG::AddTextFieldInfo(const TextFieldInfo& textFieldInfo)
432 {
433 if (textFieldInfo.nodeId == -1 || textFieldInfo.autoFillContainerNodeId == -1) {
434 return;
435 }
436
437 auto containerNodeIter = textFieldInfoMap_.find(textFieldInfo.autoFillContainerNodeId);
438 if (containerNodeIter != textFieldInfoMap_.end()) {
439 auto& innerTextFieldMap = containerNodeIter->second;
440 innerTextFieldMap[textFieldInfo.nodeId] = textFieldInfo;
441 } else {
442 std::unordered_map<int32_t, TextFieldInfo> innerTextFieldInfoMap;
443 innerTextFieldInfoMap[textFieldInfo.nodeId] = textFieldInfo;
444 textFieldInfoMap_[textFieldInfo.autoFillContainerNodeId] = innerTextFieldInfoMap;
445 }
446 }
447
RemoveTextFieldInfo(const int32_t & autoFillContainerNodeId,const int32_t & nodeId)448 void TextFieldManagerNG::RemoveTextFieldInfo(const int32_t& autoFillContainerNodeId, const int32_t& nodeId)
449 {
450 auto containerNodeIter = textFieldInfoMap_.find(autoFillContainerNodeId);
451 if (containerNodeIter != textFieldInfoMap_.end()) {
452 auto& innerTextFieldInfoMap = containerNodeIter->second;
453 auto textFieldNodeIter = innerTextFieldInfoMap.find(nodeId);
454 if (textFieldNodeIter != innerTextFieldInfoMap.end()) {
455 innerTextFieldInfoMap.erase(textFieldNodeIter);
456 }
457 }
458 }
459
UpdateTextFieldInfo(const TextFieldInfo & textFieldInfo)460 void TextFieldManagerNG::UpdateTextFieldInfo(const TextFieldInfo& textFieldInfo)
461 {
462 if (textFieldInfo.nodeId == -1 || textFieldInfo.autoFillContainerNodeId == -1) {
463 return;
464 }
465 auto containerNodeIter = textFieldInfoMap_.find(textFieldInfo.autoFillContainerNodeId);
466 if (containerNodeIter != textFieldInfoMap_.end()) {
467 auto& innerTextFieldInfoMap = containerNodeIter->second;
468 auto textFieldNodeIter = innerTextFieldInfoMap.find(textFieldInfo.nodeId);
469 if (textFieldNodeIter != innerTextFieldInfoMap.end()) {
470 innerTextFieldInfoMap.erase(textFieldNodeIter);
471 }
472 innerTextFieldInfoMap[textFieldInfo.nodeId] = textFieldInfo;
473 } else {
474 AddTextFieldInfo(textFieldInfo);
475 }
476 }
477
HasAutoFillPasswordNodeInContainer(const int32_t & autoFillContainerNodeId,const int32_t & nodeId)478 bool TextFieldManagerNG::HasAutoFillPasswordNodeInContainer(
479 const int32_t& autoFillContainerNodeId, const int32_t& nodeId)
480 {
481 auto containerNodeIter = textFieldInfoMap_.find(autoFillContainerNodeId);
482 if (containerNodeIter == textFieldInfoMap_.end()) {
483 return false;
484 }
485
486 auto& innerTextFieldInfoMap = containerNodeIter->second;
487 auto textFieldNodeIter = innerTextFieldInfoMap.find(nodeId);
488 if (textFieldNodeIter == innerTextFieldInfoMap.end()) {
489 return false;
490 }
491
492 for (const auto& textField : innerTextFieldInfoMap) {
493 auto textFieldId = textField.first;
494 auto textFieldInfo = textField.second;
495 if (textFieldId == nodeId) {
496 continue;
497 }
498
499 auto isPasswordType = IsAutoFillPasswordType(textFieldInfo);
500 if (isPasswordType && textFieldInfo.enableAutoFill) {
501 return true;
502 }
503 }
504
505 return false;
506 }
507
IsAutoFillPasswordType(const TextFieldInfo & textFieldInfo)508 bool TextFieldManagerNG::IsAutoFillPasswordType(const TextFieldInfo& textFieldInfo)
509 {
510 return textFieldInfo.inputType == TextInputType::VISIBLE_PASSWORD ||
511 textFieldInfo.inputType == TextInputType::NEW_PASSWORD ||
512 textFieldInfo.inputType == TextInputType::NUMBER_PASSWORD ||
513 textFieldInfo.contentType == TextContentType::VISIBLE_PASSWORD ||
514 textFieldInfo.contentType == TextContentType::NEW_PASSWORD;
515 }
516
SetOnFocusTextField(const WeakPtr<Pattern> & onFocusTextField)517 void TextFieldManagerNG::SetOnFocusTextField(const WeakPtr<Pattern>& onFocusTextField)
518 {
519 const auto& pattern = onFocusTextField.Upgrade();
520 if (pattern && pattern->GetHost()) {
521 onFocusTextFieldId_ = pattern->GetHost()->GetId();
522 }
523 if (onFocusTextField_ != onFocusTextField) {
524 SetImeAttached(false);
525 GetOnFocusTextFieldInfo(onFocusTextField);
526 }
527 onFocusTextField_ = onFocusTextField;
528 }
529
GetImeShow() const530 bool TextFieldManagerNG::GetImeShow() const
531 {
532 if (!imeShow_ && imeAttachCalled_) {
533 TAG_LOGI(ACE_KEYBOARD, "imeNotShown but attach called, still consider that as shown");
534 }
535 return imeShow_ || imeAttachCalled_;
536 }
537
AddAvoidKeyboardCallback(int32_t id,bool isCustomKeyboard,const std::function<void ()> && callback)538 void TextFieldManagerNG::AddAvoidKeyboardCallback(
539 int32_t id, bool isCustomKeyboard, const std::function<void()>&& callback)
540 {
541 if (isCustomKeyboard) {
542 avoidCustomKeyboardCallbacks_.insert({ id, std::move(callback) });
543 } else {
544 avoidSystemKeyboardCallbacks_.insert({ id, std::move(callback) });
545 }
546 }
547
OnAfterAvoidKeyboard(bool isCustomKeyboard)548 void TextFieldManagerNG::OnAfterAvoidKeyboard(bool isCustomKeyboard)
549 {
550 auto callbacks =
551 isCustomKeyboard ? std::move(avoidCustomKeyboardCallbacks_) : std::move(avoidSystemKeyboardCallbacks_);
552 for (const auto& pair : callbacks) {
553 if (pair.second) {
554 pair.second();
555 }
556 }
557 }
558
~TextFieldManagerNG()559 TextFieldManagerNG::~TextFieldManagerNG()
560 {
561 textFieldInfoMap_.clear();
562 textFieldFillContentMaps_.clear();
563 }
564
ParseFillContentJsonValue(const std::unique_ptr<JsonValue> & jsonObject)565 bool TextFieldManagerNG::ParseFillContentJsonValue(const std::unique_ptr<JsonValue>& jsonObject)
566 {
567 if (!jsonObject->IsValid() || !jsonObject->IsArray()) {
568 TAG_LOGW(AceLogTag::ACE_AUTO_FILL, "fillContent list format is invalid");
569 return false;
570 }
571
572 for (int32_t i = 0; i < jsonObject->GetArraySize(); ++i) {
573 auto item = jsonObject->GetArrayItem(i);
574 if (!item) {
575 continue;
576 }
577 auto nodeId = item->GetInt("id", -1);
578 if (nodeId == -1) {
579 continue;
580 }
581 FillContentMap fillContentMap;
582 auto fillContent = item->GetValue("fillContent");
583 if (!fillContent) {
584 continue;
585 }
586 GenerateFillContentMap(fillContent->GetString(), fillContentMap);
587 if (!fillContentMap.empty()) {
588 textFieldFillContentMaps_[nodeId] = fillContentMap;
589 }
590 }
591 return true;
592 }
593
GenerateFillContentMap(const std::string & fillContent,FillContentMap & map)594 void TextFieldManagerNG::GenerateFillContentMap(const std::string& fillContent, FillContentMap& map)
595 {
596 auto jsonObject = JsonUtil::ParseJsonString(fillContent);
597 CHECK_NULL_VOID(jsonObject);
598 if (!jsonObject->IsValid() || jsonObject->IsArray() || !jsonObject->IsObject()) {
599 TAG_LOGW(AceLogTag::ACE_AUTO_FILL, "fillContent format is invalid");
600 return;
601 }
602 auto child = jsonObject->GetChild();
603 while (child && child->IsValid()) {
604 if (!child->IsObject() && child->IsString()) {
605 std::string strKey = child->GetKey();
606 std::string strVal = child->GetString();
607 if (strKey.empty()) {
608 child = child->GetNext();
609 continue;
610 }
611 if (map.size() < MAX_FILL_CONTENT_SIZE) {
612 map.insert(std::pair<std::string, std::variant<std::string, bool, int32_t>>(strKey, strVal));
613 } else {
614 TAG_LOGW(AceLogTag::ACE_AUTO_FILL, "fillContent is more than 5");
615 break;
616 }
617 }
618 child = child->GetNext();
619 }
620 }
621
GetFillContentMap(int32_t id)622 FillContentMap TextFieldManagerNG::GetFillContentMap(int32_t id)
623 {
624 std::unordered_map<std::string, std::variant<std::string, bool, int32_t>> fillContentMap;
625 auto fillContentMapIter = textFieldFillContentMaps_.find(id);
626 if (fillContentMapIter != textFieldFillContentMaps_.end()) {
627 fillContentMap = fillContentMapIter->second;
628 }
629 return fillContentMap;
630 }
631
RemoveFillContentMap(int32_t id)632 void TextFieldManagerNG::RemoveFillContentMap(int32_t id)
633 {
634 auto fillContentMapIter = textFieldFillContentMaps_.find(id);
635 if (fillContentMapIter != textFieldFillContentMaps_.end()) {
636 textFieldFillContentMaps_.erase(fillContentMapIter);
637 }
638 }
639 } // namespace OHOS::Ace::NG
640