Skip to content

Commit

Permalink
Merge pull request #2781 from bobrippling/oneshot-alarms
Browse files Browse the repository at this point in the history
multitimer: allow one-shot alarms (delete after)
  • Loading branch information
gfwilliams committed Jul 3, 2023
2 parents 95d0d68 + b2e74d9 commit e61eb4e
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 165 deletions.
1 change: 1 addition & 0 deletions apps/multitimer/ChangeLog
Original file line number Diff line number Diff line change
@@ -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)
3 changes: 2 additions & 1 deletion apps/multitimer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
237 changes: 89 additions & 148 deletions apps/multitimer/alarm.js
Original file line number Diff line number Diff line change
@@ -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();
46 changes: 32 additions & 14 deletions apps/multitimer/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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 => {
Expand Down Expand Up @@ -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 => {
Expand All @@ -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);
Expand All @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions apps/multitimer/boot.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
const resetTimer = alarm => {
if (alarm.timer) alarm.timer = alarm.data.ot;
};

Bangle.on("alarmSnooze", resetTimer);
Bangle.on("alarmDismiss", resetTimer);
}
Loading

0 comments on commit e61eb4e

Please sign in to comment.