diff --git a/.eslintignore b/.eslintignore index 4af79d129..38bc5b5bf 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,4 +4,5 @@ apps/schoolCalendar/fullcalendar/main.js apps/authentiwatch/qr_packed.js apps/qrcode/qr-scanner.umd.min.js apps/gipy/pkg/gpconv.js +apps/health/chart.min.js *.test.js diff --git a/.github/ISSUE_TEMPLATE/bangle-bug-report-custom-form.yaml b/.github/ISSUE_TEMPLATE/bangle-bug-report-custom-form.yaml index 484b3ba85..045a6e18a 100644 --- a/.github/ISSUE_TEMPLATE/bangle-bug-report-custom-form.yaml +++ b/.github/ISSUE_TEMPLATE/bangle-bug-report-custom-form.yaml @@ -58,3 +58,7 @@ body: validations: required: true + - type: textarea + id: apps + attributes: + label: Installed apps \ No newline at end of file diff --git a/README.md b/README.md index fed13a358..aa8afdbca 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ This is the best way to test... **Note:** It's a great idea to get a local copy of the repository on your PC, then run `bin/sanitycheck.js` - it'll run through a bunch of common issues -that there might be. +that there might be. To get the project running locally, you have to initialize and update the git submodules first: `git submodule --init && git submodule update`. Be aware of the delay between commits and updates on github.io - it can take a few minutes (and a 'hard refresh' of your browser) for changes to take effect. diff --git a/apps/a_clock_timer/ChangeLog b/apps/a_clock_timer/ChangeLog index c01ad2077..cfb53c432 100644 --- a/apps/a_clock_timer/ChangeLog +++ b/apps/a_clock_timer/ChangeLog @@ -1 +1,4 @@ 0.01: Beta version for Bangle 2 (2021/11/28) +0.02: Shows night time on the map (2022/12/28) +0.03: Add 1 minute timer with upper taps (2023/01/05) +1.00: Page to set up custom time zones (2023/01/06) \ No newline at end of file diff --git a/apps/a_clock_timer/README.md b/apps/a_clock_timer/README.md index e8e2647a9..4e199344a 100644 --- a/apps/a_clock_timer/README.md +++ b/apps/a_clock_timer/README.md @@ -2,14 +2,17 @@ * Works with Bangle 2 * Timer - * Right tap: start/increase by 10 minutes; Left tap: decrease by 5 minutes + * Top Right tap: increase by 1 minute + * Top Left tap: decrease by 1 minute + * Bottom Right tap: increase by 10 minutes + * Bottom Left tap: decrease by 5 minutes * Short buzz at T-30, T-20, T-10 ; Double buzz at T * Other time zones - * Currently hardcoded to Paris and Tokyo (this will be customizable in a future version) + * Showing Paris and Tokyo by default, but you can customize this using the dedicated configuration page on the app store * World Map - * The yellow line shows the position of the sun + * The map shows day and night on Earth and the position of the Sun (yellow line) - +  ## Creator [@alainsaas](https://github.com/alainsaas) diff --git a/apps/a_clock_timer/app.js b/apps/a_clock_timer/app.js index 5f9a3a468..b01cec59b 100644 --- a/apps/a_clock_timer/app.js +++ b/apps/a_clock_timer/app.js @@ -18,19 +18,29 @@ var timervalue = 0; var istimeron = false; var timertick; -Bangle.on('touch',t=>{ - if (t == 1) { +Bangle.on('touch',(touchside, touchdata)=>{ + if (touchside == 1) { Bangle.buzz(30); - if (timervalue < 5*60) { timervalue = 1 ; } - else { timervalue -= 5*60; } + var changevalue = 0; + if(touchdata.y > 88) { + changevalue += 60*5; + } else { + changevalue += 60*1; + } + if (timervalue < changevalue) { timervalue = 1 ; } + else { timervalue -= changevalue; } } - else if (t == 2) { + else if (touchside == 2) { Bangle.buzz(30); if (!istimeron) { istimeron = true; timertick = setInterval(countDown, 1000); } - timervalue += 60*10; + if(touchdata.y > 88) { + timervalue += 60*10; + } else { + timervalue += 60*1; + } } }); @@ -73,12 +83,13 @@ function countDown() { function showWelcomeMessage() { g.reset().clearRect(0, 76, 44+44, g.getHeight()/2+6); g.setFontAlign(0, 0).setFont("6x8"); - g.drawString("Touch right to", 44, 80); + g.drawString("Tap right to", 44, 80); g.drawString("start timer", 44, 88); setTimeout(function(){ g.reset().clearRect(0, 76, 44+44, g.getHeight()/2+6); }, 8000); } // time +var offsets = require("Storage").readJSON("a_clock_timer.settings.json") || [ ["PAR",1], ["TYO",9] ]; var drawTimeout; function getGmt() { @@ -102,20 +113,34 @@ function queueNextDraw() { function draw() { g.reset().clearRect(0,24,g.getWidth(),g.getHeight()-IMAGEHEIGHT); g.drawImage(getImg(),0,g.getHeight()-IMAGEHEIGHT); - - var x_sun = 176 - (getGmt().getHours() / 24 * 176 + 4); + + var gmtHours = getGmt().getHours(); + + var x_sun = 176 - (gmtHours / 24 * 176 + 4); g.setColor('#ff0').drawLine(x_sun, g.getHeight()-IMAGEHEIGHT, x_sun, g.getHeight()); g.reset(); + var x_night_start = (176 - (((gmtHours-6)%24) / 24 * 176 + 4)) % 176; + var x_night_end = 176 - (((gmtHours+6)%24) / 24 * 176 + 4); + g.setColor('#000'); + for (let x = x_night_start; x < (x_night_end < x_night_start ? 176 : x_night_end); x+=2) { + g.drawLine(x, g.getHeight()-IMAGEHEIGHT, x, g.getHeight()); + } + if (x_night_end < x_night_start) { + for (let x = 0; x < x_night_end; x+=2) { + g.drawLine(x, g.getHeight()-IMAGEHEIGHT, x, g.getHeight()); + } + } + var locale = require("locale"); - + var date = new Date(); g.setFontAlign(0,0); g.setFont("Michroma36").drawString(locale.time(date,1), g.getWidth()/2, 46); g.setFont("6x8"); g.drawString(locale.date(new Date(),1), 125, 68); - g.drawString("PAR "+locale.time(getTimeFromTimezone(1),1), 125, 80); - g.drawString("TYO "+locale.time(getTimeFromTimezone(9),1), 125, 88); + g.drawString(offsets[0][0]+" "+locale.time(getTimeFromTimezone(offsets[0][1]),1), 125, 80); + g.drawString(offsets[1][0]+" "+locale.time(getTimeFromTimezone(offsets[1][1]),1), 125, 88); queueNextDraw(); } diff --git a/apps/a_clock_timer/custom.html b/apps/a_clock_timer/custom.html new file mode 100644 index 000000000..b62226340 --- /dev/null +++ b/apps/a_clock_timer/custom.html @@ -0,0 +1,58 @@ + +
+ + + +You can set the 2 additional timezones displayed by the clock.
+| Name | +UTC Offset (Hours) | +
|---|
Click
+ + + + diff --git a/apps/a_clock_timer/metadata.json b/apps/a_clock_timer/metadata.json index cc61fc57b..6507857f1 100644 --- a/apps/a_clock_timer/metadata.json +++ b/apps/a_clock_timer/metadata.json @@ -1,17 +1,19 @@ { "id": "a_clock_timer", "name": "A Clock with Timer", - "version": "0.01", + "version": "1.00", "description": "A Clock with Timer, Map and Time Zones", "icon": "app.png", - "screenshots": [{"url":"screenshot.png"}], + "screenshots": [{"url":"screenshot.png"},{"url":"screenshot-1.png"}], "type": "clock", "tags": "clock", "supports": ["BANGLEJS2"], "allow_emulator": true, "readme": "README.md", + "custom": "custom.html", "storage": [ {"name":"a_clock_timer.app.js","url":"app.js"}, {"name":"a_clock_timer.img","url":"app-icon.js","evaluate":true} - ] + ], + "data": [{"name":"a_clock_timer.settings.json"}] } diff --git a/apps/a_clock_timer/screenshot-1.png b/apps/a_clock_timer/screenshot-1.png new file mode 100644 index 000000000..ede6439de Binary files /dev/null and b/apps/a_clock_timer/screenshot-1.png differ diff --git a/apps/a_clock_timer/screenshot.png b/apps/a_clock_timer/screenshot.png index 4fb3dd9f2..893daa9d0 100644 Binary files a/apps/a_clock_timer/screenshot.png and b/apps/a_clock_timer/screenshot.png differ diff --git a/apps/a_speech_timer/ChangeLog b/apps/a_speech_timer/ChangeLog index b3aa9e0dd..73e8a67da 100644 --- a/apps/a_speech_timer/ChangeLog +++ b/apps/a_speech_timer/ChangeLog @@ -1,2 +1,3 @@ 1.00: Release (2021/12/01) 1.01: Grey font when timer is frozen (2021/12/04) +1.02: Force light theme, since the app is not designed for dark theme (2022/12/28) diff --git a/apps/a_speech_timer/app.js b/apps/a_speech_timer/app.js index 440cd92c6..cbed2ac00 100644 --- a/apps/a_speech_timer/app.js +++ b/apps/a_speech_timer/app.js @@ -166,6 +166,7 @@ function draw() { g.drawRect(88+8,138-24, 176-10, 138+22); } +g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear(); require("FontHaxorNarrow7x17").add(Graphics); g.clear(); Bangle.loadWidgets(); diff --git a/apps/a_speech_timer/metadata.json b/apps/a_speech_timer/metadata.json index 6255a6b92..fd8813991 100644 --- a/apps/a_speech_timer/metadata.json +++ b/apps/a_speech_timer/metadata.json @@ -2,7 +2,7 @@ "id":"a_speech_timer", "name":"Speech Timer", "icon": "app.png", -"version":"1.01", +"version":"1.02", "description": "A timer designed to help keeping your speeches and presentations to time.", "tags": "tool,timer", "readme":"README.md", diff --git a/apps/agenda/ChangeLog b/apps/agenda/ChangeLog index 77e11c92e..b53e657fd 100644 --- a/apps/agenda/ChangeLog +++ b/apps/agenda/ChangeLog @@ -9,3 +9,5 @@ Fix clkinfo icon 0.09: Ensure Agenda supplies an image for clkinfo items 0.10: Update clock_info to avoid a redraw +0.11: Setting to use "Today" and "Yesterday" instead of dates + Added dynamic, short and range fields to clkinfo \ No newline at end of file diff --git a/apps/agenda/agenda.clkinfo.js b/apps/agenda/agenda.clkinfo.js index 7c89446a2..d203119f4 100644 --- a/apps/agenda/agenda.clkinfo.js +++ b/apps/agenda/agenda.clkinfo.js @@ -1,7 +1,14 @@ (function() { + function getPassedSec(date) { + var now = new Date(); + var passed = (now-date)/1000; + if(passed<0) return 0; + return passed; + } var agendaItems = { name: "Agenda", img: atob("GBiBAAAAAAAAAADGMA///w///wf//wAAAA///w///w///w///x///h///h///j///D///X//+f//8wAABwAADw///w///wf//gAAAA=="), + dynamic: true, items: [] }; var locale = require("locale"); @@ -15,11 +22,15 @@ var title = entry.title.slice(0,12); var date = new Date(entry.timestamp*1000); var dateStr = locale.date(date).replace(/\d\d\d\d/,""); + var shortStr = ((date-now) > 86400000 || entry.allDay) ? dateStr : locale.time(date,1); dateStr += entry.durationInSeconds < 86400 ? "/ " + locale.time(date,1) : ""; agendaItems.items.push({ name: "Agenda "+i, - get: () => ({ text: title + "\n" + dateStr, img: agendaItems.img }), + hasRange: true, + get: () => ({ text: title + "\n" + dateStr, + img: agendaItems.img, short: shortStr.trim(), + v: getPassedSec(date), min: 0, max: entry.durationInSeconds}), show: function() {}, hide: function () {} }); diff --git a/apps/agenda/agenda.js b/apps/agenda/agenda.js index 9cffe0265..8afca95a9 100644 --- a/apps/agenda/agenda.js +++ b/apps/agenda/agenda.js @@ -33,16 +33,32 @@ CALENDAR=CALENDAR.sort((a,b)=>a.timestamp - b.timestamp); function getDate(timestamp) { return new Date(timestamp*1000); } +function formatDay(date) { + if (!settings.useToday) { + return Locale.date(date); + } + const dateformatted = date.toISOString().split('T')[0]; // yyyy-mm-dd + const today = new Date(Date.now()).toISOString().split('T')[0]; // yyyy-mm-dd + if (dateformatted == today) { + return /*LANG*/"Today "; + } else { + const tomorrow = new Date(Date.now() + 86400 * 1000).toISOString().split('T')[0]; // yyyy-mm-dd + if (dateformatted == tomorrow) { + return /*LANG*/"Tomorrow "; + } + return Locale.date(date); + } +} function formatDateLong(date, includeDay, allDay) { let shortTime = Locale.time(date,1)+Locale.meridian(date); if(allDay) shortTime = ""; - if(includeDay || allDay) - return Locale.date(date)+" "+shortTime; + if(includeDay || allDay) { + return formatDay(date)+" "+shortTime; + } return shortTime; } function formatDateShort(date, allDay) { - return Locale.date(date).replace(/\d\d\d\d/,"")+(allDay? - "" : Locale.time(date,1)+Locale.meridian(date)); + return formatDay(date).replace(/\d\d\d\d/,"")+(allDay?"":Locale.time(date,1)+Locale.meridian(date)); } var lines = []; diff --git a/apps/agenda/metadata.json b/apps/agenda/metadata.json index 8253b36bc..b5b7c1582 100644 --- a/apps/agenda/metadata.json +++ b/apps/agenda/metadata.json @@ -1,7 +1,7 @@ { "id": "agenda", "name": "Agenda", - "version": "0.10", + "version": "0.11", "description": "Simple agenda", "icon": "agenda.png", "screenshots": [{"url":"screenshot_agenda_overview.png"}, {"url":"screenshot_agenda_event1.png"}, {"url":"screenshot_agenda_event2.png"}], diff --git a/apps/agenda/settings.js b/apps/agenda/settings.js index 4220fcb63..62e0c6dbd 100644 --- a/apps/agenda/settings.js +++ b/apps/agenda/settings.js @@ -43,6 +43,13 @@ updateSettings(); } }, + /*LANG*/"Use 'Today',..." : { + value : !!settings.useToday, + onchange: v => { + settings.useToday = v; + updateSettings(); + } + }, }; E.showMenu(mainmenu); }) diff --git a/apps/agpsdata/ChangeLog b/apps/agpsdata/ChangeLog index 8ada244d7..d900841d7 100644 --- a/apps/agpsdata/ChangeLog +++ b/apps/agpsdata/ChangeLog @@ -3,3 +3,5 @@ 0.03: Do not load AGPS data on boot Increase minimum interval to 6 hours 0.04: Write AGPS data chunks with delay to improve reliability +0.05: Show last success date + Do not start A-GPS update automatically diff --git a/apps/agpsdata/app.js b/apps/agpsdata/app.js index 4a6d2ba5c..48714d6d2 100644 --- a/apps/agpsdata/app.js +++ b/apps/agpsdata/app.js @@ -23,12 +23,26 @@ Bangle.drawWidgets(); let waiting = false; -function start() { +function start(restart) { g.reset(); g.clear(); waiting = false; - display("Retry?", "touch to retry"); + if (!restart) { + display("Start?", "touch to start"); + } + else { + display("Retry?", "touch to retry"); + } Bangle.on("touch", () => { updateAgps(); }); + + const file = "agpsdata.json"; + let data = require("Storage").readJSON(file, 1) || {}; + if (data.lastUpdate) { + g.setFont("Vector", 11); + g.drawString("last success:", 5, g.getHeight() - 22); + g.drawString(new Date(data.lastUpdate).toISOString(), 5, g.getHeight() - 11); + } + } function updateAgps() { @@ -36,7 +50,7 @@ function updateAgps() { g.clear(); if (!waiting) { waiting = true; - display("Updating A-GPS...", "takes ~ 10 seconds"); + display("Updating A-GPS...", "takes ~10 seconds"); require("agpsdata").pull(function() { waiting = false; display("A-GPS updated.", "touch to close"); @@ -45,10 +59,10 @@ function updateAgps() { function(error) { waiting = false; E.showAlert(error, "Error") - .then(() => { start(); }); + .then(() => { start(true); }); }); } else { display("Waiting..."); } } -updateAgps(); +start(false); diff --git a/apps/agpsdata/metadata.json b/apps/agpsdata/metadata.json index 203a00f72..7f0b53217 100644 --- a/apps/agpsdata/metadata.json +++ b/apps/agpsdata/metadata.json @@ -2,7 +2,7 @@ "name": "A-GPS Data Downloader App", "shortName":"A-GPS Data", "icon": "agpsdata.png", - "version":"0.04", + "version":"0.05", "description": "Once installed, this app allows you to download assisted GPS (A-GPS) data directly to your Bangle.js **via Gadgetbridge on an Android phone** when you run the app. If you just want to upload the latest AGPS data from this app loader, please use the `Assisted GPS Update (AGPS)` app.", "tags": "boot,tool,assisted,gps,agps,http", "allow_emulator":true, diff --git a/apps/aiclock/ChangeLog b/apps/aiclock/ChangeLog index fb5aed3e3..6d6eeb55e 100644 --- a/apps/aiclock/ChangeLog +++ b/apps/aiclock/ChangeLog @@ -2,4 +2,6 @@ 0.02: Design improvements and fixes. 0.03: Indicate battery level through line occurrence. 0.04: Use widget_utils module. -0.05: Support for clkinfo. \ No newline at end of file +0.05: Support for clkinfo. +0.06: ClockInfo Fix: Use .get instead of .show as .show is not implemented for weather etc. +0.07: Use clock_info.addInteractive instead of a custom implementation \ No newline at end of file diff --git a/apps/aiclock/README.md b/apps/aiclock/README.md index 31dd5aa29..521bd2c5e 100644 --- a/apps/aiclock/README.md +++ b/apps/aiclock/README.md @@ -11,8 +11,7 @@ The original output of stable diffusion is shown here: My implementation is shown below. Note that horizontal lines occur randomly, but the probability is correlated with the battery level. So if your screen contains only a few lines its time to charge your bangle again ;) Also note that the upper text -implementes the clkinfo module and can be configured via touch left/right/up/down. -Touch at the center to trigger the selected action. +implements the clkinfo module and can be configured via touch and swipe left/right and up/down.  diff --git a/apps/aiclock/aiclock.app.js b/apps/aiclock/aiclock.app.js index b5bb30b9d..350832367 100644 --- a/apps/aiclock/aiclock.app.js +++ b/apps/aiclock/aiclock.app.js @@ -1,7 +1,6 @@ /************************************************ * AI Clock */ - const storage = require('Storage'); const clock_info = require("clock_info"); @@ -21,147 +20,14 @@ Graphics.prototype.setFontGochiHand = function(scale) { return this; } -/************************************************ - * Set some important constants such as width, height and center - */ -var W = g.getWidth(),R=W/2; -var H = g.getHeight(); -var cx = W/2; -var cy = H/2; -var drawTimeout; -var lock_input = false; - -/************************************************ - * SETTINGS - */ -const SETTINGS_FILE = "aiclock.setting.json"; -let settings = { - menuPosX: 0, - menuPosY: 0, -}; -let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings; -for (const key in saved_settings) { - settings[key] = saved_settings[key] -} - - -/************************************************ - * Menu - */ -function getDate(){ - var date = new Date(); - return ("0"+date.getDate()).substr(-2) + "/" + ("0"+(date.getMonth()+1)).substr(-2) -} - - -// Custom clockItems menu - therefore, its added here and not in a clkinfo.js file. -var clockItems = { - name: getDate(), - img: null, - items: [ - { name: "Week", - get: () => ({ text: "Week " + weekOfYear(), img: null}), - show: function() { clockItems.items[0].emit("redraw"); }, - hide: function () {} - }, - ] - }; - -function weekOfYear() { - var date = new Date(); - date.setHours(0, 0, 0, 0); - // Thursday in current week decides the year. - date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); - // January 4 is always in week 1. - var week1 = new Date(date.getFullYear(), 0, 4); - // Adjust to Thursday in week 1 and count number of weeks from date to week1. - return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - - 3 + (week1.getDay() + 6) % 7) / 7); -} - - - -// Load menu -var menu = clock_info.load(); -menu = menu.concat(clockItems); - - - // Ensure that our settings are still in range (e.g. app uninstall). Otherwise reset the position it. - if(settings.menuPosX >= menu.length || settings.menuPosY > menu[settings.menuPosX].items.length ){ - settings.menuPosX = 0; - settings.menuPosY = 0; - } - - // Set draw functions for each item - menu.forEach((menuItm, x) => { - menuItm.items.forEach((item, y) => { - function drawItem() { - // For the clock, we have a special case, as we don't wanna redraw - // immediately when something changes. Instead, we update data each minute - // to save some battery etc. Therefore, we hide (and disable the listener) - // immedeately after redraw... - item.hide(); - - // After drawing the item, we enable inputs again... - lock_input = false; - - var info = item.get(); - drawMenuItem(info.text, info.img); - } - - item.on('redraw', drawItem); - }) - }); - - - function canRunMenuItem(){ - if(settings.menuPosY == 0){ - return false; - } - - var menuEntry = menu[settings.menuPosX]; - var item = menuEntry.items[settings.menuPosY-1]; - return item.run !== undefined; - } - - - function runMenuItem(){ - if(settings.menuPosY == 0){ - return; - } - - var menuEntry = menu[settings.menuPosX]; - var item = menuEntry.items[settings.menuPosY-1]; - try{ - var ret = item.run(); - if(ret){ - Bangle.buzz(300, 0.6); - } - } catch (ex) { - // Simply ignore it... - } - } - - -/* - * Based on the great multi clock from https://github.com/jeffmer/BangleApps/ - */ -Graphics.prototype.drawRotRect = function(w, r1, r2, angle) { - angle = angle % 360; - var w2=w/2, h=r2-r1, theta=angle*Math.PI/180; - return this.fillPoly(this.transformVertices([-w2,0,-w2,-h,w2,-h,w2,0], - {x:cx+r1*Math.sin(theta),y:cy-r1*Math.cos(theta),rotate:theta})); -}; - - -function drawBackground() { +function drawBackground(start, end) { g.setFontAlign(0,0); - g.setColor(g.theme.fg); + g.setColor("#000"); var bat = E.getBattery() / 100.0; - var y = 0; - while(y < H){ + var y = start; + while(y < end){ // Show less lines in case of small battery level. if(Math.random() > bat){ y += 5; @@ -177,6 +43,30 @@ function drawBackground() { } +/************************************************ + * Set some important constants such as width, height and center + */ +var W = g.getWidth(),R=W/2; +var H = g.getHeight(); +var cx = W/2; +var cy = H/2; +var drawTimeout; + +var clkInfoY = 60; + + +/* + * Based on the great multi clock from https://github.com/jeffmer/BangleApps/ + */ +Graphics.prototype.drawRotRect = function(w, r1, r2, angle) { + angle = angle % 360; + var w2=w/2, h=r2-r1, theta=angle*Math.PI/180; + return this.fillPoly(this.transformVertices([-w2,0,-w2,-h,w2,-h,w2,0], + {x:cx+r1*Math.sin(theta),y:cy-r1*Math.cos(theta),rotate:theta})); +}; + + + function drawCircle(isLocked){ g.setColor(g.theme.fg); g.fillCircle(cx, cy, 12); @@ -186,56 +76,6 @@ function drawCircle(isLocked){ g.fillCircle(cx, cy, 6); } -function toAngle(a){ - if (a < 0){ - return 360 + a; - } - - if(a > 360) { - return 360 - a; - } - - return a -} - - -function drawMenuItem(text, image){ - if(text == null){ - drawTime(); - return - } - // image = atob("GBiBAAD+AAH+AAH+AAH+AAH/AAOHAAYBgAwAwBgwYBgwYBgwIBAwOBAwOBgYIBgMYBgAYAwAwAYBgAOHAAH/AAH+AAH+AAH+AAD+AA=="); - - text = String(text); - - g.reset().setBgColor("#fff").setColor("#000"); - g.setFontAlign(0,0); - g.setFont("Vector", 20); - - var imgWidth = image == null ? 0 : 24; - var strWidth = g.stringWidth(text); - var strHeight = text.split('\n').length > 1 ? 40 : Math.max(24, imgWidth+2); - var w = imgWidth + strWidth; - - g.clearRect(cx-w/2-8, 40-strHeight/2-1, cx+w/2+4, 40+strHeight/2) - - // Draw right line as designed by stable diffusion - g.drawLine(cx+w/2+5, 40-strHeight/2-1, cx+w/2+5, 40+strHeight/2); - g.drawLine(cx+w/2+6, 40-strHeight/2-1, cx+w/2+6, 40+strHeight/2); - g.drawLine(cx+w/2+7, 40-strHeight/2-1, cx+w/2+7, 40+strHeight/2); - - // And finally the text - g.drawString(text, cx+imgWidth/2, 42); - g.drawString(text, cx+1+imgWidth/2, 41); - - if(image != null) { - var scale = image.width ? imgWidth / image.width : 1; - g.drawImage(image, W/2 + -strWidth/2-4 - parseInt(imgWidth/2), 41-12, {scale: scale}); - } - - drawTime(); -} - function drawTime(){ // Draw digital time first @@ -292,35 +132,23 @@ function drawDigits(){ } -function drawDate(){ - var menuEntry = menu[settings.menuPosX]; - - // The first entry is the overview... - if(settings.menuPosY == 0){ - drawMenuItem(menuEntry.name, menuEntry.img); - return; - } - - // Draw item if needed - lock_input = true; - var item = menuEntry.items[settings.menuPosY-1]; - item.show(); +function draw(){ + // Note that we force a redraw also of the clock info as + // we want to ensure (for design purpose) that the hands + // are above the clkinfo section. + clockInfoMenu.redraw(); } - - - -function draw(){ +function drawMainClock(){ // Queue draw in one minute queueDraw(); - g.reset(); - g.clearRect(0, 0, g.getWidth(), g.getHeight()); - g.setColor(1,1,1); + g.setColor("#fff"); + g.reset().clearRect(0, clkInfoY, g.getWidth(), g.getHeight()); - drawBackground(); - drawDate(); + drawBackground(clkInfoY, H); + drawTime(); drawCircle(Bangle.isLocked()); } @@ -330,7 +158,7 @@ function draw(){ */ Bangle.on('lcdPower',on=>{ if (on) { - draw(true); + draw(); } else { // stop draw timer if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = undefined; @@ -341,66 +169,10 @@ Bangle.on('lock', function(isLocked) { drawCircle(isLocked); }); -Bangle.on('touch', function(btn, e){ - var left = parseInt(g.getWidth() * 0.22); - var right = g.getWidth() - left; - var upper = parseInt(g.getHeight() * 0.22); - var lower = g.getHeight() - upper; - - var is_upper = e.y < upper; - var is_lower = e.y > lower; - var is_left = e.x < left && !is_upper && !is_lower; - var is_right = e.x > right && !is_upper && !is_lower; - var is_center = !is_upper && !is_lower && !is_left && !is_right; - - if(lock_input){ - return; - } - - if(is_lower){ - Bangle.buzz(40, 0.6); - settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1); - - draw(); - } - - if(is_upper){ - Bangle.buzz(40, 0.6); - settings.menuPosY = settings.menuPosY-1; - settings.menuPosY = settings.menuPosY < 0 ? menu[settings.menuPosX].items.length : settings.menuPosY; - - draw(); - } - - if(is_right){ - Bangle.buzz(40, 0.6); - settings.menuPosX = (settings.menuPosX+1) % menu.length; - settings.menuPosY = 0; - draw(); - } - - if(is_left){ - Bangle.buzz(40, 0.6); - settings.menuPosY = 0; - settings.menuPosX = settings.menuPosX-1; - settings.menuPosX = settings.menuPosX < 0 ? menu.length-1 : settings.menuPosX; - draw(); - } - - if(is_center){ - if(canRunMenuItem()){ - runMenuItem(); - } - } -}); - E.on("kill", function(){ - try{ - storage.write(SETTINGS_FILE, settings); - } catch(ex){ - // If this fails, we still kill the app... - } + clockInfoMenu.remove(); + delete clockInfoMenu; }); @@ -416,6 +188,55 @@ function queueDraw() { } +/************************************************ + * Clock Info + */ +let clockInfoItems = clock_info.load(); +let clockInfoMenu = clock_info.addInteractive(clockInfoItems, { + x : 0, + y: 0, + w: W, + h: clkInfoY, + draw : (itm, info, options) => { + g.setFontAlign(0,0); + g.setFont("Vector", 20); + + g.setColor("#fff"); + g.fillRect(options.x, options.y, options.x+options.w, options.y+options.h); + drawBackground(0, clkInfoY+2); + + // Set text and font + var image = info.img; + var text = String(info.text); + + var imgWidth = image == null ? 0 : 24; + var strWidth = g.stringWidth(text); + var strHeight = text.split('\n').length > 1 ? 40 : Math.max(24, imgWidth+2); + var w = imgWidth + strWidth; + + // Draw right line as designed by stable diffusion + g.setColor(options.focus ? "#0f0" : "#fff"); + g.fillRect(cx-w/2-8, 40-strHeight/2-1, cx+w/2+4, 40+strHeight/2) + + g.setColor("#000"); + g.drawLine(cx+w/2+5, 40-strHeight/2-1, cx+w/2+5, 40+strHeight/2); + g.drawLine(cx+w/2+6, 40-strHeight/2-1, cx+w/2+6, 40+strHeight/2); + g.drawLine(cx+w/2+7, 40-strHeight/2-1, cx+w/2+7, 40+strHeight/2); + + // Draw text and image + g.drawString(text, cx+imgWidth/2, 42); + g.drawString(text, cx+1+imgWidth/2, 41); + + if(image != null) { + var scale = image.width ? imgWidth / image.width : 1; + g.drawImage(image, W/2 + -strWidth/2-4 - parseInt(imgWidth/2), 41-12, {scale: scale}); + } + + drawMainClock(); + } +}); + + /* * Lets start widgets, listen for btn etc. */ @@ -430,7 +251,7 @@ Bangle.loadWidgets(); require('widget_utils').hide(); // Clear the screen once, at startup and draw clock -g.setTheme({bg:"#fff",fg:"#000",dark:false}).clear(); +g.setTheme({bg:"#fff",fg:"#000",dark:false}); draw(); // After drawing the watch face, we can draw the widgets diff --git a/apps/aiclock/metadata.json b/apps/aiclock/metadata.json index 1dcda427f..4c01ecaa9 100644 --- a/apps/aiclock/metadata.json +++ b/apps/aiclock/metadata.json @@ -3,7 +3,7 @@ "name": "AI Clock", "shortName":"AI Clock", "icon": "aiclock.png", - "version":"0.05", + "version":"0.07", "readme": "README.md", "supports": ["BANGLEJS2"], "description": "A watch face that was designed by an AI (stable diffusion) and implemented by a human.", diff --git a/apps/alarm/ChangeLog b/apps/alarm/ChangeLog index 9994d33d9..bb8a292a0 100644 --- a/apps/alarm/ChangeLog +++ b/apps/alarm/ChangeLog @@ -37,3 +37,4 @@ 0.34: Add "Confirm" option to alarm/timer edit menus 0.35: Add automatic translation of more strings 0.36: alarm widget moved out of app +0.37: add message input and dated Events diff --git a/apps/alarm/README.md b/apps/alarm/README.md index 741946b0c..0298e0836 100644 --- a/apps/alarm/README.md +++ b/apps/alarm/README.md @@ -1,15 +1,18 @@ # Alarms & Timers -This app allows you to add/modify any alarms and timers. +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. 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. ## Menu overview - `New...` - - `New Alarm` → Configure a new alarm + - `New Alarm` → Configure a new alarm (triggered based on time and day of week) - `Repeat` → Select when the alarm will fire. You can select a predefined option (_Once_, _Every Day_, _Workdays_ or _Weekends_ or you can configure the days freely) - - `New Timer` → Configure a new timer + - `New Timer` → Configure a new timer (triggered based on amount of time elapsed in hours/minutes/seconds) + - `New Event` → Configure a new event (triggered based on time and date) - `Advanced` - `Scheduler settings` → Open the [Scheduler](https://github.com/espruino/BangleApps/tree/master/apps/sched) settings page, see its [README](https://github.com/espruino/BangleApps/blob/master/apps/sched/README.md) for details - `Enable All` → Enable _all_ disabled alarms & timers diff --git a/apps/alarm/app.js b/apps/alarm/app.js index 1414c0b90..74007d04b 100644 --- a/apps/alarm/app.js +++ b/apps/alarm/app.js @@ -48,9 +48,10 @@ function showMainMenu() { }; alarms.forEach((e, index) => { - var label = e.timer + var label = (e.timer ? require("time_utils").formatDuration(e.timer) - : require("time_utils").formatTime(e.t) + (e.rp ? ` ${decodeDOW(e)}` : ""); + : (e.date ? `${e.date.substring(5,10)} ${require("time_utils").formatTime(e.t)}` : require("time_utils").formatTime(e.t) + (e.rp ? ` ${decodeDOW(e)}` : "")) + ) + (e.msg ? " " + e.msg : ""); menu[label] = { value: e.on ? (e.timer ? iconTimerOn : iconAlarmOn) : (e.timer ? iconTimerOff : iconAlarmOff), onchange: () => setTimeout(e.timer ? showEditTimerMenu : showEditAlarmMenu, 10, e, index) @@ -67,11 +68,12 @@ function showNewMenu() { "": { "title": /*LANG*/"New..." }, "< Back": () => showMainMenu(), /*LANG*/"Alarm": () => showEditAlarmMenu(undefined, undefined), - /*LANG*/"Timer": () => showEditTimerMenu(undefined, undefined) + /*LANG*/"Timer": () => showEditTimerMenu(undefined, undefined), + /*LANG*/"Event": () => showEditAlarmMenu(undefined, undefined, true) }); } -function showEditAlarmMenu(selectedAlarm, alarmIndex) { +function showEditAlarmMenu(selectedAlarm, alarmIndex, withDate) { var isNew = alarmIndex === undefined; var alarm = require("sched").newDefaultAlarm(); @@ -82,11 +84,16 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) { } var time = require("time_utils").decodeTime(alarm.t); + if (withDate && !alarm.date) alarm.date = new Date().toLocalISOString().slice(0,10); + var date = alarm.date ? new Date(alarm.date) : undefined; + 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;} const menu = { - "": { "title": isNew ? /*LANG*/"New Alarm" : /*LANG*/"Edit Alarm" }, + "": { "title": title }, "< Back": () => { - prepareAlarmForSave(alarm, alarmIndex, time); + prepareAlarmForSave(alarm, alarmIndex, time, date); saveAndReload(); showMainMenu(); }, @@ -106,6 +113,36 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) { 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) + }, + /*LANG*/"Message": { + value: alarm.msg, + onchange: () => { + setTimeout(() => { + keyboard.input({text:alarm.msg}).then(result => { + alarm.msg = result; + prepareAlarmForSave(alarm, alarmIndex, time, date, true); + setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate); + }); + }, 100); + } + }, /*LANG*/"Enabled": { value: alarm.on, onchange: v => alarm.on = v @@ -115,8 +152,8 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) { onchange: () => setTimeout(showEditRepeatMenu, 100, alarm.rp, alarm.dow, (repeat, dow) => { alarm.rp = repeat; alarm.dow = dow; - alarm.t = require("time_utils").encodeTime(time); - setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex); + prepareAlarmForSave(alarm, alarmIndex, time, date, true); + setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate); }) }, /*LANG*/"Vibrate": require("buzz_menu").pattern(alarm.vibrate, v => alarm.vibrate = v), @@ -136,6 +173,15 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) { } }; + if (!keyboard) delete menu[/*LANG*/"Message"]; + if (alarm.date || withDate) { + delete menu[/*LANG*/"Repeat"]; + } else { + delete menu[/*LANG*/"Day"]; + delete menu[/*LANG*/"Month"]; + delete menu[/*LANG*/"Year"]; + } + if (!isNew) { menu[/*LANG*/"Delete"] = () => { E.showPrompt(/*LANG*/"Are you sure?", { title: /*LANG*/"Delete Alarm" }).then((confirm) => { @@ -145,7 +191,7 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) { showMainMenu(); } else { alarm.t = require("time_utils").encodeTime(time); - setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex); + setTimeout(showEditAlarmMenu, 10, alarm, alarmIndex, withDate); } }); }; @@ -154,14 +200,17 @@ function showEditAlarmMenu(selectedAlarm, alarmIndex) { E.showMenu(menu); } -function prepareAlarmForSave(alarm, alarmIndex, time) { +function prepareAlarmForSave(alarm, alarmIndex, time, date, temp) { alarm.t = require("time_utils").encodeTime(time); alarm.last = alarm.t < require("time_utils").getCurrentTimeMillis() ? new Date().getDate() : 0; + if(date) alarm.date = date.toLocalISOString().slice(0,10); - if (alarmIndex === undefined) { - alarms.push(alarm); - } else { - alarms[alarmIndex] = alarm; + if(!temp) { + if (alarmIndex === undefined) { + alarms.push(alarm); + } else { + alarms[alarmIndex] = alarm; + } } } @@ -255,6 +304,8 @@ function showEditTimerMenu(selectedTimer, timerIndex) { } var time = require("time_utils").decodeTime(timer.timer); + var keyboard = "textinput"; + try {keyboard = require(keyboard);} catch(e) {keyboard = null;} const menu = { "": { "title": isNew ? /*LANG*/"New Timer" : /*LANG*/"Edit Timer" }, @@ -285,6 +336,18 @@ function showEditTimerMenu(selectedTimer, timerIndex) { wrap: true, onchange: v => time.s = v }, + /*LANG*/"Message": { + value: timer.msg, + onchange: () => { + setTimeout(() => { + keyboard.input({text:timer.msg}).then(result => { + timer.msg = result; + prepareTimerForSave(timer, timerIndex, time, true); + setTimeout(showEditTimerMenu, 10, timer, timerIndex); + }); + }, 100); + } + }, /*LANG*/"Enabled": { value: timer.on, onchange: v => timer.on = v @@ -306,6 +369,7 @@ function showEditTimerMenu(selectedTimer, timerIndex) { } }; + if (!keyboard) delete menu[/*LANG*/"Message"]; if (!isNew) { menu[/*LANG*/"Delete"] = () => { E.showPrompt(/*LANG*/"Are you sure?", { title: /*LANG*/"Delete Timer" }).then((confirm) => { @@ -324,15 +388,17 @@ function showEditTimerMenu(selectedTimer, timerIndex) { E.showMenu(menu); } -function prepareTimerForSave(timer, timerIndex, time) { +function prepareTimerForSave(timer, timerIndex, time, temp) { timer.timer = require("time_utils").encodeTime(time); timer.t = require("time_utils").getCurrentTimeMillis() + timer.timer; timer.last = 0; - if (timerIndex === undefined) { - alarms.push(timer); - } else { - alarms[timerIndex] = timer; + if (!temp) { + if (timerIndex === undefined) { + alarms.push(timer); + } else { + alarms[timerIndex] = timer; + } } } diff --git a/apps/alarm/metadata.json b/apps/alarm/metadata.json index dbf090774..29e71b3d9 100644 --- a/apps/alarm/metadata.json +++ b/apps/alarm/metadata.json @@ -2,7 +2,7 @@ "id": "alarm", "name": "Alarms & Timers", "shortName": "Alarms", - "version": "0.36", + "version": "0.37", "description": "Set alarms and timers on your Bangle", "icon": "app.png", "tags": "tool,alarm", diff --git a/apps/android/ChangeLog b/apps/android/ChangeLog index 86dbdb649..1e0c14a5e 100644 --- a/apps/android/ChangeLog +++ b/apps/android/ChangeLog @@ -18,3 +18,4 @@ 0.18: Use new message library If connected to Gadgetbridge, allow GPS forwarding from phone (Gadgetbridge code still not merged) 0.19: Add automatic translation for a couple of strings. +0.20: Fix wrong event used for forwarded GPS data from Gadgetbridge and add mapper to map longitude value correctly. diff --git a/apps/android/boot.js b/apps/android/boot.js index e1e5b028b..c5a9dd746 100644 --- a/apps/android/boot.js +++ b/apps/android/boot.js @@ -134,7 +134,11 @@ event.satellites = NaN; event.course = NaN; event.fix = 1; - Bangle.emit('gps', event); + if (event.long!==undefined) { + event.lon = event.long; + delete event.long; + } + Bangle.emit('GPS', event); }, "is_gps_active": function() { gbSend({ t: "gps_power", status: Bangle._PWR && Bangle._PWR.GPS && Bangle._PWR.GPS.length>0 }); @@ -208,7 +212,7 @@ // Replace set GPS power logic to suppress activation of gps (and instead request it from the phone) Bangle.setGPSPower = (isOn, appID) => { // if not connected, use old logic - if (!NRF.getSecurityStatus().connected) return originalSetGpsPower(isOn, appID); + if (!NRF.getSecurityStatus().connected) return originalSetGpsPower(isOn, appID); // Emulate old GPS power logic if (!Bangle._PWR) Bangle._PWR={}; if (!Bangle._PWR.GPS) Bangle._PWR.GPS=[]; diff --git a/apps/android/metadata.json b/apps/android/metadata.json index d5a45edb7..883a821a4 100644 --- a/apps/android/metadata.json +++ b/apps/android/metadata.json @@ -2,7 +2,7 @@ "id": "android", "name": "Android Integration", "shortName": "Android", - "version": "0.19", + "version": "0.20", "description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.", "icon": "app.png", "tags": "tool,system,messages,notifications,gadgetbridge", diff --git a/apps/barclock/ChangeLog b/apps/barclock/ChangeLog index 88f4eaf00..96ee0141e 100644 --- a/apps/barclock/ChangeLog +++ b/apps/barclock/ChangeLog @@ -14,3 +14,4 @@ 0.14: Use ClockFace_menu.addItems 0.15: Add Power saving option 0.16: Support Fast Loading +0.17: Hide widgets instead of not loading them at all diff --git a/apps/barclock/metadata.json b/apps/barclock/metadata.json index 785c228b0..010852083 100644 --- a/apps/barclock/metadata.json +++ b/apps/barclock/metadata.json @@ -1,7 +1,7 @@ { "id": "barclock", "name": "Bar Clock", - "version": "0.16", + "version": "0.17", "description": "A simple digital clock showing seconds as a bar", "icon": "clock-bar.png", "screenshots": [{"url":"screenshot.png"},{"url":"screenshot_pm.png"}], diff --git a/apps/barclock/settings.js b/apps/barclock/settings.js index 7b88b7021..04f0a38ba 100644 --- a/apps/barclock/settings.js +++ b/apps/barclock/settings.js @@ -1,5 +1,10 @@ (function(back) { let s = require("Storage").readJSON("barclock.settings.json", true) || {}; + // migrate "don't load widgets" to "hide widgets" + if (!("hideWidgets" in s) && ("loadWidgets" in s) && !s.loadWidgets) { + s.hideWidgets = 1; + } + delete s.loadWidgets; function save(key, value) { s[key] = value; @@ -19,7 +24,7 @@ }; let items = { showDate: s.showDate, - loadWidgets: s.loadWidgets, + hideWidgets: s.hideWidgets, }; // Power saving for Bangle.js 1 doesn't make sense (no updates while screen is off anyway) if (process.env.HWVERSION>1) { diff --git a/apps/bwclk/ChangeLog b/apps/bwclk/ChangeLog index e3e059318..33cd7ef63 100644 --- a/apps/bwclk/ChangeLog +++ b/apps/bwclk/ChangeLog @@ -22,3 +22,10 @@ 0.22: Use the new clkinfo module for the menu. 0.23: Feedback of apps after run is now optional and decided by the corresponding clkinfo. 0.24: Update clock_info to avoid a redraw +0.25: Use Bangle.setUI({remove:...}) to allow loading the launcher without a full reset on fw2v16. + ClockInfo Fix: Use .get instead of .show as .show is not implemented for weather etc. +0.26: Use clkinfo.addInteractive instead of a custom implementation +0.27: Clean out some leftovers in the remove function after switching to +clkinfo.addInteractive that would cause ReferenceError. +0.28: Option to show (1) time only and (2) week of year. +0.29: use setItem of clockInfoMenu to change the active item \ No newline at end of file diff --git a/apps/bwclk/README.md b/apps/bwclk/README.md index d869fa2cf..5e2a7b55f 100644 --- a/apps/bwclk/README.md +++ b/apps/bwclk/README.md @@ -5,16 +5,12 @@ A very minimalistic clock. ## Features The BW clock implements features that are exposed by other apps through the `clkinfo` module. -For example, if you install the HomeAssistant app, this menu item will be shown if you click right -and additionally allows you to send triggers directly from the clock (select triggers via up/down and -send via click center). Here are examples of other apps that are integrated: +For example, if you install the HomeAssistant app, this menu item will be shown if you first +touch the bottom of the screen and then swipe left/right to the home assistant menu. To select +sub-items simply swipe up/down. To run an action (e.g. trigger home assistant), simply select the clkinfo (border) and touch on the item again. See also the screenshot below: -- Bangle data such as steps, heart rate, battery or charging state. -- Show agenda entries. A timer for an agenda entry can also be set by simply clicking in the middle of the screen. This can be used to not forget a meeting etc. Note that only one agenda-timer can be set at a time. *Requirement: Gadgetbridge calendar sync enabled* -- Weather temperature as well as the wind speed can be shown. *Requirement: Weather app* -- HomeAssistant triggers can be executed directly. *Requirement: HomeAssistant app* + -Note: If some apps are not installed (e.gt. weather app), then this menu item is hidden. ## Settings - Screen: Normal (widgets shown), Dynamic (widgets shown if unlocked) or Full (widgets are hidden). @@ -22,25 +18,6 @@ Note: If some apps are not installed (e.gt. weather app), then this menu item is - The colon (e.g. 7:35 = 735) can be hidden in the settings for an even larger time font to improve readability further. - Your bangle uses the sys color settings so you can change the color too. -## Menu structure -2D menu allows you to display lots of different data including data from 3rd party apps and it's also possible to control things e.g. to trigger HomeAssistant. - -Simply click left / right to go through the menu entries such as Bangle, Weather etc. -and click up/down to move into this sub-menu. You can then click in the middle of the screen -to e.g. send a trigger via HomeAssistant once you selected it. The actions really depend -on the app that provide this sub-menu through the `clkinfo` module. - -``` - Bangle -- Agenda -- Weather -- HomeAssistant - | | | | - Battery Entry 1 Temperature Trigger1 - | | | | - Steps ... ... ... - | - ... -``` - - ## Thanks to - Thanks to Gordon Williams not only for the great BangleJs, but specifically also for the implementation of `clkinfo` which simplified the BWClock a lot and moved complexety to the apps where it should be located. - Icons created by Flaticon diff --git a/apps/bwclk/app.js b/apps/bwclk/app.js index c29fdf2ef..c2518361b 100644 --- a/apps/bwclk/app.js +++ b/apps/bwclk/app.js @@ -1,3 +1,5 @@ +{ // must be inside our own scope here so that when we are unloaded everything disappears + /************************************************ * Includes */ @@ -12,8 +14,6 @@ const clock_info = require("clock_info"); const SETTINGS_FILE = "bwclk.setting.json"; const W = g.getWidth(); const H = g.getHeight(); -var lock_input = false; - /************************************************ * Settings @@ -28,7 +28,20 @@ let settings = { let saved_settings = storage.readJSON(SETTINGS_FILE, 1) || settings; for (const key in saved_settings) { - settings[key] = saved_settings[key] + settings[key] = saved_settings[key]; +} + +let isFullscreen = function() { + var s = settings.screen.toLowerCase(); + if(s == "dynamic"){ + return Bangle.isLocked(); + } else { + return s == "full"; + } +}; + +let getLineY = function(){ + return H/5*2 + (isFullscreen() ? 0 : 8); } /************************************************ @@ -74,32 +87,22 @@ Graphics.prototype.setMiniFont = function(scale) { return this; }; -function imgLock(){ +let imgLock = function() { return { width : 16, height : 16, bpp : 1, transparent : 0, buffer : E.toArrayBuffer(atob("A8AH4A5wDDAYGBgYP/w//D/8Pnw+fD58Pnw//D/8P/w=")) - } -} + }; +}; /************************************************ - * Menu + * Clock Info */ -// Custom bwItems menu - therefore, its added here and not in a clkinfo.js file. -var bwItems = { - name: null, - img: null, - items: [ - { name: "WeekOfYear", - get: () => ({ text: "Week " + weekOfYear(), img: null}), - show: function() {}, - hide: function () {} - }, - ] -}; +let clockInfoItems = clock_info.load(); -function weekOfYear() { +// Add some custom clock-infos +let weekOfYear = function() { var date = new Date(); date.setHours(0, 0, 0, 0); // Thursday in current week decides the year. @@ -111,87 +114,98 @@ function weekOfYear() { - 3 + (week1.getDay() + 6) % 7) / 7); } +clockInfoItems[0].items.unshift({ name : "weekofyear", + get : function() { return { text : "Week " + weekOfYear(), + img : null}}, + show : function() {}, + hide : function() {}, +}) -// Load menu -var menu = clock_info.load(); -menu = menu.concat(bwItems); +// Empty for large time +clockInfoItems[0].items.unshift({ name : "nop", + get : function() { return { text : null, + img : null}}, + show : function() {}, + hide : function() {}, +}) -// Ensure that our settings are still in range (e.g. app uninstall). Otherwise reset the position it. -if(settings.menuPosX >= menu.length || settings.menuPosY > menu[settings.menuPosX].items.length ){ - settings.menuPosX = 0; - settings.menuPosY = 0; -} -// Set draw functions for each item -menu.forEach((menuItm, x) => { - menuItm.items.forEach((item, y) => { - function drawItem() { - // For the clock, we have a special case, as we don't wanna redraw - // immediately when something changes. Instead, we update data each minute - // to save some battery etc. Therefore, we hide (and disable the listener) - // immedeately after redraw... - item.hide(); +let clockInfoMenu = clock_info.addInteractive(clockInfoItems, { + x : 0, + y: 135, + w: W, + h: H-135, + draw : (itm, info, options) => { + var hideClkInfo = info.text == null; - // After drawing the item, we enable inputs again... - lock_input = false; + g.setColor(g.theme.fg); + g.fillRect(options.x, options.y, options.x+options.w, options.y+options.h); - var info = item.get(); - drawMenuItem(info.text, info.img); + g.setFontAlign(0,0); + g.setColor(g.theme.bg); + + if (options.focus){ + var y = hideClkInfo ? options.y+20 : options.y+2; + var h = hideClkInfo ? options.h-20 : options.h-2; + g.drawRect(options.x, y, options.x+options.w-2, y+h-1); // show if focused + g.drawRect(options.x+1, y+1, options.x+options.w-3, y+h-2); // show if focused } - item.on('redraw', drawItem); - }) + // In case we hide the clkinfo, we show the time again as the time should + // be drawn larger. + if(hideClkInfo){ + drawTime(); + return; + } + + // Set text and font + var image = info.img; + var text = String(info.text); + if(text.split('\n').length > 1){ + g.setMiniFont(); + } else { + g.setSmallFont(); + } + + // Compute sizes + var strWidth = g.stringWidth(text); + var imgWidth = image == null ? 0 : 24; + var midx = options.x+options.w/2; + + // Draw + if (image) { + var scale = imgWidth / image.width; + g.drawImage(image, midx-parseInt(imgWidth*1.3/2)-parseInt(strWidth/2), options.y+6, {scale: scale}); + } + g.drawString(text, midx+parseInt(imgWidth*1.3/2), options.y+20); + + // In case we are in focus and the focus box changes (fullscreen yes/no) + // we draw the time again. Otherwise it could happen that a while line is + // not cleared correctly. + if(options.focus) drawTime(); + } }); -function canRunMenuItem(){ - if(settings.menuPosY == 0){ - return false; - } - - var menuEntry = menu[settings.menuPosX]; - var item = menuEntry.items[settings.menuPosY-1]; - return item.run !== undefined; -} - - -function runMenuItem(){ - if(settings.menuPosY == 0){ - return; - } - - var menuEntry = menu[settings.menuPosX]; - var item = menuEntry.items[settings.menuPosY-1]; - try{ - var ret = item.run(); - if(ret){ - Bangle.buzz(300, 0.6); - } - } catch (ex) { - // Simply ignore it... - } -} - - /************************************************ * Draw */ -function draw() { +let draw = function() { // Queue draw again queueDraw(); // Draw clock drawDate(); - drawMenuAndTime(); + drawTime(); drawLock(); drawWidgets(); -} +}; -function drawDate(){ +let drawDate = function() { // Draw background - var y = H/5*2 + (isFullscreen() ? 0 : 8); + var y = getLineY() g.reset().clearRect(0,0,W,y); // Draw date @@ -216,17 +230,17 @@ function drawDate(){ g.setMediumFont(); g.setColor(g.theme.fg); g.drawString(dateStr, W/2 - fullDateW / 2, y+2); -} +}; -function drawTime(y, smallText){ +let drawTime = function() { + var hideClkInfo = clockInfoMenu.menuA == 0 && clockInfoMenu.menuB == 0; + // Draw background + var y1 = getLineY(); + var y = y1; var date = new Date(); - // Draw time - g.setColor(g.theme.bg); - g.setFontAlign(0,0); - var hours = String(date.getHours()); var minutes = date.getMinutes(); minutes = minutes < 10 ? String("0") + minutes : minutes; @@ -236,123 +250,68 @@ function drawTime(y, smallText){ // Set y coordinates correctly y += parseInt((H - y)/2) + 5; - // Show large or small time depending on info entry - if(smallText){ + if (hideClkInfo){ + g.setLargeFont(); + } else { y -= 15; g.setMediumFont(); - } else { - g.setLargeFont(); } - g.drawString(timeStr, W/2, y); -} - -function drawMenuItem(text, image){ - // First clear the time region - var y = H/5*2 + (isFullscreen() ? 0 : 8); + // Clear region and draw time g.setColor(g.theme.fg); - g.fillRect(0,y,W,H); + g.fillRect(0,y1,W,y+20 + (hideClkInfo ? 1 : 0) + (isFullscreen() ? 3 : 0)); - // Draw menu text - var hasText = (text != null && text != ""); - if(hasText){ - g.setFontAlign(0,0); - - // For multiline text we show an even smaller font... - text = String(text); - if(text.split('\n').length > 1){ - g.setMiniFont(); - } else { - g.setSmallFont(); - } - - var imgWidth = image == null ? 0 : 24; - var strWidth = g.stringWidth(text); - g.setColor(g.theme.fg).fillRect(0, 149-14, W, H); - g.setColor(g.theme.bg).drawString(text, W/2 + imgWidth/2 + 2, 149+3); - - if(image != null){ - var scale = imgWidth / image.width; - g.drawImage(image, W/2 + -strWidth/2-4 - parseInt(imgWidth/2), 149 - parseInt(imgWidth/2), {scale: scale}); - } - } - - // Draw time - drawTime(y, hasText); -} + g.setColor(g.theme.bg); + g.setFontAlign(0,0); + g.drawString(timeStr, W/2, y); +}; -function drawMenuAndTime(){ - var menuEntry = menu[settings.menuPosX]; - - // The first entry is the overview... - if(settings.menuPosY == 0){ - drawMenuItem(menuEntry.name, menuEntry.img); - return; - } - - // Draw item if needed - lock_input = true; - var item = menuEntry.items[settings.menuPosY-1]; - item.show(); -} - - -function drawLock(){ +let drawLock = function() { if(settings.showLock && Bangle.isLocked()){ g.setColor(g.theme.fg); g.drawImage(imgLock(), W-16, 2); } -} +}; -function drawWidgets(){ +let drawWidgets = function() { if(isFullscreen()){ for (let wd of WIDGETS) {wd.draw=()=>{};wd.area="";} } else { Bangle.drawWidgets(); } -} - - -function isFullscreen(){ - var s = settings.screen.toLowerCase(); - if(s == "dynamic"){ - return Bangle.isLocked() - } else { - return s == "full" - } -} - +}; /************************************************ * Listener */ // timeout used to update every minute -var drawTimeout; +let drawTimeout; // schedule a draw for the next minute -function queueDraw() { +let queueDraw = function() { if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = setTimeout(function() { drawTimeout = undefined; draw(); }, 60000 - (Date.now() % 60000)); -} +}; // Stop updates when LCD is off, restart when on -Bangle.on('lcdPower',on=>{ +let lcdListenerBw = function(on) { if (on) { draw(); // draw immediately, queue redraw } else { // stop draw timer if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = undefined; } -}); +}; +Bangle.on('lcdPower', lcdListenerBw); -Bangle.on('lock', function(isLocked) { +let lockListenerBw = function(isLocked) { if (drawTimeout) clearTimeout(drawTimeout); drawTimeout = undefined; @@ -363,85 +322,21 @@ Bangle.on('lock', function(isLocked) { } draw(); -}); - -Bangle.on('charging',function(charging) { - if (drawTimeout) clearTimeout(drawTimeout); - drawTimeout = undefined; +}; +Bangle.on('lock', lockListenerBw); +let charging = function(charging){ // Jump to battery - settings.menuPosX = 0; - settings.menuPosY = 1; - draw(); -}); - -Bangle.on('touch', function(btn, e){ - var widget_size = isFullscreen() ? 0 : 20; // Its not exactly 24px -- empirically it seems that 20 worked better... - var left = parseInt(g.getWidth() * 0.22); - var right = g.getWidth() - left; - var upper = parseInt(g.getHeight() * 0.22) + widget_size; - var lower = g.getHeight() - upper; - - var is_upper = e.y < upper; - var is_lower = e.y > lower; - var is_left = e.x < left && !is_upper && !is_lower; - var is_right = e.x > right && !is_upper && !is_lower; - var is_center = !is_upper && !is_lower && !is_left && !is_right; - - if(lock_input){ - return; - } - - if(is_lower){ - Bangle.buzz(40, 0.6); - settings.menuPosY = (settings.menuPosY+1) % (menu[settings.menuPosX].items.length+1); - - drawMenuAndTime(); - } - - if(is_upper){ - if(e.y < widget_size){ - return; - } - - Bangle.buzz(40, 0.6); - settings.menuPosY = settings.menuPosY-1; - settings.menuPosY = settings.menuPosY < 0 ? menu[settings.menuPosX].items.length : settings.menuPosY; - - drawMenuAndTime(); - } - - if(is_right){ - Bangle.buzz(40, 0.6); - settings.menuPosX = (settings.menuPosX+1) % menu.length; - settings.menuPosY = 0; - drawMenuAndTime(); - } - - if(is_left){ - Bangle.buzz(40, 0.6); - settings.menuPosY = 0; - settings.menuPosX = settings.menuPosX-1; - settings.menuPosX = settings.menuPosX < 0 ? menu.length-1 : settings.menuPosX; - drawMenuAndTime(); - } - - if(is_center){ - if(canRunMenuItem()){ - runMenuItem(); - } - } -}); - - -E.on("kill", function(){ - try{ - storage.write(SETTINGS_FILE, settings); - } catch(ex){ - // If this fails, we still kill the app... - } -}); + clockInfoMenu.setItem(0, 2); + drawTime(); +} +Bangle.on('charging', charging); +let kill = function(){ + clockInfoMenu.remove(); + delete clockInfoMenu; +}; +E.on("kill", kill); /************************************************ * Startup Clock @@ -450,10 +345,25 @@ E.on("kill", function(){ // The upper part is inverse i.e. light if dark and dark if light theme // is enabled. In order to draw the widgets correctly, we invert the // dark/light theme as well as the colors. +let themeBackup = g.theme; g.setTheme({bg:g.theme.fg,fg:g.theme.bg, dark:!g.theme.dark}).clear(); // Show launcher when middle button pressed -Bangle.setUI("clock"); +Bangle.setUI({ + mode : "clock", + remove : function() { + // Called to unload all of the clock app + Bangle.removeListener('lcdPower', lcdListenerBw); + Bangle.removeListener('lock', lockListenerBw); + Bangle.removeListener('charging', charging); + if (drawTimeout) clearTimeout(drawTimeout); + drawTimeout = undefined; + // save settings + kill(); + E.removeListener("kill", kill); + g.setTheme(themeBackup); + } +}); // Load widgets and draw clock the first time Bangle.loadWidgets(); @@ -464,3 +374,5 @@ for (let wd of WIDGETS) {wd._draw=wd.draw; wd._area=wd.area;} // Draw first time draw(); + +} // End of app scope diff --git a/apps/bwclk/metadata.json b/apps/bwclk/metadata.json index 8ef812f41..a29102bdf 100644 --- a/apps/bwclk/metadata.json +++ b/apps/bwclk/metadata.json @@ -1,11 +1,11 @@ { "id": "bwclk", "name": "BW Clock", - "version": "0.24", - "description": "A very minimalistic clock to mainly show date and time.", + "version": "0.29", + "description": "A very minimalistic clock.", "readme": "README.md", "icon": "app.png", - "screenshots": [{"url":"screenshot.png"}, {"url":"screenshot_2.png"}, {"url":"screenshot_3.png"}, {"url":"screenshot_4.png"}], + "screenshots": [{"url":"screenshot.png"}, {"url":"screenshot_2.png"}, {"url":"screenshot_3.png"}], "type": "clock", "tags": "clock,clkinfo", "supports": ["BANGLEJS2"], diff --git a/apps/bwclk/screenshot.png b/apps/bwclk/screenshot.png index 3a75f13d1..37acf7cc0 100644 Binary files a/apps/bwclk/screenshot.png and b/apps/bwclk/screenshot.png differ diff --git a/apps/bwclk/screenshot_2.png b/apps/bwclk/screenshot_2.png index 31bf6373e..8d2f1717f 100644 Binary files a/apps/bwclk/screenshot_2.png and b/apps/bwclk/screenshot_2.png differ diff --git a/apps/bwclk/screenshot_3.png b/apps/bwclk/screenshot_3.png index 8d982cac4..d52057569 100644 Binary files a/apps/bwclk/screenshot_3.png and b/apps/bwclk/screenshot_3.png differ diff --git a/apps/bwclk/screenshot_4.png b/apps/bwclk/screenshot_4.png deleted file mode 100644 index 83de5c2ce..000000000 Binary files a/apps/bwclk/screenshot_4.png and /dev/null differ diff --git a/apps/circlesclock/ChangeLog b/apps/circlesclock/ChangeLog index 83abde6df..830bc28e8 100644 --- a/apps/circlesclock/ChangeLog +++ b/apps/circlesclock/ChangeLog @@ -39,3 +39,4 @@ 0.21: Remade all icons without a palette for dark theme Now re-adds widgets if they were hidden when fast-loading 0.22: Fixed crash if item has no image and cutting long overflowing text +0.23: Setting circles colours per clkinfo and not position diff --git a/apps/circlesclock/app.js b/apps/circlesclock/app.js index 30d6a48f4..e1fc8d846 100644 --- a/apps/circlesclock/app.js +++ b/apps/circlesclock/app.js @@ -20,24 +20,12 @@ let settings = Object.assign( storage.readJSON("circlesclock.default.json", true) || {}, storage.readJSON(SETTINGS_FILE, true) || {} ); - //TODO deprecate this (and perhaps use in the clkinfo module) -// Load step goal from health app and pedometer widget as fallback -if (settings.stepGoal == undefined) { - let d = storage.readJSON("health.json", true) || {}; - settings.stepGoal = d != undefined && d.settings != undefined ? d.settings.stepGoal : undefined; - - if (settings.stepGoal == undefined) { - d = storage.readJSON("wpedom.json", true) || {}; - settings.stepGoal = d != undefined && d.settings != undefined ? d.settings.goal : 10000; - } -} let drawTimeout; const showWidgets = settings.showWidgets || false; const circleCount = settings.circleCount || 3; const showBigWeather = settings.showBigWeather || false; -let hrtValue; //TODO deprecate this let now = Math.round(new Date().getTime() / 1000); // layout values: @@ -128,8 +116,11 @@ let draw = function() { queueDraw(); } -let getCircleColor = function(index) { - let color = settings["circle" + index + "color"]; +let getCircleColor = function(item, clkmenu) { + let colorKey = clkmenu.name; + if(!clkmenu.dynamic) colorKey += "/"+item.name; + colorKey += "_color"; + let color = settings[colorKey]; if (color && color != "") return color; return g.theme.fg; } @@ -138,7 +129,7 @@ let getGradientColor = function(color, percent) { if (isNaN(percent)) percent = 0; if (percent > 1) percent = 1; let colorList = [ - '#00FF00', '#80FF00', '#FFFF00', '#FF8000', '#FF0000' + '#00ff00', '#80ff00', '#ffff00', '#ff8000', '#ff0000' ]; if (color == "fg") { color = colorFg; @@ -151,6 +142,17 @@ let getGradientColor = function(color, percent) { let colorIndex = colorList.length - Math.round(colorList.length * percent); return colorList[Math.min(colorIndex, colorList.length)] || "#ff0000"; } + colorList = [ + '#0000ff', '#8800ff', '#ff00ff', '#ff0088', '#ff0000' + ]; + if (color == "blue-red") { + let colorIndex = Math.round(colorList.length * percent); + return colorList[Math.min(colorIndex, colorList.length) - 1] || "#0000ff"; + } + if (color == "red-blue") { + let colorIndex = colorList.length - Math.round(colorList.length * percent); + return colorList[Math.min(colorIndex, colorList.length)] || "#ff0000"; + } return color; } @@ -172,10 +174,10 @@ let drawEmpty = function(img, w, color) { .drawImage(img, w - iconOffset, h3 + radiusOuter - iconOffset, {scale: 16/24}); } -let drawCircle = function(index, item, data) { +let drawCircle = function(index, item, data, clkmenu) { var w = circlePosX[index-1]; drawCircleBackground(w); - const color = getCircleColor(index); + const color = getCircleColor(item, clkmenu); //drawEmpty(info? info.img : null, w, color); var img = data.img; var percent = 1; //fill up if no range @@ -338,7 +340,8 @@ Bangle.setUI({ let clockInfoDraw = (itm, info, options) => { //print("Draw",itm.name,options); - drawCircle(options.circlePosition, itm, info); + let clkmenu = clockInfoItems[options.menuA]; + drawCircle(options.circlePosition, itm, info, clkmenu); if (options.focus) g.reset().drawRect(options.x, options.y, options.x+options.w-2, options.y+options.h-1) }; let clockInfoItems = require("clock_info").load(); diff --git a/apps/circlesclock/default.json b/apps/circlesclock/default.json index ad409b992..9d5b3e242 100644 --- a/apps/circlesclock/default.json +++ b/apps/circlesclock/default.json @@ -3,23 +3,21 @@ "showWidgets": false, "weatherCircleData": "humidity", "circleCount": 3, - "circle1color": "green-red", - "circle2color": "#0000ff", - "circle3color": "red-green", - "circle4color": "#ffff00", + "Bangle/Battery_color":"red-green", + "Bangle/Steps_color":"#0000ff", + "Bangle/HRM_color":"green-red", + "Bangle/Altitude_color":"#00ff00", + "Weather/conditionWithTemperature_color":"#ffff00", + "Weather/condition_color":"#00ffff", + "Weather/humidity_color":"#00ffff", + "Weather/wind_color":"fg", + "Weather/temperature_color":"blue-red", + "Alarms_color":"#00ff00", + "Agenda_color":"#ff0000", "circle1colorizeIcon": true, "circle2colorizeIcon": true, "circle3colorizeIcon": true, "circle4colorizeIcon": false, "updateInterval": 60, - "showBigWeather": false, - - "minHR": 40, - "maxHR": 200, - "confidence": 0, - "stepGoal": 10000, - "stepDistanceGoal": 8000, - "stepLength": 0.8, - "hrmValidity": 60 - + "showBigWeather": false } diff --git a/apps/circlesclock/metadata.json b/apps/circlesclock/metadata.json index 1b94c00b3..45b869521 100644 --- a/apps/circlesclock/metadata.json +++ b/apps/circlesclock/metadata.json @@ -1,7 +1,7 @@ { "id": "circlesclock", "name": "Circles clock", "shortName":"Circles clock", - "version":"0.22", + "version":"0.23", "description": "A clock with three or four circles for different data at the bottom in a probably familiar style", "icon": "app.png", "screenshots": [{"url":"screenshot-dark.png"}, {"url":"screenshot-light.png"}, {"url":"screenshot-dark-4.png"}, {"url":"screenshot-light-4.png"}], diff --git a/apps/circlesclock/settings.js b/apps/circlesclock/settings.js index 5c5ea4f27..63a2b0f93 100644 --- a/apps/circlesclock/settings.js +++ b/apps/circlesclock/settings.js @@ -12,12 +12,10 @@ storage.write(SETTINGS_FILE, settings); } - const valuesColors = ["", "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", - "#00ffff", "#fff", "#000", "green-red", "red-green", "fg"]; - const namesColors = ["default", "red", "green", "blue", "yellow", "magenta", - "cyan", "white", "black", "green->red", "red->green", "foreground"]; - - const weatherData = ["empty", "humidity", "wind"]; + const valuesColors = ["", "#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", + "#00ffff", "#fff", "#000", "green-red", "red-green", "blue-red", "red-blue", "fg"]; + const namesColors = ["default", "red", "green", "blue", "yellow", "magenta", + "cyan", "white", "black", "green->red", "red->green", "blue->red", "red->blue", "foreground"]; function showMainMenu() { let menu ={ @@ -30,31 +28,11 @@ step: 1, onchange: x => save('circleCount', x), }, - /*LANG*/'circle 1': ()=>showCircleMenu(1), - /*LANG*/'circle 2': ()=>showCircleMenu(2), - /*LANG*/'circle 3': ()=>showCircleMenu(3), - /*LANG*/'circle 4': ()=>showCircleMenu(4), - /*LANG*/'battery warn': { - value: settings.batteryWarn, - min: 10, - max : 100, - step: 10, - format: x => { - return x + '%'; - }, - onchange: x => save('batteryWarn', x), - }, /*LANG*/'show widgets': { value: !!settings.showWidgets, format: () => (settings.showWidgets ? 'Yes' : 'No'), onchange: x => save('showWidgets', x), }, - /*LANG*/'weather data': { - value: weatherData.indexOf(settings.weatherCircleData), - min: 0, max: 2, - format: v => weatherData[v], - onchange: x => save('weatherCircleData', weatherData[x]), - }, /*LANG*/'update interval': { value: settings.updateInterval, min: 0, @@ -65,41 +43,54 @@ }, onchange: x => save('updateInterval', x), }, - //TODO deprecated local icons, may disappear in future - /*LANG*/'legacy weather icons': { - value: !!settings.legacyWeatherIcons, - format: () => (settings.legacyWeatherIcons ? 'Yes' : 'No'), - onchange: x => save('legacyWeatherIcons', x), - }, /*LANG*/'show big weather': { value: !!settings.showBigWeather, format: () => (settings.showBigWeather ? 'Yes' : 'No'), onchange: x => save('showBigWeather', x), - } + }, + /*LANG*/'colorize icons': ()=>showCircleMenus() }; + clock_info.load().forEach(e=>{ + if(e.dynamic) { + const colorKey = e.name + "_color"; + menu[e.name+/*LANG*/' color'] = { + value: valuesColors.indexOf(settings[colorKey]) || 0, + min: 0, max: valuesColors.length - 1, + format: v => namesColors[v], + onchange: x => save(colorKey, valuesColors[x]), + }; + } else { + let values = e.items.map(i=>e.name+"/"+i.name); + let names = e.name=="Bangle" ? e.items.map(i=>i.name) : values; + values.forEach((v,i)=>{ + const colorKey = v + "_color"; + menu[names[i]+/*LANG*/' color'] = { + value: valuesColors.indexOf(settings[colorKey]) || 0, + min: 0, max: valuesColors.length - 1, + format: v => namesColors[v], + onchange: x => save(colorKey, valuesColors[x]), + }; + }); + } + }) E.showMenu(menu); } - function showCircleMenu(circleId) { - const circleName = "circle" + circleId; - const colorKey = circleName + "color"; - const colorizeIconKey = circleName + "colorizeIcon"; - - const menu = { - '': { 'title': /*LANG*/'Circle ' + circleId }, - /*LANG*/'< Back': ()=>showMainMenu(), - /*LANG*/'color': { - value: valuesColors.indexOf(settings[colorKey]) || 0, - min: 0, max: valuesColors.length - 1, - format: v => namesColors[v], - onchange: x => save(colorKey, valuesColors[x]), - }, - /*LANG*/'colorize icon': { + function showCircleMenus() { + const menu = { + '': { 'title': /*LANG*/'Colorize icons'}, + /*LANG*/'< Back': ()=>showMainMenu(), + }; + for(var circleId=1; circleId<=4; ++circleId) { + const circleName = "circle" + circleId; + const colorKey = circleName + "color"; + const colorizeIconKey = circleName + "colorizeIcon"; + menu[/*LANG*/'circle ' + circleId] = { value: settings[colorizeIconKey] || false, - format: () => (settings[colorizeIconKey] ? 'Yes' : 'No'), - onchange: x => save(colorizeIconKey, x), - }, - }; + format: () => (settings[colorizeIconKey]? /*LANG*/'Yes': /*LANG*/'No'), + onchange: x => save(colorizeIconKey, x), + }; + } E.showMenu(menu); } diff --git a/apps/cogclock/ChangeLog b/apps/cogclock/ChangeLog index f4bfe77a5..403cd2258 100644 --- a/apps/cogclock/ChangeLog +++ b/apps/cogclock/ChangeLog @@ -1,3 +1,4 @@ 0.01: New clock 0.02: Use ClockFace library, add settings 0.03: Use ClockFace_menu.addSettingsFile +0.04: Hide widgets instead of not loading them at all diff --git a/apps/cogclock/metadata.json b/apps/cogclock/metadata.json index 29000b589..d404275ee 100644 --- a/apps/cogclock/metadata.json +++ b/apps/cogclock/metadata.json @@ -1,7 +1,7 @@ { "id": "cogclock", "name": "Cog Clock", - "version": "0.03", + "version": "0.04", "description": "A cross-shaped clock inside a cog", "icon": "icon.png", "screenshots": [{"url":"screenshot.png"}], diff --git a/apps/cogclock/settings.js b/apps/cogclock/settings.js index a91b033d0..fb1dd761c 100644 --- a/apps/cogclock/settings.js +++ b/apps/cogclock/settings.js @@ -4,7 +4,7 @@ /*LANG*/"< Back": back, }; require("ClockFace_menu").addSettingsFile(menu, "cogclock.settings.json", [ - "showDate", "loadWidgets" + "showDate", "hideWidgets" ]); E.showMenu(menu); }); diff --git a/apps/dragboard/ChangeLog b/apps/dragboard/ChangeLog index 265094e87..faf3d2d33 100644 --- a/apps/dragboard/ChangeLog +++ b/apps/dragboard/ChangeLog @@ -4,3 +4,4 @@ 0.04: Now displays the opened text string at launch. 0.05: Now scrolls text when string gets longer than screen width. 0.06: The code is now more reliable and the input snappier. Widgets will be drawn if present. +0.07: Settings for display colors diff --git a/apps/dragboard/README.md b/apps/dragboard/README.md index 8960e5749..415be5449 100644 --- a/apps/dragboard/README.md +++ b/apps/dragboard/README.md @@ -12,5 +12,8 @@ Known bugs: - Initially developed for use with dark theme set on Bangle.js 2 - that is still the preferred way to view it although it now works with other themes. - When repeatedly doing 'del' on an empty text-string, the letter case is changed back and forth between upper and lower case. -To do: -- Possibly provide a dragboard.settings.js file +Settings: +- CAPS LOCK: all characters are displayed and typed in uppercase +- ABC Color: color of the characters row +- Num Color: color of the digits and symbols row +- Highlight Color: color of the currently highlighted character diff --git a/apps/dragboard/lib.js b/apps/dragboard/lib.js index 220f075d7..83aae5f14 100644 --- a/apps/dragboard/lib.js +++ b/apps/dragboard/lib.js @@ -2,12 +2,14 @@ exports.input = function(options) { options = options||{}; var text = options.text; if ("string"!=typeof text) text=""; + let settings = require('Storage').readJSON('dragboard.json',1)||{} var R = Bangle.appRect; + const paramToColor = (param) => g.toColor(`#${settings[param].toString(16).padStart(3,0)}`); var BGCOLOR = g.theme.bg; - var HLCOLOR = g.theme.fg; - var ABCCOLOR = g.toColor(1,0,0);//'#FF0000'; - var NUMCOLOR = g.toColor(0,1,0);//'#00FF00'; + var HLCOLOR = settings.Highlight ? paramToColor("Highlight") : g.theme.fg; + var ABCCOLOR = settings.ABC ? paramToColor("ABC") : g.toColor(1,0,0);//'#FF0000'; + var NUMCOLOR = settings.Num ? paramToColor("Num") : g.toColor(0,1,0);//'#00FF00'; var BIGFONT = '6x8:3'; var BIGFONTWIDTH = parseInt(BIGFONT.charAt(0)*parseInt(BIGFONT.charAt(-1))); var SMALLFONT = '6x8:1'; @@ -102,6 +104,7 @@ exports.input = function(options) { //setTimeout(initDraw, 0); // So Bangle.appRect reads the correct environment. It would draw off to the side sometimes otherwise. function changeCase(abcHL) { + if (settings.uppercase) return; g.setColor(BGCOLOR); g.setFontAlign(-1, -1, 0); g.drawString(ABC, ABCPADDING, (R.y+R.h)/2); diff --git a/apps/dragboard/metadata.json b/apps/dragboard/metadata.json index 64b6dbe18..964ace3a7 100644 --- a/apps/dragboard/metadata.json +++ b/apps/dragboard/metadata.json @@ -1,6 +1,6 @@ { "id": "dragboard", "name": "Dragboard", - "version":"0.06", + "version":"0.07", "description": "A library for text input via swiping keyboard", "icon": "app.png", "type":"textinput", @@ -9,6 +9,7 @@ "screenshots": [{"url":"screenshot.png"}], "readme": "README.md", "storage": [ - {"name":"textinput","url":"lib.js"} + {"name":"textinput","url":"lib.js"}, + {"name":"dragboard.settings.js","url":"settings.js"} ] } diff --git a/apps/dragboard/settings.js b/apps/dragboard/settings.js new file mode 100644 index 000000000..a53914869 --- /dev/null +++ b/apps/dragboard/settings.js @@ -0,0 +1,48 @@ +(function(back) { + let settings = require('Storage').readJSON('dragboard.json',1)||{}; + const colors = { + 4095: /*LANG*/"White", + 4080: /*LANG*/"Yellow", + 3840: /*LANG*/"Red", + 3855: /*LANG*/"Magenta", + 255: /*LANG*/"Cyan", + 240: /*LANG*/"Green", + 15: /*LANG*/"Blue", + 0: /*LANG*/"Black", + '-1': /*LANG*/"Default" + }; + + const save = () => require('Storage').write('dragboard.json', settings); + function colorMenu(key) { + let menu = {'': {title: key}, '< Back': () => E.showMenu(appMenu)}; + Object.keys(colors).forEach(color => { + var label = colors[color]; + menu[label] = { + value: settings[key] == color, + onchange: () => { + if (color >= 0) { + settings[key] = color; + } else { + delete settings[key]; + } + save(); + setTimeout(E.showMenu, 10, appMenu); + } + }; + }); + return menu; + } + + const appMenu = { + '': {title: 'Dragboard'}, '< Back': back, + /*LANG*/'CAPS LOCK': { + value: !!settings.uppercase, + onchange: v => {settings.uppercase = v; save();} + }, + /*LANG*/'ABC Color': () => E.showMenu(colorMenu("ABC")), + /*LANG*/'Num Color': () => E.showMenu(colorMenu("Num")), + /*LANG*/'Highlight Color': () => E.showMenu(colorMenu("Highlight")) + }; + + E.showMenu(appMenu); +}); \ No newline at end of file diff --git a/apps/dtlaunch/ChangeLog b/apps/dtlaunch/ChangeLog index 044b8c35f..e0bd76eb0 100644 --- a/apps/dtlaunch/ChangeLog +++ b/apps/dtlaunch/ChangeLog @@ -23,4 +23,8 @@ button to exit is no longer an option. facilitate 'fast switching' of apps where available. 0.20: Bangle 2: Revert use of Bangle.load() to classic load() calls since widgets would still be loaded when they weren't supposed to. +0.21: Bangle 2: Call Bangle.drawWidgets() early on so that the widget field +immediately follows the correct theme. +0.22: Bangle 2: Change to not automatically marking the first app on a page +when moving pages. Add caching for faster startups. diff --git a/apps/dtlaunch/app-b2.js b/apps/dtlaunch/app-b2.js index a7a318c18..2070f1147 100644 --- a/apps/dtlaunch/app-b2.js +++ b/apps/dtlaunch/app-b2.js @@ -13,20 +13,25 @@ }, require('Storage').readJSON("dtlaunch.json", true) || {}); let s = require("Storage"); - var apps = s.list(/\.info$/).map(app=>{ - let a=s.readJSON(app,1); - return a && { - name:a.name, type:a.type, icon:a.icon, sortorder:a.sortorder, src:a.src - };}).filter( - app=>app && (app.type=="app" || (app.type=="clock" && settings.showClocks) || (app.type=="launch" && settings.showLaunchers) || !app.type)); - - apps.sort((a,b)=>{ - let n=(0|a.sortorder)-(0|b.sortorder); - if (n) return n; // do sortorder first - if (a.name| name | +quantity | +done | +actions | +
|---|