Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/messagegui/ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,4 @@
0.87: Make choosing of font size more repeatable
0.88: Adjust padding calculation so messages are spaced out properly even when using international fonts
0.89: Fix bugs related to empty titles and bodies
0.90: Persist music info; Fix old music info copying into new music messages
59 changes: 54 additions & 5 deletions apps/messagegui/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,52 @@ if (Bangle.MESSAGES) {
delete Bangle.MESSAGES;
}

// Update or add music message to MESSAGES array at front
function updateMusicMessage(musicMsg) {
var existingMusicIdx = MESSAGES.findIndex(m => m.id === "music");
if (existingMusicIdx >= 0) {
MESSAGES[existingMusicIdx] = musicMsg;
if (existingMusicIdx !== 0) MESSAGES.unshift(MESSAGES.splice(existingMusicIdx, 1)[0]);
} else {
MESSAGES.unshift(musicMsg);
}
}

// Load music message from storage if it exists
var musicMsg = require("messages").getMusic();
if (musicMsg && (musicMsg.track || musicMsg.artist)) {
updateMusicMessage(musicMsg);
}

var onMessagesModified = function(type,msg) {
if (msg.handled) return;
msg.handled = true;
require("messages").apply(msg, MESSAGES);

if (msg.id === "music") {
// For music messages, get the complete state from messages module
updateMusicMessage(require("messages").getMusic());
var musicMsg = MESSAGES[0]; // music is now at front

if (musicMsg.state && musicMsg.state=="play") {
openMusic = true;
} else if (musicMsg.state && musicMsg.state!="play") {
openMusic = false; // no longer playing music to go back to
}
if ((active!=undefined) && (active!="list") && (active!="music")) return; // don't open music over other screens (but do if we're in the main menu)
} else {
require("messages").apply(msg, MESSAGES);
// Move music back to front since apply() may have pushed it down
var musicIdx = MESSAGES.findIndex(m => m.id === "music");
if (musicIdx > 0) {
MESSAGES.unshift(MESSAGES.splice(musicIdx, 1)[0]);
}
}

// TODO: if new, show this new one
if (msg && msg.id!=="music" && msg.id!=="nav" && msg.new &&
!((require('Storage').readJSON('setting.json', 1) || {}).quiet)) {
require("messages").buzz(msg.src);
}
if (msg && msg.id=="music") {
if (msg.state && msg.state!="play") openMusic = false; // no longer playing music to go back to
if ((active!=undefined) && (active!="list") && (active!="music")) return; // don't open music over other screens (but do if we're in the main menu)
}
if (msg && msg.id=="nav" && msg.t=="modify" && active!="map")
return; // don't show an updated nav message if we're just in the menu
showMessage(msg&&msg.id, false);
Expand Down Expand Up @@ -518,6 +551,8 @@ function showMessage(msgid, persist) {
*/
function checkMessages(options) {
options=options||{};
// Remove/ignore stale music messages if they haven't played recently
checkMusicExpired();
// If there's been some user interaction, it's time to stop repeated buzzing
if (!options.dontStopBuzz)
require("messages").stopBuzz();
Expand Down Expand Up @@ -617,6 +652,20 @@ function returnToClockIfEmpty() {
checkMessages({clockIfNoMsg:1,clockIfAllRead:0,ignoreUnread:1,openMusic});
}

function checkMusicExpired() {
var musicTimeout = (settings && settings.musicTimeoutMinutes) ? settings.musicTimeoutMinutes : 5;
if (!isFinite(musicTimeout) || musicTimeout <= 0) return;

var now = Date.now();
MESSAGES = MESSAGES.filter(function(m) {
if (!m || m.id!="music") return true;
if (m.state=="play" || m.state=="show") return true;
if (m._lastPlayed && (now - m._lastPlayed) <= musicTimeout*60000) return true;
// otherwise drop the stale music message
return false;
});
}

function cancelReloadTimeout() {
if (!unreadTimeout) return;
clearTimeout(unreadTimeout);
Expand Down
15 changes: 11 additions & 4 deletions apps/messagegui/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,13 @@ exports.listener = function(type, msg) {
const appSettings = require("Storage").readJSON("messages.settings.json", 1) || {};
let loadMessages = (Bangle.CLOCK || msg.important); // should we load the messages app?
if (type==="music") {
if (Bangle.CLOCK && msg.state && msg.title && appSettings.openMusic) loadMessages = true;
else return;
// Music persistence is handled by messages module via pushMessage
msg.handled = true;
if (Bangle.CLOCK && msg.state && msg.title && appSettings.openMusic) {
loadMessages = true;
} else {
return; // handled
}
}
// Write the message to Bangle.MESSAGES. We'll deal with it in messageTimeout below
if (!Bangle.MESSAGES) Bangle.MESSAGES = [];
Expand All @@ -48,7 +53,9 @@ exports.listener = function(type, msg) {
// save messages from RAM to flash if we decide not to launch app
// We apply all of Bangle.MESSAGES here in one write
if (!Bangle.MESSAGES || !Bangle.MESSAGES.length) return;
let messages = require("messages").getMessages(msg);
// Load saved messages without applying the current msg to avoid
// applying it twice (getMessages(msg) would apply it already).
let messages = require("messages").getMessages();
(Bangle.MESSAGES || []).forEach(m => require("messages").apply(m, messages));
require("messages").write(messages);
delete Bangle.MESSAGES;
Expand Down Expand Up @@ -104,4 +111,4 @@ exports.open = function(msg) {
}

Bangle.load((msg && msg.new && msg.id!=="music") ? "messagegui.new.js" : "messagegui.app.js");
};
};
2 changes: 1 addition & 1 deletion apps/messagegui/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"id": "messagegui",
"name": "Message UI",
"shortName": "Messages",
"version": "0.89",
"version": "0.90",
"description": "Default app to display notifications from iOS and Gadgetbridge/Android",
"icon": "app.png",
"type": "app",
Expand Down
1 change: 1 addition & 0 deletions apps/messages/ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
0.66: Fix 'Auto-Open Unread Msg' polarity - previously checking the box would ignore unread messages
0.67: Ensure default vibration pattern is longer
Add Option to show widgets (Message GUI 0.86 removes them by default)
0.68: Fix perserving music info between messages
115 changes: 110 additions & 5 deletions apps/messages/lib.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
exports.music = {};

// Track if music has been modified and needs saving
let musicLoaded = false;
let musicDirty = false;
let killHandlerSet = false;

/**
* Emit "message" event with appropriate type from Bangle
* @param {object} msg
Expand All @@ -24,14 +30,18 @@ exports.pushMessage = function(event) {
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);
// For non-music messages, merge with stored message
// For music, skip merging to avoid old info
if (event.id !== "music") {
const old = exports.getMessages().find(m => m.id===event.id);
if (old) event = Object.assign(old, event);
}
}

// 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);
setMusic(event);
event = getMusic();
}
}
// reset state (just in case)
Expand Down Expand Up @@ -72,7 +82,7 @@ exports.apply = function(event, messages) {
messages.splice(mIdx, 1);
} else if (event.t==="add") {
if (mIdx>=0) messages.splice(mIdx, 1); // duplicate ID! erase previous version
messages.unshift(event); // add at the beginning
messages.unshift(Object.assign({}, event)); // add a copy at the beginning
} else if (event.t==="modify") {
if (mIdx>=0) messages[mIdx] = Object.assign(messages[mIdx], event);
else messages.unshift(event);
Expand Down Expand Up @@ -235,3 +245,98 @@ exports.stopBuzz = function() {
if (exports.stopTimeout) clearTimeout(exports.stopTimeout);
delete exports.stopTimeout;
};

/**
* Lazy-load music from messages.music.json if not already loaded
*/
function loadMusic() {
if (musicLoaded) return;
const stored = require("Storage").readJSON("messages.music.json", true);
if (stored) {
exports.music = stored;
}
musicLoaded = true;
}

/**
* Save music info to messages.music.json if dirty
*/
function saveMusicToFlash() {
if (!musicDirty) return;

// Debug counter to track saves
incrementSaveCounter("music");

// Only save if we have actual music data
if (Object.keys(exports.music).length > 0) {
require("Storage").writeJSON("messages.music.json", exports.music);
} else {
require("Storage").erase("messages.music.json");
}

musicDirty = false;
}

/**
* Set music info - merges music message data into exports.music, saves to flash on kill
* @param {object} msg Music message
*/
function setMusic(msg) {
// Lazy-load existing music data
loadMusic();

// Merge in artist/track/album/dur if provided
if (msg.artist !== undefined) exports.music.artist = msg.artist;
if (msg.track !== undefined) exports.music.track = msg.track;
if (msg.album !== undefined) exports.music.album = msg.album;
if (msg.dur !== undefined) exports.music.dur = msg.dur;

// Merge in state and update _lastPlayed timestamp when playing
if (msg.state !== undefined) {
exports.music.state = msg.state;
if (msg.state === "play") {
exports.music._lastPlayed = Date.now();
}
}

// If this is a musicstate message and we don't have track info yet,
// set track to "Music" so we can trigger displaying music controls
if (msg.state && !exports.music.track) {
exports.music.track = "Music";
exports.music.title = exports.music.title || "Music";
}

// Mark as dirty so it gets saved on kill
musicDirty = true;

// Set up kill handler only once
if (!killHandlerSet) {
E.on("kill", saveMusicToFlash);
killHandlerSet = true;
}
}

/**
* Get current music info with event metadata
* @returns {object} Music message object
*/
function getMusic() {
loadMusic();
const event = Object.assign({ id: "music" }, exports.music);
if (event.state === "play") event.new = true; // new track, or playback (re)started
return event;
}

/**
* Get current music message
* @returns {object} Music message object with id, state, track, artist, etc.
*/
exports.getMusic = getMusic;

function incrementSaveCounter(name) {
const storage = require("Storage");
let stats = storage.readJSON("message_stats.json", true) || {};
if (!stats[name]) stats[name] = 0;
stats[name]++;
storage.writeJSON("message_stats.json", stats);
}
2 changes: 1 addition & 1 deletion apps/messages/metadata.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"id": "messages",
"name": "Messages",
"version": "0.67",
"version": "0.68",
"description": "Library to handle, load and store message events received from Android/iOS",
"icon": "app.png",
"type": "module",
Expand Down
6 changes: 6 additions & 0 deletions apps/messages/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@
value: !!settings.openMusic,
onchange: v => updateSetting("openMusic", v)
},
/*LANG*/'Music Msg Timeout': {
value: (settings && settings.musicTimeoutMinutes!=null) ? settings.musicTimeoutMinutes : 5,
min: 0, max: 240, step: 1,
format: v => v ? v+/*LANG*/"m" : /*LANG*/"Off",
onchange: v => updateSetting("musicTimeoutMinutes", v)
},
/*LANG*/'Unlock Watch': {
value: !!settings.unlockWatch,
onchange: v => updateSetting("unlockWatch", v)
Expand Down