From 41beaed7db9bd55e842a20cc59b578e17f36af99 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Mon, 2 Mar 2026 20:51:45 +0100 Subject: [PATCH 1/5] fix(android): Replace GestureDetectorCompat with lightweight SentryGestureDetector to fix ANR GestureDetectorCompat internally uses Handler.sendMessage/removeMessages which acquires a synchronized lock on the main thread MessageQueue, plus recordGestureClassification triggers IPC calls. This caused ANRs under load (SDK-CRASHES-JAVA-596, 175K+ occurrences). Replace with a minimal custom detector that only detects click, scroll, and fling without any Handler scheduling, MessageQueue contention, or IPC overhead. Co-Authored-By: Claude Opus 4.6 --- .../gestures/SentryGestureDetector.java | 125 ++++++++++++++++++ .../gestures/SentryWindowCallback.java | 9 +- .../gestures/SentryWindowCallbackTest.kt | 3 +- 3 files changed, 129 insertions(+), 8 deletions(-) create mode 100644 sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureDetector.java diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureDetector.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureDetector.java new file mode 100644 index 0000000000..34826d109e --- /dev/null +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureDetector.java @@ -0,0 +1,125 @@ +package io.sentry.android.core.internal.gestures; + +import android.content.Context; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A lightweight gesture detector that replaces {@link + * androidx.core.view.GestureDetectorCompat}/{@link GestureDetector} to avoid ANRs caused by + * Handler/MessageQueue lock contention and IPC calls (FrameworkStatsLog.write). + * + *

