Skip to content

Commit b1f0ed0

Browse files
committed
Unfinished experiments in rendering POI maps.
Signed-off-by: Katharine Berry <ktbry@google.com>
1 parent b1e35f5 commit b1f0ed0

File tree

25 files changed

+1237
-29
lines changed

25 files changed

+1237
-29
lines changed

app/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,6 @@ add_executable(tiny_assistant_app
7878
src/c/converse/report_window.c
7979
src/c/vibes/haptic_feedback.c
8080
src/c/util/action_menu_crimes.c
81+
src/c/image_manager/image_manager.c
82+
src/c/converse/segments/widgets/map.c
8183
)

app/package.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,17 @@
9595
"REPORT_THREAD_UUID",
9696
"REPORT_SEND_RESULT",
9797
"COBBLE_WARNING",
98-
"ACTION_FEEDBACK_SENT"
98+
"ACTION_FEEDBACK_SENT",
99+
"IMAGE_WIDTH",
100+
"IMAGE_HEIGHT",
101+
"IMAGE_START_BYTE_SIZE",
102+
"IMAGE_CHUNK_OFFSET",
103+
"IMAGE_CHUNK_DATA",
104+
"IMAGE_COMPLETE",
105+
"IMAGE_ID",
106+
"MAP_WIDGET",
107+
"MAP_WIDGET_IMAGE_ID",
108+
"MAP_WIDGET_USER_LOCATION"
99109
],
100110
"resources": {
101111
"media": [

app/src/c/assistant.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@
1818
#include "consent/consent.h"
1919
#include "converse/session_window.h"
2020
#include "converse/conversation_manager.h"
21+
#include "image_manager/image_manager.h"
2122
#include "alarms/manager.h"
2223
#include "version/version.h"
2324
#include "settings/settings.h"
2425

2526
#include <pebble.h>
2627
#include <pebble-events/pebble-events.h>
2728

29+
2830
#define QUICK_LAUNCH_TIMEOUT_MS 60000
2931

3032
static RootWindow* s_root_window = NULL;
@@ -34,6 +36,7 @@ static void prv_init(void) {
3436
consent_migrate();
3537
settings_init();
3638
conversation_manager_init();
39+
image_manager_init();
3740
events_app_message_open();
3841
alarm_manager_init();
3942
}
@@ -42,6 +45,7 @@ static void prv_deinit(void) {
4245
if (s_root_window) {
4346
root_window_destroy(s_root_window);
4447
}
48+
image_manager_deinit();
4549
}
4650

4751
int main(void) {

app/src/c/converse/conversation.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
#include "conversation.h"
18+
#include "../image_manager/image_manager.h"
1819

1920

2021
struct ConversationEntry {
@@ -114,6 +115,9 @@ void conversation_destroy(Conversation* conversation) {
114115
free(entry->content.widget->widget.number.unit);
115116
}
116117
break;
118+
case ConversationWidgetTypeMap:
119+
image_manager_destroy_image(entry->content.widget->widget.map.image_id);
120+
break;
117121
}
118122
free(entry->content.widget);
119123
break;

app/src/c/converse/conversation.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ typedef enum {
3737
ConversationWidgetTypeWeatherMultiDay,
3838
ConversationWidgetTypeTimer,
3939
ConversationWidgetTypeNumber,
40+
ConversationWidgetTypeMap,
4041
} ConversationWidgetType;
4142

4243
typedef struct {
@@ -131,6 +132,10 @@ typedef struct {
131132
char *unit;
132133
} ConversationWidgetNumber;
133134

135+
typedef struct {
136+
int image_id;
137+
} ConversationWidgetMap;
138+
134139
typedef struct {
135140
ConversationWidgetType type;
136141
bool locally_created;
@@ -140,6 +145,7 @@ typedef struct {
140145
ConversationWidgetWeatherMultiDay weather_multi_day;
141146
ConversationWidgetTimer timer;
142147
ConversationWidgetNumber number;
148+
ConversationWidgetMap map;
143149
} widget;
144150
} ConversationWidget;
145151

app/src/c/converse/conversation_manager.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ static void prv_handle_app_message_inbox_dropped(AppMessageResult result, void *
3636
static void prv_process_weather_widget(int widget_type, DictionaryIterator *iter, ConversationManager *manager);
3737
static void prv_process_timer_widget(int widget_type, DictionaryIterator *iter, ConversationManager *manager);
3838
static void prv_process_highlight_widget(int widget_type, DictionaryIterator *iter, ConversationManager *manager);
39+
static void prv_process_map_widget(int widget_type, DictionaryIterator *iter, ConversationManager *manager);
3940

4041
static ConversationManager* s_conversation_manager;
4142

@@ -199,6 +200,10 @@ static void prv_handle_app_message_inbox_received(DictionaryIterator *iter, void
199200
conversation_complete_response(manager->conversation);
200201
prv_conversation_updated(manager, false);
201202
prv_process_highlight_widget(tuple->value->int32, iter, manager);
203+
} else if (tuple->key == MESSAGE_KEY_MAP_WIDGET) {
204+
conversation_complete_response(manager->conversation);
205+
prv_conversation_updated(manager, false);
206+
prv_process_map_widget(tuple->value->int32, iter, manager);
202207
}
203208
}
204209
}
@@ -348,6 +353,24 @@ static void prv_process_highlight_widget(int widget_type, DictionaryIterator *it
348353
prv_conversation_updated(manager, true);
349354
}
350355

356+
357+
static void prv_process_map_widget(int widget_type, DictionaryIterator *iter, ConversationManager *manager) {
358+
if (widget_type != 1) {
359+
return;
360+
}
361+
int image_id = dict_find(iter, MESSAGE_KEY_MAP_WIDGET_IMAGE_ID)->value->int32;
362+
ConversationWidget widget = {
363+
.type = ConversationWidgetTypeMap,
364+
.widget = {
365+
.map = {
366+
.image_id = image_id,
367+
}
368+
}
369+
};
370+
conversation_add_widget(manager->conversation, &widget);
371+
prv_conversation_updated(manager, true);
372+
}
373+
351374
static void prv_handle_app_message_inbox_dropped(AppMessageResult reason, void *context) {
352375
APP_LOG(APP_LOG_LEVEL_ERROR, "Received message dropped: %d", reason);
353376
ConversationManager* manager = context;

app/src/c/converse/segments/segment_layer.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
#include "widgets/weather_multi_day.h"
2424
#include "widgets/number.h"
2525
#include "widgets/timer.h"
26+
#include "widgets/map.h"
2627

2728
#include <pebble.h>
2829

2930

31+
3032
#define CONTENT_FONT FONT_KEY_GOTHIC_24_BOLD
3133
#define NAME_HEIGHT 20
3234

@@ -38,6 +40,7 @@ typedef enum {
3840
SegmentTypeWeatherMultiDayWidget,
3941
SegmentTypeTimerWidget,
4042
SegmentTypeNumberWidget,
43+
SegmentTypeMapWidget,
4144
} SegmentType;
4245

4346
typedef struct {
@@ -56,6 +59,7 @@ typedef struct {
5659
WeatherMultiDayWidget* weather_multi_day_widget;
5760
TimerWidget* timer_widget;
5861
NumberWidget* number_widget;
62+
MapWidget* map_widget;
5963
};
6064
} SegmentLayerData;
6165

@@ -97,6 +101,9 @@ SegmentLayer* segment_layer_create(GRect rect, ConversationEntry* entry, bool as
97101
case SegmentTypeNumberWidget:
98102
data->number_widget = number_widget_create(child_frame, entry);
99103
break;
104+
case SegmentTypeMapWidget:
105+
data->map_widget = map_widget_create(child_frame, entry);
106+
break;
100107
}
101108
layer_add_child(layer, data->layer);
102109
GSize child_size = layer_get_frame(data->layer).size;
@@ -133,6 +140,9 @@ void segment_layer_destroy(SegmentLayer* layer) {
133140
case SegmentTypeNumberWidget:
134141
number_widget_destroy(data->number_widget);
135142
break;
143+
case SegmentTypeMapWidget:
144+
map_widget_destroy(data->map_widget);
145+
break;
136146
}
137147
if (data->assistant_label_layer) {
138148
text_layer_destroy(data->assistant_label_layer);
@@ -169,6 +179,9 @@ void segment_layer_update(SegmentLayer* layer) {
169179
case SegmentTypeNumberWidget:
170180
number_widget_update(data->number_widget);
171181
break;
182+
case SegmentTypeMapWidget:
183+
map_widget_update(data->map_widget);
184+
break;
172185
}
173186
GSize child_size = layer_get_frame(data->layer).size;
174187
GPoint origin = layer_get_frame(layer).origin;
@@ -200,6 +213,8 @@ static SegmentType prv_get_segment_type(ConversationEntry* entry) {
200213
return SegmentTypeTimerWidget;
201214
case ConversationWidgetTypeNumber:
202215
return SegmentTypeNumberWidget;
216+
case ConversationWidgetTypeMap:
217+
return SegmentTypeMapWidget;
203218
}
204219
break;
205220
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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 <pebble.h>
18+
#include "../../../image_manager/image_manager.h"
19+
20+
#include "map.h"
21+
22+
static void prv_image_updated(int image_id, ImageStatus status, void *context);
23+
static void prv_layer_update(Layer *layer, GContext *ctx);
24+
25+
typedef struct {
26+
ConversationEntry* entry;
27+
GBitmap* bitmap;
28+
} MapWidgetData;
29+
30+
static inline int prv_get_image_id(MapWidgetData *data) {
31+
return conversation_entry_get_widget(data->entry)->widget.map.image_id;
32+
}
33+
34+
MapWidget* map_widget_create(GRect rect, ConversationEntry* entry) {
35+
int image_id = conversation_entry_get_widget(entry)->widget.map.image_id;
36+
GSize image_size = image_manager_get_size(image_id);
37+
Layer *layer = layer_create_with_data(GRect(rect.origin.x, rect.origin.y, rect.size.w, image_size.h + 2), sizeof(MapWidgetData));
38+
MapWidgetData* data = layer_get_data(layer);
39+
data->entry = entry;
40+
data->bitmap = NULL;
41+
image_manager_register_callback(image_id, prv_image_updated, layer);
42+
layer_set_update_proc(layer, prv_layer_update);
43+
return layer;
44+
}
45+
46+
ConversationEntry* map_widget_get_entry(MapWidget* layer) {
47+
MapWidgetData* data = layer_get_data(layer);
48+
return data->entry;
49+
}
50+
51+
void map_widget_destroy(MapWidget* layer) {
52+
MapWidgetData* data = layer_get_data(layer);
53+
if (data->bitmap) {
54+
gbitmap_destroy(data->bitmap);
55+
}
56+
int image_id = prv_get_image_id(data);
57+
image_manager_unregister_callback(image_id);
58+
layer_destroy(layer);
59+
}
60+
61+
void map_widget_update(MapWidget* layer) {
62+
// nothing to do here.
63+
}
64+
65+
static void prv_image_updated(int image_id, ImageStatus status, void *context) {
66+
Layer *layer = context;
67+
MapWidgetData* data = layer_get_data(layer);
68+
if (status == ImageStatusCompleted) {
69+
if (data->bitmap) {
70+
gbitmap_destroy(data->bitmap);
71+
}
72+
data->bitmap = image_manager_get_image(image_id);
73+
layer_mark_dirty(layer);
74+
} else if (status == ImageStatusDestroyed) {
75+
data->bitmap = NULL;
76+
layer_mark_dirty(layer);
77+
}
78+
}
79+
80+
static void prv_layer_update(Layer *layer, GContext *ctx) {
81+
MapWidgetData *data = layer_get_data(layer);
82+
GRect bounds = layer_get_bounds(layer);
83+
// bounds = grect_inset(bounds, (GEdgeInsets){.right = 5});
84+
GSize image_size = image_manager_get_size(prv_get_image_id(data));
85+
// GRect border_rect = GRect((bounds.size.w - image_size.w) / 2, 0, image_size.w + 2, image_size.h + 2);
86+
// GRect image_rect = grect_inset(border_rect, GEdgeInsets(1));
87+
GRect image_rect = grect_inset(bounds, GEdgeInsets(1, 0));
88+
graphics_context_set_stroke_color(ctx, GColorBlack);
89+
graphics_draw_line(ctx, GPoint(0, 0), GPoint(bounds.size.w, 0));
90+
graphics_draw_line(ctx, GPoint(0, bounds.size.h - 1), GPoint(bounds.size.w, bounds.size.h - 1));
91+
if (data->bitmap) {
92+
graphics_draw_bitmap_in_rect(ctx, data->bitmap, image_rect);
93+
} else {
94+
graphics_context_set_fill_color(ctx, COLOR_FALLBACK(GColorLightGray, GColorWhite));
95+
graphics_fill_rect(ctx, image_rect, 0, GCornerNone);
96+
}
97+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
#pragma once
18+
19+
#include <pebble.h>
20+
#include "../../conversation.h"
21+
22+
typedef Layer MapWidget;
23+
24+
MapWidget* map_widget_create(GRect rect, ConversationEntry* entry);
25+
ConversationEntry* map_widget_get_entry(MapWidget* layer);
26+
void map_widget_destroy(MapWidget* layer);
27+
void map_widget_update(MapWidget* layer);

0 commit comments

Comments
 (0)