Merge branch 'rigrig-split-message-library'

pull/2320/head
Gordon Williams 2022-11-28 15:03:54 +00:00
commit e3ef0b906d
41 changed files with 642 additions and 369 deletions

View File

@ -15,4 +15,5 @@
0.15: Allow method/body/headers to be specified for `http` (needs Gadgetbridge 0.68.0b or later) 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) 0.16: Bangle.http now fails immediately if there is no Bluetooth connection (fix #2152)
0.17: Now kick off Calendar sync as soon as connected to Gadgetbridge 0.17: Now kick off Calendar sync as soon as connected to Gadgetbridge
0.18: If connected to Gadgetbridge, allow GPS forwarding from phone (Gadgetbridge code still not merged) 0.18: Use new message library
If connected to Gadgetbridge, allow GPS forwarding from phone (Gadgetbridge code still not merged)

View File

@ -6,7 +6,7 @@
"description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.", "description": "Display notifications/music/etc sent from the Gadgetbridge app on Android. This replaces the old 'Gadgetbridge' Bangle.js widget.",
"icon": "app.png", "icon": "app.png",
"tags": "tool,system,messages,notifications,gadgetbridge", "tags": "tool,system,messages,notifications,gadgetbridge",
"dependencies": {"messages":"app"}, "dependencies": {"messages":"module"},
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [

View File

@ -36,7 +36,7 @@
updateSettings(); updateSettings();
} }
}, },
/*LANG*/"Messages" : ()=>load("messages.app.js"), /*LANG*/"Messages" : ()=>require("message").openGUI(),
}; };
E.showMenu(mainmenu); E.showMenu(mainmenu);
}) })

View File

@ -2,3 +2,4 @@
0.02: Saved settings when switching color scheme 0.02: Saved settings when switching color scheme
0.03: Added Button 3 opening messages (if app is installed) 0.03: Added Button 3 opening messages (if app is installed)
0.04: Use `messages` library to check for new messages 0.04: Use `messages` library to check for new messages
0.05: Use `messages` library to open message GUI

View File

@ -234,7 +234,7 @@ function handleMessages()
{ {
if(!hasMessages()) return; if(!hasMessages()) return;
E.showMessage("Loading Messages..."); E.showMessage("Loading Messages...");
load("messages.app.js"); require("messages").openGUI();
} }
function hasMessages() function hasMessages()

View File

