diff --git a/apps/agpsdata/ChangeLog b/apps/agpsdata/ChangeLog index ae26512de..89844a132 100644 --- a/apps/agpsdata/ChangeLog +++ b/apps/agpsdata/ChangeLog @@ -1,2 +1,4 @@ 0.01: First, proof of concept 0.02: Load AGPS data on app start and automatically in background +0.03: Do not load AGPS data on boot + Increase minimum interval to 6 hours diff --git a/apps/agpsdata/boot.js b/apps/agpsdata/boot.js index 6415f0b52..2b1e6819c 100644 --- a/apps/agpsdata/boot.js +++ b/apps/agpsdata/boot.js @@ -16,13 +16,6 @@ } if (settings.enabled) { - let lastUpdate = settings.lastUpdate; - if (!lastUpdate || lastUpdate + settings.refresh * 1000 * 60 < Date.now()){ - if (!waiting){ - waiting = true; - require("agpsdata").pull(successCallback, errorCallback); - } - } setInterval(() => { if (!waiting && NRF.getSecurityStatus().connected){ waiting = true; diff --git a/apps/agpsdata/metadata.json b/apps/agpsdata/metadata.json index e2f818d97..1ce299532 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.02", + "version":"0.03", "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/agpsdata/settings.js b/apps/agpsdata/settings.js index 80a2f3956..64fa25330 100644 --- a/apps/agpsdata/settings.js +++ b/apps/agpsdata/settings.js @@ -35,7 +35,7 @@ function buildMainMenu() { }, "Refresh every" : { value : settings.refresh / 60, - min : 1, + min : 6, max : 168, step : 1, format : v => v + "h", diff --git a/apps/android/ChangeLog b/apps/android/ChangeLog index 0cc7aedd4..a65326941 100644 --- a/apps/android/ChangeLog +++ b/apps/android/ChangeLog @@ -13,3 +13,4 @@ 0.13: Added Bangle.http function (see Readme file for more info) 0.14: Fix timeout of http function not being cleaned up 0.15: Allow method/body/headers to be specified for `http` (needs Gadgetbridge 0.68.0b or later) +0.16: Bangle.http now fails immediately if there is no Bluetooth connection (fix #2152) diff --git a/apps/android/boot.js b/apps/android/boot.js index bc8e3032d..0d1edae99 100644 --- a/apps/android/boot.js +++ b/apps/android/boot.js @@ -139,6 +139,8 @@ // options = {id,timeout,xpath} Bangle.http = (url,options)=>{ options = options||{}; + if (!NRF.getSecurityStatus().connected) + return Promise.reject("Not connected to Bluetooth"); if (Bangle.httpRequest === undefined) Bangle.httpRequest={}; if (options.id === undefined) { diff --git a/apps/android/metadata.json b/apps/android/metadata.json index 5d1b2f561..ab340340c 100644 --- a/apps/android/metadata.json +++ b/apps/android/metadata.json @@ -2,7 +2,7 @@ "id": "android", "name": "Android Integration", "shortName": "Android", - "version": "0.15", + "version": "0.16", "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/bigdclock/ChangeLog b/apps/bigdclock/ChangeLog index b373f1876..c92d139bb 100644 --- a/apps/bigdclock/ChangeLog +++ b/apps/bigdclock/ChangeLog @@ -4,3 +4,4 @@ 0.04: bug fix 0.05: proper fix for the race condition in queueDraw() 0.06: Tell clock widgets to hide. +0.07: Better battery graphic - now has green, yellow and red sections; battery status reflected in the bar across the middle of the screen; current battery state checked only once every 15 minutes, leading to longer-lasting battery charge diff --git a/apps/bigdclock/bigdclock.app.js b/apps/bigdclock/bigdclock.app.js index 3adf96984..a8e2b38df 100644 --- a/apps/bigdclock/bigdclock.app.js +++ b/apps/bigdclock/bigdclock.app.js @@ -11,6 +11,8 @@ Graphics.prototype.setFontOpenSans = function(scale) { }; var drawTimeout; +var lastBattCheck = 0; +var width = 0; function queueDraw(millis_now) { if (drawTimeout) clearTimeout(drawTimeout); @@ -24,12 +26,15 @@ function draw() { var date = new Date(); var h = date.getHours(), m = date.getMinutes(); - var d = date.getDate(), - w = date.getDay(); // d=1..31; w=0..6 - const level = E.getBattery(); - const width = level + (level/2); + var d = date.getDate(); var is12Hour = (require("Storage").readJSON("setting.json", 1) || {})["12hour"]; - var dows = require("date_utils").dows(0,1); + var dow = require("date_utils").dows(0,1)[date.getDay()]; + + if ((date.getTime() >= lastBattCheck + 15*60000) || Bangle.isCharging()) { + lastBattcheck = date.getTime(); + width = E.getBattery(); + width += width/2; + } g.reset(); g.clear(); @@ -47,24 +52,35 @@ function draw() { g.drawString(d, g.getWidth() -6, 98); g.setFont('Vector', 52); g.setFontAlign(-1, -1); - g.drawString(dows[w].slice(0,2).toUpperCase(), 6, 103); + g.drawString(dow.slice(0,2).toUpperCase(), 6, 103); g.fillRect(9,159,166,171); g.fillRect(167,163,170,167); if (Bangle.isCharging()) { g.setColor(1,1,0); - } else if (level > 40) { - g.setColor(0,1,0); + g.fillRect(12,162,12+width,168); } else { g.setColor(1,0,0); + g.fillRect(12,162,57,168); + g.setColor(1,1,0); + g.fillRect(58,162,72,168); + g.setColor(0,1,0); + g.fillRect(73,162,162,168); } - g.fillRect(12,162,12+width,168); - if (level < 100) { + if (width < 150) { g.setColor(g.theme.bg); g.fillRect(12+width+1,162,162,168); } - g.setColor(0, 1, 0); + if (Bangle.isCharging()) { + g.setColor(1,1,0); + } else if (width <= 45) { + g.setColor(1,0,0); + } else if (width <= 60) { + g.setColor(1,1,0); + } else { + g.setColor(0, 1, 0); + } g.fillRect(0, 90, g.getWidth(), 94); // widget redraw diff --git a/apps/bigdclock/metadata.json b/apps/bigdclock/metadata.json index ce91d921e..30352ca1a 100644 --- a/apps/bigdclock/metadata.json +++ b/apps/bigdclock/metadata.json @@ -1,7 +1,7 @@ { "id": "bigdclock", "name": "Big digit clock containing just the essentials", "shortName":"Big digit clk", - "version":"0.06", + "version":"0.07", "description": "A clock containing just the essentials, made as easy to read as possible for those of us that need glasses. It contains the time, the day-of-week, the day-of-month, and the current battery state-of-charge.", "icon": "bigdclock.png", "type": "clock", diff --git a/apps/bigdclock/screenshot.png b/apps/bigdclock/screenshot.png index 8a12b266e..acac53ea9 100644 Binary files a/apps/bigdclock/screenshot.png and b/apps/bigdclock/screenshot.png differ diff --git a/apps/messages/ChangeLog b/apps/messages/ChangeLog index 262cba1fa..da3b3ab5c 100644 --- a/apps/messages/ChangeLog +++ b/apps/messages/ChangeLog @@ -66,4 +66,8 @@ 0.50: Add `getMessages` and `status` functions to library Option to disable auto-open of messages Option to make message icons monochrome (not colored) - messages widget buzz now returns a promise \ No newline at end of file + messages widget buzz now returns a promise +0.51: Emit "message events" + Setting to hide widget + Add custom event handlers to prevent default app form loading + Move WIDGETS.messages.buzz() to require("messages").buzz() \ No newline at end of file diff --git a/apps/messages/README.md b/apps/messages/README.md index 2e583d1c2..72a989146 100644 --- a/apps/messages/README.md +++ b/apps/messages/README.md @@ -25,7 +25,7 @@ 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. -* `Widget messages` - The maximum amount of message icons to show on the widget. +* `Widget messages` - The maximum amount of message icons to show on the widget, or `Hide` the widget completely. ## New Messages @@ -56,6 +56,24 @@ _2. What the notify icon looks like (it's touchable on Bangle.js2!)_ ![](screenshot-notify.gif) +## Events (for app/widget developers) + +When a new message arrives, a `"message"` event is emitted, you can listen for +it like this: + +```js +myMessageListener = Bangle.on("message", (type, message)=>{ + if (message.handled) return; // another app already handled this message + // is one of "text", "call", "alarm", "map", "music", or "clearAll" + if (type === "clearAll") return; // not a message + // see `messages/lib.js` for possible formats + // message.t could be "add", "modify" or "remove" + E.showMessage(`${message.title}\n${message.body}`, `${message.t} ${type} message`); + // You can prevent the default `message` app from loading by setting `message.handled = true`: + message.handled = true; +}); +``` + ## Requests diff --git a/apps/messages/app.js b/apps/messages/app.js index 40dff9635..20fa8aaa3 100644 --- a/apps/messages/app.js +++ b/apps/messages/app.js @@ -54,8 +54,7 @@ var onMessagesModified = function(msg) { // TODO: if new, show this new one if (msg && msg.id!=="music" && msg.new && active!="map" && !((require('Storage').readJSON('setting.json', 1) || {}).quiet)) { - if (WIDGETS["messages"]) WIDGETS["messages"].buzz(msg.src); - else Bangle.buzz(); + require("messages").buzz(msg.src); } if (msg && msg.id=="music") { if (msg.state && msg.state!="play") openMusic = false; // no longer playing music to go back to @@ -356,13 +355,13 @@ function checkMessages(options) { // If we have a new message, show it if (options.showMsgIfUnread && newMessages.length) { showMessage(newMessages[0].id); - // buzz after showMessage, so beingbusy during layout doesn't affect the buzz pattern + // buzz after showMessage, so being busy during layout doesn't affect the buzz pattern if (global.BUZZ_ON_NEW_MESSAGE) { // this is set if we entered the messages app by loading `messages.new.js` // ... but only buzz the first time we view a new message global.BUZZ_ON_NEW_MESSAGE = false; // messages.buzz respects quiet mode - no need to check here - WIDGETS.messages.buzz(newMessages[0].src); + require("messages").buzz(newMessages[0].src); } return; } diff --git a/apps/messages/lib.js b/apps/messages/lib.js index d8599c93d..ed71ec04b 100644 --- a/apps/messages/lib.js +++ b/apps/messages/lib.js @@ -8,15 +8,11 @@ function openMusic() { /* Push a new message onto messages queue, event is: {t:"add",id:int, src,title,subject,body,sender,tel, important:bool, new:bool} {t:"add",id:int, id:"music", state, artist, track, etc} // add new - {t:"remove-",id:int} // remove + {t:"remove",id:int} // remove {t:"modify",id:int, title:string} // modified */ exports.pushMessage = function(event) { - var messages, inApp = "undefined"!=typeof MESSAGES; - if (inApp) - messages = MESSAGES; // we're in an app that has already loaded messages - else // no app - load messages - messages = require("Storage").readJSON("messages.json",1)||[]; + var messages = exports.getMessages(); // now modify/delete as appropriate var mIdx = messages.findIndex(m=>m.id==event.id); if (event.t=="remove") { @@ -35,69 +31,81 @@ exports.pushMessage = function(event) { else Object.assign(messages[mIdx], event); if (event.id=="music" && messages[mIdx].state=="play") { messages[mIdx].new = true; // new track, or playback (re)started + type = 'music'; } } require("Storage").writeJSON("messages.json",messages); + var message = mIdx<0 ? {id:event.id, t:'remove'} : messages[mIdx]; // if in app, process immediately - if (inApp) return onMessagesModified(mIdx<0 ? {id:event.id} : messages[mIdx]); + if ("undefined"!=typeof MESSAGES) return onMessagesModified(message); + // emit message event + var type = 'text'; + if (["call", "music", "map"].includes(message.id)) type = message.id; + if (message.src && message.src.toLowerCase().startsWith("alarm")) type = "alarm"; + Bangle.emit("message", type, message); // update the widget icons shown if (global.WIDGETS && WIDGETS.messages) WIDGETS.messages.update(messages,true); + var handleMessage = () => { // if no new messages now, make sure we don't load the messages app - if (event.t=="remove" && exports.messageTimeout && !messages.some(m=>m.new)) { - clearTimeout(exports.messageTimeout); - delete exports.messageTimeout; - } - // ok, saved now - if (event.id=="music" && Bangle.CLOCK && messages[mIdx].new && openMusic()) { - // just load the app to display music: no buzzing - load("messages.app.js"); - } else if (event.t!="add") { - // we only care if it's new - return; - } else if(event.new == false) { - return; - } - // otherwise load messages/show widget - var loadMessages = Bangle.CLOCK || event.important; - var quiet = (require('Storage').readJSON('setting.json',1)||{}).quiet; - var appSettings = require('Storage').readJSON('messages.settings.json',1)||{}; - var unlockWatch = appSettings.unlockWatch; - // don't auto-open messages in quiet mode if quietNoAutOpn is true - if((quiet && appSettings.quietNoAutOpn) || appSettings.noAutOpn) - loadMessages = false; - delete appSettings; - // after a delay load the app, to ensure we have all the messages - if (exports.messageTimeout) clearTimeout(exports.messageTimeout); - exports.messageTimeout = setTimeout(function() { - exports.messageTimeout = undefined; - // if we're in a clock or it's important, go straight to messages app - if (loadMessages){ - if(!quiet && unlockWatch){ - Bangle.setLocked(false); - Bangle.setLCDPower(1); // turn screen on - } - // we will buzz when we enter the messages app - return load("messages.new.js"); + if (event.t=="remove" && exports.messageTimeout && !messages.some(m => m.new)) { + clearTimeout(exports.messageTimeout); + delete exports.messageTimeout; } - if (!quiet && (!global.WIDGETS || !WIDGETS.messages)) return Bangle.buzz(); // no widgets - just buzz once to let someone know - if (global.WIDGETS && WIDGETS.messages) WIDGETS.messages.update(messages); - }, 500); + // ok, saved now + if (event.id=="music" && Bangle.CLOCK && messages[mIdx].new && openMusic()) { + // just load the app to display music: no buzzing + load("messages.app.js"); + } else if (event.t!="add") { + // we only care if it's new + return; + } else if (event.new==false) { + return; + } + // otherwise load messages/show widget + var loadMessages = Bangle.CLOCK || event.important; + var quiet = (require('Storage').readJSON('setting.json', 1) || {}).quiet; + var appSettings = require('Storage').readJSON('messages.settings.json', 1) || {}; + var unlockWatch = appSettings.unlockWatch; + // don't auto-open messages in quiet mode if quietNoAutOpn is true + if ((quiet && appSettings.quietNoAutOpn) || appSettings.noAutOpn) + loadMessages = false; + delete appSettings; + // after a delay load the app, to ensure we have all the messages + if (exports.messageTimeout) clearTimeout(exports.messageTimeout); + exports.messageTimeout = setTimeout(function() { + exports.messageTimeout = undefined; + // if we're in a clock or it's important, go straight to messages app + if (loadMessages) { + if (!quiet && unlockWatch) { + Bangle.setLocked(false); + Bangle.setLCDPower(1); // turn screen on + } + // we will buzz when we enter the messages app + return load("messages.new.js"); + } + if (global.WIDGETS && WIDGETS.messages) WIDGETS.messages.update(messages); + exports.buzz(message.src); + }, 500); + }; + setTimeout(()=>{ + if (!message.handled) handleMessage(); + },0); } /// Remove all messages -exports.clearAll = function(event) { - var messages, inApp = "undefined"!=typeof MESSAGES; - if (inApp) { +exports.clearAll = function() { + if ("undefined"!= typeof MESSAGES) { // we're in a messages app, clear that as well MESSAGES = []; - messages = MESSAGES; // we're in an app that has already loaded messages - } else // no app - empty messages - messages = []; - // Save all messages - require("Storage").writeJSON("messages.json",messages); - // update app if in app - if (inApp) return onMessagesModified(); + } + // Clear all messages + require("Storage").writeJSON("messages.json", []); // if we have a widget, update it if (global.WIDGETS && WIDGETS.messages) - WIDGETS.messages.update(messages); + WIDGETS.messages.update([]); + // let message listeners know + Bangle.emit("message", "clearAll", {}); // guarantee listeners an object as `message` + // clearAll cannot be marked as "handled" + // update app if in app + if ("function"== typeof onMessagesModified) onMessagesModified(); } /** @@ -126,6 +134,45 @@ exports.getMessages = function() { } }; +/** + * Start buzzing for new message + * @param {string} msgSrc Message src to buzz for + * @return {Promise} Resolves when initial buzz finishes (there might be repeat buzzes later) + */ +exports.buzz = function(msgSrc) { + exports.stopBuzz(); // cancel any previous buzz timeouts + if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return Promise.resolve(); // never buzz during Quiet Mode + + var pattern; + if (msgSrc && msgSrc.toLowerCase() === "phone") { + // special vibration pattern for incoming calls + pattern = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrateCalls; + } else { + pattern = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrate; + } + if (pattern === undefined) { pattern = ":"; } // pattern may be "", so we can't use || ":" here + if (!pattern) return Promise.resolve(); + + var repeat = (require('Storage').readJSON("messages.settings.json", true) || {}).repeat; + if (repeat===undefined) repeat=4; // repeat may be zero + if (repeat) { + exports.buzzTimeout = setTimeout(()=>require("buzz").pattern(pattern), repeat*1000); + var vibrateTimeout = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrateTimeout; + if (vibrateTimeout===undefined) vibrateTimeout=60; + if (vibrateTimeout && !exports.stopTimeout) exports.stopTimeout = setTimeout(exports.stopTimeout, vibrateTimeout*1000); + } + return require("buzz").pattern(pattern); +}; +/** + * Stop buzzing + */ +exports.stopBuzz = function() { + if (exports.buzzTimeout) clearTimeout(exports.buzzTimeout); + delete exports.buzzTimeout; + if (exports.stopTimeout) clearTimeout(exports.stopTimeout); + delete exports.stopTimeout; +}; + exports.getMessageImage = function(msg) { /* * icons should be 24x24px or less with 1bpp colors and 'Transparency to Color' diff --git a/apps/messages/metadata.json b/apps/messages/metadata.json index da2e0945a..057a95026 100644 --- a/apps/messages/metadata.json +++ b/apps/messages/metadata.json @@ -1,7 +1,7 @@ { "id": "messages", "name": "Messages", - "version": "0.50", + "version": "0.51", "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 0edb17797..09c9db455 100644 --- a/apps/messages/settings.js +++ b/apps/messages/settings.js @@ -73,7 +73,8 @@ }, /*LANG*/'Widget messages': { value:0|settings().maxMessages, - min: 1, max: 5, + min: 0, max: 5, + format: v => v ? v :/*LANG*/"Hide", onchange: v => updateSetting("maxMessages", v) }, /*LANG*/'Icon color mode': { diff --git a/apps/messages/widget.js b/apps/messages/widget.js index a5e1f8b6c..c8d132f82 100644 --- a/apps/messages/widget.js +++ b/apps/messages/widget.js @@ -1,4 +1,5 @@ (() => { +if ((require('Storage').readJSON("messages.settings.json", true) || {}).maxMessages===0) return; function filterMessages(msgs) { return msgs.filter(msg => msg.new && msg.id != "music") @@ -14,15 +15,14 @@ WIDGETS["messages"]={area:"tl", width:0, draw:function(recall) { } Bangle.removeListener('touch', this.touch); if (!this.width) return; - var c = (Date.now()-this.t)/1000; - let settings = Object.assign({flash:true, maxMessages:3, repeat:4, vibrateTimeout:60},require('Storage').readJSON("messages.settings.json", true) || {}); + let settings = Object.assign({flash:true, maxMessages:3},require('Storage').readJSON("messages.settings.json", true) || {}); if (recall !== true || settings.flash) { var msgsShown = E.clip(this.msgs.length, 0, settings.maxMessages); g.reset().clearRect(this.x, this.y, this.x+this.width, this.y+23); for(let i = 0;i < msgsShown;i++) { const msg = this.msgs[i]; const colors = [g.theme.bg, g.setColor(require("messages").getMessageImageCol(msg)).getColor()]; - if (settings.flash && (c&1)) { + if (settings.flash && ((Date.now()/1000)&1)) { if (colors[1] == g.theme.fg) { colors.reverse(); } else { @@ -35,38 +35,13 @@ WIDGETS["messages"]={area:"tl", width:0, draw:function(recall) { this.x + 12 + i * 24, this.y + 12, {rotate:0/*force centering*/}); } } - if (csettings.repeat*1000) { // the period between vibrations - this.l = Date.now(); - WIDGETS["messages"].buzz(); // buzz every 4 seconds - } WIDGETS["messages"].i=setTimeout(()=>WIDGETS["messages"].draw(true), 1000); if (process.env.HWVERSION>1) Bangle.on('touch', this.touch); -},update:function(rawMsgs, quiet) { +},update:function(rawMsgs) { const settings = Object.assign({maxMessages:3},require('Storage').readJSON("messages.settings.json", true) || {}); this.msgs = filterMessages(rawMsgs); - if (this.msgs.length === 0) { - delete this.t; - delete this.l; - } else { - this.t=Date.now(); // first time - this.l=Date.now()-10000; // last buzz - if (quiet) this.t -= 500000; // if quiet, set last time in the past so there is no buzzing - } this.width = 24 * E.clip(this.msgs.length, 0, settings.maxMessages); Bangle.drawWidgets(); -},buzz:function(msgSrc) { // return a promise - if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return Promise.resolve(); // never buzz during Quiet Mode - var pattern; - if (msgSrc != undefined && msgSrc.toLowerCase() == "phone") { - // special vibration pattern for incoming calls - pattern = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrateCalls; - } else { - pattern = (require('Storage').readJSON("messages.settings.json", true) || {}).vibrate; - } - if (pattern === undefined) { pattern = ":"; } // pattern may be "", so we can't use || ":" here - return require("buzz").pattern(pattern); },touch:function(b,c) { var w=WIDGETS["messages"]; if (!w||!w.width||c.xw.x+w.width||c.yw.y+24) return; @@ -74,8 +49,7 @@ WIDGETS["messages"]={area:"tl", width:0, draw:function(recall) { }}; /* We might have returned here if we were in the Messages app for a -message but then the watch was never viewed. In that case we don't -want to buzz but should still show that there are unread messages. */ +message but then the watch was never viewed. */ if (global.MESSAGES===undefined) - WIDGETS["messages"].update(require("messages").getMessages(), true); + WIDGETS["messages"].update(require("messages").getMessages()); })(); diff --git a/apps/powersave/ChangeLog b/apps/powersave/ChangeLog new file mode 100644 index 000000000..a0e6da646 --- /dev/null +++ b/apps/powersave/ChangeLog @@ -0,0 +1,2 @@ +0.01: Initial release +0.02: Removed accelerometer poll interval adjustment, fixed a few issues with detecting the current app \ No newline at end of file diff --git a/apps/powersave/README.md b/apps/powersave/README.md new file mode 100644 index 000000000..51ba044e1 --- /dev/null +++ b/apps/powersave/README.md @@ -0,0 +1,25 @@ +# Power Saver + +Save your watch's battery power by halting foreground app execution while the screen is off. + +## Features +- Stops foreground app processes +- Background processes still run +- Clears screen +- Foreground app is returned to when screen is turned back on (app state is not preserved) + +## Controls +- Automatically activates when screen times out, timing can be adjusted using normal timeout settings +- Deactivates when screen is turned back on + +## Warnings +- This is not compatible with apps that need to run in the foreground even while the screen is off, such as most stopwatch apps and some health trackers. +- If you check your watch super often (like multiple times per minute), this may end of costing you more power than it saves since the app you are using will have to restart everytime you check it. + +## Requests + +[Contact information is on my website](https://kyleplo.com/#contact) + +## Creator + +[kyleplo](https://kyleplo.com) \ No newline at end of file diff --git a/apps/powersave/boot.js b/apps/powersave/boot.js new file mode 100644 index 000000000..5c37dcc56 --- /dev/null +++ b/apps/powersave/boot.js @@ -0,0 +1,20 @@ +var Storage = Storage || require("Storage"); +Bangle.on("lock", locked => { + if(locked){ + load("powersave.screen.js"); + }else{ + const data = JSON.parse(Storage.read("powersave.json") || Storage.read("setting.json")); + load(data.app || data.clock); + } +}); +E.on("init", () => { + if("__FILE__" in global && __FILE__ !== "powersave.screen.js"){ + Storage.write("powersave.json", { + app: __FILE__ + }); + }else{ + Storage.write("powersave.json", { + app: null + }); + } +}); \ No newline at end of file diff --git a/apps/powersave/metadata.json b/apps/powersave/metadata.json new file mode 100644 index 000000000..cb2ad9456 --- /dev/null +++ b/apps/powersave/metadata.json @@ -0,0 +1,18 @@ +{ + "id": "powersave", + "name": "Power Save", + "version": "0.02", + "description": "Halts foreground app execution while screen is off while still allowing background processes.", + "readme": "README.md", + "icon": "powersave.png", + "type": "bootloader", + "tags": "tool", + "supports": ["BANGLEJS2"], + "storage": [ + {"name":"powersave.boot.js","url":"boot.js"}, + {"name":"powersave.screen.js","url":"boot.js"} + ], + "data": [ + {"name": "powersave.json"} + ] +} \ No newline at end of file diff --git a/apps/powersave/powersave.png b/apps/powersave/powersave.png new file mode 100644 index 000000000..fa0399b73 Binary files /dev/null and b/apps/powersave/powersave.png differ diff --git a/apps/powersave/screen.js b/apps/powersave/screen.js new file mode 100644 index 000000000..c920b205d --- /dev/null +++ b/apps/powersave/screen.js @@ -0,0 +1,7 @@ +g.clear(); +Bangle.setLCDBrightness(0); +if(!Bangle.isLocked()){ + var Storage = Storage || require("Storage"); + const data = JSON.parse(Storage.read("powersave.json") || Storage.read("setting.json")); + load(data.app || data.clock); +} \ No newline at end of file diff --git a/apps/twenties/ChangeLog b/apps/twenties/ChangeLog new file mode 100644 index 000000000..89eb9547f --- /dev/null +++ b/apps/twenties/ChangeLog @@ -0,0 +1,4 @@ +0.01: New Widget! +0.02: Fix calling null on draw +0.03: Only vibrate during work +0.04: Convert to boot code \ No newline at end of file diff --git a/apps/twenties/README.md b/apps/twenties/README.md new file mode 100644 index 000000000..8ee917b0e --- /dev/null +++ b/apps/twenties/README.md @@ -0,0 +1,17 @@ +# Twenties + +Follow the [20-20-20 rule](https://www.aoa.org/AOA/Images/Patients/Eye%20Conditions/20-20-20-rule.pdf) with discrete reminders. Your Bangle will buzz every 20 minutes for you to look away from your screen, and then buzz 20 seconds later to look back. Additionally, alternate between standing and sitting every 20 minutes to be standing for [more than 30 minutes](https://uwaterloo.ca/kinesiology-health-sciences/how-long-should-you-stand-rather-sit-your-work-station) per hour. + +## Usage + +Download this app and it will automatically run in the background. + +## Features + +Vibrates to remind you to stand up and look away for healthy living. + +Only vibrates during work days and hours. + +## Creator + +[@splch](https://github.com/splch/) diff --git a/apps/widtwenties/widget.png b/apps/twenties/app.png similarity index 100% rename from apps/widtwenties/widget.png rename to apps/twenties/app.png diff --git a/apps/twenties/boot.js b/apps/twenties/boot.js new file mode 100644 index 000000000..722af43bc --- /dev/null +++ b/apps/twenties/boot.js @@ -0,0 +1,19 @@ +(() => { + const move = 20 * 60 * 1000; // 20 minutes + const look = 20 * 1000; // 20 seconds + + const buzz = _ => { + const date = new Date(); + const day = date.getDay(); + const hour = date.getHours(); + // buzz at work + if (day >= 1 && day <= 5 && + hour >= 8 && hour <= 17) { + Bangle.buzz().then(_ => { + setTimeout(Bangle.buzz, look); + }); + } + }; + + setInterval(buzz, move); // buzz to stand / sit +})(); diff --git a/apps/widtwenties/metadata.json b/apps/twenties/metadata.json similarity index 52% rename from apps/widtwenties/metadata.json rename to apps/twenties/metadata.json index 5695a0ed9..b1dfe2134 100644 --- a/apps/widtwenties/metadata.json +++ b/apps/twenties/metadata.json @@ -1,13 +1,13 @@ { - "id": "widtwenties", + "id": "twenties", "name": "Twenties", "shortName": "twenties", - "version": "0.03", + "version": "0.04", "description": "Buzzes every 20m to stand / sit and look 20ft away for 20s.", - "icon": "widget.png", - "type": "widget", - "tags": "widget,tools", + "icon": "app.png", + "type": "bootloader", + "tags": "alarm,tool", "supports": ["BANGLEJS", "BANGLEJS2"], "readme": "README.md", - "storage": [{ "name": "widtwenties.wid.js", "url": "widget.js" }] + "storage": [{ "name": "twenties.boot.js", "url": "boot.js" }] } diff --git a/apps/widtwenties/ChangeLog b/apps/widtwenties/ChangeLog deleted file mode 100644 index c2fbd70a2..000000000 --- a/apps/widtwenties/ChangeLog +++ /dev/null @@ -1,3 +0,0 @@ -0.01: New Widget! -0.02: Fix calling null on draw -0.03: Only vibrate during work \ No newline at end of file diff --git a/apps/widtwenties/README.md b/apps/widtwenties/README.md deleted file mode 100644 index 1dea18b8e..000000000 --- a/apps/widtwenties/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# Twenties - -Follow the [20-20-20 rule](https://www.aoa.org/AOA/Images/Patients/Eye%20Conditions/20-20-20-rule.pdf) with discrete reminders. Your BangleJS will buzz every 20 minutes for you to look away from your screen, and then buzz 20 seconds later to look back. Additionally, alternate between standing and sitting every 20 minutes to be standing for [more than 30 minutes](https://uwaterloo.ca/kinesiology-health-sciences/how-long-should-you-stand-rather-sit-your-work-station) per hour. - -## Usage - -Download this widget and, as long as your watch-face supports widgets, it will automatically run in the background. - -## Features - -Vibrate to remind you to stand up and look away for healthy living. - -## Creator - -[@splch](https://github.com/splch/) diff --git a/apps/widtwenties/widget.js b/apps/widtwenties/widget.js deleted file mode 100644 index f722f8d03..000000000 --- a/apps/widtwenties/widget.js +++ /dev/null @@ -1,30 +0,0 @@ -// WIDGETS = {}; // <-- for development only - -/* run widgets in their own function scope so -they don't interfere with currently-running apps */ -(() => { - const move = 20 * 60 * 1000; // 20 minutes - const look = 20 * 1000; // 20 seconds - - buzz = _ => { - const date = new Date(); - const day = date.getDay(); - const hour = date.getHours(); - // buzz at work - if (day <= 5 && hour >= 8 && hour <= 17) { - Bangle.buzz().then(_ => { - setTimeout(Bangle.buzz, look); - }); - } - }; - - // add widget - WIDGETS.twenties = { - buzz: buzz, - draw: _ => { return null; }, - }; - - setInterval(WIDGETS.twenties.buzz, move); // buzz to stand / sit -})(); - -// Bangle.drawWidgets(); // <-- for development only