From 4079123c93bed52b653a8d53f4520aafc33c16b9 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sat, 19 Aug 2023 18:21:21 -0500 Subject: [PATCH 001/423] =?UTF-8?q?WIP:=20New=20app=20=E2=80=9Cstamplog?= =?UTF-8?q?=E2=80=9D,=20with=20partial=20implementation=20of=20main=20scre?= =?UTF-8?q?en?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/stamplog/app.js | 340 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 apps/stamplog/app.js diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js new file mode 100644 index 000000000..df11c5f15 --- /dev/null +++ b/apps/stamplog/app.js @@ -0,0 +1,340 @@ +Layout = require('Layout'); +locale = require('locale'); +storage = require('Storage'); + +// Storage filename to store user's timestamp log +const LOG_FILENAME = 'stamplog.json'; + +// Min number of pixels of movement to recognize a touchscreen drag/swipe +const DRAG_THRESHOLD = 10; + +var settings = { + logItemFont: '12x20' +}; + + +// Fetch a stringified image +function getIcon(id) { + if (id == 'add') { +// Graphics.createImage(` +// XX X X X X +// XX X X X X +// XXXXXX X X X X +// XXXXXX X X X X +// XX X X X X +// XX X X X X +// X XX X X +// X X X X +// X XX X +// X X X +// X X +// X XX +// XXX XX +// XXXXX +// XXXX +// XX +// `); + return "\0\x17\x10\x81\x000\t\x12`$K\xF0\x91'\xE2D\x83\t\x12\x06$H\x00\xB1 \x01$\x80\x042\x00\b(\x00 \x00A\x80\x01\xCC\x00\x03\xE0\x00\x0F\x00\x00\x18\x00\x00"; + } else if (id == 'menu') { +// Graphics.createImage(` +// +// +// +// +// XXXXXXXXXXXXXXXX +// XXXXXXXXXXXXXXXX +// +// +// XXXXXXXXXXXXXXXX +// XXXXXXXXXXXXXXXX +// +// +// XXXXXXXXXXXXXXXX +// XXXXXXXXXXXXXXXX +// +// +// `); + return "\0\x10\x10\x81\0\0\0\0\0\0\0\0\0\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\0\0\0\0\xFF\xFF\xFF\xFF\0\0\0\0"; + } +} + + +//// Data models ////////////////////////////////// + +// High-level timestamp log object that provides an interface to the +// UI for managing log entries and automatically loading/saving +// changes to flash storage. +class StampLog { + constructor(filename) { + // Name of file to save log to + this.filename = filename; + + // `true` when we have changes that need to be saved + this.isDirty = false; + // Wait at most this many msec upon first data change before + // saving (this is to avoid excessive writes to flash if several + // changes happen quickly; we wait a little bit so they can be + // rolled into a single write) + this.saveTimeout = 30000; + // setTimeout ID for scheduled save job + this.saveId = null; + // Underlying raw log data object. Outside this class it's + // recommended to use only the class methods to change it rather + // than modifying the object directly to ensure that changes are + // recognized and saved to storage. + this.log = this.load(); + } + + // Return the version of the log data that is currently in storage + load() { + let log = storage.readJSON(this.filename, true); + if (!log) log = []; + // Convert stringified datetimes back into Date objects + for (let logEntry of log) { + logEntry.stamp = new Date(logEntry.stamp); + } + return log; + } + + // Write current log data to storage if anything needs to be saved + save() { + // Cancel any pending scheduled calls to save() + if (this.saveId) { + clearTimeout(this.saveId); + this.saveId = null; + } + + if (this.isDirty) { + if (storage.writeJSON(this.filename, this.log)) { + console.log('stamplog: save to storage completed'); + this.isDirty = false; + } else { + console.log('stamplog: save to storage FAILED'); + } + } else { + console.log('stamplog: skipping save to storage because no changes made'); + } + } + + // Mark log as needing to be (re)written to storage + setDirty() { + this.isDirty = true; + if (!this.saveId) { + this.saveId = setTimeout(this.save.bind(this), this.saveTimeout); + } + } + + // Add a timestamp for the current time to the end of the log + addEntry() { + this.log.push({ + stamp: new Date() + }); + this.setDirty(); + } + + // Delete the log objects given in the array `entries` from the log + deleteEntries(entries) { + this.log = this.log.filter(entry => !entries.includes(entry)); + this.setDirty(); + } +} + + +//// UI /////////////////////////////////////////// + +// UI layout render callback for log entries +function renderLogItem(elem) { + if (elem.item) { + g.setColor(g.theme.bg) + .fillRect(elem.x, elem.y, elem.x + elem.w - 1, elem.y + elem.h - 1) + .setFont('12x20') + .setFontAlign(-1, -1) + .setColor(g.theme.fg) + .drawLine(elem.x, elem.y, elem.x + elem.w - 1, elem.y) + .drawString(locale.date(elem.item.stamp, 1) + + '\n' + + locale.time(elem.item.stamp).trim(), + elem.x, elem.y); + } else { + g.setColor(g.blendColor(g.theme.bg, g.theme.fg, 0.25)) + .fillRect(elem.x, elem.y, elem.x + elem.w - 1, elem.y + elem.h - 1); + } +} + +// Main app screen interface, launched by calling start() +class MainScreen { + constructor(stampLog) { + this.stampLog = stampLog; + + // Values set up by start() + this.logItemsPerPage = null; + this.logScrollPos = null; + this.layout = null; + + // Handlers/listeners + this.buttonTimeoutId = null; + this.listeners = {}; + } + + // Launch this UI and make it live + start() { + this.layout = this.getLayout(); + mainScreen.scrollLog('b'); + mainScreen.render(); + + Object.assign(this.listeners, this.getTouchListeners()); + Bangle.on('drag', this.listeners.drag); + } + + // Stop this UI, shut down all timers/listeners, and otherwise clean up + stop() { + if (this.buttonTimeoutId) { + clearTimeout(this.buttonTimeoutId); + this.buttonTimeoutId = null; + } + + // Kill layout handlers + Bangle.removeListener('drag', this.listeners.drag); + Bangle.setUI(); + + // Probably not necessary, but provides feedback for debugging :-) + g.clear(); + } + + // Generate the layout structure for the main UI + getLayout() { + let layout = new Layout( + {type: 'v', + c: [ + // Placeholder to force bottom alignment when there is unused + // vertical screen space + {type: '', id: 'placeholder', fillx: 1, filly: 1}, + + {type: 'v', + id: 'logItems', + + // To be filled in with log item elements once we determine + // how many will fit on screen + c: [], + }, + + {type: 'h', + id: 'buttons', + c: [ + {type: 'btn', font: '6x8:2', fillx: 1, label: '+ XX:XX', id: 'addBtn', + cb: this.addTimestamp.bind(this)}, + {type: 'btn', font: '6x8:2', label: getIcon('menu'), id: 'menuBtn', + cb: L => console.log(L)}, + ], + }, + ], + } + ); + + // Calculate how many log items per page we have space to display + layout.update(); + let availableHeight = layout.placeholder.h; + g.setFont(settings.logItemFont); + let logItemHeight = g.getFontHeight() * 2; + this.logItemsPerPage = Math.floor(availableHeight / logItemHeight); + + // Populate log items in layout + for (i = 0; i < this.logItemsPerPage; i++) { + layout.logItems.c.push( + {type: 'custom', render: renderLogItem, item: undefined, fillx: 1, height: logItemHeight} + ); + } + layout.update(); + + return layout; + } + + // Redraw a particular display `item`, or everything if `item` is falsey + render(item) { + if (!item || item == 'log') { + let layLogItems = this.layout.logItems; + let logIdx = this.logScrollPos - this.logItemsPerPage; + for (let elem of layLogItems.c) { + logIdx++; + elem.item = this.stampLog.log[logIdx]; + } + this.layout.render(layLogItems); + } + + if (!item || item == 'buttons') { + this.layout.addBtn.label = getIcon('add') + ' ' + locale.time(new Date(), 1).trim(); + this.layout.render(this.layout.buttons); + + // Auto-update time of day indication on log-add button upon next minute + if (!this.buttonTimeoutId) { + this.buttonTimeoutId = setTimeout( + () => { + this.buttonTimeoutId = null; + this.render('buttons'); + }, + 60000 - (Date.now() % 60000) + ); + } + } + + } + + getTouchListeners() { + let distanceY = null; + + function dragHandler(ev) { + // Handle up/down swipes for scrolling + if (ev.b) { + if (distanceY === null) { + // Drag started + distanceY = ev.dy; + } else { + // Drag in progress + distanceY += ev.dy; + } + } else { + // Drag ended + if (Math.abs(distanceY) > DRAG_THRESHOLD) { + this.scrollLog(distanceY > 0 ? 'u' : 'd'); + this.render('log'); + } + distanceY = null; + } + } + + return { + 'drag': dragHandler.bind(this), + }; + } + + // Add current timestamp to log and update UI display + addTimestamp() { + this.stampLog.addEntry(); + this.scrollLog('b'); + this.render('log'); + } + + // Scroll display in given direction or to given position: + // 'u': up, 'd': down, 't': to top, 'b': to bottom + scrollLog(how) { + top = (this.stampLog.log.length - 1) % this.logItemsPerPage; + bottom = this.stampLog.log.length - 1; + + if (how == 'u') { + this.logScrollPos -= this.logItemsPerPage; + } else if (how == 'd') { + this.logScrollPos += this.logItemsPerPage; + } else if (how == 't') { + this.logScrollPos = top; + } else if (how == 'b') { + this.logScrollPos = bottom; + } + + this.logScrollPos = E.clip(this.logScrollPos, top, bottom); + } +} + + +stampLog = new StampLog(); +mainScreen = new MainScreen(stampLog); +mainScreen.start(); From a7024aa52b57c7659a81d2fb3fe4e615a1878e1b Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Mon, 21 Aug 2023 15:01:42 -0500 Subject: [PATCH 002/423] Save log on quit and display some kind of alert if error occurs saving --- apps/stamplog/app.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index df11c5f15..f14777448 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -110,6 +110,7 @@ class StampLog { this.isDirty = false; } else { console.log('stamplog: save to storage FAILED'); + this.emit('saveError'); } } else { console.log('stamplog: skipping save to storage because no changes made'); @@ -335,6 +336,15 @@ class MainScreen { } +Bangle.loadWidgets(); +Bangle.drawWidgets(); + stampLog = new StampLog(); +E.on('kill', stampLog.save.bind(stampLog)); +stampLog.on('saveError', () => { + E.showAlert('Trouble saving timestamp log: Data may be lost!', + "Can't save log"); +}); + mainScreen = new MainScreen(stampLog); mainScreen.start(); From 3b5fe9c4a8e1a55f80c45d06f7e661922aaa5214 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Thu, 24 Aug 2023 15:12:25 -0500 Subject: [PATCH 003/423] Fix accidental use of global var instead of this --- apps/stamplog/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index f14777448..6263fc85a 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -180,8 +180,8 @@ class MainScreen { // Launch this UI and make it live start() { this.layout = this.getLayout(); - mainScreen.scrollLog('b'); - mainScreen.render(); + this.scrollLog('b'); + this.render(); Object.assign(this.listeners, this.getTouchListeners()); Bangle.on('drag', this.listeners.drag); From 705d6619a8d1c3b960ebeb1f49850d8f57f6f353 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Thu, 24 Aug 2023 15:14:34 -0500 Subject: [PATCH 004/423] Make save error fit screen better and try to avoid disrupting current UI --- apps/stamplog/app.js | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index 6263fc85a..d01102dd6 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -197,9 +197,6 @@ class MainScreen { // Kill layout handlers Bangle.removeListener('drag', this.listeners.drag); Bangle.setUI(); - - // Probably not necessary, but provides feedback for debugging :-) - g.clear(); } // Generate the layout structure for the main UI @@ -336,15 +333,25 @@ class MainScreen { } +function saveErrorAlert() { + currentUI.stop(); + E.showPrompt( + 'Trouble saving timestamp log; data may be lost!', + {title: "Can't save log", + img: '', + buttons: {'Ok': true}, + } + ).then(currentUI.start.bind(currentUI)); +} + + Bangle.loadWidgets(); Bangle.drawWidgets(); stampLog = new StampLog(); E.on('kill', stampLog.save.bind(stampLog)); -stampLog.on('saveError', () => { - E.showAlert('Trouble saving timestamp log: Data may be lost!', - "Can't save log"); -}); +stampLog.on('saveError', saveErrorAlert); + +var currentUI = new MainScreen(stampLog); +currentUI.start(); -mainScreen = new MainScreen(stampLog); -mainScreen.start(); From 575c09436303e574785607ce0dd954009992bf44 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Fri, 25 Aug 2023 23:30:54 -0500 Subject: [PATCH 005/423] Minor refactoring --- apps/stamplog/app.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index d01102dd6..02698cdaa 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -164,6 +164,7 @@ function renderLogItem(elem) { // Main app screen interface, launched by calling start() class MainScreen { + constructor(stampLog) { this.stampLog = stampLog; @@ -179,12 +180,11 @@ class MainScreen { // Launch this UI and make it live start() { - this.layout = this.getLayout(); + this._initLayout(); this.scrollLog('b'); this.render(); - Object.assign(this.listeners, this.getTouchListeners()); - Bangle.on('drag', this.listeners.drag); + this._initTouch(); } // Stop this UI, shut down all timers/listeners, and otherwise clean up @@ -199,8 +199,7 @@ class MainScreen { Bangle.setUI(); } - // Generate the layout structure for the main UI - getLayout() { + _initLayout() { let layout = new Layout( {type: 'v', c: [ @@ -244,7 +243,7 @@ class MainScreen { } layout.update(); - return layout; + this.layout = layout; } // Redraw a particular display `item`, or everything if `item` is falsey @@ -263,7 +262,8 @@ class MainScreen { this.layout.addBtn.label = getIcon('add') + ' ' + locale.time(new Date(), 1).trim(); this.layout.render(this.layout.buttons); - // Auto-update time of day indication on log-add button upon next minute + // Auto-update time of day indication on log-add button upon + // next minute if (!this.buttonTimeoutId) { this.buttonTimeoutId = setTimeout( () => { @@ -277,7 +277,7 @@ class MainScreen { } - getTouchListeners() { + _initTouch() { let distanceY = null; function dragHandler(ev) { @@ -300,9 +300,8 @@ class MainScreen { } } - return { - 'drag': dragHandler.bind(this), - }; + this.listeners.drag = dragHandler.bind(this); + Bangle.on('drag', this.listeners.drag); } // Add current timestamp to log and update UI display @@ -335,12 +334,12 @@ class MainScreen { function saveErrorAlert() { currentUI.stop(); + // Not `showAlert` because the icon plus message don't fit the + // screen well E.showPrompt( 'Trouble saving timestamp log; data may be lost!', {title: "Can't save log", - img: '', - buttons: {'Ok': true}, - } + buttons: {'Ok': true}} ).then(currentUI.start.bind(currentUI)); } From 1307d3b21c76bb23ac31ec8c8d3e991422867c75 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sun, 27 Aug 2023 21:53:03 -0500 Subject: [PATCH 006/423] Implement scroll bar for log display --- apps/stamplog/app.js | 84 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 71 insertions(+), 13 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index 02698cdaa..2e05dddc7 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -8,6 +8,9 @@ const LOG_FILENAME = 'stamplog.json'; // Min number of pixels of movement to recognize a touchscreen drag/swipe const DRAG_THRESHOLD = 10; +// Width of scroll indicators +const SCROLL_BAR_WIDTH = 12; + var settings = { logItemFont: '12x20' }; @@ -162,6 +165,42 @@ function renderLogItem(elem) { } } +// Render a scroll indicator +// `scroll` format: { +// pos: int, +// min: int, +// max: int, +// itemsPerPage: int, +// } +function renderScrollBar(elem, scroll) { + const border = 1; + const boxArea = elem.h - 2 * border; + const boxSize = E.clip( + Math.round( + scroll.itemsPerPage / (scroll.max - scroll.min + 1) * (elem.h - 2) + ), + 3, + boxArea + ); + const boxTop = (scroll.max - scroll.min) ? + Math.round( + (scroll.pos - scroll.min) / (scroll.max - scroll.min) + * (boxArea - boxSize) + elem.y + border + ) : elem.y + border; + + // Draw border + g.setColor(g.theme.fg) + .fillRect(elem.x, elem.y, elem.x + elem.w - 1, elem.y + elem.h - 1) + // Draw scroll box area + .setColor(g.theme.bg) + .fillRect(elem.x + border, elem.y + border, + elem.x + elem.w - border - 1, elem.y + elem.h - border - 1) + // Draw scroll box + .setColor(g.blendColor(g.theme.bg, g.theme.fg, 0.5)) + .fillRect(elem.x + border, boxTop, + elem.x + elem.w - border - 1, boxTop + boxSize - 1); +} + // Main app screen interface, launched by calling start() class MainScreen { @@ -207,14 +246,21 @@ class MainScreen { // vertical screen space {type: '', id: 'placeholder', fillx: 1, filly: 1}, - {type: 'v', - id: 'logItems', + {type: 'h', + c: [ + {type: 'v', + id: 'logItems', - // To be filled in with log item elements once we determine - // how many will fit on screen - c: [], + // To be filled in with log item elements once we + // determine how many will fit on screen + c: [], + }, + {type: 'custom', + id: 'logScroll', + render: elem => { renderScrollBar(elem, this.logScrollInfo()); } + }, + ], }, - {type: 'h', id: 'buttons', c: [ @@ -241,6 +287,8 @@ class MainScreen { {type: 'custom', render: renderLogItem, item: undefined, fillx: 1, height: logItemHeight} ); } + layout.logScroll.height = logItemHeight * this.logItemsPerPage; + layout.logScroll.width = SCROLL_BAR_WIDTH; layout.update(); this.layout = layout; @@ -256,6 +304,7 @@ class MainScreen { elem.item = this.stampLog.log[logIdx]; } this.layout.render(layLogItems); + this.layout.render(this.layout.logScroll); } if (!item || item == 'buttons') { @@ -311,23 +360,32 @@ class MainScreen { this.render('log'); } + // Get scroll information for log display + logScrollInfo() { + return { + pos: this.logScrollPos, + min: (this.stampLog.log.length - 1) % this.logItemsPerPage, + max: this.stampLog.log.length - 1, + itemsPerPage: this.logItemsPerPage + }; + } + // Scroll display in given direction or to given position: // 'u': up, 'd': down, 't': to top, 'b': to bottom scrollLog(how) { - top = (this.stampLog.log.length - 1) % this.logItemsPerPage; - bottom = this.stampLog.log.length - 1; + let scroll = this.logScrollInfo(); if (how == 'u') { - this.logScrollPos -= this.logItemsPerPage; + this.logScrollPos -= scroll.itemsPerPage; } else if (how == 'd') { - this.logScrollPos += this.logItemsPerPage; + this.logScrollPos += scroll.itemsPerPage; } else if (how == 't') { - this.logScrollPos = top; + this.logScrollPos = scroll.min; } else if (how == 'b') { - this.logScrollPos = bottom; + this.logScrollPos = scroll.max; } - this.logScrollPos = E.clip(this.logScrollPos, top, bottom); + this.logScrollPos = E.clip(this.logScrollPos, scroll.min, scroll.max); } } From b64806e96a2f3cbabb7b2ba5ffc5f6b7328471e4 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sun, 27 Aug 2023 21:55:40 -0500 Subject: [PATCH 007/423] Shorten some names --- apps/stamplog/app.js | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index 2e05dddc7..c979bf349 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -208,8 +208,8 @@ class MainScreen { this.stampLog = stampLog; // Values set up by start() - this.logItemsPerPage = null; - this.logScrollPos = null; + this.itemsPerPage = null; + this.scrollPos = null; this.layout = null; // Handlers/listeners @@ -220,7 +220,7 @@ class MainScreen { // Launch this UI and make it live start() { this._initLayout(); - this.scrollLog('b'); + this.scroll('b'); this.render(); this._initTouch(); @@ -257,7 +257,7 @@ class MainScreen { }, {type: 'custom', id: 'logScroll', - render: elem => { renderScrollBar(elem, this.logScrollInfo()); } + render: elem => { renderScrollBar(elem, this.scrollInfo()); } }, ], }, @@ -279,15 +279,15 @@ class MainScreen { let availableHeight = layout.placeholder.h; g.setFont(settings.logItemFont); let logItemHeight = g.getFontHeight() * 2; - this.logItemsPerPage = Math.floor(availableHeight / logItemHeight); + this.itemsPerPage = Math.floor(availableHeight / logItemHeight); // Populate log items in layout - for (i = 0; i < this.logItemsPerPage; i++) { + for (i = 0; i < this.itemsPerPage; i++) { layout.logItems.c.push( {type: 'custom', render: renderLogItem, item: undefined, fillx: 1, height: logItemHeight} ); } - layout.logScroll.height = logItemHeight * this.logItemsPerPage; + layout.logScroll.height = logItemHeight * this.itemsPerPage; layout.logScroll.width = SCROLL_BAR_WIDTH; layout.update(); @@ -298,7 +298,7 @@ class MainScreen { render(item) { if (!item || item == 'log') { let layLogItems = this.layout.logItems; - let logIdx = this.logScrollPos - this.logItemsPerPage; + let logIdx = this.scrollPos - this.itemsPerPage; for (let elem of layLogItems.c) { logIdx++; elem.item = this.stampLog.log[logIdx]; @@ -342,7 +342,7 @@ class MainScreen { } else { // Drag ended if (Math.abs(distanceY) > DRAG_THRESHOLD) { - this.scrollLog(distanceY > 0 ? 'u' : 'd'); + this.scroll(distanceY > 0 ? 'u' : 'd'); this.render('log'); } distanceY = null; @@ -356,36 +356,36 @@ class MainScreen { // Add current timestamp to log and update UI display addTimestamp() { this.stampLog.addEntry(); - this.scrollLog('b'); + this.scroll('b'); this.render('log'); } // Get scroll information for log display - logScrollInfo() { + scrollInfo() { return { - pos: this.logScrollPos, - min: (this.stampLog.log.length - 1) % this.logItemsPerPage, + pos: this.scrollPos, + min: (this.stampLog.log.length - 1) % this.itemsPerPage, max: this.stampLog.log.length - 1, - itemsPerPage: this.logItemsPerPage + itemsPerPage: this.itemsPerPage }; } // Scroll display in given direction or to given position: // 'u': up, 'd': down, 't': to top, 'b': to bottom - scrollLog(how) { - let scroll = this.logScrollInfo(); + scroll(how) { + let scroll = this.scrollInfo(); if (how == 'u') { - this.logScrollPos -= scroll.itemsPerPage; + this.scrollPos -= scroll.itemsPerPage; } else if (how == 'd') { - this.logScrollPos += scroll.itemsPerPage; + this.scrollPos += scroll.itemsPerPage; } else if (how == 't') { - this.logScrollPos = scroll.min; + this.scrollPos = scroll.min; } else if (how == 'b') { - this.logScrollPos = scroll.max; + this.scrollPos = scroll.max; } - this.logScrollPos = E.clip(this.logScrollPos, scroll.min, scroll.max); + this.scrollPos = E.clip(this.scrollPos, scroll.min, scroll.max); } } From 12f3a46a7e61598f7fabab5c4b9a4957b3ef72b9 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sun, 27 Aug 2023 21:58:10 -0500 Subject: [PATCH 008/423] Auto render log when scroll() called --- apps/stamplog/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index c979bf349..0b74fc2e1 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -221,7 +221,7 @@ class MainScreen { start() { this._initLayout(); this.scroll('b'); - this.render(); + this.render('buttons'); this._initTouch(); } @@ -343,7 +343,6 @@ class MainScreen { // Drag ended if (Math.abs(distanceY) > DRAG_THRESHOLD) { this.scroll(distanceY > 0 ? 'u' : 'd'); - this.render('log'); } distanceY = null; } @@ -357,7 +356,6 @@ class MainScreen { addTimestamp() { this.stampLog.addEntry(); this.scroll('b'); - this.render('log'); } // Get scroll information for log display @@ -386,6 +384,8 @@ class MainScreen { } this.scrollPos = E.clip(this.scrollPos, scroll.min, scroll.max); + + this.render('log'); } } From e8fc765943316d89dbe449f4257d5ac3f8540764 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sun, 27 Aug 2023 22:28:05 -0500 Subject: [PATCH 009/423] Improve scrolling: haptic feedback, with multiple steps per swipe available --- apps/stamplog/app.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index 0b74fc2e1..4776e173f 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -6,7 +6,7 @@ storage = require('Storage'); const LOG_FILENAME = 'stamplog.json'; // Min number of pixels of movement to recognize a touchscreen drag/swipe -const DRAG_THRESHOLD = 10; +const DRAG_THRESHOLD = 30; // Width of scroll indicators const SCROLL_BAR_WIDTH = 12; @@ -340,10 +340,13 @@ class MainScreen { distanceY += ev.dy; } } else { - // Drag ended - if (Math.abs(distanceY) > DRAG_THRESHOLD) { - this.scroll(distanceY > 0 ? 'u' : 'd'); - } + // Drag released + distanceY = null; + } + if (Math.abs(distanceY) > DRAG_THRESHOLD) { + // Scroll threshold reached + Bangle.buzz(50, .2); + this.scroll(distanceY > 0 ? 'u' : 'd'); distanceY = null; } } From 43727c1fdbeab688aaf020e1bdaa9cc75d6e5b48 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sat, 2 Sep 2023 17:14:50 -0500 Subject: [PATCH 010/423] Implement fixed log size (new entries replace old) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also fix saving log to Storage file named “undefined” instead of correct name --- apps/stamplog/app.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index 4776e173f..71269fb43 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -12,7 +12,8 @@ const DRAG_THRESHOLD = 30; const SCROLL_BAR_WIDTH = 12; var settings = { - logItemFont: '12x20' + logItemFont: '12x20', + maxLogLength: 6 }; @@ -68,9 +69,12 @@ function getIcon(id) { // UI for managing log entries and automatically loading/saving // changes to flash storage. class StampLog { - constructor(filename) { + constructor(filename, maxLength) { // Name of file to save log to this.filename = filename; + // Maximum entries for log before old entries are overwritten with + // newer ones + this.maxLength = maxLength; // `true` when we have changes that need to be saved this.isDirty = false; @@ -130,6 +134,13 @@ class StampLog { // Add a timestamp for the current time to the end of the log addEntry() { + // If log full, purge an old entry to make room for new one + if (this.maxLength) { + while (this.log.length + 1 > this.maxLength) { + this.log.shift(); + } + } + // Add new entry this.log.push({ stamp: new Date() }); @@ -408,7 +419,7 @@ function saveErrorAlert() { Bangle.loadWidgets(); Bangle.drawWidgets(); -stampLog = new StampLog(); +stampLog = new StampLog(LOG_FILENAME, settings.maxLogLength); E.on('kill', stampLog.save.bind(stampLog)); stampLog.on('saveError', saveErrorAlert); From a4a5e1eaf62d8571083584dd36ea5c3b15bddec5 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sat, 2 Sep 2023 18:19:45 -0500 Subject: [PATCH 011/423] Fix scrollbar geometry Nitpick: scrollbar box was larger than the true percentage of screen pages displayed because we weren't taking into account that we only scroll by full screens at a time. --- apps/stamplog/app.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index 71269fb43..7b5975f5f 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -268,7 +268,7 @@ class MainScreen { }, {type: 'custom', id: 'logScroll', - render: elem => { renderScrollBar(elem, this.scrollInfo()); } + render: elem => { renderScrollBar(elem, this.scrollBarInfo()); } }, ], }, @@ -382,6 +382,25 @@ class MainScreen { }; } + // Like scrollInfo, but adjust the data so as to suggest scrollbar + // geometry that accurately reflects the nature of the scrolling + // (page by page rather than item by item) + scrollBarInfo() { + const info = this.scrollInfo(); + + function toPage(scrollPos) { + return Math.floor(scrollPos / info.itemsPerPage); + } + + return { + // Define 1 “screenfull” as the unit here + itemsPerPage: 1, + pos: toPage(info.pos), + min: toPage(info.min), + max: toPage(info.max), + }; + } + // Scroll display in given direction or to given position: // 'u': up, 'd': down, 't': to top, 'b': to bottom scroll(how) { From a741e86a4145e51f539f7f98bf51622ab6ae24d6 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Mon, 4 Sep 2023 22:39:06 -0500 Subject: [PATCH 012/423] Avoid UTF-8 for now since there are encoding problems with the IDE --- apps/stamplog/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index 7b5975f5f..1d54d8e32 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -393,7 +393,7 @@ class MainScreen { } return { - // Define 1 “screenfull” as the unit here + // Define 1 "screenfull" as the unit here itemsPerPage: 1, pos: toPage(info.pos), min: toPage(info.min), From f4b3dd78d82eb7967f303bfb1e77cc6f9105b0a7 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Mon, 4 Sep 2023 23:11:32 -0500 Subject: [PATCH 013/423] Change some default configuration details --- apps/stamplog/app.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index 1d54d8e32..344f183c8 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -3,7 +3,7 @@ locale = require('locale'); storage = require('Storage'); // Storage filename to store user's timestamp log -const LOG_FILENAME = 'stamplog.json'; +const LOG_FILENAME = 'timestamplog.json'; // Min number of pixels of movement to recognize a touchscreen drag/swipe const DRAG_THRESHOLD = 30; @@ -13,7 +13,7 @@ const SCROLL_BAR_WIDTH = 12; var settings = { logItemFont: '12x20', - maxLogLength: 6 + maxLogLength: 30 }; @@ -444,4 +444,3 @@ stampLog.on('saveError', saveErrorAlert); var currentUI = new MainScreen(stampLog); currentUI.start(); - From 4134e9a322587f15e80801a9b4b92918a73a235f Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sat, 9 Sep 2023 11:50:23 -0500 Subject: [PATCH 014/423] Implement basis for settings menu --- apps/stamplog/app.js | 47 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index 344f183c8..d6317c1ca 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -2,8 +2,9 @@ Layout = require('Layout'); locale = require('locale'); storage = require('Storage'); -// Storage filename to store user's timestamp log +// Storage filenames const LOG_FILENAME = 'timestamplog.json'; +const SETTINGS_FILENAME = 'timestamplog.settings.json'; // Min number of pixels of movement to recognize a touchscreen drag/swipe const DRAG_THRESHOLD = 30; @@ -11,10 +12,20 @@ const DRAG_THRESHOLD = 30; // Width of scroll indicators const SCROLL_BAR_WIDTH = 12; -var settings = { + +// Settings + +const SETTINGS = Object.assign({ logItemFont: '12x20', + logItemFontSize: 1, maxLogLength: 30 -}; +}, storage.readJSON(SETTINGS_FILENAME, true) || {}); + +function saveSettings() { + if (!storage.writeJSON(SETTINGS_FILENAME, SETTINGS)) { + E.showAlert('Trouble saving settings', "Can't save settings"); + } +} // Fetch a stringified image @@ -278,7 +289,7 @@ class MainScreen { {type: 'btn', font: '6x8:2', fillx: 1, label: '+ XX:XX', id: 'addBtn', cb: this.addTimestamp.bind(this)}, {type: 'btn', font: '6x8:2', label: getIcon('menu'), id: 'menuBtn', - cb: L => console.log(L)}, + cb: settingsMenu}, ], }, ], @@ -288,7 +299,7 @@ class MainScreen { // Calculate how many log items per page we have space to display layout.update(); let availableHeight = layout.placeholder.h; - g.setFont(settings.logItemFont); + g.setFont(SETTINGS.logItemFont); let logItemHeight = g.getFontHeight() * 2; this.itemsPerPage = Math.floor(availableHeight / logItemHeight); @@ -423,6 +434,30 @@ class MainScreen { } +function settingsMenu() { + function endMenu() { + saveSettings(); + currentUI.start(); + } + + currentUI.stop(); + E.showMenu({ + '': { + title: 'Timestamp Logger', + back: endMenu, + }, + 'Max log size': { + value: SETTINGS.maxLogLength, + min: 5, max: 100, step: 5, + onchange: v => { + SETTINGS.maxLogLength = v; + stampLog.maxLength = v; + } + }, + }); +} + + function saveErrorAlert() { currentUI.stop(); // Not `showAlert` because the icon plus message don't fit the @@ -438,7 +473,7 @@ function saveErrorAlert() { Bangle.loadWidgets(); Bangle.drawWidgets(); -stampLog = new StampLog(LOG_FILENAME, settings.maxLogLength); +stampLog = new StampLog(LOG_FILENAME, SETTINGS.maxLogLength); E.on('kill', stampLog.save.bind(stampLog)); stampLog.on('saveError', saveErrorAlert); From 5a5cb62ebc2db997a5ed13198af409fc2dbda643 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sat, 9 Sep 2023 11:50:38 -0500 Subject: [PATCH 015/423] Make haptic scroll feedback a little stronger --- apps/stamplog/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index d6317c1ca..0663826c7 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -367,7 +367,7 @@ class MainScreen { } if (Math.abs(distanceY) > DRAG_THRESHOLD) { // Scroll threshold reached - Bangle.buzz(50, .2); + Bangle.buzz(50, .5); this.scroll(distanceY > 0 ? 'u' : 'd'); distanceY = null; } From 56d4dae3570a2d582c8380642205f74e4fada702 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sat, 9 Sep 2023 21:08:01 -0500 Subject: [PATCH 016/423] Implement log font selection --- apps/stamplog/app.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index 0663826c7..cd310f913 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -16,8 +16,8 @@ const SCROLL_BAR_WIDTH = 12; // Settings const SETTINGS = Object.assign({ - logItemFont: '12x20', - logItemFontSize: 1, + logFont: '12x20', + logFontSize: 1, maxLogLength: 30 }, storage.readJSON(SETTINGS_FILENAME, true) || {}); @@ -173,7 +173,7 @@ function renderLogItem(elem) { if (elem.item) { g.setColor(g.theme.bg) .fillRect(elem.x, elem.y, elem.x + elem.w - 1, elem.y + elem.h - 1) - .setFont('12x20') + .setFont(SETTINGS.logFont) .setFontAlign(-1, -1) .setColor(g.theme.fg) .drawLine(elem.x, elem.y, elem.x + elem.w - 1, elem.y) @@ -299,7 +299,7 @@ class MainScreen { // Calculate how many log items per page we have space to display layout.update(); let availableHeight = layout.placeholder.h; - g.setFont(SETTINGS.logItemFont); + g.setFont(SETTINGS.logFont); let logItemHeight = g.getFontHeight() * 2; this.itemsPerPage = Math.floor(availableHeight / logItemHeight); @@ -439,6 +439,7 @@ function settingsMenu() { saveSettings(); currentUI.start(); } + const fonts = g.getFonts(); currentUI.stop(); E.showMenu({ @@ -446,6 +447,14 @@ function settingsMenu() { title: 'Timestamp Logger', back: endMenu, }, + 'Log font': { + value: fonts.indexOf(SETTINGS.logFont), + min: 0, max: fonts.length - 1, + format: v => fonts[v], + onchange: v => { + SETTINGS.logFont = fonts[v]; + }, + }, 'Max log size': { value: SETTINGS.maxLogLength, min: 5, max: 100, step: 5, From 51c20c9e49e3a8bcec50608a2fbf0e6f3abd1e06 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sat, 9 Sep 2023 21:08:06 -0500 Subject: [PATCH 017/423] Fix log display cosmetics Clear app display region before restarting log UI so as to not leave old junk behind if new font leaves leftover space at top of screen Draw text starting one pixel below log separator lines so the line doesn't overstrike topmost pixels of text --- apps/stamplog/app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index cd310f913..38f8e1eb2 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -180,7 +180,7 @@ function renderLogItem(elem) { .drawString(locale.date(elem.item.stamp, 1) + '\n' + locale.time(elem.item.stamp).trim(), - elem.x, elem.y); + elem.x, elem.y + 1); } else { g.setColor(g.blendColor(g.theme.bg, g.theme.fg, 0.25)) .fillRect(elem.x, elem.y, elem.x + elem.w - 1, elem.y + elem.h - 1); @@ -242,6 +242,7 @@ class MainScreen { // Launch this UI and make it live start() { this._initLayout(); + this.layout.clear(); this.scroll('b'); this.render('buttons'); From 5a01ae0159c60c3b661d7bc14e1b3a7bf292b2b0 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sat, 9 Sep 2023 21:51:08 -0500 Subject: [PATCH 018/423] Allow both horizontal and vertical font size selection Nifty! --- apps/stamplog/app.js | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index 38f8e1eb2..cc4cfd7af 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -17,7 +17,8 @@ const SCROLL_BAR_WIDTH = 12; const SETTINGS = Object.assign({ logFont: '12x20', - logFontSize: 1, + logFontHSize: 1, + logFontVSize: 1, maxLogLength: 30 }, storage.readJSON(SETTINGS_FILENAME, true) || {}); @@ -27,6 +28,10 @@ function saveSettings() { } } +function fontSpec(name, hsize, vsize) { + return name + ':' + hsize + 'x' + vsize; +} + // Fetch a stringified image function getIcon(id) { @@ -173,7 +178,8 @@ function renderLogItem(elem) { if (elem.item) { g.setColor(g.theme.bg) .fillRect(elem.x, elem.y, elem.x + elem.w - 1, elem.y + elem.h - 1) - .setFont(SETTINGS.logFont) + .setFont(fontSpec(SETTINGS.logFont, + SETTINGS.logFontHSize, SETTINGS.logFontVSize)) .setFontAlign(-1, -1) .setColor(g.theme.fg) .drawLine(elem.x, elem.y, elem.x + elem.w - 1, elem.y) @@ -300,9 +306,11 @@ class MainScreen { // Calculate how many log items per page we have space to display layout.update(); let availableHeight = layout.placeholder.h; - g.setFont(SETTINGS.logFont); + g.setFont(fontSpec(SETTINGS.logFont, + SETTINGS.logFontHSize, SETTINGS.logFontVSize)); let logItemHeight = g.getFontHeight() * 2; - this.itemsPerPage = Math.floor(availableHeight / logItemHeight); + this.itemsPerPage = Math.max(1, + Math.floor(availableHeight / logItemHeight)); // Populate log items in layout for (i = 0; i < this.itemsPerPage; i++) { @@ -448,6 +456,14 @@ function settingsMenu() { title: 'Timestamp Logger', back: endMenu, }, + 'Max log size': { + value: SETTINGS.maxLogLength, + min: 5, max: 100, step: 5, + onchange: v => { + SETTINGS.maxLogLength = v; + stampLog.maxLength = v; + } + }, 'Log font': { value: fonts.indexOf(SETTINGS.logFont), min: 0, max: fonts.length - 1, @@ -456,13 +472,19 @@ function settingsMenu() { SETTINGS.logFont = fonts[v]; }, }, - 'Max log size': { - value: SETTINGS.maxLogLength, - min: 5, max: 100, step: 5, + 'Log font H size': { + value: SETTINGS.logFontHSize, + min: 1, max: 50, onchange: v => { - SETTINGS.maxLogLength = v; - stampLog.maxLength = v; - } + SETTINGS.logFontHSize = v; + }, + }, + 'Log font V size': { + value: SETTINGS.logFontVSize, + min: 1, max: 50, + onchange: v => { + SETTINGS.logFontVSize = v; + }, }, }); } From fe8694c7095801eb8c00e93ffa35f2b1be662c9b Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Mon, 11 Sep 2023 16:18:28 -0500 Subject: [PATCH 019/423] Remove redundant message title --- apps/stamplog/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index cc4cfd7af..0d812eabe 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -24,7 +24,7 @@ const SETTINGS = Object.assign({ function saveSettings() { if (!storage.writeJSON(SETTINGS_FILENAME, SETTINGS)) { - E.showAlert('Trouble saving settings', "Can't save settings"); + E.showAlert('Trouble saving settings'); } } From 0127a9654b37b89b94b0470195f41951d6d708c9 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Thu, 2 Nov 2023 12:28:24 -0500 Subject: [PATCH 020/423] Implement log rotate option and UI support --- apps/stamplog/app.js | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index 0d812eabe..b41c37c43 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -19,7 +19,8 @@ const SETTINGS = Object.assign({ logFont: '12x20', logFontHSize: 1, logFontVSize: 1, - maxLogLength: 30 + maxLogLength: 30, + rotateLog: false, }, storage.readJSON(SETTINGS_FILENAME, true) || {}); function saveSettings() { @@ -168,6 +169,11 @@ class StampLog { this.log = this.log.filter(entry => !entries.includes(entry)); this.setDirty(); } + + // Does the log currently contain the maximum possible number of entries? + isFull() { + return this.log.length >= this.maxLength; + } } @@ -339,7 +345,21 @@ class MainScreen { } if (!item || item == 'buttons') { - this.layout.addBtn.label = getIcon('add') + ' ' + locale.time(new Date(), 1).trim(); + let addBtn = this.layout.addBtn; + + if (!SETTINGS.rotateLog && this.stampLog.isFull()) { + // Dimmed appearance for unselectable button + addBtn.btnFaceCol = g.blendColor(g.theme.bg2, g.theme.bg, 0.5); + addBtn.btnBorderCol = g.blendColor(g.theme.fg2, g.theme.bg, 0.5); + + addBtn.label = 'Log full'; + } else { + addBtn.btnFaceCol = g.theme.bg2; + addBtn.btnBorderCol = g.theme.fg2; + + addBtn.label = getIcon('add') + ' ' + locale.time(new Date(), 1).trim(); + } + this.layout.render(this.layout.buttons); // Auto-update time of day indication on log-add button upon @@ -386,10 +406,13 @@ class MainScreen { Bangle.on('drag', this.listeners.drag); } - // Add current timestamp to log and update UI display + // Add current timestamp to log if possible and update UI display addTimestamp() { - this.stampLog.addEntry(); - this.scroll('b'); + if (SETTINGS.rotateLog || !this.stampLog.isFull()) { + this.stampLog.addEntry(); + this.scroll('b'); + this.render('buttons'); + } } // Get scroll information for log display @@ -464,6 +487,12 @@ function settingsMenu() { stampLog.maxLength = v; } }, + 'Rotate log entries': { + value: SETTINGS.rotateLog, + onchange: v => { + SETTINGS.rotateLog = !SETTINGS.rotateLog; + } + }, 'Log font': { value: fonts.indexOf(SETTINGS.logFont), min: 0, max: fonts.length - 1, From 2872ed87eccb057e36b06990812fff09aa45e130 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Thu, 2 Nov 2023 15:41:37 -0500 Subject: [PATCH 021/423] Break up settings menu into submenus Although there aren't very many items yet, I feel that minimizing scrolling on the Bangle.js 2 touchscreen makes menu navigation and use easier. --- apps/stamplog/app.js | 113 ++++++++++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 44 deletions(-) diff --git a/apps/stamplog/app.js b/apps/stamplog/app.js index b41c37c43..b9184efaf 100644 --- a/apps/stamplog/app.js +++ b/apps/stamplog/app.js @@ -169,7 +169,7 @@ class StampLog { this.log = this.log.filter(entry => !entries.includes(entry)); this.setDirty(); } - + // Does the log currently contain the maximum possible number of entries? isFull() { return this.log.length >= this.maxLength; @@ -467,55 +467,80 @@ class MainScreen { function settingsMenu() { + const fonts = g.getFonts(); + + function topMenu() { + E.showMenu({ + '': { + title: 'Stamplog', + back: endMenu, + }, + 'Log': logMenu, + 'Appearance': appearanceMenu, + }); + } + + function logMenu() { + E.showMenu({ + '': { + title: 'Log', + back: topMenu, + }, + 'Max entries': { + value: SETTINGS.maxLogLength, + min: 5, max: 100, step: 5, + onchange: v => { + SETTINGS.maxLogLength = v; + stampLog.maxLength = v; + } + }, + 'Auto-delete oldest': { + value: SETTINGS.rotateLog, + onchange: v => { + SETTINGS.rotateLog = !SETTINGS.rotateLog; + } + }, + }); + } + + function appearanceMenu() { + E.showMenu({ + '': { + title: 'Appearance', + back: topMenu, + }, + 'Log font': { + value: fonts.indexOf(SETTINGS.logFont), + min: 0, max: fonts.length - 1, + format: v => fonts[v], + onchange: v => { + SETTINGS.logFont = fonts[v]; + }, + }, + 'Log font H size': { + value: SETTINGS.logFontHSize, + min: 1, max: 50, + onchange: v => { + SETTINGS.logFontHSize = v; + }, + }, + 'Log font V size': { + value: SETTINGS.logFontVSize, + min: 1, max: 50, + onchange: v => { + SETTINGS.logFontVSize = v; + }, + }, + }); + } + function endMenu() { saveSettings(); currentUI.start(); } - const fonts = g.getFonts(); currentUI.stop(); - E.showMenu({ - '': { - title: 'Timestamp Logger', - back: endMenu, - }, - 'Max log size': { - value: SETTINGS.maxLogLength, - min: 5, max: 100, step: 5, - onchange: v => { - SETTINGS.maxLogLength = v; - stampLog.maxLength = v; - } - }, - 'Rotate log entries': { - value: SETTINGS.rotateLog, - onchange: v => { - SETTINGS.rotateLog = !SETTINGS.rotateLog; - } - }, - 'Log font': { - value: fonts.indexOf(SETTINGS.logFont), - min: 0, max: fonts.length - 1, - format: v => fonts[v], - onchange: v => { - SETTINGS.logFont = fonts[v]; - }, - }, - 'Log font H size': { - value: SETTINGS.logFontHSize, - min: 1, max: 50, - onchange: v => { - SETTINGS.logFontHSize = v; - }, - }, - 'Log font V size': { - value: SETTINGS.logFontVSize, - min: 1, max: 50, - onchange: v => { - SETTINGS.logFontVSize = v; - }, - }, - }); + topMenu(); } From 041a8ef9106ede9bd90bad0ab36e7eaef146fc95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Arguill=C3=A8re?= Date: Thu, 28 Mar 2024 09:00:07 +0100 Subject: [PATCH 022/423] fix bug where lock event would conflict with menu UI --- apps/elapsed_t/app.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/elapsed_t/app.js b/apps/elapsed_t/app.js index b38735a32..384d869c3 100644 --- a/apps/elapsed_t/app.js +++ b/apps/elapsed_t/app.js @@ -57,7 +57,7 @@ function writeSettings() { let inMenu = false; Bangle.on('touch', function (zone, e) { - if (!inMenu) { + if (!inMenu && e.y > 24) { if (drawTimeout) clearTimeout(drawTimeout); E.showMenu(menu); inMenu = true; @@ -276,6 +276,10 @@ function updateQueueMillis(displaySeconds) { } Bangle.on('lock', function (on, reason) { + if (inMenu) { // if already in a menu, nothing to do + return; + } + if (on) { // screen is locked temp_displaySeconds = false; updateQueueMillis(false); From 7e51e3b2768d74cb11a1de240706dd25a5a3df22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Arguill=C3=A8re?= Date: Thu, 28 Mar 2024 11:25:06 +0100 Subject: [PATCH 023/423] fix: set cyan for dark and blue for light --- apps/widbtstates/widget.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/widbtstates/widget.js b/apps/widbtstates/widget.js index e80da4082..3c3d40e74 100644 --- a/apps/widbtstates/widget.js +++ b/apps/widbtstates/widget.js @@ -21,8 +21,8 @@ true: "#fff", }, _a[2] = { - false: "#0ff", - true: "#00f", + false: "#00f", + true: "#0ff", }, _a); WIDGETS["bluetooth"] = { From 8223946db1a0d1bfe088cf75708ca973a8a293a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Arguill=C3=A8re?= Date: Thu, 28 Mar 2024 11:38:40 +0100 Subject: [PATCH 024/423] set black for light theme when BT on but disconnected --- apps/widbtstates/widget.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/widbtstates/widget.js b/apps/widbtstates/widget.js index 3c3d40e74..105e4111d 100644 --- a/apps/widbtstates/widget.js +++ b/apps/widbtstates/widget.js @@ -17,7 +17,7 @@ }; var colours = (_a = {}, _a[1] = { - false: "#fff", + false: "#000", true: "#fff", }, _a[2] = { From 798ce38600a209e1c2f386ecefd62cd730561ede Mon Sep 17 00:00:00 2001 From: pinq- Date: Sun, 21 Apr 2024 01:22:43 +0300 Subject: [PATCH 025/423] added timestamp --- apps/accelrec/app.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/accelrec/app.js b/apps/accelrec/app.js index cf434fd7b..175801ee5 100644 --- a/apps/accelrec/app.js +++ b/apps/accelrec/app.js @@ -6,6 +6,7 @@ var THRESH = 1.04; var accelx = new Int16Array(SAMPLES); var accely = new Int16Array(SAMPLES); // North var accelz = new Int16Array(SAMPLES); // Into clock face +var timestep = new Int16Array(SAMPLES); // Into clock face var accelIdx = 0; var lastAccel; function accelHandlerTrigger(a) {"ram" @@ -26,6 +27,7 @@ function accelHandlerRecord(a) {"ram" accelx[i] = a.x*SCALE*2; accely[i] = -a.y*SCALE*2; accelz[i] = a.z*SCALE*2; + timestep[i] = getTime(); if (accelIdx>=SAMPLES) recordStop(); } function recordStart() {"ram" @@ -167,7 +169,7 @@ function showSaveMenu() { menu["Recording "+i+(exists?" *":"")] = function() { var csv = ""; for (var i=0;i Date: Sun, 21 Apr 2024 01:40:29 +0300 Subject: [PATCH 026/423] milliseconds --- apps/accelrec/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/accelrec/app.js b/apps/accelrec/app.js index 175801ee5..d8c359acb 100644 --- a/apps/accelrec/app.js +++ b/apps/accelrec/app.js @@ -27,7 +27,7 @@ function accelHandlerRecord(a) {"ram" accelx[i] = a.x*SCALE*2; accely[i] = -a.y*SCALE*2; accelz[i] = a.z*SCALE*2; - timestep[i] = getTime(); + timestep[i] = Date.prototype.getTime(); if (accelIdx>=SAMPLES) recordStop(); } function recordStart() {"ram" From efc89bb7cfeebe81ae8ec92f138ff83cb4aa1f09 Mon Sep 17 00:00:00 2001 From: pinq- Date: Sun, 21 Apr 2024 01:45:17 +0300 Subject: [PATCH 027/423] Update app.js --- apps/accelrec/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/accelrec/app.js b/apps/accelrec/app.js index d8c359acb..58d3ce445 100644 --- a/apps/accelrec/app.js +++ b/apps/accelrec/app.js @@ -27,7 +27,7 @@ function accelHandlerRecord(a) {"ram" accelx[i] = a.x*SCALE*2; accely[i] = -a.y*SCALE*2; accelz[i] = a.z*SCALE*2; - timestep[i] = Date.prototype.getTime(); + timestep[i] = getTime(); if (accelIdx>=SAMPLES) recordStop(); } function recordStart() {"ram" @@ -169,7 +169,7 @@ function showSaveMenu() { menu["Recording "+i+(exists?" *":"")] = function() { var csv = ""; for (var i=0;i Date: Sun, 21 Apr 2024 10:33:32 +0300 Subject: [PATCH 028/423] right way this time --- apps/accelrec/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/accelrec/app.js b/apps/accelrec/app.js index 58d3ce445..512c73d9d 100644 --- a/apps/accelrec/app.js +++ b/apps/accelrec/app.js @@ -27,7 +27,7 @@ function accelHandlerRecord(a) {"ram" accelx[i] = a.x*SCALE*2; accely[i] = -a.y*SCALE*2; accelz[i] = a.z*SCALE*2; - timestep[i] = getTime(); + timestep[i] = (getTime() - tStart)*100; if (accelIdx>=SAMPLES) recordStop(); } function recordStart() {"ram" @@ -169,7 +169,7 @@ function showSaveMenu() { menu["Recording "+i+(exists?" *":"")] = function() { var csv = ""; for (var i=0;i Date: Sun, 21 Apr 2024 11:07:05 +0300 Subject: [PATCH 029/423] More time and more timestamp digits --- apps/accelrec/app.js | 4 ++-- apps/accelrec/interface.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/accelrec/app.js b/apps/accelrec/app.js index 512c73d9d..d866af9e2 100644 --- a/apps/accelrec/app.js +++ b/apps/accelrec/app.js @@ -1,6 +1,6 @@ //var acc; var HZ = 100; -var SAMPLES = 5*HZ; // 5 seconds +var SAMPLES = 8*HZ; // 8 seconds var SCALE = 5000; var THRESH = 1.04; var accelx = new Int16Array(SAMPLES); @@ -27,7 +27,7 @@ function accelHandlerRecord(a) {"ram" accelx[i] = a.x*SCALE*2; accely[i] = -a.y*SCALE*2; accelz[i] = a.z*SCALE*2; - timestep[i] = (getTime() - tStart)*100; + timestep[i] = (getTime() - tStart)*1000; if (accelIdx>=SAMPLES) recordStop(); } function recordStart() {"ram" diff --git a/apps/accelrec/interface.html b/apps/accelrec/interface.html index ce3547387..d69b2d50c 100644 --- a/apps/accelrec/interface.html +++ b/apps/accelrec/interface.html @@ -37,7 +37,7 @@ function getData() { `; promise = promise.then(function() { document.querySelector(`.btn[fn='${fn}'][act='save']`).addEventListener("click", function() { - Util.saveCSV(fn.slice(0,-4), "X,Y,Z\n"+fileData[fn]); + Util.saveCSV(fn.slice(0,-4), "Time,X,Y,Z\n"+fileData[fn]); }); document.querySelector(`.btn[fn='${fn}'][act='delete']`).addEventListener("click", function() { Util.showModal("Deleting..."); From 651cf4295372a94563add7b45989342fcee0da16 Mon Sep 17 00:00:00 2001 From: pinq- Date: Sun, 21 Apr 2024 17:14:13 +0300 Subject: [PATCH 030/423] try with 400 hz --- apps/accelrec/app.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/accelrec/app.js b/apps/accelrec/app.js index d866af9e2..41a8cb8fc 100644 --- a/apps/accelrec/app.js +++ b/apps/accelrec/app.js @@ -1,6 +1,6 @@ //var acc; -var HZ = 100; -var SAMPLES = 8*HZ; // 8 seconds +var HZ = 400; +var SAMPLES = 5*HZ; // 5 seconds var SCALE = 5000; var THRESH = 1.04; var accelx = new Int16Array(SAMPLES); @@ -35,7 +35,8 @@ function recordStart() {"ram" accelIdx = 0; lastAccel = []; Bangle.accelWr(0x18,0b01110100); // off, +-8g - Bangle.accelWr(0x1B,0x03 | 0x40); // 100hz output, ODR/2 filter + //Bangle.accelWr(0x1B,0x03 | 0x40); // 100hz output, ODR/2 filter + Bangle.accelWr(0x1B,0x05 | 0x40); // 400hz output, ODR/2 filter Bangle.accelWr(0x18,0b11110100); // +-8g Bangle.setPollInterval(10); // 100hz input setTimeout(function() { From c961406da98f835477a6459d53868a8b84cc2788 Mon Sep 17 00:00:00 2001 From: pinq- Date: Sun, 21 Apr 2024 17:28:27 +0300 Subject: [PATCH 031/423] set pollintervall to 2.5(400hz) --- apps/accelrec/app.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/accelrec/app.js b/apps/accelrec/app.js index 41a8cb8fc..1a3e0fd24 100644 --- a/apps/accelrec/app.js +++ b/apps/accelrec/app.js @@ -38,7 +38,8 @@ function recordStart() {"ram" //Bangle.accelWr(0x1B,0x03 | 0x40); // 100hz output, ODR/2 filter Bangle.accelWr(0x1B,0x05 | 0x40); // 400hz output, ODR/2 filter Bangle.accelWr(0x18,0b11110100); // +-8g - Bangle.setPollInterval(10); // 100hz input + //Bangle.setPollInterval(10); // 100hz input + Bangle.setPollInterval(2.5); // 400hz input setTimeout(function() { Bangle.on('accel',accelHandlerTrigger); g.clear(1).setFont("6x8",2).setFontAlign(0,0); From ac36b5bb5531ad4ca2d19c63117b59993ea846e9 Mon Sep 17 00:00:00 2001 From: pinq- Date: Sun, 21 Apr 2024 17:34:20 +0300 Subject: [PATCH 032/423] can't take the space. Lower time to 4 seconds --- apps/accelrec/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/accelrec/app.js b/apps/accelrec/app.js index 1a3e0fd24..d5b5e676a 100644 --- a/apps/accelrec/app.js +++ b/apps/accelrec/app.js @@ -1,6 +1,6 @@ //var acc; var HZ = 400; -var SAMPLES = 5*HZ; // 5 seconds +var SAMPLES = 4*HZ; // 5 seconds var SCALE = 5000; var THRESH = 1.04; var accelx = new Int16Array(SAMPLES); @@ -39,7 +39,7 @@ function recordStart() {"ram" Bangle.accelWr(0x1B,0x05 | 0x40); // 400hz output, ODR/2 filter Bangle.accelWr(0x18,0b11110100); // +-8g //Bangle.setPollInterval(10); // 100hz input - Bangle.setPollInterval(2.5); // 400hz input + Bangle.setPollInterval(2.5); // 400hz input setTimeout(function() { Bangle.on('accel',accelHandlerTrigger); g.clear(1).setFont("6x8",2).setFontAlign(0,0); From caae40d14d6e5f99369112483466a8115ac98027 Mon Sep 17 00:00:00 2001 From: pinq- Date: Sun, 21 Apr 2024 17:42:24 +0300 Subject: [PATCH 033/423] Cant with 400hz. No space. lets try with 200hz --- apps/accelrec/app.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/accelrec/app.js b/apps/accelrec/app.js index d5b5e676a..af1c1f5a3 100644 --- a/apps/accelrec/app.js +++ b/apps/accelrec/app.js @@ -1,6 +1,6 @@ //var acc; -var HZ = 400; -var SAMPLES = 4*HZ; // 5 seconds +var HZ = 200; +var SAMPLES = 5*HZ; // 5 seconds var SCALE = 5000; var THRESH = 1.04; var accelx = new Int16Array(SAMPLES); @@ -36,10 +36,10 @@ function recordStart() {"ram" lastAccel = []; Bangle.accelWr(0x18,0b01110100); // off, +-8g //Bangle.accelWr(0x1B,0x03 | 0x40); // 100hz output, ODR/2 filter - Bangle.accelWr(0x1B,0x05 | 0x40); // 400hz output, ODR/2 filter + Bangle.accelWr(0x1B,0x04 | 0x40); // 200hz output, ODR/2 filter Bangle.accelWr(0x18,0b11110100); // +-8g //Bangle.setPollInterval(10); // 100hz input - Bangle.setPollInterval(2.5); // 400hz input + Bangle.setPollInterval(5); // 200hz input setTimeout(function() { Bangle.on('accel',accelHandlerTrigger); g.clear(1).setFont("6x8",2).setFontAlign(0,0); From 10e56fb2de449ef16f90d7521bcf796b1297904d Mon Sep 17 00:00:00 2001 From: pinq- Date: Sun, 21 Apr 2024 17:49:40 +0300 Subject: [PATCH 034/423] update --- apps/accelrec/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/accelrec/metadata.json b/apps/accelrec/metadata.json index b8831c31b..36d700a19 100644 --- a/apps/accelrec/metadata.json +++ b/apps/accelrec/metadata.json @@ -2,7 +2,7 @@ "id": "accelrec", "name": "Acceleration Recorder", "shortName": "Accel Rec", - "version": "0.04", + "version": "0.05", "description": "This app puts the Bangle's accelerometer into 100Hz mode and reads 2 seconds worth of data after movement starts. The data can then be exported back to the PC.", "icon": "app.png", "tags": "", From 6bbcdf1de50fcc089880f862638db8096b9bd471 Mon Sep 17 00:00:00 2001 From: pinq- Date: Sun, 21 Apr 2024 18:00:37 +0300 Subject: [PATCH 035/423] Update app.js --- apps/accelrec/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/accelrec/app.js b/apps/accelrec/app.js index af1c1f5a3..92efc5c6a 100644 --- a/apps/accelrec/app.js +++ b/apps/accelrec/app.js @@ -1,5 +1,5 @@ //var acc; -var HZ = 200; +var HZ = 400; var SAMPLES = 5*HZ; // 5 seconds var SCALE = 5000; var THRESH = 1.04; @@ -36,10 +36,10 @@ function recordStart() {"ram" lastAccel = []; Bangle.accelWr(0x18,0b01110100); // off, +-8g //Bangle.accelWr(0x1B,0x03 | 0x40); // 100hz output, ODR/2 filter - Bangle.accelWr(0x1B,0x04 | 0x40); // 200hz output, ODR/2 filter + Bangle.accelWr(0x1B,0x05 | 0x40); // 400hz output, ODR/2 filter Bangle.accelWr(0x18,0b11110100); // +-8g //Bangle.setPollInterval(10); // 100hz input - Bangle.setPollInterval(5); // 200hz input + Bangle.setPollInterval(2.5); // 100hz input setTimeout(function() { Bangle.on('accel',accelHandlerTrigger); g.clear(1).setFont("6x8",2).setFontAlign(0,0); From 6cc888000aad2b8744b9841630a1455af24e3e37 Mon Sep 17 00:00:00 2001 From: pinq- Date: Fri, 26 Apr 2024 10:27:07 +0300 Subject: [PATCH 036/423] back to 100hz --- apps/accelrec/app.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/accelrec/app.js b/apps/accelrec/app.js index 92efc5c6a..cfd8d8ca4 100644 --- a/apps/accelrec/app.js +++ b/apps/accelrec/app.js @@ -1,5 +1,5 @@ //var acc; -var HZ = 400; +var HZ = 100; var SAMPLES = 5*HZ; // 5 seconds var SCALE = 5000; var THRESH = 1.04; @@ -35,11 +35,11 @@ function recordStart() {"ram" accelIdx = 0; lastAccel = []; Bangle.accelWr(0x18,0b01110100); // off, +-8g - //Bangle.accelWr(0x1B,0x03 | 0x40); // 100hz output, ODR/2 filter - Bangle.accelWr(0x1B,0x05 | 0x40); // 400hz output, ODR/2 filter + Bangle.accelWr(0x1B,0x03 | 0x40); // 100hz output, ODR/2 filter + //Bangle.accelWr(0x1B,0x05 | 0x40); // 400hz output, ODR/2 filter Bangle.accelWr(0x18,0b11110100); // +-8g - //Bangle.setPollInterval(10); // 100hz input - Bangle.setPollInterval(2.5); // 100hz input + Bangle.setPollInterval(10); // 100hz input + //Bangle.setPollInterval(2.5); // 100hz input setTimeout(function() { Bangle.on('accel',accelHandlerTrigger); g.clear(1).setFont("6x8",2).setFontAlign(0,0); From 805f13fb595a73d2a938eebd1c24394276866ac6 Mon Sep 17 00:00:00 2001 From: pinq- Date: Mon, 29 Apr 2024 19:24:44 +0300 Subject: [PATCH 037/423] higher threshold and lower scale for 16bit --- apps/accelrec/app.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/accelrec/app.js b/apps/accelrec/app.js index cfd8d8ca4..fd28b3440 100644 --- a/apps/accelrec/app.js +++ b/apps/accelrec/app.js @@ -1,8 +1,8 @@ //var acc; var HZ = 100; var SAMPLES = 5*HZ; // 5 seconds -var SCALE = 5000; -var THRESH = 1.04; +var SCALE = 2000; +var THRESH = 1.4; var accelx = new Int16Array(SAMPLES); var accely = new Int16Array(SAMPLES); // North var accelz = new Int16Array(SAMPLES); // Into clock face @@ -24,7 +24,7 @@ function accelHandlerTrigger(a) {"ram" } function accelHandlerRecord(a) {"ram" var i = accelIdx++; - accelx[i] = a.x*SCALE*2; + accelx[i] = a.x*SCALE*2; // *2 because of 8g mode accely[i] = -a.y*SCALE*2; accelz[i] = a.z*SCALE*2; timestep[i] = (getTime() - tStart)*1000; @@ -36,10 +36,8 @@ function recordStart() {"ram" lastAccel = []; Bangle.accelWr(0x18,0b01110100); // off, +-8g Bangle.accelWr(0x1B,0x03 | 0x40); // 100hz output, ODR/2 filter - //Bangle.accelWr(0x1B,0x05 | 0x40); // 400hz output, ODR/2 filter Bangle.accelWr(0x18,0b11110100); // +-8g Bangle.setPollInterval(10); // 100hz input - //Bangle.setPollInterval(2.5); // 100hz input setTimeout(function() { Bangle.on('accel',accelHandlerTrigger); g.clear(1).setFont("6x8",2).setFontAlign(0,0); From 30734047668adce7af677b273566bbde83eafbf0 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Fri, 10 May 2024 20:21:36 -0500 Subject: [PATCH 038/423] Rename app --- apps/{stamplog => timestamplog}/app.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename apps/{stamplog => timestamplog}/app.js (100%) diff --git a/apps/stamplog/app.js b/apps/timestamplog/app.js similarity index 100% rename from apps/stamplog/app.js rename to apps/timestamplog/app.js From 1dd88b29b29d3cf9036c9bb43f08adb02371ffb2 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Fri, 10 May 2024 20:26:13 -0500 Subject: [PATCH 039/423] Fix breakage after recent FW (Date object serializes differently) --- apps/timestamplog/app.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/apps/timestamplog/app.js b/apps/timestamplog/app.js index b9184efaf..521480776 100644 --- a/apps/timestamplog/app.js +++ b/apps/timestamplog/app.js @@ -129,7 +129,15 @@ class StampLog { } if (this.isDirty) { - if (storage.writeJSON(this.filename, this.log)) { + let logToSave = []; + for (let logEntry of this.log) { + // Serialize each Date object into an ISO string before saving + let newEntry = Object.assign({}, logEntry); + newEntry.stamp = logEntry.stamp.toISOString(); + logToSave.push(newEntry); + } + + if (storage.writeJSON(this.filename, logToSave)) { console.log('stamplog: save to storage completed'); this.isDirty = false; } else { From 69886274457b6c5851517eb15d2e6de59e579125 Mon Sep 17 00:00:00 2001 From: jeonlab <37269397+jeonlab@users.noreply.github.com> Date: Tue, 21 May 2024 14:55:47 -0500 Subject: [PATCH 040/423] Update app.js --- apps/messagegui/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/messagegui/app.js b/apps/messagegui/app.js index 9172bcad7..cd3fb0ab5 100644 --- a/apps/messagegui/app.js +++ b/apps/messagegui/app.js @@ -505,7 +505,7 @@ function checkMessages(options) { // draw the body g.drawString(l.join("\n"), x+10,r.y+20+pady); } - if (!longBody && msg.src) g.setFontAlign(1,1).setFont("6x8").drawString(msg.src, r.x+r.w-2, r.y+r.h-2); + if (!longBody && msg.src) g.setFontAlign(-1,1).setFont("6x8").drawString(msg.src, r.x+r.w-2, r.y+r.h-2); g.setColor("#888").fillRect(r.x,r.y+r.h-1,r.x+r.w-1,r.y+r.h-1); // dividing line between items }, select : idx => { From a2c191a0c6a16371eb4f126eadadf1aea374a5a4 Mon Sep 17 00:00:00 2001 From: jeonlab <37269397+jeonlab@users.noreply.github.com> Date: Wed, 22 May 2024 20:58:53 -0500 Subject: [PATCH 041/423] Update metadata.json --- apps/gpspoilog/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gpspoilog/metadata.json b/apps/gpspoilog/metadata.json index 0a0902cea..41a917b79 100644 --- a/apps/gpspoilog/metadata.json +++ b/apps/gpspoilog/metadata.json @@ -6,7 +6,7 @@ "description": "A simple app to log points of interest with their GPS coordinates and read them back onto your PC. Based on the https://www.espruino.com/Bangle.js+Storage tutorial", "icon": "app.png", "tags": "outdoors", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS","BANGLEJS2"], "interface": "interface.html", "storage": [ {"name":"gpspoilog.app.js","url":"app.js"}, From ab045fbd2437eebadc640504471999e15e95d5a2 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Fri, 24 May 2024 18:04:13 -0500 Subject: [PATCH 042/423] Implement ability to delete log entries --- apps/timestamplog/app.js | 135 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 134 insertions(+), 1 deletion(-) diff --git a/apps/timestamplog/app.js b/apps/timestamplog/app.js index 521480776..901ddc458 100644 --- a/apps/timestamplog/app.js +++ b/apps/timestamplog/app.js @@ -278,6 +278,7 @@ class MainScreen { // Kill layout handlers Bangle.removeListener('drag', this.listeners.drag); + Bangle.removeListener('touch', this.listeners.touch); Bangle.setUI(); } @@ -329,7 +330,8 @@ class MainScreen { // Populate log items in layout for (i = 0; i < this.itemsPerPage; i++) { layout.logItems.c.push( - {type: 'custom', render: renderLogItem, item: undefined, fillx: 1, height: logItemHeight} + {type: 'custom', render: renderLogItem, item: undefined, itemIdx: undefined, + fillx: 1, height: logItemHeight} ); } layout.logScroll.height = logItemHeight * this.itemsPerPage; @@ -347,6 +349,7 @@ class MainScreen { for (let elem of layLogItems.c) { logIdx++; elem.item = this.stampLog.log[logIdx]; + elem.itemIdx = logIdx; } this.layout.render(layLogItems); this.layout.render(this.layout.logScroll); @@ -412,6 +415,23 @@ class MainScreen { this.listeners.drag = dragHandler.bind(this); Bangle.on('drag', this.listeners.drag); + + function touchHandler(button, xy) { + // Handle taps on log entries + let logUIItems = this.layout.logItems.c; + for (var logUIObj of logUIItems) { + if (!xy.type && + logUIObj.x <= xy.x && xy.x < logUIObj.x + logUIObj.w && + logUIObj.y <= xy.y && xy.y < logUIObj.y + logUIObj.h && + logUIObj.item) { + switchUI(new LogEntryScreen(this.stampLog, logUIObj.itemIdx)); + break; + } + } + } + + this.listeners.touch = touchHandler.bind(this); + Bangle.on('touch', this.listeners.touch); } // Add current timestamp to log if possible and update UI display @@ -474,6 +494,96 @@ class MainScreen { } +// Log entry screen interface, launched by calling start() +class LogEntryScreen { + + constructor(stampLog, logIdx) { + this.stampLog = stampLog; + this.logIdx = logIdx; + this.logItem = stampLog.log[logIdx]; + + this.defaultFont = fontSpec( + SETTINGS.logFont, SETTINGS.logFontHSize, SETTINGS.logFontVSize); + } + + start() { + this._initLayout(); + this.layout.clear(); + this.render(); + } + + stop() { + Bangle.setUI(); + } + + back() { + this.stop(); + switchUI(mainUI); + } + + _initLayout() { + let layout = new Layout( + {type: 'v', + c: [ + {type: 'txt', font: this.defaultFont, label: locale.date(this.logItem.stamp, 1)}, + {type: 'txt', font: this.defaultFont, label: locale.time(this.logItem.stamp).trim()}, + {type: '', id: 'placeholder', fillx: 1, filly: 1}, + {type: 'btn', font: '6x15', label: 'Delete', cb: this.delLogItem.bind(this), + cbl: this.delLogItem.bind(this)}, + ], + }, + { + back: this.back.bind(this), + btns: [ + {label: '<', cb: this.prevLogItem.bind(this)}, + {label: '>', cb: this.nextLogItem.bind(this)}, + ], + } + ); + + layout.update(); + this.layout = layout; + } + + render(item) { + this.layout.clear(); + this.layout.render(); + } + + refresh() { + this.logItem = this.stampLog.log[this.logIdx]; + this._initLayout(); + this.render(); + } + + prevLogItem() { + this.logIdx = this.logIdx ? this.logIdx-1 : this.stampLog.log.length-1; + this.refresh(); + } + + nextLogItem() { + this.logIdx = this.logIdx == this.stampLog.log.length-1 ? 0 : this.logIdx+1; + this.refresh(); + } + + delLogItem() { + this.stampLog.deleteEntries([this.logItem]); + if (!this.stampLog.log.length) { + this.back(); + return; + } else if (this.logIdx > this.stampLog.log.length - 1) { + this.logIdx = this.stampLog.log.length - 1; + } + + // Create a brief “blink” on the screen to provide user feedback + // that the deletion has been performed + this.layout.clear(); + setTimeout(this.refresh.bind(this), 250); + } + +} + + function settingsMenu() { const fonts = g.getFonts(); @@ -508,6 +618,7 @@ function settingsMenu() { SETTINGS.rotateLog = !SETTINGS.rotateLog; } }, + 'Clear log': clearLogPrompt, }); } @@ -547,6 +658,20 @@ function settingsMenu() { currentUI.start(); } + function clearLogPrompt() { + E.showPrompt('Erase ALL log entries?', { + title: 'Clear log', + buttons: {'Erase':1, "Don't":0} + }).then((yes) => { + if (yes) { + stampLog.deleteEntries(stampLog.log) + endMenu(); + } else { + logMenu(); + } + }); + } + currentUI.stop(); topMenu(); } @@ -564,6 +689,13 @@ function saveErrorAlert() { } +function switchUI(newUI) { + currentUI.stop(); + currentUI = newUI; + currentUI.start(); +} + + Bangle.loadWidgets(); Bangle.drawWidgets(); @@ -572,4 +704,5 @@ E.on('kill', stampLog.save.bind(stampLog)); stampLog.on('saveError', saveErrorAlert); var currentUI = new MainScreen(stampLog); +var mainUI = currentUI; currentUI.start(); From 872a7a51de675226c3482372895a0578e48aa857 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sat, 25 May 2024 18:32:18 -0500 Subject: [PATCH 043/423] Add action setting for button presses --- apps/timestamplog/app.js | 44 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/apps/timestamplog/app.js b/apps/timestamplog/app.js index 901ddc458..11d48ecd8 100644 --- a/apps/timestamplog/app.js +++ b/apps/timestamplog/app.js @@ -21,8 +21,16 @@ const SETTINGS = Object.assign({ logFontVSize: 1, maxLogLength: 30, rotateLog: false, + buttonAction: 'Log time', }, storage.readJSON(SETTINGS_FILENAME, true) || {}); +const SETTINGS_BUTTON_ACTION = [ + 'Log time', + 'Show menu', + 'Quit app', + 'Do nothing', +]; + function saveSettings() { if (!storage.writeJSON(SETTINGS_FILENAME, SETTINGS)) { E.showAlert('Trouble saving settings'); @@ -279,6 +287,7 @@ class MainScreen { // Kill layout handlers Bangle.removeListener('drag', this.listeners.drag); Bangle.removeListener('touch', this.listeners.touch); + clearWatch(this.listeners.btnWatch); Bangle.setUI(); } @@ -432,6 +441,23 @@ class MainScreen { this.listeners.touch = touchHandler.bind(this); Bangle.on('touch', this.listeners.touch); + + function buttonHandler() { + let act = SETTINGS.buttonAction; + if (act == 'Log time') { + if (currentUI != mainUI) { + switchUI(mainUI); + } + mainUI.addTimestamp(); + } else if (act == 'Show menu') { + settingsMenu(); + } else if (act == 'Quit app') { + Bangle.showClock(); + } + } + + this.listeners.btnWatch = setWatch(buttonHandler, BTN, + {edge: 'falling', debounce: 50, repeat: true}); } // Add current timestamp to log if possible and update UI display @@ -509,7 +535,7 @@ class LogEntryScreen { start() { this._initLayout(); this.layout.clear(); - this.render(); + this.refresh(); } stop() { @@ -525,8 +551,8 @@ class LogEntryScreen { let layout = new Layout( {type: 'v', c: [ - {type: 'txt', font: this.defaultFont, label: locale.date(this.logItem.stamp, 1)}, - {type: 'txt', font: this.defaultFont, label: locale.time(this.logItem.stamp).trim()}, + {type: 'txt', font: this.defaultFont, id: 'date', label: '?'}, + {type: 'txt', font: this.defaultFont, id: 'time', label: '?'}, {type: '', id: 'placeholder', fillx: 1, filly: 1}, {type: 'btn', font: '6x15', label: 'Delete', cb: this.delLogItem.bind(this), cbl: this.delLogItem.bind(this)}, @@ -552,7 +578,9 @@ class LogEntryScreen { refresh() { this.logItem = this.stampLog.log[this.logIdx]; - this._initLayout(); + this.layout.date.label = locale.date(this.logItem.stamp, 1); + this.layout.time.label = locale.time(this.logItem.stamp).trim(); + this.layout.update(); this.render(); } @@ -595,6 +623,14 @@ function settingsMenu() { }, 'Log': logMenu, 'Appearance': appearanceMenu, + 'Button': { + value: SETTINGS_BUTTON_ACTION.indexOf(SETTINGS.buttonAction), + min: 0, max: SETTINGS_BUTTON_ACTION.length - 1, + format: v => SETTINGS_BUTTON_ACTION[v], + onchange: v => { + SETTINGS.buttonAction = SETTINGS_BUTTON_ACTION[v]; + }, + }, }); } From a08d580a1eb74aecc27b908c71dfcdc792a2d503 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sun, 26 May 2024 13:23:29 -0500 Subject: [PATCH 044/423] Rename function for clarity --- apps/timestamplog/app.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/timestamplog/app.js b/apps/timestamplog/app.js index 11d48ecd8..7505c6afb 100644 --- a/apps/timestamplog/app.js +++ b/apps/timestamplog/app.js @@ -320,7 +320,7 @@ class MainScreen { {type: 'btn', font: '6x8:2', fillx: 1, label: '+ XX:XX', id: 'addBtn', cb: this.addTimestamp.bind(this)}, {type: 'btn', font: '6x8:2', label: getIcon('menu'), id: 'menuBtn', - cb: settingsMenu}, + cb: launchSettingsMenu}, ], }, ], @@ -450,7 +450,7 @@ class MainScreen { } mainUI.addTimestamp(); } else if (act == 'Show menu') { - settingsMenu(); + launchSettingsMenu(); } else if (act == 'Quit app') { Bangle.showClock(); } @@ -612,7 +612,7 @@ class LogEntryScreen { } -function settingsMenu() { +function launchSettingsMenu() { const fonts = g.getFonts(); function topMenu() { From 9e2c87cad84239b31d0faac1bb2e341f2884c00b Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Sun, 26 May 2024 13:57:41 -0500 Subject: [PATCH 045/423] Remove short-press callback from log entry delete button That was just for testing; the emulator doesn't support long presses. --- apps/timestamplog/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/timestamplog/app.js b/apps/timestamplog/app.js index 7505c6afb..9e4fa2273 100644 --- a/apps/timestamplog/app.js +++ b/apps/timestamplog/app.js @@ -554,7 +554,7 @@ class LogEntryScreen { {type: 'txt', font: this.defaultFont, id: 'date', label: '?'}, {type: 'txt', font: this.defaultFont, id: 'time', label: '?'}, {type: '', id: 'placeholder', fillx: 1, filly: 1}, - {type: 'btn', font: '6x15', label: 'Delete', cb: this.delLogItem.bind(this), + {type: 'btn', font: '6x15', label: 'Hold to delete', cbl: this.delLogItem.bind(this)}, ], }, From e7477784e91626471930df76dbb772a187553ea2 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Mon, 27 May 2024 14:38:32 -0500 Subject: [PATCH 046/423] Add app metadata files --- apps/timestamplog/app-icon.js | 1 + apps/timestamplog/app.png | Bin 0 -> 851 bytes apps/timestamplog/changelog.txt | 1 + apps/timestamplog/metadata.json | 16 ++++++++++++++++ 4 files changed, 18 insertions(+) create mode 100644 apps/timestamplog/app-icon.js create mode 100644 apps/timestamplog/app.png create mode 100644 apps/timestamplog/changelog.txt create mode 100644 apps/timestamplog/metadata.json diff --git a/apps/timestamplog/app-icon.js b/apps/timestamplog/app-icon.js new file mode 100644 index 000000000..b35f05e08 --- /dev/null +++ b/apps/timestamplog/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEw4cB/4ACBQX48AFDAAUkyVJAQoDCBZACDymSoEAgVJkQRJkskGAuJCJEpBwYDCyIRHpVICI0SogRGqQyEAgdIBwIUEyAODAQkJiVJxBoDwARIgJuBiIUBCIKzKCIOCQYWkCJUkpNQoiMBkARKgmJxUIxMlOgIQIQwOJyNBiKeBCJeRyUokUoGZPYAQMRyVFgiwDAAsGCIlI0GIBYfAAgUB2wECCINJikRCIfbAgVt2DaDCIMiwR9DjdggEDtg5DgTECSQIJDtuAEwYRFSQOSBIUN2xWCgANBgVSAYKSBR4k2AgYRCxQDBSQTGKgVRCISSBoARKxUpCIKSFAA0SqNFCIKSFAA0RlUo0iSHCI0losUSRARFkmo0SSEwAPFeoORkmViiSEiARHxJXB0SSFAgQCByEAggRCqiSEilChEgwUIXgMkBgVKSQmiqFBgkQoMUoArESQdE6Y1FxIRESQco0WIEYkRiQRDSQWRmnTpojEwRhFSQOKEYOJEYkQogRESQNNEZEIPoQUCEYeKkIjEoLFBCIdTEYc0EYsiCIlKpQjCkojCNIYREpMpEYwCCEYoCB0gjBkmEEYImCgQRGyWTNYJECbQQjHJQIDBygjNpSHCEZ0QAYIjODoJHPEAgjDA==")) diff --git a/apps/timestamplog/app.png b/apps/timestamplog/app.png new file mode 100644 index 0000000000000000000000000000000000000000..ddb51fe40ba201d8b0a5fb66583b8c720e932898 GIT binary patch literal 851 zcmV-Z1FZasP)EX>4Tx04R}tkv&MmKpe$iQ%glE4rY+zkfAzRC@RuXt5Adrp;l}?mh0_0bHx5XjWeW&~)3( zrc*+`uquRK5keF^5=cslWz30U2EOC#9s#!A#aWjBxj)CCTC@}(AP~leu-ldB4a z91EyJgXH?b{@{1FR%vR|ONyj`(2L`Ii~-?Ypxtzw?_n$MpNqV!Z z#g2fXZQ$a%tI2!7A(Ki)<;agx}&FihRkJASrM_pxZfP+I| zyiD2aKJV`D?d{()o&J6R_g-?`vQ)&D0000FP)t-s00030|Nj600RR60{{R3IQ;Pcl z0004WQchCAP9xMY@lX<1YICzg5G8SnfgE}*!b@1 z51t=j#D?HRPvjnC?vEbvHUy5whXF+D?@up)yG)^UgsjtHwA)qjpRp8Q#Tjjl5Qrsmgl&!Rg)9*m z3xxVOyb-x5k5Jbb6GO^*iAbkZkyf&(oN3HwM{3nnO$T0(1`Q(rrkasj6*;dK3=x$< zc%*vbjVNJAt%mS%3?VB*)3e@O5k884o**a>CI4_47@zBE^OGhj3j#gESrQrlBufGt d*EO;!eF1)BW!G6Pv=smV002ovPDHLkV1lpxZW#ao literal 0 HcmV?d00001 diff --git a/apps/timestamplog/changelog.txt b/apps/timestamplog/changelog.txt new file mode 100644 index 000000000..ec66c5568 --- /dev/null +++ b/apps/timestamplog/changelog.txt @@ -0,0 +1 @@ +0.01: Initial version diff --git a/apps/timestamplog/metadata.json b/apps/timestamplog/metadata.json new file mode 100644 index 000000000..8114b159d --- /dev/null +++ b/apps/timestamplog/metadata.json @@ -0,0 +1,16 @@ +{ "id": "timestamplog", + "name": "Timestamp log", + "shortName":"Timestamp log", + "icon": "app.png", + "version": "0.01", + "description": "Conveniently record a series of date/time stamps", + "tags": "timestamp, log", + "supports": ["BANGLEJS2"], + "storage": [ + {"name": "timestamplog.app.js", "url": "app.js"}, + {"name": "timestamplog.img", "url": "app-icon.js", "evaluate":true} + ], + "data": [ + {"name": "timestamplog.settings", "url": "timestamplog.settings"}, + {"name": "timestamplog.json", "url": "timestamplog.json"} +} From 453a4a0697509b503e448aa743e3e81f8b96d1b2 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Mon, 27 May 2024 14:59:25 -0500 Subject: [PATCH 047/423] Fix sanity-check errors --- apps/timestamplog/changelog.txt | 1 - apps/timestamplog/metadata.json | 6 ++++-- 2 files changed, 4 insertions(+), 3 deletions(-) delete mode 100644 apps/timestamplog/changelog.txt diff --git a/apps/timestamplog/changelog.txt b/apps/timestamplog/changelog.txt deleted file mode 100644 index ec66c5568..000000000 --- a/apps/timestamplog/changelog.txt +++ /dev/null @@ -1 +0,0 @@ -0.01: Initial version diff --git a/apps/timestamplog/metadata.json b/apps/timestamplog/metadata.json index 8114b159d..98d57e4bc 100644 --- a/apps/timestamplog/metadata.json +++ b/apps/timestamplog/metadata.json @@ -1,4 +1,5 @@ -{ "id": "timestamplog", +{ + "id": "timestamplog", "name": "Timestamp log", "shortName":"Timestamp log", "icon": "app.png", @@ -8,9 +9,10 @@ "supports": ["BANGLEJS2"], "storage": [ {"name": "timestamplog.app.js", "url": "app.js"}, - {"name": "timestamplog.img", "url": "app-icon.js", "evaluate":true} + {"name": "timestamplog.img", "url": "app-icon.js", "evaluate": true} ], "data": [ {"name": "timestamplog.settings", "url": "timestamplog.settings"}, {"name": "timestamplog.json", "url": "timestamplog.json"} + ] } From 73ac306b76a046197acb8d0c56183e93e2dd81e2 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Mon, 27 May 2024 19:48:47 -0500 Subject: [PATCH 048/423] Fix metadata.json error (should not try to download data files) --- apps/timestamplog/metadata.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/timestamplog/metadata.json b/apps/timestamplog/metadata.json index 98d57e4bc..2963953c6 100644 --- a/apps/timestamplog/metadata.json +++ b/apps/timestamplog/metadata.json @@ -12,7 +12,7 @@ {"name": "timestamplog.img", "url": "app-icon.js", "evaluate": true} ], "data": [ - {"name": "timestamplog.settings", "url": "timestamplog.settings"}, - {"name": "timestamplog.json", "url": "timestamplog.json"} + {"name": "timestamplog.settings"}, + {"name": "timestamplog.json"} ] } From df84f229e4fcfaa929962b70e95327a23500d963 Mon Sep 17 00:00:00 2001 From: Travis Evans Date: Mon, 27 May 2024 19:49:08 -0500 Subject: [PATCH 049/423] Give up on push-to-delete; Layout's cbl callback just doesn't work --- apps/timestamplog/app.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/timestamplog/app.js b/apps/timestamplog/app.js index 9e4fa2273..87f227f46 100644 --- a/apps/timestamplog/app.js +++ b/apps/timestamplog/app.js @@ -554,8 +554,8 @@ class LogEntryScreen { {type: 'txt', font: this.defaultFont, id: 'date', label: '?'}, {type: 'txt', font: this.defaultFont, id: 'time', label: '?'}, {type: '', id: 'placeholder', fillx: 1, filly: 1}, - {type: 'btn', font: '6x15', label: 'Hold to delete', - cbl: this.delLogItem.bind(this)}, + {type: 'btn', font: '12x20', label: 'Delete', + cb: this.delLogItem.bind(this)}, ], }, { From d7066a081fcc5dd60bb446816abb2c5a4cb3290a Mon Sep 17 00:00:00 2001 From: jeonlab <37269397+jeonlab@users.noreply.github.com> Date: Tue, 28 May 2024 21:42:04 -0400 Subject: [PATCH 050/423] Update metadata.json --- apps/gpssetup/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/gpssetup/metadata.json b/apps/gpssetup/metadata.json index b8b6dfc23..15a7c1cf2 100644 --- a/apps/gpssetup/metadata.json +++ b/apps/gpssetup/metadata.json @@ -6,7 +6,7 @@ "description": "Configure the GPS power options and store them in the GPS nvram", "icon": "gpssetup.png", "tags": "gps,tools,outdoors", - "supports": ["BANGLEJS"], + "supports": ["BANGLEJS","BANGLEJS2"], "readme": "README.md", "storage": [ {"name":"gpssetup","url":"gpssetup.js"}, From 2d256f23c5e9a2af969dca79d8eb0841e0bc7926 Mon Sep 17 00:00:00 2001 From: lauzonhomeschool <85599144+lauzonhomeschool@users.noreply.github.com> Date: Sun, 28 Apr 2024 22:59:53 -0400 Subject: [PATCH 051/423] [datetime_picker] create app and use it in alarm app --- apps/alarm/ChangeLog | 1 + apps/alarm/README.md | 2 +- apps/alarm/app.js | 112 +++++++++++++-------- apps/alarm/metadata.json | 2 +- apps/datetime_picker/ChangeLog | 1 + apps/datetime_picker/README.md | 9 ++ apps/datetime_picker/app-icon.js | 1 + apps/datetime_picker/app.js | 5 + apps/datetime_picker/app.png | Bin 0 -> 1679 bytes apps/datetime_picker/lib.js | 145 ++++++++++++++++++++++++++++ apps/datetime_picker/metadata.json | 16 +++ apps/datetime_picker/screenshot.png | Bin 0 -> 2430 bytes 12 files changed, 252 insertions(+), 42 deletions(-) create mode 100644 apps/datetime_picker/ChangeLog create mode 100644 apps/datetime_picker/README.md create mode 100644 apps/datetime_picker/app-icon.js create mode 100644 apps/datetime_picker/app.js create mode 100644 apps/datetime_picker/app.png create mode 100644 apps/datetime_picker/lib.js create mode 100644 apps/datetime_picker/metadata.json create mode 100644 apps/datetime_picker/screenshot.png diff --git a/apps/alarm/ChangeLog b/apps/alarm/ChangeLog index 9cf5972c4..a3a5dfc1c 100644 --- a/apps/alarm/ChangeLog +++ b/apps/alarm/ChangeLog @@ -50,3 +50,4 @@ 0.45: Fix new alarm when selectedAlarm is undefined 0.46: Show alarm groups if the Show Group setting is ON. Scroll alarms menu back to previous position when getting back to it. 0.47: Fix wrap around when snoozed through midnight +0.48: Use datetimeinput for Events, if available. Scroll back when getting out of group. Menu date format setting for shorter dates on current year. diff --git a/apps/alarm/README.md b/apps/alarm/README.md index 43f72665e..77aa61d2c 100644 --- a/apps/alarm/README.md +++ b/apps/alarm/README.md @@ -2,7 +2,7 @@ This app allows you to add/modify any alarms, timers and events. -Optional: When a keyboard app is detected, you can add a message to display when any of these is triggered. +Optional: When a keyboard app is detected, you can add a message to display when any of these is triggered. If a datetime input app (e.g. datetime_picker) is detected, it will be used for the selection of the date+time of events. It uses the [`sched` library](https://github.com/espruino/BangleApps/blob/master/apps/sched) to handle the alarm scheduling in an efficient way that can work alongside other apps. diff --git a/apps/alarm/app.js b/apps/alarm/app.js index 3f9aaba68..64f233c5c 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -50,13 +50,17 @@ function handleFirstDayOfWeek(dow) { alarms.filter(e => e.timer === undefined).forEach(a => a.dow = handleFirstDayOfWeek(a.dow)); function getLabel(e) { - const dateStr = e.date && require("locale").date(new Date(e.date), 1); + const dateStr = getDateText(e.date); return (e.timer ? require("time_utils").formatDuration(e.timer) : (dateStr ? `${dateStr}${e.rp?"*":""} ${require("time_utils").formatTime(e.t)}` : require("time_utils").formatTime(e.t) + (e.rp ? ` ${decodeRepeat(e)}` : "")) ) + (e.msg ? ` ${e.msg}` : ""); } +function getDateText(d) { + return d && (settings.menuDateFormat === "mmdd" ? d.substring(d.startsWith(new Date().getFullYear()) ? 5 : 0) : require("locale").date(new Date(d), 1)); +} + function trimLabel(label, maxLength) { if(settings.showOverflow) return label; return (label.length > maxLength @@ -75,10 +79,10 @@ function formatAlarmProperty(msg) { } } -function showMainMenu(scroll, group) { +function showMainMenu(scroll, group, scrollback) { const menu = { "": { "title": group || /*LANG*/"Alarms & Timers", scroll: scroll }, - "< Back": () => group ? showMainMenu() : load(), + "< Back": () => group ? showMainMenu(scrollback) : load(), /*LANG*/"New...": () => showNewMenu(group) }; const getGroups = settings.showGroup && !group; @@ -98,7 +102,7 @@ function showMainMenu(scroll, group) { }); if (!group) { - Object.keys(groups).sort().forEach(g => menu[g] = () => showMainMenu(null, g)); + Object.keys(groups).sort().forEach(g => menu[g] = () => showMainMenu(null, g, scroller.scroll)); menu[/*LANG*/"Advanced"] = () => showAdvancedMenu(); } @@ -119,6 +123,7 @@ function showNewMenu(group) { } function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate, scroll, group) { + console.log(scroll); var isNew = alarmIndex === undefined; var alarm = require("sched").newDefaultAlarm(); @@ -138,6 +143,8 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate, scroll, group) { var title = date ? (isNew ? /*LANG*/"New Event" : /*LANG*/"Edit Event") : (isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm"); var keyboard = "textinput"; try {keyboard = require(keyboard);} catch(e) {keyboard = null;} + var datetimeinput; + try {datetimeinput = require("datetimeinput");} catch(e) {datetimeinput = null;} const menu = { "": { "title": title }, @@ -145,41 +152,66 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate, scroll, group) { prepareAlarmForSave(alarm, alarmIndex, time, date); saveAndReload(); showMainMenu(scroll, group); - }, - /*LANG*/"Hour": { - value: time.h, - format: v => ("0" + v).substr(-2), - min: 0, - max: 23, - wrap: true, - onchange: v => time.h = v - }, - /*LANG*/"Minute": { - value: time.m, - format: v => ("0" + v).substr(-2), - min: 0, - max: 59, - wrap: true, - onchange: v => time.m = v - }, - /*LANG*/"Day": { - value: date ? date.getDate() : null, - min: 1, - max: 31, - wrap: true, - onchange: v => date.setDate(v) - }, - /*LANG*/"Month": { - value: date ? date.getMonth() + 1 : null, - format: v => require("date_utils").month(v), - onchange: v => date.setMonth((v+11)%12) - }, - /*LANG*/"Year": { - value: date ? date.getFullYear() : null, - min: new Date().getFullYear(), - max: 2100, - onchange: v => date.setFullYear(v) - }, + } + }; + + if (alarm.date && datetimeinput) { + menu[`${getDateText(date.toLocalISOString().slice(0,10))} ${require("time_utils").formatTime(time)}`] = { + value: date, + format: v => "", + onchange: v => { + setTimeout(() => { + var datetime = new Date(v.getTime()); + datetime.setHours(time.h, time.m); + datetimeinput.input({datetime}).then(result => { + time.h = result.getHours(); + time.m = result.getMinutes(); + prepareAlarmForSave(alarm, alarmIndex, time, result, true); + setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate, scroll, group); + }); + }, 100); + } + }; + } else { + Object.assign(menu, { + /*LANG*/"Hour": { + value: time.h, + format: v => ("0" + v).substr(-2), + min: 0, + max: 23, + wrap: true, + onchange: v => time.h = v + }, + /*LANG*/"Minute": { + value: time.m, + format: v => ("0" + v).substr(-2), + min: 0, + max: 59, + wrap: true, + onchange: v => time.m = v + }, + /*LANG*/"Day": { + value: date ? date.getDate() : null, + min: 1, + max: 31, + wrap: true, + onchange: v => date.setDate(v) + }, + /*LANG*/"Month": { + value: date ? date.getMonth() + 1 : null, + format: v => require("date_utils").month(v), + onchange: v => date.setMonth((v+11)%12) + }, + /*LANG*/"Year": { + value: date ? date.getFullYear() : null, + min: new Date().getFullYear(), + max: 2100, + onchange: v => date.setFullYear(v) + } + }); + } + + Object.assign(menu, { /*LANG*/"Message": { value: alarm.msg, format: formatAlarmProperty, @@ -241,7 +273,7 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate, scroll, group) { saveAndReload(); showMainMenu(scroll, group); } - }; + }); if (!keyboard) delete menu[/*LANG*/"Message"]; if (!keyboard || !settings.showGroup) delete menu[/*LANG*/"Group"]; diff --git a/apps/alarm/metadata.json b/apps/alarm/metadata.json index 20969e6df..78cd4bd4e 100644 --- a/apps/alarm/metadata.json +++ b/apps/alarm/metadata.json @@ -2,7 +2,7 @@ "id": "alarm", "name": "Alarms & Timers", "shortName": "Alarms", - "version": "0.47", + "version": "0.48", "description": "Set alarms and timers on your Bangle", "icon": "app.png", "tags": "tool,alarm", diff --git a/apps/datetime_picker/ChangeLog b/apps/datetime_picker/ChangeLog new file mode 100644 index 000000000..ef4afacd0 --- /dev/null +++ b/apps/datetime_picker/ChangeLog @@ -0,0 +1 @@ +0.01: New drag/swipe date time picker, e.g. for use with dated events alarms diff --git a/apps/datetime_picker/README.md b/apps/datetime_picker/README.md new file mode 100644 index 000000000..8dac1dceb --- /dev/null +++ b/apps/datetime_picker/README.md @@ -0,0 +1,9 @@ +# App Name + +Datetime Picker allows to swipe along the bars to select date and time elements, e.g. for the datetime of Events in the Alarm App. As a standalone app, it allows to see the weekday of a given date and, once a datetime is selected, the number of days and time between that datetime and now. + +Screenshot: ![datetime with swipe controls](screenshot.png) + +## Controls + +Swipe to increase or decrease date and time elements. Press button or go back to select shown datetime. diff --git a/apps/datetime_picker/app-icon.js b/apps/datetime_picker/app-icon.js new file mode 100644 index 000000000..89250ff58 --- /dev/null +++ b/apps/datetime_picker/app-icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwgP/AAnfAgf9z4FD/AFE/gFECIoFB98+tv+voFB//C/99z3Z7+J84XC3/7DpAFhKYP3AgP3AoPAOQMD/v/84LB+Z2FABiDKPoqJFKaWe/P/9Pznuf+wKB/29z+2//uTYOeTYPtRMxZKQaPAh6hBnEBwEGAoMYgHf9+/dwP5A==")) diff --git a/apps/datetime_picker/app.js b/apps/datetime_picker/app.js new file mode 100644 index 000000000..7bc66f6c5 --- /dev/null +++ b/apps/datetime_picker/app.js @@ -0,0 +1,5 @@ +require("datetimeinput").input().then(result => { + E.showPrompt(`${result}\n\n${require("time_utils").formatDuration(Math.abs(result-Date.now()))}`, {buttons:{"Ok":true}}).then(function() { + load(); + }); +}); diff --git a/apps/datetime_picker/app.png b/apps/datetime_picker/app.png new file mode 100644 index 0000000000000000000000000000000000000000..b7cb4b46ba0943e627ad0c948b747e522735a7a0 GIT binary patch literal 1679 zcmV;A25|X_P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGma{vGma{-JZxd#9M01|XXSaefwW^{L9 za%BKVa%E+1b7*gLUR4MM000ICNklHl%v^{kwk8{n(TLEqDL) z_dM3Mow?6*pL3sco!9GJhd|%)_3Kyq`0*niwFVOt6a1G6Bqk=(fddEl2W`OY>@4lt zwTpUtdr6p@nj(FDeG&wL>g(&pB$|zghzPE)udgo&uV23=dwY8(QBhGLCed81tgKL6 zTpSZPa^#3|2!)1*lBK03C(s5=PEIm`y1F_gfn&#xk-51!zf@sqX^HOKxkKn7;K`FG z^!)jAer|7X=XXG3V({SSY-}v;-o2Y{+_*t`d3kjA?p;bxPp7?m_i_)S zV0D1x;lqdN{Q2`7udc49=;&yA_wF5~rlwLzNC=gemy<%F;P(I;1?46uC)2}+4=E@p zh=PNI>FwLMVhSZ>5kNQLW2kI=d^~^j^y$;9y>jc;Eh;D|pp1+RN=Zp!vI`3fVs7H+ z{QP{*i(c^X@SwA2&r)DuAccpA)9~;x$FY*AFfuYij~+du?CfmLgS;T!(a|BMP(l^~ zOcXyqKWPwId@V>BEN)Vs2#*U|1ob7f{BjQ>U0z zKtKSuY;JDu52VkWIl}}|c{C8d4KiyN7g+=_uCHCYMoCFYyzXXZX41`@H)&>OhFbqiOIdtX96`mWfUcKTxs2m8XHX?O^9MWKj&Ck#C@WpRyA&bDD1cvAD zV9N>oF8&hOYHU9O+9o9+KtX_|#ItA5$j;7=#T<6Vt;UxxU&JSAEH5w9$&)9UfWN;# z34?=!WMpK-Kh)ONipivbUDwOYi;4b;aQX6Oa&T~Oii!YscOF9L&YdH7 zcXv*(9-v*2>HiAYNY?{)d@Cy}Rsu}m)2B}~G&IC7*F#TF5AE5rha4RpC0lPZLPA0U z?bxw{E?&I2;bw=*15Dso0KxUA0GaaQ#S1Ye55G)6)4&A2ef!2io1jv$OaPJ@Gh17L z#&&aaqtVe(5du#Uz>y$aK{A!i5VY6c-nhuC6ZW=;-h&py|Ly=Irdu z1h66si;IhV!ZI^6BRp#Zc>VuQ0FBRc;P~<5WMN^!FSP+UPlG^JRh5#!wv^KlRj@X2 zmfbA$_4V;zXdOrexE^c8o9Z2abMyWC_t`H}hb#iHOrZAQ=isBTKhSme?%iYg4Oamk zj~t+qa5D}bJjii)Nf@?SSy{X!Vko?M^M>UN41yCUPOzH+M*yY27{dhiOlM~& z_W*hxao9Qg_wVPm9`-->D-~oBz=zSoC>TIXAYNHn$=wZ05GNuzK_4s#5>j|!_p76DDhUjkc=?MDDoTAOSYoZLVF7bpVOTQ~uR zhK4^qNNWmk9sGl>(%Ra}5dTNppxfsrOO=RwoZ3IMjy@ z9pa4vhZd;-?+9te#>TiH05_4BE?we(zL3hZQ9!x4R)tho2Tp-5E-vfV2N&p&*$Rb% Z{sH}H_||6oa%uno002ovPDHLkV1igS1n&R< literal 0 HcmV?d00001 diff --git a/apps/datetime_picker/lib.js b/apps/datetime_picker/lib.js new file mode 100644 index 000000000..c3e51ae4d --- /dev/null +++ b/apps/datetime_picker/lib.js @@ -0,0 +1,145 @@ +exports.input = function(options) { + options = options||{}; + var selectedDate; + if (options.datetime instanceof Date) { + selectedDate = new Date(options.datetime.getTime()); + } else { + selectedDate = new Date(); + selectedDate.setMinutes(0); + selectedDate.setSeconds(0); + selectedDate.setMilliseconds(0); + selectedDate.setHours(selectedDate.getHours() + 1); + } + + var R; + var tip = {w: 12, h: 10}; + var arrowRectArray; + var dragging = null; + var startPos = null; + var dateAtDragStart = null; + var SELECTEDFONT = '6x8:2'; + + function drawDateTime() { + g.clearRect(R.x+tip.w,R.y,R.x2-tip.w,R.y+40); + g.clearRect(R.x+tip.w,R.y2-60,R.x2-tip.w,R.y2-40); + + g.setFont(SELECTEDFONT).setColor(g.theme.fg).setFontAlign(-1, -1, 0); + var dateUtils = require('date_utils'); + g.drawString(selectedDate.getFullYear(), R.x+tip.w+10, R.y+15) + .drawString(dateUtils.month(selectedDate.getMonth()+1,1), R.x+tip.w+65, R.y+15) + .drawString(selectedDate.getDate(), R.x2-tip.w-40, R.y+15) + .drawString(`${dateUtils.dow(selectedDate.getDay(), 1)} ${selectedDate.toLocalISOString().slice(11,16)}`, R.x+tip.w+10, R.y2-60); + } + + let dragHandler = function(event) { + "ram"; + + if (event.b) { + if (dragging === null) { + // determine which component we are affecting + var rect = arrowRectArray.find(rect => rect.y2 + ? (event.y >= rect.y && event.y <= rect.y2 && event.x >= rect.x - 10 && event.x <= rect.x + tip.w + 10) + : (event.x >= rect.x && event.x <= rect.x2 && event.y >= rect.y - tip.w - 5 && event.y <= rect.y + 5)); + if (rect) { + dragging = rect; + startPos = dragging.y2 ? event.y : event.x; + dateAtDragStart = selectedDate; + } + } + + if (dragging) { + dragging.swipe(dragging.y2 ? startPos - event.y : event.x - startPos); + drawDateTime(); + } + } else { + dateAtDragStart = null; + dragging = null; + startPos = null; + } + }; + + let catchSwipe = ()=>{ + E.stopEventPropagation&&E.stopEventPropagation(); + }; + + return new Promise((resolve,reject) => { + // Interpret touch input + Bangle.setUI({ + mode: 'custom', + back: ()=>{ + Bangle.setUI(); + Bangle.prependListener&&Bangle.removeListener('swipe', catchSwipe); // Remove swipe listener if it was added with `Bangle.prependListener()` (fw2v19 and up). + g.clearRect(Bangle.appRect); + resolve(selectedDate); + }, + drag: dragHandler + }); + Bangle.prependListener&&Bangle.prependListener('swipe', catchSwipe); // Intercept swipes on fw2v19 and later. Should not break on older firmwares. + + R = Bangle.appRect; + g.clear(); + Bangle.loadWidgets(); + Bangle.drawWidgets(); + + function drawArrow(rect) { + if(rect.x2) { + g.fillRect(rect.x + tip.h, rect.y - tip.w + 4, rect.x2 - tip.h, rect.y - 4) + .fillPoly([rect.x + tip.h, rect.y, rect.x + tip.h, rect.y - tip.w, rect.x, rect.y - (tip.w / 2)]) + .fillPoly([rect.x2-tip.h, rect.y, rect.x2 - tip.h, rect.y - tip.w, rect.x2, rect.y - (tip.w / 2)]); + } else { + g.fillRect(rect.x + 4, rect.y + tip.h, rect.x + tip.w - 4, rect.y2 - tip.h) + .fillPoly([rect.x, rect.y + tip.h, rect.x + tip.w, rect.y + tip.h, rect.x + (tip.w / 2), rect.y]) + .fillPoly([rect.x, rect.y2 - tip.h, rect.x + tip.w, rect.y2 - tip.h, rect.x + (tip.w / 2), rect.y2]); + } + + } + + var yearArrowRect = {x: R.x, y: R.y, y2: R.y + (R.y2 - R.y) * 0.4, swipe: d => { + selectedDate = new Date(dateAtDragStart.valueOf()); + selectedDate.setFullYear(dateAtDragStart.getFullYear() + Math.floor(d/10)); + if (dateAtDragStart.getDate() != selectedDate.getDate()) selectedDate.setDate(0); + }}; + + var weekArrowRect = {x: R.x, y: yearArrowRect.y2 + 10, y2: R.y2 - tip.w - 5, swipe: d => { + selectedDate = new Date(dateAtDragStart.valueOf()); + selectedDate.setDate(dateAtDragStart.getDate() + (Math.floor(d/10) * 7)); + }}; + + var dayArrowRect = {x: R.x2 - tip.w, y: R.y, y2: R.y + (R.y2 - R.y) * 0.4, swipe: d => { + selectedDate = new Date(dateAtDragStart.valueOf()); + selectedDate.setDate(dateAtDragStart.getDate() + Math.floor(d/10)); + }}; + + var fifteenMinutesArrowRect = {x: R.x2 - tip.w, y: dayArrowRect.y2 + 10, y2: R.y2 - tip.w - 5, swipe: d => { + selectedDate = new Date(dateAtDragStart.valueOf()); + selectedDate.setMinutes((((dateAtDragStart.getMinutes() - (dateAtDragStart.getMinutes() % 15) + (Math.floor(d/14) * 15)) % 60) + 60) % 60); + }}; + + var weekdayArrowRect = {x: R.x, y: R.y2, x2: (R.x2 - R.x) * 0.3 - 5, swipe: d => { + selectedDate = new Date(dateAtDragStart.valueOf()); + selectedDate.setDate(dateAtDragStart.getDate() + Math.floor(d/10)); + }}; + + var hourArrowRect = {x: weekdayArrowRect.x2 + 5, y: R.y2, x2: weekdayArrowRect.x2 + (R.x2 - R.x) * 0.38, swipe: d => { + selectedDate = new Date(dateAtDragStart.valueOf()); + selectedDate.setHours((((dateAtDragStart.getHours() + Math.floor(d/10)) % 24) + 24) % 24); + }}; + + var minutesArrowRect = {x: hourArrowRect.x2 + 5, y: R.y2, x2: R.x2, swipe: d => { + selectedDate = new Date(dateAtDragStart.valueOf()); + selectedDate.setMinutes((((dateAtDragStart.getMinutes() + Math.floor(d/7)) % 60) + 60) % 60); + }}; + + var monthArrowRect = {x: (R.x2 - R.x) * 0.2, y: R.y2 / 2 + 5, x2: (R.x2 - R.x) * 0.8, swipe: d => { + selectedDate = new Date(dateAtDragStart.valueOf()); + selectedDate.setMonth(dateAtDragStart.getMonth() + Math.floor(d/10)); + if (dateAtDragStart.getDate() != selectedDate.getDate()) selectedDate.setDate(0); + }}; + + arrowRectArray = [yearArrowRect, weekArrowRect, dayArrowRect, fifteenMinutesArrowRect, + weekdayArrowRect, hourArrowRect, minutesArrowRect, monthArrowRect]; + + drawDateTime(); + arrowRectArray.forEach(drawArrow); + }); +}; diff --git a/apps/datetime_picker/metadata.json b/apps/datetime_picker/metadata.json new file mode 100644 index 000000000..df968589e --- /dev/null +++ b/apps/datetime_picker/metadata.json @@ -0,0 +1,16 @@ +{ "id": "datetime_picker", + "name": "Datetime picker", + "shortName":"Datetime picker", + "version":"0.01", + "description": "Allows to pick a date and time by swiping.", + "icon":"app.png", + "tags":"datetimeinput", + "supports" : ["BANGLEJS2"], + "readme": "README.md", + "screenshots" : [ { "url":"screenshot.png" } ], + "storage": [ + {"name":"datetimeinput","url":"lib.js"}, + {"name":"datetime_picker.app.js","url":"app.js"}, + {"name":"datetime_picker.img","url":"app-icon.js","evaluate":true} + ] +} diff --git a/apps/datetime_picker/screenshot.png b/apps/datetime_picker/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..6e14af8be43a781874ce26e93b10562e53db0bc6 GIT binary patch literal 2430 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 Date: Sun, 9 Jun 2024 00:19:05 -0400 Subject: [PATCH 052/423] [datetime_picker] convert to pure library, improve Readme with control details and sample code --- apps/alarm/app.js | 1 - apps/datetime_picker/README.md | 29 ++++++++++++++++++++++++++- apps/datetime_picker/metadata.json | 7 ++++--- apps/datetime_picker/screenshot2.png | Bin 0 -> 3967 bytes 4 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 apps/datetime_picker/screenshot2.png diff --git a/apps/alarm/app.js b/apps/alarm/app.js index 64f233c5c..053505187 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -123,7 +123,6 @@ function showNewMenu(group) { } function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate, scroll, group) { - console.log(scroll); var isNew = alarmIndex === undefined; var alarm = require("sched").newDefaultAlarm(); diff --git a/apps/datetime_picker/README.md b/apps/datetime_picker/README.md index 8dac1dceb..f602d44e1 100644 --- a/apps/datetime_picker/README.md +++ b/apps/datetime_picker/README.md @@ -1,9 +1,36 @@ # App Name -Datetime Picker allows to swipe along the bars to select date and time elements, e.g. for the datetime of Events in the Alarm App. As a standalone app, it allows to see the weekday of a given date and, once a datetime is selected, the number of days and time between that datetime and now. +Datetime Picker allows to swipe along the bars to select date and time elements, e.g. for the datetime of Events in the Alarm App. Screenshot: ![datetime with swipe controls](screenshot.png) ## Controls Swipe to increase or decrease date and time elements. Press button or go back to select shown datetime. + +![datetime with numbered swipe controls](screenshot2.png) + +1. Year: swipe up to increase, down to decrease +2. Month: swipe right to increase, left to decrease +3. Day: swipe up to increase, down to decrease +4. Week: swipe up to increase week (same day next week), down to decrease (same day previous week) +5. Weekday: swipe right to increase, left to decrease (basically the same effect as 3, but with a focus on the weekday) +6. Hour: swipe right to increase, left to decrease +7. Minutes: swipe right to increase, left to decrease +8. 15 minutes: 00, 15, 30 or 45 minutes; swipe up to increase, down to decrease; wrap-around i.e. goes back to 00 after increasing from 45 + +## How to use it in code + +Sample code which would show a prompt with the number of days and hours between now and the selected datetime: + + require("datetimeinput").input().then(result => { + E.showPrompt(`${result}\n\n${require("time_utils").formatDuration(Math.abs(result-Date.now()))}`, {buttons:{"Ok":true}}).then(function() { + load(); + }); + }); + +To set the initial value, pass a Date object named _datetime_, e.g. for today at 9:30 : + + var datetime = new Date(); + datetime.setHours(9, 30); + require("datetimeinput").input({datetime}).then(... \ No newline at end of file diff --git a/apps/datetime_picker/metadata.json b/apps/datetime_picker/metadata.json index df968589e..63ca7aa17 100644 --- a/apps/datetime_picker/metadata.json +++ b/apps/datetime_picker/metadata.json @@ -2,15 +2,16 @@ "name": "Datetime picker", "shortName":"Datetime picker", "version":"0.01", - "description": "Allows to pick a date and time by swiping.", + "description": "A library that allows to pick a date and time by swiping.", "icon":"app.png", + "type":"datetimeinput", "tags":"datetimeinput", - "supports" : ["BANGLEJS2"], + "supports" : ["BANGLEJS2"], + "provides_modules" : ["datetimeinput"], "readme": "README.md", "screenshots" : [ { "url":"screenshot.png" } ], "storage": [ {"name":"datetimeinput","url":"lib.js"}, - {"name":"datetime_picker.app.js","url":"app.js"}, {"name":"datetime_picker.img","url":"app-icon.js","evaluate":true} ] } diff --git a/apps/datetime_picker/screenshot2.png b/apps/datetime_picker/screenshot2.png new file mode 100644 index 0000000000000000000000000000000000000000..9a1f9d0485d19e107a5f70f2b9572e2186a83a85 GIT binary patch literal 3967 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 Date: Sun, 9 Jun 2024 00:21:49 -0400 Subject: [PATCH 053/423] [datetime_picker] build doesn't allow type --- apps/datetime_picker/metadata.json | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/datetime_picker/metadata.json b/apps/datetime_picker/metadata.json index 63ca7aa17..e92620a91 100644 --- a/apps/datetime_picker/metadata.json +++ b/apps/datetime_picker/metadata.json @@ -4,7 +4,6 @@ "version":"0.01", "description": "A library that allows to pick a date and time by swiping.", "icon":"app.png", - "type":"datetimeinput", "tags":"datetimeinput", "supports" : ["BANGLEJS2"], "provides_modules" : ["datetimeinput"], From c8fa6637f3c8e15f0a7b010265891264b0dd1e07 Mon Sep 17 00:00:00 2001 From: paul-arg Date: Mon, 10 Jun 2024 10:10:13 +0200 Subject: [PATCH 054/423] widbtstates: improve colours depending on BT state --- apps/widbtstates/widget.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/widbtstates/widget.ts b/apps/widbtstates/widget.ts index 8f02c1b8c..40f50f627 100644 --- a/apps/widbtstates/widget.ts +++ b/apps/widbtstates/widget.ts @@ -30,12 +30,12 @@ } } = { [State.Active]: { - false: "#fff", + false: "#000", true: "#fff", }, [State.Connected]: { - false: "#0ff", - true: "#00f", + false: "#00f", + true: "#0ff", }, }; From 1a422b10410771460d58013c3d9f8b62b98302c5 Mon Sep 17 00:00:00 2001 From: lauzonhomeschool <85599144+lauzonhomeschool@users.noreply.github.com> Date: Mon, 10 Jun 2024 19:46:50 -0400 Subject: [PATCH 055/423] [datetime_picker] type module --- apps/datetime_picker/metadata.json | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/datetime_picker/metadata.json b/apps/datetime_picker/metadata.json index e92620a91..173e21020 100644 --- a/apps/datetime_picker/metadata.json +++ b/apps/datetime_picker/metadata.json @@ -4,6 +4,7 @@ "version":"0.01", "description": "A library that allows to pick a date and time by swiping.", "icon":"app.png", + "type":"module", "tags":"datetimeinput", "supports" : ["BANGLEJS2"], "provides_modules" : ["datetimeinput"], From 27a6aeea8f2a876009fd882bc806ba11055437dd Mon Sep 17 00:00:00 2001 From: paul-arg Date: Tue, 11 Jun 2024 13:08:43 +0200 Subject: [PATCH 056/423] elapsed_t: add option to set clock as default --- apps/elapsed_t/app.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/apps/elapsed_t/app.js b/apps/elapsed_t/app.js index 384d869c3..c387746d0 100644 --- a/apps/elapsed_t/app.js +++ b/apps/elapsed_t/app.js @@ -206,9 +206,25 @@ var menu = { setTarget(false); updateQueueMillis(settings.displaySeconds); draw(); + }, + 'Set clock as default': function () { + setClockAsDefault(); + E.showAlert("Elapsed Time was set as default").then(function() { + E.showMenu(); + inMenu = false; + Bangle.setUI("clock"); + draw(); + }); } }; +function setClockAsDefault(){ + let storage = require('Storage'); + let settings = storage.readJSON('setting.json',true)||{clock:null}; + settings.clock = "elapsed_t.app.js"; + storage.writeJSON('setting.json', settings); +} + function setTarget(set) { if (set) { target = new Date( From e5372957179d8068cbad734c0eb4103c8e562945 Mon Sep 17 00:00:00 2001 From: paul-arg Date: Tue, 11 Jun 2024 13:09:05 +0200 Subject: [PATCH 057/423] elapsed_t: add option to set clock as default --- apps/elapsed_t/ChangeLog | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/elapsed_t/ChangeLog b/apps/elapsed_t/ChangeLog index 45cb47c9a..49fa514a8 100644 --- a/apps/elapsed_t/ChangeLog +++ b/apps/elapsed_t/ChangeLog @@ -1,2 +1,3 @@ 0.01: New App! -0.02: Handle AM/PM time in the "set target" menu. Add yesterday/today/tomorrow when showing target date to improve readability. \ No newline at end of file +0.02: Handle AM/PM time in the "set target" menu. Add yesterday/today/tomorrow when showing target date to improve readability. +0.03: Add option to set clock as default From eb55e5ddf29e63a622abfd0bfc35a4e7502d168e Mon Sep 17 00:00:00 2001 From: paul-arg Date: Tue, 11 Jun 2024 13:09:19 +0200 Subject: [PATCH 058/423] elapsed_t: add option to set clock as default --- apps/elapsed_t/metadata.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/elapsed_t/metadata.json b/apps/elapsed_t/metadata.json index 7f8460e49..fa0674e0b 100644 --- a/apps/elapsed_t/metadata.json +++ b/apps/elapsed_t/metadata.json @@ -3,7 +3,7 @@ "name": "Elapsed Time Clock", "shortName": "Elapsed Time", "type": "clock", - "version":"0.02", + "version":"0.03", "description": "A clock that calculates the time difference between now and any given target date.", "tags": "clock,tool", "supports": ["BANGLEJS2"], From 4e428a6fbc89384cb257d3c637dc3d270a06d395 Mon Sep 17 00:00:00 2001 From: paul-arg Date: Wed, 12 Jun 2024 19:09:07 +0200 Subject: [PATCH 059/423] elapsed_t: handle DST in day/month/year mode --- apps/elapsed_t/ChangeLog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/elapsed_t/ChangeLog b/apps/elapsed_t/ChangeLog index 49fa514a8..6a72c2590 100644 --- a/apps/elapsed_t/ChangeLog +++ b/apps/elapsed_t/ChangeLog @@ -1,3 +1,3 @@ 0.01: New App! 0.02: Handle AM/PM time in the "set target" menu. Add yesterday/today/tomorrow when showing target date to improve readability. -0.03: Add option to set clock as default +0.03: Add option to set clock as default, handle DST in day/month/year mode From 532abba4d5a4c039bf894e11d44298a0cbe75fe5 Mon Sep 17 00:00:00 2001 From: paul-arg Date: Wed, 12 Jun 2024 19:09:58 +0200 Subject: [PATCH 060/423] elapsed_t: handle DST in day/month/year mode --- apps/elapsed_t/app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/elapsed_t/app.js b/apps/elapsed_t/app.js index c387746d0..13fbca2cd 100644 --- a/apps/elapsed_t/app.js +++ b/apps/elapsed_t/app.js @@ -346,7 +346,7 @@ function diffToTarget() { diff.M = end.getMonth() - start.getMonth(); diff.D = end.getDate() - start.getDate(); diff.hh = end.getHours() - start.getHours(); - diff.mm = end.getMinutes() - start.getMinutes(); + diff.mm = end.getMinutes() - start.getMinutes() + end.getTimezoneOffset() - start.getTimezoneOffset(); diff.ss = end.getSeconds() - start.getSeconds(); // Adjust negative differences From 2f1dbab72eccc463ad2334652aba59ea36352d62 Mon Sep 17 00:00:00 2001 From: Albert Date: Thu, 13 Jun 2024 23:26:47 +0200 Subject: [PATCH 061/423] nostt app init --- apps/nostt/ChangeLog | 1 + apps/nostt/README.md | 8 + apps/nostt/metadata.json | 17 + apps/nostt/nostt.app.js | 506 ++++++++++++++++++++++++++++++ apps/nostt/nostt.icon.js | 1 + apps/nostt/nostt_logo.png | Bin 0 -> 1646 bytes apps/nostt/nostt_screenshot_1.png | Bin 0 -> 4415 bytes apps/nostt/nostt_screenshot_2.png | Bin 0 -> 5107 bytes 8 files changed, 533 insertions(+) create mode 100644 apps/nostt/ChangeLog create mode 100644 apps/nostt/README.md create mode 100644 apps/nostt/metadata.json create mode 100644 apps/nostt/nostt.app.js create mode 100644 apps/nostt/nostt.icon.js create mode 100644 apps/nostt/nostt_logo.png create mode 100644 apps/nostt/nostt_screenshot_1.png create mode 100644 apps/nostt/nostt_screenshot_2.png diff --git a/apps/nostt/ChangeLog b/apps/nostt/ChangeLog new file mode 100644 index 000000000..5314d96b9 --- /dev/null +++ b/apps/nostt/ChangeLog @@ -0,0 +1 @@ +1.00: NOS Teletekst finished! \ No newline at end of file diff --git a/apps/nostt/README.md b/apps/nostt/README.md new file mode 100644 index 000000000..ea3d7b3ff --- /dev/null +++ b/apps/nostt/README.md @@ -0,0 +1,8 @@ +# NOS Teletekst + + +Dutch Teletekst using the NOS Teletekst api. Requires http access via BangleJS GadgetBridge. See https://www.espruino.com/Gadgetbridge. Make sure `Allow Internet Access` is enabled. + +## Usage + +Tap once to bring up a numpad to enter the desired page. You can also swipe left/right to change the page, or swipe up/down to walk through the subpages. \ No newline at end of file diff --git a/apps/nostt/metadata.json b/apps/nostt/metadata.json new file mode 100644 index 000000000..8d59abd7a --- /dev/null +++ b/apps/nostt/metadata.json @@ -0,0 +1,17 @@ +{ + "id":"nostt", + "name":"NOS Teletekst", + "shortName": "Teletekst", + "version": "1.00", + "description": "Dutch Teletekst using the NOS Teletekst api. Requires http access via BangleJS GadgetBridge.", + "src":"nostt.app.js", + "storage": [ + {"name":"nostt.app.js","url":"nostt.app.js"}, + {"name":"nostt.img","url":"nostt.icon.js","evaluate":true} + ], + "readme": "README.md", + "icon":"nostt_logo.png", + "supports": ["BANGLEJS2"], + "screenshots": [{"url": "nostt_screenshot_1.png"}, {"url": "nostt_screenshot_2.png"}], + "tags": "nos,teletext,teletekst,news,weather" +} \ No newline at end of file diff --git a/apps/nostt/nostt.app.js b/apps/nostt/nostt.app.js new file mode 100644 index 000000000..ee8f0b5f6 --- /dev/null +++ b/apps/nostt/nostt.app.js @@ -0,0 +1,506 @@ +class View { + + constructor() { + + this.navigationState = { + prevPage: { + p: undefined, + s: undefined, + }, + prevSubPage: { + p: undefined, + s: undefined, + }, + nextPage: { + p: undefined, + s: undefined, + }, + nextSubPage: { + p: undefined, + s: undefined, + }, + currentPage: { + p: undefined, + s: undefined, + }, + }; + + this.colorArray = { + 0: [0, 0, 0], + 1: [1, 0, 0], + 2: [0, 1, 0], + 3: [1, 1, 0], + 4: [0, 0, 1], + 5: [1, 0, 1], + 6: [0, 1, 1], + 7: [1, 1, 1], + 16: [0, 0, 0], + 17: [1, 0, 0], + 18: [0, 1, 0], + 19: [1, 1, 0], + 20: [0, 0, 1], + 21: [1, 0, 1], + 22: [0, 1, 1], + 23: [1, 1, 1], + }; + + + } + + start() { + // @ts-ignore + g.clear(); + if (this.nextStartPage) { + this.show(this.nextStartPage); + this.nextStartPage = undefined; + } + else { + if (this.navigationState.currentPage.p) { + this.show(this.navigationState.currentPage.p); + } + else { + this.show(101); //load default + } + } + + } + + split_at_fourty(res, value) { + res.push(value.substring(0, 40)); + if (value.length > 40) { // at least two rows + return this.split_at_fourty(res, value.substring(40)); + } + else { + return res; + } + + } + +// strToUtf8Bytes(str) { +// const utf8 = []; +// for (let ii = 0; ii < str.length; ii++) { +// let charCode = str.charCodeAt(ii); +// if (charCode < 0x80) utf8.push(charCode); +// else if (charCode < 0x800) { +// utf8.push(0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f)); +// } else if (charCode < 0xd800 || charCode >= 0xe000) { +// utf8.push(0xe0 | (charCode >> 12), 0x80 | ((charCode >> 6) & 0x3f), 0x80 | (charCode & 0x3f)); +// } else { +// ii++; +// // Surrogate pair: +// // UTF-16 encodes 0x10000-0x10FFFF by subtracting 0x10000 and +// // splitting the 20 bits of 0x0-0xFFFFF into two halves +// charCode = 0x10000 + (((charCode & 0x3ff) << 10) | (str.charCodeAt(ii) & 0x3ff)); +// utf8.push( +// 0xf0 | (charCode >> 18), +// 0x80 | ((charCode >> 12) & 0x3f), +// 0x80 | ((charCode >> 6) & 0x3f), +// 0x80 | (charCode & 0x3f), +// ); +// } +// } +// return utf8; +// } + + loadPrevPage() { + if (this.navigationState.prevPage.p) { + this.show(this.navigationState.prevPage.p, this.navigationState.prevPage.s); + } + } + + loadNextPage() { + if (this.navigationState.nextPage.p) { + this.show(this.navigationState.nextPage.p, this.navigationState.nextPage.s); + } + } + + loadPrevSubPage() { + if (this.navigationState.prevSubPage.p) { + this.show(this.navigationState.prevSubPage.p, this.navigationState.prevSubPage.s); + } + } + + loadNextSubPage() { + if (this.navigationState.nextSubPage.p) { + this.show(this.navigationState.nextSubPage.p, this.navigationState.nextSubPage.s); + } + } + + handleSwipe(lr, ud){ + if (lr == -1 && ud == 0) { + this.loadNextPage(); + } + if (lr == 1 && ud == 0) { + this.loadPrevPage(); + } + if (lr == 0 && ud == 1) { + this.loadPrevSubPage(); + } + if (lr == 0 && ud == -1) { + this.loadNextSubPage(); + } + } + + show(pageId, subPageId) { + if(!subPageId){ + subPageId = 1; + } + + if (Bangle.http) { + Bangle.http('https://teletekst-data.nos.nl/page/' + pageId + '-' + subPageId).then((data) => { + + const res = data.resp; + g.clear(); + + + this.navigationState = { + prevPage: { + p: undefined, + s: undefined, + }, + prevSubPage: { + p: undefined, + s: undefined, + }, + nextPage: { + p: undefined, + s: undefined, + }, + nextSubPage: { + p: undefined, + s: undefined, + }, + currentPage: { + p: pageId, + s: subPageId, + }, + }; + + // set next -, previous -, next sub - and previous sub page + let navNIndex = res.indexOf('pn=n_'); + if (navNIndex > -1) { + this.navigationState.nextPage.p = parseInt(res.substring(navNIndex + 5, navNIndex + 8)); + this.navigationState.nextPage.s = parseInt(res.substring(navNIndex + 9, navNIndex + 10)); + } + let navPIndex = res.indexOf('pn=p_'); + if (navPIndex > -1) { + this.navigationState.prevPage.p = parseInt(res.substring(navPIndex + 5, navPIndex + 8)); + this.navigationState.prevPage.s = parseInt(res.substring(navPIndex + 9, navPIndex + 10)); + } + let navPSIndex = res.indexOf('pn=ps'); + if (navPSIndex > -1) { + this.navigationState.prevSubPage.p = parseInt(res.substring(navPSIndex + 5, navPSIndex + 8)); + this.navigationState.prevSubPage.s = parseInt(res.substring(navPSIndex + 9, navPSIndex + 10)); + } + let navNSIndex = res.indexOf('pn=ns'); + if (navNSIndex > -1) { + this.navigationState.nextSubPage.p = parseInt(res.substring(navNSIndex + 5, navNSIndex + 8)); + this.navigationState.nextSubPage.s = parseInt(res.substring(navNSIndex + 9, navNSIndex + 10)); + } + + let split = E.toString(res.split('
')[1].split('
')[0]); + + this.render(split); + }); + } + } + + + + + render(source) { + + g.setFontAlign(-1, -1); + g.setFont('4x6'); + + // @ts-ignore + const bytes = E.toUint8Array(E.decodeUTF8(source)); + let rowIndex = 0; + let totalIndex = 0; + let charIndex = 0; + + for (let charByte of bytes) { + { + if ((charByte >= 0 && charByte <= 7) || (charByte >= 16 && charByte <= 23)) { + const color = this.colorArray[charByte]; + g.setColor(color[0], color[1], color[2]); + } + } + g.drawString(source[totalIndex], (charIndex * 4) + 6, rowIndex * 7); + charIndex++; + totalIndex++; + if (charIndex == 40) { + rowIndex++; + charIndex = 0; + g.flip(); + } + } + } + + +} + +const BUTTON_BORDER_WITH = 2; + +class Button { +// position; +// value; +// highlightTimeoutId; + + + constructor(position, value) { + this.position = position; + this.value = value; + } + + draw(highlight) { + g.setColor(g.theme.fg); + g.fillRect( + this.position.x1, + this.position.y1, + this.position.x2, + this.position.y2 + ); + + if (highlight) { + g.setColor(g.theme.bgH); + } else { + g.setColor(g.theme.bg); + } + g.fillRect( + this.position.x1 + BUTTON_BORDER_WITH, + this.position.y1 + BUTTON_BORDER_WITH, + this.position.x2 - BUTTON_BORDER_WITH, + this.position.y2 - BUTTON_BORDER_WITH + ); + + g.setColor(g.theme.fg); + g.setFontAlign(0, 0); + g.setFont("Vector", 35); + g.drawString( + this.value, + this.position.x1 + (this.position.x2 - this.position.x1) / 2 + 2, + this.position.y1 + (this.position.y2 - this.position.y1) / 2 + 2 + ); + } + + handleTouchInput(n, e) { + if ( + e.x >= this.position.x1 && + e.x <= this.position.x2 && + e.y >= this.position.y1 && + e.y <= this.position.y2 + ) { + this.draw(true); // draw to highlight + this.highlightTimeoutId = setTimeout(() => { + this.draw(); + this.highlightTimeoutId = undefined; + }, 100); + return this.value; + } + else { + return undefined; + } + } + + disable() { + // disable button + if (this.highlightTimeoutId) { + clearTimeout(this.highlightTimeoutId); + this.highlightTimeoutId = undefined; + } + } + +} + +class Input { + + constructor(callback) { + this.inputCallback = callback; + this.inputVal = ""; + + let button1 = new Button({ x1: 1, y1: 35, x2: 58, y2: 70 }, '1'); + let button2 = new Button({ x1: 60, y1: 35, x2: 116, y2: 70 }, '2'); + let button3 = new Button({ x1: 118, y1: 35, x2: 174, y2: 70 }, '3'); + + let button4 = new Button({ x1: 1, y1: 72, x2: 58, y2: 105 }, '4'); + let button5 = new Button({ x1: 60, y1: 72, x2: 116, y2: 105 }, '5'); + let button6 = new Button({ x1: 118, y1: 72, x2: 174, y2: 105 }, '6'); + + let button7 = new Button({ x1: 1, y1: 107, x2: 58, y2: 140 }, '7'); + let button8 = new Button({ x1: 60, y1: 107, x2: 116, y2: 140 }, '8'); + let button9 = new Button({ x1: 118, y1: 107, x2: 174, y2: 140 }, '9'); + + let buttonOK = new Button({ x1: 1, y1: 142, x2: 58, y2: 174 }, "OK"); + let button0 = new Button({ x1: 60, y1: 142, x2: 116, y2: 174 }, "0"); + let buttonDelete = new Button({ x1: 118, y1: 142, x2: 174, y2: 174 }, "<-"); + + this.inputButtons = [ + button1, + button2, + button3, + button4, + button5, + button6, + button7, + button8, + button9, + buttonOK, + button0, + buttonDelete, + ]; + } + + handleTouchInput(n, e) { + let res = 'none'; + for (let button of this.inputButtons) { + const touchResult = button.handleTouchInput(n, e); + if (touchResult) { + res = touchResult; + } + } + + switch (res) { + case 'OK': + if(this.inputVal.length == 3){ + this.inputCallback(parseInt(this.inputVal)); + } + break; + case '<-': + this.removeNumber(); + this.drawField(); + break; + case 'none': + break; + default: + this.appendNumber(parseInt(res)); + this.drawField(); + } + + } + + + hide() { + for (let button of this.inputButtons) { + button.disable(); + } + } + + start(preset) { + if (preset) { + this.inputVal = preset.toString(); + } + else { + this.inputVal = ''; + } + + this.draw(); + } + + appendNumber(number) { + if (number === 0 && this.inputVal.length === 0) { + return; + } + + if (this.inputVal.length <= 2) { + this.inputVal = this.inputVal + number; + } + } + + removeNumber() { + if (this.inputVal.length > 0) { + this.inputVal = this.inputVal.slice(0, -1); + } + } + + reset() { + this.inputVal = ""; + } + + draw() { + g.clear(); + this.drawButtons(); + this.drawField(); + } + + drawButtons() { + for (let button of this.inputButtons) { + button.draw(); + } + } + + drawField() { + g.clearRect(0, 0, 176, 34); + g.setColor(g.theme.fg); + g.setFontAlign(-1, -1); + g.setFont("Vector:26x40"); + g.drawString(this.inputVal, 2, 0); + } +} + +// require('./Input'); + +class NOSTeletekstApp { + + constructor() { + console.log("this is the teletekst app!"); + this.isLeaving = false; + this.viewMode= 'VIEW'; + this.view = new View(); + this.input = new Input((newVal)=>this.inputHandler(newVal)); + this.view.start(); + + Bangle.setUI({ + mode: "custom", + remove: () => { + this.isLeaving = true; + console.log("teletext app: i am packing my stuff, goodbye"); + require("widget_utils").show(); // re-show widgets + }, + touch: (n, e) => { + if (this.viewMode == 'VIEW') { + // we need to go to input mode + this.setViewMode('INPUT'); + return; + } + if (this.viewMode == 'INPUT') { + this.input.handleTouchInput(n, e); + return; + + } + }, + swipe: (lr, ud) => { + if (this.viewMode == 'VIEW') { + this.view.handleSwipe(lr,ud); + } + if (this.viewMode == 'INPUT') { + if(lr == 1 && ud == 0){ + this.setViewMode('VIEW'); + } + } + } + + }); + + } + + inputHandler(input){ + // set viewMode back to view + this.view.nextStartPage = input; + this.setViewMode('VIEW'); + } + + setViewMode(newViewMode){ + this.viewMode = newViewMode; + if(newViewMode=='INPUT'){ + this.input.start(); + } + if(newViewMode=='VIEW'){ + this.input.hide(); + this.view.start(); + } + } + + +} +new NOSTeletekstApp(); \ No newline at end of file diff --git a/apps/nostt/nostt.icon.js b/apps/nostt/nostt.icon.js new file mode 100644 index 000000000..b9ef929be --- /dev/null +++ b/apps/nostt/nostt.icon.js @@ -0,0 +1 @@ +require("heatshrink").decompress(atob("mEwwZC/AH4A/AH4AWgmSAwn/wACB/0Agf/4EAhMkyVIB4MB/4AB/ARGpIRBpMggEPCIQAC4ARCgQRDkkAv4jEAQIRLAQIRC/4RCiVJIgJKBwARCgPACIxWCCIn8NwQAB8YRFgIRDNARcFPQgRBNYZHB+IRDEYyPDAAPwn4RJIggRBg4RDNYrGCJQQRGkCSCWYP4CIgpCUI7FFCIMfa5DFFCIL7GJQQRLgARBoBXCO4KhBAH4A/AH4A/AD4A=")) \ No newline at end of file diff --git a/apps/nostt/nostt_logo.png b/apps/nostt/nostt_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bf8f0d47d30a2c156773e5b1b4e63fa90d1d04ee GIT binary patch literal 1646 zcmV-!29f!RP) zqM!wW=EOvDX`6yl1Hl*@2q*&DLVJOuA)sjpTJ?`2Xh5Mwqfr9^Lli`4lvsI5;>sI5_-AQ2oxG0pMIt86=PA z)5;fLjGCQlYilK6QPIn0n;YXVg2CYBp_0btKUS#sMiz73m;sa)BMb~M`4fp!J1ZLB zQqa=UKb)|-P-ueA*c`_BJ)AK&!C2)8{f!AZS(uyK76=3s002RW$KMBlk*Pqy1wB3#>N%H zt&pAm^0@{1XCrM)QC6G9+u@9PQS92*zQdn=dZv7$WF8BL!}$Y-(QZ)sv|0QRIdU~! zuhf#sooNLC0I0gYd0SJ{q+1ow%gYJ9bm?=@)G4XxLnLl(d}m*a}Ee|vMY=T?&SUH+pAG;LPefGC|tlGF&I6b3=OkR~D#5A>EjJTLWb zQ2MNSoM3!jmgVOKLLL=m`4y3neFz{403e9smg->etdL0*W9+VEG`b4_mShvpYfFXb zp(|@F!Vpq)7+aq6lO|4VEh{TC z22Fb#cJD4$lgT&yIXN2v0MNz{K8S#66Whsgy92lR{jKLaJNwLbx64(!s;L@A&Q108zf;D>oBc7yT^ON5L=f8Y z1mURtrHVk>1{|7-Kq=AdX;^#X!(Sw0CbZ zQvu!shdTh1n8lb(_y7R%yx#Q$AkxU>IYfPs}LIG;#m07ngu-F_0_wMvh=G*uA>Cx?=3e zkz*uDS}X`c6CvarLWtXQQNvi4b_yXvNN~}j{<L^76W0TDr6w06rOf zAk@?}A*Lw14aTOXLK$QJbaC{0tG`}Raa0t=%~{0*fcN0RD9G|Yi_tLdB3*x->YG`z zcHr?TGoqyrm-bD+|6RQ&@*}6zZ}D}xLaAShW5MCq(jG`~j_BdlceGlK--AHi4n-0dwb2>^ilaCoe07`1jk%9xii zHi1%FWFHd+p+0+m1Hg)4@PaJMRYPqeGX>xL^eyp^o`nSQ_hHpb(vPTc^iG*kJY}05Q9t_waWo zy8YuyuqUqqMDYiNv7Q>^1SgRseT_YyCBC`k)4T_cTh9B6K;Y_{6)TqK`+RpulC+Xw ze2`$=g%DzcqA4MyLm=eC5hF&-sHv)2IqX9HU(Q!o%h84N?&w)E=XPFOn{~fs;uFqE sYinyxrhCZ2!NI}7!NI}7!QnsiFRML)y1TwejQ{`u07*qoM6N<$f|RHlq5uE@ literal 0 HcmV?d00001 diff --git a/apps/nostt/nostt_screenshot_1.png b/apps/nostt/nostt_screenshot_1.png new file mode 100644 index 0000000000000000000000000000000000000000..ad65ecba7e1f30886d876c46170024b8fb286b80 GIT binary patch literal 4415 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 FV1fYlLzw^o literal 0 HcmV?d00001 diff --git a/apps/nostt/nostt_screenshot_2.png b/apps/nostt/nostt_screenshot_2.png new file mode 100644 index 0000000000000000000000000000000000000000..0faa9b1f61450b15ae673fee14c1f070a14ef31e GIT binary patch literal 5107 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 Date: Thu, 13 Jun 2024 23:36:19 +0200 Subject: [PATCH 062/423] type added to nostt metadata.json --- apps/nostt/metadata.json | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/nostt/metadata.json b/apps/nostt/metadata.json index 8d59abd7a..7e5599096 100644 --- a/apps/nostt/metadata.json +++ b/apps/nostt/metadata.json @@ -5,6 +5,7 @@ "version": "1.00", "description": "Dutch Teletekst using the NOS Teletekst api. Requires http access via BangleJS GadgetBridge.", "src":"nostt.app.js", + "type": "app", "storage": [ {"name":"nostt.app.js","url":"nostt.app.js"}, {"name":"nostt.img","url":"nostt.icon.js","evaluate":true} From 14193641dd7f6bfb10c0b27cb5f167ffa15a75e7 Mon Sep 17 00:00:00 2001 From: Albert Date: Thu, 13 Jun 2024 23:39:13 +0200 Subject: [PATCH 063/423] creator added --- apps/nostt/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/nostt/README.md b/apps/nostt/README.md index ea3d7b3ff..f9b57cc66 100644 --- a/apps/nostt/README.md +++ b/apps/nostt/README.md @@ -5,4 +5,8 @@ Dutch Teletekst using the NOS Teletekst api. Requires http access via BangleJS G ## Usage -Tap once to bring up a numpad to enter the desired page. You can also swipe left/right to change the page, or swipe up/down to walk through the subpages. \ No newline at end of file +Tap once to bring up a numpad to enter the desired page. You can also swipe left/right to change the page, or swipe up/down to walk through the subpages. + +## Creator + +[Albert van der Meer](https://github.com/avandermeer) \ No newline at end of file From 7b044002cd909a8a6fef63960ab62e3a2ab3783e Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Sun, 16 Jun 2024 15:18:18 +0100 Subject: [PATCH 064/423] recorder interface: fix indent --- apps/recorder/interface.html | 72 ++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/apps/recorder/interface.html b/apps/recorder/interface.html index 7f1e3bbc6..6c4fc3ed8 100644 --- a/apps/recorder/interface.html +++ b/apps/recorder/interface.html @@ -235,8 +235,8 @@ ${trackData.Latitude ? ` `; - }); - if (trackList.length==0) { + }); + if (trackList.length==0) { html += `
@@ -245,8 +245,8 @@ ${trackData.Latitude ? `
`; - } - html += ` + } + html += `

