1 /*
2 * Copyright (c) 2024 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/common/container.h"
17 #include "core/common/ime/input_method_manager.h"
18 #include "core/components_ng/event/focus_hub.h"
19 #include "core/components_ng/pattern/text_field/text_field_manager.h"
20 #include "core/components_ng/pattern/window_scene/helper/window_scene_helper.h"
21 #include "core/pipeline_ng/pipeline_context.h"
22
23 #ifndef ACE_UNITTEST
24 #ifdef ENABLE_STANDARD_INPUT
25 #include "input_method_controller.h"
26 #endif
27 #endif
28
29 namespace OHOS::Ace {
30 std::unique_ptr<InputMethodManager> InputMethodManager::instance_ = nullptr;
31 std::mutex InputMethodManager::mtx_;
32
GetInstance()33 InputMethodManager* InputMethodManager::GetInstance()
34 {
35 if (instance_ == nullptr) {
36 std::lock_guard<std::mutex> lock(mtx_);
37 if (instance_ == nullptr) {
38 instance_.reset(new InputMethodManager);
39 }
40 }
41 return instance_.get();
42 }
43
OnFocusNodeChange(const RefPtr<NG::FrameNode> & curFocusNode)44 void InputMethodManager::OnFocusNodeChange(const RefPtr<NG::FrameNode>& curFocusNode)
45 {
46 auto container = Container::Current();
47 if (container && container->IsKeyboard()) {
48 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "focus in input method.");
49 return;
50 }
51 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "current focus node info : (%{public}s/%{public}d).",
52 curFocusNode->GetTag().c_str(), curFocusNode->GetId());
53
54 auto currentFocusNode = curFocusNode_.Upgrade();
55 if (currentFocusNode && currentFocusNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG &&
56 currentFocusNode != curFocusNode) {
57 curFocusNode_ = curFocusNode;
58 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "UIExtension switch focus");
59 auto pattern = curFocusNode->GetPattern();
60 if (!pattern->NeedSoftKeyboard()) {
61 HideKeyboardAcrossProcesses();
62 }
63 }
64
65 curFocusNode_ = curFocusNode;
66 #ifdef WINDOW_SCENE_SUPPORTED
67 auto isWindowScene = NG::WindowSceneHelper::IsWindowScene(curFocusNode);
68 if (isWindowScene) {
69 ProcessKeyboardInWindowScene(curFocusNode);
70 } else {
71 ProcessKeyboard(curFocusNode);
72 }
73 #else
74 CloseKeyboard(curFocusNode);
75 #endif
76 }
77
ProcessKeyboardInWindowScene(const RefPtr<NG::FrameNode> & curFocusNode)78 void InputMethodManager::ProcessKeyboardInWindowScene(const RefPtr<NG::FrameNode>& curFocusNode)
79 {
80 if (curFocusNode && NG::WindowSceneHelper::IsFocusWindowSceneCloseKeyboard(curFocusNode)) {
81 lastKeep_ = true;
82 } else {
83 lastKeep_ = false;
84 }
85 // Frame other window to SCB window Or inSCB window changes,hide keyboard.
86 if ((windowFocus_.has_value() && windowFocus_.value())) {
87 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "SCB Window focus first, ready to hide keyboard.");
88 windowFocus_.reset();
89 NG::WindowSceneHelper::IsWindowSceneCloseKeyboard(curFocusNode);
90 return;
91 }
92 // In window scene, focus change, need close keyboard.
93 auto pattern = curFocusNode->GetPattern();
94 if (!pattern->NeedSoftKeyboard()) {
95 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "SCB WindowscenePage ready to close keyboard.");
96 NG::WindowSceneHelper::IsCloseKeyboard(curFocusNode);
97 }
98 }
99
ProcessKeyboard(const RefPtr<NG::FrameNode> & curFocusNode)100 void InputMethodManager::ProcessKeyboard(const RefPtr<NG::FrameNode>& curFocusNode)
101 {
102 if (curFocusNode && curFocusNode->GetTag() == V2::SCREEN_ETS_TAG && lastKeep_) {
103 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "node is screen and last node want to save keyboard Ignore");
104 lastKeep_ = false;
105 return;
106 }
107 auto pipeline = curFocusNode->GetContextRefPtr();
108 CHECK_NULL_VOID(pipeline);
109 if (windowFocus_.has_value() && windowFocus_.value()) {
110 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Normal Window focus first, set focus flag to window.");
111 windowFocus_.reset();
112 auto callback = pipeline->GetWindowFocusCallback();
113 if (callback) {
114 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Trigger Window Focus Callback");
115 callback();
116 } else {
117 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "No Window Focus Callback");
118 if (!pipeline->NeedSoftKeyboard()) {
119 HideKeyboardAcrossProcesses();
120 }
121 }
122 return;
123 }
124
125 if (curFocusNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG ||
126 curFocusNode->GetTag() == V2::EMBEDDED_COMPONENT_ETS_TAG) {
127 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "UIExtension(%{public}s/%{public}d) not need process.",
128 curFocusNode->GetTag().c_str(), curFocusNode->GetId());
129 return;
130 }
131
132 auto container = Container::Current();
133 auto isUIExtension = container && container->IsUIExtensionWindow();
134 auto pattern = curFocusNode->GetPattern();
135 CHECK_NULL_VOID(pattern);
136 if (isUIExtension && !pattern->NeedSoftKeyboard()) {
137 HideKeyboardAcrossProcesses();
138 } else {
139 CloseKeyboard(curFocusNode);
140 }
141 }
142
SetWindowFocus(bool windowFocus)143 void InputMethodManager::SetWindowFocus(bool windowFocus)
144 {
145 windowFocus_ = windowFocus;
146 }
147
NeedSoftKeyboard() const148 bool InputMethodManager::NeedSoftKeyboard() const
149 {
150 auto currentFocusNode = curFocusNode_.Upgrade();
151 CHECK_NULL_RETURN(currentFocusNode, false);
152 auto pipeline = currentFocusNode->GetContextRefPtr();
153 if (pipeline) {
154 auto manager = AceType::DynamicCast<NG::TextFieldManagerNG>(pipeline->GetTextFieldManager());
155 if (manager && manager->GetLastRequestKeyboardId() == currentFocusNode->GetId()) {
156 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Last RequestKeyboard node is current focus node, So keep");
157 return true;
158 }
159 }
160 if (currentFocusNode && (currentFocusNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG ||
161 currentFocusNode->GetTag() == V2::EMBEDDED_COMPONENT_ETS_TAG)) {
162 return true;
163 }
164 auto pattern = currentFocusNode->GetPattern();
165 return pattern->NeedSoftKeyboard() && pattern->NeedToRequestKeyboardOnFocus();
166 }
167
CloseKeyboard()168 void InputMethodManager::CloseKeyboard()
169 {
170 ACE_LAYOUT_SCOPED_TRACE("CloseKeyboard");
171 auto currentFocusNode = curFocusNode_.Upgrade();
172 CHECK_NULL_VOID(currentFocusNode);
173 auto pipeline = currentFocusNode->GetContext();
174 CHECK_NULL_VOID(pipeline);
175 auto textFieldManager = pipeline->GetTextFieldManager();
176 CHECK_NULL_VOID(textFieldManager);
177 if (!textFieldManager->GetImeShow() && !textFieldManager->GetIsImeAttached()) {
178 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Ime Not Shown, Ime Not Attached, No need to close keyboard");
179 return;
180 }
181 textFieldManager->SetNeedToRequestKeyboard(false);
182 #if defined(ENABLE_STANDARD_INPUT)
183 // If pushpage, close it
184 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "PageChange CloseKeyboard FrameNode notNeedSoftKeyboard.");
185 auto inputMethod = MiscServices::InputMethodController::GetInstance();
186 if (inputMethod) {
187 inputMethod->Close();
188 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "PageChange CloseKeyboard SoftKeyboard Closes Successfully.");
189 }
190 #endif
191 }
192
CloseKeyboardInPipelineDestroy()193 void InputMethodManager::CloseKeyboardInPipelineDestroy()
194 {
195 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Pipeline Destroyed, Ready to close SoftKeyboard.");
196 auto inputMethod = MiscServices::InputMethodController::GetInstance();
197 if (inputMethod) {
198 inputMethod->Close();
199 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Pipelinne Destroyed, Close SoftKeyboard Successfully.");
200 }
201 }
202
CloseKeyboard(const RefPtr<NG::FrameNode> & focusNode)203 void InputMethodManager::CloseKeyboard(const RefPtr<NG::FrameNode>& focusNode)
204 {
205 #if defined(ENABLE_STANDARD_INPUT)
206 // If focus pattern does not need softkeyboard, close it, not in windowScene.
207 auto curPattern = focusNode->GetPattern<NG::Pattern>();
208 CHECK_NULL_VOID(curPattern);
209 ACE_LAYOUT_SCOPED_TRACE("CloseKeyboard[id:%d]", focusNode->GetId());
210 bool isNeedKeyBoard = curPattern->NeedSoftKeyboard();
211 if (!isNeedKeyBoard) {
212 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "FrameNode(%{public}s/%{public}d) notNeedSoftKeyboard.",
213 focusNode->GetTag().c_str(), focusNode->GetId());
214 CloseKeyboard();
215 }
216 #endif
217 }
218
HideKeyboardAcrossProcesses()219 void InputMethodManager::HideKeyboardAcrossProcesses()
220 {
221 #if defined(ENABLE_STANDARD_INPUT)
222 // If Nav, close it
223 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Nav CloseKeyboard FrameNode notNeedSoftKeyboard.");
224 auto inputMethod = MiscServices::InputMethodController::GetInstance();
225 if (inputMethod) {
226 inputMethod->RequestHideInput();
227 inputMethod->Close();
228 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Nav CloseKeyboard SoftKeyboard Closes Successfully.");
229 }
230 #endif
231 }
232
ProcessModalPageScene()233 void InputMethodManager::ProcessModalPageScene()
234 {
235 auto currentFocusNode = curFocusNode_.Upgrade();
236 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "ProcessModalPageScene");
237 if (currentFocusNode && currentFocusNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG) {
238 HideKeyboardAcrossProcesses();
239 } else {
240 CloseKeyboard();
241 }
242 }
243 } // namespace OHOS::Ace