diff --git a/apps/bee/ChangeLog b/apps/bee/ChangeLog index df0907283..cfb44c8e9 100644 --- a/apps/bee/ChangeLog +++ b/apps/bee/ChangeLog @@ -1,2 +1,3 @@ 0.01: New app! 0.02: Fix bug with regenerating index, fix bug in word lookups +0.03: Improve word search performance diff --git a/apps/bee/README.md b/apps/bee/README.md index c6461fb8e..3d0a4c14a 100644 --- a/apps/bee/README.md +++ b/apps/bee/README.md @@ -27,10 +27,7 @@ least once and yields an additional 7 points. Each game contains at least one pa The game uses an internal dictionary consisting of a newline separated list of English words ('bee.words', using the '2of12inf' word list). The dictionary is fairly large (~700kB of flash space) and thus requires appropriate space on the watch and will make installing the app somewhat slow. Because of its size it cannot be compressed (heatshrink needs to hold the compressed/uncompressed data in memory). -In order to make checking the validity of a guessed word faster an index file ('bee_lindex.json') is installed with -the app that facilitates faster word lookups. This index file is specific to the dictionary file used. If one were to -replace the dictionary file with a different version (e.g. a different language) the index file has to be regenerated. The easiest -way to do so is to delete (via the Web IDE or the fileman app on the watch) the file 'bee_lindex.json' - it will be regenerated (and saved, -i.e. it only happens once) on app startup automatically, a process that takes roughly 30 seconds. +This file can be replaced with a custom dictionary, an ASCII file containing a newline-separated (single "\n", not DOS-style "\r\n") alphabetically +sorted (sorting is important for the word lookup algorithm) list of words. ![Screenshot](./bee_screenshot.png) diff --git a/apps/bee/bee.app.js b/apps/bee/bee.app.js index ef1582baa..878e9763c 100644 --- a/apps/bee/bee.app.js +++ b/apps/bee/bee.app.js @@ -1,7 +1,7 @@ const S = require("Storage"); +const words = S.read("bee.words"); var letters = []; -var letterIdx = []; var centers = []; @@ -12,29 +12,17 @@ var score = 0; var intervalID = -1; -function prepareLetterIdx () { +function biSearch(w, ws, start, end, count) { "compile" - var li = [0]; - if (S.read("bee_lindex.json")!==undefined) li = S.readJSON("bee_lindex.json"); // check for cached index - else { - for (var i=1; i<26; ++i) { - var prefix = String.fromCharCode(97+i%26); - console.log(prefix); - li.push(S.read('bee.words').indexOf("\n"+prefix, li[i-1])+1); - } - li.push(S.read('bee.words').length); - S.writeJSON("bee_lindex.json", li); - } - for (var i=0; i<26; ++i) letterIdx[i] = S.read("bee.words", li[i], li[i+1]-li[i]); -} - -function findWord (w) { - "compile" - var ci = w.charCodeAt(0)-97; - var f = letterIdx[ci].indexOf("\n"+w+"\n"); - if (f>=0) return true; - if (letterIdx[ci].substr(0, w.length)==w) return true; - return false; + if (start>end-w.legnth || count--<=0) return ws.substr(start, end-start).indexOf("\n"+w+"\n"); + var mid = (end+start)>>1; + if (ws[mid-1]==="\n") --mid; + else while (midws[mid+i+1]) return biSearch(w, ws, mid+1, end, count); } function isPangram(w) { @@ -46,7 +34,7 @@ function isPangram(w) { function checkWord (w) { if (w.indexOf(String.fromCharCode(97+letters[0]))==-1) return false; // does it contain central letter? if (foundWords.indexOf(w)>=0) return false; // already found - if (findWord(w)) { + if (biSearch(w, words, 0, words.length, 20)>-1) { foundWords.push(w); foundWords.sort(); if (w.length==4) score++; @@ -93,13 +81,12 @@ function pickLetters() { var ltrs = ""; while (ltrs.length!==7) { ltrs = []; - var j = Math.floor(26*Math.random()); - var i = Math.floor((letterIdx[j].length-10)*Math.random()); - while (letterIdx[j][i]!="\n" && i on all apps of the types _clock_ and _launch_ * _except apps_ -> on all apps of the types _clock_ and _launch_ and in the settings * _always on_ -> always enabled when the widget is displayed +* __Oversize__ + _0px_ / _1px_ / _..._ / __20px__ / _..._ / _50px_ + To make it easier to hit the widget, this value extends the touch area of the widget in all directions. * __Drag Delay__ _off_ / _50ms_ / _100ms_ / _..._ / __500ms__ / _..._ / _1000ms_ Change the maximum delay between first touch and re-touch/drag to change the brightness or disable changing the brightness completely. @@ -85,8 +91,6 @@ This images are stored in a seperate file _(lightswitch.images.json)_. ### Worth Mentioning --- #### To do list -* Catch the touch and draw input related to this widget to prevent actions in the active app. - _(For now I have no idea how to achieve this, help is appreciated)_ * Manage images for the lock icon through a _Customize and Upload App_ page. #### Requests, Bugs and Feedback diff --git a/apps/lightswitch/metadata.json b/apps/lightswitch/metadata.json index 9ac388eda..54dc8389f 100644 --- a/apps/lightswitch/metadata.json +++ b/apps/lightswitch/metadata.json @@ -2,7 +2,7 @@ "id": "lightswitch", "name": "Light Switch Widget", "shortName": "Light Switch", - "version": "0.03", + "version": "0.04", "description": "A fast way to switch LCD backlight on/off, change the brightness and show the lock status. All in one widget.", "icon": "images/app.png", "screenshots": [ diff --git a/apps/lightswitch/settings.js b/apps/lightswitch/settings.js index bebb16d15..5ac70bc28 100644 --- a/apps/lightswitch/settings.js +++ b/apps/lightswitch/settings.js @@ -6,7 +6,8 @@ var settings = Object.assign({ colors: "011", image: "default", - touchOn: "clock,launch", + touchOn: "always", + oversize: 20, dragDelay: 500, minValue: 0.1, unlockSide: "", @@ -45,7 +46,7 @@ return { value: entry.value.indexOf(settings[key]), min : 0, - max : entry.value.length-1, + max : entry.value.length - 1, wrap : true, format: v => entry.title ? entry.title[v] : entry.value[v], onchange: function(v) { @@ -57,11 +58,11 @@ // return entry for numerical value return { value: settings[key] * entry.factor, - step: entry.step, - format: v => v > 0 ? v + entry.unit : "off", min : entry.min, max : entry.max, + step: entry.step, wrap : true, + format: v => v > 0 ? v + entry.unit : "off", onchange: function(v) { writeSetting(key, v / entry.factor, entry.drawWidgets); }, @@ -96,6 +97,14 @@ value: ["", "clock", "clock,setting.app.js", "clock,launch", "clock,setting.app.js,launch", "always"], drawWidgets: true }, + oversize: { + factor: 1, + unit: "px", + min: 0, + max: 50, + step: 1, + drawWidgets: true + }, dragDelay: { factor: 1, unit: "ms", @@ -142,6 +151,7 @@ "Image": getEntry("image"), "-- Control": 0, "Touch": getEntry("touchOn"), + "Oversize": getEntry("oversize"), "Drag Delay": getEntry("dragDelay"), "Min Value": getEntry("minValue"), "-- Unlock": 0, diff --git a/apps/lightswitch/settings.json b/apps/lightswitch/settings.json index 3d88e2282..176c3cea0 100644 --- a/apps/lightswitch/settings.json +++ b/apps/lightswitch/settings.json @@ -10,8 +10,8 @@ "101" -> magenta * image: string // - "default" -> - "random" -> + "default" -> image nearest to the default lock + "random" -> a random image from all available * touchOn: string // select when widget touch is active "" -> only on default clock @@ -19,6 +19,9 @@ "clock,launch" -> on all clocks and lanchers (default) "always" -> always + * oversize: int // extends the touch area of the widget in px in all directions + 0 to 50, 20 as default + * dragDelay: int // drag listener reset time in ms // time until a drag is needed to activate backlight changing mode 0 -> disabled @@ -59,6 +62,7 @@ "colors": "011", "image": "default", "touchOn": "clock,launch", + "oversize": 20, "dragDelay": 500, "minValue": 0.1, "unlockSide": "", diff --git a/apps/lightswitch/widget.js b/apps/lightswitch/widget.js index 119a114fe..829f75102 100644 --- a/apps/lightswitch/widget.js +++ b/apps/lightswitch/widget.js @@ -3,7 +3,8 @@ var settings = Object.assign({ colors: "011", image: "default", - touchOn: "clock,launch", + touchOn: "always", + oversize: 20, dragDelay: 500, minValue: 0.1, unlockSide: "", @@ -162,6 +163,11 @@ // change brigthness value, skip write to storage while still touching w.changeValue(value, event.b); + // masks this drag event by messing up the event handler + // see https://github.com/espruino/Espruino/issues/2151 + Bangle.removeListener("drag", w.dragListener); + Bangle["#ondrag"] = [w.dragListener].concat(Bangle["#ondrag"]); + // on touch release remove drag listener and reset drag status to indicate stopped drag action if (!event.b) { Bangle.removeListener("drag", w.dragListener); @@ -184,14 +190,14 @@ if (w.dragStatus === "off") { // check if inside widget area - if (!(!w || cursor.x < w.x || cursor.x > w.x + w.width || - cursor.y < w.y || cursor.y > w.y + 23)) { + if (!(!w || cursor.x < w.x - w.oversize || cursor.x > w.x + w.width + w.oversize || + cursor.y < w.y - w.oversize || cursor.y > w.y + 23 + w.oversize)) { // first touch feedback Bangle.buzz(25); // check if drag is disabled if (w.dragDelay) { - // add drag listener - Bangle.on("drag", w.dragListener); + // add drag listener at first position + Bangle["#ondrag"] = [w.dragListener].concat(Bangle["#ondrag"]); // set drag timeout w.dragStatus = setTimeout((w) => { // remove drag listener @@ -204,6 +210,10 @@ } // switch backlight w.changeValue(); + // masks this touch event by messing up the event handler + // see https://github.com/espruino/Espruino/issues/2151 + Bangle.removeListener("touch", w.touchListener); + Bangle["#ontouch"] = [w.touchListener].concat(Bangle["#ontouch"]); } } @@ -236,11 +246,11 @@ // add lock listener Bangle.on("lock", w.draw); - // add touch listener to control the light depending on settings + // add touch listener to control the light depending on settings at first position if (w.touchOn === "always" || !global.__FILE__ || w.touchOn.includes(__FILE__) || w.touchOn.includes(require("Storage").readJSON(__FILE__.replace("app.js", "info")).type)) - Bangle.on("touch", w.touchListener); + Bangle["#ontouch"] = [w.touchListener].concat(Bangle["#ontouch"]); // add tap listener to unlock and/or flash backlight if (w.unlockSide || w.tapSide) Bangle.on("tap", require("lightswitch.js").tapListener); diff --git a/apps/locale/locales.js b/apps/locale/locales.js index 727007ab9..3add4d3ff 100644 --- a/apps/locale/locales.js +++ b/apps/locale/locales.js @@ -688,8 +688,8 @@ var locales = { datePattern: { 0: "%d. %b %Y", "1": "%d.%m.%Y" }, // 1. Mar 2020 // 01.03.20 abmonth: "Jan,Feb,Mar,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Des", month: "Januar,Februar,Mars,April,Mai,Juni,Juli,August,September,Oktober,November,Desember", - abday: "Ma,Ti,On,To,Fr,Lø,Sø", - day: "Mandag,Tirsdag,Onsdag,Torsdag,Fredag,Lørdag,Søndag", + abday: "Sø,Ma,Ti,On,To,Fr,Lø", + day: "Søndag,Mandag,Tirsdag,Onsdag,Torsdag,Fredag,Lørdag", trans: { yes: "ja", Yes: "Ja", no: "nei", No: "Nei", ok: "ok", on: "på", off: "av", "< Back": "< Tilbake", "Delete": "Slett", "Mark Unread": "Merk som ulest" } }, /*, diff --git a/apps/messages/ChangeLog b/apps/messages/ChangeLog index 092a38eb6..3e676c21e 100644 --- a/apps/messages/ChangeLog +++ b/apps/messages/ChangeLog @@ -43,3 +43,4 @@ 0.28: Option to auto-unlock the watch when a new message arrives 0.29: Fix message list overwrites on Bangle.js 1 (fix #1642) 0.30: Add new Icons (Youtube, Twitch, MS TODO, Teams, Snapchat, Signal, Post & DHL, Nina, Lieferando, Kalender, Discord, Corona Warn, Bibel) +0.31: Option to disable icon flashing diff --git a/apps/messages/README.md b/apps/messages/README.md index 780d8e50b..da2701f35 100644 --- a/apps/messages/README.md +++ b/apps/messages/README.md @@ -21,6 +21,7 @@ is chosen if there isn't much message text, but this specifies the smallest the it starts getting clipped. * `Auto-Open Music` - Should the app automatically open when the phone starts playing music? * `Unlock Watch` - Should the app unlock the watch when a new message arrives, so you can touch the buttons at the bottom of the app? +* `Flash Icon` - Toggle flashing of the widget icon. ## New Messages diff --git a/apps/messages/metadata.json b/apps/messages/metadata.json index dfeedaef7..5c1e67702 100644 --- a/apps/messages/metadata.json +++ b/apps/messages/metadata.json @@ -1,7 +1,7 @@ { "id": "messages", "name": "Messages", - "version": "0.30", + "version": "0.31", "description": "App to display notifications from iOS and Gadgetbridge/Android", "icon": "app.png", "type": "app", diff --git a/apps/messages/settings.js b/apps/messages/settings.js index 754347f19..cc0030ec5 100644 --- a/apps/messages/settings.js +++ b/apps/messages/settings.js @@ -7,6 +7,7 @@ settings.unlockWatch=!!settings.unlockWatch; settings.openMusic=!!settings.openMusic; settings.maxUnreadTimeout=240; + if (settings.flash===undefined) settings.flash=true; return settings; } function updateSetting(setting, value) { @@ -47,6 +48,11 @@ format: v => v?/*LANG*/'Yes':/*LANG*/'No', onchange: v => updateSetting("unlockWatch", v) }, + /*LANG*/'Flash Icon': { + value: !!settings().flash, + format: v => v?/*LANG*/'Yes':/*LANG*/'No', + onchange: v => updateSetting("flash", v) + }, }; E.showMenu(mainmenu); }) diff --git a/apps/messages/widget.js b/apps/messages/widget.js index 3ac726e77..4b368ffd6 100644 --- a/apps/messages/widget.js +++ b/apps/messages/widget.js @@ -1,5 +1,5 @@ WIDGETS["messages"]={area:"tl", width:0, iconwidth:24, -draw:function() { +draw:function(recall) { // If we had a setTimeout queued from the last time we were called, remove it if (WIDGETS["messages"].i) { clearTimeout(WIDGETS["messages"].i); @@ -8,15 +8,18 @@ draw:function() { Bangle.removeListener('touch', this.touch); if (!this.width) return; var c = (Date.now()-this.t)/1000; - g.reset().clearRect(this.x, this.y, this.x+this.width, this.y+this.iconwidth); - g.drawImage((c&1) ? atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+DAADDAADDAADDwAPD8A/DOBzDDn/DA//DAHvDAPvjAPvjAPvjAPvh///gf/vAAD+AAB8AAAAA==") : atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+D///D///A//8CP/xDj/HD48DD+B8D/D+D/3vD/vvj/vvj/vvj/vvh/v/gfnvAAD+AAB8AAAAA=="), this.x, this.y); let settings = require('Storage').readJSON("messages.settings.json", true) || {}; + if (settings.flash===undefined) settings.flash = true; + if (recall !== true || settings.flash) { + g.reset().clearRect(this.x, this.y, this.x+this.width, this.y+23); + g.drawImage(settings.flash && (c&1) ? atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+DAADDAADDAADDwAPD8A/DOBzDDn/DA//DAHvDAPvjAPvjAPvjAPvh///gf/vAAD+AAB8AAAAA==") : atob("GBiBAAAAAAAAAAAAAAAAAAAAAB//+D///D///A//8CP/xDj/HD48DD+B8D/D+D/3vD/vvj/vvj/vvj/vvh/v/gfnvAAD+AAB8AAAAA=="), this.x, this.y-1); + } if (settings.repeat===undefined) settings.repeat = 4; if (c<120 && (Date.now()-this.l)>settings.repeat*1000) { this.l = Date.now(); WIDGETS["messages"].buzz(); // buzz every 4 seconds } - WIDGETS["messages"].i=setTimeout(()=>WIDGETS["messages"].draw(), 1000); + WIDGETS["messages"].i=setTimeout(()=>WIDGETS["messages"].draw(true), 1000); if (process.env.HWVERSION>1) Bangle.on('touch', this.touch); },show:function(quiet) { WIDGETS["messages"].t=Date.now(); // first time diff --git a/apps/mylocation/ChangeLog b/apps/mylocation/ChangeLog index b9eba67f4..ab8af7620 100644 --- a/apps/mylocation/ChangeLog +++ b/apps/mylocation/ChangeLog @@ -1,3 +1,4 @@ 0.01: First release 0.02: Enhanced icon, make it bolder 0.03: Fixed issue with defaulting back to London +0.04: Fixed issue selecting Frankfurt not saved diff --git a/apps/mylocation/metadata.json b/apps/mylocation/metadata.json index a7fd8356c..9182ba160 100644 --- a/apps/mylocation/metadata.json +++ b/apps/mylocation/metadata.json @@ -4,7 +4,7 @@ "icon": "mylocation.png", "type": "app", "screenshots": [{"url":"screenshot_1.png"}], - "version":"0.03", + "version":"0.04", "description": "Sets and stores the lat and long of your preferred City or it can be set from the GPS. mylocation.json can be used by other apps that need your main location lat and lon. See README", "readme": "README.md", "tags": "tool,utility", diff --git a/apps/mylocation/mylocation.app.js b/apps/mylocation/mylocation.app.js index 27ab17ea5..b9451e0fb 100644 --- a/apps/mylocation/mylocation.app.js +++ b/apps/mylocation/mylocation.app.js @@ -61,7 +61,7 @@ function showMainMenu() { min: 0, max: locations.length - 1, format: v => locations[v], onchange: v => { - if (v != 6) { + if (locations[v] !== "???") { s.location = locations[v]; s.lat = lats[v]; s.lon = lons[v];