├── LICENSE ├── README.md ├── app └── index.js ├── common └── utils.js ├── companion └── index.js ├── package.json ├── resources ├── index.gui ├── styles.css └── widgets.gui ├── screenshot.png └── settings └── index.jsx /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 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 Photo Picker 2 | 3 | Fitbit SDK example clock face which demonstrates the ImagePicker component from 4 | the Settings API. 5 | 6 | ![Screenshot](screenshot.png) 7 | 8 | Find out more information on the 9 | [Fitbit Developer Website](https://dev.fitbit.com). 10 | 11 | ## License 12 | 13 | This example is licensed under the [MIT License](./LICENSE). -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | import { me } from "appbit"; 2 | import clock from "clock"; 3 | import { display } from "display"; 4 | import document from "document"; 5 | import * as fs from "fs"; 6 | import { inbox } from "file-transfer"; 7 | import { preferences } from "user-settings"; 8 | 9 | import * as util from "../common/utils"; 10 | 11 | const SETTINGS_FILE = "settings.cbor"; 12 | const SETTINGS_TYPE = "cbor"; 13 | 14 | const labelTime = document.getElementById("labelTime"); 15 | const labelTimeShadow = document.getElementById("labelTimeShadow"); 16 | const imageBackground = document.getElementById("imageBackground"); 17 | 18 | let mySettings; 19 | loadSettings(); 20 | me.onunload = saveSettings; 21 | 22 | clock.granularity = "minutes"; 23 | 24 | clock.ontick = evt => { 25 | let today = evt.date; 26 | let hours = today.getHours(); 27 | if (preferences.clockDisplay === "12h") { 28 | hours = hours % 12 || 12; 29 | } else { 30 | hours = util.zeroPad(hours); 31 | } 32 | let mins = util.zeroPad(today.getMinutes()); 33 | let timeString = `${hours}:${mins}`; 34 | labelTime.text = timeString; 35 | labelTimeShadow.text = timeString; 36 | }; 37 | 38 | inbox.onnewfile = () => { 39 | let fileName; 40 | do { 41 | fileName = inbox.nextFile(); 42 | if (fileName) { 43 | if (mySettings.bg && mySettings.bg !== "") { 44 | fs.unlinkSync(mySettings.bg); 45 | } 46 | mySettings.bg = `/private/data/${fileName}`; 47 | applySettings(); 48 | } 49 | } while (fileName); 50 | }; 51 | 52 | function loadSettings() { 53 | try { 54 | mySettings = fs.readFileSync(SETTINGS_FILE, SETTINGS_TYPE); 55 | } catch (ex) { 56 | mySettings = {}; 57 | } 58 | applySettings(); 59 | } 60 | 61 | function saveSettings() { 62 | fs.writeFileSync(SETTINGS_FILE, mySettings, SETTINGS_TYPE); 63 | } 64 | 65 | function applySettings() { 66 | if (mySettings.bg) { 67 | imageBackground.image = mySettings.bg; 68 | } 69 | display.on = true; 70 | } 71 | -------------------------------------------------------------------------------- /common/utils.js: -------------------------------------------------------------------------------- 1 | // Add zero in front of numbers < 10 2 | export function zeroPad(i) { 3 | if (i < 10) { 4 | i = "0" + i; 5 | } 6 | return i; 7 | } 8 | -------------------------------------------------------------------------------- /companion/index.js: -------------------------------------------------------------------------------- 1 | import { outbox } from "file-transfer"; 2 | import { Image } from "image"; 3 | import { device } from "peer"; 4 | import { settingsStorage } from "settings"; 5 | 6 | settingsStorage.setItem("screenWidth", device.screen.width); 7 | settingsStorage.setItem("screenHeight", device.screen.height); 8 | 9 | settingsStorage.onchange = function(evt) { 10 | if (evt.key === "background-image") { 11 | compressAndTransferImage(evt.newValue); 12 | } 13 | }; 14 | 15 | function compressAndTransferImage(settingsValue) { 16 | const imageData = JSON.parse(settingsValue); 17 | Image.from(imageData.imageUri) 18 | .then(image => 19 | image.export("image/jpeg", { 20 | background: "#FFFFFF", 21 | quality: 40 22 | }) 23 | ) 24 | .then(buffer => outbox.enqueue(`${Date.now()}.jpg`, buffer)) 25 | .then(fileTransfer => { 26 | console.log(`Enqueued ${fileTransfer.name}`); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "fitbit": { 3 | "appType": "clockface", 4 | "appDisplayName": "sdk-photo-picker", 5 | "iconFile": "resources/icon.png", 6 | "wipeColor": "", 7 | "requestedPermissions": [], 8 | "buildTargets": [ 9 | "higgs", 10 | "meson" 11 | ], 12 | "i18n": { 13 | "en": { 14 | "name": "sdk-photo-picker" 15 | } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /resources/index.gui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /resources/styles.css: -------------------------------------------------------------------------------- 1 | .background { 2 | viewport-fill: #444444; 3 | } 4 | 5 | .time { 6 | font-size: 60; 7 | font-family: Colfax-Black; 8 | text-length: 32; 9 | text-anchor: end; 10 | x: 100%-8; 11 | y: 50; 12 | fill: white; 13 | } 14 | 15 | .time-shadow { 16 | x: 100%-6; 17 | y: 52; 18 | fill: black; 19 | } 20 | -------------------------------------------------------------------------------- /resources/widgets.gui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fitbit/sdk-photo-picker/4b75d68c06e25e0431b5282ff6a49f6da2512cf4/screenshot.png -------------------------------------------------------------------------------- /settings/index.jsx: -------------------------------------------------------------------------------- 1 | function mySettings(props) { 2 | let screenWidth = props.settingsStorage.getItem("screenWidth"); 3 | let screenHeight = props.settingsStorage.getItem("screenHeight"); 4 | 5 | return ( 6 | 7 | 16 | 17 | ); 18 | } 19 | 20 | registerSettingsPage(mySettings); 21 | --------------------------------------------------------------------------------