Appstate keep on getting change in React native in Android - android

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

Related

Recursive method is calling again and again

const callAFunction = () => {
if (AppState.currentState === 'background') {
function1()
}
}
useEffect(()=>{
AppState.addEventListener('change', callAFunction);
},[])
const function1 = () => {
axios.get('/user_login', {
params: {
username: 'john1904',
}
})
.then(function (response) {
if (response.data.status === false) {
function1()
}
})
}
I am using this above function recursively. But as the app goes background function1 is calling again and again as still the function1() i have already called. So i want that function1() call every time as the app goes background. But in Async form as if function1() then it will not call it again.
So i am not able to get how can i do this in when app is in background so it will check if this function is running then don't run it other wise run it.
Right now, if status is false, the only time between requests is the time it takes Axios to call the endpoint. This can be very little time (like a few milliseconds). If you want to poll until you get a status of true, set a timeout for the request.
// ...
.then(function (response) {
if (response.data.status === false) {
setTimeout(function1, 1000);
}
})
The above example will take 1 second between requests. You can adjust 1000 to suit your needs.
See also this question for a common issue with timeouts and React components: Can't perform a React state update on an unmounted component

When I ask for Location permissions in Android (using React Native), permission dialog never pops up and permissions are granted automatically

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.

React Native: Active state in AppState listener getting triggered when component mounts

I have set up a listener for AppState in React Native to know when my app switches from background to foreground so I can show a splash screen.
useEffect(() => {
AppState.addEventListener('change', handleAppStateChange);
},[])
const handleAppStateChange = newState => {
if (newState === 'active') {
RNBootSplash.show();
}
};
The problem is, and this only happens when I compile a release APK, for some reason it works fine in debug, the splash screen is showing each time the component mounts, while it should only trigger when the app is switched into the active state from background. Any help?
Using React Native 61.5
Try this way :
const [appState, setAppState] = React.useState(AppState.currentState)
useEffect(() => {
AppState.addEventListener('change', handleAppStateChange)
setAppState(AppState.currentState)
return AppState.removeEventListener('change')
},[])
const handleAppStateChange = newState => {
if (appState.match(/inactive|background/) && newState === 'active') {
console.log('App has come to the foreground!')
// Show the splash screen
RNBootSplash.show()
} else {
console.log('App has gone to the background!')
// do something in background
}
setAppState(newState)
}
Only thing that worked was using refs
const latestAppState = React.useRef(AppState.currentState)
const handleAppStateChange = newState => {
if (latestAppState.current.match(/inactive|background/) && newState === 'active') {
RNBootSplash.show();
}
latestAppState.current = newState
}

How to avoid "Sending 'geolocationDidChange' with no listeners registered" warning in react native?

I am going to get current location in react native.
I was used below code:
if(Platform.OS == "android") {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
'title': 'Location Permission',
'message': 'Wichz would like to access your location to get your things.'
}
)
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
// console.warn("You can use locations ")
} else {
// console.warn("Location permission denied")
}
} catch (err) {
// console.warn(err)
}
}
this.watchID = navigator.geolocation.watchPosition((position) => {
this.setState({
selectedLat: position.coords.latitude,
selectedLng: position.coords.longitude
});
Global.selectedLat = position.coords.latitude;
Global.selectedLng = position.coords.longitude;
this.getcurrent_address(position.coords.latitude, position.coords.longitude);
}, (error)=>console.log(error.message),
{enableHighAccuracy: false, timeout: 3, maximumAge: 1, distanceFilter: 1}
);
And add Location usage description like below in ios project:
It works well on both ios and android, but I have get warning box like below:
How can avoid this warning box? Thanks
It's probably too late now but I'll just detail my experience with this warning for those people in the future that have this same issue.
I encountered this error myself before. For me the cause of this warning is basically that the component is re-rendered before geolocation watchPosition call is fully completed.
In my case, it was because I had multiple fetch calls that caused the component to be re-rendered in quick succession. I fixed it by making geolocation watchPosition only be called on the final render of the component.

how can i access current location from web-page in react native webview

I have an app which developed in react-js. I want to integrate this app into react-native-webview. everything I right but I have two problems.
1-how can I access current location of the user in react-native-webview.
2-how can I debug my react-js(webpage) app inside react-native-webview.
for first problem
if I run webpage in a mobile browser and click on that event I'm getting current location of the user and at the same time when my react-native app getting started, I'm invoking permission for current location so in react-native-debugger i'm getting current location of user.i followed the similar example but this is related to react-native.
exact problem is how can I access the current location of webview in react-native-webview
for second problem
when I'm clicking on the event to fetch current location it is not showing the current location so ** I want to see what is error/response of that event**. because it is in webview I can access react-native logs in react-native-debugger but cannot see the web view logs in the debugger.I followed this one also but I don't know where to put that android config code.
my react-native code
import React, {Component} from 'react';
import {PermissionsAndroid} from 'react-native';
import { WebView } from "react-native-webview";
export default class App extends Component {
constructor(props){
super(props);
// To see all the requests in the chrome Dev tools in the network tab.
XMLHttpRequest = GLOBAL.originalXMLHttpRequest ?
GLOBAL.originalXMLHttpRequest :
GLOBAL.XMLHttpRequest;
// fetch logger
global._fetch = fetch;
global.fetch = function (uri, options, ...args) {
return global._fetch(uri, options, ...args).then((response) => {
console.log('Fetch', { request: { uri, options, ...args }, response });
return response;
});
};
}
async requestLocationPermission() {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
'title': 'Location Access Permission',
'message': 'Stanplus would like to use your location ' +
'so you we track you.'
}
)
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log("You can use the location");
if (typeof window !== 'undefined' && typeof window.navigator !== 'undefined' && navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
console.log(position.coords.latitude,'success');
},
(error) => {
console.log(error,'fail')
},
{ enableHighAccuracy: false, timeout: 5000, maximumAge: 10000 }
);
}
} else {
console.log("Location permission denied")
}
} catch (err) {
console.warn(err)
}
}
componentDidMount() {
this.requestLocationPermission();
}
render() {
return (
<WebView
source={{uri: 'https://3fa4f958.ngrok.io/steptwo'}}
style={{marginTop: 20}}
onMessage={(event)=> console.log(event.nativeEvent.data)}
scalesPageToFit={true}
javaScriptEnabled = {true}
/>
);
}
}
my React.js code which accesses current location
if (navigator.geolocation) {
alert('event clicked');//in react-native app its showing the alert
navigator.geolocation.getCurrentPosition(
//but inside here react-native can't execute because of location permission issue
position => {
let latlng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
let geocoder = new window.google.maps.Geocoder();
//do other stuff----------
}
)
}
** I want to fetch current location when user click on webpage event inside react-native app and how to debug webview code**
please help stuck since 2 days ):
A few steps are needed to get it working on Android.
Add this line to your AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
And then in your app you'll need to request location once before the webview can request it. Do it like so in your view:
import { PermissionsAndroid } from 'react-native';
...
PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'Location Access Permission',
message: 'We would like to use your location',
buttonPositive: 'Okay'
}
);
And then also on your webview add the property:
geolocationEnabled={true}
you need to add geolocationenabled https://facebook.github.io/react-native/docs/webview#geolocationenabled

Categories

Resources