BangleApps_old/apps/mtimer/mtimer.app.js

603 lines
19 KiB
JavaScript

{
Bangle.loadWidgets();
require("widget_utils").hide();
const resetImg = require("heatshrink").decompress(atob("kkkwIJGgwCBhkP/4EB4f//gIB/8P+EA/+Ah4BB8EAvkA/EAgPgh4EBh4RCgFwDAUHAwIABnADCgPAAgUcBBY9BAYUOuAEC8YDChgkCgPDFoUcBA88EgUDwJUBgF4hwDBg8AJYICBJoUegE+BgPAgPwgJkC+CCCn//J4X/DgUAsCbFA=="));
const deleteImg = require("heatshrink").decompress(atob("mUywIROgP/8AGD//8AocwgYFDhkA4AFCBQMwAgMH/4ACwAjBAAQmBjAVCsEAgwFCBIMBwAFBBIQOBBIYYssEBwIYCjkA8AYCgeAuAYDuE4DAcOjgxDgPOGIk/GIkHwAYDgPgJQihBDAdzwAYDnFwDAcchwYDAgPADAUHgE4DARaBgyV1DBTzD/AIBAoRxBABgA=="));
const okImg = require("heatshrink").decompress(atob("mEwwIWThACBiACBjgCBnACBnwCBvgCB/wCBv8AgP8AoMfwED+EPAIPAg/gv/+AYMP4P//+DBoOHAoPh+EHCwXw8AgB4ED/AmBn4CBFgV+AQg+CIgQUCj4CB+ACBh5HB+EwhEPwEB8F4jkHAoV8nwFBgfAn18AYIBBj/4AoZGBAoZdBApIRFDoopBg/AGopBCjwFCJohZFMoxxFPoqJFSopGBj4mCg6tBUwKEBAoI0BaoTZCDAb/MABg="));
const pauseImg = require("heatshrink").decompress(atob("mUywI2zh/8AIIFBgf/AIIMC//AAIIFBn/wAIIY/DH4Y9AGA="));
const playImg = require("heatshrink").decompress(atob("mUywIpmgYGF+AFEn4FEh/gDAn+Bgn/Bgk//gYE//ADAf/Bgn/Bgk///4DAn/wAYDBggFB/4YE/5TCDAQMCDARGDAoRTCDAQMCDAYsBDAYyCAooYCLAQYCAoQYCMgYwEDAQFDDAIFDDAR8FSogFEDAKuEQIYYCAok/AokPUAYYBAokAAooAkA"));
const timerImg = require("heatshrink").decompress(atob("mEwwIoln////8AqcD4ABBArXgg4FCgIpBwAFHgAFBJwd//xUEIAQABj/4AocP+AFDg/gApIRFDos+vgFDvk+Aof4j4ECgPwh5TDL4IFCMopxG4ODAofD4YFD4/jApC/XAEQ="));
const alarmImg = require("heatshrink").decompress(atob("mEwwIWThACBiACBjgCBnACBnwCBvgCB/wCBv8AgP8AoMfwED+EPAIPAg/gv/+AYMP4P//+DBoOHAoPh+EHCwXw8AgB4ED/AmBn4CBFgV+AQl8gfAIgX4AoMfAoPwAoMPI4IFDwEB8AFBg4FGAYIBeEoQBCIYQBBgJ3BAYJnBv+HHYRtBIIXwgB6BwBZBLgJZBMoKhCWAhxCPoqJFSoqfBj4mCg6tBHAKEBAoI0BaoTZCDAb/MABg"));
const alarmOnImg = require("heatshrink").decompress(atob("mEwwJC/ABMf////AFBAgIABgEBAoeAgYFD4EH8AUBAYMPwAFBgPwj4mC+H4jwEBgf8vAFCg/+vE8Aoc8AoUP/4FVDoQpFGopBFJopZFMopxFPoqJGAH4AGA="));
const alarmOffImg = require("heatshrink").decompress(atob("mEwwJC/ABMf////AFBAgIABgEBAoeAgYFD4EH8AUBAYMPwAFBgPwj8PE4X4j0/4AFBvEev4YCvE8Aoc8nn/+AFVDogpFGopBFJopZFMopxFPoqJGAH4AGA=="));
const sched = require("sched");
const settings = sched.getSettings();
const TimePicker = require("timepicker").TimePicker;
const Scroller = require("scroller").Scroller;
const dowPicker = require("dowpicker").dowPicker;
const appId = "mtimer";
let intID;
let alarms;
let timerIndexes = [];
let alarmIndexes = [];
let numActiveTimers = 0;
let numActiveAlarms = 0;
let mode = "timer"; // "timer" or "alarm"
const hms2ms = function(hms) {
let t = (hms.h * 3600000) + (hms.m * 60000) + (hms.s * 1000);
if (hms.meridiem === "PM") {
t += (3600000 * 12); // add 12 hours if we are p.m.
}
return t;
};
const ms2hms = function(ms, mode) {
const h = Math.floor(ms / 3600000);
const m = Math.floor((ms / 60000) % 60);
const s = Math.floor((ms / 1000) % 60);
let meridiem = undefined;
if (mode === "alarm") {
if (ms >= 43200000) { //12h * 60min * 60sec * 1000ms is 12:00pm
meridiem = "PM";
hms.h -= 12;
} else {
meridiem = "AM";
}
}
return { h: h, m: m, s: s, meridiem: meridiem };
};
const ms2String = function(ms, mode, includeSeconds) {
const hms = ms2hms(ms, mode);
if (hms.m < 10) hms.m = "0" + hms.m;
if (hms.s < 10) hms.s = "0" + hms.s;
if (includeSeconds === undefined) includeSeconds = true;
if (mode === "timer" || mode === undefined) {
return {time: (hms.h > 0 ? hms.h + ":" : "") + hms.m + (hms.m > 0 ? (includeSeconds ? ":" + hms.s : "") : ":" + hms.s), meridiem: undefined};
} else if (mode === "alarm") {
return {time: hms.h + ":" + hms.m, meridiem: hms.meridiem};
} else if (mode === "timeto") {
return {time: (hms.h > 0 ? hms.h + "h" : "") + hms.m + "m" + (hms.m > 0 ? (includeSeconds ? hms.s + "s" : "") : hms.s + "s"), meridiem: undefined};
}
};
// Common:
const loadAlarmsAndTimers = function() {
sched.reload();
alarms = sched.getAlarms();
timerIndexes = [];
alarmIndexes = [];
numActiveTimers = 0;
numActiveAlarms = 0;
if (alarms.length > 0) {
for (let i = alarms.length - 1; i >= 0; i--) {
if (alarms[i].appid === appId) {
if (alarms[i].timer != undefined) {
timerIndexes.push(i);
if (alarms[i].on === true) numActiveTimers++;
} else {
alarmIndexes.push(i);
if (alarms[i].on === true) numActiveAlarms++;
}
}
}
}
};
loadAlarmsAndTimers();
const saveAlarms = function() {
sched.setAlarms(alarms);
sched.reload();
};
const getCurrentTime = function() {
const time = new Date();
return (time.getHours() * 3600000) + (time.getMinutes() * 60000) + (time.getSeconds() * 1000);
};
const cancel = function() {
let s = mode === "timer" ? timerScroller : alarmScroller;
reMakeScroller(s);
showScroller(s);
};
const remove = function(idx) {
let alarm;
if (mode === "timer") {
removeScroller(timerScroller);
alarm = getTimer(idx);
} else if (mode === "alarm") {
removeScroller(alarmScroller);
alarm = getAlarm(idx);
}
sched.setAlarm(alarm.id, undefined);
loadAlarmsAndTimers();
if (mode === "timer") {
reMakeScroller(timerScroller);
showScroller(timerScroller);
} else {
reMakeScroller(alarmScroller);
showScroller(alarmScroller);
}
};
const timeup = function() {
currentTime = getCurrentTime();
for (let i = 0; i < timerIndexes.length; i++) {
timer = getTimer(i);
if (timer.on && timer.t <= currentTime) {
//timer.on = false;
timer.timer = 0;
timer.on = false;
s.scrollToCenter(i + 1);
buzz();
}
}
sched.setAlarms(alarms);
sched.reload();
};
const startRedraws = function(s) {
if (intID) clearTimeout(intID);
const redraw = function() {
g.clearRect(Bangle.appRect);
s.drawAll();
intID = setTimeout(redraw, 1000);
}
redraw();
};
const stopRedraws = function() {
if (intID) clearTimeout(intID);
intID = undefined;
};
const showScroller = function(s) {
stopRedraws();
if ((mode === "timer" && timerIndexes.length > 0) ||
(mode === "alarm" && alarmIndexes.length > 0)) {
startRedraws(s);
}
s.reload();
s.drawAll();
}
const removeScroller = function(s) {
//if (mode === "timer") {
stopRedraws();
//}
s.remove();
g.reset();
g.clearRect(Bangle.appRect);
};
const reMakeScroller = function(s) {
s.c = mode === "timer" ? timerIndexes.length : alarmIndexes.length;
s.reload();
};
const topBar = {
minH: 24,
maxH: 54,
drawMin: (r) => drawTbMin(r),
drawMax: (r) => drawTbMax(r),
handler: (d, r) => topBarHandler(d, r),
};
const drawTbMin = function(r) {
g.clearRect(r.x, r.y, r.x2, r.y2);
const h = r.y2 - r.y;
g.setFont("Vector:" + h).setFontAlign(-1, -1).drawString(mode === "timer" ? "Timers" : "Alarms");
g.setColor(0, 0.5, 0.5).fillRect(r.x, r.y2, r.x2, r.y2 - 3).setColor(g.theme.fg);
};
const drawTbMax = function(r) {
g.clearRect(r.x, r.y, r.x2, r.y2);
//g.setColor(0, 0, 0.25).fillRect(r.x, r.y, r.x + 96, r.y2);
g.setColor(0, 1, 1);
if (mode === "timer") {
g.fillRect({x: r.x, y: r.y, x2: r.x + 47, y2: r.y2, r: 10});
g.setColor(0, 0, 0).fillRect({x: r.x + 3, y: r.y + 3, x2: r.x + 44, y2: r.y2, r: 8});
} else {
g.fillRect({x: r.x + 48, y: r.y, x2: r.x + 99, y2: r.y2, r: 10});
g.setColor(0, 0, 0).fillRect({x: r.x + 51, y: r.y + 3, x2: r.x + 96, y2: r.y2, r: 8});
}
g.setColor(0, 1, 1).fillRect(r.x, r.y2, r.x2, r.y2 - 3); // bottom line
const midY = (r.y + r.y2) / 2;
const midX = (r.x2 - 20);
g.setColor(g.theme.fg);
if (mode !== "timer") g.setColor(0.5, 0.5, 0.5);
g.drawImage(timerImg, r.x, midY - 24).setColor(g.theme.fg);
if (mode !== "alarm") g.setColor(0.5, 0.5, 0.5);
g.drawImage(alarmImg, r.x + 50, midY - 24).setColor(g.theme.fg);
g.setFontAlign(0, 0).setFont("Vector:40").drawString("+", midX + 3, midY + 3);
g.setBgColor(g.theme.bg);
};
const topBarHandler = function(d, r) {
if (d.x < r.x + 50) { // timer tab
if (mode !== "timer") {
removeScroller(alarmScroller);
mode = "timer";
showScroller(timerScroller);
}
} else if (d.x > r.x + 50 && d.x < r.x + 100) { // alarm tab
if (mode !== "alarm") {
removeScroller(timerScroller);
mode = "alarm";
showScroller(alarmScroller);
}
} else if (d.x > r.x2 - 50) {
removeScroller(mode === "timer" ? timerScroller : alarmScroller);
if (mode === "timer") {
timerTimePicker.setTime({h:0, m:0, s: 0, meridiem: undefined});
timerTimePicker.draw();
} else if (mode === "alarm") {
newAlarmTimePicker.setTime({h: 8, m: 0, s: 0, meridiem: "AM"}); //TODO change this to a default alarm time variable
newAlarmTimePicker.draw();
}
}
};
const drawButton1 = function(r) {
const midX = (r.x + r.x2) / 2;
const midY = (r.y + r.y2) / 2;
const halfImg = 25;
g.setColor(1, 0, 0).fillRect({ x: r.x, y: r.y + 4, x2: r.x2 - 3, y2: r.y2 - 3}).setColor(g.theme.fg);
g.drawImage(deleteImg, midX - halfImg, midY - halfImg);
};
const drawButton2 = function(r) {
const midX = (r.x + r.x2) / 2;
const midY = (r.y + r.y2) / 2;
const halfImg = 16;
g.setColor(0, 0, 1).fillRect({ x: r.x, y: r.y + 4, x2: r.x2 - 3, y2: r.y2 - 3}).setColor(g.theme.fg);
g.drawImage(resetImg, midX - halfImg, midY - halfImg);
};
const drawEmpty = function(r, mode) {
const midX = (r.x2 + r.x) / 2;
const midY = (r.y2 + r.y) / 2;
g.setColor(0.25, 0.25, 0.25);
// g.setFontAlign(0, 0).setFont("Vector:80");
// g.drawString("o", midX, midY);
// g.drawString("/", midX, midY);
// g.drawImage(nullImg, midX, midY);
g.setColor(-1);
g.drawCircle(midX, midY - 20, 30);
g.drawLine(midX + 30, midY - 50, midX - 30, midY + 10);
let str;
if (mode === "timer") str = "No Timers";
else if (mode === "alarm") str = "No Alarms";
g.setColor(0.5, 0.5, 0.5).setFont("Vector:20").drawString(str, midX, r.y2 - 30);
};
const drawTimeUp = function(timer, r) {
const midY = r.y + (r.h / 2);
g.clearRect(r.x, r.y, r.x2, r.y2);
g.setColor(0, 0.5, 0.5).fillRect({ x: r.x, y: r.y, x2: r.x2, y2: r.y2, r: 12 });
g.setColor(g.theme.bg).fillRect({ x: r.x + 5, y: r.y + 6, x2: r.x2 - 5, y2: r.y2 - 6, r: 7 }).setColor(g.theme.fg);
g.drawImage(okImg, r.x2 - 51, midY - 24);
const time = getCurrentTime() - timer.t;
const timeStr = ms2String(time, "timer");
const timerStr = ms2String(timer.data.ot, "timer");
g.setFontAlign(-1, -1).setFont("Vector:20").drawString(timerStr.time, r.x + 6, r.y + 7);
g.setFontAlign(-1, 1).setFont("Vector:27").drawString(timeStr.time, r.x + 6, r.y2 - 3);
g.setBgColor(g.theme.bg).setColor(g.theme.fg);
};
const buzz = function() {
if (settings.unlockAtBuzz) Bangle.setLocked(false);
const pattern = settings.defaultAlarmPattern;
Bangle.buzzCount = settings.buzzCount;
const doBuzz = function() {
require("buzz").pattern(pattern).then(() => {
Bangle.buzzCount--;
if (Bangle.buzzId) clearTimeout(Bangle.buzzId);
if (Bangle.buzzCount > 0) {
Bangle.buzzId = setTimeout(doBuzz, settings.buzzIntervalMillis);
} else {
stopBuzz();
return;
}
});
};
doBuzz();
};
const stopBuzz = function() {
if (Bangle.buzzId) {
clearTimeout(Bangle.buzzId);
delete Bangle.buzzId;
}
if (Bangle.buzzCount) delete Bangle.buzzCount;
return;
};
// Timers:
const getTimer = function(idx) {
return alarms[timerIndexes[idx]];
};
const timerTimePicker = new TimePicker({
meridiem: undefined,
start: (hms) => setTimer(hms2ms(hms)),
cancel: cancel,
});
const drawTimerStart = function(x, y) {
g.drawImage(playImg, x - 24, y - 24);
};
const drawTimerPause = function(x, y) {
g.drawImage(pauseImg, x - 24, y - 24);
};
const drawTimerProgress = function(value, maxValue, x, y) {
const len = 115;
const t = 16;
const x2 = x + len;
const y2 = y + t;
const progLen = ((len - 4) / maxValue) * value;
g.setColor(0, 1, 1).fillRect({ x: x, y: y, x2: x2, y2: y2, r: t });
g.setColor(g.theme.bg).fillRect({ x: x + 2, y: y + 2, x2: x2 - 2, y2: y2 - 2, r: t - 2 }).setColor(g.theme.fg);
g.setColor(0, 1, 1).fillRect({ x: x + 4, y: y + 4, x2: x + progLen, y2: y2 - 4, r: t - 4 });
g.setColor(g.theme.fg);
};
const drawTimer = function(idx, r, dragging) {
const timer = getTimer(idx);
if (timer.timer === 0) {
drawTimeUp(timer, r);
return;
}
const midY = r.y + (r.h / 2);
g.clearRect(r.x, r.y, r.x2, r.y2);
g.setColor(g.theme.bg2).fillRect(r.x + 3, r.y + 4, r.x2 - 3, r.y2 - 3).setColor(g.theme.fg);
let time;
if (timer.on) {
drawTimerPause(r.x2 - 25, midY);
time = sched.getTimeToAlarm(timer);
} else {
drawTimerStart(r.x2 - 25, midY);
time = timer.timer;
}
const str = ms2String(time, "timer");
g.setColor(0, 0, 1);
drawTimerProgress(time, timer.data.ot, r.x + 4, r.y2 - 30);
g.setColor(g.theme.fg);
g.setFontAlign(-1, -1).setFont("Vector:27").drawString(str.time, r.x + 4, r.y + 10);
};
const playPauseTimer = function(timer) {
if (timer.on) {
changeTimerTime(timer);
timer.on = false;
numActiveTimers--;
} else {
changeTimerTime(timer);
timer.on = true;
numActiveTimers++;
}
saveAlarms();
showScroller(timerScroller);
};
const changeTimerTime = function(timer, newTime) {
const currentTime = getCurrentTime();
const timerTime = sched.getTimeToAlarm(timer);
if (timerTime) {
timer.timer = newTime === undefined ? timerTime : newTime;
}
timer.t = (currentTime + timer.timer) % 86400000;
};
const timerTouchHandler = function(idx, r, x) {
const timer = getTimer(idx);
if (x > r.x2 - 40) {
if (timer.timer === 0) dismissTimer(timer);
else playPauseTimer(timer);
}
if (timer.on === false) {
if (x < r.x + 40) {
editTimer(timer);
}
}
};
const setTimer = function(t) {
if (t > 1000) { // don't make timers less than 1 second
const id = Math.round(Date.now());
sched.setAlarm(id, {
appid: appId, timer: t, on: true,
del: settings.defaultDeleteExpiredTimers,
rp: false, as: false, dow: 0b1111111, last: 0,
data: { ot: t },
js: `try {
if (__FILE__ === 'mtimer.app.js') {
Bangle.emit("timeup");
} else {
load("sched.js");
}
} catch {
load("sched.js");
}`
}); //ot: original time
}
loadAlarmsAndTimers();
reMakeScroller(timerScroller);
showScroller(timerScroller);
};
const resetTimer = function(timer) {
changeTimerTime(timer, timer.data.ot);
if (timer.on) playPauseTimer(timer);
saveAlarms();
showScroller(timerScroller);
};
const editTimer = function(timer) {
console.log("edit function, timer: " + timer);
};
const timerScroller = new Scroller({
h: 75, c: timerIndexes.length,
draw: (i, r, d) => drawTimer(i, r, d),
drawEmpty: (r) => drawEmpty(r, "timer"),
select: (i, r, x) => timerTouchHandler(i, r, x),
button1: (i) => remove(i),
button2: (i) => resetTimer(i),
drawButton1: (r) => drawButton1(r),
drawButton2: (r) => drawButton2(r),
topBar: topBar,
});
const dismissTimer = function(timer) {
stopBuzz();
timer.timer = timer.data.ot;
sched.setAlarms(alarms);
sched.reload();
};
// Alarms:
const getAlarm = function(idx) {
return alarms[alarmIndexes[idx]];
};
const newAlarmTimePicker = new TimePicker({
meridiem: "AM",
start: (hms) => setAlarm(hms2ms(hms)),
cancel: cancel
});
const changeAlarmTime = function(alarm) {
const hms = ms2hms(alarm.t, "alarm");
const tp = new TimePicker({
meridiem: hms.meridiem,
h: hms.h,
m: hms.m,
s: 0,
start: (hms) => {
alarm.t = hms2ms(hms);
saveAlarms();
loadAlarmsAndTimers();
g.clearRect(Bangle.appRect);
//showScroller(alarmScroller);
showDowPicker(alarm);
},
cancel: cancel
})
tp.draw();
};
const drawAlarm = function(idx, r, dragging) {
const alarm = getAlarm(idx);
g.setColor(g.theme.bg2).fillRect(r.x + 3, r.y + 4, r.x2 - 3, r.y2 - 3).setColor(g.theme.fg);
let timeToAlarm;
if (alarm.on) {
g.setColor(0, 1, 0.5).drawImage(alarmOnImg, r.x2 - 50, r.y + 5).setColor(-1);
timeToAlarm = sched.getTimeToAlarm(alarm);
} else {
g.setColor(0.5, 0.5, 0.5).drawImage(alarmOffImg, r.x2 - 50, r.y + 5).setColor(-1);
timeToAlarm = 0;
}
const timeStr = ms2String(alarm.t, "alarm");
g.setColor(g.theme.fg).setFontAlign(-1, 1).setFont("Vector:35");
const sw = g.stringWidth(timeStr.time);
g.drawString(timeStr.time, r.x + 4, r.y + 40);
g.setFont("Vector:18").drawString(timeStr.meridiem, r.x + 4 + sw, r.y + 35);
if (!dragging) {
const timeToAlarmStr = timeToAlarm > 0 ? "In " + ms2String(timeToAlarm, "timeto", false).time : "";
g.setFont("Vector:20");
g.drawString(timeToAlarmStr, r.x + 4, r.y + 55);
if (alarm.rp) {
const dow = ["S", "M", "T", "W", "T", "F", "S"];
g.setFontAlign(0, 1);
g.setColor(-1);
for (let i = 0, x = 13; i <= 6; i++, x += 25) {
if ((alarm.dow >> i) & 1) { // check bit
g.setColor(-1);
} else {
g.setColor(0.25, 0.5, 0.5);
}
g.drawString(dow[i], r.x + x, r.y2-5);
}
}
}
g.setBgColor(g.theme.bg).setColor(g.theme.fg);
};
const showDowPicker = function(alarm) {
dowPicker(alarm, () => {
saveAlarms();
loadAlarmsAndTimers();
g.clearRect(Bangle.appRect);
showScroller(alarmScroller);
},
() => changeAlarmTime(alarm)
)
};
const alarmTouchHandler = function(idx, r, x) {
if (x < r.x2 - 40) {
stopRedraws();
Bangle.setUI();
removeScroller(alarmScroller);
showDowPicker(alarms[alarmIndexes[idx]])
} else if (x > r.x2 - 40) {
alarms[alarmIndexes[idx]].on = !alarms[alarmIndexes[idx]].on;
saveAlarms();
loadAlarmsAndTimers();
showScroller(alarmScroller);
}
};
const setAlarm = function(t) {
const id = Math.round(Date.now());
sched.setAlarm(id, {
appid: appId, t: t, on: true, rp: false, del: false,
dow: 0b1111111,
as: settings.defaultAutoSnooze,
vibrate: settings.defaultAlarmPattern,
last: 0,
js: `try {
if (__FILE__ === 'mtimer.app.js') {
Bangle.emit("timeup");
} else {
load("sched.js");
}
} catch {
load("sched.js");
}`
}); //ot: original time
loadAlarmsAndTimers();
reMakeScroller(alarmScroller);
showScroller(alarmScroller);
};
const alarmScroller = new Scroller({
h: 95, c: alarmIndexes.length,
draw: (i, r, d) => drawAlarm(i, r, d),
drawEmpty: (r) => drawEmpty(r, "alarm"),
select: (i, r, x) => alarmTouchHandler(i, r, x),
button1: (i) => remove(i),
button2: undefined,
drawButton1: (r) => drawButton1(r),
drawButton2: undefined,
topBar: topBar,
});
// Start
Bangle.on("timeup", timeup);
showScroller(timerScroller);
}