From e292aed9e25827febd0ba92894190295fbe674d6 Mon Sep 17 00:00:00 2001 From: "Ibrahima G. Coulibaly" Date: Thu, 26 Mar 2026 22:06:43 +0100 Subject: [PATCH] fix: ensure Android camera intent waits for activity to fully resume - Added `waitForAppActive` utility to handle transient pause states on Android 12+. - Updated `openCameraWithPermission` to wait for the app to become active before launching the camera on Android. --- mobile/utils/mediaPermissions.ts | 34 ++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/mobile/utils/mediaPermissions.ts b/mobile/utils/mediaPermissions.ts index bd3a368ab..8f34f5432 100644 --- a/mobile/utils/mediaPermissions.ts +++ b/mobile/utils/mediaPermissions.ts @@ -1,4 +1,4 @@ -import { Alert, Linking } from 'react-native'; +import { Alert, Linking, AppState, Platform } from 'react-native'; import * as ImagePicker from 'expo-image-picker'; import { Camera } from 'expo-camera'; import i18n from '../i18n/i18n'; @@ -74,6 +74,28 @@ export const ensurePermission = async ({ } }; +/** + * On Android 12+, the permission dialog dismissal puts the activity into a + * transient pause. Launching the camera immediately after causes Android to + * silently drop the intent. We wait until the activity is fully active again. + */ +const waitForAppActive = (): Promise => { + return new Promise((resolve) => { + if (AppState.currentState === 'active') { + resolve(); + return; + } + + const sub = AppState.addEventListener('change', (state) => { + if (state === 'active') { + sub.remove(); + // Small buffer for the activity window to fully settle + setTimeout(resolve, 100); + } + }); + }); +}; + export const openCameraWithPermission = async ( flow: string, options: ImagePicker.ImagePickerOptions @@ -90,6 +112,12 @@ export const openCameraWithPermission = async ( return null; } + // Wait for activity to fully resume before launching camera. + // Only needed on Android — iOS handles this natively. + if (Platform.OS === 'android') { + await waitForAppActive(); + } + console.warn(`[${flow}] Opening camera picker`); const result = await ImagePicker.launchCameraAsync(options); console.warn( @@ -111,7 +139,9 @@ export const openLibraryWithPermission = async ( }); if (!hasPermission) { - console.warn(`[${flow}] Media library permission denied, picker will not open`); + console.warn( + `[${flow}] Media library permission denied, picker will not open` + ); return null; }