Only detects click (tap), scroll, and fling — the gestures used by {@link + * SentryGestureListener}. Long-press, show-press, and double-tap detection (which require Handler + * message scheduling) are intentionally omitted. + */ +@ApiStatus.Internal +public final class SentryGestureDetector { + + private final @NotNull GestureDetector.OnGestureListener listener; + private final int touchSlopSquare; + private final int minimumFlingVelocity; + private final int maximumFlingVelocity; + + private boolean isInTapRegion; + private float downX; + private float downY; + private float lastX; + private float lastY; + private @Nullable MotionEvent currentDownEvent; + private @Nullable VelocityTracker velocityTracker; + + SentryGestureDetector( + final @NotNull Context context, final @NotNull GestureDetector.OnGestureListener listener) { + this.listener = listener; + final ViewConfiguration config = ViewConfiguration.get(context); + final int touchSlop = config.getScaledTouchSlop(); + this.touchSlopSquare = touchSlop * touchSlop; + this.minimumFlingVelocity = config.getScaledMinimumFlingVelocity(); + this.maximumFlingVelocity = config.getScaledMaximumFlingVelocity(); + } + + boolean onTouchEvent(final @NotNull MotionEvent event) { + final int action = event.getActionMasked(); + + if (velocityTracker == null) { + velocityTracker = VelocityTracker.obtain(); + } + velocityTracker.addMovement(event); + + switch (action) { + case MotionEvent.ACTION_DOWN: + downX = event.getX(); + downY = event.getY(); + lastX = downX; + lastY = downY; + isInTapRegion = true; + + if (currentDownEvent != null) { + currentDownEvent.recycle(); + } + currentDownEvent = MotionEvent.obtain(event); + + listener.onDown(event); + break; + + case MotionEvent.ACTION_MOVE: + { + final float x = event.getX(); + final float y = event.getY(); + final float dx = x - downX; + final float dy = y - downY; + final float distanceSquare = (dx * dx) + (dy * dy); + + if (distanceSquare > touchSlopSquare) { + final float scrollX = lastX - x; + final float scrollY = lastY - y; + listener.onScroll(currentDownEvent, event, scrollX, scrollY); + isInTapRegion = false; + lastX = x; + lastY = y; + } + break; + } + + case MotionEvent.ACTION_UP: + if (isInTapRegion) { + listener.onSingleTapUp(event); + } else if (velocityTracker != null) { + final int pointerId = event.getPointerId(0); + velocityTracker.computeCurrentVelocity(1000, maximumFlingVelocity); + final float velocityX = velocityTracker.getXVelocity(pointerId); + final float velocityY = velocityTracker.getYVelocity(pointerId); + + if (Math.abs(velocityX) > minimumFlingVelocity + || Math.abs(velocityY) > minimumFlingVelocity) { + listener.onFling(currentDownEvent, event, velocityX, velocityY); + } + } + cleanup(); + break; + + case MotionEvent.ACTION_CANCEL: + cleanup(); + break; + } + + return false; + } + + private void cleanup() { + if (velocityTracker != null) { + velocityTracker.recycle(); + velocityTracker = null; + } + if (currentDownEvent != null) { + currentDownEvent.recycle(); + currentDownEvent = null; + } + } +} diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryWindowCallback.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryWindowCallback.java index edb9c9f9da..8d00686157 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryWindowCallback.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryWindowCallback.java @@ -1,11 +1,8 @@ package io.sentry.android.core.internal.gestures; import android.content.Context; -import android.os.Handler; -import android.os.Looper; import android.view.MotionEvent; import android.view.Window; -import androidx.core.view.GestureDetectorCompat; import io.sentry.SentryLevel; import io.sentry.SentryOptions; import io.sentry.SpanStatus; @@ -18,7 +15,7 @@ public final class SentryWindowCallback extends WindowCallbackAdapter { private final @NotNull Window.Callback delegate; private final @NotNull SentryGestureListener gestureListener; - private final @NotNull GestureDetectorCompat gestureDetector; + private final @NotNull SentryGestureDetector gestureDetector; private final @Nullable SentryOptions options; private final @NotNull MotionEventObtainer motionEventObtainer; @@ -29,7 +26,7 @@ public SentryWindowCallback( final @Nullable SentryOptions options) { this( delegate, - new GestureDetectorCompat(context, gestureListener, new Handler(Looper.getMainLooper())), + new SentryGestureDetector(context, gestureListener), gestureListener, options, new MotionEventObtainer() {}); @@ -37,7 +34,7 @@ public SentryWindowCallback( SentryWindowCallback( final @NotNull Window.Callback delegate, - final @NotNull GestureDetectorCompat gestureDetector, + final @NotNull SentryGestureDetector gestureDetector, final @NotNull SentryGestureListener gestureListener, final @Nullable SentryOptions options, final @NotNull MotionEventObtainer motionEventObtainer) { diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryWindowCallbackTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryWindowCallbackTest.kt index 856e6d0f15..8afc1b3930 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryWindowCallbackTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryWindowCallbackTest.kt @@ -2,7 +2,6 @@ package io.sentry.android.core.internal.gestures import android.view.MotionEvent import android.view.Window -import androidx.core.view.GestureDetectorCompat import io.sentry.android.core.SentryAndroidOptions import io.sentry.android.core.internal.gestures.SentryWindowCallback.MotionEventObtainer import kotlin.test.Test @@ -18,7 +17,7 @@ class SentryWindowCallbackTest { class Fixture { val delegate = mock() val options = SentryAndroidOptions().apply { dsn = "https://key@sentry.io/proj" } - val gestureDetector = mock() + val gestureDetector = mock() val gestureListener = mock() val motionEventCopy = mock() From 664f1264e97ac4617635a77598cbfa1a200dc36b Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Mon, 2 Mar 2026 20:59:09 +0100 Subject: [PATCH 2/5] test(android): Add unit tests for SentryGestureDetector Co-Authored-By: Claude Opus 4.6 --- .../gestures/SentryGestureDetectorTest.kt | 272 ++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureDetectorTest.kt diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureDetectorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureDetectorTest.kt new file mode 100644 index 0000000000..9cc0552172 --- /dev/null +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/gestures/SentryGestureDetectorTest.kt @@ -0,0 +1,272 @@ +package io.sentry.android.core.internal.gestures + +import android.os.SystemClock +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.ViewConfiguration +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import kotlin.test.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify + +@RunWith(AndroidJUnit4::class) +class SentryGestureDetectorTest { + + class Fixture { + val listener = mock() + val context = ApplicationProvider.getApplicationContext() + val touchSlop = ViewConfiguration.get(context).scaledTouchSlop + + fun getSut(): SentryGestureDetector { + return SentryGestureDetector(context, listener) + } + } + + private val fixture = Fixture() + + @Test + fun `tap - DOWN followed by UP within touch slop fires onSingleTapUp`() { + val sut = fixture.getSut() + val downTime = SystemClock.uptimeMillis() + + val down = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 100f, 100f, 0) + val up = MotionEvent.obtain(downTime, downTime + 50, MotionEvent.ACTION_UP, 100f, 100f, 0) + + sut.onTouchEvent(down) + sut.onTouchEvent(up) + + verify(fixture.listener).onDown(down) + verify(fixture.listener).onSingleTapUp(up) + verify(fixture.listener, never()).onScroll(any(), any(), any(), any()) + verify(fixture.listener, never()).onFling(anyOrNull(), any(), any(), any()) + + down.recycle() + up.recycle() + } + + @Test + fun `no tap - DOWN followed by MOVE beyond slop and UP does not fire onSingleTapUp`() { + val sut = fixture.getSut() + val downTime = SystemClock.uptimeMillis() + val beyondSlop = fixture.touchSlop + 10f + + val down = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 100f, 100f, 0) + val move = + MotionEvent.obtain( + downTime, + downTime + 16, + MotionEvent.ACTION_MOVE, + 100f + beyondSlop, + 100f, + 0, + ) + val up = + MotionEvent.obtain(downTime, downTime + 50, MotionEvent.ACTION_UP, 100f + beyondSlop, 100f, 0) + + sut.onTouchEvent(down) + sut.onTouchEvent(move) + sut.onTouchEvent(up) + + verify(fixture.listener, never()).onSingleTapUp(any()) + + down.recycle() + move.recycle() + up.recycle() + } + + @Test + fun `scroll - DOWN followed by MOVE beyond slop fires onScroll with correct deltas`() { + val sut = fixture.getSut() + val downTime = SystemClock.uptimeMillis() + val beyondSlop = fixture.touchSlop + 10f + + val down = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 100f, 200f, 0) + val move = + MotionEvent.obtain( + downTime, + downTime + 16, + MotionEvent.ACTION_MOVE, + 100f + beyondSlop, + 200f, + 0, + ) + + sut.onTouchEvent(down) + sut.onTouchEvent(move) + + // scrollX = lastX - currentX = 100 - (100 + beyondSlop) = -beyondSlop + verify(fixture.listener).onScroll(anyOrNull(), eq(move), eq(-beyondSlop), eq(0f)) + + down.recycle() + move.recycle() + } + + @Test + fun `fling - fast swipe fires onFling`() { + val sut = fixture.getSut() + val downTime = SystemClock.uptimeMillis() + val beyondSlop = fixture.touchSlop + 10f + + val down = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 100f, 100f, 0) + // Move far and fast (large distance in short time = high velocity) + val move = + MotionEvent.obtain( + downTime, + downTime + 10, + MotionEvent.ACTION_MOVE, + 100f + beyondSlop, + 100f, + 0, + ) + val up = MotionEvent.obtain(downTime, downTime + 20, MotionEvent.ACTION_UP, 500f, 100f, 0) + + sut.onTouchEvent(down) + sut.onTouchEvent(move) + sut.onTouchEvent(up) + + verify(fixture.listener).onFling(anyOrNull(), eq(up), any(), any()) + + down.recycle() + move.recycle() + up.recycle() + } + + @Test + fun `slow release - DOWN MOVE and slow UP does not fire onFling`() { + val sut = fixture.getSut() + val downTime = SystemClock.uptimeMillis() + val beyondSlop = fixture.touchSlop + 1f + + val down = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 100f, 100f, 0) + // Move just beyond slop + val move = + MotionEvent.obtain( + downTime, + downTime + 100, + MotionEvent.ACTION_MOVE, + 100f + beyondSlop, + 100f, + 0, + ) + // Stay at the same position for a long time to ensure near-zero velocity + val moveStill = + MotionEvent.obtain( + downTime, + downTime + 10000, + MotionEvent.ACTION_MOVE, + 100f + beyondSlop, + 100f, + 0, + ) + val up = + MotionEvent.obtain( + downTime, + downTime + 10001, + MotionEvent.ACTION_UP, + 100f + beyondSlop, + 100f, + 0, + ) + + sut.onTouchEvent(down) + sut.onTouchEvent(move) + sut.onTouchEvent(moveStill) + sut.onTouchEvent(up) + + verify(fixture.listener, never()).onFling(anyOrNull(), any(), any(), any()) + + down.recycle() + move.recycle() + moveStill.recycle() + up.recycle() + } + + @Test + fun `cancel - DOWN followed by CANCEL does not fire tap or fling callbacks`() { + val sut = fixture.getSut() + val downTime = SystemClock.uptimeMillis() + + val down = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 100f, 100f, 0) + val cancel = + MotionEvent.obtain(downTime, downTime + 50, MotionEvent.ACTION_CANCEL, 100f, 100f, 0) + + sut.onTouchEvent(down) + sut.onTouchEvent(cancel) + + verify(fixture.listener).onDown(down) + verify(fixture.listener, never()).onSingleTapUp(any()) + verify(fixture.listener, never()).onScroll(any(), any(), any(), any()) + verify(fixture.listener, never()).onFling(anyOrNull(), any(), any(), any()) + + down.recycle() + cancel.recycle() + } + + @Test + fun `sequential gestures - state resets between tap and scroll`() { + val sut = fixture.getSut() + val beyondSlop = fixture.touchSlop + 10f + + // First gesture: tap + var downTime = SystemClock.uptimeMillis() + val down1 = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 100f, 100f, 0) + val up1 = MotionEvent.obtain(downTime, downTime + 50, MotionEvent.ACTION_UP, 100f, 100f, 0) + + sut.onTouchEvent(down1) + sut.onTouchEvent(up1) + verify(fixture.listener).onSingleTapUp(up1) + + // Second gesture: scroll + downTime = SystemClock.uptimeMillis() + val down2 = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 200f, 200f, 0) + val move2 = + MotionEvent.obtain( + downTime, + downTime + 16, + MotionEvent.ACTION_MOVE, + 200f + beyondSlop, + 200f, + 0, + ) + val up2 = + MotionEvent.obtain( + downTime, + downTime + 5000, + MotionEvent.ACTION_UP, + 200f + beyondSlop, + 200f, + 0, + ) + + sut.onTouchEvent(down2) + sut.onTouchEvent(move2) + sut.onTouchEvent(up2) + + verify(fixture.listener).onScroll(anyOrNull(), eq(move2), any(), any()) + // onSingleTapUp should NOT have been called again for the second gesture + verify(fixture.listener, never()).onSingleTapUp(up2) + + // Third gesture: another tap to verify clean reset + downTime = SystemClock.uptimeMillis() + val down3 = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, 300f, 300f, 0) + val up3 = MotionEvent.obtain(downTime, downTime + 50, MotionEvent.ACTION_UP, 300f, 300f, 0) + + sut.onTouchEvent(down3) + sut.onTouchEvent(up3) + verify(fixture.listener).onSingleTapUp(up3) + + down1.recycle() + up1.recycle() + down2.recycle() + move2.recycle() + up2.recycle() + down3.recycle() + up3.recycle() + } +} From 50ac45bc585f3d63934c221d6eec5a5cb8586955 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Mon, 2 Mar 2026 21:05:54 +0100 Subject: [PATCH 3/5] changelog Co-Authored-By: Claude Opus 4.6 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dba0f4dcdf..a5abf567b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ ### Fixes +- Fix ANR caused by `GestureDetectorCompat` Handler/MessageQueue lock contention in `SentryWindowCallback` ([#5138](https://github.com/getsentry/sentry-java/pull/5138)) - Fix crash when unregistering `SystemEventsBroadcastReceiver` with try-catch block. ([#5106](https://github.com/getsentry/sentry-java/pull/5106)) ### Dependencies From d5baf9412d2418f93379ab045ff30cb2fe324e56 Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Mon, 2 Mar 2026 22:50:54 +0100 Subject: [PATCH 4/5] fix(gestures): Clear VelocityTracker on ACTION_DOWN to prevent stale velocity data Matches GestureDetector behavior: if consecutive ACTION_DOWN events arrive without an intervening ACTION_UP/ACTION_CANCEL, stale motion data could bleed into fling detection. Co-Authored-By: Claude Opus 4.6 --- .../android/core/internal/gestures/SentryGestureDetector.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureDetector.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureDetector.java index 34826d109e..228ecaa850 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureDetector.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/gestures/SentryGestureDetector.java @@ -50,6 +50,10 @@ boolean onTouchEvent(final @NotNull MotionEvent event) { if (velocityTracker == null) { velocityTracker = VelocityTracker.obtain(); } + + if (action == MotionEvent.ACTION_DOWN) { + velocityTracker.clear(); + } velocityTracker.addMovement(event); switch (action) { From 7aa100ca8c59e678d38ac02a129bdf23a6048efd Mon Sep 17 00:00:00 2001 From: Roman Zavarnitsyn Date: Mon, 2 Mar 2026 23:15:18 +0100 Subject: [PATCH 5/5] fix(gestures): Remove stale GestureDetectorCompat class availability check UserInteractionIntegration gated itself on GestureDetectorCompat being available via classloader check, but SentryGestureDetector only uses Android SDK classes. Remove the check so the integration works without androidx.core. Also remove the stale proguard -keep rule. Co-Authored-By: Claude Opus 4.6 --- sentry-android-core/proguard-rules.pro | 1 - .../core/UserInteractionIntegration.java | 33 +++++++------------ .../core/UserInteractionIntegrationTest.kt | 17 ---------- 3 files changed, 11 insertions(+), 40 deletions(-) diff --git a/sentry-android-core/proguard-rules.pro b/sentry-android-core/proguard-rules.pro index 25086b4d2b..4706a95479 100644 --- a/sentry-android-core/proguard-rules.pro +++ b/sentry-android-core/proguard-rules.pro @@ -1,7 +1,6 @@ ##---------------Begin: proguard configuration for android-core ---------- ##---------------Begin: proguard configuration for androidx.core ---------- --keep class androidx.core.view.GestureDetectorCompat { (...); } -keep class androidx.core.app.FrameMetricsAggregator { (...); } -keep interface androidx.core.view.ScrollingView { *; } ##---------------End: proguard configuration for androidx.core ---------- diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java b/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java index 9f47fc8666..c0dd3f9eb7 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/UserInteractionIntegration.java @@ -28,14 +28,11 @@ public final class UserInteractionIntegration private @Nullable IScopes scopes; private @Nullable SentryAndroidOptions options; - private final boolean isAndroidXAvailable; private final boolean isAndroidxLifecycleAvailable; public UserInteractionIntegration( final @NotNull Application application, final @NotNull io.sentry.util.LoadClass classLoader) { this.application = Objects.requireNonNull(application, "Application is required"); - isAndroidXAvailable = - classLoader.isClassAvailable("androidx.core.view.GestureDetectorCompat", options); isAndroidxLifecycleAvailable = classLoader.isClassAvailable("androidx.lifecycle.Lifecycle", options); } @@ -128,27 +125,19 @@ public void register(@NotNull IScopes scopes, @NotNull SentryOptions options) { .log(SentryLevel.DEBUG, "UserInteractionIntegration enabled: %s", integrationEnabled); if (integrationEnabled) { - if (isAndroidXAvailable) { - application.registerActivityLifecycleCallbacks(this); - this.options.getLogger().log(SentryLevel.DEBUG, "UserInteractionIntegration installed."); - addIntegrationToSdkVersion("UserInteraction"); - - // In case of a deferred init, we hook into any resumed activity - if (isAndroidxLifecycleAvailable) { - final @Nullable Activity activity = CurrentActivityHolder.getInstance().getActivity(); - if (activity instanceof LifecycleOwner) { - if (((LifecycleOwner) activity).getLifecycle().getCurrentState() - == Lifecycle.State.RESUMED) { - startTracking(activity); - } + application.registerActivityLifecycleCallbacks(this); + this.options.getLogger().log(SentryLevel.DEBUG, "UserInteractionIntegration installed."); + addIntegrationToSdkVersion("UserInteraction"); + + // In case of a deferred init, we hook into any resumed activity + if (isAndroidxLifecycleAvailable) { + final @Nullable Activity activity = CurrentActivityHolder.getInstance().getActivity(); + if (activity instanceof LifecycleOwner) { + if (((LifecycleOwner) activity).getLifecycle().getCurrentState() + == Lifecycle.State.RESUMED) { + startTracking(activity); } } - } else { - options - .getLogger() - .log( - SentryLevel.INFO, - "androidx.core is not available, UserInteractionIntegration won't be installed"); } } } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/UserInteractionIntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/UserInteractionIntegrationTest.kt index 4f1495a987..f558841e6f 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/UserInteractionIntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/UserInteractionIntegrationTest.kt @@ -39,16 +39,8 @@ class UserInteractionIntegrationTest { fun getSut( callback: Window.Callback? = null, - isAndroidXAvailable: Boolean = true, isLifecycleAvailable: Boolean = true, ): UserInteractionIntegration { - whenever( - loadClass.isClassAvailable( - eq("androidx.core.view.GestureDetectorCompat"), - anyOrNull(), - ) - ) - .thenReturn(isAndroidXAvailable) whenever( loadClass.isClassAvailable( eq("androidx.lifecycle.Lifecycle"), @@ -99,15 +91,6 @@ class UserInteractionIntegrationTest { verify(fixture.application).unregisterActivityLifecycleCallbacks(any()) } - @Test - fun `when androidx is unavailable doesn't register a callback`() { - val sut = fixture.getSut(isAndroidXAvailable = false) - - sut.register(fixture.scopes, fixture.options) - - verify(fixture.application, never()).registerActivityLifecycleCallbacks(any()) - } - @Test fun `registers window callback on activity resumed`() { val sut = fixture.getSut()