Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
556e83a
move alarm recurrence onto the name row
RisPNG Aug 27, 2026
aa0534c
combine timer creation and management on one screen
RisPNG Aug 27, 2026
899e81d
align alarm names with the time row
RisPNG Aug 27, 2026
93a9e35
animate timer card insertion and removal
RisPNG Aug 28, 2026
91765ad
share list animations and add saved timer selection
RisPNG Aug 28, 2026
40de39f
animate lap table growth and center stopwatch controls
RisPNG Aug 28, 2026
5886186
animate stopwatch side buttons from behind the main button
RisPNG Aug 28, 2026
9cf1445
add timer names and controls to notifications
RisPNG Aug 28, 2026
2accf77
fix timer notification routing and add picker preferences
RisPNG Aug 28, 2026
577dc3d
coordinate timer picker visibility and stabilize wheel widths
RisPNG Aug 28, 2026
4b87e20
place the timer picker above the scrolling list
RisPNG Aug 28, 2026
c46b70f
hide the running timer picker by default
RisPNG Aug 28, 2026
1003692
keep the timer picker scrollable and restore its list anchor
RisPNG Aug 28, 2026
bf694ba
unify timer picker visibility and list animations
RisPNG Aug 28, 2026
a1139b6
use shorter default saved timer durations
RisPNG Aug 28, 2026
18b67e4
round remaining timer seconds up
RisPNG Aug 28, 2026
951ea64
allow heads-up notifications for finished timers
RisPNG Aug 28, 2026
8a802b6
suppress alarm popups while the ringing screen is visible
RisPNG Aug 29, 2026
91b74dc
silence notification channels used by ringing services
RisPNG Aug 29, 2026
44ef5b9
start the default vibration pattern immediately
RisPNG Aug 29, 2026
4297c85
start new timer vibration without an initial delay
RisPNG Aug 29, 2026
1927196
color ringing alarm notifications with the app accent
RisPNG Aug 29, 2026
cfabbf2
request live updates for running timer notifications
RisPNG Aug 29, 2026
52efeb3
raise timer notification importance for live updates
RisPNG Aug 29, 2026
e1b8583
use a darker accent for ringing alarm notifications
RisPNG Aug 29, 2026
9e73b55
share the ringing activity behavior and layout
RisPNG Aug 30, 2026
57f423d
show full-screen alerts for finished timers
RisPNG Aug 30, 2026
a311ed2
track timer overrun and configure ringing behavior
RisPNG Sep 1, 2026
5d29239
Merge branch 'main' into timer-rework-and-alarm-card-row
RisPNG Sep 1, 2026
2e03db3
Merge branch 'main' into alarm-ringing-popup
RisPNG Sep 1, 2026
439128e
Merge branch 'alarm-ringing-popup' into timer-rework-and-alarm-card-row
RisPNG Sep 1, 2026
bcff48d
configure volume button actions separately for ringing timers
RisPNG Sep 1, 2026
a2223ef
show seconds on the ringing timer screen
RisPNG Sep 2, 2026
6f98ffb
support a custom increment amount for each timer
RisPNG Sep 2, 2026
c444aed
let timers inherit the global increment setting
RisPNG Sep 2, 2026
6a72b89
label the inherited increment picker value as zero
RisPNG Sep 2, 2026
af6c64b
show the expiry time on the ringing timer screen
RisPNG Sep 2, 2026
0c72f10
show the scheduled alarm time throughout ringing and snoozing
RisPNG Sep 2, 2026
7493632
add label icons to active and saved timer names
RisPNG Sep 2, 2026
6a21c67
Merge branch 'main' into timer-rework-and-alarm-card-row
RisPNG Sep 3, 2026
d3fa6ba
widen the volume ramp picker to sixty seconds and shorten its title
RisPNG Sep 5, 2026
283705d
preserve the upcoming alarm notification channel
RisPNG Sep 5, 2026
bcc79f0
migrate saved timers and the large start button preference
RisPNG Sep 5, 2026
5f29d62
fix timer broadcast naming and countdown calculations
RisPNG Sep 5, 2026
2ae77de
migrate stored alarms to the immediate vibration default
RisPNG Sep 5, 2026
d4afa0e
default the timer volume buttons to dismiss
RisPNG Sep 6, 2026
649c024
merge main into timer-rework-and-alarm-card-row
RisPNG Sep 6, 2026
9eba419
resolve alarm filtering layout with item animations
RisPNG Sep 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
207 changes: 207 additions & 0 deletions app/schemas/com.bnyro.clock.data.database.AppDatabase/13.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
{
"formatVersion": 1,
"database": {
"version": 13,
"identityHash": "4d1e8d62329256d7dfde8d1afde39a07",
"entities": [
{
"tableName": "timeZones",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `zoneId` TEXT NOT NULL, `zoneName` TEXT NOT NULL, `countryName` TEXT NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "zoneId",
"columnName": "zoneId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "zoneName",
"columnName": "zoneName",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "countryName",
"columnName": "countryName",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"key"
]
}
},
{
"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, `snoozeEnabled` INTEGER NOT NULL DEFAULT 1, `snoozeMinutes` INTEGER NOT NULL DEFAULT 10, `soundEnabled` INTEGER NOT NULL DEFAULT 1, `vibrationPattern` TEXT NOT NULL DEFAULT '0,1000,1000,1000,1000', `vibrationPatternName` TEXT NOT NULL DEFAULT 'Default', `dismissedAt` INTEGER DEFAULT NULL, `startDate` INTEGER NOT NULL DEFAULT 0, `repeatInterval` INTEGER NOT NULL DEFAULT 1, `repeatUnit` TEXT NOT NULL DEFAULT 'WEEK', `repeatAnchor` TEXT NOT NULL DEFAULT 'DAY_OF_MONTH', `repeatDuration` INTEGER DEFAULT NULL, `repeatDurationUnit` TEXT NOT NULL DEFAULT 'DAY', `endDate` INTEGER DEFAULT NULL, `endOccurrences` INTEGER DEFAULT NULL, `advanced` INTEGER NOT NULL DEFAULT 0)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "time",
"columnName": "time",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "label",
"columnName": "label",
"affinity": "TEXT"
},
{
"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"
},
{
"fieldPath": "soundUri",
"columnName": "soundUri",
"affinity": "TEXT"
},
{
"fieldPath": "snoozeEnabled",
"columnName": "snoozeEnabled",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "1"
},
{
"fieldPath": "snoozeMinutes",
"columnName": "snoozeMinutes",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "10"
},
{
"fieldPath": "soundEnabled",
"columnName": "soundEnabled",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "1"
},
{
"fieldPath": "vibrationPattern",
"columnName": "vibrationPattern",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'0,1000,1000,1000,1000'"
},
{
"fieldPath": "vibrationPatternName",
"columnName": "vibrationPatternName",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'Default'"
},
{
"fieldPath": "dismissedAt",
"columnName": "dismissedAt",
"affinity": "INTEGER",
"defaultValue": "NULL"
},
{
"fieldPath": "startDate",
"columnName": "startDate",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
},
{
"fieldPath": "repeatInterval",
"columnName": "repeatInterval",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "1"
},
{
"fieldPath": "repeatUnit",
"columnName": "repeatUnit",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'WEEK'"
},
{
"fieldPath": "repeatAnchor",
"columnName": "repeatAnchor",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'DAY_OF_MONTH'"
},
{
"fieldPath": "repeatDuration",
"columnName": "repeatDuration",
"affinity": "INTEGER",
"defaultValue": "NULL"
},
{
"fieldPath": "repeatDurationUnit",
"columnName": "repeatDurationUnit",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "'DAY'"
},
{
"fieldPath": "endDate",
"columnName": "endDate",
"affinity": "INTEGER",
"defaultValue": "NULL"
},
{
"fieldPath": "endOccurrences",
"columnName": "endOccurrences",
"affinity": "INTEGER",
"defaultValue": "NULL"
},
{
"fieldPath": "advanced",
"columnName": "advanced",
"affinity": "INTEGER",
"notNull": true,
"defaultValue": "0"
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
}
}
],
"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, '4d1e8d62329256d7dfde8d1afde39a07')"
]
}
}
9 changes: 9 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,15 @@
</intent-filter>
</receiver>

