Skip to content

Crash: launching unregistered ActivityResultLauncher for POST_NOTIFICATIONS #84

Description

@figonzal1

@

Description

Fatal IllegalStateException reported in Crashlytics when the user taps the "Activate" button on the notification-permission cardview in QuakeFragment.

Fatal Exception: java.lang.IllegalStateException: Attempting to launch an unregistered
ActivityResultLauncher with contract j3@... and input android.permission.POST_NOTIFICATIONS.
You must ensure the ActivityResultLauncher is registered before calling launch().
    at androidx.activity.result.ActivityResultRegistry$register$2.launch(ActivityResultRegistry.kt:123)
    at androidx.fragment.app.Fragment$10.launch(Fragment.java:3637)
    at cl.figonzal.lastquakechile.core.services.notifications.utils.NotificationsExtKt.handleCvAlertPermission$lambda$2$lambda$1(NotificationsExt.kt:126)
    at android.view.View.performClick(View.java:8047)
    at android.view.View$PerformClick.run(View.java:31890)
    at android.os.Handler.handleCallback(Handler.java:958)

Root cause

notificationPermissionLauncher is registered as a Fragment property and the ActivityResultRegistry
unregisters it once the Fragment reaches DESTROYED. The button click is delivered as a queued
Runnable on the main Handler (View$PerformClick), so it can fire after the Fragment has begun
tearing down — at which point launcher.launch() throws IllegalStateException.

This is a race condition, not a logic bug. It is not device/OS specific (AndroidX framework
behavior) but is more likely on low-end devices, on rapid double-taps, or during a tab switch /
back navigation / configuration change happening at the moment of the tap. Only affects Android 13+
(TIRAMISU), since the permission cardview is gated behind @RequiresApi(TIRAMISU).

Proposed fix

Layered defensive guard around the launch (mirrors the lifecycle-guard pattern from #83 / Maps snapshot):

  1. Skip the launch when the Fragment lifecycle is below STARTED (or not added) — covers the normal teardown window.
  2. Wrap launcher.launch() in try/catch (IllegalStateException) to absorb the residual race.

Normal behavior (tap while the screen is visible) is unchanged.
@

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions