POCLocationGather/App.tsx

409 lines
11 KiB
TypeScript
Raw Normal View History

/**
* Sample React Native App
* https://github.com/facebook/react-native
*
* Generated with the TypeScript template
* https://github.com/react-native-community/react-native-template-typescript
*
* @format
*/
import React from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
View,
Switch,
Button,
Alert,
PermissionsAndroid,
// ToastAndroid,
NativeModules,
} from 'react-native';
import BackgroundFetch from 'react-native-background-fetch';
import Geolocation, {
GeolocationError,
GeolocationResponse,
} from '@react-native-community/geolocation';
const {ForegroundHeadlessModule} = NativeModules;
const Colors = {
gold: '#fedd1e',
black: '#000',
white: '#fff',
lightGrey: '#ccc',
blue: '#337AB7',
brick: '#973920',
};
/// Util class for handling fetch-event peristence in AsyncStorage.
import Event from './src/Event';
import AsyncStorage from '@react-native-async-storage/async-storage';
Geolocation.setRNConfiguration({
skipPermissionRequests: true,
locationProvider: 'auto',
});
const requestPermissions = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
title: 'Need permission to access location',
message:
'Location access is needed ' + 'so we can track your location!',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
const granted2 = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_BACKGROUND_LOCATION,
{
title: 'Need permission to access location',
message:
'Location access is needed ' + 'so we can track your location!',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
if (
granted === PermissionsAndroid.RESULTS.GRANTED &&
granted2 === PermissionsAndroid.RESULTS.GRANTED
) {
console.log('You can use the location');
} else {
console.log('Location permission denied');
}
} catch (err) {
console.warn(err);
}
};
function geoLocationPromise(): Promise<GeolocationResponse | GeolocationError> {
return new Promise((resolve, reject) => {
Geolocation.getCurrentPosition(
info => {
console.log(info);
resolve(info);
},
error => {
console.log(error);
reject(error);
},
{
timeout: 20000,
maximumAge: 0,
// enableHighAccuracy: true,
},
);
});
}
const App = () => {
const [enabled, setEnabled] = React.useState(false);
const [status, setStatus] = React.useState(-1);
const [events, setEvents] = React.useState<Event[]>([]);
React.useEffect(() => {
async function asyncTask() {
await requestPermissions();
initBackgroundFetch();
loadEvents();
}
asyncTask();
}, []);
/// Configure BackgroundFetch.
///
const initBackgroundFetch = async () => {
const status: number = await BackgroundFetch.configure(
{
minimumFetchInterval: 15, // <-- minutes (15 is minimum allowed)
stopOnTerminate: false,
enableHeadless: true,
startOnBoot: false,
// Android options
forceAlarmManager: false, // <-- Set true to bypass JobScheduler.
requiredNetworkType: BackgroundFetch.NETWORK_TYPE_NONE, // Default
requiresCharging: false, // Default
requiresDeviceIdle: false, // Default
requiresBatteryNotLow: false, // Default
requiresStorageNotLow: false, // Default
},
async (taskId: string) => {
console.log('[BackgroundFetch] taskId', taskId);
if (taskId === 'react-native-background-fetch') {
// console.log('[BackgroundFetch] taskId', taskId);
const event = await Event.create(taskId, false);
// Update state.
setEvents(prev => [...prev, event]);
}
if (taskId === 'com.transistorsoft.customtask') {
// Geolocation.getCurrentPosition(
// async info => {
// console.log(info);
// const event = await Event.create(taskId, false, `lat-${info.coords.latitude};long-${info.coords.longitude}`);
// },
// err => console.log(err),
// {
// timeout: 10000,
// maximumAge: 10000,
// enableHighAccuracy: false,
// },
// );
try {
const locInfo = (await geoLocationPromise()) as GeolocationResponse;
const event = await Event.create(
taskId,
false,
`lat-${locInfo.coords.latitude};long-${locInfo.coords.longitude}`,
);
// Update state.
setEvents(prev => [...prev, event]);
} catch (error) {
const g = error as GeolocationError;
if (g.message) {
const event = await Event.create(taskId, false, `${g.message}`);
// Update state.
setEvents(prev => [...prev, event]);
} else {
const event = await Event.create(taskId, false);
// Update state.
setEvents(prev => [...prev, event]);
}
}
// console.log('Running [customtask] here!');
}
// Finish.
BackgroundFetch.finish(taskId);
},
(taskId: string) => {
// Oh No! Our task took too long to complete and the OS has signalled
// that this task must be finished immediately.
console.log('[Fetch] TIMEOUT taskId:', taskId);
BackgroundFetch.finish(taskId);
},
);
setStatus(status);
setEnabled(true);
};
/// Load persisted events from AsyncStorage.
///
const loadEvents = () => {
Event.all()
.then(data => {
setEvents(data as Event[]);
})
.catch(error => {
Alert.alert('Error', 'Failed to load data from AsyncStorage: ' + error);
});
};
/// Toggle BackgroundFetch ON/OFF
///
const onClickToggleEnabled = (value: boolean) => {
setEnabled(value);
if (value) {
BackgroundFetch.start();
} else {
BackgroundFetch.stop();
}
};
/// [Status] button handler.
///
const onClickStatus = () => {
BackgroundFetch.status().then((status: number) => {
let statusConst = '';
switch (status) {
case BackgroundFetch.STATUS_AVAILABLE:
statusConst = 'STATUS_AVAILABLE';
break;
case BackgroundFetch.STATUS_DENIED:
statusConst = 'STATUS_DENIED';
break;
case BackgroundFetch.STATUS_RESTRICTED:
statusConst = 'STATUS_RESTRICTED';
break;
}
Alert.alert('BackgroundFetch.status()', `${statusConst} (${status})`);
});
};
/// [scheduleTask] button handler.
/// Schedules a custom-task to fire in 5000ms
///
const onClickScheduleTask = () => {
const delay = 600000;
BackgroundFetch.scheduleTask({
taskId: 'com.transistorsoft.customtask',
stopOnTerminate: false,
enableHeadless: true,
delay: delay,
forceAlarmManager: false,
periodic: true,
})
.then(() => {
Alert.alert(
'scheduleTask',
'Scheduled task with delay: ' + delay + 'ms',
);
})
.catch(error => {
Alert.alert('scheduleTask ERROR', error);
});
};
/// Clear the Events list.
///
const onClickClear = () => {
Event.destroyAll();
setEvents([]);
};
/// Fetch events renderer.
///
const renderEvents = () => {
if (!events.length) {
return (
<Text style={{padding: 10, fontSize: 16}}>
Waiting for BackgroundFetch events...
</Text>
);
}
return events
.slice()
.reverse()
.map(event => (
<View key={event.key} style={styles.event}>
<View style={{flexDirection: 'row'}}>
<Text style={styles.taskId}>
{event.taskId}&nbsp;{event.isHeadless ? '[Headless]' : ''}
</Text>
</View>
<Text style={styles.remark}>Remark - {event.location}</Text>
<Text style={styles.timestamp}>{event.timestamp}</Text>
</View>
));
};
async function onLocPress(): Promise<void> {
try {
// const locInfo = (await geoLocationPromise()) as GeolocationResponse;
// ToastAndroid.show(
// `lat-${locInfo.coords.latitude};long-${locInfo.coords.longitude}`,
// 5000,
// );
ForegroundHeadlessModule.startService();
console.log('Did it run?');
} catch (error) {
const g = error as GeolocationError;
if (g.message) {
console.log(g.message);
} else {
console.log(error);
}
}
}
function onLocStop() {
ForegroundHeadlessModule.stopService();
AsyncStorage.getItem('watchId', (err, result) => {
if (err) {
console.log('Couldnt get WatchID', err);
return;
}
console.log(result);
if (result) {
Geolocation.clearWatch(+result);
}
});
}
return (
<SafeAreaView style={{flex: 1, backgroundColor: Colors.gold}}>
<StatusBar barStyle={'light-content'}></StatusBar>
<View style={styles.container}>
<View style={styles.toolbar}>
<Text style={styles.title}>BGFetch Demo</Text>
<Button title="Loc" onPress={onLocPress} />
<Button title="Stop" onPress={onLocStop} />
<Switch value={enabled} onValueChange={onClickToggleEnabled} />
</View>
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={styles.eventList}>
{renderEvents()}
</ScrollView>
<View style={styles.toolbar}>
<Button title={'status: ' + status} onPress={onClickStatus} />
<Text>&nbsp;</Text>
<Button title="scheduleTask" onPress={onClickScheduleTask} />
<View style={{flex: 1}} />
<Button title="clear" onPress={onClickClear} />
</View>
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: 'column',
flex: 1,
},
title: {
fontSize: 24,
flex: 1,
fontWeight: 'bold',
color: Colors.black,
},
eventList: {
flex: 1,
backgroundColor: Colors.white,
},
event: {
padding: 10,
borderBottomWidth: 1,
borderColor: Colors.lightGrey,
},
taskId: {
color: Colors.blue,
fontSize: 16,
fontWeight: 'bold',
},
headless: {
fontWeight: 'bold',
},
remark: {
color: Colors.brick,
},
timestamp: {
color: Colors.black,
},
toolbar: {
height: 57,
flexDirection: 'row',
paddingLeft: 10,
paddingRight: 10,
alignItems: 'center',
backgroundColor: Colors.gold,
},
});
export default App;