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/stylus/stylus_detector_mgr.h"
17
18 #include "base/geometry/dimension.h"
19 #include "base/geometry/ng/offset_t.h"
20 #include "base/geometry/ng/rect_t.h"
21 #include "base/geometry/ng/size_t.h"
22 #include "base/geometry/point.h"
23 #include "base/utils/time_util.h"
24 #include "base/utils/utils.h"
25 #include "core/common/container.h"
26 #include "core/common/stylus/stylus_detector_default.h"
27 #include "core/common/stylus/stylus_detector_loader.h"
28 #include "core/common/stylus/stylus_detector_callback.h"
29 #include "core/components_ng/base/frame_node.h"
30 #include "core/components_ng/base/view_stack_processor.h"
31 #include "core/components_ng/gestures/recognizers/gesture_recognizer.h"
32 #include "core/components_ng/pattern/rich_editor/rich_editor_pattern.h"
33 #include "core/components_ng/pattern/search/search_text_field.h"
34 #include "core/components_ng/pattern/text/text_base.h"
35 #include "core/components_ng/pattern/text_field/text_field_pattern.h"
36 #include "core/components_v2/inspector/inspector_constants.h"
37 #include "core/pipeline_ng/pipeline_context.h"
38 #include "frameworks/base/log/log_wrapper.h"
39
40 namespace OHOS::Ace {
41 const static std::unordered_set<std::string> TEXT_FIELD_COMPONENT_TAGS = {
42 V2::TEXTINPUT_ETS_TAG,
43 V2::TEXTAREA_ETS_TAG,
44 V2::RICH_EDITOR_ETS_TAG,
45 V2::SEARCH_Field_ETS_TAG,
46 };
47
GetInstance()48 StylusDetectorMgr* StylusDetectorMgr::GetInstance()
49 {
50 static StylusDetectorMgr instance;
51 return &instance;
52 }
53
IsEnable()54 bool StylusDetectorMgr::IsEnable()
55 {
56 CHECK_NULL_RETURN(engine_, false);
57 auto isEnable = engine_->IsEnable();
58 CHECK_NULL_RETURN(isEnable, false);
59 return isEnable;
60 }
RegisterStylusInteractionListener(const std::string & bundleName,const std::shared_ptr<IStylusDetectorCallback> & callback)61 bool StylusDetectorMgr::RegisterStylusInteractionListener(
62 const std::string& bundleName, const std::shared_ptr<IStylusDetectorCallback>& callback)
63 {
64 CHECK_NULL_RETURN(engine_, false);
65 return engine_->RegisterStylusInteractionListener(bundleName, callback);
66 }
UnRegisterStylusInteractionListener(const std::string & bundleName)67 void StylusDetectorMgr::UnRegisterStylusInteractionListener(const std::string& bundleName)
68 {
69 CHECK_NULL_VOID(engine_);
70 return engine_->UnRegisterStylusInteractionListener(bundleName);
71 }
Notify(const NotifyInfo & notifyInfo)72 bool StylusDetectorMgr::Notify(const NotifyInfo& notifyInfo)
73 {
74 CHECK_NULL_RETURN(engine_, false);
75 return engine_->Notify(notifyInfo);
76 }
77
FindHitFrameNode(const TouchEvent & touchEvent,const TouchTestResult & touchTestResult)78 RefPtr<NG::FrameNode> StylusDetectorMgr::FindHitFrameNode(
79 const TouchEvent& touchEvent, const TouchTestResult& touchTestResult)
80 {
81 RefPtr<NG::FrameNode> frameNode;
82 // TextField, textInput, search and richEditor has default touchEventTarget.
83 for (const auto& entry : touchTestResult) {
84 auto recognizer = AceType::DynamicCast<NG::NGGestureRecognizer>(entry);
85 if (recognizer) {
86 continue;
87 }
88 auto nodeId = entry->GetNodeId();
89 auto UiNode = ElementRegister::GetInstance()->GetUINodeById(nodeId);
90 frameNode = AceType::DynamicCast<NG::FrameNode>(UiNode);
91 if (frameNode) {
92 break;
93 }
94 }
95 CHECK_NULL_RETURN(frameNode, nullptr);
96
97 auto pipeline = frameNode->GetContextRefPtr();
98 if (!pipeline) {
99 TAG_LOGI(AceLogTag::ACE_STYLUS, "Can't find pipeline for find hit node.");
100 return nullptr;
101 }
102 auto nanoTimestamp = pipeline->GetVsyncTime();
103 auto textBasePattern = frameNode->GetPattern<NG::TextBase>();
104 CHECK_NULL_RETURN(textBasePattern, nullptr);
105 if (!textBasePattern->IsTextEditableForStylus() ||
106 IsHitCleanNodeResponseArea({ touchEvent.x, touchEvent.y }, frameNode, nanoTimestamp)) {
107 return nullptr;
108 }
109 return frameNode;
110 }
111
IsNeedInterceptedTouchEvent(const TouchEvent & touchEvent,std::unordered_map<size_t,TouchTestResult> touchTestResults)112 bool StylusDetectorMgr::IsNeedInterceptedTouchEvent(
113 const TouchEvent& touchEvent, std::unordered_map<size_t, TouchTestResult> touchTestResults)
114 {
115 if (!IsStylusTouchEvent(touchEvent)) {
116 return false;
117 }
118
119 const auto iter = touchTestResults.find(touchEvent.id);
120 if (iter == touchTestResults.end() || iter->second.empty()) {
121 TAG_LOGI(AceLogTag::ACE_STYLUS, "TouchTestResult is empty");
122 return false;
123 }
124
125 auto frameNode = FindHitFrameNode(touchEvent, iter->second);
126 if (!frameNode) {
127 TAG_LOGI(AceLogTag::ACE_STYLUS,
128 "Stylus hit position is (" SEC_PLD(%{public}f) "," SEC_PLD(%{public}f) "). TargetNode is None",
129 SEC_PARAM(touchEvent.x), SEC_PARAM(touchEvent.y));
130 return false;
131 }
132
133 TAG_LOGI(AceLogTag::ACE_STYLUS,
134 "Stylus hit position is (" SEC_PLD(%{public}f) "," SEC_PLD(%{public}f) ")."
135 "TargetNode is %{public}s, id is " SEC_PLD(%{public}s) ".",
136 SEC_PARAM(touchEvent.x), SEC_PARAM(touchEvent.y), frameNode->GetTag().c_str(),
137 SEC_PARAM(frameNode->GetInspectorId()->c_str()));
138
139 if (!IsEnable()) {
140 TAG_LOGI(AceLogTag::ACE_STYLUS, "Stylus service is not enable");
141 return false;
142 }
143
144 auto container = Container::Current();
145 CHECK_NULL_RETURN(container, false);
146 auto bundleName = container->GetBundleName();
147 NotifyInfo info;
148 info.componentId = frameNode->GetId();
149 nodeId_ = info.componentId;
150 const auto layoutIter = textFieldLayoutInfos_.find(nodeId_);
151 if (layoutIter != textFieldLayoutInfos_.end()) {
152 layoutInfo_ = layoutIter->second;
153 }
154 info.x = touchEvent.screenX;
155 info.y = touchEvent.screenY;
156 info.bundleName = bundleName;
157 auto stylusDetectorCallback = std::make_shared<StylusDetectorCallBack>();
158 isRegistered_ = RegisterStylusInteractionListener(bundleName, stylusDetectorCallback);
159 sInd_ = -1;
160 eInd_ = -1;
161 showMenu_ = false;
162 return Notify(info);
163 }
164
AddTextFieldFrameNode(const RefPtr<NG::FrameNode> & frameNode,const WeakPtr<NG::LayoutInfoInterface> & layoutInfo)165 void StylusDetectorMgr::AddTextFieldFrameNode(const RefPtr<NG::FrameNode>& frameNode,
166 const WeakPtr<NG::LayoutInfoInterface>& layoutInfo)
167 {
168 CHECK_NULL_VOID(frameNode);
169 auto tag = frameNode->GetTag();
170 auto iter = TEXT_FIELD_COMPONENT_TAGS.find(tag);
171 if (iter == TEXT_FIELD_COMPONENT_TAGS.end()) {
172 return;
173 }
174 auto id = frameNode->GetId();
175 auto destructor = [id]() { StylusDetectorMgr::GetInstance()->RemoveTextFieldFrameNode(id); };
176 frameNode->PushDestroyCallback(std::move(destructor));
177 textFieldNodes_[id] = AceType::WeakClaim(AceType::RawPtr(frameNode));
178 textFieldLayoutInfos_[id] = layoutInfo;
179 }
180
RemoveTextFieldFrameNode(const int32_t id)181 void StylusDetectorMgr::RemoveTextFieldFrameNode(const int32_t id)
182 {
183 textFieldNodes_.erase(id);
184 textFieldLayoutInfos_.erase(id);
185 if (textFieldNodes_.empty()) {
186 auto container = Container::Current();
187 CHECK_NULL_VOID(container);
188 auto bundleName = container->GetBundleName();
189 isRegistered_ = false;
190 UnRegisterStylusInteractionListener(bundleName);
191 }
192 }
193
StylusDetectorMgr()194 StylusDetectorMgr::StylusDetectorMgr() : engine_(nullptr), isRegistered_(false)
195 {
196 auto lib = StylusDetectorLoader::Load();
197 if (!lib || !(engine_ = lib->CreateStylusDetector())) {
198 engine_ = StylusDetectorInstance(StylusDetectorDefault::GetInstance(), [](StylusDetectorInterface* e) {});
199 }
200 }
201
IsStylusTouchEvent(const TouchEvent & touchEvent) const202 bool StylusDetectorMgr::IsStylusTouchEvent(const TouchEvent& touchEvent) const
203 {
204 return touchEvent.sourceTool == SourceTool::PEN && touchEvent.type == TouchType::DOWN;
205 }
206
IsHitCleanNodeResponseArea(const NG::PointF & point,const RefPtr<NG::FrameNode> & frameNode,uint64_t nanoTimestamp)207 bool StylusDetectorMgr::IsHitCleanNodeResponseArea(
208 const NG::PointF& point, const RefPtr<NG::FrameNode>& frameNode, uint64_t nanoTimestamp)
209 {
210 CHECK_NULL_RETURN(frameNode, false);
211 if (frameNode->GetTag() != V2::TEXTINPUT_ETS_TAG) {
212 return false;
213 }
214
215 auto textFieldPattern = frameNode->GetPattern<NG::TextFieldPattern>();
216 CHECK_NULL_RETURN(textFieldPattern, false);
217 auto responseArea = textFieldPattern->GetCleanNodeResponseArea();
218 CHECK_NULL_RETURN(responseArea, false);
219 auto cleanNodeResponseArea = AceType::DynamicCast<NG::CleanNodeResponseArea>(responseArea);
220 if (!cleanNodeResponseArea->IsShow()) {
221 return false;
222 }
223
224 auto cleanNodeFrameNode = cleanNodeResponseArea->GetFrameNode();
225 CHECK_NULL_RETURN(cleanNodeFrameNode, false);
226 auto cleanNodeGeometryNode = cleanNodeFrameNode->GetGeometryNode();
227 CHECK_NULL_RETURN(cleanNodeGeometryNode, false);
228 auto globalFrameRect = cleanNodeGeometryNode->GetFrameRect();
229 globalFrameRect.SetOffset(cleanNodeFrameNode->CalculateCachedTransformRelativeOffset(nanoTimestamp));
230 return globalFrameRect.IsInRegion(point);
231 }
232 } // namespace OHOS::Ace