• 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     bool isDynamicComponent = container && container->GetUIContentType() == UIContentType::DYNAMIC_COMPONENT;
50     TAG_LOGI(AceLogTag::ACE_KEYBOARD, "current focus node: (%{public}s/%{public}d). isDynamic: %{public}d",
51         curFocusNode->GetTag().c_str(), curFocusNode->GetId(), isDynamicComponent);
52     if (!isDynamicComponent) {
53         ManageFocusNode(curFocusNode, focusReason);
54         return;
55     }
56     auto containerHandler = container->GetContainerHandler();
57     if (!containerHandler) {
58         return;
59     }
60     auto callback = [weakNode = WeakPtr<NG::FrameNode>(curFocusNode), focusReason,
61         instanceId = curFocusNode->GetInstanceId()](bool saveKeyboard) {
62         auto context = NG::PipelineContext::GetContextByContainerId(instanceId);
63         CHECK_NULL_VOID(context);
64         auto taskExecutor = context->GetTaskExecutor();
65         CHECK_NULL_VOID(taskExecutor);
66         taskExecutor->PostTask(
67             [weakNode, focusReason, saveKeyboard]() {
68                 auto curFocusNode = weakNode.Upgrade();
69                 CHECK_NULL_VOID(curFocusNode);
70                 InputMethodManager::GetInstance()->ManageFocusNode(curFocusNode, focusReason, saveKeyboard);
71             },
72             TaskExecutor::TaskType::UI, "ArkUIInputMethodManagerManageFocusNode");
73     };
74     containerHandler->GetHostFocusWindowSceneCloseKeyboard(callback);
75 }
76 
ManageFocusNode(const RefPtr<NG::FrameNode> & curFocusNode,FocusReason focusReason,bool saveKeyboard)77 void InputMethodManager::ManageFocusNode(const RefPtr<NG::FrameNode>& curFocusNode, FocusReason focusReason,
78     bool saveKeyboard)
79 {
80     if (!curFocusNode) {
81         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "CurFocusNode Not Exist");
82         return;
83     }
84     bool lastFocusNodeExist = curFocusNode_.Upgrade() ? true : false;
85     if (lastFocusNodeExist && isLastFocusUIExtension_ && lastFocusNodeId_ != curFocusNode->GetId()) {
86         curFocusNode_ = curFocusNode;
87         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "UIExtension switch focus");
88         auto pattern = curFocusNode->GetPattern();
89 #ifdef WINDOW_SCENE_SUPPORTED
90         auto needCloseKeyboard = !NG::WindowSceneHelper::IsWindowScene(curFocusNode) ||
91             !NG::WindowSceneHelper::IsFocusWindowSceneCloseKeyboard(curFocusNode);
92 #else
93         auto needCloseKeyboard = true;
94 #endif
95         if (!pattern->NeedSoftKeyboard() && needCloseKeyboard) {
96             HideKeyboardAcrossProcesses();
97         }
98     }
99 
100     isLastFocusUIExtension_ = curFocusNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG;
101     lastFocusNodeId_ = curFocusNode->GetId();
102     curFocusNode_ = curFocusNode;
103     auto pattern = curFocusNode->GetPattern();
104     if (pattern) {
105         pattern->OnFocusNodeChange(focusReason);
106     }
107 
108     if (saveKeyboard) {
109         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "No Need To Close Keyboard");
110         return;
111     }
112 
113 #ifdef WINDOW_SCENE_SUPPORTED
114     auto isWindowScene = NG::WindowSceneHelper::IsWindowScene(curFocusNode);
115     if (isWindowScene) {
116         ProcessKeyboardInWindowScene(curFocusNode);
117     } else {
118         ProcessKeyboard(curFocusNode);
119     }
120 #else
121     CloseKeyboard(curFocusNode);
122 #endif
123 }
124 
ProcessKeyboardInWindowScene(const RefPtr<NG::FrameNode> & curFocusNode)125 void InputMethodManager::ProcessKeyboardInWindowScene(const RefPtr<NG::FrameNode>& curFocusNode)
126 {
127     if (curFocusNode && NG::WindowSceneHelper::IsFocusWindowSceneCloseKeyboard(curFocusNode)) {
128         lastKeep_ = true;
129     } else {
130         lastKeep_ = false;
131     }
132     // Frame other window to SCB window Or inSCB window changes,hide keyboard.
133     if ((windowFocus_.has_value() && windowFocus_.value())) {
134         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "SCB Window focus first, ready to hide keyboard.");
135         windowFocus_.reset();
136         NG::WindowSceneHelper::IsWindowSceneCloseKeyboard(curFocusNode);
137         return;
138     }
139 
140     if (curFocusNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG ||
141         curFocusNode->GetTag() == V2::EMBEDDED_COMPONENT_ETS_TAG) {
142         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "UIExtension(%{public}s/%{public}d) not need to process.",
143             curFocusNode->GetTag().c_str(), curFocusNode->GetId());
144         return;
145     }
146 
147     // In window scene, focus change, need close keyboard.
148     auto pattern = curFocusNode->GetPattern();
149     if (!pattern->NeedSoftKeyboard()) {
150         NG::WindowSceneHelper::IsCloseKeyboard(curFocusNode);
151     }
152 }
153 
ProcessKeyboard(const RefPtr<NG::FrameNode> & curFocusNode)154 void InputMethodManager::ProcessKeyboard(const RefPtr<NG::FrameNode>& curFocusNode)
155 {
156     if (curFocusNode && curFocusNode->GetTag() == V2::SCREEN_ETS_TAG && lastKeep_) {
157         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "node is screen and last node want to save keyboard Ignore");
158         lastKeep_ = false;
159         return;
160     }
161     auto pipeline = curFocusNode->GetContextRefPtr();
162     CHECK_NULL_VOID(pipeline);
163     ACE_LAYOUT_SCOPED_TRACE("ProcessKeyboard [node:%s]", curFocusNode->GetTag().c_str());
164     if (windowFocus_.has_value() && windowFocus_.value()) {
165         windowFocus_.reset();
166         auto callback = pipeline->GetWindowFocusCallback();
167         if (callback) {
168             TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Trigger Window Focus Callback");
169             callback();
170         } else {
171             TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Trigger ArkWindow Focus Callback");
172             if (!pipeline->NeedSoftKeyboard()) {
173                 HideKeyboardAcrossProcesses();
174             }
175         }
176         return;
177     }
178 
179     if (curFocusNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG ||
180         curFocusNode->GetTag() == V2::EMBEDDED_COMPONENT_ETS_TAG) {
181         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "UIExtension(%{public}s/%{public}d) not need process.",
182             curFocusNode->GetTag().c_str(), curFocusNode->GetId());
183         return;
184     }
185 
186     auto container = Container::Current();
187     auto isUIExtension = container && container->IsUIExtensionWindow();
188     auto pattern = curFocusNode->GetPattern();
189     CHECK_NULL_VOID(pattern);
190     if (isUIExtension && !pattern->NeedSoftKeyboard() && pipeline->IsWindowFocused()) {
191         HideKeyboardAcrossProcesses();
192     } else {
193         CloseKeyboard(curFocusNode);
194     }
195 }
196 
SetWindowFocus(bool windowFocus)197 void InputMethodManager::SetWindowFocus(bool windowFocus)
198 {
199     windowFocus_ = windowFocus;
200 }
201 
NeedSoftKeyboard() const202 bool InputMethodManager::NeedSoftKeyboard() const
203 {
204     auto currentFocusNode = curFocusNode_.Upgrade();
205     CHECK_NULL_RETURN(currentFocusNode, false);
206     auto pipeline = currentFocusNode->GetContext();
207     if (pipeline) {
208         auto manager = AceType::DynamicCast<NG::TextFieldManagerNG>(pipeline->GetTextFieldManager());
209         if (manager && manager->GetLastRequestKeyboardId() == currentFocusNode->GetId()) {
210             TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Last RequestKeyboard node is current focus node, So keep");
211             return true;
212         }
213     }
214     if (currentFocusNode && (currentFocusNode->GetTag() == V2::UI_EXTENSION_COMPONENT_ETS_TAG ||
215                              currentFocusNode->GetTag() == V2::EMBEDDED_COMPONENT_ETS_TAG)) {
216         return true;
217     }
218     auto pattern = currentFocusNode->GetPattern();
219     return pattern->NeedSoftKeyboard() && pattern->NeedToRequestKeyboardOnFocus();
220 }
221 
CloseKeyboard(bool disableNeedToRequestKeyboard)222 void InputMethodManager::CloseKeyboard(bool disableNeedToRequestKeyboard)
223 {
224     ACE_LAYOUT_SCOPED_TRACE("CloseKeyboard");
225     auto currentFocusNode = curFocusNode_.Upgrade();
226     CHECK_NULL_VOID(currentFocusNode);
227     auto pipeline = currentFocusNode->GetContext();
228     CHECK_NULL_VOID(pipeline);
229     auto textFieldManager = pipeline->GetTextFieldManager();
230     CHECK_NULL_VOID(textFieldManager);
231     if (!textFieldManager->GetImeShow() && !textFieldManager->GetIsImeAttached()) {
232         return;
233     }
234     TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Ime Shown Or Attached, Need to close keyboard");
235     if (disableNeedToRequestKeyboard) {
236         textFieldManager->SetNeedToRequestKeyboard(false);
237     }
238 #if defined(ENABLE_STANDARD_INPUT)
239     auto inputMethod = MiscServices::InputMethodController::GetInstance();
240     if (!inputMethod) {
241         TAG_LOGW(AceLogTag::ACE_KEYBOARD, "Get InputMethodController Instance Failed");
242         return;
243     }
244     inputMethod->Close();
245     TAG_LOGI(AceLogTag::ACE_KEYBOARD, "PageChange CloseKeyboard FrameNode notNeedSoftKeyboard.");
246 #endif
247 }
248 
CloseKeyboardInPipelineDestroy()249 void InputMethodManager::CloseKeyboardInPipelineDestroy()
250 {
251 #if defined(ENABLE_STANDARD_INPUT)
252     auto inputMethod = MiscServices::InputMethodController::GetInstance();
253     if (!inputMethod) {
254         TAG_LOGW(AceLogTag::ACE_KEYBOARD, "Get InputMethodController Instance Failed");
255         return;
256     }
257     inputMethod->Close();
258     TAG_LOGI(AceLogTag::ACE_KEYBOARD, "Pipelinne Destroyed, Close SoftKeyboard Successfully.");
259 #endif
260 }
261 
CloseKeyboard(const RefPtr<NG::FrameNode> & focusNode)262 void InputMethodManager::CloseKeyboard(const RefPtr<NG::FrameNode>& focusNode)
263 {
264 #if defined(ENABLE_STANDARD_INPUT)
265     // If focus pattern does not need softkeyboard, close it, not in windowScene.
266     auto curPattern = focusNode->GetPattern<NG::Pattern>();
267     CHECK_NULL_VOID(curPattern);
268     ACE_LAYOUT_SCOPED_TRACE("CloseKeyboard[id:%d]", focusNode->GetId());
269     bool isNeedKeyBoard = curPattern->NeedSoftKeyboard();
270     if (!isNeedKeyBoard) {
271         TAG_LOGI(AceLogTag::ACE_KEYBOARD, "FrameNode(%{public}s/%{public}d) notNeedSoftKeyboard.",
272             focusNode->GetTag().c_str(), focusNode->GetId());
273         CloseKeyboard();
274     }
275 #endif
276 }
277 
HideKeyboardAcrossProcesses()278 void InputMethodManager::HideKeyboardAcrossProcesses()
279 {
280 #if defined(ENABLE_STANDARD_INPUT)
281     auto inputMethod = MiscServices::InputMethodController::GetInstance();
282     if (!inputMethod) {
283         TAG_LOGW(AceLogTag::ACE_KEYBOARD, "Get InputMethodController Instance Failed");
284         return;
285     }
286     inputMethod->RequestHideInput();
287     inputMethod->Close();
288     TAG_LOGI(AceLogTag::ACE_KEYBOARD, "across processes CloseKeyboard Successfully.");
289 #endif
290 }
291 
CloseKeyboardInProcess()292 void InputMethodManager::CloseKeyboardInProcess()
293 {
294 #if defined(ENABLE_STANDARD_INPUT)
295     auto inputMethod = MiscServices::InputMethodController::GetInstance();
296     if (!inputMethod) {
297         TAG_LOGW(AceLogTag::ACE_KEYBOARD, "Get InputMethodController Instance Failed");
298         return;
299     }
300     inputMethod->Close();
301     TAG_LOGI(AceLogTag::ACE_KEYBOARD, "CloseKeyboardInProcess Successfully.");
302 #endif
303 }
304 
ProcessModalPageScene()305 void InputMethodManager::ProcessModalPageScene()
306 {
307     bool lastFocusNodeExist = curFocusNode_.Upgrade() ? true : false;
308     TAG_LOGI(AceLogTag::ACE_KEYBOARD, "ProcessModalPageScene");
309     if (lastFocusNodeExist && isLastFocusUIExtension_) {
310         HideKeyboardAcrossProcesses();
311     } else {
312         CloseKeyboardInProcess();
313     }
314 }
315 } // namespace OHOS::Ace