Merge branch 'master' of github.com:espruino/BangleApps

master
Gordon Williams 2022-08-11 14:01:56 +01:00
commit 74430167c3
18 changed files with 200 additions and 38 deletions

View File

@ -2,3 +2,4 @@
0.02: Added scrollable calendar and swipe gestures
0.03: Configurable drag gestures
0.04: Use default Bangle formatter for booleans
0.05: Improved colors (connected vs disconnected)

View File

@ -123,7 +123,7 @@ function drawMinutes() {
var d = new Date();
var hours = s.MODE24 ? d.getHours().toString().padStart(2, ' ') : ((d.getHours() + 24) % 12 || 12).toString().padStart(2, ' ');
var minutes = d.getMinutes().toString().padStart(2, '0');
var textColor = NRF.getSecurityStatus().connected ? '#fff' : '#f00';
var textColor = NRF.getSecurityStatus().connected ? '#99f' : '#fff';
var size = 50;
var clock_x = (w - 20) / 2;
if (dimSeconds) {

View File

@ -1,7 +1,7 @@
{
"id": "clockcal",
"name": "Clock & Calendar",
"version": "0.04",
"version": "0.05",
"description": "Clock with Calendar",
"readme":"README.md",
"icon": "app.png",

View File

@ -4,4 +4,5 @@
0.04: Increase screen update rate when charging
0.05: Deleting Background - making Font larger
0.06: Fixing refresh issues
0.07: Fixed position after unlocking
0.07: Fixed position after unlocking
0.08: Handling exceptions

View File

@ -3,7 +3,7 @@
"name": "A Battery Widget (with percentage) - Hanks Mod",
"shortName":"H Battery Widget",
"icon": "widget.png",
"version":"0.07",
"version":"0.08",
"type": "widget",
"supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md",

View File

@ -29,9 +29,9 @@
var y = this.y;
if ((typeof x === 'undefined') || (typeof y === 'undefined')) {
} else {
const l = E.getBattery();
const l = E.getBattery(); // debug: Math.floor(Math.random() * 101);
let xl = x+4+l*(s-12)/100;
if (l != old_l){ // Delete the old value from screen
if ((l != old_l) && (typeof old_l != 'undefined') ){ // Delete the old value from screen
let xl_old = x+4+old_l*(s-12)/100;
g.setColor(COLORS.white);
// g.fillRect(x+2,y+5,x+s-6,y+18);
@ -41,9 +41,9 @@
//g.fillRect(old_x,old_y,old_x+4+l*(s-12)/100,old_y+16+3); // clear (lazy)
g.drawString(old_l, old_x + 14, old_y + 10);
g.fillRect(x+4,y+14+3,xl_old,y+16+3); // charging bar
old_l = l;
}
old_l = l;
//console.log(old_x);
g.setColor(levelColor(l));
@ -64,7 +64,7 @@
}
Bangle.on('charging',function(charging) { draw(); });
var id = setInterval(()=>WIDGETS["wid_a_battery_widget"].draw(), intervalLow);
var id = setInterval(()=>WIDGETS["hwid_a_battery_widget"].draw(), intervalLow);
WIDGETS["wid_a_battery_widget"]={area:"tr",width:30,draw:draw};
WIDGETS["hwid_a_battery_widget"]={area:"tr",width:30,draw:draw};
})();

View File

@ -1 +1,2 @@
0.01: first release
0.02: added interface for configuration from app loader

View File

@ -12,11 +12,10 @@ when the time for the last slide is approaching,
the button becomes red, when it passed,
the time will go on for another half a minute and stop automatically.
The only way to upload personalized timings is
by uploading a CSV to the bangle (i.e. from the IDE),
in the future I'll possibly figure out a better way.
You can set personalized timings from the web interface
by uploading a CSV to the bangle (floppy disk button in the app loader).
Each line in the file (which must be called `presentation_timer.csv`)
Each line in the file (`presentation_timer.csv`)
contains the time in minutes at which the slide
is supposed to finish and the slide number,
separated by a semicolon.
@ -25,8 +24,7 @@ is lasting until 1 minutes 30 seconds (yes it's decimal),
after another slide will start.
The only requirement is that timings are increasing,
so slides number don't have to be consecutive,
some can be skipped and they can even be short texts
(be careful with that, I didn't test it).
some can be skipped and they can even be short texts.
At the moment the app is just quick and dirty
but it should do its job.

View File

@ -0,0 +1,136 @@
<html>
<head>
<link rel="stylesheet" href="../../css/spectre.min.css" />
<style type="text/css">
.alert {
padding: 20px;
background-color: #f44336; /* Red */
color: white;
margin-bottom: 15px;
}
</style>
</head>
<body>
<div id="info"></div>
<button id="btnReload" class="btn btn-primary">Reload from watch</button>
<button id="btnUpload" class="btn btn-primary">Upload to watch</button>
<button id="btnDownload" class="btn btn-primary">Download</button>
<pre id="presentation_timer" contenteditable></pre>
<script src="../../core/lib/interface.js"></script>
<script>
const filePresentationTimer = "presentation_timer.csv";
const defaultValue="1.5;1\n2;2\n2.5;3\n3;4\n"
function errorFormat() {
var date = new Date();
var error =
'<p class="alert">' +
date.toUTCString() +
" : Wrong format, it should be CSV, refer to the README" +
"</p>";
return error;
}
function getEditableContent() {
//transform any tag in an EOL (should be <br> or <div>), ignore ending tags
return document.getElementById("presentation_timer")
.innerHTML.replace(/<[^>]*>/g,"\n").replace(/\n\n+/g, '\n').replace(/^\n|\n$/g, '');
}
const re = new RegExp("^([0-9]*[.])?[0-9]+;[A-z0-9]+$");
function isCorrectCsvString(str) {
let wronglines = str.split("\n").filter(e=>e && !e.match(re));
//TODO check for increasing numbers
if(wronglines.length) {
console.log(wronglines.join("\n"));
return false;
}
return true;
}
function uploadFile(fileid, contents) {
Puck.write(
`\x10(function() {
require("Storage").write("${fileid}",\`${contents}\`);
Bluetooth.print("OK");
})()\n`,
(ret) => {
console.log("uploadFile", ret);
}
);
}
/* Load settings CSV file from the watch.
*/
function loadTimes() {
document.getElementById("info").innerHTML = "";
Util.showModal("Loading...");
Puck.eval(`require('Storage').read("${filePresentationTimer}")`, (data) => {
document.getElementById("presentation_timer").innerHTML = data;
Util.hideModal();
if(!data) {
document.getElementById("presentation_timer").innerHTML = defaultValue;
}
});
}
/* Save settings as a CSV file on the watch.
*/
function uploadTimes() {
document.getElementById("info").innerHTML = "";
Util.showModal("Uploading...");
let csvTimes = getEditableContent();
if (isCorrectCsvString(csvTimes)) {
uploadFile(filePresentationTimer, csvTimes);
} else {
document.getElementById("info").innerHTML = errorFormat();
}
Util.hideModal();
}
function downloadTimes() {
document.getElementById("info").innerHTML = "";
Util.showModal("Downloading...");
let csvTimes = getEditableContent();
if (isCorrectCsvString(csvTimes)) {
var a = document.createElement("a"),
file = new Blob([csvTimes], { type: "text/csv" });
var url = URL.createObjectURL(file);
a.href = url;
a.download = filePresentationTimer;
document.body.appendChild(a);
a.click();
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
} else {
document.getElementById("info").innerHTML = errorFormat();
}
Util.hideModal();
}
document
.getElementById("btnUpload")
.addEventListener("click", function () {
uploadTimes();
});
document
.getElementById("btnDownload")
.addEventListener("click", function () {
downloadTimes();
});
document
.getElementById("btnReload")
.addEventListener("click", function () {
loadTimes();
});
function onInit() {
loadTimes();
}
</script>
</body>
</html>

View File

@ -1,15 +1,17 @@
{
"id": "presentation_timer",
"name": "Presentation Timer",
"version": "0.01",
"version": "0.02",
"description": "A touch based presentation timer for Bangle JS 2",
"icon": "presentation_timer.png",
"screenshots": [{"url":"screenshot1.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"},{"url":"screenshot4.png"}],
"tags": "tools,app",
"supports": ["BANGLEJS2"],
"readme": "README.md",
"interface": "interface.html",
"storage": [
{"name":"presentation_timer.app.js","url":"presentation_timer.app.js"},
{"name":"presentation_timer.img","url":"presentation_timer.icon.js","evaluate":true}
]
],
"data": [{ "name": "presentation_timer.csv" }]
}

View File

@ -19,10 +19,10 @@ const margin = 0.5; //half a minute tolerance
//dummy default values
var slides = [
[0.3, 1],
[0.5, 2],
[0.7, 3],
[1,4]
[1.5, 1],
[2, 2],
[2.5, 3],
[3,4]
];
function log_debug(o) {
@ -267,6 +267,6 @@ g.fillRect(0,0,w,h);
Bangle.loadWidgets();
Bangle.drawWidgets();
readSlides();
draw();
setWatch(() => load(), BTN, { repeat: false, edge: "falling" });
readSlides();

View File

@ -1,7 +1,7 @@
exports.load = (num) => {
return require("Storage").readJSON(`waypoints${num?`.${num}`:""}.json`)||[{name:"NONE"}];
return require("Storage").readJSON(`waypoints${num?"."+num:""}.json`)||[{name:"NONE"}];
};
exports.save = (waypoints,num) => {
require("Storage").writeJSON(`waypoints${num?`.${num}`:""}.json`, waypoints);
require("Storage").writeJSON(`waypoints${num?"."+num:""}.json`, waypoints);
};

View File

@ -5,3 +5,4 @@
0.05: Don't poll for GPS status, override setGPSPower handler (fix #1456)
0.06: Periodically update so the always on display does show current GPS fix
0.07: Alternative marker icon (configurable via settings)
0.08: Add ability to hide the icon when GPS is off, for a cleaner appearance.

View File

@ -14,6 +14,7 @@ There are two icons which can be used to visualize the GPS/GNSS status:
- Shows in green when the GPS is on and has a fix
2. Different place markers depending on GPS/GNSS status
You can also choose to hide the icon when the GPS is off in the settings.
Written by: [Hugh Barney](https://github.com/hughbarney) For support
and discussion please post in the [Bangle JS
@ -21,4 +22,6 @@ Forum](http://forum.espruino.com/microcosms/1424/)
Extended by Marco ([myxor](https://github.com/myxor))
Extended by khromov ([khromov](https://github.com/khromov))
Place marker icons from [icons8.com](https://icons8.com/icon/set/maps/material-outlined).

View File

@ -1 +1 @@
{"crossIcon": "true"}
{"crossIcon": true, "hideWhenGpsOff": false}

View File

@ -1,7 +1,7 @@
{
"id": "widgps",
"name": "GPS Widget",
"version": "0.07",
"version": "0.08",
"description": "Tiny widget to show the power and fix status of the GPS/GNSS",
"icon": "widget.png",
"type": "widget",

View File

@ -20,9 +20,13 @@ var mainmenu = {
'' : {'title' : 'GPS widget'},
'< Back' : back,
"Cross icon" : {
value : !!settings.crossIcon ,
value : settings.crossIcon ,
onchange : v => { writeSettings("crossIcon", v); }
},
"Hide icon when GPS off" : {
value : settings.hideWhenGpsOff ,
onchange : v => { writeSettings("hideWhenGpsOff", v); }
},
};
E.showMenu(mainmenu);
});
});

View File

@ -1,6 +1,9 @@
(function() {
let settings =
require("Storage").readJSON("widgps.json", 1) || {crossIcon : true};
let settings = Object.assign(
require('Storage').readJSON("widgps.default.json", true) || {},
require('Storage').readJSON("widgps.json", true) || {}
);
var interval;
@ -35,13 +38,23 @@ WIDGETS.gps = {
} else {
g.setColor("#FD0"); // on but no fix = amber
}
} else {
g.setColor("#888"); // off = grey
}
g.drawImage(
g.drawImage(
atob(
"GBiBAAAAAAAAAAAAAA//8B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+A//8AAAAAAAAAAAAA=="),
this.x, 2 + this.y);
} else {
if(!settings.hideWhenGpsOff) {
g.setColor("#888"); // off = grey
g.drawImage(
atob(
"GBiBAAAAAAAAAAAAAA//8B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+B//+BgYGBgYGBgYGBgYGBgYGBgYGB//+A//8AAAAAAAAAAAAA=="),
this.x, 2 + this.y);
}
}
} else { // marker icons
if (Bangle.isGPSOn()) {
const gpsObject = Bangle.getGPSFix();
@ -59,9 +72,11 @@ WIDGETS.gps = {
}
} else {
// GNSS off
g.drawImage(
atob("GBiBAAAAAAAAAAB+ABj/ABxDgA4AwAcAwAeMYAfEYAbgYAZwYAM4wAMcQAOOAAGHAAHDgADDwABm4AB+cAA8OAAYGAAAAAAAAAAAAA=="),
this.x, 2 + this.y);
if(!settings.hideWhenGpsOff) {
g.drawImage(
atob("GBiBAAAAAAAAAAB+ABj/ABxDgA4AwAcAwAeMYAfEYAbgYAZwYAM4wAMcQAOOAAGHAAHDgADDwABm4AB+cAA8OAAYGAAAAAAAAAAAAA=="),
this.x, 2 + this.y);
}
}
}
}