diff --git a/Alarm.tsx b/Alarm.tsx
deleted file mode 100644
index fed04f5..0000000
--- a/Alarm.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import AsyncStorage from '@react-native-async-storage/async-storage';
-import React, {useEffect, useState} from 'react';
-import {NativeModules, StyleSheet, Text, View} from 'react-native';
-import {Button, Modal, Portal} from 'react-native-paper';
-
-export default function Alarm() {
- const [show, setShow] = useState(false);
- const [seconds, setSeconds] = useState(0);
- const [minutes, setMinutes] = useState(0);
-
- let intervalId: number;
-
- useEffect(() => {
- if (!show) return;
- (async () => {
- const next = await AsyncStorage.getItem('nextAlarm');
- if (!next) return;
- const milliseconds = new Date(next).getTime() - new Date().getTime();
- if (milliseconds <= 0) return;
- let secondsLeft = milliseconds / 1000;
- setSeconds(Math.floor(secondsLeft % 60));
- setMinutes(Math.floor(secondsLeft / 60));
- intervalId = setInterval(() => {
- secondsLeft--;
- if (secondsLeft <= 0) return clearInterval(intervalId);
- setSeconds(Math.floor(secondsLeft % 60));
- setMinutes(Math.floor(secondsLeft / 60));
- }, 1000);
- })();
- return () => clearInterval(intervalId);
- }, [show]);
-
- const stop = async () => {
- NativeModules.AlarmModule.stop();
- clearInterval(intervalId);
- setSeconds(0);
- setMinutes(0);
- await AsyncStorage.setItem('nextAlarm', '');
- };
-
- return (
- <>
-
- setShow(false)}>
- Resting
-
- {minutes}:{seconds}
-
-
-
-
-
-
-
-
- >
- );
-}
-
-const styles = StyleSheet.create({
- center: {
- alignItems: 'center',
- alignSelf: 'center',
- marginBottom: 10,
- },
- title: {
- fontSize: 18,
- },
-});
diff --git a/Home.tsx b/Home.tsx
index 51399ba..eeff3a2 100644
--- a/Home.tsx
+++ b/Home.tsx
@@ -7,8 +7,7 @@ import {
StyleSheet,
View,
} from 'react-native';
-import {AnimatedFAB, Button, Searchbar} from 'react-native-paper';
-import Alarm from './Alarm';
+import {AnimatedFAB, Searchbar} from 'react-native-paper';
import {getDb} from './db';
import EditSet from './EditSet';
@@ -56,10 +55,7 @@ export default function Home() {
const minutes = await AsyncStorage.getItem('minutes');
const seconds = await AsyncStorage.getItem('seconds');
const milliseconds = Number(minutes) * 60 * 1000 + Number(seconds) * 1000;
- const when = new Date();
- when.setTime(when.getTime() + milliseconds);
NativeModules.AlarmModule.timer(milliseconds);
- await AsyncStorage.setItem('nextAlarm', when.toISOString());
};
const next = async () => {
@@ -87,7 +83,6 @@ export default function Home() {
onScrollEndDrag={next}
/>
-
-
);
@@ -66,6 +69,7 @@ export default function Settings({
const styles = StyleSheet.create({
container: {
padding: 10,
+ flex: 1,
},
text: {
marginBottom: 10,
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 83a4564..8504aa5 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -29,7 +29,8 @@
-
+
+
diff --git a/android/app/src/main/java/com/massive/AlarmModule.kt b/android/app/src/main/java/com/massive/AlarmModule.kt
index f2e4da2..6b341bb 100644
--- a/android/app/src/main/java/com/massive/AlarmModule.kt
+++ b/android/app/src/main/java/com/massive/AlarmModule.kt
@@ -1,23 +1,16 @@
package com.massive
-import com.facebook.react.bridge.ReactApplicationContext
-import com.facebook.react.bridge.ReactContextBaseJavaModule
-import android.app.PendingIntent
-import android.app.AlarmManager
-import androidx.annotation.RequiresApi
-import com.facebook.react.bridge.ReactMethod
import android.content.Intent
-import com.massive.MyBroadcastReceiver
-import android.app.AlarmManager.AlarmClockInfo
-import android.content.Context
import android.os.Build
import android.util.Log
+import androidx.annotation.RequiresApi
+import com.facebook.react.bridge.ReactApplicationContext
+import com.facebook.react.bridge.ReactContextBaseJavaModule
+import com.facebook.react.bridge.ReactMethod
-// replace com.your-app-name with your app’s name
class AlarmModule internal constructor(context: ReactApplicationContext?) :
ReactContextBaseJavaModule(context) {
- private var pendingIntent: PendingIntent? = null
- private var alarmManager: AlarmManager? = null
+
override fun getName(): String {
return "AlarmModule"
}
@@ -26,21 +19,8 @@ class AlarmModule internal constructor(context: ReactApplicationContext?) :
@ReactMethod
fun timer(milliseconds: Int) {
Log.d("AlarmModule", "Queue alarm for $milliseconds delay")
- val intent = Intent(reactApplicationContext, MyBroadcastReceiver::class.java)
- pendingIntent = PendingIntent.getBroadcast(
- reactApplicationContext, 69, intent, PendingIntent.FLAG_IMMUTABLE
- )
- alarmManager =
- reactApplicationContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager
- val info = AlarmClockInfo(System.currentTimeMillis() + milliseconds, pendingIntent)
- alarmManager!!.setAlarmClock(info, pendingIntent)
-
+ val intent = Intent(reactApplicationContext, TimerService::class.java)
+ intent.putExtra("milliseconds", milliseconds)
+ reactApplicationContext.startService(intent)
}
-
- @ReactMethod
- fun stop() {
- Log.d("AlarmModule", "Request to stop timer.")
- alarmManager?.cancel(pendingIntent)
- reactApplicationContext.stopService(Intent(reactApplicationContext, AlarmService::class.java))
- }
-}
\ No newline at end of file
+}
diff --git a/android/app/src/main/java/com/massive/MyBroadcastReceiver.kt b/android/app/src/main/java/com/massive/MyBroadcastReceiver.kt
deleted file mode 100644
index b309d1c..0000000
--- a/android/app/src/main/java/com/massive/MyBroadcastReceiver.kt
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.massive
-
-import androidx.annotation.RequiresApi
-import android.content.Intent
-import android.app.NotificationManager
-import android.app.NotificationChannel
-import com.massive.MyBroadcastReceiver
-import com.massive.AlarmService
-import com.massive.AlarmActivity
-import android.app.PendingIntent
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.os.Build
-import android.util.Log
-import androidx.core.app.NotificationCompat
-import androidx.core.app.NotificationManagerCompat
-
-class MyBroadcastReceiver : BroadcastReceiver() {
- @RequiresApi(api = Build.VERSION_CODES.M)
- override fun onReceive(context: Context, intent: Intent) {
- Log.d("MyBroadcastReceiver", "Received intent for BroadcastReceiver.")
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- val importance = NotificationManager.IMPORTANCE_HIGH
- val channel = NotificationChannel(CHANNEL_ID, CHANNEL_ID, importance)
- channel.description = "Alarms for rest timings."
- val notificationManager = context.getSystemService(
- NotificationManager::class.java
- )
- notificationManager.createNotificationChannel(channel)
- }
- context.startService(Intent(context, AlarmService::class.java))
- val contentIntent = Intent(context.applicationContext, AlarmActivity::class.java)
- val pendingContent =
- PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_IMMUTABLE)
- val builder = NotificationCompat.Builder(context, CHANNEL_ID)
- .setSmallIcon(R.drawable.ic_baseline_timer_24)
- .setContentTitle("Rest")
- .setContentText("Break times over!")
- .setContentIntent(pendingContent)
- .setAutoCancel(true)
- .setCategory(NotificationCompat.CATEGORY_ALARM)
- .setPriority(NotificationCompat.PRIORITY_HIGH)
- val notificationManager = NotificationManagerCompat.from(context)
- notificationManager.notify(ALARM_ID, builder.build())
- }
-
- companion object {
- private const val CHANNEL_ID = "MassiveAlarm"
- private const val ALARM_ID = 1
- }
-}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/massive/StopTimer.kt b/android/app/src/main/java/com/massive/StopTimer.kt
new file mode 100644
index 0000000..92811a8
--- /dev/null
+++ b/android/app/src/main/java/com/massive/StopTimer.kt
@@ -0,0 +1,16 @@
+package com.massive
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+
+class StopTimer : Service() {
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ applicationContext.stopService(Intent(applicationContext, TimerService::class.java))
+ return super.onStartCommand(intent, flags, startId)
+ }
+
+ override fun onBind(p0: Intent?): IBinder? {
+ return null
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/com/massive/TimerService.kt b/android/app/src/main/java/com/massive/TimerService.kt
new file mode 100644
index 0000000..d6ed1c4
--- /dev/null
+++ b/android/app/src/main/java/com/massive/TimerService.kt
@@ -0,0 +1,92 @@
+package com.massive
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.app.Service
+import android.content.Intent
+import android.os.Build
+import android.os.CountDownTimer
+import android.os.IBinder
+import android.util.Log
+import androidx.annotation.RequiresApi
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import kotlin.math.floor
+
+class TimerService : Service() {
+ private lateinit var notificationManager: NotificationManagerCompat
+ private lateinit var countdownTimer: CountDownTimer
+
+ @RequiresApi(Build.VERSION_CODES.M)
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ Log.d("TimerService", "Started timer service.")
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val importance = NotificationManager.IMPORTANCE_LOW
+ val channel = NotificationChannel(CHANNEL_ID, CHANNEL_ID, importance)
+ channel.description = "Alarms for rest timings."
+ val notificationManager = applicationContext.getSystemService(
+ NotificationManager::class.java
+ )
+ notificationManager.createNotificationChannel(channel)
+ }
+
+ val contentIntent = Intent(applicationContext, MainActivity::class.java)
+ val pendingContent =
+ PendingIntent.getActivity(applicationContext, 0, contentIntent, PendingIntent.FLAG_IMMUTABLE)
+ val actionIntent = Intent(applicationContext, StopTimer::class.java)
+ val pendingAction =
+ PendingIntent.getService(applicationContext, 0, actionIntent, PendingIntent.FLAG_IMMUTABLE)
+ val builder = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_baseline_hourglass_bottom_24)
+ .setContentTitle("Resting")
+ .setContentIntent(pendingContent)
+ .addAction(R.drawable.ic_baseline_stop_24, "STOP", pendingAction)
+
+ val endMs = intent!!.extras!!.getInt("milliseconds")
+ notificationManager = NotificationManagerCompat.from(applicationContext)
+ countdownTimer = object : CountDownTimer(endMs.toLong(), 1000) {
+ override fun onTick(currentMs: Long) {
+ val seconds = floor((currentMs / 1000).toDouble() % 60)
+ .toInt().toString().padStart(2, '0')
+ val minutes = floor((currentMs / 1000).toDouble() / 60)
+ .toInt().toString().padStart(2, '0')
+ builder.setContentText("$minutes:$seconds")
+ .setAutoCancel(false)
+ .setDefaults(0)
+ .setProgress(endMs, currentMs.toInt(), false)
+ .setCategory(NotificationCompat.CATEGORY_PROGRESS)
+ .priority = NotificationCompat.PRIORITY_LOW
+ notificationManager.notify(ALARM_ID, builder.build())
+ }
+ override fun onFinish() {
+ builder.setContentText("Timer finished.")
+ .clearActions()
+ .setAutoCancel(true)
+ .setOngoing(false)
+ .setCategory(NotificationCompat.CATEGORY_ALARM)
+ .priority = NotificationCompat.PRIORITY_HIGH
+ notificationManager.notify(ALARM_ID, builder.build())
+ applicationContext.startService(Intent(applicationContext, AlarmService::class.java))
+ }
+ }
+
+ countdownTimer.start()
+ return super.onStartCommand(intent, flags, startId)
+ }
+
+ override fun onBind(p0: Intent?): IBinder? {
+ return null
+ }
+
+ override fun onDestroy() {
+ countdownTimer.cancel()
+ notificationManager.cancel(ALARM_ID)
+ super.onDestroy()
+ }
+
+ companion object {
+ private const val CHANNEL_ID = "MassiveAlarm"
+ private const val ALARM_ID = 1
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/res/drawable/ic_baseline_hourglass_bottom_24.xml b/android/app/src/main/res/drawable/ic_baseline_hourglass_bottom_24.xml
new file mode 100644
index 0000000..e2dc75e
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_baseline_hourglass_bottom_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/android/app/src/main/res/drawable/ic_baseline_stop_24.xml b/android/app/src/main/res/drawable/ic_baseline_stop_24.xml
new file mode 100644
index 0000000..19bcbee
--- /dev/null
+++ b/android/app/src/main/res/drawable/ic_baseline_stop_24.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 7dc2df2..bc186d5 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -1,3 +1,3 @@
- massive
+ Massive
diff --git a/package.json b/package.json
index e487e22..076c64a 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,6 @@
"dependencies": {
"@babel/preset-env": "^7.1.6",
"@react-native-async-storage/async-storage": "^1.17.7",
- "@react-navigation/bottom-tabs": "^6.3.1",
"@react-navigation/material-top-tabs": "^6.2.1",
"@react-navigation/native": "^6.0.10",
"@react-navigation/native-stack": "^6.6.2",
diff --git a/yarn.lock b/yarn.lock
index 8a3e00a..790fc8e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1618,15 +1618,6 @@
resolved "https://registry.yarnpkg.com/@react-native/polyfills/-/polyfills-2.0.0.tgz#4c40b74655c83982c8cf47530ee7dc13d957b6aa"
integrity sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ==
-"@react-navigation/bottom-tabs@^6.3.1":
- version "6.3.1"
- resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-6.3.1.tgz#1552ccdb789b6c9fc05af0877f8f3a50ab28870c"
- integrity sha512-sL9F4WMhhR6I9bE7bpsPVHnK1cN9doaFHAuy5YmD+Sw6OyO0TAmNgQFx4xZWqboA5ZwSkN0tWcRCr6wGXaRRag==
- dependencies:
- "@react-navigation/elements" "^1.3.3"
- color "^3.1.3"
- warn-once "^0.1.0"
-
"@react-navigation/core@^6.2.1":
version "6.2.1"
resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-6.2.1.tgz#90459f9afd25b71a9471b0706ebea2cdd2534fc4"