Skip to content

Camera shows black screen after granting permission via Settings on Android release builds #3728

@gobi-G

Description

@gobi-G

Description

After a fresh install, when camera permission is denied and then granted via the Android Settings app, the camera view shows a black screen upon returning to the app. The camera only works after a full app restart.

This bug is only reproducible in release/preview builds (not in debug/development builds).

Steps to Reproduce

  1. Fresh install of the app
  2. Navigate to the scanner screen
  3. Deny camera permission in the system dialog (or tap "Don't Allow")
  4. Tap "Open Settings" button (app calls Linking.openSettings())
  5. Grant camera permission in Android Settings
  6. Return to the app (press back)
  7. Expected: Camera feed is visible
  8. Actual: Screen is completely black — camera only appears after restarting the app

Root Cause Analysis

useCameraPermission registers an AppState.addEventListener('change', ...) listener that calls Camera.getCameraPermissionStatus() synchronously to update hasPermission. On Android release builds, this listener appears to not correctly update hasPermission to true when returning from the Settings app with permission granted.

Because hasPermission is only initialized once via useState(() => get() === 'granted') and relies on the AppState listener to refresh, stale state persists until a full app restart causes useState to re-initialize with the correct permission status.

Why only release builds? In development builds, Metro's Fast Refresh causes component remounts that incidentally re-initialize the hook state. In release builds (Hermes, no Metro), this doesn't occur.

Workaround

Detect the hasPermission: false → true transition and force a navigation screen replacement to trigger a full component remount:

const shouldReplaceRef = useRef(!hasPermission);
useEffect(() => {
  if (!hasPermission) {
    shouldReplaceRef.current = true;
  } else if (shouldReplaceRef.current && appState === 'active') {
    shouldReplaceRef.current = false;
    navigation.replace('scanner', route.params); // force full remount
  }
}, [hasPermission, appState, navigation, route.params]);

Environment

react-native-vision-camera 4.7.3
react-native 0.79.6
expo 53.0.25
Platform Android
Build type Release / Preview (Expo EAS)
JS Engine Hermes

Additional Notes

  • Camera.getAvailableCameraDevices() does not require camera permission on Android — devices are always returned correctly, so a stale device object is not the cause.
  • The useCameraPermission AppState listener is correctly registered (confirmed via source inspection of v4.7.3). The issue appears to be with the synchronous getCameraPermissionStatus() bridge call returning a stale/incorrect value in the context of an Android release build resuming from background.

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