• 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/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