Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f6c5fbc
refactor(ui): simplify timer screen composable
nsh07 May 10, 2026
9824449
feat(db): add topic data in db
nsh07 May 14, 2026
433a3ec
fix(db): store color prefs as Long instead of String
nsh07 May 14, 2026
a149cde
fix(test): update unit tests
nsh07 May 14, 2026
4fbb86c
fix(settings): load timer settings for only current topic
nsh07 May 14, 2026
1d1cf2b
fix(topic): make all topic fields non null, remove fallback
nsh07 May 14, 2026
be36b9f
feat(settings): allow editing topics
nsh07 May 15, 2026
e8bdd13
feat(topic): add shape parameter to topics
nsh07 May 15, 2026
1edbaae
feat(topic): add basic topic editing UI
nsh07 May 16, 2026
6a206f1
feat(topic): remove grid UI, add animations
nsh07 May 17, 2026
ac075e7
feat(topic): add edit button
nsh07 May 17, 2026
e8a2b14
fix(topic): use styles to improve performance
nsh07 May 18, 2026
600f1c3
feat(topic): add topic settings editing ui
nsh07 May 18, 2026
6a2cc2e
feat(timer): allow choosing timer topic in timer screen
nsh07 May 19, 2026
3f384ad
fix(timer): fix colors, improve animation perf
nsh07 May 19, 2026
f11d322
feat(timer): add the base for the topic creation UI
nsh07 May 21, 2026
57bf611
fix(settings): possible race condition
nsh07 Jun 20, 2026
b4f6c56
fix(stats): ensure continuous history for all tags
nsh07 Jun 20, 2026
f55e418
fix(stats): last n days query
nsh07 Jun 20, 2026
9e74e62
fix(db): simplify onCreate query
nsh07 Jun 20, 2026
e356908
fix: replace repeated use of "default" literal with constant
nsh07 Jun 20, 2026
72e4cba
fix(topic): load timer properties directly from current topic
nsh07 Jun 27, 2026
bc06f62
feat(settings): allow editing topics from within timer settings
nsh07 Jul 12, 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
3 changes: 1 addition & 2 deletions androidApp/src/main/java/org/nsh07/pomodoro/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import org.nsh07.pomodoro.di.ActivityCallbacks
import org.nsh07.pomodoro.ui.AppScreen
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsViewModel
import org.nsh07.pomodoro.ui.theme.TomatoTheme
import org.nsh07.pomodoro.utils.toColor

