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");
+    }
+  });
 })();