mirror of
https://github.com/google/pebble.git
synced 2025-11-26 17:22:23 -05:00
Import of the watch repository from Pebble
This commit is contained in:
31
src/fw/apps/watch/tictoc/default/tictoc_default.c
Normal file
31
src/fw/apps/watch/tictoc/default/tictoc_default.c
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "applib/app.h"
|
||||
#include "applib/rockyjs/rocky.h"
|
||||
#include "applib/ui/app_window_stack.h"
|
||||
#include "resource/resource_ids.auto.h"
|
||||
|
||||
void tictoc_main(void) {
|
||||
// Push a window so we don't exit
|
||||
Window *window = window_create();
|
||||
app_window_stack_push(window, false/*animated*/);
|
||||
#if CAPABILITY_HAS_JAVASCRIPT
|
||||
rocky_event_loop_with_system_resource(RESOURCE_ID_JS_TICTOC);
|
||||
#else
|
||||
app_event_loop();
|
||||
#endif
|
||||
}
|
||||
258
src/fw/apps/watch/tictoc/spalding/tictoc_spalding.c
Normal file
258
src/fw/apps/watch/tictoc/spalding/tictoc_spalding.c
Normal file
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "watch_model.h"
|
||||
|
||||
#include "applib/app.h"
|
||||
#include "applib/fonts/fonts.h"
|
||||
#include "applib/graphics/gpath.h"
|
||||
#include "applib/graphics/graphics_circle.h"
|
||||
#include "applib/graphics/text.h"
|
||||
#include "util/trig.h"
|
||||
#include "applib/ui/app_window_stack.h"
|
||||
#include "applib/ui/ui.h"
|
||||
#include "kernel/pbl_malloc.h"
|
||||
#include "process_state/app_state/app_state.h"
|
||||
#include "resource/resource_ids.auto.h"
|
||||
#include "services/common/clock.h"
|
||||
#include "util/time/time.h"
|
||||
|
||||
typedef struct {
|
||||
Window window;
|
||||
GFont text_font;
|
||||
ClockModel clock_model;
|
||||
GBitmap *bg_bitmap;
|
||||
GPath *hour_path;
|
||||
GPath *minute_path;
|
||||
} MultiWatchData;
|
||||
|
||||
static const GPathInfo HOUR_PATH_INFO = {
|
||||
.num_points = 9,
|
||||
.points = (GPoint[]) {
|
||||
{-5, 10}, {-2, 10}, {-2, 15}, {2, 15}, {2, 10}, {5, 10}, {5, -51}, {0, -56}, {-5, -51},
|
||||
},
|
||||
};
|
||||
|
||||
static const GPathInfo MINUTE_PATH_INFO = {
|
||||
.num_points = 5,
|
||||
.points = (GPoint[]) {
|
||||
{-5, 10}, {5, 10}, {5, -61}, {0, -66}, {-5, -61},
|
||||
},
|
||||
};
|
||||
|
||||
void watch_model_handle_change(ClockModel *model) {
|
||||
MultiWatchData *data = app_state_get_user_data();
|
||||
data->clock_model = *model;
|
||||
layer_mark_dirty(window_get_root_layer(&data->window));
|
||||
}
|
||||
|
||||
static GPointPrecise prv_gpoint_from_polar(const GPointPrecise *center, uint32_t distance,
|
||||
int32_t angle) {
|
||||
return gpoint_from_polar_precise(center, distance << GPOINT_PRECISE_PRECISION, angle);
|
||||
}
|
||||
|
||||
static void prv_graphics_draw_centered_text(GContext *ctx, const GSize *max_size,
|
||||
const GPoint *center, const GFont font,
|
||||
const GColor color, const char *text) {
|
||||
GSize text_size = app_graphics_text_layout_get_content_size(
|
||||
text, font, (GRect) { .size = *max_size }, GTextOverflowModeFill, GTextAlignmentCenter);
|
||||
GPoint text_center = *center;
|
||||
text_center.x -= text_size.w / 2 + 1;
|
||||
text_center.y -= text_size.h * 2 / 3;
|
||||
graphics_context_set_text_color(ctx, color);
|
||||
graphics_draw_text(ctx, text, font, (GRect) { .origin = text_center, .size = text_size },
|
||||
GTextOverflowModeFill, GTextAlignmentCenter, NULL);
|
||||
}
|
||||
|
||||
static void prv_draw_watch_hand_rounded(GContext *ctx, ClockHand *hand, GPointPrecise center) {
|
||||
GPointPrecise watch_hand_end = prv_gpoint_from_polar(¢er, hand->length, hand->angle);
|
||||
if (hand->style == CLOCK_HAND_STYLE_ROUNDED_WITH_HIGHLIGHT) {
|
||||
graphics_context_set_stroke_color(ctx, GColorWhite);
|
||||
graphics_line_draw_precise_stroked_aa(ctx, center, watch_hand_end, hand->thickness + 2);
|
||||
}
|
||||
graphics_context_set_stroke_color(ctx, hand->color);
|
||||
graphics_line_draw_precise_stroked_aa(ctx, center, watch_hand_end, hand->thickness);
|
||||
}
|
||||
|
||||
static void prv_draw_watch_hand_pointed(GContext *ctx, ClockHand *hand, GPoint center,
|
||||
GPath *path) {
|
||||
graphics_context_set_fill_color(ctx, hand->color);
|
||||
gpath_rotate_to(path, hand->angle);
|
||||
gpath_move_to(path, center);
|
||||
gpath_draw_filled(ctx, path);
|
||||
}
|
||||
|
||||
static void prv_draw_watch_hand(GContext *ctx, ClockHand *hand, GPointPrecise center, GPath *path) {
|
||||
switch (hand->style) {
|
||||
case CLOCK_HAND_STYLE_POINTED:
|
||||
prv_draw_watch_hand_pointed(ctx, hand, GPointFromGPointPrecise(center), path);
|
||||
case CLOCK_HAND_STYLE_ROUNDED:
|
||||
case CLOCK_HAND_STYLE_ROUNDED_WITH_HIGHLIGHT:
|
||||
default:
|
||||
prv_draw_watch_hand_rounded(ctx, hand, center);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static GPointPrecise prv_get_clock_center_point(ClockLocation location, const GRect *bounds) {
|
||||
GPoint imprecise_center_point = {0};
|
||||
switch (location) {
|
||||
case CLOCK_LOCATION_TOP:
|
||||
imprecise_center_point = (GPoint) {
|
||||
.x = bounds->size.w / 2,
|
||||
.y = bounds->size.h / 4,
|
||||
};
|
||||
case CLOCK_LOCATION_RIGHT:
|
||||
imprecise_center_point = (GPoint) {
|
||||
.x = bounds->size.w * 3 / 4 - 5,
|
||||
.y = bounds->size.h / 2,
|
||||
};
|
||||
case CLOCK_LOCATION_BOTTOM:
|
||||
imprecise_center_point = (GPoint) {
|
||||
.x = bounds->size.w / 2,
|
||||
.y = bounds->size.h * 3 / 4 + 6,
|
||||
};
|
||||
case CLOCK_LOCATION_LEFT:
|
||||
imprecise_center_point = (GPoint) {
|
||||
.x = bounds->size.w / 4 + 4,
|
||||
.y = bounds->size.h / 2,
|
||||
};
|
||||
default:
|
||||
// aiming for width / 2 - 0.5 to get the true center
|
||||
return (GPointPrecise) {
|
||||
.x = { .integer = bounds->size.w / 2 - 1, .fraction = 3 },
|
||||
.y = { .integer = bounds->size.h / 2 - 1, .fraction = 3 }
|
||||
};
|
||||
}
|
||||
return GPointPreciseFromGPoint(imprecise_center_point);
|
||||
}
|
||||
|
||||
static void prv_draw_clock_face(GContext *ctx, ClockFace *face) {
|
||||
MultiWatchData *data = app_state_get_user_data();
|
||||
const GRect *bounds = &window_get_root_layer(&data->window)->bounds;
|
||||
const GPointPrecise center = prv_get_clock_center_point(face->location, bounds);
|
||||
|
||||
// Draw hands.
|
||||
// TODO: Need to do something about the static GPaths used for watchands. This is very inflexible.
|
||||
prv_draw_watch_hand(ctx, &face->hour_hand, center, data->hour_path);
|
||||
prv_draw_watch_hand(ctx, &face->minute_hand, center, data->minute_path);
|
||||
|
||||
// Draw bob.
|
||||
GRect bob_rect = (GRect) {
|
||||
.size = GSize(face->bob_radius * 2, face->bob_radius * 2)
|
||||
};
|
||||
GRect bob_center_rect = (GRect) {
|
||||
.size = GSize(face->bob_center_radius * 2, face->bob_center_radius * 2)
|
||||
};
|
||||
grect_align(&bob_rect, bounds, GAlignCenter, false /* clips */);
|
||||
grect_align(&bob_center_rect, bounds, GAlignCenter, false /* clips */);
|
||||
graphics_context_set_fill_color(ctx, face->bob_color);
|
||||
graphics_fill_oval(ctx, bob_rect, GOvalScaleModeFitCircle);
|
||||
graphics_context_set_fill_color(ctx, face->bob_center_color);
|
||||
graphics_fill_oval(ctx, bob_center_rect, GOvalScaleModeFitCircle);
|
||||
}
|
||||
|
||||
static void prv_draw_non_local_clock(GContext *ctx, NonLocalClockFace *clock) {
|
||||
// TODO: The non-local clock text is currently baked into the background image.
|
||||
prv_draw_clock_face(ctx, &clock->face);
|
||||
}
|
||||
|
||||
|
||||
static void prv_update_proc(Layer *layer, GContext *ctx) {
|
||||
MultiWatchData *data = app_state_get_user_data();
|
||||
|
||||
const GRect *bounds = &layer->bounds;
|
||||
|
||||
// Background.
|
||||
graphics_draw_bitmap_in_rect(ctx, data->bg_bitmap, bounds);
|
||||
|
||||
ClockModel *clock_model = &data->clock_model;
|
||||
|
||||
// Watch text. TODO: Locate the text properly, rather than hard-coding.
|
||||
if (clock_model->text.location == CLOCK_TEXT_LOCATION_BOTTOM) {
|
||||
const GPoint point = (GPoint) { 90, 140 };
|
||||
prv_graphics_draw_centered_text(ctx, &bounds->size, &point, data->text_font,
|
||||
clock_model->text.color, clock_model->text.buffer);
|
||||
} else if (clock_model->text.location == CLOCK_TEXT_LOCATION_LEFT) {
|
||||
const GRect box = (GRect) { .origin = GPoint(25, 78), .size = bounds->size };
|
||||
graphics_draw_text(ctx, clock_model->text.buffer, data->text_font, box,
|
||||
GTextOverflowModeFill, GTextAlignmentLeft, NULL);
|
||||
}
|
||||
|
||||
// Draw the clocks.
|
||||
for (uint32_t i = 0; i < clock_model->num_non_local_clocks; ++i) {
|
||||
prv_draw_non_local_clock(ctx, &clock_model->non_local_clock[i]);
|
||||
}
|
||||
prv_draw_clock_face(ctx, &clock_model->local_clock);
|
||||
}
|
||||
|
||||
static void prv_window_load(Window *window) {
|
||||
MultiWatchData *data = app_state_get_user_data();
|
||||
|
||||
layer_set_update_proc(window_get_root_layer(window), prv_update_proc);
|
||||
|
||||
watch_model_init();
|
||||
|
||||
data->hour_path = gpath_create(&HOUR_PATH_INFO);
|
||||
data->minute_path = gpath_create(&MINUTE_PATH_INFO);
|
||||
data->bg_bitmap = gbitmap_create_with_resource(data->clock_model.bg_bitmap_id);
|
||||
}
|
||||
|
||||
static void prv_window_unload(Window *window) {
|
||||
MultiWatchData *data = app_state_get_user_data();
|
||||
gpath_destroy(data->hour_path);
|
||||
gpath_destroy(data->minute_path);
|
||||
gbitmap_destroy(data->bg_bitmap);
|
||||
}
|
||||
|
||||
static void prv_app_did_focus(bool did_focus) {
|
||||
if (!did_focus) {
|
||||
return;
|
||||
}
|
||||
app_focus_service_unsubscribe();
|
||||
watch_model_start_intro();
|
||||
}
|
||||
|
||||
static void prv_init(void) {
|
||||
MultiWatchData *data = app_zalloc_check(sizeof(*data));
|
||||
app_state_set_user_data(data);
|
||||
|
||||
data->text_font = fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD);
|
||||
|
||||
window_init(&data->window, "TicToc");
|
||||
window_set_window_handlers(&data->window, &(WindowHandlers) {
|
||||
.load = prv_window_load,
|
||||
.unload = prv_window_unload,
|
||||
});
|
||||
const bool animated = true;
|
||||
app_window_stack_push(&data->window, animated);
|
||||
|
||||
app_focus_service_subscribe_handlers((AppFocusHandlers) {
|
||||
.did_focus = prv_app_did_focus,
|
||||
});
|
||||
}
|
||||
|
||||
static void prv_deinit(void) {
|
||||
MultiWatchData *data = app_state_get_user_data();
|
||||
window_destroy(&data->window);
|
||||
watch_model_cleanup();
|
||||
}
|
||||
|
||||
void tictoc_main(void) {
|
||||
prv_init();
|
||||
app_event_loop();
|
||||
prv_deinit();
|
||||
}
|
||||
212
src/fw/apps/watch/tictoc/spalding/watch_model.c
Normal file
212
src/fw/apps/watch/tictoc/spalding/watch_model.c
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "watch_model.h"
|
||||
|
||||
#include "util/trig.h"
|
||||
#include "applib/app_watch_info.h"
|
||||
#include "applib/pbl_std/pbl_std.h"
|
||||
#include "applib/tick_timer_service.h"
|
||||
#include "resource/resource_ids.auto.h"
|
||||
#include "syscall/syscall.h"
|
||||
#include "util/time/time.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
static void prv_calculate_hand_angles(struct tm *tick_time, int32_t *hour_angle,
|
||||
int32_t *minute_angle) {
|
||||
*hour_angle = (tick_time->tm_hour % 12) * TRIG_MAX_ANGLE / 12
|
||||
+ tick_time->tm_min * TRIG_MAX_ANGLE / 60 / 12;
|
||||
*minute_angle = tick_time->tm_min * TRIG_MAX_ANGLE / 60;
|
||||
}
|
||||
|
||||
static ClockFace prv_local_clock_face_default(struct tm *tick_time) {
|
||||
int32_t hour_angle, minute_angle;
|
||||
prv_calculate_hand_angles(tick_time, &hour_angle, &minute_angle);
|
||||
|
||||
// TODO: Don't return by value. This thing is massive.
|
||||
return (ClockFace) {
|
||||
.hour_hand = {
|
||||
.angle = hour_angle,
|
||||
.backwards_extension = LOCAL_HOUR_HAND_BACK_EXT_DEFAULT,
|
||||
.color = LOCAL_HOUR_HAND_COLOR_DEFAULT,
|
||||
.length = LOCAL_HOUR_HAND_LENGTH_DEFAULT,
|
||||
.style = CLOCK_HAND_STYLE_ROUNDED,
|
||||
.thickness = LOCAL_HOUR_HAND_THICKNESS_DEFAULT,
|
||||
},
|
||||
.minute_hand = {
|
||||
.angle = minute_angle,
|
||||
.backwards_extension = LOCAL_MINUTE_HAND_BACK_EXT_DEFAULT,
|
||||
.color = LOCAL_MINUTE_HAND_COLOR_DEFAULT,
|
||||
.length = LOCAL_MINUTE_HAND_LENGTH_DEFAULT,
|
||||
.style = CLOCK_HAND_STYLE_ROUNDED,
|
||||
.thickness = LOCAL_MINUTE_HAND_THICKNESS_DEFAULT,
|
||||
},
|
||||
.bob_radius = LOCAL_BOB_RADIUS_DEFAULT,
|
||||
.bob_color = LOCAL_BOB_COLOR_DEFAULT,
|
||||
.location = CLOCK_LOCATION_CENTER,
|
||||
};
|
||||
}
|
||||
|
||||
static NonLocalClockFace prv_configure_non_local_clock_face(int32_t utc_offset, const char *text,
|
||||
GColor text_color, GColor hand_color,
|
||||
ClockTextLocation location) {
|
||||
time_t t = rtc_get_time();
|
||||
struct tm* tick_time = pbl_override_gmtime(&t);
|
||||
tick_time->tm_hour += utc_offset; // TODO check if this works properly
|
||||
int32_t hour_angle, minute_angle;
|
||||
prv_calculate_hand_angles(tick_time, &hour_angle, &minute_angle);
|
||||
|
||||
// TODO: Don't return by value. This thing is massive.
|
||||
NonLocalClockFace non_local_clock = (NonLocalClockFace) {
|
||||
.face = {
|
||||
.hour_hand = {
|
||||
.length = NON_LOCAL_HOUR_HAND_LENGTH_DEFAULT,
|
||||
.thickness = NON_LOCAL_HOUR_HAND_WIDTH_DEFAULT,
|
||||
.backwards_extension = 0,
|
||||
.angle = hour_angle,
|
||||
.color = hand_color,
|
||||
.style = CLOCK_HAND_STYLE_ROUNDED,
|
||||
},
|
||||
.minute_hand = {
|
||||
.length = NON_LOCAL_MINUTE_HAND_LENGTH_DEFAULT,
|
||||
.thickness = NON_LOCAL_MINUTE_HAND_WIDTH_DEFAULT,
|
||||
.backwards_extension = 0,
|
||||
.angle = minute_angle,
|
||||
.color = hand_color,
|
||||
.style = CLOCK_HAND_STYLE_ROUNDED,
|
||||
},
|
||||
.location = location,
|
||||
},
|
||||
.text_color = text_color,
|
||||
};
|
||||
strncpy(non_local_clock.buffer, text, sizeof(non_local_clock.buffer));
|
||||
return non_local_clock;
|
||||
}
|
||||
|
||||
// Configure the text displayed on the clock.
|
||||
static ClockText prv_configure_clock_text(ClockTextType type, ClockTextLocation location,
|
||||
GColor color, struct tm *tick_time) {
|
||||
ClockText text = (ClockText) {
|
||||
.location = location,
|
||||
.color = color,
|
||||
};
|
||||
|
||||
if (type == CLOCK_TEXT_TYPE_DATE) {
|
||||
strftime(text.buffer, sizeof(text.buffer), "%a %d", tick_time);
|
||||
} else if (type == CLOCK_TEXT_TYPE_TIME) {
|
||||
strftime(text.buffer, sizeof(text.buffer), "$l:%M%P", tick_time);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < sizeof(text.buffer); i++) {
|
||||
text.buffer[i] = toupper((unsigned char)text.buffer[i]);
|
||||
}
|
||||
|
||||
// TODO: Don't return a struct
|
||||
return text;
|
||||
}
|
||||
|
||||
static ClockModel prv_clock_model_default(struct tm *tick_time) {
|
||||
// Create a generic model and configure a default clock.
|
||||
ClockModel model;
|
||||
model.local_clock = prv_local_clock_face_default(tick_time);
|
||||
|
||||
// Add watch-specific details.
|
||||
const WatchInfoColor watch_color = sys_watch_info_get_color();
|
||||
switch (watch_color) {
|
||||
case WATCH_INFO_COLOR_TIME_ROUND_BLACK_14:
|
||||
model.local_clock.minute_hand.color = GColorBlue;
|
||||
model.text = prv_configure_clock_text(CLOCK_TEXT_TYPE_DATE, CLOCK_TEXT_LOCATION_LEFT,
|
||||
GColorWhite, tick_time);
|
||||
model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_14MM_BLACK_RED;
|
||||
break;
|
||||
case WATCH_INFO_COLOR_TIME_ROUND_BLACK_20:
|
||||
model.num_non_local_clocks = 2;
|
||||
model.non_local_clock[0] = prv_configure_non_local_clock_face(-7, "LA", GColorDarkGray,
|
||||
GColorWhite,
|
||||
CLOCK_LOCATION_LEFT);
|
||||
model.non_local_clock[1] = prv_configure_non_local_clock_face(2, "PAR", GColorDarkGray,
|
||||
GColorWhite,
|
||||
CLOCK_LOCATION_RIGHT);
|
||||
model.text = prv_configure_clock_text(CLOCK_TEXT_TYPE_DATE, CLOCK_TEXT_LOCATION_BOTTOM,
|
||||
GColorWhite, tick_time);
|
||||
model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_20MM_BLACK;
|
||||
break;
|
||||
case WATCH_INFO_COLOR_TIME_ROUND_SILVER_14:
|
||||
model.local_clock.hour_hand.style = CLOCK_HAND_STYLE_POINTED;
|
||||
model.local_clock.hour_hand.color = GColorBlack;
|
||||
model.local_clock.minute_hand.style = CLOCK_HAND_STYLE_POINTED;
|
||||
model.local_clock.minute_hand.color = GColorCadetBlue;
|
||||
model.text = prv_configure_clock_text(CLOCK_TEXT_TYPE_DATE, CLOCK_TEXT_LOCATION_BOTTOM,
|
||||
GColorDarkGray, tick_time);
|
||||
model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_14MM_SILVER;
|
||||
break;
|
||||
case WATCH_INFO_COLOR_TIME_ROUND_SILVER_20:
|
||||
model.local_clock.hour_hand.style = CLOCK_HAND_STYLE_POINTED;
|
||||
model.local_clock.minute_hand.style = CLOCK_HAND_STYLE_POINTED;
|
||||
model.local_clock.minute_hand.color = GColorRed;
|
||||
model.local_clock.bob_color = GColorBlack;
|
||||
model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_20MM_SILVER_BROWN;
|
||||
break;
|
||||
case WATCH_INFO_COLOR_TIME_ROUND_ROSE_GOLD_14:
|
||||
default:
|
||||
model.local_clock.bob_center_color = GColorOrange;
|
||||
model.local_clock.minute_hand.color = GColorWhite;
|
||||
model.local_clock.minute_hand.thickness = 2;
|
||||
model.local_clock.minute_hand.length = 54;
|
||||
model.local_clock.hour_hand.color = GColorBlack;
|
||||
model.local_clock.hour_hand.thickness = 8;
|
||||
model.local_clock.hour_hand.length = 39;
|
||||
model.local_clock.bob_radius = 7;
|
||||
model.local_clock.bob_center_radius = 3;
|
||||
model.local_clock.bob_color = GColorWhite;
|
||||
model.bg_bitmap_id = RESOURCE_ID_MULTIWATCH_BACKGROUND_14MM_ROSE_GOLD;
|
||||
break;
|
||||
}
|
||||
|
||||
// disable timezones until they can be configured by the user
|
||||
model.num_non_local_clocks = 0;
|
||||
|
||||
// TODO: Don't return a struct here
|
||||
return model;
|
||||
}
|
||||
|
||||
static void prv_handle_time_update(struct tm *tick_time, TimeUnits units_changed) {
|
||||
ClockModel model = prv_clock_model_default(tick_time);
|
||||
watch_model_handle_change(&model);
|
||||
}
|
||||
|
||||
void watch_model_cleanup() {
|
||||
tick_timer_service_unsubscribe();
|
||||
}
|
||||
|
||||
static void prv_intro_animation_finished(Animation *animation) {
|
||||
const time_t t = rtc_get_time();
|
||||
prv_handle_time_update(pbl_override_localtime(&t), (TimeUnits)0xff);
|
||||
|
||||
tick_timer_service_subscribe(MINUTE_UNIT, prv_handle_time_update);
|
||||
}
|
||||
|
||||
void watch_model_start_intro() {
|
||||
prv_intro_animation_finished(NULL);
|
||||
}
|
||||
|
||||
void watch_model_init(void) {
|
||||
const time_t t = rtc_get_time();
|
||||
struct tm *tick_time = pbl_override_localtime(&t);
|
||||
ClockModel model = prv_clock_model_default(tick_time);
|
||||
watch_model_handle_change(&model);
|
||||
}
|
||||
119
src/fw/apps/watch/tictoc/spalding/watch_model.h
Normal file
119
src/fw/apps/watch/tictoc/spalding/watch_model.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "applib/graphics/gtypes.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define LOCAL_HOUR_HAND_LENGTH_DEFAULT 51
|
||||
#define LOCAL_HOUR_HAND_THICKNESS_DEFAULT 6
|
||||
#define LOCAL_HOUR_HAND_COLOR_DEFAULT GColorWhite
|
||||
#define LOCAL_HOUR_HAND_BACK_EXT_DEFAULT 0
|
||||
|
||||
#define LOCAL_MINUTE_HAND_LENGTH_DEFAULT 58
|
||||
#define LOCAL_MINUTE_HAND_THICKNESS_DEFAULT 6
|
||||
#define LOCAL_MINUTE_HAND_COLOR_DEFAULT GColorWhite
|
||||
#define LOCAL_MINUTE_HAND_BACK_EXT_DEFAULT 0
|
||||
|
||||
#define LOCAL_BOB_RADIUS_DEFAULT 6
|
||||
#define LOCAL_BOB_COLOR_DEFAULT GColorRed
|
||||
|
||||
#define NON_LOCAL_HOUR_HAND_LENGTH_DEFAULT 11
|
||||
#define NON_LOCAL_HOUR_HAND_WIDTH_DEFAULT 3
|
||||
|
||||
#define NON_LOCAL_MINUTE_HAND_LENGTH_DEFAULT 21
|
||||
#define NON_LOCAL_MINUTE_HAND_WIDTH_DEFAULT 3
|
||||
|
||||
#define NUM_NON_LOCAL_CLOCKS 3
|
||||
|
||||
#define GLANCE_TIME_OUT_MS 8000
|
||||
|
||||
typedef enum {
|
||||
CLOCK_TEXT_TYPE_NONE = 0,
|
||||
CLOCK_TEXT_TYPE_TIME,
|
||||
CLOCK_TEXT_TYPE_DATE,
|
||||
} ClockTextType;
|
||||
|
||||
typedef enum {
|
||||
CLOCK_TEXT_LOCATION_NONE = 0,
|
||||
CLOCK_TEXT_LOCATION_BOTTOM,
|
||||
CLOCK_TEXT_LOCATION_LEFT,
|
||||
} ClockTextLocation;
|
||||
|
||||
typedef enum {
|
||||
CLOCK_HAND_STYLE_ROUNDED = 0,
|
||||
CLOCK_HAND_STYLE_ROUNDED_WITH_HIGHLIGHT,
|
||||
CLOCK_HAND_STYLE_POINTED,
|
||||
} ClockHandStyle;
|
||||
|
||||
typedef enum {
|
||||
CLOCK_LOCATION_CENTER,
|
||||
CLOCK_LOCATION_LEFT,
|
||||
CLOCK_LOCATION_BOTTOM,
|
||||
CLOCK_LOCATION_RIGHT,
|
||||
CLOCK_LOCATION_TOP,
|
||||
} ClockLocation;
|
||||
|
||||
typedef struct {
|
||||
uint16_t length;
|
||||
uint16_t thickness;
|
||||
uint16_t backwards_extension;
|
||||
int32_t angle;
|
||||
GColor color;
|
||||
ClockHandStyle style;
|
||||
} ClockHand;
|
||||
|
||||
typedef struct {
|
||||
ClockHand hour_hand;
|
||||
ClockHand minute_hand;
|
||||
uint16_t bob_radius;
|
||||
uint16_t bob_center_radius;
|
||||
GColor bob_color;
|
||||
GColor bob_center_color;
|
||||
ClockLocation location;
|
||||
} ClockFace;
|
||||
|
||||
typedef struct {
|
||||
ClockFace face;
|
||||
char buffer[4];
|
||||
int32_t utc_offest;
|
||||
GColor text_color;
|
||||
} NonLocalClockFace;
|
||||
|
||||
typedef struct {
|
||||
ClockTextType type;
|
||||
ClockTextLocation location;
|
||||
char buffer[10]; // FIXME magic number
|
||||
GColor color;
|
||||
} ClockText;
|
||||
|
||||
typedef struct {
|
||||
ClockFace local_clock;
|
||||
uint32_t num_non_local_clocks;
|
||||
NonLocalClockFace non_local_clock[NUM_NON_LOCAL_CLOCKS];
|
||||
ClockText text;
|
||||
uint32_t bg_bitmap_id;
|
||||
} ClockModel;
|
||||
|
||||
void watch_model_init(void);
|
||||
|
||||
void watch_model_handle_change(ClockModel *model);
|
||||
|
||||
void watch_model_start_intro(void);
|
||||
|
||||
void watch_model_cleanup(void);
|
||||
37
src/fw/apps/watch/tictoc/tictoc.c
Normal file
37
src/fw/apps/watch/tictoc/tictoc.c
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "tictoc.h"
|
||||
|
||||
#include "resource/resource_ids.auto.h"
|
||||
|
||||
const PebbleProcessMd* tictoc_get_app_info(void) {
|
||||
static const PebbleProcessMdSystem s_app_md = {
|
||||
.common = {
|
||||
// UUID: 8f3c8686-31a1-4f5f-91f5-01600c9bdc59
|
||||
.uuid = { 0x8f, 0x3c, 0x86, 0x86, 0x31, 0xa1, 0x4f, 0x5f,
|
||||
0x91, 0xf5, 0x01, 0x60, 0x0c, 0x9b, 0xdc, 0x59 },
|
||||
.main_func = tictoc_main,
|
||||
.process_type = ProcessTypeWatchface,
|
||||
#if CAPABILITY_HAS_JAVASCRIPT && !defined(PLATFORM_SPALDING)
|
||||
.is_rocky_app = true,
|
||||
#endif
|
||||
},
|
||||
.icon_resource_id = RESOURCE_ID_MENU_ICON_TICTOC_WATCH,
|
||||
.name = "TicToc",
|
||||
};
|
||||
return (const PebbleProcessMd*) &s_app_md;
|
||||
}
|
||||
23
src/fw/apps/watch/tictoc/tictoc.h
Normal file
23
src/fw/apps/watch/tictoc/tictoc.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2024 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "process_management/pebble_process_md.h"
|
||||
|
||||
void tictoc_main(void);
|
||||
|
||||
const PebbleProcessMd* tictoc_get_app_info(void);
|
||||
Reference in New Issue
Block a user