Upload files to "src/c"

This commit is contained in:
2024-10-06 21:22:46 +00:00
parent fd557c0beb
commit a8188dedb3
3 changed files with 916 additions and 0 deletions

217
src/c/num2words.c Normal file
View File

@@ -0,0 +1,217 @@
#include "num2words.h"
#include <string.h>
#include <stdio.h>
static const char* const ONES[] = {
"zero",
"one",
"two",
"three",
"four",
"five",
"six",
"seven",
"eight",
"nine"
};
static const char* const TEENS[] ={
"",
"eleven",
"twelve",
"thirteen",
"fourteen",
"fifteen",
"sixteen",
"seventeen",
"eighteen",
"nineteen"
};
static const char* const TEENS_SPLIT[][2] = {
{"", ""},
{"eleven",""},
{"twelve",""},
{"thirteen",""},
{"four","teen"},
{"fifteen",""},
{"sixteen",""},
{"seven","teen"},
{"eight","teen"},
{"nine","teen"}
};
static const char* const TENS[] = {
"",
"ten",
"twenty",
"thirty",
"forty",
"fifty",
"sixty",
"seventy",
"eighty",
"ninety"
};
static const char* STR_OH_TICK = "o'";
static const char* STR_O_TICK = "o";
static const char* STR_CLOCK = "clock";
static const char* STR_NOON = "noon";
static const char* STR_MIDNIGHT = "midnight";
static const char* STR_QUARTER = "quarter";
static const char* STR_TO = "to";
static const char* STR_PAST = "past";
static const char* STR_HALF = "half";
static const char* STR_AFTER = "after";
static size_t append_number(char* words, int num) {
int tens_val = num / 10 % 10;
int ones_val = num % 10;
size_t len = 0;
if (tens_val > 0) {
if (tens_val == 1 && num != 10) {
strcat(words, TEENS[ones_val]);
return strlen(TEENS[ones_val]);
}
strcat(words, TENS[tens_val]);
len += strlen(TENS[tens_val]);
if (ones_val > 0) {
strcat(words, " ");
len += 1;
}
}
if (ones_val > 0 || num == 0) {
strcat(words, ONES[ones_val]);
len += strlen(ONES[ones_val]);
}
return len;
}
void fuzzy_time_to_words(int hours, int minutes, char* words) {
int fuzzy_hours = hours;
int fuzzy_minutes = ((minutes + 2) / 5) * 5;
// Handle hour & minute roll-over.
if (fuzzy_minutes > 55) {
fuzzy_minutes = 0;
fuzzy_hours += 1;
if (fuzzy_hours > 23) {
fuzzy_hours = 0;
}
}
time_to_common_words(fuzzy_hours, fuzzy_minutes, words);
}
// returns number of lines written
void time_to_common_words(int hours, int minutes, char *words) {
// TODO: make an app-safe assert
// PBL_ASSERT(hours >= 0 && hours < 24, "Invalid number of hours");
// PBL_ASSERT(minutes >= 0 && minutes < 60, "Invalid number of minutes");
size_t written = 0;
strcpy(words, "");
if (minutes != 0 && (minutes >= 10 || minutes == 5 || hours == 0 || hours == 12)) {
if (minutes == 15) {
written += sprintf(words, "%s %s ", STR_QUARTER, STR_AFTER);
} else if (minutes == 45) {
written += sprintf(words, "%s %s ", STR_QUARTER, STR_TO);
hours = (hours + 1) % 24;
} else if (minutes == 30) {
written += sprintf(words, "%s %s ", STR_HALF, STR_PAST);
} else if (minutes < 30) {
written += append_number(words, minutes);
written += sprintf(words + written, " %s ", STR_AFTER);
} else {
written += append_number(words, 60 - minutes);
written += sprintf(words + written, " %s ", STR_TO);
hours = (hours + 1) % 24;
}
}
if (hours == 0) {
written += sprintf(words + written, "%s", STR_MIDNIGHT);
} else if (hours == 12) {
strcat(words, STR_NOON);
written += sprintf(words + written, "%s", STR_NOON);
} else {
written += append_number(words, hours % 12);
}
if (minutes < 10 && minutes != 5 && !(hours == 0 || hours == 12)) {
if (minutes == 0) {
sprintf(words + written, " %s%s", STR_OH_TICK, STR_CLOCK);
} else {
sprintf(words + written, " %s%s", STR_OH_TICK, ONES[minutes]);
}
}
}
// o'clock (0) and plain number words (10..)
void minute_to_formal_words(int minutes, int displayPrefix, char *first_word, char *second_word) {
// PBL_ASSERT(minutes >= 0 && minutes < 60, "Invalid number of minutes");
strcpy(first_word, "");
strcpy(second_word, "");
if (minutes == 0) {
strcat(first_word, STR_OH_TICK);
strcat(first_word, STR_CLOCK);
return;
}
if (minutes < 10) {
if (displayPrefix) {
strcat(first_word, STR_O_TICK); //add "o"
strcat(second_word, ONES[minutes%10]); //new
}
else {
strcat(first_word, ONES[minutes%10]);
}
return;
}
if (minutes > 10 && minutes < 20) {
strcat(first_word, TEENS_SPLIT[(minutes - 10) % 10][0]);
strcat(second_word, TEENS_SPLIT[(minutes - 10) % 10][1]);
return;
}
strcat(first_word, TENS[minutes / 10 % 10]);
int minute_ones = minutes % 10;
if (minute_ones) {
strcat(second_word, ONES[minute_ones]);
}
}
void hour_to_12h_word(int hours, char *word) {
// PBL_ASSERT(hours >= 0 && hours < 24, "Invalid number of hours");
hours = hours % 12;
if (hours == 0) {
hours = 12;
}
strcpy(word, "");
append_number(word, hours);
}
void hour_to_24h_word(int hours, char *words) {
// PBL_ASSERT(hours >= 0 && hours < 24, "Invalid number of hours");
hours = hours % 24;
strcpy(words, "");
append_number(words, hours);
}

