Add vibration setting for timers

This commit is contained in:
Brandon Presley 2022-08-20 16:37:59 +12:00
parent 16ba002387
commit 4c185f0346
9 changed files with 62 additions and 34 deletions

View File

@ -62,6 +62,8 @@ const App = () => {
if (seconds === null) await setItem('seconds', '30'); if (seconds === null) await setItem('seconds', '30');
const alarmEnabled = await getItem('alarmEnabled'); const alarmEnabled = await getItem('alarmEnabled');
if (alarmEnabled === null) await setItem('alarmEnabled', 'false'); if (alarmEnabled === null) await setItem('alarmEnabled', 'false');
const vibrate = await getItem('vibrate');
if (vibrate === null) await setItem('vibrate', 'true');
if (!(await getItem('predictiveSets'))) if (!(await getItem('predictiveSets')))
await setItem('predictiveSets', 'true'); await setItem('predictiveSets', 'true');
if (!(await getItem('maxSets'))) await setItem('maxSets', '3'); if (!(await getItem('maxSets'))) await setItem('maxSets', '3');

View File

@ -29,13 +29,14 @@ export default function EditSet() {
}, [navigation]), }, [navigation]),
); );
const notify = useCallback(async () => { const startTimer = useCallback(async () => {
const enabled = await AsyncStorage.getItem('alarmEnabled'); const enabled = await AsyncStorage.getItem('alarmEnabled');
if (enabled !== 'true') return; if (enabled !== 'true') return;
const minutes = await AsyncStorage.getItem('minutes'); const minutes = await AsyncStorage.getItem('minutes');
const seconds = await AsyncStorage.getItem('seconds'); const seconds = await AsyncStorage.getItem('seconds');
const milliseconds = Number(minutes) * 60 * 1000 + Number(seconds) * 1000; const milliseconds = Number(minutes) * 60 * 1000 + Number(seconds) * 1000;
NativeModules.AlarmModule.timer(milliseconds); const vibrate = (await AsyncStorage.getItem('vibrate')) === 'true';
NativeModules.AlarmModule.timer(milliseconds, vibrate);
}, []); }, []);
const update = useCallback( const update = useCallback(
@ -57,11 +58,11 @@ export default function EditSet() {
INSERT INTO sets(name, reps, weight, created, unit) INSERT INTO sets(name, reps, weight, created, unit)
VALUES (?,?,?,strftime('%Y-%m-%dT%H:%M:%S', 'now', 'localtime'),?) VALUES (?,?,?,strftime('%Y-%m-%dT%H:%M:%S', 'now', 'localtime'),?)
`; `;
startTimer();
await db.executeSql(insert, [name, reps, weight, unit]); await db.executeSql(insert, [name, reps, weight, unit]);
notify();
navigation.goBack(); navigation.goBack();
}, },
[db, navigation, notify], [db, navigation, startTimer],
); );
const save = useCallback( const save = useCallback(

View File

@ -121,7 +121,7 @@ export default function SetList() {
todaysWorkouts[todaysWorkouts.indexOf(todaysSets[0].name!) + 1]; todaysWorkouts[todaysWorkouts.indexOf(todaysSets[0].name!) + 1];
} }
const best = await getBest(nextWorkout); const best = await getBest(nextWorkout);
setNextSet({...best, created: new Date().toISOString()}); setNextSet({...best});
}, [getTodaysSets, getTodaysPlan, getBest]); }, [getTodaysSets, getTodaysPlan, getBest]);
useFocusEffect( useFocusEffect(
@ -171,7 +171,6 @@ export default function SetList() {
reps: 0, reps: 0,
weight: 0, weight: 0,
unit: 'kg', unit: 'kg',
created: new Date().toISOString(),
}; };
navigation.navigate('EditSet', {set: nextSet || set}); navigation.navigate('EditSet', {set: nextSet || set});
}, [navigation, nextSet]); }, [navigation, nextSet]);

View File

