1+ /*
2+ * Copyright 2025 Google LLC
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+ #include "timer.h"
18+ #include "../../conversation.h"
19+ #include "../../../util/style.h"
20+ #include <pebble.h>
21+ #include <pebble-events/pebble-events.h>
22+
23+ typedef struct {
24+ ConversationEntry * entry ;
25+ GDrawCommandImage * icon ;
26+ EventHandle event_handle ;
27+ char text [12 ];
28+ } TimerWidgetData ;
29+
30+ static void prv_layer_update (Layer * layer , GContext * ctx );
31+ static void prv_handle_tick (struct tm * tick_time , TimeUnits units_changed , void * context );
32+ static void prv_update_text_buffer (TimerWidgetData * data );
33+
34+ TimerWidget * timer_widget_create (GRect rect , ConversationEntry * entry ) {
35+ Layer * layer = layer_create_with_data (GRect (rect .origin .x , rect .origin .y , rect .size .w , 53 ), sizeof (TimerWidgetData ));
36+ TimerWidgetData * data = layer_get_data (layer );
37+
38+ data -> entry = entry ;
39+ data -> icon = gdraw_command_image_create_with_resource (RESOURCE_ID_TIMER_ICON );
40+ prv_update_text_buffer (data );
41+ layer_set_update_proc (layer , prv_layer_update );
42+
43+ data -> event_handle = events_tick_timer_service_subscribe_context (SECOND_UNIT , prv_handle_tick , layer );
44+ return layer ;
45+ }
46+
47+ ConversationEntry * timer_widget_get_entry (TimerWidget * layer ) {
48+ TimerWidgetData * data = layer_get_data (layer );
49+ return data -> entry ;
50+ }
51+
52+ void timer_widget_destroy (TimerWidget * layer ) {
53+ TimerWidgetData * data = layer_get_data (layer );
54+ gdraw_command_image_destroy (data -> icon );
55+ events_tick_timer_service_unsubscribe (data -> event_handle );
56+ layer_destroy (layer );
57+ }
58+
59+ void timer_widget_update (TimerWidget * layer ) {
60+ // nothing to do here.
61+ }
62+
63+ static void prv_layer_update (Layer * layer , GContext * ctx ) {
64+ TimerWidgetData * data = layer_get_data (layer );
65+ ConversationWidgetTimer * widget = & conversation_entry_get_widget (data -> entry )-> widget .timer ;
66+ GRect bounds = layer_get_bounds (layer );
67+ #if defined(PBL_COLOR )
68+ graphics_context_set_fill_color (ctx , BRANDED_BACKGROUND_COLOUR );
69+ graphics_context_set_text_color (ctx , gcolor_legible_over (BRANDED_BACKGROUND_COLOUR ));
70+ graphics_fill_rect (ctx , bounds , 0 , GCornerNone );
71+ #else
72+ graphics_context_set_text_color (ctx , GColorBlack );
73+ #endif
74+ graphics_context_set_stroke_color (ctx , GColorBlack );
75+ graphics_draw_line (ctx , GPoint (0 , 0 ), GPoint (bounds .size .w , 0 ));
76+ graphics_draw_line (ctx , GPoint (0 , bounds .size .h - 1 ), GPoint (bounds .size .w , bounds .size .h - 1 ));
77+
78+ gdraw_command_image_draw (ctx , data -> icon , GPoint (5 , 3 ));
79+
80+ const int16_t icon_space = 26 ;
81+ const GRect title_rect = GRect (icon_space , bounds .origin .y , bounds .size .w - icon_space , 20 );
82+ const GRect time_rect = GRect (5 , bounds .origin .y + 16 , bounds .size .w - 5 , bounds .size .h );
83+
84+ GFont time_font = fonts_get_system_font (FONT_KEY_LECO_32_BOLD_NUMBERS );
85+ GFont title_font = fonts_get_system_font (FONT_KEY_GOTHIC_18_BOLD );
86+ graphics_draw_text (ctx , widget -> name ? widget -> name : "Timer" , title_font , title_rect , GTextOverflowModeTrailingEllipsis , GTextAlignmentLeft , NULL );
87+ graphics_draw_text (ctx , data -> text , time_font , time_rect , GTextOverflowModeTrailingEllipsis , GTextAlignmentLeft , NULL );
88+ }
89+
90+ static void prv_handle_tick (struct tm * tick_time , TimeUnits units_changed , void * context ) {
91+ TimerWidget * layer = context ;
92+ TimerWidgetData * data = layer_get_data (layer );
93+ prv_update_text_buffer (data );
94+ layer_mark_dirty (context );
95+ }
96+
97+ static void prv_update_text_buffer (TimerWidgetData * data ) {
98+ time_t now = time (NULL );
99+ ConversationWidgetTimer * widget = & conversation_entry_get_widget (data -> entry )-> widget .timer ;
100+
101+ if (widget -> target_time <= now ) {
102+ strncpy (data -> text , "0:00" , sizeof (data -> text ));
103+ return ;
104+ }
105+ time_t interval = widget -> target_time - now ;
106+ int hours = interval / 3600 ;
107+ int minutes = (interval % 3600 ) / 60 ;
108+ int seconds = interval % 60 ;
109+ if (hours >= 10 ) {
110+ snprintf (data -> text , sizeof (data -> text ), "%d:%02d" , hours , minutes );
111+ } else if (hours > 0 ) {
112+ snprintf (data -> text , sizeof (data -> text ), "%d:%02d:%02d" , hours , minutes , seconds );
113+ } else {
114+ snprintf (data -> text , sizeof (data -> text ), "%d:%02d" , minutes , seconds );
115+ }
116+ data -> text [sizeof (data -> text ) - 1 ] = '\0' ;
117+ }
0 commit comments