I have read and tested a lot of issues but I still can not get geolocation on Android.
I use navigator.geolocation.getCurrentPosition, on iOS everything works fine, but on Android I have no answer to this function, either success or error.
I have installed react-native-permissions to make sure that the user has activated the permissions but it does not change anything because it says that everything is "authorized".
I noticed that it came from GPS of the device. If I activate it manually, everyting works fine. However, I can not find a way to activate it for the user. On iOS, if GPS is not activated, I fall in the error callback and tell user to activate it, but on android, nothing is happennig.
I don't understand why I can't get geolocation only with getCurrentPosition (I have ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION in manifest).
Here a part of my code:
componentDidMount() {
navigator.geolocation.getCurrentPosition(
(position) => {
//do my stuff with position value
},
(error) => {
Permissions.getPermissionStatus('location')
.then((response) => {
if (response !== "authorized") {
Alert.alert(
"Error",
"We need to access to your location",
[
{
text: "Cancel",
onPress: () => {
// do my stuff
}, style: 'cancel'
},
{
text: "Open Settings",
onPress: () => Permissions.openSettings()
}
]
);
} else {
// do my stuff
}
});
},
{ enableHighAccuracy: true, timeout: 2000, maximumAge: 1000 }
);
}
Does anyone have any idea ?
Thank you
You should need to enable GPS on android
For enabling location/gps on Android I can recommend this module:
https://github.com/Richou/react-native-android-location-enabler
It is using the standard Android dialog for location:
like this
import React, { Component } from "react";
import { Text, StyleSheet, View, Platform } from "react-native";
import RNAndroidLocationEnabler from "react-native-android-location-enabler";
export default class index extends Component {
componentDidMount() {
this.getLocation();
}
onLocationEnablePressed = () => {
if (Platform.OS === "android") {
RNAndroidLocationEnabler.promptForEnableLocationIfNeeded({
interval: 10000,
fastInterval: 5000,
})
.then((data) => {
this.getLocation();
})
.catch((err) => {
alert("Error " + err.message + ", Code : " + err.code);
});
}
};
getLocation = () => {
try {
navigator.geolocation.getCurrentPosition(
(position) => {
//do my stuff with position value
},
(error) => {
Permissions.getPermissionStatus("location").then((response) => {
if (response !== "authorized") {
Alert.alert("Error", "We need to access to your location", [
{
text: "Cancel",
onPress: () => {
// do my stuff
},
style: "cancel",
},
{
text: "Open Settings",
onPress: () => Permissions.openSettings(),
},
]);
} else {
// do my stuff
}
});
},
{ enableHighAccuracy: true, timeout: 2000, maximumAge: 1000 }
);
} catch (error) {
this.onLocationEnablePressed();
}
};
}
Related
I run a React Native app on Android emulator but found networking does not work, I run it on iOS it works fine.
Here is the simple code:
import React from 'react';
import {StyleSheet, View, Text} from 'react-native';
export default () => {
React.useEffect(() => {
const useFetch = async () => {
try {
console.log('fetch ...');
let response = await fetch('https://mytestdomain.com');
console.log(response.status);
// let json = await response.json();
} catch (error) {
console.log(error);
}
};
useFetch();
const useXMLHttpRequest = async () => {
try {
var request = new XMLHttpRequest();
request.onreadystatechange = (e) => {
if (request.readyState !== 4) {
console.log(request.readyState);
return;
}
if (request.status === 200) {
console.log('success', request.responseText);
} else {
console.warn('error');
}
};
request.open('GET', 'https://mytestdomain.com');
request.send();
} catch (error) {
console.log(error);
}
};
useXMLHttpRequest();
}, []);
return (
<View style={styles.layout}>
<Text>React Native Android networking</Text>
</View>
);
};
const styles = StyleSheet.create({
layout: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
By call useFetch() I just see 'fetch ...' in console, even can not see response.status, and there is no error from catch.
By call useXMLHttpRequest() I see request.readyState is 1.
Thanks advance for any help
You probably should give this asynchronous log a more explicit message to find this specific one among other logs, like :
console.log("RS : " + response.status);
I tried to reproduce your issue, and I can't, it works on my Android simulator just fine.
Would you please share your console logs ?
A workaround is to use the #react-native-community/netinfo library to determine whether you have internet access or not.
Beware, it could be slow.
This library provides a listener with the connection details changes. Getting the connection details can be slow... you would have to test it on real devices to determine whether this workaround is efficient or not.
Quick Demo based on your source code :
[...]
import { StyleSheet, View, Text } from "react-native";
+import NetInfo from "#react-native-community/netinfo";
export default () => {
- React.useEffect(() => {
+ NetInfo.addEventListener((netInfo) => {
const useFetch = async () => {
try {
- console.log("fetch ...");
- let response = await fetch("https://mytestdomain.com");
- console.log(response.status);
- // let json = await response.json();
+ // Just because there is a connection, it does not mean that internet is accessible, so test both isConnected and isInternetReachable
+ if (netInfo.isConnected && netInfo.isInternetReachable) {
+ console.log("fetch ...");
+ let response = await fetch(
+ "https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch"
+ );
+ console.log("start 1");
+ console.log("success 1", response.status);
+ console.log("stop 1");
+ } else {
+ console.log("internet is not ready");
+ }
} catch (error) {
[...]
In my react native application, in home screen there is a button to open a compass that shows the direction of a specific location. For that I need the coordinates of the user to be passed to the compass screen to make the calculations, m passing the values in the navigator.
Iam loading the coordinates on componentDidMount() method of home screen, my problem is that getting the coordinates of the user sometimes takes a bit of time (depending on the user's gps signal strength and his/her device), so I used conditional render to show a "loading" component if the user presses on compass button before coordinates are loaded. But the problem is that m not knowing how to send him/her to compass screen after the loader, because right now after the loader he/she stays in home screen, and has to press the button again to go to the compass.
state = {
currentLongitude: "unknown",
currentLatitude: "unknown",
locationLoading: false,
};
getCards = () => [...
{id: "3",
card: this.languageCard("Compass"),
onPress: () => {
this.state.currentLatitude != "unknown"
? this.props.navigation.navigate("Compass", {
latitude: this.state.currentLatitude,
longitude: this.state.currentLongitude,
})
: this.setState({ locationLoading: true });
},
}...]
componentDidMount() {
this.requestLocation();
}
requestLocation() {
var that = this;
if (Platform.OS === "ios") {
this.callLocation(that);
} else {
async function requestLocationPermission() {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: "Location Access Required",
message: "This App needs to Access your location",
}
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
that.callLocation(that);
} else {
alert("Permission Denied");
}
} catch (err) {
alert("err", err);
console.warn(err);
}
}
requestLocationPermission();
}
}
callLocation(that) {
Geolocation.getCurrentPosition(
(position) => {
const currentLongitude = JSON.stringify(position.coords.longitude);
const currentLatitude = JSON.stringify(position.coords.latitude);
that.setState({ currentLongitude: currentLongitude });
that.setState({ currentLatitude: currentLatitude });
this.setState({ locationLoading: false });
},
(error) => alert(error.message),
{ enableHighAccuracy: false, timeout: 20000, maximumAge: 1000 }
);
}
render() {
return this.state.locationLoading ? (
<Loader />
) : (
<SafeAreaView>
....
</SafeAreaView>
I am trying to get the user location my code is working fine in the
ios and in the android it does not throw the error when user denies
the location permission.
AppState.addEventListener('change', this._handleAppStateChange);
this.geoFailure, geoOptions);
this.refresh();
}
geoSuccess = (position) => { //Success callback when user allow the
location
this.setState({
ready:true,
where: {lat:
position.coords.latitude,lng:position.coords.longitude }
})
}
geoFailure = (err) => { // i am not getting any error when user
denies the location permission in the
case of android.
this.setState({error: err.message});
console.log("Errror",err)
console.log(err.message)
if(err.message='User denied access to location services'&&Platform.OS==='ios'){
this.props.screenName==='SPLASH'&&!this.state.ready?NavigatorService.navigate(LOCATION_PERMISSION):null;
}
}
refresh=()=> // Refreshing the list when the AppState becomes
active
let geoOptions = {
enableHighAccuracy: false,
timeout: 30000,
maximumAge: 60 * 60 * 24
};
this.setState({ready:false, error: null });
navigator.geolocation.getCurrentPosition( this.geoSuccess, this.geoFailure, geoOptions);
}
I am able to navigate the user to another screen in the case of ios
if it doesn't provide the location permissions but in the case of
the android it does not giving any error when user does not provide
the location.
I am not getting how to do it , i am new to the react native.
I am not getting, what i am doing wrong.
Any help would be appreciated.
use PermissionsAndroid
import {
PermissionsAndroid
} from 'react-native';
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
Geolocation.getCurrentPosition(
(position) => {
addLocation(position.coords);
},
(error) => {
console.error(error);
},
{ enableHighAccuracy: true, timeout: 15000, maximumAge: 10000 },
);
} else {
console.log('location permission denied');
}
I am quite new to React / React Native / Redux so I feel I am doing something wrong.
The problem
I want to show a spinner while an API is called, and an error message once this API call fails. Props are not updating, and so the components don't show the desired message or spinner
The code (only the relevant chunks)
The component
class Home extends Component {
componentWillMount() {
this.props.tokenGet();
}
renderSpinner() {
if (this.props.loading) {
return (
<Spinner size="large" />
);
}
return null;
}
renderMessage() {
if (this.props.message) {
return (
<Text style={{flex: 1, background: red, color: black}}>
{ this.props.message }
</Text>
)
}
return null;
}
render() {
return (
{ this.renderSpinner() }
{ this.renderMessage() }
)
}
}
const mapStateToProps = (state) => {
const { auth } = state;
const {
loading,
token,
message
} = auth || {
loading: false,
token: null,
message: null
};
return {
loading,
token,
message
}
};
export default connect(mapStateToProps, { tokenGet } )(Home);
The action creator
export const tokenGet = () => {
return (dispatch) => {
dispatch({ type: 'TOKEN_GET_START'});
// Perform the actual API call
let requestToken = fetch(apiBaseUrl + "/tokens", {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(.....)
});
Promise
.race([timeout, requestToken])
.then((response) => response.json())
.then((responseJson) => {
... not relevant ...
})
.catch((error) => {
dispatch({ type: 'TOKEN_GET_FAIL', payload: error});
});
The timeout function, which gets called when the server fails to respond
let timeout = new Promise((resolve, reject) => {
setTimeout(reject, 2000, 'Request timed out. Please check your internet connection.');
});
The reducer
import {
TOKEN_GET_START,
TOKEN_GET_SUCCESS,
TOKEN_GET_FAIL
} from '../actions/types';
const INITIAL_STATE = {
loading: false,
token: null,
message: null
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case TOKEN_GET_START:
return { ...state, loading: true };
case TOKEN_GET_SUCCESS:
return { ...state, loading: false, token: action.payload };
case TOKEN_GET_FAIL:
return { ...state, loading: false, message: action.payload };
default:
return state;
}
};
The combined reducers
import { combineReducers } from 'redux';
import AuthReducer from './AuthReducer';
export default combineReducers({
auth: AuthReducer
});
The actual behavior is that the props don't change and no message or spinner is visible. With some console logs I know that the API call ends because of the timeout. I am not sure if the state gets updated properly though. I don't know in at which point I can console log this.
It turned out to be because of the quotes in 'TOKEN_GET_FAIL'
That is a string and not the const I need. So I changed to TOKEN_GET_FAIL and it works.
I followed this tutorial http://www.mobiledevelopersolutions.com/home/start/twominutetutorials/tmt4part1 and i have one problem. The geolocation doesn't work on the default Android browser. It does work on Chrome, IE and Chrome for Android. But not the default Android browser.
I think i have to put { enableHighAccuracy: true } somewhere put i can't get it figured out.
This is the code:
var mapdata = { destination: new google.maps.LatLng(51.3704888, 6.1723862) };
// Home page
$('#page-home').live("pageinit", function() {
$('#map_square').gmap(
{ 'center' : mapdata.destination,
'zoom' : 12,
'mapTypeControl' : false,
'navigationControl' : false,
'streetViewControl' : false
})
.bind('init', function(evt, map) {
$('#map_square').gmap('addMarker',
{ 'position': map.getCenter(),
'animation' : google.maps.Animation.DROP
});
});
$('#map_square').click( function() {
$.mobile.changePage($('#page-map'), {});
});
});
function fadingMsg (locMsg) {
$("<div class='ui-overlay-shadow ui-body-e ui-corner-all fading-msg'>" + locMsg + "</div>")
.css({ "display": "block", "opacity": 0.9, "top": $(window).scrollTop() + 100 })
.appendTo( $.mobile.pageContainer )
.delay( 2200 )
.fadeOut( 1000, function(){
$(this).remove();
});
}
//Create the map then make 'displayDirections' request
$('#page-map').live("pageinit", function() {
$('#map_canvas').gmap({'center' : mapdata.destination,
'mapTypeControl' : true,
'navigationControl' : true,
'navigationControlOptions' : {'position':google.maps.ControlPosition.LEFT_TOP}
})
.bind('init', function() {
$('.refresh').trigger('tap');
});
});
$('#page-map').live("pageshow", function() {
$('#map_canvas').gmap('refresh');
});
// Request display of directions, requires jquery.ui.map.services.js
var toggleval = true; // used for test case: static locations
$('.refresh').live("tap", function() {
// START: Tracking location with device geolocation
if ( navigator.geolocation ) {
fadingMsg('Using device geolocation to get current position.');
navigator.geolocation.getCurrentPosition (
function(position ) {
$('#map_canvas').gmap('displayDirections',
{ 'origin' : new google.maps.LatLng(position.coords.latitude, position.coords.longitude),
'destination' : mapdata.destination, 'travelMode' : google.maps.DirectionsTravelMode.DRIVING},
{ 'panel' : document.getElementById('dir_panel')},
function (result, status) {
if (status === 'OK') {
var center = result.routes[0].bounds.getCenter();
$('#map_canvas').gmap('option', 'center', center);
$('#map_canvas').gmap('refresh')
} else {
alert('Unable to get route');
}
}
);
},
function(){
alert('Unable to get location');
$.mobile.changePage($('#page-home'), { });
});
} else {
alert('Unable to get location.');
}
// END: Tracking location with device geolocation
$(this).removeClass($.mobile.activeBtnClass);
return false;
});
// Go to map page to see instruction detail (zoom) on map page
$('#dir_panel').live("tap", function() {
$.mobile.changePage($('#page-map'), {});
});
// Briefly show hint on using instruction tap/zoom
$('#page-dir').live("pageshow", function() {
fadingMsg("Tap any instruction<br/>to see details on map");
});
Thx for the help!
This is how you may need to call.
navigator.geolocation.getCurrentPosition(successCallback,
errorCallback,
{maximumAge:Infinity, timeout:0, enableHighAccuracy: true });
Ofcourse here you can change maximumAge and timeout values, but this is where you set enableHighAccuracy.
So just specify this as third param in your getcurrentposition method.
EDIT :
navigator.geolocation.getCurrentPosition (
function(position ) {
$('#map_canvas').gmap('displayDirections',
{ 'origin' : new google.maps.LatLng(position.coords.latitude, position.coords.longitude),
'destination' : mapdata.destination, 'travelMode' : google.maps.DirectionsTravelMode.DRIVING},
{ 'panel' : document.getElementById('dir_panel')},
function (result, status) {
if (status === 'OK') {
var center = result.routes[0].bounds.getCenter();
$('#map_canvas').gmap('option', 'center', center);
$('#map_canvas').gmap('refresh')
} else {
alert('Unable to get route');
}
}
);
},
function(){
alert('Unable to get location');
$.mobile.changePage($('#page-home'), { });
},
{ enableHighAccuracy: true } );
Since you want to use geolocation, have you set the sensor to true? Because if you set it false, it won't work.
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=true"></script>