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