Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Android camera permission prompt #28619

Merged
merged 9 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,29 @@
# This value is used by $RNMapboxMaps
$RNMapboxMapsImpl = 'mapbox'

# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
'require.resolve(
"react-native/scripts/react_native_pods.rb",
{paths: [process.argv[1]]},
)', __dir__]).strip
def node_require(script)
# Resolve script with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
"require.resolve(
'#{script}',
{paths: [process.argv[1]]},
)", __dir__]).strip
end

node_require('react-native/scripts/react_native_pods.rb')
node_require('react-native-permissions/scripts/setup.rb')

# Our min supported iOS version is higher than the default (min_ios_version_supported) to support libraires such as Airship
platform :ios, 13
prepare_react_native_project!

setup_permissions([
'Camera',
'LocationAccuracy',
'LocationAlways',
'LocationWhenInUse'
])

zoontek marked this conversation as resolved.
Show resolved Hide resolved
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
#
Expand Down Expand Up @@ -51,8 +63,6 @@ pre_install do |installer|
end

target 'NewExpensify' do
permissions_path = '../node_modules/react-native-permissions/ios'

project 'NewExpensify',
'DebugDevelopment' => :debug,
'DebugAdHoc' => :debug,
Expand All @@ -61,11 +71,6 @@ target 'NewExpensify' do
'ReleaseAdHoc' => :release,
'ReleaseProduction' => :release

pod 'Permission-LocationAccuracy', :path => "#{permissions_path}/LocationAccuracy"
pod 'Permission-LocationAlways', :path => "#{permissions_path}/LocationAlways"
pod 'Permission-LocationWhenInUse', :path => "#{permissions_path}/LocationWhenInUse"
pod 'Permission-Camera', :path => "#{permissions_path}/Camera"

config = use_native_modules!

# Flags change depending on the env values.
Expand Down
30 changes: 3 additions & 27 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -256,14 +256,6 @@ PODS:
- Onfido (= 27.4.0)
- React
- OpenSSL-Universal (1.1.1100)
- Permission-Camera (3.6.1):
- RNPermissions
- Permission-LocationAccuracy (3.6.1):
- RNPermissions
- Permission-LocationAlways (3.6.1):
- RNPermissions
- Permission-LocationWhenInUse (3.6.1):
- RNPermissions
- Plaid (4.1.0)
- PromisesObjC (2.2.0)
- RCT-Folly (2021.07.22.00):
Expand Down Expand Up @@ -781,7 +773,7 @@ PODS:
- React
- React-Core
- Turf
- RNPermissions (3.6.1):
- RNPermissions (3.9.3):
- React-Core
- RNReactNativeHapticFeedback (1.14.0):
- React-Core
Expand Down Expand Up @@ -867,10 +859,6 @@ DEPENDENCIES:
- lottie-react-native (from `../node_modules/lottie-react-native`)
- "onfido-react-native-sdk (from `../node_modules/@onfido/react-native-sdk`)"
- OpenSSL-Universal (= 1.1.1100)
- Permission-Camera (from `../node_modules/react-native-permissions/ios/Camera`)
- Permission-LocationAccuracy (from `../node_modules/react-native-permissions/ios/LocationAccuracy`)
- Permission-LocationAlways (from `../node_modules/react-native-permissions/ios/LocationAlways`)
- Permission-LocationWhenInUse (from `../node_modules/react-native-permissions/ios/LocationWhenInUse`)
- RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
Expand Down Expand Up @@ -1018,14 +1006,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/lottie-react-native"
onfido-react-native-sdk:
:path: "../node_modules/@onfido/react-native-sdk"
Permission-Camera:
:path: "../node_modules/react-native-permissions/ios/Camera"
Permission-LocationAccuracy:
:path: "../node_modules/react-native-permissions/ios/LocationAccuracy"
Permission-LocationAlways:
:path: "../node_modules/react-native-permissions/ios/LocationAlways"
Permission-LocationWhenInUse:
:path: "../node_modules/react-native-permissions/ios/LocationWhenInUse"
RCT-Folly:
:podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec"
RCTRequired:
Expand Down Expand Up @@ -1227,10 +1207,6 @@ SPEC CHECKSUMS:
Onfido: e36f284b865adcf99d9c905590a64ac09d4a576b
onfido-react-native-sdk: 4ecde1a97435dcff9f00a878e3f8d1eb14fabbdc
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
Permission-Camera: bf6791b17c7f614b6826019fcfdcc286d3a107f6
Permission-LocationAccuracy: 76df17de5c6b8bc2eee34e61ee92cdd7a864c73d
Permission-LocationAlways: 8d99b025c9f73c696e0cdb367e42525f2e9a26f2
Permission-LocationWhenInUse: 3ba99e45c852763f730eabecec2870c2382b7bd4
Plaid: 7d340abeadb46c7aa1a91f896c5b22395a31fcf2
PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef
RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1
Expand Down Expand Up @@ -1302,7 +1278,7 @@ SPEC CHECKSUMS:
RNGoogleSignin: ccaa4a81582cf713eea562c5dd9dc1961a715fd0
RNLocalize: d4b8af4e442d4bcca54e68fc687a2129b4d71a81
rnmapbox-maps: 6f638ec002aa6e906a6f766d69cd45f968d98e64
RNPermissions: dcdb7b99796bbeda6975a6e79ad519c41b251b1c
RNPermissions: 9b086c8f05b2e2faa587fdc31f4c5ab4509728aa
RNReactNativeHapticFeedback: 1e3efeca9628ff9876ee7cdd9edec1b336913f8c
RNReanimated: ab2e96c6d5591c3dfbb38a464f54c8d17fb34a87
RNScreens: d037903436160a4b039d32606668350d2a808806
Expand All @@ -1315,6 +1291,6 @@ SPEC CHECKSUMS:
Yoga: 3efc43e0d48686ce2e8c60f99d4e6bd349aff981
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a

