Bangle.loadWidgets(); Bangle.drawWidgets(); var R = Bangle.appRect; var layer; var drag; var timerInt1 = []; var timerInt2 = []; function getCurrentTime() { let time = new Date(); return ( time.getHours() * 3600000 + time.getMinutes() * 60000 + time.getSeconds() * 1000 ); } function decodeTime(t) { let hrs = 0 | Math.floor(t / 3600000); let mins = 0 | Math.floor(t / 60000 % 60); let secs = 0 | Math.floor(t / 1000 % 60); return { hrs: hrs, mins: mins, secs: secs }; } function encodeTime(o) { return o.hrs * 3600000 + o.mins * 60000 + o.secs * 1000; } function formatTime(t) { let o = decodeTime(t); return o.hrs + ":" + ("0" + o.mins).substr(-2) + ":" + ("0" + o.secs).substr(-2); } function decodeTimeDecis(t) { let hrs = 0 | Math.floor(t / 3600000); let mins = 0 | Math.floor(t / 60000 % 60); let secs = 0 | Math.floor(t / 1000 % 60); let decis = 0 | Math.floor(t / 100 % 100); return { hrs: hrs, mins: mins, secs: secs, decis: decis }; } function formatTimeDecis(t) { let o = decodeTimeDecis(t); return o.hrs + ":" + ("0" + o.mins).substr(-2) + ":" + ("0" + o.secs).substr(-2) + "." + ("0" + o.decis).substr(-1); } function clearInt() { for (let i = 0; i < timerInt1.length; i++) { if (timerInt1[i]) clearTimeout(timerInt1[i]); } for (let i = 0; i < timerInt2.length; i++) { if (timerInt2[i]) clearInterval(timerInt2[i]); } timerInt1 = []; 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"); var alarms = require("sched").getAlarms(); function updateTimers(idx) { if (!timerInt1[idx]) timerInt1[idx] = setTimeout(function() { s.drawItem(idx+1); if (!timerInt2[idx]) timerInt2[idx] = setInterval(function(){ s.drawItem(idx+1); }, 1000); }, 1000 - (timers[idx].t % 1000)); } var s = E.showScroller({ h : 40, c : timers.length+2, back : function() {load();}, draw : (idx, r) => { function drawMenuItem(a) { g.setClipRect(R.x,R.y,R.x2,R.y2); if (idx > 0 && timers[idx-1].msg) msg = "\n"+(timers[idx-1].msg.length > 10 ? timers[idx-1].msg.substring(0, 10)+"..." : timers[idx-1].msg); else msg = ""; return g.setColor(g.theme.bg2).fillRect({x:r.x+4,y:r.y+2,w:r.w-8, h:r.h-4, r:5}) .setColor(g.theme.fg2).setFont("6x8:2").setFontAlign(-1,0).drawString(a+msg,r.x+12,r.y+(r.h/2)); } if (idx == 0) { drawMenuItem("+ New Timer"); } if (idx == timers.length+1) { g.setColor(g.theme.bg).fillRect({x:r.x+4,y:r.y+2,w:r.w-8, h:r.h-4, r:5}) .setColor(g.theme.fg).setFont("6x8:2").setFontAlign(0,0).drawString("< Swipe >",r.x+(r.w/2),r.y+(r.h/2)); } else if (idx > 0 && idx < timers.length+1) { if (timers[idx-1].on == true) { drawMenuItem(formatTime(timers[idx-1].t-getCurrentTime())); updateTimers(idx-1); } else drawMenuItem(formatTime(timers[idx-1].timer)); } }, select : (idx) => { clearInt(); if (idx == 0) editTimer(-1); else if (idx > 0 && idx < timers.length+1) timerMenu(idx-1); } }); } function timerMenu(idx) { layer = -1; var timers = require("sched").getAlarms(); var timerIdx = []; var j = 0; for (let i = 0; i < timers.length; i++) { if (timers[i].timer && timers[i].appid == "multitimer") { a = i; timerIdx.push(a); j++; } } var a = timers[timerIdx[idx]]; var msg = ""; function updateTimer() { if (timerInt1[0] == undefined) timerInt1[0] = setTimeout(function() { s.drawItem(0); if (timerInt2[0] == undefined) timerInt2[0] = setInterval(function(){ s.drawItem(0); }, 1000); }, 1000 - (a.t % 1000)); } var s = E.showScroller({ h : 40, c : 5, back : function() { clearInt(); drawTimers(); }, draw : (i, r) => { function drawMenuItem(b) { return g.setClipRect(R.x,R.y,R.x2,R.y2).setColor(g.theme.bg2) .fillRect({x:r.x+4,y:r.y+2,w:r.w-8, h:r.h-4, r:5}) .setColor(g.theme.fg2).setFont("6x8:2").setFontAlign(-1,0).drawString(b,r.x+12,r.y+(r.h/2)); } if (i == 0) { if (a.msg) msg = "\n"+(a.msg.length > 10 ? a.msg.substring(0, 10)+"..." : a.msg); if (a.on == true) { drawMenuItem(formatTime(a.t-getCurrentTime())+msg); updateTimer(); } else { clearInt(); drawMenuItem(formatTime(a.timer)+msg); } } if (i == 1) { if (a.on == true) drawMenuItem("Pause"); else drawMenuItem("Start"); } if (i == 2) drawMenuItem("Reset"); if (i == 3) drawMenuItem("Edit"); if (i == 4) drawMenuItem("Delete"); }, select : (i) => { function saveAndReload() { require("sched").setAlarms(timers); require("sched").reload(); s.draw(); } //pause/start if (i == 1) { if (a.on == true) { clearInt(); a.timer = a.t-getCurrentTime(); a.on = false; timers[timerIdx[idx]] = a; saveAndReload(); } else { a.t = a.timer+getCurrentTime(); a.on = true; timers[timerIdx[idx]] = a; saveAndReload(); } } //reset if (i == 2) { clearInt(); a.timer = a.data.ot; if (a.on == true) a.on = false; saveAndReload(); } //edit if (i == 3) { clearInt(); editTimer(idx); } //delete if (i == 4) { clearInt(); timers.splice(timerIdx[idx], 1); saveAndReload(); drawTimers(); } } }); } function editTimer(idx, a) { layer = -1; var timers = require("sched").getAlarms().filter(a => a.timer && a.appid == "multitimer"); var alarms = require("sched").getAlarms(); var timerIdx = []; var j = 0; for (let i = 0; i < alarms.length; i++) { if (alarms[i].timer && alarms[i].appid == "multitimer") { b = i; timerIdx.push(b); j++; } } if (!a) { if (idx < 0) a = require("sched").newDefaultTimer(); else a = timers[idx]; } if (!a.data) { a.data = { hm: false }; } var t = decodeTime(a.timer); function editMsg(idx, a) { g.clear(); idx < 0 ? msg = "" : msg = a.msg; require("textinput").input({text:msg}).then(result => { if (result != "") { a.msg = result; } else delete a.msg; editTimer(idx, a); }); } function kbAlert() { E.showAlert("Must install keyboard app").then(function() { editTimer(idx, a); }); } var menu = { "": { "title": "Timer" }, "< Back": () => { a.t = getCurrentTime() + a.timer; a.last = 0; a.data.ot = a.timer; a.appid = "multitimer"; a.js = "(require('Storage').read('multitimer.alarm.js') !== undefined) ? load('multitimer.alarm.js') : load('sched.js')"; if (idx < 0) alarms.push(a); else alarms[timerIdx[idx]] = a; require("sched").setAlarms(alarms); require("sched").reload(); drawTimers(); }, "Enabled": { value: a.on, onchange: v => { delete a.last; a.on = v; } }, "Hours": { value: t.hrs, min: 0, max: 23, wrap: true, onchange: v => { t.hrs = v; a.timer = encodeTime(t); } }, "Minutes": { value: t.mins, min: 0, max: 59, wrap: true, onchange: v => { t.mins = v; a.timer = encodeTime(t); } }, "Seconds": { value: t.secs, min: 0, max: 59, wrap: true, onchange: v => { t.secs = v; a.timer = encodeTime(t); } }, "Hard Mode": { value: a.data.hm, 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 onchange: () => { var kbapp = require("Storage").read("textinput"); if (kbapp != undefined) setTimeout(editMsg, 0, idx, a); else setTimeout(kbAlert, 0); } }, "Cancel": () => { if (idx >= 0) timerMenu(idx); else drawTimers(); }, }; E.showMenu(menu); } function drawSw() { layer = 1; var sw = require("Storage").readJSON("multitimer.json", true) || []; function updateTimers(idx) { if (!timerInt1[idx]) timerInt1[idx] = setTimeout(function() { s.drawItem(idx+1); if (!timerInt2[idx]) timerInt2[idx] = setInterval(function(){ s.drawItem(idx+1); }, 1000); }, 1000 - (sw[idx].t % 1000)); } var s = E.showScroller({ h : 40, c : sw.length+2, back : function() {load();}, draw : (idx, r) => { function drawMenuItem(a) { g.setClipRect(R.x,R.y,R.x2,R.y2); if (idx > 0 && sw[idx-1].msg) msg = "\n"+(sw[idx-1].msg.length > 10 ? sw[idx-1].msg.substring(0, 10)+"..." : sw[idx-1].msg); else msg = ""; return g.setColor(g.theme.bg2).fillRect({x:r.x+4,y:r.y+2,w:r.w-8, h:r.h-4, r:5}) .setColor(g.theme.fg2).setFont("6x8:2").setFontAlign(-1,0).drawString(a+msg,r.x+12,r.y+(r.h/2)); } if (idx == 0) { drawMenuItem("+ New Chrono"); } if (idx == sw.length+1) { g.setColor(g.theme.bg).fillRect({x:r.x+4,y:r.y+2,w:r.w-8, h:r.h-4, r:5}) .setColor(g.theme.fg).setFont("6x8:2").setFontAlign(0,0).drawString("< Swipe >",r.x+(r.w/2),r.y+(r.h/2)); } else if (idx > 0 && idx < sw.length+1) { if (sw[idx-1].on == true) { drawMenuItem(formatTime(Date.now()-sw[idx-1].t)); updateTimers(idx-1); } else drawMenuItem(formatTime(sw[idx-1].t)); } }, select : (idx) => { clearInt(); if (idx == 0) swMenu(sw.length); else if (idx > 0 && idx < sw.length+1) swMenu(idx-1); } }); } function swMenu(idx, a) { layer = -1; var sw = require("Storage").readJSON("multitimer.json", true) || []; if (sw[idx]) a = sw[idx]; else { a = {"t" : 0, "on" : false, "msg" : ""}; sw[idx] = a; require("Storage").writeJSON("multitimer.json", sw); } function updateTimer() { if (timerInt1[0] == undefined) timerInt1[0] = setTimeout(function() { s.drawItem(0); if (timerInt2[0] == undefined) timerInt2[0] = setInterval(function(){ s.drawItem(0); }, 100); }, 100 - (a.t % 100)); } function editMsg(idx, a) { g.clear(); msg = a.msg; require("textinput").input({text:msg}).then(result => { if (result != "") { a.msg = result; } else delete a.msg; sw[idx] = a; require("Storage").writeJSON("multitimer.json", sw); swMenu(idx, a); }); } function kbAlert() { E.showAlert("Must install keyboard app").then(function() { swMenu(idx, a); }); } var s = E.showScroller({ h : 40, c : 5, back : function() { clearInt(); drawSw(); }, draw : (i, r) => { function drawMenuItem(b) { return g.setClipRect(R.x,R.y,R.x2,R.y2).setColor(g.theme.bg2) .fillRect({x:r.x+4,y:r.y+2,w:r.w-8, h:r.h-4, r:5}) .setColor(g.theme.fg2).setFont("6x8:2").setFontAlign(-1,0).drawString(b,r.x+12,r.y+(r.h/2)); } if (i == 0) { if (a.msg) msg = "\n"+(a.msg.length > 10 ? a.msg.substring(0, 10)+"..." : a.msg); else msg = ""; if (a.on == true) { drawMenuItem(formatTimeDecis(Date.now()-a.t)+msg); updateTimer(); } else { clearInt(); drawMenuItem(formatTimeDecis(a.t)+msg); } } if (i == 1) { if (a.on == true) drawMenuItem("Pause"); else drawMenuItem("Start"); } if (i == 2) drawMenuItem("Reset"); if (i == 3) drawMenuItem("Msg"); if (i == 4) drawMenuItem("Delete"); }, select : (i) => { function saveAndReload() { require("Storage").writeJSON("multitimer.json", sw); s.draw(); } //pause/start if (i == 1) { if (a.on == true) { clearInt(); a.t = Date.now()-a.t; a.on = false; sw[idx] = a; saveAndReload(); } else { a.t == 0 ? a.t = Date.now() : a.t = Date.now()-a.t; a.on = true; sw[idx] = a; saveAndReload(); } } //reset if (i == 2) { clearInt(); a.t = 0; if (a.on == true) a.on = false; saveAndReload(); } //edit message if (i == 3) { clearInt(); var kbapp = require("Storage").read("textinput"); if (kbapp != undefined) editMsg(idx, a); else kbAlert(); } //delete if (i == 4) { clearInt(); sw.splice(idx, 1); saveAndReload(); drawSw(); } } }); } function drawAlarms() { layer = 2; var alarms = require("sched").getAlarms().filter(a => !a.timer); var s = E.showScroller({ h : 40, c : alarms.length+2, back : function() {load();}, draw : (idx, r) => { function drawMenuItem(a) { g.setClipRect(R.x,R.y,R.x2,R.y2); var on = ""; var dow = ""; if (idx > 0 && alarms[idx-1].on == true) on = " - on"; else if (idx > 0 && alarms[idx-1].on == false) on = " - off"; if (idx > 0 && idx < alarms.length+1) dow = "\n"+"SMTWTFS".split("").map((d,n)=>alarms[idx-1].dow&(1<",r.x+(r.w/2),r.y+(r.h/2)); } else if (idx > 0 && idx < alarms.length+1){ var str = formatTime(alarms[idx-1].t); drawMenuItem(str.slice(0, -3)); } }, select : (idx) => { clearInt(); if (idx == 0) editAlarm(-1); else if (idx > 0 && idx < alarms.length+1) editAlarm(idx-1); } }); } function editDOW(dow, onchange) { const menu = { '': { 'title': 'Days of Week' }, '< Back' : () => onchange(dow) }; for (var i = 0; i < 7; i++) (i => { var dayOfWeek = require("locale").dow({ getDay: () => i }); menu[dayOfWeek] = { value: !!(dow&(1< v ? dow |= 1<= 0) a = alarms[alarmIdx[idx]]; else a = require("sched").newDefaultAlarm(); } if (!a.data) { a.data = { hm: false }; } var t = decodeTime(a.t); function editMsg(idx, a) { g.clear(); idx < 0 ? msg = "" : msg = a.msg; require("textinput").input({text:msg}).then(result => { if (result != "") { a.msg = result; } else delete a.msg; editAlarm(idx, a); }); } function kbAlert() { E.showAlert("Must install keyboard app").then(function() { editAlarm(idx, a); }); } var menu = { "": { "title": "Alarm" }, "< Back": () => { if (idx >= 0) alarms[alarmIdx[idx]] = a; else alarms.push(a); require("sched").setAlarms(alarms); require("sched").reload(); drawAlarms(); }, "Enabled": { value: a.on, onchange: v => { delete a.last; a.on = v; } }, "Hours": { value: t.hrs, min: 0, max: 23, wrap: true, onchange: v => { t.hrs = v; a.t = encodeTime(t); } }, "Minutes": { value: t.mins, min: 0, max: 59, wrap: true, onchange: v => { t.mins = v; a.t = encodeTime(t); } }, "Repeat": { value: a.rp, onchange: v => a.rp = v }, "Days": { value: "SMTWTFS".split("").map((d,n)=>a.dow&(1< editDOW(a.dow, d=>{a.dow=d;editAlarm(idx,a);}) }, "Hard Mode": { value: a.data.hm, 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 }, "Msg": { value: !a.msg ? "" : a.msg.length > 6 ? a.msg.substring(0, 6)+"..." : a.msg, //menu glitch? setTimeout required here onchange: () => { var kbapp = require("Storage").read("textinput"); if (kbapp != undefined) setTimeout(editMsg, 0, idx, a); else setTimeout(kbAlert, 0); } }, "Delete": () => { if (idx >= 0) { alarms.splice(alarmIdx[idx], 1); require("sched").setAlarms(alarms); require("sched").reload(); } drawAlarms(); }, }; E.showMenu(menu); } drawTimers(); 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; if (dx == 0) return; //horizontal swipes if (Math.abs(dx)>Math.abs(dy)+10) { //swipe left if (dx<0) layer == 2 ? layer = 0 : layer++; //swipe right if (dx>0) layer == 0 ? layer = 2 : layer--; clearInt(); if (layer == 0) drawTimers(); else if (layer == 1) drawSw(); else if (layer == 2) drawAlarms(); } } });