I am requesting multiple permissions. All are working fine except receive sms where the result is always 'never_ask_again'.
The following is the code I am trying:
_getPermissions = async () => {
try {
const resuts = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
PermissionsAndroid.PERMISSIONS.RECEIVE_SMS,
PermissionsAndroid.PERMISSIONS.READ_PHONE_STATE
]);
if (
resuts[PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION] !==
PermissionsAndroid.RESULTS.GRANTED
) {
alert("Permission not granted. The application may not work properly");
}
if (
resuts[PermissionsAndroid.PERMISSIONS.READ_PHONE_STATE] !==
PermissionsAndroid.RESULTS.GRANTED
) {
alert("Permission not granted. The application may not work properly");
}
if (
resuts[PermissionsAndroid.PERMISSIONS.RECEIVE_SMS] !==
PermissionsAndroid.RESULTS.GRANTED
) {
alert("Permission not granted. The application may not work properly");
}
} catch (err) {
console.warn(err);
}
};
Fix this line as opposite condition:
if (
resuts[PermissionsAndroid.PERMISSIONS.RECEIVE_SMS] ===
PermissionsAndroid.RESULTS.DENIED
) {
Alert.alert("Permission not granted. The application may not work properly");
}
Related
On https://reactnative.dev/docs/0.70/permissionsandroid it shows that this permission is available for verion 0.70:
POST_NOTIFICATION: 'android.permission.POST_NOTIFICATION'
On https://reactnative.dev/docs/permissionsandroid (version 0.71) it shows that the same persmission is then with an "S" at the end:
POST_NOTIFICATIONS: 'android.permission.POST_NOTIFICATIONS'
Is this a typo?
In either case, if I use any of the above, the app crashes with
IllegalArgumentException: permission is null
at android.app.ContextImpl.checkSelfPermission(ContextImpl.java:2222)
at android.content.ContextWrapper.checkSelfPermission(ContextWrapper.java:931)
at com.facebook.react.modules.permissions.PermissionsModule.requestPermission(PermissionsModule.java:105)
at java.lang.reflect.Method.invoke(Method.java)
when doing this:
async requestPermission() {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATION, // or POST_NOTIFICATIONS
{
'title': 'TEST',
'message': I18n.t('permissions.locationPermissionMessage')
}
)
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
} else {
}
} catch (err) {
}
}
The permission is in the AndroidManifest file, confirmed.
UPDATE
Set compileSdkVersion = 33 and targetSdkVersion = 33 in build.gradle:
code for requestPermission not looks like (I am using 0.70.5):
and the crash is noted like so:
Based on the source code, yes it's a typo that is fixed in 0.71.* and also see this pull request to know more details.
But about the crash, doesn't relate to typo in constants. First make sure to use compile sdk version 33. And get this permission for androids equal or greater than 13(API_LEVEL 33)
In order to check ANDROID_API_LEVEL in js side use DeviceInfo.
async requestPermission() {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATION, // or POST_NOTIFICATIONS
{
'title': 'TEST',
'message': I18n.t('permissions.locationPermissionMessage')
}
)
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
} else {
}
} catch (err) {
}
}
// where you need to get permission
if (Platform.OS == 'android' && DeviceInfo.getApiLevelSync() >= 33) {
await requestPermission();
}
I am asking for location permission for my Android React Native app. I am using the npm package: react-native-permissions. I have created a custom hook to do this.
My implementation for iOS works perfect. While trying a similar approach on Android, the dialog that asks the user for location permission never pops up. On my initial check for the permission, my app reports that permission is already granted!! But how?
I include this in my AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
and this is my hook that asks for permission:
export default (setPermissionsGranted = () => {}, setPermissionError = () => {}) => {
const appState = useRef(AppState.currentState)
const [shouldCheckPermission, setShoudCheckPermission] = useState(false)
useEffect(() => {
let isMounted = true
const handleAppState = (nextAppState) => {
if (appState.current.match(/inactive|background/) && nextAppState === 'active') {
// the app has come into the foreground. Change a boolean state variable to trigger
// handlePermission() again.
if (isMounted) setShoudCheckPermission(!shouldCheckPermission)
}
appState.current = nextAppState
}
const handlePermissionStatus = (result) => {
const status = {
unavailable: () => {},
denied: async () => {
const res = await request(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION)
if (isMounted) {
if (res === RESULTS.GRANTED) {
setPermissionsGranted(true)
} else {
setPermissionsGranted(false)
}
}
},
limited: () => {
// todo find out what limited entails
if (isMounted) setPermissionsGranted(true)
},
granted: async () => {
if (isMounted) setPermissionsGranted(true)
},
blocked: () => {
if (isMounted) setPermissionsGranted(false)
},
}
return status[result]
}
AppState.addEventListener('change', handleAppState)
const handlePermission = async () => {
console.log('permissions code is run')
try {
const res = await check(PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION)
console.log('res', res)
handlePermissionStatus(res)()
} catch (err) {
throw err
}
}
handlePermission().catch((e) => {
if (isMounted) setPermissionError(e.message)
})
return () => {
AppState.removeEventListener('change', handleAppState)
isMounted = false
}
}, [shouldCheckPermission])
}
Any explanation as to why the user never get's asked and the permission is automatically granted?
Another peculiarity, I have commented out all of the code that requests permission for Location on Android, restarted the metro server, uninstalled the app and then re-installed it. The request-permissions tag is still in the AndroidManifest.xml. Apparently that's all I needed to do and now permissions are AUTOMATICALLY granted!
My understanding is that this is a dangerous permission and should not be granted automatically but Android is treating it as a safe-permission. 🤷🏻♂️
Thanks in advance.
In my react native app, I ask for permission to access the camera roll as so:
getPermissionAsync = async () => {
const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if (status !== "granted") {
alert("Sorry, we need camera roll permissions to make this work!");
}
await this.setState({ permission: status === "granted" });
};
_pickMedia = async (index, type) => {
if (this.state.permission != true) {
await this.getPermissionAsync();
}
if (this.state.permission == true) {
// get image
} catch (E) {
console.log(E);
}
}
};
This works as expected during testing, but for my published version on Google Play, status is returned as undetermined whether or not the user gives permission. What does this mean?
Upgrading the react-native-community#react-native-permissions to 2.0.0 solves the issue.
Refer : https://github.com/react-native-community/react-native-permissions/issues/295
I am working on React native project and there I am taking location permissions. Also I have to track location permissions always like if user has given permission access after install the application and then after sometime user goes to the app settings in device settings and disable/revoked the permissions. Again once app comes from background to foreground, I have to check permission based on that, Needs to show the messages.
So that, I am using Appstate. But, In Android strangely, After installed the application, If user denied the permission with "Dont show again" checkbox, Then Appstate getting keep on changing with background and active always.
It is keep on loop.
componentDidMount = async () => {
AppState.addEventListener('change', this.handleAppStateChange);
};
componentWillUnmount() {
AppState.removeEventListener('change', this.handleAppStateChange);
Geolocation.clearWatch(this.watchID);
}
handleAppStateChange = async nextAppState => {
const {appState} = this.state;
console.log('nextAppState -->', nextAppState);
console.log('appState -->', appState);
if (appState === 'active') {
// do this
this.showLoader();
await this.requestAndroidLocationPermission();
} else if (appState === 'background') {
// do that
} else if (appState === 'inactive') {
// do that other thing
}
this.setState({appState: nextAppState});
};
requestAndroidLocationPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
this.getLatitudeLongitude();
} else if (granted === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
this.hideLoader();
this.setState({
errorMessage: 'Location permission is denied',
isLoading: false,
});
} else {
this.hideLoader();
this.requestAndroidLocationPermission();
}
} catch (err) {
console.warn(err);
}
};
It is keep on printing (loop) after denied permission with Don't show again
appState --> active
nextAppState --> background
appState --> active
nextAppState --> background
appState --> active
nextAppState --> background
appState --> active
It goes on and never stop.
How to handle this? Any suggestions?
I had the same problem. Do not use AppState. Is faulty.
the problem lies within RN's definition of "background". react-native uses android's activity (the holder of the UI thread and where your UI lives) onPause callback as the trigger for sending the "background" signal. But, onPause is called everytime SOMETHING comes in front of your activity's view hierachy, like Dialogs (like the permission box), other activities (like a file picker), etc; for android react-native, "background" means "shadowed by a foreign UI element/android task" rather than "paused and sent to background to do something else", thus causing the loops you see. The shortest solution is to override onPause in your ReactActivity, and add control conditions to make sure super.onPause is only called when you are actually going to background, like checking your task stack, or if the permission dialog is being called, so you avoid this kind of loop/faulty call. A second option would be to provide your own app lifecycle event instead, with clear triggering conditions.
today I had a similar problem.
I could solve it using "focus" in android and "change" in ios.
I have a custom hook like this:
import { useEffect } from 'react';
import { AppState, Platform } from 'react-native';
const focusEvent = Platform.OS === 'ios' ? 'focus' : 'change';
const useLocationListener = () => {
useEffect(() => {
AppState.addEventListener(focusEvent, handleAppStateChange);
getLocationAsync();
return () => {
AppState.removeEventListener(focusEvent, handleAppStateChange);
};
}, []);
const handleAppStateChange = (nextAppState: string) => {
if (nextAppState === 'active') {
getLocationAsync();
}
};
const getLocationAsync = async () => {
const { canAskAgain, status } = await Permissions.getAsync(
Permissions.LOCATION
);
if (canAskAgain) {
const response = await Permissions.askAsync(Permissions.LOCATION);
// handle location
}
// handle location with "status"
};
};
export default useLocationListener;
You can use a flag that check whether app should handle background or it's just a permission call.
const shouldHandleBackground = useRef(true)
const handler = (state) => {
if (state === 'active' && shouldHandleBackground.current) {
doStuff()
}
}
// when calling for permisson make the flag false
shouldHandleBackground.current = false
await Android.permission.LocationPermission()
shouldHandleBackground.current = true
and after permission request you can make flag true
Using https://github.com/MacKentoch/react-native-beacons-manager
Works lovely on iOS, however, on Android, after I begin ranging beacons, the beacon array shows up with nothing in it (there are 6 beacons next to me and they all show up on iOS).
Here's what I'm doing:
componentDidMount() {
// Start detecting all iBeacons in the nearby
Beacons.detectIBeacons();
Beacons.startRangingBeaconsInRegion('Estimotes', 'B9407F30-F5F8-466E-AFF9-25556B57FE6D').then((data)=>{
console.log(data);
}).catch((reason) => {
console.log(reason);
});
// Print a log of the detected iBeacons (1 per second)
DeviceEventEmitter.addListener('beaconsDidRange', (data) => {
console.log(data);
});
}
In my console, I get this:
{beacons: Array(0), uuid: "b9407f30-f5f8-466e-aff9-25556b57fe6d", identifier: "Estimotes"}
I left the UUID of the Estimotes as default so this should work. Using a Samsung Galaxy S8+ for testing. Am I doing anything wrong coding wise here? Are there additional permissions on Android that I am missing? Bluetooth and Location services are on.
Alright, I figured it out. Newer versions of android require additional permissions. In your Manifest, throw this guy in there:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
.... if you're using react-native-kontaktio (which is better than react-native-beacons-manager imo) you'll also need to throw this in your Manifest in the <application> section:
<service android:name="com.kontakt.sdk.android.ble.service.ProximityService"/>
Then in your app.js you'll need to request the permission like () make sure you
import PermissionsAndroid
from 'react-native'
:
componentDidMount() {
try {
const granted = PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
'title': 'Location Permission',
'message': 'Activeev needs to access your location.'
}
)
console.log('here', granted);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log("Location Permitted")
} else {
console.log("Location permission denied")
}
} catch (err) {
console.warn(err)
}
}
Working like a charm now. Hope this helps someone else.
Thank you for your answer. It definitely worked. Based on your answer, below is my implementation.
import React, { Component } from 'react';
import { View, DeviceEventEmitter, ListView , Text} from 'react-native';
import Beacons from 'react-native-beacons-manager';
import {PermissionsAndroid} from 'react-native'
export default class App extends Component {
async componentDidMount() {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
'title': 'Location Permission',
'message': 'Activeev needs to access your location.'
}
)
console.log('here', granted);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log("Location Permitted")
// Start detecting all iBeacons in the nearby
Beacons.detectIBeacons();
Beacons.startRangingBeaconsInRegion('test', '85d37dd8-a9dc-48a8-ab1c-b86fcb7a6a17').then((data)=>{
console.log(data);
})
.catch((reason) => {
console.log(reason);
});
// Print a log of the detected iBeacons (1 per second)
DeviceEventEmitter.addListener('beaconsDidRange', (data) => {
console.log(data);
});
} else {
console.log("Location permission denied")
}
}catch (err) {
console.warn(err)
}
}
render(){
return(
<View></View>
);
}
}