From 254d69fce6654dfdde7f197997229f66a7bbaa57 Mon Sep 17 00:00:00 2001 From: David Peer Date: Wed, 23 Feb 2022 19:17:44 +0100 Subject: [PATCH] Added simple chrono widget with easier UI --- apps/chronosimplewid/ChangeLog | 6 ++ apps/chronosimplewid/README.md | 41 ++++++++++++ apps/chronosimplewid/app-icon.js | 1 + apps/chronosimplewid/app.js | 93 ++++++++++++++++++++++++++++ apps/chronosimplewid/app.png | Bin 0 -> 1060 bytes apps/chronosimplewid/metadata.json | 17 +++++ apps/chronosimplewid/screenshot.png | Bin 0 -> 2920 bytes apps/chronosimplewid/widget.js | 79 +++++++++++++++++++++++ 8 files changed, 237 insertions(+) create mode 100644 apps/chronosimplewid/ChangeLog create mode 100644 apps/chronosimplewid/README.md create mode 100644 apps/chronosimplewid/app-icon.js create mode 100644 apps/chronosimplewid/app.js create mode 100644 apps/chronosimplewid/app.png create mode 100644 apps/chronosimplewid/metadata.json create mode 100644 apps/chronosimplewid/screenshot.png create mode 100644 apps/chronosimplewid/widget.js diff --git a/apps/chronosimplewid/ChangeLog b/apps/chronosimplewid/ChangeLog new file mode 100644 index 000000000..ed230b737 --- /dev/null +++ b/apps/chronosimplewid/ChangeLog @@ -0,0 +1,6 @@ +0.01: New widget and app! +0.02: Setting to reset values, timer buzzes at 00:00 and not later (see readme) +0.03: Display only minutes:seconds when less than 1 hour left +0.04: Change to 7 segment font, move to top widget bar + Better auto-update behaviour, less RAM used +0.05: Fix error running app on new firmwares (fix #1140) diff --git a/apps/chronosimplewid/README.md b/apps/chronosimplewid/README.md new file mode 100644 index 000000000..6e0aba681 --- /dev/null +++ b/apps/chronosimplewid/README.md @@ -0,0 +1,41 @@ +# Chronometer Widget + +Chronometer (timer) that runs as a widget. +The advantage is, that you can still see your normal watchface and other widgets when the timer is running. +The widget is always active, but only shown when the timer is on. +Hours, minutes, seconds and timer status can be set with an app. + +When there is less than one second left on the timer it buzzes. + +The widget has been tested on Bangle 1 and Bangle 2 + +## Screenshots + +![](screenshot.png) + + +## Features + +* Using other apps does not interrupt the timer, no need to keep the widget open (BUT: there will be no buzz when the time is up, for that the widget has to be loaded) +* Target time is saved to a file and timer picks up again when widget is loaded again. + +## Settings + +There are no settings section in the settings app, timer can be set using an app. + +* Reset values: Reset hours, minutes, seconds to 0; set timer on to false; write to settings file +* Hours: Set the hours for the timer +* Minutes: Set the minutes for the timer +* Seconds: Set the seconds for the timer +* Timer on: Starts the timer and displays the widget when set to 'On'. You have to leave the app to load the widget which starts the timer. The widget is always there, but only visible when timer is on. + + +## Releases + +* Official app loader: https://github.com/espruino/BangleApps/tree/master/apps/chronowid (https://banglejs.com/apps/) +* Forked app loader: https://github.com/Purple-Tentacle/BangleApps/tree/master/apps/chronowid (https://purple-tentacle.github.io/BangleApps/index.html#) +* Development: https://github.com/Purple-Tentacle/BangleAppsDev/tree/master/apps/chronowid + +## Requests + +If you have any feature requests, please write here: http://forum.espruino.com/conversations/345972/ diff --git a/apps/chronosimplewid/app-icon.js b/apps/chronosimplewid/app-icon.js new file mode 100644 index 000000000..db2010218 --- /dev/null +++ b/apps/chronosimplewid/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwIFCn/8BYYFRABcD4AFFgIFCh/wgeAAoP//8HCYMDAoPD8EAg4FB8PwgEf+EP/H4HQOAgP8uEAvwfBv0ggBFCn4CB/EBwEfgEB+AFBh+AgfgAoI1BIoQJB4AHBAoXgg4uBAIIFCCYQFGh5rDJQJUBK4IFCNYIFVDoopDGoJiBHYYFKVYRZBWIYDBA4IFBNIQzBG4IbBToKkBAQKVFUIYICVoQUCXIQmCYoIsCaITqDAoLvDNYUAA=")) \ No newline at end of file diff --git a/apps/chronosimplewid/app.js b/apps/chronosimplewid/app.js new file mode 100644 index 000000000..8e2c82b68 --- /dev/null +++ b/apps/chronosimplewid/app.js @@ -0,0 +1,93 @@ +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); + +const storage = require('Storage'); +let settingsChronowid; + +function updateSettings() { + var now = new Date(); + const goal = new Date(now.getFullYear(), now.getMonth(), now.getDate(), + now.getHours() + settingsChronowid.hours, now.getMinutes() + settingsChronowid.minutes, now.getSeconds() + settingsChronowid.seconds); + settingsChronowid.goal = goal.getTime(); + storage.writeJSON('chronowid.json', settingsChronowid); + if (WIDGETS["chronowid"]) WIDGETS["chronowid"].reload(); +} + +function resetSettings() { + settingsChronowid = { + hours : 0, + minutes : 0, + seconds : 0, + started : false, + counter : 0, + goal : 0, + }; + updateSettings(); +} + +settingsChronowid = storage.readJSON('chronowid.json',1); +if (!settingsChronowid) resetSettings(); + +E.on('kill', () => { + updateSettings(); +}); + +function showMenu() { + const timerMenu = { + '': { + 'title': 'Set timer' + }, + '< Back' : ()=>{load();}, + 'Reset Values': function() { + settingsChronowid.hours = 0; + settingsChronowid.minutes = 0; + settingsChronowid.seconds = 0; + settingsChronowid.started = false; + updateSettings(); + showMenu(); + }, + 'Hours': { + value: settingsChronowid.hours, + min: 0, + max: 24, + step: 1, + onchange: v => { + settingsChronowid.hours = v; + updateSettings(); + } + }, + 'Minutes': { + value: settingsChronowid.minutes, + min: 0, + max: 59, + step: 1, + onchange: v => { + settingsChronowid.minutes = v; + updateSettings(); + } + }, + 'Seconds': { + value: settingsChronowid.seconds, + min: 0, + max: 59, + step: 1, + onchange: v => { + settingsChronowid.seconds = v; + updateSettings(); + } + }, + 'Timer on': { + value: settingsChronowid.started, + format: v => v ? "On" : "Off", + onchange: v => { + settingsChronowid.started = v; + updateSettings(); + } + }, + }; + + return E.showMenu(timerMenu); +} + +showMenu(); diff --git a/apps/chronosimplewid/app.png b/apps/chronosimplewid/app.png new file mode 100644 index 0000000000000000000000000000000000000000..5ac7a480c5d4533c9851ba6264b74aae48035eb0 GIT binary patch literal 1060 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 literal 0 HcmV?d00001 diff --git a/apps/chronosimplewid/widget.js b/apps/chronosimplewid/widget.js new file mode 100644 index 000000000..2d1c78941 --- /dev/null +++ b/apps/chronosimplewid/widget.js @@ -0,0 +1,79 @@ +(() => { + var settingsChronowid; + var interval = 0; //used for the 1 second interval timer + var diff; + + //Convert ms to time + function getTime(t) { + var milliseconds = parseInt((t % 1000) / 100), + seconds = Math.floor((t / 1000) % 60), + minutes = Math.floor((t / (1000 * 60)) % 60), + hours = Math.floor((t / (1000 * 60 * 60)) % 24); + return hours.toString().padStart(2,0) + ":" + minutes.toString().padStart(2,0) + ":" + seconds.toString().padStart(2,0); + } + + /*function printDebug() { + print ("Goaltime: " + getTime(settingsChronowid.goal)); + print ("Goal: " + settingsChronowid.goal); + print("Difftime: " + getTime(diff)); + print("Diff: " + diff); + print ("Started: " + settingsChronowid.started); + print ("----"); + }*/ + + //counts down, calculates and displays + function countDown() { + var now = new Date(); + diff = settingsChronowid.goal - now; //calculate difference + // time is up + if (settingsChronowid.started && diff < 1000) { + Bangle.buzz(1500); + //write timer off to file + settingsChronowid.started = false; + require('Storage').writeJSON('chronowid.json', settingsChronowid); + clearInterval(interval); //stop interval + interval = undefined; + } + // calculates width and redraws accordingly + WIDGETS["chronowid"].redraw(); + } + + // add the widget + WIDGETS["chronowid"]={area:"tl",width:0,draw:function() { + if (!this.width) return; + g.reset().setFontAlign(0,0).clearRect(this.x,this.y,this.x+this.width,this.y+23); + //g.drawRect(this.x,this.y,this.x+this.width-1, this.y+23); + var scale; + var timeStr; + if (diff < 3600000) { //less than 1 hour left + width = 58; + scale = 2; + timeStr = getTime(diff).substring(3); // remove hour part 00:00:00 -> 00:00 + } else { //one hour or more left + width = 48; + scale = 1; + timeStr = getTime(diff); //display hour 00:00:00 but small + } + // Font5x9Numeric7Seg - just build this in as it's tiny + g.setFontCustom(atob("AAAAAAAAAAIAAAQCAQAAAd0BgMBdwAAAAAAAdwAB0RiMRcAAAERiMRdwAcAQCAQdwAcERiMRBwAd0RiMRBwAAEAgEAdwAd0RiMRdwAcERiMRdwAFAAd0QiEQdwAdwRCIRBwAd0BgMBAAABwRCIRdwAd0RiMRAAAd0QiEQAAAAAAAAAA="), 32, atob("BgAAAAAAAAAAAAAAAAYCAAYGBgYGBgYGBgYCAAAAAAAABgYGBgYG"), 9 + (scale<<8)); + g.drawString(timeStr, this.x+this.width/2, this.y+12); + }, redraw:function() { + var last = this.width; + if (!settingsChronowid.started) this.width = 0; + else this.width = (diff < 3600000) ? 58 : 48; + if (last != this.width) Bangle.drawWidgets(); + else this.draw(); + }, reload:function() { + settingsChronowid = require('Storage').readJSON("chronowid.json",1)||{}; + if (interval) clearInterval(interval); + interval = undefined; + // start countdown each second + if (settingsChronowid.started) interval = setInterval(countDown, 1000); + // reset everything + countDown(); + }}; + + //printDebug(); + // set width correctly, start countdown each second + WIDGETS["chronowid"].reload(); +})();