fix: Improve TimerService code by using lazy init instead of lateinit var

This commit is contained in:
Nishant Mishra
2025-09-15 16:28:30 +05:30
parent 9728216c28
commit f4f81ee94c

View File

@@ -20,41 +20,35 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.nsh07.pomodoro.R import org.nsh07.pomodoro.R
import org.nsh07.pomodoro.TomatoApplication import org.nsh07.pomodoro.TomatoApplication
import org.nsh07.pomodoro.data.AppContainer
import org.nsh07.pomodoro.data.StatRepository
import org.nsh07.pomodoro.data.TimerRepository
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerMode import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerMode
import org.nsh07.pomodoro.ui.timerScreen.viewModel.TimerState
import org.nsh07.pomodoro.utils.millisecondsToStr import org.nsh07.pomodoro.utils.millisecondsToStr
import kotlin.text.Typography.middleDot import kotlin.text.Typography.middleDot
class TimerService : Service() { class TimerService : Service() {
private lateinit var appContainer: AppContainer private val appContainer by lazy {
(application as TomatoApplication).container
private lateinit var timerRepository: TimerRepository
private lateinit var statRepository: StatRepository
private lateinit var notificationManager: NotificationManagerCompat
private lateinit var notificationBuilder: NotificationCompat.Builder
private lateinit var _timerState: MutableStateFlow<TimerState>
private lateinit var _time: MutableStateFlow<Long>
val timeStateFlow by lazy {
_time.asStateFlow()
} }
var time: Long private val timerRepository by lazy { appContainer.appTimerRepository }
private val statRepository by lazy { appContainer.appStatRepository }
private val notificationManager by lazy { NotificationManagerCompat.from(this) }
private val notificationBuilder by lazy { appContainer.notificationBuilder }
private val _timerState by lazy { appContainer.timerState }
private val _time by lazy { appContainer.time }
private val timeStateFlow by lazy { _time.asStateFlow() }
private var time: Long
get() = timeStateFlow.value get() = timeStateFlow.value
set(value) = _time.update { value } set(value) = _time.update { value }
lateinit var timerState: StateFlow<TimerState> private val timerState by lazy { _timerState.asStateFlow() }
private var cycles = 0 private var cycles = 0
private var startTime = 0L private var startTime = 0L
@@ -67,19 +61,16 @@ class TimerService : Service() {
private val alarm by lazy { private val alarm by lazy {
MediaPlayer.create( MediaPlayer.create(
this, this, Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI
Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI
) )
} }
private val vibrator by lazy { private val vibrator by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager = val vibratorManager = getSystemService(VIBRATOR_MANAGER_SERVICE) as VibratorManager
getSystemService(VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator vibratorManager.defaultVibrator
} else { } else {
@Suppress("DEPRECATION") @Suppress("DEPRECATION") getSystemService(VIBRATOR_SERVICE) as Vibrator
getSystemService(VIBRATOR_SERVICE) as Vibrator
} }
} }
@@ -89,18 +80,6 @@ class TimerService : Service() {
return null return null
} }
override fun onCreate() {
appContainer = (application as TomatoApplication).container
timerRepository = appContainer.appTimerRepository
statRepository = appContainer.appStatRepository
notificationManager = NotificationManagerCompat.from(this)
notificationBuilder = appContainer.notificationBuilder
_timerState = appContainer.timerState
_time = appContainer.time
timerState = _timerState.asStateFlow()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) { when (intent?.action) {
Actions.TOGGLE.toString() -> { Actions.TOGGLE.toString() -> {
@@ -123,26 +102,18 @@ class TimerService : Service() {
private fun toggleTimer() { private fun toggleTimer() {
if (timerState.value.timerRunning) { if (timerState.value.timerRunning) {
notificationBuilder notificationBuilder.clearActions().addTimerActions(
.clearActions() this, R.drawable.play, "Start"
.addTimerActions( )
this,
R.drawable.play,
"Start"
)
showTimerNotification(time.toInt(), paused = true) showTimerNotification(time.toInt(), paused = true)
_timerState.update { currentState -> _timerState.update { currentState ->
currentState.copy(timerRunning = false) currentState.copy(timerRunning = false)
} }
pauseTime = SystemClock.elapsedRealtime() pauseTime = SystemClock.elapsedRealtime()
} else { } else {
notificationBuilder notificationBuilder.clearActions().addTimerActions(
.clearActions() this, R.drawable.pause, "Stop"
.addTimerActions( )
this,
R.drawable.pause,
"Stop"
)
_timerState.update { it.copy(timerRunning = true) } _timerState.update { it.copy(timerRunning = true) }
if (pauseTime != 0L) pauseDuration += SystemClock.elapsedRealtime() - pauseTime if (pauseTime != 0L) pauseDuration += SystemClock.elapsedRealtime() - pauseTime
@@ -214,59 +185,52 @@ class TimerService : Service() {
else (remainingTime.toFloat() / 60000f).toInt() else (remainingTime.toFloat() / 60000f).toInt()
notificationManager.notify( notificationManager.notify(
1, 1, notificationBuilder.setContentTitle(
notificationBuilder if (!complete) {
.setContentTitle( "$currentTimer $middleDot $remainingTimeString min remaining" + if (paused) " $middleDot Paused" else ""
if (!complete) { } else "$currentTimer $middleDot Completed"
"$currentTimer $middleDot $remainingTimeString min remaining" + if (paused) " $middleDot Paused" else "" ).setContentText("Up next: $nextTimer (${timerState.value.nextTimeStr})")
} else "$currentTimer $middleDot Completed" .setStyle(NotificationCompat.ProgressStyle().also {
) // Add all the Focus, Short break and long break intervals in order
.setContentText("Up next: $nextTimer (${timerState.value.nextTimeStr})") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
.setStyle( // Android 16 and later supports live updates
NotificationCompat.ProgressStyle().also { // Set progress bar sections if on Baklava or later
// Add all the Focus, Short break and long break intervals in order for (i in 0..<timerRepository.sessionLength * 2) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) { if (i % 2 == 0) it.addProgressSegment(
// Android 16 and later supports live updates
// Set progress bar sections if on Baklava or later
for (i in 0..<timerRepository.sessionLength * 2) {
if (i % 2 == 0) it.addProgressSegment(
NotificationCompat.ProgressStyle.Segment(
timerRepository.focusTime.toInt()
).setColor(cs.primary.toArgb())
)
else if (i != (timerRepository.sessionLength * 2 - 1)) it.addProgressSegment(
NotificationCompat.ProgressStyle.Segment(
timerRepository.shortBreakTime.toInt()
).setColor(cs.tertiary.toArgb())
)
else it.addProgressSegment(
NotificationCompat.ProgressStyle.Segment(
timerRepository.longBreakTime.toInt()
).setColor(cs.tertiary.toArgb())
)
}
} else {
it.addProgressSegment(
NotificationCompat.ProgressStyle.Segment( NotificationCompat.ProgressStyle.Segment(
when (timerState.value.timerMode) { timerRepository.focusTime.toInt()
TimerMode.FOCUS -> timerRepository.focusTime.toInt() ).setColor(cs.primary.toArgb())
TimerMode.SHORT_BREAK -> timerRepository.shortBreakTime.toInt() )
else -> timerRepository.longBreakTime.toInt() else if (i != (timerRepository.sessionLength * 2 - 1)) it.addProgressSegment(
} NotificationCompat.ProgressStyle.Segment(
) timerRepository.shortBreakTime.toInt()
).setColor(cs.tertiary.toArgb())
)
else it.addProgressSegment(
NotificationCompat.ProgressStyle.Segment(
timerRepository.longBreakTime.toInt()
).setColor(cs.tertiary.toArgb())
) )
} }
} } else {
.setProgress( // Set the current progress by filling the previous intervals and part of the current interval it.addProgressSegment(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) { NotificationCompat.ProgressStyle.Segment(
(totalTime - remainingTime) + ((cycles + 1) / 2) * timerRepository.focusTime.toInt() + (cycles / 2) * timerRepository.shortBreakTime.toInt() when (timerState.value.timerMode) {
} else (totalTime - remainingTime) TimerMode.FOCUS -> timerRepository.focusTime.toInt()
TimerMode.SHORT_BREAK -> timerRepository.shortBreakTime.toInt()
else -> timerRepository.longBreakTime.toInt()
}
)
) )
) }
}
.setProgress( // Set the current progress by filling the previous intervals and part of the current interval
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
(totalTime - remainingTime) + ((cycles + 1) / 2) * timerRepository.focusTime.toInt() + (cycles / 2) * timerRepository.shortBreakTime.toInt()
} else (totalTime - remainingTime)
))
.setWhen(System.currentTimeMillis() + remainingTime) // Sets the Live Activity/Now Bar chip time .setWhen(System.currentTimeMillis() + remainingTime) // Sets the Live Activity/Now Bar chip time
.setShortCriticalText(millisecondsToStr(time.coerceAtLeast(0))) .setShortCriticalText(millisecondsToStr(time.coerceAtLeast(0))).build())
.build()
)
if (complete) { if (complete) {
startAlarm() startAlarm()
@@ -376,9 +340,7 @@ class TimerService : Service() {
TimerMode.FOCUS -> timerRepository.focusTime.toInt() TimerMode.FOCUS -> timerRepository.focusTime.toInt()
TimerMode.SHORT_BREAK -> timerRepository.shortBreakTime.toInt() TimerMode.SHORT_BREAK -> timerRepository.shortBreakTime.toInt()
else -> timerRepository.longBreakTime.toInt() else -> timerRepository.longBreakTime.toInt()
}, }, paused = true, complete = false
paused = true,
complete = false
) )
} }