@ -14,19 +14,20 @@ import MassiveSwitch from './MassiveSwitch';
const {getItem, setItem} = AsyncStorage; const {getItem, setItem} = AsyncStorage;
export default function SettingsPage() { export default function SettingsPage() {
const [vibrate, setVibrate] = useState(true);
const [minutes, setMinutes] = useState<string>(''); const [minutes, setMinutes] = useState<string>('');
const [maxSets, setMaxSets] = useState<string>('3'); const [maxSets, setMaxSets] = useState<string>('3');
const [seconds, setSeconds] = useState<string>(''); const [seconds, setSeconds] = useState<string>('');
const [alarmEnabled, setAlarmEnabled] = useState<boolean>(false); const [alarm, setAlarm] = useState<boolean>(false);
const [predictiveSets, setPredictiveSets] = useState<boolean>(false); const [predictive, setPredictive] = useState<boolean>(false);
const [showBattery, setShowBattery] = useState(false); const [battery, setBattery] = useState(false);
const [ignoring, setIgnoring] = useState(false); const [ignoring, setIgnoring] = useState(false);
const refresh = useCallback(async () => { const refresh = useCallback(async () => {
setMinutes((await getItem('minutes')) || ''); setMinutes((await getItem('minutes')) || '');
setSeconds((await getItem('seconds')) || ''); setSeconds((await getItem('seconds')) || '');
setAlarmEnabled((await getItem('alarmEnabled')) === 'true'); setAlarm((await getItem('alarmEnabled')) === 'true');
setPredictiveSets((await getItem('predictiveSets')) === 'true'); setPredictive((await getItem('predictiveSets')) === 'true');
setMaxSets((await getItem('maxSets')) || ''); setMaxSets((await getItem('maxSets')) || '');
NativeModules.AlarmModule.ignoringBattery(setIgnoring); NativeModules.AlarmModule.ignoringBattery(setIgnoring);
}, []); }, []);
@ -37,27 +38,27 @@ export default function SettingsPage() {
const changeAlarmEnabled = useCallback( const changeAlarmEnabled = useCallback(
(enabled: boolean) => { (enabled: boolean) => {
setAlarmEnabled(enabled); setAlarm(enabled);
if (enabled && !ignoring) setShowBattery(true); if (enabled && !ignoring) setBattery(true);
setItem('alarmEnabled', enabled ? 'true' : 'false'); setItem('alarmEnabled', enabled ? 'true' : 'false');
}, },
[setShowBattery, ignoring], [setBattery, ignoring],
); );
const changePredictive = useCallback( const changePredictive = useCallback(
(enabled: boolean) => { (enabled: boolean) => {
setPredictiveSets(enabled); setPredictive(enabled);
setItem('predictiveSets', enabled ? 'true' : 'false'); setItem('predictiveSets', enabled ? 'true' : 'false');
ToastAndroid.show( ToastAndroid.show(
'Predictive sets guess whats next based on todays plan.', 'Predictive sets guess whats next based on todays plan.',
ToastAndroid.LONG, ToastAndroid.LONG,
); );
}, },
[setPredictiveSets], [setPredictive],
); );
return ( const textInputs = (
<View style={styles.container}> <>
<TextInput <TextInput
label="Rest minutes" label="Rest minutes"
value={minutes} value={minutes}
@ -68,8 +69,8 @@ export default function SettingsPage() {
setItem('minutes', text); setItem('minutes', text);
}} }}
style={styles.text} style={styles.text}
selectTextOnFocus
/> />
<TextInput <TextInput
label="Rest seconds" label="Rest seconds"
value={seconds} value={seconds}
@ -80,8 +81,8 @@ export default function SettingsPage() {
setItem('seconds', s); setItem('seconds', s);
}} }}
style={styles.text} style={styles.text}
selectTextOnFocus
/> />
<TextInput <TextInput
label="Sets per workout" label="Sets per workout"
value={maxSets} value={maxSets}
@ -91,30 +92,48 @@ export default function SettingsPage() {
setItem('maxSets', value); setItem('maxSets', value);
}} }}
style={styles.text} style={styles.text}
selectTextOnFocus
/> />
</>
);
const changeVibrate = useCallback(
(value: boolean) => {
setVibrate(value);
setItem('vibrate', value ? 'true' : 'false');
},
[setVibrate],
);
return (
<View style={styles.container}>
{textInputs}
<Text style={styles.text}>Rest timers</Text> <Text style={styles.text}>Rest timers</Text>
<MassiveSwitch <MassiveSwitch
style={[styles.text, {alignSelf: 'flex-start'}]} style={[styles.text, {alignSelf: 'flex-start'}]}
value={alarmEnabled} value={alarm}
onValueChange={changeAlarmEnabled} onValueChange={changeAlarmEnabled}
/> />
<Text style={styles.text}>Vibrate</Text>
<MassiveSwitch
style={[styles.text, {alignSelf: 'flex-start'}]}
value={vibrate}
onValueChange={changeVibrate}
/>
<ConfirmDialog <ConfirmDialog
title="Battery optimizations" title="Battery optimizations"
show={showBattery} show={battery}
setShow={setShowBattery} setShow={setBattery}
onOk={() => { onOk={() => {
NativeModules.AlarmModule.openSettings(); NativeModules.AlarmModule.openSettings();
setShowBattery(false); setBattery(false);
}}> }}>
Disable battery optimizations for Massive to use rest timers. Disable battery optimizations for Massive to use rest timers.
</ConfirmDialog> </ConfirmDialog>
<Text style={styles.text}>Predictive sets</Text> <Text style={styles.text}>Predictive sets</Text>
<MassiveSwitch <MassiveSwitch
style={[styles.text, {alignSelf: 'flex-start'}]} style={[styles.text, {alignSelf: 'flex-start'}]}
value={predictiveSets} value={predictive}
onValueChange={changePredictive} onValueChange={changePredictive}
/> />
</View> </View>

View File

@ -83,7 +83,7 @@ export default function ViewBest() {
<XAxis <XAxis
style={{marginHorizontal: -10, height: xAxisHeight}} style={{marginHorizontal: -10, height: xAxisHeight}}
data={sets} data={sets}
formatLabel={(_value, index) => formatMonth(sets[index].created)} formatLabel={(_value, index) => formatMonth(sets[index].created!)}
contentInset={{left: 10, right: 10}} contentInset={{left: 10, right: 10}}
svg={axesSvg} svg={axesSvg}
/> />

View File

@ -23,10 +23,11 @@ class AlarmModule internal constructor(context: ReactApplicationContext?) :
@RequiresApi(api = Build.VERSION_CODES.O) @RequiresApi(api = Build.VERSION_CODES.O)
@ReactMethod @ReactMethod
fun timer(milliseconds: Int) { fun timer(milliseconds: Int, vibrate: Boolean) {
Log.d("AlarmModule", "Queue alarm for $milliseconds delay") Log.d("AlarmModule", "Queue alarm for $milliseconds delay")
val intent = Intent(reactApplicationContext, TimerService::class.java) val intent = Intent(reactApplicationContext, TimerService::class.java)
intent.putExtra("milliseconds", milliseconds) intent.putExtra("milliseconds", milliseconds)
intent.putExtra("vibrate", vibrate)
reactApplicationContext.startService(intent) reactApplicationContext.startService(intent)
} }

View File

@ -30,7 +30,9 @@ class AlarmService : Service(), OnPreparedListener {
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM) .setUsage(AudioAttributes.USAGE_ALARM)
.build() .build()
vibrator!!.vibrate(VibrationEffect.createWaveform(pattern, 1), audioAttributes) val vibrate = intent.extras!!.getBoolean("vibrate")
if (vibrate)
vibrator!!.vibrate(VibrationEffect.createWaveform(pattern, 1), audioAttributes)
return START_STICKY return START_STICKY
} }