7
src/c/num2words.h Normal file
View File

@@ -0,0 +1,7 @@
#pragma once
void time_to_common_words(int hours, int minutes, char *words);
void fuzzy_time_to_words(int hours, int minutes, char* words);
void minute_to_formal_words(int minutes, int displayPrefix, char *first_word, char *second_word);
void hour_to_12h_word(int hours, char *word);
void hour_to_24h_word(int hours, char *words);

692
src/c/sliding_text.c Normal file
View File

@@ -0,0 +1,692 @@
#include <pebble.h>
#include "num2words.h"
#define KEY_TEMPERATURE_IN_C 1
#define KEY_CONDITIONS 2
#define KEY_BACKGROUND_COLOR 3
#define KEY_TEXT_COLOR 4
#define KEY_WEATHER_FREQUENCY 5
#define KEY_USE_CELSIUS 6
#define KEY_DISPLAY_O_PREFIX 7
#define KEY_DISPLAY_WEATHER 8
#define KEY_MIN_SINCE_WEATHER_UPDATE 9
#define KEY_WEATHERDATE_ALIGNMENT 10
#define KEY_HOURMINUTES_ALIGNMENT 11
#define KEY_GET_WEATHER 12
#define KEY_TEMPERATURE 13
#define KEY_OPTIONS 14
typedef struct {
int tempF;
int tempC;
int weatherDateAlignment;
int hourMinuteAlignment;
int display_weather;
int use_celsius;
int weather_frequency;
int min_since_last_forecast;
int display_prefix;
int text_color;
int background_color;
char conditions[32];
} Options;
Options options;
TextLayer *weather_layer, *date_layer;
GFont weatherdate_text;
const int DEFAULT_TEMPF = 0;
const int DEFAULT_TEMPC = 0;
const int DEFAULT_MIN_SINCE_WEATHER_UPDATE = 0; //weather just updated
const int DEFAULT_DISPLAY_WEATHER = 1; //true
const int DEFAULT_WEATHER_FREQUENCY = 30; //minutes
const int DEFAULT_USE_CELSIUS = 0; //false
const int DEFAULT_DISPLAY_O_PREFIX = 0; //false
const int DEFAULT_WEATHERDATE_ALIGNMENT = 1; //center
const int DEFAULT_HOURMINUTES_ALIGNMENT = 0; //left
const int DEFAULT_TEXT_COLOR = 0xFFFFFF; //white
const int DEFAULT_BACKGROUND_COLOR = 0x000000; //black
const int HOUR_ROW = 0;
const int MINUTE_ROW1 = 1;
const int MINUTE_ROW2 = 2;
typedef enum {
MOVING_IN,
IN_FRAME,
PREPARE_TO_MOVE,
MOVING_OUT
} SlideState;
typedef struct {
TextLayer *label;
SlideState state; // animation state
char *next_string; // what to say in the next phase of animation
bool unchanged_font;
int left_pos;
int right_pos;
int still_pos;
int movement_delay;
int delay_count;
} SlidingRow;
typedef struct {
TextLayer *demo_label;
SlidingRow rows[3];
int last_hour;
int last_minute;
GFont hour_text;
GFont minute_text;
Window *window;
Animation *animation;
struct SlidingTextRenderState {
// double buffered string storage
char hours[2][32];
uint8_t next_hours;
char first_minutes[2][32];
char second_minutes[2][32];
uint8_t next_minutes;
struct SlidingTextRenderDemoTime {
int secs;
int mins;
int hour;
} demo_time;
} render_state;
} SlidingTextData;
SlidingTextData *s_data;
GTextAlignment row_alignment(int alignment) {
switch (alignment) {
case 0: return GTextAlignmentLeft;
case 1: return GTextAlignmentCenter;
case 2: return GTextAlignmentRight;
default: return GTextAlignmentLeft;
}
}
static void send(int key, int value){
DictionaryIterator *iterator;
app_message_outbox_begin(&iterator);
dict_write_int(iterator, key, &value, sizeof(int), true);
app_message_outbox_send();
}
static void set_text_layers_color(GColor textColor) {
SlidingTextData *data = s_data;
SlidingRow *row;
for (size_t i = 0; i < ARRAY_LENGTH(data->rows); ++i) {
row = &data->rows[i];
text_layer_set_text_color(row->label, textColor);
}
text_layer_set_text_color(weather_layer, textColor);
text_layer_set_text_color(date_layer, textColor);
}
void init_options() {
if (persist_exists(KEY_OPTIONS)) {
persist_read_data(KEY_OPTIONS, &options, sizeof(options));
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options.tempF: %d", options.tempF);
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options.tempC: %d", options.tempC);
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options.weatherDateAlignment: %d", options.weatherDateAlignment);
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options.hourMinuteAlignment: %d", options.hourMinuteAlignment);
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options.display_weather: %d", options.display_weather);
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options.use_celsius: %d", options.use_celsius);
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options.weather_frequency: %d", options.weather_frequency);
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options.min_since_last_forecast: %d", options.min_since_last_forecast);
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options.display_prefix: %d", options.display_prefix);
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options.text_color: %d", options.text_color);
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options.background_color: %d", options.background_color);
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options.conditions: %s", options.conditions);
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_read_data options sizeof(options) %d", sizeof(options));
}
else {
options.tempF = DEFAULT_TEMPF;
options.tempC = DEFAULT_TEMPC;
options.weatherDateAlignment = DEFAULT_WEATHERDATE_ALIGNMENT;
options.hourMinuteAlignment = DEFAULT_HOURMINUTES_ALIGNMENT;
options.display_weather = DEFAULT_DISPLAY_WEATHER;
options.use_celsius = DEFAULT_USE_CELSIUS;
options.weather_frequency = DEFAULT_WEATHER_FREQUENCY;
options.min_since_last_forecast = DEFAULT_MIN_SINCE_WEATHER_UPDATE;
options.display_prefix = DEFAULT_DISPLAY_O_PREFIX;
options.text_color = DEFAULT_TEXT_COLOR;
options.background_color = DEFAULT_BACKGROUND_COLOR;
memset(options.conditions, 0,sizeof(options.conditions));
persist_write_data(KEY_OPTIONS, &options, sizeof(options));
APP_LOG(APP_LOG_LEVEL_INFO, "init_options_new: persist_write_data options sizeof(options) %d", sizeof(options));
}
}
static void init_sliding_row(SlidingTextData *data, SlidingRow *row, GRect pos, GFont font,
int delay) {
row->label = text_layer_create(pos);
//set row alignment
text_layer_set_text_alignment(row->label, row_alignment(options.hourMinuteAlignment));
text_layer_set_background_color(row->label, GColorClear);
text_layer_set_text_color(row->label, GColorWhite);
#ifdef PBL_COLOR // If on basalt
text_layer_set_text_color(row->label, GColorFromHEX(options.text_color));
#else
if (options.text_color == 0xFFFFFF) {//white
text_layer_set_text_color(row->label, GColorWhite);
}
else if (options.text_color == 0x000000) {
text_layer_set_text_color(row->label, GColorBlack);
}
else {
text_layer_set_text_color(row->label, DEFAULT_TEXT_COLOR);
}
#endif
if (font) {
text_layer_set_font(row->label, font);
row->unchanged_font = true;
} else {
row->unchanged_font = false;
}
row->state = IN_FRAME;
row->next_string = NULL;
row->left_pos = -pos.size.w;
row->right_pos = pos.size.w;
row->still_pos = pos.origin.x;
row->movement_delay = delay;
row->delay_count = 0;
data->last_hour = -1;
data->last_minute = -1;
}
static void init_static_row(TextLayer *label, GFont font) {
//set row alignment
text_layer_set_text_alignment(label, row_alignment(options.weatherDateAlignment));
text_layer_set_background_color(label, GColorClear);
text_layer_set_text_color(label, GColorWhite);
#ifdef PBL_COLOR // If on basalt
text_layer_set_text_color(label, GColorFromHEX(options.text_color));
#else
if (options.text_color == 0xFFFFFF) {//white
text_layer_set_text_color(label, GColorWhite);
}
else if (options.text_color == 0x000000) {
text_layer_set_text_color(label, GColorBlack);
}
else {
text_layer_set_text_color(label, DEFAULT_TEXT_COLOR);
}
#endif
if (font) {
text_layer_set_font(label, font);
}
}
static void slide_in_text(SlidingTextData *data, SlidingRow *row, char* new_text) {
(void) data;
const char *old_text = text_layer_get_text(row->label);
if (old_text) {
row->next_string = new_text;
row->state = PREPARE_TO_MOVE;
} else {
text_layer_set_text(row->label, new_text);
GRect frame = layer_get_frame(text_layer_get_layer(row->label));
frame.origin.x = row->right_pos;
layer_set_frame(text_layer_get_layer(row->label), frame);
row->state = MOVING_IN;
}
}
static bool update_sliding_row(SlidingTextData *data, SlidingRow *row) {
(void) data;
GRect frame = layer_get_frame(text_layer_get_layer(row->label));
bool something_changed = true;
switch (row->state) {
case PREPARE_TO_MOVE:
frame.origin.x = row->still_pos;
row->delay_count++;
if (row->delay_count > row->movement_delay) {
row->state = MOVING_OUT;
row->delay_count = 0;
}
break;
case MOVING_IN: {
int speed = abs(frame.origin.x - row->still_pos) / 3 + 1;
frame.origin.x -= speed;
if (frame.origin.x <= row->still_pos) {
frame.origin.x = row->still_pos;
row->state = IN_FRAME;
}
}
break;
case MOVING_OUT: {
int speed = abs(frame.origin.x - row->still_pos) / 3 + 1;
frame.origin.x -= speed;
if (frame.origin.x <= row->left_pos) {
frame.origin.x = row->right_pos;
row->state = MOVING_IN;
text_layer_set_text(row->label, row->next_string);
row->next_string = NULL;
}
}
break;
case IN_FRAME:
default:
something_changed = false;
break;
}
if (something_changed) {
layer_set_frame(text_layer_get_layer(row->label), frame);
}
return something_changed;
}
static void animation_update(struct Animation *animation, const AnimationProgress time_normalized) {
SlidingTextData *data = s_data;
struct SlidingTextRenderState *rs = &data->render_state;
time_t now = time(NULL);
struct tm t = *localtime(&now);
bool something_changed = false;
if (data->last_minute != t.tm_min) {
something_changed = true;
minute_to_formal_words(t.tm_min, options.display_prefix, rs->first_minutes[rs->next_minutes], rs->second_minutes[rs->next_minutes]);
if(data->last_hour != t.tm_hour || t.tm_min <= 20
|| t.tm_min/10 != data->last_minute/10) {
slide_in_text(data, &data->rows[MINUTE_ROW1], rs->first_minutes[rs->next_minutes]);
} else {
// The tens line didn't change, so swap to the correct buffer but don't animate
text_layer_set_text(data->rows[MINUTE_ROW1].label, rs->first_minutes[rs->next_minutes]);
}
slide_in_text(data, &data->rows[MINUTE_ROW2], rs->second_minutes[rs->next_minutes]);
rs->next_minutes = rs->next_minutes ? 0 : 1;
data->last_minute = t.tm_min;
}
if (data->last_hour != t.tm_hour) {
hour_to_12h_word(t.tm_hour, rs->hours[rs->next_hours]);
slide_in_text(data, &data->rows[HOUR_ROW], rs->hours[rs->next_hours]);
rs->next_hours = rs->next_hours ? 0 : 1;
data->last_hour = t.tm_hour;
}
for (size_t i = 0; i < ARRAY_LENGTH(data->rows); ++i) {
something_changed = update_sliding_row(data, &data->rows[i]) || something_changed;
}
if (!something_changed) {
animation_unschedule(data->animation);
}
}
static void update_date_layer() {
static char date_buffer[16];
const char *current_date_layer;
//get current date_layer text
current_date_layer = text_layer_get_text(date_layer);
//get current date
time_t temp = time(NULL);
struct tm *tick_time = localtime(&temp);
strftime(date_buffer, sizeof(date_buffer), "%a %d %b", tick_time);
if (current_date_layer != date_buffer) {
text_layer_set_text(date_layer, date_buffer);
APP_LOG(APP_LOG_LEVEL_INFO, "update_date_layer: text_layer_set_text date_layer :%s", date_buffer);
}
}
static void update_weather_layer() {
static char weather_layer_buffer[32];
// Get a tm structure
time_t temp = time(NULL);
struct tm *tick_time = localtime(&temp);
static char time_buffer[] = "00:00";
strftime(time_buffer, sizeof("00:00"), "%I:%M", tick_time);
if (options.use_celsius){
snprintf(weather_layer_buffer, sizeof(weather_layer_buffer), "%dC - %s", options.tempC, options.conditions);
//snprintf(weather_layer_buffer, sizeof(weather_layer_buffer), "%dC - %s (%s)", options.tempC, options.conditions, time_buffer);
APP_LOG(APP_LOG_LEVEL_INFO, "C: Time %s set weather_layer_buffer: %s", time_buffer, weather_layer_buffer);
}
else {
snprintf(weather_layer_buffer, sizeof(weather_layer_buffer), "%dF - %s", options.tempF, options.conditions);
//snprintf(weather_layer_buffer, sizeof(weather_layer_buffer), "%dF - %s (%s)", options.tempF, options.conditions, time_buffer);
APP_LOG(APP_LOG_LEVEL_INFO, "F: Time %s set weather_layer_buffer: %s", time_buffer, weather_layer_buffer);
}
//write weather text row
APP_LOG(APP_LOG_LEVEL_INFO, "update_weather_layer: sizeof(options.conditions) %d. ", strlen(options.conditions));
if (strlen(options.conditions) != 0) {
//if (strlen(weather_layer_buffer) > 5) {
text_layer_set_text(weather_layer, weather_layer_buffer);
APP_LOG(APP_LOG_LEVEL_INFO, "update_weather_layer: Successful weather row information written at %s. ", time_buffer);
}
else { //empty temp & conditions
text_layer_set_text(weather_layer, "");
APP_LOG(APP_LOG_LEVEL_INFO, "update_weather_layer: Error displaying weather %s. ", time_buffer);
}
}
static void make_animation() {
s_data->animation = animation_create();
animation_set_duration(s_data->animation, ANIMATION_DURATION_INFINITE);
// the animation will stop itself
static const struct AnimationImplementation s_animation_implementation = {
.update = animation_update,
};
animation_set_implementation(s_data->animation, &s_animation_implementation);
animation_schedule(s_data->animation);
}
static void handle_minute_tick(struct tm *tick_time, TimeUnits units_changed) {
if (units_changed & DAY_UNIT) {
//vibes_double_pulse(); //hourly vibration (future option)
}
make_animation(); //animate mew time
update_date_layer(); //check/update date layer
//if min_since_last_forecast is 0 or greater
if(options.min_since_last_forecast >= 0) {
//add one to min_since_last_forecast
++options.min_since_last_forecast;
APP_LOG(APP_LOG_LEVEL_INFO, "handle_minute_tick options.min_since_last_forecast : options.weather_frequency %d : %d", options.min_since_last_forecast, options.weather_frequency);
APP_LOG(APP_LOG_LEVEL_INFO, "handle_minute_tick strlen(options.conditions) : %d", strlen(options.conditions));
//if min_since_last_forecast greater than or equal to weatherUpdateFrequency
if(options.min_since_last_forecast >= options.weather_frequency ||
strlen(options.conditions) == 0) {
options.min_since_last_forecast = 0;
//get updated weather
send(KEY_GET_WEATHER, 1);
//update weather layer
update_weather_layer();
}
}
}
static void inbox_received_callback(DictionaryIterator *iterator, void *context) {
SlidingTextData *data = s_data;
SlidingRow *row;
// Read first item
Tuple *t = dict_read_first(iterator);
// For all items
while(t != NULL) {
// Which key was received?
switch(t->key) {
case KEY_TEMPERATURE:
options.tempF = (int)t->value->int32;
break;
case KEY_TEMPERATURE_IN_C:
options.tempC = (int)t->value->int32;
break;
case KEY_CONDITIONS:
snprintf(options.conditions, sizeof(options.conditions), "%s", t->value->cstring);
break;
case KEY_DISPLAY_O_PREFIX:
options.display_prefix = (int)t->value->int32;
break;
case KEY_USE_CELSIUS:
options.use_celsius = (int)t->value->int32;
break;
case KEY_WEATHER_FREQUENCY:
options.weather_frequency = (int)t->value->int32;
break;
case KEY_BACKGROUND_COLOR:
options.background_color = (int)t->value->int32;
#ifdef PBL_COLOR // If on basalt
window_set_background_color(data->window, GColorFromHEX(options.background_color));
#else
if (options.background_color == 0xFFFFFF) {
window_set_background_color(data->window, GColorWhite);//white
}
else if (options.background_color == 0x000000) {//black
window_set_background_color(data->window, GColorBlack);
}
else { //didn't select white or black, default to black
window_set_background_color(data->window, GColorBlack);
}
#endif
break;
case KEY_WEATHERDATE_ALIGNMENT:
options.weatherDateAlignment = (int)t->value->int32;
text_layer_set_text_alignment(weather_layer, row_alignment(options.weatherDateAlignment));
text_layer_set_text_alignment(date_layer, row_alignment(options.weatherDateAlignment));
break;
case KEY_HOURMINUTES_ALIGNMENT:
row = &data->rows[HOUR_ROW];
options.hourMinuteAlignment = (int)t->value->int32;
text_layer_set_text_alignment(row->label, row_alignment(options.hourMinuteAlignment));
row = &data->rows[MINUTE_ROW1];
text_layer_set_text_alignment(row->label, row_alignment(options.hourMinuteAlignment));
row = &data->rows[MINUTE_ROW2];
text_layer_set_text_alignment(row->label, row_alignment(options.hourMinuteAlignment));
break;
case KEY_TEXT_COLOR:
options.text_color = (int)t->value->int32;
#ifdef PBL_COLOR // If on basalt
for (size_t i = 0; i < ARRAY_LENGTH(data->rows); ++i) {
row = &data->rows[i];
text_layer_set_text_color(row->label, GColorFromHEX(options.text_color));
}
text_layer_set_text_color(weather_layer, GColorFromHEX(options.text_color));
text_layer_set_text_color(date_layer, GColorFromHEX(options.text_color));
#else
if (options.text_color == 0xFFFFFF) {//white
set_text_layers_color(GColorWhite);
}
else if (options.text_color == 0x000000) { //black
set_text_layers_color(GColorBlack);
}
else { //didn't select white or black, default to white
set_text_layers_color(GColorWhite);
}
#endif
break;
}
// Look for next item
t = dict_read_next(iterator);
}
//weather layer
update_weather_layer();
}
char *translate_error(AppMessageResult result) {
switch (result) {
case APP_MSG_OK: return "APP_MSG_OK";
case APP_MSG_SEND_TIMEOUT: return "APP_MSG_SEND_TIMEOUT";
case APP_MSG_SEND_REJECTED: return "APP_MSG_SEND_REJECTED";
case APP_MSG_NOT_CONNECTED: return "APP_MSG_NOT_CONNECTED";
case APP_MSG_APP_NOT_RUNNING: return "APP_MSG_APP_NOT_RUNNING";
case APP_MSG_INVALID_ARGS: return "APP_MSG_INVALID_ARGS";
case APP_MSG_BUSY: return "APP_MSG_BUSY";
case APP_MSG_BUFFER_OVERFLOW: return "APP_MSG_BUFFER_OVERFLOW";
case APP_MSG_ALREADY_RELEASED: return "APP_MSG_ALREADY_RELEASED";
case APP_MSG_CALLBACK_ALREADY_REGISTERED: return "APP_MSG_CALLBACK_ALREADY_REGISTERED";
case APP_MSG_CALLBACK_NOT_REGISTERED: return "APP_MSG_CALLBACK_NOT_REGISTERED";
case APP_MSG_OUT_OF_MEMORY: return "APP_MSG_OUT_OF_MEMORY";
case APP_MSG_CLOSED: return "APP_MSG_CLOSED";
case APP_MSG_INTERNAL_ERROR: return "APP_MSG_INTERNAL_ERROR";
default: return "UNKNOWN ERROR";
}
}
void message_dropped(AppMessageResult reason, void *context) {
APP_LOG(APP_LOG_LEVEL_ERROR, "Dropped message! Reason given: %s", translate_error(reason));
}
void message_out_success(DictionaryIterator *iter, void *context) {
APP_LOG(APP_LOG_LEVEL_DEBUG, "Message sent.");
}
void message_out_failed(DictionaryIterator *iter, AppMessageResult reason, void *context) {
APP_LOG(APP_LOG_LEVEL_DEBUG, "Failed to send message. Reason = %s", translate_error(reason));
}
static void handle_deinit(void) {
//save watchface options
persist_write_data(KEY_OPTIONS, &options, sizeof(options));
APP_LOG(APP_LOG_LEVEL_INFO, "handle_deinit: persist_write_data options sizeof(options) %d", sizeof(options));
text_layer_destroy(weather_layer);
text_layer_destroy(date_layer);
tick_timer_service_unsubscribe();
free(s_data);
}
static void handle_init() {
SlidingTextData *data = (SlidingTextData*)malloc(sizeof(SlidingTextData));
s_data = data;
data->render_state.next_hours = 0;
data->render_state.next_minutes = 0;
data->render_state.demo_time.secs = 0;
data->render_state.demo_time.mins = 0;
data->render_state.demo_time.hour = 0;
data->window = window_create();
//set-retrieve watchface options
init_options();
window_set_background_color(data->window, GColorBlack);
#ifdef PBL_COLOR // If on basalt
window_set_background_color(data->window, GColorFromHEX(options.background_color));
#else
if (options.background_color == 0xFFFFFF) {
window_set_background_color(data->window, GColorWhite);//white
}
else if (options.background_color == 0x000000) {//black
window_set_background_color(data->window, GColorBlack);
}
else { //didn't select white or black, default to white
window_set_background_color(data->window, DEFAULT_BACKGROUND_COLOR);
}
#endif
data->hour_text = fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD);
data->minute_text = fonts_get_system_font(FONT_KEY_BITHAM_42_LIGHT);
weatherdate_text = fonts_get_system_font(FONT_KEY_GOTHIC_18);
Layer *window_layer = window_get_root_layer(data->window);
GRect layer_frame = layer_get_frame(window_layer);
const int16_t width = layer_frame.size.w;
init_sliding_row(data, &data->rows[HOUR_ROW], GRect(0, 20, width, 60), data->hour_text, 6);
layer_add_child(window_layer, text_layer_get_layer(data->rows[HOUR_ROW].label));
init_sliding_row(data, &data->rows[MINUTE_ROW1], GRect(0, 56, width, 96), data->minute_text, 3);
layer_add_child(window_layer, text_layer_get_layer(data->rows[MINUTE_ROW1].label));
init_sliding_row(data, &data->rows[MINUTE_ROW2], GRect(0, 92, width, 132), data->minute_text, 0);
layer_add_child(window_layer, text_layer_get_layer(data->rows[MINUTE_ROW2].label));
//weather layer
weather_layer = text_layer_create(GRect(0, 0, width, 24));
init_static_row(weather_layer, weatherdate_text);
layer_add_child(window_layer, text_layer_get_layer(weather_layer));
//date layer
date_layer = text_layer_create(GRect(0, 145, width, 198));
init_static_row(date_layer, weatherdate_text);
layer_add_child(window_layer, text_layer_get_layer(date_layer));
GFont norm14 = fonts_get_system_font(FONT_KEY_GOTHIC_14);
data->demo_label = text_layer_create(GRect(0, -3, 100, 20));
text_layer_set_background_color(data->demo_label, GColorClear);
text_layer_set_text_color(data->demo_label, GColorWhite);
text_layer_set_font(data->demo_label, norm14);
text_layer_set_text(data->demo_label, "demo mode");
layer_add_child(window_layer, text_layer_get_layer(data->demo_label));
layer_set_hidden(text_layer_get_layer(data->demo_label), true);
layer_mark_dirty(window_layer);
app_message_register_inbox_received(inbox_received_callback);
app_message_register_inbox_dropped(message_dropped);
app_message_register_outbox_sent(message_out_success);
app_message_register_outbox_failed(message_out_failed);
app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum());
update_date_layer();
APP_LOG(APP_LOG_LEVEL_DEBUG, "strlen conditions %d", strlen(options.conditions));
if (strlen(options.conditions) == 0) {
//no previous conditions, get updated weather
send(KEY_GET_WEATHER, 1);
}
update_weather_layer(); //check/update date layer
make_animation(); //animate new time layers
tick_timer_service_subscribe(MINUTE_UNIT, handle_minute_tick);
const bool animated = true;
window_stack_push(data->window, animated);
}
int main(void) {
handle_init();
app_event_loop();
handle_deinit();
}