diff --git a/apps/messageicons/ChangeLog b/apps/messageicons/ChangeLog index ebe8e8e9b..4e8972f7a 100644 --- a/apps/messageicons/ChangeLog +++ b/apps/messageicons/ChangeLog @@ -4,4 +4,5 @@ Store all icons in a separate binary file (much faster lookup) 0.04: Add message icon for 'clock' 0.05: Add message icon for 'jira' -0.06: Minor code improvements +0.06: Add message icon for 'molly' and 'threema libre' +0.07: Minor code improvements diff --git a/apps/messageicons/icons/generate.js b/apps/messageicons/icons/generate.js index 990c4e618..4b52ae167 100755 --- a/apps/messageicons/icons/generate.js +++ b/apps/messageicons/icons/generate.js @@ -71,7 +71,9 @@ Promise.all(promises).then(function() { require("fs").writeFileSync(__dirname+"/../icons.img", Buffer.from(iconData,"binary")); console.log("Saving library"); - require("fs").writeFileSync(__dirname+"/../lib.js", `exports.getImage = function(msg) { + require("fs").writeFileSync(__dirname+"/../lib.js", `// This file is auto-generated - DO NOT MODIFY +// If you want to add icons, change icons/icon_names.json and re-run icons/generate.js +exports.getImage = function(msg) { if (msg.img) return atob(msg.img); let s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase(); if (msg.id=="music") s="music"; @@ -86,16 +88,15 @@ exports.getColor = function(msg,options) { if (st.iconColorMode == 'mono') return options.default; const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase(); return { - // This file is generated by /icons/generate.js. If you need to modify its content, you should do it there instead. - // generic colors, using B2-safe colors - // DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used + /* generic colors, using B2-safe colors */ ${ /* DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used */"" } "airbnb": "#ff385c", // https://news.airbnb.com/media-assets/category/brand/ "mail": "#ff0", "music": "#f0f", "phone": "#0f0", - "sms message": "#0ff", - // brands, according to https://www.schemecolor.com/?s (picking one for multicolored logos) - // all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?) + "sms message": "#0ff", ${ /* + brands, according to https://www.schemecolor.com/?s (picking one for multicolored logos) + all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?) +*/""} "bibel": "#54342c", "bring": "#455a64", "discord": "#5865f2", // https://discord.com/branding diff --git a/apps/messageicons/icons/icon_names.json b/apps/messageicons/icons/icon_names.json index 7c09cd397..de8be9e98 100644 --- a/apps/messageicons/icons/icon_names.json +++ b/apps/messageicons/icons/icon_names.json @@ -80,6 +80,7 @@ { "app":"infinity", "icon":"reddit.png" }, { "app":"slide", "icon":"reddit.png" }, { "app":"signal", "icon":"signal.png" }, + { "app":"molly", "icon":"signal.png" }, { "app":"skype", "icon":"skype.png" }, { "app":"slack", "icon":"slack.png" }, { "app":"snapchat", "icon":"snapchat.png" }, @@ -89,6 +90,7 @@ { "app":"telegram", "icon":"telegram.png" }, { "app":"telegram foss", "icon":"telegram.png" }, { "app":"threema", "icon":"threema.png" }, + { "app":"threema libre", "icon":"threema.png" }, { "app":"tiktok", "icon":"tiktok.png" }, { "app":"to do", "icon":"task.png" }, { "app":"opentasks", "icon":"task.png" }, diff --git a/apps/messageicons/lib.js b/apps/messageicons/lib.js index 306a4ae46..b3ff2d9ff 100644 --- a/apps/messageicons/lib.js +++ b/apps/messageicons/lib.js @@ -1,3 +1,5 @@ +// This file is auto-generated - DO NOT MODIFY +// If you want to add icons, change icons/icon_names.json and re-run icons/generate.js exports.getImage = function(msg) { if (msg.img) return atob(msg.img); let s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase(); @@ -13,16 +15,12 @@ exports.getColor = function(msg,options) { if (st.iconColorMode == 'mono') return options.default; const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase(); return { - // This file is generated by /icons/generate.js. If you need to modify its content, you should do it there instead. - // generic colors, using B2-safe colors - // DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used + /* generic colors, using B2-safe colors */ "airbnb": "#ff385c", // https://news.airbnb.com/media-assets/category/brand/ "mail": "#ff0", "music": "#f0f", "phone": "#0f0", - "sms message": "#0ff", - // brands, according to https://www.schemecolor.com/?s (picking one for multicolored logos) - // all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?) + "sms message": "#0ff", "bibel": "#54342c", "bring": "#455a64", "discord": "#5865f2", // https://discord.com/branding @@ -67,4 +65,4 @@ exports.getColor = function(msg,options) { "youtube": "#f00", // https://www.youtube.com/howyoutubeworks/resources/brand-resources/#logos-icons-and-colors }[s]||options.default; }; - + \ No newline at end of file diff --git a/apps/regattatimer/ChangeLog b/apps/regattatimer/ChangeLog new file mode 100644 index 000000000..aea920eea --- /dev/null +++ b/apps/regattatimer/ChangeLog @@ -0,0 +1,3 @@ +0.1: (2024-02-23) initial alpha upload +0.2: (2024-02-23) fixed minor issues with settings +0.3: (2024-03-01) advanced settings, rearanged ui elements, fixed rendering problems diff --git a/apps/regattatimer/README.md b/apps/regattatimer/README.md new file mode 100644 index 000000000..8d906e1d7 --- /dev/null +++ b/apps/regattatimer/README.md @@ -0,0 +1,74 @@ +# Regatta Timer 5-4-1 countdown + +## Modes + +* **Idle** + On startup the application is in idle mode showing a large 5 in the centre of the screen and the time of day below. + `Button` switches to start mode. +* **Start** + During the countdown, the screen changes the layout several times to use as much space as + possible to display the numbers. + When time is up the buzzer sounds and the application switches to race mode. + `Button` switches to idle mode. +* **Race** + Race time, local time, SOA, number reachable GPS satellites and battery level are shown. + `Button` switches to "stopped mode". +* **Stoped** + The race counter stops. + `Button` switches to idle mode. + +## Screenshots + +*Idle mode: showing a big 5 and time of day below* + +![Idle mode: showing a big 5 and time of day below](screenshot-1.png) + +*Start mode: minutes and seconds* + +![Start mode: minutes and seconds](screenshot-2.png) + +*Start mode: seconds* + +![Start mode: seconds](screenshot-3.png) + +*Race mode: elapsed time, time of day, speed, satellites, battery* + +![Race mode: elapsed time, time of day, speed, satellites, battery](screenshot-4.png) + +*Race mode: with german abbreviations* + +![Race mode: with german abbreviations](screenshot-5.png) + +*Settings page: main* + +![Settings page: main](screenshot-6.png) + +*Settings page: choose the theme* + +![Settings page: choose the theme](screenshot-7.png) + +## Localization + +Localization is done by the Bangle.js 2 app "Languages" +* Go to [banglejs.com/apps](https://banglejs.com/apps/) +* Search for app "Languages" +* Click the "arrow up" or "burger" icon +* Choose your language from the dropdown +* Click `upload` + +**Some nautical abbreviations which are not part of the Bangle.js 2 app "Languages" app are stored in `translations.json`.** + +## Feedback + +Report bugs or request a feature at [github.com/naden](https://github.com/naden) + +## Roadmap +* add a seconds coundown layout; mimic a classic regatta chronograph +* add recording of gps course and race time +* add icons for light mode +* add flag icons + +## Created by +© 2021 - 2024 [naden.de](https://naden.de) + +Icons by [Icons8](https://icons8.com/) diff --git a/apps/regattatimer/app-icon.js b/apps/regattatimer/app-icon.js new file mode 100644 index 000000000..71417f0cd --- /dev/null +++ b/apps/regattatimer/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4f/8H/A4NgtfgzkgoVg50g40Awg5lnmSpMk4ARMkMkCQNgCJkJCKM////0AQLgNJkBZP5OACB0B5OKjJqNgGJLINOEZprDCJ0rmVJoARMzfAg1JLh8AQCIRNGQPghg1NhQgBghZOhMmoR9ClmSqDXJR4NLAwMhknQEhEsCINwboQRKgG59RuDCJYADLgIRPqVnCJ9CuYROgOSwYjPyUDCJ0IzwRP4mQCIsRlARH8mZWYPJbgK/BCJOSR4OTCIMGCJ8MAoIRIi3btUk3UACJYABLIcapMJCJxZLCKbMFCP4R/COQAo")) diff --git a/apps/regattatimer/app.js b/apps/regattatimer/app.js new file mode 100644 index 000000000..30d03daed --- /dev/null +++ b/apps/regattatimer/app.js @@ -0,0 +1,335 @@ +/** + * Regatta Timer + */ +const Layout = require("Layout"); +const locale = require("locale").name == "system" ? "en" : require("locale").name.substring(0, 2); +const hs = require("heatshrink"); + +// "Anton" bold font +Graphics.prototype.setFontAnton = function(scale) { + // Actual height 69 (68 - 0) + g.setFontCustom(atob("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgAAAAAAAAAAA/gAAAAAAAAAAP/gAAAAAAAAAH//gAAAAAAAAB///gAAAAAAAAf///gAAAAAAAP////gAAAAAAD/////gAAAAAA//////gAAAAAP//////gAAAAH///////gAAAB////////gAAAf////////gAAP/////////gAD//////////AA//////////gAA/////////4AAA////////+AAAA////////gAAAA///////wAAAAA//////8AAAAAA//////AAAAAAA/////gAAAAAAA////4AAAAAAAA///+AAAAAAAAA///gAAAAAAAAA//wAAAAAAAAAA/8AAAAAAAAAAA/AAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////AAAAAB///////8AAAAH////////AAAAf////////wAAA/////////4AAB/////////8AAD/////////+AAH//////////AAP//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wA//8AAAAAB//4A//wAAAAAAf/4A//gAAAAAAP/4A//gAAAAAAP/4A//gAAAAAAP/4A//wAAAAAAf/4A///////////4Af//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH//////////AAD/////////+AAB/////////8AAA/////////4AAAP////////gAAAD///////+AAAAAf//////4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/gAAAAAAAAAAP/gAAAAAAAAAAf/gAAAAAAAAAAf/gAAAAAAAAAAf/AAAAAAAAAAA//AAAAAAAAAAA/+AAAAAAAAAAB/8AAAAAAAAAAD//////////gAH//////////gAP//////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH/4AAAAB/gAAD//4AAAAf/gAAP//4AAAB//gAA///4AAAH//gAB///4AAAf//gAD///4AAA///gAH///4AAD///gAP///4AAH///gAP///4AAP///gAf///4AAf///gAf///4AB////gAf///4AD////gA////4AH////gA////4Af////gA////4A/////gA//wAAB/////gA//gAAH/////gA//gAAP/////gA//gAA///8//gA//gAD///w//gA//wA////g//gA////////A//gA///////8A//gA///////4A//gAf//////wA//gAf//////gA//gAf/////+AA//gAP/////8AA//gAP/////4AA//gAH/////gAA//gAD/////AAA//gAB////8AAA//gAA////wAAA//gAAP///AAAA//gAAD//8AAAA//gAAAP+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB/+AAAAAD/wAAB//8AAAAP/wAAB///AAAA//wAAB///wAAB//wAAB///4AAD//wAAB///8AAH//wAAB///+AAP//wAAB///+AAP//wAAB////AAf//wAAB////AAf//wAAB////gAf//wAAB////gA///wAAB////gA///wAAB////gA///w//AAf//wA//4A//AAA//wA//gA//AAAf/wA//gB//gAAf/wA//gB//gAAf/wA//gD//wAA//wA//wH//8AB//wA///////////gA///////////gA///////////gA///////////gAf//////////AAf//////////AAP//////////AAP/////////+AAH/////////8AAH///+/////4AAD///+f////wAAA///8P////gAAAf//4H///+AAAAH//gB///wAAAAAP4AAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/wAAAAAAAAAA//wAAAAAAAAAP//wAAAAAAAAB///wAAAAAAAAf///wAAAAAAAH////wAAAAAAA/////wAAAAAAP/////wAAAAAB//////wAAAAAf//////wAAAAH///////wAAAA////////wAAAP////////wAAA///////H/wAAA//////wH/wAAA/////8AH/wAAA/////AAH/wAAA////gAAH/wAAA///4AAAH/wAAA//+AAAAH/wAAA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gA///////////gAAAAAAAAH/4AAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAH/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//8AAA/////+B///AAA/////+B///wAA/////+B///4AA/////+B///8AA/////+B///8AA/////+B///+AA/////+B////AA/////+B////AA/////+B////AA/////+B////gA/////+B////gA/////+B////gA/////+A////gA//gP/gAAB//wA//gf/AAAA//wA//gf/AAAAf/wA//g//AAAAf/wA//g//AAAA//wA//g//gAAA//wA//g//+AAP//wA//g////////gA//g////////gA//g////////gA//g////////gA//g////////AA//gf///////AA//gf//////+AA//gP//////+AA//gH//////8AA//gD//////4AA//gB//////wAA//gA//////AAAAAAAH////8AAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//////gAAAAB///////+AAAAH////////gAAAf////////4AAB/////////8AAD/////////+AAH//////////AAH//////////gAP//////////gAP//////////gAf//////////wAf//////////wAf//////////wAf//////////wAf//////////4A//wAD/4AAf/4A//gAH/wAAP/4A//gAH/wAAP/4A//gAP/wAAP/4A//gAP/4AAf/4A//wAP/+AD//4A///wP//////4Af//4P//////wAf//4P//////wAf//4P//////wAf//4P//////wAP//4P//////gAP//4H//////gAH//4H//////AAH//4D/////+AAD//4D/////8AAB//4B/////4AAA//4A/////wAAAP/4AP////AAAAB/4AD///4AAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAAAAA//gAAAAAAADgA//gAAAAAAP/gA//gAAAAAH//gA//gAAAAB///gA//gAAAAP///gA//gAAAD////gA//gAAAf////gA//gAAB/////gA//gAAP/////gA//gAB//////gA//gAH//////gA//gA///////gA//gD///////gA//gf///////gA//h////////gA//n////////gA//////////gAA/////////AAAA////////wAAAA///////4AAAAA///////AAAAAA//////4AAAAAA//////AAAAAAA/////4AAAAAAA/////AAAAAAAA////8AAAAAAAA////gAAAAAAAA///+AAAAAAAAA///4AAAAAAAAA///AAAAAAAAAA//4AAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD//gB///wAAAAP//4H///+AAAA///8P////gAAB///+f////4AAD///+/////8AAH/////////+AAH//////////AAP//////////gAP//////////gAf//////////gAf//////////wAf//////////wAf//////////wA///////////wA//4D//wAB//4A//wB//gAA//4A//gA//gAAf/4A//gA//AAAf/4A//gA//gAAf/4A//wB//gAA//4A///P//8AH//4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////gAP//////////gAP//////////AAH//////////AAD/////////+AAD///+/////8AAB///8f////wAAAf//4P////AAAAH//wD///8AAAAA/+AAf//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAB///+AA/+AAAAP////gA//wAAAf////wA//4AAB/////4A//8AAD/////8A//+AAD/////+A///AAH/////+A///AAP//////A///gAP//////A///gAf//////A///wAf//////A///wAf//////A///wAf//////A///wA///////AB//4A//4AD//AAP/4A//gAB//AAP/4A//gAA//AAP/4A//gAA/+AAP/4A//gAB/8AAP/4A//wAB/8AAf/4Af//////////wAf//////////wAf//////////wAf//////////wAf//////////wAP//////////gAP//////////gAH//////////AAH/////////+AAD/////////8AAB/////////4AAAf////////wAAAP////////AAAAB///////4AAAAAD/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/AAB/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAA//AAD/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="), 46, atob("EiAnGicnJycnJycnEw=="), 78 + (scale << 8) + (1 << 16)); +}; + +function Regattatimer() { + return { + layout: undefined, + /* + layouts: { + idle: function() { + switch(settings.dial) { + case "Discs": + break; + case "Numeric": + default: + break; + } + }, + start: function(phase) { + switch(settings.dial) { + case "Discs": + break; + case "Numeric": + default: + break; + } + }, + race: function() { + + } + }, + */ + mode: "idle", // idle, start, race" + countdown: 300, // 5 minutes + counter: undefined, + interval: undefined, + theme: null, + themes: { + "Light": { + "fgColor": "#000000", + "bgColor": "#FFFF00", + "icons": { + "satellites": function() { + return hs.decompress(atob("jEYxH+AH4Ab6QIIBJAfNAAQtSC4gxSCwgYHHBYYMC6IYPC5AZOC8QYMC5YYLC5inSDH4waVbAYJCpgA/AAI=")); + }, + "battery": function() { + return hs.decompress(atob("jEYxH+AHHSAAgXmCgoaRC/4X/C/4X/C/4X/C64Ap")); + } + } + }, + "Dark": { + "fgColor": "#FFFF00", + "bgColor": "#000000", + "icons": { + "satellites": function() { + return hs.decompress(atob("jEYxH+AH4Ab6QIIBJAfNAAQtSC4gxSCwgYHHBYYMC6IYPC5AZOC8QYMC5YYLC5inSDH4waVbAYJCpgA/AAI=")); + }, + "battery": function() { + return hs.decompress(atob("jEYxH+AHHSAAgXmCgoaRC/4X/C/4X/C/4X/C64Ap")); + } + } + } + }, + settings: Object.assign({ + "debug": false, + "buzzer": true, + "dial": "Numeric", + "gps": true, + "record": false, + "theme": "Dark", + }, require('Storage').readJSON("regattatimer.json", true) || {}), + + translations: Object.assign({ + "de": { + "speed": "FüG", // Fahrt über Grund + "speed_unit": "kn" + }, + "en": { + "speed": "SOA", // SOA speed of advance + "speed_unit": "kn" + } + }, require('Storage').readJSON("translations.json", true) || {}), + + init: function() { + + if(this.settings.debug) { + this.countdown = 1; + } + + this.theme = this.themes[this.settings.theme]; + + Bangle.setLCDPower(1); + Bangle.setLCDTimeout(0); + + // in "idle", "start" or "stoped" mode, a button click (re)starts the countdown + // in "race" mode, a button click stops the counter + var onButtonClick = (function(ev) { + switch(this.mode) { + case "idle": + this.resetCounter(); + this.mode = "start"; + this.setLayoutStartMinSec(); + this.startCounter(); + this.interval = setInterval((function() { + this.startCounter(); + }).bind(this), 1000); + break; + case "stoped": + case "start": + this.resetCounter(); + this.setLayoutIdle(); + break; + case "race": + this.raceCounterStop(); + break; + } + }).bind(this); + + setWatch(onButtonClick, BTN1, true); + + this.setLayoutIdle(); + }, + + onGPS: function(fix) { + if(this.mode == "race") { + if(fix.fix && isFinite(fix.speed)) { + this.layout.clear(layout.speed); + this.layout.speed.label = fix.speed.toFixed(2); + this.layout.render(this.layout.speed); + } + this.layout.satellites.label = fix.satellites; + } + }, + + translate: function(slug) { + return this.translations[locale][slug]; + }, + // during the start phase, the clock counts down 5 4 1 0 minutes + // a button click restarts the countdown + startCounter: function() { + + this.counter --; + + if(this.counter >= 0) { + var counterMinutes = parseInt(this.counter / 60); + + if(counterMinutes > 0) { + this.layout.minutes.label = counterMinutes; + // this.layout.seconds.label = "0".concat(this.counter - counterMinutes * 60).toString().slice(-2); + this.layout.seconds.label = this.padZeroLeft(this.counter - counterMinutes * 60); + this.layout.render(); + } + else { + this.setLayoutStartSec(); + this.layout.seconds.label = this.counter.toString(); + this.layout.render(); + } + // this keeps the watch LCD lit up + g.flip(); + } + // time is up + else { + this.raceCounterStart(); + } + }, + padZeroLeft: function(str) { + return str.toString().padStart(2, "0"); + }, + formatTime: function(time) { + var + minutes = parseInt(time / 60), + seconds = time - (minutes * 60); + + return this.padZeroLeft(parseInt(time / 3600)) + ":" + this.padZeroLeft(minutes) + ":" + this.padZeroLeft(seconds); + }, + raceCounter: function() { + + if(this.counter % 60 == 0) { + this.layout.clear(this.layout.battery); + this.layout.battery.label = E.getBattery() + "%"; + this.layout.render(this.layout.battery); + } + + this.counter ++; + + this.layout.racetime.label = this.formatTime(this.counter); + this.layout.daytime.label = require("locale").time(new Date(), 1); + this.layout.render(); + + // keeps the watch screen lit up + g.flip(); + }, + raceCounterStop: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + this.mode = "stoped"; + }, + raceCounterStart: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + + if(this.settings.buzzer) { + Bangle.buzz(); + } + + this.counter = 0; + // switch to race mode + this.mode = "race"; + this.setLayoutRace(); + this.raceCounter(); + this.interval = setInterval((function() { + this.raceCounter(); + }).bind(this), 1000); + }, + + resetCounter: function() { + if(this.interval) { + clearInterval(this.interval); + this.interval = undefined; + } + this.counter = this.countdown; + }, + + setLayoutIdle: function() { + + g.clear(); + + this.mode = "idle"; + + this.layout = new Layout({ + type: "v", + bgCol: this.theme.bgColor, + c: [ + { + type: "v", + c: [ + {type: "txt", font: "Anton", label: "5", col: this.theme.fgColor, id: "minutes", fillx: 1, filly: 1}, + {type: "txt", font: "20%", label: "--:--", col: this.theme.fgColor, id: "daytime", fillx: 1, filly: 1} + ] + } + ]}, {lazy: true}); + + this.interval = setInterval((function() { + this.layout.daytime.label = require("locale").time(new Date(), 1); + this.layout.render(); + + // keeps the watch screen lit up + g.flip(); + }).bind(this), 1000); + }, + setLayoutStartMinSec: function() { + g.clear(); + + this.layout = new Layout({ + type: "v", + bgCol: this.theme.bgColor, + c: [ + { + type: "h", + c: [ + {type: "txt", font: "Anton", label: "4", col: this.theme.fgColor, id: "minutes", fillx: 1, filly: 1}, + {type: "txt", font: "Anton", label: "59", col: this.theme.fgColor, id: "seconds", fillx: 1, filly: 1}, + ] + } + ]}, {lazy: true} + ); + }, + setLayoutStartSec: function() { + g.clear(); + + this.layout = new Layout({ + type: "v", + bgCol: this.theme.bgColor, + c:[ + {type: "txt", font: "Anton", label: "", fillx: true, filly: true, col: this.theme.fgColor, id: "seconds"}, + ]}, {lazy: true}); + }, + setLayoutRace: function() { + g.clear(); + + this.layout = new Layout({ + type: "v", + bgCol: this.theme.bgColor, + c: [ + {type: "txt", font: "20%", label: "00:00:00", col: this.theme.fgColor, pad: 4, filly: 1, fillx: 1, id: "racetime"}, + {type: "txt", font: "15%", label: "-", col: this.theme.fgColor, pad: 4, filly:1, fillx:1, id: "daytime"}, + // horizontal + {type: "h", c: [ + {type: "txt", font: "10%", label: this.translate("speed"), col: this.theme.fgColor, pad:4, fillx:1, filly:1}, + {type: "txt", font: "20%", label: "0", col: this.theme.fgColor, pad:4, fillx:1, filly:1, id: "speed"}, + {type: "txt", font: "10%", label: this.translate("speed_unit"), col: this.theme.fgColor, pad:4, fillx:1, filly:1}, + ]}, + {type: "h", c: [ + {type:"img", pad: 2, src: this.theme["icons"].satellites()}, + {type: "txt", font: "10%", label: "0", col: this.theme.fgColor, pad: 2, filly:1, id: "satellites"}, + // hacky, use empty element with fillx to push the other elments to the left an right side + {type: undefined, pad: 2, fillx: 1}, + {type:"img", pad: 2, src: this.theme["icons"].battery()}, + {type: "txt", font: "10%", label: "-", col: this.theme.fgColor, pad: 2, filly: 1, id: "battery"}, + ]} + ]}, {lazy: true}); + } + }; +} + +var regattatimer = Regattatimer(); +regattatimer.init(); + +if(regattatimer.settings.gps) { + Bangle.setGPSPower(1); + Bangle.on('GPS', regattatimer.onGPS.bind(regattatimer)); +} + +Bangle.on('kill', function() { + Bangle.setLCDPower(0); + Bangle.setLCDTimeout(10); + + if(regattatimer.settings.gps) { + Bangle.setGPSPower(0); + } +}); + diff --git a/apps/regattatimer/icon.png b/apps/regattatimer/icon.png new file mode 100644 index 000000000..5ae5f1381 Binary files /dev/null and b/apps/regattatimer/icon.png differ diff --git a/apps/regattatimer/metadata.json b/apps/regattatimer/metadata.json new file mode 100644 index 000000000..74e7e3bfb --- /dev/null +++ b/apps/regattatimer/metadata.json @@ -0,0 +1,18 @@ +{ + "id": "regattatimer", + "name": "Regatta Timer", + "shortName": "RegattaTimer", + "version": "0.3", + "description": "Regatta Timer with 5-4-1 Countdown", + "icon": "icon.png", + "tags": "tool,outdoors,sailing,race,regatta,boat,timer", + "supports": ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name": "regattatimer.app.js", "url": "app.js"}, + {"name": "regattatimer.settings.js", "url": "settings.js"}, + {"name": "regattatimer.img", "url": "app-icon.js", "evaluate": true} + ], + "data": [{"name": "regattatimer.json"}], + "screenshots": [{"url": "screenshot.png"},{"url": "screenshot-1.png"},{"url": "screenshot-2.png"},{"url": "screenshot-3.png"},{"url": "screenshot-4.png"},{"url": "screenshot-5.png"},{"url": "screenshot-6.png"},{"url": "screenshot-7.png"}] +} diff --git a/apps/regattatimer/regattatimer.json b/apps/regattatimer/regattatimer.json new file mode 100644 index 000000000..e2ea78f76 --- /dev/null +++ b/apps/regattatimer/regattatimer.json @@ -0,0 +1,8 @@ +{ + "debug": false, + "buzzer": true, + "dial": "Numeric", + "gps": true, + "record": false, + "theme": "Dark" +} diff --git a/apps/regattatimer/regattatimer.translations.json b/apps/regattatimer/regattatimer.translations.json new file mode 100644 index 000000000..e89eeddf8 --- /dev/null +++ b/apps/regattatimer/regattatimer.translations.json @@ -0,0 +1,10 @@ +{ + "de": { + "speed": "FüG", + "speed_unit": "kn" + }, + "en": { + "speed": "SOA", + "speed_unit": "kn" + } +} diff --git a/apps/regattatimer/screenshot-1.png b/apps/regattatimer/screenshot-1.png new file mode 100644 index 000000000..6888ccd62 Binary files /dev/null and b/apps/regattatimer/screenshot-1.png differ diff --git a/apps/regattatimer/screenshot-2.png b/apps/regattatimer/screenshot-2.png new file mode 100644 index 000000000..bde59766d Binary files /dev/null and b/apps/regattatimer/screenshot-2.png differ diff --git a/apps/regattatimer/screenshot-3.png b/apps/regattatimer/screenshot-3.png new file mode 100644 index 000000000..4416a0fc8 Binary files /dev/null and b/apps/regattatimer/screenshot-3.png differ diff --git a/apps/regattatimer/screenshot-4.png b/apps/regattatimer/screenshot-4.png new file mode 100644 index 000000000..23cb79ac7 Binary files /dev/null and b/apps/regattatimer/screenshot-4.png differ diff --git a/apps/regattatimer/screenshot-5.png b/apps/regattatimer/screenshot-5.png new file mode 100644 index 000000000..49613d8c0 Binary files /dev/null and b/apps/regattatimer/screenshot-5.png differ diff --git a/apps/regattatimer/screenshot-6.png b/apps/regattatimer/screenshot-6.png new file mode 100644 index 000000000..391eaafd2 Binary files /dev/null and b/apps/regattatimer/screenshot-6.png differ diff --git a/apps/regattatimer/screenshot-7.png b/apps/regattatimer/screenshot-7.png new file mode 100644 index 000000000..e1f96005f Binary files /dev/null and b/apps/regattatimer/screenshot-7.png differ diff --git a/apps/regattatimer/screenshot.png b/apps/regattatimer/screenshot.png new file mode 100644 index 000000000..23cb79ac7 Binary files /dev/null and b/apps/regattatimer/screenshot.png differ diff --git a/apps/regattatimer/settings.js b/apps/regattatimer/settings.js new file mode 100644 index 000000000..2f9133d9f --- /dev/null +++ b/apps/regattatimer/settings.js @@ -0,0 +1,78 @@ +(function(back) { + var + file = "regattatimer.json", + + storage = require("Storage"), + + dials = ["Numeric", "Discs"], + + themes = ["Light", "Dark"], + + settings = Object.assign({ + "debug": false, + "buzzer": true, + "dial": "Numeric", + "gps": true, + "record": false, + "theme": "Dark", + }, storage.readJSON(file, true) || {}); + + function save(key, value) { + settings[key] = value; + storage.writeJSON(file, settings); + } + + E.showMenu({ + "" : { "title" : "Regatta Timer" }, + "< Back" : () => back(), + "GPS": { + value: !!settings.gps, // !! converts undefined to false + onchange: v => { + save("gps", v); + } + }, + "THEME": { + value: themes.indexOf(settings.theme), + min: 0, + max: themes.length - 1, + step: 1, + wrap: true, + format: v => themes[v], + onchange: (d) => { + save("theme", themes[d]); + } + }, + "BUZZER": { + value: !!settings.buzzer, // !! converts undefined to false + onchange: v => { + save("buzzer", v); + } + }, + /* + "DIAL": { + value: dials.indexOf(settings.dial), + min: 0, + max: dials.length - 1, + step: 1, + wrap: true, + format: v => dials[v], + onchange: (d) => { + save("dial", dials[d]); + } + }, + "RECORD": { + value: !!settings.record, // 0| converts undefined to 0 + onchange: v => { + settings.record = v; + save("record", v); + } + }, + */ + "DEBUG": { + value: !!settings.debug, // 0| converts undefined to 0 + onchange: v => { + save("debug", v); + } + }, + }); +}) diff --git a/apps/widlockunlock/ChangeLog b/apps/widlockunlock/ChangeLog index b5efcaa86..40a842cc7 100644 --- a/apps/widlockunlock/ChangeLog +++ b/apps/widlockunlock/ChangeLog @@ -1,2 +1,3 @@ 0.01: First commit 0.02: Add tap-to-lock functionality +0.03: Disable tap-to-lock if back button is present diff --git a/apps/widlockunlock/metadata.json b/apps/widlockunlock/metadata.json index cc4fa76cd..c3f871c84 100644 --- a/apps/widlockunlock/metadata.json +++ b/apps/widlockunlock/metadata.json @@ -1,8 +1,8 @@ { "id": "widlockunlock", "name": "Lock/Unlock Widget", - "version": "0.02", - "description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked, or an unlock icon otherwise. Tap to lock the lcd", + "version": "0.03", + "description": "On devices with always-on display (Bangle.js 2) this displays lock icon whenever the display is locked, or an unlock icon otherwise. Tap to lock the lcd (unless the back button is shown, in which case it takes priority)", "icon": "widget.png", "type": "widget", "tags": "widget,lock", diff --git a/apps/widlockunlock/widget.js b/apps/widlockunlock/widget.js index cfbbc87a3..4b78b440c 100644 --- a/apps/widlockunlock/widget.js +++ b/apps/widlockunlock/widget.js @@ -1,6 +1,8 @@ Bangle.on("lock", () => Bangle.drawWidgets()); Bangle.on('touch', (_btn, xy) => { + if (WIDGETS["back"]) return; + const oversize = 5; const w = WIDGETS.lockunlock; @@ -11,6 +13,8 @@ Bangle.on('touch', (_btn, xy) => { if(w.x - oversize <= x && x < w.x + 14 + oversize && w.y - oversize <= y && y < w.y + 24 + oversize) { + E.stopEventPropagation && E.stopEventPropagation(); + Bangle.setLocked(true); const backlightTimeout = Bangle.getOptions().backlightTimeout; // ms diff --git a/webtools b/webtools index 2ab71a33d..af870d7b8 160000 --- a/webtools +++ b/webtools @@ -1 +1 @@ -Subproject commit 2ab71a33d69bfda40465174ffe57adb03c21fc42 +Subproject commit af870d7b8386bfa824b07b268bce414e4daf3fbb