1import { H2 } from '@expo/html-elements'; 2import { Pedometer } from 'expo-sensors'; 3import * as React from 'react'; 4import { ScrollView, Text, View } from 'react-native'; 5 6import ListButton from '../components/ListButton'; 7import usePermissions from '../utilities/usePermissions'; 8import { useResolvedValue } from '../utilities/useResolvedValue'; 9 10function usePedometer({ isActive }: { isActive: boolean }): Pedometer.PedometerResult | null { 11 const [data, setData] = React.useState<Pedometer.PedometerResult | null>(null); 12 const listener = React.useRef<Pedometer.Subscription | null>(null); 13 14 React.useEffect(() => { 15 return () => { 16 listener.current?.remove(); 17 }; 18 }, []); 19 20 React.useEffect(() => { 21 if (isActive) { 22 listener.current = Pedometer.watchStepCount(setData); 23 } else { 24 listener.current?.remove(); 25 } 26 }, [isActive]); 27 28 return data; 29} 30 31function usePedometerHistory({ 32 start, 33 end, 34}: { 35 start: Date; 36 end: Date; 37}): Pedometer.PedometerResult | null { 38 const [data, setData] = React.useState<Pedometer.PedometerResult | null>(null); 39 const isMounted = React.useRef(true); 40 41 React.useEffect(() => { 42 return () => { 43 isMounted.current = false; 44 }; 45 }, []); 46 47 React.useEffect(() => { 48 Pedometer.getStepCountAsync(start, end).then((data) => { 49 if (isMounted.current) { 50 setData(data); 51 } 52 }); 53 }, [start, end]); 54 55 return data; 56} 57 58function StepTrackerView() { 59 const [isActive, setActive] = React.useState(false); 60 const data = usePedometer({ isActive }); 61 const message = data?.steps ? `Total steps ${data.steps}` : `Waiting...`; 62 return ( 63 <View style={{ padding: 10 }}> 64 <H2>Step Tracker</H2> 65 <ListButton onPress={() => setActive(true)} disabled={isActive} title="Start" /> 66 <ListButton onPress={() => setActive(false)} disabled={!isActive} title="Stop" /> 67 <Text style={{ paddingTop: 10, fontWeight: 'bold' }}>{message}</Text> 68 </View> 69 ); 70} 71 72function StepHistoryMessage() { 73 const today = React.useMemo(() => { 74 return new Date(); 75 }, []); 76 const yesterday = React.useMemo(() => { 77 const yesterday = new Date(); 78 yesterday.setDate(today.getDate() - 1); 79 return yesterday; 80 }, [today]); 81 82 const data = usePedometerHistory({ start: yesterday, end: today }); 83 84 const message = data ? `Steps in the last day: ${data.steps}` : `Loading health data...`; 85 return ( 86 <View style={{ padding: 10 }}> 87 <H2>Step History</H2> 88 <Text>{message}</Text> 89 </View> 90 ); 91} 92 93export default function PedometerGuard() { 94 const [isPermissionsGranted] = usePermissions(Pedometer.requestPermissionsAsync); 95 const [isAvailable] = useResolvedValue(Pedometer.isAvailableAsync); 96 97 if (isAvailable === null) { 98 return null; 99 } 100 101 if (!isPermissionsGranted || !isAvailable) { 102 // this can also occur if the device doesn't have a pedometer 103 const message = isAvailable 104 ? 'You have not granted permission to use the device motion on this device!' 105 : 'Your device does not have a pedometer'; 106 return ( 107 <View style={{ flex: 1, justifyContent: 'center', padding: 24, alignItems: 'center' }}> 108 <Text>{message}</Text> 109 </View> 110 ); 111 } 112 return <PedometerScreen />; 113} 114 115function PedometerScreen() { 116 return ( 117 <ScrollView> 118 <StepTrackerView /> 119 <StepHistoryMessage /> 120 </ScrollView> 121 ); 122} 123 124PedometerGuard.navigationOptions = { 125 title: 'Pedometer', 126}; 127