diff --git a/apps/medicalinfo/ChangeLog b/apps/medicalinfo/ChangeLog new file mode 100644 index 000000000..e8739a121 --- /dev/null +++ b/apps/medicalinfo/ChangeLog @@ -0,0 +1 @@ +0.01: Initial Medical Information application! diff --git a/apps/medicalinfo/README.md b/apps/medicalinfo/README.md new file mode 100644 index 000000000..6dd19d4c6 --- /dev/null +++ b/apps/medicalinfo/README.md @@ -0,0 +1,27 @@ +# Medical Information + +This app displays basic medical information, and provides a common way to set up the `medicalinfo.json` file, which other apps can use if required. + +## Medical information JSON file + +When the app is loaded from the app loader, a file named `medicalinfo.json` is loaded along with the javascript etc. +The file has the following contents: + +``` +{ + "bloodType": "", + "height": "", + "weight": "", + "medicalAlert": [ "" ] +} +``` + +## Medical information editor + +Clicking on the download icon of `Medical Information` in the app loader invokes the editor. +The editor downloads and displays the current `medicalinfo.json` file, which can then be edited. +The edited `medicalinfo.json` file is uploaded to the Bangle by clicking the `Upload` button. + +## Creator + +James Taylor ([jt-nti](https://github.com/jt-nti)) diff --git a/apps/medicalinfo/app-icon.js b/apps/medicalinfo/app-icon.js new file mode 100644 index 000000000..1ae7916fb --- /dev/null +++ b/apps/medicalinfo/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwg+7kUiCykCC4MgFykgDIIXUAQgAMiMRiREBC4YABkILBCxEBC4pHCC4kQFxIXEAAgXCGBERif/+QXHl//mIXJj//+YXHn//+IXL/8yCwsjBIIXNABIX/C63d7oDB+czmaPPC7hHR/oWBAAPfC65HRC7qnXX/4XDABAXkIIQAFI5wXXL/5f/L/5fvC9sTC5cxC5IAOC48BCxsQC44wOCxAArA")) diff --git a/apps/medicalinfo/app.js b/apps/medicalinfo/app.js new file mode 100644 index 000000000..9c4941744 --- /dev/null +++ b/apps/medicalinfo/app.js @@ -0,0 +1,61 @@ +const medicalinfo = require('medicalinfo').load(); +// const medicalinfo = { +// bloodType: "O+", +// height: "166cm", +// weight: "73kg" +// }; + +function hasAlert(info) { + return (Array.isArray(info.medicalAlert)) && (info.medicalAlert[0]); +} + +// No space for widgets! +// TODO: no padlock widget visible so prevent screen locking? + +g.clear(); +const bodyFont = g.getFonts().includes("12x20") ? "12x20" : "6x8:2"; +g.setFont(bodyFont); + +const title = hasAlert(medicalinfo) ? "MEDICAL ALERT" : "Medical Information"; +var lines = []; + +lines = g.wrapString(title, g.getWidth() - 10); +var titleCnt = lines.length; +if (titleCnt) lines.push(""); // add blank line after title + +if (hasAlert(medicalinfo)) { + medicalinfo.medicalAlert.forEach(function (details) { + lines = lines.concat(g.wrapString(details, g.getWidth() - 10)); + }); + lines.push(""); // add blank line after medical alert +} + +if (medicalinfo.bloodType) { + lines = lines.concat(g.wrapString("Blood group: " + medicalinfo.bloodType, g.getWidth() - 10)); +} +if (medicalinfo.height) { + lines = lines.concat(g.wrapString("Height: " + medicalinfo.height, g.getWidth() - 10)); +} +if (medicalinfo.weight) { + lines = lines.concat(g.wrapString("Weight: " + medicalinfo.weight, g.getWidth() - 10)); +} + +lines.push(""); + +// TODO: display instructions for updating medical info if there is none! + +E.showScroller({ + h: g.getFontHeight(), // height of each menu item in pixels + c: lines.length, // number of menu items + // a function to draw a menu item + draw: function (idx, r) { + // FIXME: in 2v13 onwards, clearRect(r) will work fine. There's a bug in 2v12 + g.setBgColor(idx < titleCnt ? g.theme.bg2 : g.theme.bg). + setColor(idx < titleCnt ? g.theme.fg2 : g.theme.fg). + clearRect(r.x, r.y, r.x + r.w, r.y + r.h); + g.setFont(bodyFont).drawString(lines[idx], r.x, r.y); + } +}); + +// Show launcher when button pressed +setWatch(() => load(), process.env.HWVERSION === 2 ? BTN : BTN3, { repeat: false, edge: "falling" }); diff --git a/apps/medicalinfo/app.png b/apps/medicalinfo/app.png new file mode 100644 index 000000000..16204ea89 Binary files /dev/null and b/apps/medicalinfo/app.png differ diff --git a/apps/medicalinfo/interface.html b/apps/medicalinfo/interface.html new file mode 100644 index 000000000..9376be32f --- /dev/null +++ b/apps/medicalinfo/interface.html @@ -0,0 +1,135 @@ + +
+ + + + + + + + + + + + + + + + + diff --git a/apps/medicalinfo/lib.js b/apps/medicalinfo/lib.js new file mode 100644 index 000000000..683005359 --- /dev/null +++ b/apps/medicalinfo/lib.js @@ -0,0 +1,21 @@ +const storage = require('Storage'); + +exports.load = function () { + const medicalinfo = storage.readJSON('medicalinfo.json') || { + bloodType: "", + height: "", + weight: "", + medicalAlert: [""] + }; + + // Don't return anything unexpected + const expectedMedicalinfo = [ + "bloodType", + "height", + "weight", + "medicalAlert" + ].filter(key => key in medicalinfo) + .reduce((obj, key) => (obj[key] = medicalinfo[key], obj), {}); + + return expectedMedicalinfo; +}; diff --git a/apps/medicalinfo/medicalinfo.json b/apps/medicalinfo/medicalinfo.json new file mode 100644 index 000000000..8b49725cb --- /dev/null +++ b/apps/medicalinfo/medicalinfo.json @@ -0,0 +1,6 @@ +{ + "bloodType": "", + "height": "", + "weight": "", + "medicalAlert": [ "" ] +} diff --git a/apps/medicalinfo/metadata.json b/apps/medicalinfo/metadata.json new file mode 100644 index 000000000..f1a0c145f --- /dev/null +++ b/apps/medicalinfo/metadata.json @@ -0,0 +1,20 @@ +{ "id": "medicalinfo", + "name": "Medical Information", + "version":"0.01", + "description": "Provides 'medicalinfo.json' used by various health apps, as well as a way to edit it from the App Loader", + "icon": "app.png", + "tags": "health,medical", + "type": "app", + "supports" : ["BANGLEJS","BANGLEJS2"], + "readme": "README.md", + "screenshots": [{"url":"screenshot_light.png"}], + "interface": "interface.html", + "storage": [ + {"name":"medicalinfo.app.js","url":"app.js"}, + {"name":"medicalinfo.img","url":"app-icon.js","evaluate":true}, + {"name":"medicalinfo","url":"lib.js"} + ], + "data": [ + {"name":"medicalinfo.json","url":"medicalinfo.json"} + ] +} diff --git a/apps/medicalinfo/screenshot_light.png b/apps/medicalinfo/screenshot_light.png new file mode 100644 index 000000000..42970f9fc Binary files /dev/null and b/apps/medicalinfo/screenshot_light.png differ diff --git a/apps/widmeda/ChangeLog b/apps/widmeda/ChangeLog index 7415150e6..6ac092a85 100644 --- a/apps/widmeda/ChangeLog +++ b/apps/widmeda/ChangeLog @@ -1 +1,2 @@ 0.01: Initial Medical Alert Widget! +0.02: Use Medical Information app for medical alert text, and to display details diff --git a/apps/widmeda/README.md b/apps/widmeda/README.md index 0bbfc4dc3..151b11058 100644 --- a/apps/widmeda/README.md +++ b/apps/widmeda/README.md @@ -11,12 +11,12 @@ Implemented: - Basic medical alert logo and message - Only display bottom widget on clocks - High contrast colours depending on theme +- Configure medical alert text (using Medical Information app) +- Show details when touched (using Medical Information app) Future: - Configure when to show bottom widget (always/never/clocks) -- Configure medical alert text -- Show details when touched ## Creator diff --git a/apps/widmeda/metadata.json b/apps/widmeda/metadata.json index 346306b8e..8ce051dd4 100644 --- a/apps/widmeda/metadata.json +++ b/apps/widmeda/metadata.json @@ -1,10 +1,11 @@ { "id": "widmeda", "name": "Medical Alert Widget", "shortName":"Medical Alert", - "version":"0.01", + "version":"0.02", "description": "Display a medical alert in the bottom widget section.", "icon": "widget.png", "type": "widget", + "dependencies" : { "medicalinfo":"app" }, "tags": "health,medical,tools,widget", "supports" : ["BANGLEJS2"], "readme": "README.md", diff --git a/apps/widmeda/widget.js b/apps/widmeda/widget.js index a2d109596..2758ae11f 100644 --- a/apps/widmeda/widget.js +++ b/apps/widmeda/widget.js @@ -1,30 +1,47 @@ (() => { + function getAlertText() { + const medicalinfo = require("medicalinfo").load(); + const alertText = ((Array.isArray(medicalinfo.medicalAlert)) && (medicalinfo.medicalAlert[0])) ? medicalinfo.medicalAlert[0] : ""; + return (g.wrapString(alertText, g.getWidth()).length === 1) ? alertText : "MEDICAL ALERT"; + } + // Top right star of life logo - WIDGETS["widmedatr"]={ + WIDGETS["widmedatr"] = { area: "tr", width: 24, - draw: function() { + draw: function () { g.reset(); g.setColor("#f00"); g.drawImage(atob("FhYBAAAAA/AAD8AAPwAc/OD/P8P8/x/z/n+/+P5/wP58A/nwP5/x/v/n/P+P8/w/z/Bz84APwAA/AAD8AAAAAA=="), this.x + 1, this.y + 1); } }; - // Bottom medical alert message - WIDGETS["widmedabl"]={ + // Bottom medical alert text + WIDGETS["widmedabl"] = { area: "bl", - width: Bangle.CLOCK?Bangle.appRect.w:0, - draw: function() { + width: Bangle.CLOCK ? Bangle.appRect.w : 0, + draw: function () { // Only show the widget on clocks if (!Bangle.CLOCK) return; g.reset(); g.setBgColor(g.theme.dark ? "#fff" : "#f00"); g.setColor(g.theme.dark ? "#f00" : "#fff"); - g.setFont("Vector",18); - g.setFontAlign(0,0); + g.setFont("Vector", 16); + g.setFontAlign(0, 0); g.clearRect(this.x, this.y, this.x + this.width - 1, this.y + 23); - g.drawString("MEDICAL ALERT", this.width / 2, this.y + ( 23 / 2 )); + + const alertText = getAlertText(); + g.drawString(alertText, this.width / 2, this.y + (23 / 2)); } }; + + Bangle.on("touch", (_, c) => { + const bl = WIDGETS.widmedabl; + const tr = WIDGETS.widmedatr; + if ((bl && c.x >= bl.x && c.x < bl.x + bl.width && c.y >= bl.y && c.y <= bl.y + 24) + || (tr && c.x >= tr.x && c.x < tr.x + tr.width && c.y >= tr.y && c.y <= tr.y + 24)) { + load("medicalinfo.app.js"); + } + }); })();