I am using React native maps for my app and I am using PermissionsAndroid from react-native.
For user current location tracking I am using react-native-geolocation-service package. When the app run for the first time, I want to show the Android user do they want to give the location permission. My first issue was it never ask me the location permission in Android. It automatically granted the location permission. I don't know why.
If user don't give the permission. then it will save it to the local state a string. I have created one button, if user does not give permission the user will click the button and it will navigate to the Android, settings option. In here the problem is after giving the permission from android settings, i don't know how to update my local state. as result the button is always navigate me to the Android device settings.
This is my AndroidManifest.xml set up
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="API KEY"/>
This is my all code
import React, {useState, useEffect, useRef, useCallback} from 'react';
import Geolocation from 'react-native-geolocation-service';
import {
Platform,
PermissionsAndroid,
Linking,
Alert,
Text,
View,
StyleSheet,
Button,
} from 'react-native';
import MapView from 'react-native-maps';
// Initial state
const initialState = {
latitude: 60.38879664415455,
longitude: 24.33366230104724,
latitudeDelta: 5,
longitudeDelta: 5,
};
export default function App() {
const [locationPermission, setLocationPermission] = useState('');
const [location, setLocation] = useState(initialState);
const mapRef = useRef<Map>(null);
const animateToRegion = useCallback(() => {
if (location?.longitudeDelta) {
mapRef?.current?.animateToRegion(location, 1000);
}
}, [location]);
useEffect(() => {
animateToRegion();
}, [location, animateToRegion]);
const hasPermissionIOS = async () => {
const status = await Geolocation.requestAuthorization('whenInUse');
if (status === 'granted') {
setLocationPermission('granted');
return true;
}
if (status === 'denied') {
setLocationPermission('denied');
}
return false;
};
const hasLocationPermission = async () => {
if (Platform.OS === 'ios') {
const hasPermission = await hasPermissionIOS();
return hasPermission;
}
const hasPermission = await PermissionsAndroid.check(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
);
if (hasPermission) {
return true;
}
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'this app wants to access your current location',
message:
'Cool Photo App needs access to your camera ' +
'so you can take awesome pictures.',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
setLocationPermission('granted');
console.log('You can use the location');
} else {
console.log('Camera permission denied');
setLocationPermission('denied');
}
} catch (err) {
console.warn(err);
}
};
useEffect(() => {
const hasPermission = hasLocationPermission();
if (!hasPermission) {
return;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const locationButton = () => {
if (locationPermission === 'denied') {
if (Platform.OS === 'ios') {
const openSetting = () => {
Linking.openSettings().catch(() => {
Alert.alert('Unable to open settings');
});
};
Alert.alert(
'Turn on Location Services to allow s-kaupat to determine your location.',
'',
[{text: 'Go to Settings', onPress: openSetting}, {text: 'Dismiss'}],
);
} else {
// android setting
Linking.openSettings();
}
} else {
console.log('you are good to go');
}
};
const onMapReady = useCallback(async () => {
const hasPermission = await hasLocationPermission();
if (!hasPermission) {
return;
}
Geolocation.getCurrentPosition(
position => {
const {latitude, longitude} = position.coords;
if (location) {
setLocation({
latitude,
longitude,
longitudeDelta: 0.4,
latitudeDelta: 0.4,
});
}
},
error => {
// eslint-disable-next-line no-console
console.log(error);
},
{
accuracy: {
android: 'high',
ios: 'best',
},
enableHighAccuracy: true,
timeout: 15000,
maximumAge: 10000,
distanceFilter: 0,
},
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<View style={styles.container}>
<MapView
style={styles.mapStyle}
initialRegion={initialState}
ref={mapRef}
showsUserLocation={true}
showsMyLocationButton={false}
userInterfaceStyle={'light'}
onMapReady={onMapReady}
renderToHardwareTextureAndroid={true}
/>
<Text style={styles.paragraph}>Location permission</Text>
<Button title="permission" onPress={locationButton} />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
paddingTop: '10%',
backgroundColor: '#ecf0f1',
padding: 8,
},
paragraph: {
margin: 24,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
},
mapStyle: {
height: '60%',
},
});
Related
I need to get the detailed bluetooth data of the surrounding devices when I press the button. I use the react-native-ble-plx library for this, but this library does not integrate with android 12. I made some necessary permisson adjustments for this. On manifest.xml file and app.js files. When I run the code, I get the following error about the "scanDeviceStart" method.
TypeError: Cannot read property 'startDeviceScan' of null
What would you suggest me to solve this error? my codes are below
React-Native-ble-plx = https://dotintent.github.io/react-native-ble-plx/#blemanager
import React, { useState, useEffect } from 'react';
import {
SafeAreaView,
View,
Text,
Button,
StyleSheet,
PermissionsAndroid ,
FlatList,
} from 'react-native';
import BleManager from 'react-native-ble-plx';
import { PERMISSIONS, requestMultiple } from 'react-native-permissions';
import DeviceInfo from 'react-native-device-info';
const requestPermissions = async () => {
if (Platform.OS === 'android') {
const apiLevel = await DeviceInfo.getApiLevel();
if (apiLevel < 31) {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'Location Permission',
message: 'Bluetooth Low Energy requires Location',
buttonNeutral: 'Ask Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
} else {
const result = await requestMultiple([
PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION,
]);
const isGranted =
result['android.permission.BLUETOOTH_CONNECT'] ===
PermissionsAndroid.RESULTS.GRANTED &&
result['android.permission.BLUETOOTH_SCAN'] ===
PermissionsAndroid.RESULTS.GRANTED &&
result['android.permission.ACCESS_FINE_LOCATION'] ===
PermissionsAndroid.RESULTS.GRANTED;
}
}
};
const App = () => {
const [devices, setDevices] = useState([]);
const [scanning, setScanning] = useState(false);
const [bleManager, setBleManager] = useState(null);
function requestBluetoothPermissions(cb) {
if (Platform.OS === 'android') {
DeviceInfo.getApiLevel().then((apiLevel) => {
if (apiLevel < 31) {
PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'Location Permission',
message: 'Bluetooth Low Energy requires Location',
buttonNeutral: 'Ask Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
).then((granted) => {
cb(granted === PermissionsAndroid.RESULTS.GRANTED);
});
} else {
requestMultiple([
PERMISSIONS.ANDROID.BLUETOOTH_SCAN,
PERMISSIONS.ANDROID.BLUETOOTH_CONNECT,
PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION,
]).then((result) => {
const isGranted =
result['android.permission.BLUETOOTH_CONNECT'] === PermissionsAndroid.RESULTS.GRANTED &&
result['android.permission.BLUETOOTH_SCAN'] === PermissionsAndroid.RESULTS.GRANTED &&
result['android.permission.ACCESS_FINE_LOCATION'] === PermissionsAndroid.RESULTS.GRANTED;
cb(isGranted);
});
}
});
} else {
cb(true);
}
}
useEffect(() => {
if (!bleManager) {
requestBluetoothPermissions((isGranted) => {
if (isGranted) {
const manager = new BleManager();
setBleManager(manager);
manager.startDeviceScan( [serialUUIDs.serviceUUID],
{scanMode: ScanMode.LowLatency}, (error, device) => {
if (error) {
console.log(error.message);
return;
}
console.log(device.name);
console.log(device.id);
if (device.name) {
setDevices((prevDevices) => [...prevDevices, device]);
}
});
setScanning(true);
}
});
}
}, []);
return (
<SafeAreaView>
<View>
{scanning ? (
<Button
title="Stop Scanning"
onPress={() => {
bleManager.stopDeviceScan();
setScanning(false);
}}
/>
) : (
<Button
title="Start Scanning"
onPress={() => {
requestBluetoothPermissions((isGranted) => {
if (isGranted) {
setDevices([]);
bleManager.startDeviceScan([serialUUIDs.serviceUUID],
{scanMode: ScanMode.LowLatency},(error, device) => {
if (error) {
console.log(error.message);
return;
}
console.log(device.name);
console.log(device.id);
if (device.name) {
setDevices((prevDevices) => [...prevDevices, device]);
}
});
setScanning(true);
}
});
}}
/>
)}
</View>
<FlatList
data={devices}
renderItem={({ item }) => (
<Text>
{item.name} ({item.id})
</Text>
)}
keyExtractor={(item) => item.id}
/>
</SafeAreaView>
);
};
export default App;
I am using react-native-call-detection package to save incoming call number (and send it on server later), I also want to reject/answer the incoming call based on the pressed button (based on server response later).
what package should I use to do it? I just found react-native-call-keep but all examples gave fake phone number to the functons and I don't know how to use its functions or how to get my call uuid.I just know there is reject/answer call function and I should call addEventListener functions before calling functions.
here is my current code:
import React, {useEffect, useState} from 'react';
import RNCallKeep from 'react-native-callkeep';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
TouchableHighlight,
PermissionsAndroid,
} from 'react-native';
import CallDetectorManager from 'react-native-call-detection';
import RNCallKeep from 'react-native-callkeep';
export default class MasterScreen extends React.Component {
constructor(props) {
super(props);
this.state = {
featureOn: false,
incoming: false,
number: null,
};
}
componentDidMount() {
this.askPermission();
this.startListenerTapped();
this.setupCallKeep();
}
setupCallKeep() {
const options = {
android: {
alertTitle: 'Permissions Required',
alertDescription:
'This application needs to access your phone calling accounts to make calls',
cancelButton: 'Cancel',
okButton: 'ok',
imageName: 'ic_launcher',
additionalPermissions: [PermissionsAndroid.PERMISSIONS.READ_CONTACTS],
},
};
try {
RNCallKeep.setup(options);
RNCallKeep.setAvailable(true); // Only used for Android, see doc above.
} catch (err) {
console.error('initializeCallKeep error:', err.message);
}
}
askPermission = async () => {
try {
const permissions = await PermissionsAndroid.requestMultiple([
PermissionsAndroid.PERMISSIONS.READ_CALL_LOG,
PermissionsAndroid.PERMISSIONS.READ_PHONE_STATE,
]);
console.log('Permissions are:', permissions);
} catch (err) {
console.warn(err);
}
};
startListenerTapped = () => {
this.setState({featureOn: true});
this.callDetector = new CallDetectorManager(
(event, phoneNumber) => {
console.log(event);
if (event === 'Disconnected') {
this.setState({incoming: false, number: null});
} else if (event === 'Incoming') {
this.setState({incoming: true, number: phoneNumber});
} else if (event === 'Offhook') {
this.setState({incoming: true, number: phoneNumber});
} else if (event === 'Missed') {
this.setState({incoming: false, number: null});
}
},
true,
() => {},
{
title: 'Phone State Permission',
message:
'This app needs access to your phone state in order to react and/or to adapt to incoming calls.',
},
);
};
stopListenerTapped = () => {
this.setState({featureOn: false});
this.callDetector && this.callDetector.dispose();
};
render() {
return (
<View style={styles.body}>
<Text>incoming call number: {this.state.number}</Text>
<TouchableOpacity
onPress={/*what to do */} style={{
width: 200,
height: 200,
justifyContent: 'center',
}}><Text>answer</Text></TouchableOpacity>
<TouchableOpacity
onPress={/*what to do */} style={{
width: 200,
height: 200,
justifyContent: 'center',
}}>
<Text>reject</Text>
</TouchableOpacity>
</View>
);
}
}
const styles = StyleSheet.create({
body: {
backgroundColor: 'honeydew',
justifyContent: 'center',
alignItems: 'center',
flex: 1,
},
text: {
padding: 20,
fontSize: 20,
},
button: {},
});`
Use RNCallKeep.rejectCall. Like so
<TouchableOpacity
onPr ess={() => RNCallKeep.rejectCall(uuid)}
st yle={{
width: 200,
height: 200,
justifyContent: 'center',
}}
>
<Text>reject</Text>
</TouchableOpacity>
My code is :
import React, { useEffect } from "react";
import * as ImagePicker from "expo-image-picker";
import Screen from "./app/components/Screen";
export default function App() {
async function permisionFunction() {
const result = await ImagePicker.getCameraPermissionsAsync();
if (!result.granted) {
console.log(result);
alert("need access to gallery for this app to work");
}
}
useEffect(() => {
permisionFunction();
}, []);
return <Screen></Screen>;
}
I denied the permissions for camera when it was promted for the first time.
Now whenever I opens the app always need access to gallery for this app to work this message is shown up. for android.
I tried by giving all the permissions to the expo app in settings but still same msg appears.
How can I solve this.
Use expo-cameramodule to access device camera.
I have curated the working example of small app with which you can access pictures from gallery as well as device camera.
Working App: Expo Snack
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View, Button, Image } from 'react-native';
import { Camera } from 'expo-camera';
import * as ImagePicker from 'expo-image-picker';
export default function Add({ navigation }) {
const [cameraPermission, setCameraPermission] = useState(null);
const [galleryPermission, setGalleryPermission] = useState(null);
const [camera, setCamera] = useState(null);
const [imageUri, setImageUri] = useState(null);
const [type, setType] = useState(Camera.Constants.Type.back);
const permisionFunction = async () => {
// here is how you can get the camera permission
const cameraPermission = await Camera.requestPermissionsAsync();
setCameraPermission(cameraPermission.status === 'granted');
const imagePermission = await ImagePicker.getMediaLibraryPermissionsAsync();
console.log(imagePermission.status);
setGalleryPermission(imagePermission.status === 'granted');
if (
imagePermission.status !== 'granted' &&
cameraPermission.status !== 'granted'
) {
alert('Permission for media access needed.');
}
};
useEffect(() => {
permisionFunction();
}, []);
const takePicture = async () => {
if (camera) {
const data = await camera.takePictureAsync(null);
console.log(data.uri);
setImageUri(data.uri);
}
};
const pickImage = async () => {
let result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
aspect: [1, 1],
quality: 1,
});
console.log(result);
if (!result.cancelled) {
setImageUri(result.uri);
}
};
return (
<View style={styles.container}>
<View style={styles.cameraContainer}>
<Camera
ref={(ref) => setCamera(ref)}
style={styles.fixedRatio}
type={type}
ratio={'1:1'}
/>
</View>
<Button title={'Take Picture'} onPress={takePicture} />
<Button title={'Gallery'} onPress={pickImage} />
{imageUri && <Image source={{ uri: imageUri }} style={{ flex: 1 }} />}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
cameraContainer: {
flex: 1,
flexDirection: 'row',
},
fixedRatio: {
flex: 1,
aspectRatio: 1,
},
button: {
flex: 0.1,
padding: 10,
alignSelf: 'flex-end',
alignItems: 'center',
},
});
It's mention in doc Configuration.
In managed apps, Camera requires Permissions.CAMERA. Video recording requires Permissions.AUDIO_RECORDING.
I have the same problem... I'm using expo SDK 42 and after a denied answer the permissions request does not trigger again anymore.
Anyway, You have some mistakes in your code, you have to watch the status property of the permissions response.
this is how to check the response status
const requestPermissions = async () => {
try {
const {
status,
} = await ImagePicker.requestMediaLibraryPermissionsAsync();
console.log('status lib', status);
setHasPickerPermission(status === 'granted');
} catch (error) {
console.log('error', error);
}
try {
const { status } = await ImagePicker.requestCameraPermissionsAsync();
console.log('status camera', status);
setHasCameraPermission(status === 'granted');
} catch (error) {
console.log('error', error);
}
};
useEffect(() => {
requestPermissions();
}, []);
If the user has denied the camera permission the first time, the second time you request permission with requestCameraPermissionsAsync() the answer will always be "denied" unless the user manually changes the permission in the app settings.
import { Linking } from "react-native";
import { useCameraPermissions } from "expo-image-picker";
export default function App() {
const [status, requestPermissions] = useCameraPermissions();
const requestPermissionAgain = () => {
Linking.openSettings();
}
useEffect(() => {
if (!status?.granted) requestPermissions();
}, []);
}
I am very new to learning React Native and would appreciate any help that can be provided with my code. I am currently working off of tutorials and have been stuck for hours. Hopefully this is enough info to get some help. Thanks in advance!
My goal
I am trying to do three things (in this order):
Get the user's current location
Fetch the items data from an API (example outlined below). It should be noted that eventually the contents of the fetch will be dependent on the user's current location.
Parse the items data to create markers and a unique list of categories and place those on the map with the user's current location at center to begin with.
I am looking to be able to watch the user's location and move and update the map accordingly. I also don't want to show the map until all of the markers are in place.
Here are my react versions: react-native-cli: 2.0.1 react-native: 0.63.2
My Bugs
I am using both the Android Studio emulator as well as the Xcode emulator and am currently running into the following problems:
The iOS emulator on Xcode renders fine the first time, but on subsequent refreshes I see 1 or two of my 5 markers missing.
On Android, the map loads perfectly then appears to immediately redraw itself and center on the Googleplex in California instead of the user's current location.
Emulator location is set to London, UK
I suspect I might have some race condition with fetching the items and current location before the map renders but I can't be sure.
My Code
//my API response structure
{
"id": "96845",
"title": "Item_title_goes_here",
"image": "https://someURL/image.JPG",
"stories": 46,
"lat": some_lat_number,
"lon": some_lon_number,
"category": "category_name",
"description": "long_description"
},
//ajax.js
export default {
async fetchInitialItems() {
try {
const response = await fetch(apiHost + '/api/v1/items/');
const responseJson = await response.json();
return responseJson;
} catch (error) {
console.error(error);
}
},
async fetchItemDetail(itemId) {
try {
const response = await fetch(apiHost + '/api/items/' + itemId);
const responseJson = await response.json();
return responseJson;
} catch (error) {
console.error(error);
}
},
};
//ExploreScreen.js (my map component)
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
Image,
Animated,
Dimensions,
TouchableOpacity,
PermissionsAndroid,
ScrollView,
Platform,
StatusBar,
} from 'react-native';
import MapView, {
PROVIDER_GOOGLE,
Marker,
Callout,
Polygon,
} from 'react-native-maps';
import PropTypes from 'prop-types';
import Geolocation from '#react-native-community/geolocation';
import { mapDarkStyle, mapStandardStyle } from '../model/mapData';
import ajax from '../utils/ajax';
import MapCarousel from './MapCarousel';
import Ionicons from 'react-native-vector-icons/Ionicons';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
import Fontisto from 'react-native-vector-icons/Fontisto';
import StarRating from '../components/StarRating';
/**
|--------------------------------------------------
| Variables
|--------------------------------------------------
*/
const { width, height } = Dimensions.get('window');
const SCREEN_HEIGHT = height;
const SCREEN_WIDTH = width;
const ASPECT_RATIO = width / height;
const LATITUDE_DELTA = 0.0422;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
const darkTheme = false;
/**
|--------------------------------------------------
| Component
|--------------------------------------------------
*/
class ExploreScreen extends React.Component {
/****** Props & States ******/
state = {
region: {
latitude: 0,
longitude: 0,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
},
items: [],
markers: [],
categories: [],
currentMapRegion: null,
};
/****** Functions ******/
///when carousel item selected
onCarouselItemSelected() {
alert('carousel item selected');
}
//when the carousel in scrolled
onCarouselIndexChange(itemID) {
const item = this.state.items.find(item => item.id == itemID);
const marker = this.state.markers.find(marker => marker.id == itemID);
const coordinates = { lat: item.lat, lon: item.lon };
this.goToLocation(coordinates);
}
//get current position
getLocation(that) {
Geolocation.getCurrentPosition(
//get the current location
position => {
const region = {
latitude: parseFloat(position.coords.latitude),
longitude: parseFloat(position.coords.longitude),
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
};
that.setState({ region });
},
error => alert(error.message),
{ enableHighAccuracy: true, timeout: 20000 },
);
//get location on location change
that.watchID = Geolocation.watchPosition(position => {
const currentRegion = {
latitude: position.coords.latitude,
longitude: position.coords.longitude,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
};
this.setState({ region: currentRegion });
});
}
//move map to a lat/lon
goToLocation(coordinates) {
if (this.map) {
this.map.animateToRegion({
latitude: coordinates.lat,
longitude: coordinates.lon,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
});
}
}
//when the region changes for the map
onRegionChangeComplete(region) {
//I dont know what to do here
}
//move map to center of current location
gotToCenter() {
if (this.map) {
this.map.animateToRegion({
latitude: this.state.region.latitude,
longitude: this.state.region.longitude,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
});
}
}
//map the categories store in the state
mapCategories = () => {
const uniqueCategories = [];
this.state.items.map(item => {
if (uniqueCategories.indexOf(item.category) === -1) {
uniqueCategories.push(item.category);
}
});
this.setState({ categories: uniqueCategories });
};
//map the items to markers and store in the state
mapMarkers = () => {
const markers = this.state.items.map(item => (
<Marker
key={item.id}
coordinate={{ latitude: item.lat, longitude: item.lon }}
image={require('../assets/map_marker.png')}
tracksViewChanges={false}
title={item.title}
description={item.description}
/>
));
this.setState({ markers: markers });
};
/****** Lifecycle Functions ******/
async componentDidMount() {
var that = this;
//Checking for the permission just after component loaded
if (Platform.OS === 'ios') {
//for ios
this.getLocation(that);
} else {
//for android
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) {
//To Check, If Permission is granted
that.getLocation(that);
} else {
alert('Permission Denied');
}
} catch (err) {
alert('err', err);
console.warn(err);
}
}
requestLocationPermission();
}
//get location on location change
that.watchID = Geolocation.watchPosition(position => {
const currentRegion = {
latitude: parseFloat(position.coords.latitude),
longitude: parseFloat(position.coords.longitude),
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
};
this.setState({ region: currentRegion });
});
console.log(this.state.region);
const items = await ajax.fetchInitialItems();
this.setState({ items });
this.mapMarkers();
this.mapCategories();
}
componentWillUnmount = () => {
Geolocation.clearWatch(this.watchID);
};
render() {
const lat = this.state.region.latitude;
const lon = this.state.region.longitude;
if (this.state.items && lat && lon) {
return (
<View>
<MapView
ref={map => {
this.map = map;
}}
onRegionChangeComplete={this.onRegionChangeComplete.bind(this)}
toolbarEnabled={false}
showsMyLocationButton={false}
provider={PROVIDER_GOOGLE}
style={styles.map}
customMapStyle={darkTheme ? mapDarkStyle : mapStandardStyle}
showsUserLocation={true}
followsUserLocation={true}
region={this.state.region}>
{this.state.markers}
</MapView>
{/* center button */}
<TouchableOpacity
onPress={this.gotToCenter.bind(this)}
style={styles.centerButtonContainer}>
<Ionicons name="md-locate" size={20} />
</TouchableOpacity>
<View style={styles.carousel}>
<MapCarousel
data={this.state.items}
onPressItem={() => {
this.onCarouselItemSelected.bind(this);
}}
onUpdateLocation={this.onCarouselIndexChange.bind(this)}
/>
</View>
</View>
);
} else {
return (
<View style={styles.container}>
<Text style={styles.header}>Loading...</Text>
</View>
);
}
}
}
/**
|--------------------------------------------------
| Styles
|--------------------------------------------------
*/
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
height: '100%',
width: '100%',
justifyContent: 'center',
alignItems: 'center',
},
map: {
height: '100%',
},
carousel: {
position: 'absolute',
bottom: 25,
},
header: {
fontSize: 50,
},
//character name
name: {
fontSize: 15,
marginBottom: 5,
},
//character image
image: {
width: 170,
height: 80,
},
//center button
centerButtonContainer: {
width: 40,
height: 40,
position: 'absolute',
bottom: 260,
right: 10,
borderColor: '#191919',
borderWidth: 0,
borderRadius: 30,
backgroundColor: '#d2d2d2',
justifyContent: 'center',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 9,
},
shadowOpacity: 0.48,
shadowRadius: 11.95,
elevation: 18,
opacity: 0.9,
},
});
export default ExploreScreen;
Is there an alternative way or tricks to run expo app in background using android? I've been searching for a couple of days looking for answer. There's answers but its complicated for beginners, I'm creating an app that track the location of user every 5 seconds and send the location to the server using node. Its working fine, my only problem is when its in the background my app stops sending data to server.
App.js
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet,
Dimensions
} from 'react-native';
import { BackgroundFetch, TaskManager } from 'expo';
import MapView from 'react-native-maps';
import {Marker} from 'react-native-maps'
export default class App extends Component {
state = {
mapRegion: null,
hasLocationPermissions: false,
locationResult: null,
marker: {
latitude: 0,
longitude: 0
},
latitude: 0,
longitude: 0,
location: null,
errorMessage: null
};
componentDidMount() {
//var handle = Interaction
this.getBackgroundStat()
this.watchCurLocation();
}
getBackgroundStat = async () =>{
const test = await BackgroundFetch.getStatusAsync({});
console.log(test)
}
watchCurLocation = () =>
setTimeout(() => {
this.watchCurLocation();
}, 5000);
}
getLocationAsync = async () => {
const { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
this.setState({
errorMessage: 'Permission to access location was denied',
});
return;
}
const location = await Location.getCurrentPositionAsync({});
console.log(location)
this.setState({ location });
};
getCurrentLocation = () =>
navigator.geolocation.getCurrentPosition(
(position) => {
let currentUserPosition = position.coords;
//alert(JSON.stringify(currentUserPosition));
},
(error) => {
console.log(error);
},
{
enableHighAccuracy: true,
timeout: 2000,
maximumAge: 0,
distanceFilter: 1
}
);
_handleMapRegionChange = mapRegion => {
//console.log(mapRegion);
this.setState({ mapRegion });
};
_watchLocationAsync = async () =>{
let watchlocation = Location.watchPositionAsync({enableHighAccuracy:true,timeInterval:4000, distanceInterval:0}, (pos)=>{
console.log(pos)
return pos
});
return watchlocation
}
_getLocationAsync = async () => {
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
this.setState({
locationResult: 'Permission to access location was denied',
});
} else {
this.setState({ hasLocationPermissions: true });
}
let location = await Location.getCurrentPositionAsync({});
let marker = Object.assign({}, this.state.marker)
marker.latitude = location.coords.latitude
marker.longitude = location.coords.longitude
this.setState({ locationResult: JSON.stringify(location) });
this.setState({marker})
// Center the map on the location we just fetched.
this.setState({ mapRegion: { latitude: location.coords.latitude, longitude: location.coords.longitude, latitudeDelta: 0.0922, longitudeDelta: 0.0421 } });
};
componentWillUnmount(){
navigator.geolocation.clearWatch(this.watchId);
}
render() {
return (
<View style={styles.container}>
<Text style={styles.paragraph}>
Pan, zoom, and tap on the map!
</Text>
{
this.state.locationResult === null ?
<Text>Finding your current location...</Text> :
this.state.hasLocationPermissions === false ?
<Text>Location permissions are not granted.</Text> :
this.state.mapRegion === null ?
<Text>Map region doesn't exist.</Text> :
<MapView
style={{ alignSelf: 'stretch', height: 400 }}
initialRegion={this.state.mapRegion}
onRegionChange={this._handleMapRegionChange}
>
<MapView.Marker
coordinate={this.state.marker}
title="GpS locator"
description="My current location"
>
</MapView.Marker>
</MapView>
}
<Text>
Location: {this.state.locationResult}
{"\n\n"}
MyOwnLocation: {this.state.latitude} {"\n\n"}
MyOwnLocation2: {this.state.longitude}
</Text>
</View>
);
}
}
Expo SDK 32 was just released that has background task support including location tracking.
Blog post about it here:
https://blog.expo.io/expo-sdk-v32-0-0-is-now-available-6b78f92a6c52
And docs here:
https://docs.expo.io/versions/v32.0.0/sdk/task-manager
https://docs.expo.io/versions/v32.0.0/sdk/location
And you might find this example helpful.
https://twitter.com/r13127igOr/status/1082761408905920512
I am using react-native-background-task and it is working fine. It works both for IOS and Android. Did you check it out ?
https://www.npmjs.com/package/react-native-background-task