mirror of https://github.com/espruino/BangleApps
messages: split library/gui/widget into separate apps
parent
a9b12f0dba
commit
2a506e7421
|
@ -15,3 +15,4 @@
|
|||
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.17: Now kick off Calendar sync as soon as connected to Gadgetbridge
|
||||
0.18: Use new message library
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
"id": "android",
|
||||
"name": "Android Integration",
|
||||
"shortName": "Android",
|
||||
"version": "0.17",
|
||||
"version": "0.18",
|
||||
"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",
|
||||
"dependencies": {"messages":"app"},
|
||||
"dependencies": {"messages":"module"},
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
updateSettings();
|
||||
}
|
||||
},
|
||||
/*LANG*/"Messages" : ()=>load("messages.app.js"),
|
||||
/*LANG*/"Messages" : ()=>require("message").openGUI(),
|
||||
};
|
||||
E.showMenu(mainmenu);
|
||||
})
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
0.01: Base code
|
||||
0.02: Saved settings when switching color scheme
|
||||
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
|
|
@ -234,7 +234,7 @@ function handleMessages()
|
|||
{
|
||||
if(!hasMessages()) return;
|
||||
E.showMessage("Loading Messages...");
|
||||
load("messages.app.js");
|
||||
require("messages").openGUI();
|
||||
}
|
||||
|
||||
function hasMessages()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "hcclock",
|
||||
"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.",
|
||||
"icon": "hcclock-icon.png",
|
||||
"type": "clock",
|
||||
|
|
|
@ -9,3 +9,4 @@
|
|||
0.09: Enable 'ams' on new firmwares (ams/ancs can now be enabled individually) (fix #1365)
|
||||
0.10: Added more bundleIds
|
||||
0.11: Added letters with caron to unicodeRemap, to properly display messages in Czech language
|
||||
0.12: Use new message library
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
// Config app not implemented yet
|
||||
setTimeout(()=>load("messages.app.js"),10);
|
||||
setTimeout(()=>require("messages").openGUI(),10);
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{
|
||||
"id": "ios",
|
||||
"name": "iOS Integration",
|
||||
"version": "0.11",
|
||||
"version": "0.12",
|
||||
"description": "Display notifications/music/etc from iOS devices",
|
||||
"icon": "app.png",
|
||||
"tags": "tool,system,ios,apple,messages,notifications",
|
||||
"dependencies": {"messages":"app"},
|
||||
"dependencies": {"messages":"module"},
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
|
|
|
@ -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
|
|
@ -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_
|
||||
|
||||

|
||||
|
||||
|
||||
## 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
|
|
@ -1,5 +1,5 @@
|
|||
/* 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 */
|
||||
global.BUZZ_ON_NEW_MESSAGE = true;
|
||||
eval(require("Storage").read("messages.app.js"));
|
||||
eval(require("Storage").read("messagegui.app.js"));
|
|
@ -19,7 +19,6 @@ require("messages").pushMessage({"t":"add","id":1,"src":"Maps","title":"0 yd - H
|
|||
// call
|
||||
require("messages").pushMessage({"t":"add","id":"call","src":"Phone","title":"Bob","body":"12421312",positive:true,negative:true})
|
||||
*/
|
||||
|
||||
var Layout = require("Layout");
|
||||
var settings = require('Storage').readJSON("messages.settings.json", true) || {};
|
||||
var fontSmall = "6x8";
|
||||
|
@ -49,8 +48,11 @@ to the clock. */
|
|||
var unreadTimeout;
|
||||
/// List of all our messages
|
||||
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
|
||||
if (msg && msg.id!=="music" && msg.new && active!="map" &&
|
||||
!((require('Storage').readJSON('setting.json', 1) || {}).quiet)) {
|
||||
|
@ -62,9 +64,15 @@ var onMessagesModified = function(msg) {
|
|||
}
|
||||
showMessage(msg&&msg.id);
|
||||
};
|
||||
Bangle.on("message", onMessagesModified);
|
||||
|
||||
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) {
|
||||
active = "map";
|
||||
|
@ -355,12 +363,16 @@ function checkMessages(options) {
|
|||
}
|
||||
// we have >0 messages
|
||||
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 (options.showMsgIfUnread && newMessages.length) {
|
||||
if ((toShow||options.showMsgIfUnread) && newMessages.length) {
|
||||
showMessage(newMessages[0].id);
|
||||
// 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`
|
||||
// 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
|
||||
global.BUZZ_ON_NEW_MESSAGE = false;
|
||||
// messages.buzz respects quiet mode - no need to check here
|
||||
|
@ -428,6 +440,7 @@ function cancelReloadTimeout() {
|
|||
g.clear();
|
||||
|
||||
Bangle.loadWidgets();
|
||||
require("messages").toggleWidget(false);
|
||||
Bangle.drawWidgets();
|
||||
|
||||
setTimeout(() => {
|
Binary file not shown.
After Width: | Height: | Size: 917 B |
|
@ -0,0 +1,3 @@
|
|||
(function() {
|
||||
Bangle.on("message", (type, msg) => require("messagegui").listener(type, msg));
|
||||
})();
|
|
@ -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");
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"id": "messagegui",
|
||||
"name": "Messages",
|
||||
"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
|
||||
}
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
@ -1,77 +1 @@
|
|||
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.01: Moved messages library into standalone library
|
|
@ -1,62 +1,25 @@
|
|||
# Messages app
|
||||
# Messages library
|
||||
|
||||
This app handles the display of messages and message notifications. It stores
|
||||
a list of currently received messages and allows them to be listed, viewed,
|
||||
and responded to.
|
||||
This library handles the passing of messages. It can storess a list of messages
|
||||
and allows them to be retrieved by other apps.
|
||||
|
||||
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`
|
||||
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?
|
||||
* `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_
|
||||
|
||||

|
||||
|
||||
_2. What the notify icon looks like (it's touchable on Bangle.js2!)_
|
||||
|
||||

|
||||
1. Gadgetbridge sends an event to your watch for an incoming message
|
||||
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 */})`
|
||||
4. Overlay Notifications shows the message in an overlay, and marks it as `handled`
|
||||
5. The default GUI app (`messages`) sees the event is marked as `handled`, so does nothing.
|
||||
6. The default widget (`widmessages`) does nothing with `handled`, and shows a notification icon.
|
||||
7. You tap the notification, in order to open the full GUI Overlay Notifications
|
||||
calls `require("messages").openGUI({/** the message */})`
|
||||
8. The default GUI app (`messages`) sees the "messageGUI" event, and launches itself
|
||||
|
||||
|
||||
## Events (for app/widget developers)
|
||||
|
||||
## Events
|
||||
|
||||
When a new message arrives, a `"message"` event is emitted, you can listen for
|
||||
it like this:
|
||||
|
@ -64,9 +27,8 @@ it like this:
|
|||
```js
|
||||
myMessageListener = Bangle.on("message", (type, message)=>{
|
||||
if (message.handled) return; // another app already handled this message
|
||||
// <type> is one of "text", "call", "alarm", "map", "music", or "clearAll"
|
||||
if (type === "clearAll") return; // not a message
|
||||
// see `messages/lib.js` for possible <message> formats
|
||||
// <type> is one of "text", "call", "alarm", "map", or "music"
|
||||
// see `messagelib/lib.js` for possible <message> 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`:
|
||||
|
@ -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
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
function openMusic() {
|
||||
// only read settings file for first music message
|
||||
if ("undefined"==typeof exports._openMusic) {
|
||||
exports._openMusic = !!((require('Storage').readJSON("messages.settings.json", true) || {}).openMusic);
|
||||
}
|
||||
return exports._openMusic;
|
||||
exports.music = {};
|
||||
/**
|
||||
* Emit "message" event with appropriate type from Bangle
|
||||
* @param {object} msg
|
||||
*/
|
||||
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:
|
||||
{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
|
||||
|
@ -12,125 +18,178 @@ function openMusic() {
|
|||
{t:"modify",id:int, title:string} // modified
|
||||
*/
|
||||
exports.pushMessage = function(event) {
|
||||
var messages = exports.getMessages();
|
||||
// now modify/delete as appropriate
|
||||
var mIdx = messages.findIndex(m=>m.id==event.id);
|
||||
if (event.t=="remove") {
|
||||
if (mIdx>=0) messages.splice(mIdx, 1); // remove item
|
||||
mIdx=-1;
|
||||
if (event.t==="remove") {
|
||||
if (event.id==="music") exports.music = {};
|
||||
} else { // add/modify
|
||||
if (event.t=="add"){
|
||||
if(event.new === undefined ) { // If 'new' has not been set yet, set it
|
||||
event.new=true; // Assume it should be new
|
||||
}
|
||||
if (event.t==="add") {
|
||||
if (event.new===undefined) 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;
|
||||
messages.unshift(event); // add new messages to the beginning
|
||||
}
|
||||
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';
|
||||
|
||||
// combine musicinfo and musicstate events
|
||||
if (event.id==="music") {
|
||||
if (event.state==="play") event.new = true; // new track, or playback (re)started
|
||||
event = Object.assign(exports.music, event);
|
||||
}
|
||||
}
|
||||
require("Storage").writeJSON("messages.json",messages);
|
||||
var message = mIdx<0 ? {id:event.id, t:'remove'} : messages[mIdx];
|
||||
// if in app, process immediately
|
||||
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
|
||||
Bangle.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 Bangle.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
|
||||
// reset state (just in case)
|
||||
delete event.handled;
|
||||
delete event.saved;
|
||||
emit(event);
|
||||
};
|
||||
|
||||
/**
|
||||
* Save a single message to flash
|
||||
* Also sets msg.saved=true
|
||||
*
|
||||
* @param {object} msg
|
||||
* @param {object} [options={}] Options:
|
||||
* {boolean} [force=false] Force save even if msg.saved is already set
|
||||
*/
|
||||
exports.save = function(msg, options) {
|
||||
if (!options) options = {};
|
||||
if (msg.saved && !options.force) return; //already saved
|
||||
let messages = exports.getMessages();
|
||||
exports.apply(msg, messages);
|
||||
exports.write(messages);
|
||||
msg.saved = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Apply incoming event to array of messages
|
||||
*
|
||||
* @param {object} event Event to apply
|
||||
* @param {array} messages Array of messages, *will be modified in-place*
|
||||
* @return {array} Modified messages array
|
||||
*/
|
||||
exports.apply = function(event, messages) {
|
||||
if (!event || !event.id) return messages;
|
||||
const mIdx = messages.findIndex(m => m.id===event.id);
|
||||
if (event.t==="remove") {
|
||||
if (mIdx<0) return messages; // already gone -> nothing to do
|
||||
messages.splice(mIdx, 1);
|
||||
} else if (event.t==="add") {
|
||||
if (mIdx>=0) messages.splice(mIdx, 1); // duplicate ID! erase previous version
|
||||
messages.unshift(event);
|
||||
} else if (event.t==="modify") {
|
||||
if (mIdx>=0) messages[mIdx] = Object.assign(messages[mIdx], event);
|
||||
else messages.unshift(event);
|
||||
}
|
||||
return messages;
|
||||
};
|
||||
|
||||
/**
|
||||
* Accept a call (or other acceptable event)
|
||||
* @param {object} msg
|
||||
*/
|
||||
exports.accept = function(msg) {
|
||||
if (msg.positive) Bangle.messageResponse(msg, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Dismiss a message (if applicable), and erase it from flash
|
||||
* Emits a "message" event with t="remove", only if message existed
|
||||
*
|
||||
* @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() {
|
||||
if ("undefined"!= typeof MESSAGES) { // we're in a messages app, clear that as well
|
||||
MESSAGES = [];
|
||||
}
|
||||
// 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();
|
||||
exports.write([]);
|
||||
Bangle.emit("message", "clearAll", {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
exports.getMessages = function() {
|
||||
if ("undefined"!=typeof MESSAGES) return MESSAGES; // loaded/managed by app
|
||||
return require("Storage").readJSON("messages.json",1)||[];
|
||||
}
|
||||
exports.getMessages = function(withMessage) {
|
||||
let messages = require("Storage").readJSON("messages.json", true);
|
||||
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
|
||||
*
|
||||
* @param {object} [withMessage] Apply this event to messages, see getMessages
|
||||
* @returns {string} "new"/"old"/"none"
|
||||
*/
|
||||
exports.status = function() {
|
||||
exports.status = function(withMessage) {
|
||||
try {
|
||||
let status= "none";
|
||||
for(const m of exports.getMessages()) {
|
||||
let status = "none";
|
||||
for(const m of exports.getMessages(withMessage)) {
|
||||
if (["music", "map"].includes(m.id)) continue;
|
||||
if (m.new) return "new";
|
||||
status = "old";
|
||||
}
|
||||
return status;
|
||||
} 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.stopBuzz(); // cancel any previous buzz timeouts
|
||||
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) || {};
|
||||
var pattern;
|
||||
if (msgSrc && msgSrc.toLowerCase() === "phone") {
|
||||
if ((require("Storage").readJSON("setting.json", 1) || {}).quiet) return Promise.resolve(); // never buzz during Quiet Mode
|
||||
const msgSettings = require("Storage").readJSON("messages.settings.json", true) || {};
|
||||
let pattern;
|
||||
if (msgSrc && msgSrc.toLowerCase()==="phone") {
|
||||
// special vibration pattern for incoming calls
|
||||
pattern = msgSettings.vibrateCalls;
|
||||
} else {
|
||||
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();
|
||||
|
||||
var repeat = msgSettings.repeat;
|
||||
if (repeat===undefined) repeat=4; // repeat may be zero
|
||||
let repeat = msgSettings.repeat;
|
||||
if (repeat===undefined) repeat = 4; // repeat may be zero
|
||||
if (repeat) {
|
||||
exports.buzzTimeout = setTimeout(()=>require("buzz").pattern(pattern), repeat*1000);
|
||||
var vibrateTimeout = msgSettings.vibrateTimeout;
|
||||
if (vibrateTimeout===undefined) vibrateTimeout=60;
|
||||
exports.buzzTimeout = setTimeout(() => require("buzz").pattern(pattern), repeat*1000);
|
||||
let vibrateTimeout = msgSettings.vibrateTimeout;
|
||||
if (vibrateTimeout===undefined) vibrateTimeout = 60;
|
||||
if (vibrateTimeout && !exports.stopTimeout) exports.stopTimeout = setTimeout(exports.stopBuzz, vibrateTimeout*1000);
|
||||
}
|
||||
return require("buzz").pattern(pattern);
|
||||
|
|
|
@ -1,23 +1,18 @@
|
|||
{
|
||||
"id": "messages",
|
||||
"name": "Messages",
|
||||
"version": "0.54",
|
||||
"description": "App to display notifications from iOS and Gadgetbridge/Android",
|
||||
"version": "0.01",
|
||||
"description": "Library to handle message events",
|
||||
"icon": "app.png",
|
||||
"type": "app",
|
||||
"type": "module",
|
||||
"tags": "tool,system",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"dependencies" : { "messageicons":"module" },
|
||||
"provides_modules" : ["messages"],
|
||||
"dependencies" : { "messagegui":"module","messagewidget":"module" },
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"messages.app.js","url":"app.js"},
|
||||
{"name":"messages.new.js","url":"app-newmessage.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"}
|
||||
{"name":"messages","url":"lib.js"},
|
||||
{"name":"messages.settings.js","url":"settings.js"}
|
||||
],
|
||||
"data": [{"name":"messages.json"},{"name":"messages.settings.json"}],
|
||||
"screenshots": [{"url":"screenshot.png"},{"url":"screenshot-notify.gif"}],
|
||||
"sortorder": -9
|
||||
"data": [{"name":"messages.json"},{"name":"messages.settings.json"}]
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
})();
|
|
@ -1,2 +1,3 @@
|
|||
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.03: Use the new messages library
|
|
@ -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.
|
||||
|
||||
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.)
|
||||
|
||||
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).
|
||||
|
||||
The icon used for this app is from [https://icons8.com](https://icons8.com).
|
||||
|
|
|
@ -1,14 +1 @@
|
|||
let showMusic = () => {
|
||||
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();
|
||||
}
|
||||
require('messages').openGUI({"t":"add","artist":" ","album":" ","track":" ","dur":0,"c":-1,"n":-1,"id":"music","title":"Music","state":"play","new":true});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"id": "messagesmusic",
|
||||
"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",
|
||||
"icon":"app.png",
|
||||
"type": "app",
|
||||
|
@ -13,6 +13,6 @@
|
|||
{"name":"messagesmusic.app.js","url":"app.js"},
|
||||
{"name":"messagesmusic.img","url":"app-icon.js","evaluate":true}
|
||||
],
|
||||
"dependencies": {"messages":"app"}
|
||||
"dependencies" : { "messagelib":"module" }
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0.01: Moved messages widget into standalone widget app
|
|
@ -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`.
|
||||
|
||||

|
||||
|
||||
## 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
|
Binary file not shown.
After Width: | Height: | Size: 917 B |
|
@ -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();
|
||||
}
|
|
@ -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"}
|
||||
]
|
||||
}
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.9 KiB |
|
@ -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();
|
||||
})();
|
|
@ -1,2 +1,3 @@
|
|||
0.01: New widget!
|
||||
0.02: Adjust to message icons moving to messageicons lib
|
||||
0.03: Use new message library
|
|
@ -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`
|
||||
|
||||
## 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.
|
||||
<!-- * `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.
|
|
@ -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();
|
||||
}
|
|
@ -1,16 +1,17 @@
|
|||
{
|
||||
"id": "widmsggrid",
|
||||
"name": "Messages Grid Widget",
|
||||
"version": "0.02",
|
||||
"description": "Widget that display notification icons in a grid",
|
||||
"version": "0.03",
|
||||
"description": "Widget that displays notification icons in a grid",
|
||||
"icon": "widget.png",
|
||||
"type": "widget",
|
||||
"dependencies": {"messages":"app"},
|
||||
"tags": "tool,system",
|
||||
"supports": ["BANGLEJS","BANGLEJS2"],
|
||||
"dependencies" : { "messageicons":"module" },
|
||||
"dependencies" : { "messages":"module" },
|
||||
"provides_modules" : ["messagewidget"],
|
||||
"readme": "README.md",
|
||||
"storage": [
|
||||
{"name":"messagewidget","url":"lib.js"},
|
||||
{"name":"widmsggrid.wid.js","url":"widget.js"}
|
||||
],
|
||||
"screenshots": [{"url":"screenshot.png"}]
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
clearTimeout(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?
|
||||
// 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
|
||||
|
@ -57,9 +57,10 @@
|
|||
.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
|
||||
}, show: function () {
|
||||
}, show: function (m) {
|
||||
delete w.hidden;
|
||||
w.width = 24;
|
||||
w.srcs = require("messages").getMessages()
|
||||
w.srcs = require("messages").getMessages(m)
|
||||
.filter(m => !['call', 'map', 'music'].includes(m.id))
|
||||
.filter(m => m.new || w.showRead)
|
||||
.map(m => m.src);
|
||||
|
@ -68,6 +69,7 @@
|
|||
Bangle.drawWidgets();
|
||||
Bangle.setLCDPower(1); // turns screen on
|
||||
}, hide: function () {
|
||||
w.hidden = true;
|
||||
w.width = 0;
|
||||
w.srcs = [];
|
||||
w.total = 0;
|
||||
|
@ -82,13 +84,16 @@
|
|||
}
|
||||
// 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;
|
||||
load("messages.app.js");
|
||||
}, listener: function () {
|
||||
w.status = require("messages").status();
|
||||
if (w.status === "new" || (w.status === "old" && w.showRead)) w.show();
|
||||
require("messages").openGUI();
|
||||
}, listener: function (t,m) {
|
||||
if (this.hidden) return;
|
||||
w.status = require("messages").status(m);
|
||||
if (w.status === "new" || (w.status === "old" && w.showRead)) w.show(m);
|
||||
else w.hide();
|
||||
delete w.hidden; // always set by w.hide(), but we checked it wasn't there before
|
||||
}
|
||||
};
|
||||
delete s;
|
||||
const w = WIDGETS["msggrid"];
|
||||
Bangle.on("message", w.listener);
|
||||
})();
|
||||
|
|
|
@ -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 */
|
||||
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) {
|
||||
|
|
Loading…
Reference in New Issue