+
+
+
+
diff --git a/apps/espruinoprog/metadata.json b/apps/espruinoprog/metadata.json
new file mode 100644
index 000000000..ebb55b23d
--- /dev/null
+++ b/apps/espruinoprog/metadata.json
@@ -0,0 +1,17 @@
+{
+ "id": "espruinoprog",
+ "name": "Espruino Programmer",
+ "shortName": "Programmer",
+ "version": "0.02",
+ "description": "Finds Bluetooth devices with a specific name (eg 'Puck.js'), connects and uploads code. Great for programming many devices at once!",
+ "icon": "app.png",
+ "tags": "tool,bluetooth",
+ "supports": ["BANGLEJS","BANGLEJS2"],
+ "readme": "README.md",
+ "custom": "custom.html",
+ "storage": [
+ {"name":"espruinoprog.app.js","url":"app.js"},
+ {"name":"espruinoprog.img","url":"app-icon.js","evaluate":true},
+ {"name":"espruinoprog.json"}
+ ]
+}
diff --git a/apps/nato/changelog.txt b/apps/espruinoterm/ChangeLog
similarity index 100%
rename from apps/nato/changelog.txt
rename to apps/espruinoterm/ChangeLog
diff --git a/apps/espruinoterm/README.md b/apps/espruinoterm/README.md
new file mode 100644
index 000000000..df26d59a0
--- /dev/null
+++ b/apps/espruinoterm/README.md
@@ -0,0 +1,22 @@
+# Espruino Terminal
+
+Send commands to other Espruino devices via the Bluetooth UART interface and
+see the result on a terminal.
+
+## Customising
+
+Once installed and you're connected to the Bangle you can click the button next to the app in the app loader
+to change the commands (they will be read from the device).
+
+When done, click `Save to Bangle.js` and your changes will be saved to the same device.
+
+## Usage
+
+* Load the app and after a few seconds you'll see a menu with Espruino devices
+in the vicinity.
+* Tap on the device you want to connect to
+* A terminal will pop up showing `Connecting...` and then `Connected`
+* Now tap on the right (or press the button) to bring up a menu with options for commands, or the option to disconnect.
+
+You can also choose `Custom` in which case a keyboard (using the currently installed text input method) will
+be displayed and you can enter the command you would like to send.
diff --git a/apps/espruinoterm/app-icon.js b/apps/espruinoterm/app-icon.js
new file mode 100644
index 000000000..f566aedf7
--- /dev/null
+++ b/apps/espruinoterm/app-icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwwcCpMkyQC/AVW//4AK/oR/COD8LCP4R/CK8DCKNsCKFt2BHPhu2CJ8BCKAjQI4OQNaIUB23bsCPMCJzp/CP4Rf/4AKCKwC/AVIA=="))
diff --git a/apps/espruinoterm/app.js b/apps/espruinoterm/app.js
new file mode 100644
index 000000000..348190db4
--- /dev/null
+++ b/apps/espruinoterm/app.js
@@ -0,0 +1,101 @@
+var uart; // require("ble_uart")
+var device; // BluetoothDevice
+var customCommand = "";
+// Set up terminal
+Bangle.loadWidgets();
+var R = Bangle.appRect;
+var termg = Graphics.createArrayBuffer(R.w, R.h, 1, {msb:true});
+var termVisible = false;
+termg.setFont("6x8");
+term = require("VT100").connect(termg, {
+ charWidth : 6,
+ charHeight : 8
+});
+term.print = str => {
+ for (var i of str) term.char(i);
+ if (termVisible) g.reset().drawImage(termg,R.x,R.y).setFont("6x8").setFontAlign(0,-1,1).drawString("MORE",R.w-1,(R.y+R.y2)/2);
+};
+
+function showConnectMenu() {
+ termVisible = false;
+ var m = { "" : {title:"Devices"} };
+ E.showMessage("Scanning...");
+ NRF.findDevices(devices => {
+ devices.forEach(dev=>{
+ m[dev.name||dev.id.substr(0,17)] = ()=>{
+ connectTo(dev);
+ };
+ });
+ m["< Back"] = () => showConnectMenu();
+ E.showMenu(m);
+ },{filters:[
+ { namePrefix: 'Puck.js' },
+ { namePrefix: 'Pixl.js' },
+ { namePrefix: 'MDBT42Q' },
+ { namePrefix: 'Bangle.js' },
+ { namePrefix: 'Espruino' },
+ { services: [ "6e400001-b5a3-f393-e0a9-e50e24dcca9e" ] }
+ ],active:true,timeout:4000});
+}
+
+function showOptionsMenu() {
+ if (!uart) showConnectMenu();
+ termVisible = false;
+ var menu = {"":{title:/*LANG*/"Options"},
+ "< Back" : () => showTerminal(),
+ };
+ var json = require("Storage").readJSON("espruinoterm.json",1);
+ if (Array.isArray(json)) {
+ json.forEach(j => { menu[j.title] = () => sendCommand(j.cmd); });
+ } else {
+ Object.assign(menu,{
+ "Version" : () => sendCommand("process.env.VERSION"),
+ "Battery" : () => sendCommand("E.getBattery()"),
+ "Flash LED" : () => sendCommand("LED.set();setTimeout(()=>LED.reset(),1000);")
+ });
+ }
+ menu[/*LANG*/"Custom"] = () => { require("textinput").input({text:customCommand}).then(result => {
+ customCommand = result;
+ sendCommand(customCommand);
+ })};
+ menu[/*LANG*/"Disconnect"] = () => { showTerminal(); term.print("\r\nDisconnecting...\r\n"); uart.disconnect(); }
+
+ E.showMenu(menu);
+}
+
+function showTerminal() {
+ E.showMenu();
+ Bangle.setUI({
+ mode : "custom",
+ btn : n=> { showOptionsMenu(); },
+ touch : (n,e) => { if (n==2) showOptionsMenu(); }
+ });
+ termVisible = true;
+ term.print(""); // redraw terminal
+}
+
+function sendCommand(cmd) {
+ showTerminal();
+ uart.write(cmd+"\n");
+}
+
+function connectTo(dev) {
+ device = dev;
+ showTerminal();
+ term.print(`\r\nConnect to ${dev.name||dev.id.substr(0,17)}...\r\n`);
+ device.on('gattserverdisconnected', function(reason) {
+ term.print(`\r\nDISCONNECTED (${reason})\r\n`);
+ uart = undefined;
+ device = undefined;
+ setTimeout(showConnectMenu, 1000);
+ });
+ require("ble_uart").connect(device).then(function(u) {
+ uart = u;
+ term.print("Connected...\r\n");
+ uart.on('data', function(d) { term.print(d); });
+ });
+}
+
+// now start
+Bangle.drawWidgets();
+showConnectMenu();
diff --git a/apps/espruinoterm/app.json b/apps/espruinoterm/app.json
new file mode 100644
index 000000000..72a12e635
--- /dev/null
+++ b/apps/espruinoterm/app.json
@@ -0,0 +1,5 @@
+[
+ {"title":"Version", "cmd":"process.env.VERSION"},
+ {"title":"Battery", "cmd":"E.getBattery()"},
+ {"title":"Flash LED", "cmd":"LED.set();setTimeout(()=>LED.reset(),1000);"}
+]
diff --git a/apps/espruinoterm/app.png b/apps/espruinoterm/app.png
new file mode 100644
index 000000000..e9a8c3758
Binary files /dev/null and b/apps/espruinoterm/app.png differ
diff --git a/apps/espruinoterm/interface.html b/apps/espruinoterm/interface.html
new file mode 100644
index 000000000..660b3a86c
--- /dev/null
+++ b/apps/espruinoterm/interface.html
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
Enter the menu items you'd like to see appear in the app below. When finished, click `Save to Bangle.js` to save the JavaScript back.
+
+
+
+
+
Title
+
Command
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/espruinoterm/metadata.json b/apps/espruinoterm/metadata.json
new file mode 100644
index 000000000..25e6183e1
--- /dev/null
+++ b/apps/espruinoterm/metadata.json
@@ -0,0 +1,20 @@
+{
+ "id": "espruinoterm",
+ "name": "Espruino Terminal",
+ "shortName": "Espruino Term",
+ "version": "0.01",
+ "description": "Send commands to other Espruino devices via the Bluetooth UART interface, and see the result on a VT100 terminal. Customisable commands!",
+ "icon": "app.png",
+ "screenshots": [{"url":"screenshot.png"}],
+ "tags": "tool,bluetooth",
+ "supports": ["BANGLEJS","BANGLEJS2"],
+ "readme": "README.md",
+ "interface": "interface.html",
+ "dependencies": {"textinput":"type"},
+ "storage": [
+ {"name":"espruinoterm.app.js","url":"app.js"},
+ {"name":"espruinoterm.img","url":"app-icon.js","evaluate":true}
+ ],"data": [
+ {"name":"espruinoterm.json","url":"app.json"}
+ ]
+}
diff --git a/apps/espruinoterm/screenshot.png b/apps/espruinoterm/screenshot.png
new file mode 100644
index 000000000..cce881a37
Binary files /dev/null and b/apps/espruinoterm/screenshot.png differ
diff --git a/apps/fclock/ChangeLog b/apps/fclock/ChangeLog
index 30e049f69..7e7307c59 100644
--- a/apps/fclock/ChangeLog
+++ b/apps/fclock/ChangeLog
@@ -1,2 +1,3 @@
0.01: First published version of app
0.02: Move to Bangle.setUI to launcher support
+0.03: Tell clock widgets to hide.
diff --git a/apps/fclock/fclock.app.js b/apps/fclock/fclock.app.js
index afa0c5e2d..838a5578d 100644
--- a/apps/fclock/fclock.app.js
+++ b/apps/fclock/fclock.app.js
@@ -173,6 +173,9 @@ const drawHR = function () {
}
};
+// Show launcher when button pressed
+Bangle.setUI("clock");
+
// clean app screen
g.clear();
Bangle.loadWidgets();
@@ -198,6 +201,3 @@ Bangle.on('HRM', function (d) {
// draw now
drawClock();
-
-// Show launcher when button pressed
-Bangle.setUI("clock");
diff --git a/apps/fclock/metadata.json b/apps/fclock/metadata.json
index da553e110..dffb197a2 100644
--- a/apps/fclock/metadata.json
+++ b/apps/fclock/metadata.json
@@ -2,7 +2,7 @@
"id": "fclock",
"name": "fclock",
"shortName": "F Clock",
- "version": "0.02",
+ "version": "0.03",
"description": "Simple design of a digital clock",
"icon": "app.png",
"type": "clock",
diff --git a/apps/files/files.js b/apps/files/files.js
index e7b42c101..e81e9589f 100644
--- a/apps/files/files.js
+++ b/apps/files/files.js
@@ -1,7 +1,5 @@
const store = require('Storage');
-const boolFormat = (v) => v ? "On" : "Off";
-
function showMainMenu() {
const mainmenu = {
'': {
diff --git a/apps/flappy/ChangeLog b/apps/flappy/ChangeLog
index 349cb9d07..d660f85aa 100644
--- a/apps/flappy/ChangeLog
+++ b/apps/flappy/ChangeLog
@@ -2,3 +2,4 @@
0.03: A few tweaks to improve rendering speed
0.04: Add "ram" keyword to allow 2v06 Espruino builds to cache function that needs to be fast
0.05: Don't use Bangle.setLCDMode, just use offscreen buffer (allows widgets)
+0.06: Bangle.js 2 enhancements - remove offscreen buffer and render direct
diff --git a/apps/flappy/app.js b/apps/flappy/app.js
index e9ca31fa5..70553fe97 100644
--- a/apps/flappy/app.js
+++ b/apps/flappy/app.js
@@ -1,19 +1,20 @@
-b = Graphics.createArrayBuffer(120,120,8);
-var gimg = {
- width:120,
- height:104,
- bpp:8,
- buffer:b.buffer
- };
-
+var Y;
if (process.env.HWVERSION==2) {
- b.flip = function() {
- g.drawImage(gimg,28,50);
- };
+ // we have offscreen graphics, so just go direct
+ b = g;
+ Y = 24; // widgets
} else {
+ b = Graphics.createArrayBuffer(120,120,8);
+ var gimg = {
+ width:120,
+ height:104,
+ bpp:8,
+ buffer:b.buffer
+ };
b.flip = function() {
g.drawImage(gimg,0,24,{scale:2});
};
+ Y = 0; // we offset for widgets anyway
}
var BIRDIMG = E.toArrayBuffer(atob("EQyI/v7+/v7+/gAAAAAAAP7+/v7+/v7+/gYG0tLS0gDXAP7+/v7+/v4A0tLS0tIA19fXAP7+/v4AAAAA0tLS0gDX1wDXAP7+ANfX19cA0tLSANfXANcA/v4A19fX19cA0tLSANfX1wD+/gDS19fX0gDS0tLSAAAAAAD+/gDS0tIA0tLS0gDAwMDAwAD+/gAAAM3Nzc0AwAAAAAAA/v7+/v4Azc3Nzc0AwMDAwAD+/v7+/v4AAM3Nzc0AAAAAAP7+/v7+/v7+AAAAAP7+/v7+/g=="))
@@ -30,14 +31,14 @@ function newBarrier(x) {
barriers.push({
x1 : x-7,
x2 : x+7,
- y : 20+Math.random()*38,
+ y : Y+20+Math.random()*38,
gap : 12+Math.random()*15
});
}
function gameStart() {
running = true;
- birdy = 48/2;
+ birdy = Y + 48/2;
birdvy = 0;
barriers = [];
for (var i=38;ibbot))
gameStop();
});
diff --git a/apps/flappy/metadata.json b/apps/flappy/metadata.json
index 910797066..cb50f0094 100644
--- a/apps/flappy/metadata.json
+++ b/apps/flappy/metadata.json
@@ -1,7 +1,7 @@
{
"id": "flappy",
"name": "Flappy Bird",
- "version": "0.05",
+ "version": "0.06",
"description": "A Flappy Bird game clone",
"icon": "app.png",
"screenshots": [{"url":"screenshot1_flappy.png"},{"url":"screenshot2_flappy.png"}],
diff --git a/apps/football/metadata.json b/apps/football/metadata.json
index 253026c39..43e7ac1bf 100644
--- a/apps/football/metadata.json
+++ b/apps/football/metadata.json
@@ -2,7 +2,7 @@
"id": "football",
"name": "football",
"shortName": "football",
- "version": "1.00",
+ "version": "1.01",
"type": "app",
"description": "Classic football game of the CASIO chronometer",
"icon": "app.png",
diff --git a/apps/ftclock/ChangeLog b/apps/ftclock/ChangeLog
index 83ec21ee6..c30dae69f 100644
--- a/apps/ftclock/ChangeLog
+++ b/apps/ftclock/ChangeLog
@@ -1,3 +1,4 @@
0.01: first release
0.02: RAM efficient version of `fourTwentyTz.js` (as suggested by @gfwilliams).
0.03: `mkFourTwentyTz.js` now handles new timezonedb.com CSV format
+0.04: Tell clock widgets to hide.
diff --git a/apps/ftclock/app.js b/apps/ftclock/app.js
index b12db10f1..4f2cef895 100644
--- a/apps/ftclock/app.js
+++ b/apps/ftclock/app.js
@@ -33,6 +33,8 @@ function draw() {
// Clear the screen once, at startup
g.clear();
+// Show launcher when middle button pressed
+Bangle.setUI("clock");
// Load widgets
Bangle.loadWidgets();
Bangle.drawWidgets();
@@ -47,5 +49,4 @@ Bangle.on('lcdPower',on=>{
drawTimeout = undefined;
}
});
-// Show launcher when middle button pressed
-Bangle.setUI("clock");
+
diff --git a/apps/ftclock/metadata.json b/apps/ftclock/metadata.json
index 876feb1bb..96a4f84b9 100644
--- a/apps/ftclock/metadata.json
+++ b/apps/ftclock/metadata.json
@@ -1,7 +1,7 @@
{
"id": "ftclock",
"name": "Four Twenty Clock",
- "version": "0.03",
+ "version": "0.04",
"description": "A clock that tells when and where it's going to be 4:20 next",
"icon": "app.png",
"screenshots": [{"url":"screenshot.png"}, {"url":"screenshot1.png"}],
diff --git a/apps/fwupdate/custom.html b/apps/fwupdate/custom.html
index 3f8f50b3f..8ef117933 100644
--- a/apps/fwupdate/custom.html
+++ b/apps/fwupdate/custom.html
@@ -83,6 +83,7 @@ function onInit(device) {
if (crc==3508163280 || crc==1418074094) version = "2v12";
if (crc==4056371285) version = "2v13";
if (crc==1038322422) version = "2v14";
+ if (crc==2560806221) version = "2v15";
if (!ok) {
version += `(⚠ update required)`;
}
diff --git a/apps/gallifr/ChangeLog b/apps/gallifr/ChangeLog
index 0e1f45042..32c1057b0 100644
--- a/apps/gallifr/ChangeLog
+++ b/apps/gallifr/ChangeLog
@@ -1,2 +1,3 @@
0.01: First released version
0.02: Changed setWatch to Bangle.setUI
+0.03: Tell clock widgets to hide.
diff --git a/apps/gallifr/app.js b/apps/gallifr/app.js
index d327bcdc1..8468eee48 100644
--- a/apps/gallifr/app.js
+++ b/apps/gallifr/app.js
@@ -238,10 +238,12 @@ Bangle.on('lcdPower', (on) => {
}
});
+// Show launcher when button pressed
+Bangle.setUI("clock");
+
g.clear();
startTimers();
Bangle.loadWidgets();
drawAll();
-// Show launcher when button pressed
-Bangle.setUI("clock");
+
diff --git a/apps/gallifr/metadata.json b/apps/gallifr/metadata.json
index 9ce7d7f97..96fe243ed 100644
--- a/apps/gallifr/metadata.json
+++ b/apps/gallifr/metadata.json
@@ -2,7 +2,7 @@
"id": "gallifr",
"name": "Time Traveller's Chronometer",
"shortName": "Time Travel Clock",
- "version": "0.02",
+ "version": "0.03",
"description": "A clock for time travellers. The light pie segment shows the minutes, the black circle, the hour. The dial itself reads 'time' just in case you forget.",
"icon": "gallifr.png",
"screenshots": [{"url":"screenshot_time.png"}],
diff --git a/apps/game1024/ChangeLog b/apps/game1024/ChangeLog
index 800fa6b9d..df36b6456 100644
--- a/apps/game1024/ChangeLog
+++ b/apps/game1024/ChangeLog
@@ -8,3 +8,4 @@
0.08: Bug fix at end of the game with victorious splash and glorious orchestra
0.09: Added settings menu, removed symbol selection button (*), added highscore reset
0.10: fixed clockmode in settings
+0.11: Use default Bangle formatter for booleans
diff --git a/apps/game1024/metadata.json b/apps/game1024/metadata.json
index 728b5dc0e..f3b72aad3 100644
--- a/apps/game1024/metadata.json
+++ b/apps/game1024/metadata.json
@@ -1,7 +1,7 @@
{ "id": "game1024",
"name": "1024 Game",
"shortName" : "1024 Game",
- "version": "0.10",
+ "version": "0.11",
"icon": "game1024.png",
"screenshots": [ {"url":"screenshot.png" } ],
"readme":"README.md",
diff --git a/apps/game1024/settings.js b/apps/game1024/settings.js
index 24a972600..b52e060b1 100644
--- a/apps/game1024/settings.js
+++ b/apps/game1024/settings.js
@@ -32,7 +32,7 @@
}
},
"Exit press:": {
- value: !settings.clockMode, // ! converts undefined to true
+ value: !settings.clockMode,
format: v => v?"short":"long",
onchange: v => {
settings.clockMode = v;
@@ -40,8 +40,7 @@
},
},
"Debug mode:": {
- value: !!settings.debugMode, // !! converts undefined to false
- format: v => v?"On":"Off",
+ value: !!settings.debugMode,
onchange: v => {
settings.debugMode = v;
writeSettings();
diff --git a/apps/gbmusic/ChangeLog b/apps/gbmusic/ChangeLog
index e2ee53ede..d8379b317 100644
--- a/apps/gbmusic/ChangeLog
+++ b/apps/gbmusic/ChangeLog
@@ -9,3 +9,4 @@
0.09: Move event listener from widget to boot code, stops music from showing up in messages
0.10: Simplify touch events
Remove date+time
+0.11: Use default Bangle formatter for booleans
diff --git a/apps/gbmusic/metadata.json b/apps/gbmusic/metadata.json
index 0ded80452..bbe2a158d 100644
--- a/apps/gbmusic/metadata.json
+++ b/apps/gbmusic/metadata.json
@@ -2,7 +2,7 @@
"id": "gbmusic",
"name": "Gadgetbridge Music Controls",
"shortName": "Music Controls",
- "version": "0.10",
+ "version": "0.11",
"description": "Control the music on your Gadgetbridge-connected phone",
"icon": "icon.png",
"screenshots": [{"url":"screenshot_v1_d.png"},{"url":"screenshot_v1_l.png"},
diff --git a/apps/gbmusic/settings.js b/apps/gbmusic/settings.js
index ae013fda5..6619eab1c 100644
--- a/apps/gbmusic/settings.js
+++ b/apps/gbmusic/settings.js
@@ -25,19 +25,16 @@
}
}
- const yesNo = (v) => translate(v ? "Yes" : "No");
let menu = {
"": {"title": "Music Control"},
};
menu[translate("< Back")] = back;
menu[translate("Auto start")] = {
value: !!s.autoStart,
- format: yesNo,
onchange: save("autoStart"),
};
menu[translate("Simple button")] = {
value: !!s.simpleButton,
- format: yesNo,
onchange: save("simpleButton"),
};
diff --git a/apps/gbridge/ChangeLog b/apps/gbridge/ChangeLog
index 059767ece..f707ffb94 100644
--- a/apps/gbridge/ChangeLog
+++ b/apps/gbridge/ChangeLog
@@ -27,3 +27,4 @@
0.25: workaround call notification
Fix inflated step number
0.26: Include charging status in battery updates to phone
+0.27: Use default Bangle formatter for booleans
diff --git a/apps/gbridge/metadata.json b/apps/gbridge/metadata.json
index db7119758..e6130b06b 100644
--- a/apps/gbridge/metadata.json
+++ b/apps/gbridge/metadata.json
@@ -1,7 +1,7 @@
{
"id": "gbridge",
"name": "Gadgetbridge",
- "version": "0.26",
+ "version": "0.27",
"description": "(NOT RECOMMENDED) Displays Gadgetbridge notifications from Android. Please use the 'Android Integration' Bangle.js app instead.",
"icon": "app.png",
"type": "widget",
diff --git a/apps/gbridge/settings.js b/apps/gbridge/settings.js
index f9c7cde90..cf6c84c73 100644
--- a/apps/gbridge/settings.js
+++ b/apps/gbridge/settings.js
@@ -27,13 +27,11 @@
"Connected" : { value : NRF.getSecurityStatus().connected?"Yes":"No" },
"Show Icon" : {
value: settings().showIcon,
- format: v => v?"Yes":"No",
onchange: setIcon
},
"Find Phone" : function() { E.showMenu(findPhone); },
"Record HRM" : {
value: !!settings().hrm,
- format: v => v?"Yes":"No",
onchange: v => updateSetting('hrm', v)
}
};
diff --git a/apps/geissclk/ChangeLog b/apps/geissclk/ChangeLog
index 7458fadee..cd46173f7 100644
--- a/apps/geissclk/ChangeLog
+++ b/apps/geissclk/ChangeLog
@@ -1,3 +1,4 @@
0.01: New App!
0.02: BTN2->launcher, use smaller text to allow "20:00" to fit on screen
0.03: Changed setWatch to Bangle.setUI
+0.04: Tell clock widgets to hide.
diff --git a/apps/geissclk/clock.js b/apps/geissclk/clock.js
index f14ea5f39..5401fb142 100644
--- a/apps/geissclk/clock.js
+++ b/apps/geissclk/clock.js
@@ -142,11 +142,13 @@ Bangle.on('lcdPower',function(on) {
animInterval = setInterval(iterate, 50);
}
});
-g.clear();
+
+// Show launcher when button pressed
+Bangle.setUI("clock");g.clear();
+
Bangle.loadWidgets();
Bangle.drawWidgets();
iterate();
animInterval = setInterval(iterate, 50);
-// Show launcher when button pressed
-Bangle.setUI("clock");
+
diff --git a/apps/geissclk/metadata.json b/apps/geissclk/metadata.json
index 456854dbd..68bd2a970 100644
--- a/apps/geissclk/metadata.json
+++ b/apps/geissclk/metadata.json
@@ -1,7 +1,7 @@
{
"id": "geissclk",
"name": "Geiss Clock",
- "version": "0.03",
+ "version": "0.04",
"description": "7 segment clock with animated background in the style of Ryan Geiss' music visualisation. NOTE: The first run will take ~1 minute to do some precalculation",
"icon": "clock.png",
"type": "clock",
diff --git a/apps/glbasic/ChangeLog b/apps/glbasic/ChangeLog
index 89aee01f8..d97fd44d5 100644
--- a/apps/glbasic/ChangeLog
+++ b/apps/glbasic/ChangeLog
@@ -1,2 +1,3 @@
0.20: New App!
+0.21: Tell clock widgets to hide.
diff --git a/apps/glbasic/glbasic.app.js b/apps/glbasic/glbasic.app.js
index ff7837ada..c1f82f74c 100644
--- a/apps/glbasic/glbasic.app.js
+++ b/apps/glbasic/glbasic.app.js
@@ -178,6 +178,8 @@ function draw() {
////////////////////////////////////////////////////
// Bangle.setBarometerPower(true);
+Bangle.setUI("clock");
+
Bangle.loadWidgets();
draw();
@@ -197,6 +199,5 @@ Bangle.on('lcdPower', on => {
}
});
-Bangle.setUI("clock");
Bangle.drawWidgets();
diff --git a/apps/glbasic/metadata.json b/apps/glbasic/metadata.json
index 7c79097da..6d4c562a3 100644
--- a/apps/glbasic/metadata.json
+++ b/apps/glbasic/metadata.json
@@ -2,7 +2,7 @@
"id": "glbasic",
"name": "GLBasic Clock",
"shortName": "GLBasic",
- "version": "0.20",
+ "version": "0.21",
"description": "A clock with large numbers",
"dependencies": {"widpedom":"app"},
"icon": "icon48.png",
diff --git a/apps/gpsautotime/ChangeLog b/apps/gpsautotime/ChangeLog
index 97b80ecdf..de7af4fc7 100644
--- a/apps/gpsautotime/ChangeLog
+++ b/apps/gpsautotime/ChangeLog
@@ -1,3 +1,4 @@
0.01: New App!
0.02: Set Bangle.js 2 compatible
0.03: Add setting to hide the widget
+0.04: Use default Bangle formatter for booleans
diff --git a/apps/gpsautotime/metadata.json b/apps/gpsautotime/metadata.json
index 217a27931..c852c6a3e 100644
--- a/apps/gpsautotime/metadata.json
+++ b/apps/gpsautotime/metadata.json
@@ -2,7 +2,7 @@
"id": "gpsautotime",
"name": "GPS auto time",
"shortName": "GPS auto time",
- "version": "0.03",
+ "version": "0.04",
"description": "A widget that automatically updates the Bangle.js time to the GPS time whenever there is a valid GPS fix.",
"icon": "widget.png",
"type": "widget",
diff --git a/apps/gpsautotime/settings.js b/apps/gpsautotime/settings.js
index dbdd121d1..be6e3bbec 100644
--- a/apps/gpsautotime/settings.js
+++ b/apps/gpsautotime/settings.js
@@ -13,9 +13,8 @@
E.showMenu({
"" : { "title" : "GPS auto time" },
"< Back" : () => back(),
- 'Show widget?': {
- value: !!settings.show, // !! converts undefined to false
- format: v => v?"Show":"Hide",
+ 'Show Widgets': {
+ value: !!settings.show,
onchange: v => {
settings.show = v;
writeSettings();
diff --git a/apps/gpsnav/ChangeLog b/apps/gpsnav/ChangeLog
index b4eaf5472..6f327f364 100644
--- a/apps/gpsnav/ChangeLog
+++ b/apps/gpsnav/ChangeLog
@@ -3,4 +3,5 @@
0.03: Add Waypoint Editor
0.04: Fix great circle formula
0.05: Use locale for speed and distance + fix Vector font sizes
-
+0.06: Move waypoints.json (and editor) to 'waypoints' app
+0.07: Add support for b2
\ No newline at end of file
diff --git a/apps/gpsnav/README.md b/apps/gpsnav/README.md
index af239b233..2b67799b8 100644
--- a/apps/gpsnav/README.md
+++ b/apps/gpsnav/README.md
@@ -4,9 +4,12 @@ The app is aimed at small boat navigation although it can also be used to mark t
The app displays direction of travel (course), speed, direction to waypoint (bearing) and distance to waypoint. The screen shot below is before the app has got a GPS fix.
+[Bangle.js 2] Button mappings in brackests. One additional feature:
+On swiping on the main screen you can change the displayed metrics: Right changes to nautical metrics, left to the default locale metrics.
+

-The large digits are the course and speed. The top of the display is a linear compass which displays the direction of travel when a fix is received and you are moving. The blue text is the name of the current waypoint. NONE means that there is no waypoint set and so bearing and distance will remain at 0. To select a waypoint, press BTN2 (middle) and wait for the blue text to turn white. Then use BTN1 and BTN3 to select a waypoint. The waypoint choice is fixed by pressing BTN2 again. In the screen shot below a waypoint giving the location of Stone Henge has been selected.
+The large digits are the course and speed. The top of the display is a linear compass which displays the direction of travel when a fix is received and you are moving. The blue text is the name of the current waypoint. NONE means that there is no waypoint set and so bearing and distance will remain at 0. To select a waypoint, press BTN2 (middle) [touch / BTN] and wait for the blue text to turn white. Then use BTN1 and BTN3 [swipe left/right] to select a waypoint. The waypoint choice is fixed by pressing BTN2 [touch / BTN] again. In the screen shot below a waypoint giving the location of Stone Henge has been selected.

@@ -18,7 +21,7 @@ The app lets you mark your current location as follows. There are vacant slots i

-Bearing and distance are both zero as WP1 has currently no GPS location associated with it. To mark the location, press BTN2.
+Bearing and distance are both zero as WP1 has currently no GPS location associated with it. To mark the location, press BTN2 [touch / BTN].

diff --git a/apps/gpsnav/app.js b/apps/gpsnav/app.js
index 8903e07fb..e2b6ee6f1 100644
--- a/apps/gpsnav/app.js
+++ b/apps/gpsnav/app.js
@@ -51,10 +51,10 @@ function drawCompass(course) {
//displayed heading
var heading = 0;
-function newHeading(m,h){
+function newHeading(m,h){
var s = Math.abs(m - h);
var delta = (m>h)?1:-1;
- if (s>=180){s=360-s; delta = -delta;}
+ if (s>=180){s=360-s; delta = -delta;}
if (s<2) return h;
var hd = h + delta*(1 + Math.round(s/5));
if (hd<0) hd+=360;
@@ -125,7 +125,7 @@ function drawN(){
g.setColor(0,0,0);
g.fillRect(10,230,60,239);
g.setColor(1,1,1);
- g.drawString("Sats " + satellites.toString(),10,230);
+ g.drawString("Sats " + satellites.toString(),10,230);
}
var savedfix;
@@ -193,11 +193,11 @@ var SCREENACCESS = {
},
release:function(){
this.withApp=true;
- startdraw();
+ startdraw();
setButtons();
}
-}
-
+}
+
Bangle.on('lcdPower',function(on) {
if (!SCREENACCESS.withApp) return;
if (on) {
@@ -207,7 +207,7 @@ Bangle.on('lcdPower',function(on) {
}
});
-var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
+var waypoints = require("waypoints").load();
wp=waypoints[0];
function nextwp(inc){
@@ -223,7 +223,7 @@ function doselect(){
if (selected && wpindex!=0 && waypoints[wpindex].lat===undefined && savedfix.fix) {
waypoints[wpindex] ={name:"@"+wp.name, lat:savedfix.lat, lon:savedfix.lon};
wp = waypoints[wpindex];
- require("Storage").writeJSON("waypoints.json", waypoints);
+ require("waypoints").save(waypoints);
}
selected=!selected;
drawN();
diff --git a/apps/gpsnav/app.min.js b/apps/gpsnav/app.min.js
index 7771e2b98..a01b251e0 100644
--- a/apps/gpsnav/app.min.js
+++ b/apps/gpsnav/app.min.js
@@ -6,6 +6,6 @@ function drawN(){var a=loc.speed(speed);buf.setColor(1);buf.setFont("6x8",2);buf
0,30);buf.setColor(selected?1:2);buf.drawString(wp.name,140,0);buf.setColor(1);buf.drawString(a,60,0);buf.drawString(loc.distance(dist),60,30);flip(buf,Yoff+130);g.setFont("6x8",1);g.setColor(0,0,0);g.fillRect(10,230,60,239);g.setColor(1,1,1);g.drawString("Sats "+satellites.toString(),10,230)}var savedfix;
function onGPS(a){savedfix=a;void 0!==a&&(course=isNaN(a.course)?course:Math.round(a.course),speed=isNaN(a.speed)?speed:a.speed,satellites=a.satellites);candraw&&(void 0!==a&&1==a.fix&&(dist=distance(a,wp),isNaN(dist)&&(dist=0),brg=bearing(a,wp),isNaN(brg)&&(brg=0)),drawN())}var intervalRef;function stopdraw(){candraw=!1;intervalRef&&clearInterval(intervalRef)}
function startTimers(){candraw=!0;intervalRefSec=setInterval(function(){heading=newHeading(course,heading);course!=heading&&drawCompass(heading)},200)}function drawAll(){g.setColor(1,.5,.5);g.fillPoly([120,Yoff+50,110,Yoff+70,130,Yoff+70]);g.setColor(1,1,1);drawN();drawCompass(heading)}function startdraw(){g.clear();Bangle.drawWidgets();startTimers();drawAll()}
-function setButtons(){setWatch(nextwp.bind(null,-1),BTN1,{repeat:!0,edge:"falling"});setWatch(doselect,BTN2,{repeat:!0,edge:"falling"});setWatch(nextwp.bind(null,1),BTN3,{repeat:!0,edge:"falling"})}var SCREENACCESS={withApp:!0,request:function(){this.withApp=!1;stopdraw();clearWatch()},release:function(){this.withApp=!0;startdraw();setButtons()}};Bangle.on("lcdPower",function(a){SCREENACCESS.withApp&&(a?startdraw():stopdraw())});var waypoints=require("Storage").readJSON("waypoints.json")||[{name:"NONE"}];
-wp=waypoints[0];function nextwp(a){selected&&(wpindex+=a,wpindex>=waypoints.length&&(wpindex=0),0>wpindex&&(wpindex=waypoints.length-1),wp=waypoints[wpindex],drawN())}function doselect(){selected&&0!=wpindex&&void 0===waypoints[wpindex].lat&&savedfix.fix&&(waypoints[wpindex]={name:"@"+wp.name,lat:savedfix.lat,lon:savedfix.lon},wp=waypoints[wpindex],require("Storage").writeJSON("waypoints.json",waypoints));selected=!selected;drawN()}g.clear();Bangle.setLCDBrightness(1);Bangle.loadWidgets();Bangle.drawWidgets();
+function setButtons(){setWatch(nextwp.bind(null,-1),BTN1,{repeat:!0,edge:"falling"});setWatch(doselect,BTN2,{repeat:!0,edge:"falling"});setWatch(nextwp.bind(null,1),BTN3,{repeat:!0,edge:"falling"})}var SCREENACCESS={withApp:!0,request:function(){this.withApp=!1;stopdraw();clearWatch()},release:function(){this.withApp=!0;startdraw();setButtons()}};Bangle.on("lcdPower",function(a){SCREENACCESS.withApp&&(a?startdraw():stopdraw())});var waypoints=require("waypoints").load();
+wp=waypoints[0];function nextwp(a){selected&&(wpindex+=a,wpindex>=waypoints.length&&(wpindex=0),0>wpindex&&(wpindex=waypoints.length-1),wp=waypoints[wpindex],drawN())}function doselect(){selected&&0!=wpindex&&void 0===waypoints[wpindex].lat&&savedfix.fix&&(waypoints[wpindex]={name:"@"+wp.name,lat:savedfix.lat,lon:savedfix.lon},wp=waypoints[wpindex],require("waypoints").save(waypoints));selected=!selected;drawN()}g.clear();Bangle.setLCDBrightness(1);Bangle.loadWidgets();Bangle.drawWidgets();
Bangle.setGPSPower(1);drawAll();startTimers();Bangle.on("GPS",onGPS);setButtons();
diff --git a/apps/gpsnav/app_b2.js b/apps/gpsnav/app_b2.js
new file mode 100644
index 000000000..e46be649f
--- /dev/null
+++ b/apps/gpsnav/app_b2.js
@@ -0,0 +1,268 @@
+var candraw = true;
+var brg = 0;
+var wpindex = 0;
+var locindex = 0;
+const labels = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"];
+var loc = {
+ speed: [
+ require("locale").speed,
+ (kph) => {
+ return (kph / 1.852).toFixed(1) + "kn ";
+ }
+ ],
+ distance: [
+ require("locale").distance,
+ (m) => {
+ return (m / 1.852).toFixed(3) + "nm ";
+ }
+ ]
+};
+
+
+function drawCompass(course) {
+ if (!candraw) return;
+ g.setColor(g.theme.fg);
+ g.setFont("Vector", 18);
+ var start = course - 90;
+ if (start < 0) start += 360;
+ g.fillRect(14, 67, 162, 71);
+ var xpos = 16;
+ var frag = 15 - start % 15;
+ if (frag < 15) xpos += Math.floor((frag * 4) / 5);
+ else frag = 0;
+ for (var i = frag; i <= 180 - frag; i += 15) {
+ var res = start + i;
+ if (res % 90 == 0) {
+ g.drawString(labels[Math.floor(res / 45) % 8], xpos - 6, 28);
+ g.fillRect(xpos - 2, 47, xpos + 2, 67);
+ } else if (res % 45 == 0) {
+ g.drawString(labels[Math.floor(res / 45) % 8], xpos - 9, 28);
+ g.fillRect(xpos - 2, 52, xpos + 2, 67);
+ } else if (res % 15 == 0) {
+ g.fillRect(xpos, 58, xpos + 1, 67);
+ }
+ xpos += 12;
+ }
+ if (wpindex != 0) {
+ var bpos = brg - course;
+ if (bpos > 180) bpos -= 360;
+ if (bpos < -180) bpos += 360;
+ bpos = Math.round((bpos * 4) / 5) + 88;
+ if (bpos < 16) bpos = 7;
+ if (bpos > 160) bpos = 169;
+ g.setColor(g.theme.bgH);
+ g.fillCircle(bpos, 63, 8);
+ }
+}
+
+//displayed heading
+var heading = 0;
+
+function newHeading(m, h) {
+ var s = Math.abs(m - h);
+ var delta = (m > h) ? 1 : -1;
+ if (s >= 180) {
+ s = 360 - s;
+ delta = -delta;
+ }
+ if (s < 2) return h;
+ var hd = h + delta * (1 + Math.round(s / 5));
+ if (hd < 0) hd += 360;
+ if (hd > 360) hd -= 360;
+ return hd;
+}
+
+var course = 0;
+var speed = 0;
+var satellites = 0;
+var wp;
+var dist = 0;
+
+function radians(a) {
+ return a * Math.PI / 180;
+}
+
+function degrees(a) {
+ var d = a * 180 / Math.PI;
+ return (d + 360) % 360;
+}
+
+function bearing(a, b) {
+ var delta = radians(b.lon - a.lon);
+ var alat = radians(a.lat);
+ var blat = radians(b.lat);
+ var y = Math.sin(delta) * Math.cos(blat);
+ var x = Math.cos(alat) * Math.sin(blat) -
+ Math.sin(alat) * Math.cos(blat) * Math.cos(delta);
+ return Math.round(degrees(Math.atan2(y, x)));
+}
+
+function distance(a, b) {
+ var x = radians(a.lon - b.lon) * Math.cos(radians((a.lat + b.lat) / 2));
+ var y = radians(b.lat - a.lat);
+ return Math.round(Math.sqrt(x * x + y * y) * 6371000);
+}
+
+var selected = false;
+
+function drawN() {
+ g.clearRect(0, 89, 175, 175);
+ var txt = loc.speed[locindex](speed);
+ g.setColor(g.theme.fg);
+ g.setFont("6x8", 2);
+ g.drawString("o", 68, 87);
+ g.setFont("6x8", 1);
+ g.drawString(txt.substring(txt.length - 3), 156, 119);
+ g.setFont("Vector", 36);
+ var cs = course.toString().padStart(3, "0");
+ g.drawString(cs, 2, 89);
+ g.drawString(txt.substring(0, txt.length - 3), 92, 89);
+ g.setColor(g.theme.fg);
+ g.setFont("Vector", 18);
+ var bs = brg.toString().padStart(3, "0");
+ g.setColor(g.theme.fg);
+ g.drawString("Brg:", 1, 128);
+ g.drawString("Dist:", 1, 148);
+ g.setColor(selected ? g.theme.bgH : g.theme.bg);
+ g.fillRect(90, 127, 175, 143);
+ g.setColor(selected ? g.theme.fgH : g.theme.fg);
+ g.drawString(wp.name, 92, 128);
+ g.setColor(g.theme.fg);
+ g.drawString(bs, 42, 128);
+ g.drawString(loc.distance[locindex](dist), 42, 148);
+ g.setFont("6x8", 0.5);
+ g.drawString("o", 75, 127);
+ g.setFont("6x8", 1);
+ g.setColor(satellites ? g.theme.bg : g.theme.bgH);
+ g.fillRect(0, 167, 75, 175);
+ g.setColor(satellites ? g.theme.fg : g.theme.fgH);
+ g.drawString("Sats:", 1, 168);
+ g.drawString(satellites.toString(), 42, 168);
+}
+
+var savedfix;
+
+function onGPS(fix) {
+ savedfix = fix;
+ if (fix !== undefined) {
+ course = isNaN(fix.course) ? course : Math.round(fix.course);
+ speed = isNaN(fix.speed) ? speed : fix.speed;
+ satellites = fix.satellites;
+ }
+ if (candraw) {
+ if (fix !== undefined && fix.fix == 1) {
+ dist = distance(fix, wp);
+ if (isNaN(dist)) dist = 0;
+ brg = bearing(fix, wp);
+ if (isNaN(brg)) brg = 0;
+ }
+ drawN();
+ }
+}
+
+var intervalRef;
+
+function stopdraw() {
+ candraw = false;
+ if (intervalRef) {
+ clearInterval(intervalRef);
+ }
+}
+
+function startTimers() {
+ candraw = true;
+ intervalRefSec = setInterval(function() {
+ heading = newHeading(course, heading);
+ if (course != heading) drawCompass(heading);
+ }, 200);
+}
+
+function drawAll() {
+ g.setColor(1, 0, 0);
+ g.fillPoly([88, 71, 78, 88, 98, 88]);
+ drawN();
+ drawCompass(heading);
+}
+
+function startdraw() {
+ g.clear();
+ Bangle.drawWidgets();
+ startTimers();
+ drawAll();
+}
+
+function setButtons() {
+ Bangle.setUI("leftright", btn => {
+ if (!btn) {
+ doselect();
+ } else {
+ nextwp(btn);
+ }
+ });
+}
+
+var SCREENACCESS = {
+ withApp: true,
+ request: function() {
+ this.withApp = false;
+ stopdraw();
+ clearWatch();
+ },
+ release: function() {
+ this.withApp = true;
+ startdraw();
+ setButtons();
+ }
+};
+
+Bangle.on('lcdPower', function(on) {
+ if (!SCREENACCESS.withApp) return;
+ if (on) {
+ startdraw();
+ } else {
+ stopdraw();
+ }
+});
+
+var waypoints = require("waypoints").load();
+wp = waypoints[0];
+
+function nextwp(inc) {
+ if (selected) {
+ wpindex += inc;
+ if (wpindex >= waypoints.length) wpindex = 0;
+ if (wpindex < 0) wpindex = waypoints.length - 1;
+ wp = waypoints[wpindex];
+ drawN();
+ } else {
+ locindex = inc > 0 ? 1 : 0;
+ drawN();
+ }
+}
+
+function doselect() {
+ if (selected && wpindex != 0 && waypoints[wpindex].lat === undefined && savedfix.fix) {
+ waypoints[wpindex] = {
+ name: "@" + wp.name,
+ lat: savedfix.lat,
+ lon: savedfix.lon
+ };
+ wp = waypoints[wpindex];
+ require("waypoints").save(waypoints);
+ }
+ selected = !selected;
+ print("selected = " + selected);
+ drawN();
+}
+
+g.clear();
+Bangle.setLCDBrightness(1);
+Bangle.loadWidgets();
+Bangle.drawWidgets();
+// load widgets can turn off GPS
+Bangle.setGPSPower(1);
+drawAll();
+startTimers();
+Bangle.on('GPS', onGPS);
+// Toggle selected
+setButtons();
\ No newline at end of file
diff --git a/apps/gpsnav/metadata.json b/apps/gpsnav/metadata.json
index 5c1830318..dce80112f 100644
--- a/apps/gpsnav/metadata.json
+++ b/apps/gpsnav/metadata.json
@@ -1,16 +1,17 @@
{
"id": "gpsnav",
"name": "GPS Navigation",
- "version": "0.05",
+ "version": "0.07",
"description": "Displays GPS Course and Speed, + Directions to waypoint and waypoint recording, now with waypoint editor",
+ "screenshots": [{"url":"screenshot-b2.png"}],
"icon": "icon.png",
"tags": "tool,outdoors,gps",
- "supports": ["BANGLEJS"],
+ "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md",
- "interface": "waypoints.html",
+ "dependencies" : { "waypoints":"type" },
"storage": [
- {"name":"gpsnav.app.js","url":"app.min.js"},
+ {"name":"gpsnav.app.js","url":"app.min.js","supports":["BANGLEJS"]},
+ {"name":"gpsnav.app.js","url":"app_b2.js","supports":["BANGLEJS2"]},
{"name":"gpsnav.img","url":"app-icon.js","evaluate":true}
- ],
- "data": [{"name":"waypoints.json","url":"waypoints.json"}]
+ ]
}
diff --git a/apps/gpsnav/screenshot-b2.png b/apps/gpsnav/screenshot-b2.png
new file mode 100644
index 000000000..34ad23679
Binary files /dev/null and b/apps/gpsnav/screenshot-b2.png differ
diff --git a/apps/gpsnav/waypoints.html b/apps/gpsnav/waypoints.html
deleted file mode 100644
index d02260732..000000000
--- a/apps/gpsnav/waypoints.html
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
List of waypoints
-
-
-
-
Name
-
Lat.
-
Long.
-
Actions
-
-
-
-
-
-
-
-
Add a new waypoint
-
-
-
-
-
-
-
-
-
diff --git a/apps/gpsnav/waypoints.json b/apps/gpsnav/waypoints.json
deleted file mode 100644
index 98a670c0d..000000000
--- a/apps/gpsnav/waypoints.json
+++ /dev/null
@@ -1,20 +0,0 @@
-[
- {
- "name":"NONE"
- },
- {
- "name":"No10",
- "lat":51.5032,
- "lon":-0.1269
- },
- {
- "name":"Stone",
- "lat":51.1788,
- "lon":-1.8260
- },
- { "name":"WP0" },
- { "name":"WP1" },
- { "name":"WP2" },
- { "name":"WP3" },
- { "name":"WP4" }
-]
\ No newline at end of file
diff --git a/apps/gpsrec/ChangeLog b/apps/gpsrec/ChangeLog
index f923739f0..5867140fb 100644
--- a/apps/gpsrec/ChangeLog
+++ b/apps/gpsrec/ChangeLog
@@ -30,3 +30,4 @@
0.26: Multiple bugfixes
0.27: Map drawing with light theme (fix #1023)
0.28: Show distance more accurately in conjunction with new locale app (fix #1523)
+0.29: Use default Bangle formatter for booleans
diff --git a/apps/gpsrec/app.js b/apps/gpsrec/app.js
index 4595f616d..acd5433b2 100644
--- a/apps/gpsrec/app.js
+++ b/apps/gpsrec/app.js
@@ -22,7 +22,6 @@ function showMainMenu() {
'': { 'title': 'GPS Record' },
'RECORD': {
value: !!settings.recording,
- format: v=>v?"On":"Off",
onchange: v => {
settings.recording = v;
updateSettings();
diff --git a/apps/gpsrec/metadata.json b/apps/gpsrec/metadata.json
index c870157df..192b05edf 100644
--- a/apps/gpsrec/metadata.json
+++ b/apps/gpsrec/metadata.json
@@ -1,7 +1,7 @@
{
"id": "gpsrec",
"name": "GPS Recorder",
- "version": "0.28",
+ "version": "0.29",
"description": "(NOT RECOMMENDED) - please use the more flexible 'Recorder' app instead. Application that allows you to record a GPS track. Can run in background",
"icon": "app.png",
"tags": "tool,outdoors,gps,widget",
diff --git a/apps/gpstouch/Changelog b/apps/gpstouch/ChangeLog
similarity index 100%
rename from apps/gpstouch/Changelog
rename to apps/gpstouch/ChangeLog
diff --git a/apps/ha/ChangeLog b/apps/ha/ChangeLog
new file mode 100644
index 000000000..e78b4ccd0
--- /dev/null
+++ b/apps/ha/ChangeLog
@@ -0,0 +1,2 @@
+0.01: Release
+0.02: Includeas the ha.lib.js library that can be used by other apps or clocks.
\ No newline at end of file
diff --git a/apps/ha/README.md b/apps/ha/README.md
new file mode 100644
index 000000000..654a262c8
--- /dev/null
+++ b/apps/ha/README.md
@@ -0,0 +1,90 @@
+# Home Assistant
+This app integrates your BangleJs into the HomeAssistant.
+
+
+# How to use
+Click on the left and right side of the screen to select the triggers that you
+configured. Click in the middle of the screen to send the trigger to HomeAssistant.
+
+
+
+
+# Initial Setup
+1.) First of all, make sure that HomeAssistant and the HomeAssistant Android App works.
+
+2.) Open your BangleJs Gadgetbridge App, click on the Settings icon of your BangleJs and enable "Allow Intent Access"
+
+3.) Enable sensor in HomeAssistant Andoird App/Configuration/Companion App/Manage Sensors/LastUpdate Trigger
+
+4.) At the bottom of the same screen click on "Add New Intent" and enter "com.espruino.gadgetbridge.banglejs.HA"
+
+5.) The HomeAssistant Android app must be restarted in order to listen for those actions
+ -- a "Force Stop" is necessary (through Android App settings) or restart your phone!
+
+This setup must be done only once -- now you are ready to configure your BangleJS to
+control some devices or entities in your HomeAssistant :)
+
+
+# Setup Trigger
+1.) Upload the app and all corresponding triggers through the AppStore UI. You must specify
+the display name, the trigger as well as an icon.
+The following icons are currently supported:
+- ha (default)
+- light
+- door
+- fire
+
+
+2.) Create an "automation" in the HomeAssistant WebUI for each trigger that you created on your BangleJs in order to tell HomeAssistant what you want to control. A sample configuration is shown in the image below -- I use this trigger to open the door:
+
+
+
+3.) Don't forget to select the action that should be executed at the bottom of each automation.
+
+
+# Default Trigger
+This app also implements two default trigger that can always be used:
+- APP_STARTED -- Will be sent whenever the app is started. So you could do some actions already when the app is sarted without the need of any user interaction.
+- TRIGGER -- Will be sent whenever some trigger is executed. So you could generically listen to that.
+
+
+# How to use the library (ha.lib.js) in my own app/clk
+This app inlcludes a library that can be used by other apps or clocks
+to read all configured intents or to send a trigger. Example code:
+
+```js
+// First of all impport the library
+var ha = require("ha.lib.js");
+
+// You can read all triggers that a user configured simply via
+var triggers = ha.getTriggers();
+
+// Get display name and icon of trigger
+var display = triggers[0].display;
+var icon = triggers[0].getIcon();
+
+// Trigger the first configured trigger
+ha.sendTrigger(triggers[0].trigger);
+
+// Send a custom trigger that is not configured by a user
+ha.sendTrigger("MY_CUSTOM_TRIGGER");
+```
+
+
+# FAQ
+
+## Sometimes the trigger is not executed
+While playing and testing a bit I found that it is very important that you allow the android HomeAssistant app, as well as BangleJs Gadgetbridge app to (1) run in background and (2), disable energy optimizations for both apps.
+Otherwise, Android could stop one of both apps and the trigger will never be sent to HomeAssistant...
+
+If you still have problems, you can try another trick:
+Install "MacroDroid" from the Android AppStore and start the HomeAssistant App
+each time the "com.espruino.gadgetbridge.banglejs.HA" intent is send together
+with the extra trigger: APP_STARTED. Then whenever you open the app on your BangleJs
+it is ensured that HomeAssistant is running...
+
+## Thanks to
+Icons created by Flaticon
+
+## Creator
+- [David Peer](https://github.com/peerdavid).
diff --git a/apps/ha/custom.html b/apps/ha/custom.html
new file mode 100644
index 000000000..49f5a2eb8
--- /dev/null
+++ b/apps/ha/custom.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
Upload Tigger
+
+
+
+
+
+
+
+
diff --git a/apps/ha/ha.app.js b/apps/ha/ha.app.js
new file mode 100644
index 000000000..d9199fb0e
--- /dev/null
+++ b/apps/ha/ha.app.js
@@ -0,0 +1,75 @@
+/**
+ * This app uses the ha library to send trigger to HomeAssistant.
+ */
+var ha = require("ha.lib.js");
+var W = g.getWidth(), H = g.getHeight();
+var position=0;
+var triggers = ha.getTriggers();
+
+
+function draw() {
+ g.reset().clearRect(Bangle.appRect);
+
+ var h = 22;
+ g.setFont("Vector", h);
+ var trigger = triggers[position];
+ var w = g.stringWidth(trigger.display);
+
+ g.setFontAlign(-1,-1);
+ var icon = trigger.getIcon();
+ g.setColor(g.theme.fg).drawImage(icon, 12, H/5-2);
+ g.drawString("Home", icon.width + 20, H/5);
+ g.drawString("Assistant", icon.width + 18, H/5+24);
+
+ g.setFontAlign(0,0);
+ var ypos = H/5*3+20;
+ g.drawRect(W/2-w/2-8, ypos-h/2-8, W/2+w/2+5, ypos+h/2+5);
+ g.fillRect(W/2-w/2-6, ypos-h/2-6, W/2+w/2+3, ypos+h/2+3);
+ g.setColor(g.theme.bg).drawString(trigger.display, W/2, ypos);
+}
+
+
+Bangle.on('touch', function(btn, e){
+ var left = parseInt(g.getWidth() * 0.3);
+ var right = g.getWidth() - left;
+ var isLeft = e.x < left;
+ var isRight = e.x > right;
+
+ if(isRight){
+ Bangle.buzz(40, 0.6);
+ position += 1;
+ position = position >= triggers.length ? 0 : position;
+ draw();
+ }
+
+ if(isLeft){
+ Bangle.buzz(40, 0.6);
+ position -= 1;
+ position = position < 0 ? triggers.length-1 : position;
+ draw();
+ }
+
+ if(!isRight && !isLeft){
+ ha.sendTrigger("TRIGGER");
+
+ // Now send the selected trigger
+ Bangle.buzz(80, 0.6).then(()=>{
+ ha.sendTrigger(triggers[position].trigger);
+ setTimeout(()=>{
+ Bangle.buzz(80, 0.6);
+ }, 250);
+ });
+ }
+});
+
+
+// Send intent that the we started the app.
+ha.sendTrigger("APP_STARTED");
+
+// Next load the widgets and draw the app
+Bangle.loadWidgets();
+Bangle.drawWidgets();
+
+// Draw app
+draw();
+setWatch(_=>load(), BTN1);
diff --git a/apps/ha/ha.icon.js b/apps/ha/ha.icon.js
new file mode 100644
index 000000000..9bf6af796
--- /dev/null
+++ b/apps/ha/ha.icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwwIjggOAAocH8AFDh/wAocfAok//gFDv/+Aof+vwoD/Ef3gFBgfwh4YCg/xx4FCh/z54FCj4LEn4XEv4jBGAX//k//4uBAokDAofAg/wnk8h/gLoIFE8ccnHH+Ef8+cnPn/EAAoYvB8ARBg4FB+EMmEPAoP4gkgj5BCwA+BAoXAHwIFC8EHAoZfBAoZfBAoZZDAsgAiA=="))
\ No newline at end of file
diff --git a/apps/ha/ha.lib.js b/apps/ha/ha.lib.js
new file mode 100644
index 000000000..b09cbeab2
--- /dev/null
+++ b/apps/ha/ha.lib.js
@@ -0,0 +1,80 @@
+/**
+ * This library can be used to read all triggers that a user
+ * configured and send a trigger to homeassistant.
+ */
+function _getIcon(trigger){
+ icon = trigger.icon;
+ if(icon == "light"){
+ return {
+ width : 48, height : 48, bpp : 1,
+ transparent : 0,
+ buffer : require("heatshrink").decompress(atob("AAMBwAFE4AFDgYFJjgFBnAFBjwXBvAFBh4jBuAFCAQPwAQMHAQPgEQQCBEgcf/AvDn/8Aof//5GDAoJOBh+BAoOB+EP8YFB4fwgfnAoPnGANHAoPjHYQFBHYQFd44pDg47C4/gh/DIIZNFLIplGgF//wFIgZ9BRIUHRII7Ch4FBUIUOAoKzCjwFEhgCBmDpIVooFFh4oCAA4LFC5b7BAob1BAYI="))
+ };
+ } else if(icon == "door"){
+ return {
+ width : 48, height : 48, bpp : 1,
+ transparent : 0,
+ buffer : require("heatshrink").decompress(atob("AAM4Aok/4AED///Aov4Aon8DgQGBAv4FpnIFKJv4FweAQFFAgQFB8AFDnADC"))
+ };
+ } else if (icon == "fire"){
+ return {
+ width : 48, height : 48, bpp : 1,
+ transparent : 0,
+ buffer : require("heatshrink").decompress(atob("ABsDAokBwAFE4AFE8AFE+AFE/AFJgf8Aon+AocHAokP/8QAokYAoUfAok//88ApF//4kDAo//AgMQAgIFCjgFEjwFCOYIFFHQIFDn/+AoJ/BAoIqBAoN//xCBAoI5BDIPAgP//gFB8AFChYFBgf//EJAogOBAoSgBAoMHAQIFEFgXAAoJEBv4FCNoQFGVYd/wAFEYYIFIvwCBDoV8UwQCBcgUPwDwDfQMBaIYADA"))
+ };
+ }
+
+ // Default is always the HA icon
+ return {
+ width : 48, height : 48, bpp : 1,
+ transparent : 0,
+ buffer : require("heatshrink").decompress(atob("AD8BwAFDg/gAocP+AFDj4FEn/8Aod//wFD/1+FAf4j+8AoMD+EPDAUH+OPAoUP+fPAoUfBYk/C4l/EYIwC//8n//FwIFEgYFD4EH+E8nkP8BdBAonjjk44/wj/nzk58/4gAFDF4PgCIMHAoPwhkwh4FB/EEkEfIIWAHwIFC4A+BAoXgg4FDL4IFDL4IFDLIYFkAEQA=="))
+ };
+}
+
+exports.getTriggers = function(){
+ var triggers = [
+ {display: "Empty", trigger: "NOP", icon: "ha"},
+ ];
+
+ try{
+ triggers = require("Storage").read("ha.trigger.json");
+ triggers = JSON.parse(triggers);
+
+ // We lazy load all icons, otherwise, we have to keep
+ // all the icons n times in memory which can be
+ // problematic for embedded devices. Therefore,
+ // we lazy load icons only if needed using the getIcon
+ // method of each trigger...
+ triggers.forEach(trigger => {
+ trigger.getIcon = function(){
+ return _getIcon(trigger);
+ }
+ })
+ } catch(e) {
+ // In case there are no user triggers yet, we show the default...
+ }
+
+ return triggers;
+}
+
+exports.sendTrigger = function(triggerName){
+ var retries=3;
+
+ while(retries > 0){
+ try{
+ // Now lets send the trigger that we sould send.
+ Bluetooth.println(JSON.stringify({
+ t:"intent",
+ action:"com.espruino.gadgetbridge.banglejs.HA",
+ extra:{
+ trigger: triggerName
+ }})
+ );
+ retries = -1;
+
+ } catch(e){
+ retries--;
+ }
+ }
+}
\ No newline at end of file
diff --git a/apps/ha/ha.png b/apps/ha/ha.png
new file mode 100644
index 000000000..8fce958e4
Binary files /dev/null and b/apps/ha/ha.png differ
diff --git a/apps/ha/ha_automation.png b/apps/ha/ha_automation.png
new file mode 100644
index 000000000..9372cfa15
Binary files /dev/null and b/apps/ha/ha_automation.png differ
diff --git a/apps/ha/metadata.json b/apps/ha/metadata.json
new file mode 100644
index 000000000..63308b933
--- /dev/null
+++ b/apps/ha/metadata.json
@@ -0,0 +1,25 @@
+{
+ "id": "ha",
+ "name": "HomeAssistant",
+ "version": "0.02",
+ "description": "Integrates your BangleJS into HomeAssistant.",
+ "icon": "ha.png",
+ "type": "app",
+ "tags": "tool",
+ "readme": "README.md",
+ "supports": ["BANGLEJS2"],
+ "custom": "custom.html",
+ "screenshots": [
+ {"url":"screenshot.png"},
+ {"url":"screenshot_2.png"},
+ {"url":"screenshot_3.png"}
+ ],
+ "data": [
+ {"name":"ha.trigger.json" }
+ ],
+ "storage": [
+ {"name":"ha.app.js","url":"ha.app.js"},
+ {"name":"ha.lib.js","url":"ha.lib.js"},
+ {"name":"ha.img","url":"ha.icon.js","evaluate":true}
+ ]
+}
diff --git a/apps/ha/screenshot.png b/apps/ha/screenshot.png
new file mode 100644
index 000000000..dc059e2de
Binary files /dev/null and b/apps/ha/screenshot.png differ
diff --git a/apps/ha/screenshot_2.png b/apps/ha/screenshot_2.png
new file mode 100644
index 000000000..55019c3b1
Binary files /dev/null and b/apps/ha/screenshot_2.png differ
diff --git a/apps/ha/screenshot_3.png b/apps/ha/screenshot_3.png
new file mode 100644
index 000000000..b9eae0b74
Binary files /dev/null and b/apps/ha/screenshot_3.png differ
diff --git a/apps/hardalarm/ChangeLog b/apps/hardalarm/ChangeLog
index dac7d317e..fea8770fc 100644
--- a/apps/hardalarm/ChangeLog
+++ b/apps/hardalarm/ChangeLog
@@ -1,3 +1,4 @@
0.01: Add a number to match to turn off alarm
0.02: Respect Quiet Mode
0.03: Fix hour/minute wrapping code for new menu system
+0.04: Use default Bangle formatter for booleans
diff --git a/apps/hardalarm/app.js b/apps/hardalarm/app.js
index 0c72a2c8f..0aa33b21b 100644
--- a/apps/hardalarm/app.js
+++ b/apps/hardalarm/app.js
@@ -66,17 +66,14 @@ function editAlarm(alarmIndex) {
},
/*LANG*/'Enabled': {
value: en,
- format: v=>v?"On":"Off",
onchange: v=>en=v
},
/*LANG*/'Repeat': {
value: en,
- format: v=>v?"Yes":"No",
onchange: v=>repeat=v
},
/*LANG*/'Auto snooze': {
value: as,
- format: v=>v?"Yes":"No",
onchange: v=>as=v
}
};
diff --git a/apps/hardalarm/metadata.json b/apps/hardalarm/metadata.json
index 1dab4501d..df287b426 100644
--- a/apps/hardalarm/metadata.json
+++ b/apps/hardalarm/metadata.json
@@ -2,7 +2,7 @@
"id": "hardalarm",
"name": "Hard Alarm",
"shortName": "HardAlarm",
- "version": "0.03",
+ "version": "0.04",
"description": "Make sure you wake up! Count to the right number to turn off the alarm",
"icon": "app.png",
"tags": "tool,alarm,widget",
diff --git a/apps/hcclock/ChangeLog b/apps/hcclock/ChangeLog
index f70653d58..289c7ac2d 100644
--- a/apps/hcclock/ChangeLog
+++ b/apps/hcclock/ChangeLog
@@ -1,3 +1,4 @@
0.01: Base code
0.02: Saved settings when switching color scheme
-0.03: Added Button 3 opening messages (if app is installed)
\ No newline at end of file
+0.03: Added Button 3 opening messages (if app is installed)
+0.04: Use `messages` library to check for new messages
\ No newline at end of file
diff --git a/apps/hcclock/hcclock.app.js b/apps/hcclock/hcclock.app.js
index de5163996..9558c052b 100644
--- a/apps/hcclock/hcclock.app.js
+++ b/apps/hcclock/hcclock.app.js
@@ -3,7 +3,7 @@
// Numbers Rect order (left, top, right, bottom)
// Each number defines a set of rects to draw
-const numbers =
+const numbers =
[
[// Zero
[0, 0, 1, 0.2],
@@ -64,7 +64,7 @@ const numbers =
[0, 0.8, 1, 1],
[0, 0, 0.1, 0.6],
[0.9, 0, 1, 1]
- ]
+ ]
];
const months = [ "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC" ];
@@ -103,7 +103,7 @@ function updateTime()
let mo = now.getMonth();
let y = now.getFullYear();
let d = now.getDate();
-
+
if(h != hour)
{
hour = h;
@@ -127,7 +127,7 @@ function updateTime()
day = d;
g.setFont("6x8", 2);
g.setFontAlign(0, -1, 0);
- g.drawString(fmtDate(d,mo,y,hour), 120, 120);
+ g.drawString(fmtDate(d,mo,y,hour), 120, 120);
}
drawMessages();
}
@@ -136,7 +136,7 @@ function drawDigits(x, value)
{
if(!Bangle.isLCDOn()) // No need to draw when LCD Off
return;
-
+
drawChar(Math.floor(value/10), 15, x, 115, x+50);
if(value%10 == 1)
drawChar(value%10, 55, x, 155, x+50);
@@ -228,27 +228,18 @@ function flipColors()
// MESSAGE HANDLING()
//
-let messages_installed = require("Storage").read("messages.app.js") != undefined;
+let messages_installed = require("Storage").read("messages") !== undefined;
function handleMessages()
{
- if(messages_installed && hasMessages() > 0)
- {
- E.showMessage("Loading Messages...");
- load("messages.app.js");
- }
+ if(!hasMessages()) return;
+ E.showMessage("Loading Messages...");
+ load("messages.app.js");
}
function hasMessages()
{
- if(!messages_installed)
- return false;
-
- var messages = require("Storage").readJSON("messages.json",1)||[];
- if (messages.some(m=>m.new))
- return true;
- else
- return false;
+ return messages_installed && require("messages").status() === 'new';
}
let msg = atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+DAADDAADDAADDwAPD8A/DOBzDDn/DA//DAHvDAPvjAPvjAPvjAPvh///gf/vAAD+AAB8AAAAA==");
@@ -256,20 +247,21 @@ let had_messages = false;
function drawMessages()
{
- if(!had_messages && hasMessages()) {
+ const has_messages = hasMessages();
+ if(has_messages === had_messages) return;
+ if(has_messages) {
g.setColor(255,255,255);
g.drawImage(msg, 184, 212);
g.setFont("6x8", 2);
g.setFontAlign(0, -1, 0);
g.drawString(">", 224, 216);
- had_messages = true;
- }
- else if (had_messages && !hasMessages())
+ }
+ else
{
g.setColor(0,0,0);
g.fillRect(180, 210, 240, 240);
- had_messages = false;
}
+ had_messages = has_messages;
}
//////////////////////////////////////////
diff --git a/apps/hcclock/metadata.json b/apps/hcclock/metadata.json
index 0d4cbe0cd..b8f8c14b9 100644
--- a/apps/hcclock/metadata.json
+++ b/apps/hcclock/metadata.json
@@ -1,7 +1,7 @@
{
"id": "hcclock",
"name": "Hi-Contrast Clock",
- "version": "0.03",
+ "version": "0.04",
"description": "Hi-Contrast Clock : A simple yet very bold clock that aims to be readable in high luninosity environments. Uses big 10x5 pixel digits. Use BTN 1 to switch background and foreground colors.",
"icon": "hcclock-icon.png",
"type": "clock",
diff --git a/apps/health/interface.html b/apps/health/interface.html
index 0791acd24..a708e2645 100644
--- a/apps/health/interface.html
+++ b/apps/health/interface.html
@@ -113,7 +113,7 @@ function getMonthList() {
Util.showModal("Deleting...");
Util.eraseStorage(filename,()=>{
Util.hideModal();
- getTrackList();
+ getMonthList();
});
}
if (task=="downloadcsv") {
diff --git a/apps/heart/ChangeLog b/apps/heart/ChangeLog
index f6fd9793e..fe03575c9 100644
--- a/apps/heart/ChangeLog
+++ b/apps/heart/ChangeLog
@@ -11,5 +11,6 @@
Reduce memory usage by ~30%
Generate scale based on defined minimum and maximum measurement
Added background line on 50% to ease estimation of drawn values
-0.06: tag HRM power requests to allow this ot work alongside other widgets/apps (fix #799)
-0.07: theme support
+0.06: Tag HRM power requests to allow this ot work alongside other widgets/apps (fix #799)
+0.07: Theme support
+0.08: Use default Bangle formatter for booleans
diff --git a/apps/heart/app.js b/apps/heart/app.js
index 5428ea06b..c10185b5f 100644
--- a/apps/heart/app.js
+++ b/apps/heart/app.js
@@ -28,7 +28,6 @@ function showMainMenu() {
'': { 'title': 'Heart Recorder' },
'RECORD': {
value: !!settings.isRecording,
- format: v=>v?"On":"Off",
onchange: v => {
settings.isRecording = v;
updateSettings();
diff --git a/apps/heart/metadata.json b/apps/heart/metadata.json
index 6265dbfef..2071bdf08 100644
--- a/apps/heart/metadata.json
+++ b/apps/heart/metadata.json
@@ -2,7 +2,7 @@
"id": "heart",
"name": "Heart Rate Recorder",
"shortName": "HRM Record",
- "version": "0.07",
+ "version": "0.08",
"description": "Application that allows you to record your heart rate. Can run in background",
"icon": "app.png",
"tags": "tool,health,widget",
diff --git a/apps/hebrew_calendar/customizer.html b/apps/hebrew_calendar/customizer.html
index bea860e53..c6d529414 100644
--- a/apps/hebrew_calendar/customizer.html
+++ b/apps/hebrew_calendar/customizer.html
@@ -7,6 +7,333 @@
Hebrew Calendar Customizer
+
+
@@ -20,11 +347,15 @@
+
+
+
+
+
+
+
diff --git a/apps/imgclock/metadata.json b/apps/imgclock/metadata.json
index 799d11acc..94dff5f17 100644
--- a/apps/imgclock/metadata.json
+++ b/apps/imgclock/metadata.json
@@ -2,18 +2,19 @@
"id": "imgclock",
"name": "Image background clock",
"shortName": "Image Clock",
- "version": "0.08",
+ "version": "0.10",
"description": "A clock with an image as a background",
"icon": "app.png",
"type": "clock",
"tags": "clock",
- "supports": ["BANGLEJS"],
+ "supports": ["BANGLEJS","BANGLEJS2"],
"custom": "custom.html",
+ "customConnect": true,
"storage": [
{"name":"imgclock.app.js","url":"app.js"},
{"name":"imgclock.img","url":"app-icon.js","evaluate":true},
{"name":"imgclock.face.img"},
{"name":"imgclock.face.json"},
- {"name":"imgclock.face.bg","content":""}
+ {"name":"imgclock.face.bg","content":"X"}
]
}
diff --git a/apps/impwclock/ChangeLog b/apps/impwclock/ChangeLog
index 6555fcc8f..0af7c99d6 100644
--- a/apps/impwclock/ChangeLog
+++ b/apps/impwclock/ChangeLog
@@ -3,3 +3,4 @@
0.03: Move to Bangle.setUI to launcher support
0.04: Tweaks for compatibility with BangleJS2
0.05: Time-word now readable on Bangle.js 2
+0.06: Tell clock widgets to hide.
diff --git a/apps/impwclock/clock-impword.js b/apps/impwclock/clock-impword.js
index c42dbda44..04421017b 100644
--- a/apps/impwclock/clock-impword.js
+++ b/apps/impwclock/clock-impword.js
@@ -154,6 +154,9 @@ Bangle.on('lcdPower', function(on) {
if (on) drawWordClock();
});
+// Show launcher when button pressed
+Bangle.setUI("clock");
+
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
@@ -172,5 +175,4 @@ Bangle.on('touch',e=>{
}
});
-// Show launcher when button pressed
-Bangle.setUI("clock");
+
diff --git a/apps/impwclock/metadata.json b/apps/impwclock/metadata.json
index 733dbb957..1b92ea3ae 100644
--- a/apps/impwclock/metadata.json
+++ b/apps/impwclock/metadata.json
@@ -1,7 +1,7 @@
{
"id": "impwclock",
"name": "Imprecise Word Clock",
- "version": "0.05",
+ "version": "0.06",
"description": "Imprecise word clock for vacations, weekends, and those who never need accurate time.",
"icon": "clock-impword.png",
"type": "clock",
diff --git a/apps/info/ChangeLog b/apps/info/ChangeLog
index 07afedd21..093dd4606 100644
--- a/apps/info/ChangeLog
+++ b/apps/info/ChangeLog
@@ -1 +1,3 @@
-0.01: Release
\ No newline at end of file
+0.01: Release
+0.02: Recfactoring and show weather data
+0.03: Show sizes for used, free and trash through storage.getStats
\ No newline at end of file
diff --git a/apps/info/info.app.js b/apps/info/info.app.js
index c61a88045..ade3f3ebb 100644
--- a/apps/info/info.app.js
+++ b/apps/info/info.app.js
@@ -1,27 +1,99 @@
-var s = require("Storage");
+const storage = require("Storage");
const locale = require('locale');
var ENV = process.env;
var W = g.getWidth(), H = g.getHeight();
var screen = 0;
-const maxScreen = 2;
+
+
+var screens = [
+ {
+ name: "General",
+ items: [
+ {name: "Steps", fun: () => getSteps()},
+ {name: "HRM", fun: () => getBpm()},
+ {name: "", fun: () => ""},
+ {name: "Temp.", fun: () => getWeatherTemp()},
+ {name: "Humidity", fun: () => getWeatherHumidity()},
+ {name: "Wind", fun: () => getWeatherWind()},
+ ]
+ },
+ {
+ name: "Hardware",
+ items: [
+ {name: "Battery", fun: () => E.getBattery() + "%"},
+ {name: "Charge?", fun: () => Bangle.isCharging() ? "Yes" : "No"},
+ {name: "TempInt.", fun: () => locale.temp(parseInt(E.getTemperature()))},
+ {name: "Bluetooth", fun: () => NRF.getSecurityStatus().connected ? "Conn" : "NoConn"},
+ {name: "GPS", fun: () => Bangle.isGPSOn() ? "On" : "Off"},
+ {name: "Compass", fun: () => Bangle.isCompassOn() ? "On" : "Off"},
+ ]
+ },
+ {
+ name: "Software",
+ items: [
+ {name: "Firmw.", fun: () => ENV.VERSION},
+ {name: "Git", fun: () => ENV.GIT_COMMIT},
+ {name: "Boot.", fun: () => getVersion("boot.info")},
+ {name: "Settings.", fun: () => getVersion("setting.info")},
+ ]
+ },
+ {
+ name: "Storage [kB]",
+ items: [
+ {name: "Total", fun: () => storage.getStats().totalBytes>>10},
+ {name: "Free", fun: () => storage.getStats().freeBytes>>10},
+ {name: "Trash", fun: () => storage.getStats().trashBytes>>10},
+ {name: "", fun: () => ""},
+ {name: "#File", fun: () => storage.getStats().fileCount},
+ {name: "#Trash", fun: () => storage.getStats().trashCount},
+ ]
+ },
+];
+
+
+function getWeatherTemp(){
+ try {
+ var weather = storage.readJSON('weather.json').weather;
+ return locale.temp(weather.temp-273.15);
+ } catch(ex) { }
+
+ return "?";
+}
+
+
+function getWeatherHumidity(){
+ try {
+ var weather = storage.readJSON('weather.json').weather;
+ return weather.hum = weather.hum + "%";
+ } catch(ex) { }
+
+ return "?";
+}
+
+
+function getWeatherWind(){
+ try {
+ var weather = storage.readJSON('weather.json').weather;
+ var speed = locale.speed(weather.wind).replace("mph", "");
+ return Math.round(speed * 1.609344) + "kph";
+ } catch(ex) { }
+
+ return "?";
+}
+
function getVersion(file) {
- var j = s.readJSON(file,1);
+ var j = storage.readJSON(file,1);
var v = ("object"==typeof j)?j.version:false;
return v?((v?"v"+v:"Unknown")):"NO ";
}
-function drawData(name, value, y){
- g.drawString(name, 5, y);
- g.drawString(value, 100, y);
-}
-
function getSteps(){
try{
return Bangle.getHealthStatus("day").steps;
} catch(e) {
- return ">= 2v12";
+ return ">2v12";
}
}
@@ -29,53 +101,36 @@ function getBpm(){
try{
return Math.round(Bangle.getHealthStatus("day").bpm) + "bpm";
} catch(e) {
- return ">= 2v12";
+ return ">2v12";
}
}
+function drawData(name, value, y){
+ g.drawString(name, 10, y);
+ g.drawString(value, 100, y);
+}
+
function drawInfo() {
g.reset().clearRect(Bangle.appRect);
var h=18, y = h;//-h;
// Header
- g.setFont("Vector", h+2).setFontAlign(0,-1);
- g.drawString("--==|| INFO ||==--", W/2, 0);
+ g.drawLine(0,25,W,25);
+ g.drawLine(0,26,W,26);
+
+ // Info body depending on screen
g.setFont("Vector",h).setFontAlign(-1,-1);
+ screens[screen].items.forEach(function (item, index){
+ drawData(item.name, item.fun(), y+=h);
+ });
- // Dynamic data
- if(screen == 0){
- drawData("Steps", getSteps(), y+=h);
- drawData("HRM", getBpm(), y+=h);
- drawData("Battery", E.getBattery() + "%", y+=h);
- drawData("Voltage", E.getAnalogVRef().toFixed(2) + "V", y+=h);
- drawData("IntTemp.", locale.temp(parseInt(E.getTemperature())), y+=h);
- }
-
- if(screen == 1){
- drawData("Charging?", Bangle.isCharging() ? "Yes" : "No", y+=h);
- drawData("Bluetooth", NRF.getSecurityStatus().connected ? "Conn." : "Disconn.", y+=h);
- drawData("GPS", Bangle.isGPSOn() ? "On" : "Off", y+=h);
- drawData("Compass", Bangle.isCompassOn() ? "On" : "Off", y+=h);
- drawData("HRM", Bangle.isHRMOn() ? "On" : "Off", y+=h);
- }
-
- // Static data
- if(screen == 2){
- drawData("Firmw.", ENV.VERSION, y+=h);
- drawData("Boot.", getVersion("boot.info"), y+=h);
- drawData("Settings", getVersion("setting.info"), y+=h);
- drawData("Storage", "", y+=h);
- drawData(" Total", ENV.STORAGE>>10, y+=h);
- drawData(" Free", require("Storage").getFree()>>10, y+=h);
- }
-
- if(Bangle.isLocked()){
- g.setFont("Vector",h-2).setFontAlign(-1,-1);
- g.drawString("Locked", 0, H-h+2);
- }
-
+ // Bottom
+ g.drawLine(0,H-h-3,W,H-h-3);
+ g.drawLine(0,H-h-2,W,H-h-2);
+ g.setFont("Vector",h-2).setFontAlign(-1,-1);
+ g.drawString(screens[screen].name, 2, H-h+2);
g.setFont("Vector",h-2).setFontAlign(1,-1);
- g.drawString((screen+1) + "/3", W, H-h+2);
+ g.drawString((screen+1) + "/" + screens.length, W, H-h+2);
}
drawInfo();
@@ -88,14 +143,15 @@ Bangle.on('touch', function(btn, e){
var isRight = e.x > right;
if(isRight){
- screen = (screen + 1) % (maxScreen+1);
+ screen = (screen + 1) % screens.length;
}
if(isLeft){
screen -= 1;
- screen = screen < 0 ? maxScreen : screen;
+ screen = screen < 0 ? screens.length-1 : screen;
}
+ Bangle.buzz(40, 0.6);
drawInfo();
});
@@ -104,5 +160,4 @@ Bangle.on('lock', function(isLocked) {
});
Bangle.loadWidgets();
-for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
-// Bangle.drawWidgets();
\ No newline at end of file
+Bangle.drawWidgets();
\ No newline at end of file
diff --git a/apps/info/metadata.json b/apps/info/metadata.json
index f05f0e134..ac56cd5c3 100644
--- a/apps/info/metadata.json
+++ b/apps/info/metadata.json
@@ -1,7 +1,7 @@
{
"id": "info",
"name": "Info",
- "version": "0.01",
+ "version": "0.03",
"description": "An application that displays information such as battery level, steps etc.",
"icon": "info.png",
"type": "app",
@@ -11,7 +11,8 @@
"screenshots": [
{"url":"screenshot_1.png"},
{"url":"screenshot_2.png"},
- {"url":"screenshot_3.png"}],
+ {"url":"screenshot_3.png"},
+ {"url":"screenshot_4.png"}],
"storage": [
{"name":"info.app.js","url":"info.app.js"},
{"name":"info.img","url":"info.icon.js","evaluate":true}
diff --git a/apps/info/screenshot_1.png b/apps/info/screenshot_1.png
index 97d42a896..6661c122c 100644
Binary files a/apps/info/screenshot_1.png and b/apps/info/screenshot_1.png differ
diff --git a/apps/info/screenshot_2.png b/apps/info/screenshot_2.png
index 2d25dd4e6..3d91fcabe 100644
Binary files a/apps/info/screenshot_2.png and b/apps/info/screenshot_2.png differ
diff --git a/apps/info/screenshot_3.png b/apps/info/screenshot_3.png
index 782e4a195..86bbb67cf 100644
Binary files a/apps/info/screenshot_3.png and b/apps/info/screenshot_3.png differ
diff --git a/apps/info/screenshot_4.png b/apps/info/screenshot_4.png
new file mode 100644
index 000000000..b8b59b1ef
Binary files /dev/null and b/apps/info/screenshot_4.png differ
diff --git a/apps/invader/ChangeLog b/apps/invader/ChangeLog
index 5560f00bc..6c5a33e59 100644
--- a/apps/invader/ChangeLog
+++ b/apps/invader/ChangeLog
@@ -1 +1,2 @@
0.01: New App!
+0.11: Changes...
diff --git a/apps/ios/ChangeLog b/apps/ios/ChangeLog
index b6a386bcb..87a71a1ed 100644
--- a/apps/ios/ChangeLog
+++ b/apps/ios/ChangeLog
@@ -7,3 +7,4 @@
0.07: Added more details from music (instead of Undefined), added more app identifiers
0.08: Added more app identifiers, added 'cannot display' in case a message goes empty because of replacements
0.09: Enable 'ams' on new firmwares (ams/ancs can now be enabled individually) (fix #1365)
+0.10: Added more bundleIds
diff --git a/apps/ios/boot.js b/apps/ios/boot.js
index 5ea7550eb..6b4ad7c5b 100644
--- a/apps/ios/boot.js
+++ b/apps/ios/boot.js
@@ -63,6 +63,7 @@ E.on('notify',msg=>{
"name" : string,
*/
var appNames = {
+ "ch.publisheria.bring": "Bring",
"com.apple.facetime": "FaceTime",
"com.apple.mobilecal": "Calendar",
"com.apple.mobilemail": "Mail",
@@ -73,6 +74,9 @@ E.on('notify',msg=>{
"com.apple.podcasts": "Podcasts",
"com.apple.reminders": "Reminders",
"com.apple.shortcuts": "Shortcuts",
+ "com.apple.TestFlight": "TestFlight",
+ "com.apple.ScreenTimeNotifications": "ScreenTime",
+ "com.apple.wifid.usernotification": "WiFi",
"com.atebits.Tweetie2": "Twitter",
"com.burbn.instagram" : "Instagram",
"com.facebook.Facebook": "Facebook",
@@ -99,19 +103,22 @@ E.on('notify',msg=>{
"com.toyopagroup.picaboo": "Snapchat",
"com.ubercab.UberClient": "Uber",
"com.ubercab.UberEats": "UberEats",
+ "com.unitedinternet.mmc.mobile.gmx.iosmailer": "GMX",
+ "com.valvesoftware.Steam": "Steam",
"com.vilcsak.bitcoin2": "Coinbase",
"com.wordfeud.free": "WordFeud",
+ "com.yourcompany.PPClient": "PayPal",
"com.zhiliaoapp.musically": "TikTok",
+ "de.no26.Number26": "N26",
"io.robbie.HomeAssistant": "Home Assistant",
+ "net.superblock.Pushover": "Pushover",
"net.weks.prowl": "Prowl",
"net.whatsapp.WhatsApp": "WhatsApp",
- "net.superblock.Pushover": "Pushover",
"nl.ah.Appie": "Albert Heijn",
"nl.postnl.TrackNTrace": "PostNL",
"org.whispersystems.signal": "Signal",
"ph.telegra.Telegraph": "Telegram",
"tv.twitch": "Twitch",
-
// could also use NRF.ancsGetAppInfo(msg.appId) here
};
var unicodeRemap = {
diff --git a/apps/ios/metadata.json b/apps/ios/metadata.json
index eb75a6dbc..63f02262f 100644
--- a/apps/ios/metadata.json
+++ b/apps/ios/metadata.json
@@ -1,7 +1,7 @@
{
"id": "ios",
"name": "iOS Integration",
- "version": "0.09",
+ "version": "0.10",
"description": "Display notifications/music/etc from iOS devices",
"icon": "app.png",
"tags": "tool,system,ios,apple,messages,notifications",
diff --git a/apps/isoclock/ChangeLog b/apps/isoclock/ChangeLog
index 809091ce4..7b57ecfa9 100644
--- a/apps/isoclock/ChangeLog
+++ b/apps/isoclock/ChangeLog
@@ -1,2 +1,3 @@
0.01: Created app based on digiclock with some small tweaks.
0.02: Swap to Bangle.setUI for launcher/buttons
+0.03: Tell clock widgets to hide.
diff --git a/apps/isoclock/checkout b/apps/isoclock/checkout
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/isoclock/isoclock.js b/apps/isoclock/isoclock.js
index 59f28e66e..7526660b9 100644
--- a/apps/isoclock/isoclock.js
+++ b/apps/isoclock/isoclock.js
@@ -89,8 +89,8 @@ Bangle.on('lcdPower',on=>{
}
});
-Bangle.loadWidgets();
-Bangle.drawWidgets();
-
// Show launcher when button pressed
Bangle.setUI("clock");
+
+Bangle.loadWidgets();
+Bangle.drawWidgets();
diff --git a/apps/isoclock/metadata.json b/apps/isoclock/metadata.json
index 313153dde..488afcb41 100644
--- a/apps/isoclock/metadata.json
+++ b/apps/isoclock/metadata.json
@@ -2,7 +2,7 @@
"id": "isoclock",
"name": "ISO Compliant Clock Face",
"shortName": "ISO Clock",
- "version": "0.02",
+ "version": "0.03",
"description": "Tweaked fork of digiclock for ISO date and time",
"icon": "isoclock.png",
"type": "clock",
diff --git a/apps/kanawatch/ChangeLog b/apps/kanawatch/ChangeLog
new file mode 100644
index 000000000..f2c991fd0
--- /dev/null
+++ b/apps/kanawatch/ChangeLog
@@ -0,0 +1,5 @@
+0.01: First release
+0.02: Improve battery life, sprite resolution, fix launcher issue and unaligned text bug
+0.03: Reduce code size, refresh once a minute and faster refresh
+0.04: Show a random kana every minute to improve learning
+0.05: Tell clock widgets to hide.
diff --git a/apps/kanawatch/README.md b/apps/kanawatch/README.md
new file mode 100644
index 000000000..e213949dc
--- /dev/null
+++ b/apps/kanawatch/README.md
@@ -0,0 +1,19 @@
+# kanawatch
+
+A simple watchface design with hiragana and katakana
+cards for learning.
+
+## Changelog
+
+0.01: First release
+0.02: Improve battery life, sprite resolution, fix launcher issue and unaligned text bug
+0.03: Reduce code size, refresh once a minute and faster refresh
+0.04: Show a random kana every minute to improve learning
+
+## Author
+
+Written by pancake in 2022, powered by insomnia
+
+## Screenshots
+
+
diff --git a/apps/kanawatch/app-icon.js b/apps/kanawatch/app-icon.js
new file mode 100644
index 000000000..a17f21d56
--- /dev/null
+++ b/apps/kanawatch/app-icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwxEBAH4A/AEn/AAgrrAA4ttGL4hF9fGsU1pMNmti43rGLwcD/3MxEAud413p6uuvFzgGI5n+GDQaD6F8i2p8KKH8Opi186AwYC4Xv08A0fnXhfn0cA0/vGCoVC7+ItHNE4vQ+oxH5toxHfGCYTC8t/xaKH5VY+CUIxd/8owSCIPxymB8wkH8UA2yTI82Byn4F6AXCwNH7YjI7UATAwAD7dHHgYuP4sAc5XLgHrBpXAjngGBwOCrmJ/whJ1syBgXw7v6Bov+xObF5rWDgHWKJWEt3l4mQjkAoHzBwvWgHhGBgMC1WIDQuw1/L427z8ygAABp+R3vqH4+I1QvO/1R5YZF+t1FINWuMAy/W+BuKZ4NRT4ReL7kc+waG/fy/n/9kA74tLAAP2jncAgPBF5W5yIeLZgPxEgf3CJOR3JTCF5WU3wvL6sA/YFC7e0CJO+ygDB94vKt3aF5fHoQDB+/dzdL4nb+YRG7VuAYP5F5VF9ovL3dP3t8pOKgFw0+CjmT84RE9tFAYP+F6/uwMm1Hd/vCk3oQYWGl3XF6aPK/e0oVwrohCmu9Bof5sVF+yPSd5PtuWA9m7o///uCwH9B4m9gHKd6W5yIuG9NV3v+//Gjn/2VA9wQF6UA2AFCyO5AYPcF5Xcjh1DAAPnp/SEYnJiy2EAAXTgGvAgP2jncAgPBF44wC/1R5a7EsZHCAAPegEA3afH4sA4wEB5dROgP/FxBgD1WIPgky/QGD5MAxYfCAAuGjnvAgNHuBLCF5nhgHWAoWvuwEC9mWLwN+Fw6aB1wEB60A44EB6ovJGAebxJSC1lF4/AyMNoXBzUN/IuF5kmyP8VgOJrgKCFxUB8QOB8Ec4CnCLIMAmWr+v/9Vy/otD+WWmu7BAXAjnFF5xgD21H7f//u+0vN/CKH9Ojse4+QHC7dH2wuPgPVCAP4yk98wqHAAf734OF82ByhCDF5pgD/9/xfhGBYAF8OLv/lFyIABU4XfxFo5ouP5toxHfFyZhE9+ngGj84tL8+jgGn94uVSQvQvkW1KUI8Opi186AIDFygwF/3MxEAuew6fp9PT2FzgGI5n+FzQwFAAPr42fu9JpN3z/G9YPFFzAxIABYtbGKItfGZYrlAH4A+A"))
diff --git a/apps/kanawatch/app.js b/apps/kanawatch/app.js
new file mode 100644
index 000000000..088dab785
--- /dev/null
+++ b/apps/kanawatch/app.js
@@ -0,0 +1,273 @@
+const stripe_width = 32;
+const stripe_pos = 40;
+const stripe2_pos = 110;
+const h = g.getHeight();
+const w = g.getWidth();
+
+/// /////////////////////////////////////////
+const katakana = {};
+const hiragana = {};
+function image(x,y,b) {
+ return {
+ bpp:1, width:x,height:y,
+ buffer:require('heatshrink').decompress(atob(b))
+ };
+}
+katakana['A'] = image(56, 51, "v//AAfwAon//AGF/wGT/gGM/A3F/BDEn/wJQoGCj4RB//gAxUB//AAwcDAwsH/+AAwcP/4tCAwMf/wGEn/8Awl/JYYGBKQkf/I9DAwJgBGwQGDGwRlBAwJsE+42DAwPzGwYGB+J7EQIIvDQIIFEAw5DEAwRDDgCIEAxCPBKIcAR4IhER4hnCLAg9BLAgoBAwgoBcQiCBMwj0BHogGBHogGBfoooEQQREFEIgGBAokAhAGFA=");
+katakana['I'] = image(54, 55, "AAkEAws+AokB/wGEg//Awk//gTE//gAwcPCYt/CYkDCYsfCYv//A0F4A0ECYg0BCYggBCYn/KwhBBGgl/EAgtBEAgMBEAZOBEAgMBEAYZB/+ABggTDBgQnDAoIaDJoIaDFgIABDQQFC74aBBgX8v4aBEwWBDQQgB/EHDQQ6BwEfGoX/+AJBDQMDWAKMBDQMPAQIaDiBFCPAgaDU4hrDDQiuDDX4acSAIaCA=");
+katakana['U'] = image(52, 55, "AAMP/gGE//ABlH/AAnvAon+Bk5EDv/vIgcHBkHPBgZwBBgn/Bi8B/+PBgcf/AMFw/wBgYEDgED/6qEv4MEKYK3F8AFDj7EED4LREv/4CQn/wASEFginBDAgfEDAIfDn67BC4YABH4QXBCQcHZoQkEEoYMCHAYlBFYZEBLwk/MgpQEAAw");
+katakana['E'] = image(58, 45, "h//AAfwgYGE/0AAwn/wE/AwngDgv4DjhDCv/wJQkf/gGEg//AwkB//AA4gc/Dn4cjbAv/34GF94GF/YGF/wcjwA=");
+katakana['O'] = image(57, 54, "AAcf+AGEh/8AwkH/wGEgf/AwkB/+AA4n/4AGEv/gAwk/GIsf/A4P/4AE+F/Awn4n4GE/kfAwn+h4cFg4GFwYGF4IGFKwYFBMQpxFAwJxEAwJxEAwJxEAwJxEK4JxEAwKqEMoQGE/o4En/8HAl//iqEAwKqEv/+VQgNBVQgNBcYgNBcYhLBcYhSCHAQKBAwI4CAwY4CD4IGBHASxBAYI4CAwY4CYwIGBHAQGBD4I4CBIJfCHASmDHAV/PYQ4Cj5QCHAUPLwQ4CgQGCOIgABOIgABHAIGEAAY=");
+katakana['KA'] = image(54, 54, "AAMP/AGEv/gAocB/+AAwcH/wTEj4arg//AAf+j4GE/F/AwnhAon/w4aZHAMP/hTEn/wKYn/4BTDgf/KYgQCDQYQCBIQQDBIQQCBIc/DQouCDQQuCEghJBEhITBH4RTBLoRTEBIJTGCAUPNwoTCDQQWBDoIuCj4TCJIX/CYQ/BZQInBH4U//0HwBTBGgPwXAXwh4PBXAXAv4PCZIIgBEYTJBn5SBDQXABAIzBCYJcCDQXwgbOCAwIDBQgI4CgEOJwIADkAGFA");
+katakana['KI'] = image(58, 55, "AAU+Awv/4AGEn/wAwkP/gGEgf/Dkk/CAc//4ABwAGBj4GC8ATBAAf4h4GE/woBAAmAAwvgFAYcIwAcD/BFDFARFD/kBIoYACv5FBAAcfRL94DgkfHgf/95EBD4RgDD4MHLwf8AogAd+CPFGwiJCS4XHJgSGB8CJEkCJJUwYABg5pDD4amTNwKmXYbgcDLoY=");
+katakana['KU'] = image(55, 55, "AAMHwAGEh/8Awkf/AGEv/wAwn/4AFDgf/EQkH/whF/4ACAwM/AoQQCBgY5BgIGDHIMHAwY5Bh4GD8AhEIAQFDIAIhBBIJACEIJpEj45CNIV/NgRpBDQIrBEoPgDQJlBEoQaDEoV/RwUP/wPBQ4Uf/gPBQ4QsBKAKSD8BvCSQXDDQYYBNYIaCGYIqBDQU//kPXoYYBj5QCEIPgj60DKoMcWga7FKoYABKogaDbojPBbojMDGob/ECYJBCbgYaDE4IaEPoIaDEAI1EbYQZECYgtBCZQGCLol/KwxxEAwJqEgIMFgIZEgA=");
+katakana['KE'] = image(60, 54, "AAMcAwsD/4HFn/wBxl/8AGEg/+BxkP/gOF//ABxcB/+AA4kf/BCGAAZOBv4HEIQIOGAwgOBh4OFGYIOFn4OFEgoOBAwvgh52BKgYDBOwJUDv5nBBwY6BAYM/BwIKBJgJjBBQSbCWoQVBRgK1D/4oDBwJJBWos/WIS1CgIVCJoRGBWowCCj61HYgpRCdIjEGLgTLEIwTLEfAv/GYqtBEghyBGYjoCAwwkDAwQVEYwYjEHQt/CopeBQgQOEIIgOBPgxeFgZ7FA");
+katakana['KO'] = image(49, 46, "v//AAYFF34FE74FE94FE+4FE/IFE/gFE/w0Dgf/AocB/+AAwf/4BHE8AFDn/wAocf/AFDh/8AocHGH4w6YZf7Aon9YYoFEejBhEAAIA=");
+katakana['SA'] = image(58, 53, "AAcD/wDBg4DC//AgEB/+AgE/+AKBv/ggEP/gGBj/4DgP/DnU//4A34CQ+DAIcEDAIcDDAQDDDAYDCDAYDD/4cDIgJADAAUfIAQACh4jCAAUHD4QACJwIfBAAQtBEYgGBI4QUDFQkP/4qEVYQvEAAIxCEIK5CBwV/AwsfAwocCAwYcCJogcBNIp3F");
+katakana['SI'] = image(56, 52, "gFwAwt+Awv/8AGF/gFDgP//4GGCocDAwIVDBoX/wAHCn4VFg4GB4AxEAwsfAworBEQYABv4GFj4DCjgrCBQYRFn/4JQfAIgIGD+F/JQcD/gGBMARQCOwcH/wNBCoUP/0PAwIrBj/8OwQGBn4fBGIIGCAQIlB+BcBAQKvDBIQRB8AfBIQUH4AXBP4RXBGgJmERoJsFAwv//yaFbYghBQIYaCeAi9FPQTZGdxKFCFASECFAZPBEIgNCJQaZEAwhDDAwRJDTAYGEQAiQBPIgAGA");
+katakana['SU'] = image(60, 51, "gH/AAYGBh4GD/AOG4AOF/gONDo+ABxAACgY7CAAd/+AGEg4OG//gAwkP/wGEgJCCAAcfKIQzEIQIzEIQozOj4zFEgIzFn4kHGYv/M4okIGYt/IQqXBFghuBHYs/bAY6DCwrJECod/HgYVB8ZLEcoMfLQYECCwYVB+BTBCwT7CCwYrBAYIKCCoQDC8BXBEIQSBNoQVBBYP4EAIoCOQPHCoYTB/xdBIwQ8B+6SET4N/dYn/4aCFFgKRFgC+EgPghivEAoI");
+katakana['SE'] = image(57, 53, "gEH/wGEgf/AwkB/+AA4n/4AGEv/gAwk/+AGEj/4AwkP/g4JjA4EBQQ4D/4DD4E/AwIuBv/vAoP/FwILCAAIuBv4GEBgn//wFEAwITEh//CgfwAwMfCIRGB/4BB/5xBAgJTBIQQGBwP/75CBAwOAD4JCBAwRmDDIKYBOIQGDOIQGDOIQbBAwSqBAwiqBAwiqBDYg4Cv4GCHAUfAwQ4Cg4GCHAUBbwbjnHAgADcYYADUYQxEEYq6CVwbDBdQi6CZQYqBAAZcCAwY1BEYi5DAAQ8CegfgA=");
+katakana['SO'] = image(52, 52, "gGAAol8AYUD/Ef4AGCn/3/wFCg/+v/wAwV/8//Bgk//AMD8f/FoQMBj/8Bgfg//gBgcPFoYMBFocP/kHFof/4AtDBgMDFoYMBFoYMBgIIBgADBwAtDj4dBHQQMCFoYqCHQQqCFoc/BIIPCCwQtDKYIpBB4IwDIAQwCh45CBIVAFgSmDFIaaDOIYfCVgYfBRYYfCTASTCUoY1BQgZPCD4l/D4kfH4g4BH4YYBH4gFBGQd//4yDBYIyDn4SEJQIlEBgRXEHAg+BFYZRGZYQADBYgAG");
+katakana['TA'] = image(55, 56, "AAMHwAGEh/8Awkf/AGEv/gAwn/4AFDgf/EQkH/4oF/4ACAwM/AoX+FAQGCHIMBCYY5BEIIAC+AhFIAIhDHIQFDF4IhBJQMHF4JDDNIUfHIRpCv5sCn/wDQJsCDwIaBEIIKBwEf/9gOAQaB/gbBFAIPB+YsC/AaB54RBFAIaBAIOAEoJvBOgPh/+DNAJWB+//DQPBQIZyBM4f4LQSQC8EPKAIpBFAMPPgKKCgEcYIZwBiAGDbohwEZ4bdEFILxFf4ghBXwLjEDQhLBCYoaEE4IaDdIQaDBgLBCDIRQENYYTIewRkEAwJCFHYicBOIkAEAhDBS4IAJ");
+katakana['TI'] = image(57, 54, "AAkGAwsfwAGE//gAocP//wBgn//gEBgIFBAAIeBAof/wAYBAwkHAof+gEDAwf4E4YAB4AGBv4TDAAM/AwoxDKQhABLQwiCAAV/MIglBMIglBHwRwDNARbF//3Awv7Awv9Awv+Awv/MQQAD34GF74GFKAUHOIYABSAJxGaYp4Uv54FP40/P4oGHQwQGKKgt/AwrUEMIQGEVYIGLg4bMFII+Fv5TGNAsPQgsHTIoAG");
+katakana['TU'] = image(54, 53, "AAMBwAGEj4FEgf8AYPwgFgn/4BIP/g+Av/ggEP/n/gP/4EAv/v/wQBFQP/z/4CAMAg/+DAMfEIICBDAN/FgN/8YYBBAIaBw4hDDQIVBAYMAn/wDAIhCCwIhDCwIBBwAIBHAIYBEIQYDBAIuBwAjBFQghCJgQhEAIIhDEYQPBh5HBM4IhDQQQhCwYeBCwMBCoSPB/0CIQQhBAQKWDvytBCYTBDv5tBZYYTCAAQTCAAYTFHAITEj4TF/4TEh4TFv4TEg//JgIMDMYIMEO4ImD/53BAAM/AwIsEEAgFBEAZNBIIgTCFocfJwo6BPgpHEgZAEgEOAogAGA==");
+katakana['TE'] = image(57, 51, "h//AAfwg4GE/kDAwn+gIGE/8AAwuAv4GE4E/Awngj4GFNWJNF/gGF/5UF/+/AwvfAwvvAwv3Awv7GJn8IQV/4BJEv59Fn/wAwkf/DJFEAYABg/+AwjJBAxbQBwAGFH4gGBH4gGIIwgGNG4IGEg//LYjyBAwiyBAxc/EQoGGFIJTLdYJvEgF+fIsYAwo=");
+katakana['TO'] = image(42, 54, "//AAgU/+AECh/8AgUD/4U/CgYPDn//wAUC/4VCCgIlDAgIKCCgIKCCgP//wUD//gCgQKCn/zBQQ+BDYP8CgMBEAQBBj4KBKYIKC54yBBQP7KYIKCG4QKB35YBBQIUCGQPjNAUD+BXDnB9Dgy8/CicAA=");
+katakana['MA'] = image(57, 50, "/4AE/l/A4s/AwvfAwoAN/YGF/oxGHokf/wGLh4GN/4GSg4GChgGDwARBAw3gAwv4Awo7BAwn/4ACBAwIKB+AGDgJtBAwcAUgOPAwYLB94GDgaFCAwTBDAwcfAwoyBAwgyBAwgyCAwgcBAwgyBNgL0ENgIADn6oHDijhFW4wcB4AGDKwPwBwl/fwzUJDgZOFgAGGngGFhADCA");
+katakana['MI'] = image(52, 53, "gPwAwkf/wFDgf///gAwU/AwIVCBgX//AME//8gEHAoQGCBgYGCv4GDFIMPBggoE4A2CCoIuCAweAAwc/BghYBMwswNw0PNwkBGAIbEG4gMCOoYMCOoQMDAwRnE4BYDKYQTEKYRuCKYY8GgCjDAAV+LAtgcTMDbYhTCHobICBwbBDBghZDZwmAZoYGCAogGBCYgiBEIidCBwQ2DS4QMCVYT2CSAb2DBoLpFn72EdJAA==");
+katakana['MU'] = image(59, 54, "AAMDwAHFv/AAwkf/gVF/4VG8AGEh4VHFgoVPFdZBdRogVBgP4CokBFogVBn/wTIkHEwYrCv4ODCoMP/wVDFIP/JYQVCBwgVBGYLICCoTIDCoQCBBwQhCn5RCCoR/DNoZCDDIRRDCoQODg4+CIQYvGCoZCCCoZRDAQV//4SBRAM//4ABwEfAgQAB/ARBAAkPAwvxAwv+Dgv/8YGF/gkD/xCB543DH4P5AoaBBewsAvgGFhgGFAAQ=");
+katakana['ME'] = image(55, 54, "AAcB8AGEgf/AwkP/wGEj/8Awk/+AGEv4iF//AFAuAAwcHFAsPFA34AYNwFAQvBgICCFAUHCAIoDDwQoDn4DBKIf/MYIoCDwIGB/5RBAwWDKIYGB456Dv//75RDAwP/JQQmBAwJ6Dj4GBOYYGCOYcP/5zEg//OYgGNDYw3BAwgvBAwaABAwgaBOARZC/wGDOoP8MQI1D+AGDFwPAAwJaBDAQNCJIc/AQJsBTYL3COQc/4ATBXoYdCSgU8J4SNCmCNCNQqoDAwQuBAwgFDFAITEAwK1DAAKZEAAIMFAA4=");
+katakana['MO'] = image(55, 49, "j//AAfAv4GFAon/wIGFgYFE/0HAwn8h4GE/AvF8A4Bv4DCAAQzBAocB/+AAwYxBCYkH/wGEh/8MIv4Awk/+AGEGyJfFAFP9AwpOBNuikeAwxfEHoLpFNoZACAwZABIgIACJYYABIAYGCIAYwCHIoABA=");
+katakana['NA'] = image(57, 55, "AAV/8AGEn/wAwkf/AGEh/8AwkH/wGEgf/AwkB/+AA4n/4A4rGoIAE/IGF/wGF/9/Awu/AwvfAwvvAwv3AwpQCOOqqEWLV/H4pGGn5GFAw0fJosfJooGGn4GGKgq6BLQoGEg4GFh4GFPoIpEDYIwFv5MFLQ4GFg6EFgaZFAAw");
+katakana['NI'] = image(56, 43, "h//AAf4A25+/AH4AuWggA5A=");
+katakana['NU'] = image(55, 51, "g//AAcAh4GFj4FD/0An4GD/kAv4GD/EADQnwgIGE8EDAwnAAwuAIIgvBAAcPF4IADn4vBAAd/8AGEFAIDBAQIsBFAMDCAIoDh4eBj4oCj4GBFAd/CIJRBgBZCAQIlD/+HQIIGD54oCNwZKDPQZPDOYRdDOYqmBOYi0BOYjCBBogGGYQSAEAwimDGATdDAwQTBH4JFBLIP8AwYTB+AqBAwITB4AGBE4bADBIJyBUIJ6CVgXgJAQzBg+BAoJkCgxcBCYRIEPArlEH4YGDO4ibBeQs+AokAsAGF");
+katakana['NE'] = image(61, 55, "AAX/4AGEg/+Bws/+AGEgP/wAHEh/8Cwt/8AGEgf/Bwsf/AMEAAYnBj4GDHwQOEDAMHA4hVBn4WFJIIADHwMPA4hgCAwZkFCQKCGBwpHBPQwOFFAJyGBwt/BwozBBwpwDGYiYEEgP+iAkF4IPDCoP8j7WCUAXhbwYVB/4RBU4n4QISfD54vBS4f+FASPD+AEB+AFB/IjBFIPnA4LzCGAfAeYIjBGAP4eYQCBwZuBeYUH/EfIwJRCAoIDBg6ACnCmDR4oqBDIKfEHgKuFS4g5CBwo8CWwqOCAAQ8DcYg8Vn48FAAo=");
+katakana['NO'] = image(47, 52, "AAcHAokP/gFDj/4Aod/+AFD//gAgUB//AAoUD/4oE/woJn4oLEQYoBwAoIh4oEj4oFJZ8HERU/EQhFEDgIiDH4JFDh4iEH4t/NAYcFHII/Dj4cEv4/DCwIcDCwIcDCwI5DCwhEBHIYQBKwf/GYYhBCwc/FoYKBFoYEBFoQKCE4RrBE4YFCHwQyBHAYnBJ4YFBcBN/AgcAPgYABA=");
+katakana['HA'] = image(62, 52, "AAP/wEH/gGCgf/gE/+AHCh4MB//AA4QMBCIQeD4ARCDwv4Dwt/8AeEgI4BDwkH/weFj4eEAgIeF8AeEAgQeEAgQeEAgQeEAgQeGMggeCMggeCQYiACQYYbCDwgbCIogbCIoZZDIoYTCMggTCEwn/CYJFDBYZFDBYYmDv4LBEwYDDg4aCh5JCDQYiDaIQWBNAQ5CMAYLDcgYmCCwgqCGIYTBFwL7EJIIWEAgPgh4WDNAPACwgMBCwiHB/wWEFwV/CwZVB/YWEDgPHXgYuBDwLbDKQPwh60CGwWAngGDgAFBkAHEsAFEAAQA==");
+katakana['HI'] = image(47, 51, "//AAgUB/+AAoUD/4QDg/+AocP/gFDj/4Aoc/+AFDv/gFw8BwIuDj+DFwf/FwcP/4uD///FwQKB/wuBJwIFBFwM/AoP8//PAgP/+IDCAAJdBAAXwg4FDEoQKCIIIgCLoQFBKYV//5qDB4aMuF1YFDFwIRDUIQAC+YFE8YFE44FEw4FEUgn+Aon8WwhKBXggA=");
+katakana['HU'] = image(49, 50, "/4AEv4FE34FE74FE94FE+4FE/YFE/oFE/w0Dg//AocD/+AAoUB//AI4ngAod/+AFDn4FEj/4Aon8AocPAokHHgg2BHhYFDHgJCLJBZCEAopIFAoxIEAoxOEApc/AojSBbwplEAoZxBAocPAojICBQhBCGYIFDBYRZCa4P/NYQuCPoYFBSoZGFZYsPAgYABA=");
+katakana['HE'] = image(61, 43, "AAMH8AHF/4HFh//wAOF/wOG/AHEv4eFg//DwoOBDwgOCDwk//YeEgf/x4eEn/8n4eDgP/4AeEj/8DAIeCBwPgLgkfDYIeECYQeDh4LBIwIeC//wDIIeCBYJdCDwV/BwIwBDwIOBCQYeBn4pCDwRIBIAQeCMIJPD/AOB4CED4BhBMwf/MISbD/kHPovwj4ODDwV/UYhYBKQJ2DRoIGDHQINEcARCCWYgGEDwIOFgb+FDwL2EDwQGFIQoeCBw0YA40AA==");
+katakana['HO'] = image(61, 54, "AAV/8AGEgf/Bwsf/AHF//AAwkH/wOFn/wAwkB/+AA4kP/g8Rg//AAngv4HFCYIAE/EfA4vAAwv+Eo3wn4HFwAGFJwZ5UgfAPIJzDn/x/+PEgR/BAoJzDP4N/8JzD//D/6KDFYI8BCwYrCCAItBPQOH/wWDCgIQBCwf/4P/wIWCCQIBDWgYBCZ4KJBE4LPDEYInBh5sBBgKLBNgQ0CJoIWB4ACCBgIiBBwP8EYU/TQLXBHQQECFAI8BCwIqB8DzCDYMPAgQbCMoI3BF4IRB44OBWwQUBv4TBJIV//InBHgQCBw4OBHgUH/EfNgKOCj0A3BsCQwNgeaSdCABA=");
+katakana['N'] = image(54, 50, "ggGFngFEgP+AwkPAws/AwkB/4GEh4GFn4Gaj///gNF/AGF4BEJAwITBgOAAwQTBh4GCnwJCCgVwLgRwMHAgTBHAgTGv4TEgYTFMIITEMAsHMBY0B+ClFCYiPFEAITEv//OIQMCTg3gBgggEDIIgDGYIgDMIJVDDAIABIIILCFoYYCJwZ0BHQgsBBgZnBBggnCKgYhBMIi3FgAFFgAA==");
+katakana['WA'] = image(51, 50, "/4Ay4A3E/AFCh4GBAoUBAoPgAwU///8AoUHBgOAD4nwAoUf//+AoUDGRYSBGQYSCGQd/94yDh/9GQZFB34yDn/zGQcPAgYSCG4YSBC4YSNv4SKJYJwDLwISEn5QDS4QSDDAJjDDAJ2DGIJ2DUYQ+DQYKcFFYYXBDASOCGIQFDGIQRCDwTaCG4YFBEgbHHN4hiFg6HEA=");
+katakana['WO'] = image(50, 52, "/4AE34FE94FE/YFE/wYYGocB/+AAwd/8AFDn/4AocP/gFDgf/KovADAnwDB43B45EE+IFE/F/KAkfBgmHAonhAonwDAn8h4MEN5X/N4l/N4k/KwkfRwgoBDwcHOohoBOoYFBEgY2BEgYFBEgYFBJIYXBFQYpBFQZ3CAoIWCKoQQCGwQLDHgR8CAoQdCAoQvCOYYFFn5gENgKREbYgAGA");
+katakana['RA'] = image(51, 50, "n//AAcHAongAon8j4GEwYFE+F/Aof+h4ME4IFE/BYr+4FE/wFE//fAon7BgpYE//vAon9CQo3Ev/gAocP/gFDgP/wASX+ASJgYSFXwJ2ECQivBDAoSEWIs//wFDbYIrDAoI+DAoIYDQ4IYCFIIABDALlDGIJhBewS/EJQQYCG4YkED4QFDD4JJF4AFDA");
+katakana['RI'] = image(43, 53, "AAf/7/4AgMf/f/AgMD/9/8AFBv/v/gEBh/9/+AgEB/+/+AKBn/3/wEBg/+//AFX4q3v4qDh/8FQQPBz4PDAYQvBEYQvCEYI/CGYRPBB4cfIYQpBB4cH/5TCDwJjD/4kCn4EBCgN/AgIUBDoP/FIJHBAAIyCDIYjBIYYaBQ4QaBJoZHDAAoA=");
+katakana['RU'] = image(61, 53, "AAUH/wHFn/wAgUB/+B/+AA4UP/gBBCgd/8ABBAwUD/4BBBwcf/ABBA4f/4ABBHQg8FHQI8/HksYHgwYBHgkPF4I8EvwlCHwOAg4gBEYI8CCIQjBHgITBCIP+HgU/CwIRBDAIgB4AMCAgMfEAIMBDAIOCBgQYCIwQMCPYJTBAQI8BBwUHEoN/8P/IYN/+AvBj4LBBwOAj/7BwZGB/4ABBwXAAQIODM4QOFHgIOC/4OBh4OCAYJGBv4OCn4OBHgJKBAYJkBIQISBaIYhCCwIOBSoTqBJQISBeYUHd4U+bYUwcAYAKA");
+katakana['RE'] = image(51, 51, "//AAocf/AFDgf/CQl/8AFDh/8AocB/+AAwc/+AFDg/+GX4ECgwyEgPgGQk+GQkP+IyDC4IyE//3GQc//gyDh//GQYYB8YyD//4GQc//wyDDAOBGQUH//gGQRvB/BlD/4DBGQU/CwIyCj4YBMoQkBBIIyBBAIYBGQIkBDAIDBGgIiD+AFBGoIyBv4eCGQIABJwQvBAAJnDEgTLCEgY8CIYLLDEgZVCAoZuBb4iaBfAj+EgE4AokAA");
+katakana['RO'] = image(50, 47, "/4AEn4FE94FE/YFE/wYF34YS4A1BgIYB+A8Cv/v/gFCj4YBAoUHDH4Y/DEbglDBQ8CAAYA==");
+katakana['YU'] = image(59, 46, "gP/AAX+A4M/A4fggEHAwf8BwIGD/4GBj4VFgYVGv4HDwEAh4GD+A+Eg46CAAf/4AGEj/4Coo6CCqJFBCot/KAIADh5QCQAhQBCrM/Myk/M3JQGh5QFMyIRBAH6NB");
+katakana['YO'] = image(50, 49, "v//AAefAonnAon5Aon+DDA1DgP/wA8E8AFDj/4AocHDFZjfDCJjxDD5WE/+/AonvAon7PgoYX/g3DAAQ");
+hiragana['A'] = image(52, 50, "gEB/wGEn/AAocD/gMcg//AAfgv4FD/wMYFIRNa54HDgYyCBgYsEBgX/+AGBHQYpBCQQaCh4JBJQPwgIdBBAP/wASB4H/j/8MIP8j5fBBIP/4P8gf+j/7/hVBj/jA4PH/C/Bn4RBv8Aj/3/Ef55FB/9/wI+D+/wj40BHwIWBL4QJB+BFBwAmB/4MBD4M/94MBD4JAB/4cBNYN/BgM//AsB/n/z4bBQgOHX4QVB/B3B/CQCAQTSC8BFCB4Q4CB4UAgIIBRQOAXojREn/gaIgAC");
+hiragana['I'] = image(58, 50, "v/gAgUggEf/AGCnkAg/+AwU/gEB/+AAwQZBDgcP/gcECQIcFCQIJCCol/4AGBgYLBj/wCokHCAIABFAIQCCon/DgQECn4cDCoItCAAI+BDggVCLoZeB+BgCCocPPQZUBwZdDJAQcEGAIcEGAIcEGQPDDghIBDggyBDggyBx4cBjxIC8aaCCAIyBLAMDM4IyBSARnC//HUIk/+IyBCASdBLAJKCGQOf/kDJQV/GQRKCJ4XgEYRPC/CoCDgOHNwl/8P/84jCDgM//5HCDgMHAwIjBgP8DwIsBQgYVBSQgVBaYZnCTIgtBbQhDCUAYkCfwYOCGIgAHA");
+hiragana['U'] = image(46, 50, "h//Aoc////8AFBAgIABgEDAofACwIAB/wWD//4CwgdBCIeAFQUfCwIADCwIAMj//+AEBv4tDAgQLBHAYFBAgf/8YFE54FECwRTB/wkCAoP7IAd/OgR2CKwcBQ4kH/hMEJYQcC4AWIh4WEn4tJg6EEj6EEVgIQDE4l/CAbABCAZqBBQgQDBQIQCXwIyCYYTIFeIhlCBQjxCLIQWBMgbdFvzYJ");
+hiragana['E'] = image(55, 50, "gF//4GE/4AB+AFBgIGC/+AgEDAwYNBg4FC/wGBh4GC/gGF/ArFFIQAD4BRVn42FLAIGEJQYGBLAhEBLAhEBLAf/8ArDBIIyEj5fCRYZYEEgJYEN4JNFDQouFDQKcBFwYGFMIIGDLQRJFAwgaBOYQuC8Y2DFwODAwcP/0HXAc//EPcQnAj5LCPAU/MwR4Cv5ECPAQ9CLoUBd4auE/guBVwf5PARaC+5qCAwXnJwSXB//HI4QGCw5ACAwUHNIn+gj/HAAg");
+hiragana['O'] = image(54, 50, "gEB/0AggGCg/4gE8AwUf8EA/gGCv+AB4QaDv/wDQn/CwIaCgP/4AaDgf/wAaCgPn/4PBAAXv/0HAwef/kfAoX+n/4v4GCAgPxCYfg/4jBAAWBGwQ1BgEDJoJQCJoJRBLYcPCAJrCgEcKAaGEHgSGDF4QPCJYYxCHoYMBn5YDBgoGBDIP8FQKiBDwabBFoIzCv/gEAJQCMwWfKAIbBh58BDQMH/l/4IaCh/xTgIaCn/P/BrD/8/4CGD/i3BDQfz/gaDv/P+AaCCAIaEHQQaDv/hGoV4h//g4VB8JnBa4ePZYRkBBwKNCbwPwCYR/C44CB4BtBfgSaD8ACBYQQWBAAYA==");
+hiragana['KA'] = image(55, 49, "gEH/AGEh/wAwkf8AGEn/AAwl/wEAhgGC/4CBngCBgP+AQP8AwMDAYIyDAYUPAwQ2CAwY2Cj/4gP/AAP4j/wgYGC/gGBg4GC/0/8EPAwsfCgd/4E/Awt/FIf/LgJmBE4IGCMwMf8JjBHwIPB4IDBgZmBv+DAYMHMwP/BQRfBOwIKCL4J2BOIQvBAgJxCGQIEBHAKPCCwIYDCwQBBQoRGBviIDIQJRC4AdCXAYdCKIcHboQ/CboY4BboghBboZKCFAYhBjAoDh/8nzME+CfBF4V/RgP/EgKVBwYGBFAMH/zIBFAQeBAwIoDboRRD4DrBJQUHAQJsDAAwA=");
+hiragana['KI'] = image(48, 50, "AAMB+AFDh4FL/AFDg4FIn//AAX4ArpHC/xNEAov/LQgFCDgYAlF4UfPx8/g/8CoQbBKgQhCAoMDFAkHAoeAh4FEDgQAB4E/FgIUBwE/HwQdBn/gAoM+AoPAAoMMAohFCAqIpCgI7C4BEBI4oICAoZfE4C9BAob2EAoISCaQgACA=");
+hiragana['KU'] = image(33, 45, "AAsB4ADC+ADC/wDBgf/wADMg//CYIDDh4DDD4UfAY/8AY34AZRDCh4DCg4DCgYbCgI/CgH/BgU/BgREBBgIQB8AMCFIRNDLoJ2Cv42DJwQdDFQIdDFQQdDFQIdDHYRkDgYhCgADDnwDChyzE");
+hiragana['KE'] = image(50, 49, "AAUB/0Ag/gAwN/wAICgEfBIIIBB4P4BAYPCh/wDAcD/gYE/4FBDAU/4AYEGIgOCDAQOBh//AAP+v+DAoX/7/AAof3+E/AoX9/gYD/9/gYFD/4YE/5QCGIJQDHYRvCJQU/N4JKCKAYYCKAQYWmAYEjwYEx6lDh/zUocDMgIYDv6cBKgUf/4yBBAMH/4eC4EBNQUfAQN/DYMPE4TjCAQQkCYgSJBDYLEBn7QCAQIbCE4UDDYP/PIV/CgLpD4EPP4UH+AkBAoIACCgIADh6LCAAMDAoYA==");
+hiragana['KO'] = image(52, 50, "h//AAX+gAFD//gBgn/BgvwBiWAAon4GwUBDIQACCQQFCn//4AFCg4lBCQc/DwYfBKQJdEDwYAB8CIihAFEgJJDIgQFEg5KEMgITEj/8D4hwED4JqEOIIfEv5eEg4fEFg0PHIwsEBigmFCYkOv65CJYPnbgn+ZgIAD8IMFewvgCYjRBE4IMDegQABIoUfAoK7HA==");
+hiragana['SA'] = image(51, 50, "AAMB/gFE/+AAwcf+AFDgf+DIl/4AFDg4fEgAfLgIfCj//AFQzCn/gLJYMELI5mEh6GGBgUHGAP4CAQ3COYILCBgUDIgYZBAoYmBn5REDwPgQQPgDAIVBj4fBJ4d+CQI1CgeAXhgSDKoYSEQQp1GQQpFBawXwD4IGBg42BaQngBgRlDBgmABgjzBRYZDCPIYvCv//MQoACA==");
+hiragana['SI'] = image(45, 50, "v/AAgUD/wKDj/wAof/wAECg/8BQc/8AbD/4bE/AbEFgcHFgk/FgcBFgkPDYhIgFgIKDFh8eFgn+FgcH/4sDv+/FgUD/osDn/vFgQ2BFgcf+YsD/+fFgUP/gsDv/HFgSKBLId/8IsCHgIXBSod/EIIKBwIhCv/4h4WBAQOAv/+IIP8AQIAC4AYBAAIkBn4KDJQIKDCwYpBCwRWCAoJhDAoK1DAAg=");
+hiragana['SU'] = image(52, 50, "AAUf8AFDgP+BjH/AYP/AAnvAon+BjJAUgf9BgZFB/4MDn4kEg4MFGIwMED4QME+E/+AyC/x0DFgPABwIMC/gMGDIn8gYMFv/4EwcP/+AKYf/BgRACBgYRB/4mCgF/AwJ6DBgoTCRohNDTZE/VAkP/gFDE4PAUQhGCI4YeEUIgYBD4gMBEpI4GgIFEAAo");
+hiragana['SE'] = image(56, 50, "AAcP/ADB//AAwP8AwkHA34FBAAn+A1JalmAGFvinFv4GF//PXghEBAwfBAwoNGEQP/+AGDn4GFh//8AGDg5PCgF/AYP/wAGEgj/CAwQADAw4mCAwZCCAAQ8BFQgGBAAQGBj4GFJQIGEJQIGEgYGFGIIGCIQQVDHQgACA");
+hiragana['SO'] = image(53, 50, "gP/AAXggEPAweAgF/AoX+gEDBgfwgEfCYoFD/EAg4MFAAQMCAAQwBBhQpBJQozBAAU/IAIACIYJUBAAV//gsJD4IsEn4sEOAn+NIn/+4FEAA39AwvvAwqQDAAP7UYhmCx5bDuBVB4BCDg5bEJ4JoEgJ1EEQKCESwIFEg5vEEA4TFh4TFv4TGYgiLBCYrFG/5dDd4YHCOQKkBDQjbDDQQwDWgR5DAwSGEEAgAEA==");
+hiragana['TA'] = image(52, 50, "gEP+AGE/4Mjgf/AAXAgE/AoX8BjUAgP+GYkf8AFDBhHnEIQMBEQQhBn/jFAWAgYMD/AMH/gMF4f/F4UH/kQGYd/KIIACg4VBBgmAQ4gMFUJcB/8DDQZgBv6iD/wuEn/gKIJGDEIl/4KCDC4KPE/+BBgYXBBgY5BAIImCj4MBTIKFB/wMBAAKSB8EPAwXnUYIMDCwLYD95RBEAIZCFQN/AwPBKISpBwEGQAgAGA==");
+hiragana['TI'] = image(51, 49, "gED/wGEv/AAocP/AFDgP/CQk/8AFDg/8Bgn/wAFDj/wBQYAqJ4M/LBZrMJYZ+Ch5aDv/f/4bCBQIABCoMDHAYTBv4+Ej4MEg4DB4IMCAoIcCwE/TwU/+ASBEQI8BVQJLCv/gS4cP/kBMgYWBjyoEgLbJEYYSCQQkHCQg2EHASCEv4SBgYOBOQ70BQoYrBEQIABFYR/DJASRED4YFCBgJDDA=");
+hiragana['TU'] = image(59, 45, "AAUP/4FFAAIGCAoX//EAg4GD//ACYYAB/kBAwgOBn4OFDgoOBAYX+BYP8j4GBwEAAgPDGwQ+C/F/BgIABCwOMLQl/+AGEg/+NIv/8BwF/gGEKwIqDAAM/HAYzDEhkfEgsDEgxJGh5JFHQPACqQrBCpkfCopXBCogcBCog5BK4jSCAwxtDDYK8EZIQcCAoQcDCYTjCJgQGCEYT0DIAYGGEgQGDEgRcEv5UEA=");
+hiragana['TE'] = image(57, 50, "/4AFv4GF34GF74GF94GF+4GF/YGF/oGF/w7Cn//4BCDAwOAAwpQEj4ZDAxP8AyUPAwwiFg4GMgZFFAw0BLQqlBNAkAv4GG8AGEn/wKgv4KhZGGHALeGH4oxNh4xFOJBjGEYt/VQwVFg//BwhOBAAI7Dv4GBHYYcBCwgcB/5CEDgQyFGYgrCUwkPKAwAC");
+hiragana['TO'] = image(46, 49, "gEH/AFDj/wAod/4AECgP/Cwn8C0cICwcDBoIWC/4NBCwMfEgV/4f/BoIWBv//LAMH/4AB8AWBAoWAgE/BQYlBDYUAh4FBHwQPEEIJQDFYJhCgYwCLQQqCDYQKDDYIKDn5xEEAYQB/x8JDYkDCAkPYIk/JoQWTAol/AocZQwR6B8aNCAAOPAgf+TIZqBAongT4QfCBYY9BW4R1BA=");
+hiragana['NA'] = image(55, 50, "AAd/wEAn4CBgH/BIXAgEB/wJEgf8AQIJCg/4AQIJBgEP+ACBBIMAj/gAQYsBEoIoCGwf/GwkB/8P/4AC4f+j4GDw/4n4GDj/wv4FC/0/8AMD/l/4IGD/H/wYGD+P/g4vELARtCMQRtDMQQKDL4YKCMQQKDMQQKDR4QKCTIYKCFYQ2bOoI2C4BgCGwWASAQ2BGQKJC8DNBBAIAB+DNBPYf4ZoKrDAgPwT4K7BAwRdBB4K3BVYIqCVYY6BAwKrB/0DVY3+v/hAwf8n4SBdIXwnxEBAwXgnBEBAwShBO4IbBSYSVCOYQAHA");
+hiragana['NI'] = image(57, 50, "AAMPwAGE//gAocf//wgFwgEH////kH/AZBAwP+gf+Bof/wP/gEDAwWAAIMBAwc/FgIGDj4sBv4GBE4P8HAIdBE4IqBAwYgBKAIGCKAYKBAwN/EYIGDn4jBAwZfBDAQfBLIPAAwZZBDgItENYN/CAIfBIAIGCLIRfDLIXwAwc/RQJmCHAPv/0PEoI4B+f/AwcH/P/w50D/l/wZ0CgP+j/BK4Q4Bg/gJoQ4BwIGBIwU/4EwAQI4CIYICCAYY/EJQMHHATcCbAQKEHARGBGgQqBCIc/D4IGDaITCDT4PAAQJfCQQRYDeQQGDSIIGEYYIGEE4IGEDgYFCcAQ+CGQZsCABAA=");
+hiragana['NU'] = image(58, 50, "gEP/AGEgf//wHE/4ABAwc/AwIPDh4OC8AGBg4GCEwUBAwX8Dod/EgoHC4AsF+BJFjAGDg4iEFgRfF/+AAwk/IwQjDFIgjDvAjDMYJlCgRHB4ABBFIUf/ABBFIXH/0HCoUf+BcBLwQpBCogpBCYIVDv+ACohNBn/wCoRxBCohNCMoIVBOIQVBAIJNCCAIVCEYIQBCoOAb4QtDCAQtC/gjCdIIXCN4QwBC4SVBDQIXBEYUP/gXBI4QEBHwPD/8ODgR/CwZNCCYN/8P/5/4GQOf+DtBKgXv/jtBKgX5/0PAwJxB/0/DAL8CvkDJYP/IYMMgFgg//fot/VYQACgYGFAAoA==");
+hiragana['NE'] = image(67, 45, "AAXwA43/4AHFn/8A4sPCA0B//+CAt///gA4kfCA0H/4QGA4IyFn4IBGQg5BIYsD//nCAt//F/CAkf/wzBCAYFBwH//BaE8ArBwBzFCAgNBLoQQCHIPADYIQD/6dBCAk/OQIQEHIQQEHIQkCCARaBO4YUCSYQQDHIQQFHIQQERQgQCLQQQEHIKBDCAPAn5fDCAP8gbNECAaJDCAbVECAPgvj+Gg72GdoqYFCAgHFKIoQDDA0AKIjODDA0ARYQAEhwHGAAIA==");
+hiragana['NO'] = image(54, 50, "h4GFn+AAocB/0IAwcH/F//4AB+Ef8IFC//A/+PAwcD/0fAoX8h/wDQk/4ITDAgMDAwcH/hGC/EAj/wIwXggF/4AGB/+AJIIFBGQJJCDQoWBDQf/wZlBDQIWBh41Dx5kE/0/Mgn4IgIGD8f8MgYaBL4IaEPQJrD/6RCGoRkCKAR/BKAgaBKAoaFNYoWCKIIaC8BKCDQWAIYQaCgJCCDQRyDDQRXDEoOBK4ahBW4K+CAgKcBDgLcBMwIwC/1/4JHBCYP5CoQwC4aND/atBRofDAgPgdQaSBHgX4hxXBHQXAhAOBAwKXCAAJlBbIIAH");
+hiragana['HA'] = image(50, 50, "AAMH/gFDgP/Bgl/4AFDj/wDBsH/4AD/oFE/9/AwoARJVXhAon4JQn+j4MEw4YLn4YEJTIfCAooYCAoX4DgQwCwBdEBgMDHoYMB//3Bgd/8AUC4A7BJQP//kHBwQGB4JYBFoX8KgMP/gGBz/+h//AIPjGAXA//wAoXwh/4DgX4gP8IgQnCF4QFBgOAEIKIEv6SCAA4A==");
+hiragana['HI'] = image(59, 50, "gP/AAOAA4U/AwPwAwUHAwP+CwYVC4AGCj4GB/AGCgYOCCod/AwPgGokH/g8GHQY8CHQYVCHQg8CwEfCAYEBgYQDAgV/JYYEBh5LDj/4GoJKEGoJLCAwP4JYZ9C/BLCNwSGDQgSGDOoaGDAwg6BEYQHDh//EomDAIP+ToaQBEIIvCKoJyCJgPH/yDCEIIVB4BNBMwIgB+CZCn/n4f+h5jBAQMw/+BOgKyCCoN/PIICBS4I0BCoQJBJQJqCBIP5NQfgD4KACn5tDGQSDEwADBTIJaBGQKZEDISvCToR8BeAQDBAQLbCb4RSCAAcHcQYACvwGFg45BAAj/DAAw=");
+hiragana['HU'] = image(55, 50, "gED/gGEg/4AwkP+EAhwGCj/ggF+AwU/4EB/wGCv+Ag4GD/4kBAwM//4AB84GBv4GC54GBAoX/x/+gIGDh/+gYFC/0P/kHAwX8AwMPAwX4j5cCGwJOBAwJIDj5jBv4QCAwIpBNoU/+AiBNoIGCJYJtBAwPhFwPANQXjAwOAgEEv+P/A2C/H+CoI2BTIIhBwY2Bh/xwH+UgUf+CwBUgSgBBYKkCn/gh/gToI1B4Ef4AvCBIM/4ZmCIAN/44oBSgKdCFAJ3CLAY0BUgQoBGgIGBEIUPAwSID+AGBQIZHBJQRECd4Q9DI4QvBJwQ2Cj4sBGATRBJwLcDFgTcDC4QGEEILqEAwIbDIARoCBgQAGA=");
+hiragana['HE'] = image(55, 50, "AAUf+AGEn/gAwl/4AECBQP/wAYC4EB/4YDwED/wYDwEH/gGCCIMP/AFBgIRBGwcDCIN/GwUH/EP/4bCDAP/AAI2C+4GCHwMfAoX/JgM/AwYjBv4GI8YGCFoN/wIGBgYCBFwIiBHYJfBNAPAn/8IwIGBwAaBh/wAwOD//4R4IfBg//+B2BDoJKB+AoBg/+JQPjOwMP/n/z/nQIMf/IOB76BBn/3/gVBMgN/94nBOQX/7/gAwKbBOwSOCHoJMCEIMH/v/CAJxBh/7/hcCF4X4KYLEC5/wj5KBEIOfGwJRCL4PzF4V/JIQvBCYJJCH4JxB4AGB/xCCFQIJDDoIMBBIRNBAQJdCIwKUCeAb5CPgQACSgIFDSgIFEAAg=");
+hiragana['HO'] = image(51, 50, "AAN+AokP+AFDgf+Bgl/4ASE/ASVv//AAX8h4FD/+BAonwn4FD/0HBgnAAogoBgP/HAk/8AFDg5LEgASM/gSFwADBFQIAC8E4Iof+/5FE5/wAof5/0fAwc/8YFD8f8PAYEB54MDJ4SRDJ4KRDj/gNYaoCLAYWBLAYWCLAQWCDYJvDgYSCCwV/NYQWBGQc/+AyDg4yBj4MBgYSBAQP4OwPwbIglBQAgpBBgZiBBgYYBBgY1CU4S0DFoIRCAAo=");
+hiragana['MA'] = image(55, 49, "gEP+AGEj/gAwk/4EAkAGCv+AgAPD/8AgYdCgP+EgkD/gdB/AGBg4DBv4GCj/w/wGCv////8AwQFB//4AwMBAwXwEQMDAwXgAwMHAwXAAwMPAwWAG4QvBLgQGBL4X/AwRfBKgIGCL4X8n/gLARUBn5YDMwM8NQaLBQYIoCAQSIDAQRZBRYaBDRYQhBFAIJCKIYyCDwKoBToZkBOAIJBPYKLCGwMH/h2CAwMfKoKKCI4PgSIYYB4afDJQMP/gpB+AhBMgIjB/AhC4EfAwIhCEoIGCwJdBaIIZBMgSkCjhMBgakBG4LICUgKDBAwQuBPgRKCjgGE4EQAwgEBAAIbBRAQACQgIDB");
+hiragana['MI'] = image(50, 50, "h+AAocD/gFDgP/CQl/4AFDn/gv//AAOP/E/AoXj/0HAoX4/+BAoX+DAuf+EfAoXn/gYD/P/gYEBG48f+AFDg5QMMYkf8BvE/BvE/wYE/4YEKAIYYgZSCDAMBJgQYCCgYDBFoYDBj4tCDAJlDDAMBGYYYBNYYYBn4xCg/4h6ECPgIHBPgfBDwaVBQgYvBToYYCFYauBaIIwB5/wcAfz/0PAoX8cAn/IgQFC55dBAoXxFILtC/grBGgL5BYIoAGA==");
+hiragana['MU'] = image(58, 50, "AAV/4AGEj/wAwkH/gGEgP/Aod+Dgv/wAcEj/gDgkH/AcEgP+Dgt/Dg3wn4mBHwYGBDAIyCAwP/8AGBAoQODh4GC/4sBgYGD/AcCAAO/IQQcC4IkCDgI7Bj5YBg//w/8EAIjCwIEBv/gMQPgLAMPFYP//h1BgZpC/4LCNwIxB4YoBFoIxB/AjBNIMH/v+n5UB/4qBn/fIoIJBv+PLYUPQwPhOIUD/gvBGYMH/3/BAX/457CBAP/84GBDgIlB/YGBCYJwB/qECDgKREwBCC34YBDgfvLYP+HIM/+YYCIwM/MoIYB/hGBMoQEBz4nBKQfDAwODGQXwKQQMB/P4j4GBAQP+ngtBUgIRBg6aBRwKiBwOAf4TNBAobjCAogAEA");
+hiragana['ME'] = image(57, 50, "gEP+AGEg/4AwkD/gGEgP+Dgv/Awt/wAGEn/Agf/BIUf8EP/40CHAMf/4tBAYP4AQImBCIP8n4GB4EH//+AwXgEwP/v4CB/EBAYIPBg4jBAwX8BYJFBCQRKDFYIGBJQJxBIgUfAQIrBAYMPCAIfBBQR8CAwR8DMAZ8Cv4GCGIQGDGIU/AwR8BAwKqCWoU/FoS1Cj4tCHASEBWogGBUAQKBAwItBHARpB8BlBBQKuCAQIKBO4SqCBQX8AwX4h/9/wGC/kP/n/DYSlCv+P/ArB4K+B4/4SIV+j/jWIX8n0P+JSBDoMOMwJWBAwOCMwM//ZOCMwI4C75nB/5bC45nBv+DAwPhTgXAb4PAoCfCQQifBYoYAHA");
+hiragana['MO'] = image(60, 50, "AAX//4GEv4HFj4GB/wGCg4GB//4AwMBAwX/4AcEDwcPAwYWBgYGDCwQVC54tCCoX8F4PgFYP4CYI+BgE//0P/gaB/ARB4F/4ApBwAVBg4OBj/8EgITB4AiB4InBBwQgBCAIOCPQPjD4MPJ4MH/0/+ALBwARB84kBBwQ0Bv/gBwc/+5bBj5tEHAR8Bn5lBBwInBBxY2CBwcDWIQOEGwIODJwIOFIoRKC4CNCBQP3AgKwCDIIOBKIQKB8/8IQJgBj4OB8E/MAfD/ytBEgX8J4KeBZwWDIgJCBCoP4ZgIzCAYIqBeYRQB8DnCK4gGBGoIDBwAyBF4IKCCQWBAwIVBEoPgF4RFBg/4F4Q2BAAQOBTwIADHoQADbIQAIA");
+hiragana['YA'] = image(54, 50, "gEf+AGEv/AAocB/4MEg/8DUv///Aj//wEDAwIcBAwMP//8BgIGBn//+IFBAwICB54GCDQQAC/0HAgXAn45BD4IDBn45Bv4MBAYPgGYJKCFAIbB8EAgf+DQRbEv/4LYYaBOQU/4EPCwIhCCYJrCgf8CYkP+BlBCYQaBv6GDOwQaECYIaEKwIaD4JWDgP+CYIaCg/4NQYTB8Z+BFwef+4aCMgN/74aCn/z/zXCIAOH/IaCh5CB44aBJoU+a4QyBwFwDQLGBCAOBX4adBGIJMBRIQaBUYI4CDQJnDFYJ7EDQKzCDQYECAA4");
+hiragana['YU'] = image(52, 49, "AAMf+AFDgP+Bgk/8AFDgYMM/gkD/4AC+EBAof/BkA5FhEAg45Cg/AgF/AQMBBIMP/4DB//gE4Xwn5dBn4GB74IBgY0Fv4FD8AfBAoYfB/gbBIAIiBg///A7B/+A/4rBCQIxBBAISB/ghBCQeBEoIMBCQI0BBgQSCDIYSB54MBgIlB+AMCj0H/0PBgIABHQQMBOgP4BgZBBBwTDCMYIMDKIIMRWQQmDAwUMYYqyBAoaxBN4IMEV4QMCcggMBWwbZCAweA");
+hiragana['YO'] = image(55, 50, "AAMHAwsP+AGEn/gAwl/4AFDgP/BgkD/whF/AGEj4oFEIsA/+AEIgoFg/8EIooFJQ3/JRcHJSgoGJQxEEg//FIkfAws/Cgv/AwUGJQX/HwMP8AoB74GBj/gh/+IoU/4BzBBQJBCJQIKBNQRzBv+AWoIIDJAP4SoMBIgIkBOYMDHoKTBAIIRBXgQBBB4IfBEIQYBFALgCCwMP/iVCJAXwJ4QfDcAX/4JRBSoRvBEIZ2DcAQGCFQIhBPoIYBcAQGBDAJqBCgQ6Bg7rIAAY=");
+hiragana['RA'] = image(48, 50, "gEP4AFDj//wAFE/gFE/4TCn4FBBgQFCBgQRC//gBgN/BYUP/EBAog3BGIIFCgH/BAIFCh4FEgQFEBoXwAqsfAoIuBAoROBEwIFBIwP+AoPnLIWALwZfBNQf/+AFE/AFBEIM/AoR6Bh/8OoIzBg4FBRgQFCL4UD/wlBAoikCAoM/W4QFBj5dCAoMGAohpDg4FEHYJ1EAog5DDgJWCb4Y/Cg7RDaARFCAoZFBAobiEeoruCAoQtCAoI+DAAgA=");
+hiragana['RI'] = image(40, 49, "ngEDn/AAg9/4Ef/AEBwF//4EBwP//4HBw4EB4F/x4EB8F/z4EB+H/n4EDAQIjBCwUPAgUAAgX+gEH/n//gEDHIMDAg3wAgP+AgvgAhBeBAhmAAiJ3BAhf8AgRUBAhBXBAAJtBAgSgCVgRcBAAJXCEwIEDj5SCBoJDCBAKSBBASSBXwKICAgQmCAgIcCv4SCAgI0DeAY=");
+hiragana['RU'] = image(51, 50, "gf/AAXAgF/AoX8gEPBgeAgIFD/EAn4MEg4FD8EACQoACn4lBAAUf/4FDDYOAAoQuBHwIACv/wDwgkEh/+DwoFDDw5ECDwRLDMwg5BLIZMBNgh/FGgIeB+AVB4AeBEYJmBBAJQBDgPBOocf/AoCVIU/Kwc/+5WDg/+Kwl/5/wh4mBh/4/A2CFgMOAoJDC8GBMgUHGAJQCCQKpCBgISBgf+SQMPCQN/4H/4YSBGIIwBCgMBDoTMCn/AEIROCLoKFEAIJvBTwZvCTAarFNIQFCXASyCYoYxBAoYAEA=");
+hiragana['RE'] = image(56, 50, "gEf8AGF+AGigP/wAGDg//GYQGBh//C4M/AYICB/AGDv///gGC+P/AwQKB+YGB/wNC+//w4GDBYMDAwn4AwQ3BFQIGF8AGF4AGFgAGEAYMDHwIGBAYIGDn5XBAwhlBAwd/Axh6CAwSPBAwMHAxEDAwqdBAwidDAw5IBOoQGDU4QGDUAIGE//fAwufCgrmCh4iCAwk4nwGE/EcAwbSBjAGFegReCUgIGJOYIUEQIYGCIYOAAwPgAwIAIA=");
+hiragana['RO'] = image(50, 50, "AAf4gEB/4AC8EAv4FC/kAj4MDwEHAofwDAgSBDAoACn/+AocfAokP/4FDE4OAApED//AAohJBAAI5BAocAIQIFEHghFCD4QFCBoU/KIQMBNQZ9BOAhOCQYYFE/B8CE4QFBM4JGB4YuDj/7AocD/xIE/+fP4c/84FDh/8QoZyBj5mE4aFDn5yEDAIFDGIIFDIgIXDDwKREv4eEv4eBiAFCDwMH+A8BIQLnEEgLnDSooqBQYQFCDgQ2DAoolCJAgAD");
+hiragana['WA'] = image(51, 50, "AAV/4AFDh/4AocB/4DBj/ggE/AQMD/0Ag/8DgWAgH/AQMP+ASB//AgISBAoIDC4Ef///+ASBh4FB/4SBgYFC+E/4IFC/8H/F///9//g/8f/3/x/+j/nAQPwv/j/H/wf+I4N/KAJlBv+P9/4MoMP/f9/xlBAIIqBwAUBn/vFwIdBg40BNIIOBIIR7B+BbC8B7BKoX4uAyCAwM+GQX5//f8IyCn/z/hHCK4N/4/8h/8/4EB/4lBF4P/z5wB8f+RYJjBPoPAFwO/BQP4IQX/wJkCTAUfVYf4gf4BgS4BbQRiCcgbSCAAILEcALkCAAM/DoYeCC4ZLBfoIeD/ASEDAhoBAoYlBDwcAg/ABggAEA=");
+hiragana['N'] = image(54, 50, "AAVgAYUP8EHwAGCv/Av4RD/8D/wFCgf8g/8DQf4j/4AwU/8E/+AaDwF//4VBgIfB/4GCD4MPAwcf+YFB/4jBn4FC/4jBAof/4AYC//n/+DBYeD/wZC/f/FgIrCGIQsCKYU/444CKYP/z4xCvxOBv+/8EBQQP4B4KFCCoJeCNIYPBQgQKBj53CAYSbBCYQDBHgJbCTYUDOQZHBM4QTBTYX/GQQxBP4Y8BDQRGBTYY4Eh5MDHgZTDAojdEbAYGEHgIGEv7/DHgIhFfAh1EEIg8GEIg8GTYYhDHhYAF");
+/// /////////////////////////////////////////
+
+let kana = katakana.KA;
+let scroll = 0;
+
+let hiramode = false;
+let curkana = 'KA';
+function next () {
+ let found = false;
+ for (const k of Object.keys(katakana).sort()) {
+ if (found) {
+ kana = hiramode ? hiragana[k] : katakana[k];
+ curkana = k;
+ return;
+ }
+ if (curkana === k) {
+ found = true;
+ }
+ }
+ curkana = 'KA';
+ kana = hiramode ? hiragana[curkana] : katakana[curkana];
+ updateWatch(ohhmm);
+}
+
+function randKana() {
+ try {
+ const keys = Object.keys(katakana);
+ const total = keys.length;
+ let index = 0 | (Math.random() * total);
+ curkana = keys[index];
+ kana = hiramode ? hiragana[curkana] : katakana[curkana];
+ } catch (e) {
+ randKana();
+ }
+}
+
+function prev () {
+ let oldk = '';
+ let count = 0;
+ for (const k of Object.keys(katakana).sort()) {
+ if (curkana === k) {
+ if (count > 0) {
+ curkana = oldk;
+ kana = katakana[curkana];
+ return;
+ }
+ }
+ oldk = k;
+ count++;
+ }
+ curkana = oldk;
+ kana = katakana[curkana];
+ updateWatch(ohhmm);
+}
+
+const kanacolors = {
+ A: []
+};
+
+
+function updateWatch (hhmm) {
+ g.setFontAlign(-1, -1, 0);
+ g.setBgColor(0, 0, 0);
+ g.setColor(0, 0, 0);
+ var whitecolor = false;
+ if (curkana.indexOf('A') != -1) {
+ g.setColor(1, 0, 0);
+ whitecolor = true;
+ } else if (curkana.indexOf('I') != -1) {
+ g.setColor(0, 1, 0);
+ } else if (curkana.indexOf('U') != -1) {
+ g.setColor(0, 0, 1);
+ whitecolor = true;
+ } else if (curkana.indexOf('E') != -1) {
+ g.setColor(1, 1, 0);
+ } else {
+ g.setColor(0, 1, 1);
+ }
+ g.fillRect(0, 0, w, h);
+
+ g.setFont('Vector', 50);
+ if (whitecolor) {
+ g.setColor(0, 0, 0);
+ } else {
+ g.setColor(0.5, 0.5, 0.5);
+ }
+ x = 26;
+ y = h - 42;
+ g.drawString(hhmm, x - 3, y - 3);
+ if (whitecolor) {
+ g.setColor(1, 1, 1);
+ } else {
+ g.setColor(0, 0, 0);
+ }
+ g.drawString(hhmm, x, y - 1);
+
+ drawKana(4 + (g.getWidth() / 6), 60);
+ drawMonthDay();
+ Bangle.drawWidgets();
+}
+
+function drawMonthDay() {
+ g.setFont('Vector', 20);
+ g.setColor(1,1,1);
+ g.setFontAlign(-1, -1, 0);
+ g.drawString(month, 4, 112);
+ g.setFontAlign(1, -1, 0);
+ g.drawString(day, w, 112);
+}
+
+function getPhoneme(k) {
+ switch (k) {
+ case "TU": return "TSU";
+ case "TI": return "CHI";
+ case "SI": return "SHI";
+ case "HU": return "FU";
+ }
+ return k;
+}
+
+function drawKana (x, y) {
+ g.setColor(0, 0, 0);
+ g.fillRect(0, 0, g.getWidth(), 6 * (h / 8) + 1);
+ g.setColor(1, 1, 1);
+ g.drawImage(kana, x + 20, 40, { scale: 1.6 });
+ g.setColor(1, 1, 1);
+ g.setFont('Vector', 24);
+ g.drawString(getPhoneme(curkana), 4, 32);
+ g.drawString(hiramode ? 'H' : 'K', w - 20, 32);
+}
+
+var ohhmm = '';
+
+function tickWatch () {
+ const now = Date();
+ month = now.getMonth() + 1;
+ day = now.getDate();
+ function zpad (n) {
+ return (n < 10) ? '0' + n : n;
+ }
+ const hhmm = zpad(now.getHours()) + ':' + zpad(now.getMinutes());
+ if (hhmm !== ohhmm) {
+ randKana();
+ updateWatch(hhmm);
+ ohhmm = hhmm;
+ }
+}
+
+Bangle.on('touch', function (tap, top) {
+ if (top.x < w / 4) {
+ prev();
+ } else if (top.x > (w - (w / 4))) {
+ next();
+ } else {
+ hiramode = !hiramode;
+ }
+ kana = hiramode ? hiragana[curkana] : katakana[curkana];
+ updateWatch(ohhmm);
+});
+
+g.clear(true);
+// show launcher when button pressed
+Bangle.setUI('clock');
+Bangle.loadWidgets();
+tickWatch();
+setInterval(tickWatch, 1000 * 60);
+
+
diff --git a/apps/kanawatch/app.png b/apps/kanawatch/app.png
new file mode 100644
index 000000000..cf081937b
Binary files /dev/null and b/apps/kanawatch/app.png differ
diff --git a/apps/kanawatch/fontmaker.zip b/apps/kanawatch/fontmaker.zip
new file mode 100644
index 000000000..39c7d5d53
Binary files /dev/null and b/apps/kanawatch/fontmaker.zip differ
diff --git a/apps/kanawatch/metadata.json b/apps/kanawatch/metadata.json
new file mode 100644
index 000000000..b14703979
--- /dev/null
+++ b/apps/kanawatch/metadata.json
@@ -0,0 +1,31 @@
+{
+ "id": "kanawatch",
+ "name": "Kanawatch",
+ "shortName": "Kanawatch",
+ "version": "0.05",
+ "type": "clock",
+ "description": "Learn Hiragana and Katakana",
+ "icon": "app.png",
+ "allow_emulator": true,
+ "tags": "clock",
+ "supports": [
+ "BANGLEJS2"
+ ],
+ "readme": "README.md",
+ "storage": [
+ {
+ "name": "kanawatch.app.js",
+ "url": "app.js"
+ },
+ {
+ "name": "kanawatch.img",
+ "url": "app-icon.js",
+ "evaluate": true
+ }
+ ],
+ "screenshots": [
+ {
+ "url": "screenshot.png"
+ }
+ ]
+}
diff --git a/apps/kanawatch/screenshot.png b/apps/kanawatch/screenshot.png
new file mode 100644
index 000000000..b1ed879aa
Binary files /dev/null and b/apps/kanawatch/screenshot.png differ
diff --git a/apps/kbmorse/ChangeLog b/apps/kbmorse/ChangeLog
index f62348ec8..c85361374 100644
--- a/apps/kbmorse/ChangeLog
+++ b/apps/kbmorse/ChangeLog
@@ -1 +1,2 @@
-0.01: New Keyboard!
\ No newline at end of file
+0.01: New Keyboard!
+0.02: Temporarily fix because of firmware bug.
diff --git a/apps/kbmorse/lib.js b/apps/kbmorse/lib.js
index 8bc177a46..997f2cb16 100644
--- a/apps/kbmorse/lib.js
+++ b/apps/kbmorse/lib.js
@@ -82,6 +82,36 @@ exports.input = function(options) {
}
return new Promise((resolve, reject) => {
+ const Layout = require("Layout");
+ let layout = new Layout({
+ type: "h", c: [
+ {
+ type: "v", width: Bangle.appRect.w-8, bgCol: g.theme.bg, c: [
+ {id: "dots", type: "txt", font: "6x8:2", label: "", fillx: 1, bgCol: g.theme.bg},
+ {filly: 1, bgCol: g.theme.bg},
+ {
+ type: "h", fillx: 1, c: [
+ {id: "del", type: "txt", font: "6x8", label: "
+ ({type: "txt", font: "6x8", height: Math.floor(Bangle.appRect.h/3), r: 1, label: l})
+ )
+ }
+ ]
+ });
function update() {
let dots = [], dashes = [];
@@ -157,36 +187,6 @@ exports.input = function(options) {
}
}
- const Layout = require("Layout");
- let layout = new Layout({
- type: "h", c: [
- {
- type: "v", width: Bangle.appRect.w-8, bgCol: g.theme.bg, c: [
- {id: "dots", type: "txt", font: "6x8:2", label: "", fillx: 1, bgCol: g.theme.bg},
- {filly: 1, bgCol: g.theme.bg},
- {
- type: "h", fillx: 1, c: [
- {id: "del", type: "txt", font: "6x8", label: "
- ({type: "txt", font: "6x8", height: Math.floor(Bangle.appRect.h/3), r: 1, label: l})
- )
- }
- ]
- });
g.reset().clear();
update();
@@ -244,4 +244,4 @@ exports.input = function(options) {
};
Bangle.on("swipe", Bangle.swipeHandler);
});
-};
\ No newline at end of file
+};
diff --git a/apps/kbmorse/metadata.json b/apps/kbmorse/metadata.json
index f9c5354f1..9111d514d 100644
--- a/apps/kbmorse/metadata.json
+++ b/apps/kbmorse/metadata.json
@@ -1,7 +1,7 @@
{
"id": "kbmorse",
"name": "Morse keyboard",
- "version": "0.01",
+ "version": "0.02",
"description": "A library for text input as morse code",
"icon": "app.png",
"type": "textinput",
diff --git a/apps/kbmulti/ChangeLog b/apps/kbmulti/ChangeLog
index 709aa3203..19739fa64 100644
--- a/apps/kbmulti/ChangeLog
+++ b/apps/kbmulti/ChangeLog
@@ -1,2 +1,4 @@
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.
+0.03: Use default Bangle formatter for booleans
+0.04: Allow moving the cursor
diff --git a/apps/kbmulti/README.md b/apps/kbmulti/README.md
index 4c83d378e..80b2b077a 100644
--- a/apps/kbmulti/README.md
+++ b/apps/kbmulti/README.md
@@ -2,7 +2,7 @@
A library that provides the ability to input text in a style familiar to anyone who had a mobile phone before they went all touchscreen.
-Swipe right for Space, left for Backspace, and up/down for Caps lock. Tap the '?' button in the app if you need a reminder!
+Swipe right for Space, left for Backspace, down for cursor moving mode, and up for Caps lock. Swipe left and right to move the cursor in moving mode. Tap the '?' button in the app if you need a reminder!
At time of writing, only the [Noteify app](http://microco.sm/out/Ffe9i) uses a keyboard.
diff --git a/apps/kbmulti/lib.js b/apps/kbmulti/lib.js
index 5ccab4204..aa54dab9c 100644
--- a/apps/kbmulti/lib.js
+++ b/apps/kbmulti/lib.js
@@ -17,18 +17,50 @@ 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: Caps lock\nDown:Move mode';
var charTimeout; // timeout after a key is pressed
var charCurrent; // current character (index in letters)
var charIndex; // index in letters[charCurrent]
+ var textIndex = text.length;
+ var textWidth = settings.showHelpBtn ? 10 : 14;
var caps = true;
var layout;
- var btnWidth = g.getWidth()/3
+ var btnWidth = g.getWidth()/3;
+
+ function getMoveChar(){
+ return "\x00\x0B\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00@\x1F\xE1\x00\x10\x00\x10\x01\x0F\xF0\x04\x01\x00";
+ }
+
+ function getMoreChar(){
+ return "\x00\x0B\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xDB\x1B`\x00\x00\x00";
+ }
+
+
+ function getCursorChar(){
+ return "\x00\x0B\x11\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xAA\xAA\x80"; }
function displayText(hideMarker) {
layout.clear(layout.text);
- layout.text.label = text.slice(settings.showHelpBtn ? -11 : -13) + (hideMarker ? " " : "_");
+
+ let charsBeforeCursor = textIndex;
+ let charsAfterCursor = Math.min(text.length - textIndex, (textWidth)/2);
+
+
+ let start = textIndex - Math.ceil(textWidth - charsAfterCursor);
+ let startMore = false;
+ if (start > 0) {start++; startMore = true}
+ if (start < 0) start = 0;
+ let cursor = textIndex + 1;
+
+ let end = cursor + Math.floor(start + textWidth - cursor);
+ if (end <= text.length) {end--; if (startMore) end--;}
+ if (end > text.length) end = text.length;
+
+ let pre = (start > 0 ? getMoreChar() : "") + text.slice(start, cursor);
+ let post = text.slice(cursor, end) + (end < text.length - 1 ? getMoreChar() : "");
+
+ layout.text.label = pre + (hideMarker ? " " : (moveMode? getMoveChar():getCursorChar())) + post;
layout.render(layout.text);
}
@@ -41,8 +73,11 @@ exports.input = function(options) {
function backspace() {
deactivateTimeout(charTimeout);
- text = text.slice(0, -1);
- newCharacter();
+ if (textIndex > -1){
+ text = text.slice(0, textIndex) + text.slice(textIndex + 1);
+ if (textIndex > -1) textIndex --;
+ newCharacter();
+ }
}
function setCaps() {
@@ -55,6 +90,7 @@ exports.input = function(options) {
function newCharacter(ch) {
displayText();
+ if (ch && textIndex < text.length) textIndex ++;
charCurrent = ch;
charIndex = 0;
}
@@ -69,7 +105,11 @@ exports.input = function(options) {
newCharacter(key);
}
var newLetter = letters[charCurrent][charIndex];
- text += (caps ? newLetter.toUpperCase() : newLetter.toLowerCase());
+ let pre = text.slice(0, textIndex);
+ let post = text.slice(textIndex, text.length);
+
+ text = pre + (caps ? newLetter.toUpperCase() : newLetter.toLowerCase()) + post;
+
// set a timeout
charTimeout = setTimeout(function() {
charTimeout = undefined;
@@ -78,14 +118,29 @@ exports.input = function(options) {
displayText(charTimeout);
}
+ var moveMode = false;
+
function onSwipe(dirLeftRight, dirUpDown) {
- if (dirUpDown) {
+ if (dirUpDown == -1) {
setCaps();
+ } else if (dirUpDown == 1) {
+ moveMode = !moveMode;
+ displayText(false);
} else if (dirLeftRight == 1) {
- text += ' ';
- newCharacter();
+ if (!moveMode){
+ text = text.slice(0, textIndex + 1) + " " + text.slice(++textIndex);
+ newCharacter();
+ } else {
+ if (textIndex < text.length) textIndex++;
+ displayText(false);
+ }
} else if (dirLeftRight == -1) {
- backspace();
+ if (!moveMode){
+ backspace();
+ } else {
+ if (textIndex > -1) textIndex--;
+ displayText(false);
+ }
}
}
diff --git a/apps/kbmulti/metadata.json b/apps/kbmulti/metadata.json
index 1efdb8847..a1f6ffa81 100644
--- a/apps/kbmulti/metadata.json
+++ b/apps/kbmulti/metadata.json
@@ -1,6 +1,6 @@
{ "id": "kbmulti",
"name": "Multitap keyboard",
- "version":"0.02",
+ "version":"0.04",
"description": "A library for text input via multitap/T9 style keypad",
"icon": "app.png",
"type":"textinput",
diff --git a/apps/kbmulti/settings.js b/apps/kbmulti/settings.js
index 8a66cd8f0..96e72b290 100644
--- a/apps/kbmulti/settings.js
+++ b/apps/kbmulti/settings.js
@@ -23,7 +23,6 @@
},
/*LANG*/'Show help button?': {
value: !!settings().showHelpBtn,
- format: v => v?"Yes":"No",
onchange: v => updateSetting("showHelpBtn", v)
}
};
diff --git a/apps/kbtouch/metadata.json b/apps/kbtouch/metadata.json
index f6d6d5228..89d121d63 100644
--- a/apps/kbtouch/metadata.json
+++ b/apps/kbtouch/metadata.json
@@ -6,10 +6,11 @@
"type":"textinput",
"tags": "keyboard",
"supports" : ["BANGLEJS2"],
- "screenshots": [{"url":"screenshot.png"}],
+ "screenshots": [{"url":"screenshot.png"}],
"readme": "README.md",
"storage": [
{"name":"textinput","url":"lib.js"},
{"name":"kbtouch.settings.js","url":"settings.js"}
- ]
+ ],
+ "sortorder":-1
}
diff --git a/apps/kitchen/ChangeLog b/apps/kitchen/ChangeLog
index 3767a9548..4e8c49c50 100644
--- a/apps/kitchen/ChangeLog
+++ b/apps/kitchen/ChangeLog
@@ -11,3 +11,4 @@
0.11: Detect when waypoints.json is not present, error E-WPT
0.12: Added stepo2 as a replacement for stepo and digi
0.13: Added long press BTN2 toggle gpsrec status in GPS clock
+0.14: Move waypoints.json (and editor) to 'waypoints' app
diff --git a/apps/kitchen/README.md b/apps/kitchen/README.md
index 102881d15..3049d9c6d 100644
--- a/apps/kitchen/README.md
+++ b/apps/kitchen/README.md
@@ -60,7 +60,7 @@ The following buttons depend on which face is currently in use

- now replaced by Stepo2 but still available if you install manually
-- Requires one of the pedominter widgets to be installed
+- Requires one of the pedominter widgets to be installed
- Displays the time in large font
- Display current step count in a doughnut gauge
- Show step count in the middle of the doughnut gauge
@@ -208,14 +208,8 @@ which will obviously limit this.
### Waypoint Editor
-Clicking on the download icon of gpsnav in the app loader invokes the
-waypoint editor. The editor downloads and displays the current
-`waypoints.json` file. Clicking the `Edit` button beside an entry
-causes the entry to be deleted from the list and displayed in the
-edit boxes. It can be restored - by clicking the `Add waypoint`
-button. A new markable entry is created by using the `Add name`
-button. The edited `waypoints.json` file is uploaded to the Bangle by
-clicking the `Upload` button.
+Clicking on the download icon of `Waypoints` in the app loader invokes the
+waypoint editor. See the `Waypoints` app for more information.
### Calibration of the Compass
diff --git a/apps/kitchen/kitchen.app.js b/apps/kitchen/kitchen.app.js
index 5564b2807..2c2cebaef 100644
--- a/apps/kitchen/kitchen.app.js
+++ b/apps/kitchen/kitchen.app.js
@@ -23,7 +23,7 @@ function nextFace(){
iface += 1
iface = iface % FACES.length;
face = FACES[iface]();
-
+
g.clear();
g.reset();
face.init(gpsObj, swObj, hrmObj, tripObject);
@@ -64,7 +64,7 @@ function buttonReleased(btn) {
clearInterval(pressTimer);
pressTimer = undefined;
}
-
+
if ( dur >= 1.5 ) {
switch(btn) {
case 1:
@@ -165,11 +165,11 @@ GPS.prototype.getLastFix = function() {
GPS.prototype.determineGPSState = function() {
this.log_debug("determineGPSState");
gpsPowerState = Bangle.isGPSOn();
-
+
//this.log_debug("last_fix.fix " + this.last_fix.fix);
//this.log_debug("gpsPowerState " + this.gpsPowerState);
//this.log_debug("last_fix.satellites " + this.last_fix.satellites);
-
+
if (!gpsPowerState) {
this.gpsState = this.GPS_OFF;
this.resetLastFix();
@@ -178,9 +178,9 @@ GPS.prototype.determineGPSState = function() {
} else {
this.gpsState = this.GPS_SATS;
}
-
+
this.log_debug("gpsState=" + this.gpsState);
-
+
if (this.gpsState !== this.GPS_OFF) {
if (this.listenerCount === 0) {
Bangle.on('GPS', processFix);
@@ -196,9 +196,9 @@ GPS.prototype.determineGPSState = function() {
}
};
-GPS.prototype.getGPSTime = function() {
+GPS.prototype.getGPSTime = function() {
var time;
-
+
if (this.last_fix !== undefined && this.last_fix.time !== undefined && this.last_fix.time.toUTCString !== undefined &&
(this.gpsState == this.GPS_SATS || this.gpsState == this.GPS_RUNNING)) {
time = this.last_fix.time.toUTCString().split(" ");
@@ -216,7 +216,7 @@ GPS.prototype.toggleGPSPower = function() {
this.gpsPowerState = Bangle.isGPSOn();
this.gpsPowerState = !this.gpsPowerState;
Bangle.setGPSPower((this.gpsPowerState ? 1 : 0), 'kitchen');
-
+
this.resetLastFix();
this.determineGPSState();
@@ -247,11 +247,11 @@ GPS.prototype.processFix = function(fix) {
//this.log_debug("GPS:processFix()");
//this.log_debug(fix);
this.last_fix.time = fix.time;
-
+
if (this.gpsState == this.GPS_TIME) {
this.gpsState = this.GPS_SATS;
}
-
+
if (fix.fix) {
//this.log_debug("Got fix - setting state to GPS_RUNNING");
this.gpsState = this.GPS_RUNNING;
@@ -271,10 +271,10 @@ GPS.prototype.formatTime = function(now) {
GPS.prototype.timeSince = function(t) {
var hms = t.split(":");
var now = new Date();
-
+
var sn = 3600*(now.getHours()) + 60*(now.getMinutes()) + 1*(now.getSeconds());
var st = 3600*(hms[0]) + 60*(hms[1]) + 1*(hms[2]);
-
+
return (sn - st);
};
@@ -313,7 +313,7 @@ GPS.prototype.getWPdistance = function() {
GPS.prototype.getWPbearing = function() {
//log_debug(this.last_fix);
//log_debug(this.wp_current);
-
+
if (this.wp_current.name === "E-WPT" || this.wp_current.name === "NONE" || this.wp_current.lat === undefined || this.wp_current.lat === 0)
return 0;
else
@@ -321,7 +321,7 @@ GPS.prototype.getWPbearing = function() {
}
GPS.prototype.loadFirstWaypoint = function() {
- var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}];
+ var waypoints = require("waypoints").load();
this.wp_index = 0;
this.wp_current = waypoints[this.wp_index];
log_debug(this.wp_current);
@@ -345,10 +345,10 @@ GPS.prototype.markWaypoint = function() {
return;
log_debug("GPS::markWaypoint()");
-
- var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}];
+
+ var waypoints = require("waypoints").load();
this.wp_current = waypoints[this.wp_index];
-
+
if (this.waypointHasLocation()) {
waypoints[this.wp_index] = {name:this.wp_current.name, lat:0, lon:0};
} else {
@@ -356,12 +356,12 @@ GPS.prototype.markWaypoint = function() {
}
this.wp_current = waypoints[this.wp_index];
- require("Storage").writeJSON("waypoints.json", waypoints);
+ require("waypoints").save(waypoints);
log_debug("GPS::markWaypoint() written");
}
GPS.prototype.nextWaypoint = function(inc) {
- var waypoints = require("Storage").readJSON("waypoints.json")||[{name:"E-WPT"}];
+ var waypoints = require("waypoints").load();
this.wp_index+=inc;
if (this.wp_index>=waypoints.length) this.wp_index=0;
if (this.wp_index<0) this.wp_index = waypoints.length-1;
@@ -520,7 +520,7 @@ function STOPWATCH() {
this.redrawLaps = true;
this.redrawTime = true;
}
-
+
STOPWATCH.prototype.log_debug = function(o) {
//console.log(o);
}
@@ -531,7 +531,7 @@ STOPWATCH.prototype.timeToText = function(t) {
let secs = Math.floor(t/1000)%60;
let text;
- if (hrs === 0)
+ if (hrs === 0)
text = ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
else
text = (""+hrs) + ":" + ("0"+mins).substr(-2) + ":" + ("0"+secs).substr(-2);
@@ -551,7 +551,7 @@ STOPWATCH.prototype.stopStart = function() {
if (this.running)
this.tStart = Date.now() + this.tStart - this.tCurrent;
-
+
this.tTotal = Date.now() + this.tTotal - this.tCurrent;
this.tCurrent = Date.now();
this.redrawButtons = true;
@@ -623,7 +623,7 @@ STOPWATCH.prototype.drawLaptimes = function() {
g.setFont("Vector",24);
g.setFontAlign(-1,-1);
g.clearRect(4, 205, 239, 229); // clear the last line of the lap times
-
+
let laps = 0;
for (let i in this.lapTimes) {
g.drawString(this.lapTimes.length-i + ": " + this.timeToText(this.lapTimes[i]), 4, this.timeY + 40 + i*24);
@@ -645,7 +645,7 @@ STOPWATCH.prototype.drawTime = function() {
g.setFont("Vector",38);
g.setFontAlign(0,0);
g.clearRect(0, this.timeY-21, 200, this.timeY+21);
- g.setColor(0xFFC0);
+ g.setColor(0xFFC0);
g.drawString(txtTotal, xTotal, this.timeY);
// current lap time
@@ -691,7 +691,7 @@ function HRM() {
this.bpm = 0;
this.confidence = 0;
}
-
+
HRM.prototype.log_debug = function(o) {
//console.log(o);
}
@@ -782,7 +782,7 @@ Debug Object
function DEBUG() {
this.logfile = require("Storage").open("debug.log","a");
}
-
+
DEBUG.prototype.log = function(msg) {
let timestamp = new Date().toString().split(" ")[4];
let line = timestamp + ", " + msg + "\n";
diff --git a/apps/kitchen/metadata.json b/apps/kitchen/metadata.json
index ab2e7183c..9c9f7b2ec 100644
--- a/apps/kitchen/metadata.json
+++ b/apps/kitchen/metadata.json
@@ -1,14 +1,14 @@
{
"id": "kitchen",
"name": "Kitchen Combo",
- "version": "0.13",
+ "version": "0.14",
"description": "Combination of the Stepo, Walkersclock, Arrow and Waypointer apps into a multiclock format. 'Everything but the kitchen sink'",
"icon": "kitchen.png",
"type": "clock",
"tags": "tool,outdoors,gps",
"supports": ["BANGLEJS"],
"readme": "README.md",
- "interface": "waypoints.html",
+ "dependencies" : { "waypoints":"type" },
"storage": [
{"name":"kitchen.app.js","url":"kitchen.app.js"},
{"name":"stepo2.kit.js","url":"stepo2.kit.js"},
@@ -16,6 +16,5 @@
{"name":"gps.kit.js","url":"gps.kit.js"},
{"name":"compass.kit.js","url":"compass.kit.js"},
{"name":"kitchen.img","url":"kitchen.icon.js","evaluate":true}
- ],
- "data": [{"name":"waypoints.json","url":"waypoints.json"}]
+ ]
}
diff --git a/apps/kitchen/waypoints.html b/apps/kitchen/waypoints.html
deleted file mode 100644
index d02260732..000000000
--- a/apps/kitchen/waypoints.html
+++ /dev/null
@@ -1,170 +0,0 @@
-
-
-
-
-
-
-
-
List of waypoints
-
-
-
-
Name
-
Lat.
-
Long.
-
Actions
-
-
-
-
-
-
-
-
Add a new waypoint
-
-
-
-
-
-
-
-
-
diff --git a/apps/kitchen/waypoints.json b/apps/kitchen/waypoints.json
deleted file mode 100644
index 98a670c0d..000000000
--- a/apps/kitchen/waypoints.json
+++ /dev/null
@@ -1,20 +0,0 @@
-[
- {
- "name":"NONE"
- },
- {
- "name":"No10",
- "lat":51.5032,
- "lon":-0.1269
- },
- {
- "name":"Stone",
- "lat":51.1788,
- "lon":-1.8260
- },
- { "name":"WP0" },
- { "name":"WP1" },
- { "name":"WP2" },
- { "name":"WP3" },
- { "name":"WP4" }
-]
\ No newline at end of file
diff --git a/apps/largeclock/ChangeLog b/apps/largeclock/ChangeLog
index 8c9b24be9..f35a02c54 100644
--- a/apps/largeclock/ChangeLog
+++ b/apps/largeclock/ChangeLog
@@ -8,3 +8,4 @@
0.08: Use Bangle.setUI for button/launcher handling
0.09: fix font size for latest firmwares
0.10: Configure the side text direction based on the wrist on which you wear your watch
+0.11: Use default Bangle formatter for booleans
diff --git a/apps/largeclock/metadata.json b/apps/largeclock/metadata.json
index dde790786..204243089 100644
--- a/apps/largeclock/metadata.json
+++ b/apps/largeclock/metadata.json
@@ -1,7 +1,7 @@
{
"id": "largeclock",
"name": "Large Clock",
- "version": "0.10",
+ "version": "0.11",
"description": "A readable and informational digital watch, with date, seconds and moon phase",
"icon": "largeclock.png",
"type": "clock",
diff --git a/apps/largeclock/settings.js b/apps/largeclock/settings.js
index f996666ab..4ebf842ce 100644
--- a/apps/largeclock/settings.js
+++ b/apps/largeclock/settings.js
@@ -74,7 +74,6 @@
"BTN3 app": () => showApps("BTN3"),
"On right hand": {
value: !!settings.right_hand,
- format: v=>v?"Yes":"No",
onchange: v=>{
settings.right_hand = v;
s.writeJSON("largeclock.json", settings);
diff --git a/apps/launch/ChangeLog b/apps/launch/ChangeLog
index 7248f69c3..44866b9f3 100644
--- a/apps/launch/ChangeLog
+++ b/apps/launch/ChangeLog
@@ -13,3 +13,4 @@
0.12: Add an option to hide clocks from the app list (fix #1015)
Add /*LANG*/ tags for internationalisation
0.13: Add fullscreen mode
+0.14: Use default Bangle formatter for booleans
diff --git a/apps/launch/metadata.json b/apps/launch/metadata.json
index da76fc4bb..19ca74e73 100644
--- a/apps/launch/metadata.json
+++ b/apps/launch/metadata.json
@@ -2,7 +2,7 @@
"id": "launch",
"name": "Launcher",
"shortName": "Launcher",
- "version": "0.13",
+ "version": "0.14",
"description": "This is needed to display a menu allowing you to choose your own applications. You can replace this with a customised launcher.",
"readme": "README.md",
"icon": "app.png",
diff --git a/apps/launch/settings.js b/apps/launch/settings.js
index 5d37e1c1b..496a6d77e 100644
--- a/apps/launch/settings.js
+++ b/apps/launch/settings.js
@@ -26,12 +26,10 @@
},
/*LANG*/"Show Clocks": {
value: settings.showClocks == true,
- format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: (m) => { save("showClocks", m) }
},
/*LANG*/"Fullscreen": {
value: settings.fullscreen == true,
- format: v => v ? /*LANG*/"Yes" : /*LANG*/"No",
onchange: (m) => { save("fullscreen", m) }
}
};
diff --git a/apps/lcars/ChangeLog b/apps/lcars/ChangeLog
index 9a8ac4008..f97ddf540 100644
--- a/apps/lcars/ChangeLog
+++ b/apps/lcars/ChangeLog
@@ -21,3 +21,4 @@
0.21: Add custom theming.
0.22: Fix alarm and add build in function for step counting.
0.23: Add warning for low flash memory
+0.24: Add ability to disable alarm functionality
\ No newline at end of file
diff --git a/apps/lcars/lcars.app.js b/apps/lcars/lcars.app.js
index e81c0d6f3..06a89a957 100644
--- a/apps/lcars/lcars.app.js
+++ b/apps/lcars/lcars.app.js
@@ -12,6 +12,7 @@ let settings = {
themeColor1BG: "#FF9900",
themeColor2BG: "#FF00DC",
themeColor3BG: "#0094FF",
+ disableAlarms: false,
};
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
for (const key in saved_settings) {
@@ -722,12 +723,12 @@ Bangle.on('touch', function(btn, e){
}
if(lcarsViewPos == 0){
- if(is_upper){
+ if(is_upper && !settings.disableAlarms){
feedback();
increaseAlarm();
drawState();
return;
- } if(is_lower){
+ } if(is_lower && !settings.disableAlarms){
feedback();
decreaseAlarm();
drawState();
diff --git a/apps/lcars/lcars.settings.js b/apps/lcars/lcars.settings.js
index b64feb30e..e4b9b0a78 100644
--- a/apps/lcars/lcars.settings.js
+++ b/apps/lcars/lcars.settings.js
@@ -13,6 +13,7 @@
themeColor1BG: "#FF9900",
themeColor2BG: "#FF00DC",
themeColor3BG: "#0094FF",
+ disableAlarms: false,
};
let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings;
for (const key in saved_settings) {
@@ -102,6 +103,14 @@
settings.themeColor3BG = bg_code[v];
save();
},
- }
+ },
+ 'Disable alarm functionality': {
+ value: settings.disableAlarms,
+ format: () => (settings.disableAlarms ? 'Yes' : 'No'),
+ onchange: () => {
+ settings.disableAlarms = !settings.disableAlarms;
+ save();
+ },
+ },
});
})
diff --git a/apps/lcars/metadata.json b/apps/lcars/metadata.json
index 62a1c67db..6533ddd52 100644
--- a/apps/lcars/metadata.json
+++ b/apps/lcars/metadata.json
@@ -3,7 +3,7 @@
"name": "LCARS Clock",
"shortName":"LCARS",
"icon": "lcars.png",
- "version":"0.23",
+ "version":"0.24",
"readme": "README.md",
"supports": ["BANGLEJS2"],
"description": "Library Computer Access Retrieval System (LCARS) clock.",
diff --git a/apps/limelight/ChangeLog b/apps/limelight/ChangeLog
index 9db0e26c5..8fe3a0b2c 100644
--- a/apps/limelight/ChangeLog
+++ b/apps/limelight/ChangeLog
@@ -1 +1,2 @@
0.01: first release
+0.02: Tell clock widgets to hide.
diff --git a/apps/limelight/limelight.app.js b/apps/limelight/limelight.app.js
index 20d79deeb..84ded1039 100644
--- a/apps/limelight/limelight.app.js
+++ b/apps/limelight/limelight.app.js
@@ -10,6 +10,8 @@
*
*/
+Bangle.setUI('clock');
+
g.clear();
const SETTINGS_FILE = "limelight.json";
@@ -259,5 +261,4 @@ Bangle.on('lcdPower',on=>{
}
});
-Bangle.setUI('clock');
draw();
diff --git a/apps/limelight/metadata.json b/apps/limelight/metadata.json
index 7c3736e1a..e484a2825 100644
--- a/apps/limelight/metadata.json
+++ b/apps/limelight/metadata.json
@@ -1,7 +1,7 @@
{
"id": "limelight",
"name": "Limelight",
- "version": "0.01",
+ "version": "0.02",
"description": "Simple analogue clock (with configurable fonts) based on the work of @Andreas_Rozek (Simple_Clock)",
"icon": "limelight.png",
"readme":"README.md",
diff --git a/apps/locale/locales.js b/apps/locale/locales.js
index de56503fd..7b3146e15 100644
--- a/apps/locale/locales.js
+++ b/apps/locale/locales.js
@@ -97,6 +97,25 @@ var locales = {
day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
// No translation for english...
},
+ "en_IE": {
+ lang: "en_IE",
+ decimal_point: ".",
+ thousands_sep: ",",
+ currency_symbol: "€",
+ int_curr_symbol: "EUR",
+ currency_first: true,
+ speed: 'kmh',
+ distance: { "0": "m", "1": "km" },
+ temperature: '°C',
+ ampm: { 0: "am", 1: "pm" },
+ timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
+ datePattern: { 0: "%d %b %Y", 1: "%d/%m/%Y" }, // 28 Feb 2020" // "28/03/2020"(short)
+ abmonth: "Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec",
+ month: "January,February,March,April,May,June,July,August,September,October,November,December",
+ abday: "Sun,Mon,Tue,Wed,Thu,Fri,Sat",
+ day: "Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday",
+ // No translation for english...
+ },
"en_NAV": { // navigation units nautical miles and knots
lang: "en_NAV",
decimal_point: ".",
@@ -141,7 +160,7 @@ var locales = {
currency_symbol: "$", currency_first: true,
int_curr_symbol: "USD",
speed: "mph",
- distance: { 0: "yd", 1: "mi" },
+ distance: { 0: "m", 1: "mi" },
temperature: "°F",
ampm: { 0: "am", 1: "pm" },
timePattern: { 0: "%HH:%MM:%SS ", 1: "%HH:%MM" },
diff --git a/apps/ltherm/ChangeLog b/apps/ltherm/ChangeLog
new file mode 100644
index 000000000..374bd5cd6
--- /dev/null
+++ b/apps/ltherm/ChangeLog
@@ -0,0 +1,2 @@
+0.01: New App!
+0.02: Use barometer temperature if available
diff --git a/apps/ltherm/app.js b/apps/ltherm/app.js
index 2cbf26e5f..552420a85 100644
--- a/apps/ltherm/app.js
+++ b/apps/ltherm/app.js
@@ -1,11 +1,10 @@
-function drawTemperature() {
+function drawTemperature(h) {
g.reset(1).clearRect(0,24,g.getWidth(),g.getHeight());
g.setFont("6x8",2).setFontAlign(0,0);
var x = g.getWidth()/2;
var y = g.getHeight()/2 + 10;
g.drawString("Temp", x, y - 45);
g.setFontVector(70).setFontAlign(0,0);
- var h = E.getTemperature();
if (avg.length < 10) {
avg[avg.length] = h;
} else {
@@ -18,7 +17,13 @@ function drawTemperature() {
}
const avg = [];
setInterval(function() {
- drawTemperature();
+ if (Bangle.getPressure){
+ Bangle.getPressure().then((p)=>{
+ drawTemperature(p.temperature);
+ });
+ } else {
+ drawTemperature(E.getTemperature());
+ }
}, 2000);
E.showMessage(/*LANG*/"Loading...");
Bangle.loadWidgets();
diff --git a/apps/ltherm/metadata.json b/apps/ltherm/metadata.json
index 83b295a3d..58c1f613c 100644
--- a/apps/ltherm/metadata.json
+++ b/apps/ltherm/metadata.json
@@ -2,7 +2,7 @@
"id": "ltherm",
"name": "Localized Thermometer",
"shortName": "Thermometer",
- "version": "0.01",
+ "version": "0.02",
"description": "Displays the current temperature in localized units.",
"icon": "thermf.png",
"tags": "tool",
diff --git a/apps/macwatch/ChangeLog b/apps/macwatch/ChangeLog
new file mode 100644
index 000000000..221d3fb57
--- /dev/null
+++ b/apps/macwatch/ChangeLog
@@ -0,0 +1 @@
+0.01: Created my first BangleJS app!
diff --git a/apps/macwatch/README.md b/apps/macwatch/README.md
new file mode 100644
index 000000000..1b7add3e5
--- /dev/null
+++ b/apps/macwatch/README.md
@@ -0,0 +1,26 @@
+# MacWatch
+
+A very simple clock using big numbers in the original Macintosh Chicago font.
+
+Touch the screen to show the date in numerical format.
+
+Touch the screen again to revert to the time.
+
+Time updates every 15 seconds.
+
+## In dark mode
+
+
+
+
+## In light mode
+
+
+
+
+## The watch in use
+
+
+## Creator
+
+Written by Giles Booth | [twitter](https://twitter.com/blogmywiki) | [blog](http://www.suppertime.co.uk/blogmywiki/)
diff --git a/apps/macwatch/app-icon.js b/apps/macwatch/app-icon.js
new file mode 100644
index 000000000..f4592b508
--- /dev/null
+++ b/apps/macwatch/app-icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwwkBIf4AOh//AggADC8YRCBIwXlI4oX/C5QWOC74WPC/4X/C6AA/AH4AmXwK5DAobBNC60P+DqHFxwXWCJAWRC44vUPwouRJKQXWBxAXlOAIgQbBDeLFxAXXAAa/RC64A/AH4AKA=="))
diff --git a/apps/macwatch/app.js b/apps/macwatch/app.js
new file mode 100644
index 000000000..fb9712a36
--- /dev/null
+++ b/apps/macwatch/app.js
@@ -0,0 +1,54 @@
+var font = atob("f3/gMB/7+AAAACA///AAAAAAQcHhsZ+LhAAAgUhsPh38eAAADAoJCI///BAA8XhkMhn8eAAAPz/0Mhn4eAAAgEAh8f+HgAAAb3/kMh/7eAAAeH5hML/z8AAAAAADYbAAAAAA");
+
+function draw() {
+ g.reset();
+ g.setFontCustom(font, 48, 8, 1801);
+ g.setFontAlign(0, -1, 0);
+ if (showDate) {
+ if (g.theme.dark) {
+ g.setColor("#00ffff"); // cyan date numbers for dark mode
+ }
+ else {
+ g.setColor("#0000ff"); // blue date numbers for light mode
+ }
+ line1 = ("0"+(new Date()).getDate()).substr(-2);
+ line2 = ("0"+((new Date()).getMonth()+1)).substr(-2);
+ }
+ else {
+ if (g.theme.dark) {
+ g.setColor(1,1,1); // white time numbers for dark mode
+ }
+ else {
+ g.setColor(0); // black time numbers for light mode
+ }
+ var d = new Date();
+ var da = d.toString().split(" ");
+ line1 = da[4].substr(0,2);
+ line2 = da[4].substr(3,2);
+ }
+ g.drawString(line1, 95, 30, true);
+ g.drawString(line2, 95, 106, true);
+ }
+
+// handle switch display on by pressing BTN1
+Bangle.on('lcdPower', function(on) {
+ if (on) draw();
+});
+
+Bangle.on('touch', function(on) {
+ if (on) {
+ showDate = !showDate; // toggle date mode on and off
+ draw();
+ }
+});
+
+g.clear();
+var showDate = 0;
+setInterval(draw, 15000); // refresh display every 15s
+draw();
+
+// Show launcher when button pressed
+Bangle.setUI("clock");
+
+Bangle.loadWidgets();
+Bangle.drawWidgets();
diff --git a/apps/macwatch/app.png b/apps/macwatch/app.png
new file mode 100644
index 000000000..9618b8b50
Binary files /dev/null and b/apps/macwatch/app.png differ
diff --git a/apps/macwatch/metadata.json b/apps/macwatch/metadata.json
new file mode 100644
index 000000000..0a2558380
--- /dev/null
+++ b/apps/macwatch/metadata.json
@@ -0,0 +1,17 @@
+{ "id": "macwatch",
+ "name": "MacWatch",
+ "shortName":"MacWatch",
+ "icon": "app.png",
+ "version":"0.01",
+ "description": "Simple clock with classic Mac font",
+ "type": "clock",
+ "tags": "clock",
+ "supports": ["BANGLEJS2"],
+ "screenshots": [{"url":"screenshot-dark-date.png"}],
+ "readme": "README.md",
+ "allow_emulator": true,
+ "storage": [
+ {"name":"macwatch.app.js","url":"app.js"},
+ {"name":"macwatch.img","url":"app-icon.js","evaluate":true}
+ ]
+}
diff --git a/apps/macwatch/photo-watch-in-use.jpg b/apps/macwatch/photo-watch-in-use.jpg
new file mode 100644
index 000000000..16ae57c3e
Binary files /dev/null and b/apps/macwatch/photo-watch-in-use.jpg differ
diff --git a/apps/macwatch/screenshot-dark-date.png b/apps/macwatch/screenshot-dark-date.png
new file mode 100644
index 000000000..68d1d80f9
Binary files /dev/null and b/apps/macwatch/screenshot-dark-date.png differ
diff --git a/apps/macwatch/screenshot-dark-time.png b/apps/macwatch/screenshot-dark-time.png
new file mode 100644
index 000000000..dc9e32df2
Binary files /dev/null and b/apps/macwatch/screenshot-dark-time.png differ
diff --git a/apps/macwatch/screenshot-light-date.png b/apps/macwatch/screenshot-light-date.png
new file mode 100644
index 000000000..e4bee235f
Binary files /dev/null and b/apps/macwatch/screenshot-light-date.png differ
diff --git a/apps/macwatch/screenshot-light-time.png b/apps/macwatch/screenshot-light-time.png
new file mode 100644
index 000000000..45abff84a
Binary files /dev/null and b/apps/macwatch/screenshot-light-time.png differ
diff --git a/apps/macwatch2/ChangeLog b/apps/macwatch2/ChangeLog
new file mode 100644
index 000000000..5eafe64d2
--- /dev/null
+++ b/apps/macwatch2/ChangeLog
@@ -0,0 +1,5 @@
+0.01: Created first version of the app with numeric date, only works in light mode
+0.02: New icon, shimmied date right a bit
+0.03: Incorporated improvements from Peer David for accuracy, fix dark mode, widgets run in background
+0.04: Changed clock to use 12/24 hour format based on locale
+0.05: Tell clock widgets to hide.
diff --git a/apps/macwatch2/IMG_3782 crop.JPG b/apps/macwatch2/IMG_3782 crop.JPG
new file mode 100644
index 000000000..2c0bc375a
Binary files /dev/null and b/apps/macwatch2/IMG_3782 crop.JPG differ
diff --git a/apps/macwatch2/README.md b/apps/macwatch2/README.md
new file mode 100644
index 000000000..4fdb9b6e9
--- /dev/null
+++ b/apps/macwatch2/README.md
@@ -0,0 +1,19 @@
+# MacWatch2
+
+A clock inspired by Susan Kare's original 1984 Macintosh desktop design.
+
+Ideas for development:
+- [x] fix dark mode either by forcing black on white or doing proper inverse display and text
+- [ ] date in text format
+- [ ] tap to load different info in window
+- [ ] unlock to show seconds, perhaps flip time and date?
+- [ ] incorporate widgets somehow either by leaving space or adding Chicago font widgets of my own in top bar
+
+
+
+
+
+## Creator
+
+Written by Giles Booth | [twitter](https://twitter.com/blogmywiki) | [blog](http://www.suppertime.co.uk/blogmywiki/)
+Improvements for accuracy, dark mode and widgets running in background by [Peer David](https://gist.github.com/peerdavid)
diff --git a/apps/macwatch2/app-icon.js b/apps/macwatch2/app-icon.js
new file mode 100644
index 000000000..be7d5e060
--- /dev/null
+++ b/apps/macwatch2/app-icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwgn/AH4A/AH4AfdIoX/C/4X4CIwYQC6wPIDBwX/C6IYNC7TVFC9IEFO6YX/VBQWPC7CmDVIoX/C/4X2AH4A/ADwA=="))
diff --git a/apps/macwatch2/app.js b/apps/macwatch2/app.js
new file mode 100644
index 000000000..4556e06ac
--- /dev/null
+++ b/apps/macwatch2/app.js
@@ -0,0 +1,64 @@
+// 68k Mac Finder desktop themed clock
+// by Giles Booth @blogmywiki
+// improvements by Peer David
+
+var img = require("heatshrink").decompress(atob("2GwgP4C6cf8AVTg/ACqcDwADBDCMBCoICCCqACEj8zAwXwmcYgEGswYHhxwBjEDGocwCoVgQxHwCoMzjwVBwPzngrCnlmDAsfNoIVBIQMBwZBEAAIVIjwVD8YVNIIc/FY9+CpcwCo9gCo0PQYUzmIVGo1is1ACokGNoaDC+PzhkAg+Gnl/aiIA/AD//AClVACmqACgr/Fd2vVqP+FYNUbKMNFYOsCqMOFa+t/f/35LC/AODK43uFYUCgGACAUB/IFDFZP6gArEsArTgFhz9w+ArRsOZzOYFaQVCFan4FaiFHFZuIFaeYQZbbVf5LbK1gVRhwrX15MGABX+K/4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Ff4r/Fe+v/4AQ/wrBq4VR/orBAClVACgr/Ff4r/AAmr6or/q/6Fae/A="));
+
+var font = atob("f3/gMB/7+AAAACA///AAAAAAQcHhsZ+LhAAAgUhsPh38eAAADAoJCI///BAA8XhkMhn8eAAAPz/0Mhn4eAAAgEAh8f+HgAAAb3/kMh/7eAAAeH5hML/z8AAAAAADYbAAAAAA");
+
+var drawTimeout;
+
+// schedule a draw for the next minute
+function queueDraw() {
+ if (drawTimeout) clearTimeout(drawTimeout);
+ drawTimeout = setTimeout(function() {
+ drawTimeout = undefined;
+ draw();
+ }, 60000 - (Date.now() % 60000));
+}
+
+
+function draw() {
+ queueDraw();
+
+ // Fix theme to "light"
+ g.setTheme({bg:"#fff", fg:"#000", dark:false}).clear();
+ g.reset();
+ g.drawImage(img,0,0);
+
+ g.setFontCustom(font, 48, 8, 1033);
+ g.setFontAlign(0, -1, 0);
+ g.setColor(0,0,0);
+ var d = new Date();
+ var dt = require("locale").time(d, 1);
+ var hh = dt.split(":")[0];
+ var mm = dt.split(":")[1];
+ g.drawString(hh, 52, 65, true);
+ g.drawString(mm, 132, 65, true);
+ g.drawString(':', 93,65);
+ dd = ("0"+(new Date()).getDate()).substr(-2);
+ mo = ("0"+((new Date()).getMonth()+1)).substr(-2);
+ yy = ("0"+((new Date()).getFullYear())).substr(-2);
+ g.setFontCustom(font, 48, 8, 521);
+ g.drawString(dd + ':' + mo + ':' + yy, 88, 120, true);
+
+ // Hide widgets
+ for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";}
+}
+
+
+// handle switch display on by pressing BTN1
+Bangle.on('lcdPower',on=>{
+ if (on) {
+ draw(); // draw immediately, queue redraw
+ } else { // stop draw timer
+ if (drawTimeout) clearTimeout(drawTimeout);
+ drawTimeout = undefined;
+ }
+});
+
+Bangle.setUI("clock");
+
+// Load widgets but hide them
+Bangle.loadWidgets();
+draw();
diff --git a/apps/macwatch2/app.png b/apps/macwatch2/app.png
new file mode 100644
index 000000000..efcdead19
Binary files /dev/null and b/apps/macwatch2/app.png differ
diff --git a/apps/macwatch2/metadata.json b/apps/macwatch2/metadata.json
new file mode 100644
index 000000000..14c48c749
--- /dev/null
+++ b/apps/macwatch2/metadata.json
@@ -0,0 +1,17 @@
+{ "id": "macwatch2",
+ "name": "MacWatch2",
+ "shortName":"MacWatch2",
+ "icon": "app.png",
+ "version":"0.05",
+ "description": "Classic Mac Finder clock",
+ "type": "clock",
+ "tags": "clock",
+ "supports": ["BANGLEJS2"],
+ "screenshots": [{"url":"screenshot.png"}],
+ "readme": "README.md",
+ "allow_emulator": true,
+ "storage": [
+ {"name":"macwatch2.app.js","url":"app.js"},
+ {"name":"macwatch2.img","url":"app-icon.js","evaluate":true}
+ ]
+}
diff --git a/apps/macwatch2/screenshot.png b/apps/macwatch2/screenshot.png
new file mode 100644
index 000000000..732b29d7f
Binary files /dev/null and b/apps/macwatch2/screenshot.png differ
diff --git a/apps/matrixclock/ChangeLog b/apps/matrixclock/ChangeLog
index 52f705301..02f7d109b 100644
--- a/apps/matrixclock/ChangeLog
+++ b/apps/matrixclock/ChangeLog
@@ -1,4 +1,7 @@
0.01: Initial Release
0.02: Support for Bangle 2
0.03: Keep the date from being overwritten, use correct colour from theme for clearing
-0.04: Removed "wake LCD on face-up"-feature: A watch-face should not set things like "wake LCD on face-up".
+0.04: Removed "wake LCD on face-up"-feature: A watch-face should not set things like "wake LCD on face-up".
+0.05: Added support to other color themes (other then black)
+0.06: Added support for 24 hour clock enabled from settings
+0.07: Tell clock widgets to hide.
diff --git a/apps/matrixclock/README.md b/apps/matrixclock/README.md
index 010524b60..01aef6544 100644
--- a/apps/matrixclock/README.md
+++ b/apps/matrixclock/README.md
@@ -2,6 +2,25 @@

+## Settings
+Please use the setting->App->Matrix Clock Menu to change the settings
+
+| Setting | Description |
+|-------------|--------------------------------------------------------------------------------------------------------------------|
+| Color | By default set to **'theme'** to follow the theme colors. Selector also offers a selection of other colour schemes |
+| Time Format | Choose between 12 hour and 24 hour time format |
+| Intensity | Changes the number of matrix streams that are falling |
+
+## Colour Themes
+
+Some of the colours schemes that are available from the settings screen
+
+|  |  |  |
+|-------------------------------|-------------------------------|-----|
+| green on black | white on black | white on gray |
+
+
+
## Requests
Please reach out to adrian@adriankirk.com if you have feature requests or notice bugs.
diff --git a/apps/matrixclock/matrix_black_on_white.jpg b/apps/matrixclock/matrix_black_on_white.jpg
new file mode 100644
index 000000000..545545c65
Binary files /dev/null and b/apps/matrixclock/matrix_black_on_white.jpg differ
diff --git a/apps/matrixclock/matrix_green_on_black.jpg b/apps/matrixclock/matrix_green_on_black.jpg
new file mode 100644
index 000000000..7caa38bec
Binary files /dev/null and b/apps/matrixclock/matrix_green_on_black.jpg differ
diff --git a/apps/matrixclock/matrix_white_on_gray.jpg b/apps/matrixclock/matrix_white_on_gray.jpg
new file mode 100644
index 000000000..dc9d2f3ba
Binary files /dev/null and b/apps/matrixclock/matrix_white_on_gray.jpg differ
diff --git a/apps/matrixclock/matrixclock.js b/apps/matrixclock/matrixclock.js
index 2e4ba1ac4..9618c3a47 100644
--- a/apps/matrixclock/matrixclock.js
+++ b/apps/matrixclock/matrixclock.js
@@ -3,24 +3,107 @@
*
* Matrix Clock
*
- * A simple clock inspired by the movie.
- * Text shards move down the screen as a background to the
+ * A simple clock inspired by the movie.
+ * Text shards move down the screen as a background to the
* time and date
**/
const Locale = require('locale');
-const SHARD_COLOR =[0,1.0,0];
+const PREFERENCE_FILE = "matrixclock.settings.json";
+const settings = Object.assign({color: "theme", time_format: '12 hour', intensity: 'light'},
+ require('Storage').readJSON(PREFERENCE_FILE, true) || {});
+
+var format_time;
+if(settings.time_format == '24 hour'){
+ format_time = (t) => format_time_24_hour(t);
+} else {
+ format_time = (t) => format_time_12_hour(t);
+}
+
+const colors = {
+ 'gray' :[0.5,0.5,0.5],
+ 'green': [0,1.0,0],
+ 'red' : [1.0,0.0,0.0],
+ 'blue' : [0.0,0.0,1.0],
+ 'black': [0.0,0.0,0.0],
+ 'purple': [1.0,0.0,1.0],
+ 'white': [1.0,1.0,1.0],
+ 'yellow': [1.0,1.0,0.0]
+};
+
+const color_schemes = {
+ 'black on white': ['white','black'],
+ 'green on white' : ['white','green'],
+ 'green on black' : ['black','green'],
+ 'red on black' : ['black', 'red'],
+ 'red on white' : ['white', 'red'],
+ 'white on gray' : ['gray', 'white'],
+ 'white on red' : ['red', 'white'],
+ 'white on blue': ['blue','white'],
+ 'white on purple': ['purple', 'white']
+};
+
+function int2Color(color_int){
+ var blue_int = color_int & 31;
+ var blue = (blue_int)/31.0;
+
+ var green_int = (color_int >> 5) & 31;
+ var green = (green_int)/31.0;
+
+ var red_int = (color_int >> 11) & 31;
+ var red = red_int/ 31.0;
+ return [red,green,blue];
+}
+
+var fg_color = colors.black;
+var bg_color = colors.white;
+
+// now lets deal with the settings
+if(settings.color === "theme"){
+ bg_color = int2Color(g.theme.bg);
+ if(g.theme.bg === 0) {
+ fg_color = colors.green;
+ } else {
+ fg_color = int2Color(g.theme.fg);
+ }
+} else {
+ var color_scheme = color_schemes[settings.color];
+ bg_color = colors[color_scheme[0]];
+ fg_color = colors[color_scheme[1]];
+ g.setBgColor(bg_color[0],bg_color[1],bg_color[2]);
+}
+if(fg_color === undefined)
+ fg_color = colors.black;
+
+if(bg_color === undefined)
+ bg_color = colors.white;
+
+const intensity_schemes = {
+ 'light': 3,
+ 'medium': 4,
+ 'high': 5
+};
+
+var noShards = intensity_schemes.light;
+if(settings.intensity !== undefined){
+ noShards = intensity_schemes[settings.intensity];
+}
+if(noShards === undefined){
+ noShards = intensity_schemes.light;
+}
+
const SHARD_FONT_SIZE = 12;
const SHARD_Y_START = 30;
+
const w = g.getWidth();
/**
-* The text shard object is responsible for creating the
-* shards of text that move down the screen. As the
-* shard moves down the screen the latest character added
-* is brightest with characters being coloured darker and darker
-* going back to the eldest
-*/
+ * The text shard object is responsible for creating the
+ * shards of text that move down the screen. As the
+ * shard moves down the screen the latest character added
+ * is brightest with characters being coloured darker and darker
+ * going back to the eldest
+ */
class TextShard {
constructor(x,y,length){
@@ -34,44 +117,46 @@ class TextShard {
this.txt = [];
}
/**
- * The add method call adds another random character to
- * the chain
- */
+ * The add method call adds another random character to
+ * the chain
+ */
add(){
this.txt.push(randomChar());
}
/**
- * The show method displays the latest shard image to the
- * screen with the following rules:
- * - latest addition is brightest, oldest is darker
- * - display up to defined length of characters only
- * of the shard to save cpu
- */
+ * The show method displays the latest shard image to the
+ * screen with the following rules:
+ * - latest addition is brightest, oldest is darker
+ * - display up to defined length of characters only
+ * of the shard to save cpu
+ */
show(){
g.setFontAlign(-1,-1,0);
for(var i=0; i this.length - 2){
color_strength = 0;
- }
- g.setColor(color_strength*SHARD_COLOR[0],
- color_strength*SHARD_COLOR[1],
- color_strength*SHARD_COLOR[2]);
+ }
+ var bg_color_strength = 1 - color_strength;
+ g.setColor(Math.abs(color_strength*fg_color[0] - bg_color_strength*bg_color[0]),
+ Math.abs(color_strength*fg_color[1] - bg_color_strength*bg_color[1]),
+ Math.abs(color_strength*fg_color[2] - bg_color_strength*bg_color[2])
+ );
g.setFont("Vector",SHARD_FONT_SIZE);
- g.drawString(this.txt[idx], this.x, this.y + idx*SHARD_FONT_SIZE);
+ g.drawString(this.txt[idx], this.x, this.y + idx*SHARD_FONT_SIZE);
}
}
/**
- * Method tests to see if any part of the shard chain is still
- * visible on the screen
- */
+ * Method tests to see if any part of the shard chain is still
+ * visible on the screen
+ */
isVisible(){
- return (this.y + (this.txt.length - this.length - 2)*SHARD_FONT_SIZE < g.getHeight());
+ return (this.y + (this.txt.length - this.length - 2)*SHARD_FONT_SIZE < g.getHeight());
}
/**
- * resets the shard back to the top of the screen
- */
+ * resets the shard back to the top of the screen
+ */
reset(){
this.y = SHARD_Y_START;
this.txt = [];
@@ -79,8 +164,8 @@ class TextShard {
}
/**
-* random character chooser to be called by the shard when adding characters
-*/
+ * random character chooser to be called by the shard when adding characters
+ */
const CHAR_CODE_START = 33;
const CHAR_CODE_LAST = 126;
const CHAR_CODE_LENGTH = CHAR_CODE_LAST - CHAR_CODE_START;
@@ -90,11 +175,10 @@ function randomChar(){
// Now set up the shards
// we are going to have a limited no of shards (to save cpu)
-// but randomize the x value and length every reset to make it look as if there
+// but randomize the x value and length every reset to make it look as if there
// are more
var shards = [];
-const NO_SHARDS = 3;
-const channel_width = g.getWidth()/NO_SHARDS;
+const channel_width = g.getWidth()/noShards;
function shard_x(i){
return i*channel_width + Math.random() * channel_width;
@@ -104,7 +188,7 @@ function shard_length(){
return Math.floor(Math.random()*5) + 3;
}
-for(var i=0; i 99 || value < 0)
- throw "must be between in range 0-99";
- if(value < 10)
- return "0" + value.toString();
- else
- return value.toString();
+ var value = (num | 0);
+ if(value > 99 || value < 0)
+ throw "must be between in range 0-99";
+ if(value < 10)
+ return "0" + value.toString();
+ else
+ return value.toString();
}
// The interval reference for updating the clock
@@ -215,12 +304,12 @@ function startTimers(){
clearTimers();
if (Bangle.isLCDOn()) {
intervalRef = setInterval(() => {
- if (!shouldRedraw()) {
- //console.log("draw clock callback - skipped redraw");
- } else {
- draw_clock();
- }
- }, 100
+ if (!shouldRedraw()) {
+ //console.log("draw clock callback - skipped redraw");
+ } else {
+ draw_clock();
+ }
+ }, 100
);
draw_clock();
} else {
@@ -239,11 +328,9 @@ Bangle.on('lcdPower', (on) => {
}
});
+Bangle.setUI("clock");
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
startTimers();
-Bangle.setUI("clock");
-
-
diff --git a/apps/matrixclock/matrixclock.settings.js b/apps/matrixclock/matrixclock.settings.js
new file mode 100644
index 000000000..1f22a045f
--- /dev/null
+++ b/apps/matrixclock/matrixclock.settings.js
@@ -0,0 +1,52 @@
+(function(back) {
+ const PREFERENCE_FILE = "matrixclock.settings.json";
+ var settings = Object.assign({color : "theme", time_format: '12 hour', intensity: "light"},
+ require('Storage').readJSON(PREFERENCE_FILE, true) || {});
+
+ console.log("loaded:" + JSON.stringify(settings));
+
+ function writeSettings() {
+ console.log("saving:" + JSON.stringify(settings));
+ require('Storage').writeJSON(PREFERENCE_FILE, settings);
+ }
+
+ // Helper method which uses int-based menu item for set of string values
+ function stringItems(startvalue, writer, values) {
+ return {
+ value: (startvalue === undefined ? 0 : values.indexOf(startvalue)),
+ format: v => values[v],
+ min: 0,
+ max: values.length - 1,
+ wrap: true,
+ step: 1,
+ onchange: v => {
+ writer(values[v]);
+ writeSettings();
+ }
+ };
+ }
+
+ // Helper method which breaks string set settings down to local settings object
+ function stringInSettings(name, values) {
+ return stringItems(settings[name], v => settings[name] = v, values);
+ }
+
+ // Show the menu
+ E.showMenu({
+ "" : { "title" : "Matrix Clock" },
+ "< Back" : () => back(),
+ "Colour": stringInSettings("color", ['theme',
+ 'black on white',
+ 'green on white',
+ 'green on black',
+ 'red on white',
+ 'white on gray',
+ 'white on red',
+ 'white on blue'
+ ]),
+ "Time Format": stringInSettings("time_format", ['12 hour','24 hour']),
+ "Intensity": stringInSettings("intensity", ['light',
+ 'medium',
+ 'high'])
+ });
+})
\ No newline at end of file
diff --git a/apps/matrixclock/metadata.json b/apps/matrixclock/metadata.json
index 122cee3a1..718b878e5 100644
--- a/apps/matrixclock/metadata.json
+++ b/apps/matrixclock/metadata.json
@@ -1,10 +1,10 @@
{
"id": "matrixclock",
"name": "Matrix Clock",
- "version": "0.04",
+ "version": "0.07",
"description": "inspired by The Matrix, a clock of the same style",
"icon": "matrixclock.png",
- "screenshots": [{"url":"screenshot_matrix.png"}],
+ "screenshots": [{"url":"matrix_green_on_black.jpg"}],
"type": "clock",
"tags": "clock",
"supports": ["BANGLEJS","BANGLEJS2"],
@@ -12,6 +12,8 @@
"allow_emulator": true,
"storage": [
{"name":"matrixclock.app.js","url":"matrixclock.js"},
+ { "name":"matrixclock.settings.js","url":"matrixclock.settings.js"},
{"name":"matrixclock.img","url":"matrixclock-icon.js","evaluate":true}
- ]
+ ],
+ "data": [{"name": "matrixclock.settings.json"}]
}
diff --git a/apps/matrixclock/screenshot_matrix.png b/apps/matrixclock/screenshot_matrix.png
deleted file mode 100644
index 3d843848c..000000000
Binary files a/apps/matrixclock/screenshot_matrix.png and /dev/null differ
diff --git a/apps/mclock/ChangeLog b/apps/mclock/ChangeLog
index 05b422406..e3b164942 100644
--- a/apps/mclock/ChangeLog
+++ b/apps/mclock/ChangeLog
@@ -5,3 +5,4 @@
Fix issue where first digit could get stuck going from "2x:xx" to " x:xx" (fix #365)
0.06: Support 12 hour time
0.07: Use Bangle.setUI for button/launcher handling
+0.08: Tell clock widgets to hide.
diff --git a/apps/mclock/clock-morphing.js b/apps/mclock/clock-morphing.js
index f1254860b..bd133206e 100644
--- a/apps/mclock/clock-morphing.js
+++ b/apps/mclock/clock-morphing.js
@@ -209,6 +209,9 @@ Bangle.on('lcdPower',function(on) {
}
});
+// Show launcher when button pressed
+Bangle.setUI("clock");
+
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
@@ -216,5 +219,3 @@ Bangle.drawWidgets();
timeInterval = setInterval(showTime, 1000);
showTime();
-// Show launcher when button pressed
-Bangle.setUI("clock");
diff --git a/apps/mclock/metadata.json b/apps/mclock/metadata.json
index 513f823a1..a7d56f752 100644
--- a/apps/mclock/metadata.json
+++ b/apps/mclock/metadata.json
@@ -1,7 +1,7 @@
{
"id": "mclock",
"name": "Morphing Clock",
- "version": "0.07",
+ "version": "0.08",
"description": "7 segment clock that morphs between minutes and hours",
"icon": "clock-morphing.png",
"type": "clock",
diff --git a/apps/messages/ChangeLog b/apps/messages/ChangeLog
index 759d32b05..262cba1fa 100644
--- a/apps/messages/ChangeLog
+++ b/apps/messages/ChangeLog
@@ -52,6 +52,18 @@
0.37: Now use the setUI 'back' icon in the top left rather than specific buttons/menu items
0.38: Add telegram foss handling
0.39: Set default color for message icons according to theme
- Don't turn on the screen after unread timeout expires (#1873)
-0.40: Improved buzzing implementation when receiving notifications
-
+0.40: Use default Bangle formatter for booleans
+0.41: Add notification icons in the widget
+0.42: Fix messages ignoring "Vibrate: Off" setting
+0.43: Add new Icons (Airbnb, warnwetter)
+0.44: Separate buzz pattern for incoming calls
+0.45: Added new app colors and icons
+0.46: Add 'Vibrate Timer' option to set how long to vibrate for, and fix Repeat:off
+ Fix message removal from widget bar (previously caused exception as .hide has been removed)
+0.47: Add new Icons (Nextbike, Mattermost, etc.)
+0.48: When getting new message from the clock, only buzz once the messages app is loaded
+0.49: Change messages icon (to fit within 24px) and ensure widget renders icons centrally
+0.50: Add `getMessages` and `status` functions to library
+ Option to disable auto-open of messages
+ Option to make message icons monochrome (not colored)
+ messages widget buzz now returns a promise
\ No newline at end of file
diff --git a/apps/messages/README.md b/apps/messages/README.md
index da2701f35..2e583d1c2 100644
--- a/apps/messages/README.md
+++ b/apps/messages/README.md
@@ -12,8 +12,11 @@ You can change settings by going to the global `Settings` app, then `App Setting
and `Messages`:
* `Vibrate` - This is the pattern of buzzes that should be made when a new message is received
+* `Vibrate for calls` - This is the pattern of buzzes that should be made when an incoming call is received
* `Repeat` - How often should buzzes repeat - the default of 4 means the Bangle will buzz every 4 seconds
-* `Unread Timer` - When a new message is received we go into the Messages app.
+* `Vibrate Timer` - When a new message is received when in a non-clock app, we display the message icon and
+buzz every `Repeat` seconds. This is how long we continue to do that.
+* `Unread Timer` - When a new message is received when showing the clock we go into the Messages app.
If there is no user input for this amount of time then the app will exit and return
to the clock where a ringing bell will be shown in the Widget bar.
* `Min Font` - The minimum font size used when displaying messages on the screen. A bigger font
@@ -22,12 +25,13 @@ it starts getting clipped.
* `Auto-Open Music` - Should the app automatically open when the phone starts playing music?
* `Unlock Watch` - Should the app unlock the watch when a new message arrives, so you can touch the buttons at the bottom of the app?
* `Flash Icon` - Toggle flashing of the widget icon.
+* `Widget messages` - The maximum amount of message icons to show on the widget.
## New Messages
When a new message is received:
-* If you're in an app, the Bangle will buzz and a 'new message' icon appears in the Widget bar. You can tap this bar to view the message.
+* If you're in an app, the Bangle will buzz and a message icon appears in the Widget bar. You can tap this icon to view the message.
* If you're in a clock, the Messages app will automatically start and show the message
When a message is shown, you'll see a screen showing the message title and text.
diff --git a/apps/messages/app-newmessage.js b/apps/messages/app-newmessage.js
new file mode 100644
index 000000000..328927c70
--- /dev/null
+++ b/apps/messages/app-newmessage.js
@@ -0,0 +1,5 @@
+/* Called when we have a new message when we're in the clock...
+BUZZ_ON_NEW_MESSAGE is set so when messages.app.js loads it knows
+that it should buzz */
+global.BUZZ_ON_NEW_MESSAGE = true;
+eval(require("Storage").read("messages.app.js"));
diff --git a/apps/messages/app.js b/apps/messages/app.js
index d4540b797..40dff9635 100644
--- a/apps/messages/app.js
+++ b/apps/messages/app.js
@@ -48,13 +48,13 @@ we should start a timeout for settings.unreadTimeout to return
to the clock. */
var unreadTimeout;
/// List of all our messages
-var MESSAGES = require("Storage").readJSON("messages.json",1)||[];
+var MESSAGES = require("messages").getMessages();
if (!Array.isArray(MESSAGES)) MESSAGES=[];
var onMessagesModified = function(msg) {
// TODO: if new, show this new one
if (msg && msg.id!=="music" && msg.new && active!="map" &&
!((require('Storage').readJSON('setting.json', 1) || {}).quiet)) {
- if (WIDGETS["messages"]) WIDGETS["messages"].buzz();
+ if (WIDGETS["messages"]) WIDGETS["messages"].buzz(msg.src);
else Bangle.buzz();
}
if (msg && msg.id=="music") {
@@ -286,6 +286,7 @@ function showMessage(msgid) {
}
}
function goBack() {
+ layout = undefined;
msg.new = false; saveMessages(); // read mail
cancelReloadTimeout(); // don't auto-reload to clock now
checkMessages({clockIfNoMsg:1,clockIfAllRead:0,showMsgIfUnread:0,openMusic:openMusic});
@@ -353,8 +354,18 @@ function checkMessages(options) {
// we have >0 messages
var newMessages = MESSAGES.filter(m=>m.new&&m.id!="music");
// If we have a new message, show it
- if (options.showMsgIfUnread && newMessages.length)
- return showMessage(newMessages[0].id);
+ if (options.showMsgIfUnread && newMessages.length) {
+ showMessage(newMessages[0].id);
+ // buzz after showMessage, so beingbusy during layout doesn't affect the buzz pattern
+ if (global.BUZZ_ON_NEW_MESSAGE) {
+ // this is set if we entered the messages app by loading `messages.new.js`
+ // ... but only buzz the first time we view a new message
+ global.BUZZ_ON_NEW_MESSAGE = false;
+ // messages.buzz respects quiet mode - no need to check here
+ WIDGETS.messages.buzz(newMessages[0].src);
+ }
+ return;
+ }
// no new messages: show playing music? (only if we have playing music to show)
if (options.openMusic && MESSAGES.some(m=>m.id=="music" && m.track && m.state=="play"))
return showMessage('music');
diff --git a/apps/messages/lib.js b/apps/messages/lib.js
index 5e1572384..d8599c93d 100644
--- a/apps/messages/lib.js
+++ b/apps/messages/lib.js
@@ -40,12 +40,12 @@ exports.pushMessage = function(event) {
require("Storage").writeJSON("messages.json",messages);
// if in app, process immediately
if (inApp) return onMessagesModified(mIdx<0 ? {id:event.id} : messages[mIdx]);
- // if we've removed the last new message, hide the widget
- if (event.t=="remove" && !messages.some(m=>m.new)) {
- if (global.WIDGETS && WIDGETS.messages) WIDGETS.messages.hide();
+ // update the widget icons shown
+ if (global.WIDGETS && WIDGETS.messages) WIDGETS.messages.update(messages,true);
// if no new messages now, make sure we don't load the messages app
- if (exports.messageTimeout && !messages.some(m=>m.new))
- clearTimeout(exports.messageTimeout);
+ if (event.t=="remove" && exports.messageTimeout && !messages.some(m=>m.new)) {
+ clearTimeout(exports.messageTimeout);
+ delete exports.messageTimeout;
}
// ok, saved now
if (event.id=="music" && Bangle.CLOCK && messages[mIdx].new && openMusic()) {
@@ -62,39 +62,25 @@ exports.pushMessage = function(event) {
var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet;
var appSettings = require('Storage').readJSON('messages.settings.json',1)||{};
var unlockWatch = appSettings.unlockWatch;
- var quietNoAutOpn = appSettings.quietNoAutOpn;
- delete appSettings;
// don't auto-open messages in quiet mode if quietNoAutOpn is true
- if(quiet && quietNoAutOpn) {
- loadMessages = false;
- }
- if (!quiet && loadMessages && unlockWatch != false){
- Bangle.setLocked(false);
- Bangle.setLCDPower(1); // turn screen on
- }
+ if((quiet && appSettings.quietNoAutOpn) || appSettings.noAutOpn)
+ loadMessages = false;
+ delete appSettings;
// after a delay load the app, to ensure we have all the messages
if (exports.messageTimeout) clearTimeout(exports.messageTimeout);
exports.messageTimeout = setTimeout(function() {
exports.messageTimeout = undefined;
- var cont = function() {
- // if we're in a clock or it's important, go straight to messages app
- if (loadMessages){
- return load("messages.app.js");
+ // if we're in a clock or it's important, go straight to messages app
+ if (loadMessages){
+ if(!quiet && unlockWatch){
+ Bangle.setLocked(false);
+ Bangle.setLCDPower(1); // turn screen on
}
- if (global.WIDGETS && WIDGETS.messages) { // show messages if widgets are loaded
- WIDGETS.messages.show();
- }
- };
- if (quiet) {
- //Be quiet and cont()inue displaying.
- cont();
- } else {
- //We have to wait for buzzing to complete before cont()inuing
- if(global.WIDGETS && WIDGETS.messages)
- WIDGETS.messages.buzz().then(()=>cont());
- else
- Bangle.buzz().then(()=>cont());
+ // we will buzz when we enter the messages app
+ return load("messages.new.js");
}
+ if (!quiet && (!global.WIDGETS || !WIDGETS.messages)) return Bangle.buzz(); // no widgets - just buzz once to let someone know
+ if (global.WIDGETS && WIDGETS.messages) WIDGETS.messages.update(messages);
}, 500);
}
/// Remove all messages
@@ -111,54 +97,94 @@ exports.clearAll = function(event) {
if (inApp) return onMessagesModified();
// if we have a widget, update it
if (global.WIDGETS && WIDGETS.messages)
- WIDGETS.messages.hide();
+ WIDGETS.messages.update(messages);
}
+/**
+ * @returns {array} All messages
+ */
+exports.getMessages = function() {
+ if ("undefined"!=typeof MESSAGES) return MESSAGES; // loaded/managed by app
+ return require("Storage").readJSON("messages.json",1)||[];
+}
+
+/**
+ * Check if there are any messages
+ * @returns {string} "new"/"old"/"none"
+ */
+ exports.status = function() {
+ try {
+ let status= "none";
+ for(const m of exports.getMessages()) {
+ if (["music", "map"].includes(m.id)) continue;
+ if (m.new) return "new";
+ status = "old";
+ }
+ return status;
+ } catch(e) {
+ return "none"; // don't bother e.g. the widget with errors
+ }
+};
+
exports.getMessageImage = function(msg) {
/*
- * icons should be 24x24px with 1bpp colors and 'Transparency to Color'
+ * icons should be 24x24px or less with 1bpp colors and 'Transparency to Color'
* http://www.espruino.com/Image+Converter
*/
if (msg.img) return atob(msg.img);
- var s = (msg.src||"").toLowerCase();
+ const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
+ if (s=="airbnb") return atob("GBgBAAAAAAAAAAAAADwAAH4AAGYAAMMAAIEAAYGAAYGAAzzAA2bABmZgBmZgDGYwDDwwCDwQCBgQDDwwB+fgA8PAAAAAAAAAAAAA");
if (s=="alarm" || s =="alarmclockreceiver") return atob("GBjBAP////8AAAAAAAACAEAHAOAefng5/5wTgcgHAOAOGHAMGDAYGBgYGBgYGBgYGBgYDhgYBxgMATAOAHAHAOADgcAB/4AAfgAAAAAAAAA=");
if (s=="bibel") return atob("GBgBAAAAA//wD//4D//4H//4H/f4H/f4H+P4H4D4H4D4H/f4H/f4H/f4H/f4H/f4H//4H//4H//4GAAAEAAAEAAACAAAB//4AAAA");
- if (s=="calendar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA==");
+ if (s=="bring") return atob("GBgBAAAAAAAAAAAAAAAAAHwAAFoAAf+AA/+AA/+AA/+AA/eAA+eAA0+AAx+AA7+AA/+AA//AA/+AAf8AAAIAAAAAAAAAAAAAAAAA");
+ if (s=="calendar" || s=="etar") return atob("GBiBAAAAAAAAAAAAAA//8B//+BgAGBgAGBgAGB//+B//+B//+B9m2B//+B//+Btm2B//+B//+Btm+B//+B//+A//8AAAAAAAAAAAAA==");
if (s=="corona-warn") return atob("GBgBAAAAABwAAP+AAf/gA//wB/PwD/PgDzvAHzuAP8EAP8AAPAAAPMAAP8AAH8AAHzsADzuAB/PAB/PgA//wAP/gAH+AAAwAAAAA");
if (s=="discord") return atob("GBgBAAAAAAAAAAAAAIEABwDgDP8wH//4H//4P//8P//8P//8Pjx8fhh+fzz+f//+f//+e//ePH48HwD4AgBAAAAAAAAAAAAAAAAA");
if (s=="facebook" || s=="messenger") return atob("GBiBAAAAAAAAAAAYAAD/AAP/wAf/4A/48A/g8B/g+B/j+B/n+D/n/D8A/B8A+B+B+B/n+A/n8A/n8Afn4APnwADnAAAAAAAAAAAAAA==");
- if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA==");
- if (s=="hangouts") return atob("FBaBAAH4AH/gD/8B//g//8P//H5n58Y+fGPnxj5+d+fmfj//4//8H//B//gH/4A/8AA+AAHAABgAAAA=");
+ if (s=="gmx") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEJmfmd8Zuc85v847/88Z9s8fttmHIHiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ if (s=="google") return atob("GBiBAAAAAAD/AAP/wAf/4A/D4B8AwDwAADwAAHgAAHgAAHAAAHAH/nAH/nAH/ngH/ngAHjwAPDwAfB8A+A/D8Af/4AP/wAD/AAAAAA==");
+ if (s=="google home") return atob("GBiCAAAAAAAAAAAAAAAAAAAAAoAAAAAACqAAAAAAKqwAAAAAqroAAAACquqAAAAKq+qgAAAqr/qoAACqv/6qAAKq//+qgA6r///qsAqr///6sAqv///6sAqv///6sAqv///6sA6v///6sA6v///qsA6qqqqqsA6qqqqqsA6qqqqqsAP7///vwAAAAAAAAAAAAAAAAA=="); // 2 bit unpaletted
if (s=="home assistant") return atob("FhaBAAAAAADAAAeAAD8AAf4AD/3AfP8D7fwft/D/P8ec572zbzbNsOEhw+AfD8D8P4fw/z/D/P8P8/w/z/AAAAA=");
if (s=="instagram") return atob("GBiBAAAAAAAAAAAAAAAAAAP/wAYAYAwAMAgAkAh+EAjDEAiBEAiBEAiBEAiBEAjDEAh+EAgAEAwAMAYAYAP/wAAAAAAAAAAAAAAAAA==");
if (s=="kalender") return atob("GBgBBgBgBQCgff++RQCiRgBiQAACf//+QAACQAACR//iRJkiRIEiR//iRNsiRIEiRJkiR//iRIEiRIEiR//iQAACQAACf//+AAAA");
if (s=="lieferando") return atob("GBgBABgAAH5wAP9wAf/4A//4B//4D//4H//4P/88fV8+fV4//V4//Vw/HVw4HVw4HBg4HBg4HBg4HDg4Hjw4Hj84Hj44Hj44Hj44");
+ if (s=="mattermost") return atob("GBgBAAAAAPAAA+EAB4MADgcYHAcYOA8MOB8OeD8GcD8GcH8GcD8HcD8HeBwHeAAOfAAOfgAePwA8P8D8H//4D//wB//gAf/AAH4A");
+ if (s=="n26") return atob("GBgBAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAOIAAOIAAPIAANoAANoAAM4AAMYAAMYAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAA");
+ if (s=="nextbike") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAACAfgDAPwDAP4HAH4N4H8f8D82GMd8CMDsDMGMDMGGGGMHOD4D8AAAAAAAAAAAAAAAAAAAAAAAA");
if (s=="nina") return atob("GBgBAAAABAAQCAAICAAIEAAEEgAkJAgSJBwSKRxKSj4pUn8lVP+VVP+VUgAlSgApKQBKJAASJAASEgAkEAAECAAICAAIBAAQAAAA");
if (s=="outlook mail") return atob("HBwBAAAAAAAAAAAIAAAfwAAP/gAB/+AAP/5/A//v/D/+/8P/7/g+Pv8Dye/gPd74w5znHDnOB8Oc4Pw8nv/Dwe/8Pj7/w//v/D/+/8P/7/gf/gAA/+AAAfwAAACAAAAAAAAAAAA=");
+ if (s=="paypal") return atob("GBgBAAAAAAAAAAAAAf+AAf/AAf/gA//gA//gA//wA//wA//wA//wB//wB//wB//gB/+AB/gAB/gAB/gAAPgAAPgAAAAAAAAAAAAA");
if (s=="phone") return atob("FxeBABgAAPgAAfAAB/AAD+AAH+AAP8AAP4AAfgAA/AAA+AAA+AAA+AAB+AAB+AAB+OAB//AB//gB//gA//AA/8AAf4AAPAA=");
if (s=="post & dhl") return atob("GBgBAPgAE/5wMwZ8NgN8NgP4NgP4HgP4HgPwDwfgD//AB/+AAf8AAAAABs7AHcdgG4MwAAAAGESAFESAEkSAEnyAEkSAFESAGETw");
if (s=="signal") return atob("GBgBAAAAAGwAAQGAAhggCP8QE//AB//oJ//kL//wD//0D//wT//wD//wL//0J//kB//oA//ICf8ABfxgBYBAADoABMAABAAAAAAA");
if (s=="skype") return atob("GhoBB8AAB//AA//+Af//wH//+D///w/8D+P8Afz/DD8/j4/H4fP5/A/+f4B/n/gP5//B+fj8fj4/H8+DB/PwA/x/A/8P///B///gP//4B//8AD/+AAA+AA==");
if (s=="slack") return atob("GBiBAAAAAAAAAABAAAHvAAHvAADvAAAPAB/PMB/veD/veB/mcAAAABzH8B3v+B3v+B3n8AHgAAHuAAHvAAHvAADGAAAAAAAAAAAAAA==");
if (s=="snapchat") return atob("GBgBAAAAAAAAAH4AAf+AAf+AA//AA//AA//AA//AA//AH//4D//wB//gA//AB//gD//wH//4f//+P//8D//wAf+AAH4AAAAAAAAA");
+ if (s=="steam") return atob("GBgBAAAAAAAAAAAAAAAAAAAAAAfgAAwwAAvQABvQABvQADvQgDww4H/g+f8A/zwAf9gAH9AAB8AAACAAAcAAAAAAAAAAAAAAAAAA");
if (s=="teams") return atob("GBgBAAAAAAAAAAQAAB4AAD8IAA8cP/M+f/scf/gIeDgAfvvefvvffvvffvvffvvff/vff/veP/PeAA/cAH/AAD+AAD8AAAQAAAAA");
if (s=="telegram" || s=="telegram foss") return atob("GBiBAAAAAAAAAAAAAAAAAwAAHwAA/wAD/wAf3gD/Pgf+fh/4/v/z/P/H/D8P/Acf/AM//AF/+AF/+AH/+ADz+ADh+ADAcAAAMAAAAA==");
if (s=="threema") return atob("GBjB/4Yx//8AAAAAAAAAAAAAfgAB/4AD/8AH/+AH/+AP//AP2/APw/APw/AHw+AH/+AH/8AH/4AH/gAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=");
- if (s=="to do") return atob("GBgBAAAAAAAAAAAwAAB4AAD8AAH+AAP/DAf/Hg//Px/+f7/8///4///wf//gP//AH/+AD/8AB/4AA/wAAfgAAPAAAGAAAAAAAAAA");
+ if (s=="to do" || s=="opentasks") return atob("GBgBAAAAAAAAAAAwAAB4AAD8AAH+AAP/DAf/Hg//Px/+f7/8///4///wf//gP//AH/+AD/8AB/4AA/wAAfgAAPAAAGAAAAAAAAAA");
if (s=="twitch") return atob("GBgBH//+P//+P//+eAAGeAAGeAAGeDGGeDOGeDOGeDOGeDOGeDOGeDOGeAAOeAAOeAAcf4/4f5/wf7/gf//Af/+AA/AAA+AAAcAA");
if (s=="twitter") return atob("GhYBAABgAAB+JgA/8cAf/ngH/5+B/8P8f+D///h///4f//+D///g///wD//8B//+AP//gD//wAP/8AB/+AB/+AH//AAf/AAAYAAA");
+ if (s=="warnapp") return atob("GBgBAAAAAAAAAAAAAH4AAP8AA//AA//AD//gP//gf//4f//+/+P+/8H//8n//4n/fxh/fzg+Pj88Dn44AA4AAAwAAAwAAAgAAAAA");
if (s=="whatsapp") return atob("GBiBAAB+AAP/wAf/4A//8B//+D///H9//n5//nw//vw///x///5///4///8e//+EP3/APn/wPn/+/j///H//+H//8H//4H//wMB+AA==");
if (s=="wordfeud") return atob("GBgCWqqqqqqlf//////9v//////+v/////++v/////++v8///Lu+v8///L++v8///P/+v8v//P/+v9v//P/+v+fx/P/+v+Pk+P/+v/PN+f/+v/POuv/+v/Ofdv/+v/NvM//+v/I/Y//+v/k/k//+v/i/w//+v/7/6//+v//////+v//////+f//////9Wqqqqqql");
- if (s=="youtube") return atob("GBgBAAAAAAAAAAAAAAAAAf8AH//4P//4P//8P//8P5/8P4/8f4P8f4P8P4/8P5/8P//8P//8P//4H//4Af8AAAAAAAAAAAAAAAAA");
+ if (s=="youtube" || s=="newpipe") return atob("GBgBAAAAAAAAAAAAAAAAAf8AH//4P//4P//8P//8P5/8P4/8f4P8f4P8P4/8P5/8P//8P//8P//4H//4Af8AAAAAAAAAAAAAAAAA");
if (msg.id=="music") return atob("FhaBAH//+/////////////h/+AH/4Af/gB/+H3/7/f/v9/+/3/7+f/vB/w8H+Dwf4PD/x/////////////3//+A=");
// if (s=="sms message" || s=="mail" || s=="gmail") // .. default icon (below)
- return atob("HBKBAD///8H///iP//8cf//j4//8f5//j/x/8//j/H//H4//4PB//EYj/44HH/Hw+P4//8fH//44///xH///g////A==");
+ return atob("FhKBAH//+P//yf/+c//z5/+fz/z/n+f/Pz/+ef/8D///////////////////////f//4///A");
};
exports.getMessageImageCol = function(msg,def) {
+ let iconColorMode = (require('Storage').readJSON("messages.settings.json", 1) || {}).iconColorMode;
+ if (iconColorMode == 'mono')
+ return g.theme.fg;
+ const s = (("string"=== typeof msg) ? msg : (msg.src || "")).toLowerCase();
return {
// generic colors, using B2-safe colors
- "alarm": "#fff",
+ // DO NOT USE BLACK OR WHITE HERE, just leave the declaration out and then the theme's fg color will be used
+ "airbnb": "#f00",
"mail": "#ff0",
"music": "#f0f",
"phone": "#0f0",
@@ -166,31 +192,40 @@ exports.getMessageImageCol = function(msg,def) {
// brands, according to https://www.schemecolor.com/?s (picking one for multicolored logos)
// all dithered on B2, but we only use the color for the icons. (Could maybe pick the closest 3-bit color for B2?)
"bibel": "#54342c",
+ "bring": "#455a64",
"discord": "#738adb",
+ "etar": "#36a18b",
"facebook": "#4267b2",
"gmail": "#ea4335",
+ "gmx": "#1c449b",
+ "google": "#4285F4",
"google home": "#fbbc05",
- "hangouts": "#1ba261",
- "home assistant": "#fff", // ha-blue is #41bdf5, but that's the background
+// "home assistant": "#41bdf5", // ha-blue is #41bdf5, but that's the background
"instagram": "#dd2a7b",
- "liferando": "#ee5c00",
+ "lieferando": "#ee5c00",
"messenger": "#0078ff",
+ "mattermost": "#00f",
+ "n26": "#36a18b",
+ "nextbike": "#00f",
+ "newpipe": "#f00",
"nina": "#e57004",
+ "opentasks": "#409f8f",
"outlook mail": "#0072c6",
+ "paypal": "#003087",
"post & dhl": "#f2c101",
"signal": "#00f",
"skype": "#00aff0",
"slack": "#e51670",
"snapchat": "#ff0",
+ "steam": "#171a21",
"teams": "#464eb8",
"telegram": "#0088cc",
"telegram foss": "#0088cc",
- "threema": "#000",
"to do": "#3999e5",
"twitch": "#6441A4",
"twitter": "#1da1f2",
"whatsapp": "#4fce5d",
"wordfeud": "#e7d3c7",
"youtube": "#f00",
- }[(msg.src||"").toLowerCase()]||(def !== undefined?def:g.theme.fg);
+ }[s]||(def !== undefined?def:g.theme.fg);
};
diff --git a/apps/messages/metadata.json b/apps/messages/metadata.json
index 4bc9f59e4..da2e0945a 100644
--- a/apps/messages/metadata.json
+++ b/apps/messages/metadata.json
@@ -1,7 +1,7 @@
{
"id": "messages",
"name": "Messages",
- "version": "0.39",
+ "version": "0.50",
"description": "App to display notifications from iOS and Gadgetbridge/Android",
"icon": "app.png",
"type": "app",
@@ -10,6 +10,7 @@
"readme": "README.md",
"storage": [
{"name":"messages.app.js","url":"app.js"},
+ {"name":"messages.new.js","url":"app-newmessage.js"},
{"name":"messages.settings.js","url":"settings.js"},
{"name":"messages.img","url":"app-icon.js","evaluate":true},
{"name":"messages.wid.js","url":"widget.js"},
diff --git a/apps/messages/screenshot-notify.gif b/apps/messages/screenshot-notify.gif
index 3d0ed0b32..e5cc669bd 100644
Binary files a/apps/messages/screenshot-notify.gif and b/apps/messages/screenshot-notify.gif differ
diff --git a/apps/messages/settings.js b/apps/messages/settings.js
index 1b166dcf6..0edb17797 100644
--- a/apps/messages/settings.js
+++ b/apps/messages/settings.js
@@ -1,9 +1,15 @@
(function(back) {
+ const iconColorModes = ['color', 'mono'];
+
function settings() {
let settings = require('Storage').readJSON("messages.settings.json", true) || {};
if (settings.vibrate===undefined) settings.vibrate=":";
+ if (settings.vibrateCalls===undefined) settings.vibrateCalls=":";
if (settings.repeat===undefined) settings.repeat=4;
+ if (settings.vibrateTimeout===undefined) settings.vibrateTimeout=60;
if (settings.unreadTimeout===undefined) settings.unreadTimeout=60;
+ if (settings.maxMessages===undefined) settings.maxMessages=3;
+ if (settings.iconColorMode === undefined) settings.iconColorMode = iconColorModes[0];
settings.unlockWatch=!!settings.unlockWatch;
settings.openMusic=!!settings.openMusic;
settings.maxUnreadTimeout=240;
@@ -20,12 +26,19 @@
"" : { "title" : /*LANG*/"Messages" },
"< Back" : back,
/*LANG*/'Vibrate': require("buzz_menu").pattern(settings().vibrate, v => updateSetting("vibrate", v)),
+ /*LANG*/'Vibrate for calls': require("buzz_menu").pattern(settings().vibrateCalls, v => updateSetting("vibrateCalls", v)),
/*LANG*/'Repeat': {
value: settings().repeat,
min: 0, max: 10,
format: v => v?v+"s":/*LANG*/"Off",
onchange: v => updateSetting("repeat", v)
},
+ /*LANG*/'Vibrate timer': {
+ value: settings().vibrateTimeout,
+ min: 0, max: settings().maxUnreadTimeout, step : 10,
+ format: v => v?v+"s":/*LANG*/"Off",
+ onchange: v => updateSetting("vibrateTimeout", v)
+ },
/*LANG*/'Unread timer': {
value: settings().unreadTimeout,
min: 0, max: settings().maxUnreadTimeout, step : 10,
@@ -40,24 +53,35 @@
},
/*LANG*/'Auto-Open Music': {
value: !!settings().openMusic,
- format: v => v?/*LANG*/'Yes':/*LANG*/'No',
onchange: v => updateSetting("openMusic", v)
},
/*LANG*/'Unlock Watch': {
value: !!settings().unlockWatch,
- format: v => v?/*LANG*/'Yes':/*LANG*/'No',
onchange: v => updateSetting("unlockWatch", v)
},
/*LANG*/'Flash Icon': {
value: !!settings().flash,
- format: v => v?/*LANG*/'Yes':/*LANG*/'No',
onchange: v => updateSetting("flash", v)
},
/*LANG*/'Quiet mode disables auto-open': {
value: !!settings().quietNoAutOpn,
- format: v => v?/*LANG*/'Yes':/*LANG*/'No',
onchange: v => updateSetting("quietNoAutOpn", v)
},
+ /*LANG*/'Disable auto-open': {
+ value: !!settings().noAutOpn,
+ onchange: v => updateSetting("noAutOpn", v)
+ },
+ /*LANG*/'Widget messages': {
+ value:0|settings().maxMessages,
+ min: 1, max: 5,
+ onchange: v => updateSetting("maxMessages", v)
+ },
+ /*LANG*/'Icon color mode': {
+ value: Math.max(0,iconColorModes.indexOf(settings().iconColorMode)),
+ min: 0, max: iconColorModes.length - 1,
+ format: v => iconColorModes[v],
+ onchange: v => updateSetting("iconColorMode", iconColorModes[v])
+ }
};
E.showMenu(mainmenu);
-})
+});
diff --git a/apps/messages/widget.js b/apps/messages/widget.js
index 014faf12e..99d98ca77 100644
--- a/apps/messages/widget.js
+++ b/apps/messages/widget.js
@@ -1,5 +1,12 @@
-WIDGETS["messages"]={area:"tl", width:0, iconwidth:24,
-draw:function(recall) {
+(() => {
+
+function filterMessages(msgs) {
+ return msgs.filter(msg => msg.new && msg.id != "music")
+ .map(m => m.src) // we only need this for icon/color
+ .filter((msg, i, arr) => arr.findIndex(nmsg => msg.src == nmsg.src) == i);
+}
+
+WIDGETS["messages"]={area:"tl", width:0, draw:function(recall) {
// If we had a setTimeout queued from the last time we were called, remove it
if (WIDGETS["messages"].i) {
clearTimeout(WIDGETS["messages"].i);
@@ -8,42 +15,67 @@ draw:function(recall) {
Bangle.removeListener('touch', this.touch);
if (!this.width) return;
var c = (Date.now()-this.t)/1000;
- let settings = require('Storage').readJSON("messages.settings.json", true) || {};
- if (settings.flash===undefined) settings.flash = true;
+ let settings = Object.assign({flash:true, maxMessages:3, repeat:4, vibrateTimeout:60},require('Storage').readJSON("messages.settings.json", true) || {});
if (recall !== true || settings.flash) {
+ var msgsShown = E.clip(this.msgs.length, 0, settings.maxMessages);
g.reset().clearRect(this.x, this.y, this.x+this.width, this.y+23);
- g.drawImage(settings.flash && (c&1) ? atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+DAADDAADDAADDwAPD8A/DOBzDDn/DA//DAHvDAPvjAPvjAPvjAPvh///gf/vAAD+AAB8AAAAA==") : atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+D///D///A//8CP/xDj/HD48DD+B8D/D+D/3vD/vvj/vvj/vvj/vvh/v/gfnvAAD+AAB8AAAAA=="), this.x, this.y-1);
+ for(let i = 0;i < msgsShown;i++) {
+ const msg = this.msgs[i];
+ const colors = [g.theme.bg, g.setColor(require("messages").getMessageImageCol(msg)).getColor()];
+ if (settings.flash && (c&1)) {
+ if (colors[1] == g.theme.fg) {
+ colors.reverse();
+ } else {
+ colors[1] = g.theme.fg;
+ }
+ }
+ g.setColor(colors[1]).setBgColor(colors[0]);
+ // draw the icon, or '...' if too many messages
+ g.drawImage(i == (settings.maxMessages - 1) && this.msgs.length > settings.maxMessages ? atob("EASBAGGG88/zz2GG") : require("messages").getMessageImage(msg),
+ this.x + 12 + i * 24, this.y + 12, {rotate:0/*force centering*/});
+ }
}
- if (settings.repeat===undefined) settings.repeat = 4;
- if (c<120 && settings.repeat>0 && (Date.now()-this.l)>settings.repeat*1000) {
+ if (csettings.repeat*1000) { // the period between vibrations
this.l = Date.now();
WIDGETS["messages"].buzz(); // buzz every 4 seconds
}
WIDGETS["messages"].i=setTimeout(()=>WIDGETS["messages"].draw(true), 1000);
if (process.env.HWVERSION>1) Bangle.on('touch', this.touch);
-},show:function(quiet) {
- WIDGETS["messages"].t=Date.now(); // first time
- WIDGETS["messages"].l=Date.now()-10000; // last buzz
- if (quiet) WIDGETS["messages"].t -= 500000; // if quiet, set last time in the past so there is no buzzing
- WIDGETS["messages"].width=this.iconwidth;
+},update:function(rawMsgs, quiet) {
+ const settings = Object.assign({maxMessages:3},require('Storage').readJSON("messages.settings.json", true) || {});
+ this.msgs = filterMessages(rawMsgs);
+ if (this.msgs.length === 0) {
+ delete this.t;
+ delete this.l;
+ } else {
+ this.t=Date.now(); // first time
+ this.l=Date.now()-10000; // last buzz
+ if (quiet) this.t -= 500000; // if quiet, set last time in the past so there is no buzzing
+ }
+ this.width = 24 * E.clip(this.msgs.length, 0, settings.maxMessages);
Bangle.drawWidgets();
-},hide:function() {
- delete WIDGETS["messages"].t;
- delete WIDGETS["messages"].l;
- WIDGETS["messages"].width=0;
- Bangle.drawWidgets();
-},buzz:function() {
- if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return new Promise((success) => { success(); }); // never buzz during Quiet Mode
- return require("buzz").pattern((require('Storage').readJSON("messages.settings.json", true) || {}).vibrate || ":");
+},buzz:function(msgSrc) { // return a promise
+ if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return Promise.,resolve(); // never buzz during Quiet Mode
+ var pattern;
+ if (msgSrc != undefined && msgSrc.toLowerCase() == "phone") {
+ // special vibration pattern for incoming calls
+ pattern = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrateCalls;
+ } else {
+ pattern = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrate;
+ }
+ if (pattern === undefined) { pattern = ":"; } // pattern may be "", so we can't use || ":" here
+ return require("buzz").pattern(pattern);
},touch:function(b,c) {
var w=WIDGETS["messages"];
- if (!w||!w.width||c.xw.x+w.width||c.yw.y+w.iconwidth) return;
+ if (!w||!w.width||c.xw.x+w.width||c.yw.y+24) return;
load("messages.app.js");
}};
+
/* We might have returned here if we were in the Messages app for a
message but then the watch was never viewed. In that case we don't
want to buzz but should still show that there are unread messages. */
-if (global.MESSAGES===undefined) (function() {
- var messages = require("Storage").readJSON("messages.json",1)||[];
- if (messages.some(m=>m.new&&m.id!="music")) WIDGETS["messages"].show(true);
+if (global.MESSAGES===undefined)
+ WIDGETS["messages"].update(require("messages").getMessages(), true);
})();
diff --git a/apps/messagesmusic/ChangeLog b/apps/messagesmusic/ChangeLog
index 5560f00bc..9f4cafb0e 100644
--- a/apps/messagesmusic/ChangeLog
+++ b/apps/messagesmusic/ChangeLog
@@ -1 +1,2 @@
0.01: New App!
+0.02: Remove one line of code that didn't do anything other than in some instances hinder the function of the app.
diff --git a/apps/messagesmusic/README.md b/apps/messagesmusic/README.md
index 7aa9209df..85608118d 100644
--- a/apps/messagesmusic/README.md
+++ b/apps/messagesmusic/README.md
@@ -6,9 +6,9 @@ Making the music controls accessible this way lets one start a music stream on t
It is suggested to use Messages Music along side the app Quick Launch.
-Messages Music v0.01 has been verified to work with Messages v0.31 on Bangle.js 2 fw2v13.
+Messages Music v0.02 has been verified to work with Messages v0.41 on Bangle.js 2 fw2v14.
-Music Messages should work with forks of the original Messages app. At least as long as functions pushMessage() in the library and showMusicMessage() in app.js hasn't been changed too much.
+Messages Music should work with forks of the original Messages app. At least as long as functions pushMessage() in the library and showMusicMessage() in app.js hasn't been changed too much.
Messages app is created by Gordon Williams with contributions from [Jeroen Peters](https://github.com/jeroenpeters1986).
diff --git a/apps/messagesmusic/app.js b/apps/messagesmusic/app.js
index a6f7e075e..27f3f6e4d 100644
--- a/apps/messagesmusic/app.js
+++ b/apps/messagesmusic/app.js
@@ -1,7 +1,6 @@
let showMusic = () => {
Bangle.CLOCK = 1; // To pass condition in messages library
require('messages').pushMessage({"t":"add","artist":" ","album":" ","track":" ","dur":0,"c":-1,"n":-1,"id":"music","title":"Music","state":"play","new":true});
- Bangle.CLOCK = undefined;
};
var settings = require('Storage').readJSON('messages.settings.json', true) || {}; //read settings if they exist else set to empty dict
diff --git a/apps/messagesmusic/metadata.json b/apps/messagesmusic/metadata.json
index edc6835ed..c29ffbc34 100644
--- a/apps/messagesmusic/metadata.json
+++ b/apps/messagesmusic/metadata.json
@@ -1,7 +1,7 @@
{
"id": "messagesmusic",
"name":"Messages Music",
- "version":"0.01",
+ "version":"0.02",
"description": "Uses Messages library to push a music message which in turn displays Messages app music controls",
"icon":"app.png",
"type": "app",
diff --git a/apps/miclock/ChangeLog b/apps/miclock/ChangeLog
index e92bad2e3..d1ac3e388 100644
--- a/apps/miclock/ChangeLog
+++ b/apps/miclock/ChangeLog
@@ -2,3 +2,4 @@
0.03: Localization
0.04: move jshint to the top
0.05: Use Bangle.setUI for button/launcher handling
+0.06: Tell clock widgets to hide.
diff --git a/apps/miclock/clock-mixed.js b/apps/miclock/clock-mixed.js
index b3d6bea8d..cb3235406 100644
--- a/apps/miclock/clock-mixed.js
+++ b/apps/miclock/clock-mixed.js
@@ -77,11 +77,13 @@ Bangle.on('lcdPower', function(on) {
drawMixedClock();
});
+// Show launcher when button pressed
+Bangle.setUI("clock");
+
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
setInterval(drawMixedClock, 5E3);
drawMixedClock();
-// Show launcher when button pressed
-Bangle.setUI("clock");
+
diff --git a/apps/miclock/metadata.json b/apps/miclock/metadata.json
index 6eece46b0..2c216dc33 100644
--- a/apps/miclock/metadata.json
+++ b/apps/miclock/metadata.json
@@ -1,7 +1,7 @@
{
"id": "miclock",
"name": "Mixed Clock",
- "version": "0.05",
+ "version": "0.06",
"description": "A mix of analog and digital Clock",
"icon": "clock-mixed.png",
"type": "clock",
diff --git a/apps/minimal_clock/ChangeLog b/apps/minimal_clock/ChangeLog
new file mode 100644
index 000000000..54ee389e3
--- /dev/null
+++ b/apps/minimal_clock/ChangeLog
@@ -0,0 +1,3 @@
+...
+0.03: First update with ChangeLog Added
+0.04: Tell clock widgets to hide.
diff --git a/apps/minimal_clock/app.js b/apps/minimal_clock/app.js
index d78790347..47eca3c66 100644
--- a/apps/minimal_clock/app.js
+++ b/apps/minimal_clock/app.js
@@ -3,6 +3,7 @@
let outerRadius = Math.min(CenterX,CenterY) * 0.9;
+ Bangle.setUI('clock');
Bangle.loadWidgets();
/**** updateClockFaceSize ****/
@@ -225,6 +226,5 @@
}
});
- Bangle.loadWidgets();
- Bangle.setUI('clock');
+ Bangle.loadWidgets();
diff --git a/apps/minimal_clock/metadata.json b/apps/minimal_clock/metadata.json
index 1702d97a9..3089780ce 100644
--- a/apps/minimal_clock/metadata.json
+++ b/apps/minimal_clock/metadata.json
@@ -1,7 +1,7 @@
{ "id": "minimal_clock",
"name": "Minimal Analog Clock",
"shortName":"Minimal Clock",
- "version":"0.03",
+ "version":"0.04",
"description": "a minimal analog clock - just with some hands and no clock face",
"icon": "app-icon.png",
"type": "clock",
diff --git a/apps/minionclk/ChangeLog b/apps/minionclk/ChangeLog
index a8b6efc81..5949a786d 100644
--- a/apps/minionclk/ChangeLog
+++ b/apps/minionclk/ChangeLog
@@ -3,3 +3,4 @@
0.03: Fixed rendering for Espruino v2.06
0.04: Fixed overlapped rendering of dates
0.05: Use Bangle.setUI for button/launcher handling
+0.06: Tell clock widgets to hide.
diff --git a/apps/minionclk/app b/apps/minionclk/app
new file mode 100644
index 000000000..e69de29bb
diff --git a/apps/minionclk/app.js b/apps/minionclk/app.js
index 9648e3d89..c61f8d3bf 100644
--- a/apps/minionclk/app.js
+++ b/apps/minionclk/app.js
@@ -78,8 +78,10 @@ Bangle.on('lcdPower', (on) => {
}
});
+// Show launcher when button pressed
+Bangle.setUI("clock");
+
Bangle.loadWidgets();
startDrawing();
-// Show launcher when button pressed
-Bangle.setUI("clock");
+
diff --git a/apps/minionclk/metadata.json b/apps/minionclk/metadata.json
index 44fc2a82d..4df2ddc6b 100644
--- a/apps/minionclk/metadata.json
+++ b/apps/minionclk/metadata.json
@@ -1,7 +1,7 @@
{
"id": "minionclk",
"name": "Minion clock",
- "version": "0.05",
+ "version": "0.06",
"description": "Minion themed clock.",
"icon": "minionclk.png",
"type": "clock",
diff --git a/apps/multitimer/ChangeLog b/apps/multitimer/ChangeLog
index 9b60f403a..9a2ab0ff4 100644
--- a/apps/multitimer/ChangeLog
+++ b/apps/multitimer/ChangeLog
@@ -1,2 +1,3 @@
0.01: Initial version
0.02: Update for time_utils module
+0.03: Use default Bangle formatter for booleans
diff --git a/apps/multitimer/app.js b/apps/multitimer/app.js
index e5d77d860..8832d1a25 100644
--- a/apps/multitimer/app.js
+++ b/apps/multitimer/app.js
@@ -267,7 +267,6 @@ function editTimer(idx, a) {
},
"Enabled": {
value: a.on,
- format: v => v ? "On" : "Off",
onchange: v => a.on = v
},
"Hours": {
@@ -293,7 +292,6 @@ function editTimer(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),
@@ -535,7 +533,6 @@ function editDOW(dow, onchange) {
var dayOfWeek = require("locale").dow({ getDay: () => i });
menu[dayOfWeek] = {
value: !!(dow&(1< v ? "Yes" : "No",
onchange: v => v ? dow |= 1< v ? "On" : "Off",
onchange: v => a.on = v
},
"Hours": {
@@ -614,7 +610,6 @@ function editAlarm(idx, a) {
},
"Repeat": {
value: a.rp,
- format: v => v ? "Yes" : "No",
onchange: v => a.rp = v
},
"Days": {
@@ -623,13 +618,11 @@ function 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": {
diff --git a/apps/multitimer/metadata.json b/apps/multitimer/metadata.json
index abb958b90..ee77d2ecb 100644
--- a/apps/multitimer/metadata.json
+++ b/apps/multitimer/metadata.json
@@ -1,7 +1,7 @@
{
"id": "multitimer",
"name": "Multi Timer",
- "version": "0.02",
+ "version": "0.03",
"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": [
diff --git a/apps/mysticclock/ChangeLog b/apps/mysticclock/ChangeLog
index b486a29a1..cd91abe00 100644
--- a/apps/mysticclock/ChangeLog
+++ b/apps/mysticclock/ChangeLog
@@ -1,2 +1,3 @@
1.00: First published version.
1.01: Use Bangle.setUI for Launcher/buttons
+1.02: Tell clock widgets to hide.
diff --git a/apps/mysticclock/metadata.json b/apps/mysticclock/metadata.json
index 571a55ecd..bd2df2f8d 100644
--- a/apps/mysticclock/metadata.json
+++ b/apps/mysticclock/metadata.json
@@ -1,7 +1,7 @@
{
"id": "mysticclock",
"name": "Mystic Clock",
- "version": "1.01",
+ "version": "1.02",
"description": "A retro-inspired watchface featuring time, date, and an interactive data display line.",
"icon": "mystic-clock.png",
"type": "clock",
diff --git a/apps/mysticclock/mystic-clock-app.js b/apps/mysticclock/mystic-clock-app.js
index 2d95633fe..d7f4ab1c3 100644
--- a/apps/mysticclock/mystic-clock-app.js
+++ b/apps/mysticclock/mystic-clock-app.js
@@ -189,6 +189,13 @@ Bangle.on('touch', (button) => {
if (button === 3 && Bangle.isLCDOn()) Bangle.setLCDPower(false);
});
+// Show launcher when button pressed
+Bangle.setUI("clockupdown", btn=>{
+ if (btn<0) prevInfo();
+ if (btn>0) nextInfo();
+ drawAll();
+});
+
// clean app screen
g.clear();
Bangle.loadWidgets();
@@ -200,9 +207,3 @@ if (Bangle.isLCDOn()) {
drawAll(); // draw immediately
}
-// Show launcher when button pressed
-Bangle.setUI("clockupdown", btn=>{
- if (btn<0) prevInfo();
- if (btn>0) nextInfo();
- drawAll();
-});
diff --git a/apps/nato/ChangeLog b/apps/nato/ChangeLog
new file mode 100644
index 000000000..5560f00bc
--- /dev/null
+++ b/apps/nato/ChangeLog
@@ -0,0 +1 @@
+0.01: New App!
diff --git a/apps/ncrclk/ChangeLog b/apps/ncrclk/ChangeLog
index 31e5d42c8..0c326161a 100644
--- a/apps/ncrclk/ChangeLog
+++ b/apps/ncrclk/ChangeLog
@@ -1,2 +1,3 @@
0.01: A copy of the analogimgclk to work for NodeConf Remote
0.02: Use Bangle.setUI for button/launcher handling
+0.03: Tell clock widgets to hide.
diff --git a/apps/ncrclk/app.js b/apps/ncrclk/app.js
index 16724fa5e..805ac1b95 100644
--- a/apps/ncrclk/app.js
+++ b/apps/ncrclk/app.js
@@ -120,10 +120,10 @@ Bangle.on('lcdPower', (on) => {
}
});
+// Show launcher when button pressed
+Bangle.setUI("clock");
+
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
drawHands(true);
-
-// Show launcher when button pressed
-Bangle.setUI("clock");
diff --git a/apps/ncrclk/metadata.json b/apps/ncrclk/metadata.json
index b50b554e1..fdab77450 100644
--- a/apps/ncrclk/metadata.json
+++ b/apps/ncrclk/metadata.json
@@ -2,7 +2,7 @@
"id": "ncrclk",
"name": "NCR Clock",
"shortName": "NCR Clock",
- "version": "0.02",
+ "version": "0.03",
"description": "NodeConf Remote clock",
"icon": "app.png",
"type": "clock",
diff --git a/apps/noteify/ChangeLog b/apps/noteify/ChangeLog
index ec66c5568..d7bc46dcd 100644
--- a/apps/noteify/ChangeLog
+++ b/apps/noteify/ChangeLog
@@ -1 +1,2 @@
0.01: Initial version
+0.02: Use default Bangle formatter for booleans
diff --git a/apps/noteify/README.md b/apps/noteify/README.md
index c846709de..dbdceb399 100644
--- a/apps/noteify/README.md
+++ b/apps/noteify/README.md
@@ -1,6 +1,6 @@
# WARNING
-This app uses the [Scheduler library](https://banglejs.com/apps/?id=sched) and requires a keyboard such as [Swipe keyboard](https://banglejs.com/apps/?id=kbswipe).
+This app uses the [Scheduler library](https://banglejs.com/apps/?id=sched) and requires a [keyboard library](https://banglejs.com/apps/?c=textinput#).
## Usage
diff --git a/apps/noteify/app.js b/apps/noteify/app.js
index 2b3ee64f0..02d43c065 100644
--- a/apps/noteify/app.js
+++ b/apps/noteify/app.js
@@ -171,7 +171,6 @@ function editDOW(dow, onchange) {
var dayOfWeek = require("locale").dow({ getDay: () => i });
menu[dayOfWeek] = {
value: !!(dow&(1< v ? "Yes" : "No",
onchange: v => v ? dow |= 1<v?"On":"Off",
onchange: v=>a.on=v
},
'Repeat': {
value: a.rp,
- format: v=>v?"Yes":"No",
onchange: v=>a.rp=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
}
};
@@ -278,7 +274,6 @@ function editTimer(alarmIndex, alarm) {
},
'Enabled': {
value: a.on,
- format: v=>v?"On":"Off",
onchange: v=>a.on=v
},
'Vibrate': require("buzz_menu").pattern(a.vibrate, v => a.vibrate=v ),
diff --git a/apps/noteify/metadata.json b/apps/noteify/metadata.json
index 7e897d1f0..fbd5a88f1 100644
--- a/apps/noteify/metadata.json
+++ b/apps/noteify/metadata.json
@@ -1,11 +1,11 @@
{
"id": "noteify",
"name": "Noteify",
- "version": "0.01",
+ "version": "0.02",
"description": "Write notes using an onscreen keyboard and use them as custom messages for alarms or timers.",
"icon": "app.png",
"tags": "tool,alarm",
- "supports": ["BANGLEJS2"],
+ "supports": ["BANGLEJS", "BANGLEJS2"],
"readme": "README.md",
"storage": [
{"name":"noteify.app.js","url":"app.js"},
diff --git a/apps/novaclock/ChangeLog b/apps/novaclock/ChangeLog
new file mode 100644
index 000000000..8b05ff9ec
--- /dev/null
+++ b/apps/novaclock/ChangeLog
@@ -0,0 +1,3 @@
+...
+0.10: First update with ChangeLog Added
+0.11: Tell clock widgets to hide.
diff --git a/apps/novaclock/app.js b/apps/novaclock/app.js
index e5bd37b06..52bee0dbd 100644
--- a/apps/novaclock/app.js
+++ b/apps/novaclock/app.js
@@ -249,13 +249,13 @@ var open = false;
var timemode = true;
var clockmode;
var novaYPos = -7;
+Bangle.setUI("clock");
g.clear();
Bangle.loadWidgets();
Bangle.drawWidgets();
g.drawImage(nova(), -10, -10, {
scale: 2.2
});
-Bangle.setUI("clock");
g.drawImage(star(), 5, -5, {scale:0.8});
g.drawImage(star(), -10, 120, {scale:0.8});
diff --git a/apps/novaclock/metadata.json b/apps/novaclock/metadata.json
index c1dad60a1..69b7627f8 100644
--- a/apps/novaclock/metadata.json
+++ b/apps/novaclock/metadata.json
@@ -3,7 +3,7 @@
"shortName":"Nova Clock",
"icon": "app.png",
"type": "clock",
- "version":"0.1",
+ "version":"0.11",
"description": "A clock inspired by the Kirby series",
"tags": "clock",
"supports": ["BANGLEJS2"],
diff --git a/apps/openstmap/ChangeLog b/apps/openstmap/ChangeLog
index 6cb9d061e..2a6c15a09 100644
--- a/apps/openstmap/ChangeLog
+++ b/apps/openstmap/ChangeLog
@@ -10,3 +10,5 @@
0.10: Improve scale factor calculation to fix scaling issues (#984)
0.11: Add slight offset to OSM data to align it properly (fix #984)
Fix alignment of satellite info text
+0.12: switch to using normal OpenStreetMap tiles (opentopomap was too slow)
+0.13: Use a single image file with 'frames' of data (drastically reduces file count, possibility of >1 map on device)
diff --git a/apps/openstmap/custom.html b/apps/openstmap/custom.html
index 6e79a6e9a..6f611dd86 100644
--- a/apps/openstmap/custom.html
+++ b/apps/openstmap/custom.html
@@ -64,9 +64,10 @@ TODO:
var OSMTILECOUNT = 3; // how many tiles do we download in each direction (Math.floor(MAPSIZE / OSMTILESIZE)+1)
/* Can see possible tiles on http://leaflet-extras.github.io/leaflet-providers/preview/
However some don't allow cross-origin use */
- var TILELAYER = 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png'; // simple, high contrast
- var PREVIEWTILELAYER = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
+ //var TILELAYER = 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png'; // simple, high contrast, TOO SLOW
//var TILELAYER = 'http://a.tile.stamen.com/toner/{z}/{x}/{y}.png'; // black and white
+ var TILELAYER = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
+ var PREVIEWTILELAYER = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
// Create map and try and set the location to where the browser thinks we are
var map = L.map('map').locate({setView: true, maxZoom: 16, enableHighAccuracy:true});
@@ -128,7 +129,7 @@ TODO:
var min = Math.min(rgba[i+0],rgba[i+1],rgba[i+2]);
var max = Math.max(rgba[i+0],rgba[i+1],rgba[i+2]);
var d = max-min;
- if (max<120) { // black
+ if (max<120 || (d<8 && max<215)) { // black, or a darker grey
rgba[i+0]=0;
rgba[i+1]=0;
rgba[i+2]=0;
@@ -147,7 +148,7 @@ TODO:
console.log("Compression options", options);
var w = Math.round(width / TILESIZE);
var h = Math.round(height / TILESIZE);
- var tiles = [];
+ var tiledImage;
for (var y=0;y {
document.getElementById("uploadbuttons").style.display="";
mapFiles = tilesLoaded(ctx, canvas.width, canvas.height);
- mapFiles.unshift({name:"openstmap.json",content:JSON.stringify({
+ mapFiles.unshift({name:"openstmap.0.json",content:JSON.stringify({
imgx : canvas.width,
imgy : canvas.height,
tilesize : TILESIZE,
scale : scale, // how much of Bangle.project(latlon) does one pixel equate to?
lat : centerlatlon.lat,
- lon : centerlatlon.lng
+ lon : centerlatlon.lng,
+ w : Math.round(canvas.width / TILESIZE), // width in tiles
+ h : Math.round(canvas.height / TILESIZE), // height in tiles
+ fn : "openstmap.0.img"
})});
console.log(mapFiles);
});
diff --git a/apps/openstmap/metadata.json b/apps/openstmap/metadata.json
index 2dc9bd427..32093f70e 100644
--- a/apps/openstmap/metadata.json
+++ b/apps/openstmap/metadata.json
@@ -2,7 +2,7 @@
"id": "openstmap",
"name": "OpenStreetMap",
"shortName": "OpenStMap",
- "version": "0.11",
+ "version": "0.13",
"description": "Loads map tiles from OpenStreetMap onto your Bangle.js and displays a map of where you are. Once installed this also adds map functionality to `GPS Recorder` and `Recorder` apps",
"icon": "app.png",
"tags": "outdoors,gps,osm",
diff --git a/apps/openstmap/openstmap.js b/apps/openstmap/openstmap.js
index d995aca25..2bd7d2e2e 100644
--- a/apps/openstmap/openstmap.js
+++ b/apps/openstmap/openstmap.js
@@ -22,7 +22,7 @@ function center() {
*/
-var map = require("Storage").readJSON("openstmap.json");
+var map = require("Storage").readJSON("openstmap.0.json");
map.center = Bangle.project({lat:map.lat,lon:map.lon});
exports.map = map;
exports.lat = map.lat; // actual position of middle of screen
@@ -30,7 +30,7 @@ exports.lon = map.lon; // actual position of middle of screen
var m = exports;
exports.draw = function() {
- var s = require("Storage");
+ var img = require("Storage").read(map.fn);
var cx = g.getWidth()/2;
var cy = g.getHeight()/2;
var p = Bangle.project({lat:m.lat,lon:m.lon});
@@ -41,13 +41,11 @@ exports.draw = function() {
var ty = 0|(iy/map.tilesize);
var ox = (tx*map.tilesize)-ix;
var oy = (ty*map.tilesize)-iy;
- for (var x=ox,ttx=tx;x=0 && ttx=0 && tty v ? /*LANG*/"On" : /*LANG*/"Off";
(function(back) {
const SETTINGS_FILE = 'openwindsettings.json'
// initialize with default settings...
@@ -29,7 +28,6 @@ const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off";
'< Back': back,
'True wind': {
value: settings.truewind,
- format: boolFormat,
onchange: save('truewind'),
},
'Mounting angle': {
diff --git a/apps/owmweather/ChangeLog b/apps/owmweather/ChangeLog
new file mode 100644
index 000000000..5560f00bc
--- /dev/null
+++ b/apps/owmweather/ChangeLog
@@ -0,0 +1 @@
+0.01: New App!
diff --git a/apps/owmweather/README.md b/apps/owmweather/README.md
new file mode 100644
index 000000000..ba0427455
--- /dev/null
+++ b/apps/owmweather/README.md
@@ -0,0 +1,13 @@
+# OpenWeatherMap weather provider
+
+This updates [Weather](https://banglejs.com/apps/#weather) with data from the OpenWeatherMap API
+
+## Usage
+
+Just install and configure the app. This needs an internet-enabled Gadgetbridge version.
+Install [My Location](https://banglejs.com/apps/#mylocation) to change the location for the weather requests.
+Install one of the text input libraries to set the API key in the app settings or use the web interface.
+
+## Creator
+
+[halemmerich](https://github.com/halemmerich)
diff --git a/apps/owmweather/app.png b/apps/owmweather/app.png
new file mode 100644
index 000000000..bbfc0ace0
Binary files /dev/null and b/apps/owmweather/app.png differ
diff --git a/apps/owmweather/boot.js b/apps/owmweather/boot.js
new file mode 100644
index 000000000..64d2df3e9
--- /dev/null
+++ b/apps/owmweather/boot.js
@@ -0,0 +1,28 @@
+(function() {
+ let waiting = false;
+ let settings = require("Storage").readJSON("owmweather.json", 1) || {
+ enabled: false
+ };
+
+ function completion(){
+ waiting = false;
+ }
+
+ if (settings.enabled) {
+ let weather = require("Storage").readJSON('weather.json') || {};
+ let lastUpdate;
+ if (weather && weather.weather && weather.weather.time) lastUpdate = weather.weather.time;
+ if (!lastUpdate || lastUpdate + settings.refresh * 1000 * 60 < Date.now()){
+ if (!waiting){
+ waiting = true;
+ require("owmweather").pull(completion);
+ }
+ }
+ setInterval(() => {
+ if (!waiting && NRF.getSecurityStatus().connected){
+ waiting = true;
+ require("owmweather").pull(completion);
+ }
+ }, settings.refresh * 1000 * 60);
+ }
+})();
diff --git a/apps/owmweather/default.json b/apps/owmweather/default.json
new file mode 100644
index 000000000..9d8998867
--- /dev/null
+++ b/apps/owmweather/default.json
@@ -0,0 +1 @@
+{"enabled":false,"refresh":180}
diff --git a/apps/owmweather/interface.html b/apps/owmweather/interface.html
new file mode 100644
index 000000000..3f9467a83
--- /dev/null
+++ b/apps/owmweather/interface.html
@@ -0,0 +1,63 @@
+
+
+
+
+
+