]> git.earman.xyz Git - sensor-watch.git/commitdiff
Minor enhancements for the countdown face (#177)
authorKonrad Rieck <konrad@mlsec.org>
Mon, 23 Jan 2023 20:18:28 +0000 (21:18 +0100)
committerGitHub <noreply@github.com>
Mon, 23 Jan 2023 20:18:28 +0000 (14:18 -0600)
* minor enhancements for the countdown face

* Changed usage of countdown timer.

- Long button presses have been removed.

- The light button is used to restore the last countdown as well as enter setting mode when pressed again.

- The visual state has been removed from the display.

- The internal states have been renamed to reflect the new logic.

* restore time when countdown finished.

* support for fast forward on long press (untested).

* support for beeps similar to stop watch

* fixed bug wiht display when fast forwarding

movement/watch_faces/complication/countdown_face.c
movement/watch_faces/complication/countdown_face.h

index b2206b8f61bedab4e4ce9796014fb34234080821..60f0a5b050d7b3c534638f95c893dac7888602fe 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * MIT License
  *
+ * Copyright (c) 2023 Konrad Rieck
  * Copyright (c) 2022 Wesley Ellis
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
 #include "watch.h"
 #include "watch_utility.h"
 
+/*
+    Slight extension of the original countdown face by Wesley Ellis.
+
+    - Press the light button to enter setting mode and adjust the
+      countdown timer.
+
+    - Start and pause the countdown using the alarm button, similar to the
+      stopwatch face.
+
+    - When paused or terminated, press the light button to restore the
+      last entered countdown.
+*/
 
 #define CD_SELECTIONS 3
 #define DEFAULT_MINUTES 3
 
+static bool quick_ticks_running;
+
+static void abort_quick_ticks(countdown_state_t *state) {
+    if (quick_ticks_running) {
+        quick_ticks_running = false;
+        if (state->mode == cd_setting)
+            movement_request_tick_frequency(4);
+        else
+            movement_request_tick_frequency(1);
+    }
+}
 
 static inline int32_t get_tz_offset(movement_settings_t *settings) {
     return movement_timezone_offsets[settings->bit.time_zone] * 60;
 }
 
+static inline void store_countdown(countdown_state_t *state) {
+    /* Store set countdown time */
+    state->set_hours = state->hours;
+    state->set_minutes = state->minutes;
+    state->set_seconds = state->seconds;
+}
+
+static inline void load_countdown(countdown_state_t *state) {
+    /* Load set countdown time */
+    state->hours = state->set_hours;
+    state->minutes = state->set_minutes;
+    state->seconds = state->set_seconds;
+}
+
+static inline void button_beep(movement_settings_t *settings) {
+    // play a beep as confirmation for a button press (if applicable)
+    if (settings->bit.button_should_sound)
+        watch_buzzer_play_note(BUZZER_NOTE_C7, 50);
+}
+
 static void start(countdown_state_t *state, movement_settings_t *settings) {
     watch_date_time now = watch_rtc_get_date_time();
 
@@ -55,25 +99,24 @@ static void draw(countdown_state_t *state, uint8_t subsecond) {
 
     uint32_t delta;
     div_t result;
-    uint8_t hour, min, sec;
 
     switch (state->mode) {
         case cd_running:
             delta = state->target_ts - state->now_ts;
             result = div(delta, 60);
-            sec = result.rem;
+            state->seconds = result.rem;
             result = div(result.quot, 60);
-            hour = result.quot;
-            min = result.rem;
-
-            sprintf(buf, "CD  %2d%02d%02d", hour, min, sec);
+            state->hours = result.quot;
+            state->minutes = result.rem;
+            sprintf(buf, "CD  %2d%02d%02d", state->hours, state->minutes, state->seconds);
             break;
-        case cd_waiting:
+        case cd_reset:
+        case cd_paused:
             sprintf(buf, "CD  %2d%02d%02d", state->hours, state->minutes, state->seconds);
             break;
         case cd_setting:
             sprintf(buf, "CD  %2d%02d%02d", state->hours, state->minutes, state->seconds);
-            if (subsecond % 2) {
+            if (!quick_ticks_running && subsecond % 2) {
                 switch(state->selection) {
                     case 0:
                         buf[4] = buf[5] = ' ';
@@ -93,10 +136,17 @@ static void draw(countdown_state_t *state, uint8_t subsecond) {
     watch_display_string(buf, 0);
 }
 
+static void pause(countdown_state_t *state) {
+    state->mode = cd_paused;
+    movement_cancel_background_task();
+    watch_clear_indicator(WATCH_INDICATOR_BELL);
+}
+
 static void reset(countdown_state_t *state) {
-    state->mode = cd_waiting;
+    state->mode = cd_reset;
     movement_cancel_background_task();
     watch_clear_indicator(WATCH_INDICATOR_BELL);
+    load_countdown(state);
 }
 
 static void ring(countdown_state_t *state) {
@@ -131,6 +181,8 @@ void countdown_face_setup(movement_settings_t *settings, uint8_t watch_face_inde
         countdown_state_t *state = (countdown_state_t *)*context_ptr;
         memset(*context_ptr, 0, sizeof(countdown_state_t));
         state->minutes = DEFAULT_MINUTES;
+        state->mode = cd_reset;
+        store_countdown(state);
     }
 }
 
@@ -143,8 +195,10 @@ void countdown_face_activate(movement_settings_t *settings, void *context) {
         watch_set_indicator(WATCH_INDICATOR_BELL);
     }
     watch_set_colon();
-}
 
+    movement_request_tick_frequency(1);
+    quick_ticks_running = false;
+}
 
 bool countdown_face_loop(movement_event_t event, movement_settings_t *settings, void *context) {
     (void) settings;
@@ -155,12 +209,20 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings,
             draw(state, event.subsecond);
             break;
         case EVENT_TICK:
+            if (quick_ticks_running) {
+                if (watch_get_pin_level(BTN_ALARM))
+                    settings_increment(state);
+                else
+                    abort_quick_ticks(state);
+            }
+
             if (state->mode == cd_running) {
                 state->now_ts++;
             }
             draw(state, event.subsecond);
             break;
         case EVENT_MODE_BUTTON_UP:
+            abort_quick_ticks(state);
             movement_move_to_next_face();
             break;
         case EVENT_LIGHT_BUTTON_UP:
@@ -168,16 +230,23 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings,
                 case cd_running:
                     movement_illuminate_led();
                     break;
-                case cd_waiting:
+                case cd_paused:
+                    reset(state);
+                    button_beep(settings);
+                    break;
+                case cd_reset:
                     state->mode = cd_setting;
                     movement_request_tick_frequency(4);
+                    button_beep(settings);
                     break;
                 case cd_setting:
                     state->selection++;
                     if(state->selection >= CD_SELECTIONS) {
                         state->selection = 0;
-                        state->mode = cd_waiting;
+                        state->mode = cd_reset;
+                        store_countdown(state);
                         movement_request_tick_frequency(1);
+                        button_beep(settings);
                     }
                     break;
             }
@@ -186,12 +255,15 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings,
         case EVENT_ALARM_BUTTON_UP:
             switch(state->mode) {
                 case cd_running:
-                    reset(state);
+                    pause(state);
+                    button_beep(settings);
                     break;
-                case cd_waiting:
+                case cd_reset:
+                case cd_paused:
                     if (!(state->hours == 0 && state->minutes == 0 && state->seconds == 0)) {
                         // Only start the timer if we have a valid time.
                         start(state, settings);
+                        button_beep(settings);
                     }
                     break;
                 case cd_setting:
@@ -200,19 +272,20 @@ bool countdown_face_loop(movement_event_t event, movement_settings_t *settings,
             }
             draw(state, event.subsecond);
             break;
-        case EVENT_BACKGROUND_TASK:
-            ring(state);
-            break;
         case EVENT_ALARM_LONG_PRESS:
             if (state->mode == cd_setting) {
-                    state->hours = 0;
-                    state->minutes = 0;
-                    state->seconds = 0;
-                    draw(state, event.subsecond);
-                    break;
+                quick_ticks_running = true;
+                movement_request_tick_frequency(8);
             }
             break;
+        case EVENT_ALARM_LONG_UP:
+            abort_quick_ticks(state);
+            break;
+        case EVENT_BACKGROUND_TASK:
+            ring(state);
+            break;
         case EVENT_TIMEOUT:
+            abort_quick_ticks(state);
             movement_move_to_face(0);
             break;
         case EVENT_LOW_ENERGY_UPDATE:
@@ -228,6 +301,7 @@ void countdown_face_resign(movement_settings_t *settings, void *context) {
     countdown_state_t *state = (countdown_state_t *)context;
     if (state->mode == cd_setting) {
         state->selection = 0;
-        state->mode = cd_waiting;
+        state->mode = cd_reset;
+        store_countdown(state);
     }
 }
index f6c845d694fa0eb907f3c90882c08c814da82a07..12bb1d1e6b5ab37bf4161988b52c6c98d9a78323 100644 (file)
@@ -40,9 +40,10 @@ movement_schedule_background_task() while the timer is running.
 
 
 typedef enum {
-    cd_waiting,
+    cd_paused,
     cd_running,
-    cd_setting
+    cd_setting,
+    cd_reset
 } countdown_mode_t;
 
 typedef struct {
@@ -51,6 +52,9 @@ typedef struct {
     uint8_t hours;
     uint8_t minutes;
     uint8_t seconds;
+    uint8_t set_hours;
+    uint8_t set_minutes;
+    uint8_t set_seconds;
     uint8_t selection;
     countdown_mode_t mode;
 } countdown_state_t;