forked from FOSS/BangleApps
commit
26bcffcc15
|
@ -0,0 +1 @@
|
|||
0.01: Initial version
|
|
@ -0,0 +1,10 @@
|
|||
# Multi Timer
|
||||
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.
|
||||
|
||||
## 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).
|
||||
* 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.
|
|
@ -0,0 +1,148 @@
|
|||
//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 = "";
|
||||
msg += require("sched").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 * 2;
|
||||
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);
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
atob("MDCBAYAAAAAAfwAAAAAAPz//////Pz//////Pz//////Pz//////Pz//////PwAAAAAAPwAAAAAAPz//////Pz//////Pz//////Pz//////Pz//////Pz48AAAfPz4YAAAPPz//////Pz//////Pz//////Pz48AAAfPz4YAAAPPz//////Pz//////Pz//////Pz4YAAAPPz4YAAAfPz//////vz///////z///////z4YAAH4Pz48AAP4Dz////95hz////594z////x/8T////zP+T////nH+D////nj/AAAADnw/AAAABn4/P////n4/P////n//P////n/+f////z/+f////x/8/////4/4/////8Ph/////+AH//////gfw==")
|
|
@ -0,0 +1,680 @@
|
|||
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 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 = {};
|
||||
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 = "load('multitimer.alarm.js')";
|
||||
if (idx < 0) alarms.push(a);
|
||||
else alarms[timerIdx[idx]] = a;
|
||||
require("sched").setAlarms(alarms);
|
||||
require("sched").reload();
|
||||
drawTimers();
|
||||
},
|
||||
"Enabled": {
|
||||
value: a.on,
|
||||
format: v => v ? "On" : "Off",
|
||||
onchange: v => 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,
|
||||
format: v => v ? "On" : "Off",
|
||||
onchange: v => a.data.hm = v
|
||||
},
|
||||
"Vibrate": require("buzz_menu").pattern(a.vibrate, v => a.vibrate = 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<<n)?d:".").join("");
|
||||
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+on+dow,r.x+12,r.y+(r.h/2));
|
||||
}
|
||||
|
||||
if (idx == 0) {
|
||||
drawMenuItem("+ New Alarm");
|
||||
}
|
||||
if (idx == alarms.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 < 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<<i)),
|
||||
format: v => v ? "Yes" : "No",
|
||||
onchange: v => v ? dow |= 1<<i : dow &= ~(1<<i),
|
||||
};
|
||||
})(i);
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function editAlarm(idx, a) {
|
||||
layer = -1;
|
||||
var alarms = require("sched").getAlarms();
|
||||
var alarmIdx = [];
|
||||
var j = 0;
|
||||
for (let i = 0; i < alarms.length; i++) {
|
||||
if (!alarms[i].timer) {
|
||||
b = i;
|
||||
alarmIdx.push(b);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
if (!a) {
|
||||
if (idx >= 0) a = alarms[alarmIdx[idx]];
|
||||
else a = require("sched").newDefaultAlarm();
|
||||
}
|
||||
if (!a.data) {
|
||||
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 (a.data.hm == true) a.js = "load('multitimer.alarm.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);
|
||||
require("sched").reload();
|
||||
drawAlarms();
|
||||
},
|
||||
"Enabled": {
|
||||
value: a.on,
|
||||
format: v => v ? "On" : "Off",
|
||||
onchange: v => 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,
|
||||
format: v => v ? "Yes" : "No",
|
||||
onchange: v => a.rp = v
|
||||
},
|
||||
"Days": {
|
||||
value: "SMTWTFS".split("").map((d,n)=>a.dow&(1<<n)?d:".").join(""),
|
||||
onchange: () => editDOW(a.dow, d=>{a.dow=d;editAlarm(idx,a);})
|
||||
},
|
||||
"Hard Mode": {
|
||||
value: a.data.hm,
|
||||
format: v => v ? "On" : "Off",
|
||||
onchange: v => a.data.hm = v
|
||||
},
|
||||
"Vibrate": require("buzz_menu").pattern(a.vibrate, v => a.vibrate = v),
|
||||
"Auto Snooze": {
|
||||
value: a.as,
|
||||
format: v => v ? "Yes" : "No",
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
Binary file not shown.
After Width: | Height: | Size: 971 B |
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"id": "multitimer",
|
||||
"name": "Multi Timer",
|
||||
"version": "0.01",
|
||||
"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": [
|
||||
{"url":"screenshot1.png"},
|
||||
{"url":"screenshot2.png"},
|
||||
{"url":"screenshot3.png"}
|
||||
],
|
||||
"tags": "tool,alarm",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"multitimer.app.js","url":"app.js"},
|
||||
{"name":"multitimer.alarm.js","url":"alarm.js"},
|
||||
{"name":"multitimer.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"data": [{"name":"multitimer.json"}],
|
||||
"dependencies": {"scheduler":"type"}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
Loading…
Reference in New Issue