1
0
Fork 0

Merge pull request #1829 from frigis1/master

New app - Multi Timer
master
Gordon Williams 2022-05-16 14:47:33 +01:00 committed by GitHub
commit 26bcffcc15
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 862 additions and 0 deletions

View File

@ -0,0 +1 @@
0.01: Initial version

10
apps/multitimer/README.md Normal file
View File

@ -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.

148
apps/multitimer/alarm.js Normal file
View File

@ -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);
}

View File

@ -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==")

680
apps/multitimer/app.js Normal file
View File

@ -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();
}
}
});

BIN
apps/multitimer/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 971 B

View File

@ -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