├── .gitignore ├── CONTRIBUTING.txt ├── DCO.txt ├── LICENCE ├── README.md ├── app ├── index.js ├── views.js └── views │ ├── view-1.js │ ├── view-2.js │ └── view-3.js ├── package.json ├── resources ├── index.gui ├── styles.css ├── views │ ├── view-1.gui │ ├── view-2.gui │ └── view-3.gui └── widgets.gui └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | node_modules 4 | package-lock.json 5 | yarn.lock -------------------------------------------------------------------------------- /CONTRIBUTING.txt: -------------------------------------------------------------------------------- 1 | The current version of our contributing guide can be found at https://dev.fitbit.com/community/contributing/. 2 | -------------------------------------------------------------------------------- /DCO.txt: -------------------------------------------------------------------------------- 1 | Developer Certificate of Origin 2 | Version 1.1 3 | 4 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 5 | 1 Letterman Drive 6 | Suite D4700 7 | San Francisco, CA, 94129 8 | 9 | Everyone is permitted to copy and distribute verbatim copies of this 10 | license document, but changing it is not allowed. 11 | 12 | Developer's Certificate of Origin 1.1 13 | 14 | By making a contribution to this project, I certify that: 15 | 16 | (a) The contribution was created in whole or in part by me and I 17 | have the right to submit it under the open source license 18 | indicated in the file; or 19 | 20 | (b) The contribution is based upon previous work that, to the best 21 | of my knowledge, is covered under an appropriate open source 22 | license and I have the right under that license to submit that 23 | work with modifications, whether created in whole or in part 24 | by me, under the same open source license (unless I am 25 | permitted to submit under a different license), as indicated 26 | in the file; or 27 | 28 | (c) The contribution was provided directly to me by some other 29 | person who certified (a), (b) or (c) and I have not modified 30 | it. 31 | 32 | (d) I understand and agree that this project and the contribution 33 | are public and that a record of the contribution (including all 34 | personal information I submit with it, including my sign-off) is 35 | maintained indefinitely and may be redistributed consistent with 36 | this project or the open source license(s) involved. 37 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Fitbit, Inc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sdk-multi-view 2 | 3 | SDK example application which demonstrates the usage of multi-view SVG that was 4 | added in Fitbit OS 4.0. 5 | 6 | ## Structure 7 | 8 | `/resources/views/` contains an SVG file for each view in the application. 9 | `/app/views/` contains a JavaScript file for each view in the application. 10 | `/app/index.js` initializes the list of views. 11 | 12 | When `views.navigate()` is called, the current view is unloaded and all event 13 | handlers are unregistered. The JavaScript for the selected view is dynamically 14 | loaded, and the document for the selected view is loaded. 15 | 16 | The `BACK` button can be used to navigate to the previous view. 17 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | import { init } from "./views"; 2 | 3 | /** 4 | * Definition for each view in the resources/views folder, and the associated 5 | * JavaScript module is lazily loaded alongside its view. 6 | */ 7 | const views = init( 8 | [ 9 | ["view-1", () => import("./views/view-1")], 10 | ["view-2", () => import("./views/view-2")], 11 | ["view-3", () => import("./views/view-3")] 12 | ], 13 | "./resources/views/" 14 | ); 15 | 16 | // Select the first view (view-1) after 1 second 17 | setTimeout(() => { 18 | views.navigate("view-1"); 19 | }, 1000); 20 | -------------------------------------------------------------------------------- /app/views.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A basic module to simplify navigation within multi-view applications. 3 | */ 4 | import document from "document"; 5 | 6 | /** 7 | * Initialize the views module with views from a specific folder. 8 | * @param {Object[]} _views An array of views containing the view filename excluding 9 | * its file extension, and an associated JavaScript `import()`. 10 | * @param {string} _prefix The folder name where the view files reside. 11 | */ 12 | export function init(_views, _prefix) { 13 | let views = _views; 14 | let viewsPrefix = _prefix; 15 | const viewsSuffix = ".gui"; 16 | let viewSelected; 17 | 18 | /** 19 | * Select a specific view by its index. The view's associated JavaScript is 20 | * loaded and executed, and the current view is replaced by the selected one. 21 | * @param {number} _index The array position of the view to be selected. 22 | */ 23 | const select = _index => { 24 | const [viewGUI, viewJSLoader] = views[_index]; 25 | viewSelected = viewGUI; 26 | viewJSLoader() 27 | .then(({ init }) => { 28 | document.replaceSync(`${viewsPrefix}${viewGUI}${viewsSuffix}`); 29 | init({ navigate }); 30 | }) 31 | .catch(() => { 32 | console.error(`Failed to load view JS: ${viewGUI}`); 33 | }); 34 | }; 35 | 36 | /** 37 | * Navigate to a specific view using its view name. 38 | * @param {string} _viewName The name of a .gui file, excluding its path or 39 | * file extension. 40 | */ 41 | const navigate = _viewName => { 42 | const index = views.indexOf(views.filter(el => el[0] == _viewName)[0]); 43 | select(index); 44 | }; 45 | 46 | return { 47 | navigate, 48 | viewSelected: () => viewSelected 49 | }; 50 | } 51 | -------------------------------------------------------------------------------- /app/views/view-1.js: -------------------------------------------------------------------------------- 1 | import document from "document"; 2 | 3 | let views; 4 | 5 | export function init(_views) { 6 | views = _views; 7 | console.log("view-1 init()"); 8 | onMount(); 9 | } 10 | 11 | /** 12 | * When this view is mounted, setup elements and events. 13 | */ 14 | function onMount() { 15 | let btn = document.getElementById("v1-button"); 16 | btn.addEventListener("click", clickHandler); 17 | } 18 | 19 | /** 20 | * Sample button click with navigation. 21 | */ 22 | function clickHandler(_evt) { 23 | console.log("view-1 Button Clicked!"); 24 | /* Navigate to another screen */ 25 | views.navigate("view-2"); 26 | } 27 | -------------------------------------------------------------------------------- /app/views/view-2.js: -------------------------------------------------------------------------------- 1 | import document from "document"; 2 | 3 | let views; 4 | 5 | export function init(_views) { 6 | views = _views; 7 | console.log("view-2 init()"); 8 | onMount(); 9 | } 10 | 11 | /** 12 | * When this view is mounted, setup elements and events. 13 | */ 14 | function onMount() { 15 | let btn = document.getElementById("v2-button"); 16 | btn.addEventListener("click", clickHandler); 17 | document.addEventListener("keypress", keyHandler); 18 | } 19 | 20 | /** 21 | * Sample button click with navigation. 22 | */ 23 | function clickHandler(_evt) { 24 | console.log("view-2 Button Clicked!"); 25 | /* Navigate to another screen */ 26 | views.navigate("view-3"); 27 | } 28 | 29 | /** 30 | * Sample keypress handler to navigate backwards. 31 | */ 32 | function keyHandler(evt) { 33 | if (evt.key === "back") { 34 | evt.preventDefault(); 35 | views.navigate("view-1"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/views/view-3.js: -------------------------------------------------------------------------------- 1 | import document from "document"; 2 | 3 | let views; 4 | 5 | export function init(_views) { 6 | views = _views; 7 | console.log("view-3 init()"); 8 | onMount(); 9 | } 10 | 11 | /** 12 | * When this view is mounted, setup elements and events. 13 | */ 14 | function onMount() { 15 | let btn = document.getElementById("v3-button"); 16 | btn.addEventListener("click", clickHandler); 17 | document.addEventListener("keypress", keyHandler); 18 | } 19 | 20 | /** 21 | * Sample button click with navigation. 22 | */ 23 | function clickHandler(_evt) { 24 | console.log("view-3 Button Clicked!"); 25 | /* Navigate to another screen */ 26 | views.navigate("view-1"); 27 | } 28 | 29 | /** 30 | * Sample keypress handler to navigate backwards. 31 | */ 32 | function keyHandler(evt) { 33 | if (evt.key === "back") { 34 | evt.preventDefault(); 35 | views.navigate("view-2"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sdk-multi-view", 3 | "version": "0.1.0", 4 | "private": true, 5 | "license": "UNLICENSED", 6 | "devDependencies": { 7 | "@fitbit/sdk": "~4.0.0", 8 | "@fitbit/sdk-cli": "^1.7.0" 9 | }, 10 | "fitbit": { 11 | "appUUID": "3bcad842-5fb8-4540-9bc1-ec9f6f0467fb", 12 | "appType": "app", 13 | "appDisplayName": "SDK MultiView", 14 | "iconFile": "resources/icon.png", 15 | "wipeColor": "#ffffff", 16 | "requestedPermissions": [], 17 | "buildTargets": [ 18 | "higgs", 19 | "meson", 20 | "gemini", 21 | "mira" 22 | ], 23 | "i18n": {}, 24 | "defaultLanguage": "en-US" 25 | }, 26 | "scripts": { 27 | "build": "fitbit-build", 28 | "debug": "fitbit" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /resources/index.gui: -------------------------------------------------------------------------------- 1 | 2 | Loading 6 | 7 | -------------------------------------------------------------------------------- /resources/styles.css: -------------------------------------------------------------------------------- 1 | .title { 2 | x: 50%; 3 | y: 50%+15; 4 | fill: fb-yellow; 5 | font-family: System-Regular; 6 | font-size: 40; 7 | font-weight: bold; 8 | text-anchor: middle; 9 | } 10 | 11 | .btn { 12 | y: 100%-60; 13 | height: 60; 14 | fill: fb-red; 15 | font-family: System-Regular; 16 | font-size: 40; 17 | } 18 | 19 | #text { 20 | y: 12; 21 | fill: white; 22 | } -------------------------------------------------------------------------------- /resources/views/view-1.gui: -------------------------------------------------------------------------------- 1 | 2 | This is view-1. 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/views/view-2.gui: -------------------------------------------------------------------------------- 1 | 2 | This is view-2. 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/views/view-3.gui: -------------------------------------------------------------------------------- 1 | 2 | This is view-3. 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/widgets.gui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/@fitbit/sdk/sdk-tsconfig.json" 3 | } 4 | --------------------------------------------------------------------------------