Skip to content

Commit fddc6d0

Browse files
runningcodeclaude
andcommitted
fix(android): Re-arm shake detector on re-register (JAVA-618)
The closed latch added to neutralize a warm-up drained after close() was permanent, so re-registering the same integration (e.g. a second Sentry.init reusing the same options) left shake detection off with no recovery. register() now re-arms the detector via reopen(), which the stale-warm-up path (init()) deliberately does not, preserving the drain-after-close guard. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 6b4dae5 commit fddc6d0

3 files changed

Lines changed: 32 additions & 0 deletions

File tree

sentry-android-core/src/main/java/io/sentry/android/core/FeedbackShakeIntegration.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ public void register(final @NotNull IScopes scopes, final @NotNull SentryOptions
5050
return;
5151
}
5252

53+
// Re-arm the detector in case this integration is being re-registered after a previous close()
54+
// (e.g. a second Sentry.init reusing the same options), otherwise the closed latch would keep
55+
// shake detection off permanently.
56+
shakeDetector.reopen();
57+
5358
// Resolving the accelerometer is the most expensive part of init (the first SensorManager
5459
// access), so warm it up off the main thread. start() re-runs init() on demand, so shake
5560
// detection still works if an activity resumes before this completes.

sentry-android-core/src/main/java/io/sentry/android/core/SentryShakeDetector.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ public SentryShakeDetector(final @NotNull ILogger logger) {
5252
this.logger = logger;
5353
}
5454

55+
/**
56+
* Re-arms the detector after a previous {@link #close()} so it can be reused when the owning
57+
* integration is registered again (e.g. a second {@code Sentry.init}).
58+
*/
59+
synchronized void reopen() {
60+
closed = false;
61+
}
62+
5563
/**
5664
* Initializes the sensor manager and accelerometer sensor. This is separated from start() so the
5765
* values can be resolved once and reused across activity transitions.

sentry-android-core/src/test/java/io/sentry/android/core/FeedbackShakeIntegrationTest.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import kotlin.test.BeforeTest
1212
import kotlin.test.Test
1313
import org.junit.runner.RunWith
1414
import org.mockito.kotlin.any
15+
import org.mockito.kotlin.atLeastOnce
1516
import org.mockito.kotlin.eq
1617
import org.mockito.kotlin.mock
1718
import org.mockito.kotlin.never
@@ -92,6 +93,24 @@ class FeedbackShakeIntegrationTest {
9293
verify(fixture.application, never()).getSystemService(eq(Context.SENSOR_SERVICE))
9394
}
9495

96+
@Test
97+
fun `re-registering after close re-arms shake detection`() {
98+
// A second Sentry.init reusing the same integration must revive shake detection rather than
99+
// stay off because of the closed latch.
100+
val deferredExecutor = DeferredExecutorService()
101+
fixture.options.executorService = deferredExecutor
102+
whenever(fixture.application.getSystemService(any())).thenReturn(null)
103+
104+
val sut = fixture.getSut(useShakeGesture = true)
105+
sut.register(fixture.scopes, fixture.options)
106+
sut.close()
107+
sut.register(fixture.scopes, fixture.options)
108+
109+
deferredExecutor.runAll()
110+
111+
verify(fixture.application, atLeastOnce()).getSystemService(eq(Context.SENSOR_SERVICE))
112+
}
113+
95114
@Test
96115
fun `when useShakeGesture is disabled does not register activity lifecycle callbacks`() {
97116
val sut = fixture.getSut(useShakeGesture = false)

0 commit comments

Comments
 (0)