diff --git a/App.tsx b/App.tsx index 125fe1b..53c4e1f 100644 --- a/App.tsx +++ b/App.tsx @@ -2,116 +2,406 @@ * 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 type {PropsWithChildren} from 'react'; import { SafeAreaView, ScrollView, StatusBar, StyleSheet, Text, - useColorScheme, View, + Switch, + Button, + Alert, + PermissionsAndroid, + // ToastAndroid, + NativeModules, } from 'react-native'; -import { - Colors, - DebugInstructions, - Header, - LearnMoreLinks, - ReloadInstructions, -} from 'react-native/Libraries/NewAppScreen'; +import BackgroundFetch from 'react-native-background-fetch'; +import Geolocation, { + GeolocationError, + GeolocationResponse, +} from '@react-native-community/geolocation'; -type SectionProps = PropsWithChildren<{ - title: string; -}>; +const {ForegroundHeadlessModule} = NativeModules; -function Section({children, title}: SectionProps): React.JSX.Element { - const isDarkMode = useColorScheme() === 'dark'; - return ( - - - {title} - - - {children} - - - ); +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 { + 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, + }, + ); + }); } -function App(): React.JSX.Element { - const isDarkMode = useColorScheme() === 'dark'; +const App = () => { + const [enabled, setEnabled] = React.useState(false); + const [status, setStatus] = React.useState(-1); + const [events, setEvents] = React.useState([]); - const backgroundStyle = { - backgroundColor: isDarkMode ? Colors.darker : Colors.lighter, + 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); }; - return ( - - - -
- -
- Edit App.tsx to change this - screen and then come back to see your edits. -
-
- -
-
- -
-
- Read the docs to discover what to do next: -
- + /// 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 ( + + Waiting for BackgroundFetch events... + + ); + } + return events + .slice() + .reverse() + .map(event => ( + + + + {event.taskId} {event.isHeadless ? '[Headless]' : ''} + + + Remark - {event.location} + {event.timestamp} - + )); + }; + + async function onLocPress(): Promise { + 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 ( + + + + + BGFetch Demo +