From 501c7e79cfe6916154a48a8b086ef589e682c893 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 2 Mar 2022 16:55:38 +0800 Subject: [PATCH 01/36] Update app.js Support some invalid base32 characters. --- apps/authentiwatch/app.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 73b8bdeea..69890c90c 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -32,9 +32,12 @@ if (settings.tokens) tokens = settings.tokens; /* v0.03+ settings */ function b32decode(seedstr) { // RFC4648 - var i, buf = 0, bitcount = 0, retstr = ""; - for (i in seedstr) { - var c = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".indexOf(seedstr.charAt(i).toUpperCase(), 0); + var buf = 0, bitcount = 0, retstr = ""; + for (var c of seedstr.toUpperCase()) { + if (c=='0')c='O'; + if (c=='1')c='I'; + if (c=='8')c='B'; + c = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".indexOf(c); if (c != -1) { buf <<= 5; buf |= c; @@ -47,7 +50,7 @@ function b32decode(seedstr) { } } var retbuf = new Uint8Array(retstr.length); - for (i in retstr) { + for (var i in retstr) { retbuf[i] = retstr.charCodeAt(i); } return retbuf; From cb0aec47a43919235df08c4e2472c5dd64df5183 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 2 Mar 2022 16:57:48 +0800 Subject: [PATCH 02/36] Update app.js Add language translation tags --- apps/authentiwatch/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 69890c90c..e9d00a265 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -8,9 +8,9 @@ const algos = { "SHA256":{sha:crypto.SHA256,retsz:32,blksz:64 }, "SHA1" :{sha:crypto.SHA1 ,retsz:20,blksz:64 }, }; -const calculating = "Calculating"; -const notokens = "No tokens"; -const notsupported = "Not supported"; +const calculating = /*LANG*/"Calculating"; +const notokens = /*LANG*/"No tokens"; +const notsupported = /*LANG*/"Not supported"; // sample settings: // {tokens:[{"algorithm":"SHA1","digits":6,"period":30,"issuer":"","account":"","secret":"Bbb","label":"Aaa"}],misc:{}} From fe8345ed15e5a1202dd4f2a027ede2705969f849 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 2 Mar 2022 22:44:13 +0800 Subject: [PATCH 03/36] Update interface.html Refactoring --- apps/authentiwatch/interface.html | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/authentiwatch/interface.html b/apps/authentiwatch/interface.html index 5ee32fd8e..bfc0e55d1 100644 --- a/apps/authentiwatch/interface.html +++ b/apps/authentiwatch/interface.html @@ -54,9 +54,9 @@ var tokens = settings.tokens; */ function base32clean(val, nows) { var ret = val.replaceAll(/\s+/g, ' '); - ret = ret.replaceAll(/0/g, 'O'); - ret = ret.replaceAll(/1/g, 'I'); - ret = ret.replaceAll(/8/g, 'B'); + ret = ret.replaceAll('0', 'O'); + ret = ret.replaceAll('1', 'I'); + ret = ret.replaceAll('8', 'B'); ret = ret.replaceAll(/[^A-Za-z2-7 ]/g, ''); if (nows) { ret = ret.replaceAll(/\s+/g, ''); @@ -81,9 +81,9 @@ function b32encode(str) { function b32decode(seedstr) { // RFC4648 - var i, buf = 0, bitcount = 0, ret = ''; - for (i in seedstr) { - var c = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.indexOf(seedstr.charAt(i).toUpperCase(), 0); + var buf = 0, bitcount = 0, ret = ''; + for (var c of seedstr.toUpperCase()) { + c = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'.indexOf(c); if (c != -1) { buf <<= 5; buf |= c; @@ -487,7 +487,7 @@ function startScan(handler,cancel) { document.body.className = 'scanning'; navigator.mediaDevices .getUserMedia({video:{facingMode:'environment'}}) - .then(function(stream){ + .then(stream => { scanning=true; video.setAttribute('playsinline',true); video.srcObject = stream; From 7810dd2cfc01145b58998886b13ce78893219852 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Mon, 7 Mar 2022 23:33:23 +0800 Subject: [PATCH 04/36] Update app.js Bangle 2: Improve drag responsiveness and exit on button press --- apps/authentiwatch/app.js | 48 +++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index e9d00a265..b8181e4a2 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -34,9 +34,9 @@ function b32decode(seedstr) { // RFC4648 var buf = 0, bitcount = 0, retstr = ""; for (var c of seedstr.toUpperCase()) { - if (c=='0')c='O'; - if (c=='1')c='I'; - if (c=='8')c='B'; + if (c == '0') c = 'O'; + if (c == '1') c = 'I'; + if (c == '8') c = 'B'; c = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".indexOf(c); if (c != -1) { buf <<= 5; @@ -166,11 +166,7 @@ function drawToken(id, r) { } while (g.stringWidth(state.otp) > (r.w - adj)); g.drawString(state.otp, (x1 + adj + x2) / 2, y1 + tokenextraheight, false); } - // shaded lines top and bottom - g.setColor(0.5, 0.5, 0.5) - .drawLine(x1, y1, x2, y1) - .drawLine(x1, y2, x2, y2) - .setClipRect(0, 0, g.getWidth(), g.getHeight()); + g.setClipRect(0, 0, g.getWidth(), g.getHeight()); } function draw() { @@ -212,7 +208,7 @@ function draw() { if (id == state.curtoken && (tokens[id].period <= 0 || state.nextTime != 0)) { drewcur = true; } - id += 1; + id++; y += tokenheight; } if (drewcur) { @@ -273,11 +269,33 @@ function onTouch(zone, e) { } function onDrag(e) { - if (e.x > g.getWidth() || e.y > g.getHeight()) return; - if (e.dx == 0 && e.dy == 0) return; - var newy = Math.min(state.listy - e.dy, tokens.length * tokenheight - Bangle.appRect.h); - state.listy = Math.max(0, newy); - draw(); + if (e.b != 0 && e.x < g.getWidth() && e.y < g.getHeight() && e.dy != 0) { + var y = Math.max(0, Math.min(state.listy - e.dy, tokens.length * tokenheight - Bangle.appRect.h)); + if (state.listy != y) { + var id, dy = state.listy - y; + state.listy = y; + g.setClipRect(Bangle.appRect.x,Bangle.appRect.y,Bangle.appRect.x2,Bangle.appRect.y2) + .scroll(0, dy); + if (dy > 0) { + id = Math.floor((state.listy + dy) / tokenheight); + y = id * tokenheight + Bangle.appRect.y - state.listy; + do { + drawToken(id, {x:Bangle.appRect.x, y:y, w:Bangle.appRect.w, h:tokenheight}); + id--; + y -= tokenheight; + } while (y > 0); + } + if (dy < 0) { + id = Math.floor((state.listy + dy + Bangle.appRect.h) / tokenheight); + y = id * tokenheight + Bangle.appRect.y - state.listy; + while (y < Bangle.appRect.y2) { + drawToken(id, {x:Bangle.appRect.x, y:y, w:Bangle.appRect.w, h:tokenheight}); + id++; + y += tokenheight; + } + } + } + } } function onSwipe(e) { @@ -332,6 +350,8 @@ if (typeof BTN2 == 'number') { setWatch(function(){bangle1Btn(-1);}, BTN1, {edge:"rising" , debounce:50, repeat:true}); setWatch(function(){exitApp(); }, BTN2, {edge:"falling", debounce:50}); setWatch(function(){bangle1Btn( 1);}, BTN3, {edge:"rising" , debounce:50, repeat:true}); +} else { + setWatch(function(){exitApp(); }, BTN1, {edge:"falling", debounce:50}); } Bangle.loadWidgets(); From 929920fcb6708a715b0961e830c03cca7ef8a358 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Mon, 7 Mar 2022 23:33:53 +0800 Subject: [PATCH 05/36] Update ChangeLog 0.07 --- apps/authentiwatch/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/authentiwatch/ChangeLog b/apps/authentiwatch/ChangeLog index bb2945db4..655916170 100644 --- a/apps/authentiwatch/ChangeLog +++ b/apps/authentiwatch/ChangeLog @@ -4,3 +4,4 @@ 0.04: Fix tapping at very bottom of list, exit on inactivity 0.05: Add support for bulk importing and exporting tokens 0.06: Add spaces to codes for improved readability (thanks @BartS23) +0.07: Bangle 2: Improve drag responsiveness and exit on button press From 51744f842df0484bab0b72bc3bcbe8f089e8c8e3 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Mon, 7 Mar 2022 23:34:18 +0800 Subject: [PATCH 06/36] Update metadata.json 0.07 --- apps/authentiwatch/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/authentiwatch/metadata.json b/apps/authentiwatch/metadata.json index b4ed34a12..36e1ea34d 100644 --- a/apps/authentiwatch/metadata.json +++ b/apps/authentiwatch/metadata.json @@ -4,7 +4,7 @@ "shortName": "AuthWatch", "icon": "app.png", "screenshots": [{"url":"screenshot1.png"},{"url":"screenshot2.png"},{"url":"screenshot3.png"},{"url":"screenshot4.png"}], - "version": "0.06", + "version": "0.07", "description": "Google Authenticator compatible tool.", "tags": "tool", "interface": "interface.html", From 915ad6394795ea25e28d05480095ad23247836ec Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Tue, 8 Mar 2022 15:58:46 +0800 Subject: [PATCH 07/36] Update interface.html Use push instead of concat. --- apps/authentiwatch/interface.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/authentiwatch/interface.html b/apps/authentiwatch/interface.html index bfc0e55d1..7d567d34f 100644 --- a/apps/authentiwatch/interface.html +++ b/apps/authentiwatch/interface.html @@ -405,7 +405,7 @@ class proto3decoder { constructor(str) { this.buf = []; for (let i in str) { - this.buf = this.buf.concat(str.charCodeAt(i)); + this.buf.push(str.charCodeAt(i)); } } getVarint() { From d7def5d5e1c95fcd456a406ef8bda3da183e9f34 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Tue, 8 Mar 2022 21:52:57 +0800 Subject: [PATCH 08/36] Update app.js Cache font size calculations --- apps/authentiwatch/app.js | 45 +++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index b8181e4a2..8a8aeb962 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -18,18 +18,6 @@ var settings = require("Storage").readJSON("authentiwatch.json", true) || {token if (settings.data ) tokens = settings.data ; /* v0.02 settings */ if (settings.tokens) tokens = settings.tokens; /* v0.03+ settings */ -// QR Code Text -// -// Example: -// -// otpauth://totp/${url}:AA_${algorithm}_${digits}dig_${period}s@${url}?algorithm=${algorithm}&digits=${digits}&issuer=${url}&period=${period}&secret=${secret} -// -// ${algorithm} : one of SHA1 / SHA256 / SHA512 -// ${digits} : one of 6 / 8 -// ${period} : one of 30 / 60 -// ${url} : a domain name "example.com" -// ${secret} : the seed code - function b32decode(seedstr) { // RFC4648 var buf = 0, bitcount = 0, retstr = ""; @@ -75,6 +63,10 @@ function do_hmac(key, message, algo) { var v = new DataView(ret, ret[ret.length - 1] & 0x0F, 4); return v.getUint32(0) & 0x7FFFFFFF; } +function formatOtp(otp, digits) { + var re = (digits % 3 == 0 || (digits % 3 >= digits % 4 && digits % 4 != 0)) ? "" : "."; + return otp.replace(new RegExp("(..." + re + ")", "g"), "$1 ").trim(); +} function hotp(d, token, dohmac) { var tick; if (token.period > 0) { @@ -98,8 +90,7 @@ function hotp(d, token, dohmac) { ret = "0" + ret; } // add a space after every 3rd or 4th digit - var re = (token.digits % 3 == 0 || (token.digits % 3 >= token.digits % 4 && token.digits % 4 != 0)) ? "" : "."; - ret = ret.replace(new RegExp("(..." + re + ")", "g"), "$1 ").trim(); + ret = formatOtp(ret, token.digits); } catch(err) { ret = notsupported; } @@ -107,6 +98,7 @@ function hotp(d, token, dohmac) { return {hotp:ret, next:((token.period > 0) ? ((tick + 1) * token.period * 1000) : d.getTime() + 30000)}; } +var fontsz_cache = {}; var state = { listy: 0, prevcur:0, @@ -117,12 +109,25 @@ var state = { hide:0 }; +function size_font(id, txt, w) { + var sz = fontsz_cache[id]; + if (sz) { + g.setFont("Vector", sz); + } else { + sz = tokendigitsheight; + do { + g.setFont("Vector", sz--); + } while (g.stringWidth(txt) > w); + fontsz_cache[id] = sz + 1; + } +} + function drawToken(id, r) { var x1 = r.x; var y1 = r.y; var x2 = r.x + r.w - 1; var y2 = r.y + r.h - 1; - var adj, lbl, sz; + var adj, lbl; g.setClipRect(Math.max(x1, Bangle.appRect.x ), Math.max(y1, Bangle.appRect.y ), Math.min(x2, Bangle.appRect.x2), Math.min(y2, Bangle.appRect.y2)); lbl = tokens[id].label.substr(0, 10); @@ -137,10 +142,7 @@ function drawToken(id, r) { } else { g.setColor(g.theme.fg) .setBgColor(g.theme.bg); - sz = tokendigitsheight; - do { - g.setFont("Vector", sz--); - } while (g.stringWidth(lbl) > r.w); + size_font("l" + id, lbl, r.w); // center in box g.setFontAlign(0, 0, 0); adj = (y1 + y2) / 2; @@ -160,10 +162,7 @@ function drawToken(id, r) { adj = 12; } // digits just below label - sz = tokendigitsheight; - do { - g.setFont("Vector", sz--); - } while (g.stringWidth(state.otp) > (r.w - adj)); + size_font("d" + id, state.otp, r.w - adj); g.drawString(state.otp, (x1 + adj + x2) / 2, y1 + tokenextraheight, false); } g.setClipRect(0, 0, g.getWidth(), g.getHeight()); From ebdea05bff9df19ea85ed7a636967b27f740a21f Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 9 Mar 2022 21:53:24 +0800 Subject: [PATCH 09/36] Update app.js Improve code style --- apps/authentiwatch/app.js | 93 ++++++++++++++++++++------------------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 8a8aeb962..fb130969f 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -1,6 +1,6 @@ -const tokenextraheight = 16; -var tokendigitsheight = 30; -var tokenheight = tokendigitsheight + tokenextraheight; +const TOKEN_EXTRA_HEIGHT = 16; +var TOKEN_DIGITS_HEIGHT = 30; +var TOKEN_HEIGHT = TOKEN_DIGITS_HEIGHT + TOKEN_EXTRA_HEIGHT; // Hash functions const crypto = require("crypto"); const algos = { @@ -8,9 +8,9 @@ const algos = { "SHA256":{sha:crypto.SHA256,retsz:32,blksz:64 }, "SHA1" :{sha:crypto.SHA1 ,retsz:20,blksz:64 }, }; -const calculating = /*LANG*/"Calculating"; -const notokens = /*LANG*/"No tokens"; -const notsupported = /*LANG*/"Not supported"; +const CALCULATING = /*LANG*/"Calculating"; +const NO_TOKENS = /*LANG*/"No tokens"; +const NOT_SUPPORTED = /*LANG*/"Not supported"; // sample settings: // {tokens:[{"algorithm":"SHA1","digits":6,"period":30,"issuer":"","account":"","secret":"Bbb","label":"Aaa"}],misc:{}} @@ -43,7 +43,8 @@ function b32decode(seedstr) { } return retbuf; } -function do_hmac(key, message, algo) { + +function doHmac(key, message, algo) { var a = algos[algo]; // RFC2104 if (key.length > a.blksz) { @@ -63,11 +64,13 @@ function do_hmac(key, message, algo) { var v = new DataView(ret, ret[ret.length - 1] & 0x0F, 4); return v.getUint32(0) & 0x7FFFFFFF; } + function formatOtp(otp, digits) { var re = (digits % 3 == 0 || (digits % 3 >= digits % 4 && digits % 4 != 0)) ? "" : "."; return otp.replace(new RegExp("(..." + re + ")", "g"), "$1 ").trim(); } -function hotp(d, token, dohmac) { + +function hotp(d, token, calcHmac) { var tick; if (token.period > 0) { // RFC6238 - timed @@ -81,10 +84,10 @@ function hotp(d, token, dohmac) { var v = new DataView(msg.buffer); v.setUint32(0, tick >> 16 >> 16); v.setUint32(4, tick & 0xFFFFFFFF); - var ret = calculating; - if (dohmac) { + var ret = CALCULATING; + if (calcHmac) { try { - var hash = do_hmac(b32decode(token.secret), msg, token.algorithm.toUpperCase()); + var hash = doHmac(b32decode(token.secret), msg, token.algorithm.toUpperCase()); ret = "" + hash % Math.pow(10, token.digits); while (ret.length < token.digits) { ret = "0" + ret; @@ -92,13 +95,13 @@ function hotp(d, token, dohmac) { // add a space after every 3rd or 4th digit ret = formatOtp(ret, token.digits); } catch(err) { - ret = notsupported; + ret = NOT_SUPPORTED; } } return {hotp:ret, next:((token.period > 0) ? ((tick + 1) * token.period * 1000) : d.getTime() + 30000)}; } -var fontsz_cache = {}; +var fontszCache = {}; var state = { listy: 0, prevcur:0, @@ -109,16 +112,16 @@ var state = { hide:0 }; -function size_font(id, txt, w) { - var sz = fontsz_cache[id]; +function sizeFont(id, txt, w) { + var sz = fontszCache[id]; if (sz) { g.setFont("Vector", sz); } else { - sz = tokendigitsheight; + sz = TOKEN_DIGITS_HEIGHT; do { g.setFont("Vector", sz--); } while (g.stringWidth(txt) > w); - fontsz_cache[id] = sz + 1; + fontszCache[id] = sz + 1; } } @@ -135,14 +138,14 @@ function drawToken(id, r) { // current token g.setColor(g.theme.fgH) .setBgColor(g.theme.bgH) - .setFont("Vector", tokenextraheight) + .setFont("Vector", TOKEN_EXTRA_HEIGHT) // center just below top line .setFontAlign(0, -1, 0); adj = y1; } else { g.setColor(g.theme.fg) .setBgColor(g.theme.bg); - size_font("l" + id, lbl, r.w); + sizeFont("l" + id, lbl, r.w); // center in box g.setFontAlign(0, 0, 0); adj = (y1 + y2) / 2; @@ -162,8 +165,8 @@ function drawToken(id, r) { adj = 12; } // digits just below label - size_font("d" + id, state.otp, r.w - adj); - g.drawString(state.otp, (x1 + adj + x2) / 2, y1 + tokenextraheight, false); + sizeFont("d" + id, state.otp, r.w - adj); + g.drawString(state.otp, (x1 + adj + x2) / 2, y1 + TOKEN_EXTRA_HEIGHT, false); } g.setClipRect(0, 0, g.getWidth(), g.getHeight()); } @@ -174,7 +177,7 @@ function draw() { var d = new Date(); if (state.curtoken != -1) { var t = tokens[state.curtoken]; - if (state.otp == calculating) { + if (state.otp == CALCULATING) { state.otp = hotp(d, t, true).hotp; } if (d.getTime() > state.nextTime) { @@ -200,20 +203,20 @@ function draw() { } if (tokens.length > 0) { var drewcur = false; - var id = Math.floor(state.listy / tokenheight); - var y = id * tokenheight + Bangle.appRect.y - state.listy; + var id = Math.floor(state.listy / TOKEN_HEIGHT); + var y = id * TOKEN_HEIGHT + Bangle.appRect.y - state.listy; while (id < tokens.length && y < Bangle.appRect.y2) { - drawToken(id, {x:Bangle.appRect.x, y:y, w:Bangle.appRect.w, h:tokenheight}); + drawToken(id, {x:Bangle.appRect.x, y:y, w:Bangle.appRect.w, h:TOKEN_HEIGHT}); if (id == state.curtoken && (tokens[id].period <= 0 || state.nextTime != 0)) { drewcur = true; } id++; - y += tokenheight; + y += TOKEN_HEIGHT; } if (drewcur) { // the current token has been drawn - schedule a redraw if (tokens[state.curtoken].period > 0) { - timerdly = (state.otp == calculating) ? 1 : 1000; // timed + timerdly = (state.otp == CALCULATING) ? 1 : 1000; // timed } else { timerdly = state.nexttime - d.getTime(); // counter } @@ -230,9 +233,9 @@ function draw() { state.nexttime = 0; } } else { - g.setFont("Vector", tokendigitsheight) + g.setFont("Vector", TOKEN_DIGITS_HEIGHT) .setFontAlign(0, 0, 0) - .drawString(notokens, Bangle.appRect.x + Bangle.appRect.w / 2, Bangle.appRect.y + Bangle.appRect.h / 2, false); + .drawString(NO_TOKENS, Bangle.appRect.x + Bangle.appRect.w / 2, Bangle.appRect.y + Bangle.appRect.h / 2, false); } if (state.drawtimer) { clearTimeout(state.drawtimer); @@ -242,18 +245,18 @@ function draw() { function onTouch(zone, e) { if (e) { - var id = Math.floor((state.listy + (e.y - Bangle.appRect.y)) / tokenheight); + var id = Math.floor((state.listy + (e.y - Bangle.appRect.y)) / TOKEN_HEIGHT); if (id == state.curtoken || tokens.length == 0 || id >= tokens.length) { id = -1; } if (state.curtoken != id) { if (id != -1) { - var y = id * tokenheight - state.listy; + var y = id * TOKEN_HEIGHT - state.listy; if (y < 0) { state.listy += y; y = 0; } - y += tokenheight; + y += TOKEN_HEIGHT; if (y > Bangle.appRect.h) { state.listy += (y - Bangle.appRect.h); } @@ -269,28 +272,28 @@ function onTouch(zone, e) { function onDrag(e) { if (e.b != 0 && e.x < g.getWidth() && e.y < g.getHeight() && e.dy != 0) { - var y = Math.max(0, Math.min(state.listy - e.dy, tokens.length * tokenheight - Bangle.appRect.h)); + var y = Math.max(0, Math.min(state.listy - e.dy, tokens.length * TOKEN_HEIGHT - Bangle.appRect.h)); if (state.listy != y) { var id, dy = state.listy - y; state.listy = y; g.setClipRect(Bangle.appRect.x,Bangle.appRect.y,Bangle.appRect.x2,Bangle.appRect.y2) .scroll(0, dy); if (dy > 0) { - id = Math.floor((state.listy + dy) / tokenheight); - y = id * tokenheight + Bangle.appRect.y - state.listy; + id = Math.floor((state.listy + dy) / TOKEN_HEIGHT); + y = id * TOKEN_HEIGHT + Bangle.appRect.y - state.listy; do { - drawToken(id, {x:Bangle.appRect.x, y:y, w:Bangle.appRect.w, h:tokenheight}); + drawToken(id, {x:Bangle.appRect.x, y:y, w:Bangle.appRect.w, h:TOKEN_HEIGHT}); id--; - y -= tokenheight; + y -= TOKEN_HEIGHT; } while (y > 0); } if (dy < 0) { - id = Math.floor((state.listy + dy + Bangle.appRect.h) / tokenheight); - y = id * tokenheight + Bangle.appRect.y - state.listy; + id = Math.floor((state.listy + dy + Bangle.appRect.h) / TOKEN_HEIGHT); + y = id * TOKEN_HEIGHT + Bangle.appRect.y - state.listy; while (y < Bangle.appRect.y2) { - drawToken(id, {x:Bangle.appRect.x, y:y, w:Bangle.appRect.w, h:tokenheight}); + drawToken(id, {x:Bangle.appRect.x, y:y, w:Bangle.appRect.w, h:TOKEN_HEIGHT}); id++; - y += tokenheight; + y += TOKEN_HEIGHT; } } } @@ -324,12 +327,12 @@ function bangle1Btn(e) { } state.curtoken = Math.max(state.curtoken, 0); state.curtoken = Math.min(state.curtoken, tokens.length - 1); - state.listy = state.curtoken * tokenheight; - state.listy -= (Bangle.appRect.h - tokenheight) / 2; - state.listy = Math.min(state.listy, tokens.length * tokenheight - Bangle.appRect.h); + state.listy = state.curtoken * TOKEN_HEIGHT; + state.listy -= (Bangle.appRect.h - TOKEN_HEIGHT) / 2; + state.listy = Math.min(state.listy, tokens.length * TOKEN_HEIGHT - Bangle.appRect.h); state.listy = Math.max(state.listy, 0); var fakee = {}; - fakee.y = state.curtoken * tokenheight - state.listy + Bangle.appRect.y; + fakee.y = state.curtoken * TOKEN_HEIGHT - state.listy + Bangle.appRect.y; state.curtoken = -1; state.nextTime = 0; onTouch(0, fakee); From f96ba8d6a5df56459ebbcf2249c925c3deae72a3 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 9 Mar 2022 21:56:52 +0800 Subject: [PATCH 10/36] Add files via upload Update screenshots --- apps/authentiwatch/screenshot1.png | Bin 2708 -> 1595 bytes apps/authentiwatch/screenshot2.png | Bin 2914 -> 1835 bytes apps/authentiwatch/screenshot3.png | Bin 2656 -> 1425 bytes apps/authentiwatch/screenshot4.png | Bin 2951 -> 1721 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/authentiwatch/screenshot1.png b/apps/authentiwatch/screenshot1.png index c7ca744b497c2098743be85b15d16878f7196a54..c3ac0e3b7e98b4db240c3928901179496a6ff69c 100644 GIT binary patch literal 1595 zcmaKsdpOez7{_NO4E6JzM09N9q@u)fEt)YWlWQiE#7K_he#>EwOIy>CYY5@NVXe$8 zA(q<`!Xmjdw~mxZiOpr~=v@9ePfve*-}iYw|GdxhdEV!nXlHXq2C4`Jfj}}?3(UEl zUHLT<;GGE{2$9@b;-Oe;bMd#LlF~{FBm{@EgCqkjoI^k$8~E3V>?7t(>_CV&+8PZ4 zF;k=j1ThdutP_htUiCUT^3bf;ePvt>O!RJe{)X0WQTjuM8@ zV_UiLF5Z(mI>!@&)T|=*8)RZrzG4a&QG2 zRP9mAW^TP<(+5{QYipjb)h|8FIqL;cBG;NdA=XuI5<@-@*AjY5$TTTo#~W{l}5^z;N31HlrE47D=RQ7G|7_@n_U~ z8}da@Y4msd_Tw_!>z(mQhu064@^^qdC@v zWH2FPoknSn%tp&l)a=A_W_vMZZ+jmW5e0u>H#SFEwQM&PNO9f3FJ|qLdDyI~T@JFp zt_)Fv?*uvhO+Gd{jStIX^WY{DPeiUS$D!($yGSS|t~gg(Y`6A)t$^jL{i= z9U5$6X#4T|;Z)@<&r$}!N?YfqAWa!;JApDjGWcAI`w3yX zfCR+^Ly;qs^Agz#s%K+kN%!WLbG3X zP<@yNtDT@M)NXw+O^)uK8bn{&R8=~id|0kHgJ>%#;5y%^;5!LKU#%Az@LFq~FL>7V z;5K-z`uGb`iK4Pahqn7m!={_M48r(U;T8U?vh@>gFD@XiDUPSipPw9YnLO7E{p^m` z6J(alfiu>eD6grLkn&O#n`(OF%ml}59Nk5L_ zs%^0D`cME`HT&j48+c#j{J=#-GE(JKLpTDL27B6QYsnAPRoW)N;M9t<$~8=1JH&(j zi>lkwkQs8vkm+uhc5#N1q89m|2Klb#|GMT0aevISEeUFd(P|@^36)>8-T<-GF&xMF zJ&h!SIwNLj0jUx!aO+p19Od%tUM!wJ-6rD{y|il^)}AfU__I2+V_&jG#xnNZA=}74LRqH5R3_^nq3n{K98|K5veg+h zB1<{SE{Z9GETcIk%W=}{e1AW@*Y$q5@BfG2eO>pL`~Sc0w6pe>0tg8N0008kR%T8I z+VpS1c@8?Qo#k^NAlb>%1ZW(VTmb-ty|tOK3&n?Bc8Of2cIalwuU}PUQx9EyNQV;k z#lYOΞ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 diff --git a/apps/authentiwatch/screenshot2.png b/apps/authentiwatch/screenshot2.png index 8156dd3e8a8070e3b57b93163f8cd44ca5e90e85..26bafdbb2db394b8652c4197ce828c4463281351 100644 GIT binary patch literal 1835 zcmb7FdpHvc8y}X2aTX=!wsmw?a*0|lCzlyQ>>@)#R?cyV$)!<3)@)Dh z005xs=IZ1n<3Iihki0CzMq-s@v^~zv(|P->f)W_Ie{e?9B$JfRyPk{%05GtBLQXR& z|D8+(hN3)C06<-aiiEfg08ngkb3%D1j4c*jOTTXeb|wx>rTxfS7f04!Y8N>|Sfp*+ zw%;g(`_@VYnPOM0)XGYcO2qkgycCy-_aEdPjFVka5feOLVG)=mU3AX7`XR?21r(GBw#e+P!MI^JFrqq zV;Bgj>FYYDx=d3-?W=$67!>8_00}p|z|aFZ-lCwrKL$F#nL1PEl{&rEEzXJzjmp#9 z!w6QP;9#r9R|icz`*xsv668^FcrwMwu8sopz^y3G!4y!e+kfe3f{b$T*5;4i^|ec6 zQ(kN%%F!9m9SD;U*i*P7B=SOxikh4}@W0`8TIC|i^9xeSu^50`-2G`oQOm+%0s%6H zZ~IYC{u+=9R{&xt!O6_Djybq80um4PmtNfXCJ1jEZi?rspA#^Ao;L=}(!y;fOc+g< zNT(uSucaO5a0&*dSo{Fe!d9cHW&&87=i?li!BRzTcKJrH64vh>>~S&47k_Q-f&jUHgS6Q{3ragojiD_`&{+# z!RBrVvY#opegx@Ju7>B2k`@zpHSSg(y+ewx#PZ;r=fv`y1qXcV?_7!D{ggGZI=ex_pak;dm7N9Y&oc59K)o&;7!$}RRY^L zV^+K}gdwMS=l6;r9NkAl7w3iN_{WD2ww;7jpE6Ein?~C2KkaYzZt*a|Kl2A|&d#2+ z&vSg@Q6V-bbw&;U+s62EL37qoS-0RpicYXTq5^3d#>d472J>lc{3g4xr#?VIlwFH= zP)ci{v!~-{c`f|C$*)wTVI*L{Cg1Z^oL zhF9iyEEaeq1#wQ@5L){W_uCUKFQI8u%7KE~%ddciv0`jRY7}_#G{I>ZQl?cRTFP*L z|LYI7*8Ve7#W*E)PG29WE}6Ujg|=!QmQrW?DZ=-@J&R0%4SiX|Y|5=M56s!+M@KWD zd56f30{@!gmEwHH;UEuv**ZhSBRJE`%SI;z-}KDMpr12L3^HQ%Nd*n(NRS3z&j4Kb zw`-D3Fn!bTrGXCJ$`)I0XC0>>kUqL@j@vvDJ!%(uJ}a$QCn@ zc$lzKH5(w&Fd|Mveo68h4tc!S>G&J>+3^=p;_3~y^=>H@>NK_(F(E|0=nc+qh0BET zB_jh+rtz}vwj55TH4UKf-DHpvYU!$OunJo7->(11{_jOe5uIh0u9#GBa-Vb{^WoA7 zAZnod#F_q)n6G2U;TsYG;7*r$&)nrJ%OrH2PmdGPHP zNTPixL?lX0&p_{_e+VJ?wKdsft$;&SPjbzJAa`!nA1W*etMXAV9ii)!qU3gTA3sO5y1bFQ>UQRmfl9qi7r}$`P1^aC&FZRXECmT6 zWKP9A9&?d+{YOSz>*`@&#z~hD%%7&RODWiR;x;ZEj+695ZklG8e}@$G9bZtq~GV)1NPy<|+^+d@RJo1z`)d7>&YDCs|9F;0mG%f1@G%^BnL I&@q_uFAa7*R{#J2 literal 2914 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 Z(A4{6$wZuM;{MYNIM|-BskI8c@jt}ERJ#BG diff --git a/apps/authentiwatch/screenshot3.png b/apps/authentiwatch/screenshot3.png index 6d14e0b965efa1b522d034bc0fee2f8415a950d5..80f2fb172b55fdfc19ac61f2c7878d718ebdc1fb 100644 GIT binary patch delta 1418 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 KMNUMnLSTZ>#1y{( literal 2951 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 From 1523a15b8e9c271ee85da212fe66e5f3768874ce Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Sun, 20 Mar 2022 17:36:34 +0800 Subject: [PATCH 11/36] Update app.js Rewrite UI code and drawing --- apps/authentiwatch/app.js | 440 +++++++++++++++++++++----------------- 1 file changed, 248 insertions(+), 192 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index fb130969f..2734a064a 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -1,3 +1,4 @@ +const COUNTER_TRIANGLE_SIZE = 10; const TOKEN_EXTRA_HEIGHT = 16; var TOKEN_DIGITS_HEIGHT = 30; var TOKEN_HEIGHT = TOKEN_DIGITS_HEIGHT + TOKEN_EXTRA_HEIGHT; @@ -44,8 +45,8 @@ function b32decode(seedstr) { return retbuf; } -function doHmac(key, message, algo) { - var a = algos[algo]; +function hmac(key, message, algo) { + var a = algos[algo.toUpperCase()]; // RFC2104 if (key.length > a.blksz) { key = a.sha(key); @@ -66,50 +67,52 @@ function doHmac(key, message, algo) { } function formatOtp(otp, digits) { + // add 0 padding + var ret = "" + otp % Math.pow(10, digits); + while (ret.length < digits) { + ret = "0" + ret; + } + // add a space after every 3rd or 4th digit var re = (digits % 3 == 0 || (digits % 3 >= digits % 4 && digits % 4 != 0)) ? "" : "."; - return otp.replace(new RegExp("(..." + re + ")", "g"), "$1 ").trim(); + return ret.replace(new RegExp("(..." + re + ")", "g"), "$1 ").trim(); } -function hotp(d, token, calcHmac) { - var tick; +function hotp(token) { + var d = Date.now(); + var tick, next; if (token.period > 0) { // RFC6238 - timed - var seconds = Math.floor(d.getTime() / 1000); + var seconds = Math.floor(d / 1000); tick = Math.floor(seconds / token.period); + next = (tick + 1) * token.period * 1000; } else { // RFC4226 - counter tick = -token.period; + next = d + 30000; } var msg = new Uint8Array(8); var v = new DataView(msg.buffer); v.setUint32(0, tick >> 16 >> 16); v.setUint32(4, tick & 0xFFFFFFFF); - var ret = CALCULATING; - if (calcHmac) { - try { - var hash = doHmac(b32decode(token.secret), msg, token.algorithm.toUpperCase()); - ret = "" + hash % Math.pow(10, token.digits); - while (ret.length < token.digits) { - ret = "0" + ret; - } - // add a space after every 3rd or 4th digit - ret = formatOtp(ret, token.digits); - } catch(err) { - ret = NOT_SUPPORTED; - } + var ret; + try { + ret = hmac(b32decode(token.secret), msg, token.algorithm); + ret = formatOtp(ret, token.digits); + } catch(err) { + ret = NOT_SUPPORTED; } - return {hotp:ret, next:((token.period > 0) ? ((tick + 1) * token.period * 1000) : d.getTime() + 30000)}; + return {hotp:ret, next:next}; } +// Tokens are displayed in three states: +// 1. Unselected (state.id==-1) +// 2. Selected, inactive (no code) (state.id!=-1,state.hotp.hotp=="") +// 3. Selected, active (code showing) (state.id!=-1,state.hotp.hotp!="") var fontszCache = {}; var state = { - listy: 0, - prevcur:0, - curtoken:-1, - nextTime:0, - otp:"", - rem:0, - hide:0 + listy:0, // list scroll position + id:-1, // current token ID + hotp:{hotp:"",next:0} }; function sizeFont(id, txt, w) { @@ -125,117 +128,42 @@ function sizeFont(id, txt, w) { } } -function drawToken(id, r) { - var x1 = r.x; - var y1 = r.y; - var x2 = r.x + r.w - 1; - var y2 = r.y + r.h - 1; - var adj, lbl; - g.setClipRect(Math.max(x1, Bangle.appRect.x ), Math.max(y1, Bangle.appRect.y ), - Math.min(x2, Bangle.appRect.x2), Math.min(y2, Bangle.appRect.y2)); - lbl = tokens[id].label.substr(0, 10); - if (id == state.curtoken) { - // current token - g.setColor(g.theme.fgH) - .setBgColor(g.theme.bgH) - .setFont("Vector", TOKEN_EXTRA_HEIGHT) - // center just below top line - .setFontAlign(0, -1, 0); - adj = y1; - } else { - g.setColor(g.theme.fg) - .setBgColor(g.theme.bg); - sizeFont("l" + id, lbl, r.w); - // center in box - g.setFontAlign(0, 0, 0); - adj = (y1 + y2) / 2; - } - g.clearRect(x1, y1, x2, y2) - .drawString(lbl, (x1 + x2) / 2, adj, false); - if (id == state.curtoken) { - if (tokens[id].period > 0) { - // timed - draw progress bar - let xr = Math.floor(Bangle.appRect.w * state.rem / tokens[id].period); - g.fillRect(x1, y2 - 4, xr, y2 - 1); - adj = 0; - } else { - // counter - draw triangle as swipe hint - let yc = (y1 + y2) / 2; - g.fillPoly([0, yc, 10, yc - 10, 10, yc + 10, 0, yc]); - adj = 12; - } - // digits just below label - sizeFont("d" + id, state.otp, r.w - adj); - g.drawString(state.otp, (x1 + adj + x2) / 2, y1 + TOKEN_EXTRA_HEIGHT, false); - } - g.setClipRect(0, 0, g.getWidth(), g.getHeight()); -} +tokenY = id => id * TOKEN_HEIGHT + AR.y - state.listy; +half = n => Math.floor(n / 2); -function draw() { - var timerfn = exitApp; - var timerdly = 10000; - var d = new Date(); - if (state.curtoken != -1) { - var t = tokens[state.curtoken]; - if (state.otp == CALCULATING) { - state.otp = hotp(d, t, true).hotp; - } - if (d.getTime() > state.nextTime) { - if (state.hide == 0) { - // auto-hide the current token - if (state.curtoken != -1) { - state.prevcur = state.curtoken; - state.curtoken = -1; +function timerCalc() { + let timerfn = exitApp; + let timerdly = 10000; + let id = state.id; + if (id != -1) { + if (state.hotp.hotp != "") { + if (tokens[id].period > 0) { + // timed HOTP + if (state.hotp.next < Date.now()) { + if (state.cnt > 0) { + --state.cnt; + state.hotp = hotp(tokens[id]); + } else { + state.hotp.hotp = ""; + } + timerdly = 1; + timerfn = updateCurrentToken; + } else { + timerdly = 1000; + timerfn = updateProgressBar; } - state.nextTime = 0; } else { - // time to generate a new token - var r = hotp(d, t, state.otp != ""); - state.nextTime = r.next; - state.otp = r.hotp; - if (t.period <= 0) { - state.hide = 1; + // counter HOTP + if (state.cnt > 0) { + --state.cnt; + timerdly = 30000; + } else { + state.hotp.hotp = ""; + timerdly = 1; } - state.hide--; + timerfn = updateCurrentToken; } } - state.rem = Math.max(0, Math.floor((state.nextTime - d.getTime()) / 1000)); - } - if (tokens.length > 0) { - var drewcur = false; - var id = Math.floor(state.listy / TOKEN_HEIGHT); - var y = id * TOKEN_HEIGHT + Bangle.appRect.y - state.listy; - while (id < tokens.length && y < Bangle.appRect.y2) { - drawToken(id, {x:Bangle.appRect.x, y:y, w:Bangle.appRect.w, h:TOKEN_HEIGHT}); - if (id == state.curtoken && (tokens[id].period <= 0 || state.nextTime != 0)) { - drewcur = true; - } - id++; - y += TOKEN_HEIGHT; - } - if (drewcur) { - // the current token has been drawn - schedule a redraw - if (tokens[state.curtoken].period > 0) { - timerdly = (state.otp == CALCULATING) ? 1 : 1000; // timed - } else { - timerdly = state.nexttime - d.getTime(); // counter - } - timerfn = draw; - if (tokens[state.curtoken].period <= 0) { - state.hide = 0; - } - } else { - // de-select the current token if it is scrolled out of view - if (state.curtoken != -1) { - state.prevcur = state.curtoken; - state.curtoken = -1; - } - state.nexttime = 0; - } - } else { - g.setFont("Vector", TOKEN_DIGITS_HEIGHT) - .setFontAlign(0, 0, 0) - .drawString(NO_TOKENS, Bangle.appRect.x + Bangle.appRect.w / 2, Bangle.appRect.y + Bangle.appRect.h / 2, false); } if (state.drawtimer) { clearTimeout(state.drawtimer); @@ -243,101 +171,230 @@ function draw() { state.drawtimer = setTimeout(timerfn, timerdly); } -function onTouch(zone, e) { - if (e) { - var id = Math.floor((state.listy + (e.y - Bangle.appRect.y)) / TOKEN_HEIGHT); - if (id == state.curtoken || tokens.length == 0 || id >= tokens.length) { - id = -1; - } - if (state.curtoken != id) { - if (id != -1) { - var y = id * TOKEN_HEIGHT - state.listy; - if (y < 0) { - state.listy += y; - y = 0; +function updateCurrentToken() { + drawToken(state.id); + timerCalc(); +} + +function updateProgressBar() { + drawProgressBar(); + timerCalc(); +} + +function drawProgressBar() { + let id = state.id; + if (id != -1) { + if (tokens[id].period > 0) { + let rem = Math.floor((state.hotp.next - Date.now()) / 1000); + if (rem >= 0) { + let y1 = tokenY(id); + let y2 = y1 + TOKEN_HEIGHT - 1; + if (y2 >= AR.y && y1 <= AR.y2) { + // token visible + if ((y2 - 3) <= AR.y2) + { + // progress bar visible + y2 = Math.min(y2, AR.y2); + rem = Math.min(rem, tokens[id].period); + let xr = Math.floor(AR.w * rem / tokens[id].period) + AR.x; + g.setColor(g.theme.fgH) + .setBgColor(g.theme.bgH) + .fillRect(AR.x, y2 - 3, xr, y2) + .clearRect(xr + 1, y2 - 3, AR.x2, y2); + } + } else { + // token not visible + state.id = -1; } - y += TOKEN_HEIGHT; - if (y > Bangle.appRect.h) { - state.listy += (y - Bangle.appRect.h); - } - state.otp = ""; } - state.nextTime = 0; - state.curtoken = id; - state.hide = 2; } } - draw(); +} + +// id = token ID number (0...) +function drawToken(id) { + var x1 = AR.x; + var y1 = tokenY(id); + var x2 = AR.x2; + var y2 = y1 + TOKEN_HEIGHT - 1; + var adj, lbl; + g.setClipRect(x1, Math.max(y1, AR.y), x2, Math.min(y2, AR.y2)); + lbl = tokens[id].label.substr(0, 10); + if (id === state.id) { + g.setColor(g.theme.fgH) + .setBgColor(g.theme.bgH); + } else { + g.setColor(g.theme.fg) + .setBgColor(g.theme.bg); + } + if (id == state.id && state.hotp.hotp != "") { + // small label centered just below top line + g.setFont("Vector", TOKEN_EXTRA_HEIGHT) + .setFontAlign(0, -1, 0); + adj = y1; + } else { + // large label centered in box + sizeFont("l" + id, lbl, AR.w); + g.setFontAlign(0, 0, 0); + adj = half(y1 + y2); + } + g.clearRect(x1, y1, x2, y2) + .drawString(lbl, half(x1 + x2), adj, false); + if (id == state.id && state.hotp.hotp != "") { + adj = 0; + if (tokens[id].period <= 0) { + // counter - draw triangle as swipe hint + let yc = half(y1 + y2); + adj = COUNTER_TRIANGLE_SIZE; + g.fillPoly([AR.x, yc, AR.x + adj, yc - adj, AR.x + adj, yc + adj]); + adj += 2; + } + // digits just below label + x1 = half(x1 + adj + x2); + y1 += TOKEN_EXTRA_HEIGHT; + if (state.hotp.hotp == CALCULATING) { + sizeFont("c", CALCULATING, AR.w - adj); + g.drawString(CALCULATING, x1, y1, false) + .flip(); + state.hotp = hotp(tokens[id]); + g.clearRect(AR.x + adj, y1, AR.x2, y2); + } + sizeFont("d" + id, state.hotp.hotp, AR.w - adj); + g.drawString(state.hotp.hotp, x1, y1, false); + if (tokens[id].period > 0) { + drawProgressBar(); + } + } + g.setClipRect(0, 0, g.getWidth(), g.getHeight()); +} + +function startupDraw() { + if (tokens.length > 0) { + let id = 0; + let y = tokenY(id); + while (id < tokens.length && y < AR.y2) { + if ((y + TOKEN_HEIGHT) > AR.y) { + drawToken(id); + } + id++; + y += TOKEN_HEIGHT; + } + } else { + let x = AR.x + half(AR.w); + let y = AR.y + half(AR.h); + g.setFont("Vector", TOKEN_DIGITS_HEIGHT) + .setFontAlign(0, 0, 0) + .drawString(NO_TOKENS, x, y, false); + } + timerCalc(); } function onDrag(e) { - if (e.b != 0 && e.x < g.getWidth() && e.y < g.getHeight() && e.dy != 0) { - var y = Math.max(0, Math.min(state.listy - e.dy, tokens.length * TOKEN_HEIGHT - Bangle.appRect.h)); + state.cnt = 1; + if (e.b != 0 && e.dy != 0) { + var y = E.clip(state.listy - e.dy, 0, tokens.length * TOKEN_HEIGHT - AR.h); if (state.listy != y) { var id, dy = state.listy - y; state.listy = y; - g.setClipRect(Bangle.appRect.x,Bangle.appRect.y,Bangle.appRect.x2,Bangle.appRect.y2) + g.setClipRect(AR.x, AR.y, AR.x2, AR.y2) .scroll(0, dy); if (dy > 0) { id = Math.floor((state.listy + dy) / TOKEN_HEIGHT); - y = id * TOKEN_HEIGHT + Bangle.appRect.y - state.listy; + y = tokenY(id + 1); do { - drawToken(id, {x:Bangle.appRect.x, y:y, w:Bangle.appRect.w, h:TOKEN_HEIGHT}); + drawToken(id); id--; y -= TOKEN_HEIGHT; - } while (y > 0); + } while (y > AR.y); } if (dy < 0) { - id = Math.floor((state.listy + dy + Bangle.appRect.h) / TOKEN_HEIGHT); - y = id * TOKEN_HEIGHT + Bangle.appRect.y - state.listy; - while (y < Bangle.appRect.y2) { - drawToken(id, {x:Bangle.appRect.x, y:y, w:Bangle.appRect.w, h:TOKEN_HEIGHT}); + id = Math.floor((state.listy + dy + AR.h) / TOKEN_HEIGHT); + y = tokenY(id); + while (y < AR.y2) { + drawToken(id); id++; y += TOKEN_HEIGHT; } } } } + if (e.b == 0) { + timerCalc(); + } +} + +function changeId(id) { + if (id != state.id) { + state.hotp.hotp = CALCULATING; + let pid = state.id; + state.id = id; + if (pid != -1) { + drawToken(pid); + } + if (id != -1) { + drawToken( id); + } + timerCalc(); + } +} + +function onTouch(zone, e) { + state.cnt = 1; + if (e) { + var id = Math.floor((state.listy + e.y - AR.y) / TOKEN_HEIGHT); + if (id == state.id || tokens.length == 0 || id >= tokens.length) { + id = -1; + } + if (state.id != id) { + if (id != -1) { + // scroll token into view if necessary + var fakee = {b:1,x:0,y:0,dx:0,dy:0}; + var y = id * TOKEN_HEIGHT - state.listy; + if (y < 0) { + fakee.dy -= y; + y = 0; + } + y += TOKEN_HEIGHT; + if (y > AR.h) { + fakee.dy -= (y - AR.h); + } + onDrag(fakee); + } + changeId(id); + } + } } function onSwipe(e) { + state.cnt = 1; + let id = state.id; if (e == 1) { exitApp(); } - if (e == -1 && state.curtoken != -1 && tokens[state.curtoken].period <= 0) { - tokens[state.curtoken].period--; + if (e == -1 && id != -1 && tokens[id].period <= 0) { + tokens[id].period--; let newsettings={tokens:tokens,misc:settings.misc}; require("Storage").writeJSON("authentiwatch.json", newsettings); - state.nextTime = 0; - state.otp = ""; - state.hide = 2; + state.hotp.hotp = CALCULATING; + drawToken(id); } - draw(); } function bangle1Btn(e) { + state.cnt = 1; if (tokens.length > 0) { - if (state.curtoken == -1) { - state.curtoken = state.prevcur; - } else { - switch (e) { - case -1: state.curtoken--; break; - case 1: state.curtoken++; break; - } + var id = state.id; + switch (e) { + case -1: id--; break; + case 1: id++; break; } - state.curtoken = Math.max(state.curtoken, 0); - state.curtoken = Math.min(state.curtoken, tokens.length - 1); - state.listy = state.curtoken * TOKEN_HEIGHT; - state.listy -= (Bangle.appRect.h - TOKEN_HEIGHT) / 2; - state.listy = Math.min(state.listy, tokens.length * TOKEN_HEIGHT - Bangle.appRect.h); - state.listy = Math.max(state.listy, 0); - var fakee = {}; - fakee.y = state.curtoken * TOKEN_HEIGHT - state.listy + Bangle.appRect.y; - state.curtoken = -1; - state.nextTime = 0; - onTouch(0, fakee); + id = E.clip(id, 0, tokens.length - 1); + var fakee = {b:1,x:0,y:0,dx:0}; + fakee.dy = state.listy - E.clip(id * TOKEN_HEIGHT - half(AR.h - TOKEN_HEIGHT), 0, tokens.length * TOKEN_HEIGHT - AR.h); + onDrag(fakee); + changeId(id); } else { - draw(); // resets idle timer + timerCalc(); } } @@ -356,8 +413,7 @@ if (typeof BTN2 == 'number') { setWatch(function(){exitApp(); }, BTN1, {edge:"falling", debounce:50}); } Bangle.loadWidgets(); - -// Clear the screen once, at startup -g.clear(); -draw(); +const AR = Bangle.appRect; +g.clear(); // Clear the screen once, at startup +startupDraw(); Bangle.drawWidgets(); From ed36b7286e2b8ff5505a504df0e429c898233f99 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Sun, 20 Mar 2022 23:11:18 +0800 Subject: [PATCH 12/36] Update app.js Workaround Bangle1 issues --- apps/authentiwatch/app.js | 97 ++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 48 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 2734a064a..8e25e5ef2 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -135,34 +135,32 @@ function timerCalc() { let timerfn = exitApp; let timerdly = 10000; let id = state.id; - if (id != -1) { - if (state.hotp.hotp != "") { - if (tokens[id].period > 0) { - // timed HOTP - if (state.hotp.next < Date.now()) { - if (state.cnt > 0) { - --state.cnt; - state.hotp = hotp(tokens[id]); - } else { - state.hotp.hotp = ""; - } - timerdly = 1; - timerfn = updateCurrentToken; - } else { - timerdly = 1000; - timerfn = updateProgressBar; - } - } else { - // counter HOTP + if (id != -1 && state.hotp.hotp != "") { + if (tokens[id].period > 0) { + // timed HOTP + if (state.hotp.next < Date.now()) { if (state.cnt > 0) { --state.cnt; - timerdly = 30000; + state.hotp = hotp(tokens[id]); } else { state.hotp.hotp = ""; - timerdly = 1; } + timerdly = 1; timerfn = updateCurrentToken; + } else { + timerdly = 1000; + timerfn = updateProgressBar; } + } else { + // counter HOTP + if (state.cnt > 0) { + --state.cnt; + timerdly = 30000; + } else { + state.hotp.hotp = ""; + timerdly = 1; + } + timerfn = updateCurrentToken; } } if (state.drawtimer) { @@ -183,29 +181,27 @@ function updateProgressBar() { function drawProgressBar() { let id = state.id; - if (id != -1) { - if (tokens[id].period > 0) { - let rem = Math.floor((state.hotp.next - Date.now()) / 1000); - if (rem >= 0) { - let y1 = tokenY(id); - let y2 = y1 + TOKEN_HEIGHT - 1; - if (y2 >= AR.y && y1 <= AR.y2) { - // token visible - if ((y2 - 3) <= AR.y2) - { - // progress bar visible - y2 = Math.min(y2, AR.y2); - rem = Math.min(rem, tokens[id].period); - let xr = Math.floor(AR.w * rem / tokens[id].period) + AR.x; - g.setColor(g.theme.fgH) - .setBgColor(g.theme.bgH) - .fillRect(AR.x, y2 - 3, xr, y2) - .clearRect(xr + 1, y2 - 3, AR.x2, y2); - } - } else { - // token not visible - state.id = -1; + if (id != -1 && tokens[id].period > 0) { + let rem = Math.floor((state.hotp.next - Date.now()) / 1000); + if (rem >= 0) { + let y1 = tokenY(id); + let y2 = y1 + TOKEN_HEIGHT - 1; + if (y2 >= AR.y && y1 <= AR.y2) { + // token visible + if ((y2 - 3) <= AR.y2) + { + // progress bar visible + y2 = Math.min(y2, AR.y2); + rem = Math.min(rem, tokens[id].period); + let xr = Math.floor(AR.w * rem / tokens[id].period) + AR.x; + g.setColor(g.theme.fgH) + .setBgColor(g.theme.bgH) + .fillRect(AR.x, y2 - 3, xr, y2) + .clearRect(xr + 1, y2 - 3, AR.x2, y2); } + } else { + // token not visible + state.id = -1; } } } @@ -265,10 +261,10 @@ function drawToken(id) { drawProgressBar(); } } - g.setClipRect(0, 0, g.getWidth(), g.getHeight()); + g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1); } -function startupDraw() { +function drawAll() { if (tokens.length > 0) { let id = 0; let y = tokenY(id); @@ -391,8 +387,13 @@ function bangle1Btn(e) { id = E.clip(id, 0, tokens.length - 1); var fakee = {b:1,x:0,y:0,dx:0}; fakee.dy = state.listy - E.clip(id * TOKEN_HEIGHT - half(AR.h - TOKEN_HEIGHT), 0, tokens.length * TOKEN_HEIGHT - AR.h); - onDrag(fakee); - changeId(id); + //onDrag(fakee); + //changeId(id); + // onDrag() (specifically g.scroll()) doesn't appear to work with the Bangle1 + state.id = id; + state.hotp.hotp = CALCULATING; + state.listy -= fakee.dy; + drawAll(); } else { timerCalc(); } @@ -415,5 +416,5 @@ if (typeof BTN2 == 'number') { Bangle.loadWidgets(); const AR = Bangle.appRect; g.clear(); // Clear the screen once, at startup -startupDraw(); +drawAll(); Bangle.drawWidgets(); From 9886125fbd796feabfea5beef484487c67324e63 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Tue, 22 Mar 2022 23:16:17 +0800 Subject: [PATCH 13/36] Update app.js Refactoring and extra range checks. --- apps/authentiwatch/app.js | 117 ++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 67 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 8e25e5ef2..b2608a5dc 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -2,6 +2,7 @@ const COUNTER_TRIANGLE_SIZE = 10; const TOKEN_EXTRA_HEIGHT = 16; var TOKEN_DIGITS_HEIGHT = 30; var TOKEN_HEIGHT = TOKEN_DIGITS_HEIGHT + TOKEN_EXTRA_HEIGHT; +const SETTINGS = "authentiwatch.json"; // Hash functions const crypto = require("crypto"); const algos = { @@ -15,7 +16,7 @@ const NOT_SUPPORTED = /*LANG*/"Not supported"; // sample settings: // {tokens:[{"algorithm":"SHA1","digits":6,"period":30,"issuer":"","account":"","secret":"Bbb","label":"Aaa"}],misc:{}} -var settings = require("Storage").readJSON("authentiwatch.json", true) || {tokens:[],misc:{}}; +var settings = require("Storage").readJSON(SETTINGS, true) || {tokens:[],misc:{}}; if (settings.data ) tokens = settings.data ; /* v0.02 settings */ if (settings.tokens) tokens = settings.tokens; /* v0.03+ settings */ @@ -134,14 +135,13 @@ half = n => Math.floor(n / 2); function timerCalc() { let timerfn = exitApp; let timerdly = 10000; - let id = state.id; - if (id != -1 && state.hotp.hotp != "") { - if (tokens[id].period > 0) { + if (state.id != -1 && state.hotp.hotp != "") { + if (tokens[state.id].period > 0) { // timed HOTP if (state.hotp.next < Date.now()) { if (state.cnt > 0) { - --state.cnt; - state.hotp = hotp(tokens[id]); + state.cnt--; + state.hotp = hotp(tokens[state.id]); } else { state.hotp.hotp = ""; } @@ -154,7 +154,7 @@ function timerCalc() { } else { // counter HOTP if (state.cnt > 0) { - --state.cnt; + state.cnt--; timerdly = 30000; } else { state.hotp.hotp = ""; @@ -215,7 +215,7 @@ function drawToken(id) { var y2 = y1 + TOKEN_HEIGHT - 1; var adj, lbl; g.setClipRect(x1, Math.max(y1, AR.y), x2, Math.min(y2, AR.y2)); - lbl = tokens[id].label.substr(0, 10); + lbl = (id >= 0 && id < tokens.length) ? tokens[id].label.substr(0, 10) : ""; if (id === state.id) { g.setColor(g.theme.fgH) .setBgColor(g.theme.bgH); @@ -264,31 +264,24 @@ function drawToken(id) { g.setClipRect(0, 0, g.getWidth() - 1, g.getHeight() - 1); } -function drawAll() { - if (tokens.length > 0) { - let id = 0; - let y = tokenY(id); - while (id < tokens.length && y < AR.y2) { - if ((y + TOKEN_HEIGHT) > AR.y) { - drawToken(id); - } - id++; - y += TOKEN_HEIGHT; +function changeId(id) { + if (id != state.id) { + state.hotp.hotp = CALCULATING; + let pid = state.id; + state.id = id; + if (pid != -1) { + drawToken(pid); + } + if (id != -1) { + drawToken( id); } - } else { - let x = AR.x + half(AR.w); - let y = AR.y + half(AR.h); - g.setFont("Vector", TOKEN_DIGITS_HEIGHT) - .setFontAlign(0, 0, 0) - .drawString(NO_TOKENS, x, y, false); } - timerCalc(); } function onDrag(e) { state.cnt = 1; if (e.b != 0 && e.dy != 0) { - var y = E.clip(state.listy - e.dy, 0, tokens.length * TOKEN_HEIGHT - AR.h); + var y = E.clip(state.listy - E.clip(e.dy, -AR.h, AR.h), 0, Math.max(0, tokens.length * TOKEN_HEIGHT - AR.h)); if (state.listy != y) { var id, dy = state.listy - y; state.listy = y; @@ -319,21 +312,6 @@ function onDrag(e) { } } -function changeId(id) { - if (id != state.id) { - state.hotp.hotp = CALCULATING; - let pid = state.id; - state.id = id; - if (pid != -1) { - drawToken(pid); - } - if (id != -1) { - drawToken( id); - } - timerCalc(); - } -} - function onTouch(zone, e) { state.cnt = 1; if (e) { @@ -344,36 +322,39 @@ function onTouch(zone, e) { if (state.id != id) { if (id != -1) { // scroll token into view if necessary - var fakee = {b:1,x:0,y:0,dx:0,dy:0}; + var dy = 0; var y = id * TOKEN_HEIGHT - state.listy; if (y < 0) { - fakee.dy -= y; + dy -= y; y = 0; } y += TOKEN_HEIGHT; if (y > AR.h) { - fakee.dy -= (y - AR.h); + dy -= (y - AR.h); } - onDrag(fakee); + onDrag({b:1, dy:dy}); } changeId(id); } } + timerCalc(); } function onSwipe(e) { state.cnt = 1; - let id = state.id; - if (e == 1) { + switch (e) { + case 1: exitApp(); + break; + case -1: + if (state.id != -1 && tokens[state.id].period <= 0) { + tokens[state.id].period--; + require("Storage").writeJSON(SETTINGS, {tokens:tokens, misc:settings.misc}); + state.hotp.hotp = CALCULATING; + drawToken(state.id); + } } - if (e == -1 && id != -1 && tokens[id].period <= 0) { - tokens[id].period--; - let newsettings={tokens:tokens,misc:settings.misc}; - require("Storage").writeJSON("authentiwatch.json", newsettings); - state.hotp.hotp = CALCULATING; - drawToken(id); - } + timerCalc(); } function bangle1Btn(e) { @@ -385,18 +366,11 @@ function bangle1Btn(e) { case 1: id++; break; } id = E.clip(id, 0, tokens.length - 1); - var fakee = {b:1,x:0,y:0,dx:0}; - fakee.dy = state.listy - E.clip(id * TOKEN_HEIGHT - half(AR.h - TOKEN_HEIGHT), 0, tokens.length * TOKEN_HEIGHT - AR.h); - //onDrag(fakee); - //changeId(id); - // onDrag() (specifically g.scroll()) doesn't appear to work with the Bangle1 - state.id = id; - state.hotp.hotp = CALCULATING; - state.listy -= fakee.dy; - drawAll(); - } else { - timerCalc(); + var dy = state.listy - E.clip(id * TOKEN_HEIGHT - half(AR.h - TOKEN_HEIGHT), 0, Math.max(0, tokens.length * TOKEN_HEIGHT - AR.h)); + onDrag({b:1, dy:dy}); + changeId(id); } + timerCalc(); } function exitApp() { @@ -415,6 +389,15 @@ if (typeof BTN2 == 'number') { } Bangle.loadWidgets(); const AR = Bangle.appRect; -g.clear(); // Clear the screen once, at startup -drawAll(); +// draw the initial display +g.clear(); +if (tokens.length > 0) { + state.listy = AR.h; + onDrag({b:1, dy:AR.h}); +} else { + g.setFont("Vector", TOKEN_DIGITS_HEIGHT) + .setFontAlign(0, 0, 0) + .drawString(NO_TOKENS, AR.x + half(AR.w), AR.y + half(AR.h), false); +} +timerCalc(); Bangle.drawWidgets(); From 1d7c090c21a10aad2dbc64ebb9c95de05d432bdb Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Tue, 22 Mar 2022 23:26:50 +0800 Subject: [PATCH 14/36] Update app.js Update progress bar on Bangle1 button presses. Clean up timer before exiting. --- apps/authentiwatch/app.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index b2608a5dc..58d712451 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -369,11 +369,15 @@ function bangle1Btn(e) { var dy = state.listy - E.clip(id * TOKEN_HEIGHT - half(AR.h - TOKEN_HEIGHT), 0, Math.max(0, tokens.length * TOKEN_HEIGHT - AR.h)); onDrag({b:1, dy:dy}); changeId(id); + drawProgressBar(); } timerCalc(); } function exitApp() { + if (state.drawtimer) { + clearTimeout(state.drawtimer); + } Bangle.showLauncher(); } From 94207739c08a368b7f76c38eb9f3879757a77f94 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 23 Mar 2022 14:22:17 +0800 Subject: [PATCH 15/36] Update app.js Refactoring --- apps/authentiwatch/app.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 58d712451..aae570e13 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -16,7 +16,7 @@ const NOT_SUPPORTED = /*LANG*/"Not supported"; // sample settings: // {tokens:[{"algorithm":"SHA1","digits":6,"period":30,"issuer":"","account":"","secret":"Bbb","label":"Aaa"}],misc:{}} -var settings = require("Storage").readJSON(SETTINGS, true) || {tokens:[],misc:{}}; +var settings = require("Storage").readJSON(SETTINGS, true) || {tokens:[], misc:{}}; if (settings.data ) tokens = settings.data ; /* v0.02 settings */ if (settings.tokens) tokens = settings.tokens; /* v0.03+ settings */ @@ -106,9 +106,9 @@ function hotp(token) { } // Tokens are displayed in three states: -// 1. Unselected (state.id==-1) -// 2. Selected, inactive (no code) (state.id!=-1,state.hotp.hotp=="") -// 3. Selected, active (code showing) (state.id!=-1,state.hotp.hotp!="") +// 1. Unselected (state.id<0) +// 2. Selected, inactive (no code) (state.id>=0,state.hotp.hotp=="") +// 3. Selected, active (code showing) (state.id>=0,state.hotp.hotp!="") var fontszCache = {}; var state = { listy:0, // list scroll position @@ -135,7 +135,7 @@ half = n => Math.floor(n / 2); function timerCalc() { let timerfn = exitApp; let timerdly = 10000; - if (state.id != -1 && state.hotp.hotp != "") { + if (state.id >= 0 && state.hotp.hotp != "") { if (tokens[state.id].period > 0) { // timed HOTP if (state.hotp.next < Date.now()) { @@ -181,7 +181,7 @@ function updateProgressBar() { function drawProgressBar() { let id = state.id; - if (id != -1 && tokens[id].period > 0) { + if (id >= 0 && tokens[id].period > 0) { let rem = Math.floor((state.hotp.next - Date.now()) / 1000); if (rem >= 0) { let y1 = tokenY(id); @@ -269,10 +269,10 @@ function changeId(id) { state.hotp.hotp = CALCULATING; let pid = state.id; state.id = id; - if (pid != -1) { + if (pid >= 0) { drawToken(pid); } - if (id != -1) { + if (id >= 0) { drawToken( id); } } @@ -320,7 +320,7 @@ function onTouch(zone, e) { id = -1; } if (state.id != id) { - if (id != -1) { + if (id >= 0) { // scroll token into view if necessary var dy = 0; var y = id * TOKEN_HEIGHT - state.listy; @@ -347,7 +347,7 @@ function onSwipe(e) { exitApp(); break; case -1: - if (state.id != -1 && tokens[state.id].period <= 0) { + if (state.id >= 0 && tokens[state.id].period <= 0) { tokens[state.id].period--; require("Storage").writeJSON(SETTINGS, {tokens:tokens, misc:settings.misc}); state.hotp.hotp = CALCULATING; From b26d3416092f8ad12fa790b012a3336a93e8a18a Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 23 Mar 2022 16:13:57 +0800 Subject: [PATCH 16/36] Update app.js Refactoring --- apps/authentiwatch/app.js | 46 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index aae570e13..5ea8dd476 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -182,7 +182,7 @@ function updateProgressBar() { function drawProgressBar() { let id = state.id; if (id >= 0 && tokens[id].period > 0) { - let rem = Math.floor((state.hotp.next - Date.now()) / 1000); + let rem = Math.min(tokens[id].period, Math.floor((state.hotp.next - Date.now()) / 1000)); if (rem >= 0) { let y1 = tokenY(id); let y2 = y1 + TOKEN_HEIGHT - 1; @@ -192,7 +192,6 @@ function drawProgressBar() { { // progress bar visible y2 = Math.min(y2, AR.y2); - rem = Math.min(rem, tokens[id].period); let xr = Math.floor(AR.w * rem / tokens[id].period) + AR.x; g.setColor(g.theme.fgH) .setBgColor(g.theme.bgH) @@ -209,13 +208,13 @@ function drawProgressBar() { // id = token ID number (0...) function drawToken(id) { - var x1 = AR.x; - var y1 = tokenY(id); - var x2 = AR.x2; - var y2 = y1 + TOKEN_HEIGHT - 1; - var adj, lbl; + let x1 = AR.x; + let y1 = tokenY(id); + let x2 = AR.x2; + let y2 = y1 + TOKEN_HEIGHT - 1; + let lbl = (id >= 0 && id < tokens.length) ? tokens[id].label.substr(0, 10) : ""; + let adj; g.setClipRect(x1, Math.max(y1, AR.y), x2, Math.min(y2, AR.y2)); - lbl = (id >= 0 && id < tokens.length) ? tokens[id].label.substr(0, 10) : ""; if (id === state.id) { g.setColor(g.theme.fgH) .setBgColor(g.theme.bgH); @@ -281,9 +280,9 @@ function changeId(id) { function onDrag(e) { state.cnt = 1; if (e.b != 0 && e.dy != 0) { - var y = E.clip(state.listy - E.clip(e.dy, -AR.h, AR.h), 0, Math.max(0, tokens.length * TOKEN_HEIGHT - AR.h)); + let y = E.clip(state.listy - E.clip(e.dy, -AR.h, AR.h), 0, Math.max(0, tokens.length * TOKEN_HEIGHT - AR.h)); if (state.listy != y) { - var id, dy = state.listy - y; + let id, dy = state.listy - y; state.listy = y; g.setClipRect(AR.x, AR.y, AR.x2, AR.y2) .scroll(0, dy); @@ -315,15 +314,15 @@ function onDrag(e) { function onTouch(zone, e) { state.cnt = 1; if (e) { - var id = Math.floor((state.listy + e.y - AR.y) / TOKEN_HEIGHT); + let id = Math.floor((state.listy + e.y - AR.y) / TOKEN_HEIGHT); if (id == state.id || tokens.length == 0 || id >= tokens.length) { id = -1; } if (state.id != id) { if (id >= 0) { // scroll token into view if necessary - var dy = 0; - var y = id * TOKEN_HEIGHT - state.listy; + let dy = 0; + let y = id * TOKEN_HEIGHT - state.listy; if (y < 0) { dy -= y; y = 0; @@ -357,17 +356,16 @@ function onSwipe(e) { timerCalc(); } -function bangle1Btn(e) { +function bangleBtn(e) { state.cnt = 1; if (tokens.length > 0) { - var id = state.id; + let id = state.id; switch (e) { case -1: id--; break; case 1: id++; break; } id = E.clip(id, 0, tokens.length - 1); - var dy = state.listy - E.clip(id * TOKEN_HEIGHT - half(AR.h - TOKEN_HEIGHT), 0, Math.max(0, tokens.length * TOKEN_HEIGHT - AR.h)); - onDrag({b:1, dy:dy}); + onDrag({b:1, dy:state.listy - E.clip(id * TOKEN_HEIGHT - half(AR.h - TOKEN_HEIGHT), 0, Math.max(0, tokens.length * TOKEN_HEIGHT - AR.h))}); changeId(id); drawProgressBar(); } @@ -384,12 +382,14 @@ function exitApp() { Bangle.on('touch', onTouch); Bangle.on('drag' , onDrag ); Bangle.on('swipe', onSwipe); -if (typeof BTN2 == 'number') { - setWatch(function(){bangle1Btn(-1);}, BTN1, {edge:"rising" , debounce:50, repeat:true}); - setWatch(function(){exitApp(); }, BTN2, {edge:"falling", debounce:50}); - setWatch(function(){bangle1Btn( 1);}, BTN3, {edge:"rising" , debounce:50, repeat:true}); -} else { - setWatch(function(){exitApp(); }, BTN1, {edge:"falling", debounce:50}); +if (typeof BTN1 == 'number') { + if (typeof BTN2 == 'number' && typeof BTN3 == 'number') { + setWatch(()=>bangleBtn(-1), BTN1, {edge:"rising" , debounce:50, repeat:true}); + setWatch(()=>exitApp() , BTN2, {edge:"falling", debounce:50}); + setWatch(()=>bangleBtn( 1), BTN3, {edge:"rising" , debounce:50, repeat:true}); + } else { + setWatch(()=>exitApp() , BTN1, {edge:"falling", debounce:50}); + } } Bangle.loadWidgets(); const AR = Bangle.appRect; From bf30b35ac07fc3bde3299e0dd5dc6dc2886c9d82 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 23 Mar 2022 16:18:21 +0800 Subject: [PATCH 17/36] Update app.js Fix progress bar drawing. --- apps/authentiwatch/app.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 5ea8dd476..69e4a1ae1 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -188,15 +188,16 @@ function drawProgressBar() { let y2 = y1 + TOKEN_HEIGHT - 1; if (y2 >= AR.y && y1 <= AR.y2) { // token visible - if ((y2 - 3) <= AR.y2) + y1 = y2 - 3; + if (y1 <= AR.y2) { // progress bar visible y2 = Math.min(y2, AR.y2); let xr = Math.floor(AR.w * rem / tokens[id].period) + AR.x; g.setColor(g.theme.fgH) .setBgColor(g.theme.bgH) - .fillRect(AR.x, y2 - 3, xr, y2) - .clearRect(xr + 1, y2 - 3, AR.x2, y2); + .fillRect(AR.x, y1, xr, y2) + .clearRect(xr + 1, y1, AR.x2, y2); } } else { // token not visible From 47332eba50f743ccfb7e85439cc41f5321bf910f Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 23 Mar 2022 16:26:48 +0800 Subject: [PATCH 18/36] Update app.js Simplify. --- apps/authentiwatch/app.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 69e4a1ae1..3920ebddd 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -360,12 +360,7 @@ function onSwipe(e) { function bangleBtn(e) { state.cnt = 1; if (tokens.length > 0) { - let id = state.id; - switch (e) { - case -1: id--; break; - case 1: id++; break; - } - id = E.clip(id, 0, tokens.length - 1); + let id = E.clip(state.id + e, 0, tokens.length - 1); onDrag({b:1, dy:state.listy - E.clip(id * TOKEN_HEIGHT - half(AR.h - TOKEN_HEIGHT), 0, Math.max(0, tokens.length * TOKEN_HEIGHT - AR.h))}); changeId(id); drawProgressBar(); From 05b75d2241844d4858992b46335f1d141b680f1a Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 23 Mar 2022 16:30:24 +0800 Subject: [PATCH 19/36] Update app.js Fix missing break. --- apps/authentiwatch/app.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 3920ebddd..3a452b6e9 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -353,6 +353,7 @@ function onSwipe(e) { state.hotp.hotp = CALCULATING; drawToken(state.id); } + break; } timerCalc(); } From b3cc125a3f733b8f8a9cd591d537f947ca2488b1 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 23 Mar 2022 16:36:42 +0800 Subject: [PATCH 20/36] Update app.js Use let instead of var. --- apps/authentiwatch/app.js | 41 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 3a452b6e9..5198254af 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -22,8 +22,8 @@ if (settings.tokens) tokens = settings.tokens; /* v0.03+ settings */ function b32decode(seedstr) { // RFC4648 - var buf = 0, bitcount = 0, retstr = ""; - for (var c of seedstr.toUpperCase()) { + let buf = 0, bitcount = 0, retstr = ""; + for (let c of seedstr.toUpperCase()) { if (c == '0') c = 'O'; if (c == '1') c = 'I'; if (c == '8') c = 'B'; @@ -39,63 +39,62 @@ function b32decode(seedstr) { } } } - var retbuf = new Uint8Array(retstr.length); - for (var i in retstr) { + let retbuf = new Uint8Array(retstr.length); + for (let i in retstr) { retbuf[i] = retstr.charCodeAt(i); } return retbuf; } function hmac(key, message, algo) { - var a = algos[algo.toUpperCase()]; + let a = algos[algo.toUpperCase()]; // RFC2104 if (key.length > a.blksz) { key = a.sha(key); } - var istr = new Uint8Array(a.blksz + message.length); - var ostr = new Uint8Array(a.blksz + a.retsz); - for (var i = 0; i < a.blksz; ++i) { - var c = (i < key.length) ? key[i] : 0; + let istr = new Uint8Array(a.blksz + message.length); + let ostr = new Uint8Array(a.blksz + a.retsz); + for (let i = 0; i < a.blksz; ++i) { + let c = (i < key.length) ? key[i] : 0; istr[i] = c ^ 0x36; ostr[i] = c ^ 0x5C; } istr.set(message, a.blksz); ostr.set(a.sha(istr), a.blksz); - var ret = a.sha(ostr); + let ret = a.sha(ostr); // RFC4226 dynamic truncation - var v = new DataView(ret, ret[ret.length - 1] & 0x0F, 4); + let v = new DataView(ret, ret[ret.length - 1] & 0x0F, 4); return v.getUint32(0) & 0x7FFFFFFF; } function formatOtp(otp, digits) { // add 0 padding - var ret = "" + otp % Math.pow(10, digits); + let ret = "" + otp % Math.pow(10, digits); while (ret.length < digits) { ret = "0" + ret; } // add a space after every 3rd or 4th digit - var re = (digits % 3 == 0 || (digits % 3 >= digits % 4 && digits % 4 != 0)) ? "" : "."; + let re = (digits % 3 == 0 || (digits % 3 >= digits % 4 && digits % 4 != 0)) ? "" : "."; return ret.replace(new RegExp("(..." + re + ")", "g"), "$1 ").trim(); } function hotp(token) { - var d = Date.now(); - var tick, next; + let d = Date.now(); + let tick, next; if (token.period > 0) { // RFC6238 - timed - var seconds = Math.floor(d / 1000); - tick = Math.floor(seconds / token.period); + tick = Math.floor(Math.floor(d / 1000) / token.period); next = (tick + 1) * token.period * 1000; } else { // RFC4226 - counter tick = -token.period; next = d + 30000; } - var msg = new Uint8Array(8); - var v = new DataView(msg.buffer); + let msg = new Uint8Array(8); + let v = new DataView(msg.buffer); v.setUint32(0, tick >> 16 >> 16); v.setUint32(4, tick & 0xFFFFFFFF); - var ret; + let ret; try { ret = hmac(b32decode(token.secret), msg, token.algorithm); ret = formatOtp(ret, token.digits); @@ -117,7 +116,7 @@ var state = { }; function sizeFont(id, txt, w) { - var sz = fontszCache[id]; + let sz = fontszCache[id]; if (sz) { g.setFont("Vector", sz); } else { From 6158d76f176315b014498e997a93a6ad1a267b30 Mon Sep 17 00:00:00 2001 From: Micha <97034053+foostuff@users.noreply.github.com> Date: Wed, 23 Mar 2022 14:37:44 +0100 Subject: [PATCH 21/36] configurable drag gestures --- apps/clockcal/app.js | 172 ++++++++++++++++++++++++------------------- 1 file changed, 95 insertions(+), 77 deletions(-) diff --git a/apps/clockcal/app.js b/apps/clockcal/app.js index 86fa0815a..5e8c7f796 100644 --- a/apps/clockcal/app.js +++ b/apps/clockcal/app.js @@ -4,12 +4,13 @@ var s = Object.assign({ CAL_ROWS: 4, //number of calendar rows.(weeks) Shouldn't exceed 5 when using widgets. BUZZ_ON_BT: true, //2x slow buzz on disconnect, 2x fast buzz on connect. Will be extra widget eventually MODE24: true, //24h mode vs 12h mode - FIRSTDAYOFFSET: 6, //First day of the week: 0-6: Sun, Sat, Fri, Thu, Wed, Tue, Mon + FIRSTDAY: 6, //First day of the week: mo, tu, we, th, fr, sa, su REDSUN: true, // Use red color for sunday? REDSAT: true, // Use red color for saturday? - DRAGENABLED: true, - DRAGMUSIC: true, - DRAGMESSAGES: true + DRAGDOWN: "[AI:messg]", + DRAGRIGHT: "[AI:music]", + DRAGLEFT: "[ignore]", + DRAGUP: "[calend.]" }, require('Storage').readJSON("clockcal.json", true) || {}); const h = g.getHeight(); @@ -27,13 +28,13 @@ var monthOffset = 0; */ function drawFullCalendar(monthOffset) { addMonths = function (_d, _am) { - var ay = 0, m = _d.getMonth(), y = _d.getFullYear(); - while ((m + _am) > 11) { ay++; _am -= 12; } - while ((m + _am) < 0) { ay--; _am += 12; } - n = new Date(_d.getTime()); - n.setMonth(m + _am); - n.setFullYear(y + ay); - return n; + var ay = 0, m = _d.getMonth(), y = _d.getFullYear(); + while ((m + _am) > 11) { ay++; _am -= 12; } + while ((m + _am) < 0) { ay--; _am += 12; } + n = new Date(_d.getTime()); + n.setMonth(m + _am); + n.setFullYear(y + ay); + return n; }; monthOffset = (typeof monthOffset == "undefined") ? 0 : monthOffset; state = "calendar"; @@ -45,22 +46,22 @@ function drawFullCalendar(monthOffset) { if (typeof minuteInterval !== "undefined") clearTimeout(minuteInterval); d = addMonths(Date(), monthOffset); tdy = Date().getDate() + "." + Date().getMonth(); - newmonth=false; + newmonth = false; c_y = 0; g.reset(); g.setBgColor(0); g.clear(); - var prevmonth = addMonths(d, -1) + var prevmonth = addMonths(d, -1); const today = prevmonth.getDate(); var rD = new Date(prevmonth.getTime()); rD.setDate(rD.getDate() - (today - 1)); - const dow = (s.FIRSTDAYOFFSET + rD.getDay()) % 7; + const dow = (s.FIRSTDAY + rD.getDay()) % 7; rD.setDate(rD.getDate() - dow); var rDate = rD.getDate(); bottomrightY = c_y - 3; - clrsun=s.REDSUN?'#f00':'#fff'; - clrsat=s.REDSUN?'#f00':'#fff'; - var fg=[clrsun,'#fff','#fff','#fff','#fff','#fff',clrsat]; + clrsun = s.REDSUN ? '#f00' : '#fff'; + clrsat = s.REDSUN ? '#f00' : '#fff'; + var fg = [clrsun, '#fff', '#fff', '#fff', '#fff', '#fff', clrsat]; for (var y = 1; y <= 11; y++) { bottomrightY += CELL_H; bottomrightX = -2; @@ -69,14 +70,14 @@ function drawFullCalendar(monthOffset) { rMonth = rD.getMonth(); rDate = rD.getDate(); if (tdy == rDate + "." + rMonth) { - caldrawToday(rDate); + caldrawToday(rDate); } else if (rDate == 1) { - caldrawFirst(rDate); + caldrawFirst(rDate); } else { - caldrawNormal(rDate,fg[rD.getDay()]); + caldrawNormal(rDate, fg[rD.getDay()]); } if (newmonth && x == 7) { - caldrawMonth(rDate,monthclr[rMonth % 6],months[rMonth],rD); + caldrawMonth(rDate, monthclr[rMonth % 6], months[rMonth], rD); } rD.setDate(rDate + 1); } @@ -84,7 +85,7 @@ function drawFullCalendar(monthOffset) { delete addMonths; if (DEBUG) console.log("Calendar performance (ms):" + (Date().getTime() - start)); } -function caldrawMonth(rDate,c,m,rD) { +function caldrawMonth(rDate, c, m, rD) { g.setColor(c); g.setFont("Vector", 18); g.setFontAlign(-1, 1, 1); @@ -93,29 +94,29 @@ function caldrawMonth(rDate,c,m,rD) { newmonth = false; } function caldrawToday(rDate) { - g.setFont("Vector", 16); - g.setFontAlign(1, 1); - g.setColor('#0f0'); - g.fillRect(bottomrightX - CELL2_W + 1, bottomrightY - CELL_H - 1, bottomrightX, bottomrightY - 2); - g.setColor('#000'); - g.drawString(rDate, bottomrightX, bottomrightY); + g.setFont("Vector", 16); + g.setFontAlign(1, 1); + g.setColor('#0f0'); + g.fillRect(bottomrightX - CELL2_W + 1, bottomrightY - CELL_H - 1, bottomrightX, bottomrightY - 2); + g.setColor('#000'); + g.drawString(rDate, bottomrightX, bottomrightY); } function caldrawFirst(rDate) { - g.flip(); - g.setFont("Vector", 16); - g.setFontAlign(1, 1); - bottomrightY += 3; - newmonth = true; - g.setColor('#0ff'); - g.fillRect(bottomrightX - CELL2_W + 1, bottomrightY - CELL_H - 1, bottomrightX, bottomrightY - 2); - g.setColor('#000'); - g.drawString(rDate, bottomrightX, bottomrightY); + g.flip(); + g.setFont("Vector", 16); + g.setFontAlign(1, 1); + bottomrightY += 3; + newmonth = true; + g.setColor('#0ff'); + g.fillRect(bottomrightX - CELL2_W + 1, bottomrightY - CELL_H - 1, bottomrightX, bottomrightY - 2); + g.setColor('#000'); + g.drawString(rDate, bottomrightX, bottomrightY); } -function caldrawNormal(rDate,c) { - g.setFont("Vector", 16); - g.setFontAlign(1, 1); - g.setColor(c); - g.drawString(rDate, bottomrightX, bottomrightY);//100 +function caldrawNormal(rDate, c) { + g.setFont("Vector", 16); + g.setFontAlign(1, 1); + g.setColor(c); + g.drawString(rDate, bottomrightX, bottomrightY);//100 } function drawMinutes() { if (DEBUG) console.log("|-->minutes"); @@ -163,7 +164,7 @@ function drawWatch() { g.clear(); drawMinutes(); if (!dimSeconds) drawSeconds(); - const dow = (s.FIRSTDAYOFFSET + d.getDay()) % 7; //MO=0, SU=6 + const dow = (s.FIRSTDAY + d.getDay()) % 7; //MO=0, SU=6 const today = d.getDate(); var rD = new Date(d.getTime()); rD.setDate(rD.getDate() - dow); @@ -205,27 +206,52 @@ function BTevent() { setTimeout(function () { Bangle.buzz(interval); }, interval * 3); } } - +function action(a) { + g.reset(); + if (typeof secondInterval !== "undefined") clearTimeout(secondInterval); + if (DEBUG) console.log("action:" + a); + switch (a) { + case "[ignore]": + break; + case "[calend.]": + drawFullCalendar(); + break; + case "[AI:music]": + l = require("Storage").list(RegExp("music.*app.js")); + if (l.length > 0) { + load(l[0]); + } else E.showAlert("Music app not found", "Not found").then(drawWatch); + break; + case "[AI:messg]": + l = require("Storage").list(RegExp("message.*app.js")); + if (l.length > 0) { + load(l[0]); + } else E.showAlert("Message app not found", "Not found").then(drawWatch); + break; + default: + l = require("Storage").list(RegExp(a + ".app.js")); + if (l.length > 0) { + load(l[0]); + } else E.showAlert(a + ": App not found", "Not found").then(drawWatch); + break; + } +} function input(dir) { - if (s.DRAGENABLED) { - Bangle.buzz(100,1); - console.log("swipe:"+dir); + Bangle.buzz(100, 1); + if (DEBUG) console.log("swipe:" + dir); switch (dir) { case "r": if (state == "calendar") { drawWatch(); } else { - if (s.DRAGMUSIC) { - l=require("Storage").list(RegExp("music.*app")); - if (l.length > 0) { - load(l[0]); - } else Bangle.buzz(3000,1);//not found - } + action(s.DRAGRIGHT); } break; case "l": if (state == "calendar") { drawWatch(); + } else { + action(s.DRAGLEFT); } break; case "d": @@ -233,21 +259,15 @@ function input(dir) { monthOffset--; drawFullCalendar(monthOffset); } else { - if (s.DRAGMESSAGES) { - l=require("Storage").list(RegExp("message.*app")); - if (l.length > 0) { - load(l[0]); - } else Bangle.buzz(3000,1);//not found - } + action(s.DRAGDOWN); } break; case "u": - if (state == "watch") { - state = "calendar"; - drawFullCalendar(0); - } else if (state == "calendar") { + if (state == "calendar") { monthOffset++; drawFullCalendar(monthOffset); + } else { + action(s.DRAGUP); } break; default: @@ -255,26 +275,24 @@ function input(dir) { drawWatch(); } break; + } - } } let drag; Bangle.on("drag", e => { - if (s.DRAGENABLED) { - if (!drag) { - drag = { x: e.x, y: e.y }; - } else if (!e.b) { - const dx = e.x - drag.x, dy = e.y - drag.y; - var dir = "t"; - if (Math.abs(dx) > Math.abs(dy) + 10) { - dir = (dx > 0) ? "r" : "l"; - } else if (Math.abs(dy) > Math.abs(dx) + 10) { - dir = (dy > 0) ? "d" : "u"; - } - drag = null; - input(dir); + if (!drag) { + drag = { x: e.x, y: e.y }; + } else if (!e.b) { + const dx = e.x - drag.x, dy = e.y - drag.y; + var dir = "t"; + if (Math.abs(dx) > Math.abs(dy) + 20) { + dir = (dx > 0) ? "r" : "l"; + } else if (Math.abs(dy) > Math.abs(dx) + 20) { + dir = (dy > 0) ? "d" : "u"; } + drag = null; + input(dir); } }); From d0426a2093caf025b0fdf3b4ae930bca83b9ee75 Mon Sep 17 00:00:00 2001 From: Micha <97034053+foostuff@users.noreply.github.com> Date: Wed, 23 Mar 2022 14:38:42 +0100 Subject: [PATCH 22/36] Configurable drag gestures --- apps/clockcal/settings.js | 65 +++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/apps/clockcal/settings.js b/apps/clockcal/settings.js index c4ec764c9..abedad99b 100644 --- a/apps/clockcal/settings.js +++ b/apps/clockcal/settings.js @@ -1,19 +1,22 @@ (function (back) { var FILE = "clockcal.json"; - - settings = Object.assign({ + defaults={ CAL_ROWS: 4, //number of calendar rows.(weeks) Shouldn't exceed 5 when using widgets. BUZZ_ON_BT: true, //2x slow buzz on disconnect, 2x fast buzz on connect. Will be extra widget eventually MODE24: true, //24h mode vs 12h mode FIRSTDAY: 6, //First day of the week: mo, tu, we, th, fr, sa, su REDSUN: true, // Use red color for sunday? REDSAT: true, // Use red color for saturday? - DRAGENABLED: true, //Enable drag gestures (bigger calendar etc) - DRAGMUSIC: true, //Enable drag down for music (looks for "music*app") - DRAGMESSAGES: true //Enable drag right for messages (looks for "message*app") - }, require('Storage').readJSON(FILE, true) || {}); - + DRAGDOWN: "[AI:messg]", + DRAGRIGHT: "[AI:music]", + DRAGLEFT: "[ignore]", + DRAGUP: "[calend.]" + }; + settings = Object.assign(defaults, require('Storage').readJSON(FILE, true) || {}); + actions = ["[ignore]","[calend.]","[AI:music]","[AI:messg]"]; + require("Storage").list(RegExp(".app.js")).forEach(element => actions.push(element.replace(".app.js",""))); + function writeSettings() { require('Storage').writeJSON(FILE, settings); } @@ -70,27 +73,39 @@ writeSettings(); } }, - 'Swipes (big cal.)?': { - value: settings.DRAGENABLED, - format: v => v ? "On" : "Off", + 'Drag Up ': { + min:0, max:actions.length-1, + value: actions.indexOf(settings.DRAGUP), + format: v => actions[v], onchange: v => { - settings.DRAGENABLED = v; + settings.DRAGUP = actions[v]; writeSettings(); } }, - 'Swipes (music)?': { - value: settings.DRAGMUSIC, - format: v => v ? "On" : "Off", + 'Drag Right': { + min:0, max:actions.length-1, + value: actions.indexOf(settings.DRAGRIGHT), + format: v => actions[v], onchange: v => { - settings.DRAGMUSIC = v; + settings.DRAGRIGHT = actions[v]; writeSettings(); } }, - 'Swipes (messg)?': { - value: settings.DRAGMESSAGES, - format: v => v ? "On" : "Off", + 'Drag Down': { + min:0, max:actions.length-1, + value: actions.indexOf(settings.DRAGDOWN), + format: v => actions[v], onchange: v => { - settings.DRAGMESSAGES = v; + settings.DRGDOWN = actions[v]; + writeSettings(); + } + }, + 'Drag Left': { + min:0, max:actions.length-1, + value: actions.indexOf(settings.DRAGLEFT), + format: v => actions[v], + onchange: v => { + settings.DRAGLEFT = actions[v]; writeSettings(); } }, @@ -100,17 +115,7 @@ format: v => ["No", "Yes"][v], onchange: v => { if (v == 1) { - settings = { - CAL_ROWS: 4, //number of calendar rows.(weeks) Shouldn't exceed 5 when using widgets. - BUZZ_ON_BT: true, //2x slow buzz on disconnect, 2x fast buzz on connect. - MODE24: true, //24h mode vs 12h mode - FIRSTDAY: 6, //First day of the week: mo, tu, we, th, fr, sa, su - REDSUN: true, // Use red color for sunday? - REDSAT: true, // Use red color for saturday? - DRAGENABLED: true, - DRAGMUSIC: true, - DRAGMESSAGES: true - }; + settings = defaults; writeSettings(); load(); } From abf684449780be69a38ad881f9f7c61ed9894a42 Mon Sep 17 00:00:00 2001 From: Micha <97034053+foostuff@users.noreply.github.com> Date: Wed, 23 Mar 2022 14:40:31 +0100 Subject: [PATCH 23/36] increment version --- apps/clockcal/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/clockcal/metadata.json b/apps/clockcal/metadata.json index a42e1ad2e..dde32f746 100644 --- a/apps/clockcal/metadata.json +++ b/apps/clockcal/metadata.json @@ -1,7 +1,7 @@ { "id": "clockcal", "name": "Clock & Calendar", - "version": "0.2", + "version": "0.3", "description": "Clock with Calendar", "readme":"README.md", "icon": "app.png", From 00d74ceef61fb3596b8468db3219b7e6598962d5 Mon Sep 17 00:00:00 2001 From: Micha <97034053+foostuff@users.noreply.github.com> Date: Wed, 23 Mar 2022 14:46:03 +0100 Subject: [PATCH 24/36] Update ChangeLog --- apps/clockcal/ChangeLog | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/clockcal/ChangeLog b/apps/clockcal/ChangeLog index 299b1ec69..f73c6ed97 100644 --- a/apps/clockcal/ChangeLog +++ b/apps/clockcal/ChangeLog @@ -1,2 +1,3 @@ 0.01: Initial upload 0.2: Added scrollable calendar and swipe gestures +0.3: Configurable drag gestures From d6c8f634c7689662dea624ab07f91ee2469d1bd3 Mon Sep 17 00:00:00 2001 From: Micha <97034053+foostuff@users.noreply.github.com> Date: Wed, 23 Mar 2022 14:56:17 +0100 Subject: [PATCH 25/36] Update README.md --- apps/clockcal/README.md | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/apps/clockcal/README.md b/apps/clockcal/README.md index da6887177..d30205be0 100644 --- a/apps/clockcal/README.md +++ b/apps/clockcal/README.md @@ -9,25 +9,24 @@ I know that it seems redundant because there already **is** a *time&cal*-app, bu |![unlocked screen](screenshot2.png)|unlocked: smaller clock, but with seconds| |![big calendar](screenshot3.png)|swipe up for big calendar, (up down to scroll, left/right to exit)| - - - ## Configurable Features - Number of calendar rows (weeks) - Buzz on connect/disconnect (I know, this should be an extra widget, but for now, it is included) -- Clock Mode (24h/12h). Doesn't have an am/pm indicator. It's only there because it was easy. +- Clock Mode (24h/12h). (No am/pm indicator) - First day of the week -- Red Saturday -- Red Sunday -- Swipes (to disable all gestures) -- Swipes: music (swipe down) -- Spipes: messages (swipe right) +- Red Saturday/Sunday +- Swipe/Drag gestures to launch features or apps. ## Auto detects your message/music apps: -- swiping down will search your files for an app with the string "music" in its filename and launch it -- swiping right will search your files for an app with the string "message" in its filename and launch it. -- Configurable apps coming soon. +- swiping down will search your files for an app with the string "message" in its filename and launch it. (configurable) +- swiping right will search your files for an app with the string "music" in its filename and launch it. (configurable) ## Feedback The clock works for me in a 24h/MondayFirst/WeekendFree environment but is not well-tested with other settings. So if something isn't working, please tell me: https://github.com/foostuff/BangleApps/issues + +## Planned features: + - Internal lightweight music control, because switching apps has a loading time. + - Clean up settings + - Maybe am/pm indicator for 12h-users + - Step count (optional) From 00ad2a12780da264d897ddd1c39d06074211cb97 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Wed, 23 Mar 2022 22:24:14 +0800 Subject: [PATCH 26/36] Update app.js Define some constants. Add RFC comments. --- apps/authentiwatch/app.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 5198254af..6e2951b9f 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -2,6 +2,8 @@ const COUNTER_TRIANGLE_SIZE = 10; const TOKEN_EXTRA_HEIGHT = 16; var TOKEN_DIGITS_HEIGHT = 30; var TOKEN_HEIGHT = TOKEN_DIGITS_HEIGHT + TOKEN_EXTRA_HEIGHT; +const PROGRESSBAR_HEIGHT = 3; +const IDLE_REPEATS = 1; // when idle, the number of extra timed periods to show before hiding const SETTINGS = "authentiwatch.json"; // Hash functions const crypto = require("crypto"); @@ -21,7 +23,7 @@ if (settings.data ) tokens = settings.data ; /* v0.02 settings */ if (settings.tokens) tokens = settings.tokens; /* v0.03+ settings */ function b32decode(seedstr) { - // RFC4648 + // RFC4648 Base16/32/64 Data Encodings let buf = 0, bitcount = 0, retstr = ""; for (let c of seedstr.toUpperCase()) { if (c == '0') c = 'O'; @@ -48,7 +50,7 @@ function b32decode(seedstr) { function hmac(key, message, algo) { let a = algos[algo.toUpperCase()]; - // RFC2104 + // RFC2104 HMAC if (key.length > a.blksz) { key = a.sha(key); } @@ -62,7 +64,7 @@ function hmac(key, message, algo) { istr.set(message, a.blksz); ostr.set(a.sha(istr), a.blksz); let ret = a.sha(ostr); - // RFC4226 dynamic truncation + // RFC4226 HOTP (dynamic truncation) let v = new DataView(ret, ret[ret.length - 1] & 0x0F, 4); return v.getUint32(0) & 0x7FFFFFFF; } @@ -187,7 +189,7 @@ function drawProgressBar() { let y2 = y1 + TOKEN_HEIGHT - 1; if (y2 >= AR.y && y1 <= AR.y2) { // token visible - y1 = y2 - 3; + y1 = y2 - PROGRESSBAR_HEIGHT; if (y1 <= AR.y2) { // progress bar visible @@ -278,7 +280,7 @@ function changeId(id) { } function onDrag(e) { - state.cnt = 1; + state.cnt = IDLE_REPEATS; if (e.b != 0 && e.dy != 0) { let y = E.clip(state.listy - E.clip(e.dy, -AR.h, AR.h), 0, Math.max(0, tokens.length * TOKEN_HEIGHT - AR.h)); if (state.listy != y) { @@ -312,7 +314,7 @@ function onDrag(e) { } function onTouch(zone, e) { - state.cnt = 1; + state.cnt = IDLE_REPEATS; if (e) { let id = Math.floor((state.listy + e.y - AR.y) / TOKEN_HEIGHT); if (id == state.id || tokens.length == 0 || id >= tokens.length) { @@ -340,7 +342,7 @@ function onTouch(zone, e) { } function onSwipe(e) { - state.cnt = 1; + state.cnt = IDLE_REPEATS; switch (e) { case 1: exitApp(); @@ -358,7 +360,7 @@ function onSwipe(e) { } function bangleBtn(e) { - state.cnt = 1; + state.cnt = IDLE_REPEATS; if (tokens.length > 0) { let id = E.clip(state.id + e, 0, tokens.length - 1); onDrag({b:1, dy:state.listy - E.clip(id * TOKEN_HEIGHT - half(AR.h - TOKEN_HEIGHT), 0, Math.max(0, tokens.length * TOKEN_HEIGHT - AR.h))}); From 287cbf6eea1376f02e1d5472a1c9b9956c953205 Mon Sep 17 00:00:00 2001 From: marko Date: Wed, 23 Mar 2022 14:56:07 -0400 Subject: [PATCH 27/36] New app 2047++ --- apps/2047++/2047++.app.js | 129 ++++++++++++++++++++++++++++++++++++++ apps/2047++/README.md | 6 ++ apps/2047++/app-icon.js | 1 + apps/2047++/app.png | Bin 0 -> 759 bytes apps/2047++/metadata.json | 14 +++++ 5 files changed, 150 insertions(+) create mode 100644 apps/2047++/2047++.app.js create mode 100644 apps/2047++/README.md create mode 100644 apps/2047++/app-icon.js create mode 100644 apps/2047++/app.png create mode 100644 apps/2047++/metadata.json diff --git a/apps/2047++/2047++.app.js b/apps/2047++/2047++.app.js new file mode 100644 index 000000000..b508d8f07 --- /dev/null +++ b/apps/2047++/2047++.app.js @@ -0,0 +1,129 @@ + + +class TwoK { + constructor() { + this.b = Array(4).fill().map(() => Array(4).fill(0)); + this.score = 0; + this.cmap = {0: "#caa", 2:"#ccc", 4: "#bcc", 8: "#ba6", 16: "#e61", 32: "#d20", 64: "#d00", 128: "#da0", 256: "#ec0", 512: "#dd0"}; + } + drawBRect(x1, y1, x2, y2, th, c, cf) { + g.setColor(c); + for (i=0; i 4) g.setColor(1, 1, 1); + else g.setColor(0, 0, 0); + g.setFont("Vector", bh*(b>8 ? (b>64 ? (b>512 ? 0.32 : 0.4) : 0.6) : 0.7)); + if (b>0) g.drawString(b.toString(), xo+(x+0.5)*bw+1, yo+(y+0.5)*bh); + } + } + shift(d) { // +/-1: shift x, +/- 2: shift y + var crc = E.CRC32(this.b.toString()); + if (d==-1) { // shift x left + for (y=0; y<4; ++y) { + for (x=2; x>=0; x--) + if (this.b[y][x]==0) { + for (i=x; i<3; i++) this.b[y][i] = this.b[y][i+1]; + this.b[y][3] = 0; moved = true; + } + for (x=0; x<3; ++x) + if (this.b[y][x]==this.b[y][x+1]) { + this.score += 2*this.b[y][x]; + this.b[y][x] += this.b[y][x+1]; + for (j=x+1; j<3; ++j) this.b[y][j] = this.b[y][j+1]; + this.b[y][3] = 0; moved = true; + } + } + } + else if (d==1) { // shift x right + for (y=0; y<4; ++y) { + for (x=1; x<4; x++) + if (this.b[y][x]==0) { + for (i=x; i>0; i--) this.b[y][i] = this.b[y][i-1]; + this.b[y][0] = 0; + } + for (x=3; x>0; --x) + if (this.b[y][x]==this.b[y][x-1]) { + this.score += 2*this.b[y][x]; + this.b[y][x] += this.b[y][x-1] ; + for (j=x-1; j>0; j--) this.b[y][j] = this.b[y][j-1]; + this.b[y][0] = 0; + } + } + } + else if (d==-2) { // shift y down + for (x=0; x<4; ++x) { + for (y=1; y<4; y++) + if (this.b[y][x]==0) { + for (i=y; i>0; i--) this.b[i][x] = this.b[i-1][x]; + this.b[0][x] = 0; + } + for (y=3; y>0; y--) + if (this.b[y][x]==this.b[y-1][x] || this.b[y][x]==0) { + this.score += 2*this.b[y][x]; + this.b[y][x] += this.b[y-1][x]; + for (j=y-1; j>0; j--) this.b[j][x] = this.b[j-1][x]; + this.b[0][x] = 0; + } + } + } + else if (d==2) { // shift y up + for (x=0; x<4; ++x) { + for (y=2; y>=0; y--) + if (this.b[y][x]==0) { + for (i=y; i<3; i++) this.b[i][x] = this.b[i+1][x]; + this.b[3][x] = 0; + } + for (y=0; y<3; ++y) + if (this.b[y][x]==this.b[y+1][x] || this.b[y][x]==0) { + this.score += 2*this.b[y][x]; + this.b[y][x] += this.b[y+1][x]; + for (j=y+1; j<3; ++j) this.b[j][x] = this.b[j+1][x]; + this.b[3][x] = 0; + } + } + } + return (E.CRC32(this.b.toString())!=crc); + } + addDigit() { + var d = Math.random()>0.9 ? 4 : 2; + var id = Math.floor(Math.random()*16); + while (this.b[Math.floor(id/4)][id%4] > 0) id = Math.floor(Math.random()*16); + this.b[Math.floor(id/4)][id%4] = d; + } +} + +function dragHandler(e) { + if (e.b && (Math.abs(e.dx)>7 || Math.abs(e.dy)>7)) { + var res = false; + if (Math.abs(e.dx)>Math.abs(e.dy)) { + if (e.dx>0) res = twok.shift(1); + if (e.dx<0) res = twok.shift(-1); + } + else { + if (e.dy>0) res = twok.shift(-2); + if (e.dy<0) res = twok.shift(2); + } + if (res) twok.addDigit(); + twok.render(); + } +} + +var twok = new TwoK(); +twok.addDigit(); twok.addDigit(); +twok.render(); +Bangle.on("drag", dragHandler); diff --git a/apps/2047++/README.md b/apps/2047++/README.md new file mode 100644 index 000000000..5b70423e4 --- /dev/null +++ b/apps/2047++/README.md @@ -0,0 +1,6 @@ + +# Game of 2047++ + +Tile shifting game inspired by the well known 2048 game. Also very similar to another Bangle game, Game1024. + +Attempt to combine equal numbers by swiping left, right, up or down. diff --git a/apps/2047++/app-icon.js b/apps/2047++/app-icon.js new file mode 100644 index 000000000..4086d1879 --- /dev/null +++ b/apps/2047++/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A31gAeFtoxPF9wujGBYQG1YAWF6ur5gAYGIovOFzIABF6ReaMAwv/F/4v/F7ejv9/0Yvq1Eylksv4vqvIuBF9ZeDF9ZeBqovr1AsB0YvrLwXMF9ReDF9ZeBq1/v4vBqowKF7lWFYIAFF/7vXAAa/qF+jxB0YvsABov/F/4v/F6WsF7YgEF5xgaLwgvPGIQAWDwwvQADwvJGEguKF+AxhFpoA/AH4A/AFI=")) \ No newline at end of file diff --git a/apps/2047++/app.png b/apps/2047++/app.png new file mode 100644 index 0000000000000000000000000000000000000000..d1fb4a5e5cbadce3da7b36806d4907f680f8f1bb GIT binary patch literal 759 zcmVYCuD-J|F04S(qype>Ll^oVPK(#&(MEqX}dr+-4N41U&D6G<#pk8p=ry~PL z8P~wDv0Fxk@G(D*-Esf`5DS+uaYFpJB+Wpns^Zb}E7&tzFF7%tm102J6nd69|4+P3 zr1VfMt5{y0fI>`0^KD2mu=F8{y6M673*Til--d7lyX64hte$~F4EL^Xh;F_M;RY5n zQPi8Q(T|*z{|6UpV5c0w+w9;*9}v8ZE@h%j4t73gCg!Qfe`}$Ac#sB~}C%=m9 zQmlcEAIAXzTR($HE?;WPt>nU3$%Ta*5c>_tUp2cB`Ud77yzh$Lczg#y>rX6t^Z|D> zcQA?REP&Q#P6pBq$e1?!8Tl#X8W=W?3|OSe*3omHjttb47#RG02|5f6e$ zObTJ!ml%i%20ylab9U#WUDz$7W@n({nZhq+5~`IO*5Pi07qm053E*;P(4-Kmo@>1; z>;uNw7hc?M3Z*1!=?Nlw%8PRi7>4l#z$>YW4#!KwFcx?T+e^N5I_>#$tusSJ+zSuc p5YZ-MEM*wRg54#bi;K&M^BdjW!v$Q*XjK3J002ovPDHLkV1lLySIGbX literal 0 HcmV?d00001 diff --git a/apps/2047++/metadata.json b/apps/2047++/metadata.json new file mode 100644 index 000000000..f887d95d9 --- /dev/null +++ b/apps/2047++/metadata.json @@ -0,0 +1,14 @@ +{ "id": "2047++", + "name": "2047++", + "shortName":"2047++", + "icon": "app.png", + "version":"0.01", + "description": "Bangle version of a tile shifting game", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "tags": "game", + "storage": [ + {"name":"2047++.app.js","url":"2047++.app.js"}, + {"name":"2047++.img","url":"app-icon.js","evaluate":true} + ] +} From 1e10f4a25eb1c7a48513694b71fa1b0aee623217 Mon Sep 17 00:00:00 2001 From: marko Date: Wed, 23 Mar 2022 14:59:23 -0400 Subject: [PATCH 28/36] Change name of new app --- apps/2047++/2047++.app.js | 129 -------------------------------------- apps/2047++/README.md | 6 -- apps/2047++/app-icon.js | 1 - apps/2047++/app.png | Bin 759 -> 0 bytes apps/2047++/metadata.json | 14 ----- 5 files changed, 150 deletions(-) delete mode 100644 apps/2047++/2047++.app.js delete mode 100644 apps/2047++/README.md delete mode 100644 apps/2047++/app-icon.js delete mode 100644 apps/2047++/app.png delete mode 100644 apps/2047++/metadata.json diff --git a/apps/2047++/2047++.app.js b/apps/2047++/2047++.app.js deleted file mode 100644 index b508d8f07..000000000 --- a/apps/2047++/2047++.app.js +++ /dev/null @@ -1,129 +0,0 @@ - - -class TwoK { - constructor() { - this.b = Array(4).fill().map(() => Array(4).fill(0)); - this.score = 0; - this.cmap = {0: "#caa", 2:"#ccc", 4: "#bcc", 8: "#ba6", 16: "#e61", 32: "#d20", 64: "#d00", 128: "#da0", 256: "#ec0", 512: "#dd0"}; - } - drawBRect(x1, y1, x2, y2, th, c, cf) { - g.setColor(c); - for (i=0; i 4) g.setColor(1, 1, 1); - else g.setColor(0, 0, 0); - g.setFont("Vector", bh*(b>8 ? (b>64 ? (b>512 ? 0.32 : 0.4) : 0.6) : 0.7)); - if (b>0) g.drawString(b.toString(), xo+(x+0.5)*bw+1, yo+(y+0.5)*bh); - } - } - shift(d) { // +/-1: shift x, +/- 2: shift y - var crc = E.CRC32(this.b.toString()); - if (d==-1) { // shift x left - for (y=0; y<4; ++y) { - for (x=2; x>=0; x--) - if (this.b[y][x]==0) { - for (i=x; i<3; i++) this.b[y][i] = this.b[y][i+1]; - this.b[y][3] = 0; moved = true; - } - for (x=0; x<3; ++x) - if (this.b[y][x]==this.b[y][x+1]) { - this.score += 2*this.b[y][x]; - this.b[y][x] += this.b[y][x+1]; - for (j=x+1; j<3; ++j) this.b[y][j] = this.b[y][j+1]; - this.b[y][3] = 0; moved = true; - } - } - } - else if (d==1) { // shift x right - for (y=0; y<4; ++y) { - for (x=1; x<4; x++) - if (this.b[y][x]==0) { - for (i=x; i>0; i--) this.b[y][i] = this.b[y][i-1]; - this.b[y][0] = 0; - } - for (x=3; x>0; --x) - if (this.b[y][x]==this.b[y][x-1]) { - this.score += 2*this.b[y][x]; - this.b[y][x] += this.b[y][x-1] ; - for (j=x-1; j>0; j--) this.b[y][j] = this.b[y][j-1]; - this.b[y][0] = 0; - } - } - } - else if (d==-2) { // shift y down - for (x=0; x<4; ++x) { - for (y=1; y<4; y++) - if (this.b[y][x]==0) { - for (i=y; i>0; i--) this.b[i][x] = this.b[i-1][x]; - this.b[0][x] = 0; - } - for (y=3; y>0; y--) - if (this.b[y][x]==this.b[y-1][x] || this.b[y][x]==0) { - this.score += 2*this.b[y][x]; - this.b[y][x] += this.b[y-1][x]; - for (j=y-1; j>0; j--) this.b[j][x] = this.b[j-1][x]; - this.b[0][x] = 0; - } - } - } - else if (d==2) { // shift y up - for (x=0; x<4; ++x) { - for (y=2; y>=0; y--) - if (this.b[y][x]==0) { - for (i=y; i<3; i++) this.b[i][x] = this.b[i+1][x]; - this.b[3][x] = 0; - } - for (y=0; y<3; ++y) - if (this.b[y][x]==this.b[y+1][x] || this.b[y][x]==0) { - this.score += 2*this.b[y][x]; - this.b[y][x] += this.b[y+1][x]; - for (j=y+1; j<3; ++j) this.b[j][x] = this.b[j+1][x]; - this.b[3][x] = 0; - } - } - } - return (E.CRC32(this.b.toString())!=crc); - } - addDigit() { - var d = Math.random()>0.9 ? 4 : 2; - var id = Math.floor(Math.random()*16); - while (this.b[Math.floor(id/4)][id%4] > 0) id = Math.floor(Math.random()*16); - this.b[Math.floor(id/4)][id%4] = d; - } -} - -function dragHandler(e) { - if (e.b && (Math.abs(e.dx)>7 || Math.abs(e.dy)>7)) { - var res = false; - if (Math.abs(e.dx)>Math.abs(e.dy)) { - if (e.dx>0) res = twok.shift(1); - if (e.dx<0) res = twok.shift(-1); - } - else { - if (e.dy>0) res = twok.shift(-2); - if (e.dy<0) res = twok.shift(2); - } - if (res) twok.addDigit(); - twok.render(); - } -} - -var twok = new TwoK(); -twok.addDigit(); twok.addDigit(); -twok.render(); -Bangle.on("drag", dragHandler); diff --git a/apps/2047++/README.md b/apps/2047++/README.md deleted file mode 100644 index 5b70423e4..000000000 --- a/apps/2047++/README.md +++ /dev/null @@ -1,6 +0,0 @@ - -# Game of 2047++ - -Tile shifting game inspired by the well known 2048 game. Also very similar to another Bangle game, Game1024. - -Attempt to combine equal numbers by swiping left, right, up or down. diff --git a/apps/2047++/app-icon.js b/apps/2047++/app-icon.js deleted file mode 100644 index 4086d1879..000000000 --- a/apps/2047++/app-icon.js +++ /dev/null @@ -1 +0,0 @@ -require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A31gAeFtoxPF9wujGBYQG1YAWF6ur5gAYGIovOFzIABF6ReaMAwv/F/4v/F7ejv9/0Yvq1Eylksv4vqvIuBF9ZeDF9ZeBqovr1AsB0YvrLwXMF9ReDF9ZeBq1/v4vBqowKF7lWFYIAFF/7vXAAa/qF+jxB0YvsABov/F/4v/F6WsF7YgEF5xgaLwgvPGIQAWDwwvQADwvJGEguKF+AxhFpoA/AH4A/AFI=")) \ No newline at end of file diff --git a/apps/2047++/app.png b/apps/2047++/app.png deleted file mode 100644 index d1fb4a5e5cbadce3da7b36806d4907f680f8f1bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 759 zcmVYCuD-J|F04S(qype>Ll^oVPK(#&(MEqX}dr+-4N41U&D6G<#pk8p=ry~PL z8P~wDv0Fxk@G(D*-Esf`5DS+uaYFpJB+Wpns^Zb}E7&tzFF7%tm102J6nd69|4+P3 zr1VfMt5{y0fI>`0^KD2mu=F8{y6M673*Til--d7lyX64hte$~F4EL^Xh;F_M;RY5n zQPi8Q(T|*z{|6UpV5c0w+w9;*9}v8ZE@h%j4t73gCg!Qfe`}$Ac#sB~}C%=m9 zQmlcEAIAXzTR($HE?;WPt>nU3$%Ta*5c>_tUp2cB`Ud77yzh$Lczg#y>rX6t^Z|D> zcQA?REP&Q#P6pBq$e1?!8Tl#X8W=W?3|OSe*3omHjttb47#RG02|5f6e$ zObTJ!ml%i%20ylab9U#WUDz$7W@n({nZhq+5~`IO*5Pi07qm053E*;P(4-Kmo@>1; z>;uNw7hc?M3Z*1!=?Nlw%8PRi7>4l#z$>YW4#!KwFcx?T+e^N5I_>#$tusSJ+zSuc p5YZ-MEM*wRg54#bi;K&M^BdjW!v$Q*XjK3J002ovPDHLkV1lLySIGbX diff --git a/apps/2047++/metadata.json b/apps/2047++/metadata.json deleted file mode 100644 index f887d95d9..000000000 --- a/apps/2047++/metadata.json +++ /dev/null @@ -1,14 +0,0 @@ -{ "id": "2047++", - "name": "2047++", - "shortName":"2047++", - "icon": "app.png", - "version":"0.01", - "description": "Bangle version of a tile shifting game", - "supports" : ["BANGLEJS2"], - "readme": "README.md", - "tags": "game", - "storage": [ - {"name":"2047++.app.js","url":"2047++.app.js"}, - {"name":"2047++.img","url":"app-icon.js","evaluate":true} - ] -} From 80246d2398a93311aaa131886f873b54da705a66 Mon Sep 17 00:00:00 2001 From: marko Date: Wed, 23 Mar 2022 15:00:54 -0400 Subject: [PATCH 29/36] New app 2047pp --- apps/2047pp/2047pp.app.js | 129 ++++++++++++++++++++++++++++++++++++++ apps/2047pp/README.md | 6 ++ apps/2047pp/app-icon.js | 1 + apps/2047pp/app.png | Bin 0 -> 759 bytes apps/2047pp/metadata.json | 14 +++++ 5 files changed, 150 insertions(+) create mode 100644 apps/2047pp/2047pp.app.js create mode 100644 apps/2047pp/README.md create mode 100644 apps/2047pp/app-icon.js create mode 100644 apps/2047pp/app.png create mode 100644 apps/2047pp/metadata.json diff --git a/apps/2047pp/2047pp.app.js b/apps/2047pp/2047pp.app.js new file mode 100644 index 000000000..b508d8f07 --- /dev/null +++ b/apps/2047pp/2047pp.app.js @@ -0,0 +1,129 @@ + + +class TwoK { + constructor() { + this.b = Array(4).fill().map(() => Array(4).fill(0)); + this.score = 0; + this.cmap = {0: "#caa", 2:"#ccc", 4: "#bcc", 8: "#ba6", 16: "#e61", 32: "#d20", 64: "#d00", 128: "#da0", 256: "#ec0", 512: "#dd0"}; + } + drawBRect(x1, y1, x2, y2, th, c, cf) { + g.setColor(c); + for (i=0; i 4) g.setColor(1, 1, 1); + else g.setColor(0, 0, 0); + g.setFont("Vector", bh*(b>8 ? (b>64 ? (b>512 ? 0.32 : 0.4) : 0.6) : 0.7)); + if (b>0) g.drawString(b.toString(), xo+(x+0.5)*bw+1, yo+(y+0.5)*bh); + } + } + shift(d) { // +/-1: shift x, +/- 2: shift y + var crc = E.CRC32(this.b.toString()); + if (d==-1) { // shift x left + for (y=0; y<4; ++y) { + for (x=2; x>=0; x--) + if (this.b[y][x]==0) { + for (i=x; i<3; i++) this.b[y][i] = this.b[y][i+1]; + this.b[y][3] = 0; moved = true; + } + for (x=0; x<3; ++x) + if (this.b[y][x]==this.b[y][x+1]) { + this.score += 2*this.b[y][x]; + this.b[y][x] += this.b[y][x+1]; + for (j=x+1; j<3; ++j) this.b[y][j] = this.b[y][j+1]; + this.b[y][3] = 0; moved = true; + } + } + } + else if (d==1) { // shift x right + for (y=0; y<4; ++y) { + for (x=1; x<4; x++) + if (this.b[y][x]==0) { + for (i=x; i>0; i--) this.b[y][i] = this.b[y][i-1]; + this.b[y][0] = 0; + } + for (x=3; x>0; --x) + if (this.b[y][x]==this.b[y][x-1]) { + this.score += 2*this.b[y][x]; + this.b[y][x] += this.b[y][x-1] ; + for (j=x-1; j>0; j--) this.b[y][j] = this.b[y][j-1]; + this.b[y][0] = 0; + } + } + } + else if (d==-2) { // shift y down + for (x=0; x<4; ++x) { + for (y=1; y<4; y++) + if (this.b[y][x]==0) { + for (i=y; i>0; i--) this.b[i][x] = this.b[i-1][x]; + this.b[0][x] = 0; + } + for (y=3; y>0; y--) + if (this.b[y][x]==this.b[y-1][x] || this.b[y][x]==0) { + this.score += 2*this.b[y][x]; + this.b[y][x] += this.b[y-1][x]; + for (j=y-1; j>0; j--) this.b[j][x] = this.b[j-1][x]; + this.b[0][x] = 0; + } + } + } + else if (d==2) { // shift y up + for (x=0; x<4; ++x) { + for (y=2; y>=0; y--) + if (this.b[y][x]==0) { + for (i=y; i<3; i++) this.b[i][x] = this.b[i+1][x]; + this.b[3][x] = 0; + } + for (y=0; y<3; ++y) + if (this.b[y][x]==this.b[y+1][x] || this.b[y][x]==0) { + this.score += 2*this.b[y][x]; + this.b[y][x] += this.b[y+1][x]; + for (j=y+1; j<3; ++j) this.b[j][x] = this.b[j+1][x]; + this.b[3][x] = 0; + } + } + } + return (E.CRC32(this.b.toString())!=crc); + } + addDigit() { + var d = Math.random()>0.9 ? 4 : 2; + var id = Math.floor(Math.random()*16); + while (this.b[Math.floor(id/4)][id%4] > 0) id = Math.floor(Math.random()*16); + this.b[Math.floor(id/4)][id%4] = d; + } +} + +function dragHandler(e) { + if (e.b && (Math.abs(e.dx)>7 || Math.abs(e.dy)>7)) { + var res = false; + if (Math.abs(e.dx)>Math.abs(e.dy)) { + if (e.dx>0) res = twok.shift(1); + if (e.dx<0) res = twok.shift(-1); + } + else { + if (e.dy>0) res = twok.shift(-2); + if (e.dy<0) res = twok.shift(2); + } + if (res) twok.addDigit(); + twok.render(); + } +} + +var twok = new TwoK(); +twok.addDigit(); twok.addDigit(); +twok.render(); +Bangle.on("drag", dragHandler); diff --git a/apps/2047pp/README.md b/apps/2047pp/README.md new file mode 100644 index 000000000..e685946c1 --- /dev/null +++ b/apps/2047pp/README.md @@ -0,0 +1,6 @@ + +# Game of 2047pp (2047++) + +Tile shifting game inspired by the well known 2048 game. Also very similar to another Bangle game, Game1024. + +Attempt to combine equal numbers by swiping left, right, up or down. diff --git a/apps/2047pp/app-icon.js b/apps/2047pp/app-icon.js new file mode 100644 index 000000000..4086d1879 --- /dev/null +++ b/apps/2047pp/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwxH+AH4A/AH4A31gAeFtoxPF9wujGBYQG1YAWF6ur5gAYGIovOFzIABF6ReaMAwv/F/4v/F7ejv9/0Yvq1Eylksv4vqvIuBF9ZeDF9ZeBqovr1AsB0YvrLwXMF9ReDF9ZeBq1/v4vBqowKF7lWFYIAFF/7vXAAa/qF+jxB0YvsABov/F/4v/F6WsF7YgEF5xgaLwgvPGIQAWDwwvQADwvJGEguKF+AxhFpoA/AH4A/AFI=")) \ No newline at end of file diff --git a/apps/2047pp/app.png b/apps/2047pp/app.png new file mode 100644 index 0000000000000000000000000000000000000000..d1fb4a5e5cbadce3da7b36806d4907f680f8f1bb GIT binary patch literal 759 zcmVYCuD-J|F04S(qype>Ll^oVPK(#&(MEqX}dr+-4N41U&D6G<#pk8p=ry~PL z8P~wDv0Fxk@G(D*-Esf`5DS+uaYFpJB+Wpns^Zb}E7&tzFF7%tm102J6nd69|4+P3 zr1VfMt5{y0fI>`0^KD2mu=F8{y6M673*Til--d7lyX64hte$~F4EL^Xh;F_M;RY5n zQPi8Q(T|*z{|6UpV5c0w+w9;*9}v8ZE@h%j4t73gCg!Qfe`}$Ac#sB~}C%=m9 zQmlcEAIAXzTR($HE?;WPt>nU3$%Ta*5c>_tUp2cB`Ud77yzh$Lczg#y>rX6t^Z|D> zcQA?REP&Q#P6pBq$e1?!8Tl#X8W=W?3|OSe*3omHjttb47#RG02|5f6e$ zObTJ!ml%i%20ylab9U#WUDz$7W@n({nZhq+5~`IO*5Pi07qm053E*;P(4-Kmo@>1; z>;uNw7hc?M3Z*1!=?Nlw%8PRi7>4l#z$>YW4#!KwFcx?T+e^N5I_>#$tusSJ+zSuc p5YZ-MEM*wRg54#bi;K&M^BdjW!v$Q*XjK3J002ovPDHLkV1lLySIGbX literal 0 HcmV?d00001 diff --git a/apps/2047pp/metadata.json b/apps/2047pp/metadata.json new file mode 100644 index 000000000..7c120efec --- /dev/null +++ b/apps/2047pp/metadata.json @@ -0,0 +1,14 @@ +{ "id": "2047pp", + "name": "2047pp", + "shortName":"2047pp", + "icon": "app.png", + "version":"0.01", + "description": "Bangle version of a tile shifting game", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "tags": "game", + "storage": [ + {"name":"2047pp.app.js","url":"2047pp.app.js"}, + {"name":"2047pp.img","url":"app-icon.js","evaluate":true} + ] +} From 0342b131249937c8a692f0a500748d264b3ab05e Mon Sep 17 00:00:00 2001 From: marko Date: Wed, 23 Mar 2022 15:38:16 -0400 Subject: [PATCH 30/36] Support Bangles 1&2 --- apps/2047pp/2047pp.app.js | 31 +++++++++++++++++++++---------- apps/2047pp/README.md | 3 ++- apps/2047pp/metadata.json | 2 +- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/apps/2047pp/2047pp.app.js b/apps/2047pp/2047pp.app.js index b508d8f07..9b2283036 100644 --- a/apps/2047pp/2047pp.app.js +++ b/apps/2047pp/2047pp.app.js @@ -1,15 +1,13 @@ - - class TwoK { constructor() { this.b = Array(4).fill().map(() => Array(4).fill(0)); this.score = 0; this.cmap = {0: "#caa", 2:"#ccc", 4: "#bcc", 8: "#ba6", 16: "#e61", 32: "#d20", 64: "#d00", 128: "#da0", 256: "#ec0", 512: "#dd0"}; } - drawBRect(x1, y1, x2, y2, th, c, cf) { + drawBRect(x1, y1, x2, y2, th, c, cf, fill) { g.setColor(c); for (i=0; i 4) g.setColor(1, 1, 1); else g.setColor(0, 0, 0); g.setFont("Vector", bh*(b>8 ? (b>64 ? (b>512 ? 0.32 : 0.4) : 0.6) : 0.7)); @@ -38,14 +36,14 @@ class TwoK { for (x=2; x>=0; x--) if (this.b[y][x]==0) { for (i=x; i<3; i++) this.b[y][i] = this.b[y][i+1]; - this.b[y][3] = 0; moved = true; + this.b[y][3] = 0; } for (x=0; x<3; ++x) if (this.b[y][x]==this.b[y][x+1]) { this.score += 2*this.b[y][x]; this.b[y][x] += this.b[y][x+1]; for (j=x+1; j<3; ++j) this.b[y][j] = this.b[y][j+1]; - this.b[y][3] = 0; moved = true; + this.b[y][3] = 0; } } } @@ -123,7 +121,20 @@ function dragHandler(e) { } } +function swipeHandler() { + +} + +function buttonHandler() { + +} + var twok = new TwoK(); twok.addDigit(); twok.addDigit(); twok.render(); -Bangle.on("drag", dragHandler); +if (process.env.HWVERSION==2) Bangle.on("drag", dragHandler); +else if (process.env.HWVERSION==1) { + Bangle.on("swipe", (e) => { res = twok.shift(e); if (res) twok.addDigit(); twok.render(); }); + setWatch(() => { res = twok.shift(2); if (res) twok.addDigit(); twok.render(); }, BTN1, {repeat: true}); + setWatch(() => { res = twok.shift(-2); if (res) twok.addDigit(); twok.render(); }, BTN3, {repeat: true}); +} diff --git a/apps/2047pp/README.md b/apps/2047pp/README.md index e685946c1..8228892fd 100644 --- a/apps/2047pp/README.md +++ b/apps/2047pp/README.md @@ -3,4 +3,5 @@ Tile shifting game inspired by the well known 2048 game. Also very similar to another Bangle game, Game1024. -Attempt to combine equal numbers by swiping left, right, up or down. +Attempt to combine equal numbers by swiping left, right, up or down (on Bangle 2) or swiping left/right and using +the top/bottom button (Bangle 1). diff --git a/apps/2047pp/metadata.json b/apps/2047pp/metadata.json index 7c120efec..0241e9933 100644 --- a/apps/2047pp/metadata.json +++ b/apps/2047pp/metadata.json @@ -4,7 +4,7 @@ "icon": "app.png", "version":"0.01", "description": "Bangle version of a tile shifting game", - "supports" : ["BANGLEJS2"], + "supports" : ["BANGLEJS1","BANGLEJS2"], "readme": "README.md", "tags": "game", "storage": [ From 0c24bd397aeb82f5d3d1af2794e95942e9039c5f Mon Sep 17 00:00:00 2001 From: marko Date: Wed, 23 Mar 2022 15:40:28 -0400 Subject: [PATCH 31/36] Use correct string for supported devices --- apps/2047pp/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/2047pp/metadata.json b/apps/2047pp/metadata.json index 0241e9933..0a362c583 100644 --- a/apps/2047pp/metadata.json +++ b/apps/2047pp/metadata.json @@ -4,7 +4,7 @@ "icon": "app.png", "version":"0.01", "description": "Bangle version of a tile shifting game", - "supports" : ["BANGLEJS1","BANGLEJS2"], + "supports" : ["BANGLEJS","BANGLEJS2"], "readme": "README.md", "tags": "game", "storage": [ From fd8cc60283512c3aa649f525a4d01482af79e531 Mon Sep 17 00:00:00 2001 From: marko Date: Wed, 23 Mar 2022 17:24:42 -0400 Subject: [PATCH 32/36] Make border thicknesses more consistent; add screenshot; allow running in emulator. --- apps/2047pp/2047pp.app.js | 8 ++++---- apps/2047pp/2047pp_screenshot.png | Bin 0 -> 4541 bytes apps/2047pp/README.md | 2 ++ apps/2047pp/metadata.json | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 apps/2047pp/2047pp_screenshot.png diff --git a/apps/2047pp/2047pp.app.js b/apps/2047pp/2047pp.app.js index 9b2283036..58738d04a 100644 --- a/apps/2047pp/2047pp.app.js +++ b/apps/2047pp/2047pp.app.js @@ -18,11 +18,11 @@ class TwoK { bw = Math.floor(w/4); g.clearRect(0, 0, g.getWidth()-1, yo).setFontAlign(0, 0, 0); g.setFont("Vector", 16).setColor("#fff").drawString("Score:"+this.score.toString(), g.getWidth()/2, 8); - this.drawBRect(xo-2, yo-2, xo+w+2, yo+h, 2, "#a88", "#caa", false); + this.drawBRect(xo-3, yo-3, xo+w+2, yo+h+2, 4, "#a88", "#caa", false); for (y=0; y<4; ++y) for (x=0; x<4; ++x) { b = this.b[y][x]; - this.drawBRect(xo+x*bw, yo+y*bh-2, xo+(x+1)*bh, yo+(y+1)*bh-2, 4, "#a88", this.cmap[b], true); + this.drawBRect(xo+x*bw, yo+y*bh-1, xo+(x+1)*bh-1, yo+(y+1)*bh-2, 4, "#a88", this.cmap[b], true); if (b > 4) g.setColor(1, 1, 1); else g.setColor(0, 0, 0); g.setFont("Vector", bh*(b>8 ? (b>64 ? (b>512 ? 0.32 : 0.4) : 0.6) : 0.7)); @@ -35,7 +35,7 @@ class TwoK { for (y=0; y<4; ++y) { for (x=2; x>=0; x--) if (this.b[y][x]==0) { - for (i=x; i<3; i++) this.b[y][i] = this.b[y][i+1]; + for (i=x; i<3; i++) this.b[y][i] = this.b[y][i+1]; this.b[y][3] = 0; } for (x=0; x<3; ++x) @@ -133,7 +133,7 @@ var twok = new TwoK(); twok.addDigit(); twok.addDigit(); twok.render(); if (process.env.HWVERSION==2) Bangle.on("drag", dragHandler); -else if (process.env.HWVERSION==1) { +if (process.env.HWVERSION==1) { Bangle.on("swipe", (e) => { res = twok.shift(e); if (res) twok.addDigit(); twok.render(); }); setWatch(() => { res = twok.shift(2); if (res) twok.addDigit(); twok.render(); }, BTN1, {repeat: true}); setWatch(() => { res = twok.shift(-2); if (res) twok.addDigit(); twok.render(); }, BTN3, {repeat: true}); diff --git a/apps/2047pp/2047pp_screenshot.png b/apps/2047pp/2047pp_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..8c407fb6fe4b78d2f60c30651c60f8513b5c658d GIT binary patch literal 4541 zcmb7IeLR!v|G)0dHnvO;jF41Ehnc7j<{^iv6cR%7w1%Pxqhd_8IW4K^(L)KTwDOR7 zikP~kA}SAgs>WFdQN%op%J1f!*ZKYN{pb7rW4rHN_x1T)*XMJ6-tW)*Uc94&jXYiz z4*-zgxnrv{a+NN8WUeIRz-D5Dw-l)7IVsiw%R9=Da08V82n~HFK1fT@J?grx`!HyioMg5>d)+R z)=tU^O%<$7YFRznr?2Dctv@_G{IjCxcSTE{HCc+{4)%T>oPTT)G$45L!$t7%MLxUM z$?UZLv8I}-miAbHx__&i2#@BnW$#&?7oSgTZjay$ji z@{!9^I_h)9+xxNlzCh8VFmL%OZaO4+dgngih@*7>ozy>27Z!52DG9&xFqXTm z+We!2wEg|N_nIEDBk8jnM*MMMVU zx~KeGk+_4bkuvp+oi0|ri|bZ|yH7-q_)I1>p40}gQchO;ixw@-lrCqZ%w&b+Z-3g= zI=o)$9Db+!h+y`bYQw~Q{-GHDRk8gF;HA@Mcc!hYtMvYaN1@=Z(9>b)(65tAi!{Lf z#wvx&wqt)D-V9=j?*~ZMA)eRmzqC=Dhx(6!H1LGOE_e4)q2ecQ!D(}I`}czK6iTy# z;ts^>Di*I!hkylZY`_!`3=Fv_oEzXO+%T_5M6Cgt4uTr;+{{e*yT}*q0fG6Vs_&D> zGFL@Td})Zg@KPHvcK9iwfEhU?ZmDi45bdaM=P=L4#Ni{vE#$FF<5(EPxG{Uzu8ukk zmcU?$^{tAQ_YMQl24G~Hpyx4&(Q}@1s=p-gJ^$M3EpZJpDFBm41Cmt(N5w7mo)e0* zW&0T3lVd;ncPD0iSR>kAWEg!`h=;hCjcTVoqRz>%<+~3J0I?8!@^t3|*zA|*VRpKq zPCy{RFl}1a(iWV_wl#bx8Nt5~bn;EXMRH3GSjiYX;7IBF8cEuH493a#$7{;c`Tjz`VP(zrvYANgF03ql9M0W~v4&Z;Pn4Iy zv14v)z(4DPSE{LoENjg+Pe@wo``xKBiTsr3TJruGa!{E{3;F&4?sq@0SKD*&Zj*hM zYmVxR4lMnvX=PK6uhppA8VPLmRM{w~Z`UzqsTs&4lN3XdvcSph>�s2LAZhiWN(X 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 literal 0 HcmV?d00001 diff --git a/apps/2047pp/README.md b/apps/2047pp/README.md index 8228892fd..cac3323a6 100644 --- a/apps/2047pp/README.md +++ b/apps/2047pp/README.md @@ -5,3 +5,5 @@ Tile shifting game inspired by the well known 2048 game. Also very similar to an Attempt to combine equal numbers by swiping left, right, up or down (on Bangle 2) or swiping left/right and using the top/bottom button (Bangle 1). + +![Screenshot](./2047pp_screenshot.png) diff --git a/apps/2047pp/metadata.json b/apps/2047pp/metadata.json index 0a362c583..f0fd6c1e3 100644 --- a/apps/2047pp/metadata.json +++ b/apps/2047pp/metadata.json @@ -5,6 +5,7 @@ "version":"0.01", "description": "Bangle version of a tile shifting game", "supports" : ["BANGLEJS","BANGLEJS2"], + "allow_emulator": true, "readme": "README.md", "tags": "game", "storage": [ From dd27b11eb87b5087e668549ab59b908260c10545 Mon Sep 17 00:00:00 2001 From: Andrew Gregory Date: Thu, 24 Mar 2022 09:43:32 +0800 Subject: [PATCH 33/36] Update app.js Fix automatic font sizing. --- apps/authentiwatch/app.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/authentiwatch/app.js b/apps/authentiwatch/app.js index 6e2951b9f..05d94fc46 100644 --- a/apps/authentiwatch/app.js +++ b/apps/authentiwatch/app.js @@ -119,15 +119,14 @@ var state = { function sizeFont(id, txt, w) { let sz = fontszCache[id]; - if (sz) { - g.setFont("Vector", sz); - } else { + if (!sz) { sz = TOKEN_DIGITS_HEIGHT; do { g.setFont("Vector", sz--); } while (g.stringWidth(txt) > w); - fontszCache[id] = sz + 1; + fontszCache[id] = ++sz; } + g.setFont("Vector", sz); } tokenY = id => id * TOKEN_HEIGHT + AR.y - state.listy; From ae74a508384a9f92ed3acc7bf776106a251fcd58 Mon Sep 17 00:00:00 2001 From: Alessandro Cocco Date: Wed, 23 Mar 2022 22:54:32 +0100 Subject: [PATCH 34/36] [Wave Clock] Show the day of the week --- apps/waveclk/ChangeLog | 1 + apps/waveclk/app.js | 4 ++++ apps/waveclk/metadata.json | 2 +- apps/waveclk/screenshot.png | Bin 2884 -> 6237 bytes 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/waveclk/ChangeLog b/apps/waveclk/ChangeLog index 8c2a33143..f1fb77c59 100644 --- a/apps/waveclk/ChangeLog +++ b/apps/waveclk/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! 0.02: Load widgets after setUI so widclk knows when to hide +0.03: Show the day of the week diff --git a/apps/waveclk/app.js b/apps/waveclk/app.js index f1c67ce2f..18b28500b 100644 --- a/apps/waveclk/app.js +++ b/apps/waveclk/app.js @@ -41,6 +41,7 @@ function draw() { var date = new Date(); var timeStr = require("locale").time(date,1); var dateStr = require("locale").date(date).toUpperCase(); + var dowStr = require("locale").dow(date).toUpperCase(); // draw time g.setFontAlign(0,0).setFont("ZCOOL"); g.drawString(timeStr,x,y); @@ -48,6 +49,9 @@ function draw() { y += 35; g.setFontAlign(0,0,1).setFont("6x8"); g.drawString(dateStr,g.getWidth()-8,g.getHeight()/2); + // draw the day of the week + g.setFontAlign(0,0,3).setFont("6x8"); + g.drawString(dowStr,8,g.getHeight()/2); // queue draw in one minute queueDraw(); } diff --git a/apps/waveclk/metadata.json b/apps/waveclk/metadata.json index a8d270da2..9ba2798ff 100644 --- a/apps/waveclk/metadata.json +++ b/apps/waveclk/metadata.json @@ -1,7 +1,7 @@ { "id": "waveclk", "name": "Wave Clock", - "version": "0.02", + "version": "0.03", "description": "A clock using a wave image by [Lillith May](https://www.instagram.com/_lilustrations_/)", "icon": "app.png", "screenshots": [{"url":"screenshot.png"}], diff --git a/apps/waveclk/screenshot.png b/apps/waveclk/screenshot.png index 7f05ce68880935db0de4aaf840077e9b30413db7..161ef96ef0194e95c23821555e073a21607dfc83 100644 GIT binary patch literal 6237 zcmV-j7^3HiP)Py27fD1xRCr$Po!OG(APhyP|No;i727yM)^f$}#PcxK-N7v4AT9!&zt_Ll>)-S5 zvA|6&@b>_G)1G^(y;@@#H{cC zRIyxF!&jODv}~$8@B?sN?=14PBon?HVD2Hkl`85v(E)+xw+JA2s0N2PDk1LWfggbH zz+||N6yoS2?j8Vh4~cqA&6J+!=FdHzHw!V$ZTwpFaSj=6e7}Ej29NcT0$<2lhPl1Mn~~k%Df$h=Vln z12DqDEz|r)7;V(f+OrckbP)W`M#cm1P_Ske_T4*iGcEg+J;Vahowy;Dk_xFh3o)(M zPr(n@#KNwk?|N9aN?7ew;>lC*OHw!$>MA)Z-w1fEe_ z?zEiVwvV*mNjnE%G``rwkTc6J>9|p|1Zo$l4L$k^wAFZkqlRi}oS9{V9g-y*<$d3U zMr{*qPs@HJWsd{6#z3`=pILUaqiOn5?_2U*jg8PY*7l=N&KQ6_Ap4 z0%i0A*aOIGOih$vX4wJbn@L+k=-IoZ-kzO#o*n8i2Vk@zr4W}s=jqSrEKmZ4=vwB5!Zf4Iqi*W~ zd}nw|yl?Bp&uExt_SbS>_C3=Wt;&wdpVq*&&d>A0qswFUS;qsn0?0GC^m(tcdA^bi zX$?T@{49I+vRj}J*8q5?AyzCq(1A^oqWT^QHJ0X8*pT@EN2lDvhKi5PEZft|E!*kk zRRZ12flt~q7vPxy%`7{0fETgwz-#q+D}YP#scgeMfM)_UvutDDlQ$0Y-GcYotH|a1GHHU~2&tXp5TEgTaHTrVf#-L8%wX?x&SBaL@z3pi2R{A$^#eO6Cb zQ`fK1HHty5(3JWX|2d=39^XOxN(*krM zS9F*M)FY3{%PjC`>}$Wee}P+BorgYE4DbVPmwpm~-R>3qZC4C-X5T+wi~mWW!vIro zTZPiPpm48DGjK~T!u)-R{(6vk^h@E;k3t;I;9jVVuWf?!4VLutN6%eXC$w+0pR*SI z{r$55rsa>f#MlzCt>i~aoKO~b5AnEsf=fOEbUJ$#4sR@h`~Y0sOW*aNu2sw1DapBF zl_o$;>-_aH!@ZUKgP!*SZBVO9FiP9`#nAGxLv%6!=(hoe#8|l}TJ1>-Tnj)+4sc2E zUV2G!`I?=1a{&C%Vo%?RlWsTESbQ~NkNRkl0Y24Bff-K@NIsz4^U@F-XiP&VgE=gp z>}LVCz_mOm^`_}(dX`+#S=n%*NVPhfbP4|ZXSs^JBfH(ZBeC7IFUtM#2aJi?h8Os zf>3#JWOk-ya)U=CGP@Aix4+ZK%d7=q6~N%bXa~$nM?0xRCZM3x_Hm8eqU~DUH>jBx zKFed;Sp+zNDrsvWJU28f2Qp|c;%*vBlf14c5hO4Be#VPNqd)9%7ehv0x3* ztzm3fYy*L`c>A?v*f(k|Z)jRsv+TbP;HcO2VzfMbmh5PS)k0k;!3S)f=$d%np3>O^ z=arj8FZ8nzH}k$xaOr^7VDvzwde>YutKl#C$mGWp+BHBHj!2rV6t1U%MWAP&>dyfN zvBNyoHR+o{6kZOV1S0pe^mh$halLCz@5VN8i}#L0@UFTQEmdCl+wC_pqVP(Q2CDR= zMHmEvG)65zy)g~Q2rUaMYLY#pc-gm2r|Mt2_>dMNq!G6UIE8#7OAXEYY97|(%=~-- zfX&^1Mdumj3HV~DsF@WW7%mSOV45dJAcyN= zO(op3+&v0WD;bJCtrx1Ih6TWIJ2!J4kvYcA`I_Kdz@_>6NTO_ptaRr1R%anv!yD@A z$bzc{z_fBBys@o?@(ZTqV9{)?e48l5Ib{`gW|J-6Sr8YtIW$1M#I)^9Z_R}G79M!0 z=Ve>4TfZu21F}AscD6hykk8NEF}n0-=M|8Alr(&&aZ8rLDjefXd7ZBUn73=^dtiKZ zQ(;E^(H8JJ>wvv*ZAVNA@Ly_7&+AHOcT24!d0$?q(!AMLd9ssei!JR(i=T&nCU1E7 z*Xk$U!WbqSN;ez0PL;fO1zZ>JpYq}!^7uQvks{oXKC@{Pmb@_7tV zq@A`Gx~&OqX=F2Y!`1BeuXMtT_O5oY=M?7?4~QIfK;Yf&q}0_qqB$$_ zzz21)1_p!35hvnp4|@x@j4!f(FUbj{Uf>LM%_^d+k%|j^VA!^DiCUcdu5T)>P z$-oVPz%0p;$aB)lytqReJ=g*!8ooTMr3}s%0W}CMlmci?lU8OnO&Y*D^I<(Ar_{*Hmb0H7sIQt6@kZ^0L)zyP<{!#97YdLX+jX= znMZvuwvcW#u)nF^Sh&uUmEA!n{~Wy9lv=zK2YO&s?^1osW522HQW|gkSPSnJFyK*YfTt>z}2{2^nt@{Ll3dG-d{!w@(uPZ4R6a`6wY=28qfrCoF zOxp-4Ni!K_+l!|zlN3J-@2DsQT@YA{HOY%1(>??Xl1RJBy8(V!%iF-9AAS~IIz3yfd-tnm6;iS= zjB=(lJqipWm`A5Ap?tJKmHvLw%pBnJ_d^;~cv-39KMij$fF*GGRxSlVk_hlZi}fnk zqQ%8J8dqlLIMv^@AG1S2f&-^wr#zy|L^bR9o7}vK((-TGK_m@DH;(m51-AY z0b&B&%KK88V4V*RXkK1#*#i%R_eKzJ9+-|b4yW`P;(6aTu)<9Va1~$c@Z zSN+KoR(iz9Ks(b`lz9bs0=yj{J`a2eZ;A4CzuXHMz>?0DJgIa9@^nn{E37Us!|~wv z74O$;zuUp!6am&~YwdxHsKy?;bY{zF16GAks)Fc*dJ~$Ky}8ZfJNGKy{&N*&7nv>_ z*JZcqH7X&!V@)dBT34N2L?t5YHw)qn;4rumK+qRyNDvF7n@WuBXS!&TwsU)QYR5cb zfF+|UBRvW>GA`3M(i>KqZTjCt&?i+>&kP=wgt2EM+(TxbVVn70`?$Ur6*;b|c zBE*R$FKlE1Ydy=ydkbg6ZRzD%4X}1{Yj&p^4acJ#B!F(jDzy;e9%}{$Ors>GCr;4{ z%S&&-A79C31!2?PLaH}9s67}rZ_W%R?NqWE5RnnO2bKVy4XOWMu-=@%hVcWoT?pW88AUzpKC1VPk279-v|jq#iR=4} ziL-DnooN8aCwbV&MX|K8KPGCeNZb79yjj+VQ4`dWZJa6k6kq7cGOUS=Q~e!A>#xls!LAPhRP ztNX1b#Kt1gtLL{!jA*|hUKoY<8b%YWu6f@5Ve+m9ugA_IBga>xhCqw!mX2(q&@%Y> z3$*<0#`O*VmGql+1|n(#@i335bu|Mc8ypGrXzf-_&)^j|LXNsi8!S?@PG_)cn-{H@ ze660m4d?6tA`^DNW%+z*=S@9}*mWW-wmH-Mny#_ia56E-yAYFV!8dbCSYk zp^e3F16X()kYx`oawCDgb?%MqwG_bT$Vi@+zLSjVS5z|GtTQ0bVCY`$c?!1%I$t2) z1>*GprqKlLAUD+0B+-pLiYFW1pYy{&Nis-qqBE7~jc;H#3*b?3$8RM^0lo^o+8o9| zFL++-wg(C>ZOFw7(^+qPi*HlnSb%TfRW3cJoH=fCU5OQ@dH1_%<014P{n7{E%B|Z` z(~oOt0bVs^=5FS=rELjJh$XL_E+G8MAtJU0YYXrlJ*>L=+EFsM(9=RSvakg>mLQfE zgK8w>OBc{QqO&#Aztjm>yn|kw>V*y2aIr>#=S4I!hxjzH^A5gm*`nUP$Hxkq%}jv# zL)q`&dI;Q*4dP~RCx9h6;Xy3w^1IE7LHmCImVhKvXy$qDW)yT1vCi~rc?1E@Hik6& zDvLV_P|3+q&~LE-RP64yW7}*?cj7FM$~E3M0$c*GqJgaq3-mn|(s(gv#Nz=~dbH{@ zc(M4t>VH&9+7vUKillY5&7IwzX(dZD<$wEDh*`VroipK@lyP<%b^NWw zldluA7nakea>+yUjT%W7)adxEPd!_B0I(C2Uh;c?9tD>I8Ufg|64Gg@u2R|x%}qg; zb`)*>zc)-DEYkWw9&j4O*F`FOd*&&&_d6Z$HhjMaX5jMa>@-^DQH^(6P`Et3#$iU_ zC7pDAtGNz@=Y?riXef{WTy(JsX&0jL#r4GX6|mYs^nAy?J{J~fsutTmIJzz^UNt>B z8{l*4ZV7{f%00000NkvXX Hu0mjfXH(f- literal 2884 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 iR539+GBi3hI4dwQIxsMs^94u%0000 Date: Thu, 24 Mar 2022 15:52:19 +0000 Subject: [PATCH 35/36] first hack at support for running the App Loader inside Gadgetbridge --- gadgetbridge.js | 162 ++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 1 + 2 files changed, 163 insertions(+) create mode 100644 gadgetbridge.js diff --git a/gadgetbridge.js b/gadgetbridge.js new file mode 100644 index 000000000..679fffc60 --- /dev/null +++ b/gadgetbridge.js @@ -0,0 +1,162 @@ +/* Detects if we're running under Gadgetbridge in a WebView, and if +so it overwrites the 'Puck' library with a special one that calls back +into Gadgetbridge to handle watch communications */ + +/*// test code +Android = { + bangleTx : function(data) { + console.log("TX : "+JSON.stringify(data)); + } +};*/ + +if (typeof Android!=="undefined") { + console.log("Running under Gadgetbridge, overwrite Puck library"); + + var isBusy = false; + var queue = []; + var connection = { + cb : function(data) {}, + write : function(data, writecb) { + Android.bangleTx(data); + Puck.writeProgress(data.length, data.length); + if (writecb) setTimeout(writecb,10); + }, + close : function() {}, + received : "", + hadData : false + } + + function bangleRx(data) { +// document.getElementById("status").innerText = "RX:"+data; + connection.received += data; + connection.hadData = true; + if (connection.cb) connection.cb(data); + } + + function log(level, s) { + if (Puck.log) Puck.log(level, s); + } + + function handleQueue() { + if (!queue.length) return; + var q = queue.shift(); + log(3,"Executing "+JSON.stringify(q)+" from queue"); + if (q.type == "write") Puck.write(q.data, q.callback, q.callbackNewline); + else log(1,"Unknown queue item "+JSON.stringify(q)); + } + + /* convenience function... Write data, call the callback with data: + callbackNewline = false => if no new data received for ~0.2 sec + callbackNewline = true => after a newline */ + function write(data, callback, callbackNewline) { + let result; + /// If there wasn't a callback function, then promisify + if (typeof callback !== 'function') { + callbackNewline = callback; + + result = new Promise((resolve, reject) => callback = (value, err) => { + if (err) reject(err); + else resolve(value); + }); + } + + if (isBusy) { + log(3, "Busy - adding Puck.write to queue"); + queue.push({type:"write", data:data, callback:callback, callbackNewline:callbackNewline}); + return result; + } + + var cbTimeout; + function onWritten() { + if (callbackNewline) { + connection.cb = function(d) { + var newLineIdx = connection.received.indexOf("\n"); + if (newLineIdx>=0) { + var l = connection.received.substr(0,newLineIdx); + connection.received = connection.received.substr(newLineIdx+1); + connection.cb = undefined; + if (cbTimeout) clearTimeout(cbTimeout); + cbTimeout = undefined; + if (callback) + callback(l); + isBusy = false; + handleQueue(); + } + }; + } + // wait for any received data if we have a callback... + var maxTime = 300; // 30 sec - Max time we wait in total, even if getting data + var dataWaitTime = callbackNewline ? 100/*10 sec if waiting for newline*/ : 3/*300ms*/; + var maxDataTime = dataWaitTime; // max time we wait after having received data + cbTimeout = setTimeout(function timeout() { + cbTimeout = undefined; + if (maxTime) maxTime--; + if (maxDataTime) maxDataTime--; + if (connection.hadData) maxDataTime=dataWaitTime; + if (maxDataTime && maxTime) { + cbTimeout = setTimeout(timeout, 100); + } else { + connection.cb = undefined; + if (callback) + callback(connection.received); + isBusy = false; + handleQueue(); + connection.received = ""; + } + connection.hadData = false; + }, 100); + } + + if (!connection.txInProgress) connection.received = ""; + isBusy = true; + connection.write(data, onWritten); + return result + } + + // ---------------------------------------------------------- + + Puck = { + /// Are we writing debug information? 0 is no, 1 is some, 2 is more, 3 is all. + debug : Puck.debug, + /// Should we use flow control? Default is true + flowControl : true, + /// Used internally to write log information - you can replace this with your own function + log : function(level, s) { if (level <= this.debug) console.log(" "+s)}, + /// Called with the current send progress or undefined when done - you can replace this with your own function + writeProgress : Puck.writeProgress, + connect : function(callback) { + setTimeout(callback, 10); + }, + write : write, + eval : function(expr, cb) { + const response = write('\x10Bluetooth.println(JSON.stringify(' + expr + '))\n', true) + .then(function (d) { + try { + return JSON.parse(d); + } catch (e) { + log(1, "Unable to decode " + JSON.stringify(d) + ", got " + e.toString()); + return Promise.reject(d); + } + }); + if (cb) { + return void response.then(cb, (err) => cb(null, err)); + } else { + return response; + } + }, + isConnected : function() { return true; }, + getConnection : function() { return connection; }, + close : function() { + if (connection) + connection.close(); + }, + }; + // no need for header + document.getElementsByTagName("header")[0].style="display:none"; + // force connection attempt automatically + setTimeout(function() { + getInstalledApps(true).catch(err => { + showToast("Device connection failed, "+err,"error"); + }); + }, 100); +} diff --git a/index.html b/index.html index a418b48eb..bd8ddea5a 100644 --- a/index.html +++ b/index.html @@ -179,5 +179,6 @@ + From 0a347ee03166662d5dbed0eb3d7ba292079dd15f Mon Sep 17 00:00:00 2001 From: Gordon Williams Date: Thu, 24 Mar 2022 16:16:25 +0000 Subject: [PATCH 36/36] boot 0.46: Fix no clock found error on Bangle.js 2 --- apps/boot/ChangeLog | 1 + apps/boot/bootloader.js | 2 +- apps/boot/metadata.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog index ef437fe3b..87b5f7def 100644 --- a/apps/boot/ChangeLog +++ b/apps/boot/ChangeLog @@ -49,3 +49,4 @@ 0.43: Fix Gadgetbridge handling with Programmable:off 0.44: Write .boot0 without ever having it all in RAM (fix Bangle.js 1 issues with BTHRM) 0.45: Fix 0.44 regression (auto-add semi-colon between each boot code chunk) +0.46: Fix no clock found error on Bangle.js 2 diff --git a/apps/boot/bootloader.js b/apps/boot/bootloader.js index 3cf885ac9..45e271f30 100644 --- a/apps/boot/bootloader.js +++ b/apps/boot/bootloader.js @@ -14,6 +14,6 @@ if (!clockApp) { if (clockApp) clockApp = require("Storage").read(clockApp.src); } -if (!clockApp) clockApp=`E.showMessage("No Clock Found");setWatch(()=>{Bangle.showLauncher();}, BTN2, {repeat:false,edge:"falling"});`; +if (!clockApp) clockApp=`E.showMessage("No Clock Found");setWatch(()=>{Bangle.showLauncher();}, global.BTN2||BTN, {repeat:false,edge:"falling"});`; eval(clockApp); delete clockApp; diff --git a/apps/boot/metadata.json b/apps/boot/metadata.json index c21ab6833..11884576f 100644 --- a/apps/boot/metadata.json +++ b/apps/boot/metadata.json @@ -1,7 +1,7 @@ { "id": "boot", "name": "Bootloader", - "version": "0.45", + "version": "0.46", "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", "icon": "bootloader.png", "type": "bootloader",