Skip to content

Commit

Permalink
fix: Android camera permission request
Browse files Browse the repository at this point in the history
  • Loading branch information
mastro993 committed Feb 4, 2025
1 parent 435848a commit 531604a
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ exports[`Check the addition for new fields to the persisted store. If one of thi
exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'persistedPreferences' state 1`] = `
{
"continueWithRootOrJailbreak": false,
"hasRequestedCameraPermission": false,
"isCustomEmailChannelEnabled": {
"kind": "PotNone",
},
Expand Down
14 changes: 13 additions & 1 deletion ts/boot/configureStoreAndPersistor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import { configureReactotron } from "./configureRectotron";
/**
* Redux persist will migrate the store to the current version
*/
const CURRENT_REDUX_STORE_VERSION = 39;
const CURRENT_REDUX_STORE_VERSION = 40;

// see redux-persist documentation:
// https://github.com/rt2zz/redux-persist/blob/master/docs/migrations.md
Expand Down Expand Up @@ -466,6 +466,18 @@ const migrations: MigrationManifest = {
isIOMarkdownEnabledOnMessagesAndServices: false
}
};
},
// Version 40
// Add 'hasRequestedCameraPermission' to 'persistedPreferences'
"40": (state: PersistedState) => {
const typedState = state as GlobalState;
return {
...state,
persistedPreferences: {
...typedState.persistedPreferences,
hasRequestedCameraPermission: false
}
};
}
};

Expand Down
50 changes: 45 additions & 5 deletions ts/features/barcode/hooks/useIOBarcodeCameraScanner.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable sonarjs/cognitive-complexity */
import {
IOColors,
IOStyles,
Expand All @@ -17,7 +18,7 @@ import {
useRef,
useState
} from "react";
import { Linking, StyleSheet, View } from "react-native";
import { Linking, Platform, StyleSheet, View } from "react-native";
import Animated, { FadeIn } from "react-native-reanimated";
import {
Camera,
Expand All @@ -38,6 +39,10 @@ import {
} from "../types/IOBarcode";
import { decodeIOBarcode } from "../types/decoders";
import { BarcodeFailure } from "../types/failure";
import { useIODispatch, useIOSelector } from "../../../store/hooks";
import { setHasRequestedCameraPermission } from "../../../store/actions/persistedPreferences";
import { hasRequestedCameraPermissionSelector } from "../../../store/reducers/persistedPreferences";
import { appStateSelector } from "../../../store/reducers/appState";

type IOBarcodeFormatsType = {
[K in IOBarcodeFormat]: BarcodeFormat;
Expand Down Expand Up @@ -180,6 +185,8 @@ export const useIOBarcodeCameraScanner = ({
barcodeTypes,
isLoading = false
}: IOBarcodeCameraScannerConfiguration): IOBarcodeCameraScanner => {
const dispatch = useIODispatch();

const acceptedFormats = useMemo<Array<IOBarcodeFormat>>(
() => barcodeFormats || ["QR_CODE", "DATA_MATRIX"],
[barcodeFormats]
Expand All @@ -199,6 +206,11 @@ export const useIOBarcodeCameraScanner = ({
const scannerReactivateTimeoutHandler = useRef<number>();
const [isResting, setIsResting] = useState(false);

const { appState } = useIOSelector(appStateSelector);
const hasRequestedCameraPermission = useIOSelector(
hasRequestedCameraPermissionSelector
);

const [cameraPermissionStatus, setCameraPermissionStatus] =
useState<CameraPermissionStatus>("not-determined");

Expand Down Expand Up @@ -283,13 +295,40 @@ export const useIOBarcodeCameraScanner = ({
onCodeScanned: handleScannedBarcodes
});

/** Translates the detected camera permission status to the real one based on the Platform */
const getRealCameraPermissionStatus = useCallback(
(detectedPermission: CameraPermissionStatus) =>
Platform.select({
default: detectedPermission,
/**
* On Android, if the permission is denied but it was not asked before since app install,
* means that the permission is requestable
*/
android:
detectedPermission === "denied" && !hasRequestedCameraPermission
? "not-determined"
: detectedPermission
}),
[hasRequestedCameraPermission]
);

/**
* Hook that checks the camera permission on mount
* Get the camera permission status
*/
useEffect(() => {
const getCameraPermissionStatus = useCallback(() => {
const permission = Camera.getCameraPermissionStatus();
setCameraPermissionStatus(permission);
}, []);
const realPermission = getRealCameraPermissionStatus(permission);
setCameraPermissionStatus(realPermission);
}, [getRealCameraPermissionStatus]);

/**
* Hook that checks the camera permission on mount and when the app state changes to active
*/
useEffect(() => {
if (appState === "active") {
getCameraPermissionStatus();
}
}, [getCameraPermissionStatus, appState]);

/**
* Hook that clears the timeout handler on unmount
Expand All @@ -306,6 +345,7 @@ export const useIOBarcodeCameraScanner = ({
*/
const requestCameraPermission = async () => {
const permissions = await Camera.requestCameraPermission();
dispatch(setHasRequestedCameraPermission(true));
setCameraPermissionStatus(permissions);
};

Expand Down
5 changes: 5 additions & 0 deletions ts/store/actions/persistedPreferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export const setIOMarkdownEnabledOnMessagesAndServices = createStandardAction(
"PREFERENCES_IO_MARKDOWN_ON_MESSAGES_AND_SERVICES_SET"
)<{ enabledOnMessagesAndServices: boolean }>();

export const setHasRequestedCameraPermission = createStandardAction(
"PREFERENCES_HAS_REQUESTED_CAMERA_PERMISSION_SET"
)<boolean>();

export type PersistedPreferencesActions = ActionType<
| typeof preferenceFingerprintIsEnabledSaveSuccess
| typeof preferredCalendarSaveSuccess
Expand All @@ -66,4 +70,5 @@ export type PersistedPreferencesActions = ActionType<
| typeof preferencesIdPayTestSetEnabled
| typeof preferencesDesignSystemSetEnabled
| typeof setIOMarkdownEnabledOnMessagesAndServices
| typeof setHasRequestedCameraPermission
>;
18 changes: 16 additions & 2 deletions ts/store/reducers/persistedPreferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import {
preferencesPnTestEnvironmentSetEnabled,
preferencesIdPayTestSetEnabled,
preferencesDesignSystemSetEnabled,
setIOMarkdownEnabledOnMessagesAndServices
setIOMarkdownEnabledOnMessagesAndServices,
setHasRequestedCameraPermission
} from "../actions/persistedPreferences";
import { Action } from "../actions/types";
import { differentProfileLoggedIn } from "../actions/crossSessions";
Expand All @@ -46,6 +47,9 @@ export type PersistedPreferencesState = Readonly<{
// be sure to handle such case when reading and using this value
isDesignSystemEnabled: boolean;
isIOMarkdownEnabledOnMessagesAndServices: boolean;
// Required to know if the camera permission has been requested on Android
// and avoid asking the permission again if it was denied
hasRequestedCameraPermission: boolean;
}>;

export const initialPreferencesState: PersistedPreferencesState = {
Expand All @@ -60,7 +64,8 @@ export const initialPreferencesState: PersistedPreferencesState = {
isPnTestEnabled: false,
isIdPayTestEnabled: false,
isDesignSystemEnabled: false,
isIOMarkdownEnabledOnMessagesAndServices: false
isIOMarkdownEnabledOnMessagesAndServices: false,
hasRequestedCameraPermission: false
};

export default function preferencesReducer(
Expand Down Expand Up @@ -164,6 +169,12 @@ export default function preferencesReducer(
};
}

if (isActionOf(setHasRequestedCameraPermission, action)) {
return {
...state,
hasRequestedCameraPermission: action.payload
};
}
return state;
}

Expand Down Expand Up @@ -211,6 +222,9 @@ export const isIOMarkdownEnabledLocallySelector = (
): boolean =>
state.persistedPreferences.isIOMarkdownEnabledOnMessagesAndServices;

export const hasRequestedCameraPermissionSelector = (state: GlobalState) =>
state.persistedPreferences.hasRequestedCameraPermission;

// returns the preferred language as an Option from the persisted store
export const preferredLanguageSelector = createSelector<
GlobalState,
Expand Down

0 comments on commit 531604a

Please sign in to comment.