• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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