From a650ef50d63e81ec9748094634c5f62b798df153 Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 20:51:02 +0000 Subject: [PATCH 01/14] feat: Add select specific date string to strings xml --- app/src/main/res/values/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b129f5c3c..c21d495aa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -36,6 +36,7 @@ Missed alarm Replaced by another alarm Alarm timed out + Select specific date Timers are running From 404a7278a3cb04203957db97ae062522ef8576aa Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 20:58:27 +0000 Subject: [PATCH 02/14] Feat: Added layout configuration for data selection in alarm edit screen Moved alarm days holder below new component Referenced newly added string select_specific_date to layout --- app/src/main/res/layout/dialog_edit_alarm.xml | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/dialog_edit_alarm.xml b/app/src/main/res/layout/dialog_edit_alarm.xml index 43505b40c..abac43295 100644 --- a/app/src/main/res/layout/dialog_edit_alarm.xml +++ b/app/src/main/res/layout/dialog_edit_alarm.xml @@ -33,11 +33,48 @@ android:textSize="@dimen/big_text_size" tools:text="@string/tomorrow" /> + + + + + + + + + Date: Tue, 6 Jan 2026 21:00:33 +0000 Subject: [PATCH 03/14] Feat: added calendar WHITE drawable vectorr prefixed with IC as per https://github.com/FossifyOrg/General-Discussion#contribution-rules-for-developers --- app/src/main/res/drawable/ic_calendar_vector.xml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 app/src/main/res/drawable/ic_calendar_vector.xml diff --git a/app/src/main/res/drawable/ic_calendar_vector.xml b/app/src/main/res/drawable/ic_calendar_vector.xml new file mode 100644 index 000000000..3da91b675 --- /dev/null +++ b/app/src/main/res/drawable/ic_calendar_vector.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file From 017c792e6a7b178a3b0dcb1da9c632c680aef0e9 Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 21:13:38 +0000 Subject: [PATCH 04/14] feat: modified data model to handle specific dates optionally with backward compatibility updated isRecurring, isToday and isTomorrow to factor in specific dates being set added a function to get date labels so even if the alarm is set for a specific date, e.g. if that date happens to be tomorrow, it will show as tomorrow. added date formatting functions to show the data if it's set for longer than a week away (more intuitive to show the day when the alarm is set for the same week). added functions to handle checking if specific calendar dates are today or tomorrow (also added edge cases handling for difference in years --- .../kotlin/org/fossify/clock/models/Alarm.kt | 91 ++++++++++++++++++- 1 file changed, 87 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt b/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt index 263ca1fac..d5391aa0a 100644 --- a/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt +++ b/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt @@ -3,6 +3,9 @@ package org.fossify.clock.models import androidx.annotation.Keep import org.fossify.clock.helpers.TODAY_BIT import org.fossify.clock.helpers.TOMORROW_BIT +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale @Keep @kotlinx.serialization.Serializable @@ -16,12 +19,91 @@ data class Alarm( var soundUri: String, var label: String, var oneShot: Boolean = false, + var specificDate: Long? = null, // Unix timestamp in milliseconds for specific date alarms ) { - fun isRecurring() = days > 0 + + fun isRecurring() = days > 0 && specificDate == null + + fun isToday(): Boolean { + if (days == TODAY_BIT) return true + return specificDate?.let { isDateToday(it) } ?: false + } + + fun isTomorrow(): Boolean { + if (days == TOMORROW_BIT) return true + return specificDate?.let { isDateTomorrow(it) } ?: false + } - fun isToday() = days == TODAY_BIT + fun hasSpecificDate() = specificDate != null - fun isTomorrow() = days == TOMORROW_BIT + fun getDateLabel(): String { + return when { + isToday() -> "Today" + isTomorrow() -> "Tomorrow" + specificDate != null -> formatSpecificDate(specificDate!!) + isRecurring() -> formatRecurringDays() + else -> "One time" + } + } + + private fun formatSpecificDate(timestamp: Long): String { + val calendar = Calendar.getInstance().apply { timeInMillis = timestamp } + val today = Calendar.getInstance() + val daysDiff = getDaysDifference(today, calendar) + + return when { + daysDiff in 2..6 -> { + // Within next 7 days - show day name + calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault()) ?: "" + } + else -> { + // Show formatted date + SimpleDateFormat("MMM d, yyyy", Locale.getDefault()).format(calendar.time) + } + } + } + + /** + * Formats the recurring weekdays as a comma-separated string + */ + private fun formatRecurringDays(): String { + // This would ideally use the weekday bits to build a string like "Mon, Wed, Fri" + return "Recurring" + } + + private fun isDateToday(timestamp: Long): Boolean { + val alarmDate = Calendar.getInstance().apply { timeInMillis = timestamp } + val today = Calendar.getInstance() + return isSameDay(alarmDate, today) + } + + private fun isDateTomorrow(timestamp: Long): Boolean { + val alarmDate = Calendar.getInstance().apply { timeInMillis = timestamp } + val tomorrow = Calendar.getInstance().apply { add(Calendar.DAY_OF_MONTH, 1) } + return isSameDay(alarmDate, tomorrow) + } + + private fun isSameDay(cal1: Calendar, cal2: Calendar): Boolean { + return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && + cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR) + } + + private fun getDaysDifference(from: Calendar, to: Calendar): Int { + val fromMidnight = from.clone() as Calendar + fromMidnight.set(Calendar.HOUR_OF_DAY, 0) + fromMidnight.set(Calendar.MINUTE, 0) + fromMidnight.set(Calendar.SECOND, 0) + fromMidnight.set(Calendar.MILLISECOND, 0) + + val toMidnight = to.clone() as Calendar + toMidnight.set(Calendar.HOUR_OF_DAY, 0) + toMidnight.set(Calendar.MINUTE, 0) + toMidnight.set(Calendar.SECOND, 0) + toMidnight.set(Calendar.MILLISECOND, 0) + + val diffMillis = toMidnight.timeInMillis - fromMidnight.timeInMillis + return (diffMillis / (1000 * 60 * 60 * 24)).toInt() + } } @Keep @@ -35,6 +117,7 @@ data class ObfuscatedAlarm( var g: String, var h: String, var i: Boolean = false, + var j: Long? = null ) { - fun toAlarm() = Alarm(a, b, c, d, e, f, g, h, i) + fun toAlarm() = Alarm(a, b, c, d, e, f, g, h, i, j) } From 38232720df55b119102bcc5624cf73c782af0d1c Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 21:22:56 +0000 Subject: [PATCH 05/14] feat: DB Helper Updates Added specific date column Updated DB_Version since the data structure is changing Added COL_SPECIFIC_DATA element to ALARM TABLE respecting code line width restrictions (Added on new line) Added secondary DB upgrade for adding in specific dates if missing when users upgrade to new version Filling date inside alarm content values Added retrieval of specific date (if present else null) in getAlarms DB query and populated Alarm object --- .../org/fossify/clock/helpers/DBHelper.kt | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt b/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt index b40975992..d6c7ccb36 100644 --- a/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt +++ b/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt @@ -34,11 +34,12 @@ class DBHelper private constructor( private val COL_SOUND_URI = "sound_uri" private val COL_LABEL = "label" private val COL_ONE_SHOT = "one_shot" + private val COL_SPECIFIC_DATE = "specific_date" private val mDb = writableDatabase companion object { - private const val DB_VERSION = 2 + private const val DB_VERSION = 3 const val DB_NAME = "alarms.db" @SuppressLint("StaticFieldLeak") @@ -56,7 +57,8 @@ class DBHelper private constructor( override fun onCreate(db: SQLiteDatabase) { db.execSQL( "CREATE TABLE IF NOT EXISTS $ALARMS_TABLE_NAME ($COL_ID INTEGER PRIMARY KEY AUTOINCREMENT, $COL_TIME_IN_MINUTES INTEGER, $COL_DAYS INTEGER, " + - "$COL_IS_ENABLED INTEGER, $COL_VIBRATE INTEGER, $COL_SOUND_TITLE TEXT, $COL_SOUND_URI TEXT, $COL_LABEL TEXT, $COL_ONE_SHOT INTEGER)" + "$COL_IS_ENABLED INTEGER, $COL_VIBRATE INTEGER, $COL_SOUND_TITLE TEXT, $COL_SOUND_URI TEXT, $COL_LABEL TEXT, $COL_ONE_SHOT INTEGER, " + + "$COL_SPECIFIC_DATE INTEGER)" ) insertInitialAlarms(db) } @@ -65,6 +67,10 @@ class DBHelper private constructor( if (oldVersion == 1 && newVersion > oldVersion) { db.execSQL("ALTER TABLE $ALARMS_TABLE_NAME ADD COLUMN $COL_ONE_SHOT INTEGER NOT NULL DEFAULT 0") } + if (oldVersion < 3 && newVersion >= 3) { + db.execSQL("ALTER TABLE $ALARMS_TABLE_NAME ADD COLUMN $COL_SPECIFIC_DATE INTEGER") + } + } } private fun insertInitialAlarms(db: SQLiteDatabase) { @@ -121,6 +127,7 @@ class DBHelper private constructor( put(COL_SOUND_URI, alarm.soundUri) put(COL_LABEL, alarm.label) put(COL_ONE_SHOT, alarm.oneShot) + put(COL_SPECIFIC_DATE, alarm.specificDate) } } @@ -154,6 +161,12 @@ class DBHelper private constructor( val soundUri = cursor.getStringValue(COL_SOUND_URI) val label = cursor.getStringValue(COL_LABEL) val oneShot = cursor.getIntValue(COL_ONE_SHOT) == 1 + val specificDateIndex = cursor.getColumnIndex(COL_SPECIFIC_DATE) + val specificDate = if (specificDateIndex != -1 && !cursor.isNull(specificDateIndex)) { + cursor.getLong(specificDateIndex) + } else { + null + } val alarm = Alarm( id = id, @@ -164,7 +177,8 @@ class DBHelper private constructor( soundTitle = soundTitle, soundUri = soundUri, label = label, - oneShot = oneShot + oneShot = oneShot, + specificDate = specificDate ) alarms.add(alarm) } catch (e: Exception) { From b73e3a99d950d57ee5b031c93aecdab44f36054f Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 21:27:19 +0000 Subject: [PATCH 06/14] feat: Update helper functions in constants Added check to updateNonRecurringAlarm, as specific date alarms should not be updated. Added handling of specific data alarms to getTimeOfNextAlarm so it's handled before existing standard alarm logic --- .../kotlin/org/fossify/clock/helpers/Constants.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/org/fossify/clock/helpers/Constants.kt b/app/src/main/kotlin/org/fossify/clock/helpers/Constants.kt index 2d812c03d..16d041e8a 100644 --- a/app/src/main/kotlin/org/fossify/clock/helpers/Constants.kt +++ b/app/src/main/kotlin/org/fossify/clock/helpers/Constants.kt @@ -274,6 +274,15 @@ fun getAllTimeZones() = arrayListOf( ) fun getTimeOfNextAlarm(alarm: Alarm): Calendar? { + if (alarm.specificDate != null) { + return Calendar.getInstance().apply { + timeInMillis = alarm.specificDate!! + set(Calendar.HOUR_OF_DAY, alarm.timeInMinutes / 60) + set(Calendar.MINUTE, alarm.timeInMinutes % 60) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + } + } return getTimeOfNextAlarm(alarm.timeInMinutes, alarm.days) } @@ -304,7 +313,7 @@ fun getTimeOfNextAlarm(alarmTimeInMinutes: Int, days: Int): Calendar? { } fun updateNonRecurringAlarmDay(alarm: Alarm) { - if (alarm.isRecurring()) return + if (alarm.isRecurring() || alarm.hasSpecificDate()) return alarm.days = if (alarm.timeInMinutes > getCurrentDayMinutes()) { TODAY_BIT } else { From b1ce55757667f77467ce2fa77c23463166f6f619 Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 21:36:42 +0000 Subject: [PATCH 07/14] feat: Updating alarm edit dialogue with calendar date picker functions Applying color Filters to layouts for Calendar Icon, Clear Date Buotton and Date Selector Panel Call update function to ensure alarm dates are correctly updated when dialogue is initialised Specific Date alarms are "dayless" so added check in function, updating label text accordingly using utility function. Added date picker function that opens a DatePickerDialog from android package. --- .../fossify/clock/dialogs/EditAlarmDialog.kt | 75 ++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/org/fossify/clock/dialogs/EditAlarmDialog.kt b/app/src/main/kotlin/org/fossify/clock/dialogs/EditAlarmDialog.kt index 06a992637..efba57ffb 100644 --- a/app/src/main/kotlin/org/fossify/clock/dialogs/EditAlarmDialog.kt +++ b/app/src/main/kotlin/org/fossify/clock/dialogs/EditAlarmDialog.kt @@ -1,5 +1,6 @@ package org.fossify.clock.dialogs +import android.app.DatePickerDialog import android.app.TimePickerDialog import android.graphics.drawable.Drawable import android.media.AudioManager @@ -27,6 +28,7 @@ import org.fossify.commons.dialogs.SelectAlarmSoundDialog import org.fossify.commons.extensions.addBit import org.fossify.commons.extensions.applyColorFilter import org.fossify.commons.extensions.beVisibleIf +import org.fossify.commons.extensions.beGone import org.fossify.commons.extensions.getAlertDialogBuilder import org.fossify.commons.extensions.getDefaultAlarmSound import org.fossify.commons.extensions.getProperBackgroundColor @@ -38,6 +40,7 @@ import org.fossify.commons.extensions.setupDialogStuff import org.fossify.commons.extensions.toast import org.fossify.commons.extensions.value import org.fossify.commons.models.AlarmSound +import java.util.Calendar class EditAlarmDialog( val activity: SimpleActivity, @@ -51,6 +54,7 @@ class EditAlarmDialog( init { restoreLastAlarm() updateAlarmTime() + updateDateSelectorUI() binding.apply { editAlarmTime.setOnClickListener { @@ -119,11 +123,21 @@ class EditAlarmDialog( editAlarmLabelImage.applyColorFilter(textColor) editAlarm.setText(alarm.label) + // Date selector setup + editAlarmCalendarIcon.applyColorFilter(textColor) + editAlarmDateClear.applyColorFilter(textColor) + editAlarmDateSelector.setOnClickListener { + showDatePicker() + } + editAlarmDateClear.setOnClickListener { + clearSpecificDate() + } val dayLetters = activity.resources.getStringArray(org.fossify.commons.R.array.week_day_letters) .toList() as ArrayList val dayIndexes = activity.rotateWeekdays(arrayListOf(0, 1, 2, 3, 4, 5, 6)) + dayIndexes.forEach { val bitmask = 1 shl it @@ -154,6 +168,7 @@ class EditAlarmDialog( editAlarmDaysHolder.addView(day) } + updateWeekdaysVisibility() } activity.getAlertDialogBuilder() @@ -239,7 +254,7 @@ class EditAlarmDialog( } private fun checkDaylessAlarm() { - if (!alarm.isRecurring()) { + if (!alarm.isRecurring() && !alarm.hasSpecificDate()) { val textId = if (alarm.timeInMinutes > getCurrentDayMinutes()) { org.fossify.commons.R.string.today } else { @@ -247,6 +262,8 @@ class EditAlarmDialog( } binding.editAlarmDaylessLabel.text = "(${activity.getString(textId)})" + } else if (alarm.hasSpecificDate()) { + binding.editAlarmDaylessLabel.text = "(${alarm.getDateLabel()})" } binding.editAlarmDaylessLabel.beVisibleIf(!alarm.isRecurring()) } @@ -268,4 +285,60 @@ class EditAlarmDialog( alarm.soundUri = alarmSound.uri binding.editAlarmSound.text = alarmSound.title } + + private fun showDatePicker() { + val calendar = Calendar.getInstance() + alarm.specificDate?.let { + calendar.timeInMillis = it + } + + DatePickerDialog( + activity, + { _, year, month, dayOfMonth -> + val selectedDate = Calendar.getInstance().apply { + set(Calendar.YEAR, year) + set(Calendar.MONTH, month) + set(Calendar.DAY_OF_MONTH, dayOfMonth) + set(Calendar.HOUR_OF_DAY, 0) + set(Calendar.MINUTE, 0) + set(Calendar.SECOND, 0) + set(Calendar.MILLISECOND, 0) + } + alarm.specificDate = selectedDate.timeInMillis + alarm.days = 0 // Clear recurring days when setting specific date + updateDateSelectorUI() + updateWeekdaysVisibility() + checkDaylessAlarm() + }, + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH) + ).apply { + datePicker.minDate = System.currentTimeMillis() - 1000 // Allow today + show() + } + } + + private fun clearSpecificDate() { + alarm.specificDate = null + updateDateSelectorUI() + updateWeekdaysVisibility() + checkDaylessAlarm() + } + + private fun updateDateSelectorUI() { + binding.apply { + if (alarm.hasSpecificDate()) { + editAlarmDateLabel.text = alarm.getDateLabel() + editAlarmDateClear.beVisibleIf(true) + } else { + editAlarmDateLabel.text = activity.getString(R.string.select_specific_date) + editAlarmDateClear.beGone() + } + } + } + + private fun updateWeekdaysVisibility() { + binding.editAlarmDaysHolder.beVisibleIf(!alarm.hasSpecificDate()) + } } From 575c067985c793391602deb65bcd9da5ac534317 Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 21:50:32 +0000 Subject: [PATCH 08/14] Fix: Removing additional extra bracket --- app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt b/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt index d6c7ccb36..97ee64717 100644 --- a/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt +++ b/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt @@ -71,7 +71,6 @@ class DBHelper private constructor( db.execSQL("ALTER TABLE $ALARMS_TABLE_NAME ADD COLUMN $COL_SPECIFIC_DATE INTEGER") } } - } private fun insertInitialAlarms(db: SQLiteDatabase) { val weekDays = MONDAY_BIT or TUESDAY_BIT or WEDNESDAY_BIT or THURSDAY_BIT or FRIDAY_BIT From 7539b32dca53f4a528227e83d0728781542b4754 Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 22:05:43 +0000 Subject: [PATCH 09/14] feat: Updated changelog to reflect new feature added --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d603b7cc3..96ed8b4ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [1.6.0] - 2026-01-06 +### Changed +- Added specific date alarm functionality ([#42]) + ## [1.5.0] - 2025-12-16 ### Changed @@ -93,6 +97,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Initial release. [#11]: https://github.com/FossifyOrg/Clock/issues/11 +[#42]: https://github.com/FossifyOrg/Clock/issues/42 [#107]: https://github.com/FossifyOrg/Clock/issues/107 [#144]: https://github.com/FossifyOrg/Clock/issues/144 [#158]: https://github.com/FossifyOrg/Clock/issues/158 From 3ad2a5de89f36f2bf3bcb486aed086a0f296faf4 Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 22:08:09 +0000 Subject: [PATCH 10/14] fix: Missed retrieving specific date from database --- app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt b/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt index 97ee64717..446a6747b 100644 --- a/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt +++ b/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt @@ -143,7 +143,8 @@ class DBHelper private constructor( COL_SOUND_TITLE, COL_SOUND_URI, COL_LABEL, - COL_ONE_SHOT + COL_ONE_SHOT, + COL_SPECIFIC_DATE ) var cursor: Cursor? = null try { From 244eed5d4a2492f430f81c3474f9e24a7671aca9 Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 22:18:07 +0000 Subject: [PATCH 11/14] feat: Offloaded determination of string to utility function pasing in activity as context to extract translations. Ensuring utility function getDateLabel handles today, tomorrow, specific dates and recurring alarms. If none of those criteria are met then an error should be thrown. This is not a valid state. --- .../kotlin/org/fossify/clock/adapters/AlarmsAdapter.kt | 3 +-- app/src/main/kotlin/org/fossify/clock/models/Alarm.kt | 9 +++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/kotlin/org/fossify/clock/adapters/AlarmsAdapter.kt b/app/src/main/kotlin/org/fossify/clock/adapters/AlarmsAdapter.kt index c29d72fe6..5adb30551 100644 --- a/app/src/main/kotlin/org/fossify/clock/adapters/AlarmsAdapter.kt +++ b/app/src/main/kotlin/org/fossify/clock/adapters/AlarmsAdapter.kt @@ -209,8 +209,7 @@ class AlarmsAdapter( return when { !isEnabled -> resources.getString(R.string.not_scheduled) - alarm.isToday() -> resources.getString(org.fossify.commons.R.string.today) - else -> resources.getString(org.fossify.commons.R.string.tomorrow) + else -> alarm.getDateLabel(activity) } } diff --git a/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt b/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt index d5391aa0a..c72f8f52a 100644 --- a/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt +++ b/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt @@ -1,5 +1,6 @@ package org.fossify.clock.models +import android.content.Context import androidx.annotation.Keep import org.fossify.clock.helpers.TODAY_BIT import org.fossify.clock.helpers.TOMORROW_BIT @@ -36,13 +37,13 @@ data class Alarm( fun hasSpecificDate() = specificDate != null - fun getDateLabel(): String { + fun getDateLabel(context: Context): String { return when { - isToday() -> "Today" - isTomorrow() -> "Tomorrow" + isToday() -> context.getString(org.fossify.commons.R.string.today) + isTomorrow() -> context.getString(org.fossify.commons.R.string.tomorrow) specificDate != null -> formatSpecificDate(specificDate!!) isRecurring() -> formatRecurringDays() - else -> "One time" + else -> error("Invalid alarm state: days=$days, specificDate=$specificDate") } } From 956922c5bc6d06523ebc13e74cd809f5511f3a8f Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 22:26:28 +0000 Subject: [PATCH 12/14] Fix: ensuring to pass in activity context for util function to retrieve translated text --- .../main/kotlin/org/fossify/clock/dialogs/EditAlarmDialog.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/org/fossify/clock/dialogs/EditAlarmDialog.kt b/app/src/main/kotlin/org/fossify/clock/dialogs/EditAlarmDialog.kt index efba57ffb..ac3f9be78 100644 --- a/app/src/main/kotlin/org/fossify/clock/dialogs/EditAlarmDialog.kt +++ b/app/src/main/kotlin/org/fossify/clock/dialogs/EditAlarmDialog.kt @@ -263,7 +263,7 @@ class EditAlarmDialog( binding.editAlarmDaylessLabel.text = "(${activity.getString(textId)})" } else if (alarm.hasSpecificDate()) { - binding.editAlarmDaylessLabel.text = "(${alarm.getDateLabel()})" + binding.editAlarmDaylessLabel.text = "(${alarm.getDateLabel(activity)})" } binding.editAlarmDaylessLabel.beVisibleIf(!alarm.isRecurring()) } @@ -329,7 +329,7 @@ class EditAlarmDialog( private fun updateDateSelectorUI() { binding.apply { if (alarm.hasSpecificDate()) { - editAlarmDateLabel.text = alarm.getDateLabel() + editAlarmDateLabel.text = alarm.getDateLabel(activity) editAlarmDateClear.beVisibleIf(true) } else { editAlarmDateLabel.text = activity.getString(R.string.select_specific_date) From f4d45cc05d4ea8d031c37449f1c901122a3120bd Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 23:24:06 +0000 Subject: [PATCH 13/14] Fix: Linting issues Created DB_VERSION constants to represent versions and avoid using magic numbers Created companion object associated with Alarm model to store numbers used in Data calculations --- .../org/fossify/clock/helpers/DBHelper.kt | 14 +++++++---- .../kotlin/org/fossify/clock/models/Alarm.kt | 24 ++++++++++--------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt b/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt index 446a6747b..5c1825335 100644 --- a/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt +++ b/app/src/main/kotlin/org/fossify/clock/helpers/DBHelper.kt @@ -20,6 +20,7 @@ import org.fossify.commons.helpers.THURSDAY_BIT import org.fossify.commons.helpers.TUESDAY_BIT import org.fossify.commons.helpers.WEDNESDAY_BIT +@Suppress("VariableNaming") class DBHelper private constructor( val context: Context, ) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) { @@ -40,6 +41,8 @@ class DBHelper private constructor( companion object { private const val DB_VERSION = 3 + private const val DB_VERSION_1 = 1 + private const val DB_VERSION_3 = 3 const val DB_NAME = "alarms.db" @SuppressLint("StaticFieldLeak") @@ -56,18 +59,19 @@ class DBHelper private constructor( override fun onCreate(db: SQLiteDatabase) { db.execSQL( - "CREATE TABLE IF NOT EXISTS $ALARMS_TABLE_NAME ($COL_ID INTEGER PRIMARY KEY AUTOINCREMENT, $COL_TIME_IN_MINUTES INTEGER, $COL_DAYS INTEGER, " + - "$COL_IS_ENABLED INTEGER, $COL_VIBRATE INTEGER, $COL_SOUND_TITLE TEXT, $COL_SOUND_URI TEXT, $COL_LABEL TEXT, $COL_ONE_SHOT INTEGER, " + - "$COL_SPECIFIC_DATE INTEGER)" + "CREATE TABLE IF NOT EXISTS $ALARMS_TABLE_NAME ($COL_ID INTEGER PRIMARY KEY AUTOINCREMENT, " + + "$COL_TIME_IN_MINUTES INTEGER, $COL_DAYS INTEGER, $COL_IS_ENABLED INTEGER, " + + "$COL_VIBRATE INTEGER, $COL_SOUND_TITLE TEXT, $COL_SOUND_URI TEXT, $COL_LABEL TEXT, " + + "$COL_ONE_SHOT INTEGER, $COL_SPECIFIC_DATE INTEGER)" ) insertInitialAlarms(db) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { - if (oldVersion == 1 && newVersion > oldVersion) { + if (oldVersion == DB_VERSION_1 && newVersion > oldVersion) { db.execSQL("ALTER TABLE $ALARMS_TABLE_NAME ADD COLUMN $COL_ONE_SHOT INTEGER NOT NULL DEFAULT 0") } - if (oldVersion < 3 && newVersion >= 3) { + if (oldVersion < DB_VERSION_3 && newVersion >= DB_VERSION_3) { db.execSQL("ALTER TABLE $ALARMS_TABLE_NAME ADD COLUMN $COL_SPECIFIC_DATE INTEGER") } } diff --git a/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt b/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt index c72f8f52a..5aca110db 100644 --- a/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt +++ b/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt @@ -10,6 +10,7 @@ import java.util.Locale @Keep @kotlinx.serialization.Serializable +@Suppress("TooManyFunctions") data class Alarm( var id: Int, var timeInMinutes: Int, @@ -22,6 +23,14 @@ data class Alarm( var oneShot: Boolean = false, var specificDate: Long? = null, // Unix timestamp in milliseconds for specific date alarms ) { + companion object { + private const val MIN_DAYS_FOR_DAY_NAME = 2 + private const val MAX_DAYS_FOR_DAY_NAME = 6 + private const val MILLIS_IN_SECOND = 1000L + private const val SECONDS_IN_MINUTE = 60 + private const val MINUTES_IN_HOUR = 60 + private const val HOURS_IN_DAY = 24 + } fun isRecurring() = days > 0 && specificDate == null @@ -42,7 +51,7 @@ data class Alarm( isToday() -> context.getString(org.fossify.commons.R.string.today) isTomorrow() -> context.getString(org.fossify.commons.R.string.tomorrow) specificDate != null -> formatSpecificDate(specificDate!!) - isRecurring() -> formatRecurringDays() + isRecurring() -> "Recurring" else -> error("Invalid alarm state: days=$days, specificDate=$specificDate") } } @@ -53,7 +62,7 @@ data class Alarm( val daysDiff = getDaysDifference(today, calendar) return when { - daysDiff in 2..6 -> { + daysDiff in MIN_DAYS_FOR_DAY_NAME..MAX_DAYS_FOR_DAY_NAME -> { // Within next 7 days - show day name calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault()) ?: "" } @@ -64,14 +73,6 @@ data class Alarm( } } - /** - * Formats the recurring weekdays as a comma-separated string - */ - private fun formatRecurringDays(): String { - // This would ideally use the weekday bits to build a string like "Mon, Wed, Fri" - return "Recurring" - } - private fun isDateToday(timestamp: Long): Boolean { val alarmDate = Calendar.getInstance().apply { timeInMillis = timestamp } val today = Calendar.getInstance() @@ -103,7 +104,8 @@ data class Alarm( toMidnight.set(Calendar.MILLISECOND, 0) val diffMillis = toMidnight.timeInMillis - fromMidnight.timeInMillis - return (diffMillis / (1000 * 60 * 60 * 24)).toInt() + val millisInDay = MILLIS_IN_SECOND * SECONDS_IN_MINUTE * MINUTES_IN_HOUR * HOURS_IN_DAY + return (diffMillis / millisInDay).toInt() } } From 63c5de6338e3e5c40786cfad82e754721e2abb11 Mon Sep 17 00:00:00 2001 From: AshBash Date: Tue, 6 Jan 2026 23:46:29 +0000 Subject: [PATCH 14/14] Fix: Remove unneccessary linting suppression for too many functions --- app/src/main/kotlin/org/fossify/clock/models/Alarm.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt b/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt index 5aca110db..5f6a355a4 100644 --- a/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt +++ b/app/src/main/kotlin/org/fossify/clock/models/Alarm.kt @@ -10,7 +10,6 @@ import java.util.Locale @Keep @kotlinx.serialization.Serializable -@Suppress("TooManyFunctions") data class Alarm( var id: Int, var timeInMinutes: Int,