From 2f1dbab72eccc463ad2334652aba59ea36352d62 Mon Sep 17 00:00:00 2001 From: Albert Date: Thu, 13 Jun 2024 23:26:47 +0200 Subject: [PATCH] nostt app init --- apps/nostt/ChangeLog | 1 + apps/nostt/README.md | 8 + apps/nostt/metadata.json | 17 + apps/nostt/nostt.app.js | 506 ++++++++++++++++++++++++++++++ apps/nostt/nostt.icon.js | 1 + apps/nostt/nostt_logo.png | Bin 0 -> 1646 bytes apps/nostt/nostt_screenshot_1.png | Bin 0 -> 4415 bytes apps/nostt/nostt_screenshot_2.png | Bin 0 -> 5107 bytes 8 files changed, 533 insertions(+) create mode 100644 apps/nostt/ChangeLog create mode 100644 apps/nostt/README.md create mode 100644 apps/nostt/metadata.json create mode 100644 apps/nostt/nostt.app.js create mode 100644 apps/nostt/nostt.icon.js create mode 100644 apps/nostt/nostt_logo.png create mode 100644 apps/nostt/nostt_screenshot_1.png create mode 100644 apps/nostt/nostt_screenshot_2.png diff --git a/apps/nostt/ChangeLog b/apps/nostt/ChangeLog new file mode 100644 index 000000000..5314d96b9 --- /dev/null +++ b/apps/nostt/ChangeLog @@ -0,0 +1 @@ +1.00: NOS Teletekst finished! \ No newline at end of file diff --git a/apps/nostt/README.md b/apps/nostt/README.md new file mode 100644 index 000000000..ea3d7b3ff --- /dev/null +++ b/apps/nostt/README.md @@ -0,0 +1,8 @@ +# NOS Teletekst + + +Dutch Teletekst using the NOS Teletekst api. Requires http access via BangleJS GadgetBridge. See https://www.espruino.com/Gadgetbridge. Make sure `Allow Internet Access` is enabled. + +## Usage + +Tap once to bring up a numpad to enter the desired page. You can also swipe left/right to change the page, or swipe up/down to walk through the subpages. \ No newline at end of file diff --git a/apps/nostt/metadata.json b/apps/nostt/metadata.json new file mode 100644 index 000000000..8d59abd7a --- /dev/null +++ b/apps/nostt/metadata.json @@ -0,0 +1,17 @@ +{ + "id":"nostt", + "name":"NOS Teletekst", + "shortName": "Teletekst", + "version": "1.00", + "description": "Dutch Teletekst using the NOS Teletekst api. Requires http access via BangleJS GadgetBridge.", + "src":"nostt.app.js", + "storage": [ + {"name":"nostt.app.js","url":"nostt.app.js"}, + {"name":"nostt.img","url":"nostt.icon.js","evaluate":true} + ], + "readme": "README.md", + "icon":"nostt_logo.png", + "supports": ["BANGLEJS2"], + "screenshots": [{"url": "nostt_screenshot_1.png"}, {"url": "nostt_screenshot_2.png"}], + "tags": "nos,teletext,teletekst,news,weather" +} \ No newline at end of file diff --git a/apps/nostt/nostt.app.js b/apps/nostt/nostt.app.js new file mode 100644 index 000000000..ee8f0b5f6 --- /dev/null +++ b/apps/nostt/nostt.app.js @@ -0,0 +1,506 @@ +class View { + + constructor() { + + this.navigationState = { + prevPage: { + p: undefined, + s: undefined, + }, + prevSubPage: { + p: undefined, + s: undefined, + }, + nextPage: { + p: undefined, + s: undefined, + }, + nextSubPage: { + p: undefined, + s: undefined, + }, + currentPage: { + p: undefined, + s: undefined, + }, + }; + + this.colorArray = { + 0: [0, 0, 0], + 1: [1, 0, 0], + 2: [0, 1, 0], + 3: [1, 1, 0], + 4: [0, 0, 1], + 5: [1, 0, 1], + 6: [0, 1, 1], + 7: [1, 1, 1], + 16: [0, 0, 0], + 17: [1, 0, 0], + 18: [0, 1, 0], + 19: [1, 1, 0], + 20: [0, 0, 1], + 21: [1, 0, 1], + 22: [0, 1, 1], + 23: [1, 1, 1], + }; + + + } + + start() { + // @ts-ignore + g.clear(); + if (this.nextStartPage) { + this.show(this.nextStartPage); + this.nextStartPage = undefined; + } + else { + if (this.navigationState.currentPage.p) { + this.show(this.navigationState.currentPage.p); + } + else { + this.show(101); //load default + } + } + + } + + split_at_fourty(res, value) { + res.push(value.substring(0, 40)); + if (value.length > 40) { // at least two rows + return this.split_at_fourty(res, value.substring(40)); + } + else { + return res; + } + + } + +// strToUtf8Bytes(str) { +// const utf8 = []; +// for (let ii = 0; ii < str.length; ii++) { +// let charCode = str.charCodeAt(ii); +// if (charCode < 0x80) utf8.push(charCode); +// else if (charCode < 0x800) { +// utf8.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f)); +// } else if (charCode < 0xd800 || charCode >= 0xe000) { +// utf8.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f)); +// } else { +// ii++; +// // Surrogate pair: +// // UTF-16 encodes 0x10000-0x10FFFF by subtracting 0x10000 and +// // splitting the 20 bits of 0x0-0xFFFFF into two halves +// charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (str.charCodeAt(ii) & 0x3ff)); +// utf8.push( +// 0xf0 | (charCode >> 18), +// 0x80 | ((charCode >> 12) & 0x3f), +// 0x80 | ((charCode >> 6) & 0x3f), +// 0x80 | (charCode & 0x3f), +// ); +// } +// } +// return utf8; +// } + + loadPrevPage() { + if (this.navigationState.prevPage.p) { + this.show(this.navigationState.prevPage.p, this.navigationState.prevPage.s); + } + } + + loadNextPage() { + if (this.navigationState.nextPage.p) { + this.show(this.navigationState.nextPage.p, this.navigationState.nextPage.s); + } + } + + loadPrevSubPage() { + if (this.navigationState.prevSubPage.p) { + this.show(this.navigationState.prevSubPage.p, this.navigationState.prevSubPage.s); + } + } + + loadNextSubPage() { + if (this.navigationState.nextSubPage.p) { + this.show(this.navigationState.nextSubPage.p, this.navigationState.nextSubPage.s); + } + } + + handleSwipe(lr, ud){ + if (lr == -1 && ud == 0) { + this.loadNextPage(); + } + if (lr == 1 && ud == 0) { + this.loadPrevPage(); + } + if (lr == 0 && ud == 1) { + this.loadPrevSubPage(); + } + if (lr == 0 && ud == -1) { + this.loadNextSubPage(); + } + } + + show(pageId, subPageId) { + if(!subPageId){ + subPageId = 1; + } + + if (Bangle.http) { + Bangle.http('https://teletekst-data.nos.nl/page/' + pageId + '-' + subPageId).then((data) => { + + const res = data.resp; + g.clear(); + + + this.navigationState = { + prevPage: { + p: undefined, + s: undefined, + }, + prevSubPage: { + p: undefined, + s: undefined, + }, + nextPage: { + p: undefined, + s: undefined, + }, + nextSubPage: { + p: undefined, + s: undefined, + }, + currentPage: { + p: pageId, + s: subPageId, + }, + }; + + // set next -, previous -, next sub - and previous sub page + let navNIndex = res.indexOf('pn=n_'); + if (navNIndex > -1) { + this.navigationState.nextPage.p = parseInt(res.substring(navNIndex + 5, navNIndex + 8)); + this.navigationState.nextPage.s = parseInt(res.substring(navNIndex + 9, navNIndex + 10)); + } + let navPIndex = res.indexOf('pn=p_'); + if (navPIndex > -1) { + this.navigationState.prevPage.p = parseInt(res.substring(navPIndex + 5, navPIndex + 8)); + this.navigationState.prevPage.s = parseInt(res.substring(navPIndex + 9, navPIndex + 10)); + } + let navPSIndex = res.indexOf('pn=ps'); + if (navPSIndex > -1) { + this.navigationState.prevSubPage.p = parseInt(res.substring(navPSIndex + 5, navPSIndex + 8)); + this.navigationState.prevSubPage.s = parseInt(res.substring(navPSIndex + 9, navPSIndex + 10)); + } + let navNSIndex = res.indexOf('pn=ns'); + if (navNSIndex > -1) { + this.navigationState.nextSubPage.p = parseInt(res.substring(navNSIndex + 5, navNSIndex + 8)); + this.navigationState.nextSubPage.s = parseInt(res.substring(navNSIndex + 9, navNSIndex + 10)); + } + + let split = E.toString(res.split('
')[1].split('
')[0]); + + this.render(split); + }); + } + } + + + + + render(source) { + + g.setFontAlign(-1, -1); + g.setFont('4x6'); + + // @ts-ignore + const bytes = E.toUint8Array(E.decodeUTF8(source)); + let rowIndex = 0; + let totalIndex = 0; + let charIndex = 0; + + for (let charByte of bytes) { + { + if ((charByte >= 0 && charByte <= 7) || (charByte >= 16 && charByte <= 23)) { + const color = this.colorArray[charByte]; + g.setColor(color[0], color[1], color[2]); + } + } + g.drawString(source[totalIndex], (charIndex * 4) + 6, rowIndex * 7); + charIndex++; + totalIndex++; + if (charIndex == 40) { + rowIndex++; + charIndex = 0; + g.flip(); + } + } + } + + +} + +const BUTTON_BORDER_WITH = 2; + +class Button { +// position; +// value; +// highlightTimeoutId; + + + constructor(position, value) { + this.position = position; + this.value = value; + } + + draw(highlight) { + g.setColor(g.theme.fg); + g.fillRect( + this.position.x1, + this.position.y1, + this.position.x2, + this.position.y2 + ); + + if (highlight) { + g.setColor(g.theme.bgH); + } else { + g.setColor(g.theme.bg); + } + g.fillRect( + this.position.x1 + BUTTON_BORDER_WITH, + this.position.y1 + BUTTON_BORDER_WITH, + this.position.x2 - BUTTON_BORDER_WITH, + this.position.y2 - BUTTON_BORDER_WITH + ); + + g.setColor(g.theme.fg); + g.setFontAlign(0, 0); + g.setFont("Vector", 35); + g.drawString( + this.value, + this.position.x1 + (this.position.x2 - this.position.x1) / 2 + 2, + this.position.y1 + (this.position.y2 - this.position.y1) / 2 + 2 + ); + } + + handleTouchInput(n, e) { + if ( + e.x >= this.position.x1 && + e.x <= this.position.x2 && + e.y >= this.position.y1 && + e.y <= this.position.y2 + ) { + this.draw(true); // draw to highlight + this.highlightTimeoutId = setTimeout(() => { + this.draw(); + this.highlightTimeoutId = undefined; + }, 100); + return this.value; + } + else { + return undefined; + } + } + + disable() { + // disable button + if (this.highlightTimeoutId) { + clearTimeout(this.highlightTimeoutId); + this.highlightTimeoutId = undefined; + } + } + +} + +class Input { + + constructor(callback) { + this.inputCallback = callback; + this.inputVal = ""; + + let button1 = new Button({ x1: 1, y1: 35, x2: 58, y2: 70 }, '1'); + let button2 = new Button({ x1: 60, y1: 35, x2: 116, y2: 70 }, '2'); + let button3 = new Button({ x1: 118, y1: 35, x2: 174, y2: 70 }, '3'); + + let button4 = new Button({ x1: 1, y1: 72, x2: 58, y2: 105 }, '4'); + let button5 = new Button({ x1: 60, y1: 72, x2: 116, y2: 105 }, '5'); + let button6 = new Button({ x1: 118, y1: 72, x2: 174, y2: 105 }, '6'); + + let button7 = new Button({ x1: 1, y1: 107, x2: 58, y2: 140 }, '7'); + let button8 = new Button({ x1: 60, y1: 107, x2: 116, y2: 140 }, '8'); + let button9 = new Button({ x1: 118, y1: 107, x2: 174, y2: 140 }, '9'); + + let buttonOK = new Button({ x1: 1, y1: 142, x2: 58, y2: 174 }, "OK"); + let button0 = new Button({ x1: 60, y1: 142, x2: 116, y2: 174 }, "0"); + let buttonDelete = new Button({ x1: 118, y1: 142, x2: 174, y2: 174 }, "<-"); + + this.inputButtons = [ + button1, + button2, + button3, + button4, + button5, + button6, + button7, + button8, + button9, + buttonOK, + button0, + buttonDelete, + ]; + } + + handleTouchInput(n, e) { + let res = 'none'; + for (let button of this.inputButtons) { + const touchResult = button.handleTouchInput(n, e); + if (touchResult) { + res = touchResult; + } + } + + switch (res) { + case 'OK': + if(this.inputVal.length == 3){ + this.inputCallback(parseInt(this.inputVal)); + } + break; + case '<-': + this.removeNumber(); + this.drawField(); + break; + case 'none': + break; + default: + this.appendNumber(parseInt(res)); + this.drawField(); + } + + } + + + hide() { + for (let button of this.inputButtons) { + button.disable(); + } + } + + start(preset) { + if (preset) { + this.inputVal = preset.toString(); + } + else { + this.inputVal = ''; + } + + this.draw(); + } + + appendNumber(number) { + if (number === 0 && this.inputVal.length === 0) { + return; + } + + if (this.inputVal.length <= 2) { + this.inputVal = this.inputVal + number; + } + } + + removeNumber() { + if (this.inputVal.length > 0) { + this.inputVal = this.inputVal.slice(0, -1); + } + } + + reset() { + this.inputVal = ""; + } + + draw() { + g.clear(); + this.drawButtons(); + this.drawField(); + } + + drawButtons() { + for (let button of this.inputButtons) { + button.draw(); + } + } + + drawField() { + g.clearRect(0, 0, 176, 34); + g.setColor(g.theme.fg); + g.setFontAlign(-1, -1); + g.setFont("Vector:26x40"); + g.drawString(this.inputVal, 2, 0); + } +} + +// require('./Input'); + +class NOSTeletekstApp { + + constructor() { + console.log("this is the teletekst app!"); + this.isLeaving = false; + this.viewMode= 'VIEW'; + this.view = new View(); + this.input = new Input((newVal)=>this.inputHandler(newVal)); + this.view.start(); + + Bangle.setUI({ + mode: "custom", + remove: () => { + this.isLeaving = true; + console.log("teletext app: i am packing my stuff, goodbye"); + require("widget_utils").show(); // re-show widgets + }, + touch: (n, e) => { + if (this.viewMode == 'VIEW') { + // we need to go to input mode + this.setViewMode('INPUT'); + return; + } + if (this.viewMode == 'INPUT') { + this.input.handleTouchInput(n, e); + return; + + } + }, + swipe: (lr, ud) => { + if (this.viewMode == 'VIEW') { + this.view.handleSwipe(lr,ud); + } + if (this.viewMode == 'INPUT') { + if(lr == 1 && ud == 0){ + this.setViewMode('VIEW'); + } + } + } + + }); + + } + + inputHandler(input){ + // set viewMode back to view + this.view.nextStartPage = input; + this.setViewMode('VIEW'); + } + + setViewMode(newViewMode){ + this.viewMode = newViewMode; + if(newViewMode=='INPUT'){ + this.input.start(); + } + if(newViewMode=='VIEW'){ + this.input.hide(); + this.view.start(); + } + } + + +} +new NOSTeletekstApp(); \ No newline at end of file diff --git a/apps/nostt/nostt.icon.js b/apps/nostt/nostt.icon.js new file mode 100644 index 000000000..b9ef929be --- /dev/null +++ b/apps/nostt/nostt.icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwZC/AH4A/AH4AWgmSAwn/wACB/0Agf/4EAhMkyVIB4MB/4AB/ARGpIRBpMggEPCIQAC4ARCgQRDkkAv4jEAQIRLAQIRC/4RCiVJIgJKBwARCgPACIxWCCIn8NwQAB8YRFgIRDNARcFPQgRBNYZHB+IRDEYyPDAAPwn4RJIggRBg4RDNYrGCJQQRGkCSCWYP4CIgpCUI7FFCIMfa5DFFCIL7GJQQRLgARBoBXCO4KhBAH4A/AH4A/AD4A=")) \ No newline at end of file diff --git a/apps/nostt/nostt_logo.png b/apps/nostt/nostt_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bf8f0d47d30a2c156773e5b1b4e63fa90d1d04ee GIT binary patch literal 1646 zcmV-!29f!RP) zqM!wW=EOvDX`6yl1Hl*@2q*&DLVJOuA)sjpTJ?`2Xh5Mwqfr9^Lli`4lvsI5;>sI5_-AQ2oxG0pMIt86=PA z)5;fLjGCQlYilK6QPIn0n;YXVg2CYBp_0btKUS#sMiz73m;sa)BMb~M`4fp!J1ZLB zQqa=UKb)|-P-ueA*c`_BJ)AK&!C2)8{f!AZS(uyK76=3s002RW$KMBlk*Pqy1wB3#>N%H zt&pAm^0@{1XCrM)QC6G9+u@9PQS92*zQdn=dZv7$WF8BL!}$Y-(QZ)sv|0QRIdU~! zuhf#sooNLC0I0gYd0SJ{q+1ow%gYJ9bm?=@)G4XxLnLl(d}m*a}Ee|vMY=T?&SUH+pAG;LPefGC|tlGF&I6b3=OkR~D#5A>EjJTLWb zQ2MNSoM3!jmgVOKLLL=m`4y3neFz{403e9smg->etdL0*W9+VEG`b4_mShvpYfFXb zp(|@F!Vpq)7+aq6lO|4VEh{TC z22Fb#cJD4$lgT&yIXN2v0MNz{K8S#66Whsgy92lR{jKLaJNwLbx64(!s;L@A&Q108zf;D>oBc7yT^ON5L=f8Y z1mURtrHVk>1{|7-Kq=AdX;^#X!(Sw0CbZ zQvu!shdTh1n8lb(_y7R%yx#Q$AkxU>IYfPs}LIG;#m07ngu-F_0_wMvh=G*uA>Cx?=3e zkz*uDS}X`c6CvarLWtXQQNvi4b_yXvNN~}j{<L^76W0TDr6w06rOf zAk@?}A*Lw14aTOXLK$QJbaC{0tG`}Raa0t=%~{0*fcN0RD9G|Yi_tLdB3*x->YG`z zcHr?TGoqyrm-bD+|6RQ&@*}6zZ}D}xLaAShW5MCq(jG`~j_BdlceGlK--AHi4n-0dwb2>^ilaCoe07`1jk%9xii zHi1%FWFHd+p+0+m1Hg)4@PaJMRYPqeGX>xL^eyp^o`nSQ_hHpb(vPTc^iG*kJY}05Q9t_waWo zy8YuyuqUqqMDYiNv7Q>^1SgRseT_YyCBC`k)4T_cTh9B6K;Y_{6)TqK`+RpulC+Xw ze2`$=g%DzcqA4MyLm=eC5hF&-sHv)2IqX9HU(Q!o%h84N?&w)E=XPFOn{~fs;uFqE sYinyxrhCZ2!NI}7!NI}7!QnsiFRML)y1TwejQ{`u07*qoM6N<$f|RHlq5uE@ literal 0 HcmV?d00001 diff --git a/apps/nostt/nostt_screenshot_1.png b/apps/nostt/nostt_screenshot_1.png new file mode 100644 index 0000000000000000000000000000000000000000..ad65ecba7e1f30886d876c46170024b8fb286b80 GIT binary patch literal 4415 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 FV1fYlLzw^o literal 0 HcmV?d00001 diff --git a/apps/nostt/nostt_screenshot_2.png b/apps/nostt/nostt_screenshot_2.png new file mode 100644 index 0000000000000000000000000000000000000000..0faa9b1f61450b15ae673fee14c1f070a14ef31e GIT binary patch literal 5107 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