• 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/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, "Stylus hit position is (%{public}f, %{public}f). TargetNode is None",
128             touchEvent.x, touchEvent.y);
129         return false;
130     }
131 
132     TAG_LOGI(AceLogTag::ACE_STYLUS,
133         "Stylus hit position is (%{public}f, %{public}f). TargetNode is %{public}s, id is %{public}s", touchEvent.x,
134         touchEvent.y, frameNode->GetTag().c_str(), frameNode->GetInspectorId()->c_str());
135 
136     if (!IsEnable()) {
137         TAG_LOGI(AceLogTag::ACE_STYLUS, "Stylus service is not enable");
138         return false;
139     }
140 
141     auto container = Container::Current();
142     CHECK_NULL_RETURN(container, false);
143     auto bundleName = container->GetBundleName();
144     NotifyInfo info;
145     info.componentId = frameNode->GetId();
146     nodeId_ = info.componentId;
147     const auto layoutIter = textFieldLayoutInfos_.find(nodeId_);
148     if (layoutIter != textFieldLayoutInfos_.end()) {
149         layoutInfo_ = layoutIter->second;
150     }
151     info.x = touchEvent.screenX;
152     info.y = touchEvent.screenY;
153     info.bundleName = bundleName;
154     auto stylusDetectorCallback = std::make_shared<StylusDetectorCallBack>();
155     isRegistered_ = RegisterStylusInteractionListener(bundleName, stylusDetectorCallback);
156     sInd_ = -1;
157     eInd_ = -1;
158     showMenu_ = false;
159     return Notify(info);
160 }
161 
AddTextFieldFrameNode(const RefPtr<NG::FrameNode> & frameNode,const WeakPtr<NG::LayoutInfoInterface> & layoutInfo)162 void StylusDetectorMgr::AddTextFieldFrameNode(const RefPtr<NG::FrameNode>& frameNode,
163     const WeakPtr<NG::LayoutInfoInterface>& layoutInfo)
164 {
165     CHECK_NULL_VOID(frameNode);
166     auto tag = frameNode->GetTag();
167     auto iter = TEXT_FIELD_COMPONENT_TAGS.find(tag);
168     if (iter == TEXT_FIELD_COMPONENT_TAGS.end()) {
169         return;
170     }
171     auto id = frameNode->GetId();
172     auto destructor = [id]() { StylusDetectorMgr::GetInstance()->RemoveTextFieldFrameNode(id); };
173     frameNode->PushDestroyCallback(std::move(destructor));
174     textFieldNodes_[id] = AceType::WeakClaim(AceType::RawPtr(frameNode));
175     textFieldLayoutInfos_[id] = layoutInfo;
176 }
177 
RemoveTextFieldFrameNode(const int32_t id)178 void StylusDetectorMgr::RemoveTextFieldFrameNode(const int32_t id)
179 {
180     textFieldNodes_.erase(id);
181     textFieldLayoutInfos_.erase(id);
182     if (textFieldNodes_.empty()) {
183         auto container = Container::Current();
184         CHECK_NULL_VOID(container);
185         auto bundleName = container->GetBundleName();
186         isRegistered_ = false;
187         UnRegisterStylusInteractionListener(bundleName);
188     }
189 }
190 
StylusDetectorMgr()191 StylusDetectorMgr::StylusDetectorMgr() : engine_(nullptr), isRegistered_(false)
192 {
193     auto lib = StylusDetectorLoader::Load();
194     if (!lib || !(engine_ = lib->CreateStylusDetector())) {
195         engine_ = StylusDetectorInstance(StylusDetectorDefault::GetInstance(), [](StylusDetectorInterface* e) {});
196     }
197 }
198 
IsStylusTouchEvent(const TouchEvent & touchEvent) const199 bool StylusDetectorMgr::IsStylusTouchEvent(const TouchEvent& touchEvent) const
200 {
201     return touchEvent.sourceTool == SourceTool::PEN && touchEvent.type == TouchType::DOWN;
202 }
203 
IsHitCleanNodeResponseArea(const NG::PointF & point,const RefPtr<NG::FrameNode> & frameNode,uint64_t nanoTimestamp)204 bool StylusDetectorMgr::IsHitCleanNodeResponseArea(
205     const NG::PointF& point, const RefPtr<NG::FrameNode>& frameNode, uint64_t nanoTimestamp)
206 {
207     CHECK_NULL_RETURN(frameNode, false);
208     if (frameNode->GetTag() != V2::TEXTINPUT_ETS_TAG) {
209         return false;
210     }
211 
212     auto textFieldPattern = frameNode->GetPattern<NG::TextFieldPattern>();
213     CHECK_NULL_RETURN(textFieldPattern, false);
214     auto responseArea = textFieldPattern->GetCleanNodeResponseArea();
215     CHECK_NULL_RETURN(responseArea, false);
216     auto cleanNodeResponseArea = AceType::DynamicCast<NG::CleanNodeResponseArea>(responseArea);
217     if (!cleanNodeResponseArea->IsShow()) {
218         return false;
219     }
220 
221     auto cleanNodeFrameNode = cleanNodeResponseArea->GetFrameNode();
222     CHECK_NULL_RETURN(cleanNodeFrameNode, false);
223     auto cleanNodeGeometryNode = cleanNodeFrameNode->GetGeometryNode();
224     CHECK_NULL_RETURN(cleanNodeGeometryNode, false);
225     auto globalFrameRect = cleanNodeGeometryNode->GetFrameRect();
226     globalFrameRect.SetOffset(cleanNodeFrameNode->CalculateCachedTransformRelativeOffset(nanoTimestamp));
227     return globalFrameRect.IsInRegion(point);
228 }
229 } // namespace OHOS::Ace