class MainActivity : ComponentActivity() {

Expand Down Expand Up @@ -61,7 +60,7 @@ class MainActivity : ComponentActivity() {
else -> isSystemInDarkTheme()
}

val seed = settingsState.colorScheme.toColor()
val seed = settingsState.colorScheme

val isPlus by settingsViewModel.isPlus.collectAsStateWithLifecycle()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ import org.nsh07.pomodoro.BuildConfig
import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.data.AppPreferenceRepository
import org.nsh07.pomodoro.data.AppStatRepository
import org.nsh07.pomodoro.data.AppTopicRepository
import org.nsh07.pomodoro.data.PreferenceRepository
import org.nsh07.pomodoro.data.StatRepository
import org.nsh07.pomodoro.data.StateRepository
import org.nsh07.pomodoro.data.TopicRepository
import org.nsh07.pomodoro.service.AndroidTimerHelper
import org.nsh07.pomodoro.service.TimerHelper
import org.nsh07.pomodoro.service.TimerManager
Expand All @@ -50,6 +52,7 @@ val servicesModule = module {

single<AppInfo> { create(::createAppInfo) }
single<AppStatRepository>() bind StatRepository::class
single<AppTopicRepository>() bind TopicRepository::class
single<AppPreferenceRepository>() bind PreferenceRepository::class
single<StateRepository>()
single<AndroidTimerHelper>() bind TimerHelper::class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class AndroidTimerHelper(private val context: Context) : TimerHelper {
context.startService(it)
}

is TimerAction.SetInfiniteFocus -> {
else -> {
Log.e("StartService", "Invalid action: $action")
}
}
Expand Down
40 changes: 22 additions & 18 deletions androidApp/src/main/java/org/nsh07/pomodoro/service/TimerService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerMode
import org.nsh07.pomodoro.utils.millisecondsToStr
import org.nsh07.pomodoro.widget.TimerAppWidget
import kotlin.text.Typography.middleDot
import kotlin.time.Duration.Companion.milliseconds

class TimerService : Service(), KoinComponent {

Expand Down Expand Up @@ -207,14 +208,15 @@ class TimerService : Service(), KoinComponent {
remainingTime: Int, paused: Boolean = false, complete: Boolean = false
) {
val settingsState = _settingsState.value
val currentTopic = stateRepository.currentTopic.value
val timerState = _timerState.value

if (complete) notificationBuilder.clearActions().addStopAlarmAction(this)

val totalTime = when (timerState.timerMode) {
TimerMode.FOCUS -> settingsState.focusTime.toInt()
TimerMode.SHORT_BREAK -> settingsState.shortBreakTime.toInt()
else -> settingsState.longBreakTime.toInt()
TimerMode.FOCUS -> currentTopic.focusTime.toInt()
TimerMode.SHORT_BREAK -> currentTopic.shortBreakTime.toInt()
else -> currentTopic.longBreakTime.toInt()
}

val currentTimer = when (timerState.timerMode) {
Expand Down Expand Up @@ -259,7 +261,7 @@ class TimerService : Service(), KoinComponent {
if (timerState.timerMode == TimerMode.FOCUS) (Long.MAX_VALUE - remainingTime).toInt()
else (totalTime - remainingTime)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA && !settingsState.singleProgressBar) {
(totalTime - remainingTime) + ((timerManager.cycles + 1) / 2) * settingsState.focusTime.toInt() + (timerManager.cycles / 2) * settingsState.shortBreakTime.toInt()
(totalTime - remainingTime) + ((timerManager.cycles + 1) / 2) * currentTopic.focusTime.toInt() + (timerManager.cycles / 2) * currentTopic.shortBreakTime.toInt()
} else (totalTime - remainingTime)
)
)
Expand Down Expand Up @@ -293,37 +295,38 @@ class TimerService : Service(), KoinComponent {

private fun updateProgressSegments() {
val settingsState = _settingsState.value
val currentTopic = stateRepository.currentTopic.value
notificationStyle = NotificationCompat.ProgressStyle()
.also {
// Add all the Focus, Short break and long break intervals in order
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA && !settingsState.singleProgressBar && !_timerState.value.infiniteFocus) {
// Android 16 and later supports live updates
// Set progress bar sections if on Baklava or later
for (i in 0..<settingsState.sessionLength * 2) {
for (i in 0..<currentTopic.sessionLength * 2) {
if (i % 2 == 0) it.addProgressSegment(
NotificationCompat.ProgressStyle.Segment(
settingsState.focusTime.toInt()
currentTopic.focusTime.toInt()
)
.setColor(cs.primary.toArgb())
)
else if (i != (settingsState.sessionLength * 2 - 1)) it.addProgressSegment(
else if (i != (currentTopic.sessionLength * 2 - 1)) it.addProgressSegment(
NotificationCompat.ProgressStyle.Segment(
settingsState.shortBreakTime.toInt()
currentTopic.shortBreakTime.toInt()
).setColor(cs.tertiary.toArgb())
)
else it.addProgressSegment(
NotificationCompat.ProgressStyle.Segment(
settingsState.longBreakTime.toInt()
currentTopic.longBreakTime.toInt()
).setColor(cs.tertiary.toArgb())
)
}
} else {
it.addProgressSegment(
NotificationCompat.ProgressStyle.Segment(
when (_timerState.value.timerMode) {
TimerMode.FOCUS -> settingsState.focusTime.toInt()
TimerMode.SHORT_BREAK -> settingsState.shortBreakTime.toInt()
else -> settingsState.longBreakTime.toInt()
TimerMode.FOCUS -> currentTopic.focusTime.toInt()
TimerMode.SHORT_BREAK -> currentTopic.shortBreakTime.toInt()
else -> currentTopic.longBreakTime.toInt()
}
)
)
Expand All @@ -338,7 +341,7 @@ class TimerService : Service(), KoinComponent {
activityCallbacks.activityTurnScreenOn(true)

autoAlarmStopScope = CoroutineScope(Dispatchers.IO).launch {
delay(1 * 60 * 1000)
delay((1 * 60 * 1000).milliseconds)
stopAlarm(fromAutoStop = true)
}

Expand Down Expand Up @@ -374,6 +377,7 @@ class TimerService : Service(), KoinComponent {
updateProgressSegments() // Make sure notification style is initialized

val settingsState = _settingsState.value
val currentTopic = stateRepository.currentTopic.value
autoAlarmStopScope?.cancel()

if (settingsState.alarmEnabled) {
Expand All @@ -398,13 +402,13 @@ class TimerService : Service(), KoinComponent {
)
showTimerNotification(
when (_timerState.value.timerMode) {
TimerMode.FOCUS -> settingsState.focusTime.toInt()
TimerMode.SHORT_BREAK -> settingsState.shortBreakTime.toInt()
else -> settingsState.longBreakTime.toInt()
TimerMode.FOCUS -> currentTopic.focusTime.toInt()
TimerMode.SHORT_BREAK -> currentTopic.shortBreakTime.toInt()
else -> currentTopic.longBreakTime.toInt()
}, paused = true, complete = false
)

if (settingsState.autostartNextSession && !fromAutoStop) // auto start next session
if (currentTopic.autostartNextSession && !fromAutoStop) // auto start next session
toggleTimer()

CoroutineScope(Dispatchers.IO).launch {
Expand Down Expand Up @@ -444,7 +448,7 @@ class TimerService : Service(), KoinComponent {
}

private fun setDoNotDisturb(doNotDisturb: Boolean) {
if (_settingsState.value.dndEnabled && notificationManagerService.isNotificationPolicyAccessGranted()) {
if (stateRepository.currentTopic.value.dndEnabled && notificationManagerService.isNotificationPolicyAccessGranted()) {
if (doNotDisturb) {
notificationManagerService.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_PRIORITY)
} else notificationManagerService.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL)
Expand Down
Loading
Loading