From c27d300764886868dd7ec2149b03cc83b933ea83 Mon Sep 17 00:00:00 2001 From: Joshua van Kleef Date: Tue, 4 Mar 2025 15:31:30 +0100 Subject: [PATCH] run pomodoro in background --- manifest.xml | 24 +++--- resources/settings/settings.xml | 6 +- source/GarmodoroApp.mc | 28 +++---- source/GarmodoroDelegate.mc | 71 ++++++++++------- source/GarmodoroPersistentStorage.mc | 49 ++++++++++++ source/GarmodoroServiceDelegate.mc | 111 +++++++++++++++++++++++++++ source/GarmodoroView.mc | 4 + source/StopMenuDelegate.mc | 11 ++- 8 files changed, 243 insertions(+), 61 deletions(-) create mode 100644 source/GarmodoroPersistentStorage.mc create mode 100644 source/GarmodoroServiceDelegate.mc diff --git a/manifest.xml b/manifest.xml index 41d1cf9..662f69c 100644 --- a/manifest.xml +++ b/manifest.xml @@ -22,19 +22,19 @@ + + + + + - - - - - - + @@ -65,13 +65,12 @@ - - + + - @@ -84,6 +83,7 @@ + @@ -139,7 +139,9 @@ - + + + eng pol @@ -147,4 +149,4 @@ - + \ No newline at end of file diff --git a/resources/settings/settings.xml b/resources/settings/settings.xml index e3a36fa..5b97dd2 100644 --- a/resources/settings/settings.xml +++ b/resources/settings/settings.xml @@ -1,12 +1,12 @@ - + - + - + diff --git a/source/GarmodoroApp.mc b/source/GarmodoroApp.mc index 4512b7c..fb0ba7a 100644 --- a/source/GarmodoroApp.mc +++ b/source/GarmodoroApp.mc @@ -1,23 +1,17 @@ using Toybox.Application as App; -using Toybox.Timer as Timer; +using Toybox.System; +(:background) class GarmodoroApp extends App.AppBase { + function initialize() { + AppBase.initialize(); + } - function initialize() { - AppBase.initialize(); - } + function getInitialView() { + return [new GarmodoroView(), new GarmodoroDelegate()]; + } - function onStart(state) { - timer = new Timer.Timer(); - tickTimer = new Timer.Timer(); - } - - function onStop(state) { - tickTimer.stop(); - timer.stop(); - } - - function getInitialView() { - return [ new GarmodoroView(), new GarmodoroDelegate() ]; - } + public function getServiceDelegate() as [System.ServiceDelegate] { + return [new GarmodoroServiceDelegate()]; + } } diff --git a/source/GarmodoroDelegate.mc b/source/GarmodoroDelegate.mc index 84052b6..1bedfe1 100644 --- a/source/GarmodoroDelegate.mc +++ b/source/GarmodoroDelegate.mc @@ -1,13 +1,11 @@ using Toybox.Application as App; using Toybox.Attention as Attention; using Toybox.WatchUi as Ui; +using Toybox.System; + var timer; var tickTimer; -var minutes = 0; -var pomodoroNumber = 1; -var isPomodoroTimerStarted = false; -var isBreakTimerStarted = false; function ping( dutyCycle, length ) { if ( Attention has :vibrate ) { @@ -26,49 +24,69 @@ function idleCallback() { } function isLongBreak() { - return ( pomodoroNumber % App.getApp().getProperty( "numberOfPomodorosBeforeLongBreak" ) ) == 0; + var pomodoroNumber = getStorageValue(POMODORO_NUMBER_PROPERTY_STORAGE_KEY, 1); + return ( + pomodoroNumber % + App.getApp().getProperty("numberOfPomodorosBeforeLongBreak") == + 0 + ); } function resetMinutes() { - minutes = App.getApp().getProperty( "pomodoroLength" ); + var minutes = App.getApp().getProperty("pomodoroLength"); + var minutesAsSeconds = minutes * 60; + setEndTimeBySeconds(minutesAsSeconds); } class GarmodoroDelegate extends Ui.BehaviorDelegate { function initialize() { + timer = new Timer.Timer(); + tickTimer = new Timer.Timer(); Ui.BehaviorDelegate.initialize(); - timer.start( method( :idleCallback ), 60 * 1000, true ); - } + timer.start( method( :idleCallback ), 10 * 1000, true ); + var isPomodoroTimerStarted = getStorageValue(POMODORO_TIMER_STARTED_PROPERTY_STORAGE_KEY, false); + var isBreakTimerStarted = getStorageValue(BREAK_TIMER_STARTED_PROPERTY_STORAGE_KEY, false); - function pomodoroCallback() { - minutes -= 1; + if(isPomodoroTimerStarted){ + pomodoroCallback(); + } + + if(isBreakTimerStarted){ + breakCallback(); + } + } - if ( minutes == 0 ) { + function pomodoroCallback() { + var seconds = getSeconds(); + if (seconds <= 0 ) { play( 10 ); // Attention.TONE_LAP ping( 100, 1500 ); tickTimer.stop(); timer.stop(); - isPomodoroTimerStarted = false; - minutes = App.getApp().getProperty( isLongBreak() ? "longBreakLength" : "shortBreakLength" ); - - timer.start( method( :breakCallback ), 60 * 1000, true ); - isBreakTimerStarted = true; + setStorageValue(POMODORO_TIMER_STARTED_PROPERTY_STORAGE_KEY, false); + var breakLengthInMinutes = App.getApp().getProperty( + isLongBreak() ? "longBreakLength" : "shortBreakLength" + ); + setEndTimeBySeconds(breakLengthInMinutes * 60); + setStorageValue(BREAK_TIMER_STARTED_PROPERTY_STORAGE_KEY, true); + timer.start( method( :breakCallback ), 10 * 1000, true ); } Ui.requestUpdate(); } function breakCallback() { - minutes -= 1; - - if ( minutes == 0 ) { + var seconds = getSeconds(); + if ( seconds <= 0 ) { play( 7 ); // Attention.TONE_INTERVAL_ALERT ping( 100, 1500 ); timer.stop(); - - isBreakTimerStarted = false; - pomodoroNumber += 1; + setStorageValue(BREAK_TIMER_STARTED_PROPERTY_STORAGE_KEY, false); + var pomodoroNumber = getStorageValue(POMODORO_NUMBER_PROPERTY_STORAGE_KEY, 1); + setStorageValue(POMODORO_NUMBER_PROPERTY_STORAGE_KEY, pomodoroNumber + 1); + setStorageValue(POMODORO_TIMER_STARTED_PROPERTY_STORAGE_KEY, true); resetMinutes(); - timer.start( method( :idleCallback ), 60 * 1000, true ); + timer.start( method( :idleCallback ), 10 * 1000, true ); } Ui.requestUpdate(); @@ -96,6 +114,8 @@ class GarmodoroDelegate extends Ui.BehaviorDelegate { } function onSelect() { + var isBreakTimerStarted = getStorageValue(BREAK_TIMER_STARTED_PROPERTY_STORAGE_KEY, false); + var isPomodoroTimerStarted = getStorageValue(POMODORO_TIMER_STARTED_PROPERTY_STORAGE_KEY, false); if ( isBreakTimerStarted || isPomodoroTimerStarted ) { Ui.pushView( new Rez.Menus.StopMenu(), new StopMenuDelegate(), Ui.SLIDE_UP ); return true; @@ -105,12 +125,11 @@ class GarmodoroDelegate extends Ui.BehaviorDelegate { ping( 75, 1500 ); timer.stop(); resetMinutes(); - timer.start( method( :pomodoroCallback ), 60 * 1000, true ); + timer.start( method( :pomodoroCallback ), 10 * 1000, true ); if ( me.shouldTick() ) { tickTimer.start( method( :tickCallback ), App.getApp().getProperty( "tickFrequency" ) * 1000, true ); } - isPomodoroTimerStarted = true; - + setStorageValue(POMODORO_TIMER_STARTED_PROPERTY_STORAGE_KEY, true); Ui.requestUpdate(); return true; diff --git a/source/GarmodoroPersistentStorage.mc b/source/GarmodoroPersistentStorage.mc new file mode 100644 index 0000000..f25db24 --- /dev/null +++ b/source/GarmodoroPersistentStorage.mc @@ -0,0 +1,49 @@ +using Toybox.Time; +using Toybox.Background; +using Toybox.Math; +using Toybox.Application.Storage; + +const ENDTIME_PROPERTY_STORAGE_KEY = "end_time"; +const POMODORO_NUMBER_PROPERTY_STORAGE_KEY = "pomodoro_number"; +const POMODORO_TIMER_STARTED_PROPERTY_STORAGE_KEY = "pomodoro_timer_started"; +const BREAK_TIMER_STARTED_PROPERTY_STORAGE_KEY = "break_timer_started"; + +function setEndTimeBySeconds(seconds) { + var date = + seconds != null ? Time.now().add(new Time.Duration(seconds)).value() : null; + Storage.setValue(ENDTIME_PROPERTY_STORAGE_KEY, date); + if (Background.getTemporalEventRegisteredTime() != null) { + Background.deleteTemporalEvent(); + } + if (seconds != null) { + Background.registerForTemporalEvent(new Time.Duration(seconds)); + } +} + +function setStorageValue(key, value) { + Storage.setValue(key, value); +} + +function getStorageValue(key, defaultValue) { + var value = Storage.getValue(key); + return value != null ? value : defaultValue; +} + +function getSeconds() { + var currentTime = Time.now(); + var endTime = getStorageValue(ENDTIME_PROPERTY_STORAGE_KEY, null); + if (endTime == null) { + return null; + } + var remainingSeconds = endTime - currentTime.value(); + return remainingSeconds; +} + +function getMinutes() { + var remainingSeconds = getSeconds(); + if (remainingSeconds == null) { + return null; + } + var remainingMinutes = Math.ceil(remainingSeconds.toFloat() / 60); // Convert seconds to minutes + return remainingMinutes; +} diff --git a/source/GarmodoroServiceDelegate.mc b/source/GarmodoroServiceDelegate.mc new file mode 100644 index 0000000..b52c98c --- /dev/null +++ b/source/GarmodoroServiceDelegate.mc @@ -0,0 +1,111 @@ +using Toybox.System; +using Toybox.Application.Storage; +using Toybox.Time; +using Toybox.Application as App; + +(:background) +class GarmodoroServiceDelegate extends System.ServiceDelegate { + hidden function getStorageValue(key, defaultValue) { + var value = Storage.getValue(key); + return value != null ? value : defaultValue; + } + + hidden function setStorageValue(key, value) { + Storage.setValue(key, value); + } + + hidden function getSeconds() { + var currentTime = Time.now(); + var endTime = getStorageValue(ENDTIME_PROPERTY_STORAGE_KEY, null); + if (endTime == null) { + return null; + } + var remainingSeconds = endTime - currentTime.value(); + return remainingSeconds; + } + + public function onTemporalEvent() as Void { + var seconds = me.getSeconds(); + if (seconds <= 60) { + var isPomodoroTimerStarted = getStorageValue( + POMODORO_TIMER_STARTED_PROPERTY_STORAGE_KEY, + false + ); + var isBreakTimerStarted = getStorageValue( + BREAK_TIMER_STARTED_PROPERTY_STORAGE_KEY, + false + ); + + if (isPomodoroTimerStarted) { + me.pomodoroLogic(); + var message = isLongBreak() + ? "Pomodoro is over. Take a long break." + : "Pomodoro is over. Take a short break."; + Background.requestApplicationWake(message); + } + + if (isBreakTimerStarted) { + me.breakLogic(); + Background.requestApplicationWake( + "Break is over. Time to start a pomodoro." + ); + } + Background.exit(null); + } + } + + public function breakLogic() { + me.setStorageValue(BREAK_TIMER_STARTED_PROPERTY_STORAGE_KEY, false); + var pomodoroNumber = getStorageValue( + POMODORO_NUMBER_PROPERTY_STORAGE_KEY, + 1 + ); + me.setStorageValue( + POMODORO_NUMBER_PROPERTY_STORAGE_KEY, + pomodoroNumber + 1 + ); + me.setStorageValue(POMODORO_TIMER_STARTED_PROPERTY_STORAGE_KEY, true); + resetMinutes(); + } + + public function pomodoroLogic() { + setStorageValue(POMODORO_TIMER_STARTED_PROPERTY_STORAGE_KEY, false); + var breakLengthInMinutes = App.getApp().getProperty( + isLongBreak() ? "longBreakLength" : "shortBreakLength" + ); + setEndTimeBySeconds(breakLengthInMinutes * 60); + setStorageValue(BREAK_TIMER_STARTED_PROPERTY_STORAGE_KEY, true); + } + + function isLongBreak() { + var pomodoroNumber = getStorageValue( + POMODORO_NUMBER_PROPERTY_STORAGE_KEY, + 1 + ); + return ( + pomodoroNumber % + App.getApp().getProperty("numberOfPomodorosBeforeLongBreak") == + 0 + ); + } + + function resetMinutes() { + var minutes = App.getApp().getProperty("pomodoroLength"); + var minutesAsSeconds = minutes * 60; + setEndTimeBySeconds(minutesAsSeconds); + } + + function setEndTimeBySeconds(seconds) { + var date = + seconds != null + ? Time.now().add(new Time.Duration(seconds)).value() + : null; + Storage.setValue(ENDTIME_PROPERTY_STORAGE_KEY, date); + if (Background.getTemporalEventRegisteredTime() != null) { + Background.deleteTemporalEvent(); + } + if (seconds != null) { + Background.registerForTemporalEvent(new Time.Duration(seconds)); + } + } +} diff --git a/source/GarmodoroView.mc b/source/GarmodoroView.mc index fb0376c..fc8d73c 100644 --- a/source/GarmodoroView.mc +++ b/source/GarmodoroView.mc @@ -55,6 +55,9 @@ class GarmodoroView extends Ui.View { function onUpdate( dc ) { dc.setColor( Gfx.COLOR_TRANSPARENT, Gfx.COLOR_BLACK ); dc.clear(); + var isBreakTimerStarted = getStorageValue(BREAK_TIMER_STARTED_PROPERTY_STORAGE_KEY, false); + var isPomodoroTimerStarted = getStorageValue(POMODORO_TIMER_STARTED_PROPERTY_STORAGE_KEY, false); + var pomodoroNumber = getStorageValue(POMODORO_NUMBER_PROPERTY_STORAGE_KEY, 1); if ( isBreakTimerStarted ) { dc.setColor( Gfx.COLOR_GREEN, Gfx.COLOR_TRANSPARENT ); dc.drawText( me.centerX, me.pomodoroOffset, Gfx.FONT_MEDIUM, isLongBreak() ? me.longBreakLabel : me.shortBreakLabel, Gfx.TEXT_JUSTIFY_CENTER ); @@ -82,6 +85,7 @@ class GarmodoroView extends Ui.View { } hidden function drawMinutes( dc ) { + var minutes = getMinutes(); dc.drawText( me.centerX, me.minutesOffset, Gfx.FONT_NUMBER_THAI_HOT, minutes.format( "%02d" ), Gfx.TEXT_JUSTIFY_CENTER ); } diff --git a/source/StopMenuDelegate.mc b/source/StopMenuDelegate.mc index 226cb4a..c6c3558 100644 --- a/source/StopMenuDelegate.mc +++ b/source/StopMenuDelegate.mc @@ -16,10 +16,13 @@ class StopMenuDelegate extends Ui.MenuInputDelegate { timer.stop(); resetMinutes(); - pomodoroNumber = 1; - isPomodoroTimerStarted = false; - isBreakTimerStarted = false; - timer.start( method( :idleCallback ), 60 * 1000, true ); + setStorageValue(POMODORO_TIMER_STARTED_PROPERTY_STORAGE_KEY, false); + setStorageValue(BREAK_TIMER_STARTED_PROPERTY_STORAGE_KEY, false); + setStorageValue(POMODORO_NUMBER_PROPERTY_STORAGE_KEY, 1); + + setEndTimeBySeconds(null); + Background.deleteTemporalEvent(); + timer.start( method( :idleCallback ), 10 * 1000, true ); Ui.requestUpdate(); } else if ( item == :exit ) {