diff --git a/README.md b/README.md
index 45643d6df..a0a5e5ba4 100644
--- a/README.md
+++ b/README.md
@@ -120,7 +120,7 @@ Apps are listed in the Bangle.js menu, accessible from a clock app via the middl
#### `app-icon.js`
-The icon image and short description is used in the menu entry as selection possibility.
+The icon image and short description is used in Bangle.js's launcher.
Use the Espruino [image converter](https://www.espruino.com/Image+Converter) and upload your `app.png` file.
@@ -136,11 +136,14 @@ Follow this steps to create a readable icon as image string.
Replace this line with the image converter output:
```
-require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA=="));
+require("heatshrink").decompress(atob("mEwwJC/AH4A/AH4AgA=="))
```
-Keep in mind to use this converter for creating images you like to draw with `g.drawImage()` with your app.
+You can also use this converter for creating images you like to draw with `g.drawImage()` with your app.
+Apps that need widgets can call `Bangle.loadWidgets()` **once** at startup to load
+them, and then `Bangle.drawWidgets()` to draw them onto the screen whenever the app
+has call to completely clear the screen. Widgets themselves will update as and when needed.
### Widget Example
@@ -149,6 +152,23 @@ The widget example is available in [`apps/_example_widget`](apps/_example_widget
* `add_to_apps.json` - insert into `apps.json`, describes the widget to bootloader and loader
* `widget.js` - widget code
+Widgets are just small bits of code that run whenever an app that supports them
+calls `Bangle.loadWidgets()`. If they want to display something in the 24px high
+widget bars at the top and bottom of the screen they can add themselves to
+the global `WIDGETS` array with:
+
+```
+WIDGETS["mywidget"]={
+ area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right)
+ width: 24, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
+ draw:draw // called to draw the widget
+};
+```
+
+When the widget is to be drawn, `x` and `y` values are set up in `WIDGETS["mywidget"]`
+and `draw` can then use `this.x` and `this.y` to figure out where it needs to draw to.
+
+
### `app.info` format
This is the file that's **auto-generated** and loaded onto Bangle.js by the App Loader,
@@ -306,6 +326,11 @@ See [apps/gpsrec/interface.html](the GPS Recorder) for a full example.
- 'Welcome' apps define a file called `welcome.js` which the booloader picks up. This then chain-loads the welcome app itself.
+- 'Alarm' apps define a file called `alarm.js` which handles the actual alarm window.
+
+- Locale is handled by `require("locale")`. An app may create a `locale` file in Storage which is
+a module that overwrites Bangle.js's default locale.
+
### Graphic areas
diff --git a/apps.json b/apps.json
index ab6542ad3..3ffdbe5d4 100644
--- a/apps.json
+++ b/apps.json
@@ -2,7 +2,7 @@
{ "id": "boot",
"name": "Bootloader",
"icon": "bootloader.png",
- "version":"0.07",
+ "version":"0.10",
"description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings",
"tags": "tool,system",
"type":"bootloader",
@@ -28,7 +28,7 @@
{ "id": "about",
"name": "About",
"icon": "app.png",
- "version":"0.01",
+ "version":"0.04",
"description": "Bangle.js About page - showing software version, stats, and a collaborative mural from the Bangle.js KickStarter backers",
"tags": "tool,system",
"allow_emulator":true,
@@ -37,10 +37,23 @@
{"name":"about.img","url":"app-icon.js","evaluate":true}
]
},
+ { "id": "locale",
+ "name": "Languages",
+ "icon": "locale.png",
+ "version":"0.01",
+ "description": "Translations for different countries",
+ "tags": "tool,system,locale,translate",
+ "type": "locale",
+ "custom":"locale.html",
+ "storage": [
+ {"name":"locale"}
+ ],
+ "sortorder" : -10
+ },
{ "id": "welcome",
"name": "Welcome",
"icon": "app.png",
- "version":"0.03",
+ "version":"0.04",
"description": "Appears at first boot and explains how to use Bangle.js",
"tags": "start,welcome",
"allow_emulator":true,
@@ -53,7 +66,7 @@
{ "id": "gbridge",
"name": "Gadgetbridge",
"icon": "app.png",
- "version":"0.03",
+ "version":"0.04",
"description": "The default notification handler for Gadgetbridge notifications from Android",
"tags": "tool,system,android,widget",
"storage": [
@@ -79,7 +92,7 @@
{ "id": "setting",
"name": "Settings",
"icon": "settings.png",
- "version":"0.05",
+ "version":"0.06",
"description": "A menu for setting up Bangle.js",
"tags": "tool,system",
"storage": [
@@ -93,7 +106,7 @@
"name": "Default Alarm",
"shortName":"Alarms",
"icon": "app.png",
- "version":"0.02",
+ "version":"0.04",
"description": "Set and respond to alarms",
"tags": "tool,alarm,widget",
"storage": [
@@ -130,16 +143,17 @@
{"name":"aclock.img","url":"clock-analog-icon.js","evaluate":true}
]
},
- { "id": "clck3x2",
- "name": "3x2 Pixel Clock",
- "icon": "clock3x2.png",
- "version":"0.03",
- "description": "This is a simple clock using minimalistic 3x2 pixel numerical digits",
+ { "id": "clock2x3",
+ "name": "2x3 Pixel Clock",
+ "icon": "clock2x3.png",
+ "version":"0.04",
+ "description": "This is a simple clock using minimalist 2x3 pixel numerical digits",
"tags": "clock",
+ "type": "clock",
"allow_emulator":true,
"storage": [
- {"name":"clck3x2.app.js","url":"clock3x2.js"},
- {"name":"clck3x2.img","url":"clock3x2-icon.js","evaluate":true}
+ {"name":"clock2x3.app.js","url":"clock2x3-app.js"},
+ {"name":"clock2x3.img","url":"clock2x3-icon.js","evaluate":true}
]
},
{ "id": "trex",
@@ -236,7 +250,7 @@
{ "id": "gpsrec",
"name": "GPS Recorder",
"icon": "app.png",
- "version":"0.04",
+ "version":"0.06",
"interface": "interface.html",
"description": "Application that allows you to record a GPS track. Can run in background",
"tags": "tool,outdoors,gps,widget",
@@ -247,6 +261,20 @@
{"name":"gpsrec.wid.js","url":"widget.js"}
]
},
+ { "id": "heart",
+ "name": "Heart Rate Recorder",
+ "icon": "app.png",
+ "version":"0.01",
+ "interface": "interface.html",
+ "description": "Application that allows you to record your heart rate. Can run in background",
+ "tags": "tool,health,widget",
+ "storage": [
+ {"name":"heart.app.js","url":"app.js"},
+ {"name":"heart.json","url":"app-settings.json","evaluate":true},
+ {"name":"heart.img","url":"app-icon.js","evaluate":true},
+ {"name":"heart.wid.js","url":"widget.js"}
+ ]
+ },
{ "id": "slevel",
"name": "Spirit Level",
"icon": "spiritlevel.png",
@@ -272,7 +300,7 @@
{ "id": "widbat",
"name": "Battery Level Widget",
"icon": "widget.png",
- "version":"0.02",
+ "version":"0.04",
"description": "Show the current battery level and charging status in the top right of the clock",
"tags": "widget,battery",
"type":"widget",
@@ -283,7 +311,7 @@
{ "id": "widbt",
"name": "Bluetooth Widget",
"icon": "widget.png",
- "version":"0.01",
+ "version":"0.03",
"description": "Show the current Bluetooth connection status in the top right of the clock",
"tags": "widget,bluetooth",
"type":"widget",
@@ -305,7 +333,7 @@
{ "id": "widhrm",
"name": "Simple Heart Rate widget",
"icon": "widget.png",
- "version":"0.01",
+ "version":"0.03",
"description": "When the screen is on, the widget turns on the heart rate monitor and displays the current heart rate (or last known in grey). For this to work well you'll need at least a 15 second LCD Timeout.",
"tags": "health,widget",
"type": "widget",
@@ -316,7 +344,7 @@
{ "id": "stetho",
"name": "Stethoscope",
"icon": "stetho.png",
- "version":"0.0198",
+ "version":"0.01",
"description": "Hear your heart rate",
"tags": "health",
"storage": [
@@ -459,7 +487,7 @@
{ "id": "widnceu",
"name": "NCEU Logo Widget",
"icon": "widget.png",
- "version":"0.01",
+ "version":"0.02",
"description": "Show the NodeConf EU logo in the top left",
"tags": "widget",
"type":"widget",
@@ -467,9 +495,6 @@
{"name":"widnceu.wid.js","url":"widget.js"}
]
},
-
-
-
{ "id": "sclock",
"name": "Simple Clock",
"icon": "clock-simple.png",
@@ -638,7 +663,7 @@
"id": "gpsinfo",
"name": "GPS Info",
"icon": "gps-info.png",
- "version":"0.01",
+ "version":"0.02",
"description": "An application that displays information about altitude, lat/lon, satellites and time",
"tags": "gps",
"type": "app",
@@ -691,7 +716,7 @@
{ "id": "widclk",
"name": "Digital clock widget",
"icon": "widget.png",
- "version":"0.01",
+ "version":"0.03",
"description": "A simple digital clock widget",
"tags": "widget,clock",
"type":"widget",
@@ -702,7 +727,7 @@
{ "id": "widpedom",
"name": "Pedometer widget",
"icon": "widget.png",
- "version":"0.06",
+ "version":"0.08",
"description": "Daily pedometer widget",
"tags": "widget",
"type":"widget",
@@ -753,6 +778,7 @@
{ "id": "flagrse",
"name": "Espruino Flag Raiser",
"icon": "app.png",
+ "version":"0.01",
"description": "App to send a command to another Espruino to cause it to raise a flag",
"tags": "",
"storage": [
@@ -774,6 +800,19 @@
{"name":"pipboy.img","url":"app-icon.js","evaluate":true}
]
},
+ { "id": "torch",
+ "name": "Torch",
+ "shortName":"Torch",
+ "icon": "app.png",
+ "version":"0.01",
+ "description": "Turns screen white to help you see in the dark. Select from the launcher or press BTN3 four times in quick succession to start when in normal clock mode",
+ "tags": "tool,torch",
+ "storage": [
+ {"name":"torch.app.js","url":"app.js"},
+ {"name":"torch.wid.js","url":"widget.js"},
+ {"name":"torch.img","url":"app-icon.js","evaluate":true}
+ ]
+ },
{ "id": "wohrm",
"name": "Workout Heart Rate Monitor",
"icon": "wohrm.png",
@@ -783,9 +822,19 @@
"type": "app",
"allow_emulator":true,
"storage": [
- {"name":"+wohrm","url":"wohrm.json"},
- {"name":"-wohrm","url":"wohrm.js"},
- {"name":"*wohrm","url":"wohrm-icon.js","evaluate":true}
+ {"name":"wohrm.app.js","url":"wohrm.js"},
+ {"name":"wohrm.img","url":"wohrm-icon.js","evaluate":true}
+ ]
+ },
+ { "id": "widid",
+ "name": "Bluetooth ID Widget",
+ "icon": "widget.png",
+ "version":"0.02",
+ "description": "Display the last two tuple of your Bangle.js MAC address in the widget section. This is useful for figuring out which Bangle.js to connect to if you have more than one Bangle.js!",
+ "tags": "widget,address,mac",
+ "type":"widget",
+ "storage": [
+ {"name":"widid.wid.js","url":"widget.js"}
]
}
]
diff --git a/apps/_example_app/add_to_apps.json b/apps/_example_app/add_to_apps.json
index c75f9ed7d..ca75a7bd8 100644
--- a/apps/_example_app/add_to_apps.json
+++ b/apps/_example_app/add_to_apps.json
@@ -7,8 +7,7 @@
"description": "A detailed description of my great app",
"tags": "",
"storage": [
- {"name":"+7chname","url":"app.json"},
- {"name":"-7chname","url":"app.js"},
- {"name":"*7chname","url":"app-icon.js","evaluate":true}
+ {"name":"7chname.app.js","url":"app.js"},
+ {"name":"7chname.img","url":"app-icon.js","evaluate":true}
]
}
diff --git a/apps/_example_widget/add_to_apps.json b/apps/_example_widget/add_to_apps.json
index 3011fe744..5d0057960 100644
--- a/apps/_example_widget/add_to_apps.json
+++ b/apps/_example_widget/add_to_apps.json
@@ -8,7 +8,6 @@
"tags": "widget",
"type": "widget",
"storage": [
- {"name":"+7chname","url":"widget.json"},
- {"name":"=7chname","url":"widget.js"}
+ {"name":"7chname.wid.js","url":"widget.js"}
]
}
diff --git a/apps/_example_widget/widget.js b/apps/_example_widget/widget.js
index 9ee9bfee8..3893e3096 100644
--- a/apps/_example_widget/widget.js
+++ b/apps/_example_widget/widget.js
@@ -1,17 +1,16 @@
/* run widgets in their own function scope so they don't interfere with
currently-running apps */
(() => {
- // add the width
- var xpos = WIDGETPOS.tr-24;/**/;
- WIDGETPOS.tr-= 28;/* the widget width plus some extra pixel to keep distance to others */;
-
- // draw your widget at xpos
function draw() {
g.reset(); // reset the graphics context to defaults (color/font/etc)
// add your code
+ g.drawString("X", this.x, this.y);
}
// add your widget
- WIDGETS["mywidget"]={draw:draw};
-
+ WIDGETS["mywidget"]={
+ area:"tl", // tl (top left), tr (top right), bl (bottom left), br (bottom right)
+ width: 28, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
+ draw:draw // called to draw the widget
+ };
})()
diff --git a/apps/about/ChangeLog b/apps/about/ChangeLog
index 5560f00bc..2c81c0537 100644
--- a/apps/about/ChangeLog
+++ b/apps/about/ChangeLog
@@ -1 +1,4 @@
0.01: New App!
+0.02: Update version checker for new filename type
+0.03: Actual pixels as of 5 Mar 2020
+0.04: Actual pixels as of 9 Mar 2020
diff --git a/apps/about/app.js b/apps/about/app.js
index 35ea07828..dc7b0cad8 100644
--- a/apps/about/app.js
+++ b/apps/about/app.js
@@ -11,13 +11,13 @@ g.drawString("Powered by Espruino",0,y+=4+h);
g.drawString("Version "+ENV.VERSION,0,y+=h);
g.drawString("Commit "+ENV.GIT_COMMIT,0,y+=h);
function getVersion(name,file) {
- var j = s.readJSON(file);
+ var j = s.readJSON(file,1);
var v = ("object"==typeof j)?j.version:false;
g.drawString(v?(name+" "+(v?"v"+v:"Unknown")):"NO "+name,0,y+=h);
}
-getVersion("Bootloader","+boot");
-getVersion("Launcher","+launch");
-getVersion("Settings","+setting");
+getVersion("Bootloader","boot.info");
+getVersion("Launcher","launch.info");
+getVersion("Settings","setting.info");
y+=h;
g.drawString(MEM.total+" JS Variables available",0,y+=h);
@@ -28,6 +28,6 @@ g.setFontAlign(0,-1);
g.drawString(NRF.getAddress(),120,232);
g.flip();
-// Pixel chooser 'test' image
-g.drawImage(require("heatshrink").decompress(atob("+FQgnu93ACRkO9xU/AEkOhvd43PmV3v0Oh0Mk0GxGIxnMh2+gSIDs1my0LgGw4HP3+/4AOBs1gCAPu8EI/AACBAMHu4DBh47E5fLDQMPBQUMhgMChGIhWqKQvMZBcDgcQN5kM5hACu4hB4EN/tutHhv53DgFgO4OIh/O90MgR3DhMGhczO4hGB4FhCAXg8EPx4ACBIUHHoREF2CBC+ADB6AABBgWAhUKLQ0AhOf/hZB0Gg1QRCgcziCHEu66Cc4nuIAVw5uNO4LvB5OvO4nGd4WIwAkBOwPCgEGcQPuhe7O4X7/52B4DhCd4LRBd4p3DcgX/OATYGJYZ3Dd4x3GBoOgCIUDn0QUglweZR3BKYUINYPq9xuB3fbXgQADGAInBeAcO8DvD4BeDgFxd4gYCIQcHO4kKhjdCGQwbBAQMIO4QFBxG7B4jvMiISEd4QfCABEzmEI9GIx2u/BdBXYglBmAbJO4QpKZAfA5kAyBCCSgREB/n6d5AAExDzE2AyEO4gAFKIIHFd4WA0AtJnjvCs1u9TvBIYcNEoP+O5T8DLJZ3BcYMJHwQJCFILvEAB8IwDvFAAVQO4+jA4ruBgGKd5kO7vd7HY5pfBMIXg8DvMACEMZALuCO4cEAQP/+AgRwDvBO54dLd5RMCvp3BxHe6B3ChsHhsPmZNBhOZBIKLBDYngHR8Jd4MKzQHCgoCBh4UIQJMIxZ3bxDEN3wuBd4PPMwhnB/5cDAQMNDQkOhwGE5iSBO5EAyF61VwHxcP+EPxGPMaIAf5h3BgBHINop3EhAID4GA4EIUgMMOwvPFAKICTIN6vQvH4AYDO4P/AAIHChfrK5ibLhewO6JMCJoIID1WgaZH5O4eIhOQAwOAgEIQAgADL4J3F6AABHQ48DAA8H853NPAUM2G8BYh2SHQUI9CcFzIPE8BYBgBwCO4MIeoRrBDoJ6BgHMeAjvDg2eDYQ6JO5bvOu4DChoABO68MHQMO9zwFMAQAGbAQAId4UMOwgAEFAKWCABHADBIAUFoJYEO4O7O6PMd4YAD1J3CcQIqDhH/QQ8HEw5tHA4IJEVgJxEdxZxOTxcAhbwLcwLnBAA2PM4+JBAPuGIMP//wB413OxwdCO4pxBAQTpbO5gbOeRH4M48ATAjvJvyoGIo8KGQ7uB4AABPDaTbfBDwId4XgAQMPxCHCZ4jeBhuAOYYDDu9wgEJzWZgEN6HQgHQd6MPDIJ3vd4IGE95cChIVChAVF4HgZ4gNBdYgEC9gDCd4R1BPQRzBeARGKAYMJz5uf5hmFO5MP/57CAQPOJAsOwEEAoSABZ4zvB9ydG5w3CO4YADJhEN7qTEAQMPO50Hu54QKAeZB5ZQC4B0Bd4YAC9zvGgFwJB3M46NBzQ3BdgPQAQIPC+ELO4isCX4LBHABVwAAISPa4mQB5MI9GIwENHKQ2PG4TvCAA8O0B3EAgfgWIoAMu93H4sIYwQDDO4sJO5cO90AwDEBDQwZBACMIxIJHO5UNIJLuH5i1MW42AAY0AhgDCO5jvBDAI6FhkMSYQeEAAsMJIsIzAqHzQ2J4DEFd5YSBxB3RABB3DgGZCRmAwA6FO4IRHPIMP/4PCAYQMDvBCOLQ7uHeAylBbAgAEg93BRGwHKBmHhAvGdwUGswTGh/PB4X5OCi1BexLuDAQgAr4BnBABBKEO4UiO47rFACvQBZTsDeAwAnd44AKd4cN7oPJgZ2UO5YAR9vdf5cIhAeOhreCACkP14LJ4UiG4MIhYABgG72EL3YEDBQUA7p3E+DEOhhOIfxuAO5/Q5gpFJIQeEAIOQBIkHu7veY42IKJrFWACENPAJINAYUGux7GAETvQdxENO7sAO4sLAAITIg9nW4XNOUA4EO5A/GhnMAAR4FGSjlHO68Nh3gBIWQhOZfIkHuCxS6DRE4B3H3Z3FB44ACh4BB+H/GRndAIJGP2GwA4uAAwrvGhINFu93AYPMBYJaB1WgXZK6HAAakBO4wALh4ABPYQAphWq1RlBAAJ3H/PwgHdUwUM/KbC0B3F8AABXIK8BGRKtCW4wAKfYXAfwvwAAJrWhOYO5QBC/4ABCwmZyEAAQXt7zvEO5EOxzwDO40I9ADBLwqHMO4UMAQI2BNqcM/B3IxJ3NzIABUoIMDOgLvCxxaCAAW7gGqO4rwBAYXThswQgsOAYPXHhH+GoYYBDQoAFdoQADVQfA6ARE56XEd43MTQ8Hu93uAvEO4oASNQYAIhEIDRfuSQYAXhvdA4ruHMwLvC553IB4J3DhLxBAYR3UYwqEHB5jvFwDvKdQUP/4FDBYWwCQruGhcLO4UM//8fAgvIOIJzZAAUP57vHZY2tEJ+ALwUIDokPwCkHABDcBdwQABhOYd5IAC4HtgH/yHwz4tQd6H5/IjHO5btBd4ZyEOILvIJpl+d4oAChgUJhkOO4LvOnOTyZ8TzwkJ0DqLEprvRg/sAodwBoyhEAAndAQMJzIoLmczO4KZH4D0KYpNQS6YiFd4ruMIZSnGPhAoMnJ3C4AsFh/PCxLtBd5DOMZRAAehkOhzxFwAAChJ3Sc4ikGVY7/C/P5EphtBN455BeBYACcBoAQOwTvUO5YAZg5uGAwIIEhEId8EA9gEBh+/CZh3GhYkKAC2Zzt9vvX7rwFAAt3u7FFeJB8bh/7Cqew2C0fO4OQeBArgd50OhzvQABPM5gRRCRf/ZxLwHAF8ECKELK4X/Ch4RBCRfM4AKIeE0HE40PIw1AAIICBADkOAol3AALvVeFx3HdwNP+juO3e7B5nu8A9RzOQMUSwFTpx3HogABd5+gHpvu9wOL6BhThEIAgWAwAVO8CwSgEMtmGsDvEeAIAFhQbJZgw3BHAZ2BeBh3UOYJ8Dd7sHeIsM5h3FABEKcxoAChvdMahVKhSFVd6p3GtmIUgJ5Dgfn2D3EhT4HdwSCF9vNHBoARhsK7QYVhhEBAQLvMg8HRgwQHhYKGolAEZEJKo19B43s5x3Wd4KhFACp3MCiEIuD3BACvgAAIIF53sC5n/hBsaAEBTBfIL7GABMMhgECyEAYo1wCo3M5gDBhKoEBIIAB4HwxBOSwC9G5/wI4kA4BJDeC9wPB/AAAIIFu5zHd453F9wAC8DvUCY6WBDjTwGgEGd6jwCdRApFUYi/WAB3JO6cAwDvLdgTvMhMPh7wBA4Wgd4YVI3YuGhIGE93MPsi+ByDBChnMIw0IhAKBR47uCAB2f4HwBZDdQO5oMCyB9i5y+Ix3uN4zuBAATuMJgKiCO82QwEIB4sAh53bd5MOO4/oxCvhO6QAGwEJyEIAwXy/8gh/wChH/BYP/gHwRBkEF46CB4CCHSQoCBCI4AMJpMHAIgABuAeLOoJ5BdgYABl4zKOQR5BO6ZrDmAKGd4p3CAQUL3boZhlwAIcAhMOd5hLFO4XwUJIJBd4YPJd5R3KB4/A5h3C9Z3ZAAx3NyAQCESDqCOpoAgd7YAqfgI425nAOXDrBAHXAO4t/uDq/ADVwwABGd5YGF+5EoACkB0EA0Iok2EwAIbvqhwUTHgI9FAAMJhKglhAAEBYsHDqELAAIMJ6ENAQft8BERhoABAgQLFwB3ldwmwBYtwdaOIIwwhGd5kL1WgBI2AAALvBDBIbD3ZrSg1m9y0Sd5sN7AGEhAFE3ZFB74uR3erO47sEd4kJhIDBzIHChWqFp6WCO4Nm8EIwGIAQIaOhYFE+AABYYjnFdAZ1B2EMUyW7d6UJyB4ByCvDxAlHgUwBoWM5nH4/M4B3Bt1gZAOAZYoAKVYR3Id4xhGd6kL1aUDd55EBd4YAEPgktkAICx///n84tQTIbvTyCpDh4ABd4gANMJIATd4QEBh3e6B3EIogWIh8yBAUM5jvC4vg8A+Wd4oA/RxzEIh3u")),0,135);
+// Pixel chooser image
+g.drawImage(require("heatshrink").decompress(atob("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")),0,135);
g.flip();
diff --git a/apps/alarm/ChangeLog b/apps/alarm/ChangeLog
index 248faad95..67feb024f 100644
--- a/apps/alarm/ChangeLog
+++ b/apps/alarm/ChangeLog
@@ -1,2 +1,4 @@
0.01: New App!
0.02: Fix issues with alarm scheduling
+0.03: More alarm scheduling issues
+0.04: Tweaks for variable size widget system
diff --git a/apps/alarm/alarm.js b/apps/alarm/alarm.js
index 70dc75b49..7f0027bc8 100644
--- a/apps/alarm/alarm.js
+++ b/apps/alarm/alarm.js
@@ -10,7 +10,7 @@ function formatTime(t) {
function getCurrentHr() {
var time = new Date();
- return time.getHours()+(time.getMinutes()/60);
+ return time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600);
}
function showAlarm(alarm) {
@@ -47,8 +47,8 @@ function showAlarm(alarm) {
// Check for alarms
var day = (new Date()).getDate();
-var hr = getCurrentHr();
-var alarms = require("Storage").readJSON("alarm.json")||[];
+var hr = getCurrentHr()+10000; // get current time - 10s in future to ensure we alarm if we've started the app a tad early
+var alarms = require("Storage").readJSON("alarm.json",1)||[];
var active = alarms.filter(a=>a.on&&(a.hr {
- var alarms = require('Storage').readJSON('alarm.json')||[];
+ var alarms = require('Storage').readJSON('alarm.json',1)||[];
alarms = alarms.filter(alarm=>alarm.on);
- if (!alarms.length) return;
+ if (!alarms.length) return; // no alarms, no widget!
delete alarms;
- // add the width
- var xpos = WIDGETPOS.tl;
- WIDGETPOS.tl += 24;/* the widget width plus some extra pixel to keep distance to others */;
-
- // draw your widget at xpos
-
// add the widget
- WIDGETS["alarm"]={draw:function() {
+ WIDGETS["alarm"]={area:"tl",width:24,draw:function() {
g.setColor(-1);
- g.drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),xpos,0);
+ g.drawImage(atob("GBgBAAAAAAAAABgADhhwDDwwGP8YGf+YMf+MM//MM//MA//AA//AA//AA//AA//AA//AB//gD//wD//wAAAAADwAABgAAAAAAAAA"),this.x,this.y);
}};
})()
diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog
index 3c6dfec29..093f017b2 100644
--- a/apps/boot/ChangeLog
+++ b/apps/boot/ChangeLog
@@ -4,3 +4,7 @@
0.05: Add Welcome screen on boot
0.06: Disable GPS time log messages, add (default=1) setting to hide log messages
0.07: Fix issues with alarm scheduling
+0.08: Fix issues if BLE=off, 'Make Connectable' is chosen, and the loader resets Bangle.js (fix #108)
+0.09: Only check GPS for time after a fresh boot
+0.10: Stop users calling save() (fix #125)
+ If Debug info is set to 'show' don't move to Terminal if connected!
diff --git a/apps/boot/boot0.js b/apps/boot/boot0.js
index 7741dd376..cb8d49ba0 100644
--- a/apps/boot/boot0.js
+++ b/apps/boot/boot0.js
@@ -1,7 +1,7 @@
// This ALWAYS runs at boot
E.setFlags({pretokenise:1});
// Load settings...
-var s = require('Storage').readJSON('setting.json')||{};
+var s = require('Storage').readJSON('setting.json',1)||{};
if (s.ble!==false) {
if (s.HID) { // Human interface device
Bangle.HID = E.toUint8Array(atob("BQEJBqEBhQIFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJXMFBxkAKXOBAAkFFQAm/wB1CJUCsQLABQwJAaEBhQEVACUBdQGVAQm1gQIJtoECCbeBAgm4gQIJzYECCeKBAgnpgQIJ6oECwA=="));
@@ -12,11 +12,12 @@ if (s.blerepl===false) { // If not programmable, force terminal off Bluetooth
if (s.log) Terminal.setConsole(true); // if showing debug, force REPL onto terminal
else E.setConsole(null,{force:true}); // on new (2v05+) firmware we have E.setConsole which allows a 'null' console
} else {
- if (s.log) Terminal.setConsole(); // if showing debug, put REPL on terminal (until connection)
+ if (s.log && !NRF.getSecurityStatus().connected) Terminal.setConsole(); // if showing debug, put REPL on terminal (until connection)
else Bluetooth.setConsole(true); // else if no debug, force REPL to Bluetooth
}
-// we just reset, so BLE should be on
-if (s.ble===false) NRF.sleep();
+// we just reset, so BLE should be on.
+// Don't disconnect if something is already connected to us
+if (s.ble===false && !NRF.getSecurityStatus().connected) NRF.sleep();
// Set time, vibrate, beep, etc
if (!s.vibrate) Bangle.buzz=Promise.resolve;
if (!s.beep) Bangle.beep=Promise.resolve;
@@ -24,45 +25,27 @@ Bangle.setLCDTimeout(s.timeout);
if (!s.timeout) Bangle.setLCDPower(1);
E.setTimeZone(s.timezone);
delete s;
+// stop users doing bad things!
+global.save = function() { throw new Error("You can't use save() on Bangle.js without overwriting the bootloader!"); }
// check for alarms
-function checkAlarm() {
- var alarms = require('Storage').readJSON('alarm.json')||[];
- var time = new Date();
- var active = alarms.filter(a=>a.on&&(a.last!=time.getDate()));
- if (active.length) {
- active = active.sort((a,b)=>a.hr-b.hr);
- var hr = time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600);
- if (!require('Storage').read("alarm.js")) {
- console.log("No alarm app!");
- require('Storage').write('alarm.json',"[]")
- } else {
- var t = 3600000*(active[0].hr-hr);
- if (t<1000) t=1000;
- /* execute alarm at the correct time. We avoid execing immediately
- since this code will get called AGAIN when alarm.js is loaded. alarm.js
- will then clearInterval() to get rid of this call so it can proceed
- normally. */
- setTimeout(function() {
- load("alarm.js");
- },t);
- }
+var alarms = require('Storage').readJSON('alarm.json',1)||[];
+var time = new Date();
+var active = alarms.filter(a=>a.on&&(a.last!=time.getDate()));
+if (active.length) {
+ active = active.sort((a,b)=>a.hr-b.hr);
+ var hr = time.getHours()+(time.getMinutes()/60)+(time.getSeconds()/3600);
+ if (!require('Storage').read("alarm.js")) {
+ console.log("No alarm app!");
+ require('Storage').write('alarm.json',"[]")
+ } else {
+ var t = 3600000*(active[0].hr-hr);
+ if (t<1000) t=1000;
+ /* execute alarm at the correct time. We avoid execing immediately
+ since this code will get called AGAIN when alarm.js is loaded. alarm.js
+ will then clearInterval() to get rid of this call so it can proceed
+ normally. */
+ setTimeout(function() {
+ load("alarm.js");
+ },t);
}
}
-// check to see if our clock is wrong - if it is use GPS time
-if ((new Date()).getFullYear()==1970) {
- //console.log("Searching for GPS time");
- Bangle.on('GPS',function cb(g) {
- Bangle.setGPSPower(0);
- Bangle.removeListener("GPS",cb);
- if (!g.time || (g.time.getFullYear()<2000) ||
- (g.time.getFullYear()==2250)) {
- //console.log("GPS receiver's time not set");
- return;
- }
- setTime(g.time.getTime()/1000);
- //console.log("GPS time",g.time.toString());
- checkAlarm();
- });
- Bangle.setGPSPower(1);
-} else checkAlarm();
-delete checkAlarm;
diff --git a/apps/boot/bootloader.js b/apps/boot/bootloader.js
index 07a1623eb..febc4fc19 100644
--- a/apps/boot/bootloader.js
+++ b/apps/boot/bootloader.js
@@ -1,6 +1,5 @@
// This runs after a 'fresh' boot
-var settings={};
-try { settings = require("Storage").readJSON('setting.json'); } catch (e) {}
+var settings=require("Storage").readJSON('setting.json',1)||{};
if (!settings.welcomed && require("Storage").read("welcome.js")!==undefined) {
setTimeout(()=>load("welcome.js"));
} else {
@@ -8,16 +7,32 @@ if (!settings.welcomed && require("Storage").read("welcome.js")!==undefined) {
var clockApp = settings.clock;
if (clockApp) clockApp = require("Storage").read(clockApp)
if (!clockApp) {
- var clockApps = require("Storage").list(/\.info$/).map(app=>{
- try { return require("Storage").readJSON(app); }
- catch (e) {}
- }).filter(app=>app.type=="clock").sort((a, b) => a.sortorder - b.sortorder);
+ var clockApps = require("Storage").list(/\.info$/).map(app=>require("Storage").readJSON(app,1)||{}).filter(app=>app.type=="clock").sort((a, b) => a.sortorder - b.sortorder);
if (clockApps && clockApps.length > 0)
clockApp = require("Storage").read(clockApps[0].src);
delete clockApps;
}
- if (clockApp) eval(clockApp);
- else E.showMessage("No Clock Found");
- delete clockApp;
+ if (!clockApp) clockApp='E.showMessage("No Clock Found")';
+ delete settings;
+ // check to see if our clock is wrong - if it is use GPS time
+ if ((new Date()).getFullYear()==1970) {
+ E.showMessage("Searching for\nGPS time");
+ Bangle.on('GPS',function cb(g) {
+ Bangle.setGPSPower(0);
+ Bangle.removeListener("GPS",cb);
+ if (!g.time || (g.time.getFullYear()<2000) ||
+ (g.time.getFullYear()==2250)) {
+ // GPS receiver's time not set - just boot clock anyway
+ eval(clockApp);delete clockApp;
+ return;
+ }
+ // We have a GPS time. Set time and reboot (to load alarms properly)
+ setTime(g.time.getTime()/1000);
+ load();
+ });
+ Bangle.setGPSPower(1);
+ } else {
+ eval(clockApp);
+ delete clockApp;
+ }
}
-delete settings;
diff --git a/apps/clck3x2/ChangeLog b/apps/clock2x3/ChangeLog
similarity index 73%
rename from apps/clck3x2/ChangeLog
rename to apps/clock2x3/ChangeLog
index c10571cae..88876affa 100644
--- a/apps/clck3x2/ChangeLog
+++ b/apps/clock2x3/ChangeLog
@@ -1,2 +1,3 @@
0.02: Modified for use with new bootloader and firmware
0.03: Added 'reset' so we don't get the font color from widgets
+0.04: Changed name from clck3x2 to clock2x3
diff --git a/apps/clck3x2/clock3x2.js b/apps/clock2x3/clock2x3-app.js
similarity index 95%
rename from apps/clck3x2/clock3x2.js
rename to apps/clock2x3/clock2x3-app.js
index 49c18b747..511a7662b 100644
--- a/apps/clck3x2/clock3x2.js
+++ b/apps/clock2x3/clock2x3-app.js
@@ -76,8 +76,7 @@ function drawTime() {
}
let t = d.getSeconds()*1000 + d.getMilliseconds();
- let delta = (60000 - t) % 60000; // time till next minute
- idTimeout = setTimeout(drawTime, delta);
+ idTimeout = setTimeout(drawTime, 60000 - t); // time till next minute
}
// special function to handle display switch on
diff --git a/apps/clck3x2/clock3x2-icon.js b/apps/clock2x3/clock2x3-icon.js
similarity index 100%
rename from apps/clck3x2/clock3x2-icon.js
rename to apps/clock2x3/clock2x3-icon.js
diff --git a/apps/clck3x2/clock3x2.png b/apps/clock2x3/clock2x3.png
similarity index 100%
rename from apps/clck3x2/clock3x2.png
rename to apps/clock2x3/clock2x3.png
diff --git a/apps/files/files.js b/apps/files/files.js
index ab8324b43..31353cf96 100644
--- a/apps/files/files.js
+++ b/apps/files/files.js
@@ -69,7 +69,7 @@ function showApps() {
var list = storage.list(/\.info$/).filter((a)=> {
return a !== 'setting.info';
}).sort().map((app) => {
- var ret = storage.readJSON(app);
+ var ret = storage.readJSON(app,1)||{};
ret[''] = app;
return ret;
});
diff --git a/apps/gbridge/Changelog b/apps/gbridge/ChangeLog
similarity index 76%
rename from apps/gbridge/Changelog
rename to apps/gbridge/ChangeLog
index 3180266ad..28789ec04 100644
--- a/apps/gbridge/Changelog
+++ b/apps/gbridge/ChangeLog
@@ -1,3 +1,4 @@
0.01: Initial version
0.02: Increase contrast (darker notification background, white text)
0.03: Gadgetbridge widget now shows connection state
+0.04: Tweaks for variable size widget system
diff --git a/apps/gbridge/widget.js b/apps/gbridge/widget.js
index 5eb4f70d4..a787d7e0b 100644
--- a/apps/gbridge/widget.js
+++ b/apps/gbridge/widget.js
@@ -109,19 +109,17 @@
function draw() {
g.setColor(-1);
if (NRF.getSecurityStatus().connected)
- g.drawImage(require("heatshrink").decompress(atob("i0WwgHExAABCIwJCBYwJEBYkIBQ2ACgvzCwoECx/z/AKDD4WD+YLBEIYKCx//+cvnAKCBwU/mc4/8/HYv//Ev+Y4EEAePn43DBQkzn4rCEIoABBIwKHO4cjmczK42I6mqlqEEBQeIBQaDED4IgDUhi6KaBbmIA==")),xpos+1,1);
+ g.drawImage(require("heatshrink").decompress(atob("i0WwgHExAABCIwJCBYwJEBYkIBQ2ACgvzCwoECx/z/AKDD4WD+YLBEIYKCx//+cvnAKCBwU/mc4/8/HYv//Ev+Y4EEAePn43DBQkzn4rCEIoABBIwKHO4cjmczK42I6mqlqEEBQeIBQaDED4IgDUhi6KaBbmIA==")),this.x+1,this.y+1);
else
- g.drawImage(require("heatshrink").decompress(atob("i0WwQFC1WgAgYFDAgIFClQFCwEK1W/AoIPB1f+CAMq1f7/WqwQPB/fq1Gq1/+/4dC/2/CAIaB/YbBAAO///qAoX/B4QbBDQQ7BDQQrBAAWoIIIACIIIVC0ECB4cACAZiBAoRtCAoIDBA")),xpos+1,1);
+ g.drawImage(require("heatshrink").decompress(atob("i0WwQFC1WgAgYFDAgIFClQFCwEK1W/AoIPB1f+CAMq1f7/WqwQPB/fq1Gq1/+/4dC/2/CAIaB/YbBAAO///qAoX/B4QbBDQQ7BDQQrBAAWoIIIACIIIVC0ECB4cACAZiBAoRtCAoIDBA")),this.x+1,this.y+1);
}
function changed() {
- draw();
+ WIDGETS["gbridgew"].draw();
g.flip();// turns screen on
}
NRF.on('connected',changed);
NRF.on('disconnected',changed);
-var xpos = WIDGETPOS.tl;
-WIDGETPOS.tl+=24;
-WIDGETS["gbridgew"]={draw:draw};
+WIDGETS["gbridgew"]={area:"tl",width:24,draw:draw};
})();
diff --git a/apps/gpsinfo/ChangeLog b/apps/gpsinfo/ChangeLog
new file mode 100644
index 000000000..50d79e72d
--- /dev/null
+++ b/apps/gpsinfo/ChangeLog
@@ -0,0 +1 @@
+0.02: Ensure screen doesn't display garbage at startup
diff --git a/apps/gpsinfo/gps-info.js b/apps/gpsinfo/gps-info.js
index 334310755..f7daf245a 100644
--- a/apps/gpsinfo/gps-info.js
+++ b/apps/gpsinfo/gps-info.js
@@ -2,6 +2,7 @@ var img = require("heatshrink").decompress(atob("mEwghC/AH4AKg9wC6t3u4uVC6wWBI6t
Bangle.setGPSPower(1);
Bangle.setLCDMode("doublebuffered");
+E.showMessage("Loading..."); // avoid showing rubbish on screen
var lastFix = {
fix: 0,
diff --git a/apps/gpsrec/ChangeLog b/apps/gpsrec/ChangeLog
index 668ce1991..9a47bdd9a 100644
--- a/apps/gpsrec/ChangeLog
+++ b/apps/gpsrec/ChangeLog
@@ -2,3 +2,5 @@
0.02: Fix GPS time logging
0.03: Fix GPS time display in gpsrec app
0.04: Properly Fix GPS time display in gpsrec app
+0.05: Tweaks for variable size widget system
+0.06: Ensure widget update itself (fix #118) and change to using icons
diff --git a/apps/gpsrec/app.js b/apps/gpsrec/app.js
index bac7e92f8..58b4295a6 100644
--- a/apps/gpsrec/app.js
+++ b/apps/gpsrec/app.js
@@ -1,7 +1,7 @@
Bangle.loadWidgets();
Bangle.drawWidgets();
-var settings = require("Storage").readJSON("gpsrec.json")||{};
+var settings = require("Storage").readJSON("gpsrec.json",1)||{};
function getFN(n) {
return ".gpsrc"+n.toString(36);
diff --git a/apps/gpsrec/interface.html b/apps/gpsrec/interface.html
index d86800923..b87d75b3c 100644
--- a/apps/gpsrec/interface.html
+++ b/apps/gpsrec/interface.html
@@ -176,7 +176,7 @@ function getTrackList() {
for (var i=0;i {
var button = event.currentTarget;
- var trackid = button.getAttribute("trackid");
+ var trackid = parseInt(button.getAttribute("trackid"));
var task = button.getAttribute("task");
if (task=="delete") {
showModal("Deleting Track...");
diff --git a/apps/gpsrec/widget.js b/apps/gpsrec/widget.js
index 7e25a92ad..2ad0cfc8c 100644
--- a/apps/gpsrec/widget.js
+++ b/apps/gpsrec/widget.js
@@ -1,44 +1,29 @@
(() => {
- // add the width
- var xpos = WIDGETPOS.tl;
- WIDGETPOS.tl += 24;/* the widget width plus some extra pixel to keep distance to others */;
var settings = {};
var hasFix = false;
var fixToggle = false; // toggles once for each reading
var gpsTrack; // file for GPS track
var periodCtr = 0;
- // draw your widget at xpos
+ // draw your widget
function draw() {
+ if (!settings.recording) return;
g.reset();
- g.setFont("4x6");
- g.setFontAlign(0,0);
- g.clearRect(xpos,0,xpos+23,23);
-
- if (!settings.recording) {
- g.setColor("#606060");
+ g.drawImage(atob("GBgCAAAAAAAAAAQAAAAAAD8AAAAAAP/AAAAAAP/wAAAAAH/8C9AAAB/8L/QAAAfwv/wAAAHS//wAAAAL//gAAAAf/+AAAAAf/4AAAAL//gAAAAD/+DwAAAB/Uf8AAAAfA//AAAACAf/wAAAAAH/0AAAAAB/wAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"),this.x,this.y);
+ if (hasFix) {
+ g.setColor("#FF0000");
+ g.drawImage(fixToggle ? atob("CgoCAAAAA0AAOAAD5AAPwAAAAAAAAAAAAAAAAA==") : atob("CgoCAAABw0AcOAHj5A8PwHwAAvgAB/wABUAAAA=="),this.x,this.y+14);
} else {
- g.setColor("#ff0000");
- if (hasFix) {
- if (fixToggle) {
- g.fillCircle(xpos+11,11,9);
- g.setColor("#000000");
- } else
- g.drawCircle(xpos+11,11,9);
- } else {
- g.setColor(fixToggle ? "#ff0000" : "#7f0000");
- g.drawString("NO",xpos+12,5);
- g.drawString("FIX",xpos+12,19);
- }
+ g.setColor("#0000FF");
+ if (fixToggle)
+ g.setFont("6x8").drawString("?",this.x,this.y+14);
}
- g.drawString("GPS",xpos+12,12);
- g.setColor(-1); // change color back to be nice to other apps
}
function onGPS(fix) {
hasFix = fix.fix;
fixToggle = !fixToggle;
- draw();
+ WIDGETS["gpsrec"].draw();
if (hasFix) {
periodCtr--;
if (periodCtr<=0) {
@@ -53,25 +38,30 @@
}
}
- // Called by the GPS app to reload settings and decide what's
+ // Called by the GPS app to reload settings and decide what to do
function reload() {
- settings = require("Storage").readJSON("gpsrec.json")||{};
+ settings = require("Storage").readJSON("gpsrec.json",1)||{};
settings.period = settings.period||1;
settings.file |= 0;
Bangle.removeListener('GPS',onGPS);
if (settings.recording) {
+ WIDGETS["gpsrec"].width = 24;
Bangle.on('GPS',onGPS);
Bangle.setGPSPower(1);
var n = settings.file.toString(36);
gpsTrack = require("Storage").open(".gpsrc"+n,"a");
} else {
+ WIDGETS["gpsrec"].width = 0;
Bangle.setGPSPower(0);
gpsTrack = undefined;
}
- draw();
}
- reload();
// add the widget
- WIDGETS["gpsrec"]={draw:draw,reload:reload};
+ WIDGETS["gpsrec"]={area:"tl",width:24,draw:draw,reload:function() {
+ reload();
+ Bangle.drawWidgets(); // relayout all widgets
+ }};
+ // load settings, set correct widget width
+ reload();
})()
diff --git a/apps/heart/ChangeLog b/apps/heart/ChangeLog
new file mode 100644
index 000000000..4c4db83bc
--- /dev/null
+++ b/apps/heart/ChangeLog
@@ -0,0 +1,2 @@
+0.01: New App!
+
diff --git a/apps/heart/app-icon.js b/apps/heart/app-icon.js
new file mode 100644
index 000000000..a7f67291e
--- /dev/null
+++ b/apps/heart/app-icon.js
@@ -0,0 +1 @@
+require("heatshrink").decompress(atob("mEwwhC/AH4AWzIAByAHDhIICCpINDDAgIIFpAADBBQuKE4QIIFxgAKC7g9HABSbIBQQXWGxgXEKQxOMC5AhBC66WMC5AuBJ5h3ICoI3LeAwKBBAICBD4TmHC48ACgQCCfxC/HAgYXDL44vFA4YRDAoiOIHAgXFYRAXFBwwIIOw4OGIxKmIC5ylHGAoXIXpBIGLxxIIIx6IJFxwwNCxQwLFxYwLCxgwJFxowJCxwwHFx4wHCyAwFFyIwFCyQYDCygA/AH4AFA"))
\ No newline at end of file
diff --git a/apps/heart/app-settings.json b/apps/heart/app-settings.json
new file mode 100644
index 000000000..d57d970d0
--- /dev/null
+++ b/apps/heart/app-settings.json
@@ -0,0 +1,4 @@
+{
+ "isRecording":false,
+ "fileNbr":0
+}
diff --git a/apps/heart/app.js b/apps/heart/app.js
new file mode 100644
index 000000000..366a1068d
--- /dev/null
+++ b/apps/heart/app.js
@@ -0,0 +1,100 @@
+Bangle.loadWidgets();
+Bangle.drawWidgets();
+
+var settings = require("Storage").readJSON("heart.json",1)||{};
+
+function getFileNbr(n) {
+ return ".heart"+n.toString(36);
+}
+
+function updateSettings() {
+ require("Storage").write("heart.json", settings);
+ if (WIDGETS["heart"])
+ WIDGETS["heart"].reload();
+}
+
+function showMainMenu() {
+ const mainMenu = {
+ '': { 'title': 'Heart Recorder' },
+ 'RECORD': {
+ value: !!settings.isRecording,
+ format: v=>v?"On":"Off",
+ onchange: v => {
+ settings.isRecording = v;
+ updateSettings();
+ }
+ },
+ 'File Number': {
+ value: settings.fileNbr|0,
+ min: 0,
+ max: 35,
+ step: 1,
+ onchange: v => {
+ settings.isRecording = false;
+ settings.fileNbr = v;
+ updateSettings();
+ }
+ },
+ 'View Records': viewRecords,
+ '< Back': ()=>{load();}
+ };
+ return E.showMenu(mainMenu);
+}
+
+function viewRecords() {
+ const menu = {
+ '': { 'title': 'Heart Records' }
+ };
+ var found = false;
+ for (var n=0;n<36;n++) {
+ var f = require("Storage").open(getFileNbr(n),"r");
+ if (f.readLine()!==undefined) {
+ menu["Record "+n] = viewRecord.bind(null,n);
+ found = true;
+ }
+ }
+ if (!found)
+ menu["No Records Found"] = function(){};
+ menu['< Back'] = showMainMenu;
+ return E.showMenu(menu);
+}
+
+function viewRecord(n) {
+ const menu = {
+ '': { 'title': 'Heart Record '+n }
+ };
+ var heartCount = 0;
+ var heartTime;
+ var f = require("Storage").open(getFileNbr(n),"r");
+ var l = f.readLine();
+ if (l!==undefined) {
+ var c = l.split(",");
+ heartTime = new Date(c[0]*1000);
+ }
+ while (l!==undefined) {
+ heartCount++;
+ // TODO: min/max/average of heart rate?
+ l = f.readLine();
+ }
+ if (heartTime)
+ menu[" "+heartTime.toString().substr(4,17)] = function(){};
+ menu[heartCount+" records"] = function(){};
+ // TODO: option to draw it? Just scan through, project using min/max
+ menu['Erase'] = function() {
+ E.showPrompt("Delete Record?").then(function(v) {
+ if (v) {
+ settings.isRecording = false;
+ updateSettings();
+ var f = require("Storage").open(getFileNbr(n),"r");
+ f.erase();
+ viewRecords();
+ } else
+ viewRecord(n);
+ });
+ };
+ menu['< Back'] = viewRecords;
+ print(menu);
+ return E.showMenu(menu);
+}
+
+showMainMenu();
diff --git a/apps/heart/app.png b/apps/heart/app.png
new file mode 100644
index 000000000..72b2805b8
Binary files /dev/null and b/apps/heart/app.png differ
diff --git a/apps/heart/interface.html b/apps/heart/interface.html
new file mode 100644
index 000000000..c2f115564
--- /dev/null
+++ b/apps/heart/interface.html
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
Please wait
+
+
+
+ Loading...
+
+
+
+
+
+
+
+
+
diff --git a/apps/heart/widget.js b/apps/heart/widget.js
new file mode 100644
index 000000000..7d46aa239
--- /dev/null
+++ b/apps/heart/widget.js
@@ -0,0 +1,50 @@
+(() => {
+ var settings = {};
+ var hrmToggle = true; // toggles once for each reading
+ var recFile; // file for heart rate recording
+
+ // draw your widget
+ function draw() {
+ if (!settings.isRecording) return;
+ g.reset();
+ g.setFontAlign(0,0);
+ g.clearRect(this.x,this.y,this.x+23,this.y+23);
+ g.setColor(hrmToggle?"#ff0000":"#ff8000");
+ g.fillCircle(this.x+6,this.y+6,4); // draw heart left circle
+ g.fillCircle(this.x+16,this.y+6,4); // draw heart right circle
+ g.fillPoly([this.x+2,this.y+8,this.x+20,this.y+8,this.x+11,this.y+18]); // draw heart bottom triangle
+ g.setColor(-1); // change color back to be nice to other apps
+ }
+
+ function onHRM(hrm) {
+ hrmToggle = !hrmToggle;
+ WIDGETS["heart"].draw();
+ if (recFile) recFile.write([getTime().toFixed(0),hrm.bpm,hrm.confidence].join(",")+"\n");
+ }
+
+ // Called by the heart app to reload settings and decide what's
+ function reload() {
+ settings = require("Storage").readJSON("heart.json",1)||{};
+ settings.fileNbr |= 0;
+
+ Bangle.removeListener('HRM',onHRM);
+ if (settings.isRecording) {
+ WIDGETS["heart"].width = 24;
+ Bangle.on('HRM',onHRM);
+ Bangle.setHRMPower(1);
+ var n = settings.fileNbr.toString(36);
+ recFile = require("Storage").open(".heart"+n,"a");
+ } else {
+ WIDGETS["heart"].width = 0;
+ Bangle.setHRMPower(0);
+ recFile = undefined;
+ }
+ }
+ // add the widget
+ WIDGETS["heart"]={area:"tl",width:24,draw:draw,reload:function() {
+ reload();
+ Bangle.drawWidgets(); // relayout all widgets
+ }};
+ // load settings, set correct widget width
+ reload();
+})()
diff --git a/apps/hidbkbd/hid-binary-keyboard.js b/apps/hidbkbd/hid-binary-keyboard.js
index d48d97b47..fa1017714 100644
--- a/apps/hidbkbd/hid-binary-keyboard.js
+++ b/apps/hidbkbd/hid-binary-keyboard.js
@@ -5,7 +5,7 @@ the touchscreen
var storage = require('Storage');
-const settings = storage.readJSON('setting.json') || { HID: false };
+const settings = storage.readJSON('setting.json',1) || { HID: false };
const KEY = {
A : 4 ,
B : 5 ,
diff --git a/apps/hidkbd/hid-keyboard.js b/apps/hidkbd/hid-keyboard.js
index fe850024e..ed406e093 100644
--- a/apps/hidkbd/hid-keyboard.js
+++ b/apps/hidkbd/hid-keyboard.js
@@ -1,6 +1,6 @@
var storage = require('Storage');
-const settings = storage.readJSON('setting.json') || { HID: false };
+const settings = storage.readJSON('setting.json',1) || { HID: false };
var sendHid, next, prev, toggle, up, down, profile;
diff --git a/apps/hidmsic/hid-music.js b/apps/hidmsic/hid-music.js
index afee7ade9..034bbd231 100644
--- a/apps/hidmsic/hid-music.js
+++ b/apps/hidmsic/hid-music.js
@@ -1,6 +1,6 @@
var storage = require('Storage');
-const settings = storage.readJSON('setting.json') || { HID: false };
+const settings = storage.readJSON('setting.json',1) || { HID: false };
var sendHid, next, prev, toggle, up, down, profile;
diff --git a/apps/launch/app.js b/apps/launch/app.js
index e93c2a330..682122f82 100644
--- a/apps/launch/app.js
+++ b/apps/launch/app.js
@@ -1,8 +1,5 @@
var s = require("Storage");
-var apps = s.list(/\.info$/).map(app=>{
- try { return s.readJSON(app); }
- catch (e) { return {name:"DEAD: "+app.substr(1)} }
-}).filter(app=>app.type=="app" || app.type=="clock" || !app.type);
+var apps = s.list(/\.info$/).map(app=>s.readJSON(app,1)||{name:"DEAD: "+app.substr(1)}).filter(app=>app.type=="app" || app.type=="clock" || !app.type);
apps.sort((a,b)=>{
var n=(0|a.sortorder)-(0|b.sortorder);
if (n) return n; // do sortorder first
diff --git a/apps/locale/ChangeLog b/apps/locale/ChangeLog
new file mode 100644
index 000000000..5560f00bc
--- /dev/null
+++ b/apps/locale/ChangeLog
@@ -0,0 +1 @@
+0.01: New App!
diff --git a/apps/locale/locale.html b/apps/locale/locale.html
new file mode 100644
index 000000000..931bde033
--- /dev/null
+++ b/apps/locale/locale.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+