Your current firmware version is unknown and bootloader is unknown
+
Your current firmware version is unknown and DFU is unknown
-
If you have an early (KickStarter or developer) Bangle.js device and still have the old 2v10.x bootloader, the Firmware Update
- will fail with a message about the bootloader version. If so, please click here to update to bootloader 2v12 and then click the 'Upload' button that appears.
+
If you have an early (KickStarter or developer) Bangle.js device and still have the old 2v10.x DFU, the Firmware Update
+ will fail with a message about the DFU version. If so, please click here to update to DFU 2v12 and then click the 'Upload' button that appears.
The currently available Espruino firmware releases are:
To update, click a link above and then click the 'Upload' button that appears.
DFU stands for Device Firmware Update. This is the first
+ bit of code that runs when Bangle.js starts, and it is able to update the
+ Bangle.js firmware. Normally you would update firmware via this Firmware
+ Updater app, but if for some reason Bangle.js will not boot, you can
+ always use DFU to to the update manually.
+
DFU is itself a bootloader, but here we're calling it DFU to avoid confusion
+ with the Bootloader app in the app loader (which prepares Bangle.js for running apps).
Firmware updates via this tool work differently to the NRF Connect method mentioned on
the Bangle.js 2 page. Firmware
- is uploaded to a file on the Bangle. Once complete the Bangle reboots and the bootloader copies
+ is uploaded to a file on the Bangle. Once complete the Bangle reboots and DFU copies
the new firmware into internal Storage.
In addition to the links above, you can upload a hex or zip file directly below. This file should be an .app_hex
- file, *not* the normal .hex (as that contains the bootloader as well).
+ file, *not* the normal .hex (as that contains the DFU as well).
DANGER! No verification is performed on uploaded ZIP or HEX files - you could
- potentially overwrite your bootloader with the wrong binary and brick your Bangle.
+ potentially overwrite your DFU with the wrong binary and brick your Bangle.
@@ -73,7 +87,7 @@ function onInit(device) {
document.getElementById("fw-ok").style = "";
}
Puck.eval("E.CRC32(E.memoryArea(0xF7000,0x7000))", crc => {
- console.log("Bootloader CRC = "+crc);
+ console.log("DFU CRC = "+crc);
var version = `unknown (CRC ${crc})`;
var ok = true;
if (crc==1339551013) { version = "2v10.219"; ok = false; }
@@ -299,8 +313,8 @@ function createJS_app(binary, startAddress, endAddress) {
bin32[3] = VERSION; // VERSION! Use this to test ourselves
console.log("CRC 0x"+bin32[2].toString(16));
hexJS = "";//`\x10if (E.CRC32(E.memoryArea(${startAddress},${endAddress-startAddress}))==${bin32[2]}) { print("FIRMWARE UP TO DATE!"); load();}\n`;
- hexJS += `\x10if (E.CRC32(E.memoryArea(0xF7000,0x7000))==1339551013) { print("BOOTLOADER 2v10.219 needs update"); load();}\n`;
- hexJS += `\x10if (E.CRC32(E.memoryArea(0xF7000,0x7000))==1207580954) { print("BOOTLOADER 2v10.236 needs update"); load();}\n`;
+ hexJS += `\x10if (E.CRC32(E.memoryArea(0xF7000,0x7000))==1339551013) { print("DFU 2v10.219 needs update"); load();}\n`;
+ hexJS += `\x10if (E.CRC32(E.memoryArea(0xF7000,0x7000))==1207580954) { print("DFU 2v10.236 needs update"); load();}\n`;
hexJS += '\x10var s = require("Storage");\n';
hexJS += '\x10s.erase(".firmware");\n';
var CHUNKSIZE = 2048;
@@ -320,7 +334,7 @@ function createJS_app(binary, startAddress, endAddress) {
function createJS_bootloader(binary, startAddress, endAddress) {
var crc = CRC32(binary);
console.log("CRC 0x"+crc.toString(16));
- hexJS = `\x10if (E.CRC32(E.memoryArea(${startAddress},${endAddress-startAddress}))==${crc}) { print("BOOTLOADER UP TO DATE!"); load();}\n`;
+ hexJS = `\x10if (E.CRC32(E.memoryArea(${startAddress},${endAddress-startAddress}))==${crc}) { print("DFU UP TO DATE!"); load();}\n`;
hexJS += `\x10var _fw = new Uint8Array(${binary.length})\n`;
var CHUNKSIZE = 1024;
for (var i=0;i { drawImage("${fileName}"); }`); // Unfortunately, eval is the only reasonable way to do this
+}
+
+let cachedOptions = Bangle.getOptions(); // We will change the backlight and timeouts later, and need to restore them when displaying the menu
+let backlightSetting = storage.readJSON('setting.json').brightness; // LCD brightness is not included in there for some reason
+
+let angle = 0; // Store the angle of rotation
+let image; // Cache the image here because we access it in multiple places
+
+function drawMenu() {
+ Bangle.removeListener('touch', drawMenu); // We no longer want touching to reload the menu
+ Bangle.setOptions(cachedOptions); // The drawImage function set no timeout, undo that
+ Bangle.setLCDBrightness(backlightSetting); // Restore backlight
+ image = undefined; // Delete the image from memory
+
+ E.showMenu(imageMenu);
+}
+
+function drawImage(fileName) {
+ E.showMenu(); // Remove the menu to prevent it from breaking things
+ setTimeout(() => { Bangle.on('touch', drawMenu); }, 300); // Touch the screen to go back to the image menu (300ms timeout to allow user to lift finger)
+ Bangle.setOptions({ // Disable display power saving while showing the image
+ lockTimeout: 0,
+ lcdPowerTimeout: 0,
+ backlightTimeout: 0
+ });
+ Bangle.setLCDBrightness(1); // Full brightness
+
+ image = eval(storage.read(fileName)); // Sadly, the only reasonable way to do this
+ g.clear().reset().drawImage(image, 88, 88, { rotate: angle });
+}
+
+setWatch(info => {
+ if (image) {
+ if (angle == 0) angle = Math.PI;
+ else angle = 0;
+ Bangle.buzz();
+
+ g.clear().reset().drawImage(image, 88, 88, { rotate: angle })
+ }
+}, BTN1, { repeat: true });
+
+// We don't load the widgets because there is no reasonable way to unload them
+drawMenu();
\ No newline at end of file
diff --git a/apps/gallery/icon.js b/apps/gallery/icon.js
new file mode 100644
index 000000000..11fee53eb
--- /dev/null
+++ b/apps/gallery/icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwgIOLgf/AAX8Av4FBJgkMAos/CIfMAv4Fe4AF/Apq5EAAw"))
\ No newline at end of file
diff --git a/apps/gallery/icon.png b/apps/gallery/icon.png
new file mode 100644
index 000000000..71835e93d
Binary files /dev/null and b/apps/gallery/icon.png differ
diff --git a/apps/gallery/metadata.json b/apps/gallery/metadata.json
new file mode 100644
index 000000000..89f7606aa
--- /dev/null
+++ b/apps/gallery/metadata.json
@@ -0,0 +1,26 @@
+{
+ "id": "gallery",
+ "name": "Gallery",
+ "version": "0.02",
+ "description": "A gallery that lets you view images uploaded with the IDE (see README)",
+ "readme": "README.md",
+ "icon": "icon.png",
+ "type": "app",
+ "tags": "tools",
+ "supports": [
+ "BANGLEJS2",
+ "BANGLEJS"
+ ],
+ "allow_emulator": true,
+ "storage": [
+ {
+ "name": "gallery.app.js",
+ "url": "app.js"
+ },
+ {
+ "name": "gallery.img",
+ "url": "icon.js",
+ "evaluate": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/apps/gpsinfo/metadata.json b/apps/gpsinfo/metadata.json
index 60bd90c03..002febd86 100644
--- a/apps/gpsinfo/metadata.json
+++ b/apps/gpsinfo/metadata.json
@@ -5,7 +5,7 @@
"description": "An application that displays information about altitude, lat/lon, satellites and time",
"icon": "gps-info.png",
"type": "app",
- "tags": "gps",
+ "tags": "gps,outdoors",
"supports": ["BANGLEJS","BANGLEJS2"],
"storage": [
{"name":"gpsinfo.app.js","url":"gps-info.js"},
diff --git a/apps/gpstrek/ChangeLog b/apps/gpstrek/ChangeLog
index d46ada767..c36c23c72 100644
--- a/apps/gpstrek/ChangeLog
+++ b/apps/gpstrek/ChangeLog
@@ -8,3 +8,8 @@
Fix widget adding listeners more than once
0.07: Show checkered flag for target markers
Single waypoints are now shown in the compass view
+0.08: Better handle state in widget
+ Slightly faster drawing by doing some caching
+ Reconstruct battery voltage by using calibrated batFullVoltage
+ Averaging for smoothing compass headings
+ Save state if route or waypoint has been chosen
diff --git a/apps/gpstrek/app.js b/apps/gpstrek/app.js
index f26811ed3..b3ec79fd2 100644
--- a/apps/gpstrek/app.js
+++ b/apps/gpstrek/app.js
@@ -1,48 +1,69 @@
+
+{ //run in own scope for fast switch
const STORAGE = require("Storage");
-const showWidgets = true;
-let numberOfSlices=4;
+const BAT_FULL = require("Storage").readJSON("setting.json").batFullVoltage || 0.3144;
+
+let init = function(){
+ global.screen = 1;
+ global.drawTimeout = undefined;
+ global.lastDrawnScreen = 0;
+ global.firstDraw = true;
+ global.slices = [];
+ global.maxScreens = 1;
+ global.scheduleDraw = false;
-if (showWidgets){
Bangle.loadWidgets();
-}
+ WIDGETS.gpstrek.start(false);
+ if (!WIDGETS.gpstrek.getState().numberOfSlices) WIDGETS.gpstrek.getState().numberOfSlices = 3;
+};
-let state = WIDGETS.gpstrek.getState();
-WIDGETS.gpstrek.start(false);
+let cleanup = function(){
+ if (global.drawTimeout) clearTimeout(global.drawTimeout);
+ delete global.screen;
+ delete global.drawTimeout;
+ delete global.lastDrawnScreen;
+ delete global.firstDraw;
+ delete global.slices;
+ delete global.maxScreens;
+};
-function parseNumber(toParse){
+init();
+scheduleDraw = true;
+
+let parseNumber = function(toParse){
if (toParse.includes(".")) return parseFloat(toParse);
return parseFloat("" + toParse + ".0");
-}
+};
-function parseWaypoint(filename, offset, result){
+let parseWaypoint = function(filename, offset, result){
result.lat = parseNumber(STORAGE.read(filename, offset, 11));
result.lon = parseNumber(STORAGE.read(filename, offset += 11, 12));
return offset + 12;
-}
+};
-function parseWaypointWithElevation(filename, offset, result){
+let parseWaypointWithElevation = function (filename, offset, result){
offset = parseWaypoint(filename, offset, result);
result.alt = parseNumber(STORAGE.read(filename, offset, 6));
return offset + 6;
-}
+};
-function parseWaypointWithName(filename, offset, result){
+let parseWaypointWithName = function(filename, offset, result){
offset = parseWaypoint(filename, offset, result);
return parseName(filename, offset, result);
-}
+};
-function parseName(filename, offset, result){
+let parseName = function(filename, offset, result){
let nameLength = STORAGE.read(filename, offset, 2) - 0;
result.name = STORAGE.read(filename, offset += 2, nameLength);
return offset + nameLength;
-}
+};
-function parseWaypointWithElevationAndName(filename, offset, result){
+let parseWaypointWithElevationAndName = function(filename, offset, result){
offset = parseWaypointWithElevation(filename, offset, result);
return parseName(filename, offset, result);
-}
+};
-function getEntry(filename, offset, result){
+let getEntry = function(filename, offset, result){
result.fileOffset = offset;
let type = STORAGE.read(filename, offset++, 1);
if (type == "") return -1;
@@ -68,12 +89,12 @@ function getEntry(filename, offset, result){
result.fileLength = offset - result.fileOffset;
//print(result);
return offset;
-}
+};
const labels = ["N","NE","E","SE","S","SW","W","NW"];
const loc = require("locale");
-function matchFontSize(graphics, text, height, width){
+let matchFontSize = function(graphics, text, height, width){
graphics.setFontVector(height);
let metrics;
let size = 1;
@@ -81,13 +102,19 @@ function matchFontSize(graphics, text, height, width){
size -= 0.05;
graphics.setFont("Vector",Math.floor(height*size));
}
-}
+};
-function getDoubleLineSlice(title1,title2,provider1,provider2,refreshTime){
+let getDoubleLineSlice = function(title1,title2,provider1,provider2,refreshTime){
let lastDrawn = Date.now() - Math.random()*refreshTime;
+ let lastValue1 = 0;
+ let lastValue2 = 0;
return {
refresh: function (){
- return Date.now() - lastDrawn > (Bangle.isLocked()?(refreshTime?refreshTime:5000):(refreshTime?refreshTime*2:10000));
+ let bigChange1 = (Math.abs(lastValue1 - provider1()) > 1);
+ let bigChange2 = (Math.abs(lastValue2 - provider2()) > 1);
+ let refresh = (Bangle.isLocked()?(refreshTime?refreshTime*5:10000):(refreshTime?refreshTime*2:1000));
+ let old = (Date.now() - lastDrawn) > refresh;
+ return (bigChange1 || bigChange2) && old;
},
draw: function (graphics, x, y, height, width){
lastDrawn = Date.now();
@@ -95,29 +122,29 @@ function getDoubleLineSlice(title1,title2,provider1,provider2,refreshTime){
if (typeof title2 == "function") title2 = title2();
graphics.clearRect(x,y,x+width,y+height);
- let value = provider1();
- matchFontSize(graphics, title1 + value, Math.floor(height*0.5), width);
+ lastValue1 = provider1();
+ matchFontSize(graphics, title1 + lastValue1, Math.floor(height*0.5), width);
graphics.setFontAlign(-1,-1);
graphics.drawString(title1, x+2, y);
graphics.setFontAlign(1,-1);
- graphics.drawString(value, x+width, y);
+ graphics.drawString(lastValue1, x+width, y);
- value = provider2();
- matchFontSize(graphics, title2 + value, Math.floor(height*0.5), width);
+ lastValue2 = provider2();
+ matchFontSize(graphics, title2 + lastValue2, Math.floor(height*0.5), width);
graphics.setFontAlign(-1,-1);
graphics.drawString(title2, x+2, y+(height*0.5));
graphics.setFontAlign(1,-1);
- graphics.drawString(value, x+width, y+(height*0.5));
+ graphics.drawString(lastValue2, x+width, y+(height*0.5));
}
};
-}
+};
-function getTargetSlice(targetDataSource){
+let getTargetSlice = function(targetDataSource){
let nameIndex = 0;
let lastDrawn = Date.now() - Math.random()*3000;
return {
refresh: function (){
- return Date.now() - lastDrawn > (Bangle.isLocked()?10000:3000);
+ return Date.now() - lastDrawn > (Bangle.isLocked()?3000:10000);
},
draw: function (graphics, x, y, height, width){
lastDrawn = Date.now();
@@ -174,9 +201,9 @@ function getTargetSlice(targetDataSource){
}
}
};
-}
+};
-function drawCompass(graphics, x, y, height, width, increment, start){
+let drawCompass = function(graphics, x, y, height, width, increment, start){
graphics.setFont12x20();
graphics.setFontAlign(0,-1);
graphics.setColor(graphics.theme.fg);
@@ -197,14 +224,19 @@ function drawCompass(graphics, x, y, height, width, increment, start){
xpos+=increment*15;
if (xpos > width + 20) break;
}
-}
+};
-function getCompassSlice(compassDataSource){
+let getCompassSlice = function(compassDataSource){
let lastDrawn = Date.now() - Math.random()*2000;
+ let lastDrawnValue = 0;
const buffers = 4;
let buf = [];
return {
- refresh : function (){return Bangle.isLocked()?(Date.now() - lastDrawn > 2000):true;},
+ refresh : function (){
+ let bigChange = (Math.abs(lastDrawnValue - compassDataSource.getCourse()) > 2);
+ let old = (Bangle.isLocked()?(Date.now() - lastDrawn > 2000):true);
+ return bigChange && old;
+ },
draw: function (graphics, x,y,height,width){
lastDrawn = Date.now();
const max = 180;
@@ -212,12 +244,14 @@ function getCompassSlice(compassDataSource){
graphics.clearRect(x,y,x+width,y+height);
- var start = compassDataSource.getCourse() - 90;
- if (isNaN(compassDataSource.getCourse())) start = -90;
+ lastDrawnValue = compassDataSource.getCourse();
+
+ var start = lastDrawnValue - 90;
+ if (isNaN(lastDrawnValue)) start = -90;
if (start<0) start+=360;
start = start % 360;
- if (state.acc && compassDataSource.getCourseType() == "MAG"){
+ if (WIDGETS.gpstrek.getState().acc && compassDataSource.getCourseType() == "MAG"){
drawCompass(graphics,0,y+width*0.05,height-width*0.05,width,increment,start);
} else {
drawCompass(graphics,0,y,height,width,increment,start);
@@ -226,7 +260,8 @@ function getCompassSlice(compassDataSource){
if (compassDataSource.getPoints){
for (let p of compassDataSource.getPoints()){
- var bpos = p.bearing - compassDataSource.getCourse();
+ g.reset();
+ var bpos = p.bearing - lastDrawnValue;
if (bpos>180) bpos -=360;
if (bpos<-180) bpos +=360;
bpos+=120;
@@ -251,6 +286,7 @@ function getCompassSlice(compassDataSource){
}
if (compassDataSource.getMarkers){
for (let m of compassDataSource.getMarkers()){
+ g.reset();
g.setColor(m.fillcolor);
let mpos = m.xpos * width;
if (m.xpos < 0.05) mpos = Math.floor(width*0.05);
@@ -263,9 +299,9 @@ function getCompassSlice(compassDataSource){
graphics.setColor(g.theme.fg);
graphics.fillRect(x,y,Math.floor(width*0.05),y+height);
graphics.fillRect(Math.ceil(width*0.95),y,width,y+height);
- if (state.acc && compassDataSource.getCourseType() == "MAG") {
- let xh = E.clip(width*0.5-height/2+(((state.acc.x+1)/2)*height),width*0.5 - height/2, width*0.5 + height/2);
- let yh = E.clip(y+(((state.acc.y+1)/2)*height),y,y+height);
+ if (WIDGETS.gpstrek.getState().acc && compassDataSource.getCourseType() == "MAG") {
+ let xh = E.clip(width*0.5-height/2+(((WIDGETS.gpstrek.getState().acc.x+1)/2)*height),width*0.5 - height/2, width*0.5 + height/2);
+ let yh = E.clip(y+(((WIDGETS.gpstrek.getState().acc.y+1)/2)*height),y,y+height);
graphics.fillRect(width*0.5 - height/2, y, width*0.5 + height/2, y + Math.floor(width*0.05));
@@ -287,44 +323,48 @@ function getCompassSlice(compassDataSource){
graphics.drawRect(Math.floor(width*0.05),y,Math.ceil(width*0.95),y+height);
}
};
-}
+};
-function radians(a) {
+let radians = function(a) {
return a*Math.PI/180;
-}
+};
-function degrees(a) {
- var d = a*180/Math.PI;
+let degrees = function(a) {
+ let d = a*180/Math.PI;
return (d+360)%360;
-}
+};
-function bearing(a,b){
+let bearing = function(a,b){
if (!a || !b || !a.lon || !a.lat || !b.lon || !b.lat) return Infinity;
- 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) -
+ let delta = radians(b.lon-a.lon);
+ let alat = radians(a.lat);
+ let blat = radians(b.lat);
+ let y = Math.sin(delta) * Math.cos(blat);
+ let 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){
+let distance = function(a,b){
if (!a || !b || !a.lon || !a.lat || !b.lon || !b.lat) return Infinity;
- var x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2));
- var y = radians(b.lat-a.lat);
+ let x = radians(a.lon-b.lon) * Math.cos(radians((a.lat+b.lat)/2));
+ let y = radians(b.lat-a.lat);
return Math.round(Math.sqrt(x*x + y*y) * 6371000);
-}
+};
-function triangle (x, y, width, height){
+let getAveragedCompass = function(){
+ return Math.round(WIDGETS.gpstrek.getState().avgComp);
+};
+
+let triangle = function(x, y, width, height){
return [
Math.round(x),Math.round(y),
Math.round(x+width * 0.5), Math.round(y+height),
Math.round(x-width * 0.5), Math.round(y+height)
];
-}
+};
-function onSwipe(dir){
+let onSwipe = function(dir){
if (dir < 0) {
nextScreen();
} else if (dir > 0) {
@@ -332,9 +372,9 @@ function onSwipe(dir){
} else {
nextScreen();
}
-}
+};
-function setButtons(){
+let setButtons = function(){
let options = {
mode: "custom",
swipe: onSwipe,
@@ -342,9 +382,9 @@ function setButtons(){
touch: nextScreen
};
Bangle.setUI(options);
-}
+};
-function getApproxFileSize(name){
+let getApproxFileSize = function(name){
let currentStart = STORAGE.getStats().totalBytes;
let currentSize = 0;
for (let i = currentStart; i > 500; i/=2){
@@ -358,9 +398,9 @@ function getApproxFileSize(name){
currentSize += currentDiff;
}
return currentSize;
-}
+};
-function parseRouteData(filename, progressMonitor){
+let parseRouteData = function(filename, progressMonitor){
let routeInfo = {};
routeInfo.filename = filename;
@@ -406,40 +446,40 @@ function parseRouteData(filename, progressMonitor){
set(routeInfo, 0);
return routeInfo;
-}
+};
-function hasPrev(route){
+let hasPrev = function(route){
if (route.mirror) return route.index < (route.count - 1);
return route.index > 0;
-}
+};
-function hasNext(route){
+let hasNext = function(route){
if (route.mirror) return route.index > 0;
return route.index < (route.count - 1);
-}
+};
-function next(route){
+let next = function(route){
if (!hasNext(route)) return;
if (route.mirror) set(route, --route.index);
if (!route.mirror) set(route, ++route.index);
-}
+};
-function set(route, index){
+let set = function(route, index){
route.currentWaypoint = {};
route.index = index;
getEntry(route.filename, route.refs[index], route.currentWaypoint);
-}
+};
-function prev(route){
+let prev = function(route){
if (!hasPrev(route)) return;
if (route.mirror) set(route, ++route.index);
if (!route.mirror) set(route, --route.index);
-}
+};
let lastMirror;
let cachedLast;
-function getLast(route){
+let getLast = function(route){
let wp = {};
if (lastMirror != route.mirror){
if (route.mirror) getEntry(route.filename, route.refs[0], wp);
@@ -448,14 +488,14 @@ function getLast(route){
cachedLast = wp;
}
return cachedLast;
-}
+};
-function removeMenu(){
+let removeMenu = function(){
E.showMenu();
switchNav();
-}
+};
-function showProgress(progress, title, max){
+let showProgress = function(progress, title, max){
//print("Progress",progress,max)
let message = title? title: "Loading";
if (max){
@@ -466,17 +506,17 @@ function showProgress(progress, title, max){
for (let i = dots; i < 4; i++) message += " ";
}
E.showMessage(message);
-}
+};
-function handleLoading(c){
+let handleLoading = function(c){
E.showMenu();
- state.route = parseRouteData(c, showProgress);
- state.waypoint = null;
+ WIDGETS.gpstrek.getState().route = parseRouteData(c, showProgress);
+ WIDGETS.gpstrek.getState().waypoint = null;
+ WIDGETS.gpstrek.getState().route.mirror = false;
removeMenu();
- state.route.mirror = false;
-}
+};
-function showRouteSelector (){
+let showRouteSelector = function(){
var menu = {
"" : {
back : showRouteMenu,
@@ -488,9 +528,9 @@ function showRouteSelector (){
});
E.showMenu(menu);
-}
+};
-function showRouteMenu(){
+let showRouteMenu = function(){
var menu = {
"" : {
"title" : "Route",
@@ -499,48 +539,48 @@ function showRouteMenu(){
"Select file" : showRouteSelector
};
- if (state.route){
+ if (WIDGETS.gpstrek.getState().route){
menu.Mirror = {
- value: state && state.route && !!state.route.mirror || false,
+ value: WIDGETS.gpstrek.getState() && WIDGETS.gpstrek.getState().route && !!WIDGETS.gpstrek.getState().route.mirror || false,
onchange: v=>{
- state.route.mirror = v;
+ WIDGETS.gpstrek.getState().route.mirror = v;
}
};
menu['Select closest waypoint'] = function () {
- if (state.currentPos && state.currentPos.lat){
- setClosestWaypoint(state.route, null, showProgress); removeMenu();
+ if (WIDGETS.gpstrek.getState().currentPos && WIDGETS.gpstrek.getState().currentPos.lat){
+ setClosestWaypoint(WIDGETS.gpstrek.getState().route, null, showProgress); removeMenu();
} else {
E.showAlert("No position").then(()=>{E.showMenu(menu);});
}
};
menu['Select closest waypoint (not visited)'] = function () {
- if (state.currentPos && state.currentPos.lat){
- setClosestWaypoint(state.route, state.route.index, showProgress); removeMenu();
+ if (WIDGETS.gpstrek.getState().currentPos && WIDGETS.gpstrek.getState().currentPos.lat){
+ setClosestWaypoint(WIDGETS.gpstrek.getState().route, WIDGETS.gpstrek.getState().route.index, showProgress); removeMenu();
} else {
E.showAlert("No position").then(()=>{E.showMenu(menu);});
}
};
menu['Select waypoint'] = {
- value : state.route.index,
- min:1,max:state.route.count,step:1,
- onchange : v => { set(state.route, v-1); }
+ value : WIDGETS.gpstrek.getState().route.index,
+ min:1,max:WIDGETS.gpstrek.getState().route.count,step:1,
+ onchange : v => { set(WIDGETS.gpstrek.getState().route, v-1); }
};
menu['Select waypoint as current position'] = function (){
- state.currentPos.lat = state.route.currentWaypoint.lat;
- state.currentPos.lon = state.route.currentWaypoint.lon;
- state.currentPos.alt = state.route.currentWaypoint.alt;
+ WIDGETS.gpstrek.getState().currentPos.lat = WIDGETS.gpstrek.getState().route.currentWaypoint.lat;
+ WIDGETS.gpstrek.getState().currentPos.lon = WIDGETS.gpstrek.getState().route.currentWaypoint.lon;
+ WIDGETS.gpstrek.getState().currentPos.alt = WIDGETS.gpstrek.getState().route.currentWaypoint.alt;
removeMenu();
};
}
- if (state.route && hasPrev(state.route))
- menu['Previous waypoint'] = function() { prev(state.route); removeMenu(); };
- if (state.route && hasNext(state.route))
- menu['Next waypoint'] = function() { next(state.route); removeMenu(); };
+ if (WIDGETS.gpstrek.getState().route && hasPrev(WIDGETS.gpstrek.getState().route))
+ menu['Previous waypoint'] = function() { prev(WIDGETS.gpstrek.getState().route); removeMenu(); };
+ if (WIDGETS.gpstrek.getState().route && hasNext(WIDGETS.gpstrek.getState().route))
+ menu['Next waypoint'] = function() { next(WIDGETS.gpstrek.getState().route); removeMenu(); };
E.showMenu(menu);
-}
+};
-function showWaypointSelector(){
+let showWaypointSelector = function(){
let waypoints = require("waypoints").load();
var menu = {
"" : {
@@ -550,41 +590,41 @@ function showWaypointSelector(){
waypoints.forEach((wp,c)=>{
menu[waypoints[c].name] = function (){
- state.waypoint = waypoints[c];
- state.waypointIndex = c;
- state.route = null;
+ WIDGETS.gpstrek.getState().waypoint = waypoints[c];
+ WIDGETS.gpstrek.getState().waypointIndex = c;
+ WIDGETS.gpstrek.getState().route = null;
removeMenu();
};
});
E.showMenu(menu);
-}
+};
-function showCalibrationMenu(){
+let showCalibrationMenu = function(){
let menu = {
"" : {
"title" : "Calibration",
back : showMenu,
},
"Barometer (GPS)" : ()=>{
- if (!state.currentPos || isNaN(state.currentPos.alt)){
+ if (!WIDGETS.gpstrek.getState().currentPos || isNaN(WIDGETS.gpstrek.getState().currentPos.alt)){
E.showAlert("No GPS altitude").then(()=>{E.showMenu(menu);});
} else {
- state.calibAltDiff = state.altitude - state.currentPos.alt;
- E.showAlert("Calibrated Altitude Difference: " + state.calibAltDiff.toFixed(0)).then(()=>{removeMenu();});
+ WIDGETS.gpstrek.getState().calibAltDiff = WIDGETS.gpstrek.getState().altitude - WIDGETS.gpstrek.getState().currentPos.alt;
+ E.showAlert("Calibrated Altitude Difference: " + WIDGETS.gpstrek.getState().calibAltDiff.toFixed(0)).then(()=>{removeMenu();});
}
},
"Barometer (Manual)" : {
- value : Math.round(state.currentPos && (state.currentPos.alt != undefined && !isNaN(state.currentPos.alt)) ? state.currentPos.alt: state.altitude),
+ value : Math.round(WIDGETS.gpstrek.getState().currentPos && (WIDGETS.gpstrek.getState().currentPos.alt != undefined && !isNaN(WIDGETS.gpstrek.getState().currentPos.alt)) ? WIDGETS.gpstrek.getState().currentPos.alt: WIDGETS.gpstrek.getState().altitude),
min:-2000,max: 10000,step:1,
- onchange : v => { state.calibAltDiff = state.altitude - v; }
+ onchange : v => { WIDGETS.gpstrek.getState().calibAltDiff = WIDGETS.gpstrek.getState().altitude - v; }
},
"Reset Compass" : ()=>{ Bangle.resetCompass(); removeMenu();},
};
E.showMenu(menu);
-}
+};
-function showWaypointMenu(){
+let showWaypointMenu = function(){
let menu = {
"" : {
"title" : "Waypoint",
@@ -593,21 +633,21 @@ function showWaypointMenu(){
"Select waypoint" : showWaypointSelector,
};
E.showMenu(menu);
-}
+};
-function showBackgroundMenu(){
+let showBackgroundMenu = function(){
let menu = {
"" : {
"title" : "Background",
back : showMenu,
},
- "Start" : ()=>{ E.showPrompt("Start?").then((v)=>{ if (v) {WIDGETS.gpstrek.start(true); removeMenu();} else {showMenu();}}).catch(()=>{E.showMenu(mainmenu);});},
- "Stop" : ()=>{ E.showPrompt("Stop?").then((v)=>{ if (v) {WIDGETS.gpstrek.stop(true); removeMenu();} else {showMenu();}}).catch(()=>{E.showMenu(mainmenu);});},
+ "Start" : ()=>{ E.showPrompt("Start?").then((v)=>{ if (v) {WIDGETS.gpstrek.start(true); removeMenu();} else {showMenu();}}).catch(()=>{showMenu();});},
+ "Stop" : ()=>{ E.showPrompt("Stop?").then((v)=>{ if (v) {WIDGETS.gpstrek.stop(true); removeMenu();} else {showMenu();}}).catch(()=>{showMenu();});},
};
E.showMenu(menu);
-}
+};
-function showMenu(){
+let showMenu = function(){
var mainmenu = {
"" : {
"title" : "Main",
@@ -617,50 +657,55 @@ function showMenu(){
"Waypoint" : showWaypointMenu,
"Background" : showBackgroundMenu,
"Calibration": showCalibrationMenu,
- "Reset" : ()=>{ E.showPrompt("Do Reset?").then((v)=>{ if (v) {WIDGETS.gpstrek.resetState(); removeMenu();} else {E.showMenu(mainmenu);}});},
+ "Reset" : ()=>{ E.showPrompt("Do Reset?").then((v)=>{ if (v) {WIDGETS.gpstrek.resetState(); removeMenu();} else {E.showMenu(mainmenu);}}).catch(()=>{E.showMenu(mainmenu);});},
"Info rows" : {
- value : numberOfSlices,
+ value : WIDGETS.gpstrek.getState().numberOfSlices,
min:1,max:6,step:1,
- onchange : v => { setNumberOfSlices(v); }
+ onchange : v => { WIDGETS.gpstrek.getState().numberOfSlices = v; }
},
};
E.showMenu(mainmenu);
-}
+};
-let scheduleDraw = true;
-function switchMenu(){
- screen = 0;
- scheduleDraw = false;
- showMenu();
-}
+let switchMenu = function(){
+ stopDrawing();
+ showMenu();
+};
-function drawInTimeout(){
- setTimeout(()=>{
+let stopDrawing = function(){
+ if (drawTimeout) clearTimeout(drawTimeout);
+ scheduleDraw = false;
+};
+
+let drawInTimeout = function(){
+ if (global.drawTimeout) clearTimeout(drawTimeout);
+ drawTimeout = setTimeout(()=>{
+ drawTimeout = undefined;
draw();
- if (scheduleDraw)
- setTimeout(drawInTimeout, 0);
- },0);
-}
+ },50);
+};
-function switchNav(){
+let switchNav = function(){
if (!screen) screen = 1;
setButtons();
scheduleDraw = true;
+ firstDraw = true;
drawInTimeout();
-}
+};
-function nextScreen(){
+let nextScreen = function(){
screen++;
if (screen > maxScreens){
screen = 1;
}
-}
+ drawInTimeout();
+};
-function setClosestWaypoint(route, startindex, progress){
- if (startindex >= state.route.count) startindex = state.route.count - 1;
- if (!state.currentPos.lat){
+let setClosestWaypoint = function(route, startindex, progress){
+ if (startindex >= WIDGETS.gpstrek.getState().route.count) startindex = WIDGETS.gpstrek.getState().route.count - 1;
+ if (!WIDGETS.gpstrek.getState().currentPos.lat){
set(route, startindex);
return;
}
@@ -670,7 +715,7 @@ function setClosestWaypoint(route, startindex, progress){
if (progress && (i % 5 == 0)) progress(i-(startindex?startindex:0), "Searching", route.count);
let wp = {};
getEntry(route.filename, route.refs[i], wp);
- let curDist = distance(state.currentPos, wp);
+ let curDist = distance(WIDGETS.gpstrek.getState().currentPos, wp);
if (curDist < minDist){
minDist = curDist;
minIndex = i;
@@ -679,30 +724,28 @@ function setClosestWaypoint(route, startindex, progress){
}
}
set(route, minIndex);
-}
-
-let screen = 1;
+};
const finishIcon = atob("CggB//meZmeZ+Z5n/w==");
const compassSliceData = {
getCourseType: function(){
- return (state.currentPos && state.currentPos.course) ? "GPS" : "MAG";
+ return (WIDGETS.gpstrek.getState().currentPos && WIDGETS.gpstrek.getState().currentPos.course) ? "GPS" : "MAG";
},
getCourse: function (){
- if(compassSliceData.getCourseType() == "GPS") return state.currentPos.course;
- return state.compassHeading?state.compassHeading:undefined;
+ if(compassSliceData.getCourseType() == "GPS") return WIDGETS.gpstrek.getState().currentPos.course;
+ return getAveragedCompass();
},
getPoints: function (){
let points = [];
- if (state.currentPos && state.currentPos.lon && state.route && state.route.currentWaypoint){
- points.push({bearing:bearing(state.currentPos, state.route.currentWaypoint), color:"#0f0"});
+ if (WIDGETS.gpstrek.getState().currentPos && WIDGETS.gpstrek.getState().currentPos.lon && WIDGETS.gpstrek.getState().route && WIDGETS.gpstrek.getState().route.currentWaypoint){
+ points.push({bearing:bearing(WIDGETS.gpstrek.getState().currentPos, WIDGETS.gpstrek.getState().route.currentWaypoint), color:"#0f0"});
}
- if (state.currentPos && state.currentPos.lon && state.route){
- points.push({bearing:bearing(state.currentPos, getLast(state.route)), icon: finishIcon});
+ if (WIDGETS.gpstrek.getState().currentPos && WIDGETS.gpstrek.getState().currentPos.lon && WIDGETS.gpstrek.getState().route){
+ points.push({bearing:bearing(WIDGETS.gpstrek.getState().currentPos, getLast(WIDGETS.gpstrek.getState().route)), icon: finishIcon});
}
- if (state.currentPos && state.currentPos.lon && state.waypoint){
- points.push({bearing:bearing(state.currentPos, state.waypoint), icon: finishIcon});
+ if (WIDGETS.gpstrek.getState().currentPos && WIDGETS.gpstrek.getState().currentPos.lon && WIDGETS.gpstrek.getState().waypoint){
+ points.push({bearing:bearing(WIDGETS.gpstrek.getState().currentPos, WIDGETS.gpstrek.getState().waypoint), icon: finishIcon});
}
return points;
},
@@ -714,79 +757,74 @@ const compassSliceData = {
const waypointData = {
icon: atob("EBCBAAAAAAAAAAAAcIB+zg/uAe4AwACAAAAAAAAAAAAAAAAA"),
getProgress: function() {
- return (state.route.index + 1) + "/" + state.route.count;
+ return (WIDGETS.gpstrek.getState().route.index + 1) + "/" + WIDGETS.gpstrek.getState().route.count;
},
getTarget: function (){
- if (distance(state.currentPos,state.route.currentWaypoint) < 30 && hasNext(state.route)){
- next(state.route);
+ if (distance(WIDGETS.gpstrek.getState().currentPos,WIDGETS.gpstrek.getState().route.currentWaypoint) < 30 && hasNext(WIDGETS.gpstrek.getState().route)){
+ next(WIDGETS.gpstrek.getState().route);
Bangle.buzz(1000);
}
- return state.route.currentWaypoint;
+ return WIDGETS.gpstrek.getState().route.currentWaypoint;
},
getStart: function (){
- return state.currentPos;
+ return WIDGETS.gpstrek.getState().currentPos;
}
};
const finishData = {
icon: atob("EBABAAA/4DmgJmAmYDmgOaAmYD/gMAAwADAAMAAwAAAAAAA="),
getTarget: function (){
- if (state.route) return getLast(state.route);
- if (state.waypoint) return state.waypoint;
+ if (WIDGETS.gpstrek.getState().route) return getLast(WIDGETS.gpstrek.getState().route);
+ if (WIDGETS.gpstrek.getState().waypoint) return WIDGETS.gpstrek.getState().waypoint;
},
getStart: function (){
- return state.currentPos;
+ return WIDGETS.gpstrek.getState().currentPos;
}
};
-let sliceHeight;
-function setNumberOfSlices(number){
- numberOfSlices = number;
- sliceHeight = Math.floor((g.getHeight()-(showWidgets?24:0))/numberOfSlices);
-}
-
-let slices = [];
-let maxScreens = 1;
-setNumberOfSlices(3);
+let getSliceHeight = function(number){
+ return Math.floor(Bangle.appRect.h/WIDGETS.gpstrek.getState().numberOfSlices);
+};
let compassSlice = getCompassSlice(compassSliceData);
let waypointSlice = getTargetSlice(waypointData);
let finishSlice = getTargetSlice(finishData);
let eleSlice = getDoubleLineSlice("Up","Down",()=>{
- return loc.distance(state.up,3) + "/" + (state.route ? loc.distance(state.route.up,3):"---");
+ return loc.distance(WIDGETS.gpstrek.getState().up,3) + "/" + (WIDGETS.gpstrek.getState().route ? loc.distance(WIDGETS.gpstrek.getState().route.up,3):"---");
},()=>{
- return loc.distance(state.down,3) + "/" + (state.route ? loc.distance(state.route.down,3): "---");
+ return loc.distance(WIDGETS.gpstrek.getState().down,3) + "/" + (WIDGETS.gpstrek.getState().route ? loc.distance(WIDGETS.gpstrek.getState().route.down,3): "---");
});
let statusSlice = getDoubleLineSlice("Speed","Alt",()=>{
let speed = 0;
- if (state.currentPos && state.currentPos.speed) speed = state.currentPos.speed;
+ if (WIDGETS.gpstrek.getState().currentPos && WIDGETS.gpstrek.getState().currentPos.speed) speed = WIDGETS.gpstrek.getState().currentPos.speed;
return loc.speed(speed,2);
},()=>{
let alt = Infinity;
- if (!isNaN(state.altitude)){
- alt = isNaN(state.calibAltDiff) ? state.altitude : (state.altitude - state.calibAltDiff);
+ if (!isNaN(WIDGETS.gpstrek.getState().altitude)){
+ alt = isNaN(WIDGETS.gpstrek.getState().calibAltDiff) ? WIDGETS.gpstrek.getState().altitude : (WIDGETS.gpstrek.getState().altitude - WIDGETS.gpstrek.getState().calibAltDiff);
}
- if (state.currentPos && state.currentPos.alt) alt = state.currentPos.alt;
+ if (WIDGETS.gpstrek.getState().currentPos && WIDGETS.gpstrek.getState().currentPos.alt) alt = WIDGETS.gpstrek.getState().currentPos.alt;
+ if (isNaN(alt)) return "---";
return loc.distance(alt,3);
});
let status2Slice = getDoubleLineSlice("Compass","GPS",()=>{
- return (state.compassHeading?Math.round(state.compassHeading):"---") + "°";
+ return getAveragedCompass() + "°";
},()=>{
let course = "---°";
- if (state.currentPos && state.currentPos.course) course = state.currentPos.course + "°";
+ if (WIDGETS.gpstrek.getState().currentPos && WIDGETS.gpstrek.getState().currentPos.course) course = WIDGETS.gpstrek.getState().currentPos.course + "°";
return course;
},200);
let healthSlice = getDoubleLineSlice("Heart","Steps",()=>{
- return state.bpm;
+ return WIDGETS.gpstrek.getState().bpm || "---";
},()=>{
- return state.steps;
+ return !isNaN(WIDGETS.gpstrek.getState().steps)? WIDGETS.gpstrek.getState().steps: "---";
});
let system2Slice = getDoubleLineSlice("Bat","",()=>{
- return (Bangle.isCharging()?"+":"") + E.getBattery().toFixed(0)+"% " + NRF.getBattery().toFixed(2) + "V";
+ return (Bangle.isCharging()?"+":"") + E.getBattery().toFixed(0)+"% " + (analogRead(D3)*4.2/BAT_FULL).toFixed(2) + "V";
},()=>{
return "";
});
@@ -798,17 +836,17 @@ let systemSlice = getDoubleLineSlice("RAM","Storage",()=>{
return (STORAGE.getFree()/1024).toFixed(0)+"kB";
});
-function updateSlices(){
+let updateSlices = function(){
slices = [];
slices.push(compassSlice);
- if (state.currentPos && state.currentPos.lat && state.route && state.route.currentWaypoint && state.route.index < state.route.count - 1) {
+ if (WIDGETS.gpstrek.getState().currentPos && WIDGETS.gpstrek.getState().currentPos.lat && WIDGETS.gpstrek.getState().route && WIDGETS.gpstrek.getState().route.currentWaypoint && WIDGETS.gpstrek.getState().route.index < WIDGETS.gpstrek.getState().route.count - 1) {
slices.push(waypointSlice);
}
- if (state.currentPos && state.currentPos.lat && (state.route || state.waypoint)) {
+ if (WIDGETS.gpstrek.getState().currentPos && WIDGETS.gpstrek.getState().currentPos.lat && (WIDGETS.gpstrek.getState().route || WIDGETS.gpstrek.getState().waypoint)) {
slices.push(finishSlice);
}
- if ((state.route && state.route.down !== undefined) || state.down != undefined) {
+ if ((WIDGETS.gpstrek.getState().route && WIDGETS.gpstrek.getState().route.down !== undefined) || WIDGETS.gpstrek.getState().down != undefined) {
slices.push(eleSlice);
}
slices.push(statusSlice);
@@ -816,42 +854,44 @@ function updateSlices(){
slices.push(healthSlice);
slices.push(systemSlice);
slices.push(system2Slice);
- maxScreens = Math.ceil(slices.length/numberOfSlices);
-}
+ maxScreens = Math.ceil(slices.length/WIDGETS.gpstrek.getState().numberOfSlices);
+};
-function clear() {
- g.clearRect(0,(showWidgets ? 24 : 0), g.getWidth(),g.getHeight());
-}
-let lastDrawnScreen;
-let firstDraw = true;
+let clear = function() {
+ g.clearRect(Bangle.appRect);
+};
-function draw(){
- if (!screen) return;
- let ypos = showWidgets ? 24 : 0;
+let draw = function(){
+ if (!global.screen) return;
+ let ypos = Bangle.appRect.y;
- let firstSlice = (screen-1)*numberOfSlices;
+ let firstSlice = (screen-1)*WIDGETS.gpstrek.getState().numberOfSlices;
updateSlices();
let force = lastDrawnScreen != screen || firstDraw;
if (force){
clear();
- if (showWidgets){
- Bangle.drawWidgets();
- }
}
+ if (firstDraw) Bangle.drawWidgets();
lastDrawnScreen = screen;
- for (let slice of slices.slice(firstSlice,firstSlice + numberOfSlices)) {
+ let sliceHeight = getSliceHeight();
+ for (let slice of slices.slice(firstSlice,firstSlice + WIDGETS.gpstrek.getState().numberOfSlices)) {
g.reset();
if (!slice.refresh || slice.refresh() || force) slice.draw(g,0,ypos,sliceHeight,g.getWidth());
ypos += sliceHeight+1;
g.drawLine(0,ypos-1,g.getWidth(),ypos-1);
}
+
+ if (scheduleDraw){
+ drawInTimeout();
+ }
firstDraw = false;
-}
+};
switchNav();
-g.clear();
+clear();
+}
diff --git a/apps/gpstrek/metadata.json b/apps/gpstrek/metadata.json
index cf5d06baa..3e27a3247 100644
--- a/apps/gpstrek/metadata.json
+++ b/apps/gpstrek/metadata.json
@@ -1,7 +1,7 @@
{
"id": "gpstrek",
"name": "GPS Trekking",
- "version": "0.07",
+ "version": "0.08",
"description": "Helper for tracking the status/progress during hiking. Do NOT depend on this for navigation!",
"icon": "icon.png",
"screenshots": [{"url":"screen1.png"},{"url":"screen2.png"},{"url":"screen3.png"},{"url":"screen4.png"}],
diff --git a/apps/gpstrek/widget.js b/apps/gpstrek/widget.js
index 347df2df5..363ade8ee 100644
--- a/apps/gpstrek/widget.js
+++ b/apps/gpstrek/widget.js
@@ -1,6 +1,28 @@
(() => {
+const SAMPLES=5;
+function initState(){
+ //cleanup volatile state here
+ state = {};
+ state.compassSamples = new Array(SAMPLES).fill(0);
+ state.lastSample = 0;
+ state.sampleIndex = 0;
+ state.currentPos={};
+ state.steps = 0;
+ state.calibAltDiff = 0;
+ state.numberOfSlices = 3;
+ state.steps = 0;
+ state.up = 0;
+ state.down = 0;
+ state.saved = 0;
+ state.avgComp = 0;
+}
+
const STORAGE=require('Storage');
-let state = STORAGE.readJSON("gpstrek.state.json")||{};
+let state = STORAGE.readJSON("gpstrek.state.json");
+if (!state) {
+ state = {};
+ initState();
+}
let bgChanged = false;
function saveState(){
@@ -8,12 +30,13 @@ function saveState(){
STORAGE.writeJSON("gpstrek.state.json", state);
}
-E.on("kill",()=>{
- if (bgChanged){
+function onKill(){
+ if (bgChanged || state.route || state.waypoint){
saveState();
}
-});
+}
+E.on("kill", onKill);
function onPulse(e){
state.bpm = e.bpm;
@@ -23,27 +46,47 @@ function onGPS(fix) {
if(fix.fix) state.currentPos = fix;
}
-function onMag(e) {
- if (!state.compassHeading) state.compassHeading = e.heading;
+let radians = function(a) {
+ return a*Math.PI/180;
+};
- //if (a+180)mod 360 == b then
- //return (a+b)/2 mod 360 and ((a+b)/2 mod 360) + 180 (they are both the solution, so you may choose one depending if you prefer counterclockwise or clockwise direction)
-//else
- //return arctan( (sin(a)+sin(b)) / (cos(a)+cos(b) )
+let degrees = function(a) {
+ let d = a*180/Math.PI;
+ return (d+360)%360;
+};
- /*
- let average;
- let a = radians(compassHeading);
- let b = radians(e.heading);
- if ((a+180) % 360 == b){
- average = ((a+b)/2 % 360); //can add 180 depending on rotation
- } else {
- average = Math.atan( (Math.sin(a)+Math.sin(b))/(Math.cos(a)+Math.cos(b)) );
+function average(samples){
+ let s = 0;
+ let c = 0;
+ for (let h of samples){
+ s += Math.sin(radians(h));
+ c += Math.cos(radians(h));
+ }
+ s /= samples.length;
+ c /= samples.length;
+ let result = degrees(Math.atan(s/c));
+
+ if (c < 0) result += 180;
+ if (s < 0 && c > 0) result += 360;
+
+ result%=360;
+ return result;
+}
+
+function onMag(e) {
+ if (!isNaN(e.heading)){
+ if (Bangle.isLocked() || (Bangle.getGPSFix() && Bangle.getGPSFix().lon))
+ state.avgComp = e.heading;
+ else {
+ state.compassSamples[state.sampleIndex++] = e.heading;
+ state.lastSample = Date.now();
+ if (state.sampleIndex > SAMPLES - 1){
+ state.sampleIndex = 0;
+ let avg = average(state.compassSamples);
+ state.avgComp = average([state.avgComp,avg]);
+ }
+ }
}
- print("Angle",compassHeading,e.heading, average);
- compassHeading = (compassHeading + degrees(average)) % 360;
- */
- state.compassHeading = Math.round(e.heading);
}
function onStep(e) {
@@ -73,6 +116,16 @@ function onAcc (e){
state.acc = e;
}
+function update(){
+ if (state.active){
+ start(false);
+ }
+ if (state.active == !(WIDGETS.gpstrek.width)) {
+ if(WIDGETS.gpstrek) WIDGETS.gpstrek.width = state.active?24:0;
+ Bangle.drawWidgets();
+ }
+}
+
function start(bg){
Bangle.removeListener('GPS', onGPS);
Bangle.removeListener("HRM", onPulse);
@@ -94,9 +147,9 @@ function start(bg){
if (bg){
if (!state.active) bgChanged = true;
state.active = true;
+ update();
saveState();
}
- Bangle.drawWidgets();
}
function stop(bg){
@@ -114,22 +167,10 @@ function stop(bg){
Bangle.removeListener("step", onStep);
Bangle.removeListener("pressure", onPressure);
Bangle.removeListener('accel', onAcc);
+ E.removeListener("kill", onKill);
}
+ update();
saveState();
- Bangle.drawWidgets();
-}
-
-function initState(){
- //cleanup volatile state here
- state.currentPos={};
- state.steps = Bangle.getStepCount();
- state.calibAltDiff = 0;
- state.up = 0;
- state.down = 0;
-}
-
-if (state.saved && state.saved < Date.now() - 60000){
- initState();
}
if (state.active){
@@ -141,11 +182,15 @@ WIDGETS["gpstrek"]={
width:state.active?24:0,
resetState: initState,
getState: function() {
+ if (state.saved && Date.now() - state.saved > 60000 || !state){
+ initState();
+ }
return state;
},
start:start,
stop:stop,
draw:function() {
+ update();
if (state.active){
g.reset();
g.drawImage(atob("GBiBAAAAAAAAAAAYAAAYAAAYAAA8AAA8AAB+AAB+AADbAADbAAGZgAGZgAMYwAMYwAcY4AYYYA5+cA3/sB/D+B4AeBAACAAAAAAAAA=="), this.x, this.y);
diff --git a/apps/ha/metadata.json b/apps/ha/metadata.json
index 052e82fe0..fad052544 100644
--- a/apps/ha/metadata.json
+++ b/apps/ha/metadata.json
@@ -5,7 +5,7 @@
"description": "Integrates your BangleJS into HomeAssistant.",
"icon": "ha.png",
"type": "app",
- "tags": "tool",
+ "tags": "tool,clkinfo",
"readme": "README.md",
"supports": ["BANGLEJS2"],
"custom": "custom.html",
diff --git a/apps/hworldclock/ChangeLog b/apps/hworldclock/ChangeLog
index a4bd84390..8c1517842 100644
--- a/apps/hworldclock/ChangeLog
+++ b/apps/hworldclock/ChangeLog
@@ -7,3 +7,5 @@
0.21: Add Settings
0.22: Use default Bangle formatter for booleans
0.23: Added note to configure position in "my location" if not done yet. Small fixes.
+0.24: Added fast load
+0.25: Minor code optimization
diff --git a/apps/hworldclock/app.js b/apps/hworldclock/app.js
index a0fb4cd20..c80b712da 100644
--- a/apps/hworldclock/app.js
+++ b/apps/hworldclock/app.js
@@ -1,3 +1,5 @@
+{ // must be inside our own scope here so that when we are unloaded everything disappears
+
// ------- Settings file
const SETTINGSFILE = "hworldclock.json";
var secondsMode;
@@ -153,15 +155,15 @@ function updatePos() {
function drawSeconds() {
// get date
- var d = new Date();
- var da = d.toString().split(" ");
+ let d = new Date();
+ let da = d.toString().split(" ");
// default draw styles
g.reset().setBgColor(g.theme.bg).setFontAlign(0, 0);
// draw time
- var time = da[4].split(":");
- var seconds = time[2];
+ let time = da[4].split(":");
+ let seconds = time[2];
g.setFont("5x9Numeric7Seg",primaryTimeFontSize - 3);
if (g.theme.dark) {
@@ -184,15 +186,15 @@ function drawSeconds() {
function draw() {
// get date
- var d = new Date();
- var da = d.toString().split(" ");
+ let d = new Date();
+ let da = d.toString().split(" ");
// default draw styles
g.reset().setBgColor(g.theme.bg).setFontAlign(0, 0);
// draw time
- var time = da[4].split(":");
- var hours = time[0],
+ let time = da[4].split(":");
+ let hours = time[0],
minutes = time[1];
@@ -223,7 +225,7 @@ function draw() {
// am / PM ?
if (_12hour){
//do 12 hour stuff
- //var ampm = require("locale").medidian(new Date()); Not working
+ //let ampm = require("locale").medidian(new Date()); Not working
g.setFont("Vector", 17);
g.drawString(ampm, xyCenterSeconds, yAmPm, true);
}
@@ -232,14 +234,14 @@ function draw() {
// draw Day, name of month, Date
//DATE
- var localDate = require("locale").date(new Date(), 1);
+ let localDate = require("locale").date(new Date(), 1);
localDate = localDate.substring(0, localDate.length - 5);
g.setFont("Vector", 17);
g.drawString(require("locale").dow(new Date(), 1).toUpperCase() + ", " + localDate, xyCenter, yposDate, true);
g.setFont(font, primaryDateFontSize);
// set gmt to UTC+0
- var gmt = new Date(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
+ let gmt = new Date(d.getTime() + d.getTimezoneOffset() * 60 * 1000);
// Loop through offset(s) and render
offsets.forEach((offset, index) => {
@@ -249,7 +251,7 @@ function draw() {
if (offsets.length === 1) {
- var date = [require("locale").dow(new Date(), 1), require("locale").date(new Date(), 1)];
+ let date = [require("locale").dow(new Date(), 1), require("locale").date(new Date(), 1)];
// For a single secondary timezone, draw it bigger and drop time zone to second line
const xOffset = 30;
g.setFont(font, secondaryTimeFontSize).drawString(`${hours}:${minutes}`, xyCenter, yposTime2, true);
@@ -295,8 +297,18 @@ g.clear();
// Init the settings of the app
loadMySettings();
-// Show launcher when button pressed
-Bangle.setUI("clock");
+// Show launcher when middle button pressed
+Bangle.setUI({
+ mode : "clock",
+ remove : function() {
+ // Called to unload all of the clock app
+ if (PosInterval) clearInterval(PosInterval);
+ PosInterval = undefined;
+ if (drawTimeoutSeconds) clearTimeout(drawTimeoutSeconds);
+ drawTimeoutSeconds = undefined;
+ if (drawTimeout) clearTimeout(drawTimeout);
+ drawTimeout = undefined;
+ }});
Bangle.loadWidgets();
Bangle.drawWidgets();
@@ -307,7 +319,7 @@ draw();
if (!Bangle.isLocked()) { // Initial state
if (showSunInfo) {
- if (PosInterval != 0) clearInterval(PosInterval);
+ if (PosInterval != 0 && typeof PosInterval != 'undefined') clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*10E3); // refesh every 10 mins
updatePos();
}
@@ -333,7 +345,7 @@ if (!Bangle.isLocked()) { // Initial state
drawTimeout = undefined;
if (showSunInfo) {
- if (PosInterval != 0) clearInterval(PosInterval);
+ if (PosInterval != 0 && typeof PosInterval != 'undefined') clearInterval(PosInterval);
PosInterval = setInterval(updatePos, 60*60E3); // refesh every 60 mins
updatePos();
}
@@ -378,4 +390,5 @@ Bangle.on('lock',on=>{
}
draw(); // draw immediately, queue redraw
}
- });
\ No newline at end of file
+ });
+}
\ No newline at end of file
diff --git a/apps/hworldclock/metadata.json b/apps/hworldclock/metadata.json
index 653cfc59c..e26599373 100644
--- a/apps/hworldclock/metadata.json
+++ b/apps/hworldclock/metadata.json
@@ -2,7 +2,7 @@
"id": "hworldclock",
"name": "Hanks World Clock",
"shortName": "Hanks World Clock",
- "version": "0.23",
+ "version": "0.25",
"description": "Current time zone plus up to three others",
"allow_emulator":true,
"icon": "app.png",
diff --git a/apps/iconlaunch/ChangeLog b/apps/iconlaunch/ChangeLog
index c71da1467..b03599ae6 100644
--- a/apps/iconlaunch/ChangeLog
+++ b/apps/iconlaunch/ChangeLog
@@ -12,3 +12,9 @@
used Object.assing for the settings
fix cache not deleted when "showClocks" options is changed
added timeOut to return to the clock
+0.11: Cleanup timeout when changing to clock
+ Reset timeout on swipe and drag
+0.12: Use Bangle.load and Bangle.showClock
+0.13: Fix automatic switch to clock
+0.14: Revert use of Bangle.load to classic load calls since widgets would
+still be loaded when they weren't supposed to.
diff --git a/apps/iconlaunch/app.js b/apps/iconlaunch/app.js
index 479956019..ccc39f3bb 100644
--- a/apps/iconlaunch/app.js
+++ b/apps/iconlaunch/app.js
@@ -9,7 +9,6 @@
timeOut:"Off"
}, s.readJSON("iconlaunch.json", true) || {});
- console.log(settings);
if (!settings.fullscreen) {
Bangle.loadWidgets();
Bangle.drawWidgets();
@@ -133,6 +132,7 @@
g.flip();
const itemsN = Math.ceil(launchCache.apps.length / appsN);
let onDrag = function(e) {
+ updateTimeout();
g.setColor(g.theme.fg);
g.setBgColor(g.theme.bg);
let dy = e.dy;
@@ -182,36 +182,27 @@
drag: onDrag,
touch: (_, e) => {
if (e.y < R.y - 4) return;
+ updateTimeout();
let i = YtoIdx(e.y);
selectItem(i, e);
},
- swipe: (h,_) => { if(settings.swipeExit && h==1) { returnToClock(); } },
+ swipe: (h,_) => { if(settings.swipeExit && h==1) { Bangle.showClock(); } },
+ btn: _=> { if (settings.oneClickExit) Bangle.showClock(); },
+ remove: function() {
+ if (timeout) clearTimeout(timeout);
+ }
};
- const returnToClock = function() {
- Bangle.setUI();
- delete launchCache;
- delete launchHash;
- delete drawItemAuto;
- delete drawText;
- delete selectItem;
- delete onDrag;
- delete drawItems;
- delete drawItem;
- delete returnToClock;
- delete idxToY;
- delete YtoIdx;
- delete settings;
- setTimeout(eval, 0, s.read(".bootcde"));
- };
-
-
- if (settings.oneClickExit) mode.btn = returnToClock;
+ let timeout;
+ const updateTimeout = function(){
if (settings.timeOut!="Off"){
let time=parseInt(settings.timeOut); //the "s" will be trimmed by the parseInt
- setTimeout(returnToClock,time*1000);
- }
-
+ if (timeout) clearTimeout(timeout);
+ timeout = setTimeout(Bangle.showClock,time*1000);
+ }
+ };
+
+ updateTimeout();
Bangle.setUI(mode);
}
diff --git a/apps/iconlaunch/metadata.json b/apps/iconlaunch/metadata.json
index 13e7aee08..155e7bd9b 100644
--- a/apps/iconlaunch/metadata.json
+++ b/apps/iconlaunch/metadata.json
@@ -2,7 +2,7 @@
"id": "iconlaunch",
"name": "Icon Launcher",
"shortName" : "Icon launcher",
- "version": "0.10",
+ "version": "0.14",
"icon": "app.png",
"description": "A launcher inspired by smartphones, with an icon-only scrollable menu.",
"tags": "tool,system,launcher",
diff --git a/apps/imageclock/ChangeLog b/apps/imageclock/ChangeLog
index f81bbf185..df7bfd0de 100644
--- a/apps/imageclock/ChangeLog
+++ b/apps/imageclock/ChangeLog
@@ -12,3 +12,9 @@
0.10: Fix clock not correctly refreshing when drawing in timeouts option is not on
0.11: Additional option in customizer to force drawing directly
Fix some problems in handling timeouts
+0.12: Use widget_utils module
+ Fix colorsetting in promises in generated code
+ Some performance improvements by caching lookups
+ Activate UI after first draw is complete to prevent drawing over launcher
+0.13: Use widget_utils swipeOn()
+ Allows minification by combining all but picture data into one file
diff --git a/apps/imageclock/app.js b/apps/imageclock/app.js
index ff3f5a62d..90c6163cd 100644
--- a/apps/imageclock/app.js
+++ b/apps/imageclock/app.js
@@ -1,7 +1,12 @@
-let unlockedDrawInterval = [];
-let lockedDrawInterval = [];
-let showWidgets = false;
-let firstDraw = true;
+let s = {};
+// unlocked draw intervals
+s.udi = [];
+// locked draw intervals
+s.ldi = [];
+// full draw
+s.fd = true;
+// performance log
+s.pl = {};
{
let x = g.getWidth()/2;
@@ -21,12 +26,10 @@ let firstDraw = true;
let precompiledJs = eval(require("Storage").read("imageclock.draw.js"));
let settings = require('Storage').readJSON("imageclock.json", true) || {};
- let performanceLog = {};
-
let startPerfLog = () => {};
let endPerfLog = () => {};
Bangle.printPerfLog = () => {print("Deactivated");};
- Bangle.resetPerfLog = () => {performanceLog = {};};
+ Bangle.resetPerfLog = () => {s.pl = {};};
let colormap={
"#000":0,
@@ -64,35 +67,37 @@ let firstDraw = true;
if (settings.perflog){
startPerfLog = function(name){
let time = getTime();
- if (!performanceLog.start) performanceLog.start={};
- performanceLog.start[name] = time;
+ if (!s.pl.start) s.pl.start={};
+ s.pl.start[name] = time;
};
- endPerfLog = function (name){
+ endPerfLog = function (name, once){
let time = getTime();
- if (!performanceLog.last) performanceLog.last={};
- let duration = time - performanceLog.start[name];
- performanceLog.last[name] = duration;
- if (!performanceLog.cum) performanceLog.cum={};
- if (!performanceLog.cum[name]) performanceLog.cum[name] = 0;
- performanceLog.cum[name] += duration;
- if (!performanceLog.count) performanceLog.count={};
- if (!performanceLog.count[name]) performanceLog.count[name] = 0;
- performanceLog.count[name]++;
+ if (!s.pl.start[name]) return;
+ if (!s.pl.last) s.pl.last={};
+ let duration = time - s.pl.start[name];
+ s.pl.last[name] = duration;
+ if (!s.pl.cum) s.pl.cum={};
+ if (!s.pl.cum[name]) s.pl.cum[name] = 0;
+ s.pl.cum[name] += duration;
+ if (!s.pl.count) s.pl.count={};
+ if (!s.pl.count[name]) s.pl.count[name] = 0;
+ s.pl.count[name]++;
+ if (once){s.pl.start[name] = undefined}
};
Bangle.printPerfLog = function(){
let result = "";
let keys = [];
- for (let c in performanceLog.cum){
+ for (let c in s.pl.cum){
keys.push(c);
}
keys.sort();
for (let k of keys){
- print(k, "last:", (performanceLog.last[k] * 1000).toFixed(0), "average:", (performanceLog.cum[k]/performanceLog.count[k]*1000).toFixed(0), "count:", performanceLog.count[k], "total:", (performanceLog.cum[k] * 1000).toFixed(0));
+ print(k, "last:", (s.pl.last[k] * 1000).toFixed(0), "average:", (s.pl.cum[k]/s.pl.count[k]*1000).toFixed(0), "count:", s.pl.count[k], "total:", (s.pl.cum[k] * 1000).toFixed(0));
}
};
}
-
+ startPerfLog("fullDraw");
startPerfLog("loadFunctions");
let delayTimeouts = {};
@@ -202,27 +207,39 @@ let firstDraw = true;
let firstDigitY = element.Y;
let imageIndex = element.ImageIndex ? element.ImageIndex : 0;
- let firstImage;
- if (imageIndex){
- firstImage = getByPath(resources, [], "" + (0 + imageIndex));
- } else {
- firstImage = getByPath(resources, element.ImagePath, 0);
+ let firstImage = element.cachedFirstImage;
+ if (!firstImage && !element.cachedFirstImageMissing){
+ if (imageIndex){
+ firstImage = getByPath(resources, [], "" + (0 + imageIndex));
+ } else {
+ firstImage = getByPath(resources, element.ImagePath, 0);
+ }
+ element.cachedFirstImage = firstImage;
+ if (!firstImage) element.cachedFirstImageMissing = true;
}
- let minusImage;
- if (imageIndexMinus){
- minusImage = getByPath(resources, [], "" + (0 + imageIndexMinus));
- } else {
- minusImage = getByPath(resources, element.ImagePath, "minus");
+ let minusImage = element.cachedMinusImage;
+ if (!minusImage && !element.cachedMinusImageMissing){
+ if (imageIndexMinus){
+ minusImage = getByPath(resources, [], "" + (0 + imageIndexMinus));
+ } else {
+ minusImage = getByPath(resources, element.ImagePath, "minus");
+ }
+ element.cachedMinusImage = minusImage;
+ if (!minusImage) element.cachedMinusImageMissing = true;
}
- let unitImage;
+ let unitImage = element.cachedUnitImage;
//print("Get image for unit", imageIndexUnit);
- if (imageIndexUnit !== undefined){
- unitImage = getByPath(resources, [], "" + (0 + imageIndexUnit));
- //print("Unit image is", unitImage);
- } else if (element.Unit){
- unitImage = getByPath(resources, element.ImagePath, getMultistate(element.Unit, "unknown"));
+ if (!unitImage && !element.cachedUnitImageMissing){
+ if (imageIndexUnit !== undefined){
+ unitImage = getByPath(resources, [], "" + (0 + imageIndexUnit));
+ //print("Unit image is", unitImage);
+ } else if (element.Unit){
+ unitImage = getByPath(resources, element.ImagePath, getMultistate(element.Unit, "unknown"));
+ }
+ unitImage = element.cachedUnitImage;
+ if (!unitImage) element.cachedUnitImageMissing = true;
}
let numberWidth = (numberOfDigits * firstImage.width) + (Math.max((numberOfDigits - 1),0) * spacing);
@@ -292,14 +309,7 @@ let firstDraw = true;
if (resource){
prepareImg(resource);
//print("lastElem", typeof resource)
- if (resource) {
- element.cachedImage[cacheKey] = resource;
- //print("cache res ",typeof element.cachedImage[cacheKey]);
- } else {
- element.cachedImage[cacheKey] = null;
- //print("cache null",typeof element.cachedImage[cacheKey]);
- //print("Could not create image from", resource);
- }
+ element.cachedImage[cacheKey] = resource;
} else {
//print("Could not get resource from", element, lastElem);
}
@@ -604,18 +614,22 @@ let firstDraw = true;
promise.then(()=>{
let currentDrawingTime = Date.now();
- if (showWidgets && global.WIDGETS){
- //print("Draw widgets");
- restoreWidgetDraw();
- Bangle.drawWidgets();
- g.setColor(g.theme.fg);
- g.drawLine(0,24,g.getWidth(),24);
- }
lastDrawTime = Date.now() - start;
isDrawing=false;
- firstDraw=false;
+ s.fd=false;
requestRefresh = false;
endPerfLog("initialDraw");
+ endPerfLog("fullDraw", true);
+
+ if (!Bangle.uiRemove){
+ setUi();
+ let orig = Bangle.drawWidgets;
+ Bangle.drawWidgets = ()=>{};
+ Bangle.loadWidgets();
+ Bangle.drawWidgets = orig;
+ require("widget_utils").swipeOn();
+ Bangle.drawWidgets();
+ }
}).catch((e)=>{
print("Error during drawing", e);
});
@@ -699,16 +713,16 @@ let firstDraw = true;
let handleLock = function(isLocked, forceRedraw){
//print("isLocked", Bangle.isLocked());
- for (let i of unlockedDrawInterval){
+ for (let i of s.udi){
//print("Clearing unlocked", i);
clearInterval(i);
}
- for (let i of lockedDrawInterval){
+ for (let i of s.ldi){
//print("Clearing locked", i);
clearInterval(i);
}
- unlockedDrawInterval = [];
- lockedDrawInterval = [];
+ s.udi = [];
+ s.ldi = [];
if (!isLocked){
if (forceRedraw || !redrawEvents || (redrawEvents.includes("unlock"))){
@@ -724,7 +738,7 @@ let firstDraw = true;
initialDraw(watchfaceResources, watchface);
},unlockedRedraw, (v)=>{
//print("New matched unlocked interval", v);
- unlockedDrawInterval.push(v);
+ s.udi.push(v);
}, lastDrawTime);
if (!events || events.includes("HRM")) Bangle.setHRMPower(1, "imageclock");
if (!events || events.includes("pressure")) Bangle.setBarometerPower(1, 'imageclock');
@@ -742,54 +756,13 @@ let firstDraw = true;
initialDraw(watchfaceResources, watchface);
},lockedRedraw, (v)=>{
//print("New matched locked interval", v);
- lockedDrawInterval.push(v);
+ s.ldi.push(v);
}, lastDrawTime);
Bangle.setHRMPower(0, "imageclock");
Bangle.setBarometerPower(0, 'imageclock');
}
};
-
- let showWidgetsChanged = false;
- let currentDragDistance = 0;
-
- let restoreWidgetDraw = function(){
- if (global.WIDGETS) {
- for (let w in global.WIDGETS) {
- let wd = global.WIDGETS[w];
- wd.draw = originalWidgetDraw[w];
- wd.area = originalWidgetArea[w];
- }
- }
- };
-
- let handleDrag = function(e){
- //print("handleDrag");
- currentDragDistance += e.dy;
- if (Math.abs(currentDragDistance) < 10) return;
- dragDown = currentDragDistance > 0;
- currentDragDistance = 0;
- if (!showWidgets && dragDown){
- //print("Enable widgets");
- restoreWidgetDraw();
- showWidgetsChanged = true;
- }
- if (showWidgets && !dragDown){
- //print("Disable widgets");
- clearWidgetsDraw();
- firstDraw = true;
- showWidgetsChanged = true;
- }
- if (showWidgetsChanged){
- showWidgetsChanged = false;
- //print("Draw after widget change");
- showWidgets = dragDown;
- initialDraw();
- }
- };
-
- Bangle.on('drag', handleDrag);
-
if (!events || events.includes("pressure")){
Bangle.on('pressure', handlePressure);
try{
@@ -808,69 +781,43 @@ let firstDraw = true;
if (!events || events.includes("charging")) {
Bangle.on('charging', handleCharging);
}
-
- let originalWidgetDraw = {};
- let originalWidgetArea = {};
-
- let clearWidgetsDraw = function(){
- //print("Clear widget draw calls");
- if (global.WIDGETS) {
- originalWidgetDraw = {};
- originalWidgetArea = {};
- for (let w in global.WIDGETS) {
- let wd = global.WIDGETS[w];
- originalWidgetDraw[w] = wd.draw;
- originalWidgetArea[w] = wd.area;
- wd.draw = () => {};
- wd.area = "";
- }
- }
- }
handleLock(Bangle.isLocked(), true);
- Bangle.setUI({
- mode : "clock",
- remove : function() {
- //print("remove calls");
- // Called to unload all of the clock app
- Bangle.setHRMPower(0, "imageclock");
- Bangle.setBarometerPower(0, 'imageclock');
+ let setUi = function(){
+ Bangle.setUI({
+ mode : "clock",
+ remove : function() {
+ //print("remove calls");
+ // Called to unload all of the clock app
+ Bangle.setHRMPower(0, "imageclock");
+ Bangle.setBarometerPower(0, 'imageclock');
- Bangle.removeListener('drag', handleDrag);
- Bangle.removeListener('lock', handleLock);
- Bangle.removeListener('charging', handleCharging);
- Bangle.removeListener('HRM', handleHrm);
- Bangle.removeListener('pressure', handlePressure);
+ Bangle.removeListener('lock', handleLock);
+ Bangle.removeListener('charging', handleCharging);
+ Bangle.removeListener('HRM', handleHrm);
+ Bangle.removeListener('pressure', handlePressure);
- if (deferredTimout) clearTimeout(deferredTimout);
- if (initialDrawTimeoutUnlocked) clearTimeout(initialDrawTimeoutUnlocked);
- if (initialDrawTimeoutLocked) clearTimeout(initialDrawTimeoutLocked);
+ if (deferredTimout) clearTimeout(deferredTimout);
+ if (initialDrawTimeoutUnlocked) clearTimeout(initialDrawTimeoutUnlocked);
+ if (initialDrawTimeoutLocked) clearTimeout(initialDrawTimeoutLocked);
- for (let i of unlockedDrawInterval){
- //print("Clearing unlocked", i);
- clearInterval(i);
+ for (let i of global.s.udi){
+ //print("Clearing unlocked", i);
+ clearInterval(i);
+ }
+ for (let i of global.s.ldi){
+ //print("Clearing locked", i);
+ clearInterval(i);
+ }
+
+ delete Bangle.printPerfLog;
+ if (settings.perflog){
+ delete Bangle.resetPerfLog;
+ }
+ cleanupDelays();
+ require("widget_utils").show();
}
- delete unlockedDrawInterval;
- for (let i of lockedDrawInterval){
- //print("Clearing locked", i);
- clearInterval(i);
- }
- delete lockedDrawInterval;
- delete showWidgets;
- delete firstDraw;
-
- delete Bangle.printPerfLog;
- if (settings.perflog){
- delete Bangle.resetPerfLog;
- delete performanceLog;
- }
-
- cleanupDelays();
- restoreWidgetDraw();
- }
- });
-
- Bangle.loadWidgets();
- clearWidgetsDraw();
+ });
+ }
}
diff --git a/apps/imageclock/custom.html b/apps/imageclock/custom.html
index e595b51ca..784e6cbdd 100644
--- a/apps/imageclock/custom.html
+++ b/apps/imageclock/custom.html
@@ -4,8 +4,8 @@
-
-
+
+
@@ -25,6 +25,8 @@
+
+
@@ -32,7 +34,7 @@
Select watchface folder:
or
Select watchface zip file:
-
+
@@ -55,15 +57,15 @@
var expectedFiles = 0;
var rootZip = new JSZip();
var resourcesZip = rootZip.folder("resources");
-
+
function isNativeFormat(){
return document.getElementById("useNative").checked;
}
-
+
function addDebug(){
return document.getElementById("debugprints").checked;
}
-
+
function convertAmazfitTime(time){
var result = {};
if (time.Hours){
@@ -88,7 +90,7 @@
}
return result;
}
-
+
function convertAmazfitDate(date){
var result = {};
if (date.MonthAndDay.Separate.Day) result.Day = convertAmazfitNumber(date.MonthAndDay.Separate.Day, "Day");
@@ -98,11 +100,11 @@
}
return result;
}
-
+
var filesToMove={};
-
+
var zipChangePromise = Promise.resolve();
-
+
function performFileChanges(){
var promise = Promise.resolve();
//rename all files to just numbers without leading zeroes
@@ -111,7 +113,7 @@
var tmp = resultJson[c];
delete resultJson[c];
resultJson[Number(c)] = tmp;
-
+
async function modZip(c){
console.log("Async modification of ", c)
var fileRegex = new RegExp(c + ".*");
@@ -120,27 +122,27 @@
console.log("Filedata is", fileData);
var extension = resourcesZip.file(fileRegex)[0].name.match(/\.[^.]*$/);
var newName = Number(c) + extension;
-
+
console.log("Renaming to", newName);
resourcesZip.remove(c + extension);
resourcesZip.file(newName, fileData);
}
promise = promise.then(modZip(c));
-
+
}
-
-
+
+
console.log("File moves:", filesToMove);
-
+
for (var c in filesToMove){
var tmp = resultJson[c];
console.log("Handle filemove", c, filesToMove[c], tmp);
-
+
var element = resultJson;
var path = filesToMove[c];
-
-
+
+
async function modZip(c){
console.log("Async modification of ", c)
var fileRegex = new RegExp(c + ".*");
@@ -149,13 +151,13 @@
console.log("Filedata is", fileData);
var extension = resourcesZip.file(fileRegex)[0].name.match(/\.[^.]*$/);
var newName = Number(c) + extension;
-
+
console.log("Copying to", newName);
resourcesZip.file(filesToMove[c].join("/") + extension, fileData);
}
promise = promise.then(modZip(c));
-
-
+
+
for (var i = 0; i< path.length; i++){
if (!element[path[i]]) element[path[i]] = {};
if (i == path.length - 1){
@@ -164,7 +166,7 @@
element = element[path[i]];
}
}
-
+
}
promise.then(()=>{
document.getElementById('btnUpload').disabled = true;
@@ -172,7 +174,7 @@
console.log("After moves", resultJson);
return promise;
};
-
+
function convertAmazfitMultistate(multistate, value, minValue, maxValue){
var result = {
MultiState: {
@@ -188,18 +190,18 @@
if (multistate.ImageIndexOff) filesToMove[multistate.ImageIndexOff] = ["status", value, "off"];
return result;
}
-
+
function convertAmazfitStatus(status){
var result = {};
-
+
if (status.Alarm) result.Alarm = convertAmazfitMultistate(status.Alarm,"Alarm");
if (status.Bluetooth) result.Bluetooth = convertAmazfitMultistate(status.Bluetooth,"Bluetooth");
if (status.DoNotDisturb) result.DoNotDisturb = convertAmazfitMultistate(status.DoNotDisturb,"Notifications");
if (status.Lock) result.Lock = convertAmazfitMultistate(status.Lock,"Lock");
-
+
return result;
}
-
+
function convertAmazfitNumber(element, value, minValue, maxValue){
var number = {};
var result = {
@@ -233,10 +235,10 @@
if (maxValue !== undefined) number.MinValue = minValue;
return result;
}
-
+
function moveWeatherIcons(icon){
filesToMove[icon.ImageIndex + 0] = ["weather", "fallback"];
-
+
// Light clouds
filesToMove[icon.ImageIndex + 1] = ["weather", 801];
// Cloudy, possible rain
@@ -282,7 +284,7 @@
// Very heavy shower
filesToMove[icon.ImageIndex + 22] = ["weather", 531];
}
-
+
function convertAmazfitTemperature(temp){
var result = {};
result = convertAmazfitNumber(temp.Number, "WeatherTemperature");
@@ -294,15 +296,15 @@
}
return result;
}
-
+
function convertAmazfitWeather(weather){
var result = {};
-
+
if (weather.Temperature && weather.Temperature.Current){
if (!result.Temperature) result.Temperature = {};
result.Temperature.Current = convertAmazfitTemperature(weather.Temperature.Current);
}
-
+
if (weather.Temperature && weather.Temperature.Today){
if (!result.Temperature) result.Temperature = {};
if (weather.Temperature.Today.Separate){
@@ -327,10 +329,10 @@
}
return result;
}
-
+
function convertAmazfitActivity(activity){
var result = {};
-
+
if (activity.Steps){
result.Steps = convertAmazfitNumber(activity.Steps, "Steps");
}
@@ -339,7 +341,7 @@
}
return result;
}
-
+
function convertAmazfitScale(scale, value, minValue, maxValue){
var result = {};
result.Scale = {
@@ -356,10 +358,10 @@
Y: c.Y
});
}
-
+
return result;
}
-
+
function convertAmazfitStepsProgress(steps){
var result = {};
if (steps.GoalImage){
@@ -378,7 +380,7 @@
}
return result;
}
-
+
function convertAmazfitBattery(battery){
var result = {};
if (battery.Scale){
@@ -389,7 +391,7 @@
}
return result;
}
-
+
function convertAmazfitImage(image){
var result = {
Image: {
@@ -401,11 +403,11 @@
};
return result;
}
-
+
function convertAmazfitColor(color){
return "#" + color.substring(2);
}
-
+
function convertAmazfitHand(hand, rotationValue, minRotationValue, maxRotationValue){
var result = {
Filled: !hand.OnlyBorder,
@@ -418,18 +420,18 @@
MaxRotationValue: maxRotationValue,
MinRotationValue: minRotationValue
};
-
+
result.Vertices = []
for (var c of hand.Shape){
result.Vertices.push(c);
}
return { Poly: result };
}
-
+
function convertAmazfitAnalog(analog, face){
var result = {
};
-
+
if (analog.Hours){
result.Hours = {};
result.Hours.Hand = convertAmazfitHand(analog.Hours, "Hour12Analog", 0, 12);
@@ -464,14 +466,14 @@
}
return result;
}
-
+
function restructureAmazfitFormat(dataString){
console.log("Amazfit data:", dataString);
-
-
+
+
var json = JSON.parse(dataString);
faceJson = json;
-
+
var result = {};
result.Properties = {};
@@ -479,8 +481,8 @@
result.Properties.Redraw.Unlocked = 60000;
result.Properties.Redraw.Locked = 60000;
result.Properties.Redraw.Clear = true;
-
-
+
+
if (json.Background){
result.Background = json.Background;
result.Background.Image.ImagePath = [];
@@ -491,32 +493,32 @@
result.Time = convertAmazfitTime(json.Time);
if (json.AnalogDialFace) result.Time.Plane = 1;
}
-
+
if (json.Date){
result.Date = convertAmazfitDate(json.Date);
if (json.AnalogDialFace) result.Date.Plane = 1;
}
-
+
if (json.Status){
result.Status = convertAmazfitStatus(json.Status);
if (json.AnalogDialFace) result.Status.Plane = 1;
}
-
+
if (json.Weather){
result.Weather = convertAmazfitWeather(json.Weather);
if (json.AnalogDialFace) result.Weather.Plane = 1;
}
-
+
if (json.Activity){
result.Activity = convertAmazfitActivity(json.Activity);
if (json.AnalogDialFace) result.Activity.Plane = 1;
}
-
+
if (json.StepsProgress){
result.StepsProgress = convertAmazfitStepsProgress(json.StepsProgress);
if (json.AnalogDialFace) result.StepsProgress.Plane = 1;
}
-
+
if (json.Battery){
result.Battery = convertAmazfitBattery(json.Battery);
if (json.AnalogDialFace) result.Battery.Plane = 1;
@@ -529,7 +531,7 @@
return result;
}
-
+
function parseFaceJson(jsonString){
if (isNativeFormat()){
return JSON.parse(jsonString);
@@ -537,7 +539,7 @@
return restructureAmazfitFormat(jsonString);
}
}
-
+
function combineProperty(name, source, target){
if (source[name] && target[name]){
if (Array.isArray(target[name])){
@@ -556,7 +558,7 @@
if (typeof element == "string" || typeof element == "number") return [];
for (var c in element){
var next = element[c];
-
+
combineProperty("X",element,next);
combineProperty("Y",element,next);
combineProperty("Width",element,next);
@@ -571,7 +573,7 @@
combineProperty("MaxRotationValue",element,next);
if (typeof element.Plane == "number") next.Plane = element.Plane;
next.Layer = element.Layer ? (element.Layer) : "" + c;
-
+
if (["MultiState","Image","CodedImage","Number","Circle","Poly","Rect","Scale"].includes(c)){
result.push({type:c, value: next});
} else {
@@ -580,12 +582,12 @@
}
return result;
}
-
+
function convertToCode(elements, properties, wrapInTimeouts, forceUseOrigPlane){
var code = "(function (wr, wf) {\n";
code += "var lc;\n";
code += "var p = Promise.resolve();\n";
-
+
//get mapped by layer
var counter = 0;
var planes = {};
@@ -606,20 +608,20 @@
}
if (!planeNumbers.includes(0)) planeNumbers.push(0);
planeNumbers.sort().reverse();
-
+
console.log("Found planes", planes, "with numbers", planeNumbers)
-
+
code += "p0 = g;\n";
-
+
for (var planeIndex = 0; planeIndex < planeNumbers.length; planeIndex++){
var layers = planes[planeNumbers[planeIndex]];
var plane = planeNumbers[planeIndex];
-
+
var lastSetColor;
var lastSetBgColor;
-
+
if (plane != 0) code += "if (!p" + plane + ") p" + plane + " = Graphics.createArrayBuffer(g.getWidth(),g.getHeight(),4,{msb:true});\n";
-
+
if (properties.Redraw && properties.Redraw.Clear){
if (wrapInTimeouts && (plane != 0 || forceUseOrigPlane)){
code += "p = p.then(()=>delay(0)).then(()=>{\n";
@@ -632,31 +634,31 @@
code += 'endPerfLog("initialDraw_g.clear");'+ "\n";
code += "});\n";
}
-
+
var previousPlane = plane + 1;
if (previousPlane < planeNumbers.length){
code += "p = p.then(()=>{\n";
-
+
if (addDebug()) code += 'print("Copying of plane ' + previousPlane + ' to display");'+"\n";
//code += "g.drawImage(p" + i + ".asImage());";
code += "p0.drawImage({width: p" + previousPlane + ".getWidth(), height: p" + previousPlane + ".getHeight(), bpp: p" + previousPlane + ".getBPP(), buffer: p" + previousPlane + ".buffer, palette: palette});\n";
code += "});\n";
}
-
+
console.log("Got layers", layers);
for (var layername in layers){
var layerElements = layers[layername];
-
+
console.log("Layer elements", layername, layerElements);
//code for whole layer
-
+
if (addDebug()) code += 'print("Starting layer ' + layername + '");' + "\n";
-
+
var checkForLayerChange = false;
var checkcode = "";
-
+
if (!(properties.Redraw && properties.Redraw.Clear)){
- checkcode = 'firstDraw';
+ checkcode = 's.fd';
for (var i = 0; i< layerElements.length; i++){
var layerElement = layerElements[i];
var referencedElement = elements[layerElements[i].index];
@@ -664,38 +666,38 @@
console.log("Check for change:", layerElement, referencedElement);
if (layerElement.element.Value){
if (elementType == "MultiState" && layerElement.element.Value) {
- checkcode += '| isChangedMultistate(wf.Collapsed[' + layerElement.index + '].value)';
+ checkcode += '| isChangedMultistate(wf.c[' + layerElement.index + '].value)';
} else {
- checkcode += '| isChangedNumber(wf.Collapsed[' + layerElement.index + '].value)';
+ checkcode += '| isChangedNumber(wf.c[' + layerElement.index + '].value)';
}
checkForLayerChange = true;
}
}
}
-
-
+
+
//code for elements
for (var i = 0; i< layerElements.length; i++){
var elementIndex = layerElements[i].index;
var c = elements[elementIndex];
console.log("convert to code", c);
-
+
var condition = "";
if (checkcode.length > 0 && checkForLayerChange){
if (condition.length > 0) condition += " && ";
condition = '(' + checkcode + ')';
}
-
+
if (c.value.HideOn && c.value.HideOn.includes("Lock")){
if (condition.length > 0) condition += " && ";
condition = '!Bangle.isLocked()';
}
-
+
if (c.value.Type == "Once"){
if (condition.length > 0) condition += " && ";
- condition += "firstDraw";
+ condition += "s.fd";
}
-
+
var planeName = "p" + plane;
var colorsetting = "";
if (c.value.ForegroundColor && lastSetColor != c.value.ForegroundColor){
@@ -712,28 +714,28 @@
else
colorsetting += planeName + ".setBgColor(\"" + c.value.BackgroundColor + "\");\n";
}
-
+
if (addDebug()) code += 'print("Element condition is ' + condition + '");' + "\n";
- code += "" + colorsetting;
code += (condition.length > 0 ? "if (" + condition + "){\n" : "");
if (wrapInTimeouts && (plane != 0 || forceUseOrigPlane)){
code += "p = p.then(()=>delay(0)).then(()=>{\n";
} else {
code += "p = p.then(()=>{\n";
}
+ code += "" + colorsetting;
if (addDebug()) code += 'print("Drawing element ' + elementIndex + ' with type ' + c.type + ' on plane ' + planeName + '");' + "\n";
- code += "draw" + c.type + "(" + planeName + ", wr, wf.Collapsed[" + elementIndex + "].value);\n";
+ code += "draw" + c.type + "(" + planeName + ", wr, wf.c[" + elementIndex + "].value);\n";
code += "});\n";
code += (condition.length > 0 ? "}\n" : "");
}
}
console.log("Current plane is", plane);
-
-
+
+
}
-
+
code += "return p;})";
console.log("Code:", code);
return code
@@ -742,14 +744,14 @@
function postProcess(){
moveData(resultJson);
console.log("Created data file", resourceDataString, resourceDataOffset, resultJson);
-
+
var properties = faceJson.Properties;
- faceJson = { Properties: properties, Collapsed: collapseTree(faceJson,{X:0,Y:0})};
+ faceJson = { Properties: properties, c: collapseTree(faceJson,{X:0,Y:0})};
console.log("After collapsing", faceJson);
- precompiledJs = convertToCode(faceJson.Collapsed, properties, document.getElementById('timeoutwrap').checked, document.getElementById('forceOrigPlane').checked);
+ precompiledJs = convertToCode(faceJson.c, properties, document.getElementById('timeoutwrap').checked, document.getElementById('forceOrigPlane').checked);
console.log("After precompiling", precompiledJs);
}
-
+
function convertJsToJson(imgstr){
var E = {};
E.toArrayBuffer = (s)=>s;
@@ -768,7 +770,7 @@
function imageLoaded() {
var options = {};
-
+
options.diffusion = infoJson.diffusion ? infoJson.diffusion : "none";
options.compression = false;
options.alphaToColor = false;
@@ -779,12 +781,12 @@
options.contrast = 0;
options.mode = infoJson.color ? infoJson.color : "1bit";
options.output = "object";
-
+
console.log("Loaded image has path", this.path);
var jsonPath = this.path.split("/");
-
+
var forcedTransparentColorMatch = jsonPath[jsonPath.length-1].match(/.*\.t([^.]+)\..*/)
-
+
var forcedTransparentColor;
if (jsonPath[jsonPath.length-1].includes(".t.")){
options.transparent = true;
@@ -792,13 +794,13 @@
options.transparent = false;
forcedTransparentColor = forcedTransparentColorMatch[1];
}
-
-
+
+
console.log("image has transparency", options.transparent);
console.log("image has forced transparent color", forcedTransparentColor);
jsonPath[jsonPath.length-1] = jsonPath[jsonPath.length-1].replace(/([^.]*)\..*/, "$1");
console.log("Loaded image has json path", jsonPath);
-
+
var canvas = document.getElementById("canvas")
canvas.width = this.width*2;
canvas.height = this.height;
@@ -819,7 +821,7 @@
imgstr = imageconverter.RGBAtoString(rgba, options);
var outputImageData = new ImageData(options.rgbaOut, options.width, options.height);
ctx.putImageData(outputImageData,this.width,0);
-
+
imgstr = convertJsToJson(imgstr);
// checkerboard for transparency on original image
@@ -827,9 +829,9 @@
imageconverter.RGBAtoCheckerboard(imageData.data, {width:this.width,height:this.height});
ctx.putImageData(imageData,0,0);
-
+
var currentElement = resultJson;
-
+
for (var i = 0; i < jsonPath.length; i++){
if (i == jsonPath.length - 1){
var resultingObject = JSON.parse(imgstr);
@@ -841,18 +843,18 @@
currentElement = currentElement[jsonPath[i]];
}
}
-
+
handledFiles++;
console.log("Expected:", expectedFiles, " handled:", handledFiles);
-
+
if (handledFiles == expectedFiles){
if (!isNativeFormat()) {
performFileChanges().then(()=>{
postProcess();
-
+
rootZip.file("face.json", JSON.stringify(faceJson, null, 2));
rootZip.file("info.json", JSON.stringify(infoJson, null, 2));
-
+
document.getElementById('btnSave').disabled = false;
document.getElementById('btnSaveFace').disabled = false;
document.getElementById('btnSaveZip').disabled = false;
@@ -860,21 +862,21 @@
});
} else {
postProcess();
-
+
document.getElementById('btnSave').disabled = false;
document.getElementById('btnSaveFace').disabled = false;
document.getElementById('btnUpload').disabled = false;
}
}
}
-
+
function handleWatchFace(infoFile, faceFile, resourceFiles){
if (isNativeFormat()){
var reader = new FileReader();
reader.path = infoFile.webkitRelativePath;
reader.onload = function(event) {
infoJson = JSON.parse(reader.result);
-
+
handleFaceJson(faceFile, resourceFiles);
};
reader.readAsText(infoFile);
@@ -883,18 +885,18 @@
handleFaceJson(faceFile, resourceFiles);
}
}
-
+
function handleFaceJson(faceFile, resourceFiles){
var reader = new FileReader();
reader.path = faceFile.webkitRelativePath;
reader.onload = function(event) {
faceJson = parseFaceJson(reader.result);
-
+
handleResourceFiles(resourceFiles);
};
reader.readAsText(faceFile);
}
-
+
function handleResourceFiles(files){
for (var current of files){
console.log('Handle resource file ', current);
@@ -917,25 +919,25 @@
reader.readAsDataURL(current);
}
}
-
+
function handleFileSelect(event) {
handledFiles = 0;
expectedFiles = undefined;
-
+
document.getElementById('btnSave').disabled = true;
document.getElementById('btnSaveZip').disabled = true;
document.getElementById('btnSaveFace').disabled = true;
document.getElementById('btnUpload').disabled = true;
-
+
console.log("File select event", event);
if (event.target.files.length == 0) return;
result = "";
resultJson= {};
-
+
var resourceFiles = [];
var faceFile;
var infoFile;
-
+
for (var current of event.target.files){
console.log('Handle file ', current);
if (isNativeFormat()){
@@ -970,10 +972,10 @@
}
}
handleWatchFace(infoFile, faceFile, resourceFiles);
-
+
};
document.getElementById('fileLoader').addEventListener('change', handleFileSelect, false);
-
+
function moveData(json){
console.log("MoveData for", json);
for (var k in json){
@@ -997,11 +999,11 @@
}
}
}
-
+
document.getElementById("timeoutwrap").addEventListener("click", function() {
document.getElementById("forceOrigPlane").disabled = !document.getElementById("timeoutwrap").checked;
});
-
+
document.getElementById("btnSave").addEventListener("click", function() {
var h = document.createElement('a');
h.href = 'data:text/json;charset=utf-8,' + encodeURI(JSON.stringify(resultJson));
@@ -1010,25 +1012,48 @@
h.click();
});
document.getElementById("btnUpload").addEventListener("click", function() {
-
+
+ console.log("Fetching app");
+ fetch('app.js').then((r) => {
+ console.log("Got response", r);
+ return r.text();
+ }
+ ).then((imageclockSrc) => {
+ console.log("Got src", imageclockSrc)
+
+ if (!document.getElementById('separateFiles').checked){
+ if (precompiledJs.length > 0){
+ const replacementString = 'eval(require("Storage").read("imageclock.draw.js"))';
+ console.log("Can replace:", imageclockSrc.includes(replacementString));
+ imageclockSrc = imageclockSrc.replace(replacementString, precompiledJs);
+ }
+ imageclockSrc = imageclockSrc.replace('require("Storage").readJSON("imageclock.face.json")', JSON.stringify(faceJson));
+ imageclockSrc = imageclockSrc.replace('require("Storage").readJSON("imageclock.resources.json")', JSON.stringify(resultJson));
+ }
var appDef = {
id : "imageclock",
storage:[
- {name:"imageclock.app.js", url:"app.js"},
- {name:"imageclock.resources.json", content: JSON.stringify(resultJson)},
{name:"imageclock.img", url:"app-icon.js", evaluate:true},
]
};
+ if (document.getElementById('separateFiles').checked){
+ appDef.storage.push({name:"imageclock.app.js", url:"app.js"});
+ if (precompiledJs.length > 0){
+ appDef.storage.push({name:"imageclock.draw.js", content:precompiledJs});
+ }
+ appDef.storage.push({name:"imageclock.face.json", content: JSON.stringify(faceJson)});
+ appDef.storage.push({name:"imageclock.resources.json", content: JSON.stringify(resultJson)});
+ } else {
+ appDef.storage.push({name:"imageclock.app.js", url:"pleaseminifycontent.js", content:imageclockSrc});
+ }
if (resourceDataString.length > 0){
appDef.storage.push({name:"imageclock.resources.data", content: resourceDataString});
}
- appDef.storage.push({name:"imageclock.draw.js", content: precompiledJs.length > 0 ? precompiledJs : "//empty"});
- appDef.storage.push({name:"imageclock.face.json", content: JSON.stringify(faceJson)});
-
console.log("Uploading app:", appDef);
sendCustomizedApp(appDef);
+ });
});
-
+
function handleZipSelect(evt) {
@@ -1040,18 +1065,18 @@
document.getElementById('btnSaveZip').disabled = true;
document.getElementById('btnUpload').disabled = true;
JSZip.loadAsync(f).then(function(zip) {
-
+
console.log("Zip loaded", zip);
result = "";
resultJson= {};
-
+
var resourceFiles = [];
-
+
var promise = zip.file("face.json").async("string").then((data)=>{
console.log("face.json data", data);
faceJson = parseFaceJson(data);
});
-
+
if (isNativeFormat()){
promise = promise.then(zip.file("info.json").async("string").then((data)=>{
console.log("info.json data", data);
@@ -1062,12 +1087,12 @@
"color": "3bit",
"transparent": true
};
-
+
}
-
+
zip.folder("resources").forEach(function (relativePath, file){
console.log("iterating over", relativePath);
-
+
if (!file.dir){
expectedFiles++;
promise = promise.then(file.async("blob").then(function (blob) {
@@ -1083,10 +1108,10 @@
reader.readAsDataURL(blob);
}));
}
-
+
});
-
-
+
+
}, function (e) {
console.log("Error reading " + f.name + ": " + e.message);
});
@@ -1095,11 +1120,11 @@
console.log("Zip select event", evt);
var files = evt.target.files;
-
+
if (files.length > 1){
alert("Only one file allowed");
}
-
+
handleFile(files[0]);
}
@@ -1113,7 +1138,7 @@
});
}
-
+
document.getElementById("btnSaveFace").addEventListener("click", function() {
var h = document.createElement('a');
h.href = 'data:text/json;charset=utf-8,' + encodeURI(JSON.stringify(faceJson));
@@ -1121,14 +1146,14 @@
h.download = "face.json";
h.click();
});
-
+
document.getElementById('zipLoader').addEventListener('change', handleZipSelect, false);
document.getElementById('btnSaveZip').addEventListener('click', handleZipExport, false);
document.getElementById('btnSave').disabled = true;
document.getElementById('btnSaveFace').disabled = true;
document.getElementById('btnSaveZip').disabled = true;
document.getElementById('btnUpload').disabled = true;
-
+
diff --git a/apps/imageclock/metadata.json b/apps/imageclock/metadata.json
index e068b9fa7..b291ab01e 100644
--- a/apps/imageclock/metadata.json
+++ b/apps/imageclock/metadata.json
@@ -2,7 +2,7 @@
"id": "imageclock",
"name": "Imageclock",
"shortName": "Imageclock",
- "version": "0.11",
+ "version": "0.13",
"type": "clock",
"description": "BETA!!! File formats still subject to change --- This app is a highly customizable watchface. To use it, you need to select a watchface. You can build the watchfaces yourself without programming anything. All you need to do is write some json and create image files.",
"icon": "app.png",
diff --git a/apps/imgclock/custom.html b/apps/imgclock/custom.html
index 1d8e06c07..68d059b80 100644
--- a/apps/imgclock/custom.html
+++ b/apps/imgclock/custom.html
@@ -10,7 +10,7 @@