From 4682053e21a3124fbeb390c275b02f70d7c38093 Mon Sep 17 00:00:00 2001 From: Suhas Dissanayake Date: Sat, 9 Dec 2023 12:08:33 +0530 Subject: [PATCH] feat: add option to create one-time alarms (#236) * feat: add option to create one-time alarms --- .../com.bnyro.clock.db.AppDatabase/5.json | 115 ++++++++++++++++++ .../java/com/bnyro/clock/db/AppDatabase.kt | 5 +- .../main/java/com/bnyro/clock/obj/Alarm.kt | 4 +- .../bnyro/clock/receivers/AlarmReceiver.kt | 11 +- .../com/bnyro/clock/ui/components/AlarmRow.kt | 103 +++++++++------- app/src/main/res/values/strings.xml | 1 + 6 files changed, 192 insertions(+), 47 deletions(-) create mode 100644 app/schemas/com.bnyro.clock.db.AppDatabase/5.json diff --git a/app/schemas/com.bnyro.clock.db.AppDatabase/5.json b/app/schemas/com.bnyro.clock.db.AppDatabase/5.json new file mode 100644 index 00000000..4255ff6b --- /dev/null +++ b/app/schemas/com.bnyro.clock.db.AppDatabase/5.json @@ -0,0 +1,115 @@ +{ + "formatVersion": 1, + "database": { + "version": 5, + "identityHash": "fbfe4cceb885036244d87a982461448d", + "entities": [ + { + "tableName": "timeZones", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `displayName` TEXT NOT NULL, `offset` INTEGER NOT NULL, PRIMARY KEY(`name`))", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "displayName", + "columnName": "displayName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "offset", + "columnName": "offset", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "name" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "alarms", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `time` INTEGER NOT NULL, `label` TEXT, `enabled` INTEGER NOT NULL, `days` TEXT NOT NULL, `vibrate` INTEGER NOT NULL, `soundName` TEXT, `soundUri` TEXT, `repeat` INTEGER NOT NULL DEFAULT 1)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "enabled", + "columnName": "enabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "days", + "columnName": "days", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "vibrate", + "columnName": "vibrate", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "soundName", + "columnName": "soundName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "soundUri", + "columnName": "soundUri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "repeat", + "columnName": "repeat", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'fbfe4cceb885036244d87a982461448d')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/com/bnyro/clock/db/AppDatabase.kt b/app/src/main/java/com/bnyro/clock/db/AppDatabase.kt index fbb07653..fc2ef773 100644 --- a/app/src/main/java/com/bnyro/clock/db/AppDatabase.kt +++ b/app/src/main/java/com/bnyro/clock/db/AppDatabase.kt @@ -14,13 +14,14 @@ import com.bnyro.clock.obj.TimeZone @Database( entities = [TimeZone::class, Alarm::class], - version = 4, + version = 5, autoMigrations = [ AutoMigration( from = 2, to = 3, spec = AppDatabase.RemoveSoundColumnAutoMigration::class - ) + ), + AutoMigration(from = 4, to = 5) ] ) @TypeConverters(Converters::class) diff --git a/app/src/main/java/com/bnyro/clock/obj/Alarm.kt b/app/src/main/java/com/bnyro/clock/obj/Alarm.kt index 0c6967af..cd9a2d11 100644 --- a/app/src/main/java/com/bnyro/clock/obj/Alarm.kt +++ b/app/src/main/java/com/bnyro/clock/obj/Alarm.kt @@ -1,5 +1,6 @@ package com.bnyro.clock.obj +import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.PrimaryKey @@ -12,5 +13,6 @@ data class Alarm( var days: List = listOf(0, 1, 2, 3, 4, 5, 6), var vibrate: Boolean = false, var soundName: String? = null, - var soundUri: String? = null + var soundUri: String? = null, + @ColumnInfo(defaultValue = "1") var repeat: Boolean = false ) diff --git a/app/src/main/java/com/bnyro/clock/receivers/AlarmReceiver.kt b/app/src/main/java/com/bnyro/clock/receivers/AlarmReceiver.kt index 3cdb23db..be29d86c 100644 --- a/app/src/main/java/com/bnyro/clock/receivers/AlarmReceiver.kt +++ b/app/src/main/java/com/bnyro/clock/receivers/AlarmReceiver.kt @@ -21,13 +21,20 @@ class AlarmReceiver : BroadcastReceiver() { val currentDay = TimeHelper.getCurrentWeekDay() - if (currentDay - 1 in alarm.days) { + if (currentDay - 1 in alarm.days || !alarm.repeat) { val playAlarm = Intent(context, AlarmService::class.java) playAlarm.putExtra(AlarmHelper.EXTRA_ID, id) ContextCompat.startForegroundService(context, playAlarm) } // re-enqueue the alarm for the next day - AlarmHelper.enqueue(context, alarm) + if (alarm.repeat) { + AlarmHelper.enqueue(context, alarm) + } else { + alarm.enabled = false + runBlocking { + DatabaseHolder.instance.alarmsDao().update(alarm) + } + } } } diff --git a/app/src/main/java/com/bnyro/clock/ui/components/AlarmRow.kt b/app/src/main/java/com/bnyro/clock/ui/components/AlarmRow.kt index a9635368..76be91b3 100644 --- a/app/src/main/java/com/bnyro/clock/ui/components/AlarmRow.kt +++ b/app/src/main/java/com/bnyro/clock/ui/components/AlarmRow.kt @@ -87,10 +87,9 @@ fun AlarmRow(alarm: Alarm, alarmModel: AlarmModel) { Column( modifier = Modifier .fillMaxWidth() - .padding(horizontal = 15.dp, vertical = 10.dp) ) { Row( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth().padding(horizontal = 15.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween ) { @@ -160,52 +159,72 @@ fun AlarmRow(alarm: Alarm, alarmModel: AlarmModel) { } } AnimatedVisibility(visible = expanded) { - Column { - val chosenDays = remember { - alarm.days.toMutableStateList() + Column( + Modifier.background(MaterialTheme.colorScheme.primaryContainer) + .padding(horizontal = 15.dp, vertical = 10.dp) + ) { + var repeat by remember { + mutableStateOf(alarm.repeat) } Row( - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 15.dp), - horizontalArrangement = Arrangement.SpaceBetween + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically ) { - val daysOfWeek = remember { - AlarmHelper.getDaysOfWeekByLocale() + Text(text = stringResource(R.string.repeat)) + Checkbox(checked = repeat, onCheckedChange = { + repeat = it + alarm.repeat = it + alarmModel.updateAlarm(context, alarm) + }) + } + AnimatedVisibility(visible = repeat) { + val chosenDays = remember { + alarm.days.toMutableStateList() } + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 15.dp), + horizontalArrangement = Arrangement.SpaceBetween + ) { + val daysOfWeek = remember { + AlarmHelper.getDaysOfWeekByLocale() + } - daysOfWeek.forEach { (day, index) -> - val enabled = chosenDays.contains(index) - Box( - modifier = Modifier - .size(30.dp) - .background( - if (enabled) MaterialTheme.colorScheme.secondary else Color.Transparent, - CircleShape - ) - .clip(CircleShape) - .border( - if (enabled) 0.dp else 1.dp, - MaterialTheme.colorScheme.outline, - CircleShape + daysOfWeek.forEach { (day, index) -> + val enabled = chosenDays.contains(index) + Box( + modifier = Modifier + .size(30.dp) + .background( + if (enabled) MaterialTheme.colorScheme.primary else Color.Transparent, + CircleShape + ) + .clip(CircleShape) + .border( + if (enabled) 0.dp else 1.dp, + MaterialTheme.colorScheme.primary, + CircleShape + ) + .clickable { + if (enabled) { + chosenDays.remove(index) + } else { + chosenDays.add( + index + ) + } + alarm.days = chosenDays + alarmModel.updateAlarm(context, alarm) + }, + contentAlignment = Alignment.Center + ) { + Text( + text = day, + color = if (enabled) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onPrimaryContainer ) - .clickable { - if (enabled) { - chosenDays.remove(index) - } else { - chosenDays.add( - index - ) - } - alarm.days = chosenDays - alarmModel.updateAlarm(context, alarm) - }, - contentAlignment = Alignment.Center - ) { - Text( - text = day, - color = if (enabled) MaterialTheme.colorScheme.onSecondary else MaterialTheme.colorScheme.onBackground - ) + } } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 99d43661..4a99afa2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -62,6 +62,7 @@ Snooze Dismiss Same time + Repeat %d hour ahead %d hours ahead