Merge branch 'espruino:master' into StoragwAnalyser
|
|
@ -5,7 +5,7 @@
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots": [{"url":"screenshot.png"}],
|
"screenshots": [{"url":"screenshot.png"}],
|
||||||
"type": "clkinfo",
|
"type": "clkinfo",
|
||||||
"tags": "clkinfo",
|
"tags": "clkinfo,clock",
|
||||||
"supports" : ["BANGLEJS2"],
|
"supports" : ["BANGLEJS2"],
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"clkinfoclk.clkinfo.js","url":"clkinfo.js"}
|
{"name":"clkinfoclk.clkinfo.js","url":"clkinfo.js"}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
0.01: New App!
|
||||||
|
After Width: | Height: | Size: 4.9 KiB |
|
|
@ -0,0 +1,22 @@
|
||||||
|
(function() {
|
||||||
|
let strideLength = (require("Storage").readJSON("myprofile.json",1)||{}).strideLength ?? 0.79,
|
||||||
|
lastSteps = 0;
|
||||||
|
function stepUpdateHandler() { distance.emit("redraw"); }
|
||||||
|
var distance = {
|
||||||
|
name : "Distance",
|
||||||
|
get : () => { let v = (Bangle.getHealthStatus("day").steps - lastSteps)*strideLength; return {
|
||||||
|
text : require("locale").distance(v,1),
|
||||||
|
img : atob("GBiBAAMAAAeAAA/AAA/AAA/gAA/gwAfh4AfD4APD4AOH4AAH4ADj4AHjwAHhwADgAAACAAAHgAAPAAAHAAgCEBgAGD///BgAGAgAEA==")
|
||||||
|
};},
|
||||||
|
run : function() {
|
||||||
|
lastSteps = (lastSteps>=Bangle.getHealthStatus("day").steps) ? 0 : Bangle.getHealthStatus("day").steps;
|
||||||
|
this.emit("redraw");
|
||||||
|
},
|
||||||
|
show : function() { Bangle.on("step", stepUpdateHandler); stepUpdateHandler(); },
|
||||||
|
hide : function() { Bangle.removeListener("step", stepUpdateHandler); }
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
name: "Bangle",
|
||||||
|
items: [ distance ]
|
||||||
|
};
|
||||||
|
})
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
|
|
@ -0,0 +1,14 @@
|
||||||
|
{ "id": "clkinfodist",
|
||||||
|
"name": "Clockinfo Distance",
|
||||||
|
"version":"0.01",
|
||||||
|
"description": "Uses the 'My Profile' app's Stride Length to calculate distance travelled based on step count. Tap to reset for measuring distances.",
|
||||||
|
"icon": "app.png",
|
||||||
|
"screenshots": [{"url":"screenshot.png"}],
|
||||||
|
"type": "clkinfo",
|
||||||
|
"tags": "clkinfo,distance,steps,outdoors,tool",
|
||||||
|
"dependencies": {"myprofile":"app"},
|
||||||
|
"supports" : ["BANGLEJS2"],
|
||||||
|
"storage": [
|
||||||
|
{"name":"clkinfodist.clkinfo.js","url":"clkinfo.js"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 4.9 KiB |
|
|
@ -17,3 +17,5 @@
|
||||||
0.16: Add BLE clkinfo entry
|
0.16: Add BLE clkinfo entry
|
||||||
0.17: Fix BLE icon alignment and border on some clocks
|
0.17: Fix BLE icon alignment and border on some clocks
|
||||||
0.18: Tweak BLE icon to add gap and ensure middle of B isn't filled
|
0.18: Tweak BLE icon to add gap and ensure middle of B isn't filled
|
||||||
|
0.19: Fix Altitude ClockInfo after BLE added
|
||||||
|
Tapping Altitude now updates the reading
|
||||||
|
|
@ -39,22 +39,23 @@ exports.load = function() {
|
||||||
var hrm = 0;
|
var hrm = 0;
|
||||||
var alt = "--";
|
var alt = "--";
|
||||||
// callbacks (needed for easy removal of listeners)
|
// callbacks (needed for easy removal of listeners)
|
||||||
function batteryUpdateHandler() { bangleItems[0].emit("redraw"); }
|
function batteryUpdateHandler() { bangleItems.find(i=>i.name=="Battery").emit("redraw"); }
|
||||||
function stepUpdateHandler() { bangleItems[1].emit("redraw"); }
|
function stepUpdateHandler() { bangleItems.find(i=>i.name=="Steps").emit("redraw"); }
|
||||||
function hrmUpdateHandler(e) {
|
function hrmUpdateHandler(e) {
|
||||||
if (e && e.confidence>60) hrm = Math.round(e.bpm);
|
if (e && e.confidence>60) hrm = Math.round(e.bpm);
|
||||||
bangleItems[2].emit("redraw");
|
bangleItems.find(i=>i.name=="HRM").emit("redraw");
|
||||||
}
|
}
|
||||||
function altUpdateHandler() {
|
function altUpdateHandler() {
|
||||||
try {
|
try {
|
||||||
Bangle.getPressure().then(data=>{
|
Bangle.getPressure().then(data=>{
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
alt = Math.round(data.altitude) + "m";
|
alt = Math.round(data.altitude) + "m";
|
||||||
bangleItems[3].emit("redraw");
|
bangleItems.find(i=>i.name=="Altitude").emit("redraw");
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print("Caught "+e+"\n in function altUpdateHandler in module clock_info");
|
print("Caught "+e+"\n in function altUpdateHandler in module clock_info");
|
||||||
bangleItems[3].emit('redraw');}
|
bangleItems.find(i=>i.name=="Altitude").emit('redraw');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// actual menu
|
// actual menu
|
||||||
var menu = [{
|
var menu = [{
|
||||||
|
|
@ -120,7 +121,6 @@ exports.load = function() {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ name: "BLE",
|
{ name: "BLE",
|
||||||
hasRange: false,
|
|
||||||
isOn: () => {
|
isOn: () => {
|
||||||
const s = NRF.getSecurityStatus();
|
const s = NRF.getSecurityStatus();
|
||||||
return s.advertising || s.connected;
|
return s.advertising || s.connected;
|
||||||
|
|
@ -156,6 +156,7 @@ exports.load = function() {
|
||||||
min : 0, max : settings.maxAltitude,
|
min : 0, max : settings.maxAltitude,
|
||||||
img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAAACAAAGAAAPAAEZgAOwwAPwQAZgYAwAMBgAGBAACDAADGAABv///////wAAAAAAAAAAAAAAAAAAAA==")
|
img : atob("GBiBAAAAAAAAAAAAAAAAAAAAAAACAAAGAAAPAAEZgAOwwAPwQAZgYAwAMBgAGBAACDAADGAABv///////wAAAAAAAAAAAAAAAAAAAA==")
|
||||||
}),
|
}),
|
||||||
|
run : function() { alt = "--"; this.emit("redraw"); altUpdateHandler(); },
|
||||||
show : function() { this.interval = setInterval(altUpdateHandler, 60000); alt = "--"; altUpdateHandler(); },
|
show : function() { this.interval = setInterval(altUpdateHandler, 60000); alt = "--"; altUpdateHandler(); },
|
||||||
hide : function() { clearInterval(this.interval); delete this.interval; },
|
hide : function() { clearInterval(this.interval); delete this.interval; },
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{ "id": "clock_info",
|
{ "id": "clock_info",
|
||||||
"name": "Clock Info Module",
|
"name": "Clock Info Module",
|
||||||
"shortName": "Clock Info",
|
"shortName": "Clock Info",
|
||||||
"version":"0.18",
|
"version":"0.19",
|
||||||
"description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)",
|
"description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
||||||
|
|
@ -38,4 +38,5 @@
|
||||||
0.33: Ensure readAllRecordsSince always includes the current day
|
0.33: Ensure readAllRecordsSince always includes the current day
|
||||||
Speed improvements (put temporary functions in RAM where possible)
|
Speed improvements (put temporary functions in RAM where possible)
|
||||||
0.34: Fix readFullDatabase (was skipping first month of data)
|
0.34: Fix readFullDatabase (was skipping first month of data)
|
||||||
0.35: Update boot/lib.min.js
|
0.35: Update boot/lib.min.js
|
||||||
|
0.36: Fix Distance graphs that used '1*' to remove the suffix
|
||||||
|
|
@ -32,7 +32,7 @@ function menuStepCount() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function menuDistance() {
|
function menuDistance() {
|
||||||
const distMult = 1*require("locale").distance(myprofile.strideLength, 2); // hackish: this removes the distance suffix, e.g. 'm'
|
const distMult = parseFloat(require("locale").distance(myprofile.strideLength, 2)); // this removes the distance suffix, e.g. 'm'
|
||||||
E.showMenu({
|
E.showMenu({
|
||||||
"": { title:/*LANG*/"Distance" },
|
"": { title:/*LANG*/"Distance" },
|
||||||
/*LANG*/"< Back": () => menuStepCount(),
|
/*LANG*/"< Back": () => menuStepCount(),
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "health",
|
"id": "health",
|
||||||
"name": "Health Tracking",
|
"name": "Health Tracking",
|
||||||
"shortName": "Health",
|
"shortName": "Health",
|
||||||
"version": "0.35",
|
"version": "0.36",
|
||||||
"description": "Logs health data and provides an app to view it",
|
"description": "Logs health data and provides an app to view it",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"screenshots" : [ { "url":"screenshot.png" } ],
|
"screenshots" : [ { "url":"screenshot.png" } ],
|
||||||
|
|
|
||||||
|
|
@ -117,3 +117,4 @@
|
||||||
Remove workaround for 2v10 (>3 years ago) - assume everyone is on never firmware now
|
Remove workaround for 2v10 (>3 years ago) - assume everyone is on never firmware now
|
||||||
0.86: Default to showing message scroller (with title, bigger icon)
|
0.86: Default to showing message scroller (with title, bigger icon)
|
||||||
0.87: Make choosing of font size more repeatable
|
0.87: Make choosing of font size more repeatable
|
||||||
|
0.88: Adjust padding calculation so messages are spaced out properly even when using international fonts
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
// a message
|
// a message
|
||||||
require("messages").pushMessage({"t":"add","id":1575479849,"src":"WhatsApp","title":"My Friend","body":"Hey! How's everything going?",reply:1,negative:1})
|
require("messages").pushMessage({"t":"add","id":1575479849,"src":"WhatsApp","title":"My Friend","body":"Hey! How's everything going?",reply:1,negative:1})
|
||||||
require("messages").pushMessage({"t":"add","id":1575479849,"src":"Skype","title":"My Friend","body":"Hey! How's everything going? This is a really really long message that is really so super long you'll have to scroll it lots and lots",positive:1,negative:1})
|
require("messages").pushMessage({"t":"add","id":1575479850,"src":"Skype","title":"My Friend","body":"Hey! How's everything going? This is a really really long message that is really so super long you'll have to scroll it lots and lots",positive:1,negative:1})
|
||||||
require("messages").pushMessage({"t":"add","id":23232,"src":"Skype","title":"Mr. Bobby McBobFace","body":"Boopedy-boop",positive:1,negative:1})
|
require("messages").pushMessage({"t":"add","id":23232,"src":"Skype","title":"Mr. Bobby McBobFace","body":"Boopedy-boop",positive:1,negative:1})
|
||||||
require("messages").pushMessage({"t":"add","id":23233,"src":"Skype","title":"Thyttan test","body":"Nummerplåtsbelysning trodo",positive:1,negative:1})
|
require("messages").pushMessage({"t":"add","id":23233,"src":"Skype","title":"Thyttan test","body":"Nummerplåtsbelysning trodo",positive:1,negative:1})
|
||||||
require("messages").pushMessage({"t":"add","id":23234,"src":"Skype","title":"Thyttan test 2","body":"Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo",positive:1,negative:1})
|
require("messages").pushMessage({"t":"add","id":23234,"src":"Skype","title":"Thyttan test 2","body":"Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo Nummerplåtsbelysning trodo",positive:1,negative:1})
|
||||||
|
|
@ -391,8 +391,6 @@ function showMessage(msgid, persist) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lines = g.setFont(bodyFont).wrapString(body, w);
|
lines = g.setFont(bodyFont).wrapString(body, w);
|
||||||
if (lines.length<3)
|
|
||||||
lines.unshift(""); // if less lines, pad them out a bit at the top!
|
|
||||||
}
|
}
|
||||||
let negHandler,posHandler,rowLeftDraw,rowRightDraw;
|
let negHandler,posHandler,rowLeftDraw,rowRightDraw;
|
||||||
if (msg.negative) {
|
if (msg.negative) {
|
||||||
|
|
@ -432,12 +430,14 @@ function showMessage(msgid, persist) {
|
||||||
}
|
}
|
||||||
let fontHeight = g.setFont(bodyFont).getFontHeight();
|
let fontHeight = g.setFont(bodyFont).getFontHeight();
|
||||||
let lineHeight = (fontHeight>25)?fontHeight:25;
|
let lineHeight = (fontHeight>25)?fontHeight:25;
|
||||||
if (title.includes("\n")) lineHeight=25; // ensure enough room for 2 lines of title in header
|
if (title.includes("\n") && lineHeight<25) lineHeight=25; // ensure enough room for 2 lines of title in header
|
||||||
let linesPerRow = 2;
|
let linesPerRow = 2;
|
||||||
if (fontHeight<17) {
|
if (fontHeight<17) {
|
||||||
lineHeight = 16;
|
lineHeight = 16;
|
||||||
linesPerRow = 3;
|
linesPerRow = 3;
|
||||||
}
|
}
|
||||||
|
if ((lines.length+4.5)*lineHeight < Bangle.appRect.h)
|
||||||
|
lines.unshift(""); // if less lines, pad them out a bit at the top!
|
||||||
let rowHeight = lineHeight*linesPerRow;
|
let rowHeight = lineHeight*linesPerRow;
|
||||||
let textLineOffset = -(linesPerRow + ((rowLeftDraw||rowRightDraw)?1:0));
|
let textLineOffset = -(linesPerRow + ((rowLeftDraw||rowRightDraw)?1:0));
|
||||||
let msgIcon = require("messageicons").getImage(msg);
|
let msgIcon = require("messageicons").getImage(msg);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"id": "messagegui",
|
"id": "messagegui",
|
||||||
"name": "Message UI",
|
"name": "Message UI",
|
||||||
"shortName": "Messages",
|
"shortName": "Messages",
|
||||||
"version": "0.87",
|
"version": "0.88",
|
||||||
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
|
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
|
|
|
||||||
|
|
@ -88,3 +88,4 @@ of 'Select Clock'
|
||||||
0.77: Save altitude calibration when user exits via reset
|
0.77: Save altitude calibration when user exits via reset
|
||||||
0.78: Fix menu scroll restore on BangleJS1
|
0.78: Fix menu scroll restore on BangleJS1
|
||||||
0.79: Ensure that tapping on pressure/altitude doesn't cause a menu to display temporarily
|
0.79: Ensure that tapping on pressure/altitude doesn't cause a menu to display temporarily
|
||||||
|
0.80: Add option to set LCD brightness to 0, default brightness is now 0 as well.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "setting",
|
"id": "setting",
|
||||||
"name": "Settings",
|
"name": "Settings",
|
||||||
"version": "0.79",
|
"version": "0.80",
|
||||||
"description": "A menu for setting up Bangle.js",
|
"description": "A menu for setting up Bangle.js",
|
||||||
"icon": "settings.png",
|
"icon": "settings.png",
|
||||||
"tags": "tool,system",
|
"tags": "tool,system",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
Bangle.loadWidgets();
|
Bangle.loadWidgets();
|
||||||
Bangle.drawWidgets();
|
Bangle.drawWidgets();
|
||||||
|
|
||||||
|
|
@ -474,11 +475,11 @@ function LCDMenu() {
|
||||||
Object.assign(lcdMenu, {
|
Object.assign(lcdMenu, {
|
||||||
/*LANG*/'LCD Brightness': {
|
/*LANG*/'LCD Brightness': {
|
||||||
value: settings.brightness,
|
value: settings.brightness,
|
||||||
min: 0.1,
|
min : BANGLEJS2 ? 0 : 0.1,
|
||||||
max: 1,
|
max: 1,
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
onchange: v => {
|
onchange: v => {
|
||||||
settings.brightness = v || 1;
|
settings.brightness = v ?? 1;
|
||||||
updateSettings();
|
updateSettings();
|
||||||
Bangle.setLCDBrightness(settings.brightness);
|
Bangle.setLCDBrightness(settings.brightness);
|
||||||
}
|
}
|
||||||
|
|
@ -1077,4 +1078,4 @@ function showAltitude() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Show the main menu
|
// Show the main menu
|
||||||
pushMenu(mainMenu());
|
pushMenu(mainMenu());
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@
|
||||||
0.05: Make the "App source not found" warning less buggy
|
0.05: Make the "App source not found" warning less buggy
|
||||||
0.06: Fixed a crash if an app has no tags (app.tags is undefined)
|
0.06: Fixed a crash if an app has no tags (app.tags is undefined)
|
||||||
0.07: Clear cached app list when updating showClocks setting
|
0.07: Clear cached app list when updating showClocks setting
|
||||||
|
0.08: Add haptic feedback option when selecting app or category in menu, increase vector size limit in settings
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ Settings
|
||||||
|
|
||||||
- `Font` - The font used (`4x6`, `6x8`, `12x20`, `6x15` or `Vector`). Default `12x20`.
|
- `Font` - The font used (`4x6`, `6x8`, `12x20`, `6x15` or `Vector`). Default `12x20`.
|
||||||
- `Vector Font Size` - The size of the font if `Font` is set to `Vector`. Default `10`.
|
- `Vector Font Size` - The size of the font if `Font` is set to `Vector`. Default `10`.
|
||||||
|
- `Haptic Feedback` - Whether or not to vibrate slightly when selecting an app or category in the launcher. Default `No`.
|
||||||
- `Show Clocks` - If set to `No` then clocks won't appear in the app list. Default `Yes`.
|
- `Show Clocks` - If set to `No` then clocks won't appear in the app list. Default `Yes`.
|
||||||
- `Fullscreen` - If set to `Yes` then widgets won't be loaded. Default `No`.
|
- `Fullscreen` - If set to `Yes` then widgets won't be loaded. Default `No`.
|
||||||
|
|
||||||
|
|
@ -28,3 +29,4 @@ Contributors
|
||||||
|
|
||||||
- [atjn](https://github.com/atjn)
|
- [atjn](https://github.com/atjn)
|
||||||
- [BlueFox4](https://github.com/BlueFox4)
|
- [BlueFox4](https://github.com/BlueFox4)
|
||||||
|
- [RKBoss6](https://github.com/RKBoss6)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@ let vectorval = 20;
|
||||||
let font = g.getFonts().includes("12x20") ? "12x20" : "6x8:2";
|
let font = g.getFonts().includes("12x20") ? "12x20" : "6x8:2";
|
||||||
let settings = Object.assign({
|
let settings = Object.assign({
|
||||||
showClocks: true,
|
showClocks: true,
|
||||||
fullscreen: false
|
fullscreen: false,
|
||||||
|
buzz:false
|
||||||
}, s.readJSON("taglaunch.json", true) || {});
|
}, s.readJSON("taglaunch.json", true) || {});
|
||||||
if ("vectorsize" in settings)
|
if ("vectorsize" in settings)
|
||||||
vectorval = parseInt(settings.vectorsize);
|
vectorval = parseInt(settings.vectorsize);
|
||||||
|
|
@ -108,15 +109,25 @@ let showTagMenu = (tag) => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
select : i => {
|
select : i => {
|
||||||
let app = appsByTag[tag][i];
|
const loadApp = () => {
|
||||||
if (!app) return;
|
let app = appsByTag[tag][i];
|
||||||
if (!app.src || require("Storage").read(app.src)===undefined) {
|
if (!app) return;
|
||||||
Bangle.setUI();
|
if (!app.src || require("Storage").read(app.src)===undefined) {
|
||||||
E.showMessage(/*LANG*/"App Source\nNot found");
|
Bangle.setUI();
|
||||||
setTimeout(showMainMenu, 2000);
|
E.showMessage(/*LANG*/"App Source\nNot found");
|
||||||
} else {
|
setTimeout(showMainMenu, 2000);
|
||||||
load(app.src);
|
} else {
|
||||||
|
load(app.src);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if(settings.buzz){
|
||||||
|
Bangle.buzz(25);
|
||||||
|
//let the buzz have effect
|
||||||
|
setTimeout(loadApp,27);
|
||||||
|
}else{
|
||||||
|
loadApp();
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
back : showMainMenu,
|
back : showMainMenu,
|
||||||
remove: unload
|
remove: unload
|
||||||
|
|
@ -138,6 +149,7 @@ let showMainMenu = () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
select : i => {
|
select : i => {
|
||||||
|
if(settings.buzz)Bangle.buzz(25);
|
||||||
let tag = tagKeys[i];
|
let tag = tagKeys[i];
|
||||||
showTagMenu(tag);
|
showTagMenu(tag);
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
"id": "taglaunch",
|
"id": "taglaunch",
|
||||||
"name": "Tag Launcher",
|
"name": "Tag Launcher",
|
||||||
"shortName": "Taglauncher",
|
"shortName": "Taglauncher",
|
||||||
"version": "0.07",
|
"version": "0.08",
|
||||||
"description": "Launcher that puts all applications into submenus based on their tag. With many applications installed this can result in a faster application selection than the linear access of the default launcher.",
|
"description": "Launcher that puts all applications into submenus based on their tag. With many applications installed this can result in a faster application selection than the linear access from the default launcher.",
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
"icon": "app.png",
|
"icon": "app.png",
|
||||||
"type": "launch",
|
"type": "launch",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
(function(back) {
|
(function(back) {
|
||||||
let settings = Object.assign({
|
let settings = Object.assign({
|
||||||
showClocks: true,
|
showClocks: true,
|
||||||
fullscreen: false
|
fullscreen: false,
|
||||||
|
buzz:false
|
||||||
}, require("Storage").readJSON("taglaunch.json", true) || {});
|
}, require("Storage").readJSON("taglaunch.json", true) || {});
|
||||||
|
|
||||||
let fonts = g.getFonts();
|
let fonts = g.getFonts();
|
||||||
|
|
@ -21,9 +22,16 @@
|
||||||
},
|
},
|
||||||
/*LANG*/"Vector Font Size": {
|
/*LANG*/"Vector Font Size": {
|
||||||
value: settings.vectorsize || 10,
|
value: settings.vectorsize || 10,
|
||||||
min:10, max: 20,step:1,wrap:true,
|
min:10, max: 25,step:1,wrap:true,
|
||||||
onchange: (m) => {save("vectorsize", m)}
|
onchange: (m) => {save("vectorsize", m)}
|
||||||
},
|
},
|
||||||
|
/*LANG*/"Haptic Feedback": {
|
||||||
|
value: settings.buzz == true,
|
||||||
|
onchange: (m) => {
|
||||||
|
save("buzz", m);
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
/*LANG*/"Show Clocks": {
|
/*LANG*/"Show Clocks": {
|
||||||
value: settings.showClocks == true,
|
value: settings.showClocks == true,
|
||||||
onchange: (m) => {
|
onchange: (m) => {
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,4 @@
|
||||||
0.04: Get time zone from settings for showing the clock
|
0.04: Get time zone from settings for showing the clock
|
||||||
0.05: Minor code improvements
|
0.05: Minor code improvements
|
||||||
0.06: Adjust format of title, save counter before leaving help screen
|
0.06: Adjust format of title, save counter before leaving help screen
|
||||||
|
0.07: Refactor code, fix stuttering timer, add settings menu
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
A simple timer. You can easily set up the time. The initial time is 2:30
|
A simple timer. You can easily set up the time. The initial time is 2:30
|
||||||
|
|
||||||
On the first screen, you can
|
On the first screen, you can
|
||||||
- tap to get help
|
- double tap to get help
|
||||||
- swipe up/down to change the timer by +/- one minute
|
- swipe up/down to change the timer by +/- one minute
|
||||||
- swipe left/right to change the time by +/- 15 seconds
|
- swipe left/right to change the time by +/- 15 seconds
|
||||||
- press Btn1 to start
|
- press Btn1 to start
|
||||||
|
|
@ -12,24 +12,31 @@ Press Btn1 again to stop the timer
|
||||||
- when time is up, your Bangle will buzz for 15 seconds
|
- when time is up, your Bangle will buzz for 15 seconds
|
||||||
- and it will count up to 60 seconds and stop after that
|
- and it will count up to 60 seconds and stop after that
|
||||||
|
|
||||||
## Images
|
The time changes can be adjusted in the settings menu.
|
||||||
_1. Startscreen_
|
|
||||||
|
|
||||||

|
## Images
|
||||||
|
_1. Start screen_
|
||||||
|
|
||||||
|

|
||||||
Current time is displayed below the Title. Initial time is 2:30.
|
Current time is displayed below the Title. Initial time is 2:30.
|
||||||
|
|
||||||
_2. Help Screen_
|
_2. Help Screen_
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
_3. Tea Timer running_
|
_3. Tea Timer running_
|
||||||
|
|
||||||

|

|
||||||
Remainig time is shown in big font size. Above the initial time is shown.
|
Remainig time is shown in big font size.
|
||||||
|
|
||||||
_4. When time is up_
|
_4. Pause Timer
|
||||||
|
|
||||||

|

|
||||||
|
While the timer is running, you can pause and unpause it by pressing BTN1.
|
||||||
|
|
||||||
|
_5. When time is up_
|
||||||
|
|
||||||
|

|
||||||
When time is up, the watch will buzz for 15 seconds. It will count up to 60 seconds.
|
When time is up, the watch will buzz for 15 seconds. It will count up to 60 seconds.
|
||||||
|
|
||||||
## Requests
|
## Requests
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
|
@ -1,237 +1,217 @@
|
||||||
// Tea Timer
|
const FILE = "teatimer.json";
|
||||||
// Button press stops timer, next press restarts timer
|
const DEFAULTS = {
|
||||||
let drag;
|
timerDuration: 150,
|
||||||
var counter = 0;
|
bigJump: 60,
|
||||||
var counterStart = 150; // 150 seconds
|
smallJump: 15,
|
||||||
var counterInterval;
|
finishBuzzDuration: 1500,
|
||||||
const states = {
|
overtimeBuzzDuration: 100,
|
||||||
init: 1, // unused
|
overtimeBuzzLimit: 60,
|
||||||
help: 2, // show help text
|
overtimeBuzzSeconds: 15
|
||||||
start: 4, // show/change initial counter
|
|
||||||
count: 8, // count down
|
|
||||||
countUp: 16, // count up after timer finished
|
|
||||||
stop: 32 // timer stopped
|
|
||||||
};
|
};
|
||||||
var state = states.start;
|
|
||||||
let setting = require("Storage").readJSON("setting.json",1);
|
|
||||||
E.setTimeZone(setting.timezone);
|
|
||||||
|
|
||||||
// Title showing current time
|
// Enum for states
|
||||||
function appTitle() {
|
const STATES = {
|
||||||
return "Tea Timer\n" + currentTime();
|
INIT: "init",
|
||||||
|
RUNNING: "running",
|
||||||
|
PAUSED: "paused",
|
||||||
|
FINISHED: "finished",
|
||||||
|
OVERTIME: "overtime"
|
||||||
|
};
|
||||||
|
|
||||||
|
let savedSettings = require("Storage").readJSON(FILE, 1) || {};
|
||||||
|
let settings = Object.assign({}, DEFAULTS, savedSettings);
|
||||||
|
|
||||||
|
let state = STATES.INIT;
|
||||||
|
let showHelp = false;
|
||||||
|
|
||||||
|
let startTime = 0;
|
||||||
|
let remaining = settings.timerDuration;
|
||||||
|
let target = 0;
|
||||||
|
|
||||||
|
let drag = null;
|
||||||
|
let dragAdjusted = false;
|
||||||
|
let lastTapTime = 0;
|
||||||
|
|
||||||
|
// === Helpers ===
|
||||||
|
function formatTime(s) {
|
||||||
|
let m = Math.floor(s / 60);
|
||||||
|
let sec = (s % 60).toString().padStart(2, '0');
|
||||||
|
return `${m}:${sec}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function currentTime() {
|
function getTimeStr() {
|
||||||
let min = Date().getMinutes();
|
let d = new Date();
|
||||||
if (min < 10) min = "0" + min;
|
return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
|
||||||
return Date().getHours() + ":" + min;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function timeFormated(sec) {
|
function isState(s) {
|
||||||
let min = Math.floor(sec / 60);
|
return state === s;
|
||||||
sec = sec % 60;
|
|
||||||
if (sec < 10) sec = "0" + sec;
|
|
||||||
return min + ":" + sec;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// initialize timer and show timer value => state: start
|
function setState(s) {
|
||||||
function initTimer() {
|
state = s;
|
||||||
counter = counterStart;
|
|
||||||
setState(states.start);
|
|
||||||
showCounter(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// timer value (counter) can be changed in state start
|
// === UI Drawing ===
|
||||||
function changeCounter(diff) {
|
function drawUI() {
|
||||||
if (state == states.start) {
|
g.reset();
|
||||||
if (counter + diff > 0) {
|
g.setBgColor(g.theme.bg).clear();
|
||||||
counter = counter + diff;
|
g.setColor(g.theme.fg);
|
||||||
showCounter(true);
|
let cx = g.getWidth() / 2;
|
||||||
}
|
|
||||||
|
// Time (top right)
|
||||||
|
g.setFont("6x8", 2);
|
||||||
|
g.setFontAlign(1, 0);
|
||||||
|
g.drawString(getTimeStr(), g.getWidth() - 4, 10);
|
||||||
|
|
||||||
|
// Help text
|
||||||
|
if (showHelp) {
|
||||||
|
g.setFontAlign(0, 0);
|
||||||
|
g.setFont("Vector", 15);
|
||||||
|
g.drawString(
|
||||||
|
`Swipe up/down: ±${settings.bigJump}s\nSwipe left/right: ±${settings.smallJump}s\n\nBTN1: Start/Pause\nDouble Tap: Hide Help`,
|
||||||
|
cx, 80
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title
|
||||||
|
g.setFont("Vector", 20);
|
||||||
|
g.setFontAlign(0, 0);
|
||||||
|
let label = (isState(STATES.OVERTIME)) ? "Time's Up!" : "Tea Timer";
|
||||||
|
g.drawString(label, cx, 40);
|
||||||
|
|
||||||
|
// Time remaining / overtime
|
||||||
|
g.setFont("Vector", 60);
|
||||||
|
g.setColor(isState(STATES.OVERTIME) ? "#f00" : g.theme.fg);
|
||||||
|
g.drawString(formatTime(remaining), cx, 100);
|
||||||
|
|
||||||
|
// Bottom state text
|
||||||
|
g.setFontAlign(0, 0);
|
||||||
|
if (isState(STATES.PAUSED)) {
|
||||||
|
g.setFont("6x8", 2);
|
||||||
|
g.drawString("paused", cx, g.getHeight() - 20);
|
||||||
|
} else if (!isState(STATES.RUNNING) && !isState(STATES.OVERTIME)) {
|
||||||
|
g.setFont("Vector", 13);
|
||||||
|
g.drawString("double tap for help", cx, g.getHeight() - 20);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// start or restart timer => state: count
|
// === Timer Logic ===
|
||||||
function startTimer() {
|
function startTimer() {
|
||||||
counterStart = counter;
|
setState(STATES.RUNNING);
|
||||||
setState(states.count);
|
startTime = Date.now();
|
||||||
countDown();
|
target = startTime + remaining * 1000;
|
||||||
if (!counterInterval)
|
|
||||||
counterInterval = setInterval(countDown, 1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* show current counter value at start and while count down
|
function pauseTimer() {
|
||||||
Show
|
if (isState(STATES.RUNNING)) {
|
||||||
- Title with current time
|
remaining = Math.max(0, Math.ceil((target - Date.now()) / 1000));
|
||||||
- initial timer value
|
setState(STATES.PAUSED);
|
||||||
- remaining time
|
|
||||||
- hint for help in state start
|
|
||||||
*/
|
|
||||||
function showCounter(withHint) {
|
|
||||||
g.reset(); // workaround for E.showMessage bg color in 2v14 and earlier
|
|
||||||
E.showMessage("", appTitle());
|
|
||||||
g.reset().setFontAlign(0,0); // center font
|
|
||||||
// draw the current counter value
|
|
||||||
g.setBgColor(-1).setColor(0,0,1); // blue
|
|
||||||
g.setFont("Vector",20); // vector font, 20px
|
|
||||||
g.drawString("Timer: " + timeFormated(counterStart),80,55);
|
|
||||||
g.setFont("Vector",60); // vector font, 60px
|
|
||||||
g.drawString(timeFormated(counter),83,100);
|
|
||||||
if (withHint) {
|
|
||||||
g.setFont("Vector",20); // vector font, 80px
|
|
||||||
g.drawString("Tap for help",80,150);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// count down and update every second
|
function resumeTimer() {
|
||||||
// when time is up, start counting up
|
if (isState(STATES.PAUSED)) {
|
||||||
function countDown() {
|
startTime = Date.now();
|
||||||
counter--;
|
target = startTime + remaining * 1000;
|
||||||
// Out of time
|
setState(STATES.RUNNING);
|
||||||
if (counter<=0) {
|
|
||||||
outOfTime();
|
|
||||||
countUp();
|
|
||||||
counterInterval = setInterval(countUp, 1000);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
showCounter(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
function outOfTime() {
|
|
||||||
E.showMessage("Time is up!",appTitle());
|
|
||||||
setState(states.countUp);
|
|
||||||
resetTimer();
|
|
||||||
Bangle.buzz();
|
|
||||||
Bangle.buzz();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this counts up (one minute), after time is up
|
|
||||||
Show
|
|
||||||
- Title with current time
|
|
||||||
- initial timer value
|
|
||||||
- "Time is up!"
|
|
||||||
- time since timer finished
|
|
||||||
*/
|
|
||||||
function countUp() {
|
|
||||||
// buzz for 15 seconds
|
|
||||||
counter++;
|
|
||||||
if (counter <=15) {
|
|
||||||
Bangle.buzz();
|
|
||||||
}
|
|
||||||
// stop counting up after 60 seconds
|
|
||||||
if (counter > 60) {
|
|
||||||
outOfTime();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
g.reset(); // workaround for E.showMessage bg color in 2v14 and earlier
|
|
||||||
E.showMessage("", appTitle());
|
|
||||||
g.reset().setFontAlign(0,0); // center font
|
|
||||||
g.setBgColor(-1).setColor(0,0,1); // blue
|
|
||||||
g.setFont("Vector",20); // vector font, 20px
|
|
||||||
g.drawString("Timer: " + timeFormated(counterStart),80,55);
|
|
||||||
g.setFont("Vector",30); // vector font, 80px
|
|
||||||
g.setBgColor(-1).setColor(1,0,0); // red
|
|
||||||
g.drawString("Time is up!",85,85);
|
|
||||||
g.setFont("Vector",40); // vector font, 80px
|
|
||||||
// draw the current counter value
|
|
||||||
g.drawString(timeFormated(counter),80,130);
|
|
||||||
}
|
|
||||||
|
|
||||||
// reset when interupted by user oder 60 seconds after timer finished
|
|
||||||
function resetTimer() {
|
function resetTimer() {
|
||||||
clearInterval();
|
setState(STATES.INIT);
|
||||||
counterInterval = undefined;
|
remaining = settings.timerDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
// timer is stopped by user => state: stop
|
function tick() {
|
||||||
function stopTimer() {
|
if (isState(STATES.RUNNING)) {
|
||||||
resetTimer();
|
remaining -= 1;
|
||||||
E.showMessage("Timer stopped!", appTitle());
|
if (remaining <= 0) {
|
||||||
setState(states.stop);
|
remaining = 0;
|
||||||
}
|
setState(STATES.OVERTIME);
|
||||||
|
startTime = Date.now();
|
||||||
// timer is stopped by user while counting up => state: start
|
remaining = 0; // Start overtime count-up from 0
|
||||||
function stopTimer2() {
|
Bangle.buzz(settings.finishBuzzDuration);
|
||||||
resetTimer();
|
}
|
||||||
initTimer();
|
} else if (isState(STATES.OVERTIME)) {
|
||||||
}
|
remaining += 1;
|
||||||
|
if (remaining <= settings.overtimeBuzzSeconds) {
|
||||||
|
Bangle.buzz(settings.overtimeBuzzDuration, 0.3);
|
||||||
function setState(st) {
|
}
|
||||||
state = st;
|
if (remaining >= settings.overtimeBuzzLimit) {
|
||||||
}
|
resetTimer(); // Stop overtime after max duration
|
||||||
|
|
||||||
function buttonPressed() {
|
|
||||||
switch(state) {
|
|
||||||
case states.init:
|
|
||||||
initTimer();
|
|
||||||
break;
|
|
||||||
case states.help:
|
|
||||||
initTimer();
|
|
||||||
break;
|
|
||||||
case states.start:
|
|
||||||
startTimer();
|
|
||||||
break;
|
|
||||||
case states.count:
|
|
||||||
stopTimer();
|
|
||||||
break;
|
|
||||||
case states.countUp:
|
|
||||||
stopTimer2();
|
|
||||||
break;
|
|
||||||
case states.stop:
|
|
||||||
initTimer();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
initTimer();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Change initial counter value by swiping
|
|
||||||
swipe up: +1 minute
|
|
||||||
swipe down: -1 minute
|
|
||||||
swipe right: +15 seconds
|
|
||||||
swipe left: -15 seconds */
|
|
||||||
function initDragEvents() {
|
|
||||||
Bangle.on("drag", e => {
|
|
||||||
if (state == states.start) {
|
|
||||||
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 (Math.abs(dx)>Math.abs(dy)+10) {
|
|
||||||
// horizontal
|
|
||||||
changeCounter(dx>0 ? 15 : -15);
|
|
||||||
} else if (Math.abs(dy)>Math.abs(dx)+10) {
|
|
||||||
// vertical
|
|
||||||
changeCounter(dy>0 ? -60 : 60);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
drawUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
// show help text while in start state (see initDragEvents())
|
// === UI Controls ===
|
||||||
function showHelp() {
|
function toggleTimer() {
|
||||||
if (state == states.start) {
|
if (showHelp) {
|
||||||
state = states.help;
|
showHelp = false;
|
||||||
g.setBgColor(g.theme.bg);
|
} else if (isState(STATES.OVERTIME)) {
|
||||||
g.setColor(g.theme.fg);
|
resetTimer();
|
||||||
E.showMessage("Swipe up/down\n+/- one minute\n\nSwipe left/right\n+/- 15 seconds\n\nPress Btn1 to start","Tea timer help");
|
} else if (isState(STATES.INIT)) {
|
||||||
|
startTimer();
|
||||||
|
} else if (isState(STATES.PAUSED)) {
|
||||||
|
resumeTimer();
|
||||||
|
} else if (isState(STATES.RUNNING)) {
|
||||||
|
pauseTimer();
|
||||||
}
|
}
|
||||||
// return to start
|
|
||||||
else if (state == states.help) {
|
drawUI();
|
||||||
counterStart = counter;
|
}
|
||||||
initTimer();
|
|
||||||
|
function handleDoubleTap() {
|
||||||
|
if (isState(STATES.INIT)) {
|
||||||
|
let now = Date.now();
|
||||||
|
if (now - lastTapTime < 400) {
|
||||||
|
showHelp = !showHelp;
|
||||||
|
drawUI();
|
||||||
|
}
|
||||||
|
lastTapTime = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// drag events in start state (to change counter value)
|
function adjustTimer(diff) {
|
||||||
initDragEvents();
|
if (isState(STATES.INIT)) {
|
||||||
// Show help test in start state
|
remaining = Math.max(5, remaining + diff);
|
||||||
Bangle.on('touch', function(button, xy) { showHelp(); });
|
settings.timerDuration = remaining;
|
||||||
// event handling for button1
|
drawUI();
|
||||||
setWatch(buttonPressed, BTN1, {repeat: true});
|
}
|
||||||
initTimer();
|
}
|
||||||
|
|
||||||
|
function handleDrag(e) {
|
||||||
|
if (isState(STATES.INIT) && !showHelp) {
|
||||||
|
if (e.b) {
|
||||||
|
if (!drag) {
|
||||||
|
drag = { x: e.x, y: e.y };
|
||||||
|
dragAdjusted = false;
|
||||||
|
} else if (!dragAdjusted) {
|
||||||
|
let dx = e.x - drag.x;
|
||||||
|
let dy = e.y - drag.y;
|
||||||
|
|
||||||
|
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > settings.smallJump) {
|
||||||
|
adjustTimer(dx > 0 ? settings.smallJump : -settings.smallJump);
|
||||||
|
dragAdjusted = true;
|
||||||
|
} else if (Math.abs(dy) > Math.abs(dx) && Math.abs(dy) > settings.bigJump) {
|
||||||
|
adjustTimer(dy > 0 ? -settings.bigJump : settings.bigJump);
|
||||||
|
dragAdjusted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
drag = null;
|
||||||
|
dragAdjusted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Init App ===
|
||||||
|
setWatch(toggleTimer, BTN1, { repeat: true });
|
||||||
|
Bangle.on("drag", handleDrag);
|
||||||
|
Bangle.on("touch", handleDoubleTap);
|
||||||
|
|
||||||
|
resetTimer();
|
||||||
|
drawUI();
|
||||||
|
setInterval(tick, 1000);
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,26 @@
|
||||||
{
|
{
|
||||||
"id": "teatimer",
|
"id": "teatimer",
|
||||||
"name": "Tea Timer",
|
"name": "Tea Timer",
|
||||||
"version": "0.06",
|
"version": "0.07",
|
||||||
"description": "A simple timer. You can easily set up the time.",
|
"description": "A simple timer. You can easily set up the time.",
|
||||||
"icon": "teatimer.png",
|
"icon": "teatimer.png",
|
||||||
"type": "app",
|
"type": "app",
|
||||||
"tags": "tool",
|
"tags": "tool",
|
||||||
"supports": ["BANGLEJS2"],
|
"supports": ["BANGLEJS2"],
|
||||||
"readme": "README.md",
|
"readme": "README.md",
|
||||||
|
"data": [
|
||||||
|
{ "name": "teatimer.json" }
|
||||||
|
],
|
||||||
"storage": [
|
"storage": [
|
||||||
{"name":"teatimer.app.js","url":"app.js"},
|
{"name":"teatimer.app.js","url":"app.js"},
|
||||||
|
{"name": "teatimer.settings.js", "url": "settings.js" },
|
||||||
{"name":"teatimer.img","url":"app-icon.js","evaluate":true}
|
{"name":"teatimer.img","url":"app-icon.js","evaluate":true}
|
||||||
],
|
],
|
||||||
"screenshots": [
|
"screenshots": [
|
||||||
{"url":"TeatimerStart.jpg"},
|
{"url":"TeatimerStart.png"},
|
||||||
{"url":"TeatimerHelp.jpg"},
|
{"url":"TeatimerHelp.png"},
|
||||||
{"url":"TeatimerRun.jpg"},
|
{"url":"TeatimerRun.png"},
|
||||||
{"url":"TeatimerUp.jpg"}
|
{"url":"TeatimerPause.png"},
|
||||||
|
{"url":"TeatimerUp.png"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
(function(back) {
|
||||||
|
const FILE = "teatimer.json";
|
||||||
|
const DEFAULTS = {
|
||||||
|
timerDuration: 150, // Initial timer duration in seconds
|
||||||
|
bigJump: 60, // Jump for vertical swipes
|
||||||
|
smallJump: 15 // Jump for horizontal swipes
|
||||||
|
};
|
||||||
|
|
||||||
|
let settings = require("Storage").readJSON(FILE, 1) || DEFAULTS;
|
||||||
|
|
||||||
|
function saveSettings() {
|
||||||
|
require("Storage").writeJSON(FILE, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
function showSettingsMenu() {
|
||||||
|
E.showMenu({
|
||||||
|
'': { title: 'Tea Timer Settings' },
|
||||||
|
'< Back': back,
|
||||||
|
'Default Duration (sec)': {
|
||||||
|
value: settings.timerDuration,
|
||||||
|
min: 5, max: 900, step: 5,
|
||||||
|
onchange: v => {
|
||||||
|
settings.timerDuration = v;
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Swipe Up/Down (sec)': {
|
||||||
|
value: settings.bigJump,
|
||||||
|
min: 5, max: 300, step: 5,
|
||||||
|
onchange: v => {
|
||||||
|
settings.bigJump = v;
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'Swipe Left/Right (sec)': {
|
||||||
|
value: settings.smallJump,
|
||||||
|
min: 5, max: 60, step: 5,
|
||||||
|
onchange: v => {
|
||||||
|
settings.smallJump = v;
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
showSettingsMenu();
|
||||||
|
})
|
||||||