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