View File

@ -19,17 +19,19 @@ class TimerService : Service() {
private var endMs: Int? = null private var endMs: Int? = null
private var currentMs: Long? = null private var currentMs: Long? = null
private var countdownTimer: CountDownTimer? = null private var countdownTimer: CountDownTimer? = null
private var vibrate: Boolean = true
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d("TimerService", "Started timer service.") Log.d("TimerService", "Started timer service.")
Log.d("TimerService", "endMs=$endMs,currentMs=$currentMs") Log.d("TimerService", "endMs=$endMs,currentMs=$currentMs")
if (intent?.action == "add") { vibrate = intent!!.extras!!.getBoolean("vibrate")
if (intent.action == "add") {
endMs = currentMs!!.toInt().plus(60000) endMs = currentMs!!.toInt().plus(60000)
applicationContext.stopService(Intent(applicationContext, AlarmService::class.java)) applicationContext.stopService(Intent(applicationContext, AlarmService::class.java))
} }
else { else {
endMs = intent!!.extras!!.getInt("milliseconds") endMs = intent.extras!!.getInt("milliseconds")
} }
Log.d("TimerService", "endMs=$endMs,currentMs=$currentMs") Log.d("TimerService", "endMs=$endMs,currentMs=$currentMs")
notificationManager = getManager(applicationContext) notificationManager = getManager(applicationContext)
@ -68,7 +70,9 @@ class TimerService : Service() {
.setCategory(NotificationCompat.CATEGORY_ALARM) .setCategory(NotificationCompat.CATEGORY_ALARM)
.priority = NotificationCompat.PRIORITY_HIGH .priority = NotificationCompat.PRIORITY_HIGH
notificationManager.notify(NOTIFICATION_ID, builder.build()) notificationManager.notify(NOTIFICATION_ID, builder.build())
applicationContext.startService(Intent(applicationContext, AlarmService::class.java)) val alarmIntent = Intent(applicationContext, AlarmService::class.java)
alarmIntent.putExtra("vibrate", vibrate)
applicationContext.startService(alarmIntent)
} }
} }
} }

2
set.ts
View File

@ -3,6 +3,6 @@ export default interface Set {
name: string; name: string;
reps: number; reps: number;
weight: number; weight: number;
created: string; created?: string;
unit?: string; unit?: string;
} }