From a3b49a0d824625d847e7cc7fff3ab9dab7ff5bfd Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Wed, 19 Jan 2022 20:19:56 +0100 Subject: [PATCH 1/4] Support for localhost appstore - Adds a script to generate `apps.local.json` from all metadata - Makes the loader use `apps.local.json` when serving from localhost - Adds npm `local` script to watch for changed metadata, while serving from localhost --- .gitignore | 2 +- bin/update_local_apps_json.js | 62 +++++++++++++++++++++++++++++++++++ core | 2 +- loader.js | 5 +++ package.json | 8 +++++ 5 files changed, 77 insertions(+), 2 deletions(-) create mode 100755 bin/update_local_apps_json.js diff --git a/.gitignore b/.gitignore index 273fdeae4..523dc5f20 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,4 @@ appdates.csv _config.yml tests/Layout/bin/tmp.* tests/Layout/testresult.bmp -apps.json +apps.local.json \ No newline at end of file diff --git a/bin/update_local_apps_json.js b/bin/update_local_apps_json.js new file mode 100755 index 000000000..3ba54f289 --- /dev/null +++ b/bin/update_local_apps_json.js @@ -0,0 +1,62 @@ +#!/usr/bin/nodejs +/* Merge all apps/metadata.json files into apps.local.json +*/ + +const fs = require("fs"); + +const BASEDIR = __dirname+"/../"; +const APPSDIR = BASEDIR+"apps/"; +const APPSFILE = "apps.local.json"; +const APPSPATH = BASEDIR+ APPSFILE; + +function ERROR(s) { + console.error("ERROR: "+s); + process.exit(1); +} +function INFO(s) { + console.info(s); +} + +const apps = []; +const dirs = fs.readdirSync(APPSDIR, {withFileTypes: true}); +dirs.forEach(dir => { + let appsFile; + if (dir.name.startsWith("_example")) { + return; + } + try { + appsFile = fs.readFileSync(APPSDIR+dir.name+"/metadata.json").toString(); + } catch(e) { + return; + } + try { + apps.push(JSON.parse(appsFile)); + } catch(e) { + console.log(e); + const m = e.toString().match(/in JSON at position (\d+)/); + if (m) { + const char = parseInt(m[1]); + console.log("==============================================="); + console.log("LINE "+appsFile.substr(0, char).split("\n").length); + console.log("==============================================="); + console.log(appsFile.substr(char-10, 20)); + console.log("==============================================="); + } + console.log(m); + ERROR(dir.name+"/metadata.json not valid JSON"); + } +}); +// order doesn't matter as the loader sorts apps, but sort by anyway +apps.sort((a, b) => ((0|a.sortorder)-(0|b.sortorder)) || a.id.localeCompare(b.id)); +const json = JSON.stringify(apps, null, 2); +let update = false; +if (fs.existsSync(APPSPATH)) { + const old = fs.readFileSync(APPSPATH).toString(); + if (old===json) { + INFO(`${APPSFILE} is already up-to-date`); + process.exit(); + } + update = true; +} +fs.writeFileSync(APPSPATH, json); +INFO(`${update ? 'Updated' : 'Wrote'} ${APPSFILE}`); \ No newline at end of file diff --git a/core b/core index 5023ee122..3093d78a5 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit 5023ee1228030130ba9f026d5dbe920f7527ee7d +Subproject commit 3093d78a5d752cbf03ea8f9a1a7c0b50b9c8123b diff --git a/loader.js b/loader.js index 0355ea89c..d8ba26269 100644 --- a/loader.js +++ b/loader.js @@ -5,6 +5,11 @@ if (window.location.host=="banglejs.com") { document.title += " [Development]"; document.getElementById("apploaderlinks").innerHTML = 'This is the development Bangle.js App Loader - you can also try the Official Version for stable apps.'; +} else if (window.location.hostname==='localhost') { + document.title += " [Local]"; + Const.APPS_JSON_FILE = "apps.local.json"; + document.getElementById("apploaderlinks").innerHTML = + 'This is your local Bangle.js App Loader - you can try the Official Version here.'; } else { document.title += " [Unofficial]"; document.getElementById("apploaderlinks").innerHTML = diff --git a/package.json b/package.json index b796044c9..aa1b0b88a 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,17 @@ "scripts": { "lint-apps": "eslint ./apps --ext .js", "test": "node bin/sanitycheck.js && eslint ./apps --ext .js", + "update-local-apps": "node bin/update_local_apps_json.js", + "local": "npm-watch & npx http-server -a localhost -c-1", "start": "npx http-server -c-1" }, + "watch": { + "update-local-apps": "apps/*/metadata.json" + }, "dependencies": { "acorn": "^7.2.0" + }, + "devDpendencies": { + "npm-watch": "^0.11.0" } } From e75a828fce5f6cb243ce93c98a60f2ca44bebecf Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Thu, 20 Jan 2022 20:22:58 +0100 Subject: [PATCH 2/4] always use shell script to generate apps.json, remove js version Gets rid of duplicated functionality, but allows passing a filename argument to write to a different file instead of `apps.json`. --- apps.json | 5 ++- bin/create_apps_json.sh | 17 +++++++--- bin/update_local_apps_json.js | 62 ----------------------------------- package.json | 2 +- 4 files changed, 18 insertions(+), 68 deletions(-) delete mode 100755 bin/update_local_apps_json.js diff --git a/apps.json b/apps.json index 822af47f2..537a4f697 100644 --- a/apps.json +++ b/apps.json @@ -7,7 +7,10 @@ # Otherwise nothing has changed. GitHub Pages will automatically # create apps.json as your site is hosted, or if you're hosting # yourself you can run bin/create_apps_json.sh -# +# +# If you serve the store from localhost for development/testing, +# the loader looks for apps.local.json instead, you can run +# `bin/create_apps_json.sh apps.local.json` to create that file. # ================================================================= # Uncomment the following line if you only want explicitly listed diff --git a/bin/create_apps_json.sh b/bin/create_apps_json.sh index adc5f8a62..d61f7afe1 100755 --- a/bin/create_apps_json.sh +++ b/bin/create_apps_json.sh @@ -13,17 +13,26 @@ # # If you do this, please do not attempt to commit your modified # apps.json back into the main BangleApps repository! +# +# You can pass an optional filename to this script, and it will write +# to that instead, apps.local.json is used when opening the loader on localhost +outfile="${1:-apps.json}" cd `dirname $0`/.. -echo "[" > apps.json +echo "[" > "$outfile" +first=1 for app in apps/*/; do echo "Processing $app..."; if [[ "$app" =~ ^apps/_example.* ]]; then echo "Ignoring $app" else - cat ${app}metadata.json >> apps.json + if [ $first -eq 1 ]; then + first=0; + else + echo "," >> "$outfile" + fi; + cat ${app}metadata.json >> "$outfile" # echo ",\"$app\"," >> apps.json # DEBUG ONLY - echo "," >> apps.json fi done -echo "null]" >> apps.json +echo "]" >> "$outfile" diff --git a/bin/update_local_apps_json.js b/bin/update_local_apps_json.js deleted file mode 100755 index 3ba54f289..000000000 --- a/bin/update_local_apps_json.js +++ /dev/null @@ -1,62 +0,0 @@ -#!/usr/bin/nodejs -/* Merge all apps/metadata.json files into apps.local.json -*/ - -const fs = require("fs"); - -const BASEDIR = __dirname+"/../"; -const APPSDIR = BASEDIR+"apps/"; -const APPSFILE = "apps.local.json"; -const APPSPATH = BASEDIR+ APPSFILE; - -function ERROR(s) { - console.error("ERROR: "+s); - process.exit(1); -} -function INFO(s) { - console.info(s); -} - -const apps = []; -const dirs = fs.readdirSync(APPSDIR, {withFileTypes: true}); -dirs.forEach(dir => { - let appsFile; - if (dir.name.startsWith("_example")) { - return; - } - try { - appsFile = fs.readFileSync(APPSDIR+dir.name+"/metadata.json").toString(); - } catch(e) { - return; - } - try { - apps.push(JSON.parse(appsFile)); - } catch(e) { - console.log(e); - const m = e.toString().match(/in JSON at position (\d+)/); - if (m) { - const char = parseInt(m[1]); - console.log("==============================================="); - console.log("LINE "+appsFile.substr(0, char).split("\n").length); - console.log("==============================================="); - console.log(appsFile.substr(char-10, 20)); - console.log("==============================================="); - } - console.log(m); - ERROR(dir.name+"/metadata.json not valid JSON"); - } -}); -// order doesn't matter as the loader sorts apps, but sort by anyway -apps.sort((a, b) => ((0|a.sortorder)-(0|b.sortorder)) || a.id.localeCompare(b.id)); -const json = JSON.stringify(apps, null, 2); -let update = false; -if (fs.existsSync(APPSPATH)) { - const old = fs.readFileSync(APPSPATH).toString(); - if (old===json) { - INFO(`${APPSFILE} is already up-to-date`); - process.exit(); - } - update = true; -} -fs.writeFileSync(APPSPATH, json); -INFO(`${update ? 'Updated' : 'Wrote'} ${APPSFILE}`); \ No newline at end of file diff --git a/package.json b/package.json index aa1b0b88a..32c96e3ea 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts": { "lint-apps": "eslint ./apps --ext .js", "test": "node bin/sanitycheck.js && eslint ./apps --ext .js", - "update-local-apps": "node bin/update_local_apps_json.js", + "update-local-apps": "./bin/create_apps_json.sh apps.local.json", "local": "npm-watch & npx http-server -a localhost -c-1", "start": "npx http-server -c-1" }, From 46fbccf71123a96497d84251bdd31b17afe2ec8b Mon Sep 17 00:00:00 2001 From: Richard de Boer Date: Thu, 20 Jan 2022 20:39:06 +0100 Subject: [PATCH 3/4] tell git to ignore modified `apps.json` when running create_apps.json.sh Only if no arguments given: `create_apps_json.sh apps.json` will leave it alone if someone really wants to overwrite+commit apps.json. --- bin/create_apps_json.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/bin/create_apps_json.sh b/bin/create_apps_json.sh index d61f7afe1..dd883b22a 100755 --- a/bin/create_apps_json.sh +++ b/bin/create_apps_json.sh @@ -36,3 +36,13 @@ for app in apps/*/; do fi done echo "]" >> "$outfile" + +if [ -z "$1"]; then + # Running with no arguments: prevent accidental commit of modified apps.json. + # You can use `create_apps.json.sh apps.json` if you really want to both + # overwrite and still commit apps.json + git update-index --skip-worktree apps.json + echo "Told git to ignore modified apps.json." + # If you want to unignore it, use + # 'git update-index --no-skip-worktree apps.json' +fi \ No newline at end of file From dbdd731511eda6c007ae08efa3c8d199e6df4fa3 Mon Sep 17 00:00:00 2001 From: Bela Date: Fri, 21 Jan 2022 11:46:13 +0100 Subject: [PATCH 4/4] add timerclk app --- apps/timerclk/ChangeLog | 1 + apps/timerclk/README.md | 72 ++++++ apps/timerclk/alarm.alert.js | 57 +++++ apps/timerclk/alarm.info | 1 + apps/timerclk/alarm.js | 116 ++++++++++ apps/timerclk/app-icon.js | 1 + apps/timerclk/app-icon.png | Bin 0 -> 9025 bytes apps/timerclk/app.js | 151 ++++++++++++ apps/timerclk/boot.js | 48 ++++ apps/timerclk/lib.js | 127 +++++++++++ apps/timerclk/metadata.json | 38 +++ apps/timerclk/pause-24.png | Bin 0 -> 4688 bytes apps/timerclk/play-24.png | Bin 0 -> 4761 bytes apps/timerclk/remove-24.png | Bin 0 -> 4753 bytes apps/timerclk/reset-24.png | Bin 0 -> 4724 bytes apps/timerclk/screenshot.png | Bin 0 -> 18113 bytes apps/timerclk/screenshot_settings1.png | Bin 0 -> 3189 bytes apps/timerclk/screenshot_settings2.png | Bin 0 -> 3348 bytes apps/timerclk/screenshot_settings3.png | Bin 0 -> 3425 bytes apps/timerclk/screenshot_stopwatch1.png | Bin 0 -> 14735 bytes apps/timerclk/screenshot_stopwatch2.png | Bin 0 -> 14382 bytes apps/timerclk/settings.js | 292 ++++++++++++++++++++++++ apps/timerclk/stopwatch.info | 1 + apps/timerclk/stopwatch.js | 135 +++++++++++ apps/timerclk/timer.alert.js | 62 +++++ apps/timerclk/timer.info | 1 + apps/timerclk/timer.js | 139 +++++++++++ apps/timerclk/wid.js | 7 + 28 files changed, 1249 insertions(+) create mode 100644 apps/timerclk/ChangeLog create mode 100644 apps/timerclk/README.md create mode 100644 apps/timerclk/alarm.alert.js create mode 100644 apps/timerclk/alarm.info create mode 100644 apps/timerclk/alarm.js create mode 100644 apps/timerclk/app-icon.js create mode 100644 apps/timerclk/app-icon.png create mode 100644 apps/timerclk/app.js create mode 100644 apps/timerclk/boot.js create mode 100644 apps/timerclk/lib.js create mode 100644 apps/timerclk/metadata.json create mode 100644 apps/timerclk/pause-24.png create mode 100644 apps/timerclk/play-24.png create mode 100644 apps/timerclk/remove-24.png create mode 100644 apps/timerclk/reset-24.png create mode 100644 apps/timerclk/screenshot.png create mode 100644 apps/timerclk/screenshot_settings1.png create mode 100644 apps/timerclk/screenshot_settings2.png create mode 100644 apps/timerclk/screenshot_settings3.png create mode 100644 apps/timerclk/screenshot_stopwatch1.png create mode 100644 apps/timerclk/screenshot_stopwatch2.png create mode 100644 apps/timerclk/settings.js create mode 100644 apps/timerclk/stopwatch.info create mode 100644 apps/timerclk/stopwatch.js create mode 100644 apps/timerclk/timer.alert.js create mode 100644 apps/timerclk/timer.info create mode 100644 apps/timerclk/timer.js create mode 100644 apps/timerclk/wid.js diff --git a/apps/timerclk/ChangeLog b/apps/timerclk/ChangeLog new file mode 100644 index 000000000..5560f00bc --- /dev/null +++ b/apps/timerclk/ChangeLog @@ -0,0 +1 @@ +0.01: New App! diff --git a/apps/timerclk/README.md b/apps/timerclk/README.md new file mode 100644 index 000000000..fd6d2b16b --- /dev/null +++ b/apps/timerclk/README.md @@ -0,0 +1,72 @@ +# Timer Clock + +A clock based on the Anton Clock with stopwatches, timers and alarms based on the Stopwatch Touch style and an alarm widget based on the one from Default alarm & timer. + +## Features + +* two slots for stopwatches / timers on the clock screen +* configurable font and size (Anton font has fixed size) +* stopwatch with modifiable start value +* timer that can be paused +* alarms +* multiple stopwatches, timers and alarms +* stopwatches and timers keep running in the background + +## Images + +![](screenshot.png) + +### Stopwatch + +![](screenshot_stopwatch1.png) + +![](screenshot_stopwatch2.png) + +### Settings + +![](screenshot_settings1.png) + +![](screenshot_settings2.png) + +![](screenshot_settings3.png) + +## Controls + +### Bangle.js 1 + +#### Clock + +* Left: Stopwatch +* Right: Timer +* Button 1 / 2: Alarm + +#### Stopwatch / Timer / Alarm + +* Button 1: + * edit mode: increase + * control mode: play / pause +* Button 2: switch between edit / control mode +* Button 3: + * edit mode: decrease + * control mode: reset / remove +* Left: + * edit mode: previous index + * control mode: previous stopwatch / timer / alarm +* Right: + * edit mode: next index + * control mode: next stopwatch / timer / alarm + +### Bangle.js 2 + +#### Clock + +* Swipe left: Stopwatch +* Swipe right: Timer +* Swipe over date: Alarm + +#### Stopwatch / Timer / Alarm + +* Swipe left: previous stopwatch / timer / alarm +* Swipe right: next stopwatch / timer / alarm +* Swipe up: increase index swiped over +* Swipe down: decrease index swiped over \ No newline at end of file diff --git a/apps/timerclk/alarm.alert.js b/apps/timerclk/alarm.alert.js new file mode 100644 index 000000000..f4b61822a --- /dev/null +++ b/apps/timerclk/alarm.alert.js @@ -0,0 +1,57 @@ +if (timerclkAlarmTimeout) clearInterval(timerclkAlarmTimeout); +var timerclk = require("timerclk.lib.js"); +var settings = require('Storage').readJSON("timerclk.json", true) || {}; +settings = Object.assign({ + "vibrate":10 +}, settings.alarm||{}); + +function showAlarm(alarm) { + Bangle.loadWidgets(); + Bangle.drawWidgets(); + Bangle.setLocked(false); + E.showPrompt("Alarm!",{ + title:"ALARM!", + buttons : {/*LANG*/"Ok":true} + }).then(function(ok) { + buzzCount = 0; + if (ok) { + alarm.last = new Date().getDate(); + } + require("Storage").write("timerclk.alarm.json",JSON.stringify(alarms)); + load(); + }); + function vibrate(counter) { + VIBRATE.write(1); + setTimeout(() => VIBRATE.write(0), 100); + if (--counter) setTimeout(() => vibrate(counter), 250); + } + function buzz() { + if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence + vibrate(4); + if (buzzCount--) + setTimeout(buzz, 3000); + else { // auto-snooze + buzzCount = settings.vibrate; + setTimeout(buzz, 600000); + } + } + var buzzCount = settings.vibrate; + buzz(); +} + +// Check for alarms +console.log("checking for alarms..."); +var alarms = require("Storage").readJSON("timerclk.alarm.json",1)||[]; +var active = alarms.filter(e=>e.on); +if (active.length) { + // if there's an alarm, show it + active = active.sort((a,b)=>(a.time-b.time)+(a.last-b.last)*86400000); + if (active[0].last != new Date().getDate()) { + showAlarm(active[0]); + } else { + setTimeout(load, 100); + } +} else { + // otherwise just go back to default app + setTimeout(load, 100); +} diff --git a/apps/timerclk/alarm.info b/apps/timerclk/alarm.info new file mode 100644 index 000000000..1289f8cef --- /dev/null +++ b/apps/timerclk/alarm.info @@ -0,0 +1 @@ +{"id":"timerclk","name":"tclk Alarm","src":"timerclk.alarm.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10} diff --git a/apps/timerclk/alarm.js b/apps/timerclk/alarm.js new file mode 100644 index 000000000..4acaa6cf0 --- /dev/null +++ b/apps/timerclk/alarm.js @@ -0,0 +1,116 @@ +var timerclk = require("timerclk.lib.js"); +const height = g.getHeight(), width = g.getWidth(); + +var all = require("Storage").readJSON("timerclk.alarm.json") || []; +var settings = require('Storage').readJSON("timerclk.json", true) || {}; +settings = Object.assign({ + "font":"Vector", + "fontSize":40, + "indexFont":"6x8", + "indexFontSize":3, + "buttonHeight":40, + "vibrate":4, +}, settings = settings.alarm||{}); +var defaultElement = {time:43200000, on:true, last:null}; + +var current = 0; +var editIndex = 0; +var drawInterval; +var drawIntervalTimeout; +var buttons; +var dragBorderHrsMins=0, dragBorderMinsSecs=0; + +function update() { + if (drawInterval) clearInterval(drawInterval); + if (drawIntervalTimeout) clearTimeout(drawIntervalTimeout); + if (all[current].start) { + drawIntervalTimeout = setTimeout(() => {drawInterval = setInterval(draw, 1000); draw();}, 1000 - (timerclk.getTime(all[current]) % 1000)); + } else { + drawInterval = null; + drawIntervalTimeout = null; + } + draw(); + drawButtons(); +} +function activate() { + all[current].on = !all[current].on; + all[current].last = null; + update(); + require("Storage").write("timerclk.alarm.json",JSON.stringify(all)); + timerclkCheckAlarms(); +} +function remove() { + all.splice(current, 1); + if (current == all.length) current--; + if (all.length == 0) { + all.push(defaultElement.clone()); + current++; + } + update(); + require("Storage").write("timerclk.alarm.json",JSON.stringify(all)); + timerclkCheckAlarms(); +} + +function edit(position, change) { + if (position == 1) all[current].time += change*1000; + else if (position == 2) all[current].time += change*60000; + else if (position == 3) all[current].time += change*3600000; + require("Storage").write("timerclk.alarm.json",JSON.stringify(all)); + timerclkCheckAlarms(); +} + +var buttons = { + reset: {pos:[0, height-settings.buttonHeight, width/2, height], callback: remove, img: timerclk.remove_img, col:"#f50"}, // remove + play: {pos:[width/2, height-settings.buttonHeight, width, height], callback: activate, img: timerclk.play_img, col:"#0ff"}, // active +}; + + +function drawButtons() { + if (all[current].on) { + buttons.play.img = timerclk.pause_img; + } else { + buttons.play.img = timerclk.play_img; + } + for (var button of buttons) { + g.setColor(button.col); + g.fillRect(button.pos[0], button.pos[1], button.pos[2], button.pos[3]); + g.setColor("#000"); + // scale 24px images + let iw = settings.buttonHeight-10; + var scale = iw/24; + let ix = button.pos[0] + ((button.pos[2]-button.pos[0] - iw) /2); + let iy = button.pos[1] + ((button.pos[3]-button.pos[1] - iw) /2); + g.drawImage(button.img, ix, iy, {scale: scale}); + } +} + +function draw() { + var x = g.getWidth()/2; + var y = g.getHeight()/2; + g.reset(); + + g.clearRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2-settings.buttonHeight); + g.setFontAlign(0,0).setFont(settings.indexFont, settings.indexFontSize); + g.drawString(current+1, x, Bangle.appRect.y + (g.stringMetrics("0").height/2)); + g.setFontAlign(0,0).setFont(settings.font, settings.fontSize); + var timeStr = timerclk.formatTime(all[current].time, false, false, true); + g.drawString(timeStr,x,y); + var start = (width-g.stringMetrics(timeStr).width)/2; + timeStr = timeStr.split(":"); + var markerPosChange = g.stringMetrics("__").width/2; + if (editIndex == 3) x = start + g.stringMetrics(timeStr[0]).width - markerPosChange; + else if (editIndex == 2) x = start + g.stringMetrics(timeStr[0]+":"+timeStr[1]).width - markerPosChange; + else if (editIndex == 1) x = start + g.stringMetrics(timeStr[0]+":"+timeStr[1]+":"+timeStr[2]).width - markerPosChange; + else x = 0; + if (x) g.drawString("__", x, y); + dragBorderHrsMins = start+g.stringMetrics(timeStr[0]).width+g.stringMetrics(":").width/2; + dragBorderMinsSecs = start+g.stringMetrics(timeStr[0]+":"+timeStr[1]).width+g.stringMetrics(":").width/2; +} + +if (all.length == 0) { + all.push(defaultElement.clone()); +} +timerclk.registerControls(this); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +update(); diff --git a/apps/timerclk/app-icon.js b/apps/timerclk/app-icon.js new file mode 100644 index 000000000..278cf4bb6 --- /dev/null +++ b/apps/timerclk/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgP/AFHzvmf+f8z/8tnv/vs9/1t/v+kv94jR/H4n/wn4CBAYPwnEP8AFDg/AAoUwAoPgmABBwfQAonwAo0/4gFC4AFE4gFLmGEAoQDBxgFCwEQAIIFIj4FD/k//hNBAoZZBAoc8j6oS8/P+1NAoP63+7+wMCz/u/YEB/v/v4dI1+pAQIFBx/J/2/AoP5tFJr71eA==")) diff --git a/apps/timerclk/app-icon.png b/apps/timerclk/app-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..074db4ed7da57867d5206fc76fccea500cd7b3f9 GIT binary patch literal 9025 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 bmI+RfWKUa0v@{Eiae$GYxo*iR*RcNq2S*`S literal 0 HcmV?d00001 diff --git a/apps/timerclk/app.js b/apps/timerclk/app.js new file mode 100644 index 000000000..eeb3ac4cd --- /dev/null +++ b/apps/timerclk/app.js @@ -0,0 +1,151 @@ +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)); +}; + +var timerclk = require("timerclk.lib.js"); +var settings = require('Storage').readJSON("timerclk.json", true) || {}; +settings = Object.assign({ + "timeFont":"Anton", + "timeFontSize":0, + "dateFont":"6x8", + "dateFontSize":2, + "dowFont":"6x8", + "dowFontSize":2, + "specialFont":"6x8", + "specialFontSize":2, + "shortDate":true, + "showStopwatches":true, + "showTimers":true, +}, settings.clock||{}); + +var stopwatches = [], timers = []; +if (settings.showStopwatches) { + stopwatches = require("Storage").readJSON("timerclk.stopwatch.json") || []; + stopwatches = stopwatches.filter(e=>e.start||e.time); +} +if (settings.showTimers) { + timers = require("Storage").readJSON("timerclk.timer.json") || []; + timers = timers.filter(e=>e.start||e.timeAdd); +} + +// timeout used to update every minute +var drawTimeout; +var drawSpecialTimeout; +// border between time and date/dow +var dragBorder = g.getHeight()/2; + +// schedule a draw for the next minute +function queueDraw(timeout, interval, func) { + if (timeout) clearTimeout(timeout); + timeout = setTimeout(function() { + timeout = undefined; + func(); + }, interval - (Date.now() % interval)); +} + +function drawSpecial() { + var interval = 60000; + var stopwatch = 0, timer = 0, time; + var x = g.getWidth()/4; + g.setColor(g.theme.fg); + g.setFontAlign(0,0).setFont(settings.specialFont, settings.specialFontSize); + var y = Bangle.appRect.y + g.stringMetrics("00:00").height/2; + g.clearRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y+g.stringMetrics("00:00").height); + + if (stopwatches.length) { + time = timerclk.getTime(stopwatches[stopwatch]); + g.drawString(timerclk.formatTime(time, true), x, y); + if (Math.floor(time/3600000) === 0) interval = 1000; + stopwatch++; + } else if (timers.length > 1) { + time = timers[timer].time - timerclk.getTime(timers[timer]); + g.drawString(timerclk.formatTime(time, true), x, y); + if (Math.floor(time/3600000) === 0) interval = 1000; + timer++; + } + x += g.getWidth()/2; + if (timers.length) { + time = timers[timer].time - timerclk.getTime(timers[timer]); + g.drawString(timerclk.formatTime(time, true), x, y); + if (Math.floor(time/3600000) === 0) interval = 1000; + } else if (stopwatches.length > 1) { + time = timerclk.getTime(stopwatches[stopwatch]); + g.drawString(timerclk.formatTime(time, true), x, y); + if (Math.floor(time/3600000) === 0) interval = 1000; + } + queueDraw(drawSpecialTimeout, interval, drawSpecial); +} + +function draw() { + var x = g.getWidth()/2; + var y = g.getHeight()/2; + g.reset(); + var date = new Date(); + var timeStr = require("locale").time(date,1); + var dateStr = require("locale").date(date,settings.shortDate).toUpperCase(); + var dowStr = require("locale").dow(date).toUpperCase(); + + // draw time + if (settings.timeFont == "Anton") { + g.setFontAlign(0,0).setFont("Anton"); + } else { + g.setFontAlign(0,0).setFont(settings.timeFont, settings.timeFontSize); + } + g.clearRect(Bangle.appRect.x, x-g.stringMetrics(timeStr).height/2, Bangle.appRect.x2, Bangle.appRect.y2); // clear the background + g.drawString(timeStr,x,y); + // draw date + y += g.stringMetrics(timeStr).height/2; + g.setFontAlign(0,0).setFont(settings.dateFont, settings.dateFontSize); + dragBorder = y; + y += g.stringMetrics(dateStr).height/2; + g.drawString(dateStr,x,y); + //draw day of week + y += g.stringMetrics(dateStr).height/2; + g.setFontAlign(0,0).setFont(settings.dowFont, settings.dowFontSize); + y += g.stringMetrics(dowStr).height/2; + g.drawString(dowStr,x,y); + // queue draw in one minute + queueDraw(drawTimeout, 60000, draw); +} + +if (process.env.HWVERSION==1) { + setWatch(()=>load("timerclk.stopwatch.js"), BTN4); + setWatch(()=>load("timerclk.timer.js"), BTN5); + setWatch(()=>load("timerclk.alarm.js"), BTN3); + setWatch(()=>load("timerclk.alarm.js"), BTN1); +} else { + var absY, lastX, lastY; + Bangle.on('drag', e=>{ + if (!e.b) { + if (lastX > 50) { // right + if (absY < dragBorder) { // drag over time + load("timerclk.timer.js"); + }else { // drag over date/dow + load("timerclk.alarm.js"); + } + } else if (lastX < -50) { // left + if (absY < dragBorder) { // drag over time + load("timerclk.stopwatch.js"); + }else { // drag over date/dow + load("timerclk.alarm.js"); + } + } else if (lastY > 50) { // down + } else if (lastY < -50) { // up + } + lastX = 0; + lastY = 0; + } else { + lastX = lastX + e.dx; + lastY = lastY + e.dy; + absY = e.y; + } + }); +} + +Bangle.setUI("clock"); // Show launcher when middle button pressed +g.clear(); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +draw(); +if (stopwatches || timers) drawSpecial(); diff --git a/apps/timerclk/boot.js b/apps/timerclk/boot.js new file mode 100644 index 000000000..9a09f68f3 --- /dev/null +++ b/apps/timerclk/boot.js @@ -0,0 +1,48 @@ +var timerclkTimerTimeout; +var timerclkAlarmTimeout; +function timerclkCheckTimers() { + if (timerclkTimerTimeout) clearTimeout(timerclkTimerTimeout); + var timers = require('Storage').readJSON('timerclk.timer.json',1)||[]; + timers = timers.filter(e=>e.start); + if (timers.length) { + timers = timers.sort((a,b)=>{ + var at = a.timeAdd; + if (a.start) at += Date.now()-a.start; + at = a.period-at; + var bt = b.timeAdd; + if (b.start) bt += Date.now()-b.start; + bt = b.period-bt; + return at-bt; + }); + if (!require('Storage').read("timerclk.timer.alert.js")) { + console.log("No timer app!"); + } else { + var time = timers[0].timeAdd; + if (timers[0].start) time += Date.now()-timers[0].start; + time = timers[0].time - time; + if (time<1000) t=1000; + if (timerclkTimerTimeout) clearTimeout(timerclkTimerTimeout); + timerclkTimerTimeout = setTimeout(() => load("timerclk.timer.alert.js"),time); + } + } +} +function timerclkCheckAlarms() { + if (timerclkAlarmTimeout) clearTimeout(timerclkAlarmTimeout); + var alarms = require('Storage').readJSON('timerclk.alarm.json',1)||[]; + var currentTime = require("timerclk.lib.js").getCurrentTime(); + alarms = alarms.filter(e=>e.on); + if (alarms.length) { + alarms = alarms.sort((a,b)=>(a.time-b.time)+(a.last-b.last)*86400000); + if (!require('Storage').read("timerclk.alarm.alert.js")) { + console.log("No alarm app!"); + } else { + var time = alarms[0].time-currentTime; + if (alarms[0].last == new Date().getDate() || time < 0) time += 86400000; + if (time<1000) t=1000; + if (timerclkAlarmTimeout) clearTimeout(timerclkAlarmTimeout); + timerclkAlarmTimeout = setTimeout(() => load("timerclk.alarm.alert.js"),time); + } + } +} +timerclkCheckTimers(); +timerclkCheckAlarms(); diff --git a/apps/timerclk/lib.js b/apps/timerclk/lib.js new file mode 100644 index 000000000..718962fe0 --- /dev/null +++ b/apps/timerclk/lib.js @@ -0,0 +1,127 @@ +exports.pause_img = atob("GBiBAf///////////+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B+D/B////////////w=="); +exports.play_img = atob("GBiBAf////////////P///D///A///Af//AH//AB//AAf/AAH/AAB/AAB/AAH/AAf/AB//AH//Af//A///D///P//////////////w=="); +exports.reset_img = atob("GBiBAf////////////AAD+AAB+f/5+f/5+f/5+cA5+cA5+cA5+cA5+cA5+cA5+cA5+cA5+f/5+f/5+f/5+AAB/AAD////////////w=="); +exports.remove_img = atob("GBiBAf///////////+P/x+H/h+D/B/B+D/g8H/wYP/4Af/8A//+B//+B//8A//4Af/wYP/g8H/B+D+D/B+H/h+P/x////////////w=="); + +exports.formatTime = function(t, short, tnthEnable, fullTime) { + var negative = ""; + if (t < 0) { + t = t*(-1); + negative = "-"; + } + let hrs = Math.floor(t/3600000); + let mins = Math.floor(t/60000)%60; + let secs = Math.floor(t/1000)%60; + var tnth = ""; + if (tnthEnable) { + tnth = Math.floor(t/100)%10; + tnth = "."+tnth; + } + var hrsStr = hrs; + if (hrs < 10 && !negative) hrsStr = "0"+hrs; + var text; + if (short) { + if (hrs === 0) text = negative + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2); + else text = negative + hrsStr + "/" + ("0"+mins).substr(-2); + } else { + if (hrs === 0 && !fullTime) text = negative + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2) + tnth; + else text = negative + hrsStr + ":" + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2); + } + return text; +}; + +exports.getTime = function(e) { + var time = e.timeAdd; + if (e.start) { + time += Date.now() - e.start; + } + return time; +}; + +exports.getCurrentTime = function() { + var date = new Date(); + return date.getHours()*3600000+date.getMinutes()*60000+date.getSeconds()*1000+date.getMilliseconds(); +}; + +exports.registerControls = function(o) { + if (process.env.HWVERSION==1) { + setWatch(()=>{ + if (o.editIndex == 0) o.buttons.play.callback(); + else o.edit(o.editIndex, 1); + o.draw(); + }, BTN1, {repeat:true}); + setWatch(()=>{ + o.editIndex = !o.editIndex; + o.draw(); + }, BTN2, {repeat:true}); + setWatch(()=>{ + if (o.editIndex == 0) o.buttons.reset.callback(); + else o.edit(o.editIndex, -1); + o.draw(); + }, BTN3, {repeat:true}); + setWatch(()=>{ + if (o.editIndex) { + o.editIndex++; + if (o.editIndex > 3) o.editIndex = 1; + } else if (o.current > 0) o.current--; + o.update(); + }, BTN4, {repeat:true}); + setWatch(()=>{ + if (o.editIndex) { + o.editIndex--; + if (o.editIndex < 1) o.editIndex = 3; + } else { + o.current++; + if (o.current == o.all.length) o.all.push(o.defaultElement.clone()); + } + o.update(); + }, BTN5, {repeat:true}); + } else { + setWatch(()=>load(), BTN1); + Bangle.on('touch',(n,e)=>{ + for (var button of o.buttons) { + if (e.x>=button.pos[0] && e.y>=button.pos[1] && + e.x{ + if (!e.b) { + if (lastX > 40) { // right + o.current++; + if (o.current == o.all.length) o.all.push(o.defaultElement.clone()); + } else if (lastX < -40) { // left + if (o.current > 0) { + o.current--; + } + } else if (lastY > 30) { // down + if (absX < o.dragBorderHrsMins) { + o.edit(3, -1); + } else if (absX > o.dragBorderHrsMins && absX < o.dragBorderMinsSecs) { + o.edit(2, -1); + } else { + o.edit(1, -1); + } + } else if (lastY < -30) { // up + if (absX < o.dragBorderHrsMins) { + o.edit(3, 1); + } else if (absX > o.dragBorderHrsMins && absX < o.dragBorderMinsSecs) { + o.edit(2, 1); + } else { + o.edit(1, 1); + } + } + lastX = 0; + lastY = 0; + o.update(); + } else { + absX = e.x; + lastX = lastX + e.dx; + lastY = lastY + e.dy; + } + }); + } +}; diff --git a/apps/timerclk/metadata.json b/apps/timerclk/metadata.json new file mode 100644 index 000000000..6b415c0fc --- /dev/null +++ b/apps/timerclk/metadata.json @@ -0,0 +1,38 @@ +{ + "id": "timerclk", + "name": "Timer Clock", + "shortName":"Timer Clock", + "version":"0.01", + "description": "A clock with stopwatches, timers and alarms build in.", + "icon": "app-icon.png", + "type": "clock", + "tags": "clock", + "supports" : ["BANGLEJS","BANGLEJS2"], + "screenshots": [ + {"url":"screenshot.png"}, + {"url":"screenshot_stopwatch1.png"}, + {"url":"screenshot_stopwatch2.png"}, + {"url":"screenshot_settings1.png"}, + {"url":"screenshot_settings2.png"}, + {"url":"screenshot_settings3.png"} + ], + "readme": "README.md", + "storage": [ + {"name":"timerclk.app.js","url":"app.js"}, + {"name":"timerclk.img","url":"app-icon.js","evaluate":true}, + {"name":"timerclk.boot.js","url":"boot.js"}, + {"name":"timerclk.lib.js","url":"lib.js"}, + {"name":"timerclk.wid.js","url":"wid.js"}, + {"name":"timerclk.settings.js","url":"settings.js"}, + {"name":"timerclk.stopwatch.js","url":"stopwatch.js"}, + {"name":"timerclk.timer.js","url":"timer.js"}, + {"name":"timerclk.timer.alert.js","url":"timer.alert.js"}, + {"name":"timerclk.alarm.js","url":"alarm.js"}, + {"name":"timerclk.alarm.alert.js","url":"alarm.alert.js"}, + {"name":"timerclk.stopwatch.info","url":"stopwatch.info"}, + {"name":"timerclk.timer.info","url":"timer.info"}, + {"name":"timerclk.alarm.info","url":"alarm.info"} + ], + "data": [{"name":"timerclk.json"},{"name":"timerclk.stopwatch.json"},{"name":"timerclk.timer.json"},{"name":"timerclk.alarm.json"}], + "sortorder": 0 +} diff --git a/apps/timerclk/pause-24.png b/apps/timerclk/pause-24.png new file mode 100644 index 0000000000000000000000000000000000000000..7ff72e906976e641cfb698780e56411b8eb52501 GIT binary patch literal 4688 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 HlQRATsbuV8 literal 0 HcmV?d00001 diff --git a/apps/timerclk/play-24.png b/apps/timerclk/play-24.png new file mode 100644 index 0000000000000000000000000000000000000000..26fa8d99cfb7dc07295e2ac8b1c38a89397f9018 GIT binary patch literal 4761 zcmeHLdss|c8=n-zRLbqBMBC&OvgbZiGn$kdHDxlVnIpufJu`c1XfExUYKjnv)TtAP z2*+I|<&w~GD^6b_r_hCnB*cS4pOQ;w%@peS&Oe^#JO4HF?3uma^{(G~-{1RN?=x$X zgaJO51Umu_hqL7SdIy4U8^bkp2>5J_=62$6gI>jo=j#HIXh?&p<%)0=(#2>{2-Pd( zIGn!zWrpXb<|!7uj-8U!5dP4lMDa#dh+o-* zi}Eh>q{A7%)|{&jWF1(feVmx64qcji06zL=MNIpR0&c^2i_LqszOA{&{ETD;Hy*KD z*u^XVP;-1{boGj7ACj$iZ%OdEF(E^EJ|UrU`0r-*MMv_)r;qyIPAj-@Vdmx4PQ3I_ zcV4Pn6I(1x-7c*ddL=Qi8S`j*ILXp-AzE5oAZQ_6ljaO9KkM3%b7{YA{*%*df6KlH z$ExQZJxN>Q)%>sW$N3NHM?LdjfA4Ku-S&bd@XAv=b_ObzC#`hT4&S}(`h4EC!Fx${ z`zH7=fK%>Ld2?P4i$&>fgkixHbMW%F!;A4D|=Y_T~r8MUhpyLtS1z<83}edIX1 zie0Gf;uPl&9@*#6mB~LPy86Cb$2EU&p(^u~iko-Go^EwXIC;b1j}A}UZ&^J_+3#`k z{dnijQu~s`ah@bU3)2yqLBhzvo#{(NnlaYZ8!-Fyjm59B{>X1{6+q@EL-y=*uY~TL zF|#Jl$!cfYSFci>Zbxj7 zPw*RIx?`HKxohij?aYB3{s>5rni!+E|}c>Yu8H3f!G-nuzw3|*UByU6WqY=pMs zc^)=qQRYJb!smsxwUp4c*yTD)(}j0qGPtdo?@ru(wY(v??A^1XW#oq6JL>N?PBiT( zn{qiZjZ^Q5KXo(RihC7V@9^KSQNc>T%$VoXZyl=S+!egM)CG61wp(1SPv00HC0bka zVxnint}u!zC1AV9y+I}{2EO3>)X?ErNqBCGXs?A6bDJeW0&TOB>}n6m=$vHN{CVtY z+54n%-9gyYs!zY%i&nK}|J-T&Jo$DE!7+PBWBcCjw*TC3Dti^YYh~T*TV0OrBf5sh zCAiOW*qc`6Fp2TH%pntxxSZ$qu0$9 z9h^1>@4UfPmL2eeH_yJ1z{|E?x^CY1Isfj$3+y(H9_v$5*w|PpsVOO45QGo%wEk!k zp~C`o=_6bH39#f`o8IARgI3?_?vb6bT^^ZYnyK26k)noD{nk|vcGu)SnLfnKyJ>-Z8|(Fu*X@Utn)^bBRV= z4!Ad%$wbKLqFcr#&KC$FFExfj3=)Gxfq8mG6piRkfZQ;doD=9hyB7jHafx9%orXgu zM@L7KqUj_x7D}eF*=#a}MyAnV-~nr6R60ZttF(>=h#m}YR4c_48l6I|f()35L>;N) z5{Y0O`VyZ~BM^LrS801$0DO@3h=xohQOHUqx$g|Eju!<$dIS2~8Co%D=;T0DtB%B^ zC@%_C=^Xn)`0xe7uLy>eLKRAlF)2V^zr|$IuYQ_HEZpcLlakSJR0%+}AUR)?shr~wLqlN11lDV4KbSrQt|l(Hx= zgF!)HHVVL85hjC0VKW&hL(&IgE~WsgM8f+J)B?#f0bFoViqzzkH%2GO7{ zFv_A+geiA1LK*77@f7m8L>h_m%_0m(baFMOf>2F3SkUW7*UJoRjWaD{OQ zf{e1|Akv;iv`7>xGcE+?^$bbF5LGA&_IIzKzStFiS1gvqg~6uNUMf&y zVo4Y*8AC#sA#7HEbgf#hi$*ZiGZbVBvH=1zW&=$zay+%acXSwPAdmv~HiZgP*kTHm zL#1&jOlNRIp%BTv36l-G`b)-c-Yj5{)x#joWe&?dfW zV)ct1qt2BkhYpG)<`!oOciuktN3D3q7038zH>NxtL%SmGC~xA07nQuXwu&pPh^m`LjaS`~?jZ6H@>H literal 0 HcmV?d00001 diff --git a/apps/timerclk/remove-24.png b/apps/timerclk/remove-24.png new file mode 100644 index 0000000000000000000000000000000000000000..b59505bcbcb1c3d4d00c2ed30bc77013bd1ccbdb GIT binary patch literal 4753 zcmeI0X;c$g7RQ4igeWSgBj7@eCrL6 z$qx+{S&{5X1OmZI93Th--?sQcGzXs#()k?(!l0L#5mEXuB%Q3$s^y9ljI7VlU}Vgo zkP`@oyD!%Hu6gV{RM=iBT^TPX=0A$3S!XYXyXGV1Dc4wx%~a%CC?^1&L37kx&8{KEIXPVduBoDpMQR~Gxwaj z{P|VY!Yp@z+W9&0?L^11fvYc>Qd~f7a`lJrrLXd>@C_K2Q7H%4wp5&xA|;n8RvDoWZ&|lA%3w{ zLVi*8?DdDv3f8|jJ5g;nA$Lh-%3#pa0@^TR-0VFV=`H8H0a_T;%Y9Jvh?DcZKtL>z%~s3j&`9b~l+-`wW@l^6c%< zH7kM$6LS=~Oh+;y$uTgOgK{Wl4JH*esYP`j#8ocCxxYtmU$jEE{+!eFWEaJ z${uvtVt3kRiac6R&*Ht=B7&c{ZJVH!?v{v|Asex~@1hfq?Tn2&^~51`+fVk?F#0uB zQY$+vAT-9wzK&23K4ruqpG8)eYf^L2&DQTO|5T8xqX4`rn-*>2E-=|sS%~qWUx`^3J zy!;%t{9G?;o%GJaQquW)MEBq>)BC37#ofNQ4iE??OBDY8p<;jkFO?7Ky)=6@Kj3WW zsN9h7GMCX7uFJTxir{|;gyRm9gcT!_a%YX7G54c|#D2}FF`}A-4<1~V-mIyOj z5#32F?znZEpBmm+YbaXSy!qztmTBf@f``%a^_Z~>z41B?kODi1AF6}+=BBiPoXQbk6bKie@q?v z^j#rVb=`jYpr4|0%&bYfoVN@eOTU{MHf?*W=gUV9+RrveMrjLAU)Z;Z#JW)B&^dg; z)ms-sf(_z7xb+_%avdi5B#ueEG4AQ7JM-+FeA?^|H_Yh@9KZg9DO zhF|y(v=+AW8M5%SGBPxAvqjU@)(zD@-BA7hmdlkHE3aFSCHn)L2o_6EC;nJ{JG-gz zrq`i`StV~jnbc%!RbSAb3wC7HoP)k-%+rj}xE2BijcDgwdN+n_WdJGC81vlcuENhrN)>P5U-yz8T!>uld4TI+Q?8UmVzk(R0rat z_p{e4hK0t!c=L=rHnMwMV(f^Q2CBm;y+ z@Hz!#<2cBL=dZ;Oy;>WgR;TbOcw}T8X)JHDXV0btD0ILg1F!hrnh(e3^<4Gb0x1gP z6q#%+TONY;IME?#n9S%1tm~OV6A@Jc2JY|Pg8DM9_>0Yg*)WHT!g7egVS~+LqAUoZ zv89lV1q6}OnOqKw)1R(W%k}Aq7V}L2nSyLU0U5I)I~z5g+TS`o5yJ(ffqR=qhiGsF zjn1RNJT}`EKcP{my$MtCyZTGUp49)*#M5ZdrwxF0J!7D~fNn+o+OB#v!)5#%U%h+r zH+lf*0Vm(3?|@tba($Nq-(@^dT?2A`mjd5qJWyT#H@QgP9(XVn_!X269%o8zG6TV* zkhwHSBp`I-?_W=puLUEPnt*5>fiPqwewYwSO7Z`AEc9ZD(4xh}l3?jRxyi*J45`Ed z--v+oFA9B!5hfB%$9Hunv?sF0nwW&VmmgSBzqOL@93S0&c~f3om96E3*OuDA1Dk3B zPEQObUCXpCEqheC#%%aMc>>7|7V%u@lS?neC!4m0tqnqF^AgKKgduM%lV{{lnQ4>P e_E?u`+d=4jWM@n8ZQBSu6U4$`L6u*8&VK=8xcs01 literal 0 HcmV?d00001 diff --git a/apps/timerclk/reset-24.png b/apps/timerclk/reset-24.png new file mode 100644 index 0000000000000000000000000000000000000000..73fb28dec1e37e92ad8c107879295272790fe34d GIT binary patch literal 4724 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 literal 0 HcmV?d00001 diff --git a/apps/timerclk/screenshot_settings1.png b/apps/timerclk/screenshot_settings1.png new file mode 100644 index 0000000000000000000000000000000000000000..da187e49625eef735f752b8864a27fa6869ae7b8 GIT binary patch literal 3189 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 bi~@fFzTOr28Eo&E00000NkvXXu0mjfyK~@# literal 0 HcmV?d00001 diff --git a/apps/timerclk/screenshot_settings2.png b/apps/timerclk/screenshot_settings2.png new file mode 100644 index 0000000000000000000000000000000000000000..4b12848d0953df90200e3d3fb91b25cb03296616 GIT binary patch literal 3348 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 D`=nbc literal 0 HcmV?d00001 diff --git a/apps/timerclk/screenshot_stopwatch1.png b/apps/timerclk/screenshot_stopwatch1.png new file mode 100644 index 0000000000000000000000000000000000000000..f50d7a1d11c9c5030163023711e6507c71f07d92 GIT binary patch literal 14735 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 literal 0 HcmV?d00001 diff --git a/apps/timerclk/screenshot_stopwatch2.png b/apps/timerclk/screenshot_stopwatch2.png new file mode 100644 index 0000000000000000000000000000000000000000..89f91ae1bf2a5cc8b8126dc2ef7c7072020527c3 GIT binary patch literal 14382 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 literal 0 HcmV?d00001 diff --git a/apps/timerclk/settings.js b/apps/timerclk/settings.js new file mode 100644 index 000000000..556dded98 --- /dev/null +++ b/apps/timerclk/settings.js @@ -0,0 +1,292 @@ +(function(back) { + const FILE = "timerclk.json"; + const BOOL_FORMAT = v=>v?/*LANG*/"On":/*LANG*/"Off"; + // Load settings + var settings = require('Storage').readJSON(FILE, true) || {} + settings.clock = Object.assign({ + "timeFont":"Anton", + "timeFontSize":0, + "dateFont":"6x8", + "dateFontSize":2, + "dowFont":"6x8", + "dowFontSize":2, + "specialFont":"6x8", + "specialFontSize":2, + "shortDate":true, + "showStopwatches":true, + "showTimers":true, + }, settings.clock||{}); + settings.stopwatch = Object.assign({ + "font":"Vector", + "fontSize":40, + "indexFont":"6x8", + "indexFontSize":3, + "buttonHeight":40, + }, settings.stopwatch||{}); + settings.timer = Object.assign({ + "font":"Vector", + "fontSize":40, + "indexFont":"6x8", + "indexFontSize":3, + "buttonHeight":40, + "vibrate":10, + }, settings.timer||{}); + settings.alarm = Object.assign({ + "font":"Vector", + "fontSize":40, + "indexFont":"6x8", + "indexFontSize":3, + "buttonHeight":40, + "vibrate":10, + }, settings.alarm||{}); + var timeFonts = ["Anton"].concat(g.getFonts()); + + function writeSettings() { + require('Storage').writeJSON(FILE, settings); + } + + // Show the menu + var mainMenu = { + "" : { "title" : "Timer Clock" }, + "< Back" : () => back(), + "Clock": ()=>{E.showMenu(clockMenu);}, + "Stopwatch": ()=>{E.showMenu(stopwatchMenu);}, + "Timer": ()=>{E.showMenu(timerMenu);}, + "Alarm": ()=>{E.showMenu(alarmMenu);}, + }; + var clockMenu = { + "" : { "title" : "Clock" }, + "< Back" : () => E.showMenu(mainMenu), + "time font":{ + value: 0|timeFonts.indexOf(settings.clock.timeFont), + format: v => timeFonts[v], + min: 0, max: timeFonts.length-1, + onchange: v => { + settings.clock.timeFont = timeFonts[v]; + writeSettings(); + } + }, + "time size":{ + value: 0|settings.clock.timeFontSize, + min: 0, + onchange: v => { + settings.clock.timeFontSize = v; + writeSettings(); + } + }, + "date font":{ + value: 0|g.getFonts().indexOf(settings.clock.dateFont), + format: v => g.getFonts()[v], + min: 0, max: g.getFonts().length-1, + onchange: v => { + settings.clock.dateFont = g.getFonts()[v]; + writeSettings(); + } + }, + "date size":{ + value: 0|settings.clock.dateFontSize, + min: 0, + onchange: v => { + settings.clock.dateFontSize = v; + writeSettings(); + } + }, + "dow font":{ + value: 0|g.getFonts().indexOf(settings.clock.dowFont), + format: v => g.getFonts()[v], + min: 0, max: g.getFonts().length-1, + onchange: v => { + settings.clock.dowFont = g.getFonts()[v]; + writeSettings(); + } + }, + "dow size":{ + value: 0|settings.clock.dowFontSize, + min: 0, + onchange: v => { + settings.clock.dowFontSize = v; + writeSettings(); + } + }, + "short date": { + value: !!settings.clock.shortDate, + format: BOOL_FORMAT, + onchange: v => { + settings.clock.shortDate = v; + writeSettings(); + } + }, + "stopwatches": { + value: !!settings.clock.showStopwatches, + format: v=>v?/*LANG*/"Show":/*LANG*/"Hide", + onchange: v => { + settings.clock.showStopwatches = v; + writeSettings(); + } + }, + "timers": { + value: !!settings.clock.showTimers, + format: v=>v?/*LANG*/"Show":/*LANG*/"Hide", + onchange: v => { + settings.clock.showTimers = v; + writeSettings(); + } + }, + }; + + var stopwatchMenu = { + "" : { "title" : "Stopwatch" }, + "< Back" : () => E.showMenu(mainMenu), + "font":{ + value: 0|g.getFonts().indexOf(settings.stopwatch.font), + format: v => g.getFonts()[v], + min: 0, max: g.getFonts().length-1, + onchange: v => { + settings.settings.stopwatch.font = g.getFonts()[v]; + writeSettings(); + } + }, + "fontsize":{ + value: 0|settings.stopwatch.fontSize, + min: 0, + onchange: v => { + settings.stopwatch.fontSize = v; + writeSettings(); + } + }, + "index font":{ + value: 0|g.getFonts().indexOf(settings.stopwatch.indexFont), + format: v => g.getFonts()[v], + min: 0, max: g.getFonts().length-1, + onchange: v => { + settings.settings.stopwatch.indexFont = g.getFonts()[v]; + writeSettings(); + } + }, + "index size":{ + value: 0|settings.stopwatch.indexFontSize, + min: 0, + onchange: v => { + settings.stopwatch.indexFontSize = v; + writeSettings(); + } + }, + "button height":{ + value: 0|settings.stopwatch.buttonHeight, + min: 0, + onchange: v => { + settings.stopwatch.buttonHeight = v; + writeSettings(); + } + }, + }; + var timerMenu = { + "" : { "title" : "Timer" }, + "< Back" : () => E.showMenu(mainMenu), + "font":{ + value: 0|g.getFonts().indexOf(settings.timer.font), + format: v => g.getFonts()[v], + min: 0, max: g.getFonts().length-1, + onchange: v => { + settings.settings.timer.font = g.getFonts()[v]; + writeSettings(); + } + }, + "fontsize":{ + value: 0|settings.timer.fontSize, + min: 0, + onchange: v => { + settings.timer.fontSize = v; + writeSettings(); + } + }, + "index font":{ + value: 0|g.getFonts().indexOf(settings.timer.indexFont), + format: v => g.getFonts()[v], + min: 0, max: g.getFonts().length-1, + onchange: v => { + settings.settings.timer.indexFont = g.getFonts()[v]; + writeSettings(); + } + }, + "index size":{ + value: 0|settings.timer.indexFontSize, + min: 0, + onchange: v => { + settings.timer.indexFontSize = v; + writeSettings(); + } + }, + "button height":{ + value: 0|settings.timer.buttonHeight, + min: 0, + onchange: v => { + settings.timer.buttonHeight = v; + writeSettings(); + } + }, + "vibrate":{ + value: 0|settings.timer.vibrate, + min: 0, + onchange: v=>{ + settings.timer.vibrate = v; + writeSettings(); + } + } + }; + var alarmMenu = { + "" : { "title" : "Alarm" }, + "< Back" : () => E.showMenu(mainMenu), + "font":{ + value: 0|g.getFonts().indexOf(settings.alarm.font), + format: v => g.getFonts()[v], + min: 0, max: g.getFonts().length-1, + onchange: v => { + settings.settings.alarm.font = g.getFonts()[v]; + writeSettings(); + } + }, + "fontsize":{ + value: 0|settings.alarm.fontSize, + min: 0, + onchange: v => { + settings.alarm.fontSize = v; + writeSettings(); + } + }, + "index font":{ + value: 0|g.getFonts().indexOf(settings.alarm.indexFont), + format: v => g.getFonts()[v], + min: 0, max: g.getFonts().length-1, + onchange: v => { + settings.settings.alarm.indexFont = g.getFonts()[v]; + writeSettings(); + } + }, + "index size":{ + value: 0|settings.alarm.indexFontSize, + min: 0, + onchange: v => { + settings.alarm.indexFontSize = v; + writeSettings(); + } + }, + "button height":{ + value: 0|settings.alarm.buttonHeight, + min: 0, + onchange: v => { + settings.alarm.buttonHeight = v; + writeSettings(); + } + }, + "vibrate":{ + value: 0|settings.alarm.vibrate, + min: 0, + onchange: v=>{ + settings.alarm.vibrate = v; + writeSettings(); + } + } + }; + E.showMenu(mainMenu); +}); diff --git a/apps/timerclk/stopwatch.info b/apps/timerclk/stopwatch.info new file mode 100644 index 000000000..72ad418b1 --- /dev/null +++ b/apps/timerclk/stopwatch.info @@ -0,0 +1 @@ +{"id":"timerclk","name":"tclk Stopwatch","src":"timerclk.stopwatch.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10} diff --git a/apps/timerclk/stopwatch.js b/apps/timerclk/stopwatch.js new file mode 100644 index 000000000..8ac6d30a7 --- /dev/null +++ b/apps/timerclk/stopwatch.js @@ -0,0 +1,135 @@ +var timerclk = require("timerclk.lib.js"); +const height = g.getHeight(), width = g.getWidth(); + +var all = require("Storage").readJSON("timerclk.stopwatch.json") || []; + +var settings = require('Storage').readJSON("timerclk.json", true) || {}; +settings = Object.assign({ + "font":"Vector", + "fontSize":40, + "indexFont":"6x8", + "indexFontSize":3, + "buttonHeight":40, +}, settings.stopwatch||{}); +var defaultElement = {start:null, timeAdd:0}; +var current = 0; +var editIndex = 0; +var drawInterval; +var drawIntervalTimeout; +var buttons; + +function update() { + if (drawInterval) clearInterval(drawInterval); + if (drawIntervalTimeout) clearTimeout(drawIntervalTimeout); + var interval = Math.floor(timerclk.getTime(all[current])/3600000)?1000:100; + if (all[current].start) { + drawIntervalTimeout = setTimeout(() => {drawInterval = setInterval(draw, interval); draw();}, interval - (timerclk.getTime(all[current]) % interval)); + } else { + drawInterval = null; + drawIntervalTimeout = null; + } + draw(); + drawButtons(); +} +function play() { + if (all[current].start) { // running + all[current].timeAdd += Date.now() - all[current].start; + all[current].start = null; + update(); + } else { // paused + all[current].start = Date.now(); + update(); + } + require("Storage").write("timerclk.stopwatch.json",JSON.stringify(all)); +} +function reset() { + all[current] = defaultElement.clone(); + update(); + require("Storage").write("timerclk.stopwatch.json",JSON.stringify(all)); +} +function remove() { + all.splice(current, 1); + if (current == all.length) current--; + if (all.length == 0) { + all.push(defaultElement.clone()); + current++; + } + update(); + require("Storage").write("timerclk.stopwatch.json",JSON.stringify(all)); +} + +function edit(position, change) { + if (position == 1) all[current].timeAdd += change*1000; + else if (position == 2) all[current].timeAdd += change*60000; + else if (position == 3) all[current].timeAdd += change*3600000; + require("Storage").write("timerclk.stopwatch.json",JSON.stringify(all)); +} + + +var buttonsRunning = { + reset: {pos:[0, height-settings.buttonHeight, width/2, height], callback: reset, img: timerclk.reset_img, col:"#f50"}, + play: {pos:[width/2, height-settings.buttonHeight, width, height], callback: play, img: timerclk.play_img, col:"#0ff"}, +}; +var buttonsNormal = { + reset: {pos:[0, height-settings.buttonHeight, width/2, height], callback: remove, img: timerclk.remove_img, col:buttonsRunning.reset.col}, + play: {pos:[width/2, height-settings.buttonHeight, width, height], callback: play, img: timerclk.play_img, col:buttonsRunning.play.col}, +}; +buttons = buttonsNormal; + +function drawButtons() { + if (all[current].start || all[current].time) { + buttons = buttonsRunning; + if (all[current].start) { + buttons.play.img = timerclk.pause_img; + } else { + buttons.play.img = timerclk.play_img; + } + } else { + buttons = buttonsNormal; + } + for (var button of buttons) { + g.setColor(button.col); + g.fillRect(button.pos[0], button.pos[1], button.pos[2], button.pos[3]); + g.setColor("#000"); + // scale 24px images + let iw = settings.buttonHeight-10; + var scale = iw/24; + let ix = button.pos[0] + ((button.pos[2]-button.pos[0] - iw) /2); + let iy = button.pos[1] + ((button.pos[3]-button.pos[1] - iw) /2); + g.drawImage(button.img, ix, iy, {scale: scale}); + } +} + +function draw() { + var x = g.getWidth()/2; + var y = g.getHeight()/2; + g.reset(); + + var timeStr = timerclk.formatTime(timerclk.getTime(all[current]), false, true); + g.clearRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2-settings.buttonHeight); + g.setFontAlign(0,0).setFont(settings.indexFont, settings.indexFontSize); + g.drawString(current+1, x, Bangle.appRect.y + (g.stringMetrics("0").height/2)); + g.setFontAlign(0,0).setFont(settings.font, settings.fontSize); + g.drawString(timeStr,x,y); + + var start = (width-g.stringMetrics(timeStr).width)/2; + timeStr = timeStr.split(".")[0].split(":"); + if (timeStr.length < 3) timeStr = [""].concat(timeStr); + var markerPosChange = g.stringMetrics("__").width/2; + if (editIndex == 3) x = start + g.stringMetrics(timeStr[0]).width - markerPosChange; + else if (editIndex == 2) x = start + g.stringMetrics(timeStr[0]+":"+timeStr[1]).width - markerPosChange; + else if (editIndex == 1) x = start + g.stringMetrics(timeStr[0]+":"+timeStr[1]+":"+timeStr[2]).width - markerPosChange; + else x = 0; + if (x) g.drawString("__", x, y); + dragBorderHrsMins = start+g.stringMetrics(timeStr[0]).width+g.stringMetrics(":").width/2; + dragBorderMinsSecs = start+g.stringMetrics(timeStr[0]+":"+timeStr[1]).width+g.stringMetrics(":").width/2; +} + +if (all.length == 0) { + all.push(defaultElement.clone()); +} +timerclk.registerControls(this); + +Bangle.loadWidgets(); +Bangle.drawWidgets(); +update(); diff --git a/apps/timerclk/timer.alert.js b/apps/timerclk/timer.alert.js new file mode 100644 index 000000000..f51ea6767 --- /dev/null +++ b/apps/timerclk/timer.alert.js @@ -0,0 +1,62 @@ +if (timerclkTimerTimeout) clearInterval(timerclkTimerTimeout); +var timerclk = require("timerclk.lib.js"); +var settings = require('Storage').readJSON("timerclk.json", true) || {}; +settings = Object.assign({ + "vibrate":10 +}, settings.timer||{}); + +function showTimer(timer) { + Bangle.loadWidgets(); + Bangle.drawWidgets(); + Bangle.setLocked(false); + E.showPrompt("Timer finished!",{ + title:"TIMER!", + buttons : {/*LANG*/"Ok":true} + }).then(function(ok) { + buzzCount = 0; + if (ok) { + timer.time += Date.now() - timer.start; + timer.start = null; + } + require("Storage").write("timerclk.timer.json",JSON.stringify(timers)); + load(); + }); + function vibrate(counter) { + VIBRATE.write(1); + setTimeout(() => VIBRATE.write(0), 100); + if (--counter) setTimeout(() => vibrate(counter), 250); + } + function buzz() { + if ((require('Storage').readJSON('setting.json',1)||{}).quiet>1) return; // total silence + vibrate(4); + if (buzzCount--) + setTimeout(buzz, 3000); + else { // auto-snooze + buzzCount = settings.vibrate; + setTimeout(buzz, 600000); + } + } + var buzzCount = settings.vibrate; + buzz(); +} + +// Check for timers +console.log("checking for timers..."); +var timers = require("Storage").readJSON("timerclk.timer.json",1)||[]; +var active = timers.filter(e=>e.start); +if (active.length) { + // if there's an timer, show it + active = active.sort((a,b)=>{ + var at = a.time; + if (a.start) at += Date.now()-a.start; + at = a.period-at; + var bt = b.time; + if (b.start) bt += Date.now()-b.start; + bt = b.period-bt; + return at-bt; + }); + showTimer(active[0]); +} else { + // otherwise just go back to default app + setTimeout(load, 100); +} diff --git a/apps/timerclk/timer.info b/apps/timerclk/timer.info new file mode 100644 index 000000000..39a338693 --- /dev/null +++ b/apps/timerclk/timer.info @@ -0,0 +1 @@ +{"id":"timerclk","name":"tclk Timer","src":"timerclk.timer.js","icon":"timerclk.img","version":"0.01","tags":"","files":"","sortorder":10} diff --git a/apps/timerclk/timer.js b/apps/timerclk/timer.js new file mode 100644 index 000000000..060c07813 --- /dev/null +++ b/apps/timerclk/timer.js @@ -0,0 +1,139 @@ +var timerclk = require("timerclk.lib.js"); +const height = g.getHeight(), width = g.getWidth(); + +var all = require("Storage").readJSON("timerclk.timer.json") || []; +var settings = require('Storage').readJSON("timerclk.json", true) || {}; +settings = Object.assign({ + "font":"Vector", + "fontSize":40, + "indexFont":"6x8", + "indexFontSize":3, + "buttonHeight":40, + "vibrate":4, +}, settings = settings.timer||{}); +var defaultElement = {time:300000, start:null, timeAdd:0}; + +var current = 0; +var editIndex = 0; +var drawInterval; +var drawIntervalTimeout; +var buttons; +var dragBorderHrsMins=0, dragBorderMinsSecs=0; + +function update() { + if (drawInterval) clearInterval(drawInterval); + if (drawIntervalTimeout) clearTimeout(drawIntervalTimeout); + if (all[current].start) { + drawIntervalTimeout = setTimeout(() => {drawInterval = setInterval(draw, 1000); draw();}, 1000 - (timerclk.getTime(all[current]) % 1000)); + } else { + drawInterval = null; + drawIntervalTimeout = null; + } + draw(); + drawButtons(); +} +function play() { + if (all[current].start) { // running + all[current].timeAdd += Date.now() - all[current].start; + all[current].start = null; + update(); + } else { // paused + all[current].start = Date.now(); + update(); + } + require("Storage").write("timerclk.timer.json",JSON.stringify(all)); + timerclkCheckTimers(); +} +function reset() { + all[current] = defaultElement.clone(); + update(); + require("Storage").write("timerclk.timer.json",JSON.stringify(all)); + timerclkCheckTimers(); +} +function remove() { + all.splice(current, 1); + if (current == all.length) current--; + if (all.length == 0) { + all.push(defaultElement.clone()); + current++; + } + update(); + require("Storage").write("timerclk.timer.json",JSON.stringify(all)); + timerclkCheckTimers(); +} + +function edit(position, change) { + if (position == 1) all[current].time += change*1000; + else if (position == 2) all[current].time += change*60000; + else if (position == 3) all[current].time += change*3600000; + require("Storage").write("timerclk.timer.json",JSON.stringify(all)); + timerclkCheckTimers(); +} + +var buttonsRunning = { + reset: {pos:[0, height-settings.buttonHeight, width/2, height], callback: reset, img: timerclk.reset_img, col:"#f50"}, + play: {pos:[width/2, height-settings.buttonHeight, width, height], callback: play, img: timerclk.play_img, col:"#0ff"}, +}; +var buttonsNormal = { + reset: {pos:[0, height-settings.buttonHeight, width/2, height], callback: remove, img: timerclk.remove_img, col:buttonsRunning.reset.col}, + play: {pos:[width/2, height-settings.buttonHeight, width, height], callback: play, img: timerclk.play_img, col:buttonsRunning.play.col}, +}; +buttons = buttonsNormal; + + +function drawButtons() { + if (all[current].start || all[current].timeAdd) { + buttons = buttonsRunning; + if (all[current].start) { + buttons.play.img = timerclk.pause_img; + } else { + buttons.play.img = timerclk.play_img; + } + } else { + buttons = buttonsNormal; + } + for (var button of buttons) { + g.setColor(button.col); + g.fillRect(button.pos[0], button.pos[1], button.pos[2], button.pos[3]); + g.setColor("#000"); + // scale 24px images + let iw = settings.buttonHeight-10; + var scale = iw/24; + let ix = button.pos[0] + ((button.pos[2]-button.pos[0] - iw) /2); + let iy = button.pos[1] + ((button.pos[3]-button.pos[1] - iw) /2); + g.drawImage(button.img, ix, iy, {scale: scale}); + } +} + +function draw() { + var x = g.getWidth()/2; + var y = g.getHeight()/2; + g.reset(); + + var time = all[current].time - timerclk.getTime(all[current]); + g.clearRect(Bangle.appRect.x, Bangle.appRect.y, Bangle.appRect.x2, Bangle.appRect.y2-settings.buttonHeight); + g.setFontAlign(0,0).setFont(settings.indexFont, settings.indexFontSize); + g.drawString(current+1, x, Bangle.appRect.y + (g.stringMetrics("0").height/2)); + g.setFontAlign(0,0).setFont(settings.font, settings.fontSize); + var timeStr = timerclk.formatTime(time, false, false, true); + g.drawString(timeStr,x,y); + + var start = (width-g.stringMetrics(timeStr).width)/2; + timeStr = timeStr.split(":"); + var markerPosChange = g.stringMetrics("__").width/2; + if (editIndex == 3) x = start + g.stringMetrics(timeStr[0]).width - markerPosChange; + else if (editIndex == 2) x = start + g.stringMetrics(timeStr[0]+":"+timeStr[1]).width - markerPosChange; + else if (editIndex == 1) x = start + g.stringMetrics(timeStr[0]+":"+timeStr[1]+":"+timeStr[2]).width - markerPosChange; + else x = 0; + if (x) g.drawString("__", x, y); + dragBorderHrsMins = start+g.stringMetrics(timeStr[0]).width+g.stringMetrics(":").width/2; + dragBorderMinsSecs = start+g.stringMetrics(timeStr[0]+":"+timeStr[1]).width+g.stringMetrics(":").width/2; +} + +if (all.length == 0) { + all.push(defaultElement.clone()); +} +timerclk.registerControls(this); +Bangle.loadWidgets(); +Bangle.drawWidgets(); +update(); diff --git a/apps/timerclk/wid.js b/apps/timerclk/wid.js new file mode 100644 index 000000000..e3ddeb791 --- /dev/null +++ b/apps/timerclk/wid.js @@ -0,0 +1,7 @@ +WIDGETS["timerclk.alarm"]={area:"tl",width:0,draw:function() { + if (this.width) g.reset().drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y); + },reload:function() { + WIDGETS["timerclk.alarm"].width = (require('Storage').readJSON('timerclk.alarm.json',1)||[]).some(alarm=>alarm.on) ? 24 : 0; + } +}; +WIDGETS["timerclk.alarm"].reload();