#PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

69
2015 Pebble Developer Retreat Creating Pebble Apps for Aplite, Basalt, and Chalk Kevin Conley, Firmware Engineer

Transcript of #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Page 1: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

2015 Pebble Developer Retreat

Creating Pebble Apps for Aplite, Basalt, and Chalk

Kevin Conley, Firmware Engineer

Page 2: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Why Target Multiple Platforms?

Page 3: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Maximize Userbase

Pebble Time

Pebble Classic

Page 4: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Continuity

1. User installs your app on an older platform

2. User upgrades to a new Pebble 3. User already has the most recent

version of your app in their locker :)

Page 5: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Developer Portal Icons

Page 6: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Developer Portal Icons

Page 7: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Agenda

•Runtime Platforms vs. SDK Versions •Colors •Display Shapes •SDK Compatibility

Page 8: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Runtime Platforms vs. SDK Versions

Page 9: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

“Runtime Platform”

•Set of runtime attributes and capabilities •Does NOT refer to a single watch hardware model

Page 10: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Aplite

2.x ✅

Pebble Classic Pebble Steel

OS Compatibility

•2.x is legacy •3.x for Aplite coming soon!

Page 11: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Aplite Basalt

2.x ✅

3.0 ❌ ✅

Pebble Time Pebble Time Steel

OS Compatibility

Page 12: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Aplite Basalt Chalk

2.x ✅

3.0 ❌ ✅

3.6 ❌ ✅

Pebble Time Round

OS Compatibility

Page 13: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Aplite Basalt Chalk

CPU 24kARM Cortex M3

64k ARM Cortex M4

Resolution 144x168px(Rectangular)

180x180px (Circular)

Color Black & White 64 Colors

SmartStrap ❌ ✅

Mic ❌ ✅

Hardware Features

Page 14: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Aplite, Basalt, and Chalk are runtime platforms, not SDK versions.

Page 15: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Aplite Basalt Chalk

2.x ✅

3.0 ❌ ✅

3.6 ❌ ✅

3.x ✅

Unified 3.x SDK later this year

OS Compatibility

Page 16: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

runs on

2.x 3.x

Aplite Basalt

2.x Aplite ✅ ✅

3.x Basalt ❌ ✅

com

pile

d a

gai

nst

2.x Aplite runs on 3.x Basalt

SDK Compatibility

Page 17: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

runs on

2.x 3.x

Aplite Aplite Basalt Chalk

2.x Aplite ✅ ✅ ✅ ❌

3.x

Aplite ❌ ✅ ❌ ❌

Basalt ❌ ❌ ✅ ❌

Chalk ❌ ❌ ❌ ✅

com

pile

d a

gai

nst

3.x apps are compiled per platform

SDK Compatibility

Page 18: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Colors

Page 19: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Colors

• Aplite •Black and white

• Basalt and Chalk •64 colors •Pebble color picker tool: •https://developer.getpebble.com/more/color-picker

•Download color palettes for Photoshop, GIMP, ImageMagick •https://developer.getpebble.com/guides/pebble-apps/display-and-animations/intro-to-colors/#color-palettes

Page 20: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Open-Source Dithering Library•Created by Mathew Reiss •Display approximate colors beyond provided color palette •https://github.com/mathewreiss/dithering

Aplite: shades of gray Basalt/Chalk: > 300 colors

Page 21: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

#define COLOR_FALLBACK(_____, _____)

Page 22: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

#define COLOR_FALLBACK(color_value, bw_value)

Page 23: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

#define COLOR_FALLBACK(color_value, bw_value)

(deprecated)

Page 24: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Organize Code Using Color Macros

#define PBL_IF_COLOR_ELSE(if_true, if_false)

#define PBL_IF_BW_ELSE(if_true, if_false)

Returns if_true on watches that support color and if_false otherwise

Returns if_true on watches that only support black & white and if_false otherwise

Page 25: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Organize Code Using Color Defines

#define PBL_COLOR

#define PBL_BW

Defined on watches that support color and undefined otherwise

Defined on watches that only support black & white and undefined otherwise

Page 26: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Color-Specific Resources

"resources": { "media": [ { "type": "png", "name": "MY_IMAGE", "file": "images/image.png" } ]}

appinfo.json: resources/ images/ image~bw.png image~color.png

image~bw.png image~color.png

Page 27: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

WatchInfoColor

