mirror of
https://git.wetfence.xyz/RandyTheSilly/Trigalarm.git
synced 2026-08-27 22:26:25 -04:00
Make awareness alarm configurable and vastly more useful
This commit is contained in:
+4
-1
@@ -34,7 +34,10 @@
|
||||
"messageKeys": [
|
||||
"PKJS_READY",
|
||||
"PKJS_PAVLOK_ENABLE",
|
||||
"PKJS_PAVLOK_ZAP"
|
||||
"PKJS_PAVLOK_ZAP",
|
||||
"PKJS_AWARENESS_TIME_GAP",
|
||||
"PKJS_AWARENESS_TIME_LIMIT",
|
||||
"PKJS_AWARENESS_NUDGE_ENABLE"
|
||||
],
|
||||
"resources": {
|
||||
"media": [
|
||||
|
||||
+93
-58
@@ -1,17 +1,21 @@
|
||||
#include "main.h"
|
||||
#include "scheduler.h"
|
||||
#include "settings.h"
|
||||
#include "triangles.h"
|
||||
#include <pebble.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// settings
|
||||
static AppSettings settings;
|
||||
|
||||
// layer-age
|
||||
static Window *s_window;
|
||||
static Window *s_status_window;
|
||||
static Window *s_drop_window;
|
||||
static Layer *s_top_bar_layer;
|
||||
static Layer *s_bottom_bar_layer;
|
||||
static TextLayer *s_counter_txt_layer;
|
||||
static TextLayer *s_countdown_txt_layer;
|
||||
static TextLayer *s_status_txt_layer;
|
||||
//// setter
|
||||
static BitmapLayer *s_clock_bmp_layer;
|
||||
@@ -56,7 +60,8 @@ static uint8_t s_highlight_pos = 0;
|
||||
static WakeupReason s_alarm_reason;
|
||||
static uint8_t s_solution_indices[3] = {8, 8, 8};
|
||||
static uint8_t s_selected_indices[3] = {9, 9, 9};
|
||||
static uint8_t s_stopwatch_time[2] = {0, 0};
|
||||
static uint8_t s_awareness_countdown_int = 0;
|
||||
static char s_awareness_countdown_buf[4];
|
||||
static uint32_t s_vibe_segments[5];
|
||||
static VibePattern s_vibe_pattern;
|
||||
|
||||
@@ -85,7 +90,8 @@ static char *s_options_under[2];
|
||||
////////////////////// STATUS //////////////////////
|
||||
|
||||
static void click_handler_disable_tutorial(void *recognizer, void *ctx) {
|
||||
persist_write_bool(STORAGE_KEY_DISABLE_TUTORIAL, true);
|
||||
settings.TutorialEnabled = false;
|
||||
persist_write_data(STORAGE_KEY_SETTINGS, &settings, sizeof(settings));
|
||||
window_stack_pop_all(true);
|
||||
}
|
||||
|
||||
@@ -133,7 +139,7 @@ static void status_window_load(StatusWindowPurpose purpose) {
|
||||
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).");
|
||||
text_layer_set_text(s_status_txt_layer, "Correct! An awareness alarm is now being set. Solve it within the time limit to disarm Trigalarm.\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: {
|
||||
@@ -173,21 +179,27 @@ static void push_status_window(StatusWindowPurpose purpose) {
|
||||
|
||||
////////////////////// PKJS //////////////////////
|
||||
|
||||
static void apply_uint8_from_tuple(DictionaryIterator *iter, uint32_t key, void *dest) {
|
||||
Tuple *t = dict_find(iter, key);
|
||||
if (!t)
|
||||
return;
|
||||
*(uint8_t *)dest = t->value->uint8;
|
||||
}
|
||||
|
||||
static void inbox_received_handler(DictionaryIterator *iter, void *context) {
|
||||
Tuple *ready_tuple = dict_find(iter, MESSAGE_KEY_PKJS_READY);
|
||||
if (ready_tuple) {
|
||||
return; // TODO use ready signal better
|
||||
}
|
||||
|
||||
Tuple *clay_tuple = dict_find(iter, MESSAGE_KEY_PKJS_PAVLOK_ENABLE);
|
||||
if (clay_tuple) {
|
||||
persist_write_bool(STORAGE_KEY_PAVLOK_ENABLED, clay_tuple->value->uint8);
|
||||
return;
|
||||
}
|
||||
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_PAVLOK_ENABLE, &settings.PavlokEnabled);
|
||||
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_AWARENESS_TIME_GAP, &settings.AwarenessTimeGapMinutes);
|
||||
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_AWARENESS_TIME_LIMIT, &settings.AwarenessTimeLimitSeconds);
|
||||
apply_uint8_from_tuple(iter, MESSAGE_KEY_PKJS_AWARENESS_NUDGE_ENABLE, &settings.AwarenessNudgeEnabled);
|
||||
persist_write_data(STORAGE_KEY_SETTINGS, &settings, sizeof(settings));
|
||||
}
|
||||
|
||||
static void send_zap() {
|
||||
if (persist_read_bool(STORAGE_KEY_PAVLOK_ENABLED) == false) {
|
||||
if (!settings.PavlokEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -216,7 +228,7 @@ static bool schedule_alarm(WakeupReason schedule_reason) {
|
||||
status = wakeup_schedule_simple_robust(target, 15, schedule_reason);
|
||||
break;
|
||||
case WAKE_AWARENESS_ALARM:
|
||||
target = time(NULL) + 9 * SECONDS_PER_MINUTE;
|
||||
target = time(NULL) + settings.AwarenessTimeGapMinutes * SECONDS_PER_MINUTE;
|
||||
wakeup_cancel_all();
|
||||
status = wakeup_schedule_simple_robust(target, SECONDS_PER_MINUTE, schedule_reason);
|
||||
break;
|
||||
@@ -239,7 +251,7 @@ static bool schedule_alarm(WakeupReason schedule_reason) {
|
||||
persist_write_int(STORAGE_KEY_ALARM_REASON, WAKE_MAIN_ALARM);
|
||||
return false;
|
||||
}
|
||||
if (s_alarm_reason != schedule_reason) {
|
||||
if (persist_read_int(STORAGE_KEY_ALARM_REASON) != schedule_reason) {
|
||||
persist_write_int(STORAGE_KEY_ALARM_REASON, schedule_reason);
|
||||
}
|
||||
return true;
|
||||
@@ -270,26 +282,20 @@ static void add_bar_layers_to_window(Layer *window_layer, bool small_top) {
|
||||
|
||||
////////////////////// ALARM //////////////////////
|
||||
|
||||
static void alarm_seconds_handler(struct tm *tick_time, TimeUnits units_changed) {
|
||||
// update stopwatch
|
||||
if (s_stopwatch_time[1] == 59) {
|
||||
s_stopwatch_time[1] = 0;
|
||||
s_stopwatch_time[0]++;
|
||||
send_zap();
|
||||
// fall back to main alarm and resume vibes
|
||||
if (s_alarm_reason != WAKE_MAIN_ALARM) {
|
||||
// this may be overwritten when the recovery alarm is set,
|
||||
// but it will still allow vibes to occur since the alarm reason
|
||||
// will not be awareness
|
||||
s_alarm_reason = WAKE_MAIN_ALARM;
|
||||
static void main_alarm_seconds_handler(struct tm *tick_time, TimeUnits units_changed) {
|
||||
vibes_enqueue_custom_pattern(s_vibe_pattern);
|
||||
}
|
||||
|
||||
static void awareness_alarm_seconds_handler(struct tm *tick_time, TimeUnits units_changed) {
|
||||
if (s_awareness_countdown_int > 0) {
|
||||
s_awareness_countdown_int--;
|
||||
snprintf(s_awareness_countdown_buf, sizeof(s_awareness_countdown_buf), "%03u", s_awareness_countdown_int);
|
||||
text_layer_set_text(s_countdown_txt_layer, s_awareness_countdown_buf);
|
||||
if (s_awareness_countdown_int == 0) {
|
||||
send_zap(); // TODO implement repeat interval
|
||||
}
|
||||
} else {
|
||||
s_stopwatch_time[1]++;
|
||||
}
|
||||
static char s_stopwatch_buffer[9];
|
||||
snprintf(s_stopwatch_buffer, sizeof(s_stopwatch_buffer), "%02u:%02u", s_stopwatch_time[0], s_stopwatch_time[1]);
|
||||
text_layer_set_text(s_counter_txt_layer, s_stopwatch_buffer);
|
||||
if (s_alarm_reason != WAKE_AWARENESS_ALARM) {
|
||||
// TODO flash countdown txt layer every second
|
||||
vibes_enqueue_custom_pattern(s_vibe_pattern);
|
||||
}
|
||||
}
|
||||
@@ -600,14 +606,18 @@ static void alarm_window_load() {
|
||||
s_drop_window,
|
||||
(WindowHandlers){.load = drop_menu_window_load, .unload = drop_menu_window_unload});
|
||||
|
||||
// stopwatch
|
||||
s_counter_txt_layer = text_layer_create(GRect(0, (s_small_top_bar_height / 2) - PBL_IF_ROUND_ELSE(6, 10), PBL_DISPLAY_WIDTH, s_small_top_bar_height));
|
||||
text_layer_set_background_color(s_counter_txt_layer, GColorClear);
|
||||
text_layer_set_text_color(s_counter_txt_layer, GColorWhite);
|
||||
text_layer_set_text_alignment(s_counter_txt_layer, GTextAlignmentCenter);
|
||||
text_layer_set_font(s_counter_txt_layer, s_font_lecoish_small);
|
||||
text_layer_set_text(s_counter_txt_layer, "00:00");
|
||||
layer_add_child(window_layer, text_layer_get_layer(s_counter_txt_layer));
|
||||
// countdown
|
||||
if (s_alarm_reason == WAKE_AWARENESS_ALARM) {
|
||||
s_countdown_txt_layer = text_layer_create(GRect(0, (s_small_top_bar_height / 2) - PBL_IF_ROUND_ELSE(6, 10), PBL_DISPLAY_WIDTH, s_small_top_bar_height));
|
||||
text_layer_set_background_color(s_countdown_txt_layer, GColorClear);
|
||||
text_layer_set_text_color(s_countdown_txt_layer, GColorWhite);
|
||||
text_layer_set_text_alignment(s_countdown_txt_layer, GTextAlignmentCenter);
|
||||
text_layer_set_font(s_countdown_txt_layer, s_font_lecoish_small);
|
||||
s_awareness_countdown_int = settings.AwarenessTimeLimitSeconds;
|
||||
snprintf(s_awareness_countdown_buf, sizeof(s_awareness_countdown_buf), "%03u", s_awareness_countdown_int);
|
||||
text_layer_set_text(s_countdown_txt_layer, s_awareness_countdown_buf);
|
||||
layer_add_child(window_layer, text_layer_get_layer(s_countdown_txt_layer));
|
||||
}
|
||||
|
||||
// setup vibes
|
||||
s_vibe_segments[0] = (uint32_t)(s_triangle_layer->angle_A * 2);
|
||||
@@ -621,12 +631,18 @@ static void alarm_window_load() {
|
||||
};
|
||||
|
||||
schedule_alarm(WAKE_RECOVERY_ALARM); // output is not captured, as it is not actionable
|
||||
tick_timer_service_subscribe(SECOND_UNIT, alarm_seconds_handler);
|
||||
if (s_alarm_reason == WAKE_AWARENESS_ALARM) {
|
||||
tick_timer_service_subscribe(SECOND_UNIT, awareness_alarm_seconds_handler);
|
||||
} else {
|
||||
tick_timer_service_subscribe(SECOND_UNIT, main_alarm_seconds_handler);
|
||||
}
|
||||
}
|
||||
|
||||
static void alarm_window_unload() {
|
||||
tick_timer_service_unsubscribe();
|
||||
text_layer_destroy(s_counter_txt_layer);
|
||||
if (s_countdown_txt_layer != NULL) {
|
||||
text_layer_destroy(s_countdown_txt_layer);
|
||||
}
|
||||
window_destroy(s_drop_window);
|
||||
text_layer_destroy(s_side_a_txt_layer);
|
||||
text_layer_destroy(s_side_b_txt_layer);
|
||||
@@ -698,20 +714,19 @@ static void alarm_select_hold_handler(void *recognizer, void *ctx) {
|
||||
// no-longer-important recovery alarm
|
||||
wakeup_service_subscribe(NULL);
|
||||
|
||||
if (s_alarm_reason == WAKE_AWARENESS_ALARM) {
|
||||
// set main alarm if the awareness alarm was solved
|
||||
tick_timer_service_unsubscribe(); // alarm is fully disarmed - stop vibes+countdown for confirmation window
|
||||
if (s_awareness_countdown_int > 0) {
|
||||
// set main alarm if the awareness alarm was solved within time limit
|
||||
tick_timer_service_unsubscribe(); // alarm is fully disarmed - prevent stimulus in confirmation window
|
||||
if (schedule_alarm(WAKE_MAIN_ALARM)) {
|
||||
push_status_window(STATUS_CONFIRMATION);
|
||||
}
|
||||
} else {
|
||||
// otherwise, set awareness alarm and exit
|
||||
if (schedule_alarm(WAKE_AWARENESS_ALARM)) {
|
||||
if (persist_read_bool(STORAGE_KEY_DISABLE_TUTORIAL)) {
|
||||
window_stack_pop_all(true);
|
||||
} else {
|
||||
// show tutorial if it has not yet been disabled
|
||||
if (settings.TutorialEnabled && s_alarm_reason == WAKE_MAIN_ALARM) {
|
||||
push_status_window(STATUS_MAIN_TUTORIAL);
|
||||
} else {
|
||||
window_stack_pop_all(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -759,7 +774,7 @@ static void setter_seconds_handler(struct tm *tick_time, TimeUnits units_changed
|
||||
|
||||
static char s_countdown_buffer[12];
|
||||
snprintf(s_countdown_buffer, sizeof(s_countdown_buffer), "%02u:%02u:%02u", hours, minutes, seconds);
|
||||
text_layer_set_text(s_counter_txt_layer, s_countdown_buffer);
|
||||
text_layer_set_text(s_countdown_txt_layer, s_countdown_buffer);
|
||||
}
|
||||
|
||||
static void update_countdown_display() {
|
||||
@@ -909,12 +924,12 @@ static void setter_window_load() {
|
||||
layer_add_child(window_layer, bitmap_layer_get_layer(s_check_bmp_layer));
|
||||
|
||||
// countdown
|
||||
s_counter_txt_layer = text_layer_create(GRect(PBL_IF_ROUND_ELSE(0, 43), (s_top_bar_grect.size.h / 2) + PBL_IF_ROUND_ELSE(10, -10), PBL_IF_ROUND_ELSE(s_top_bar_grect.size.w, s_top_bar_grect.size.w - 39), 32));
|
||||
text_layer_set_text_alignment(s_counter_txt_layer, GTextAlignmentCenter);
|
||||
text_layer_set_text_color(s_counter_txt_layer, GColorWhite);
|
||||
text_layer_set_background_color(s_counter_txt_layer, GColorClear);
|
||||
text_layer_set_font(s_counter_txt_layer, s_font_lecoish_small);
|
||||
layer_add_child(window_layer, text_layer_get_layer(s_counter_txt_layer));
|
||||
s_countdown_txt_layer = text_layer_create(GRect(PBL_IF_ROUND_ELSE(0, 43), (s_top_bar_grect.size.h / 2) + PBL_IF_ROUND_ELSE(10, -10), PBL_IF_ROUND_ELSE(s_top_bar_grect.size.w, s_top_bar_grect.size.w - 39), 32));
|
||||
text_layer_set_text_alignment(s_countdown_txt_layer, GTextAlignmentCenter);
|
||||
text_layer_set_text_color(s_countdown_txt_layer, GColorWhite);
|
||||
text_layer_set_background_color(s_countdown_txt_layer, GColorClear);
|
||||
text_layer_set_font(s_countdown_txt_layer, s_font_lecoish_small);
|
||||
layer_add_child(window_layer, text_layer_get_layer(s_countdown_txt_layer));
|
||||
update_countdown_display();
|
||||
|
||||
// highlighter
|
||||
@@ -938,7 +953,7 @@ static void setter_window_unload() {
|
||||
tick_timer_service_unsubscribe();
|
||||
text_layer_destroy(s_setter_txt_layer);
|
||||
layer_destroy(s_setter_highlight_layer);
|
||||
text_layer_destroy(s_counter_txt_layer);
|
||||
text_layer_destroy(s_countdown_txt_layer);
|
||||
bitmap_layer_destroy(s_check_bmp_layer);
|
||||
bitmap_layer_destroy(s_clock_bmp_layer);
|
||||
gbitmap_destroy(s_check_bmp);
|
||||
@@ -964,15 +979,35 @@ static void init() {
|
||||
the_time.HourSet = the_time.HourWIP;
|
||||
the_time.MinuteSet = the_time.MinuteWIP;
|
||||
|
||||
// initialize pkjs TODO maybe only do in alarm mode?
|
||||
// initialize pkjs
|
||||
app_message_register_inbox_received(inbox_received_handler);
|
||||
app_message_open(64, 64); // TODO tighten sizes
|
||||
|
||||
// load settings from persist (or set defaults if not present)
|
||||
if (persist_exists(STORAGE_KEY_SETTINGS)) {
|
||||
persist_read_data(STORAGE_KEY_SETTINGS, &settings, sizeof(settings));
|
||||
} else {
|
||||
settings.TutorialEnabled = true;
|
||||
settings.PavlokEnabled = false;
|
||||
settings.AwarenessTimeGapMinutes = 9;
|
||||
settings.AwarenessTimeLimitSeconds = 30;
|
||||
settings.AwarenessNudgeEnabled = true;
|
||||
}
|
||||
|
||||
s_alarm_reason = persist_read_int(STORAGE_KEY_ALARM_REASON);
|
||||
switch (launch_reason()) {
|
||||
case APP_LAUNCH_WAKEUP:
|
||||
if (s_alarm_reason == WAKE_RECOVERY_ALARM) {
|
||||
switch (s_alarm_reason) {
|
||||
case WAKE_RECOVERY_ALARM:
|
||||
send_zap();
|
||||
break;
|
||||
case WAKE_AWARENESS_ALARM:
|
||||
if (settings.AwarenessNudgeEnabled) {
|
||||
vibes_short_pulse();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
push_alarm_window();
|
||||
break;
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define STORAGE_KEY_ALARM_REASON 10 // BUMP ON UPDATES TO PREVENT USERS GETTING STUCK W/O ALARM
|
||||
#define STORAGE_KEY_ALARM_HOUR 0
|
||||
#define STORAGE_KEY_ALARM_MINUTE 1
|
||||
#define STORAGE_KEY_DISABLE_TUTORIAL 2
|
||||
#define STORAGE_KEY_PAVLOK_ENABLED 3
|
||||
|
||||
typedef enum
|
||||
{
|
||||
STATUS_MAIN_TUTORIAL = 0,
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// BUMP ON UPDATES
|
||||
#define STORAGE_KEY_SETTINGS 50
|
||||
#define STORAGE_KEY_ALARM_REASON 100
|
||||
|
||||
#define STORAGE_KEY_ALARM_HOUR 0
|
||||
#define STORAGE_KEY_ALARM_MINUTE 1
|
||||
|
||||
typedef struct AppSettings {
|
||||
bool TutorialEnabled;
|
||||
bool PavlokEnabled;
|
||||
uint8_t AwarenessTimeGapMinutes;
|
||||
uint8_t AwarenessTimeLimitSeconds;
|
||||
uint8_t AwarenessNudgeEnabled;
|
||||
} __attribute__((__packed__)) AppSettings;
|
||||
|
||||
_Static_assert(sizeof(AppSettings) <= 256, "AppSettings exceeds Pebble 256-byte persist limit!");
|
||||
+39
-4
@@ -28,10 +28,10 @@ module.exports = [
|
||||
"id": "pavlok_zap_intensity",
|
||||
"type": "slider",
|
||||
"messageKey": "PKJS_PAVLOK_ZAP_INTENSITY",
|
||||
defaultValue: 30,
|
||||
min: 5,
|
||||
max: 100,
|
||||
step: 5,
|
||||
"defaultValue": 30,
|
||||
"min": 5,
|
||||
"max": 100,
|
||||
"step": 5,
|
||||
"label": "Zap Intensity"
|
||||
},
|
||||
{
|
||||
@@ -42,6 +42,41 @@ module.exports = [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"items": [
|
||||
{
|
||||
"type": "heading",
|
||||
"size": 3,
|
||||
"defaultValue": "Awareness Alarm"
|
||||
},
|
||||
{
|
||||
"id": "awareness_time_gap",
|
||||
"type": "slider",
|
||||
"messageKey": "PKJS_AWARENESS_TIME_GAP",
|
||||
"defaultValue": 9,
|
||||
"min": 1,
|
||||
"max": 30,
|
||||
"label": "Gap From Main Alarm (Minutes)"
|
||||
},
|
||||
{
|
||||
"id": "awareness_time_limit",
|
||||
"type": "slider",
|
||||
"messageKey": "PKJS_AWARENESS_TIME_LIMIT",
|
||||
"defaultValue": 30,
|
||||
"min": 3,
|
||||
"max": 120,
|
||||
"label": "Time Limit (Seconds)"
|
||||
},
|
||||
{
|
||||
"id": "awareness_nudge_enable",
|
||||
"type": "toggle",
|
||||
"messageKey": "PKJS_AWARENESS_NUDGE_ENABLE",
|
||||
"defaultValue": true,
|
||||
"label": "Awareness Nudge"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "submit",
|
||||
"defaultValue": "Save Settings"
|
||||
|
||||
Reference in New Issue
Block a user