diff --git a/apps/multitimer/ChangeLog b/apps/multitimer/ChangeLog index 9a2ab0ff44..842384c8db 100644 --- a/apps/multitimer/ChangeLog +++ b/apps/multitimer/ChangeLog @@ -1,3 +1,4 @@ 0.01: Initial version 0.02: Update for time_utils module 0.03: Use default Bangle formatter for booleans +0.04: Remove copied sched alarm.js & import newer features (oneshot alarms) diff --git a/apps/multitimer/README.md b/apps/multitimer/README.md index f1e2eb281f..04bdf4a6ed 100644 --- a/apps/multitimer/README.md +++ b/apps/multitimer/README.md @@ -2,9 +2,10 @@ With this app, you can set timers and chronographs (stopwatches) and watch them count down/up in real time. You can also set alarms - swipe left or right to switch between the three functions. "Hard mode" is also available for timers and alarms. It will double the number of buzz counts and you will have to swipe the screen five to eight times correctly - make a mistake, and you will need to start over. +"Delete after expiration" can be set on a timer/alarm to have it delete itself once it's sounded (the same as the alarm app). ## WARNING * Editing timers in another app (such as the default Alarm app) is not recommended. Editing alarms should not be a problem (in theory). -* This app uses the [Scheduler library](https://banglejs.com/apps/?id=sched). +* This app uses the [Scheduler library](https://banglejs.com/apps/?id=sched). * To avoid potential conflicts with other apps that uses sched (especially ones that make use of the data and js field), this app only lists timers and alarms that it created - any made outside the app will be ignored. GB alarms are currently an exception as they do not make use of the data and js field. * A keyboard app is only used for adding messages to timers and is therefore not strictly needed. diff --git a/apps/multitimer/alarm.js b/apps/multitimer/alarm.js index eb1b3b259b..b202ae6623 100644 --- a/apps/multitimer/alarm.js +++ b/apps/multitimer/alarm.js @@ -1,148 +1,89 @@ -//sched.js, modified -// Chances are boot0.js got run already and scheduled *another* -// 'load(sched.js)' - so let's remove it first! -if (Bangle.SCHED) { - clearInterval(Bangle.SCHED); - delete Bangle.SCHED; -} - -function hardMode(tries, max) { - var R = Bangle.appRect; - - function adv() { - tries++; - hardMode(tries, max); - } - - if (tries < max) { - g.clear(); - g.reset(); - g.setClipRect(R.x,R.y,R.x2,R.y2); - var code = Math.abs(E.hwRand()%4); - if (code == 0) dir = "up"; - else if (code == 1) dir = "right"; - else if (code == 2) dir = "down"; - else dir = "left"; - g.setFont("6x8:2").setFontAlign(0,0).drawString(tries+"/"+max+"\nSwipe "+dir, (R.x2-R.x)/2, (R.y2-R.y)/2); - var drag; - Bangle.setUI({ - mode : "custom", - drag : e=>{ - if (!drag) { // start dragging - drag = {x: e.x, y: e.y}; - } else if (!e.b) { // released - const dx = e.x-drag.x, dy = e.y-drag.y; - drag = null; - //horizontal swipes - if (Math.abs(dx)>Math.abs(dy)+10) { - //left - if (dx<0 && code == 3) adv(); - //right - else if (dx>0 && code == 1) adv(); - //wrong swipe - reset - else startHM(); - } - //vertical swipes - else if (Math.abs(dy)>Math.abs(dx)+10) { - //up - if (dy<0 && code == 0) adv(); - //down - else if (dy>0 && code == 2) adv(); - //wrong swipe - reset - else startHM(); - } - } - } - }); - } - else { - if (!active[0].timer) active[0].last = (new Date()).getDate(); - if (!active[0].rp) active[0].on = false; - if (active[0].timer) active[0].timer = active[0].data.ot; - require("sched").setAlarms(alarms); - load(); - } -} - -function startHM() { - //between 5-8 random swipes - hardMode(0, Math.abs(E.hwRand()%4)+5); -} - -function showAlarm(alarm) { - const settings = require("sched").getSettings(); - - let msg = ""; - if (alarm.timer) msg += require("time_utils").formatTime(alarm.timer); - if (alarm.msg) { - msg += "\n"+alarm.msg; - } - else msg = atob("ACQswgD//33vRcGHIQAAABVVVAAAAAAAABVVVAAAAAAAABVVVAAAAAAAABVVVAAAAAAAABVVVAAAAAAAABVVVAAAAAAAAAP/wAAAAAAAAAP/wAAAAAAAAAqqoAPAAAAAAqqqqoP8AAAAKqqqqqv/AAACqqqqqqq/wAAKqqqlWqqvwAAqqqqlVaqrAACqqqqlVVqqAAKqqqqlVVaqgAKqaqqlVVWqgAqpWqqlVVVqoAqlWqqlVVVaoCqlV6qlVVVaqCqVVfqlVVVWqCqVVf6lVVVWqKpVVX/lVVVVqqpVVV/+VVVVqqpVVV//lVVVqqpVVVfr1VVVqqpVVVfr1VVVqqpVVVb/lVVVqqpVVVW+VVVVqqpVVVVVVVVVqiqVVVVVVVVWqCqVVVVVVVVWqCqlVVVVVVVaqAqlVVVVVVVaoAqpVVVVVVVqoAKqVVVVVVWqgAKqlVVVVVaqgACqpVVVVVqqAAAqqlVVVaqoAAAKqqVVWqqgAAACqqqqqqqAAAAAKqqqqqgAAAAAAqqqqoAAAAAAAAqqoAAAAA==")+" "+msg; - - Bangle.loadWidgets(); - Bangle.drawWidgets(); - - let buzzCount = settings.buzzCount; - - if (alarm.data.hm && alarm.data.hm == true) { - //hard mode extends auto-snooze time - buzzCount = buzzCount * 3; - startHM(); - } - - else { - E.showPrompt(msg,{ - title: "TIMER!", - buttons : {"Snooze":true,"Ok":false} // default is sleep so it'll come back in 10 mins - }).then(function(sleep) { - buzzCount = 0; - if (sleep) { - if(alarm.ot===undefined) alarm.ot = alarm.t; - alarm.t += settings.defaultSnoozeMillis; - } else { - if (!alarm.timer) alarm.last = (new Date()).getDate(); - if (alarm.ot!==undefined) { - alarm.t = alarm.ot; - delete alarm.ot; - } - if (!alarm.rp) alarm.on = false; - } - //reset timer value - if (alarm.timer) alarm.timer = alarm.data.ot; - // alarm is still a member of 'alarms', so writing to array writes changes back directly - require("sched").setAlarms(alarms); - load(); - }); - } - - function buzz() { - if (settings.unlockAtBuzz) { - Bangle.setLocked(false); - } - - require("buzz").pattern(alarm.vibrate === undefined ? "::" : alarm.vibrate).then(() => { - if (buzzCount--) { - setTimeout(buzz, settings.buzzIntervalMillis); - } else if (alarm.as) { // auto-snooze - buzzCount = settings.buzzCount; - setTimeout(buzz, settings.defaultSnoozeMillis); - } - }); - } - - if ((require("Storage").readJSON("setting.json", 1) || {}).quiet > 1) - return; - - buzz(); -} - -// Check for alarms -let alarms = require("sched").getAlarms(); -let active = require("sched").getActiveAlarms(alarms); -if (active.length) { - // if there's an alarm, show it - showAlarm(active[0]); -} else { - // otherwise just go back to default app - setTimeout(load, 100); -} +// called by getActiveAlarms(...)[0].js +if (Bangle.SCHED) { + clearInterval(Bangle.SCHED); + delete Bangle.SCHED; +} + +function hardMode(tries, max) { + var R = Bangle.appRect; + + function adv() { + tries++; + hardMode(tries, max); + } + + if (tries < max) { + g.clear(); + g.reset(); + g.setClipRect(R.x,R.y,R.x2,R.y2); + var code = Math.abs(E.hwRand()%4); + if (code == 0) dir = "up"; + else if (code == 1) dir = "right"; + else if (code == 2) dir = "down"; + else dir = "left"; + g.setFont("6x8:2").setFontAlign(0,0).drawString(tries+"/"+max+"\nSwipe "+dir, (R.x2-R.x)/2, (R.y2-R.y)/2); + var drag; + Bangle.setUI({ + mode : "custom", + drag : e=>{ + if (!drag) { // start dragging + drag = {x: e.x, y: e.y}; + } else if (!e.b) { // released + const dx = e.x-drag.x, dy = e.y-drag.y; + drag = null; + //horizontal swipes + if (Math.abs(dx)>Math.abs(dy)+10) { + //left + if (dx<0 && code == 3) adv(); + //right + else if (dx>0 && code == 1) adv(); + //wrong swipe - reset + else startHM(); + } + //vertical swipes + else if (Math.abs(dy)>Math.abs(dx)+10) { + //up + if (dy<0 && code == 0) adv(); + //down + else if (dy>0 && code == 2) adv(); + //wrong swipe - reset + else startHM(); + } + } + } + }); + } + else { + if (!active[0].timer) active[0].last = (new Date()).getDate(); + if (!active[0].rp) active[0].on = false; + if (active[0].timer) active[0].timer = active[0].data.ot; + require("sched").setAlarms(alarms); + load(); + } +} + +function startHM() { + //between 5-8 random swipes + hardMode(0, Math.abs(E.hwRand()%4)+5); +} + +function buzz() { + const settings = require("sched").getSettings(); + let buzzCount = 3 * settings.buzzCount; + + require("buzz").pattern(alarm.vibrate === undefined ? "::" : alarm.vibrate).then(() => { + if (buzzCount--) { + setTimeout(buzz, settings.buzzIntervalMillis); + } else if (alarm.as) { // auto-snooze + buzzCount = settings.buzzCount; + setTimeout(buzz, settings.defaultSnoozeMillis); + } + }); +} + +let alarms = require("sched").getAlarms(); +let active = require("sched").getActiveAlarms(alarms); +let alarm = active[0]; +// active[0] is a HM alarm (otherwise we'd have triggered sched.js instead of this file) +startHM(); +buzz(); diff --git a/apps/multitimer/app.js b/apps/multitimer/app.js index 8832d1a255..ae8647db0b 100644 --- a/apps/multitimer/app.js +++ b/apps/multitimer/app.js @@ -56,6 +56,14 @@ function clearInt() { timerInt2 = []; } +function setHM(alarm, on) { + if (on) + alarm.js = "(require('Storage').read('multitimer.alarm.js') !== undefined) ? load('multitimer.alarm.js') : load('sched.js')"; + else + delete alarm.js; + alarm.data.hm = on; +} + function drawTimers() { layer = 0; var timers = require("sched").getAlarms().filter(a => a.timer && a.appid == "multitimer"); @@ -228,12 +236,11 @@ function editTimer(idx, a) { else a = timers[idx]; } if (!a.data) { - a.data = {}; - a.data.hm = false; + a.data = { hm: false }; } var t = decodeTime(a.timer); - function editMsg(idx, a) { + function editMsg(idx, a) { g.clear(); idx < 0 ? msg = "" : msg = a.msg; require("textinput").input({text:msg}).then(result => { @@ -267,7 +274,10 @@ function editTimer(idx, a) { }, "Enabled": { value: a.on, - onchange: v => a.on = v + onchange: v => { + delete a.last; + a.on = v; + } }, "Hours": { value: t.hrs, min: 0, max: 23, wrap: true, @@ -292,9 +302,13 @@ function editTimer(idx, a) { }, "Hard Mode": { value: a.data.hm, - onchange: v => a.data.hm = v + onchange: v => setHM(a, v), }, "Vibrate": require("buzz_menu").pattern(a.vibrate, v => a.vibrate = v), + "Delete After Expiration": { + value: !!a.del, + onchange: v => a.del = v + }, "Msg": { value: !a.msg ? "" : a.msg.length > 6 ? a.msg.substring(0, 6)+"..." : a.msg, //menu glitch? setTimeout required here @@ -382,7 +396,7 @@ function swMenu(idx, a) { }, 100 - (a.t % 100)); } - function editMsg(idx, a) { + function editMsg(idx, a) { g.clear(); msg = a.msg; require("textinput").input({text:msg}).then(result => { @@ -556,12 +570,11 @@ function editAlarm(idx, a) { else a = require("sched").newDefaultAlarm(); } if (!a.data) { - a.data = {}; - a.data.hm = false; + a.data = { hm: false }; } var t = decodeTime(a.t); - function editMsg(idx, a) { + function editMsg(idx, a) { g.clear(); idx < 0 ? msg = "" : msg = a.msg; require("textinput").input({text:msg}).then(result => { @@ -582,8 +595,6 @@ function editAlarm(idx, a) { var menu = { "": { "title": "Alarm" }, "< Back": () => { - if (a.data.hm == true) a.js = "(require('Storage').read('multitimer.alarm.js') !== undefined) ? load('multitimer.alarm.js') : load('sched.js')"; - if (a.data.hm == false && a.js) delete a.js; if (idx >= 0) alarms[alarmIdx[idx]] = a; else alarms.push(a); require("sched").setAlarms(alarms); @@ -592,7 +603,10 @@ function editAlarm(idx, a) { }, "Enabled": { value: a.on, - onchange: v => a.on = v + onchange: v => { + delete a.last; + a.on = v; + } }, "Hours": { value: t.hrs, min: 0, max: 23, wrap: true, @@ -618,9 +632,13 @@ function editAlarm(idx, a) { }, "Hard Mode": { value: a.data.hm, - onchange: v => a.data.hm = v + onchange: v => setHM(a, v), }, "Vibrate": require("buzz_menu").pattern(a.vibrate, v => a.vibrate = v), + "Delete After Expiration": { + value: !!a.del, + onchange: v => a.del = v + }, "Auto Snooze": { value: a.as, onchange: v => a.as = v @@ -653,7 +671,7 @@ Bangle.on("drag", e=>{ if (layer < 0) return; if (!drag) { // start dragging drag = {x: e.x, y: e.y}; - } + } else if (!e.b) { // released const dx = e.x-drag.x, dy = e.y-drag.y; drag = null; diff --git a/apps/multitimer/boot.js b/apps/multitimer/boot.js new file mode 100644 index 0000000000..70b9032f69 --- /dev/null +++ b/apps/multitimer/boot.js @@ -0,0 +1,8 @@ +{ + const resetTimer = alarm => { + if (alarm.timer) alarm.timer = alarm.data.ot; + }; + + Bangle.on("alarmSnooze", resetTimer); + Bangle.on("alarmDismiss", resetTimer); +} diff --git a/apps/multitimer/metadata.json b/apps/multitimer/metadata.json index ee77d2ecbc..7a23052c99 100644 --- a/apps/multitimer/metadata.json +++ b/apps/multitimer/metadata.json @@ -1,7 +1,7 @@ { "id": "multitimer", "name": "Multi Timer", - "version": "0.03", + "version": "0.04", "description": "Set timers and chronographs (stopwatches) and watch them count down in real time. Pause, create, edit, and delete timers and chronos, and add custom labels/messages. Also sets alarms.", "icon": "app.png", "screenshots": [ @@ -14,6 +14,7 @@ "readme": "README.md", "storage": [ {"name":"multitimer.app.js","url":"app.js"}, + {"name":"multitimer.boot.js","url":"boot.js"}, {"name":"multitimer.alarm.js","url":"alarm.js"}, {"name":"multitimer.img","url":"app-icon.js","evaluate":true} ], diff --git a/apps/sched/sched.js b/apps/sched/sched.js index 3deb9d1f06..bd84c3e47f 100644 --- a/apps/sched/sched.js +++ b/apps/sched/sched.js @@ -102,7 +102,7 @@ function showAlarm(alarm) { date = new Date(date.getFullYear() + rp.num, date.getMonth(), alarm.od); if (date.getDate() != alarm.od) date.setDate(0); break; - default: + default: console.log(`sched: unknown repeat '${JSON.stringify(rp)}'`); break; }