Settings

@@ -256,39 +256,39 @@ ${trackData.Latitude ? `
`; - domTracks.innerHTML = html; - document.getElementById("settings-allow-no-gps").addEventListener("change",event=>{ - var allowNoGPS = event.target.checked; - localStorage.setItem("recorder-allow-no-gps", allowNoGPS); - }); - Util.hideModal(); - var buttons = domTracks.querySelectorAll("button"); + domTracks.innerHTML = html; + document.getElementById("settings-allow-no-gps").addEventListener("change",event=>{ + var allowNoGPS = event.target.checked; + localStorage.setItem("recorder-allow-no-gps", allowNoGPS); + }); + Util.hideModal(); + var buttons = domTracks.querySelectorAll("button"); for (var i=0;i { - var button = event.currentTarget; - var filename = button.getAttribute("filename"); - var trackid = parseInt(button.getAttribute("trackid")); - if (!filename || trackid===undefined) return; - var task = button.getAttribute("task"); - if (task=="delete") { - Util.showModal("Deleting Track..."); - Util.eraseStorageFile(filename,()=>{ - Util.hideModal(); - getTrackList(); - }); - } - if (task=="downloadkml") { - downloadTrack(filename, track => saveKML(track, `Bangle.js Track ${trackid}`)); - } - if (task=="downloadgpx") { - downloadTrack(filename, track => saveGPX(track, `Bangle.js Track ${trackid}`)); - } - if (task=="downloadcsv") { - downloadTrack(filename, track => saveCSV(track, `Bangle.js Track ${trackid}`)); - } - }); - } - }); + buttons[i].addEventListener("click",event => { + var button = event.currentTarget; + var filename = button.getAttribute("filename"); + var trackid = parseInt(button.getAttribute("trackid")); + if (!filename || trackid===undefined) return; + var task = button.getAttribute("task"); + if (task=="delete") { + Util.showModal("Deleting Track..."); + Util.eraseStorageFile(filename,()=>{ + Util.hideModal(); + getTrackList(); + }); + } + if (task=="downloadkml") { + downloadTrack(filename, track => saveKML(track, `Bangle.js Track ${trackid}`)); + } + if (task=="downloadgpx") { + downloadTrack(filename, track => saveGPX(track, `Bangle.js Track ${trackid}`)); + } + if (task=="downloadcsv") { + downloadTrack(filename, track => saveCSV(track, `Bangle.js Track ${trackid}`)); + } + }); + } + }); }); } From 9561a4b5fcd0e4651036c0ce2c5be4d55831abed Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Sun, 16 Jun 2024 15:19:52 +0100 Subject: [PATCH 065/423] recorder interface: convert `if`s to `switch` --- apps/recorder/interface.html | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/apps/recorder/interface.html b/apps/recorder/interface.html index 6c4fc3ed8..3f3d48e86 100644 --- a/apps/recorder/interface.html +++ b/apps/recorder/interface.html @@ -270,21 +270,24 @@ ${trackData.Latitude ? ` var trackid = parseInt(button.getAttribute("trackid")); if (!filename || trackid===undefined) return; var task = button.getAttribute("task"); - if (task=="delete") { - Util.showModal("Deleting Track..."); - Util.eraseStorageFile(filename,()=>{ - Util.hideModal(); - getTrackList(); - }); - } - if (task=="downloadkml") { - downloadTrack(filename, track => saveKML(track, `Bangle.js Track ${trackid}`)); - } - if (task=="downloadgpx") { - downloadTrack(filename, track => saveGPX(track, `Bangle.js Track ${trackid}`)); - } - if (task=="downloadcsv") { - downloadTrack(filename, track => saveCSV(track, `Bangle.js Track ${trackid}`)); + switch(task) { + case "delete": + Util.showModal("Deleting Track..."); + Util.eraseStorageFile(filename,()=>{ + Util.hideModal(); + getTrackList(); + }); + break; + + case "downloadkml": + downloadTrack(filename, track => saveKML(track, `Bangle.js Track ${trackid}`)); + break; + case "downloadgpx": + downloadTrack(filename, track => saveGPX(track, `Bangle.js Track ${trackid}`)); + break; + case "downloadcsv": + downloadTrack(filename, track => saveCSV(track, `Bangle.js Track ${trackid}`)); + break; } }); } From 7e7945b6e273dcbe48cb1366472f601e9a7823cb Mon Sep 17 00:00:00 2001 From: Rob Pilling Date: Sun, 16 Jun 2024 15:20:13 +0100 Subject: [PATCH 066/423] recorder interface: add download-all functionality --- apps/recorder/interface.html | 42 +++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/apps/recorder/interface.html b/apps/recorder/interface.html index 3f3d48e86..ef43633bd 100644 --- a/apps/recorder/interface.html +++ b/apps/recorder/interface.html @@ -159,6 +159,28 @@ function downloadTrack(filename, callback) { }); } +function downloadAll(trackList, cb) { + const tracks = trackList.slice(); + + const downloadOne = () => { + const track = tracks.pop(); + if(!track) { + showToast("Finished downloading all.", "success"); + return; + } + + downloadTrack( + track.filename, + lines => { + cb(lines, `Bangle.js Track ${track.number}`); + downloadOne(); + } + ); + }; + + downloadOne(); +} + function getTrackList() { Util.showModal("Loading Track List..."); domTracks.innerHTML = ""; @@ -248,6 +270,12 @@ ${trackData.Latitude ? ` } html += ` +

Batch

+
+ + + +

Settings