diff --git a/apps/smpltmr/ChangeLog b/apps/smpltmr/ChangeLog index 07afedd21..bf128e2fb 100644 --- a/apps/smpltmr/ChangeLog +++ b/apps/smpltmr/ChangeLog @@ -1 +1,2 @@ -0.01: Release \ No newline at end of file +0.01: Release +0.02: Rewrite with new interface \ No newline at end of file diff --git a/apps/smpltmr/README.md b/apps/smpltmr/README.md index 1296166e2..2c6e6f9ab 100644 --- a/apps/smpltmr/README.md +++ b/apps/smpltmr/README.md @@ -1,21 +1,12 @@ # Simple Timer -A simple app to set a timer quickly. Simply tab on top/bottom/left/right -to select the minutes and tab in the middle of the screen to start/stop -the timer. Note that this timer depends on qalarm. - -# Overview -If you open the app, you can simply control the timer -by clicking on top, bottom, left or right of the screen. -If you tab at the middle of the screen, the timer is -started / stopped. - -![](description.png) +A simple app to set a timer quickly. Drag or tap on the up and down buttons over the hour, minute or second to set the time. +This app uses the `sched` library, which allows the timer to continue to run in the background when this app is closed. # Creator [David Peer](https://github.com/peerdavid) - +[Sir Indy](https://github.com/sir-indy) # Thanks to... Time icon created by CreativeCons - Flaticon \ No newline at end of file diff --git a/apps/smpltmr/app.js b/apps/smpltmr/app.js index eb01e27d0..5f893b054 100644 --- a/apps/smpltmr/app.js +++ b/apps/smpltmr/app.js @@ -1,124 +1,173 @@ -/* - * SIMPLE TIMER - * - * Creator: David Peer - * Date: 02/2022 - */ +const secondsToTime = (s) => new Object({h:Math.floor((s/3600) % 24), m:Math.floor((s/60) % 60), s:Math.floor(s % 60)}); +const clamp = (num, min, max) => Math.min(Math.max(num, min), max); +function formatTime(s) { + var t = secondsToTime(s); + if (t.h) { + return t.h + ':' + ("0" + t.m).substr(-2) + ':' + ("0" + t.s).substr(-2); + } else { + return t.m + ':' + ("0" + t.s).substr(-2); + } +} +const timerID = "simpletimer"; Bangle.loadWidgets(); +Bangle.drawWidgets(); +var Layout = require("Layout"); +var seconds = 5 * 60; // Default to 5 minutes +var drawTimeout; +var imgArrow = Graphics.createImage(` + x + xxx + xxx + xxxxx + xxxxx + xxx xxx + xxx xxx +xxx xxx +xxx xxx +`); -const alarm = require("sched"); +const imgPause = atob("GBiBAP+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B/w=="); +const imgPlay = atob("GBiBAIAAAOAAAPgAAP4AAP+AAP/gAP/4AP/+AP//gP//4P//+P///v///v//+P//4P//gP/+AP/4AP/gAP+AAP4AAPgAAOAAAIAAAA=="); -const TIMER_IDX = "smpltmr"; -const screenWidth = g.getWidth(); -const screenHeight = g.getHeight(); -const cx = parseInt(screenWidth/2); -const cy = parseInt(screenHeight/2)-12; -var minutes = 5; -var interval; //used for the 1 second interval timer - - -function isTimerEnabled(){ - var alarmObj = alarm.getAlarm(TIMER_IDX); - if(alarmObj===undefined || !alarmObj.on){ - return false; +function onDrag(event) { + Bangle.buzz(20, 0.3); + var diff = -Math.round(event.dy/5); + if (event.x < timePickerLayout.hours.w) { + diff *= 3600; + } else if (event.x > timePickerLayout.mins.x && event.x < timePickerLayout.secs.x) { + diff *= 60; } - - return true; + updateTimePicker(diff); } -function getTimerMin(){ - var alarmObj = alarm.getAlarm(TIMER_IDX); - return Math.round(alarm.getTimeToAlarm(alarmObj)/(60*1000)); -} - -function setTimer(minutes){ - alarm.setAlarm(TIMER_IDX, { - // msg : "Simple Timer", - timer : minutes*60*1000, - }); - alarm.reload(); -} - -function deleteTimer(){ - alarm.setAlarm(TIMER_IDX, undefined); - alarm.reload(); -} - -setWatch(_=>load(), BTN1); -function draw(){ - g.clear(1); - Bangle.drawWidgets(); - - if (interval) { - clearInterval(interval); +function onTouch(button, xy) { + var touchMidpoint = timePickerLayout.hours.y + timePickerLayout.hours.h/2; + var diff = 0; + if (xy.y > 24 && xy.y < touchMidpoint - 10) { + Bangle.buzz(40, 0.3); + diff = 1; + } else if (xy.y > touchMidpoint + 10 && xy.y < timePickerLayout.btnStart.y) { + Bangle.buzz(40, 0.3); + diff = -1; + } else if (xy.y > timePickerLayout.btnStart.y) { + Bangle.buzz(40, 0.6); + runTimer(); + return; } - interval = undefined; - - // Write time - g.setFontAlign(0, 0, 0); - g.setFont("Vector", 32).setFontAlign(0,-1); - - var started = isTimerEnabled(); - var text = minutes + " min."; - if(started){ - var min = getTimerMin(); - text = min + " min."; + if (xy.x < timePickerLayout.hours.w) { + diff *= 3600; + } else if (xy.x > timePickerLayout.mins.x && xy.x < timePickerLayout.secs.x) { + diff *= 60; } + updateTimePicker(diff); +} - var rectWidth = parseInt(g.stringWidth(text) / 2); - - if(started){ - interval = setInterval(draw, 1000); - g.setColor("#ff0000"); +function onButton() { + var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm(timerID)); + g.clearRect(Bangle.appRect); + if (timeToNext != undefined) { + runTimer(); } else { - g.setColor(g.theme.fg); + runTimePicker(); } - g.fillRect(cx-rectWidth-5, cy-5, cx+rectWidth, cy+30); - - g.setColor(g.theme.bg); - g.drawString(text, cx, cy); } +function updateTimePicker(diff) { + seconds = clamp(seconds + (diff || 0), 0, 24 * 3600 - 1); + var set_time = secondsToTime(seconds); + updateLayoutField(timePickerLayout, 'hours', set_time.h); + updateLayoutField(timePickerLayout, 'mins', set_time.m); + updateLayoutField(timePickerLayout, 'secs', set_time.s); +} -Bangle.on('touch', function(btn, e){ - var left = parseInt(g.getWidth() * 0.25); - var right = g.getWidth() - left; - var upper = parseInt(g.getHeight() * 0.25); - var lower = g.getHeight() - upper; +function updateLayoutField(layout, field, value) { + layout.clear(layout[field]); + layout[field].label = value; + layout.render(layout[field]); +} - var isLeft = e.x < left; - var isRight = e.x > right; - var isUpper = e.y < upper; - var isLower = e.y > lower; - var isMiddle = !isLeft && !isRight && !isUpper && !isLower; - var started = isTimerEnabled(); +function updateTimer() { + var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm(timerID)); + updateLayoutField(timerLayout, 'timer', formatTime(timeToNext / 1000)); + queueDraw(1000); +} - if(isRight && !started){ - minutes += 1; - Bangle.buzz(40, 0.3); - } else if(isLeft && !started){ - minutes -= 1; - Bangle.buzz(40, 0.3); - } else if(isUpper && !started){ - minutes += 5; - Bangle.buzz(40, 0.3); - } else if(isLower && !started){ - minutes -= 5; - Bangle.buzz(40, 0.3); - } else if(isMiddle) { - if(!started){ - setTimer(minutes); - } else { - deleteTimer(); - } - Bangle.buzz(80, 0.6); - } - minutes = Math.max(0, minutes); +function queueDraw(millisecs) { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + updateTimer(); + }, millisecs - (Date.now() % millisecs)); +} - draw(); +function timerStop() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + seconds = require("sched").getTimeToAlarm(require("sched").getAlarm(timerID)) / 1000; + require("sched").setAlarm(timerID, undefined); + require("sched").reload(); + runTimePicker(); +} + +function runTimePicker() { + g.clearRect(Bangle.appRect); + Bangle.setUI({ + mode : "custom", + touch : function(n,e) {onTouch(n,e);}, + drag : function(e) {onDrag(e);}, + btn : function(n) {onButton();}, + }); + timePickerLayout.render(); + updateTimePicker(); + //timePickerLayout.debug(); +} + +function runTimer() { + require("sched").setAlarm(timerID, { + vibrate : ".-.-", + hidden: true, + timer : seconds * 1000 + }); + require("sched").reload(); + g.clearRect(Bangle.appRect); + timerLayout.render(); + updateTimer(); +} + +var timePickerLayout = new Layout({ + type:"v", c: [ + {type:undefined, height:2}, + {type:"h", c: [ + {type:"v", width:g.getWidth()/3, c: [ + {type:"txt", font:"6x8", label:/*LANG*/"Hours", col:g.theme.fg2}, + {type:"img", pad:8, src:imgArrow, col:g.theme.fg2}, + {type:"txt", font:"20%", label:"00", id:"hours", filly:1, fillx:1}, + {type:"img", pad:8, src:imgArrow, col:g.theme.fg2, r:2} + ]}, + {type:"v", width:g.getWidth()/3, c: [ + {type:"txt", font:"6x8", label:/*LANG*/"Minutes", col:g.theme.fg2}, + {type:"img", pad:8, src:imgArrow, col:g.theme.fg2}, + {type:"txt", font:"20%", label:"00", id:"mins", filly:1, fillx:1}, + {type:"img", pad:8, src:imgArrow, col:g.theme.fg2, r:2} + ]}, + {type:"v", width:g.getWidth()/3, c: [ + {type:"txt", font:"6x8", label:/*LANG*/"Seconds", col:g.theme.fg2}, + {type:"img", pad:8, src:imgArrow, col:g.theme.fg2}, + {type:"txt", font:"20%", label:"00", id:"secs", filly:1, fillx:1}, + {type:"img", pad:8, src:imgArrow, col:g.theme.fg2, r:2} + ]}, + ]}, + {type:"btn", src:imgPlay, id:"btnStart", fillx:1 } + ], filly:1 }); -g.reset(); -draw(); \ No newline at end of file +var timerLayout = new Layout({ + type:"v", c: [ + {type:"txt", font:"22%", label:"0:00", id:"timer", fillx:1, filly:1 }, + {type:"btn", src:imgPause, cb: l=>timerStop(), fillx:1 } + ], filly:1 +}); + +onButton(); \ No newline at end of file diff --git a/apps/smpltmr/description.png b/apps/smpltmr/description.png deleted file mode 100644 index 1286d1ab9..000000000 Binary files a/apps/smpltmr/description.png and /dev/null differ diff --git a/apps/smpltmr/metadata.json b/apps/smpltmr/metadata.json index 06bad962d..ee5c7ce9b 100644 --- a/apps/smpltmr/metadata.json +++ b/apps/smpltmr/metadata.json @@ -2,10 +2,10 @@ "id": "smpltmr", "name": "Simple Timer", "shortName": "Simple Timer", - "version": "0.01", + "version": "0.02", "description": "A very simple app to start a timer.", "icon": "app.png", - "tags": "tool", + "tags": "tool,alarm,timer", "dependencies": {"scheduler":"type"}, "supports": ["BANGLEJS2"], "screenshots": [{"url":"screenshot.png"}, {"url": "screenshot_2.png"}], diff --git a/apps/timersimple/ChangeLog b/apps/timersimple/ChangeLog deleted file mode 100644 index 7b83706bf..000000000 --- a/apps/timersimple/ChangeLog +++ /dev/null @@ -1 +0,0 @@ -0.01: First release diff --git a/apps/timersimple/README.md b/apps/timersimple/README.md deleted file mode 100644 index 894a46e7a..000000000 --- a/apps/timersimple/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Simple Timer - -TESTING - DO NOT INSTALL, MAY BE BROKEN - -Does one thing well. Set a time in hours, minutes and seconds, and alerts you when time is up. Opening the app while the timer is running (or just leaving the app open) shows how much time is left on the timer. This is the part I felt was missing from the Alarms and Timer app. - -Drag or tap on the up and down buttons over the hour, minute or second to set the time. - -![](timersimple-scr1.png) -![](timersimple-scr2.png) - -Written by: [Sir Indy](https://github.com/sir-indy) For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/) diff --git a/apps/timersimple/icons8-time-span-48.png b/apps/timersimple/icons8-time-span-48.png deleted file mode 100644 index 9a9dfb6ab..000000000 Binary files a/apps/timersimple/icons8-time-span-48.png and /dev/null differ diff --git a/apps/timersimple/metadata.json b/apps/timersimple/metadata.json deleted file mode 100644 index 5d0e5d5e4..000000000 --- a/apps/timersimple/metadata.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "id":"timersimple", - "name":"Timer Simple", - "shortName": "Timer Simple", - "version": "0.01", - "description": "Sets a single timer, and tells you how long left.", - "readme": "README.md", - "icon":"icons8-time-span-48.png", - "screenshots": [{"url":"timersimple-scr1.png"},{"url":"timersimple-scr2.png"}], - "tags": "tool,alarm,timer", - "supports": ["BANGLEJS2"], - "dependencies": {"scheduler":"type"}, - "allow_emulator": true, - "storage": [ - {"name":"timersimple.app.js","url":"timersimple.app.js"}, - {"name":"timersimple.img","url":"timersimple.icon.js","evaluate":true} - ] - } \ No newline at end of file diff --git a/apps/timersimple/timersimple-scr1.png b/apps/timersimple/timersimple-scr1.png deleted file mode 100644 index 5556f8ecc..000000000 Binary files a/apps/timersimple/timersimple-scr1.png and /dev/null differ diff --git a/apps/timersimple/timersimple-scr2.png b/apps/timersimple/timersimple-scr2.png deleted file mode 100644 index 819adb960..000000000 Binary files a/apps/timersimple/timersimple-scr2.png and /dev/null differ diff --git a/apps/timersimple/timersimple.app.js b/apps/timersimple/timersimple.app.js deleted file mode 100644 index 1c775b908..000000000 --- a/apps/timersimple/timersimple.app.js +++ /dev/null @@ -1,185 +0,0 @@ -const secondsToTime = (s) => new Object({h:Math.floor((s/3600) % 24), m:Math.floor((s/60) % 60), s:Math.floor(s % 60)}); -const clamp = (num, min, max) => Math.min(Math.max(num, min), max); -function formatTime(s) { - var t = secondsToTime(s); - if (t.h) { - return t.h + ':' + ("0" + t.m).substr(-2) + ':' + ("0" + t.s).substr(-2); - } else { - return t.m + ':' + ("0" + t.s).substr(-2); - } -} -const timerID = "simpletimer"; - -Bangle.loadWidgets(); -Bangle.drawWidgets(); - -var Layout = require("Layout"); -var seconds = 5 * 60; // Default to 5 minutes -var drawTimeout; -var timerLayout; -var timePickerLayout; -var imgArrow = Graphics.createImage(` - x - xxx - xxx - xxxxx - xxxxx - xxx xxx - xxx xxx -xxx xxx -xxx xxx -`); - -const imgPause = atob("GBiBAP+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B//+B/w=="); -const imgPlay = atob("GBiBAIAAAOAAAPgAAP4AAP+AAP/gAP/4AP/+AP//gP//4P//+P///v///v//+P//4P//gP/+AP/4AP/gAP+AAP4AAPgAAOAAAIAAAA=="); - -function onDrag(event) { - Bangle.buzz(20, 0.3); - var diff = -Math.round(event.dy/5); - if (event.x < timePickerLayout.hours.w) { - diff *= 3600; - } else if (event.x > timePickerLayout.mins.x && event.x < timePickerLayout.secs.x) { - diff *= 60; - } - updateTimePicker(diff); -} - -function onTouch(button, xy) { - var touchMidpoint = timePickerLayout.hours.y + timePickerLayout.hours.h/2; - var diff = 0; - if (xy.y > 24 && xy.y < touchMidpoint - 10) { - Bangle.buzz(40, 0.3); - diff = 1; - } else if (xy.y > touchMidpoint + 10 && xy.y < timePickerLayout.btnStart.y) { - Bangle.buzz(40, 0.3); - diff = -1; - } else if (xy.y > timePickerLayout.btnStart.y) { - Bangle.buzz(40, 0.6); - runTimer(); - return; - } - if (xy.x < timePickerLayout.hours.w) { - diff *= 3600; - } else if (xy.x > timePickerLayout.mins.x && xy.x < timePickerLayout.secs.x) { - diff *= 60; - } - updateTimePicker(diff); -} - -function updateTimePicker(diff) { - seconds = clamp(seconds + (diff || 0), 0, 24 * 3600 - 1); - var set_time = secondsToTime(seconds); - updateLayoutField(timePickerLayout, 'hours', set_time.h); - updateLayoutField(timePickerLayout, 'mins', set_time.m); - updateLayoutField(timePickerLayout, 'secs', set_time.s); -} - -function updateLayoutField(layout, field, value) { - layout.clear(layout[field]); - layout[field].label = value; - layout.render(layout[field]); -} - -function updateTimer() { - var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm(timerID)); - updateLayoutField(timerLayout, 'timer', formatTime(timeToNext / 1000)); - //var d = new Date(); - //updateLayoutField(timerLayout, 'time', require("locale").time(d,1)); - queueDraw(1000); -} - -function queueDraw(millisecs) { - if (drawTimeout) clearTimeout(drawTimeout); - drawTimeout = setTimeout(function() { - drawTimeout = undefined; - updateTimer(); - }, millisecs - (Date.now() % millisecs)); -} - -function timerStop() { - if (drawTimeout) clearTimeout(drawTimeout); - drawTimeout = undefined; - seconds = require("sched").getTimeToAlarm(require("sched").getAlarm(timerID)) / 1000; - require("sched").setAlarm(timerID, undefined); - require("sched").reload(); - runTimePicker(); -} - -var timePickerLayoutCode = { - type:"v", c: [ - {type:undefined, height:2}, - //{type:"txt", font:"15%", label:"TIMER", id:"title"}, - {type:"h", c: [ - {type:"v", width:g.getWidth()/3, c: [ - {type:"txt", font:"6x8", label:/*LANG*/"Hours", col:g.theme.fg2}, - {type:"img", pad:8, src:imgArrow, col:g.theme.fg2}, - {type:"txt", font:"20%", label:"00", id:"hours", filly:1, fillx:1}, - {type:"img", pad:8, src:imgArrow, col:g.theme.fg2, r:2} - ]}, - {type:"v", width:g.getWidth()/3, c: [ - {type:"txt", font:"6x8", label:/*LANG*/"Minutes", col:g.theme.fg2}, - {type:"img", pad:8, src:imgArrow, col:g.theme.fg2}, - {type:"txt", font:"20%", label:"00", id:"mins", filly:1, fillx:1}, - {type:"img", pad:8, src:imgArrow, col:g.theme.fg2, r:2} - ]}, - {type:"v", width:g.getWidth()/3, c: [ - {type:"txt", font:"6x8", label:/*LANG*/"Seconds", col:g.theme.fg2}, - {type:"img", pad:8, src:imgArrow, col:g.theme.fg2}, - {type:"txt", font:"20%", label:"00", id:"secs", filly:1, fillx:1}, - {type:"img", pad:8, src:imgArrow, col:g.theme.fg2, r:2} - ]}, - ]}, - {type:"btn", src:imgPlay, id:"btnStart", fillx:1 } - //{type:"btn", label:'Start', font:"20%", id:"btnStart", fillx:0 } - ], filly:1 -}; - -var timerLayoutCode = { - type:"v", c: [ - {type:undefined, height:8}, - //{type:"txt", font:"6x8", label:/*LANG*/"Timer", id:"title", col:g.theme.fg2}, - {type:"txt", font:"22%", label:"0:00", id:"timer", fillx:1, filly:1 }, - //{type:"h", c: [ - // {type:"txt", font:"6x8", pad:8, label:/*LANG*/"Time Now:", halign:-1, col:g.theme.fg2}, - // {type:"txt", font:"6x8", label:"00:00", id:"time", halign:1, col:g.theme.fg2}, - //]}, - {type:"btn", src:imgPause, cb: l=>timerStop(), fillx:1 } - ], filly:1 -}; - -function runTimePicker() { - g.clearRect(Bangle.appRect); - timePickerLayout = new Layout(timePickerLayoutCode); - Bangle.setUI({ - mode : "custom", - touch : function(n,e) {onTouch(n,e);}, - drag : function(e) {onDrag(e);}, - btn : function(n) {runTimer();}, - }); - timePickerLayout.render(); - updateTimePicker(); - //timePickerLayout.debug(); -} - -function runTimer() { - require("sched").setAlarm(timerID, { - vibrate : ".-.-", - hidden: true, - timer : seconds * 1000 - }); - require("sched").reload(); - g.clearRect(Bangle.appRect); - timerLayout = new Layout(timerLayoutCode); - timerLayout.render(); - updateTimer(); -} - -var timeToNext = require("sched").getTimeToAlarm(require("sched").getAlarm(timerID)); -if (timeToNext != undefined) { - g.clearRect(Bangle.appRect); - timerLayout = new Layout(timerLayoutCode); - timerLayout.render(); - updateTimer(); -} else { - runTimePicker(); -} diff --git a/apps/timersimple/timersimple.icon.js b/apps/timersimple/timersimple.icon.js deleted file mode 100644 index cd40303a0..000000000 --- a/apps/timersimple/timersimple.icon.js +++ /dev/null @@ -1 +0,0 @@ -require("heatshrink").decompress(atob("mEwwcBkmSpIC/ARf//9JkQRMCAIRBAwIRKv4RFpARIz4QCCIdJiREJAAgJCCI0nCI3+BgOJCIs/CI3/9MkyJoIAAxuGp4RJ8gRQ/mSogRDu4RJNwKSEqXfCJPSCImSrYRJ+SkEyVfCJP6CIo1B4wRHUgIREA4MAj4SHCIeUCIP//EAt4RHkQRF//ggIDB+EHCJf/wEAAAQRM/0CoAmCCJf/4VDI5pcCNwoRKNZ4RMUIQRLYowAIYozpRrYRJ+QREqVLCJPSpGSCIdJv5GIyQREpVJfA///mSogRDpNJloRH8mSBwQRDku/CIwMBCIspkmXCAvpkmRCIslAYKkETwMkxIRFkmkyVLNwYJCBwgCDAwyeEAQqSBAwiMEAQwGFBxACDygDBkQOKAX4CD")) \ No newline at end of file