Skip to content

Commit f898556

Browse files
authored
Merge pull request #132 from nsh07/architecture-cleanup
Architecture cleanup (part 1)
2 parents af899c0 + 8f22452 commit f898556

File tree

23 files changed

+419
-259
lines changed

23 files changed

+419
-259
lines changed

app/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ android {
6161
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
6262
)
6363
}
64+
debug {
65+
applicationIdSuffix = ".debug"
66+
}
6467
}
6568

6669
flavorDimensions += "version"

app/src/foss/java/org/nsh07/pomodoro/billing/FossBillingManager.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import kotlinx.coroutines.flow.asStateFlow
2525
*/
2626
class FossBillingManager : BillingManager {
2727
override val isPlus = MutableStateFlow(true).asStateFlow()
28-
override val isLoaded = MutableStateFlow(true).asStateFlow()
2928
}
3029

3130
object BillingManagerProvider {
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright (c) 2025 Nishant Mishra
3+
*
4+
* This file is part of Tomato - a minimalist pomodoro timer for Android.
5+
*
6+
* Tomato is free software: you can redistribute it and/or modify it under the terms of the GNU
7+
* General Public License as published by the Free Software Foundation, either version 3 of the
8+
* License, or (at your option) any later version.
9+
*
10+
* Tomato is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
11+
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
12+
* Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along with Tomato.
15+
* If not, see <https://www.gnu.org/licenses/>.
16+
*/
17+
18+
package org.nsh07.pomodoro.ui.settingsScreen.components
19+
20+
import androidx.compose.foundation.layout.Arrangement
21+
import androidx.compose.foundation.layout.Row
22+
import androidx.compose.foundation.layout.height
23+
import androidx.compose.foundation.layout.size
24+
import androidx.compose.material3.Button
25+
import androidx.compose.material3.ButtonColors
26+
import androidx.compose.material3.Icon
27+
import androidx.compose.material3.Text
28+
import androidx.compose.runtime.Composable
29+
import androidx.compose.ui.Alignment
30+
import androidx.compose.ui.Modifier
31+
import androidx.compose.ui.platform.LocalUriHandler
32+
import androidx.compose.ui.res.painterResource
33+
import androidx.compose.ui.res.stringResource
34+
import androidx.compose.ui.unit.dp
35+
import org.nsh07.pomodoro.R
36+
37+
@Composable
38+
fun TopButton(
39+
buttonColors: ButtonColors,
40+
modifier: Modifier = Modifier
41+
) {
42+
val uriHandler = LocalUriHandler.current
43+
Button(
44+
colors = buttonColors,
45+
onClick = { uriHandler.openUri("https://coff.ee/nsh07") },
46+
modifier = modifier
47+
) {
48+
Row(
49+
horizontalArrangement = Arrangement.spacedBy(8.dp),
50+
verticalAlignment = Alignment.CenterVertically
51+
) {
52+
Icon(
53+
painterResource(R.drawable.bmc),
54+
contentDescription = null,
55+
modifier = Modifier.height(24.dp)
56+
)
57+
58+
Text(text = stringResource(R.string.bmc))
59+
}
60+
}
61+
}
62+
63+
@Composable
64+
fun BottomButton(
65+
buttonColors: ButtonColors,
66+
modifier: Modifier = Modifier
67+
) {
68+
val uriHandler = LocalUriHandler.current
69+
Button(
70+
colors = buttonColors,
71+
onClick = { uriHandler.openUri("https://hosted.weblate.org/engage/tomato/") },
72+
modifier = modifier
73+
) {
74+
Row(
75+
horizontalArrangement = Arrangement.spacedBy(8.dp),
76+
verticalAlignment = Alignment.CenterVertically
77+
) {
78+
Icon(
79+
painterResource(R.drawable.weblate),
80+
contentDescription = null,
81+
modifier = Modifier.size(20.dp)
82+
)
83+
84+
Text(text = stringResource(R.string.help_with_translation))
85+
}
86+
}
87+
}

app/src/main/java/org/nsh07/pomodoro/MainActivity.kt

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -50,34 +50,22 @@ class MainActivity : ComponentActivity() {
5050
}
5151

5252
setContent {
53-
val preferencesState by settingsViewModel.preferencesState.collectAsStateWithLifecycle()
53+
val settingsState by settingsViewModel.settingsState.collectAsStateWithLifecycle()
5454

55-
val darkTheme = when (preferencesState.theme) {
55+
val darkTheme = when (settingsState.theme) {
5656
"dark" -> true
5757
"light" -> false
5858
else -> isSystemInDarkTheme()
5959
}
6060

61-
val seed = preferencesState.colorScheme.toColor()
61+
val seed = settingsState.colorScheme.toColor()
6262

6363
val isPlus by settingsViewModel.isPlus.collectAsStateWithLifecycle()
64-
val isPurchaseStateLoaded by settingsViewModel.isPurchaseStateLoaded.collectAsStateWithLifecycle()
65-
val isSettingsLoaded by settingsViewModel.isSettingsLoaded.collectAsStateWithLifecycle()
66-
67-
LaunchedEffect(isPurchaseStateLoaded, isPlus, isSettingsLoaded) {
68-
if (isPurchaseStateLoaded && isSettingsLoaded) {
69-
if (!isPlus) {
70-
settingsViewModel.resetPaywalledSettings()
71-
} else {
72-
settingsViewModel.reloadSettings()
73-
}
74-
}
75-
}
7664

7765
TomatoTheme(
7866
darkTheme = darkTheme,
7967
seedColor = seed,
80-
blackTheme = preferencesState.blackTheme
68+
blackTheme = settingsState.blackTheme
8169
) {
8270
val colorScheme = colorScheme
8371
LaunchedEffect(colorScheme) {
@@ -86,7 +74,7 @@ class MainActivity : ComponentActivity() {
8674

8775
AppScreen(
8876
isPlus = isPlus,
89-
isAODEnabled = preferencesState.aodEnabled,
77+
isAODEnabled = settingsState.aodEnabled,
9078
setTimerFrequency = {
9179
appContainer.appTimerRepository.timerFrequency = it
9280
}
@@ -105,6 +93,6 @@ class MainActivity : ComponentActivity() {
10593
override fun onStart() {
10694
super.onStart()
10795
// Increase the timer loop frequency again when visible to make the progress smoother
108-
appContainer.appTimerRepository.timerFrequency = 10f
96+
appContainer.appTimerRepository.timerFrequency = 60f
10997
}
11098
}

app/src/main/java/org/nsh07/pomodoro/billing/BillingManager.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,4 @@ import kotlinx.coroutines.flow.StateFlow
2121

2222
interface BillingManager {
2323
val isPlus: StateFlow<Boolean>
24-
val isLoaded: StateFlow<Boolean>
2524
}

app/src/main/java/org/nsh07/pomodoro/data/AppContainer.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import android.content.Context
2323
import androidx.compose.ui.graphics.Color
2424
import androidx.compose.ui.graphics.toArgb
2525
import androidx.core.app.NotificationCompat
26+
import androidx.core.app.NotificationCompat.VISIBILITY_PUBLIC
2627
import androidx.core.app.NotificationManagerCompat
2728
import kotlinx.coroutines.flow.MutableStateFlow
2829
import org.nsh07.pomodoro.R
@@ -83,6 +84,7 @@ class DefaultAppContainer(context: Context) : AppContainer {
8384
.setSilent(true)
8485
.setOngoing(true)
8586
.setRequestPromotedOngoing(true)
87+
.setVisibility(VISIBILITY_PUBLIC)
8688
}
8789

8890
override val timerState: MutableStateFlow<TimerState> by lazy {

app/src/main/java/org/nsh07/pomodoro/data/TimerRepository.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class AppTimerRepository : TimerRepository {
5454
override var shortBreakTime = 5 * 60 * 1000L
5555
override var longBreakTime = 15 * 60 * 1000L
5656
override var sessionLength = 4
57-
override var timerFrequency: Float = 10f
57+
override var timerFrequency: Float = 60f
5858
override var alarmEnabled = true
5959
override var vibrateEnabled = true
6060
override var dndEnabled: Boolean = false

app/src/main/java/org/nsh07/pomodoro/ui/AlwaysOnDisplay.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ fun SharedTransitionScope.AlwaysOnDisplay(
113113
}
114114

115115
onDispose {
116-
setTimerFrequency(10f)
116+
setTimerFrequency(60f)
117117
window.clearFlags(
118118
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or
119119
WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON

app/src/main/java/org/nsh07/pomodoro/ui/AppScreen.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ import androidx.compose.runtime.Composable
4545
import androidx.compose.runtime.getValue
4646
import androidx.compose.runtime.mutableStateOf
4747
import androidx.compose.runtime.remember
48-
import androidx.compose.runtime.rememberUpdatedState
4948
import androidx.compose.runtime.setValue
5049
import androidx.compose.ui.Modifier
5150
import androidx.compose.ui.platform.LocalContext
@@ -79,9 +78,7 @@ fun AppScreen(
7978
val context = LocalContext.current
8079

8180
val uiState by timerViewModel.timerState.collectAsStateWithLifecycle()
82-
val remainingTime by timerViewModel.time.collectAsStateWithLifecycle()
83-
84-
val progress by rememberUpdatedState((uiState.totalTime.toFloat() - remainingTime) / uiState.totalTime)
81+
val progress by timerViewModel.progress.collectAsStateWithLifecycle()
8582

8683
val layoutDirection = LocalLayoutDirection.current
8784
val motionScheme = motionScheme

app/src/main/java/org/nsh07/pomodoro/ui/settingsScreen/SettingsScreen.kt

Lines changed: 13 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ package org.nsh07.pomodoro.ui.settingsScreen
1919

2020
import android.annotation.SuppressLint
2121
import android.app.LocaleManager
22-
import android.content.Intent
23-
import android.net.Uri
2422
import android.os.Build
2523
import androidx.compose.animation.fadeIn
2624
import androidx.compose.animation.fadeOut
@@ -55,7 +53,6 @@ import androidx.compose.runtime.setValue
5553
import androidx.compose.runtime.snapshots.SnapshotStateList
5654
import androidx.compose.ui.Alignment
5755
import androidx.compose.ui.Modifier
58-
import androidx.compose.ui.graphics.Color
5956
import androidx.compose.ui.input.nestedscroll.nestedScroll
6057
import androidx.compose.ui.platform.LocalContext
6158
import androidx.compose.ui.res.painterResource
@@ -68,7 +65,6 @@ import androidx.lifecycle.viewmodel.compose.viewModel
6865
import androidx.navigation3.runtime.entryProvider
6966
import androidx.navigation3.ui.NavDisplay
7067
import org.nsh07.pomodoro.R
71-
import org.nsh07.pomodoro.service.TimerService
7268
import org.nsh07.pomodoro.ui.Screen
7369
import org.nsh07.pomodoro.ui.settingsScreen.components.AboutCard
7470
import org.nsh07.pomodoro.ui.settingsScreen.components.ClickableListItem
@@ -77,7 +73,8 @@ import org.nsh07.pomodoro.ui.settingsScreen.components.PlusPromo
7773
import org.nsh07.pomodoro.ui.settingsScreen.screens.AlarmSettings
7874
import org.nsh07.pomodoro.ui.settingsScreen.screens.AppearanceSettings
7975
import org.nsh07.pomodoro.ui.settingsScreen.screens.TimerSettings
80-
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.PreferencesState
76+
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsAction
77+
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsState
8178
import org.nsh07.pomodoro.ui.settingsScreen.viewModel.SettingsViewModel
8279
import org.nsh07.pomodoro.ui.settingsScreens
8380
import org.nsh07.pomodoro.ui.theme.AppFonts.robotoFlexTopBar
@@ -92,8 +89,6 @@ fun SettingsScreenRoot(
9289
modifier: Modifier = Modifier,
9390
viewModel: SettingsViewModel = viewModel(factory = SettingsViewModel.Factory)
9491
) {
95-
val context = LocalContext.current
96-
9792
val backStack = viewModel.backStack
9893

9994
DisposableEffect(Unit) {
@@ -106,12 +101,8 @@ fun SettingsScreenRoot(
106101
val longBreakTimeInputFieldState = viewModel.longBreakTimeTextFieldState
107102

108103
val isPlus by viewModel.isPlus.collectAsStateWithLifecycle()
109-
val alarmEnabled by viewModel.alarmEnabled.collectAsStateWithLifecycle(true)
110-
val vibrateEnabled by viewModel.vibrateEnabled.collectAsStateWithLifecycle(true)
111-
val dndEnabled by viewModel.dndEnabled.collectAsStateWithLifecycle(false)
112-
val alarmSound by viewModel.alarmSound.collectAsStateWithLifecycle(viewModel.currentAlarmSound)
113104

114-
val preferencesState by viewModel.preferencesState.collectAsStateWithLifecycle()
105+
val settingsState by viewModel.settingsState.collectAsStateWithLifecycle()
115106

116107
val sessionsSliderState = rememberSaveable(
117108
saver = SliderState.Saver(
@@ -124,30 +115,13 @@ fun SettingsScreenRoot(
124115

125116
SettingsScreen(
126117
isPlus = isPlus,
127-
preferencesState = preferencesState,
118+
settingsState = settingsState,
128119
backStack = backStack,
129120
focusTimeInputFieldState = focusTimeInputFieldState,
130121
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
131122
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
132123
sessionsSliderState = sessionsSliderState,
133-
alarmEnabled = alarmEnabled,
134-
vibrateEnabled = vibrateEnabled,
135-
dndEnabled = dndEnabled,
136-
alarmSound = alarmSound,
137-
onAlarmEnabledChange = viewModel::saveAlarmEnabled,
138-
onVibrateEnabledChange = viewModel::saveVibrateEnabled,
139-
onBlackThemeChange = viewModel::saveBlackTheme,
140-
onAodEnabledChange = viewModel::saveAodEnabled,
141-
onDndEnabledChange = viewModel::saveDndEnabled,
142-
onAlarmSoundChanged = {
143-
viewModel.saveAlarmSound(it)
144-
Intent(context, TimerService::class.java).apply {
145-
action = TimerService.Actions.RESET.toString()
146-
context.startService(this)
147-
}
148-
},
149-
onThemeChange = viewModel::saveTheme,
150-
onColorSchemeChange = viewModel::saveColorScheme,
124+
onAction = viewModel::onAction,
151125
setShowPaywall = setShowPaywall,
152126
modifier = modifier
153127
)
@@ -158,24 +132,13 @@ fun SettingsScreenRoot(
158132
@Composable
159133
private fun SettingsScreen(
160134
isPlus: Boolean,
161-
preferencesState: PreferencesState,
135+
settingsState: SettingsState,
162136
backStack: SnapshotStateList<Screen.Settings>,
163137
focusTimeInputFieldState: TextFieldState,
164138
shortBreakTimeInputFieldState: TextFieldState,
165139
longBreakTimeInputFieldState: TextFieldState,
166140
sessionsSliderState: SliderState,
167-
alarmEnabled: Boolean,
168-
vibrateEnabled: Boolean,
169-
dndEnabled: Boolean,
170-
alarmSound: String,
171-
onAlarmEnabledChange: (Boolean) -> Unit,
172-
onVibrateEnabledChange: (Boolean) -> Unit,
173-
onBlackThemeChange: (Boolean) -> Unit,
174-
onAodEnabledChange: (Boolean) -> Unit,
175-
onDndEnabledChange: (Boolean) -> Unit,
176-
onAlarmSoundChanged: (Uri?) -> Unit,
177-
onThemeChange: (String) -> Unit,
178-
onColorSchemeChange: (Color) -> Unit,
141+
onAction: (SettingsAction) -> Unit,
179142
setShowPaywall: (Boolean) -> Unit,
180143
modifier: Modifier = Modifier
181144
) {
@@ -312,38 +275,29 @@ private fun SettingsScreen(
312275

313276
entry<Screen.Settings.Alarm> {
314277
AlarmSettings(
315-
preferencesState = preferencesState,
316-
alarmEnabled = alarmEnabled,
317-
vibrateEnabled = vibrateEnabled,
318-
alarmSound = alarmSound,
319-
onAlarmEnabledChange = onAlarmEnabledChange,
320-
onVibrateEnabledChange = onVibrateEnabledChange,
321-
onAlarmSoundChanged = onAlarmSoundChanged,
278+
settingsState = settingsState,
279+
onAction = onAction,
322280
onBack = backStack::removeLastOrNull
323281
)
324282
}
325283
entry<Screen.Settings.Appearance> {
326284
AppearanceSettings(
327-
preferencesState = preferencesState,
285+
settingsState = settingsState,
328286
isPlus = isPlus,
329-
onBlackThemeChange = onBlackThemeChange,
330-
onThemeChange = onThemeChange,
331-
onColorSchemeChange = onColorSchemeChange,
287+
onAction = onAction,
332288
setShowPaywall = setShowPaywall,
333289
onBack = backStack::removeLastOrNull
334290
)
335291
}
336292
entry<Screen.Settings.Timer> {
337293
TimerSettings(
338294
isPlus = isPlus,
339-
aodEnabled = preferencesState.aodEnabled,
340-
dndEnabled = dndEnabled,
295+
settingsState = settingsState,
341296
focusTimeInputFieldState = focusTimeInputFieldState,
342297
shortBreakTimeInputFieldState = shortBreakTimeInputFieldState,
343298
longBreakTimeInputFieldState = longBreakTimeInputFieldState,
344299
sessionsSliderState = sessionsSliderState,
345-
onAodEnabledChange = onAodEnabledChange,
346-
onDndEnabledChange = onDndEnabledChange,
300+
onAction = onAction,
347301
setShowPaywall = setShowPaywall,
348302
onBack = backStack::removeLastOrNull,
349303
)

0 commit comments

Comments
 (0)