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,FocusReason focusReason)44 void InputMethodManager::OnFocusNodeChange(const RefPtr<NG::FrameNode>& curFocusNode, FocusReason focusReason)
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 auto pattern = curFocusNode->GetPattern();
67 if (pattern) {
68 pattern->OnFocusNodeChange(focusReason);
69 }
70 #ifdef WINDOW_SCENE_SUPPORTED
71 auto isWindowScene = NG::WindowSceneHelper::IsWindowScene(curFocusNode);
72 if (isWindowScene) {
73 ProcessKeyboardInWindowScene(curFocusNode);
74 } else {
75 ProcessKeyboard(curFocusNode);
76 }
77 #else
78 CloseKeyboard(curFocusNode);
79 #endif
80 }
81
ProcessKeyboardInWindowScene(const RefPtr<NG::FrameNode> & curFocusNode)82 void InputMethodManager::ProcessKeyboardInWindowScene(const RefPtr<NG::FrameNode>& curFocusNode)
83 {
84 if (curFocusNode && NG::WindowSceneHelper::IsFocusWindowSceneCloseKeyboard(curFocusNode)) {
85 lastKeep_ = true;
86 } else {
87 lastKeep_ = false;
88 }
89 // Frame other window to SCB window Or inSCB window changes,hide keyboard.
90 if ((windowFocus_.has_value() && windowFocus_.value())) {
91 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "SCB Window focus first, ready to hide keyboard.");
92 windowFocus_.reset();
93 NG::WindowSceneHelper::IsWindowSceneCloseKeyboard(curFocusNode);
94 return;
95 }
96 // In window scene, focus change, need close keyboard.
97 auto pattern = curFocusNode->GetPattern();
98 if (!pattern->NeedSoftKeyboard()) {
99 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "SCB WindowscenePage ready to close keyboard.");
100 NG::WindowSceneHelper::IsCloseKeyboard(curFocusNode);
101 }
102 }
103
ProcessKeyboard(const RefPtr<NG::FrameNode> & curFocusNode)104 void InputMethodManager::ProcessKeyboard(const RefPtr<NG::FrameNode>& curFocusNode)
105 {
106 if (curFocusNode && curFocusNode->GetTag() == V2::SCREEN_ETS_TAG && lastKeep_) {
107 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "node is screen and last node want to save keyboard Ignore");
108 lastKeep_ = false;
109 return;
110 }
111 auto pipeline = curFocusNode->GetContextRefPtr();
112 CHECK_NULL_VOID(pipeline);
113 if (windowFocus_.has_value() && windowFocus_.value()) {
114 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Normal Window focus first, set focus flag to window.");
115 windowFocus_.reset();
116 auto callback = pipeline->GetWindowFocusCallback();
117 if (callback) {
118 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Trigger Window Focus Callback");
119 callback();
120 } else {
121 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "No Window Focus Callback");
122 if (!pipeline->NeedSoftKeyboard()) {
123 HideKeyboardAcrossProcesses();
124 }
125 }
126 return;
127 }
128
129 if (curFocusNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG ||
130 curFocusNode->GetTag() == V2::EMBEDDED_COMPONENT_ETS_TAG) {
131 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "UIExtension(%{public}s/%{public}d) not need process.",
132 curFocusNode->GetTag().c_str(), curFocusNode->GetId());
133 return;
134 }
135
136 auto container = Container::Current();
137 auto isUIExtension = container && container->IsUIExtensionWindow();
138 auto pattern = curFocusNode->GetPattern();
139 CHECK_NULL_VOID(pattern);
140 if (isUIExtension && !pattern->NeedSoftKeyboard()) {
141 HideKeyboardAcrossProcesses();
142 } else {
143 CloseKeyboard(curFocusNode);
144 }
145 }
146
SetWindowFocus(bool windowFocus)147 void InputMethodManager::SetWindowFocus(bool windowFocus)
148 {
149 windowFocus_ = windowFocus;
150 }
151
NeedSoftKeyboard() const152 bool InputMethodManager::NeedSoftKeyboard() const
153 {
154 auto currentFocusNode = curFocusNode_.Upgrade();
155 CHECK_NULL_RETURN(currentFocusNode, false);
156 auto pipeline = currentFocusNode->GetContextRefPtr();
157 if (pipeline) {
158 auto manager = AceType::DynamicCast<NG::TextFieldManagerNG>(pipeline->GetTextFieldManager());
159 if (manager && manager->GetLastRequestKeyboardId() == currentFocusNode->GetId()) {
160 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Last RequestKeyboard node is current focus node, So keep");
161 return true;
162 }
163 }
164 if (currentFocusNode && (currentFocusNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG ||
165 currentFocusNode->GetTag() == V2::EMBEDDED_COMPONENT_ETS_TAG)) {
166 return true;
167 }
168 auto pattern = currentFocusNode->GetPattern();
169 return pattern->NeedSoftKeyboard() && pattern->NeedToRequestKeyboardOnFocus();
170 }
171
CloseKeyboard()172 void InputMethodManager::CloseKeyboard()
173 {
174 ACE_LAYOUT_SCOPED_TRACE("CloseKeyboard");
175 auto currentFocusNode = curFocusNode_.Upgrade();
176 CHECK_NULL_VOID(currentFocusNode);
177 auto pipeline = currentFocusNode->GetContext();
178 CHECK_NULL_VOID(pipeline);
179 auto textFieldManager = pipeline->GetTextFieldManager();
180 CHECK_NULL_VOID(textFieldManager);
181 if (!textFieldManager->GetImeShow() && !textFieldManager->GetIsImeAttached()) {
182 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Ime Not Shown, Ime Not Attached, No need to close keyboard");
183 return;
184 }
185 textFieldManager->SetNeedToRequestKeyboard(false);
186 #if defined(ENABLE_STANDARD_INPUT)
187 // If pushpage, close it
188 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "PageChange CloseKeyboard FrameNode notNeedSoftKeyboard.");
189 auto inputMethod = MiscServices::InputMethodController::GetInstance();
190 if (inputMethod) {
191 inputMethod->Close();
192 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "PageChange CloseKeyboard SoftKeyboard Closes Successfully.");
193 }
194 #endif
195 }
196
CloseKeyboardInPipelineDestroy()197 void InputMethodManager::CloseKeyboardInPipelineDestroy()
198 {
199 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Pipeline Destroyed, Ready to close SoftKeyboard.");
200 auto inputMethod = MiscServices::InputMethodController::GetInstance();
201 if (inputMethod) {
202 inputMethod->Close();
203 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Pipelinne Destroyed, Close SoftKeyboard Successfully.");
204 }
205 }
206
CloseKeyboard(const RefPtr<NG::FrameNode> & focusNode)207 void InputMethodManager::CloseKeyboard(const RefPtr<NG::FrameNode>& focusNode)
208 {
209 #if defined(ENABLE_STANDARD_INPUT)
210 // If focus pattern does not need softkeyboard, close it, not in windowScene.
211 auto curPattern = focusNode->GetPattern<NG::Pattern>();
212 CHECK_NULL_VOID(curPattern);
213 ACE_LAYOUT_SCOPED_TRACE("CloseKeyboard[id:%d]", focusNode->GetId());
214 bool isNeedKeyBoard = curPattern->NeedSoftKeyboard();
215 if (!isNeedKeyBoard) {
216 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "FrameNode(%{public}s/%{public}d) notNeedSoftKeyboard.",
217 focusNode->GetTag().c_str(), focusNode->GetId());
218 CloseKeyboard();
219 }
220 #endif
221 }
222
HideKeyboardAcrossProcesses()223 void InputMethodManager::HideKeyboardAcrossProcesses()
224 {
225 #if defined(ENABLE_STANDARD_INPUT)
226 // If Nav, close it
227 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Nav CloseKeyboard FrameNode notNeedSoftKeyboard.");
228 auto inputMethod = MiscServices::InputMethodController::GetInstance();
229 if (inputMethod) {
230 inputMethod->RequestHideInput();
231 inputMethod->Close();
232 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Nav CloseKeyboard SoftKeyboard Closes Successfully.");
233 }
234 #endif
235 }
236
ProcessModalPageScene()237 void InputMethodManager::ProcessModalPageScene()
238 {
239 auto currentFocusNode = curFocusNode_.Upgrade();
240 TAG_LOGI(AceLogTag::ACE_KEYBOARD, "ProcessModalPageScene");
241 if (currentFocusNode && currentFocusNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG) {
242 HideKeyboardAcrossProcesses();
243 } else {
244 CloseKeyboard();
245 }
246 }
247 } // namespace OHOS::Ace