Skip to content

Commit

Permalink
Show app message state updates on watch
Browse files Browse the repository at this point in the history
  • Loading branch information
mddub committed Aug 1, 2016
1 parent 5c86d50 commit 12ed394
Show file tree
Hide file tree
Showing 11 changed files with 198 additions and 33 deletions.
5 changes: 5 additions & 0 deletions appinfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@
"name": "CONN_ISSUE_RIG",
"file": "images/conn_issue_rig.png"
},
{
"type": "pbi",
"name": "CONN_REFRESHING",
"file": "images/conn_refreshing.png"
},
{
"type": "pbi",
"name": "BATTERY_10",
Expand Down
Binary file added resources/images/conn_refreshing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 44 additions & 9 deletions src/comm.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ static AppTimer *timeout_timer = NULL;

static void (*data_callback)(DataMessage *data);
static void (*prefs_callback)(DictionaryIterator *received);
static void (*request_state_callback)(RequestState state, AppMessageResult reason);
static void schedule_update(uint32_t delay);
static void request_update();
static void timeout_handler();
Expand All @@ -38,6 +39,15 @@ static int timeout_length() {
}

static void timeout_handler() {
if (phone_contact) {
// Requests time out more quickly immediately after load (before any phone
// contact), so only timeouts after phone contact are legitimate
request_state_callback(REQUEST_STATE_TIMED_OUT, 0);
} else if (!phone_contact && !connection_service_peek_pebble_app_connection()) {
// ...unless the app just loaded and there's no Bluetooth connection
request_state_callback(REQUEST_STATE_NO_BLUETOOTH, 0);
}

timeout_timer = NULL;
if (update_in_progress) {
update_in_progress = false;
Expand All @@ -56,13 +66,21 @@ static void schedule_update(uint32_t delay) {
}

static void request_update() {
DictionaryIterator *send_message;
app_message_outbox_begin(&send_message);
app_message_outbox_send();

request_timer = NULL;
update_in_progress = true;
timeout_timer = app_timer_register(timeout_length(), timeout_handler, NULL);

if (!connection_service_peek_pebble_app_connection()) {
request_state_callback(REQUEST_STATE_NO_BLUETOOTH, 0);
schedule_update(NO_BLUETOOTH_RETRY_DELAY);
} else {
request_state_callback(REQUEST_STATE_WAITING, 0);

DictionaryIterator *send_message;
app_message_outbox_begin(&send_message);
app_message_outbox_send();

update_in_progress = true;
timeout_timer = app_timer_register(timeout_length(), timeout_handler, NULL);
}
}

static void in_received_handler(DictionaryIterator *received, void *context) {
Expand All @@ -74,6 +92,7 @@ static void in_received_handler(DictionaryIterator *received, void *context) {

int32_t msg_type;
if (!get_int32(received, &msg_type, APP_KEY_MSG_TYPE, true, 0)) {
request_state_callback(REQUEST_STATE_BAD_APP_MESSAGE, 0);
schedule_update(BAD_APP_MESSAGE_RETRY_DELAY);
return;
}
Expand All @@ -90,27 +109,33 @@ static void in_received_handler(DictionaryIterator *received, void *context) {
delay = next_update < 0 ? LATE_DATA_UPDATE_FREQUENCY : next_update;
}
schedule_update((uint32_t) delay);

staleness_update_data_received(now, last_data_message->recency);
request_state_callback(REQUEST_STATE_SUCCESS, 0);
data_callback(last_data_message);
} else {
request_state_callback(REQUEST_STATE_BAD_APP_MESSAGE, 0);
schedule_update(BAD_APP_MESSAGE_RETRY_DELAY);
}
} else if (msg_type == MSG_TYPE_PREFERENCES) {
request_state_callback(REQUEST_STATE_SUCCESS, 0);
prefs_callback(received);
} else {
request_state_callback(REQUEST_STATE_FETCH_ERROR, 0);
schedule_update(ERROR_RETRY_DELAY);
}
}

static void in_dropped_handler(AppMessageResult reason, void *context) {
// https://developer.getpebble.com/docs/c/Foundation/AppMessage/#AppMessageResult
APP_LOG(APP_LOG_LEVEL_DEBUG, "Incoming AppMessage dropped, %d", reason);

request_state_callback(REQUEST_STATE_IN_DROPPED, reason);
update_in_progress = false;
schedule_update(IN_RETRY_DELAY);
}

static void out_failed_handler(DictionaryIterator *failed, AppMessageResult reason, void *context) {
// https://developer.getpebble.com/docs/c/Foundation/AppMessage/#AppMessageResult
request_state_callback(REQUEST_STATE_OUT_FAILED, reason);
update_in_progress = false;
schedule_update(OUT_RETRY_DELAY);
}
Expand All @@ -121,9 +146,14 @@ static void bluetooth_connection_handler(bool connected) {
}
}

void init_comm(void (*callback_for_data)(DataMessage *data), void (*callback_for_prefs)(DictionaryIterator *received)) {
void init_comm(
void (*callback_for_data)(DataMessage *data),
void (*callback_for_prefs)(DictionaryIterator *received),
void (*callback_for_request_state)(RequestState state, AppMessageResult reason)
) {
data_callback = callback_for_data;
prefs_callback = callback_for_prefs;
request_state_callback = callback_for_request_state;
app_message_register_inbox_received(in_received_handler);
app_message_register_inbox_dropped(in_dropped_handler);
app_message_register_outbox_failed(out_failed_handler);
Expand All @@ -133,6 +163,7 @@ void init_comm(void (*callback_for_data)(DataMessage *data), void (*callback_for

// We expect the JS to initiate sending data first.
update_in_progress = true;
request_state_callback(REQUEST_STATE_WAITING, 0);
timeout_timer = app_timer_register(timeout_length(), timeout_handler, NULL);

last_data_message = malloc(sizeof(DataMessage));
Expand All @@ -149,3 +180,7 @@ void deinit_comm() {
app_message_deregister_callbacks();
free(last_data_message);
}

bool comm_is_update_in_progress() {
return update_in_progress;
}
21 changes: 19 additions & 2 deletions src/comm.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,29 @@
// There are many failure modes...
#define INITIAL_TIMEOUT 1000
#define DEFAULT_TIMEOUT (20*1000)
#define TIMEOUT_RETRY_DELAY (10*1000)
#define TIMEOUT_RETRY_DELAY (20*1000)
#define NO_BLUETOOTH_RETRY_DELAY (60*1000)
#define OUT_RETRY_DELAY (20*1000)
#define IN_RETRY_DELAY 100
#define LATE_DATA_UPDATE_FREQUENCY (60*1000)
#define ERROR_RETRY_DELAY (60*1000)
#define BAD_APP_MESSAGE_RETRY_DELAY (60*1000)

void init_comm(void (*callback_for_data)(DataMessage *data), void (*callback_for_prefs)(DictionaryIterator *received));
typedef enum {
REQUEST_STATE_WAITING,
REQUEST_STATE_SUCCESS,
REQUEST_STATE_FETCH_ERROR,
REQUEST_STATE_BAD_APP_MESSAGE,
REQUEST_STATE_TIMED_OUT,
REQUEST_STATE_NO_BLUETOOTH,
REQUEST_STATE_OUT_FAILED,
REQUEST_STATE_IN_DROPPED,
} RequestState;

void init_comm(
void (*callback_for_data)(DataMessage *data),
void (*callback_for_prefs)(DictionaryIterator *received),
void (*callback_for_request_state)(RequestState state, AppMessageResult reason)
);
void deinit_comm();
bool comm_is_update_in_progress();
124 changes: 106 additions & 18 deletions src/connection_status_component.c
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
#include "comm.h"
#include "config.h"
#include "connection_status_component.h"
#include "fonts.h"
#include "layout.h"
#include "staleness.h"

#define REASON_ICON_WIDTH 25
#define INITIAL_TEXT_SIZE 40
#define TEXT_PADDING_R 2
#define TEXT_MARGIN_L 1
#define REQUEST_STATE_MESSAGE_DURATION_MS 2000

// This matches STALENESS_REASON_*
const int CONN_ISSUE_ICONS[] = {
Expand All @@ -16,19 +17,24 @@ const int CONN_ISSUE_ICONS[] = {
RESOURCE_ID_CONN_ISSUE_RIG,
};

static FontChoice font;

ConnectionStatusComponent* connection_status_component_create(Layer *parent, int x, int y) {
BitmapLayer *icon_layer = bitmap_layer_create(GRect(x, y, REASON_ICON_WIDTH, REASON_ICON_WIDTH));
// draw the icon background over the graph
bitmap_layer_set_compositing_mode(icon_layer, get_element_data(parent)->black ? GCompOpAssignInverted : GCompOpAssign);
layer_set_hidden(bitmap_layer_get_layer(icon_layer), true);
layer_add_child(parent, bitmap_layer_get_layer(icon_layer));

FontChoice font = get_font(FONT_18_BOLD);
int16_t initial_text_width = element_get_bounds(parent).size.w - x - REASON_ICON_WIDTH - TEXT_MARGIN_L;
font = get_font(FONT_18_BOLD);
int16_t initial_text_height = 2 * (font.height + font.padding_top + font.padding_bottom);

TextLayer *staleness_text = text_layer_create(GRect(
x + REASON_ICON_WIDTH + 1,
x + REASON_ICON_WIDTH + TEXT_MARGIN_L,
y + (REASON_ICON_WIDTH - font.height) / 2 - font.padding_top,
INITIAL_TEXT_SIZE,
font.height + font.padding_top + font.padding_bottom
initial_text_width,
initial_text_height
));
text_layer_set_font(staleness_text, fonts_get_system_font(font.key));
text_layer_set_background_color(staleness_text, element_bg(parent));
Expand All @@ -41,6 +47,15 @@ ConnectionStatusComponent* connection_status_component_create(Layer *parent, int
c->icon_layer = icon_layer;
c->icon_bitmap = NULL;
c->staleness_text = staleness_text;
c->background = element_bg(parent);
c->initial_text_width = initial_text_width;
c->initial_text_height = initial_text_height;
if (comm_is_update_in_progress()) {
c->is_showing_request_state = true;
connection_status_component_show_request_state(c, REQUEST_STATE_WAITING, 0);
} else {
c->is_showing_request_state = false;
}
return c;
}

Expand Down Expand Up @@ -69,19 +84,46 @@ static char* staleness_text(int staleness_seconds) {
return buf;
}

static void resize_text_frame(ConnectionStatusComponent *c, int16_t width) {
GRect frame = layer_get_frame(text_layer_get_layer(c->staleness_text));
layer_set_frame(text_layer_get_layer(c->staleness_text), GRect(
frame.origin.x, frame.origin.y, width, frame.size.h
));
static void _resize_text_frame(ConnectionStatusComponent *c, int16_t width, int16_t height, bool fill_background) {
// Make the background transparent during the resizing to avoid a flash
text_layer_set_background_color(c->staleness_text, fill_background ? c->background : GColorClear);

TextLayer *t = c->staleness_text;
GRect frame = layer_get_frame(text_layer_get_layer(t));
layer_set_frame(
text_layer_get_layer(t),
GRect(frame.origin.x, frame.origin.y, width, height)
);
}

static void _trim_text_frame(void *callback_data) {
ConnectionStatusComponent *c = callback_data;
_resize_text_frame(
c,
text_layer_get_content_size(c->staleness_text).w,
text_layer_get_content_size(c->staleness_text).h,
true
);
}

static void trim_text_frame(void *callback_data) {
static void fix_text_frame(ConnectionStatusComponent *c) {
// XXX: need this on Basalt, but not on Aplite or emulator
_resize_text_frame(c, c->initial_text_width, c->initial_text_height, false);
layer_mark_dirty(text_layer_get_layer(c->staleness_text));
app_timer_register(100, _trim_text_frame, c);
}

static void clear_request_state(void *callback_data) {
ConnectionStatusComponent *c = callback_data;
resize_text_frame(c, text_layer_get_content_size(c->staleness_text).w);
c->is_showing_request_state = false;
connection_status_component_tick(c);
}

void connection_status_component_refresh(ConnectionStatusComponent *c) {
void connection_status_component_tick(ConnectionStatusComponent *c) {
if (c->is_showing_request_state) {
return;
}

ConnectionIssue issue = connection_issue();
if (issue.reason == CONNECTION_ISSUE_NONE) {
layer_set_hidden(bitmap_layer_get_layer(c->icon_layer), true);
Expand All @@ -97,9 +139,55 @@ void connection_status_component_refresh(ConnectionStatusComponent *c) {
layer_set_hidden(text_layer_get_layer(c->staleness_text), false);
text_layer_set_text(c->staleness_text, staleness_text(issue.staleness));

// XXX: need this on Basalt, but not on Aplite or emulator
resize_text_frame(c, INITIAL_TEXT_SIZE);
layer_mark_dirty(text_layer_get_layer(c->staleness_text));
app_timer_register(100, trim_text_frame, c);
fix_text_frame(c);
}
}

void connection_status_component_show_request_state(ConnectionStatusComponent *c, RequestState state, AppMessageResult reason) {
if (state == REQUEST_STATE_SUCCESS) {
clear_request_state(c);
return;
}

c->is_showing_request_state = true;

layer_set_hidden(bitmap_layer_get_layer(c->icon_layer), false);
if (c->icon_bitmap != NULL) {
gbitmap_destroy(c->icon_bitmap);
}
if (state == REQUEST_STATE_FETCH_ERROR) {
c->icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_CONN_ISSUE_NETWORK);
} else {
c->icon_bitmap = gbitmap_create_with_resource(RESOURCE_ID_CONN_REFRESHING);
}
bitmap_layer_set_bitmap(c->icon_layer, c->icon_bitmap);

if (state == REQUEST_STATE_WAITING || state == REQUEST_STATE_FETCH_ERROR) {
layer_set_hidden(text_layer_get_layer(c->staleness_text), true);
} else {
layer_set_hidden(text_layer_get_layer(c->staleness_text), false);

static char state_text[32];
switch(state) {
case REQUEST_STATE_BAD_APP_MESSAGE: strcpy(state_text, "Bad app msg"); break;
case REQUEST_STATE_TIMED_OUT: strcpy(state_text, "Timed out"); break;
case REQUEST_STATE_NO_BLUETOOTH: strcpy(state_text, "No BT"); break;
case REQUEST_STATE_OUT_FAILED: strcpy(state_text, "Msg failed"); break;
case REQUEST_STATE_IN_DROPPED: strcpy(state_text, "Msg dropped"); break;
default: strcpy(state_text, "Msg error"); break;
}

if (reason != 0) {
static char reason_text[16];
snprintf(reason_text, sizeof(reason_text), "\nCode %d", reason);
strcat(state_text, reason_text);
}

text_layer_set_text(c->staleness_text, state_text);
fix_text_frame(c);
}

if (state != REQUEST_STATE_WAITING) {
app_timer_register(REQUEST_STATE_MESSAGE_DURATION_MS, clear_request_state, c);
}
}
7 changes: 6 additions & 1 deletion src/connection_status_component.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ typedef struct ConnectionStatusComponent {
BitmapLayer *icon_layer;
GBitmap *icon_bitmap;
TextLayer *staleness_text;
GColor background;
int16_t initial_text_width;
int16_t initial_text_height;
bool is_showing_request_state;
} ConnectionStatusComponent;

ConnectionStatusComponent* connection_status_component_create(Layer *parent, int x, int y);
void connection_status_component_destroy(ConnectionStatusComponent *c);
void connection_status_component_refresh(ConnectionStatusComponent *c);
void connection_status_component_tick(ConnectionStatusComponent *c);
void connection_status_component_show_request_state(ConnectionStatusComponent *c, RequestState state, AppMessageResult reason);
8 changes: 6 additions & 2 deletions src/graph_element.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,13 @@ void graph_element_update(GraphElement *el, DataMessage *data) {
memcpy(graph_data->sgvs, data->sgvs, data->sgv_count * sizeof(uint8_t));
memcpy(graph_data->extra, data->graph_extra, data->sgv_count * sizeof(uint8_t));
layer_mark_dirty(el->graph_layer);
connection_status_component_refresh(el->conn_status);
connection_status_component_tick(el->conn_status);
}

void graph_element_tick(GraphElement *el) {
connection_status_component_refresh(el->conn_status);
connection_status_component_tick(el->conn_status);
}

void graph_element_show_request_state(GraphElement *el, RequestState state, AppMessageResult reason) {
connection_status_component_show_request_state(el->conn_status, state, reason);
}
2 changes: 2 additions & 0 deletions src/graph_element.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <pebble.h>
#include "app_messages.h"
#include "comm.h"
#include "connection_status_component.h"

#define GRAPH_EXTRA_BOLUS_OFFSET 0
Expand All @@ -25,3 +26,4 @@ GraphElement* graph_element_create(Layer *parent);
void graph_element_destroy(GraphElement *el);
void graph_element_update(GraphElement *el, DataMessage *data);
void graph_element_tick(GraphElement *el);
void graph_element_show_request_state(GraphElement *el, RequestState state, AppMessageResult reason);
Loading

0 comments on commit 12ed394

Please sign in to comment.