From 96a9f268143673c1fe6029855107bd73f48eb2c1 Mon Sep 17 00:00:00 2001 From: Randall Winkhart Date: Fri, 14 Aug 2026 01:26:26 -0400 Subject: [PATCH] Add awareness alarm tutorial status page --- README.md | 7 ++++++ package.json | 2 +- src/c/main.c | 70 ++++++++++++++++++++++++++++++++-------------------- src/c/main.h | 8 +++--- 4 files changed, 56 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index a6c9d7e..3081284 100644 --- a/README.md +++ b/README.md @@ -14,3 +14,10 @@ Because it requires the perfect amount of thought to activate your brain in the - The vibration pattern is randomized. It derives the length of the three pulses from the angles in the generated triangle. Neat! - Some of the alarm scheduling code was taken from [Gentle Wake](https://github.com/SeaPea/GentleWake). It seems trusted by the community, so... I stole it! The beauty of open source, no AI needed! - A custom font was made to satisfy the need for something that looks like LECO but is monospaced. The version I made contains only numbers, period, and colon, but if you want to build off of it, [it's public](https://git.wetfence.xyz/RandyTheSilly/pebble-misc/src/branch/main/fonts) and easy to modify! + +## Future Goals (1.1 and beyond) +- [Pavlok](https://pavlok.com/pages/how-it-works) integration to shock the user on failure to complete secondary (awareness) alarm and when force-exiting alarm +- Timeline integration +- Speaker support +- Full touch navigation +- Draw measurements based on triangle side locations, rather than in fixed positions diff --git a/package.json b/package.json index a315d9b..49386ec 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ ] }, { - "characterRegex": "[°ntype:θ=acuobsrigh¯¹0123456789]", + "characterRegex": "[sincostaeθ=¯¹0123456789hldSELECTubm]", "file": "mansalva_regular.ttf", "name": "FONT_MANSALVA_REGULAR_22", "type": "font" diff --git a/src/c/main.c b/src/c/main.c index d96cc7f..a41edf6 100644 --- a/src/c/main.c +++ b/src/c/main.c @@ -84,61 +84,72 @@ static char *s_options_under[2]; ////////////////////// STATUS ////////////////////// -static void status_0_1_back_click_handler(void *recognizer, void *ctx) { +static void click_handler_disable_tutorial(void *recognizer, void *ctx) { + persist_write_bool(STORAGE_KEY_DISABLE_TUTORIAL, true); + window_stack_pop_all(true); +} + +static void click_handler_push_setter(void *recognizer, void *ctx) { s_highlight_pos = 0; window_stack_pop_all(true); push_setter_window(); } -static void status_0_select_click_handler(void *recognizer, void *ctx) { +static void click_handler_exit(void *recognizer, void *ctx) { window_stack_pop_all(true); } -static void status_2_select_click_handler(void *recognizer, void *ctx) { +static void click_handler_retry(void *recognizer, void *ctx) { window_stack_pop(true); } -static void status_0_click_config_provider(void *ctx) { - window_single_click_subscribe(BUTTON_ID_UP, scroll_layer_scroll_up_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, scroll_layer_scroll_down_click_handler); - window_single_click_subscribe(BUTTON_ID_BACK, status_0_1_back_click_handler); - window_single_click_subscribe(BUTTON_ID_SELECT, status_0_select_click_handler); +static void status_main_tutorial_click_config_provider(void *ctx) { + window_single_click_subscribe(BUTTON_ID_SELECT, click_handler_exit); + window_single_click_subscribe(BUTTON_ID_BACK, click_handler_disable_tutorial); } -static void status_1_click_config_provider(void *ctx) { - window_single_click_subscribe(BUTTON_ID_BACK, status_0_1_back_click_handler); - window_single_click_subscribe(BUTTON_ID_SELECT, status_0_1_back_click_handler); - window_single_click_subscribe(BUTTON_ID_UP, status_0_1_back_click_handler); - window_single_click_subscribe(BUTTON_ID_DOWN, status_0_1_back_click_handler); +static void status_confirmation_click_config_provider(void *ctx) { + window_single_click_subscribe(BUTTON_ID_BACK, click_handler_push_setter); + window_single_click_subscribe(BUTTON_ID_SELECT, click_handler_exit); } -static void status_2_click_config_provider(void *ctx) { - window_single_click_subscribe(BUTTON_ID_SELECT, status_2_select_click_handler); +static void status_set_failure_click_config_provider(void *ctx) { + window_single_click_subscribe(BUTTON_ID_BACK, click_handler_push_setter); + window_single_click_subscribe(BUTTON_ID_SELECT, click_handler_push_setter); + window_single_click_subscribe(BUTTON_ID_UP, click_handler_push_setter); + window_single_click_subscribe(BUTTON_ID_DOWN, click_handler_push_setter); +} + +static void status_math_failure_click_config_provider(void *ctx) { + window_single_click_subscribe(BUTTON_ID_SELECT, click_handler_retry); } static void status_window_load(StatusWindowPurpose purpose) { Layer *window_layer = window_get_root_layer(s_status_window); s_status_txt_layer = text_layer_create(GRect(0, 0, PBL_DISPLAY_WIDTH, PBL_DISPLAY_HEIGHT)); + text_layer_set_font(s_status_txt_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24)); switch (purpose) { + case STATUS_MAIN_TUTORIAL: + // don't disable ticks to ensure vibrations remain till the tutorial is closed + text_layer_set_text(s_status_txt_layer, "Congrats! To ensure you're awake, I'll re-open without vibrating in 9 minutes. Complete the equation within 60 seconds to disarm me. Take too long and I'll vibrate and re-schedule another alarm.\n\nPress SELECT to exit or BACK to disable this tutorial in the future (and exit)."); + window_set_click_config_provider(s_status_window, status_main_tutorial_click_config_provider); + break; case STATUS_CONFIRMATION: tick_timer_service_unsubscribe(); - text_layer_set_font(s_status_txt_layer, fonts_get_system_font(FONT_KEY_GOTHIC_24)); - static char s_status_confirmed_buffer[105]; + static char s_status_confirmed_buffer[106]; snprintf(s_status_confirmed_buffer, sizeof(s_status_confirmed_buffer), "Alarm disarmed and re-set (%02u:%02u) successfully.\n\nPress SELECT to exit or BACK to change the alarm time.", the_time.HourSet, the_time.MinuteSet); text_layer_set_text(s_status_txt_layer, s_status_confirmed_buffer); - window_set_click_config_provider(s_status_window, status_0_click_config_provider); + window_set_click_config_provider(s_status_window, status_confirmation_click_config_provider); break; case STATUS_SET_FAILURE: tick_timer_service_unsubscribe(); - text_layer_set_font(s_status_txt_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28)); text_layer_set_text(s_status_txt_layer, "Failed to set an alarm.\nPress any button to return to alarm setter."); - window_set_click_config_provider(s_status_window, status_1_click_config_provider); + window_set_click_config_provider(s_status_window, status_set_failure_click_config_provider); break; - case 2: - text_layer_set_font(s_status_txt_layer, fonts_get_system_font(FONT_KEY_GOTHIC_28)); + case STATUS_MATH_FAILURE: text_layer_set_text(s_status_txt_layer, "Incorrect equation.\nTry again!"); - window_set_click_config_provider(s_status_window, status_2_click_config_provider); + window_set_click_config_provider(s_status_window, status_math_failure_click_config_provider); } text_layer_set_background_color(s_status_txt_layer, GColorClear); layer_set_frame(text_layer_get_layer(s_status_txt_layer), GRect(0, (PBL_DISPLAY_HEIGHT / 2) - (text_layer_get_content_size(s_status_txt_layer).h / 2), PBL_DISPLAY_WIDTH, PBL_DISPLAY_HEIGHT)); @@ -201,7 +212,7 @@ static bool schedule_alarm(WakeupReason schedule_reason) { } } if (status < 0) { - push_status_window(1); + push_status_window(STATUS_SET_FAILURE); return false; } return true; @@ -647,7 +658,7 @@ static void alarm_select_hold_handler(void *recognizer, void *ctx) { // check solution for (int i = 0; i < 3; ++i) { if (s_selected_indices[i] != s_solution_indices[i]) { - push_status_window(2); + push_status_window(STATUS_MATH_FAILURE); return; } } @@ -655,12 +666,17 @@ static void alarm_select_hold_handler(void *recognizer, void *ctx) { if (s_alarming_for_awareness) { // set main alarm if the awareness alarm was solved if (schedule_alarm(MAIN_ALARM)) { - push_status_window(0); + push_status_window(STATUS_CONFIRMATION); } } else { // otherwise, set awareness alarm and exit if (schedule_alarm(AWARENESS_ALARM)) { - window_stack_pop_all(true); + if (persist_read_bool(STORAGE_KEY_DISABLE_TUTORIAL)) { + window_stack_pop_all(true); + } else { + // show tutorial if it has not yet been disabled + push_status_window(STATUS_MAIN_TUTORIAL); + } } } } diff --git a/src/c/main.h b/src/c/main.h index 305b9be..86646a8 100644 --- a/src/c/main.h +++ b/src/c/main.h @@ -3,12 +3,14 @@ #define STORAGE_KEY_ALARM_HOUR 0 #define STORAGE_KEY_ALARM_MINUTE 1 #define STORAGE_KEY_ALARMING_FOR_AWARENESS 2 +#define STORAGE_KEY_DISABLE_TUTORIAL 3 typedef enum { - STATUS_CONFIRMATION = 0, - STATUS_SET_FAILURE = 1, - STATUS_MATH_FAILURE = 2 + STATUS_MAIN_TUTORIAL = 0, + STATUS_CONFIRMATION = 1, + STATUS_SET_FAILURE = 2, + STATUS_MATH_FAILURE = 3 } StatusWindowPurpose; static void push_setter_window();