├── .gitignore ├── LICENSE ├── README.md ├── appinfo.json ├── screenshots └── screenshot1.png ├── src ├── app-push-pin.c └── js │ ├── app.js │ └── timeline.js └── wscript /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Ignore build generated files 3 | build 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2015 Pebble Technology 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # push-pin-app 2 | 3 | This is a Pebble SDK 3.0 example app that demonstrates how an app can use 4 | PebbleKit JS to push a pin to itself through the 5 | [public timeline API](https://developer.getpebble.com/guides/timeline/timeline-public/). 6 | 7 | It includes a segment of JS code (marked 'timeline lib') that can be re-used in 8 | any Pebble app that needs to push pins to its user. 9 | 10 | ## Setup 11 | 12 | In order for it to work, you must do the following: 13 | 14 | 1. Generate a new UUID for `appinfo.json` with `uuidgen` or similar method. 15 | 16 | 2. Upload the modified app to the 17 | [Developer Portal](https://dev-portal.getpebble.com) (it does not need to be 18 | published). 19 | 20 | 3. [Enable it for the timeline](https://developer.getpebble.com/guides/timeline/timeline-enabling/). 21 | 22 | ## Try it out! 23 | 24 | The app will push a pin with a random `id` to a time one hour in the future. 25 | After running the app, check the future of your timeline! 26 | 27 | ![](screenshots/screenshot1.png) 28 | 29 | You can remove the pins by pressing the Select button on a pin in the timeline 30 | and choosing 'Remove'. -------------------------------------------------------------------------------- /appinfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "uuid": "ce0e040b-37a6-48ca-9ca9-099b9f14bae4", 3 | "shortName": "app-push-pin", 4 | "longName": "app-push-pin", 5 | "companyName": "MakeAwesomeHappen", 6 | "versionCode": 1, 7 | "versionLabel": "1.0", 8 | "sdkVersion": "3", 9 | "targetPlatforms": ["aplite", "basalt", "chalk"], 10 | "watchapp": { 11 | "watchface": false 12 | }, 13 | "appKeys": { 14 | "dummy": 0 15 | }, 16 | "resources": { 17 | "media": [] 18 | }, 19 | "enableMultiJS": true 20 | } 21 | -------------------------------------------------------------------------------- /screenshots/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pebble-examples/timeline-push-pin/c870f99fdae958733be4d74ecb2795ddd7d14ec5/screenshots/screenshot1.png -------------------------------------------------------------------------------- /src/app-push-pin.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static Window *s_main_window; 4 | static TextLayer *s_text_layer; 5 | 6 | static void window_load(Window *window) { 7 | Layer *window_layer = window_get_root_layer(window); 8 | GRect bounds = layer_get_bounds(window_layer); 9 | 10 | const GEdgeInsets text_insets = {.top = 10}; 11 | s_text_layer = text_layer_create(grect_inset(bounds, text_insets)); 12 | text_layer_set_text_alignment(s_text_layer, GTextAlignmentCenter); 13 | text_layer_set_overflow_mode(s_text_layer, GTextOverflowModeWordWrap); 14 | text_layer_set_font(s_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); 15 | text_layer_set_text_color(s_text_layer, GColorWhite); 16 | text_layer_set_background_color(s_text_layer, GColorClear); 17 | text_layer_set_text(s_text_layer, "See the app logs to check the insert success.\n\n Check the timeline after ~15s to see the pin!"); 18 | layer_add_child(window_layer, text_layer_get_layer(s_text_layer)); 19 | 20 | #if defined(PBL_ROUND) 21 | text_layer_enable_screen_text_flow_and_paging(s_text_layer, 3); 22 | #endif 23 | } 24 | 25 | static void window_unload(Window *window) { 26 | text_layer_destroy(s_text_layer); 27 | } 28 | 29 | static void init() { 30 | s_main_window = window_create(); 31 | window_set_background_color(s_main_window, GColorFolly); 32 | window_set_window_handlers(s_main_window, (WindowHandlers) { 33 | .load = window_load, 34 | .unload = window_unload, 35 | }); 36 | window_stack_push(s_main_window, true); 37 | } 38 | 39 | static void deinit() { 40 | window_destroy(s_main_window); 41 | } 42 | 43 | int main() { 44 | init(); 45 | app_event_loop(); 46 | deinit(); 47 | } 48 | -------------------------------------------------------------------------------- /src/js/app.js: -------------------------------------------------------------------------------- 1 | var timeline = require('./timeline'); 2 | 3 | var PIN_ID = "timeline-push-pin-test"; 4 | 5 | Pebble.addEventListener('ready', function() { 6 | console.log('PebbleKit JS ready!'); 7 | 8 | // An hour ahead 9 | var date = new Date(); 10 | date.setHours(date.getHours() + 1); 11 | 12 | // Create the pin 13 | var pin = { 14 | "id": "pin-" + PIN_ID, 15 | "time": date.toISOString(), 16 | "layout": { 17 | "type": "genericPin", 18 | "title": "Example Pin", 19 | "body": "This is an example pin from the timeline-push-pin example app!", 20 | "tinyIcon": "system://images/SCHEDULED_EVENT" 21 | } 22 | }; 23 | 24 | console.log('Inserting pin in the future: ' + JSON.stringify(pin)); 25 | 26 | timeline.insertUserPin(pin, function(responseText) { 27 | console.log('Result: ' + responseText); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /src/js/timeline.js: -------------------------------------------------------------------------------- 1 | // The timeline public URL root 2 | var API_URL_ROOT = 'https://timeline-api.getpebble.com/'; 3 | 4 | /** 5 | * Send a request to the Pebble public web timeline API. 6 | * @param pin The JSON pin to insert. Must contain 'id' field. 7 | * @param type The type of request, either PUT or DELETE. 8 | * @param topics Array of topics if a shared pin, 'null' otherwise. 9 | * @param apiKey Timeline API key for this app, available from dev-portal.getpebble.com 10 | * @param callback The callback to receive the responseText after the request has completed. 11 | */ 12 | function timelineRequest(pin, type, topics, apiKey, callback) { 13 | // User or shared? 14 | var url = API_URL_ROOT + 'v1/' + ((topics != null) ? 'shared/' : 'user/') + 'pins/' + pin.id; 15 | 16 | // Create XHR 17 | var xhr = new XMLHttpRequest(); 18 | xhr.onload = function () { 19 | console.log('timeline: response received: ' + this.responseText); 20 | callback(this.responseText); 21 | }; 22 | xhr.open(type, url); 23 | 24 | // Set headers 25 | xhr.setRequestHeader('Content-Type', 'application/json'); 26 | if(topics != null) { 27 | xhr.setRequestHeader('X-Pin-Topics', '' + topics.join(',')); 28 | xhr.setRequestHeader('X-API-Key', '' + apiKey); 29 | } 30 | 31 | // Get token 32 | Pebble.getTimelineToken(function(token) { 33 | // Add headers 34 | xhr.setRequestHeader('X-User-Token', '' + token); 35 | 36 | // Send 37 | xhr.send(JSON.stringify(pin)); 38 | console.log('timeline: request sent.'); 39 | }, function(error) { console.log('timeline: error getting timeline token: ' + error); }); 40 | } 41 | 42 | /** 43 | * Insert a pin into the timeline for this user. 44 | * @param pin The JSON pin to insert. 45 | * @param callback The callback to receive the responseText after the request has completed. 46 | */ 47 | function insertUserPin(pin, callback) { 48 | timelineRequest(pin, 'PUT', null, null, callback); 49 | } 50 | 51 | /** 52 | * Delete a pin from the timeline for this user. 53 | * @param pin The JSON pin to delete. 54 | * @param callback The callback to receive the responseText after the request has completed. 55 | */ 56 | function deleteUserPin(pin, callback) { 57 | timelineRequest(pin, 'DELETE', null, null, callback); 58 | } 59 | 60 | /** 61 | * Insert a pin into the timeline for these topics. 62 | * @param pin The JSON pin to insert. 63 | * @param topics Array of topics to insert pin to. 64 | * @param apiKey Timeline API key for this app, available from dev-portal.getpebble.com 65 | * @param callback The callback to receive the responseText after the request has completed. 66 | */ 67 | function insertSharedPin(pin, topics, apiKey, callback) { 68 | timelineRequest(pin, 'PUT', topics, apiKey, callback); 69 | } 70 | 71 | /** 72 | * Delete a pin from the timeline for these topics. 73 | * @param pin The JSON pin to delete. 74 | * @param topics Array of topics to delete pin from. 75 | * @param apiKey Timeline API key for this app, available from dev-portal.getpebble.com 76 | * @param callback The callback to receive the responseText after the request has completed. 77 | */ 78 | function deleteSharedPin(pin, topics, apiKey, callback) { 79 | timelineRequest(pin, 'DELETE', topics, apiKey, callback); 80 | } 81 | 82 | // Export 83 | module.exports.insertUserPin = insertUserPin; 84 | module.exports.deleteUserPin = deleteUserPin; 85 | module.exports.insertSharedPin = insertSharedPin; 86 | module.exports.deleteSharedPin = deleteSharedPin; 87 | -------------------------------------------------------------------------------- /wscript: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # This file is the default set of rules to compile a Pebble project. 4 | # 5 | # Feel free to customize this to your needs. 6 | # 7 | 8 | import os.path 9 | 10 | top = '.' 11 | out = 'build' 12 | 13 | def options(ctx): 14 | ctx.load('pebble_sdk') 15 | 16 | def configure(ctx): 17 | ctx.load('pebble_sdk') 18 | 19 | def build(ctx): 20 | ctx.load('pebble_sdk') 21 | 22 | build_worker = os.path.exists('worker_src') 23 | binaries = [] 24 | 25 | for p in ctx.env.TARGET_PLATFORMS: 26 | ctx.set_env(ctx.all_envs[p]) 27 | ctx.set_group(ctx.env.PLATFORM_NAME) 28 | app_elf='{}/pebble-app.elf'.format(ctx.env.BUILD_DIR) 29 | ctx.pbl_program(source=ctx.path.ant_glob('src/**/*.c'), 30 | target=app_elf) 31 | 32 | if build_worker: 33 | worker_elf='{}/pebble-worker.elf'.format(ctx.env.BUILD_DIR) 34 | binaries.append({'platform': p, 'app_elf': app_elf, 'worker_elf': worker_elf}) 35 | ctx.pbl_worker(source=ctx.path.ant_glob('worker_src/**/*.c'), 36 | target=worker_elf) 37 | else: 38 | binaries.append({'platform': p, 'app_elf': app_elf}) 39 | 40 | ctx.set_group('bundle') 41 | ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob('src/js/**/*.js')) 42 | --------------------------------------------------------------------------------