PODFILE CHECKSUM: 2daf34c870819a933f3fefe426801d54b2ff2a14
PODFILE CHECKSUM: ff769666b7221c15936ebc5576a8c8e467dc6879

COCOAPODS: 1.12.1
11 changes: 7 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@
"react-native-pager-view": "^6.2.0",
"react-native-pdf": "^6.7.1",
"react-native-performance": "^5.1.0",
"react-native-permissions": "^3.0.1",
"react-native-permissions": "^3.9.3",
"react-native-picker-select": "git+https://github.com/Expensify/react-native-picker-select.git#eae05855286dc699954415cc1d629bfd8e8e47e2",
"react-native-plaid-link-sdk": "^10.0.0",
"react-native-qrcode-svg": "^6.2.0",
Expand Down
57 changes: 33 additions & 24 deletions src/pages/iou/ReceiptSelector/index.native.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,7 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator})

const camera = useRef(null);
const [flash, setFlash] = useState(false);
const [permissions, setPermissions] = useState('granted');
const isAndroidBlockedPermissionRef = useRef(false);
const appState = useRef(AppState.currentState);
const [cameraPermissionStatus, setCameraPermissionStatus] = useState(undefined);

const iouType = lodashGet(route, 'params.iouType', '');
const pageIndex = lodashGet(route, 'params.pageIndex', 1);
Expand All @@ -105,16 +103,23 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator})

const CameraComponent = isInTabNavigator ? TabNavigationAwareCamera : NavigationAwareCamera;

// We want to listen to if the app has come back from background and refresh the permissions status to show camera when permissions were granted
useEffect(() => {
const subscription = AppState.addEventListener('change', (nextAppState) => {
if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
CameraPermission.getCameraPermissionStatus().then((permissionStatus) => {
setPermissions(permissionStatus);
});
const refreshCameraPermissionStatus = () => {
CameraPermission.getCameraPermissionStatus()
.then(setCameraPermissionStatus)
.catch(() => setCameraPermissionStatus(RESULTS.UNAVAILABLE));
};

// Check initial camera permission status
refreshCameraPermissionStatus();

// Refresh permission status when app gain focus
const subscription = AppState.addEventListener('change', (appState) => {
if (appState !== 'active') {
return;
}

appState.current = nextAppState;
refreshCameraPermissionStatus();
});

return () => {
Expand Down Expand Up @@ -154,14 +159,17 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator})
const askForPermissions = () => {
// There's no way we can check for the BLOCKED status without requesting the permission first
// https://github.com/zoontek/react-native-permissions/blob/a836e114ce3a180b2b23916292c79841a267d828/README.md?plain=1#L670
if (permissions === RESULTS.BLOCKED || isAndroidBlockedPermissionRef.current) {
Linking.openSettings();
} else if (permissions === RESULTS.DENIED) {
CameraPermission.requestCameraPermission().then((permissionStatus) => {
setPermissions(permissionStatus);
isAndroidBlockedPermissionRef.current = permissionStatus === RESULTS.BLOCKED;
CameraPermission.requestCameraPermission()
.then((status) => {
setCameraPermissionStatus(status);

if (status === RESULTS.BLOCKED) {
showPermissionsAlert();
}
})
.catch(() => {
setCameraPermissionStatus(RESULTS.UNAVAILABLE);
});
}
};

/**
Expand Down Expand Up @@ -230,13 +238,14 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator})
});
}, [flash, iouType, iou, report, translate, transactionID, route.path]);

CameraPermission.getCameraPermissionStatus().then((permissionStatus) => {
setPermissions(permissionStatus);
});
// Wait for camera permission status to render
if (cameraPermissionStatus == null) {
return null;
}

return (
<View style={styles.flex1}>
{permissions !== RESULTS.GRANTED && (
{cameraPermissionStatus !== RESULTS.GRANTED && (
<View style={[styles.cameraView, styles.permissionView]}>
<Hand
width={CONST.RECEIPT.HAND_ICON_WIDTH}
Expand All @@ -255,7 +264,7 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator})
/>
</View>
)}
{permissions === RESULTS.GRANTED && device == null && (
{cameraPermissionStatus === RESULTS.GRANTED && device == null && (
<View style={[styles.cameraView]}>
<ActivityIndicator
size={CONST.ACTIVITY_INDICATOR_SIZE.LARGE}
Expand All @@ -264,7 +273,7 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator})
/>
</View>
)}
{permissions === RESULTS.GRANTED && device != null && (
{cameraPermissionStatus === RESULTS.GRANTED && device != null && (
<CameraComponent
ref={camera}
device={device}
Expand Down Expand Up @@ -322,7 +331,7 @@ function ReceiptSelector({route, report, iou, transactionID, isInTabNavigator})
accessibilityRole={CONST.ACCESSIBILITY_ROLE.BUTTON}
accessibilityLabel={translate('receipt.flash')}
style={[styles.alignItemsEnd]}
disabled={permissions !== RESULTS.GRANTED}
disabled={cameraPermissionStatus !== RESULTS.GRANTED}
onPress={() => setFlash((prevFlash) => !prevFlash)}
>
<Icon
Expand Down
Loading