Skip to content
This repository has been archived by the owner on Apr 27, 2020. It is now read-only.

Commit

Permalink
Added options for vibration and greeting text
Browse files Browse the repository at this point in the history
Due to a request from /u/PiwwowPants, the app now allows you to choose a "simple" vibation (i.e. two taps), rather than the full scary shaking that happened before. The app now also allows for a simple "Hello" rather than default showing step count.
  • Loading branch information
aaronhktan committed Nov 28, 2016
1 parent d13ca9f commit dc969c7
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 93 deletions.
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"author": "cheeseisdisgusting@gmail.com",
"dependencies": {
"pebble-clay": "^1.0.3"
"pebble-clay": "^1.0.4"
},
"keywords": [],
"name": "breathe",
Expand All @@ -16,11 +16,12 @@
"backgroundColor",
"circleColor",
"vibrationEnabled",
"heartRateEnabled",
"reminderHours",
"reminderHoursStart",
"rememberDuration",
"breathsPerMinute"
"breathsPerMinute",
"vibrationType",
"displayText"
],
"projectType": "native",
"resources": {
Expand Down Expand Up @@ -54,5 +55,5 @@
"watchface": false
}
},
"version": "0.28.0"
"version": "0.29.0"
}
2 changes: 1 addition & 1 deletion src/c/appglance.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ void appglance_update_app_glance(AppGlanceReloadSession *session, size_t limit,
.icon = APP_GLANCE_SLICE_DEFAULT_ICON,
.subtitle_template_string = message
},
// TODO: Change to APP_GLANCE_SLICE_NO_EXPIRATION in SDK 4-dp2

.expiration_time = APP_GLANCE_SLICE_NO_EXPIRATION
};

Expand Down
86 changes: 46 additions & 40 deletions src/c/breathe_window.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

