diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ccfd8486..f2896f58 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -69,9 +69,17 @@
-
+
+
+
+
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/di/PomodoroModule.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/di/PomodoroModule.kt
new file mode 100644
index 00000000..cc6444b3
--- /dev/null
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/di/PomodoroModule.kt
@@ -0,0 +1,27 @@
+package com.pomonyang.mohanyang.presentation.di
+
+import com.pomonyang.mohanyang.data.repository.pomodoro.PomodoroTimerRepository
+import com.pomonyang.mohanyang.presentation.service.PomodoroTimer
+import com.pomonyang.mohanyang.presentation.service.focus.FocusTimer
+import com.pomonyang.mohanyang.presentation.service.rest.RestTimer
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ServiceComponent
+
+@Module
+@InstallIn(ServiceComponent::class)
+internal object PomodoroModule {
+
+ @Provides
+ @FocusTimerType
+ fun provideFocusTimer(
+ timerRepository: PomodoroTimerRepository
+ ): PomodoroTimer = FocusTimer(timerRepository = timerRepository)
+
+ @Provides
+ @RestTimerType
+ fun provideRestTimer(
+ timerRepository: PomodoroTimerRepository
+ ): PomodoroTimer = RestTimer(timerRepository = timerRepository)
+}
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/di/Qualifier.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/di/Qualifier.kt
index 0c408aba..74f9b48d 100644
--- a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/di/Qualifier.kt
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/di/Qualifier.kt
@@ -5,3 +5,11 @@ import javax.inject.Qualifier
@Qualifier
@Retention(AnnotationRetention.BINARY)
annotation class PomodoroNotification
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class FocusTimerType
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class RestTimerType
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/noti/PomodoroNotificationManager.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/noti/PomodoroNotificationManager.kt
new file mode 100644
index 00000000..a988ed87
--- /dev/null
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/noti/PomodoroNotificationManager.kt
@@ -0,0 +1,66 @@
+package com.pomonyang.mohanyang.presentation.noti
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import androidx.core.app.NotificationCompat
+import com.pomonyang.mohanyang.presentation.di.PomodoroNotification
+import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.POMODORO_NOTIFICATION_CHANNEL_ID
+import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.POMODORO_NOTIFICATION_CHANNEL_NAME
+import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.POMODORO_NOTIFICATION_ID
+import javax.inject.Inject
+import timber.log.Timber
+
+internal class PomodoroNotificationManager @Inject constructor(
+ @PomodoroNotification private val notificationBuilder: NotificationCompat.Builder,
+ private val notificationManager: NotificationManager
+) {
+ fun createNotification(isFocus: Boolean): Notification {
+ Timber.tag("TIMER").d("createNotification > isFocus $isFocus")
+ val notificationChannelId = POMODORO_NOTIFICATION_CHANNEL_ID
+ val notificationChannel = NotificationChannel(
+ notificationChannelId,
+ POMODORO_NOTIFICATION_CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_DEFAULT
+ )
+ notificationManager.createNotificationChannel(notificationChannel)
+
+ return notificationBuilder
+ .setContentText(if (isFocus) "집중 시간이다냥" else "휴식 시간이다냥")
+ .build().apply {
+ flags = Notification.FLAG_NO_CLEAR
+ }
+ }
+
+ fun notifyFocusEnd() {
+ notificationManager.notify(
+ POMODORO_NOTIFICATION_ID,
+ notificationBuilder.setContentText("집중 시간이 끝났습니다!").build()
+ )
+ }
+
+ fun notifyFocusExceed() {
+ notificationManager.notify(
+ POMODORO_NOTIFICATION_ID,
+ notificationBuilder.setContentText(
+ "너무 오랫동안 자리를 비웠다냥"
+ ).build()
+ )
+ }
+
+ fun notifyRestEnd() {
+ notificationManager.notify(
+ POMODORO_NOTIFICATION_ID,
+ notificationBuilder.setContentText("휴식 시간이 끝났습니다!").build()
+ )
+ }
+
+ fun notifyRestExceed() {
+ notificationManager.notify(
+ POMODORO_NOTIFICATION_ID,
+ notificationBuilder.setContentText(
+ "너무 오랫동안 자리를 비웠다냥"
+ ).build()
+ )
+ }
+}
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/screen/home/setting/PomodoroSettingScreen.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/screen/home/setting/PomodoroSettingScreen.kt
index 4a27144b..81a91d01 100644
--- a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/screen/home/setting/PomodoroSettingScreen.kt
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/screen/home/setting/PomodoroSettingScreen.kt
@@ -62,7 +62,8 @@ import com.pomonyang.mohanyang.presentation.theme.MnTheme
import com.pomonyang.mohanyang.presentation.util.MnNotificationManager
import com.pomonyang.mohanyang.presentation.util.clickableSingle
import com.pomonyang.mohanyang.presentation.util.collectWithLifecycle
-import com.pomonyang.mohanyang.presentation.util.stopTimer
+import com.pomonyang.mohanyang.presentation.util.stopFocusTimer
+import com.pomonyang.mohanyang.presentation.util.stopRestTimer
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -113,8 +114,8 @@ fun PomodoroSettingRoute(
}
private fun stopAllNotification(context: Context) {
- context.stopTimer(false)
- context.stopTimer(true)
+ context.stopFocusTimer()
+ context.stopRestTimer()
MnNotificationManager.stopInterrupt(context)
}
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/screen/pomodoro/focus/PomodoroFocusScreen.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/screen/pomodoro/focus/PomodoroFocusScreen.kt
index 9b1e28d3..01ab7198 100644
--- a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/screen/pomodoro/focus/PomodoroFocusScreen.kt
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/screen/pomodoro/focus/PomodoroFocusScreen.kt
@@ -44,8 +44,8 @@ import com.pomonyang.mohanyang.presentation.util.DevicePreviews
import com.pomonyang.mohanyang.presentation.util.MnNotificationManager.startInterrupt
import com.pomonyang.mohanyang.presentation.util.MnNotificationManager.stopInterrupt
import com.pomonyang.mohanyang.presentation.util.collectWithLifecycle
-import com.pomonyang.mohanyang.presentation.util.startTimer
-import com.pomonyang.mohanyang.presentation.util.stopTimer
+import com.pomonyang.mohanyang.presentation.util.startFocusTimer
+import com.pomonyang.mohanyang.presentation.util.stopFocusTimer
@Composable
fun PomodoroFocusRoute(
@@ -98,7 +98,7 @@ fun PomodoroFocusRoute(
LaunchedEffect(state.maxFocusTime) {
if (state.maxFocusTime != 0) {
- context.startTimer(true, state.maxFocusTime)
+ context.startFocusTimer(state.maxFocusTime)
}
}
@@ -125,7 +125,7 @@ fun PomodoroFocusRoute(
private fun stopNotification(context: Context) {
stopInterrupt(context)
- context.stopTimer(true)
+ context.stopFocusTimer()
}
@Composable
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/screen/pomodoro/rest/PomodoroRestScreen.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/screen/pomodoro/rest/PomodoroRestScreen.kt
index 52216d56..fae897d2 100644
--- a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/screen/pomodoro/rest/PomodoroRestScreen.kt
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/screen/pomodoro/rest/PomodoroRestScreen.kt
@@ -40,8 +40,8 @@ import com.pomonyang.mohanyang.presentation.screen.pomodoro.PomodoroTimerViewMod
import com.pomonyang.mohanyang.presentation.theme.MnTheme
import com.pomonyang.mohanyang.presentation.util.DevicePreviews
import com.pomonyang.mohanyang.presentation.util.collectWithLifecycle
-import com.pomonyang.mohanyang.presentation.util.startTimer
-import com.pomonyang.mohanyang.presentation.util.stopTimer
+import com.pomonyang.mohanyang.presentation.util.startRestTimer
+import com.pomonyang.mohanyang.presentation.util.stopRestTimer
@Composable
fun PomodoroRestRoute(
@@ -59,12 +59,12 @@ fun PomodoroRestRoute(
when (effect) {
is PomodoroRestEffect.ShowSnackbar -> onShowSnackbar(effect.message, effect.iconRes)
PomodoroRestEffect.GoToHome -> {
- context.stopTimer(false)
+ context.stopRestTimer()
goToHome()
}
PomodoroRestEffect.GoToPomodoroFocus -> {
- context.stopTimer(false)
+ context.stopRestTimer()
goToFocus()
}
}
@@ -76,7 +76,7 @@ fun PomodoroRestRoute(
LaunchedEffect(timerState.maxRestTime) {
if (timerState.maxRestTime != 0) {
- context.startTimer(false, timerState.maxRestTime)
+ context.startRestTimer(timerState.maxRestTime)
}
}
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/PomodoroTimer.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/PomodoroTimer.kt
new file mode 100644
index 00000000..8be7eefe
--- /dev/null
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/PomodoroTimer.kt
@@ -0,0 +1,6 @@
+package com.pomonyang.mohanyang.presentation.service
+
+internal interface PomodoroTimer {
+ fun startTimer(maxTime: Int, eventHandler: PomodoroTimerEventHandler)
+ fun stopTimer()
+}
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/PomodoroTimerEventHandler.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/PomodoroTimerEventHandler.kt
new file mode 100644
index 00000000..e377c883
--- /dev/null
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/PomodoroTimerEventHandler.kt
@@ -0,0 +1,6 @@
+package com.pomonyang.mohanyang.presentation.service
+
+internal interface PomodoroTimerEventHandler {
+ fun onTimeEnd()
+ fun onTimeExceeded()
+}
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/util/PomodoroTimerServiceExtras.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/PomodoroTimerServiceExtras.kt
similarity index 56%
rename from presentation/src/main/java/com/pomonyang/mohanyang/presentation/util/PomodoroTimerServiceExtras.kt
rename to presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/PomodoroTimerServiceExtras.kt
index 96daf58b..3f692d57 100644
--- a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/util/PomodoroTimerServiceExtras.kt
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/PomodoroTimerServiceExtras.kt
@@ -1,7 +1,6 @@
-package com.pomonyang.mohanyang.presentation.util
+package com.pomonyang.mohanyang.presentation.service
-object PomodoroTimerServiceExtras {
- const val INTENT_TIMER_IS_FOCUS = "mohanyang.intent.TIMER_FOCUS"
+internal object PomodoroTimerServiceExtras {
const val INTENT_TIMER_MAX_TIME = "mohanyang.intent.MAX_TIME"
const val ACTION_TIMER_START = "mohanyang.action.TIMER_START"
const val ACTION_TIMER_STOP = "mohanyang.action.TIMER_STOP"
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/focus/FocusTimer.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/focus/FocusTimer.kt
new file mode 100644
index 00000000..b19480d5
--- /dev/null
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/focus/FocusTimer.kt
@@ -0,0 +1,52 @@
+package com.pomonyang.mohanyang.presentation.service.focus
+
+import com.pomonyang.mohanyang.data.repository.pomodoro.PomodoroTimerRepository
+import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.MAX_EXCEEDED_TIME
+import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.ONE_SECOND
+import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.TIMER_DELAY
+import com.pomonyang.mohanyang.presentation.service.PomodoroTimer
+import com.pomonyang.mohanyang.presentation.service.PomodoroTimerEventHandler
+import java.util.Timer
+import javax.inject.Inject
+import kotlin.concurrent.fixedRateTimer
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.launch
+import timber.log.Timber
+
+internal class FocusTimer @Inject constructor(
+ private val timerRepository: PomodoroTimerRepository
+) : PomodoroTimer {
+
+ private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
+ private var timer: Timer? = null
+ private var timeElapsed = 0
+
+ override fun startTimer(maxTime: Int, eventHandler: PomodoroTimerEventHandler) {
+ Timber.tag("TIMER").d("startFocus timer / maxTime : $maxTime")
+ if (timer == null) {
+ timeElapsed = 0
+ timer = fixedRateTimer(initialDelay = TIMER_DELAY, period = TIMER_DELAY) {
+ scope.launch {
+ timeElapsed += ONE_SECOND
+ timerRepository.incrementFocusedTime()
+
+ Timber.tag("TIMER").d("countFocusTime: $timeElapsed ")
+
+ if (timeElapsed >= maxTime) {
+ eventHandler.onTimeEnd()
+ } else if (timeElapsed >= maxTime + MAX_EXCEEDED_TIME) {
+ eventHandler.onTimeExceeded()
+ stopTimer()
+ }
+ }
+ }
+ }
+ }
+
+ override fun stopTimer() {
+ timer?.cancel()
+ timer = null
+ }
+}
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/focus/PomodoroFocusTimerService.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/focus/PomodoroFocusTimerService.kt
new file mode 100644
index 00000000..fea420f0
--- /dev/null
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/focus/PomodoroFocusTimerService.kt
@@ -0,0 +1,64 @@
+package com.pomonyang.mohanyang.presentation.service.focus
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import com.pomonyang.mohanyang.presentation.di.FocusTimerType
+import com.pomonyang.mohanyang.presentation.noti.PomodoroNotificationManager
+import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.POMODORO_NOTIFICATION_ID
+import com.pomonyang.mohanyang.presentation.service.PomodoroTimer
+import com.pomonyang.mohanyang.presentation.service.PomodoroTimerEventHandler
+import com.pomonyang.mohanyang.presentation.service.PomodoroTimerServiceExtras
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+import timber.log.Timber
+
+@AndroidEntryPoint
+internal class PomodoroFocusTimerService :
+ Service(),
+ PomodoroTimerEventHandler {
+
+ @FocusTimerType
+ @Inject
+ lateinit var focusTimer: PomodoroTimer
+
+ @Inject
+ lateinit var pomodoroNotificationManager: PomodoroNotificationManager
+
+ override fun onBind(intent: Intent?): IBinder? = null
+
+ override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
+ val maxTime = intent.getIntExtra(PomodoroTimerServiceExtras.INTENT_TIMER_MAX_TIME, 0)
+ Timber.tag("TIMER").d("onStartCommand > ${intent.action} / maxTime: $maxTime")
+ when (intent.action) {
+ PomodoroTimerServiceExtras.ACTION_TIMER_START -> {
+ startForeground(
+ POMODORO_NOTIFICATION_ID,
+ pomodoroNotificationManager.createNotification(true)
+ )
+ focusTimer.startTimer(maxTime, this)
+ }
+
+ PomodoroTimerServiceExtras.ACTION_TIMER_STOP -> {
+ stopForeground(STOP_FOREGROUND_REMOVE)
+ focusTimer.stopTimer()
+ }
+ }
+
+ return START_NOT_STICKY
+ }
+
+ override fun onTimeEnd() {
+ pomodoroNotificationManager.notifyFocusEnd()
+ }
+
+ override fun onTimeExceeded() {
+ pomodoroNotificationManager.notifyFocusExceed()
+ }
+
+ override fun stopService(name: Intent?): Boolean {
+ stopForeground(STOP_FOREGROUND_REMOVE)
+ focusTimer.stopTimer()
+ return super.stopService(name)
+ }
+}
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/rest/PomodoroRestTimerService.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/rest/PomodoroRestTimerService.kt
new file mode 100644
index 00000000..9e5ec6d5
--- /dev/null
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/rest/PomodoroRestTimerService.kt
@@ -0,0 +1,62 @@
+package com.pomonyang.mohanyang.presentation.service.rest
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import com.pomonyang.mohanyang.presentation.di.RestTimerType
+import com.pomonyang.mohanyang.presentation.noti.PomodoroNotificationManager
+import com.pomonyang.mohanyang.presentation.service.PomodoroTimer
+import com.pomonyang.mohanyang.presentation.service.PomodoroTimerEventHandler
+import com.pomonyang.mohanyang.presentation.service.PomodoroTimerServiceExtras
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+import kotlin.random.Random
+
+@AndroidEntryPoint
+internal class PomodoroRestTimerService :
+ Service(),
+ PomodoroTimerEventHandler {
+
+ @RestTimerType
+ @Inject
+ lateinit var restTimer: PomodoroTimer
+
+ @Inject
+ lateinit var pomodoroNotificationManager: PomodoroNotificationManager
+
+ override fun onBind(intent: Intent?): IBinder? = null
+
+ override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
+ val maxTime = intent.getIntExtra(PomodoroTimerServiceExtras.INTENT_TIMER_MAX_TIME, 0)
+ when (intent.action) {
+ PomodoroTimerServiceExtras.ACTION_TIMER_START -> {
+ startForeground(
+ Random.nextInt(),
+ pomodoroNotificationManager.createNotification(false)
+ )
+ restTimer.startTimer(maxTime, this)
+ }
+
+ PomodoroTimerServiceExtras.ACTION_TIMER_STOP -> {
+ stopForeground(STOP_FOREGROUND_REMOVE)
+ restTimer.stopTimer()
+ }
+ }
+
+ return START_NOT_STICKY
+ }
+
+ override fun onTimeEnd() {
+ pomodoroNotificationManager.notifyRestEnd()
+ }
+
+ override fun onTimeExceeded() {
+ pomodoroNotificationManager.notifyRestExceed()
+ }
+
+ override fun stopService(name: Intent?): Boolean {
+ stopForeground(STOP_FOREGROUND_REMOVE)
+ restTimer.stopTimer()
+ return super.stopService(name)
+ }
+}
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/rest/RestTimer.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/rest/RestTimer.kt
new file mode 100644
index 00000000..c8eaf877
--- /dev/null
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/service/rest/RestTimer.kt
@@ -0,0 +1,51 @@
+package com.pomonyang.mohanyang.presentation.service.rest
+
+import com.pomonyang.mohanyang.data.repository.pomodoro.PomodoroTimerRepository
+import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.MAX_EXCEEDED_TIME
+import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.ONE_SECOND
+import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.TIMER_DELAY
+import com.pomonyang.mohanyang.presentation.service.PomodoroTimer
+import com.pomonyang.mohanyang.presentation.service.PomodoroTimerEventHandler
+import java.util.Timer
+import javax.inject.Inject
+import kotlin.concurrent.fixedRateTimer
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import timber.log.Timber
+
+internal class RestTimer @Inject constructor(
+ private val timerRepository: PomodoroTimerRepository
+) : PomodoroTimer {
+
+ private var timer: Timer? = null
+ private var timeElapsed = 0
+ private val scope = CoroutineScope(Dispatchers.IO)
+
+ override fun startTimer(maxTime: Int, eventHandler: PomodoroTimerEventHandler) {
+ Timber.tag("TIMER").d("startRest timer / maxTime : $maxTime")
+ if (timer == null) {
+ timeElapsed = 0
+ timer = fixedRateTimer(initialDelay = TIMER_DELAY, period = TIMER_DELAY) {
+ scope.launch {
+ timeElapsed += ONE_SECOND
+ timerRepository.incrementRestedTime()
+
+ Timber.tag("TIMER").d("countRestTime: $timeElapsed ")
+
+ if (timeElapsed >= maxTime) {
+ eventHandler.onTimeEnd()
+ } else if (timeElapsed >= maxTime + MAX_EXCEEDED_TIME) {
+ eventHandler.onTimeExceeded()
+ stopTimer()
+ }
+ }
+ }
+ }
+ }
+
+ override fun stopTimer() {
+ timer?.cancel()
+ timer = null
+ }
+}
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/util/PomodoroTimerService.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/util/PomodoroTimerService.kt
deleted file mode 100644
index 08847799..00000000
--- a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/util/PomodoroTimerService.kt
+++ /dev/null
@@ -1,177 +0,0 @@
-package com.pomonyang.mohanyang.presentation.util
-
-import android.app.Notification
-import android.app.NotificationChannel
-import android.app.NotificationManager
-import android.app.Service
-import android.content.Intent
-import android.os.IBinder
-import androidx.core.app.NotificationCompat
-import com.pomonyang.mohanyang.data.repository.pomodoro.PomodoroTimerRepository
-import com.pomonyang.mohanyang.presentation.di.PomodoroNotification
-import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.MAX_EXCEEDED_TIME
-import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.ONE_SECOND
-import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.POMODORO_NOTIFICATION_CHANNEL_ID
-import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.POMODORO_NOTIFICATION_CHANNEL_NAME
-import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.POMODORO_NOTIFICATION_ID
-import com.pomonyang.mohanyang.presentation.screen.PomodoroConstants.TIMER_DELAY
-import com.pomonyang.mohanyang.presentation.util.MnNotificationManager.notifyFocusEnd
-import com.pomonyang.mohanyang.presentation.util.MnNotificationManager.notifyRestEnd
-import dagger.hilt.android.AndroidEntryPoint
-import java.util.Timer
-import javax.inject.Inject
-import kotlin.concurrent.fixedRateTimer
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import timber.log.Timber
-
-@AndroidEntryPoint
-class PomodoroTimerService : Service() {
-
- @Inject
- lateinit var pomodoroTimerRepository: PomodoroTimerRepository
-
- @PomodoroNotification
- @Inject
- lateinit var notificationBuilder: NotificationCompat.Builder
-
- @Inject
- lateinit var notificationManager: NotificationManager
-
- private var focusTimer: Timer? = null
- private var restTimer: Timer? = null
-
- private var scope = CoroutineScope(Dispatchers.IO)
-
- override fun onBind(intent: Intent?): IBinder? = null
-
- override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
- val isFocus = intent.getBooleanExtra(PomodoroTimerServiceExtras.INTENT_TIMER_IS_FOCUS, true)
- val maxTime = intent.getIntExtra(PomodoroTimerServiceExtras.INTENT_TIMER_MAX_TIME, 0)
- val action = intent.action
-
- Timber.tag(TAG).d("isFocus $isFocus / maxTime $maxTime / $action")
-
- when (action) {
- PomodoroTimerServiceExtras.ACTION_TIMER_START -> {
- startForeground(POMODORO_NOTIFICATION_ID, createNotification(isFocus))
- if (isFocus) {
- startFocusTimer(maxTime)
- } else {
- startRestTimer(maxTime)
- }
- }
-
- PomodoroTimerServiceExtras.ACTION_TIMER_STOP -> {
- stopForeground(STOP_FOREGROUND_REMOVE)
- if (isFocus) {
- stopFocusTimer()
- } else {
- stopRestTimer()
- }
- }
- }
-
- return START_NOT_STICKY
- }
-
- override fun stopService(name: Intent?): Boolean {
- Timber.tag(TAG).d("stopService")
- stopForeground(STOP_FOREGROUND_REMOVE)
- stopFocusTimer()
- stopRestTimer()
- return super.stopService(name)
- }
-
- private fun startFocusTimer(maxTime: Int) {
- if (focusTimer == null) {
- var focusTimeElapsed = 0
- Timber.tag(TAG).d("Focus 타이머 시작 ${this@PomodoroTimerService.hashCode()}")
- focusTimer = fixedRateTimer(initialDelay = TIMER_DELAY, period = TIMER_DELAY) {
- scope.launch {
- focusTimeElapsed += ONE_SECOND
-
- if (focusTimeElapsed == maxTime) {
- Timber.tag(TAG).d("Focus 타이머가 maxTime $maxTime 에 도달하여 집중 끝 알림 발송")
- notifyFocusEnd(this@PomodoroTimerService)
- }
-
- if (focusTimeElapsed > maxTime + MAX_EXCEEDED_TIME) {
- Timber.tag(TAG).d("Focus 타이머가 최대 머무를 수 있는 ${maxTime + MAX_EXCEEDED_TIME} 에 도달하여 중지됨")
- notificationManager.notify(
- POMODORO_NOTIFICATION_ID,
- notificationBuilder.setContentText(
- "너무 오랫동안 자리를 비웠다냥"
- ).build()
- )
- stopFocusTimer()
- } else {
- Timber.tag(TAG).d("Focus 타이머 작동 중 / 경과 시간: $focusTimeElapsed ${this@PomodoroTimerService.hashCode()}")
- pomodoroTimerRepository.incrementFocusedTime()
- }
- }
- }
- }
- }
-
- private fun startRestTimer(maxTime: Int) {
- if (restTimer == null) {
- var restTimeElapsed = 0
- Timber.tag(TAG).d("Rest 타이머 시작 ${this@PomodoroTimerService.hashCode()}")
- restTimer = fixedRateTimer(initialDelay = TIMER_DELAY, period = TIMER_DELAY) {
- scope.launch {
- restTimeElapsed += ONE_SECOND
-
- if (restTimeElapsed == maxTime) {
- Timber.tag(TAG).d("Rest 타이머가 maxTime $maxTime 에 도달하여 휴식 끝 알림 발송")
- notifyRestEnd(this@PomodoroTimerService)
- }
-
- if (restTimeElapsed > maxTime + MAX_EXCEEDED_TIME) {
- Timber.tag(TAG).d("Rest 타이머가 최대 머무를 수 있는 ${maxTime + MAX_EXCEEDED_TIME} 에 도달하여 중지됨")
- stopRestTimer()
- } else {
- Timber.tag(TAG).d("Rest 타이머 작동 중 / 경과 시간: $restTimeElapsed ${this@PomodoroTimerService.hashCode()}")
- pomodoroTimerRepository.incrementRestedTime()
- }
- }
- }
- }
- }
-
- private fun stopFocusTimer() {
- focusTimer?.cancel()
- focusTimer = null
- Timber.tag(TAG).d("Focus 타이머 중지 ${this@PomodoroTimerService.hashCode()}")
- }
-
- private fun stopRestTimer() {
- restTimer?.cancel()
- restTimer = null
- Timber.tag(TAG).d("Rest 타이머 중지 ${this@PomodoroTimerService.hashCode()}")
- }
-
- private fun createNotification(isFocus: Boolean): Notification {
- val notificationChannelId = POMODORO_NOTIFICATION_CHANNEL_ID
- val notificationChannel = NotificationChannel(
- /* id = */
- notificationChannelId,
- /* name = */
- POMODORO_NOTIFICATION_CHANNEL_NAME,
- /* importance = */
- NotificationManager.IMPORTANCE_DEFAULT
- )
- notificationManager.createNotificationChannel(notificationChannel)
-
- return notificationBuilder
- .setContentText(if (isFocus) "집중 시간이다냥" else "휴식 시간이다냥")
- .build().apply {
- flags = Notification.FLAG_NO_CLEAR
- }
- }
-
- companion object {
- private const val TAG = "PomodoroTimerService"
- }
-}
diff --git a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/util/TimerUtils.kt b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/util/TimerUtils.kt
index 8c25f9d3..39a34d3d 100644
--- a/presentation/src/main/java/com/pomonyang/mohanyang/presentation/util/TimerUtils.kt
+++ b/presentation/src/main/java/com/pomonyang/mohanyang/presentation/util/TimerUtils.kt
@@ -3,14 +3,18 @@ package com.pomonyang.mohanyang.presentation.util
import android.content.Context
import android.content.Intent
import androidx.core.os.bundleOf
+import com.pomonyang.mohanyang.presentation.service.PomodoroTimerServiceExtras
+import com.pomonyang.mohanyang.presentation.service.focus.PomodoroFocusTimerService
+import com.pomonyang.mohanyang.presentation.service.rest.PomodoroRestTimerService
+import timber.log.Timber
-fun Context.startTimer(isFocus: Boolean, maxTime: Int) {
+internal fun Context.startFocusTimer(maxTime: Int) {
+ Timber.tag("TIMER").d("startFocusTimer")
startService(
- Intent(this, PomodoroTimerService::class.java).apply {
+ Intent(this, PomodoroFocusTimerService::class.java).apply {
action = PomodoroTimerServiceExtras.ACTION_TIMER_START
putExtras(
bundleOf(
- PomodoroTimerServiceExtras.INTENT_TIMER_IS_FOCUS to isFocus,
PomodoroTimerServiceExtras.INTENT_TIMER_MAX_TIME to maxTime
)
)
@@ -18,11 +22,34 @@ fun Context.startTimer(isFocus: Boolean, maxTime: Int) {
)
}
-fun Context.stopTimer(isFocus: Boolean) {
+internal fun Context.startRestTimer(maxTime: Int) {
+ Timber.tag("TIMER").d("startRestTimer")
startService(
- Intent(this, PomodoroTimerService::class.java).apply {
+ Intent(this, PomodoroRestTimerService::class.java).apply {
+ action = PomodoroTimerServiceExtras.ACTION_TIMER_START
+ putExtras(
+ bundleOf(
+ PomodoroTimerServiceExtras.INTENT_TIMER_MAX_TIME to maxTime
+ )
+ )
+ }
+ )
+}
+
+internal fun Context.stopFocusTimer() {
+ Timber.tag("TIMER").d("stopFocusTimer")
+ startService(
+ Intent(this, PomodoroFocusTimerService::class.java).apply {
+ action = PomodoroTimerServiceExtras.ACTION_TIMER_STOP
+ }
+ )
+}
+
+internal fun Context.stopRestTimer() {
+ Timber.tag("TIMER").d("stopRestTimer")
+ startService(
+ Intent(this, PomodoroRestTimerService::class.java).apply {
action = PomodoroTimerServiceExtras.ACTION_TIMER_STOP
- putExtra(PomodoroTimerServiceExtras.INTENT_TIMER_IS_FOCUS, isFocus)
}
)
}