@ -1,7 +1,7 @@
{ {
"id": "hcclock", "id": "hcclock",
"name": "Hi-Contrast Clock", "name": "Hi-Contrast Clock",
"version": "0.04", "version": "0.05",
"description": "Hi-Contrast Clock : A simple yet very bold clock that aims to be readable in high luninosity environments. Uses big 10x5 pixel digits. Use BTN 1 to switch background and foreground colors.", "description": "Hi-Contrast Clock : A simple yet very bold clock that aims to be readable in high luninosity environments. Uses big 10x5 pixel digits. Use BTN 1 to switch background and foreground colors.",
"icon": "hcclock-icon.png", "icon": "hcclock-icon.png",
"type": "clock", "type": "clock",

View File

@ -9,3 +9,4 @@
0.09: Enable 'ams' on new firmwares (ams/ancs can now be enabled individually) (fix #1365) 0.09: Enable 'ams' on new firmwares (ams/ancs can now be enabled individually) (fix #1365)
0.10: Added more bundleIds 0.10: Added more bundleIds
0.11: Added letters with caron to unicodeRemap, to properly display messages in Czech language 0.11: Added letters with caron to unicodeRemap, to properly display messages in Czech language
0.12: Use new message library

View File

@ -1,2 +1,2 @@
// Config app not implemented yet // Config app not implemented yet
setTimeout(()=>load("messages.app.js"),10); setTimeout(()=>require("messages").openGUI(),10);

View File

@ -1,11 +1,11 @@
{ {
"id": "ios", "id": "ios",
"name": "iOS Integration", "name": "iOS Integration",
"version": "0.11", "version": "0.12",
"description": "Display notifications/music/etc from iOS devices", "description": "Display notifications/music/etc from iOS devices",
"icon": "app.png", "icon": "app.png",
"tags": "tool,system,ios,apple,messages,notifications", "tags": "tool,system,ios,apple,messages,notifications",
"dependencies": {"messages":"app"}, "dependencies": {"messages":"module"},
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [

79
apps/messagegui/ChangeLog Normal file
View File

@ -0,0 +1,79 @@
0.01: New App!
0.02: Add 'messages' library
0.03: Fixes for Bangle.js 1
0.04: Add require("messages").clearAll()
0.05: Handling of message actions (ok/clear)
0.06: New messages now go at the start (fix #898)
Answering true/false now exits the messages app if no new messages
Back now marks a message as read
Clicking top-left opens a menu which allows you to delete a message or mark unread
0.07: Added settings menu with option to choose vibrate pattern and frequency (fix #909)
0.08: Fix rendering of long messages (fix #969)
buzz on new message (fix #999)
0.09: Message now disappears after 60s if no action taken and clock loads (fix 922)
Fix phone icon (#1014)
0.10: Respect the 'new' attribute if it was set from iOS integrations
0.11: Open app when touching the widget (Bangle.js 2 only)
0.12: Extra app-specific notification icons
New animated notification icon (instead of large blinking 'MESSAGES')
Added screenshots
0.13: Add /*LANG*/ comments for internationalisation
Add 'Delete All' option to message options
Now update correctly when 'require("messages").clearAll()' is called
0.14: Hide widget when all unread notifications are dismissed from phone
0.15: Don't buzz when Quiet Mode is active
0.16: Fix text wrapping so it fits the screen even if title is big (fix #1147)
0.17: Fix: Get dynamic dimensions of notify icon, fixed notification font
0.18: Use app-specific icon colors
Spread message action buttons out
Back button now goes back to list of messages
If showMessage called with no message (eg all messages deleted) now return to the clock (fix #1267)
0.19: Use a larger font for message text if it'll fit
0.20: Allow tapping on the body to show a scrollable view of the message and title in a bigger font (fix #1405, #1031)
0.21: Improve list readability on dark theme
0.22: Add Home Assistant icon
Allow repeat to be switched Off, so there is no buzzing repetition.
Also gave the widget a pixel more room to the right
0.23: Change message colors to match current theme instead of using green
Now attempt to use Large/Big/Medium fonts, and allow minimum font size to be configured
0.24: Remove left-over debug statement
0.25: Fix widget memory usage issues if message received and watch repeatedly calls Bangle.drawWidgets (fix #1550)
0.26: Setting to auto-open music
0.27: Add 'mark all read' option to popup menu (fix #1624)
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
0.32: Added an option to allow quiet mode to override message auto-open
0.33: Timeout from the message list screen if the message being displayed is removed and there is a timer going
0.34: Don't buzz for 'map' update messages
0.35: Reset graphics colors before rendering a message (possibly fix #1752)
0.36: Ensure a new message plus an almost immediate deletion of that message doesn't load the messages app (fix #1362)
0.37: Now use the setUI 'back' icon in the top left rather than specific buttons/menu items
0.38: Add telegram foss handling
0.39: Set default color for message icons according to theme
0.40: Use default Bangle formatter for booleans
0.41: Add notification icons in the widget
0.42: Fix messages ignoring "Vibrate: Off" setting
0.43: Add new Icons (Airbnb, warnwetter)
0.44: Separate buzz pattern for incoming calls
0.45: Added new app colors and icons
0.46: Add 'Vibrate Timer' option to set how long to vibrate for, and fix Repeat:off
Fix message removal from widget bar (previously caused exception as .hide has been removed)
0.47: Add new Icons (Nextbike, Mattermost, etc.)
0.48: When getting new message from the clock, only buzz once the messages app is loaded
0.49: Change messages icon (to fit within 24px) and ensure widget renders icons centrally
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
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()
0.52: Fix require("messages").buzz() regression
Fix background color in messages list after one unread message is shown
0.53: Messages now uses Bangle.load() to load messages app faster (if possible)
0.54: Move icons out to messageicons module
0.55: Rename to messagegui, move global message handling library to message module
Move widget to widmessage

68
apps/messagegui/README.md Normal file
View File

@ -0,0 +1,68 @@
# Messages app
Default app to handle the display of messages and message notifications. It allows
them to be listed, viewed, and responded to.
It is installed automatically if you install `Android Integration` or `iOS Integration`.
It is a replacement for the old `notify`/`gadgetbridge` apps.
## Settings
You can change settings by going to the global `Settings` app, then `App Settings`
and `Messages`:
* `Vibrate` - This is the pattern of buzzes that should be made when a new message is received
* `Vibrate for calls` - This is the pattern of buzzes that should be made when an incoming call is received
* `Repeat` - How often should buzzes repeat - the default of 4 means the Bangle will buzz every 4 seconds
* `Vibrate Timer` - When a new message is received when in a non-clock app, we display the message icon and
buzz every `Repeat` seconds. This is how long we continue to do that.
* `Unread Timer` - When a new message is received when showing the clock we go into the Messages app.
If there is no user input for this amount of time then the app will exit and return
to the clock where a ringing bell will be shown in the Widget bar.
* `Min Font` - The minimum font size used when displaying messages on the screen. A bigger font
is chosen if there isn't much message text, but this specifies the smallest the font should get before
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?
## New Messages
When a new message is received:
* If you're in an app, the Bangle will buzz and a message icon appears in the Widget bar. You can tap this icon to view the message.
* If you're in a clock, the Messages app will automatically start and show the message
When a message is shown, you'll see a screen showing the message title and text.
* The 'back-arrow' button (or physical button on Bangle.js 2) goes back to Messages, marking the current message as read.
* The top-left icon shows more options, for instance deleting the message of marking unread
* On Bangle.js 2 you can tap on the message body to view a scrollable version of the title and text (or can use the top-left icon + `View Message`)
* If shown, the 'tick' button:
* **Android** opens the notification on the phone
* **iOS** responds positively to the notification (accept call/etc)
* If shown, the 'cross' button:
* **Android** dismisses the notification on the phone
* **iOS** responds negatively to the notification (dismiss call/etc)
## Images
_1. Screenshot of a notification_
![](screenshot.png)
## Requests
Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=messages%20app
## Creator
Gordon Williams
## Contributors
[Jeroen Peters](https://github.com/jeroenpeters1986)
## Attributions
Icons used in this app are from https://icons8.com

View File

@ -1,5 +1,5 @@
/* Called when we have a new message when we're in the clock... /* Called when we have a new message when we're in the clock...
BUZZ_ON_NEW_MESSAGE is set so when messages.app.js loads it knows BUZZ_ON_NEW_MESSAGE is set so when messagegui.app.js loads it knows
that it should buzz */ that it should buzz */
global.BUZZ_ON_NEW_MESSAGE = true; global.BUZZ_ON_NEW_MESSAGE = true;
eval(require("Storage").read("messages.app.js")); eval(require("Storage").read("messagegui.app.js"));

View File

@ -19,7 +19,6 @@ require("messages").pushMessage({"t":"add","id":1,"src":"Maps","title":"0 yd - H
// call // call
require("messages").pushMessage({"t":"add","id":"call","src":"Phone","title":"Bob","body":"12421312",positive:true,negative:true}) require("messages").pushMessage({"t":"add","id":"call","src":"Phone","title":"Bob","body":"12421312",positive:true,negative:true})
*/ */
var Layout = require("Layout"); var Layout = require("Layout");
var settings = require('Storage').readJSON("messages.settings.json", true) || {}; var settings = require('Storage').readJSON("messages.settings.json", true) || {};
var fontSmall = "6x8"; var fontSmall = "6x8";
@ -49,8 +48,11 @@ to the clock. */
var unreadTimeout; var unreadTimeout;
/// List of all our messages /// List of all our messages
var MESSAGES = require("messages").getMessages(); var MESSAGES = require("messages").getMessages();
if (!Array.isArray(MESSAGES)) MESSAGES=[];
var onMessagesModified = function(msg) { var onMessagesModified = function(type,msg) {
if (msg.handled) return;
msg.handled = true;
require("messages").apply(msg, MESSAGES);
// TODO: if new, show this new one // TODO: if new, show this new one
if (msg && msg.id!=="music" && msg.new && active!="map" && if (msg && msg.id!=="music" && msg.new && active!="map" &&
!((require('Storage').readJSON('setting.json', 1) || {}).quiet)) { !((require('Storage').readJSON('setting.json', 1) || {}).quiet)) {
@ -62,9 +64,15 @@ var onMessagesModified = function(msg) {
} }
showMessage(msg&&msg.id); showMessage(msg&&msg.id);
}; };
Bangle.on("message", onMessagesModified);
function saveMessages() { function saveMessages() {
require("Storage").writeJSON("messages.json",MESSAGES) require("messages").write(MESSAGES.map(m => {
delete m.show;
return m;
}));
} }
E.on("kill", saveMessages);
function showMapMessage(msg) { function showMapMessage(msg) {
active = "map"; active = "map";
@ -355,12 +363,16 @@ function checkMessages(options) {
} }
// we have >0 messages // we have >0 messages
var newMessages = MESSAGES.filter(m=>m.new&&m.id!="music"); var newMessages = MESSAGES.filter(m=>m.new&&m.id!="music");
var toShow = MESSAGES.find(m=>m.show);
if (toShow) {
newMessages.unshift(toShow);
}
// If we have a new message, show it // If we have a new message, show it
if (options.showMsgIfUnread && newMessages.length) { if ((toShow||options.showMsgIfUnread) && newMessages.length) {
showMessage(newMessages[0].id); showMessage(newMessages[0].id);
// buzz after showMessage, so being busy 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) { if (global.BUZZ_ON_NEW_MESSAGE) {
// this is set if we entered the messages app by loading `messages.new.js` // this is set if we entered the messages app by loading `messagegui.new.js`
// ... but only buzz the first time we view a new message // ... but only buzz the first time we view a new message
global.BUZZ_ON_NEW_MESSAGE = false; global.BUZZ_ON_NEW_MESSAGE = false;
// messages.buzz respects quiet mode - no need to check here // messages.buzz respects quiet mode - no need to check here
@ -428,6 +440,7 @@ function cancelReloadTimeout() {
g.clear(); g.clear();
Bangle.loadWidgets(); Bangle.loadWidgets();
require("messages").toggleWidget(false);
Bangle.drawWidgets(); Bangle.drawWidgets();
setTimeout(() => { setTimeout(() => {

BIN
apps/messagegui/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

3
apps/messagegui/boot.js Normal file
View File

@ -0,0 +1,3 @@
(function() {
Bangle.on("message", (type, msg) => require("messagegui").listener(type, msg));
})();

60
apps/messagegui/lib.js Normal file
View File

@ -0,0 +1,60 @@
/**
* Listener set up in boot.js, calls into here to keep boot.js short
*/
exports.listener = function(type, msg) {
// Default handler: Launch the GUI for all unhandled messages (except music if disabled in settings)
if (msg.handled || (global.__FILE__ && __FILE__.startsWith('messagegui.'))) return; // already handled or app open
// if no new messages now, make sure we don't load the messages app
if (exports.messageTimeout && !msg.new && require("messages").status(msg) !== "new") {
clearTimeout(exports.messageTimeout);
delete exports.messageTimeout;
}
if (msg.t==="remove") return;
const appSettings = require("Storage").readJSON("messages.settings.json", 1) || {};
let loadMessages = (Bangle.CLOCK || event.important);
if (type==="music") {
if (Bangle.CLOCK && msg.new && appSettings.openMusic) loadMessages = true;
else return;
}
require("messages").save(msg);
msg.handled = true;
if (msg.t!=="add" || !msg.new) {
return;
}
const quiet = (require("Storage").readJSON("setting.json", 1) || {}).quiet;
const unlockWatch = appSettings.unlockWatch;
// don't auto-open messages in quiet mode if quietNoAutOpn is true
if ((quiet && appSettings.quietNoAutOpn) || appSettings.noAutOpn)
loadMessages = false;
// after a delay load the app, to ensure we have all the messages
if (exports.messageTimeout) clearTimeout(exports.messageTimeout);
exports.messageTimeout = setTimeout(function() {
delete exports.messageTimeout;
if (type!=="music") {
if (!loadMessages) return require("messages").buzz(msg.src); // no opening the app, just buzz
if (!quiet && unlockWatch) {
Bangle.setLocked(false);
Bangle.setLCDPower(1); // turn screen on
}
}
exports.open(msg);
}, 500);
};
/**
* Launch GUI app with given message
* @param {object} msg
*/
exports.open = function(msg) {
if (msg && msg.id && !msg.show) {
// store which message to load
msg.show = 1;
require("messages").save(msg, {force: 1});
}
Bangle.load((msg && msg.new && msg.id!=="music") ? "messagegui.new.js" : "messagegui.app.js");
};

View File

@ -0,0 +1,22 @@
{
"id": "messagegui",
"name": "Message UI",
"version": "0.55",
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
"icon": "app.png",
"type": "app",
"tags": "tool,system",
"supports": ["BANGLEJS","BANGLEJS2"],
"dependencies" : { "messageicons":"module" },
"provides_modules": ["messagegui"],
"readme": "README.md",
"storage": [
{"name":"messagegui","url":"lib.js"},
{"name":"messagegui.app.js","url":"app.js"},
{"name":"messagegui.new.js","url":"app-newmessage.js"},
{"name":"messagegui.boot.js","url":"boot.js"},
{"name":"messagegui.img","url":"app-icon.js","evaluate":true}
],
"screenshots": [{"url":"screenshot.png"}],
"sortorder": -9
}

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1,77 +1 @@
0.01: New App! 0.55: Moved messages library into standalone library
0.02: Add 'messages' library
0.03: Fixes for Bangle.js 1
0.04: Add require("messages").clearAll()
0.05: Handling of message actions (ok/clear)
0.06: New messages now go at the start (fix #898)
Answering true/false now exits the messages app if no new messages
Back now marks a message as read
Clicking top-left opens a menu which allows you to delete a message or mark unread
0.07: Added settings menu with option to choose vibrate pattern and frequency (fix #909)
0.08: Fix rendering of long messages (fix #969)
buzz on new message (fix #999)
0.09: Message now disappears after 60s if no action taken and clock loads (fix 922)
Fix phone icon (#1014)
0.10: Respect the 'new' attribute if it was set from iOS integrations
0.11: Open app when touching the widget (Bangle.js 2 only)
0.12: Extra app-specific notification icons
New animated notification icon (instead of large blinking 'MESSAGES')
Added screenshots
0.13: Add /*LANG*/ comments for internationalisation
Add 'Delete All' option to message options
Now update correctly when 'require("messages").clearAll()' is called
0.14: Hide widget when all unread notifications are dismissed from phone
0.15: Don't buzz when Quiet Mode is active
0.16: Fix text wrapping so it fits the screen even if title is big (fix #1147)
0.17: Fix: Get dynamic dimensions of notify icon, fixed notification font
0.18: Use app-specific icon colors
Spread message action buttons out
Back button now goes back to list of messages
If showMessage called with no message (eg all messages deleted) now return to the clock (fix #1267)
0.19: Use a larger font for message text if it'll fit
0.20: Allow tapping on the body to show a scrollable view of the message and title in a bigger font (fix #1405, #1031)
0.21: Improve list readability on dark theme
0.22: Add Home Assistant icon
Allow repeat to be switched Off, so there is no buzzing repetition.
Also gave the widget a pixel more room to the right
0.23: Change message colors to match current theme instead of using green
Now attempt to use Large/Big/Medium fonts, and allow minimum font size to be configured
0.24: Remove left-over debug statement
0.25: Fix widget memory usage issues if message received and watch repeatedly calls Bangle.drawWidgets (fix #1550)
0.26: Setting to auto-open music
0.27: Add 'mark all read' option to popup menu (fix #1624)
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
0.32: Added an option to allow quiet mode to override message auto-open
0.33: Timeout from the message list screen if the message being displayed is removed and there is a timer going
0.34: Don't buzz for 'map' update messages
0.35: Reset graphics colors before rendering a message (possibly fix #1752)
0.36: Ensure a new message plus an almost immediate deletion of that message doesn't load the messages app (fix #1362)
0.37: Now use the setUI 'back' icon in the top left rather than specific buttons/menu items
0.38: Add telegram foss handling
0.39: Set default color for message icons according to theme
0.40: Use default Bangle formatter for booleans
0.41: Add notification icons in the widget
0.42: Fix messages ignoring "Vibrate: Off" setting
0.43: Add new Icons (Airbnb, warnwetter)
0.44: Separate buzz pattern for incoming calls
0.45: Added new app colors and icons
0.46: Add 'Vibrate Timer' option to set how long to vibrate for, and fix Repeat:off
Fix message removal from widget bar (previously caused exception as .hide has been removed)
0.47: Add new Icons (Nextbike, Mattermost, etc.)
0.48: When getting new message from the clock, only buzz once the messages app is loaded
0.49: Change messages icon (to fit within 24px) and ensure widget renders icons centrally
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
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()
0.52: Fix require("messages").buzz() regression
Fix background color in messages list after one unread message is shown
0.53: Messages now uses Bangle.load() to load messages app faster (if possible)
0.54: Move icons out to messageicons module

View File

@ -1,62 +1,25 @@
# Messages app # Messages library
This app handles the display of messages and message notifications. It stores This library handles the passing of messages. It can storess a list of messages
a list of currently received messages and allows them to be listed, viewed, and allows them to be retrieved by other apps.
and responded to.
It is a replacement for the old `notify`/`gadgetbridge` apps. ## Example
## Settings Assuming you are using GadgetBridge and "overlay notifications":
You can change settings by going to the global `Settings` app, then `App Settings` 1. Gadgetbridge sends an event to your watch for an incoming message
and `Messages`: 2. The `android` app parses the message, and calls `require("messages").pushMessage({/** the message */})`
3. `require("messages")` (provided by `messagelib`) calls `Bangle.emit("message", "text", {/** the message */})`
* `Vibrate` - This is the pattern of buzzes that should be made when a new message is received 4. Overlay Notifications shows the message in an overlay, and marks it as `handled`
* `Vibrate for calls` - This is the pattern of buzzes that should be made when an incoming call is received 5. The default GUI app (`messages`) sees the event is marked as `handled`, so does nothing.
* `Repeat` - How often should buzzes repeat - the default of 4 means the Bangle will buzz every 4 seconds 6. The default widget (`widmessages`) does nothing with `handled`, and shows a notification icon.
* `Vibrate Timer` - When a new message is received when in a non-clock app, we display the message icon and 7. You tap the notification, in order to open the full GUI Overlay Notifications
buzz every `Repeat` seconds. This is how long we continue to do that. calls `require("messages").openGUI({/** the message */})`
* `Unread Timer` - When a new message is received when showing the clock we go into the Messages app. 8. The default GUI app (`messages`) sees the "messageGUI" event, and launches itself
If there is no user input for this amount of time then the app will exit and return
to the clock where a ringing bell will be shown in the Widget bar.
* `Min Font` - The minimum font size used when displaying messages on the screen. A bigger font
is chosen if there isn't much message text, but this specifies the smallest the font should get before
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, or `Hide` the widget completely.
## New Messages
When a new message is received:
* If you're in an app, the Bangle will buzz and a message icon appears in the Widget bar. You can tap this icon to view the message.
* If you're in a clock, the Messages app will automatically start and show the message
When a message is shown, you'll see a screen showing the message title and text.
* The 'back-arrow' button (or physical button on Bangle.js 2) goes back to Messages, marking the current message as read.
* The top-left icon shows more options, for instance deleting the message of marking unread
* On Bangle.js 2 you can tap on the message body to view a scrollable version of the title and text (or can use the top-left icon + `View Message`)
* If shown, the 'tick' button:
* **Android** opens the notification on the phone
* **iOS** responds positively to the notification (accept call/etc)
* If shown, the 'cross' button:
* **Android** dismisses the notification on the phone
* **iOS** responds negatively to the notification (dismiss call/etc)
## Images
_1. Screenshot of a notification_
![](screenshot.png)
_2. What the notify icon looks like (it's touchable on Bangle.js2!)_
![](screenshot-notify.gif)
## Events (for app/widget developers)
## Events
When a new message arrives, a `"message"` event is emitted, you can listen for When a new message arrives, a `"message"` event is emitted, you can listen for
it like this: it like this:
@ -64,9 +27,8 @@ it like this:
```js ```js
myMessageListener = Bangle.on("message", (type, message)=>{ myMessageListener = Bangle.on("message", (type, message)=>{
if (message.handled) return; // another app already handled this message if (message.handled) return; // another app already handled this message
// <type> is one of "text", "call", "alarm", "map", "music", or "clearAll" // <type> is one of "text", "call", "alarm", "map", or "music"
if (type === "clearAll") return; // not a message // see `messagelib/lib.js` for possible <message> formats
// see `messages/lib.js` for possible <message> formats
// message.t could be "add", "modify" or "remove" // message.t could be "add", "modify" or "remove"
E.showMessage(`${message.title}\n${message.body}`, `${message.t} ${type} message`); 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`: // You can prevent the default `message` app from loading by setting `message.handled = true`:
@ -74,10 +36,23 @@ myMessageListener = Bangle.on("message", (type, message)=>{
}); });
``` ```
Apps can launch the full GUI by calling `require("messages").openGUI()`, if you
want to write your own GUI, it should include boot code that listens for
`"messageGUI"` events:
```js
Bangle.on("messageGUI", message=>{
if (message.handled) return; // another app already opened it's GUI
message.handled = true; // prevent other apps form launching
Bangle.load("my_message_gui.app.js");
})
```
## Requests ## Requests
Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=messages%20app Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=messagelib%library
## Creator ## Creator

View File

@ -1,10 +1,16 @@
function openMusic() { exports.music = {};
// only read settings file for first music message /**
if ("undefined"==typeof exports._openMusic) { * Emit "message" event with appropriate type from Bangle
exports._openMusic = !!((require('Storage').readJSON("messages.settings.json", true) || {}).openMusic); * @param {object} msg
} */
return exports._openMusic; function emit(msg) {
let type = "text";
if (["call", "music", "map"].includes(msg.id)) type = msg.id;
if (type==="music" && msg.t!=="remove" && (!("state" in msg) || (!("track" in msg)))) return; // wait for complete music info
if (msg.src && msg.src.toLowerCase().startsWith("alarm")) type = "alarm";
Bangle.emit("message", type, msg);
} }
/* Push a new message onto messages queue, event is: /* 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, src,title,subject,body,sender,tel, important:bool, new:bool}
{t:"add",id:int, id:"music", state, artist, track, etc} // add new {t:"add",id:int, id:"music", state, artist, track, etc} // add new
@ -12,125 +18,178 @@ function openMusic() {
{t:"modify",id:int, title:string} // modified {t:"modify",id:int, title:string} // modified
*/ */
exports.pushMessage = function(event) { exports.pushMessage = function(event) {
var messages = exports.getMessages();
// now modify/delete as appropriate // now modify/delete as appropriate
var mIdx = messages.findIndex(m=>m.id==event.id); if (event.t==="remove") {
if (event.t=="remove") { if (event.id==="music") exports.music = {};
if (mIdx>=0) messages.splice(mIdx, 1); // remove item
mIdx=-1;
} else { // add/modify } else { // add/modify
if (event.t=="add"){ if (event.t==="add") {
if(event.new === undefined ) { // If 'new' has not been set yet, set it if (event.new===undefined) event.new = true; // Assume it should be new
event.new=true; // Assume it should be new } else if (event.t==="modify") {
} const old = exports.getMessages().find(m => m.id===event.id);
if (old) event = Object.assign(old, event);
} }
if (mIdx<0) {
mIdx=0; // combine musicinfo and musicstate events
messages.unshift(event); // add new messages to the beginning if (event.id==="music") {
} if (event.state==="play") event.new = true; // new track, or playback (re)started
else Object.assign(messages[mIdx], event); event = Object.assign(exports.music, 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); // reset state (just in case)
var message = mIdx<0 ? {id:event.id, t:'remove'} : messages[mIdx]; delete event.handled;
// if in app, process immediately delete event.saved;
if ("undefined"!=typeof MESSAGES) return onMessagesModified(message); emit(event);
// 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"; * Save a single message to flash
Bangle.emit("message", type, message); * Also sets msg.saved=true
// update the widget icons shown *
if (global.WIDGETS && WIDGETS.messages) WIDGETS.messages.update(messages,true); * @param {object} msg
var handleMessage = () => { * @param {object} [options={}] Options:
// if no new messages now, make sure we don't load the messages app * {boolean} [force=false] Force save even if msg.saved is already set
if (event.t=="remove" && exports.messageTimeout && !messages.some(m => m.new)) { */
clearTimeout(exports.messageTimeout); exports.save = function(msg, options) {
delete exports.messageTimeout; if (!options) options = {};
} if (msg.saved && !options.force) return; //already saved
// ok, saved now let messages = exports.getMessages();
if (event.id=="music" && Bangle.CLOCK && messages[mIdx].new && openMusic()) { exports.apply(msg, messages);
// just load the app to display music: no buzzing exports.write(messages);
Bangle.load("messages.app.js"); msg.saved = true;
} else if (event.t!="add") { };
// we only care if it's new
return; /**
} else if (event.new==false) { * Apply incoming event to array of messages
return; *
} * @param {object} event Event to apply
// otherwise load messages/show widget * @param {array} messages Array of messages, *will be modified in-place*
var loadMessages = Bangle.CLOCK || event.important; * @return {array} Modified messages array
var quiet = (require('Storage').readJSON('setting.json', 1) || {}).quiet; */
var appSettings = require('Storage').readJSON('messages.settings.json', 1) || {}; exports.apply = function(event, messages) {
var unlockWatch = appSettings.unlockWatch; if (!event || !event.id) return messages;
// don't auto-open messages in quiet mode if quietNoAutOpn is true const mIdx = messages.findIndex(m => m.id===event.id);
if ((quiet && appSettings.quietNoAutOpn) || appSettings.noAutOpn) if (event.t==="remove") {
loadMessages = false; if (mIdx<0) return messages; // already gone -> nothing to do
delete appSettings; messages.splice(mIdx, 1);
// after a delay load the app, to ensure we have all the messages } else if (event.t==="add") {
if (exports.messageTimeout) clearTimeout(exports.messageTimeout); if (mIdx>=0) messages.splice(mIdx, 1); // duplicate ID! erase previous version
exports.messageTimeout = setTimeout(function() { messages.unshift(event);
exports.messageTimeout = undefined; } else if (event.t==="modify") {
// if we're in a clock or it's important, go straight to messages app if (mIdx>=0) messages[mIdx] = Object.assign(messages[mIdx], event);
if (loadMessages) { else messages.unshift(event);
if (!quiet && unlockWatch) { }
Bangle.setLocked(false); return messages;
Bangle.setLCDPower(1); // turn screen on };
}
// we will buzz when we enter the messages app /**
return Bangle.load("messages.new.js"); * Accept a call (or other acceptable event)
} * @param {object} msg
if (global.WIDGETS && WIDGETS.messages) WIDGETS.messages.update(messages); */
exports.buzz(message.src); exports.accept = function(msg) {
}, 500); if (msg.positive) Bangle.messageResponse(msg, true);
}; };
setTimeout(()=>{
if (!message.handled) handleMessage(); /**
},0); * Dismiss a message (if applicable), and erase it from flash
} * Emits a "message" event with t="remove", only if message existed
/// Remove all messages *
* @param {object} msg
*/
exports.dismiss = function(msg) {
if (msg.negative) Bangle.messageResponse(msg, false);
let messages = exports.getMessages();
const mIdx = messages.findIndex(m=>m.id===msg.id);
if (mIdx<0) return;
messages.splice(mIdx, 1);
exports.write(messages);
if (msg.t==="remove") return; // already removed, don't re-emit
msg.t = "remove";
emit(msg); // emit t="remove", so e.g. widgets know to update
};
/**
* Emit a "type=openGUI" event, to open GUI app
*
* @param {object} [msg={}] Message the app should show
*/
exports.openGUI = function(msg) {
if (!require("Storage").read("messagegui")) return; // "messagegui" module is missing!
// Mark the event as unhandled for GUI, but leave passed arguments intact
let copy = Object.assign({}, msg);
delete copy.handled;
require("messagegui").open(copy);
};
/**
* Show/hide the messages widget
*
* @param {boolean} show
*/
exports.toggleWidget = function(show) {
if (!require("Storage").read("messagewidget")) return; // "messagewidget" module is missing!
if (show) require("messagewidget").show();
else require("messagewidget").hide();
};
/**
* Replace all stored messages
* @param {array} messages Messages to save
*/
exports.write = function(messages) {
require("Storage").writeJSON("messages.json", messages.map(m => {
// we never want to save saved/handled status to file;
delete m.saved;
delete m.handled;
return m;
}));
};
/**
* Erase all messages
*/
exports.clearAll = function() { exports.clearAll = function() {
if ("undefined"!= typeof MESSAGES) { // we're in a messages app, clear that as well exports.write([]);
MESSAGES = []; Bangle.emit("message", "clearAll", {});
}
// Clear all messages
require("Storage").writeJSON("messages.json", []);
// if we have a widget, update it
if (global.WIDGETS && WIDGETS.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();
} }
/** /**
* Get saved messages
*
* Optionally pass in a message to apply to the list, this is for event handlers:
* By passing the message from the event, you can make sure the list is up-to-date,
* even if the message has not been saved (yet)
*
* Example:
* Bangle.on("message", (type, msg) => {
* console.log("All messages:", require("messages").getMessages(msg));
* });
*
* @param {object} [withMessage] Apply this event to messages
* @returns {array} All messages * @returns {array} All messages
*/ */
exports.getMessages = function() { exports.getMessages = function(withMessage) {
if ("undefined"!=typeof MESSAGES) return MESSAGES; // loaded/managed by app let messages = require("Storage").readJSON("messages.json", true);
return require("Storage").readJSON("messages.json",1)||[]; messages = Array.isArray(messages) ? messages : []; // make sure we always return an array
} if (withMessage && withMessage.id) exports.apply(withMessage, messages);
return messages;
};
/** /**
* Check if there are any messages * Check if there are any messages
*
* @param {object} [withMessage] Apply this event to messages, see getMessages
* @returns {string} "new"/"old"/"none" * @returns {string} "new"/"old"/"none"
*/ */
exports.status = function() { exports.status = function(withMessage) {
try { try {
let status= "none"; let status = "none";
for(const m of exports.getMessages()) { for(const m of exports.getMessages(withMessage)) {
if (["music", "map"].includes(m.id)) continue; if (["music", "map"].includes(m.id)) continue;
if (m.new) return "new"; if (m.new) return "new";
status = "old"; status = "old";
} }
return status; return status;
} catch(e) { } catch(e) {
return "none"; // don't bother e.g. the widget with errors return "none"; // don't bother callers with errors
} }
}; };
@ -141,24 +200,24 @@ exports.getMessages = function() {
*/ */
exports.buzz = function(msgSrc) { exports.buzz = function(msgSrc) {
exports.stopBuzz(); // cancel any previous buzz timeouts exports.stopBuzz(); // cancel any previous buzz timeouts
if ((require('Storage').readJSON('setting.json',1)||{}).quiet) return Promise.resolve(); // never buzz during Quiet Mode if ((require("Storage").readJSON("setting.json", 1) || {}).quiet) return Promise.resolve(); // never buzz during Quiet Mode
var msgSettings = require('Storage').readJSON("messages.settings.json", true) || {}; const msgSettings = require("Storage").readJSON("messages.settings.json", true) || {};
var pattern; let pattern;
if (msgSrc && msgSrc.toLowerCase() === "phone") { if (msgSrc && msgSrc.toLowerCase()==="phone") {
// special vibration pattern for incoming calls // special vibration pattern for incoming calls
pattern = msgSettings.vibrateCalls; pattern = msgSettings.vibrateCalls;
} else { } else {
pattern = msgSettings.vibrate; pattern = msgSettings.vibrate;
} }
if (pattern === undefined) { pattern = ":"; } // pattern may be "", so we can't use || ":" here if (pattern===undefined) { pattern = ":"; } // pattern may be "", so we can't use || ":" here
if (!pattern) return Promise.resolve(); if (!pattern) return Promise.resolve();
var repeat = msgSettings.repeat; let repeat = msgSettings.repeat;
if (repeat===undefined) repeat=4; // repeat may be zero if (repeat===undefined) repeat = 4; // repeat may be zero
if (repeat) { if (repeat) {
exports.buzzTimeout = setTimeout(()=>require("buzz").pattern(pattern), repeat*1000); exports.buzzTimeout = setTimeout(() => require("buzz").pattern(pattern), repeat*1000);
var vibrateTimeout = msgSettings.vibrateTimeout; let vibrateTimeout = msgSettings.vibrateTimeout;
if (vibrateTimeout===undefined) vibrateTimeout=60; if (vibrateTimeout===undefined) vibrateTimeout = 60;
if (vibrateTimeout && !exports.stopTimeout) exports.stopTimeout = setTimeout(exports.stopBuzz, vibrateTimeout*1000); if (vibrateTimeout && !exports.stopTimeout) exports.stopTimeout = setTimeout(exports.stopBuzz, vibrateTimeout*1000);
} }
return require("buzz").pattern(pattern); return require("buzz").pattern(pattern);

View File

@ -1,23 +1,18 @@
{ {
"id": "messages", "id": "messages",
"name": "Messages", "name": "Messages",
"version": "0.54", "version": "0.55",
"description": "App to display notifications from iOS and Gadgetbridge/Android", "description": "Library to handle, load and store message events received from Android/iOS",
"icon": "app.png", "icon": "app.png",
"type": "app", "type": "module",
"tags": "tool,system", "tags": "tool,system",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"dependencies" : { "messageicons":"module" }, "provides_modules" : ["messages"],
"dependencies" : { "messagegui":"module","messagewidget":"module" },
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"messages.app.js","url":"app.js"}, {"name":"messages","url":"lib.js"},
{"name":"messages.new.js","url":"app-newmessage.js"}, {"name":"messages.settings.js","url":"settings.js"}
{"name":"messages.settings.js","url":"settings.js"},
{"name":"messages.img","url":"app-icon.js","evaluate":true},
{"name":"messages.wid.js","url":"widget.js"},
{"name":"messages","url":"lib.js"}
], ],
"data": [{"name":"messages.json"},{"name":"messages.settings.json"}], "data": [{"name":"messages.json"},{"name":"messages.settings.json"}]
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot-notify.gif"}],
"sortorder": -9
} }

View File

@ -1,56 +0,0 @@
(() => {
if ((require('Storage').readJSON("messages.settings.json", true) || {}).maxMessages===0) return;
function filterMessages(msgs) {
return msgs.filter(msg => msg.new && msg.id != "music")
.map(m => m.src) // we only need this for icon/color
.filter((msg, i, arr) => arr.findIndex(nmsg => msg.src == nmsg.src) == i);
}
WIDGETS["messages"]={area:"tl", width:0, 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);
delete WIDGETS["messages"].i;
}
Bangle.removeListener('touch', this.touch);
if (!this.width) return;
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,
require("messageicons").getColor(msg, {settings:settings})];
if (settings.flash && ((Date.now()/1000)&1)) {
if (colors[1] == g.theme.fg) {
colors.reverse();
} else {
colors[1] = g.theme.fg;
}
}
g.setColor(colors[1]).setBgColor(colors[0]);
// draw the icon, or '...' if too many messages
g.drawImage(i == (settings.maxMessages - 1) && this.msgs.length > settings.maxMessages ? atob("EASBAGGG88/zz2GG") : require("messageicons").getImage(msg),
this.x + 12 + i * 24, this.y + 12, {rotate:0/*force centering*/});
}
}
WIDGETS["messages"].i=setTimeout(()=>WIDGETS["messages"].draw(true), 1000);
if (process.env.HWVERSION>1) Bangle.on('touch', this.touch);
},update:function(rawMsgs) {
const settings = Object.assign({maxMessages:3},require('Storage').readJSON("messages.settings.json", true) || {});
this.msgs = filterMessages(rawMsgs);
this.width = 24 * E.clip(this.msgs.length, 0, settings.maxMessages);
Bangle.drawWidgets();
},touch:function(b,c) {
var w=WIDGETS["messages"];
if (!w||!w.width||c.x<w.x||c.x>w.x+w.width||c.y<w.y||c.y>w.y+24) return;
load("messages.app.js");
}};
/* We might have returned here if we were in the Messages app for a
message but then the watch was never viewed. */
if (global.MESSAGES===undefined)
WIDGETS["messages"].update(require("messages").getMessages());
})();

View File

@ -1,2 +1,3 @@
0.01: New App! 0.01: New App!
0.02: Remove one line of code that didn't do anything other than in some instances hinder the function of the app. 0.02: Remove one line of code that didn't do anything other than in some instances hinder the function of the app.
0.03: Use the new messages library

View File

@ -1,15 +1,9 @@
Hacky app that uses Messages app and it's library to push a message that triggers the music controls. It's nearly not an app, and yet it moves. Hacky app that uses Messages app and it's library to push a message that triggers the music controls. It's nearly not an app, and yet it moves.
This app require Messages setting 'Auto-open Music' to be 'Yes'. If it isn't, the app will change it to 'Yes' and let it stay that way.
Making the music controls accessible this way lets one start a music stream on the phone in some situations even though the message app didn't receive a music message from gadgetbridge to begin with. (I think.) Making the music controls accessible this way lets one start a music stream on the phone in some situations even though the message app didn't receive a music message from gadgetbridge to begin with. (I think.)
It is suggested to use Messages Music along side the app Quick Launch. It is suggested to use Messages Music along side the app Quick Launch.
Messages Music v0.02 has been verified to work with Messages v0.41 on Bangle.js 2 fw2v14.
Messages Music should work with forks of the original Messages app. At least as long as functions pushMessage() in the library and showMusicMessage() in app.js hasn't been changed too much.
Messages app is created by Gordon Williams with contributions from [Jeroen Peters](https://github.com/jeroenpeters1986). Messages app is created by Gordon Williams with contributions from [Jeroen Peters](https://github.com/jeroenpeters1986).
The icon used for this app is from [https://icons8.com](https://icons8.com). The icon used for this app is from [https://icons8.com](https://icons8.com).

View File

@ -1,14 +1 @@
let showMusic = () => { require('messages').openGUI({"t":"add","artist":" ","album":" ","track":" ","dur":0,"c":-1,"n":-1,"id":"music","title":"Music","state":"play","new":true});
Bangle.CLOCK = 1; // To pass condition in messages library
require('messages').pushMessage({"t":"add","artist":" ","album":" ","track":" ","dur":0,"c":-1,"n":-1,"id":"music","title":"Music","state":"play","new":true});
};
var settings = require('Storage').readJSON('messages.settings.json', true) || {}; //read settings if they exist else set to empty dict
if (!settings.openMusic) {
settings.openMusic = true; // This app/hack works as intended only if this setting is true
require('Storage').writeJSON('messages.settings.json', settings);
E.showMessage("First run:\n\nMessages setting\n\n 'Auto-Open Music'\n\n set to 'Yes'");
setTimeout(()=>{showMusic();}, 5000);
} else {
showMusic();
}

View File

@ -1,7 +1,7 @@
{ {
"id": "messagesmusic", "id": "messagesmusic",
"name":"Messages Music", "name":"Messages Music",
"version":"0.02", "version":"0.03",
"description": "Uses Messages library to push a music message which in turn displays Messages app music controls", "description": "Uses Messages library to push a music message which in turn displays Messages app music controls",
"icon":"app.png", "icon":"app.png",
"type": "app", "type": "app",
@ -13,6 +13,6 @@
{"name":"messagesmusic.app.js","url":"app.js"}, {"name":"messagesmusic.app.js","url":"app.js"},
{"name":"messagesmusic.img","url":"app-icon.js","evaluate":true} {"name":"messagesmusic.img","url":"app-icon.js","evaluate":true}
], ],
"dependencies": {"messages":"app"} "dependencies" : { "messagelib":"module" }
} }

View File

@ -0,0 +1 @@
0.01: Moved messages widget into standalone widget app

View File

@ -0,0 +1,30 @@
# Messages widget
The default widget to show icons for new messages
It is installed automatically if you install `Android Integration` or `iOS Integration`.
![screenshot](screenshot.gif)
## Settings
You can change settings by going to the global `Settings` app, then `App Settings`
and `Messages`:
* `Flash icon` Toggle flashing of the widget icons.
<!-- * `Show read` - Also show the widget when there are only old messages. -->
* `Widget messages` Not used by this widget.
## Requests
Please file any issues on https://github.com/espruino/BangleApps/issues/new?title=widmessages%widget
## Creator
Gordon Williams
## Contributors
[Jeroen Peters](https://github.com/jeroenpeters1986)
## Attributions
Icons used in this app are from https://icons8.com

BIN
apps/widmessages/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 917 B

8
apps/widmessages/lib.js Normal file
View File

@ -0,0 +1,8 @@
exports.hide = function() {
if (!global.WIDGETS||!WIDGETS["messages"]) return;
WIDGETS["messages"].hide();
}
exports.show = function() {
if (!global.WIDGETS||!WIDGETS["messages"]) return;
WIDGETS["messages"].show();
}

View File

@ -0,0 +1,18 @@
{
"id": "widmessages",
"name": "Message Widget",
"version": "0.01",
"description": "Widget showing new messages",
"icon": "app.png",
"type": "widget",
"tags": "tool,system",
"supports": ["BANGLEJS","BANGLEJS2"],
"screenshots": [{"url": "screenshot.gif"}],
"dependencies" : { "messageicons":"module" },
"provides_modules" : ["messagewidget"],
"readme": "README.md",
"storage": [
{"name":"messagewidget","url":"lib.js"},
{"name":"widmessages.wid.js","url":"widget.js"}
]
}

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,73 @@
(() => {
if ((require("Storage").readJSON("messages.settings.json", true) || {}).maxMessages===0) return;
function filterMessages(msgs) {
return msgs.filter(msg => msg.new && msg.id != "music")
.map(m => m.src) // we only need this for icon/color
.filter((msg, i, arr) => arr.findIndex(nmsg => msg.src == nmsg.src) == i);
}
WIDGETS["messages"] = {
area: "tl", width: 0, srcs: [], 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);
delete WIDGETS["messages"].i;
}
Bangle.removeListener("touch", this.touch);
if (!this.width) return;
let settings = Object.assign({flash: true, maxMessages: 3}, require("Storage").readJSON("messages.settings.json", true) || {});
if (recall!==true || settings.flash) {
const msgsShown = E.clip(this.srcs.length, 0, settings.maxMessages),
srcs = Object.keys(this.srcs);
g.reset().clearRect(this.x, this.y, this.x+this.width, this.y+23);
for(let i = 0; i<msgsShown; i++) {
const src = srcs[i];
const colors = [
g.theme.bg,
require("messageicons").getColor(src, {settings: settings})
];
if (settings.flash && ((Date.now()/1000)&1)) {
if (colors[1]==g.theme.fg) {
colors.reverse();
} else {
colors[1] = g.theme.fg;
}
}
g.setColor(colors[1]).setBgColor(colors[0]);
// draw the icon, or '...' if too many messages
g.drawImage(i==(settings.maxMessages-1) && this.srcs.length>settings.maxMessages ? atob("EASBAGGG88/zz2GG") : require("messageicons").getImage(src),
this.x+12+i*24, this.y+12, {rotate: 0/*force centering*/});
}
}
WIDGETS["messages"].i = setTimeout(() => WIDGETS["messages"].draw(true), 1000);
if (process.env.HWVERSION>1) Bangle.on("touch", this.touch);
}, onMsg: function(type, msg) {
if (this.hidden) return;
if (type==="music") return;
if (msg.id && !msg.new && msg.t!=="remove") return;
this.srcs = filterMessages(require("messages").getMessages(msg));
const settings = Object.assign({maxMessages:3},require('Storage').readJSON("messages.settings.json", true) || {});
this.width = 24 * E.clip(this.srcs.length, 0, settings.maxMessages);
if (type!=="init") Bangle.drawWidgets(); // "init" is not a real message type: see below
}, touch: function(b, c) {
var w = WIDGETS["messages"];
if (!w || !w.width || c.x<w.x || c.x>w.x+w.width || c.y<w.y || c.y>w.y+24) return;
require("messages").openGUI();
}, hide() {
this.hidden=true;
if (this.width) {
// hide widget
this.width = 0;
Bangle.drawWidgets();
}
}, show() {
delete this.hidden
this.onMsg("show", {}); // reload messages+redraw
}
};
Bangle.on("message", WIDGETS["messages"].onMsg);
this.srcs = {};
WIDGETS["messages"].onMsg("init", {}); // abuse type="init" to prevent Bangle.drawWidgets();
})();

View File

@ -1,2 +1,3 @@
0.01: New widget! 0.01: New widget!
0.02: Adjust to message icons moving to messageicons lib 0.02: Adjust to message icons moving to messageicons lib
0.03: Use new message library

View File

@ -20,9 +20,9 @@ You probably want to disable the default widget, to do so:
3. Scroll down to the `Widget messages` entry, and change it to `Hide` 3. Scroll down to the `Widget messages` entry, and change it to `Hide`
## Settings ## Settings
This widget uses the `Widget` settings from the `messages` app: You can change settings by going to the global `Settings` app, then `App Settings`
and `Messages`:
### Widget
* `Flash icon` Toggle flashing of the widget icons. * `Flash icon` Toggle flashing of the widget icons.
<!-- * `Show read` - Also show the widget when there are only old messages. --> <!-- * `Show read` - Also show the widget when there are only old messages. -->
* `Widget messages` Not used by this widget, but you should select `Hide` to hide the default widget. * `Widget messages` Not used by this widget.

8
apps/widmsggrid/lib.js Normal file
View File

@ -0,0 +1,8 @@
exports.hide = function() {
if (!global.WIDGETS||!WIDGETS["msggrid"]) return;
WIDGETS["msggrid"].hide();
}
exports.show = function() {
if (!global.WIDGETS||!WIDGETS["msggrid"]) return;
WIDGETS["msggrid"].show();
}

View File

@ -1,16 +1,17 @@
{ {
"id": "widmsggrid", "id": "widmsggrid",
"name": "Messages Grid Widget", "name": "Messages Grid Widget",
"version": "0.02", "version": "0.03",
"description": "Widget that display notification icons in a grid", "description": "Widget that displays notification icons in a grid",
"icon": "widget.png", "icon": "widget.png",
"type": "widget", "type": "widget",
"dependencies": {"messages":"app"},
"tags": "tool,system", "tags": "tool,system",
"supports": ["BANGLEJS","BANGLEJS2"], "supports": ["BANGLEJS","BANGLEJS2"],
"dependencies" : { "messageicons":"module" }, "dependencies" : { "messages":"module" },
"provides_modules" : ["messagewidget"],
"readme": "README.md", "readme": "README.md",
"storage": [ "storage": [
{"name":"messagewidget","url":"lib.js"},
{"name":"widmsggrid.wid.js","url":"widget.js"} {"name":"widmsggrid.wid.js","url":"widget.js"}
], ],
"screenshots": [{"url":"screenshot.png"}] "screenshots": [{"url":"screenshot.png"}]

View File

@ -24,7 +24,7 @@
clearTimeout(w.t); clearTimeout(w.t);
delete w.t; delete w.t;
} }
if (!w.width) return; if (!w.width || this.hidden) return;
const b = w.flash && w.status === "new" && ((Date.now() / 1000) & 1), // Blink(= inverse colors) on this second? const b = w.flash && w.status === "new" && ((Date.now() / 1000) & 1), // Blink(= inverse colors) on this second?
// show multiple icons in a grid, by scaling them down // show multiple icons in a grid, by scaling them down
cols = Math.ceil(Math.sqrt(w.srcs.length - 0.1)); // cols===rows, -0.1 to work around rounding error cols = Math.ceil(Math.sqrt(w.srcs.length - 0.1)); // cols===rows, -0.1 to work around rounding error
@ -57,9 +57,10 @@
.drawString(w.total, w.x + w.width - 1, w.y + 24, w.total > 9); .drawString(w.total, w.x + w.width - 1, w.y + 24, w.total > 9);
} }
if (w.flash && w.status === "new") w.t = setTimeout(w.draw, 1000); // schedule redraw while blinking if (w.flash && w.status === "new") w.t = setTimeout(w.draw, 1000); // schedule redraw while blinking
}, show: function () { }, show: function (m) {
delete w.hidden;
w.width = 24; w.width = 24;
w.srcs = require("messages").getMessages() w.srcs = require("messages").getMessages(m)
.filter(m => !['call', 'map', 'music'].includes(m.id)) .filter(m => !['call', 'map', 'music'].includes(m.id))
.filter(m => m.new || w.showRead) .filter(m => m.new || w.showRead)
.map(m => m.src); .map(m => m.src);
@ -68,6 +69,7 @@
Bangle.drawWidgets(); Bangle.drawWidgets();
Bangle.setLCDPower(1); // turns screen on Bangle.setLCDPower(1); // turns screen on
}, hide: function () { }, hide: function () {
w.hidden = true;
w.width = 0; w.width = 0;
w.srcs = []; w.srcs = [];
w.total = 0; w.total = 0;
@ -82,13 +84,16 @@
} }
// Bangle.js 2: open app when touching the widget // Bangle.js 2: open app when touching the widget
else if (c.x < w.x || c.x > w.x + w.width || c.y < w.y || c.y > w.y + 24) return; else if (c.x < w.x || c.x > w.x + w.width || c.y < w.y || c.y > w.y + 24) return;
load("messages.app.js"); require("messages").openGUI();
}, listener: function () { }, listener: function (t,m) {
w.status = require("messages").status(); if (this.hidden) return;
if (w.status === "new" || (w.status === "old" && w.showRead)) w.show(); w.status = require("messages").status(m);
if (w.status === "new" || (w.status === "old" && w.showRead)) w.show(m);
else w.hide(); else w.hide();
delete w.hidden; // always set by w.hide(), but we checked it wasn't there before
} }
}; };
delete s; delete s;
const w = WIDGETS["msggrid"]; const w = WIDGETS["msggrid"];
Bangle.on("message", w.listener);
})(); })();

View File

@ -92,7 +92,8 @@ const INTERNAL_FILES_IN_APP_TYPE = { // list of app types and files they SHOULD
}; };
/* These are warnings we know about but don't want in our output */ /* These are warnings we know about but don't want in our output */
var KNOWN_WARNINGS = [ var KNOWN_WARNINGS = [
"App gpsrec data file wildcard .gpsrc? does not include app ID" "App gpsrec data file wildcard .gpsrc? does not include app ID",
"App widmessages storage file messagewidget is also listed as storage file for app widmsggrid",
]; ];
function globToRegex(pattern) { function globToRegex(pattern) {