static Window *s_main_window;
static Layer *s_circle_layer, *s_inside_text_layer, *s_upper_text_layer, *s_lower_text_layer;
static AppTimer *s_animation_completed_timer, *s_main_animation_ended_timer, *animationTimer[69], *s_show_relax_text_timer, *s_show_inhale_timer, *s_hide_exhale_timer, *s_show_exhale_timer, *s_hide_lower_text_layer, *s_click_provider_timer;
static AppTimer *s_animation_completed_timer, *s_main_animation_ended_timer, *animationTimer[69], *s_show_relax_text_timer, *s_show_inhale_timer, *s_show_exhale_timer, *s_hide_lower_text_layer, *s_click_provider_timer;
static GRect bounds;
static uint8_t s_radius_final, s_radius = 0;
static int s_min_to_breathe = 1, s_min_breathed_today = 0, s_times_played = 0, s_breath_duration, s_breaths_per_minute;
Expand All @@ -29,7 +29,7 @@ static void inside_text_layer_update_proc(Layer *s_inside_text_layer, GContext *

// Draws text at top of screen
static void upper_text_layer_update_proc(Layer *s_inside_text_layer, GContext *ctx) {
graphics_draw_upper_text(ctx, bounds, s_animating, settings_get_heartRateEnabled(), settings_get_textColor(), s_greet_text);
graphics_draw_upper_text(ctx, bounds, s_animating, settings_get_displayText(), settings_get_textColor(), s_greet_text);
}

// Draws text at bottom of screen
Expand Down Expand Up @@ -153,9 +153,6 @@ static void main_animation() {
layer_set_hidden(s_inside_text_layer, true);
#endif

layer_set_hidden(s_upper_text_layer, true);
layer_set_hidden(s_lower_text_layer, true);

// Circle expands for 3 seconds and delays for 1 second
Animation *circle_expand = animation_create();
animation_set_duration(circle_expand, s_breath_duration);
Expand All @@ -182,44 +179,60 @@ static void main_animation() {
s_times_played++; // Used to keep track to see how many should be played to fill time

if (settings_get_vibrationEnabled()) {
// Vibrations! (play for 0, rest for 1500, play for 25, rest for 25, etc.)
static uint32_t segments[52];
switch(s_breaths_per_minute) {
case 4: ; // 15000 milliseconds long, with an empty statement after a label before a declaration
static const uint32_t four_segments[52] = {0, 2500, 25, 50, 25, 50, 25, 65, 25, 65, 25, 75, 25, 75, 25, 80, 25, 80, 25, 100, 25, 100, 25, 150, 25, 150, 25, 175, 25, 175, 25, 225, 25, 225, 25, 275, 25, 275, 25, 375, 25, 375, 25, 450, 25, 450, 25, 500, 25, 7300};
memcpy(segments, four_segments, sizeof(four_segments));
break;
case 5: ; // 12000 milliseconds long (actually 11665 but who cares)
static const uint32_t five_segments[45] = {0, 2000, 25, 50, 25, 50, 25, 65, 25, 65, 25, 75, 25, 75, 25, 80, 25, 80, 25, 100, 25, 100, 25, 150, 25, 150, 25, 175, 25, 175, 25, 225, 25, 225, 25, 275, 25, 275, 25, 375, 25, 375, 25, 6000};
memcpy(segments, five_segments, sizeof(five_segments));
break;
case 6:
case 7: ; // 8000 milliseconds long (actually 7650 milliseconds long), with an empty statement after a label before a declaration
static const uint32_t seven_segments[31] = {0, 1500, 25, 25, 25, 25, 25, 25, 25, 25, 25, 50, 25, 75, 25, 125, 25, 125, 25, 125, 25, 125, 25, 200, 25, 325, 25, 550, 25, 4000};
memcpy(segments, seven_segments, sizeof(seven_segments));
switch(settings_get_vibrationType()) {
case 0: ;
// Vibrations! (play for 0, rest for 1500, play for 25, rest for 25, etc.)
static uint32_t segments[52];
switch(s_breaths_per_minute) {
case 4: ; // 15000 milliseconds long, with an empty statement after a label before a declaration
static const uint32_t four_segments[52] = {0, 2500, 25, 50, 25, 50, 25, 65, 25, 65, 25, 75, 25, 75, 25, 80, 25, 80, 25, 100, 25, 100, 25, 150, 25, 150, 25, 175, 25, 175, 25, 225, 25, 225, 25, 275, 25, 275, 25, 375, 25, 375, 25, 450, 25, 450, 25, 500, 25, 7300};
memcpy(segments, four_segments, sizeof(four_segments));
break;
case 5: ; // 12000 milliseconds long (actually 11665 but who cares)
static const uint32_t five_segments[45] = {0, 2000, 25, 50, 25, 50, 25, 65, 25, 65, 25, 75, 25, 75, 25, 80, 25, 80, 25, 100, 25, 100, 25, 150, 25, 150, 25, 175, 25, 175, 25, 225, 25, 225, 25, 275, 25, 275, 25, 375, 25, 375, 25, 6000};
memcpy(segments, five_segments, sizeof(five_segments));
break;
case 6:
case 7: ; // 8000 milliseconds long (actually 7650 milliseconds long), with an empty statement after a label before a declaration
static const uint32_t seven_segments[31] = {0, 1500, 25, 25, 25, 25, 25, 25, 25, 25, 25, 50, 25, 75, 25, 125, 25, 125, 25, 125, 25, 125, 25, 200, 25, 325, 25, 550, 25, 4000};
memcpy(segments, seven_segments, sizeof(seven_segments));
break;
case 8: // 7000 milliseconds long (actually 7000) (wow!!)
case 9: ;
static const uint32_t nine_segments[29] = {0, 1500, 25, 25, 25, 25, 25, 25, 25, 50, 25, 75, 25, 100, 25, 125, 25, 150, 25, 200, 25, 250, 25, 300, 25, 350, 25, 3500};
memcpy(segments, nine_segments, sizeof(nine_segments));
break;
default: ; // 6000 milliseconds long (actually 5075)
static const uint32_t ten_segments[31] = {0, 1100, 25, 25, 25, 25, 25, 25, 25, 50, 25, 75, 25, 100, 25, 100, 25, 125, 25, 125, 25, 150, 25, 250, 25, 275, 25, 300, 25, 2000};
memcpy(segments, ten_segments, sizeof(ten_segments));
break;
}
VibePattern vibes = {
.durations = segments,
.num_segments = ARRAY_LENGTH(segments),
};
vibes_enqueue_custom_pattern(vibes);
break;
case 8: // 7000 milliseconds long (actually 7000) (wow!!)
case 9: ;
static const uint32_t nine_segments[29] = {0, 1500, 25, 25, 25, 25, 25, 25, 25, 50, 25, 75, 25, 100, 25, 125, 25, 150, 25, 200, 25, 250, 25, 300, 25, 350, 25, 3500};
memcpy(segments, nine_segments, sizeof(nine_segments));
break;
default: ; // 6000 milliseconds long (actually 5075)
static const uint32_t ten_segments[31] = {0, 1100, 25, 25, 25, 25, 25, 25, 25, 50, 25, 75, 25, 100, 25, 100, 25, 125, 25, 125, 25, 150, 25, 250, 25, 275, 25, 300, 25, 2000};
memcpy(segments, ten_segments, sizeof(ten_segments));
default: ;// 1000 delay for animation, 50 play, 100 stop, 50 play, rest for breath duration and delay and subtract (50 + 100 + 50), and vibrate again.
const uint32_t segments_simple[] = {0, 1000, 50, 100, 50, settings_get_breathDuration() + 1000 - 200, 50, 100, 50, settings_get_breathDuration() - 300};
VibePattern vibes_simple = {
.durations = segments_simple,
.num_segments = ARRAY_LENGTH(segments_simple),
};
vibes_enqueue_custom_pattern(vibes_simple);
break;
}
VibePattern vibes = {
.durations = segments,
.num_segments = ARRAY_LENGTH(segments),
};
vibes_enqueue_custom_pattern(vibes);
}
}

// Schedules next animation if the number of times played is less than 7 times the number of minutes (seven breaths per minute)
static void main_animation_callback () {
if (s_times_played < s_breaths_per_minute * s_min_to_breathe) {
animationTimer[s_times_played] = app_timer_register(2 * s_breath_duration + 2000, main_animation_callback, NULL);
if (!layer_get_hidden(s_upper_text_layer) || !layer_get_hidden(s_lower_text_layer)) {
layer_set_hidden(s_upper_text_layer, true);
layer_set_hidden(s_lower_text_layer, true);
}
main_animation();
}
}
Expand All @@ -240,11 +253,6 @@ static void first_breath_out_callback(void *context) {
layer_set_hidden(s_lower_text_layer, false);
}

// Hides bottom text
static void first_breath_out_hide_callback(void *context) {
layer_set_hidden(s_lower_text_layer, true);
}

// Start animation show text
static void animation_start_callback(void *context) {
// Sets strings as English, change if watch is set to another language
Expand Down Expand Up @@ -396,8 +404,6 @@ static void select_click_handler(ClickRecognizerRef recognizer, void *context) {
/* Shows the instruction to exhale at after one breathe in
Also hides the first instruction */
s_show_exhale_timer = app_timer_register(7100 + s_breath_duration, first_breath_out_callback, NULL);
// Hides the instruction to exhale after one breath in and one breath out
s_hide_exhale_timer = app_timer_register(7100 + 2 * s_breath_duration, first_breath_out_hide_callback, NULL);

// First animationTimer, which will schedule the next time the circle expands or contracts
animationTimer[0] = app_timer_register(6000, main_animation_callback, NULL);
Expand Down
19 changes: 16 additions & 3 deletions src/c/graphics.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ static GPath *s_up_triangle, *s_down_triangle;
#endif

// Method for updating the upper text layer
void graphics_draw_upper_text(GContext *ctx, GRect bounds, bool is_animating, bool heart_rate, GColor textColor, char *greet_text) {
void graphics_draw_upper_text(GContext *ctx, GRect bounds, bool is_animating, int display_text, GColor textColor, char *greet_text) {
#if defined(PBL_HEALTH)
const char *steps_buffer = data_get_current_steps_buffer(); // Pebble Health exists; fetch the number of steps walked today
#endif
Expand All @@ -48,15 +48,28 @@ void graphics_draw_upper_text(GContext *ctx, GRect bounds, bool is_animating, bo
GRect((bounds.size.w - greet_text_bounds.w) / 2, PBL_IF_RECT_ELSE(5, 15), greet_text_bounds.w, greet_text_bounds.h),
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
} else {
if (heart_rate && data_get_current_heart_rate() > 0) { // If heart rate monitor is enabled in configuration and is available, show heart rate
if (display_text == 2 && data_get_current_heart_rate() > 0) { // If heart rate monitor is enabled in configuration and is available, show heart rate
APP_LOG(APP_LOG_LEVEL_DEBUG, "The app is showing the heart rate.");
const char *heart_rate_buffer = data_get_current_heart_rate_buffer();
graphics_draw_text(ctx, heart_rate_buffer, fonts_get_system_font(FONT_KEY),
GRect((bounds.size.w - greet_text_bounds.w) / 2, PBL_IF_RECT_ELSE(5, 15), greet_text_bounds.w, greet_text_bounds.h),
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
} else { // Otherwise, show step counts if Pebble Health, and string if not.
graphics_draw_text(ctx, PBL_IF_HEALTH_ELSE(steps_buffer, greet_text), fonts_get_system_font(FONT_KEY),
switch(display_text) {
case 0:
APP_LOG(APP_LOG_LEVEL_DEBUG, "The app is showing the default greeting text.");
graphics_draw_text(ctx, greet_text, fonts_get_system_font(FONT_KEY),
GRect((bounds.size.w - greet_text_bounds.w) / 2, PBL_IF_RECT_ELSE(5, 15), greet_text_bounds.w, greet_text_bounds.h),
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
break;
case 1:
case 2:
APP_LOG(APP_LOG_LEVEL_DEBUG, "The app is showing the number of steps.");
graphics_draw_text(ctx, PBL_IF_HEALTH_ELSE(steps_buffer, greet_text), fonts_get_system_font(FONT_KEY),
GRect((bounds.size.w - greet_text_bounds.w) / 2, PBL_IF_RECT_ELSE(5, 15), greet_text_bounds.w, greet_text_bounds.h),
GTextOverflowModeWordWrap, GTextAlignmentCenter, NULL);
break;
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/c/graphics.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#define FONT_KEY FONT_KEY_GOTHIC_14_BOLD
#endif

void graphics_draw_upper_text(GContext *ctx, GRect bounds, bool is_animating, bool heart_rate, GColor textColor, char *);
void graphics_draw_upper_text(GContext *ctx, GRect bounds, bool is_animating, int displayText, GColor textColor, char *);

void graphics_draw_lower_text(GContext *ctx, GRect bounds, bool is_animating, GColor textColor, char *);

Expand Down
2 changes: 1 addition & 1 deletion src/c/localize.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ char * localize_get_hello_text() {
} else if (strncmp(localize_get_locale(), "es", 2) == 0) {
return "¡HOLA!";
} else {
return "HELLO.";
return "HELLO!";
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/c/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include "src/c/appglance.h"

static void init() {
APP_LOG(APP_LOG_LEVEL_INFO, "You are running version 0.2.8 of the Breathe app.");
APP_LOG(APP_LOG_LEVEL_INFO, "You are running version 0.2.9 of the Breathe app.");
#if PBL_HEALTH
health_init(); // Subscribe to health service if health API is available
#endif
Expand Down
25 changes: 18 additions & 7 deletions src/c/settings.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ void settings_init() {
settings.circleColor = PBL_IF_COLOR_ELSE(GColorJaegerGreen, GColorWhite);
settings.textColor = GColorWhite;
settings.vibrationEnabled = true;
settings.vibrationType = 0;
settings.rememberDuration = false;
#if PBL_API_EXISTS(health_service_peek_current_value)
settings.heartRateEnabled = true;
settings.displayText = 2;
#else
settings.heartRateEnabled = false;
settings.displayText = 1;
#endif
settings.reminderHours = 4;
settings.reminderHoursStart = 8;
Expand Down Expand Up @@ -52,9 +53,15 @@ void settings_handle_settings(DictionaryIterator *iter, void *context) {
settings.vibrationEnabled = vibration_enabled_t->value->int32 == 1;
}

Tuple *heartRate_enabled_t = dict_find(iter, MESSAGE_KEY_heartRateEnabled);
if (heartRate_enabled_t) {
settings.heartRateEnabled = heartRate_enabled_t->value->int32 == 1;
Tuple *vibration_type_t = dict_find(iter, MESSAGE_KEY_vibrationType);
if (vibration_type_t) {
settings.vibrationType = vibration_type_t->value->int8;
}

Tuple *displayText_t = dict_find(iter, MESSAGE_KEY_displayText);
if (displayText_t) {
settings.displayText = displayText_t->value->int8;
APP_LOG(APP_LOG_LEVEL_DEBUG, "The displayText value is %d.", settings.displayText);
}

Tuple *reminder_hours_start_t = dict_find(iter, MESSAGE_KEY_reminderHoursStart);
Expand Down Expand Up @@ -101,8 +108,12 @@ bool settings_get_vibrationEnabled() {
return settings.vibrationEnabled;
}

bool settings_get_heartRateEnabled() {
return settings.heartRateEnabled;
int settings_get_vibrationType() {
return settings.vibrationType;
}

int settings_get_displayText() {
return settings.displayText;
}

int settings_get_reminderHours() {
Expand Down
6 changes: 4 additions & 2 deletions src/c/settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ typedef struct ClaySettings {
GColor circleColor;
GColor textColor;
bool vibrationEnabled;
bool heartRateEnabled;
int vibrationType;
int displayText;
int reminderHours;
bool rememberDuration;
int reminderHoursStart;
Expand All @@ -25,7 +26,8 @@ GColor settings_get_backgroundColor();
GColor settings_get_circleColor();
GColor settings_get_textColor();
bool settings_get_vibrationEnabled();
bool settings_get_heartRateEnabled();
int settings_get_vibrationType();
int settings_get_displayText();
int settings_get_reminderHours();
bool settings_get_rememberDuration();
int settings_get_reminderHoursStart();
Expand Down
49 changes: 40 additions & 9 deletions src/pkjs/config-es.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module.exports = [
},
{
"type": "text",
"defaultValue": "Esta es la página de ajustes para la app Breathe. Estás usando la version 0.2.8 de la app.",
"defaultValue": "Esta es la página de ajustes para la app Breathe. Estás usando la versión 0.2.9 de la app.",
},
{
"type": "section",
Expand Down Expand Up @@ -49,6 +49,23 @@ module.exports = [
"defaultValue": true,
"label": "¿Activar las vibraciones durante la respiración?",
},
{
"type": "select",
"messageKey": "vibrationType",
"defaultValue": "0",
"label": "Tipo de vibración",
"description": "La condición base es como el Apple Watch; Simple es un doble toque.",
"options": [
{
"label": "Condicón base",
"value": "0"
},
{
"label": "Simple",
"value": "1"
}
]
},
{
"type": "slider",
"messageKey": "breathsPerMinute",
Expand All @@ -62,18 +79,32 @@ module.exports = [
},
{
"type": "section",
"capabilities": ["NOT_PLATFORM_APLITE", "NOT_PLATFORM_BASALT", "NOT_PLATFORM_CHALK"],
"capabilities": ["NOT_PLATFORM_APLITE"],
"items": [
{"type": "heading",
"defaultValue": "Salud"
},
{
"type": "toggle",
"messageKey": "heartRateEnabled",
"defaultValue": true,
"label": "¿Mostrar el ritmo cardiaco?",
"description": "Si activado, el app mostrará el ritmo cardiaco en el menú principal. Si no, el app mostrará el número de pasos que has tomado hoy."
}
"type": "select",
"messageKey": "displayText",
"defaultValue": "1",
"label": "Mostrar...",
"description": "Esto determina lo que está mostrado en el menu principal. NOTA: El ritmo cardiaco requiere un reloj con un monitor de ritmo cardiaco.",
"options": [
{
"label": "Saludo",
"value": "0"
},
{
"label": "Pasos hoy",
"value": "1"
},
{
"label": "Ritmo cardiaco",
"value": "2"
}
]
},
]
},
{
Expand Down Expand Up @@ -130,7 +161,7 @@ module.exports = [
},
{
"type": "text",
"defaultValue": "<em><center>Muchas gracias a los probadores beta: <br>Paula&nbsp;Bosca, Nikita&nbsp;Cheng, Ayush&nbsp;Gupta, Ellen&nbsp;Huang, Yvonne&nbsp;Tan, y David&nbsp;Voicu</center></em>",
"defaultValue": "<em><center>Muchas gracias a los probadores beta: <br>Paula&nbsp;Bosca, Nikita&nbsp;Cheng, Ayush&nbsp;Gupta, Ellen&nbsp;Huang, Yvonne&nbsp;Tan, David&nbsp;Voicu, y /u/PiwwowPants</center></em>",
},
{
"type": "submit",
Expand Down
Loading

0 comments on commit dc969c7

Please sign in to comment.