parent
4a95ed050c
commit
fcce1ad9ef
12
App.tsx
12
App.tsx
|
@ -50,11 +50,15 @@ const App = () => {
|
|||
)
|
||||
|
||||
useEffect(() => {
|
||||
DeviceEventEmitter.addListener(TOAST, ({value}: {value: string}) => {
|
||||
const description = DeviceEventEmitter.addListener(
|
||||
TOAST,
|
||||
({value}: {value: string}) => {
|
||||
console.log(`${Routes.name}.toast:`, {value})
|
||||
setSnackbar(value)
|
||||
})
|
||||
if (AppDataSource.isInitialized) return setInitialized(true)
|
||||
},
|
||||
)
|
||||
if (AppDataSource.isInitialized) setInitialized(true)
|
||||
else {
|
||||
AppDataSource.initialize().then(async () => {
|
||||
const settings = await settingsRepo.findOne({where: {}})
|
||||
console.log(`${App.name}.useEffect:`, {gotSettings: settings})
|
||||
|
@ -62,6 +66,8 @@ const App = () => {
|
|||
setColor(settings.color)
|
||||
setInitialized(true)
|
||||
})
|
||||
}
|
||||
return description.remove
|
||||
}, [])
|
||||
|
||||
const paperTheme = useMemo(() => {
|
||||
|
|
|
@ -7,6 +7,7 @@ import HomePage from './HomePage'
|
|||
import PlanPage from './PlanPage'
|
||||
import Route from './route'
|
||||
import SettingsPage from './SettingsPage'
|
||||
import TimerPage from './TimerPage'
|
||||
import useDark from './use-dark'
|
||||
import WorkoutsPage from './WorkoutsPage'
|
||||
|
||||
|
@ -21,6 +22,7 @@ export default function Routes() {
|
|||
{name: 'Plans', component: PlanPage, icon: 'event'},
|
||||
{name: 'Best', component: BestPage, icon: 'insights'},
|
||||
{name: 'Workouts', component: WorkoutsPage, icon: 'fitness-center'},
|
||||
{name: 'Timer', component: TimerPage, icon: 'access-time'},
|
||||
{name: 'Settings', component: SettingsPage, icon: 'settings'},
|
||||
],
|
||||
[],
|
||||
|
|
|
@ -1,62 +1,45 @@
|
|||
import {useFocusEffect} from '@react-navigation/native';
|
||||
import React, {useCallback, useState} from 'react';
|
||||
import {NativeModules, View} from 'react-native';
|
||||
import {Button, Subheading, Title} from 'react-native-paper';
|
||||
import DrawerHeader from './DrawerHeader';
|
||||
import MassiveFab from './MassiveFab';
|
||||
import Page from './Page';
|
||||
import {getSettings, updateSettings} from './settings.service';
|
||||
import {useSettings} from './use-settings';
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {NativeEventEmitter, NativeModules, View} from 'react-native'
|
||||
import {Button, Subheading, Title} from 'react-native-paper'
|
||||
import {PADDING} from './constants'
|
||||
import {settingsRepo} from './db'
|
||||
import DrawerHeader from './DrawerHeader'
|
||||
import MassiveFab from './MassiveFab'
|
||||
import Settings from './settings'
|
||||
|
||||
interface TickEvent {
|
||||
minutes: string
|
||||
seconds: string
|
||||
}
|
||||
|
||||
export default function TimerPage() {
|
||||
const [remaining, setRemaining] = useState(0);
|
||||
const {settings} = useSettings();
|
||||
const [minutes, setMinutes] = useState('00')
|
||||
const [seconds, setSeconds] = useState('00')
|
||||
const [settings, setSettings] = useState<Settings>()
|
||||
|
||||
const minutes = Math.floor(remaining / 1000 / 60);
|
||||
const seconds = Math.floor((remaining / 1000) % 60);
|
||||
let interval = 0;
|
||||
|
||||
const tick = useCallback(() => {
|
||||
let newRemaining = 0;
|
||||
getSettings().then(gotSettings => {
|
||||
if (!gotSettings.nextAlarm) return;
|
||||
const date = new Date(gotSettings.nextAlarm);
|
||||
newRemaining = date.getTime() - new Date().getTime();
|
||||
if (newRemaining < 0) setRemaining(0);
|
||||
else setRemaining(newRemaining);
|
||||
});
|
||||
interval = setInterval(() => {
|
||||
console.log({newRemaining});
|
||||
newRemaining -= 1000;
|
||||
if (newRemaining > 0) return setRemaining(newRemaining);
|
||||
clearInterval(interval);
|
||||
setRemaining(0);
|
||||
}, 1000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
useFocusEffect(tick);
|
||||
useEffect(() => {
|
||||
settingsRepo.findOne({where: {}}).then(setSettings)
|
||||
const emitter = new NativeEventEmitter()
|
||||
const listener = emitter.addListener('tick', (event: TickEvent) => {
|
||||
console.log(`${TimerPage.name}.tick:`, {event})
|
||||
setMinutes(event.minutes)
|
||||
setSeconds(event.seconds)
|
||||
})
|
||||
return listener.remove
|
||||
}, [])
|
||||
|
||||
const stop = () => {
|
||||
NativeModules.AlarmModule.stop();
|
||||
clearInterval(interval);
|
||||
setRemaining(0);
|
||||
updateSettings({...settings, nextAlarm: undefined});
|
||||
};
|
||||
NativeModules.AlarmModule.stop()
|
||||
}
|
||||
|
||||
const add = async () => {
|
||||
if (!settings.nextAlarm) return;
|
||||
const date = new Date(settings.nextAlarm);
|
||||
date.setTime(date.getTime() + 1000 * 60);
|
||||
NativeModules.AlarmModule.add(date, settings.vibrate, settings.sound);
|
||||
await updateSettings({...settings, nextAlarm: date.toISOString()});
|
||||
tick();
|
||||
};
|
||||
NativeModules.AlarmModule.add(settings.vibrate, settings.sound)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<DrawerHeader name="Timer" />
|
||||
<Page>
|
||||
<View style={{flexGrow: 1, padding: PADDING}}>
|
||||
<View
|
||||
style={{
|
||||
flex: 1,
|
||||
|
@ -69,8 +52,8 @@ export default function TimerPage() {
|
|||
</Subheading>
|
||||
<Button onPress={add}>Add 1 min</Button>
|
||||
</View>
|
||||
</Page>
|
||||
</View>
|
||||
<MassiveFab icon="stop" onPress={stop} />
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
|
|
@ -16,10 +16,8 @@ import android.util.Log
|
|||
import android.widget.Toast
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.facebook.react.bridge.Callback
|
||||
import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
||||
import com.facebook.react.bridge.ReactMethod
|
||||
import com.facebook.react.bridge.*
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule
|
||||
import kotlin.math.floor
|
||||
|
||||
|
||||
|
@ -27,39 +25,63 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
ReactContextBaseJavaModule(context) {
|
||||
|
||||
var countdownTimer: CountDownTimer? = null
|
||||
var currentMs: Long = 0
|
||||
var running = false
|
||||
|
||||
override fun getName(): String {
|
||||
return "AlarmModule"
|
||||
}
|
||||
|
||||
private val receiver = object : BroadcastReceiver() {
|
||||
private val stopReceiver = object : BroadcastReceiver() {
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
Log.d("AlarmModule", "Received broadcast intent")
|
||||
Toast.makeText(reactApplicationContext, "called from test receiver", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
stop()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
reactApplicationContext.registerReceiver(stopReceiver, IntentFilter(STOP_BROADCAST))
|
||||
}
|
||||
|
||||
override fun onCatalystInstanceDestroy() {
|
||||
reactApplicationContext.unregisterReceiver(stopReceiver)
|
||||
super.onCatalystInstanceDestroy()
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
@ReactMethod
|
||||
fun add(milliseconds: Int, vibrate: Boolean, sound: String?) {
|
||||
fun add(vibrate: Boolean, sound: String?) {
|
||||
Log.d("AlarmModule", "Add 1 min to alarm.")
|
||||
val addIntent = Intent(reactApplicationContext, TimerService::class.java)
|
||||
addIntent.action = "add"
|
||||
addIntent.putExtra("vibrate", vibrate)
|
||||
addIntent.putExtra("sound", sound)
|
||||
addIntent.data = Uri.parse("$milliseconds")
|
||||
reactApplicationContext.startService(addIntent)
|
||||
countdownTimer?.cancel()
|
||||
val newMs = if (running) currentMs.toInt().plus(60000) else 60000
|
||||
countdownTimer = getTimer(newMs, vibrate, sound)
|
||||
countdownTimer?.start()
|
||||
running = true
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
@ReactMethod
|
||||
fun stop() {
|
||||
Log.d("AlarmModule", "Stop alarm.")
|
||||
val timerIntent = Intent(reactApplicationContext, TimerService::class.java)
|
||||
reactApplicationContext.stopService(timerIntent)
|
||||
val alarmIntent = Intent(reactApplicationContext, AlarmService::class.java)
|
||||
reactApplicationContext.stopService(alarmIntent)
|
||||
countdownTimer?.cancel()
|
||||
running = false
|
||||
reactApplicationContext?.stopService(
|
||||
Intent(
|
||||
reactApplicationContext,
|
||||
AlarmService::class.java
|
||||
)
|
||||
)
|
||||
val manager = getManager()
|
||||
manager.cancel(NOTIFICATION_ID_DONE)
|
||||
manager.cancel(NOTIFICATION_ID_PENDING)
|
||||
val params = Arguments.createMap().apply {
|
||||
putString("minutes", "00")
|
||||
putString("seconds", "00")
|
||||
}
|
||||
reactApplicationContext
|
||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
||||
.emit("tick", params)
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
|
@ -76,6 +98,7 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
countdownTimer?.cancel()
|
||||
countdownTimer = getTimer(milliseconds, vibrate, sound)
|
||||
countdownTimer?.start()
|
||||
running = true
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
|
@ -114,6 +137,7 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
return object : CountDownTimer(endMs.toLong(), 1000) {
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
override fun onTick(current: Long) {
|
||||
currentMs = current
|
||||
val seconds =
|
||||
floor((current / 1000).toDouble() % 60).toInt().toString().padStart(2, '0')
|
||||
val minutes =
|
||||
|
@ -124,6 +148,13 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
NotificationCompat.PRIORITY_LOW
|
||||
val manager = getManager()
|
||||
manager.notify(NOTIFICATION_ID_PENDING, builder.build())
|
||||
val params = Arguments.createMap().apply {
|
||||
putString("minutes", minutes)
|
||||
putString("seconds", seconds)
|
||||
}
|
||||
reactApplicationContext
|
||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
||||
.emit("tick", params)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
|
@ -149,6 +180,9 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
alarmIntent.putExtra("vibrate", vibrate)
|
||||
alarmIntent.putExtra("sound", sound)
|
||||
context.startService(alarmIntent)
|
||||
reactApplicationContext
|
||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
||||
.emit("finish", Arguments.createMap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -160,11 +194,10 @@ class AlarmModule constructor(context: ReactApplicationContext?) :
|
|||
val contentIntent = Intent(context, MainActivity::class.java)
|
||||
val pendingContent =
|
||||
PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_IMMUTABLE)
|
||||
reactApplicationContext.registerReceiver(receiver, IntentFilter(STOP_BROADCAST))
|
||||
val stopIntent = Intent(STOP_BROADCAST)
|
||||
stopIntent.flags =Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
val stopBroadcast = Intent(STOP_BROADCAST)
|
||||
stopBroadcast.setPackage(reactApplicationContext.packageName)
|
||||
val pendingStop =
|
||||
PendingIntent.getService(context, 0, stopIntent, PendingIntent.FLAG_MUTABLE)
|
||||
PendingIntent.getBroadcast(context, 0, stopBroadcast, PendingIntent.FLAG_IMMUTABLE)
|
||||
return NotificationCompat.Builder(context, CHANNEL_ID_PENDING)
|
||||
.setSmallIcon(R.drawable.ic_baseline_hourglass_bottom_24).setContentTitle("Resting")
|
||||
.setContentIntent(pendingContent)
|
||||
|
|
|
@ -4,4 +4,5 @@ export type DrawerParamList = {
|
|||
Best: {}
|
||||
Plans: {}
|
||||
Workouts: {}
|
||||
Timer: {}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user