3232@ ApiStatus .Internal
3333public final class SentryGestureListener implements GestureDetector .OnGestureListener {
3434
35+ private enum GestureType {
36+ Click ,
37+ Scroll ,
38+ Swipe ,
39+ Unknown
40+ }
41+
3542 static final String UI_ACTION = "ui.action" ;
3643 private static final String TRACE_ORIGIN = "auto.ui.gesture_listener" ;
3744
@@ -41,7 +48,7 @@ public final class SentryGestureListener implements GestureDetector.OnGestureLis
4148
4249 private @ Nullable UiElement activeUiElement = null ;
4350 private @ Nullable ITransaction activeTransaction = null ;
44- private @ Nullable String activeEventType = null ;
51+ private @ NotNull GestureType activeEventType = GestureType . Unknown ;
4552
4653 private final ScrollState scrollState = new ScrollState ();
4754
@@ -61,7 +68,7 @@ public void onUp(final @NotNull MotionEvent motionEvent) {
6168 return ;
6269 }
6370
64- if (scrollState .type == null ) {
71+ if (scrollState .type == GestureType . Unknown ) {
6572 options
6673 .getLogger ()
6774 .log (SentryLevel .DEBUG , "Unable to define scroll type. No breadcrumb captured." );
@@ -107,8 +114,8 @@ public boolean onSingleTapUp(final @Nullable MotionEvent motionEvent) {
107114 return false ;
108115 }
109116
110- addBreadcrumb (target , "click" , Collections .emptyMap (), motionEvent );
111- startTracing (target , "click" );
117+ addBreadcrumb (target , GestureType . Click , Collections .emptyMap (), motionEvent );
118+ startTracing (target , GestureType . Click );
112119 return false ;
113120 }
114121
@@ -123,7 +130,7 @@ public boolean onScroll(
123130 return false ;
124131 }
125132
126- if (scrollState .type == null ) {
133+ if (scrollState .type == GestureType . Unknown ) {
127134 final @ Nullable UiElement target =
128135 ViewUtils .findTarget (
129136 options , decorView , firstEvent .getX (), firstEvent .getY (), UiElement .Type .SCROLLABLE );
@@ -140,7 +147,7 @@ public boolean onScroll(
140147 }
141148
142149 scrollState .setTarget (target );
143- scrollState .type = "scroll" ;
150+ scrollState .type = GestureType . Scroll ;
144151 }
145152 return false ;
146153 }
@@ -151,7 +158,7 @@ public boolean onFling(
151158 final @ Nullable MotionEvent motionEvent1 ,
152159 final float v ,
153160 final float v1 ) {
154- scrollState .type = "swipe" ;
161+ scrollState .type = GestureType . Swipe ;
155162 return false ;
156163 }
157164
@@ -164,32 +171,37 @@ public void onLongPress(MotionEvent motionEvent) {}
164171 // region utils
165172 private void addBreadcrumb (
166173 final @ NotNull UiElement target ,
167- final @ NotNull String eventType ,
174+ final @ NotNull GestureType eventType ,
168175 final @ NotNull Map <String , Object > additionalData ,
169176 final @ NotNull MotionEvent motionEvent ) {
170177
171178 if (!options .isEnableUserInteractionBreadcrumbs ()) {
172179 return ;
173180 }
174181
182+ final String type = getGestureType (eventType );
183+
175184 final Hint hint = new Hint ();
176185 hint .set (ANDROID_MOTION_EVENT , motionEvent );
177186 hint .set (ANDROID_VIEW , target .getView ());
178187
179188 hub .addBreadcrumb (
180189 Breadcrumb .userInteraction (
181- eventType ,
182- target .getResourceName (),
183- target .getClassName (),
184- target .getTag (),
185- additionalData ),
190+ type , target .getResourceName (), target .getClassName (), target .getTag (), additionalData ),
186191 hint );
187192 }
188193
189- private void startTracing (final @ NotNull UiElement target , final @ NotNull String eventType ) {
190- final UiElement uiElement = activeUiElement ;
194+ private void startTracing (final @ NotNull UiElement target , final @ NotNull GestureType eventType ) {
195+
196+ final boolean isNewGestureSameAsActive =
197+ (eventType == activeEventType && target .equals (activeUiElement ));
198+ final boolean isClickGesture = eventType == GestureType .Click ;
199+ // we always want to start new transaction/traces for clicks, for swipe/scroll only if the
200+ // target changed
201+ final boolean isNewInteraction = isClickGesture || !isNewGestureSameAsActive ;
202+
191203 if (!(options .isTracingEnabled () && options .isEnableUserInteractionTracing ())) {
192- if (!( target . equals ( uiElement ) && eventType . equals ( activeEventType )) ) {
204+ if (isNewInteraction ) {
193205 TracingUtils .startNewTrace (hub );
194206 activeUiElement = target ;
195207 activeEventType = eventType ;
@@ -206,9 +218,7 @@ private void startTracing(final @NotNull UiElement target, final @NotNull String
206218 final @ Nullable String viewIdentifier = target .getIdentifier ();
207219
208220 if (activeTransaction != null ) {
209- if (target .equals (uiElement )
210- && eventType .equals (activeEventType )
211- && !activeTransaction .isFinished ()) {
221+ if (!isNewInteraction && !activeTransaction .isFinished ()) {
212222 options
213223 .getLogger ()
214224 .log (
@@ -233,7 +243,7 @@ private void startTracing(final @NotNull UiElement target, final @NotNull String
233243
234244 // we can only bind to the scope if there's no running transaction
235245 final String name = getActivityName (activity ) + "." + viewIdentifier ;
236- final String op = UI_ACTION + "." + eventType ;
246+ final String op = UI_ACTION + "." + getGestureType ( eventType ) ;
237247
238248 final TransactionOptions transactionOptions = new TransactionOptions ();
239249 transactionOptions .setWaitForChildren (true );
@@ -270,13 +280,15 @@ void stopTracing(final @NotNull SpanStatus status) {
270280 }
271281 hub .configureScope (
272282 scope -> {
283+ // avoid method refs on Android due to some issues with older AGP setups
284+ // noinspection Convert2MethodRef
273285 clearScope (scope );
274286 });
275287 activeTransaction = null ;
276288 if (activeUiElement != null ) {
277289 activeUiElement = null ;
278290 }
279- activeEventType = null ;
291+ activeEventType = GestureType . Unknown ;
280292 }
281293
282294 @ VisibleForTesting
@@ -337,11 +349,32 @@ void applyScope(final @NotNull Scope scope, final @NotNull ITransaction transact
337349 }
338350 return decorView ;
339351 }
352+
353+ @ NotNull
354+ private static String getGestureType (final @ NotNull GestureType eventType ) {
355+ final @ NotNull String type ;
356+ switch (eventType ) {
357+ case Click :
358+ type = "click" ;
359+ break ;
360+ case Scroll :
361+ type = "scroll" ;
362+ break ;
363+ case Swipe :
364+ type = "swipe" ;
365+ break ;
366+ default :
367+ case Unknown :
368+ type = "unknown" ;
369+ break ;
370+ }
371+ return type ;
372+ }
340373 // endregion
341374
342375 // region scroll logic
343376 private static final class ScrollState {
344- private @ Nullable String type = null ;
377+ private @ NotNull GestureType type = GestureType . Unknown ;
345378 private @ Nullable UiElement target ;
346379 private float startX = 0f ;
347380 private float startY = 0f ;
@@ -378,7 +411,7 @@ private void setTarget(final @NotNull UiElement target) {
378411
379412 private void reset () {
380413 target = null ;
381- type = null ;
414+ type = GestureType . Unknown ;
382415 startX = 0f ;
383416 startY = 0f ;
384417 }
0 commit comments