1 /*
2  * Copyright (c) 2021-2022 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/gestures/drag_recognizer.h"
17 
18 namespace OHOS::Ace {
19 namespace {
20 
21 #ifndef WEARABLE_PRODUCT
22 constexpr double DELTA_DURATION = 3.0;
23 #else
24 constexpr double DELTA_DURATION = 15.0;
25 #endif
26 
27 } // namespace
28 
OnAccepted(size_t touchId)29 void DragRecognizer::OnAccepted(size_t touchId)
30 {
31     auto iter = dragFingers_.find(touchId);
32     if (iter == dragFingers_.end()) {
33         LOGE("the dragFingers_ is not ready to receive accepted, id is %{public}zu", touchId);
34         return;
35     }
36 
37     auto& dragInfo = iter->second;
38     dragInfo.states_ = DetectState::DETECTED;
39     if (onDragStart_) {
40         const auto& firstPoint = dragInfo.velocityTracker_.GetFirstTrackPoint();
41         DragStartInfo startInfo(firstPoint.id);
42         startInfo.SetGlobalLocation(firstPoint.GetOffset())
43             .SetLocalLocation(firstPoint.GetOffset() - coordinateOffset_);
44         startInfo.SetTimeStamp(firstPoint.time);
45         AsyncCallback(onDragStart_, startInfo);
46     }
47     if (onDragUpdate_) {
48         const auto& currentPoint = dragInfo.velocityTracker_.GetCurrentTrackPoint();
49         const auto dragOffsetInMainAxis =
50             axis_ == Axis::VERTICAL ? dragInfo.dragOffset_.GetY() : dragInfo.dragOffset_.GetX();
51         DragUpdateInfo updateInfo(currentPoint.id);
52         updateInfo.SetDelta(dragInfo.dragOffset_)
53             .SetMainDelta(dragOffsetInMainAxis)
54             .SetGlobalLocation(currentPoint.GetOffset())
55             .SetLocalLocation(currentPoint.GetOffset() - coordinateOffset_);
56         updateInfo.SetTimeStamp(currentPoint.time);
57         AsyncCallback(onDragUpdate_, updateInfo);
58     }
59 }
60 
OnRejected(size_t touchId)61 void DragRecognizer::OnRejected(size_t touchId)
62 {
63     auto iter = dragFingers_.find(touchId);
64     if (iter == dragFingers_.end()) {
65         LOGE("the dragFingers_ is not ready to receive rejected, id is %{public}zu", touchId);
66         return;
67     }
68     // Resets drag state to ready.
69     iter->second.states_ = DetectState::READY;
70 }
71 
HandleTouchDownEvent(const TouchEvent & event)72 void DragRecognizer::HandleTouchDownEvent(const TouchEvent& event)
73 {
74     if ((touchRestrict_.forbiddenType & TouchRestrict::SWIPE) == TouchRestrict::SWIPE) {
75         return;
76     }
77     if (((touchRestrict_.forbiddenType & TouchRestrict::SWIPE_HORIZONTAL) == TouchRestrict::SWIPE_HORIZONTAL) &&
78         axis_ == Axis::HORIZONTAL) {
79         return;
80     }
81     if (((touchRestrict_.forbiddenType & TouchRestrict::SWIPE_VERTICAL) == TouchRestrict::SWIPE_VERTICAL) &&
82         axis_ == Axis::VERTICAL) {
83         return;
84     }
85     DragFingersInfo dragFingerInfo(axis_);
86     auto result = dragFingers_.insert_or_assign(event.id, dragFingerInfo);
87 
88     auto& dragInfo = result.first->second;
89     if (dragInfo.states_ == DetectState::READY) {
90         AddToReferee(event.id, Claim(this));
91         dragInfo.dragOffset_.Reset();
92         dragInfo.velocityTracker_.Reset();
93         dragInfo.velocityTracker_.UpdateTouchPoint(event);
94         dragInfo.states_ = DetectState::DETECTING;
95     } else {
96         LOGE("the state is not ready to receive touch down event, state is %{public}d, id is %{public}d",
97             dragInfo.states_, event.id);
98     }
99 }
100 
HandleTouchMoveEvent(const TouchEvent & event)101 void DragRecognizer::HandleTouchMoveEvent(const TouchEvent& event)
102 {
103     auto iter = dragFingers_.find(event.id);
104     if (iter == dragFingers_.end()) {
105         LOGE("the dragFingers_ is not ready to receive touch move event, id is %{public}d", event.id);
106         return;
107     }
108 
109     auto& dragInfo = iter->second;
110     dragInfo.velocityTracker_.UpdateTouchPoint(event);
111     if (dragInfo.states_ == DetectState::DETECTED) {
112         if (onDragUpdate_) {
113             DragUpdateInfo info(event.id);
114 
115             info.SetDelta(dragInfo.velocityTracker_.GetDelta())
116                 .SetMainDelta(dragInfo.velocityTracker_.GetMainAxisDeltaPos())
117                 .SetGlobalLocation(event.GetOffset())
118                 .SetLocalLocation(event.GetOffset() - coordinateOffset_);
119 
120             info.SetTimeStamp(event.time);
121             onDragUpdate_(info);
122             if (onDragUpdateNotifyCall_) {
123                 onDragUpdateNotifyCall_(event.GetOffset().GetX(), event.GetOffset().GetY(), info);
124             }
125         }
126     } else if (dragInfo.states_ == DetectState::DETECTING) {
127         dragInfo.dragOffset_ += dragInfo.velocityTracker_.GetDelta();
128         double dragOffsetInMainAxis = 0.0;
129         if (axis_ == Axis::FREE) {
130             dragOffsetInMainAxis = dragInfo.dragOffset_.GetDistance();
131         } else if (axis_ == Axis::VERTICAL) {
132             dragOffsetInMainAxis = dragInfo.dragOffset_.GetY();
133         } else {
134             dragOffsetInMainAxis = dragInfo.dragOffset_.GetX();
135         }
136         if (IsDragGestureAccept(dragOffsetInMainAxis)) {
137             Accept(event.id);
138         }
139     }
140 }
141 
HandleTouchUpEvent(const TouchEvent & event)142 void DragRecognizer::HandleTouchUpEvent(const TouchEvent& event)
143 {
144     auto iter = dragFingers_.find(event.id);
145     if (iter == dragFingers_.end()) {
146         LOGE("the dragFingers_ is not ready to receive touch up event, id is %{public}d", event.id);
147         return;
148     }
149 
150     auto& dragInfo = iter->second;
151     dragInfo.velocityTracker_.UpdateTouchPoint(event, true);
152     if (dragInfo.states_ == DetectState::DETECTED) {
153         bool upSuccess = true;
154         for (auto entry = dragFingers_.begin(); entry != dragFingers_.end(); ++entry) {
155             if (entry == iter) {
156                 continue;
157             }
158             auto& otherDragInfo = entry->second;
159             if (otherDragInfo.states_ == DetectState::DETECTED) {
160                 upSuccess = false;
161             }
162         }
163         if (upSuccess) {
164             if (onDragEnd_) {
165                 DragEndInfo endInfo(event.id);
166                 endInfo.SetVelocity(dragInfo.velocityTracker_.GetVelocity())
167                     .SetMainVelocity(dragInfo.velocityTracker_.GetMainAxisVelocity())
168                     .SetGlobalLocation(event.GetOffset())
169                     .SetLocalLocation(event.GetOffset() - coordinateOffset_);
170                 endInfo.SetTimeStamp(event.time);
171                 onDragEnd_(endInfo);
172             }
173         }
174         if (onDragEndNotifyCall_) {
175             DragEndInfo endInfo(event.id);
176 
177             endInfo.SetVelocity(dragInfo.velocityTracker_.GetVelocity())
178                 .SetMainVelocity(dragInfo.velocityTracker_.GetMainAxisVelocity())
179                 .SetGlobalLocation(event.GetOffset())
180                 .SetLocalLocation(event.GetOffset() - coordinateOffset_);
181 
182             endInfo.SetTimeStamp(event.time);
183             onDragEndNotifyCall_(event.GetOffset().GetX(), event.GetOffset().GetY(), endInfo);
184             if (onDragEnd_) {
185                 AsyncCallback(onDragEnd_, endInfo);
186             }
187         }
188     } else if (dragInfo.states_ == DetectState::DETECTING) {
189         Reject(event.id);
190     }
191     dragInfo.states_ = DetectState::READY;
192 }
193 
HandleTouchCancelEvent(const TouchEvent & event)194 void DragRecognizer::HandleTouchCancelEvent(const TouchEvent& event)
195 {
196     auto iter = dragFingers_.find(event.id);
197     if (iter == dragFingers_.end()) {
198         LOGE("the dragFingers_ is not ready to receive touch cancel event, id is %{public}d", event.id);
199         return;
200     }
201 
202     auto& dragInfo = iter->second;
203     if (dragInfo.states_ == DetectState::DETECTED) {
204         if (onDragCancel_) {
205             AsyncCallback(onDragCancel_);
206         }
207     } else if (dragInfo.states_ == DetectState::DETECTING) {
208         Reject(event.id);
209     }
210     dragInfo.states_ = DetectState::READY;
211 }
212 
IsDragGestureAccept(double offset) const213 bool DragRecognizer::IsDragGestureAccept(double offset) const
214 {
215     if (std::abs(offset) > DELTA_DURATION) {
216         if (axis_ == Axis::HORIZONTAL) {
217             uint32_t flag = offset > 0 ? TouchRestrict::SWIPE_RIGHT : TouchRestrict::SWIPE_LEFT;
218             if ((touchRestrict_.forbiddenType & flag) != flag) {
219                 return true;
220             }
221         } else if (axis_ == Axis::VERTICAL) {
222             uint32_t flag = offset > 0 ? TouchRestrict::SWIPE_DOWN : TouchRestrict::SWIPE_UP;
223             if ((touchRestrict_.forbiddenType & flag) != flag) {
224                 return true;
225             }
226         } else {
227             return true;
228         }
229     }
230     return false;
231 }
232 
Accept(size_t touchId)233 void DragRecognizer::Accept(size_t touchId)
234 {
235     std::set<size_t> ids;
236     ids.insert(touchId);
237     BatchAdjudicate(ids, Claim(this), GestureDisposal::ACCEPT);
238 }
239 
Reject(size_t touchId)240 void DragRecognizer::Reject(size_t touchId)
241 {
242     std::set<size_t> ids;
243     ids.insert(touchId);
244     BatchAdjudicate(ids, Claim(this), GestureDisposal::REJECT);
245 }
246 } // namespace OHOS::Ace
247