<activity
android:name=".presentation.screens.timer.TimerAlertActivity"
android:directBootAware="true"
android:configChanges="orientation|keyboardHidden|keyboard|navigation"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
android:taskAffinity=""
android:theme="@style/Theme.ClockYou" />

<receiver
android:name=".util.services.TimerAlarmReceiver"
android:directBootAware="true"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/assets/vibration_patterns.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"Default": [
1000,
0,
1000,
1000,
1000,
Expand Down
21 changes: 19 additions & 2 deletions app/src/main/java/com/bnyro/clock/data/database/AppDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import com.bnyro.clock.domain.model.TimeZone

@Database(
entities = [TimeZone::class, Alarm::class],
version = 12,
version = 13,
autoMigrations = [
AutoMigration(
from = 2,
Expand Down Expand Up @@ -91,6 +91,22 @@ abstract class AppDatabase : RoomDatabase() {
}
}

val MIGRATION_12_13 = object : Migration(12, 13) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL("ALTER TABLE alarms RENAME TO temp_table")
db.execSQL("CREATE TABLE IF NOT EXISTS `alarms` (`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, `snoozeEnabled` INTEGER NOT NULL DEFAULT 1, `snoozeMinutes` INTEGER NOT NULL DEFAULT 10, `soundEnabled` INTEGER NOT NULL DEFAULT 1, `vibrationPattern` TEXT NOT NULL DEFAULT '0,1000,1000,1000,1000', `vibrationPatternName` TEXT NOT NULL DEFAULT 'Default', `dismissedAt` INTEGER DEFAULT NULL, `startDate` INTEGER NOT NULL DEFAULT 0, `repeatInterval` INTEGER NOT NULL DEFAULT 1, `repeatUnit` TEXT NOT NULL DEFAULT 'WEEK', `repeatAnchor` TEXT NOT NULL DEFAULT 'DAY_OF_MONTH', `repeatDuration` INTEGER DEFAULT NULL, `repeatDurationUnit` TEXT NOT NULL DEFAULT 'DAY', `endDate` INTEGER DEFAULT NULL, `endOccurrences` INTEGER DEFAULT NULL, `advanced` INTEGER NOT NULL DEFAULT 0)")
db.execSQL(
"INSERT INTO alarms (id, time, label, enabled, days, vibrate, soundName, soundUri, snoozeEnabled, snoozeMinutes, soundEnabled, vibrationPattern, vibrationPatternName, dismissedAt, startDate, repeatInterval, repeatUnit, repeatAnchor, repeatDuration, repeatDurationUnit, endDate, endOccurrences, advanced) " +
"SELECT id, time, label, enabled, days, vibrate, soundName, soundUri, snoozeEnabled, snoozeMinutes, soundEnabled, vibrationPattern, vibrationPatternName, dismissedAt, startDate, repeatInterval, repeatUnit, repeatAnchor, repeatDuration, repeatDurationUnit, endDate, endOccurrences, advanced FROM temp_table"
)
db.execSQL(
"UPDATE alarms SET vibrationPattern = '0,1000,1000,1000,1000' " +
"WHERE vibrationPattern = '1000,1000,1000,1000,1000' AND vibrationPatternName = 'Default'"
)
db.execSQL("DROP TABLE temp_table")
}
}

fun getDatabase(context: Context): AppDatabase {
return INSTANCE ?: synchronized(this) {
val targetContext = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Expand All @@ -116,7 +132,8 @@ abstract class AppDatabase : RoomDatabase() {
MIGRATION_1_2,
MIGRATION_3_4,
MIGRATION_7_8,
MIGRATION_11_12
MIGRATION_11_12,
MIGRATION_12_13
)
.build()
INSTANCE = instance
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/bnyro/clock/domain/model/Alarm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ data class Alarm(
@ColumnInfo(defaultValue = "1") var snoozeEnabled: Boolean = true,
@ColumnInfo(defaultValue = "10") var snoozeMinutes: Int = 10,
@ColumnInfo(defaultValue = "1") var soundEnabled: Boolean = true,
@ColumnInfo(defaultValue = "1000,1000,1000,1000,1000") var vibrationPattern: List<Int> = List(5) { 1000 },
@ColumnInfo(defaultValue = "0,1000,1000,1000,1000") var vibrationPattern: List<Int> = listOf(0, 1000, 1000, 1000, 1000),
@ColumnInfo(defaultValue = "Default") var vibrationPatternName: String = "Default",
@ColumnInfo(defaultValue = "NULL") var dismissedAt: Long? = null,
@ColumnInfo(defaultValue = "0") var startDate: Long = LocalDate.now().toEpochDay(),
Expand Down
47 changes: 0 additions & 47 deletions app/src/main/java/com/bnyro/clock/domain/model/PersistentTimer.kt

This file was deleted.

16 changes: 12 additions & 4 deletions app/src/main/java/com/bnyro/clock/domain/model/TimerDescriptor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ import kotlinx.parcelize.Parcelize

@Parcelize
data class TimerDescriptor(
var id: Int = 0,
var currentPosition: Int = 0,
var id: Int,
var settings: TimerSettings
) : Parcelable {
fun asScheduledObject(): TimerObject {
return TimerObject(
id = id,
currentPosition = mutableStateOf(currentPosition),
label = mutableStateOf(settings.label),
currentPosition = mutableStateOf(settings.seconds * 1000),
soundName = settings.soundName,
soundUri = settings.soundUri,
soundEnabled = settings.soundEnabled,
vibrate = settings.vibrate,
vibrationPattern = settings.vibrationPattern,
vibrationPatternName = settings.vibrationPatternName,
incrementSeconds = settings.incrementSeconds
)
}
}
}
Loading