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