From c1555ee6befb69eec678ba50ef0899ca861182b2 Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Wed, 9 Oct 2024 14:51:49 +0100 Subject: [PATCH] Add 'fact clock' that puts one of 300 facts (according to Google AI) on the screen --- apps/factclock/ChangeLog | 1 + apps/factclock/README.md | 5 + apps/factclock/app-icon.js | 1 + apps/factclock/app.js | 110 +++++++++++++ apps/factclock/icon.png | Bin 0 -> 13898 bytes apps/factclock/metadata.json | 17 ++ apps/factclock/screenshot.png | Bin 0 -> 5923 bytes apps/textfacts/ChangeLog | 1 + apps/textfacts/README.md | 30 ++++ apps/textfacts/facts.txt | 288 ++++++++++++++++++++++++++++++++++ apps/textfacts/icon.png | Bin 0 -> 2441 bytes apps/textfacts/lib.js | 29 ++++ apps/textfacts/metadata.json | 16 ++ 13 files changed, 498 insertions(+) create mode 100644 apps/factclock/ChangeLog create mode 100644 apps/factclock/README.md create mode 100644 apps/factclock/app-icon.js create mode 100644 apps/factclock/app.js create mode 100644 apps/factclock/icon.png create mode 100644 apps/factclock/metadata.json create mode 100644 apps/factclock/screenshot.png create mode 100644 apps/textfacts/ChangeLog create mode 100644 apps/textfacts/README.md create mode 100644 apps/textfacts/facts.txt create mode 100644 apps/textfacts/icon.png create mode 100644 apps/textfacts/lib.js create mode 100644 apps/textfacts/metadata.json diff --git a/apps/factclock/ChangeLog b/apps/factclock/ChangeLog new file mode 100644 index 000000000..09953593e --- /dev/null +++ b/apps/factclock/ChangeLog @@ -0,0 +1 @@ +0.01: New Clock! diff --git a/apps/factclock/README.md b/apps/factclock/README.md new file mode 100644 index 000000000..83b590c0b --- /dev/null +++ b/apps/factclock/README.md @@ -0,0 +1,5 @@ +# Fact Clock + +A clock that displays a random fact alongside the time. + +This uses `text_facts` for the list of facts, but you can implement new apps that provide the `textsource` module (see the readme for the `text_facts` app) to make this clock display different information. \ No newline at end of file diff --git a/apps/factclock/app-icon.js b/apps/factclock/app-icon.js new file mode 100644 index 000000000..6eb4bb2a5 --- /dev/null +++ b/apps/factclock/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwcF23btoCV7QfBrYRM2kAhMkgEtCJegiwDBgBBLtAMCoA3BCwQRHoADCNIUCKxMDAYPAPgZcICIWwRwgRI6ARBwAREhYRIjYRBgVggEDCJOACIO07dsgETtsBCJcTsEGBIORCJRHCmwJB2LRHg0DGQIFC7ZKBNwnRkmSm0ACIsAgO2yRcCwIRDhgRILgWJCIMWgENR4fbhuBCINBMgMJCJNt2YLBoEtCIcGwEN2wRE3+kCI1C4DpDR4WdGoIRFpHYCIUBR4XtCJFYCIacBtgRIpKECCIdnpJZHpMhZIRcB7c7AwIRHkktEYNN23ZaYQRIwDhDrwRLyUCBoOXCoYRJpMgIgIIECJMkEAKwBCJ41OCMoFECJlpNaJZ0I/0BCKEACIsAn//AAX0yUQCIQAGFQ2QCJMACIuAlu2wASIAAkBJYPQBIsF0AHFhZgEARoA==")) \ No newline at end of file diff --git a/apps/factclock/app.js b/apps/factclock/app.js new file mode 100644 index 000000000..6ae457d4e --- /dev/null +++ b/apps/factclock/app.js @@ -0,0 +1,110 @@ +Graphics.prototype.setFontBebasNeue = function() { + // Actual height 31 (32 - 2) + // 1 BPP + return this.setFontCustom( + atob('AAAAAAAAAAAAAAAAAAAPgAAAAAD4AAAAAA+AAAAAAPgAAAAAD4AAAAAAAAAAAAAAgAAAAAA4AAAAAB+AAAAAD/gAAAAD/4AAAAH/4AAAAH/4AAAAP/wAAAAP/wAAAAf/gAAAAf/gAAAA//AAAAA/+AAAAAP+AAAAAD8AAAAAA8AAAAAAIAAAAAAAAAAAAAAAAAAAAAAf//8AAAf///wAAP///+AAH////wAD////+AA/////gAPgAAD4AD4AAA+AA+AAAPgAPgAAD4AD////+AAf////AAH////wAA////4AAH///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAAAADwAAAAAA8AAAAAAfAAAAAAHwAAAAAD////4AB////+AA/////gAP////4AD////+AA/////gAAAAAAAAAAAAAAAAAcAAHgAB/gA/4AA/4A/+AAf+Af/gAP/gP/4AD/wH/+AA+AD+PgAPgD+D4AD4B/A+AA+B/gPgAP//wD4AD//4A+AAf/8APgAD/8AD4AAf8AAeAAAAAAAAAAAAAAAAADgAfAAAH4AH8AAD+AB/wAB/gAf8AA/4AH/gAPwPgH4AD4D4A+AA+A+APgAPgPwD4AD8H+B+AA/////gAH////wAB//f/8AAP/j/+AAA/gf+AAAAAAAAAAAAAHgAAAAAP8AAAAAP/AAAAAP/wAAAAf/8AAAAf//AAAA//HwAAA/+B8AAA/+AfAAA/+AHwAAP////4AD////+AA/////gAP////4AD////+AAAAAHwAAAAAB8AAAAAAAAAAAAAAAAAAAAh4AAD//4fwAA//+H+AAP//h/wAD//4f+AA//+B/gAPgeAD4AD4PAA+AA+DwAPgAPh+AD4AD4f//+AA+D///gAPg///wAD4H//4AA8A//8AAAAAAAAAAAAAAAAAA///gAAB////AAA////4AAf////AAP////4AD////+AA+A8APgAPgfAD4AD4HwA+AA+B8APgAP8f//4AD/H//+AAfx///AAD8P//gAAfB//wAAAAB/AAAAAAAAAAAAAAAAAA8AAAAAAPgAAAAAD4AAACAA+AAAHgAPgAAf4AD4AA/+AA+AD//gAPgH//4AD4P//wAA+///gAAP//+AAAD//8AAAA//wAAAAP/AAAAAD+AAAAAAAAAAAAAAAAAAAAAH4D/gAAH/j/+AAD/9//wAB////8AA/////gAP9/4H4AD4D8A+AA+A+APgAPgPgD4AD4D8A+AA/////gAP////4AB////8AAP/3/+AAB/4f/AAAAAA+AAAAAAAAAAAAAAAAAAH/wHAAAH//B8AAH//4fwAB///H8AA///x/gAPwH8H4AD4AfA+AA+AHwPgAPgB4D4AD4A+B+AA/////gAH////wAB////8AAP///+AAA///+AAAAAAAAAAAAAAAAAAAAAAAAAAPgA+AAAD4APgAAA+AD4AAAPgA+AAAD4APgAAAAAAAAA'), + 46, + atob("CBIRDxEREhESERIRCA=="), + 44|65536 + ); +}; + +{ + // the font we're using + const factFont = "6x15"; + // swap every 10 minutes + const minsPerFact = 5; + // timeout used to update every minute + let drawTimeout; + // the fact we're going to display (pre-rendered with a border) + let factGfx; + // how long until the next fact? + let factCounter = minsPerFact; + // the gfx we use for the time so we can gat a shadow on it + let timeGfx = Graphics.createArrayBuffer(g.getWidth()>>1, 48, 2, {msb:true}); + timeGfx.transparent = 0; + timeGfx.palette = new Uint16Array([ + 0, g.toColor(g.theme.bg), 0, g.toColor(g.theme.fg) + ]); + + + let getNewFact = () => { + let fact = require("textsource").getRandomText(); + // wrap to fit the screen + let lines = g.setFont(factFont).wrapString(fact.txt, g.getWidth()-10); + let txt = lines.join("\n"); + // allocate a gfx for this + factGfx = Graphics.createArrayBuffer(g.getWidth(), g.stringMetrics(txt).height+4, 2, {msb:true}); + factGfx.transparent = 0; + factGfx.setFont(factFont).setFontAlign(0,-1).setColor(3).drawString(txt, factGfx.getWidth()/2, 2); + if (factGfx.filter) factGfx.filter([ // add shadow behind text + 0,1,1,1,0, + 1,1,1,1,1, + 1,1,1,1,1, + 1,1,1,1,1, + 0,1,1,1,0, + ], { w:5, h:5, div:1, max:1, filter:"max" }); + factGfx.palette = new Uint16Array([ + 0, g.toColor(g.theme.bg), 0, g.toColor(g.theme.fg) + ]); + }; + getNewFact(); + + // schedule a draw for the next minute + let queueDraw = function() { + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = setTimeout(function() { + drawTimeout = undefined; + draw(); + }, 60000 - (Date.now() % 60000)); + }; + + let draw = function() { + // queue next draw in one minute + queueDraw(); + // new fact? + if (--factCounter < 0) { + factCounter = minsPerFact; + getNewFact(); + } + // Work out where to draw... + g.reset(); + require("clockbg").fillRect(Bangle.appRect); + // work out locale-friendly date/time + var date = new Date(); + var timeStr = require("locale").time(date,1); + var dateStr = require("locale").date(date,1); + // draw time to buffer + timeGfx.clear(1); + timeGfx.setFontAlign(0,-1).setFont("BebasNeue"); + timeGfx.drawString(timeStr,timeGfx.getWidth()/2,2); + timeGfx.setFontAlign(0,1).setFont("6x8"); + timeGfx.drawString(dateStr,timeGfx.getWidth()/2,timeGfx.getHeight()-2); + // add shadow to buffer and render + if (timeGfx.filter) timeGfx.filter([ // add shadow behind text + 0,1,1,1,0, + 1,1,1,1,1, + 1,1,1,1,1, + 1,1,1,1,1, + 0,1,1,1,0, + ], { w:5, h:5, div:1, max:1, filter:"max" }); + var y = (Bangle.appRect.y+g.getHeight()-(factGfx.getHeight()+timeGfx.getHeight()*2))>>1; + g.drawImage(timeGfx,0, y, {scale:2}); + // draw the fact + g.drawImage(factGfx,0, g.getHeight()-factGfx.getHeight()); + }; + + // Show launcher when middle button pressed + Bangle.setUI({mode:"clock", remove:function() { + // free any memory we allocated to allow fast loading + delete Graphics.prototype.setFontBebasNeue; + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + require('widget_utils').show(); // re-show widgets + }}); + // Load widgets + Bangle.loadWidgets(); + require("widget_utils").swipeOn(); + // draw immediately at first, queue update + draw(); +} \ No newline at end of file diff --git a/apps/factclock/icon.png b/apps/factclock/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3186d0a965b28db693d8146834c69e7ceb810004 GIT binary patch literal 13898 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 literal 0 HcmV?d00001 diff --git a/apps/factclock/metadata.json b/apps/factclock/metadata.json new file mode 100644 index 000000000..35b13f393 --- /dev/null +++ b/apps/factclock/metadata.json @@ -0,0 +1,17 @@ +{ "id": "factclock", + "name": "Fact Clock", + "shortName":"Facts", + "version":"0.01", + "description": "A clock that displays a random fact alongside the time", + "icon": "icon.png", + "screenshots": [{"url":"screenshot.png"}], + "type": "clock", + "tags": "clock", + "supports" : ["BANGLEJS2"], + "dependencies" : { "textsource":"module", "clockbg":"module" }, + "readme": "README.md", + "storage": [ + {"name":"factclock.app.js","url":"app.js"}, + {"name":"factclock.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/factclock/screenshot.png b/apps/factclock/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..e52590491e47df0884ae6c19e07f6cbde6656c20 GIT binary patch literal 5923 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 { + var next = s.indexOf("\n",idx+1); + if (next>=0) { + var len = next-idx; + if (len>255) throw new Error("Line too long!"); + lengths.push(len); + } + idx = next; + } + print(`Count = ${lengths.length}`); + print(`var idxs = E.toUint8Array(atob("${btoa(lengths)}"));`);*/ + var idxs = E.toUint8Array(atob("PUovVCpNWElbLi0uKkEsPydJTVhkKSBVNDFNO1EsTz5ELjIvMTwyKiowRl8mMj4/MCczSjczKEFRLCxKGE4rLyxdY1YdMURdX1I5V0oePEJQMiZHQEo4NVQyWVNSPUNCMS1PLS5GZhlHNUI2TVFDRkU+O2Q8YTEwNTtAKzxYGiQxSDM1RXAyMRlpIzFFO0I8MjE/KDRJNz07PVNBRUA4UzIuP0E6VFBDNEMqO0gyMjExUFA4KlQxR0Y/TChWTBouODI4NVJIQkAeLEFENi8vPi5EKx0mQCouMTUcTlBWM0VOYi81MztIRC8sRDI1PzwqLSk5LzwvPTkzQEgwSk4xPTtCLFNFHSJINkFNJjg1RipGNUlAHks5OShIQDgbME0q")); + if (n<0 || n>=idxs.length) return; + var idx = n ? E.sum(new Uint8Array(idxs.buffer,0,n))+1 : 0; + var len = idxs[n]-1; + return require("Storage").read("textsource.txt").substring(idx, idx+len); +}; +exports.getRandomText = function() { + let n = Math.floor(Math.random()*exports.getCount()); + return { idx : n, txt : exports.getText(n) }; +}; \ No newline at end of file diff --git a/apps/textfacts/metadata.json b/apps/textfacts/metadata.json new file mode 100644 index 000000000..a8728185e --- /dev/null +++ b/apps/textfacts/metadata.json @@ -0,0 +1,16 @@ +{ "id": "textfacts", + "name": "List of Facts", + "shortName":"Facts", + "version":"0.01", + "description": "A list of hundreds of 'facts' from Google Gemini. Provides a `textsource` module that apps can use to query a random fact.", + "icon": "icon.png", + "type": "module", + "tags": "", + "provides_modules" : ["textsource"], + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "storage": [ + {"name":"textsource.txt","url":"facts.txt"}, + {"name":"textsource","url":"lib.js"} + ] +}