GColor text_foreground_color, text_background_color; switch(watch_info_get_color()) { case WATCH_INFO_COLOR_TIME_STEEL_GOLD: // White on red theme text_foreground_color = GColorWhite; text_background_color = GColorRed; break; case WATCH_INFO_COLOR_TIME_ROUND_BLACK_20: // White on black theme text_foreground_color = GColorWhite; text_background_color = GColorBlack; break; /* Other cases... */ } text_layer_set_text_color(s_label_layer, text_foreground_color);text_layer_set_background_color(s_label_layer, text_background_color);

•Customize your app’s UI using the color of the Pebble watch

Page 28: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Display Shapes

Page 29: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Display Shapes

• Round has 7% more visible pixels than rectangular

Rectangular (144 x 168)

Round (visible)

Round (total) (180 x 180)

(0, 0)

Page 30: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Calculate Position/Size as a Function of Other Layers

const Layer *window_layer = window_get_root_layer(window);

const GRect text_frame = GRect(0, 72, 144, 20);

const TextLayer *text_layer = text_layer_create(text_frame); text_layer_set_text(text_layer, "Hello world!"); text_layer_set_text_alignment(text_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(text_layer));

Page 31: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Calculate Position/Size as a Function of Other Layers

const Layer *window_layer = window_get_root_layer(window);

const GRect text_frame = GRect(0, 72, 144, 20);

const TextLayer *text_layer = text_layer_create(text_frame); text_layer_set_text(text_layer, "Hello world!"); text_layer_set_text_alignment(text_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(text_layer));

Page 32: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Calculate Position/Size as a Function of Other Layers

const Layer *window_layer = window_get_root_layer(window);

const GRect text_frame = GRect(0, 72, 144, 20);

const TextLayer *text_layer = text_layer_create(text_frame); text_layer_set_text(text_layer, "Hello world!"); text_layer_set_text_alignment(text_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(text_layer));

Page 33: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Calculate Position/Size as a Function of Other Layers

const Layer *window_layer = window_get_root_layer(window);

const GRect window_bounds = layer_get_bounds(window_layer); const int16_t text_height = 20; const int16_t text_frame_y = (window_bounds.size.h - text_height) / 2; const GRect text_frame = (GRect) { .origin = GPoint(0, text_frame_y), .size = GSize(window_bounds.size.w, text_height) };

const TextLayer *text_layer = text_layer_create(text_frame); text_layer_set_text(text_layer, "Hello world!"); text_layer_set_text_alignment(text_layer, GTextAlignmentCenter); layer_add_child(window_layer, text_layer_get_layer(text_layer));

Page 34: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

void grect_align(GRect *rect, const GRect *inside_rect, const GAlign alignment, const bool clip);

GAlignCenter clip = false

rect

inside_rect

Align Rectangles Within Other Rectangles Using grect_align()

inside_rect

GAlignTop clip = true

rect

Page 35: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

typedef enum GAlign { GAlignCenter, GAlignTopLeft, GAlignTopRight, GAlignTop, GAlignLeft, GAlignBottom, GAlignRight, GAlignBottomRight, GAlignBottomLeft} GAlign; GAlignBottomRight

GAlignRight

GAlignTopRight

GAlignBottom

GAlignCenter

GAlignTop

GAlignBottomLeft

GAlignLeft

GAlignTopLeft

Page 36: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

const GRect a = grect_inset(rect, GEdgeInsets(16)); // all sides const GRect b = grect_inset(rect, GEdgeInsets(25, 16)); // top & bottom, right & left const GRect c = grect_inset(rect, GEdgeInsets(25, 16, 10)); // top, right & left, bottom const GRect d = grect_inset(rect, GEdgeInsets(25, -20, 10, 16)); // top, right, bottom, left

a b c d

16px

16px 25px 25px 25px

25px10px 10px

16px 16px 16px 16px16px 16px 16px 20px

Inset Rectangles Using grect_inset() and GEdgeInsets()

Page 37: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

rect

GPoint grect_center_point(const GRect *rect);

result

Find the Center of Rectangles Using grect_center_point()

Page 38: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

•Use PBL_IF_[ROUND | RECT]_ELSE() macros to choose values or simple expressions based on the display shape •Similar to PBL_IF_[COLOR | BW]_ELSE()

Organize Code Using Display Shape Macros/Defines

const uint16_t area = PBL_IF_ROUND_ELSE(PI * (width * width / 4), (width * height));

Page 39: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

•For multiple lines, choose code using PBL_[ROUND | RECT] defines

#if defined(PBL_ROUND) const GPoint center_point = grect_center_point(&rect); graphics_fill_circle(ctx, center_point, radius);#else const uint16_t corner_radius = 0; graphics_fill_rect(ctx, rect, corner_radius, GCornerNone);#endif

Organize Code Using Display Shape Macros/Defines

Page 40: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

• For > 5 lines, break code into functions with _rect/_round suffixes • If the functions share the same arguments, you can simplify calling them by using the PBL_IF_[ROUND | RECT]_ELSE() macro

• Otherwise, use the PBL_[ROUND | RECT] defines:

Organize Code Using Display Shape Macros/Defines

PBL_IF_RECT_ELSE(function_rect, function_round)(arg1, arg2, ...);

#if defined(PBL_RECT) function_rect(arg1);#else function_round(arg1, arg2);#endif

Page 41: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

• For > 2 display-specific functions, break into separate _rect/_round files • Use wrapper file to choose appropriate implementation using display shape defines/macros

• Alternatively, if the files share a common API:

• (also requires ifdef’ing out implementations)

Organize Code Using Display Shape Macros/Defines

// main.c #if defined(PBL_RECT) #include “file_rect.h"#else #include “file_round.h"#endif

src/ file_rect.c file_rect.h file_round.c file_round.h file.c file.h main.c

Page 42: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Display-Specific Resources

"resources": { "media": [ { "type": "png", "name": "MY_IMAGE", "file": "images/image.png" } ]}

resources/ images/ image~rect.png image~color~round.png

appinfo.json:

image~rect.png image~color~round.png

•Most-specific resource will be used •Ambiguity will result in compiler error

Page 43: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Two-Pixel Margin on Round Displays

•Extend background colors to all outer edges •Avoid thin rings around the edge of the display • Manufacturing variations may result in off-center appearance

• Instead, use thick rings or significantly inset from the edge

Page 44: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Platform-Specific Designs

“Caltrain” by Katharine Berry: https://github.com/Katharine/pebble-caltrain/

Rectangular Round

Page 45: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

UI Components

Page 46: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Status Bar• For SDK 2.x, the status bar appears by default and insets the window from the top unless you use:window_set_fullscreen(window, true);

• For SDK 3.x, the status bar is a UI component you must create, configure, and add to the layer hierarchy yourself:

static StatusBarLayer *s_status_bar;s_status_bar = status_bar_layer_create();status_bar_layer_set_separator_mode(s_status_bar, StatusBarLayerSeparatorModeDotted); status_bar_layer_set_colors(s_status_bar, GColorBlack, GColorWhite);const Layer *status_bar_layer = status_bar_layer_get_layer(s_status_bar);layer_add_child(window_root_layer, status_bar_layer);

Page 47: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Status Bar

Rectangular Round

Page 48: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Status Bar

Page 49: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

s_status_bar = status_bar_layer_create();GRect status_bar_rect = window_bounds;status_bar_rect.size.h = STATUS_BAR_LAYER_HEIGHT; layer_set_frame( status_bar_layer_get_layer(s_status_bar), status_bar_rect);// top/bottom: statusbar, left/right: 0 const GEdgeInsets insets = GEdgeInsets( STATUS_BAR_LAYER_HEIGHT, 0); GRect menu_layer_rect = grect_inset( window_bounds, insets);

Status Bar

Page 50: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

ActionBarRectangular

Round

Page 51: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

ActionBar

Page 52: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

s_action_bar = action_bar_layer_create();action_bar_layer_add_to_window( s_action_bar, window);// top: statusbar, right: actionbar, bottom/left:0 const GEdgeInsets insets = GEdgeInsets( STATUS_BAR_LAYER_HEIGHT, ACTION_BAR_WIDTH, 0, 0);GRect content_rect = grect_inset( window_bounds, insets);

ActionBar

Page 53: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

MenuLayer

Rectangular Round

Page 54: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

MENU_CELL_ROUND_UNFOCUSED_SHORT_CELL_HEIGHT

MENU_CELL_ROUND_FOCUSED_TALL_CELL_HEIGHTMENU_CELL_ROUND_UNFOCUSED_SHORT_CELL_HEIGHT

MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT

MENU_CELL_ROUND_FOCUSED_SHORT_CELL_HEIGHT

MENU_CELL_ROUND_UNFOCUSED_TALL_CELL_HEIGHT

MENU_CELL_BASIC_CELL_HEIGHT

MENU_CELL_BASIC_CELL_HEIGHT

MENU_CELL_BASIC_CELL_HEIGHT

MenuLayer

Page 55: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

// default on Chalk menu_layer_set_center_focused(menu_layer, true);

static int16_t get_cell_height_callback(MenuLayer *menu_layer, MenuIndex *cell_index, void *callback_context) {

#if defined(PBL_ROUND) // when center_focused, MenuLayer can handle cell heights // that change based on the selection status if (menu_layer_is_index_selected(menu_layer, cell_index)) { return MENU_CELL_ROUND_FOCUSED_TALL_CELL_HEIGHT; } else { return MENU_CELL_ROUND_UNFOCUSED_SHORT_CELL_HEIGHT; } #else return MENU_CELL_BASIC_CELL_HEIGHT; #endif}

MenuLayer

Page 56: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

TextLayer

•To maximize content visible on round displays, enable text flow and paging with an optional inset (for padding):

text_layer_enable_screen_text_flow_and_paging(s_text_layer, 8);

Page 57: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

TextLayer

•Text flow on a round display without pagination looks weird…

Page 58: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

WatchInfo• Inspect what watch the app is running on • Useful e.g. if you want to align visual elements with buttons •Next to different screen coordinates for different watches

GRect layer_frame = GRectZero; switch(watch_info_get_model()) { case WATCH_INFO_MODEL_PEBBLE_STEEL: layer_frame = GRect(0, 64, 144, 44); break; case WATCH_INFO_MODEL_PEBBLE_TIME: layer_frame = GRect(0, 58, 144, 56); break; /* Other cases */}

Page 59: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

SDK Compatibility

Page 60: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Platform Defines

•Feature/capability defines, like PBL_COLOR, PBL_ROUND, etc. should be used in favor of these •Exception: CPU/memory-specific code

#define PBL_PLATFORM_APLITE #define PBL_PLATFORM_BASALT #define PBL_PLATFORM_CHALK

Page 61: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Platform Specific Resources

• Can also use ~aplite, ~basalt, and ~chalk suffixes • But overall, in most cases it’s best to use ~rect, ~round, ~bw, ~color suffixes instead

"resources": { "media": [ { "type": "png", "name": "MY_IMAGE", "file": “images/image.png”, "targetPlatforms": [ "basalt" ] } ]}

Page 62: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

GPoint shim_gpoint_from_polar(GRect rect, int32_t angle) {#if defined(PBL_SDK_2) const GPoint center = GPoint(rect.origin.x + (rect.size.w / 2), rect.origin.y + (rect.size.h / 2)); const int16_t radius = MIN(rect.size.w, rect.size.h) / 2; return GPoint((sin_lookup(angle) * radius / TRIG_MAX_RATIO) + center.x, (-cos_lookup(angle) * radius / TRIG_MAX_RATIO) + center.y);#else return gpoint_from_polar(rect, GOvalScaleModeFitCircle, angle);#endif}

Shims•Newer SDK 3.x functions do not exist in SDK 2.x •You can create shims for them using PBL_SDK_2/3 defines:

Page 63: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

PebbleKit JS API Availability

•Some PebbleKit JS APIs don’t exist on Pebble Classic mobile apps, •Check if they are available and fail gracefully if necessary

if (Pebble.getActiveWatchInfo) { // API is available; use it! var info = Pebble.getActiveWatchInfo();

console.log('Pebble model: ' + info.model); } else { // Gracefully handle unavailable API }

Page 64: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Check Return Values• APIs may return NULL if: • Unsupported by platform • Permissions not given • Phone disconnected

• Check the return value before using it and fail gracefully if necessary

const DictationSession *session = dictation_session_create(0, callback, callback_context); if (session) { /* Success! */ dictation_session_start(session); } else { /* DictationSession is unsupported, need to fail gracefully here! */ }

Page 65: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Marcel: Friday 1 pm

Starting with Chalk, you need to compile with PebbleKit iOS 3.0

Since PebbleOS 3.0, you need to compile with PebbleKit Android 3.0

AndroidiOS

Mobile App Compatibility

Page 66: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Running Multiple Emulators at Once• pebble install --emulator=aplite && pebble install --emulator=basalt && pebble install —-emulator=chalk

Page 67: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Automating Taking Screenshots• pebble screenshot --emulator=aplite && pebble screenshot --emulator=basalt && pebble screenshot —-emulator=chalk

Page 68: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Recap

•Runtime Platforms vs. SDK Versions •Colors •Display Shapes •SDK Compatibility

Page 69: #PDR15 Creating Pebble Apps for Aplite, Basalt, and Chalk

Questions?