diff --git a/src/main/kotlin/com/github/akshayashokcode/devfocus/services/pomodoro/PomodoroTimerService.kt b/src/main/kotlin/com/github/akshayashokcode/devfocus/services/pomodoro/PomodoroTimerService.kt
index ba2fb4c..4ff1091 100644
--- a/src/main/kotlin/com/github/akshayashokcode/devfocus/services/pomodoro/PomodoroTimerService.kt
+++ b/src/main/kotlin/com/github/akshayashokcode/devfocus/services/pomodoro/PomodoroTimerService.kt
@@ -2,7 +2,10 @@ package com.github.akshayashokcode.devfocus.services.pomodoro
import com.github.akshayashokcode.devfocus.model.PomodoroMode
import com.github.akshayashokcode.devfocus.model.PomodoroSettings
+import com.intellij.notification.NotificationGroupManager
+import com.intellij.notification.NotificationType
import com.intellij.openapi.components.Service
+import com.intellij.openapi.project.Project
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -15,17 +18,20 @@ import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit
@Service(Service.Level.PROJECT)
-class PomodoroTimerService {
+class PomodoroTimerService(private val project: Project) {
companion object {
private const val ONE_SECOND = 1000L
+ private const val NOTIFICATION_GROUP_ID = "DevFocus Notifications"
}
enum class TimerState { IDLE, RUNNING, PAUSED }
+ enum class TimerPhase { WORK, BREAK }
private val coroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
private var job: Job? = null
private var settings = PomodoroMode.CLASSIC.toSettings()
+ private var currentPhase = TimerPhase.WORK
private var remainingTimeMs: Long = TimeUnit.MINUTES.toMillis(settings.sessionMinutes.toLong())
private val _timeLeft = MutableStateFlow(formatTime(remainingTimeMs))
@@ -55,10 +61,73 @@ class PomodoroTimerService {
}
if (remainingTimeMs <= 0) {
_state.value = TimerState.IDLE
+ onSessionComplete()
}
}
}
+ private fun onSessionComplete() {
+ val currentSessionNum = _currentSession.value
+ val totalSessions = settings.sessionsPerRound
+
+ if (currentPhase == TimerPhase.WORK) {
+ // Work session complete
+ if (currentSessionNum >= totalSessions) {
+ // Last session complete - all done!
+ NotificationGroupManager.getInstance()
+ .getNotificationGroup(NOTIFICATION_GROUP_ID)
+ .createNotification(
+ "\uD83C\uDF89 All Sessions Complete!",
+ "You've completed all $totalSessions sessions. Take a well-deserved break!",
+ NotificationType.INFORMATION
+ )
+ .notify(project)
+
+ // Reset to initial state
+ currentPhase = TimerPhase.WORK
+ _currentSession.value = 1
+ remainingTimeMs = TimeUnit.MINUTES.toMillis(settings.sessionMinutes.toLong())
+ _timeLeft.value = formatTime(remainingTimeMs)
+ } else {
+ // Work session complete - start break
+ NotificationGroupManager.getInstance()
+ .getNotificationGroup(NOTIFICATION_GROUP_ID)
+ .createNotification(
+ "\uD83C\uDF45 Session Complete!",
+ "Great work! Starting ${settings.breakMinutes}-minute break ☕.",
+ NotificationType.INFORMATION
+ )
+ .notify(project)
+
+ // Start break timer
+ currentPhase = TimerPhase.BREAK
+ remainingTimeMs = TimeUnit.MINUTES.toMillis(settings.breakMinutes.toLong())
+ _timeLeft.value = formatTime(remainingTimeMs)
+ start()
+ }
+
+ } else {
+ // Break complete
+ // More sessions remaining - start next session
+ NotificationGroupManager.getInstance()
+ .getNotificationGroup(NOTIFICATION_GROUP_ID)
+ .createNotification(
+ "☕ Break Complete!",
+ "Starting session ${currentSessionNum + 1} of $totalSessions.",
+ NotificationType.INFORMATION
+ )
+ .notify(project)
+
+ // Start next work session
+ currentPhase = TimerPhase.WORK
+ _currentSession.value = currentSessionNum + 1
+ remainingTimeMs = TimeUnit.MINUTES.toMillis(settings.sessionMinutes.toLong())
+ _timeLeft.value = formatTime(remainingTimeMs)
+ start()
+
+ }
+ }
+
fun pause() {
if (_state.value == TimerState.RUNNING) {
job?.cancel()
@@ -72,6 +141,7 @@ class PomodoroTimerService {
job = null
// Reset to initial state
+ currentPhase = TimerPhase.WORK
remainingTimeMs = TimeUnit.MINUTES.toMillis(settings.sessionMinutes.toLong())
_timeLeft.value = formatTime(remainingTimeMs)
_currentSession.value = 1
@@ -92,6 +162,7 @@ class PomodoroTimerService {
settings = newSettings
_settings.value = newSettings
+ currentPhase = TimerPhase.WORK
remainingTimeMs = TimeUnit.MINUTES.toMillis(newSettings.sessionMinutes.toLong())
_timeLeft.value = formatTime(remainingTimeMs)
_currentSession.value = 1
@@ -105,7 +176,11 @@ class PomodoroTimerService {
fun getSettings(): PomodoroSettings = settings
fun getProgress(): Float {
- val totalMs = TimeUnit.MINUTES.toMillis(settings.sessionMinutes.toLong())
+ val totalMs = if (currentPhase == TimerPhase.WORK) {
+ TimeUnit.MINUTES.toMillis(settings.sessionMinutes.toLong())
+ } else {
+ TimeUnit.MINUTES.toMillis(settings.breakMinutes.toLong())
+ }
return if (totalMs > 0) remainingTimeMs.toFloat() / totalMs.toFloat() else 0f
}
}
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/akshayashokcode/devfocus/ui/components/CircularTimerPanel.kt b/src/main/kotlin/com/github/akshayashokcode/devfocus/ui/components/CircularTimerPanel.kt
index 1a6a0e4..2bd455f 100644
--- a/src/main/kotlin/com/github/akshayashokcode/devfocus/ui/components/CircularTimerPanel.kt
+++ b/src/main/kotlin/com/github/akshayashokcode/devfocus/ui/components/CircularTimerPanel.kt
@@ -2,6 +2,7 @@ package com.github.akshayashokcode.devfocus.ui.components
import java.awt.*
import javax.swing.JPanel
+import javax.swing.UIManager
class CircularTimerPanel : JPanel() {
@@ -13,7 +14,6 @@ class CircularTimerPanel : JPanel() {
private val workColor = Color(74, 144, 226) // Blue for focus/work
private val breakColor = Color(80, 200, 120) // Green for rest
private val backgroundColor = Color(224, 224, 224) // Light gray
- private val textColor = Color(60, 60, 60) // Dark gray for text
private val diameter = 180
private val strokeWidth = 10f
@@ -34,7 +34,7 @@ class CircularTimerPanel : JPanel() {
super.paintComponent(g)
val g2d = g as Graphics2D
- // Enable anti-aliasing for smooth circles
+ // Enable antialiasing for smooth circles
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON)
@@ -60,7 +60,7 @@ class CircularTimerPanel : JPanel() {
g2d.drawArc(arcX, arcY, arcSize, arcSize, 90, -arcAngle) // Negative for clockwise
// Draw time text in center
- g2d.color = textColor
+ g2d.color = UIManager.getColor("Label.foreground") ?: Color.BLACK
val font = Font("SansSerif", Font.BOLD, 36)
g2d.font = font
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index 251881d..c41685c 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -33,5 +33,6 @@
/>
+