Merge branch 'master' of github.com:espruino/BangleApps
|
@ -26,3 +26,4 @@
|
|||
Add "Enable All", "Disable All" and "Remove All" actions
|
||||
0.25: Fix redrawing selected Alarm/Timer entry inside edit submenu
|
||||
0.26: Add support for Monday as first day of the week (#1780)
|
||||
0.27: New UI!
|
||||
|
|
|
@ -1,7 +1,31 @@
|
|||
Alarms & Timers
|
||||
===============
|
||||
# Alarms & Timers
|
||||
|
||||
This app allows you to add/modify any alarms and timers.
|
||||
|
||||
It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched)
|
||||
to handle the alarm scheduling in an efficient way that can work alongside other apps.
|
||||
It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched) to handle the alarm scheduling in an efficient way that can work alongside other apps.
|
||||
|
||||
## Menu overview
|
||||
|
||||
- `New...`
|
||||
- `New Alarm` → Configure a new alarm
|
||||
- `Repeat` → Select when the alarm will fire. You can select a predefined option (_Once_, _Every Day_, _Workdays_ or _Weekends_ or you can configure the days freely)
|
||||
- `New Timer` → Configure a new timer
|
||||
- `Advanced`
|
||||
- `Scheduler settings` → Open the [Scheduler](https://github.com/espruino/BangleApps/tree/master/apps/sched) settings page, see its [README](https://github.com/espruino/BangleApps/blob/master/apps/sched/README.md) for details
|
||||
- `Enable All` → Enable _all_ disabled alarms & timers
|
||||
- `Disable All` → Disable _all_ enabled alarms & timers
|
||||
- `Delete All` → Delete _all_ alarms & timers
|
||||
|
||||
## Creator
|
||||
|
||||
- [Gordon Williams](https://github.com/gfwilliams)
|
||||
|
||||
## Main Contributors
|
||||
|
||||
- [Alessandro Cocco](https://github.com/alessandrococco) - New UI, full rewrite, new features
|
||||
- [Sabin Iacob](https://github.com/m0n5t3r) - Auto snooze support
|
||||
- [storm64](https://github.com/storm64) - Fix redrawing in submenus
|
||||
|
||||
## Attributions
|
||||
|
||||
All icons used in this app are from [icons8](https://icons8.com).
|
||||
|
|
|
@ -1,20 +1,160 @@
|
|||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
|
||||
// 0 = Sunday (default), 1 = Monday
|
||||
const firstDayOfWeek = (require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0;
|
||||
const WORKDAYS = 62
|
||||
const WEEKEND = firstDayOfWeek ? 192 : 65;
|
||||
const EVERY_DAY = firstDayOfWeek ? 254 : 127;
|
||||
|
||||
const iconAlarmOn = "\0" + atob("GBiBAAAAAAAAAAYAYA4AcBx+ODn/nAP/wAf/4A/n8A/n8B/n+B/n+B/n+B/n+B/h+B/4+A/+8A//8Af/4AP/wAH/gAB+AAAAAAAAAA==");
|
||||
const iconAlarmOff = "\0" + (g.theme.dark
|
||||
? atob("GBjBAP////8AAAAAAAAGAGAOAHAcfjg5/5wD/8AH/+AP5/AP5/Af5/gf5/gf5wAf5gAf4Hgf+f4P+bYP8wMH84cD84cB8wMAebYAAf4AAHg=")
|
||||
: atob("GBjBAP//AAAAAAAAAAAGAGAOAHAcfjg5/5wD/8AH/+AP5/AP5/Af5/gf5/gf5wAf5gAf4Hgf+f4P+bYP8wMH84cD84cB8wMAebYAAf4AAHg="));
|
||||
|
||||
const iconTimerOn = "\0" + (g.theme.dark
|
||||
? atob("GBjBAP////8AAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5wAAwwABgYABgYABgYAH/+AH/+AAAAAAAAAAAAA=")
|
||||
: atob("GBjBAP//AAAAAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5wAAwwABgYABgYABgYAH/+AH/+AAAAAAAAAAAAA="));
|
||||
const iconTimerOff = "\0" + (g.theme.dark
|
||||
? atob("GBjBAP////8AAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5HgAwf4BgbYBgwMBg4cH84cH8wMAAbYAAf4AAHg=")
|
||||
: atob("GBjBAP//AAAAAAAAAAAAAAAH/+AH/+ABgYABgYABgYAA/wAA/wAAfgAAPAAAPAAAfgAA5HgAwf4BgbYBgwMBg4cH84cH8wMAAbYAAf4AAHg="));
|
||||
|
||||
// An array of alarm objects (see sched/README.md)
|
||||
var alarms = require("sched").getAlarms();
|
||||
|
||||
// 0 = Sunday
|
||||
// 1 = Monday
|
||||
var firstDayOfWeek = (require("Storage").readJSON("setting.json", true) || {}).firstDayOfWeek || 0;
|
||||
function handleFirstDayOfWeek(dow) {
|
||||
if (firstDayOfWeek == 1) {
|
||||
if ((dow & 1) == 1) {
|
||||
// In the scheduler API Sunday is 1.
|
||||
// Here the week starts on Monday and Sunday is ON so
|
||||
// when I read the dow I need to move Sunday to 128...
|
||||
dow += 127;
|
||||
} else if ((dow & 128) == 128) {
|
||||
// ... and then when I write the dow I need to move Sunday back to 1.
|
||||
dow -= 127;
|
||||
}
|
||||
}
|
||||
return dow;
|
||||
}
|
||||
|
||||
function getCurrentTime() {
|
||||
var time = new Date();
|
||||
return (
|
||||
time.getHours() * 3600000 +
|
||||
time.getMinutes() * 60000 +
|
||||
time.getSeconds() * 1000
|
||||
);
|
||||
// Check the first day of week and update the dow field accordingly.
|
||||
alarms.forEach(alarm => alarm.dow = handleFirstDayOfWeek(alarm.dow));
|
||||
|
||||
function showMainMenu() {
|
||||
const menu = {
|
||||
"": { "title": /*LANG*/"Alarms & Timers" },
|
||||
"< Back": () => load(),
|
||||
/*LANG*/"New...": () => showNewMenu()
|
||||
};
|
||||
|
||||
alarms.forEach((e, index) => {
|
||||
var label = e.timer
|
||||
? require("time_utils").formatDuration(e.timer)
|
||||
: require("time_utils").formatTime(e.t) + (e.dow > 0 ? (" " + decodeDOW(e)) : "");
|
||||
menu[label] = {
|
||||
value: e.on ? (e.timer ? iconTimerOn : iconAlarmOn) : (e.timer ? iconTimerOff : iconAlarmOff),
|
||||
onchange: () => setTimeout(e.timer ? showEditTimerMenu : showEditAlarmMenu, 10, e, index)
|
||||
};
|
||||
});
|
||||
|
||||
menu[/*LANG*/"Advanced"] = () => showAdvancedMenu();
|
||||
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function showNewMenu() {
|
||||
E.showMenu({
|
||||
"": { "title": /*LANG*/"New..." },
|
||||
"< Back": () => showMainMenu(),
|
||||
/*LANG*/"Alarm": () => showEditAlarmMenu(undefined, undefined),
|
||||
/*LANG*/"Timer": () => showEditTimerMenu(undefined, undefined)
|
||||
});
|
||||
}
|
||||
|
||||
function showEditAlarmMenu(selectedAlarm, alarmIndex) {
|
||||
var isNew = alarmIndex === undefined;
|
||||
|
||||
var alarm = require("sched").newDefaultAlarm();
|
||||
alarm.dow = handleFirstDayOfWeek(alarm.dow);
|
||||
|
||||
if (selectedAlarm) {
|
||||
Object.assign(alarm, selectedAlarm);
|
||||
}
|
||||
|
||||
var time = require("time_utils").decodeTime(alarm.t);
|
||||
|
||||
const menu = {
|
||||
"": { "title": isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm" },
|
||||
"< Back": () => {
|
||||
saveAlarm(alarm, alarmIndex, time);
|
||||
showMainMenu();
|
||||
},
|
||||
/*LANG*/"Hour": {
|
||||
value: time.h,
|
||||
format: v => ("0" + v).substr(-2),
|
||||
min: 0,
|
||||
max: 23,
|
||||
wrap: true,
|
||||
onchange: v => time.h = v
|
||||
},
|
||||
/*LANG*/"Minute": {
|
||||
value: time.m,
|
||||
format: v => ("0" + v).substr(-2),
|
||||
min: 0,
|
||||
max: 59,
|
||||
wrap: true,
|
||||
onchange: v => time.m = v
|
||||
},
|
||||
/*LANG*/"Enabled": {
|
||||
value: alarm.on,
|
||||
onchange: v => alarm.on = v
|
||||
},
|
||||
/*LANG*/"Repeat": {
|
||||
value: decodeDOW(alarm),
|
||||
onchange: () => setTimeout(showEditRepeatMenu, 100, alarm.dow, dow => {
|
||||
alarm.rp = dow > 0;
|
||||
alarm.dow = dow;
|
||||
alarm.t = require("time_utils").encodeTime(time);
|
||||
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex);
|
||||
})
|
||||
},
|
||||
/*LANG*/"Vibrate": require("buzz_menu").pattern(alarm.vibrate, v => alarm.vibrate = v),
|
||||
/*LANG*/"Auto Snooze": {
|
||||
value: alarm.as,
|
||||
onchange: v => alarm.as = v
|
||||
},
|
||||
/*LANG*/"Cancel": () => showMainMenu()
|
||||
};
|
||||
|
||||
if (!isNew) {
|
||||
menu[/*LANG*/"Delete"] = () => {
|
||||
E.showPrompt(/*LANG*/"Are you sure?", { title: /*LANG*/"Delete Alarm" }).then((confirm) => {
|
||||
if (confirm) {
|
||||
alarms.splice(alarmIndex, 1);
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
} else {
|
||||
alarm.t = require("time_utils").encodeTime(time);
|
||||
setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function saveAlarm(alarm, alarmIndex, time) {
|
||||
alarm.t = require("time_utils").encodeTime(time);
|
||||
alarm.last = alarm.t < require("time_utils").getCurrentTimeMillis() ? new Date().getDate() : 0;
|
||||
|
||||
if (alarmIndex === undefined) {
|
||||
alarms.push(alarm);
|
||||
} else {
|
||||
alarms[alarmIndex] = alarm;
|
||||
}
|
||||
|
||||
saveAndReload();
|
||||
}
|
||||
|
||||
function saveAndReload() {
|
||||
|
@ -23,249 +163,187 @@ function saveAndReload() {
|
|||
|
||||
require("sched").setAlarms(alarms);
|
||||
require("sched").reload();
|
||||
|
||||
// Fix after save
|
||||
alarms.forEach(a => a.dow = handleFirstDayOfWeek(a.dow, firstDayOfWeek));
|
||||
}
|
||||
|
||||
function showMainMenu() {
|
||||
// Timer img "\0"+atob("DhKBAP////MDDAwwMGGBzgPwB4AeAPwHOBhgwMMzDez////w")
|
||||
// Alarm img "\0"+atob("FBSBAABgA4YcMPDGP8Zn/mx/48//PP/zD/8A//AP/wD/8A//AP/wH/+D//w//8AAAADwAAYA")
|
||||
const menu = {
|
||||
'': { 'title': /*LANG*/'Alarms&Timers' },
|
||||
/*LANG*/'< Back': () => { load(); },
|
||||
/*LANG*/'New Alarm': () => editAlarm(-1),
|
||||
/*LANG*/'New Timer': () => editTimer(-1)
|
||||
};
|
||||
alarms.forEach((alarm, idx) => {
|
||||
alarm.dow = handleFirstDayOfWeek(alarm.dow, firstDayOfWeek);
|
||||
function decodeDOW(alarm) {
|
||||
return alarm.rp
|
||||
? require("date_utils")
|
||||
.dows(firstDayOfWeek, 2)
|
||||
.map((day, index) => alarm.dow & (1 << (index + firstDayOfWeek)) ? day : "_")
|
||||
.join("")
|
||||
.toLowerCase()
|
||||
: "Once"
|
||||
}
|
||||
|
||||
var type, txt; // a leading space is currently required (JS error in Espruino 2v12)
|
||||
if (alarm.timer) {
|
||||
type = /*LANG*/"Timer";
|
||||
txt = " " + require("sched").formatTime(alarm.timer);
|
||||
} else {
|
||||
type = /*LANG*/"Alarm";
|
||||
txt = " " + require("sched").formatTime(alarm.t);
|
||||
function showEditRepeatMenu(dow, dowChangeCallback) {
|
||||
var originalDow = dow;
|
||||
var isCustom = dow > 0 && dow != WORKDAYS && dow != WEEKEND && dow != EVERY_DAY;
|
||||
|
||||
const menu = {
|
||||
"": { "title": /*LANG*/"Repeat Alarm" },
|
||||
"< Back": () => dowChangeCallback(dow),
|
||||
/*LANG*/"Once": { // No days set: the alarm will fire once
|
||||
value: dow == 0,
|
||||
onchange: () => dowChangeCallback(0)
|
||||
},
|
||||
/*LANG*/"Workdays": {
|
||||
value: dow == WORKDAYS,
|
||||
onchange: () => dowChangeCallback(WORKDAYS)
|
||||
},
|
||||
/*LANG*/"Weekends": {
|
||||
value: dow == WEEKEND,
|
||||
onchange: () => dowChangeCallback(WEEKEND)
|
||||
},
|
||||
/*LANG*/"Every Day": {
|
||||
value: dow == EVERY_DAY,
|
||||
onchange: () => dowChangeCallback(EVERY_DAY)
|
||||
},
|
||||
/*LANG*/"Custom": {
|
||||
value: isCustom ? decodeDOW({ rp: true, dow: dow }) : false,
|
||||
onchange: () => setTimeout(showCustomDaysMenu, 10, isCustom ? dow : EVERY_DAY, dowChangeCallback, originalDow)
|
||||
}
|
||||
if (alarm.rp) txt += "\0" + atob("FBaBAAABgAAcAAHn//////wAHsABzAAYwAAMAADAAAAAAwAAMAADGAAzgAN4AD//////54AAOAABgAA=");
|
||||
// rename duplicate alarms
|
||||
if (menu[type + txt]) {
|
||||
var n = 2;
|
||||
while (menu[type + " " + n + txt]) n++;
|
||||
txt = type + " " + n + txt;
|
||||
} else txt = type + txt;
|
||||
// add to menu
|
||||
menu[txt] = {
|
||||
value: "\0" + atob(alarm.on ? "EhKBAH//v/////////////5//x//j//H+eP+Mf/A//h//z//////////3//g" : "EhKBAH//v//8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA8AA///3//g"),
|
||||
onchange: function () {
|
||||
setTimeout(alarm.timer ? editTimer : editAlarm, 10, idx, alarm);
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
if (alarms.some(e => !e.on)) {
|
||||
menu[/*LANG*/"Enable All"] = () => enableAll(true);
|
||||
}
|
||||
if (alarms.some(e => e.on)) {
|
||||
menu[/*LANG*/"Disable All"] = () => enableAll(false);
|
||||
}
|
||||
if (alarms.length > 0) {
|
||||
menu[/*LANG*/"Delete All"] = () => deleteAll();
|
||||
}
|
||||
|
||||
if (WIDGETS["alarm"]) WIDGETS["alarm"].reload();
|
||||
return E.showMenu(menu);
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function editDOW(dow, onchange) {
|
||||
function showCustomDaysMenu(dow, dowChangeCallback, originalDow) {
|
||||
const menu = {
|
||||
'': { 'title': /*LANG*/'Days of Week' },
|
||||
/*LANG*/'< Back': () => onchange(dow)
|
||||
"": { "title": /*LANG*/"Custom Days" },
|
||||
"< Back": () => dowChangeCallback(dow),
|
||||
};
|
||||
|
||||
require("date_utils").dows(firstDayOfWeek).forEach((day, i) => {
|
||||
menu[day] = {
|
||||
value: !!(dow & (1 << (i + firstDayOfWeek))),
|
||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
||||
onchange: v => v ? (dow |= 1 << (i + firstDayOfWeek)) : (dow &= ~(1 << (i + firstDayOfWeek)))
|
||||
};
|
||||
});
|
||||
|
||||
menu[/*LANG*/"Cancel"] = () => setTimeout(showEditRepeatMenu, 10, originalDow, dowChangeCallback)
|
||||
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function editAlarm(alarmIndex, alarm) {
|
||||
var newAlarm = alarmIndex < 0;
|
||||
var a = require("sched").newDefaultAlarm();
|
||||
a.dow = handleFirstDayOfWeek(a.dow, firstDayOfWeek);
|
||||
function showEditTimerMenu(selectedTimer, timerIndex) {
|
||||
var isNew = timerIndex === undefined;
|
||||
|
||||
if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
|
||||
if (alarm) Object.assign(a, alarm);
|
||||
var t = require("sched").decodeTime(a.t);
|
||||
var timer = require("sched").newDefaultTimer();
|
||||
|
||||
if (selectedTimer) {
|
||||
Object.assign(timer, selectedTimer);
|
||||
}
|
||||
|
||||
var time = require("time_utils").decodeTime(timer.timer);
|
||||
|
||||
const menu = {
|
||||
'': { 'title': /*LANG*/'Alarm' },
|
||||
/*LANG*/'< Back': () => {
|
||||
saveAlarm(newAlarm, alarmIndex, a, t);
|
||||
"": { "title": isNew ? /*LANG*/"New Timer" : /*LANG*/"Edit Timer" },
|
||||
"< Back": () => {
|
||||
saveTimer(timer, timerIndex, time);
|
||||
showMainMenu();
|
||||
},
|
||||
/*LANG*/'Hours': {
|
||||
value: t.hrs, min: 0, max: 23, wrap: true,
|
||||
onchange: v => t.hrs = v
|
||||
/*LANG*/"Hours": {
|
||||
value: time.h,
|
||||
min: 0,
|
||||
max: 23,
|
||||
wrap: true,
|
||||
onchange: v => time.h = v
|
||||
},
|
||||
/*LANG*/'Minutes': {
|
||||
value: t.mins, min: 0, max: 59, wrap: true,
|
||||
onchange: v => t.mins = v
|
||||
/*LANG*/"Minutes": {
|
||||
value: time.m,
|
||||
min: 0,
|
||||
max: 59,
|
||||
wrap: true,
|
||||
onchange: v => time.m = v
|
||||
},
|
||||
/*LANG*/'Enabled': {
|
||||
value: a.on,
|
||||
format: v => v ? /*LANG*/"On" : /*LANG*/"Off",
|
||||
onchange: v => a.on = v
|
||||
/*LANG*/"Enabled": {
|
||||
value: timer.on,
|
||||
onchange: v => timer.on = v
|
||||
},
|
||||
/*LANG*/'Repeat': {
|
||||
value: a.rp,
|
||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
||||
onchange: v => a.rp = v
|
||||
},
|
||||
/*LANG*/'Days': {
|
||||
value: decodeDOW(a.dow),
|
||||
onchange: () => setTimeout(editDOW, 100, a.dow, d => {
|
||||
a.dow = d;
|
||||
a.t = require("sched").encodeTime(t);
|
||||
editAlarm(alarmIndex, a);
|
||||
})
|
||||
},
|
||||
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate = v),
|
||||
/*LANG*/'Auto Snooze': {
|
||||
value: a.as,
|
||||
format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
|
||||
onchange: v => a.as = v
|
||||
}
|
||||
/*LANG*/"Vibrate": require("buzz_menu").pattern(timer.vibrate, v => timer.vibrate = v),
|
||||
};
|
||||
|
||||
menu[/*LANG*/"Cancel"] = () => showMainMenu();
|
||||
|
||||
if (!newAlarm) {
|
||||
menu[/*LANG*/"Delete"] = function () {
|
||||
alarms.splice(alarmIndex, 1);
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
if (!isNew) {
|
||||
menu[/*LANG*/"Delete"] = () => {
|
||||
E.showPrompt(/*LANG*/"Are you sure?", { title: /*LANG*/"Delete Timer" }).then((confirm) => {
|
||||
if (confirm) {
|
||||
alarms.splice(timerIndex, 1);
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
} else {
|
||||
timer.timer = require("time_utils").encodeTime(time);
|
||||
setTimeout(showEditTimerMenu, 10, timer, timerIndex)
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return E.showMenu(menu);
|
||||
E.showMenu(menu);
|
||||
}
|
||||
|
||||
function saveAlarm(newAlarm, alarmIndex, a, t) {
|
||||
a.t = require("sched").encodeTime(t);
|
||||
a.last = (a.t < getCurrentTime()) ? (new Date()).getDate() : 0;
|
||||
function saveTimer(timer, timerIndex, time) {
|
||||
timer.timer = require("time_utils").encodeTime(time);
|
||||
timer.t = require("time_utils").getCurrentTimeMillis() + timer.timer;
|
||||
timer.last = 0;
|
||||
|
||||
if (newAlarm) {
|
||||
alarms.push(a);
|
||||
if (timerIndex === undefined) {
|
||||
alarms.push(timer);
|
||||
} else {
|
||||
alarms[alarmIndex] = a;
|
||||
alarms[timerIndex] = timer;
|
||||
}
|
||||
|
||||
saveAndReload();
|
||||
}
|
||||
|
||||
function editTimer(alarmIndex, alarm) {
|
||||
var newAlarm = alarmIndex < 0;
|
||||
var a = require("sched").newDefaultTimer();
|
||||
if (!newAlarm) Object.assign(a, alarms[alarmIndex]);
|
||||
if (alarm) Object.assign(a, alarm);
|
||||
var t = require("sched").decodeTime(a.timer);
|
||||
|
||||
const menu = {
|
||||
'': { 'title': /*LANG*/'Timer' },
|
||||
/*LANG*/'< Back': () => {
|
||||
saveTimer(newAlarm, alarmIndex, a, t);
|
||||
showMainMenu();
|
||||
},
|
||||
/*LANG*/'Hours': {
|
||||
value: t.hrs, min: 0, max: 23, wrap: true,
|
||||
onchange: v => t.hrs = v
|
||||
},
|
||||
/*LANG*/'Minutes': {
|
||||
value: t.mins, min: 0, max: 59, wrap: true,
|
||||
onchange: v => t.mins = v
|
||||
},
|
||||
/*LANG*/'Enabled': {
|
||||
value: a.on,
|
||||
format: v => v ? /*LANG*/"On" : /*LANG*/"Off",
|
||||
onchange: v => a.on = v
|
||||
},
|
||||
/*LANG*/'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate = v),
|
||||
};
|
||||
|
||||
menu[/*LANG*/"Cancel"] = () => showMainMenu();
|
||||
|
||||
if (!newAlarm) {
|
||||
menu[/*LANG*/"Delete"] = function () {
|
||||
alarms.splice(alarmIndex, 1);
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
};
|
||||
}
|
||||
return E.showMenu(menu);
|
||||
}
|
||||
|
||||
function saveTimer(newAlarm, alarmIndex, a, t) {
|
||||
a.timer = require("sched").encodeTime(t);
|
||||
a.t = getCurrentTime() + a.timer;
|
||||
a.last = 0;
|
||||
|
||||
if (newAlarm) {
|
||||
alarms.push(a);
|
||||
} else {
|
||||
alarms[alarmIndex] = a;
|
||||
}
|
||||
|
||||
saveAndReload();
|
||||
}
|
||||
|
||||
function handleFirstDayOfWeek(dow, firstDayOfWeek) {
|
||||
if (firstDayOfWeek == 1) {
|
||||
if ((dow & 1) == 1) {
|
||||
// By default 1 = Sunday.
|
||||
// Here the week starts on Monday and Sunday is ON so move Sunday to 128.
|
||||
dow += 127;
|
||||
} else if ((dow & 128) == 128) {
|
||||
dow -= 127;
|
||||
}
|
||||
}
|
||||
return dow;
|
||||
}
|
||||
|
||||
function decodeDOW(dow) {
|
||||
return require("date_utils")
|
||||
.dows(firstDayOfWeek, 2)
|
||||
.map((day, index) => dow & (1 << (index + firstDayOfWeek)) ? day : "_")
|
||||
.join("");
|
||||
function showAdvancedMenu() {
|
||||
E.showMenu({
|
||||
"": { "title": /*LANG*/"Advanced" },
|
||||
"< Back": () => showMainMenu(),
|
||||
/*LANG*/"Scheduler Settings": () => eval(require("Storage").read("sched.settings.js"))(() => showAdvancedMenu()),
|
||||
/*LANG*/"Enable All": () => enableAll(true),
|
||||
/*LANG*/"Disable All": () => enableAll(false),
|
||||
/*LANG*/"Delete All": () => deleteAll()
|
||||
});
|
||||
}
|
||||
|
||||
function enableAll(on) {
|
||||
E.showPrompt(/*LANG*/"Are you sure?", {
|
||||
title: on ? /*LANG*/"Enable All" : /*LANG*/"Disable All"
|
||||
}).then((confirm) => {
|
||||
if (confirm) {
|
||||
alarms.forEach(alarm => alarm.on = on);
|
||||
saveAndReload();
|
||||
}
|
||||
|
||||
showMainMenu();
|
||||
});
|
||||
if (alarms.filter(e => e.on == !on).length == 0) {
|
||||
E.showPrompt(on ? /*LANG*/"Nothing to Enable" : /*LANG*/"Nothing to Disable", {
|
||||
title: on ? /*LANG*/"Enable All" : /*LANG*/"Disable All",
|
||||
buttons: { /*LANG*/"Ok": true }
|
||||
}).then(() => showAdvancedMenu());
|
||||
} else {
|
||||
E.showPrompt(/*LANG*/"Are you sure?", { title: on ? "/*LANG*/Enable All" : /*LANG*/"Disable All" }).then((confirm) => {
|
||||
if (confirm) {
|
||||
alarms.forEach(alarm => alarm.on = on);
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
} else {
|
||||
showAdvancedMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function deleteAll() {
|
||||
E.showPrompt(/*LANG*/"Are you sure?", {
|
||||
title: /*LANG*/"Delete All"
|
||||
}).then((confirm) => {
|
||||
if (confirm) {
|
||||
alarms = [];
|
||||
saveAndReload();
|
||||
}
|
||||
|
||||
showMainMenu();
|
||||
});
|
||||
if (alarms.length == 0) {
|
||||
E.showPrompt(/*LANG*/"Nothing to delete", { title: /*LANG*/"Delete All", buttons: { /*LANG*/"Ok": true } }).then(() => showAdvancedMenu());
|
||||
} else {
|
||||
E.showPrompt(/*LANG*/"Are you sure?", {
|
||||
title: /*LANG*/"Delete All"
|
||||
}).then((confirm) => {
|
||||
if (confirm) {
|
||||
alarms = [];
|
||||
saveAndReload();
|
||||
showMainMenu();
|
||||
} else {
|
||||
showAdvancedMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
showMainMenu();
|
||||
|
|
|
@ -2,16 +2,29 @@
|
|||
"id": "alarm",
|
||||
"name": "Alarms & Timers",
|
||||
"shortName": "Alarms",
|
||||
"version": "0.26",
|
||||
"version": "0.27",
|
||||
"description": "Set alarms and timers on your Bangle",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,alarm,widget",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"supports": [ "BANGLEJS", "BANGLEJS2" ],
|
||||
"readme": "README.md",
|
||||
"dependencies": {"scheduler":"type"},
|
||||
"dependencies": { "scheduler":"type" },
|
||||
"storage": [
|
||||
{"name":"alarm.app.js","url":"app.js"},
|
||||
{"name":"alarm.img","url":"app-icon.js","evaluate":true},
|
||||
{"name":"alarm.wid.js","url":"widget.js"}
|
||||
{ "name": "alarm.app.js", "url": "app.js" },
|
||||
{ "name": "alarm.img", "url": "app-icon.js", "evaluate": true },
|
||||
{ "name": "alarm.wid.js", "url": "widget.js" }
|
||||
],
|
||||
"screenshots": [
|
||||
{ "url": "screenshot-1.png" },
|
||||
{ "url": "screenshot-2.png" },
|
||||
{ "url": "screenshot-3.png" },
|
||||
{ "url": "screenshot-4.png" },
|
||||
{ "url": "screenshot-5.png" },
|
||||
{ "url": "screenshot-6.png" },
|
||||
{ "url": "screenshot-7.png" },
|
||||
{ "url": "screenshot-8.png" },
|
||||
{ "url": "screenshot-9.png" },
|
||||
{ "url": "screenshot-10.png" },
|
||||
{ "url": "screenshot-11.png" }
|
||||
]
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.2 KiB |
|
@ -7,3 +7,5 @@
|
|||
0.07: Update to use Bangle.setUI instead of setWatch
|
||||
0.08: Use theme colors, Layout library
|
||||
0.09: Fix time/date disappearing after fullscreen notification
|
||||
0.10: Use ClockFace library
|
||||
0.11: Use ClockFace.is12Hour
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
* A simple digital clock showing seconds as a bar
|
||||
**/
|
||||
// Check settings for what type our clock should be
|
||||
const is12Hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"];
|
||||
let locale = require("locale");
|
||||
{ // add some more info to locale
|
||||
let date = new Date();
|
||||
|
@ -11,13 +10,9 @@ let locale = require("locale");
|
|||
date.setMonth(1, 3); // februari: months are zero-indexed
|
||||
const localized = locale.date(date, true);
|
||||
locale.dayFirst = /3.*2/.test(localized);
|
||||
|
||||
locale.hasMeridian = false;
|
||||
if (typeof locale.meridian==="function") { // function does not exist if languages app is not installed
|
||||
locale.hasMeridian = (locale.meridian(date)!=="");
|
||||
}
|
||||
locale.hasMeridian = (locale.meridian(date)!=="");
|
||||
}
|
||||
Bangle.loadWidgets();
|
||||
|
||||
function renderBar(l) {
|
||||
if (!this.fraction) {
|
||||
// zero-size fillRect stills draws one line of pixels, we don't want that
|
||||
|
@ -27,35 +22,9 @@ function renderBar(l) {
|
|||
g.fillRect(l.x, l.y, l.x+width-1, l.y+l.height-1);
|
||||
}
|
||||
|
||||
const Layout = require("Layout");
|
||||
const layout = new Layout({
|
||||
type: "v", c: [
|
||||
{
|
||||
type: "h", c: [
|
||||
{id: "time", label: "88:88", type: "txt", font: "6x8:5", bgCol: g.theme.bg}, // size updated below
|
||||
{id: "ampm", label: " ", type: "txt", font: "6x8:2", bgCol: g.theme.bg},
|
||||
],
|
||||
},
|
||||
{id: "bar", type: "custom", fraction: 0, fillx: 1, height: 6, col: g.theme.fg2, render: renderBar},
|
||||
{height: 40},
|
||||
{id: "date", type: "txt", font: "10%", valign: 1},
|
||||
],
|
||||
}, {lazy: true});
|
||||
// adjustments based on screen size and whether we display am/pm
|
||||
let thickness; // bar thickness, same as time font "pixel block" size
|
||||
if (is12Hour) {
|
||||
// Maximum font size = (<screen width> - <ampm: 2chars * (2*6)px>) / (5chars * 6px)
|
||||
thickness = Math.floor((g.getWidth()-24)/(5*6));
|
||||
} else {
|
||||
layout.ampm.label = "";
|
||||
thickness = Math.floor(g.getWidth()/(5*6));
|
||||
}
|
||||
layout.bar.height = thickness+1;
|
||||
layout.time.font = "6x8:"+thickness;
|
||||
layout.update();
|
||||
|
||||
function timeText(date) {
|
||||
if (!is12Hour) {
|
||||
if (!clock.is12Hour) {
|
||||
return locale.time(date, true);
|
||||
}
|
||||
const date12 = new Date(date.getTime());
|
||||
|
@ -68,7 +37,7 @@ function timeText(date) {
|
|||
return locale.time(date12, true);
|
||||
}
|
||||
function ampmText(date) {
|
||||
return (is12Hour && locale.hasMeridian)? locale.meridian(date) : "";
|
||||
return (clock.is12Hour && locale.hasMeridian) ? locale.meridian(date) : "";
|
||||
}
|
||||
function dateText(date) {
|
||||
const dayName = locale.dow(date, true),
|
||||
|
@ -78,31 +47,48 @@ function dateText(date) {
|
|||
return `${dayName} ${dayMonth}`;
|
||||
}
|
||||
|
||||
draw = function draw(force) {
|
||||
if (!Bangle.isLCDOn()) {return;} // no drawing, also no new update scheduled
|
||||
const date = new Date();
|
||||
layout.time.label = timeText(date);
|
||||
layout.ampm.label = ampmText(date);
|
||||
layout.date.label = dateText(date);
|
||||
const SECONDS_PER_MINUTE = 60;
|
||||
layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE;
|
||||
if (force) {
|
||||
Bangle.drawWidgets();
|
||||
layout.forgetLazyState();
|
||||
}
|
||||
layout.render();
|
||||
// schedule update at start of next second
|
||||
const millis = date.getMilliseconds();
|
||||
setTimeout(draw, 1000-millis);
|
||||
};
|
||||
|
||||
// Show launcher when button pressed
|
||||
Bangle.setUI("clock");
|
||||
Bangle.on("lcdPower", function(on) {
|
||||
if (on) {
|
||||
draw(true);
|
||||
}
|
||||
});
|
||||
g.reset().clear();
|
||||
Bangle.drawWidgets();
|
||||
draw();
|
||||
const ClockFace = require("ClockFace"),
|
||||
clock = new ClockFace({
|
||||
precision:1,
|
||||
init: function() {
|
||||
const Layout = require("Layout");
|
||||
this.layout = new Layout({
|
||||
type: "v", c: [
|
||||
{
|
||||
type: "h", c: [
|
||||
{id: "time", label: "88:88", type: "txt", font: "6x8:5", col:g.theme.fg, bgCol: g.theme.bg}, // size updated below
|
||||
{id: "ampm", label: " ", type: "txt", font: "6x8:2", col:g.theme.fg, bgCol: g.theme.bg},
|
||||
],
|
||||
},
|
||||
{id: "bar", type: "custom", fraction: 0, fillx: 1, height: 6, col: g.theme.fg2, render: renderBar},
|
||||
{height: 40},
|
||||
{id: "date", type: "txt", font: "10%", valign: 1},
|
||||
],
|
||||
}, {lazy: true});
|
||||
// adjustments based on screen size and whether we display am/pm
|
||||
let thickness; // bar thickness, same as time font "pixel block" size
|
||||
if (this.is12Hour) {
|
||||
// Maximum font size = (<screen width> - <ampm: 2chars * (2*6)px>) / (5chars * 6px)
|
||||
thickness = Math.floor((Bangle.appRect.w-24)/(5*6));
|
||||
} else {
|
||||
this.layout.ampm.label = "";
|
||||
thickness = Math.floor(Bangle.appRect.w/(5*6));
|
||||
}
|
||||
this.layout.bar.height = thickness+1;
|
||||
this.layout.time.font = "6x8:"+thickness;
|
||||
this.layout.update();
|
||||
},
|
||||
update: function(date, c) {
|
||||
if (c.m) this.layout.time.label = timeText(date);
|
||||
if (c.h) this.layout.ampm.label = ampmText(date);
|
||||
if (c.d) this.layout.date.label = dateText(date);
|
||||
const SECONDS_PER_MINUTE = 60;
|
||||
if (c.s) this.layout.bar.fraction = date.getSeconds()/SECONDS_PER_MINUTE;
|
||||
this.layout.render();
|
||||
},
|
||||
resume: function() {
|
||||
this.layout.forgetLazyState();
|
||||
},
|
||||
});
|
||||
clock.start();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "barclock",
|
||||
"name": "Bar Clock",
|
||||
"version": "0.09",
|
||||
"version": "0.11",
|
||||
"description": "A simple digital clock showing seconds as a bar",
|
||||
"icon": "clock-bar.png",
|
||||
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}],
|
||||
|
|
|
@ -3,4 +3,5 @@
|
|||
0.03: Adapt colors based on the theme of the user.
|
||||
0.04: Steps can be hidden now such that the time is even larger.
|
||||
0.05: Included icons for information.
|
||||
0.06: Design and usability improvements.
|
||||
0.06: Design and usability improvements.
|
||||
0.07: Improved positioning.
|
|
@ -259,11 +259,12 @@ function draw() {
|
|||
|
||||
function drawDate(){
|
||||
// Draw background
|
||||
var y = H/5*2 + (settings.fullscreen ? 0 : 8);
|
||||
var y = H/5*2;
|
||||
g.reset().clearRect(0,0,W,W);
|
||||
|
||||
// Draw date
|
||||
y -= settings.fullscreen ? 8 : 0;
|
||||
y = parseInt(y/2);
|
||||
y += settings.fullscreen ? 0 : 15;
|
||||
var date = new Date();
|
||||
var dateStr = date.getDate();
|
||||
dateStr = ("0" + dateStr).substr(-2);
|
||||
|
@ -276,14 +277,14 @@ function drawDate(){
|
|||
var dayW = Math.max(g.stringWidth(dayStr), g.stringWidth(monthStr));
|
||||
var fullDateW = dateW + 10 + dayW;
|
||||
|
||||
g.setFontAlign(-1,1);
|
||||
g.setFontAlign(-1,0);
|
||||
g.setMediumFont();
|
||||
g.setColor(g.theme.fg);
|
||||
g.drawString(dateStr, W/2 - fullDateW / 2, y+5);
|
||||
|
||||
g.setSmallFont();
|
||||
g.drawString(monthStr, W/2 - fullDateW/2 + 10 + dateW, y+3);
|
||||
g.drawString(dayStr, W/2 - fullDateW/2 + 10 + dateW, y-23);
|
||||
g.drawString(monthStr, W/2 - fullDateW/2 + 10 + dateW, y+14);
|
||||
g.drawString(dayStr, W/2 - fullDateW/2 + 10 + dateW, y-10);
|
||||
}
|
||||
|
||||
|
||||
|
@ -296,9 +297,9 @@ function drawTime(){
|
|||
|
||||
// Draw time
|
||||
g.setColor(g.theme.bg);
|
||||
g.setFontAlign(0,-1);
|
||||
g.setFontAlign(0,0);
|
||||
var timeStr = locale.time(date,1);
|
||||
y += settings.fullscreen ? 14 : 10;
|
||||
y += parseInt((H - y)/2) + 5;
|
||||
|
||||
var infoEntry = getInfoEntry();
|
||||
var infoStr = infoEntry[0];
|
||||
|
@ -307,9 +308,9 @@ function drawTime(){
|
|||
|
||||
// Show large or small time depending on info entry
|
||||
if(infoStr == null){
|
||||
y += 10;
|
||||
g.setLargeFont();
|
||||
} else {
|
||||
y -= 15;
|
||||
g.setMediumFont();
|
||||
}
|
||||
g.drawString(timeStr, W/2, y);
|
||||
|
@ -319,7 +320,7 @@ function drawTime(){
|
|||
return;
|
||||
}
|
||||
|
||||
y += H/5*2-5;
|
||||
y += 35;
|
||||
g.setFontAlign(0,0);
|
||||
g.setSmallFont();
|
||||
var imgWidth = 0;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "bwclk",
|
||||
"name": "BW Clock",
|
||||
"version": "0.06",
|
||||
"version": "0.07",
|
||||
"description": "BW Clock.",
|
||||
"readme": "README.md",
|
||||
"icon": "app.png",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: App created
|
|
@ -0,0 +1 @@
|
|||
E.toArrayBuffer(atob("ICABAAAAAAAAAAAAAAAAAAHAAAAP8AAAfn4AA/APwA+DwfAPg8HwD+AH8Az4HzAMPnwwDAfgMAwBgDAMCYAwDA2YMAwhmDAMIZAwDCGDMA2BgzAMgYAwDAGAMA8BgPADwYPAAPGPgAB9ngAAH/gAAAfgAAABgAAAAAAAAAAAAAAAAAA="))
|
|
@ -0,0 +1,108 @@
|
|||
var init_message = true;
|
||||
var acc_data;
|
||||
var die_roll = 1;
|
||||
var selected_die = 0;
|
||||
var roll = 0;
|
||||
const dices = [4, 6, 10, 12, 20];
|
||||
|
||||
g.setFontAlign(0,0);
|
||||
|
||||
Bangle.on('touch', function(button, xy) {
|
||||
// Change die if not rolling
|
||||
if(roll < 1){
|
||||
if(selected_die <= 3){
|
||||
selected_die++;
|
||||
}else{
|
||||
selected_die = 0;
|
||||
}
|
||||
}
|
||||
//Disable initial message
|
||||
init_message = false;
|
||||
});
|
||||
|
||||
function rect(){
|
||||
x1 = g.getWidth()/2 - 35;
|
||||
x2 = g.getWidth()/2 + 35;
|
||||
y1 = g.getHeight()/2 - 35;
|
||||
y2 = g.getHeight()/2 + 35;
|
||||
g.drawRect(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
function pentagon(){
|
||||
x1 = g.getWidth()/2;
|
||||
y1 = g.getHeight()/2 - 50;
|
||||
x2 = g.getWidth()/2 - 50;
|
||||
y2 = g.getHeight()/2 - 10;
|
||||
x3 = g.getWidth()/2 - 30;
|
||||
y3 = g.getHeight()/2 + 30;
|
||||
x4 = g.getWidth()/2 + 30;
|
||||
y4 = g.getHeight()/2 + 30;
|
||||
x5 = g.getWidth()/2 + 50;
|
||||
y5 = g.getHeight()/2 - 10;
|
||||
g.drawPoly([x1, y1, x2, y2, x3, y3, x4, y4, x5, y5], true);
|
||||
}
|
||||
|
||||
function triangle(){
|
||||
x1 = g.getWidth()/2;
|
||||
y1 = g.getHeight()/2 - 57;
|
||||
x2 = g.getWidth()/2 - 50;
|
||||
y2 = g.getHeight()/2 + 23;
|
||||
x3 = g.getWidth()/2 + 50;
|
||||
y3 = g.getHeight()/2 + 23;
|
||||
g.drawPoly([x1, y1, x2, y2, x3, y3], true);
|
||||
}
|
||||
|
||||
function drawDie(variant) {
|
||||
if(variant == 1){
|
||||
//Rect, 6
|
||||
rect();
|
||||
}else if(variant == 3){
|
||||
//Pentagon, 12
|
||||
pentagon();
|
||||
}else{
|
||||
//Triangle, 4, 10, 20
|
||||
triangle();
|
||||
}
|
||||
}
|
||||
|
||||
function initMessage(){
|
||||
g.setFont("6x8", 2);
|
||||
g.drawString("Dice-n-Roll", g.getWidth()/2, 20);
|
||||
g.drawString("Shake to roll", g.getWidth()/2, 60);
|
||||
g.drawString("Tap to change", g.getWidth()/2, 80);
|
||||
g.drawString("Tap to start", g.getWidth()/2, 150);
|
||||
}
|
||||
|
||||
function rollDie(){
|
||||
acc_data = Bangle.getAccel();
|
||||
if(acc_data.diff > 0.3){
|
||||
roll = 3;
|
||||
}
|
||||
//Mange the die "roll" by chaning the number a few times
|
||||
if(roll > 0){
|
||||
g.drawString("Rolling!", g.getWidth()/2, 150);
|
||||
die_roll = Math.abs(E.hwRand()) % dices[selected_die] + 1;
|
||||
roll--;
|
||||
}
|
||||
//Draw dice graphics
|
||||
drawDie(selected_die);
|
||||
//Draw dice number
|
||||
g.setFontAlign(0,0);
|
||||
g.setFont("Vector", 45);
|
||||
g.drawString(die_roll, g.getWidth()/2, g.getHeight()/2);
|
||||
//Draw selected die in right corner
|
||||
g.setFont("6x8", 2);
|
||||
g.drawString(dices[selected_die], g.getWidth()-15, 15);
|
||||
}
|
||||
|
||||
function main() {
|
||||
g.clear();
|
||||
if(init_message){
|
||||
initMessage();
|
||||
}else{
|
||||
rollDie();
|
||||
}
|
||||
Bangle.setLCDPower(1);
|
||||
}
|
||||
|
||||
var interval = setInterval(main, 300);
|
After Width: | Height: | Size: 637 B |
After Width: | Height: | Size: 2.4 KiB |
|
@ -0,0 +1,14 @@
|
|||
{ "id": "diceroll",
|
||||
"name": "Dice-n-Roll",
|
||||
"shortName":"Dice-n-Roll",
|
||||
"icon": "app.png",
|
||||
"version":"0.01",
|
||||
"description": "A dice app with a few different dice.",
|
||||
"screenshots": [{"url":"diceroll_screenshot.png"}],
|
||||
"tags": "game",
|
||||
"supports": ["BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"diceroll.app.js","url":"app.js"},
|
||||
{"name":"diceroll.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -11,3 +11,4 @@
|
|||
0.11: Fix bangle.js 1 white icons not displaying
|
||||
0.12: On Bangle 2 change to swiping up/down to move between pages as to match page indicator. Swiping from left to right now loads the clock.
|
||||
0.13: Added swipeExit setting so that left-right to exit is an option
|
||||
0.14: Don't move pages when doing exit swipe.
|
||||
|
|
|
@ -29,6 +29,6 @@ Bangle 2:
|
|||
|
||||
**Touch** - icon to select, scond touch launches app
|
||||
|
||||
**Swipe Left** - move to next page of app icons
|
||||
**Swipe Left/Up** - move to next page of app icons
|
||||
|
||||
**Swipe Right** - move to previous page of app icons
|
||||
**Swipe Right/Down** - move to previous page of app icons
|
||||
|
|
|
@ -93,7 +93,7 @@ Bangle.on("swipe",(dirLeftRight, dirUpDown)=>{
|
|||
if (dirUpDown==-1||dirLeftRight==-1){
|
||||
++page; if (page>maxPage) page=0;
|
||||
drawPage(page);
|
||||
} else if (dirUpDown==1||dirLeftRight==1){
|
||||
} else if (dirUpDown==1||(dirLeftRight==1 && !settings.swipeExit)){
|
||||
--page; if (page<0) page=maxPage;
|
||||
drawPage(page);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "dtlaunch",
|
||||
"name": "Desktop Launcher",
|
||||
"version": "0.13",
|
||||
"version": "0.14",
|
||||
"description": "Desktop style App Launcher with six (four for Bangle 2) apps per page - fast access if you have lots of apps installed.",
|
||||
"screenshots": [{"url":"shot1.png"},{"url":"shot2.png"},{"url":"shot3.png"}],
|
||||
"icon": "icon.png",
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: New App!
|
|
@ -0,0 +1,33 @@
|
|||
# F9 Lander
|
||||
|
||||
Land a Falcon 9 booster on a drone ship.
|
||||
|
||||
## Game play
|
||||
|
||||
Attempt to land your Falcon 9 booster on a drone ship before running out of fuel.
|
||||
A successful landing requires:
|
||||
* setting down on the ship
|
||||
* the booster has to be mostly vertical
|
||||
* the landing speed cannot be too high
|
||||
|
||||
## Controls
|
||||
|
||||
The angle of the booster is controlled by tilting the watch side-to-side. The
|
||||
throttle level is controlled by tilting the watch forward and back:
|
||||
* screen horizontal (face up) means no throttle
|
||||
* screen vertical corresponds to full throttle
|
||||
|
||||
The fuel burn rate is proportional to the throttle level.
|
||||
|
||||
## Creators
|
||||
Liam Kl. B.
|
||||
|
||||
Marko Kl. B.
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwcA/4AD/P8yVJkgCCye27dt2wRE//kCIuSuwRIBwgCCpwRQpIRRnYRQkmdCIvPCJICBEZ4RG/IRP/15CJ/z5IRPz4RM/gQB/n+BxICCn/z/P/BxQCDz7mIAX4Cq31/CJ+ebpiYE/IR/CNP/5IROnn//4jP5DFQ5sJCKAjPk3oCMMk4QRQAX4Ckn7jBAA/5CK8nCJPJNHA"))
|
|
@ -0,0 +1,150 @@
|
|||
const falcon9 = Graphics.createImage(`
|
||||
xxxxx
|
||||
xxxxx xxxxx
|
||||
x x
|
||||
x x
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxx
|
||||
xxxxxxxxx
|
||||
xx xxxxx xx
|
||||
xx xx`);
|
||||
|
||||
const droneShip = Graphics.createImage(`
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
`);
|
||||
|
||||
const droneX = Math.floor(Math.random()*(g.getWidth()-droneShip.width-40) + 20)
|
||||
const cloudOffs = Math.floor(Math.random()*g.getWidth()/2);
|
||||
|
||||
const oceanHeight = g.getHeight()*0.1;
|
||||
|
||||
const targetY = g.getHeight()-oceanHeight-falcon9.height/2;
|
||||
|
||||
var booster = { x : g.getWidth()/4 + Math.random()*g.getWidth()/2,
|
||||
y : 20,
|
||||
vx : 0,
|
||||
vy : 0,
|
||||
mass : 100,
|
||||
fuel : 100 };
|
||||
|
||||
var exploded = false;
|
||||
var nExplosions = 0;
|
||||
var landed = false;
|
||||
|
||||
const gravity = 4;
|
||||
const dt = 0.1;
|
||||
const fuelBurnRate = 20*(176/g.getHeight());
|
||||
const maxV = 12;
|
||||
|
||||
function flameImageGen (throttle) {
|
||||
var str = " xxx \n xxx \n";
|
||||
str += "xxxxx\n".repeat(throttle);
|
||||
str += " xxx \n x \n";
|
||||
return Graphics.createImage(str);
|
||||
}
|
||||
|
||||
function drawFalcon(x, y, throttle, angle) {
|
||||
g.setColor(1, 1, 1).drawImage(falcon9, x, y, {rotate:angle});
|
||||
if (throttle>0) {
|
||||
var flameImg = flameImageGen(throttle);
|
||||
var r = falcon9.height/2 + flameImg.height/2-1;
|
||||
var xoffs = -Math.sin(angle)*r;
|
||||
var yoffs = Math.cos(angle)*r;
|
||||
if (Math.random()>0.7) g.setColor(1, 0.5, 0);
|
||||
else g.setColor(1, 1, 0);
|
||||
g.drawImage(flameImg, x+xoffs, y+yoffs, {rotate:angle});
|
||||
}
|
||||
}
|
||||
|
||||
function drawBG() {
|
||||
g.setBgColor(0.2, 0.2, 1).clear();
|
||||
g.setColor(0, 0, 1).fillRect(0, g.getHeight()-oceanHeight, g.getWidth()-1, g.getHeight()-1);
|
||||
g.setColor(0.5, 0.5, 1).fillCircle(cloudOffs+34, 30, 15).fillCircle(cloudOffs+60, 35, 20).fillCircle(cloudOffs+75, 20, 10);
|
||||
g.setColor(1, 1, 0).fillCircle(g.getWidth(), 0, 20);
|
||||
g.setColor(1, 1, 1).drawImage(droneShip, droneX, g.getHeight()-oceanHeight-1);
|
||||
}
|
||||
|
||||
function showFuel() {
|
||||
g.setColor(0, 0, 0).setFont("4x6:2").setFontAlign(-1, -1, 0).drawString("Fuel: "+Math.abs(booster.fuel).toFixed(0), 4, 4);
|
||||
}
|
||||
|
||||
function renderScreen(input) {
|
||||
drawBG();
|
||||
showFuel();
|
||||
drawFalcon(booster.x, booster.y, Math.floor(input.throttle*12), input.angle);
|
||||
}
|
||||
|
||||
function getInputs() {
|
||||
var accel = Bangle.getAccel();
|
||||
var a = Math.PI/2 + Math.atan2(accel.y, accel.x);
|
||||
var t = (1+accel.z);
|
||||
if (t > 1) t = 1;
|
||||
if (t < 0) t = 0;
|
||||
if (booster.fuel<=0) t = 0;
|
||||
return {throttle: t, angle: a};
|
||||
}
|
||||
|
||||
function epilogue(str) {
|
||||
g.setFont("Vector", 24).setFontAlign(0, 0, 0).setColor(0, 0, 0).drawString(str, g.getWidth()/2, g.getHeight()/2).flip();
|
||||
g.setFont("Vector", 16).drawString("<= again exit =>", g.getWidth()/2, g.getHeight()/2+20);
|
||||
clearInterval(stepInterval);
|
||||
Bangle.on("swipe", (d) => { if (d>0) load(); else load('f9lander.app.js'); });
|
||||
}
|
||||
|
||||
function gameStep() {
|
||||
if (exploded) {
|
||||
if (nExplosions++ < 15) {
|
||||
var r = Math.random()*25;
|
||||
var x = Math.random()*30 - 15;
|
||||
var y = Math.random()*30 - 15;
|
||||
g.setColor(1, Math.random()*0.5+0.5, 0).fillCircle(booster.x+x, booster.y+y, r);
|
||||
if (nExplosions==1) Bangle.buzz(600);
|
||||
}
|
||||
else epilogue("You crashed!");
|
||||
}
|
||||
else {
|
||||
var input = getInputs();
|
||||
if (booster.y >= targetY) {
|
||||
// console.log(booster.x + " " + booster.y + " " + booster.vy + " " + droneX + " " + input.angle);
|
||||
if (Math.abs(booster.x-droneX-droneShip.width/2)<droneShip.width/2 && Math.abs(input.angle)<Math.PI/8 && booster.vy<maxV) {
|
||||
renderScreen({angle:0, throttle:0});
|
||||
epilogue("You landed!");
|
||||
}
|
||||
else exploded = true;
|
||||
}
|
||||
else {
|
||||
booster.x += booster.vx*dt;
|
||||
booster.y += booster.vy*dt;
|
||||
booster.vy += gravity*dt;
|
||||
booster.fuel -= input.throttle*dt*fuelBurnRate;
|
||||
booster.vy += -Math.cos(input.angle)*input.throttle*gravity*3*dt;
|
||||
booster.vx += Math.sin(input.angle)*input.throttle*gravity*3*dt;
|
||||
renderScreen(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var stepInterval;
|
||||
Bangle.setLCDTimeout(0);
|
||||
renderScreen({angle:0, throttle:0});
|
||||
g.setFont("Vector", 24).setFontAlign(0, 0, 0).setColor(0, 0, 0).drawString("Swipe to start", g.getWidth()/2, g.getHeight()/2);
|
||||
Bangle.on("swipe", () => {
|
||||
stepInterval = setInterval(gameStep, Math.floor(1000*dt));
|
||||
Bangle.removeListener("swipe");
|
||||
});
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 722 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.2 KiB |
|
@ -0,0 +1,15 @@
|
|||
{ "id": "f9lander",
|
||||
"name": "Falcon9 Lander",
|
||||
"shortName":"F9lander",
|
||||
"version":"0.01",
|
||||
"description": "Land a rocket booster",
|
||||
"icon": "f9lander.png",
|
||||
"screenshots" : [ { "url":"f9lander_screenshot1.png" }, { "url":"f9lander_screenshot2.png" }, { "url":"f9lander_screenshot3.png" }],
|
||||
"readme": "README.md",
|
||||
"tags": "game",
|
||||
"supports" : ["BANGLEJS", "BANGLEJS2"],
|
||||
"storage": [
|
||||
{"name":"f9lander.app.js","url":"app.js"},
|
||||
{"name":"f9lander.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -13,3 +13,4 @@
|
|||
0.12: Add setting for Daily Step Goal
|
||||
0.13: Add support for internationalization
|
||||
0.14: Move settings
|
||||
0.15: Fix charts (fix #1366)
|
||||
|
|
|
@ -46,7 +46,7 @@ function menuHRM() {
|
|||
|
||||
function stepsPerHour() {
|
||||
E.showMessage(/*LANG*/"Loading...");
|
||||
let data = new Uint16Array(24);
|
||||
var data = new Uint16Array(24);
|
||||
require("health").readDay(new Date(), h=>data[h.hr]+=h.steps);
|
||||
g.clear(1);
|
||||
Bangle.drawWidgets();
|
||||
|
@ -57,7 +57,7 @@ function stepsPerHour() {
|
|||
|
||||
function stepsPerDay() {
|
||||
E.showMessage(/*LANG*/"Loading...");
|
||||
let data = new Uint16Array(31);
|
||||
var data = new Uint16Array(31);
|
||||
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.steps);
|
||||
g.clear(1);
|
||||
Bangle.drawWidgets();
|
||||
|
@ -68,8 +68,8 @@ function stepsPerDay() {
|
|||
|
||||
function hrmPerHour() {
|
||||
E.showMessage(/*LANG*/"Loading...");
|
||||
let data = new Uint16Array(24);
|
||||
let cnt = new Uint8Array(23);
|
||||
var data = new Uint16Array(24);
|
||||
var cnt = new Uint8Array(23);
|
||||
require("health").readDay(new Date(), h=>{
|
||||
data[h.hr]+=h.bpm;
|
||||
if (h.bpm) cnt[h.hr]++;
|
||||
|
@ -84,8 +84,8 @@ function hrmPerHour() {
|
|||
|
||||
function hrmPerDay() {
|
||||
E.showMessage(/*LANG*/"Loading...");
|
||||
let data = new Uint16Array(31);
|
||||
let cnt = new Uint8Array(31);
|
||||
var data = new Uint16Array(31);
|
||||
var cnt = new Uint8Array(31);
|
||||
require("health").readDailySummaries(new Date(), h=>{
|
||||
data[h.day]+=h.bpm;
|
||||
if (h.bpm) cnt[h.day]++;
|
||||
|
@ -100,7 +100,7 @@ function hrmPerDay() {
|
|||
|
||||
function movementPerHour() {
|
||||
E.showMessage(/*LANG*/"Loading...");
|
||||
let data = new Uint16Array(24);
|
||||
var data = new Uint16Array(24);
|
||||
require("health").readDay(new Date(), h=>data[h.hr]+=h.movement);
|
||||
g.clear(1);
|
||||
Bangle.drawWidgets();
|
||||
|
@ -111,7 +111,7 @@ function movementPerHour() {
|
|||
|
||||
function movementPerDay() {
|
||||
E.showMessage(/*LANG*/"Loading...");
|
||||
let data = new Uint16Array(31);
|
||||
var data = new Uint16Array(31);
|
||||
require("health").readDailySummaries(new Date(), h=>data[h.day]+=h.movement);
|
||||
g.clear(1);
|
||||
Bangle.drawWidgets();
|
||||
|
@ -183,7 +183,7 @@ function drawBarChart() {
|
|||
}
|
||||
|
||||
// draw a fake 0 height bar if chart_index is outside the bounds of the array
|
||||
if ((chart_index + bar - 1) >= 0 && (chart_index + bar - 1) < data_len)
|
||||
if ((chart_index + bar - 1) >= 0 && (chart_index + bar - 1) < data_len && chart_max_datum > 0)
|
||||
bar_top = bar_bot - 100 * (chart_data[chart_index + bar - 1]) / chart_max_datum;
|
||||
else
|
||||
bar_top = bar_bot;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "health",
|
||||
"name": "Health Tracking",
|
||||
"version": "0.14",
|
||||
"version": "0.15",
|
||||
"description": "Logs health data and provides an app to view it",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system,health",
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# This is a simple homework app
|
||||
Use the touchscreen to navigate.
|
||||
|
||||
Requires the "textinput" library. (Tap keyboard)
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/AH4AbhvQCyvd7oYTCwQYTCwgYRCwwYPIgpKQCA4YOBxIYMBhYLLHhgYEC5BsKDAYXHCwUBiUikAYIC4wtDC5IYCA4pEEC5QYBYRUCkQXJAA8K1Wq0AXHhGIxGAC5ZHHC8ZDDC4cM5qaBC8ZHHC68N6czmAXrL94X/C/4XHgUiCYIDDa54XXO/4XHAH4A/ABY="))
|
|
@ -0,0 +1,212 @@
|
|||
var Layout = require("Layout");
|
||||
|
||||
var homework = require("Storage").readJSON("homework.txt", "r");
|
||||
var mainCheckHomeworkMenu;
|
||||
|
||||
var nhwmn = { // New homework Menu
|
||||
"": {
|
||||
"title": "New HW Subject:"
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
function newHomeworkMenu() {
|
||||
E.showMessage("Getting subjects...");
|
||||
var rawsubjects = require("Storage").read("subjects.txt"); // This code reads out the subjects list and removes the newline character at the end
|
||||
var splitsubjects = rawsubjects.split(",");
|
||||
var lastItem = splitsubjects[splitsubjects.length - 1];
|
||||
var thiscurrentsubject;
|
||||
var command;
|
||||
lastItem = lastItem.slice(0, -1);
|
||||
splitsubjects[splitsubjects.length - 1] = lastItem;
|
||||
for (let i = 0; i < splitsubjects.length; i++) { // loop through array and add to menu
|
||||
thiscurrentsubject = splitsubjects[i];
|
||||
command = addNewHomework(thiscurrentsubject);
|
||||
nhwmn[splitsubjects[i]] = addNewHomework.bind(null, thiscurrentsubject);
|
||||
}
|
||||
nhwmn["Back"] = function() {E.showMenu(mainMenu);};
|
||||
console.log(nhwmn);
|
||||
E.showMenu(nhwmn);
|
||||
}
|
||||
var mode = "mainmenu";
|
||||
var statusmsg;
|
||||
var mainMenu = {
|
||||
"": {
|
||||
title: "--Main Menu--"
|
||||
},
|
||||
"New Homework": function() {
|
||||
newHomeworkMenu();
|
||||
mode = "newhomework";
|
||||
},
|
||||
"Check Homework": function() {
|
||||
checkUnfinishedHomeworkAssembler();
|
||||
},
|
||||
"Reset Homework": function() {
|
||||
E.showPrompt("Are you sure you want to delete homework.txt?", {
|
||||
buttons: {
|
||||
"No": false,
|
||||
"Yes": true
|
||||
}
|
||||
}).then(function(v) {
|
||||
if (v) {
|
||||
require("Storage").write("homework.txt", '{"homework":[]}');
|
||||
homework = require("Storage").readJSON("homework.txt", "r");
|
||||
E.showMenu(mainMenu);
|
||||
|
||||
}else{
|
||||
E.showMenu(mainMenu);
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
function checkUnfinishedHomeworkAssembler() {
|
||||
homework = require("Storage").readJSON("homework.txt", "r");
|
||||
var hwcount = Object.keys(homework.homework).length;
|
||||
mainCheckHomeworkMenu = {
|
||||
'': {
|
||||
'title': 'Unfinished HW:'
|
||||
}
|
||||
};
|
||||
// This code snippet gets the unfinished HW and puts it in mainCheckHomeworkMenu
|
||||
// btw mainCheckHomeworkMenu displays all the homework, when tapping on it you get more details with checkPreciseHomework function
|
||||
for (var i = 0; i < hwcount; ++i) {
|
||||
if (homework.homework[i].done === false) {
|
||||
var currentsubject = i; //attempting to pass i
|
||||
mainCheckHomeworkMenu[homework.homework[i].subject] = checkPreciseHomework.bind(null, currentsubject);
|
||||
}
|
||||
|
||||
}
|
||||
mainCheckHomeworkMenu["See Archived HW"] = function() {
|
||||
checkFinishedHomeworkAssembler();
|
||||
};
|
||||
mainCheckHomeworkMenu["Back to Main Menu"] = function() {
|
||||
mode = "mainmenu";
|
||||
E.showMenu(mainMenu);
|
||||
};
|
||||
console.log(mainCheckHomeworkMenu);
|
||||
E.showMenu(mainCheckHomeworkMenu);
|
||||
}
|
||||
|
||||
function checkFinishedHomeworkAssembler() {
|
||||
homework = require("Storage").readJSON("homework.txt", "r");
|
||||
var hwcount = Object.keys(homework.homework).length;
|
||||
mainCheckHomeworkMenu = {
|
||||
'': {
|
||||
'title': 'Archived HW:'
|
||||
}
|
||||
};
|
||||
|
||||
// This code snippet gets the unfinished HW and puts it in mainCheckHomeworkMenu
|
||||
// btw mainCheckHomeworkMenu displays all the homework, when tapping on it you get more details with checkPreciseHomework function (currently being written)
|
||||
for (var i = 0; i < hwcount; ++i) {
|
||||
if (homework.homework[i].done === true) {
|
||||
var currentsubject = i; //attempting to pass i
|
||||
mainCheckHomeworkMenu[homework.homework[i].subject] = checkPreciseHomework.bind(null, currentsubject);
|
||||
}
|
||||
|
||||
}
|
||||
mainCheckHomeworkMenu["Back"] = function() {
|
||||
mode = "mainmenu";
|
||||
E.showMenu(mainMenu);
|
||||
};
|
||||
E.showMenu(mainCheckHomeworkMenu);
|
||||
}
|
||||
|
||||
function checkPreciseHomework(subjectnum) { // P A I N
|
||||
homework = require("Storage").read("homework.txt", "r");
|
||||
homework = JSON.parse(homework);
|
||||
var subject = homework.homework[subjectnum].subject;
|
||||
var task = homework.homework[subjectnum].task;
|
||||
var taskmsg = "Task: " + homework.homework[subjectnum].task;
|
||||
if (homework.homework[subjectnum].done === false) {
|
||||
statusmsg = "Status: Unfinished";
|
||||
} else {
|
||||
statusmsg = "Status: Finished";
|
||||
}
|
||||
var datetimerecieved = homework.homework[subjectnum].datetimerecievehw;
|
||||
var datetimerecievedmsg = "Recieved: " + homework.homework[subjectnum].datetimerecievehw;
|
||||
var checkPreciseHomeworkMenu = {
|
||||
'': {
|
||||
'title': subject
|
||||
},
|
||||
};
|
||||
checkPreciseHomeworkMenu[subject] = function() {},
|
||||
checkPreciseHomeworkMenu[taskmsg] = function() {},
|
||||
checkPreciseHomeworkMenu[statusmsg] = function() {
|
||||
status = "Status: Finished";
|
||||
var d = new Date();
|
||||
var currenttime = require("locale").time(d, 1);
|
||||
var currentdate = require("locale").date(d);
|
||||
var datetime = (currenttime + " " + currentdate);
|
||||
delete homework.homework[subjectnum];
|
||||
homework.homework.push({
|
||||
subject: subject,
|
||||
task: task,
|
||||
done: true,
|
||||
datetimerecievehw: datetimerecieved,
|
||||
datetimehwdone: datetime
|
||||
});
|
||||
require("Storage").write("homework.txt", JSON.stringify(homework));
|
||||
checkUnfinishedHomeworkAssembler();
|
||||
},
|
||||
checkPreciseHomeworkMenu[datetimerecievedmsg] = function() {},
|
||||
checkPreciseHomeworkMenu["Back"] = function() {
|
||||
checkUnfinishedHomeworkAssembler();
|
||||
},
|
||||
|
||||
E.showMenu(checkPreciseHomeworkMenu);
|
||||
|
||||
|
||||
}
|
||||
|
||||
function pushHomework(subject, status, datetimehwdone) {
|
||||
homework = require("Storage").readJSON("homework.txt", "r");
|
||||
|
||||
}
|
||||
|
||||
function addNewHomework(subject) { // Pass subject
|
||||
console.log(subject);
|
||||
require("textinput").input().then(result => {
|
||||
if (result === "") {
|
||||
mode = "newhomework";
|
||||
newHomeworkMenu();
|
||||
} else {
|
||||
var d = new Date();
|
||||
// update time and date
|
||||
var currenttime = require("locale").time(d, 1);
|
||||
var currentdate = require("locale").date(d);
|
||||
var datetime = (currenttime + " " + currentdate);
|
||||
homework.homework.push({
|
||||
subject: subject,
|
||||
task: result,
|
||||
done: false,
|
||||
datetimerecievehw: datetime
|
||||
}); // TODO: when HW is done, add datetimeendhw !!!
|
||||
console.log("subject is" + subject);
|
||||
|
||||
//homework.homework[subject] = result;
|
||||
require("Storage").write("homework.txt", JSON.stringify(homework));
|
||||
E.showMenu(mainMenu);
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function main() { // why does this still exist
|
||||
if (mode === "mainmenu") {
|
||||
E.showMenu(mainMenu);
|
||||
|
||||
} else if (mode === "newhomework") {
|
||||
newHomeworkMenu()
|
||||
|
||||
}
|
||||
}
|
||||
g.clear();
|
||||
Bangle.loadWidgets();
|
||||
Bangle.drawWidgets();
|
||||
main();
|
||||
//loop = setInterval(main, 1);
|
After Width: | Height: | Size: 684 B |
|
@ -0,0 +1,16 @@
|
|||
|
||||
{ "id": "homework",
|
||||
"name": "Homework",
|
||||
"shortName":"Homework",
|
||||
"version":"0.1",
|
||||
"description": "A simple app to manage homework",
|
||||
"icon": "app.png",
|
||||
"tags": "tool",
|
||||
"supports" : ["BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"custom": "subjects.html",
|
||||
"storage": [
|
||||
{"name":"homework.app.js","url":"app.js"},
|
||||
{"name":"homework.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="../../css/spectre.min.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Subjects: <input type="text" id="subjects" class="form-input" value="Seperate subjects by comma, eg. German,Maths,Geography,..."></p>
|
||||
<p>Click <button id="upload" class="btn btn-primary">Upload</button></p>
|
||||
|
||||
<script src="../../core/lib/customize.js"></script>
|
||||
|
||||
<script>
|
||||
// When the 'upload' button is clicked...
|
||||
document.getElementById("upload").addEventListener("click", function() {
|
||||
// get the text to add
|
||||
var text = document.getElementById("subjects").value;
|
||||
console.log(text);
|
||||
// build the app's text using a templated String
|
||||
var app = text;
|
||||
// send finished app (in addition to contents of app.json)
|
||||
sendCustomizedApp({
|
||||
storage:[
|
||||
{name:"subjects.txt"},
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1 +1,2 @@
|
|||
0.01: New keyboard
|
||||
0.02: Introduce setting "Show help button?". Make setting firstLaunch invisible by removing corresponding code from settings.js. Add marker that shows when character selection timeout has run out. Display opened text on launch when editing existing text string. Perfect horizontal alignment of buttons. Tweak help message letter casing.
|
||||
|
|
|
@ -10,7 +10,8 @@ Uses the multitap keypad logic originally from here: http://www.espruino.com/Mor
|
|||
|
||||

|
||||

|
||||

|
||||
|
||||
Written by: [Sir Indy](https://github.com/sir-indy) and [Thyttan](https://github.com/thyttan)
|
||||
|
||||
For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/)
|
||||
For support and discussion please post in the [Bangle JS Forum](http://forum.espruino.com/microcosms/1424/)
|
||||
|
|
|
@ -8,6 +8,7 @@ exports.input = function(options) {
|
|||
var settings = require('Storage').readJSON("kbmulti.settings.json", true) || {};
|
||||
if (settings.firstLaunch===undefined) { settings.firstLaunch = true; }
|
||||
if (settings.charTimeout===undefined) { settings.charTimeout = 500; }
|
||||
if (settings.showHelpBtn===undefined) { settings.showHelpBtn = true; }
|
||||
|
||||
var fontSize = "6x15";
|
||||
var Layout = require("Layout");
|
||||
|
@ -16,26 +17,30 @@ exports.input = function(options) {
|
|||
"4":"GHI4","5":"JKL5","6":"MNO6",
|
||||
"7":"PQRS7","8":"TUV80","9":"WXYZ9",
|
||||
};
|
||||
var helpMessage = 'swipe:\nRight: Space\nLeft:Backspace\nUp/Down: Caps lock\n';
|
||||
var helpMessage = 'Swipe:\nRight: Space\nLeft:Backspace\nUp/Down: Caps lock\n';
|
||||
|
||||
var charTimeout; // timeout after a key is pressed
|
||||
var charCurrent; // current character (index in letters)
|
||||
var charIndex; // index in letters[charCurrent]
|
||||
var caps = true;
|
||||
var layout;
|
||||
var btnWidth = g.getWidth()/3
|
||||
|
||||
function displayText() {
|
||||
function displayText(hideMarker) {
|
||||
layout.clear(layout.text);
|
||||
layout.text.label = text.slice(-12);
|
||||
layout.text.label = text.slice(settings.showHelpBtn ? -11 : -13) + (hideMarker ? " " : "_");
|
||||
layout.render(layout.text);
|
||||
}
|
||||
|
||||
function backspace() {
|
||||
// remove the timeout if we had one
|
||||
function deactivateTimeout(charTimeout) {
|
||||
if (charTimeout!==undefined) {
|
||||
clearTimeout(charTimeout);
|
||||
charTimeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function backspace() {
|
||||
deactivateTimeout(charTimeout);
|
||||
text = text.slice(0, -1);
|
||||
newCharacter();
|
||||
}
|
||||
|
@ -55,11 +60,7 @@ exports.input = function(options) {
|
|||
}
|
||||
|
||||
function onKeyPad(key) {
|
||||
// remove the timeout if we had one
|
||||
if (charTimeout!==undefined) {
|
||||
clearTimeout(charTimeout);
|
||||
charTimeout = undefined;
|
||||
}
|
||||
deactivateTimeout(charTimeout);
|
||||
// work out which char was pressed
|
||||
if (key==charCurrent) {
|
||||
charIndex = (charIndex+1) % letters[charCurrent].length;
|
||||
|
@ -69,12 +70,12 @@ exports.input = function(options) {
|
|||
}
|
||||
var newLetter = letters[charCurrent][charIndex];
|
||||
text += (caps ? newLetter.toUpperCase() : newLetter.toLowerCase());
|
||||
displayText();
|
||||
// set a timeout
|
||||
charTimeout = setTimeout(function() {
|
||||
charTimeout = undefined;
|
||||
newCharacter();
|
||||
}, settings.charTimeout);
|
||||
displayText(charTimeout);
|
||||
}
|
||||
|
||||
function onSwipe(dirLeftRight, dirUpDown) {
|
||||
|
@ -104,25 +105,26 @@ exports.input = function(options) {
|
|||
type:"v", c: [
|
||||
{type:"h", c: [
|
||||
{type:"txt", font:"12x20", label:text.slice(-12), id:"text", fillx:1},
|
||||
{type:"btn", font:'6x8', label:'?', cb: l=>onHelp(resolve,reject), filly:1 },
|
||||
(settings.showHelpBtn ? {type:"btn", font:'6x8', label:'?', cb: l=>onHelp(resolve,reject), filly:1 } : {}),
|
||||
]},
|
||||
{type:"h", c: [
|
||||
{type:"btn", font:fontSize, label:letters[1], cb: l=>onKeyPad(1), id:'1', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[2], cb: l=>onKeyPad(2), id:'2', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[3], cb: l=>onKeyPad(3), id:'3', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[1], cb: l=>onKeyPad(1), id:'1', width:btnWidth, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[2], cb: l=>onKeyPad(2), id:'2', width:btnWidth, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[3], cb: l=>onKeyPad(3), id:'3', width:btnWidth, filly:1 },
|
||||
]},
|
||||
{type:"h", filly:1, c: [
|
||||
{type:"btn", font:fontSize, label:letters[4], cb: l=>onKeyPad(4), id:'4', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[5], cb: l=>onKeyPad(5), id:'5', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[6], cb: l=>onKeyPad(6), id:'6', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[4], cb: l=>onKeyPad(4), id:'4', width:btnWidth, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[5], cb: l=>onKeyPad(5), id:'5', width:btnWidth, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[6], cb: l=>onKeyPad(6), id:'6', width:btnWidth, filly:1 },
|
||||
]},
|
||||
{type:"h", filly:1, c: [
|
||||
{type:"btn", font:fontSize, label:letters[7], cb: l=>onKeyPad(7), id:'7', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[8], cb: l=>onKeyPad(8), id:'8', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[9], cb: l=>onKeyPad(9), id:'9', fillx:1, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[7], cb: l=>onKeyPad(7), id:'7', width:btnWidth, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[8], cb: l=>onKeyPad(8), id:'8', width:btnWidth, filly:1 },
|
||||
{type:"btn", font:fontSize, label:letters[9], cb: l=>onKeyPad(9), id:'9', width:btnWidth, filly:1 },
|
||||
]},
|
||||
]
|
||||
},{back: ()=>{
|
||||
deactivateTimeout(charTimeout);
|
||||
Bangle.setUI();
|
||||
Bangle.removeListener("swipe", onSwipe);
|
||||
g.clearRect(Bangle.appRect);
|
||||
|
@ -132,12 +134,13 @@ exports.input = function(options) {
|
|||
|
||||
return new Promise((resolve,reject) => {
|
||||
g.clearRect(Bangle.appRect);
|
||||
if (settings.firstLaunch) {
|
||||
onHelp(resolve,reject);
|
||||
if (settings.firstLaunch) {
|
||||
onHelp(resolve,reject);
|
||||
settings.firstLaunch = false;
|
||||
require('Storage').writeJSON("kbmulti.settings.json", settings);
|
||||
} else {
|
||||
generateLayout(resolve,reject);
|
||||
displayText(false);
|
||||
Bangle.on('swipe', onSwipe);
|
||||
layout.render();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{ "id": "kbmulti",
|
||||
"name": "Multitap keyboard",
|
||||
"version":"0.01",
|
||||
"version":"0.02",
|
||||
"description": "A library for text input via multitap/T9 style keypad",
|
||||
"icon": "app.png",
|
||||
"type":"textinput",
|
||||
|
|
After Width: | Height: | Size: 3.4 KiB |
|
@ -1,7 +1,7 @@
|
|||
(function(back) {
|
||||
function settings() {
|
||||
var settings = require('Storage').readJSON("kbmulti.settings.json", true) || {};
|
||||
if (settings.firstLaunch===undefined) { settings.firstLaunch = true; }
|
||||
if (settings.showHelpBtn===undefined) { settings.showHelpBtn = true; }
|
||||
if (settings.charTimeout===undefined) { settings.charTimeout = 500; }
|
||||
return settings;
|
||||
}
|
||||
|
@ -21,11 +21,11 @@
|
|||
format: v => v,
|
||||
onchange: v => updateSetting("charTimeout", v),
|
||||
},
|
||||
/*LANG*/'Show help on first launch': {
|
||||
value: !!settings().firstLaunch,
|
||||
/*LANG*/'Show help button?': {
|
||||
value: !!settings().showHelpBtn,
|
||||
format: v => v?"Yes":"No",
|
||||
onchange: v => updateSetting("firstLaunch", v)
|
||||
onchange: v => updateSetting("showHelpBtn", v)
|
||||
}
|
||||
};
|
||||
E.showMenu(mainmenu);
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
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"}
|
||||
}
|
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 3.4 KiB |
|
@ -7,3 +7,4 @@
|
|||
0.07: Update settings
|
||||
Correct `decodeTime(t)` to return a more likely expected time
|
||||
0.08: add day of week check to getActiveAlarms()
|
||||
0.09: Move some functions to new time_utils module
|
||||
|
|
|
@ -61,12 +61,12 @@ exports.reload = function() {
|
|||
exports.newDefaultAlarm = function () {
|
||||
const settings = exports.getSettings();
|
||||
|
||||
let alarm = {
|
||||
var alarm = {
|
||||
t: 12 * 3600000, // Default to 12:00
|
||||
on: true,
|
||||
rp: settings.defaultRepeat,
|
||||
as: settings.defaultAutoSnooze,
|
||||
dow: 0b1111111,
|
||||
dow: settings.defaultRepeat ? 0b1111111 : 0b0000000,
|
||||
last: 0,
|
||||
vibrate: settings.defaultAlarmPattern,
|
||||
};
|
||||
|
@ -79,7 +79,7 @@ exports.newDefaultAlarm = function () {
|
|||
exports.newDefaultTimer = function () {
|
||||
const settings = exports.getSettings();
|
||||
|
||||
let timer = {
|
||||
var timer = {
|
||||
timer: 5 * 60 * 1000, // 5 minutes
|
||||
on: true,
|
||||
rp: false,
|
||||
|
@ -113,20 +113,3 @@ exports.getSettings = function () {
|
|||
exports.setSettings = function(settings) {
|
||||
require("Storage").writeJSON("sched.settings.json", settings);
|
||||
};
|
||||
|
||||
// time in ms -> { hrs, mins }
|
||||
exports.decodeTime = function(t) {
|
||||
t = Math.ceil(t / 60000); // sanitise to full minutes
|
||||
let hrs = 0 | (t / 60);
|
||||
return { hrs: hrs, mins: t - hrs * 60 };
|
||||
}
|
||||
|
||||
// time in { hrs, mins } -> ms
|
||||
exports.encodeTime = function(o) {
|
||||
return o.hrs * 3600000 + o.mins * 60000;
|
||||
}
|
||||
|
||||
exports.formatTime = function(t) {
|
||||
let o = exports.decodeTime(t);
|
||||
return o.hrs + ":" + ("0" + o.mins).substr(-2);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "sched",
|
||||
"name": "Scheduler",
|
||||
"version": "0.08",
|
||||
"version": "0.09",
|
||||
"description": "Scheduling library for alarms and timers",
|
||||
"icon": "app.png",
|
||||
"type": "scheduler",
|
||||
|
|
|
@ -9,7 +9,7 @@ function showAlarm(alarm) {
|
|||
const settings = require("sched").getSettings();
|
||||
|
||||
let msg = "";
|
||||
msg += alarm.timer ? require("sched").formatTime(alarm.timer) : require("sched").formatTime(alarm.t);
|
||||
msg += require("time_utils").formatTime(alarm.timer ? alarm.timer : alarm.t);
|
||||
if (alarm.msg) {
|
||||
msg += "\n"+alarm.msg;
|
||||
} else {
|
||||
|
@ -26,7 +26,7 @@ function showAlarm(alarm) {
|
|||
|
||||
E.showPrompt(msg,{
|
||||
title:alarm.timer ? /*LANG*/"TIMER!" : /*LANG*/"ALARM!",
|
||||
buttons : {/*LANG*/"Snooze":true,/*LANG*/"Ok":false} // default is sleep so it'll come back in 10 mins
|
||||
buttons : {/*LANG*/"Snooze":true,/*LANG*/"Stop":false} // default is sleep so it'll come back in 10 mins
|
||||
}).then(function(sleep) {
|
||||
buzzCount = 0;
|
||||
if (sleep) {
|
||||
|
|
|
@ -2,3 +2,5 @@
|
|||
0.02: Fix crash on start #1423
|
||||
0.03: Added power saving mode, move all read/write log actions into lib/module
|
||||
0.04: Fix #1445, display loading info, add icons to display service states
|
||||
0.05: Fix LOW_MEMORY,MEMORY error on to big log size
|
||||
0.06: Reduced log size further to 750 entries
|
||||
|
|
|
@ -21,7 +21,8 @@ also provides a power saving mode using the built in movement calculation. The i
|
|||
* __Logging__
|
||||
To minimize the log size only a changed state is logged. The logged timestamp is matching the beginning of its measurement period.
|
||||
When not on power saving mode a movement is detected nearly instantaneous and the detection of a no movement period is delayed by the minimal no movement duration. To match the beginning of the measurement period a cached timestamp (_sleeplog.firstnomodate_) is logged.
|
||||
On power saving mode the measurement period is fixed to 10 minutes and all logged timestamps are also set back 10 minutes.
|
||||
On power saving mode the measurement period is fixed to 10 minutes and all logged timestamps are also set back 10 minutes.
|
||||
To prevent a LOW_MEMORY,MEMORY error the log size is limited to 750 entries, older entries will be overwritten.
|
||||
|
||||
---
|
||||
### Control
|
||||
|
|
|
@ -98,6 +98,9 @@ exports = {
|
|||
input = log;
|
||||
}
|
||||
|
||||
// check and if neccessary reduce logsize to prevent low mem
|
||||
if (input.length > 750) input = input.slice(-750);
|
||||
|
||||
// simple check for log plausibility
|
||||
if (input[0].length > 1 && input[0][0] * 1 > 9E11) {
|
||||
// write log to storage
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id":"sleeplog",
|
||||
"name":"Sleep Log",
|
||||
"shortName": "SleepLog",
|
||||
"version": "0.04",
|
||||
"version": "0.06",
|
||||
"description": "Log and view your sleeping habits. This app derived from SleepPhaseAlarm and uses also the principe of Estimation of Stationary Sleep-segments (ESS). It also provides a power saving mode using the built in movement calculation.",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
|
|
|
@ -6,3 +6,4 @@
|
|||
0.06: Add logging
|
||||
use Layout library and display ETA
|
||||
0.07: Add check for day of week
|
||||
0.08: Update to new time_utils module
|
||||
|
|
|
@ -46,8 +46,8 @@ function calc_ess(acc_magn) {
|
|||
var nextAlarm;
|
||||
active.forEach(alarm => {
|
||||
const now = new Date();
|
||||
const t = require("sched").decodeTime(alarm.t);
|
||||
var dateAlarm = new Date(now.getFullYear(), now.getMonth(), now.getDate(), t.hrs, t.mins);
|
||||
const time = require("time_utils").decodeTime(alarm.t);
|
||||
var dateAlarm = new Date(now.getFullYear(), now.getMonth(), now.getDate(), time.h, time.m);
|
||||
if (dateAlarm < now) { // dateAlarm in the past, add 24h
|
||||
dateAlarm.setTime(dateAlarm.getTime() + (24*60*60*1000));
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"id": "sleepphasealarm",
|
||||
"name": "SleepPhaseAlarm",
|
||||
"shortName": "SleepPhaseAlarm",
|
||||
"version": "0.07",
|
||||
"version": "0.08",
|
||||
"description": "Uses the accelerometer to estimate sleep and wake states with the principle of Estimation of Stationary Sleep-segments (ESS, see https://ubicomp.eti.uni-siegen.de/home/datasets/ichi14/index.html.en). This app will read the next alarm from the alarm application and will wake you up to 30 minutes early at the best guessed time when you are almost already awake.",
|
||||
"icon": "app.png",
|
||||
"tags": "alarm",
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
"Messages": "Messaggi",
|
||||
"No Messages": "Nessun messaggio",
|
||||
"Keep Msgs": "Tieni i messaggi",
|
||||
"Mark all read": "Segna tutto come letto",
|
||||
"Mark Unread": "Segna come non letto",
|
||||
"Vibrate": "Vibrazione",
|
||||
"Are you sure": "Sei sicuro/a",
|
||||
|
@ -83,8 +84,8 @@
|
|||
"Background": "Sfondo",
|
||||
"Foreground 2": "Primo piano 2",
|
||||
"Background 2": "Sfondo 2",
|
||||
"Highlight FG": "Selezione PP",
|
||||
"Highlight BG": "Selezione Sf",
|
||||
"Highlight FG": "Primo piano selezione",
|
||||
"Highlight BG": "Sfondo selezione",
|
||||
"Utilities": "Utilità",
|
||||
"Storage": "Memoria",
|
||||
"Compact Storage": "Compatta memoria",
|
||||
|
@ -110,7 +111,7 @@
|
|||
"Loading": "Caricamento",
|
||||
"Launcher Settings": "Impostazioni Launcher",
|
||||
"Font": "Font",
|
||||
"Show clocks": "Mostra orologi",
|
||||
"Show Clocks": "Mostra orologi",
|
||||
"Log": "Log",
|
||||
"Steps": "Passi",
|
||||
"steps": "passi",
|
||||
|
@ -151,19 +152,76 @@
|
|||
"start&lap/reset, BTN1: EXIT": "start&lap/reset, BTN1: EXIT",
|
||||
"back": "indietro",
|
||||
"color": "colore",
|
||||
"BACK": "INDIETRO"
|
||||
"BACK": "INDIETRO",
|
||||
"Select App": "Seleziona app",
|
||||
"No Apps Found": "Nessuna app trovata",
|
||||
"Edit Alarm": "Modifica sveglia",
|
||||
"Auto Snooze": "Ripeti automaticamente",
|
||||
"Delete Alarm": "Cancella sveglia",
|
||||
"Repeat Alarm": "Ripeti sveglia",
|
||||
"Once": "Una volta",
|
||||
"Workdays": "Giorni lavorativi",
|
||||
"Weekends": "Weekend",
|
||||
"Every Day": "Ogni giorno",
|
||||
"Custom": "Personalizza",
|
||||
"Edit Timer": "Modifica timer",
|
||||
"Delete Timer": "Cancella timer",
|
||||
"Scheduler Settings": "Impostazioni schedulatore",
|
||||
"Nothing to Enable": "Niente da attivare",
|
||||
"Nothing to Disable": "Niente da disattivare",
|
||||
"Enable All": "Attiva tutto",
|
||||
"Disable All": "Disattiva tutto",
|
||||
"Delete All": "Cancella tutto",
|
||||
"Updating boot0": "Aggiornamento boot0 in corso",
|
||||
"Reloading": "Ricaricamento in corso",
|
||||
"Date & Time": "Data & ora",
|
||||
"Small": "Piccolo",
|
||||
"Medium": "Medio",
|
||||
"Big": "Grande",
|
||||
"Text size": "Dimensione testo",
|
||||
"Find Phone": "Trova telefono",
|
||||
"Movement": "Movimento",
|
||||
"Heart Rate": "Frequenza cardiaca",
|
||||
"Step Counting": "Conteggio passi",
|
||||
"Daily Step Goal": "Obiettivo passi giornalieri",
|
||||
"Fullscreen": "Schermo intero",
|
||||
"Unlock Watch": "Sblocca orologio",
|
||||
"Flash Icon": "Icona lampeggiante",
|
||||
"Auto-Open Music": "Apri modalità musica automaticamente",
|
||||
"Colour": "Colore",
|
||||
"Notifications": "Notifiche",
|
||||
"Scheduler": "Schedulatore",
|
||||
"Stop": "Stop",
|
||||
"Min Font": "Dimensione minima del font"
|
||||
},
|
||||
"//2": "App-specific overrides",
|
||||
"alarm": {
|
||||
"//": "'Crea' instead of 'Nuovo' because 'Nuovo sveglia' would be weird (and wrong)",
|
||||
"New": "Crea",
|
||||
"Custom Days": "Personalizza i giorni",
|
||||
"Advanced": "Altre funzionalità"
|
||||
},
|
||||
"health": {
|
||||
"Health Tracking": "Monitoraggio salute",
|
||||
"HRM Interval": "Intervallo monitoraggio cardiaco",
|
||||
"3 min": "3 min",
|
||||
"10 min": "10 min",
|
||||
"Always": "Sempre"
|
||||
},
|
||||
"launch": {
|
||||
"Vector font size": "Dim. font vett.",
|
||||
"Vector Font Size": "Dim. font vett.",
|
||||
"App Source\nNot found": "Codice app\nnon trovato"
|
||||
},
|
||||
"messages": {
|
||||
"Unread timer": "Timer msg non letti"
|
||||
"Unread timer": "Timer messaggi non letti"
|
||||
},
|
||||
"run": {
|
||||
"Record Run": "Registra corsa"
|
||||
},
|
||||
"sched": {
|
||||
"Unlock at Buzz": "Sblocca quando vibra",
|
||||
"s": "s"
|
||||
},
|
||||
"setting": {
|
||||
"Clock Style": "Formato ora",
|
||||
"Compacting...\nTakes approx\n1 minute": "Compattamento in corso...\nCi vorrà circa un minuto",
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
Most of the boilerplate needed to run a clock.
|
||||
See ClockFace.md for documentation
|
||||
*/
|
||||
function ClockFace(options) {
|
||||
if ("function"=== typeof options) options = {draw: options}; // simple usage
|
||||
// some validation, in the hopes of at least catching typos/basic mistakes
|
||||
Object.keys(options).forEach(k => {
|
||||
if (![
|
||||
"precision",
|
||||
"init", "draw", "update",
|
||||
"pause", "resume",
|
||||
"up", "down", "upDown"
|
||||
].includes(k)) throw `Invalid ClockFace option: ${k}`;
|
||||
});
|
||||
if (!options.draw && !options.update) throw "ClockFace needs at least one of draw() or update() functions";
|
||||
this.draw = options.draw || (t=> {
|
||||
options.update.apply(this, [t, {d: true, h: true, m: true, s: true}]);
|
||||
});
|
||||
this.update = options.update || (t => {
|
||||
g.clear();
|
||||
options.draw.apply(this, [t, {d: true, h: true, m: true, s: true}]);
|
||||
});
|
||||
if (options.precision===1000||options.precision===60000) throw "ClockFace precision is in seconds, not ms";
|
||||
this.precision = (options.precision || 60);
|
||||
if (options.init) this.init = options.init;
|
||||
if (options.pause) this._pause = options.pause;
|
||||
if (options.resume) this._resume = options.resume;
|
||||
if ((options.up || options.down) && options.upDown) throw "ClockFace up/down and upDown cannot be used together";
|
||||
if (options.up || options.down) this._upDown = (dir) => {
|
||||
if (dir<0 && options.up) options.up.apply(this);
|
||||
if (dir>0 && options.down) options.down.apply(this);
|
||||
};
|
||||
if (options.upDown) this._upDown = options.upDown;
|
||||
|
||||
this.is12Hour = !!(require("Storage").readJSON("setting.json", 1) || {})["12hour"];
|
||||
}
|
||||
|
||||
ClockFace.prototype.tick = function() {
|
||||
const time = new Date();
|
||||
const now = {
|
||||
d: `${time.getFullYear()}-${time.getMonth()}-${time.getDate()}`,
|
||||
h: time.getHours(),
|
||||
m: time.getMinutes(),
|
||||
s: time.getSeconds(),
|
||||
};
|
||||
if (!this._last) {
|
||||
g.clear(true);
|
||||
Bangle.drawWidgets();
|
||||
g.reset();
|
||||
this.draw.apply(this, [time, {d: true, h: true, m: true, s: true}]);
|
||||
} else {
|
||||
let c = {d: false, h: false, m: false, s: false}; // changed
|
||||
if (now.d!==this._last.d) c.d = c.h = c.m = c.s = true;
|
||||
else if (now.h!==this._last.h) c.h = c.m = c.s = true;
|
||||
else if (now.m!==this._last.m) c.m = c.s = true;
|
||||
else if (now.s!==this._last.s) c.s = true;
|
||||
g.reset();
|
||||
this.update.apply(this, [time, c]);
|
||||
}
|
||||
this._last = now;
|
||||
if (this.paused) return; // called redraw() while still paused
|
||||
// figure out timeout: if e.g. precision=60s, update at the start of a new minute
|
||||
const interval = this.precision*1000;
|
||||
this._timeout = setTimeout(() => this.tick(), interval-(Date.now()%interval));
|
||||
};
|
||||
|
||||
ClockFace.prototype.start = function() {
|
||||
Bangle.loadWidgets();
|
||||
if (this.init) this.init.apply(this);
|
||||
if (this._upDown) Bangle.setUI("clockupdown", d=>this._upDown.apply(this,[d]));
|
||||
else Bangle.setUI("clock");
|
||||
delete this._last;
|
||||
this.paused = false;
|
||||
this.tick();
|
||||
|
||||
Bangle.on("lcdPower", on => {
|
||||
if (on) this.resume();
|
||||
else this.pause();
|
||||
});
|
||||
};
|
||||
|
||||
ClockFace.prototype.pause = function() {
|
||||
if (!this._timeout) return; // already paused
|
||||
clearTimeout(this._timeout);
|
||||
delete this._timeout;
|
||||
this.paused = true; // apps might want to check this
|
||||
if (this._pause) this._pause.apply(this);
|
||||
};
|
||||
ClockFace.prototype.resume = function() {
|
||||
if (this._timeout) return; // not paused
|
||||
delete this._last;
|
||||
this.paused = false;
|
||||
if (this._resume) this._resume.apply(this);
|
||||
this.tick(true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Force a complete redraw
|
||||
*/
|
||||
ClockFace.prototype.redraw = function() {
|
||||
delete this._last;
|
||||
this.tick();
|
||||
};
|
||||
|
||||
exports = ClockFace;
|
|
@ -0,0 +1,136 @@
|
|||
ClockFace
|
||||
=========
|
||||
|
||||
This module handles most of the tasks needed to set up a clock, so you can
|
||||
concentrate on drawing the time.
|
||||
|
||||
Example
|
||||
-------
|
||||
Tthe [tutorial clock](https://www.espruino.com/Bangle.js+Clock) converted to use
|
||||
this module:
|
||||
|
||||
```js
|
||||
|
||||
// Load fonts
|
||||
require("Font7x11Numeric7Seg").add(Graphics);
|
||||
// position on screen
|
||||
const X = 160, Y = 140;
|
||||
|
||||
var ClockFace = require("ClockFace");
|
||||
var clock = new ClockFace({
|
||||
precision: 1, // update every second
|
||||
draw: function(d) {
|
||||
// work out how to display the current time
|
||||
var h = d.getHours(), m = d.getMinutes();
|
||||
var time = (" "+h).substr(-2)+":"+("0"+m).substr(-2);
|
||||
// draw the current time (4x size 7 segment)
|
||||
g.setFont("7x11Numeric7Seg", 4);
|
||||
g.setFontAlign(1, 1); // align right bottom
|
||||
g.drawString(time, X, Y, true /*clear background*/);
|
||||
// draw the seconds (2x size 7 segment)
|
||||
g.setFont("7x11Numeric7Seg", 2);
|
||||
g.drawString(("0"+d.getSeconds()).substr(-2), X+30, Y, true /*clear background*/);
|
||||
// draw the date, in a normal font
|
||||
g.setFont("6x8");
|
||||
g.setFontAlign(0, 1); // align center bottom
|
||||
// pad the date - this clears the background if the date were to change length
|
||||
var dateStr = " "+require("locale").date(d)+" ";
|
||||
g.drawString(dateStr, g.getWidth()/2, Y+15, true /*clear background*/);
|
||||
}
|
||||
});
|
||||
clock.start();
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
Complete Usage
|
||||
--------------
|
||||
|
||||
```js
|
||||
|
||||
var ClockFace = require("ClockFace");
|
||||
var clock = new ClockFace({
|
||||
precision: 1, // optional, defaults to 60: how often to call update(), in seconds
|
||||
init: function() { // optional
|
||||
// called only once before starting the clock, but after setting up the
|
||||
// screen/widgets, so you can use Bangle.appRect
|
||||
},
|
||||
draw: function(time, changed) { // at least draw or update is required
|
||||
// (re)draw entire clockface, time is a Date object
|
||||
// `changed` is the same format as for update() below, but always all true
|
||||
// You can use `this.is12Hour` to test if the 'Time Format' setting is set to "12h" or "24h"
|
||||
},
|
||||
// The difference between draw() and update() is that the screen is cleared
|
||||
// before draw() is called, so it needs to always redraw the entire clock
|
||||
update: function(time, changed) { // at least draw or update is required
|
||||
// redraw date/time, time is a Date object
|
||||
// if you want, you can only redraw the changed parts:
|
||||
if (changed.d) // redraw date (changed.h/m/s will also all be true)
|
||||
if (changed.h) // redraw hours
|
||||
if (changed.m) // redraw minutes
|
||||
if (changed.s) // redraw seconds
|
||||
},
|
||||
pause: function() { // optional, called when the screen turns off
|
||||
// for example: turn off GPS/compass if the watch used it
|
||||
},
|
||||
resume: function() { // optional, called when the screen turns on
|
||||
// for example: turn GPS/compass back on
|
||||
},
|
||||
up: function() { // optional, up handler
|
||||
},
|
||||
down: function() { // optional, down handler
|
||||
},
|
||||
upDown: function(dir) { // optional, combined up/down handler
|
||||
if (dir === -1) // Up
|
||||
else // (dir === 1): Down
|
||||
},
|
||||
});
|
||||
clock.start();
|
||||
|
||||
```
|
||||
|
||||
|
||||
Simple Usage
|
||||
------------
|
||||
Basic clocks can pass just a function to redraw the entire screen every minute:
|
||||
|
||||
```js
|
||||
|
||||
var ClockFace = require("ClockFace");
|
||||
var clock = new ClockFace(function(time) {
|
||||
// draw the current time at the center of the screen
|
||||
g.setFont("Vector:50").setFontAlign(0, 0)
|
||||
.drawString(
|
||||
require("locale").time(time, true),
|
||||
Bangle.appRect.w/2, Bangle.appRect.h/2
|
||||
);
|
||||
});
|
||||
clock.start();
|
||||
|
||||
```
|
||||
|
||||
Properties
|
||||
----------
|
||||
The following properties are automatically set on the clock:
|
||||
* `is12Hour`: `true` if the "Time Format" setting is set to "12h", `false` for "24h".
|
||||
* `paused`: `true` while the clock is paused. (You don't need to check this inside your `draw()` code)
|
||||
|
||||
Inside the `draw()`/`update()` function you can access these using `this`:
|
||||
|
||||
```js
|
||||
|
||||
var ClockFace = require("ClockFace");
|
||||
var clock = new ClockFace({
|
||||
draw: function (time) {
|
||||
if (this.is12Hour) // draw 12h time
|
||||
else // use 24h format
|
||||
}
|
||||
});
|
||||
clock.start();
|
||||
|
||||
Bangle.on('step', function(steps) {
|
||||
if (clock.paused === false) // draw step count
|
||||
});
|
||||
|
||||
```
|
|
@ -0,0 +1,57 @@
|
|||
// module "time_utils"
|
||||
//
|
||||
// Utility functions useful to work with time and durations.
|
||||
// Functions usually receive or return a {h, m} object or a
|
||||
// number of milliseconds representing a time or a duration.
|
||||
//
|
||||
|
||||
/**
|
||||
* @param {object} time {h, m}
|
||||
* @returns the milliseconds contained in the passed time object
|
||||
*/
|
||||
exports.encodeTime = (time) => time.h * 3600000 + time.m * 60000;
|
||||
|
||||
/**
|
||||
* @param {int} millis the number of milliseconds
|
||||
* @returns a time object {h, m} built from the milliseconds
|
||||
*/
|
||||
exports.decodeTime = (millis) => {
|
||||
millis = Math.ceil(millis / 60000);
|
||||
var h = 0 | (millis / 60);
|
||||
return {
|
||||
h: h,
|
||||
m: millis - h * 60
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object|int} value {h,m} object or milliseconds
|
||||
* @returns an human-readable time string like "10:25"
|
||||
*/
|
||||
exports.formatTime = (value) => {
|
||||
var time = (value.h === undefined || value.m === undefined) ? exports.decodeTime(value) : value;
|
||||
return time.h + ":" + ("0" + time.m).substr(-2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object|int} value {h,m} object or milliseconds
|
||||
* @returns an human-readable duration string like "1h 10m"
|
||||
*/
|
||||
exports.formatDuration = (value) => {
|
||||
var duration;
|
||||
|
||||
var time = (value.h === undefined || value.m === undefined) ? exports.decodeTime(value) : value;
|
||||
|
||||
if (time.h == 0) {
|
||||
duration = time.m + "m"
|
||||
} else {
|
||||
duration = time.h + "h" + (time.m ? (" " + time.m + "m") : "")
|
||||
}
|
||||
|
||||
return duration
|
||||
}
|
||||
|
||||
exports.getCurrentTimeMillis = () => {
|
||||
var time = new Date();
|
||||
return (time.getHours() * 3600 + time.getMinutes() * 60 + time.getSeconds()) * 1000;
|
||||
}
|