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