Skip to content

Commit

Permalink
Merge pull request #741 from rigrig/gbmusic
Browse files Browse the repository at this point in the history
gbmusic: Setting to disable double/triple press control, remove touch controls setting, reduce fadeout flicker
  • Loading branch information
gfwilliams committed May 13, 2021
2 parents b3a3dae + d642ad5 commit 2b356a2
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 46 deletions.
2 changes: 1 addition & 1 deletion apps.json
Expand Up @@ -3044,7 +3044,7 @@
"name": "Gadgetbridge Music Controls",
"shortName":"Music Controls",
"icon": "icon.png",
"version":"0.04",
"version":"0.05",
"description": "Control the music on your Gadgetbridge-connected phone",
"tags": "tools,bluetooth,gadgetbridge,music",
"type":"app",
Expand Down
1 change: 1 addition & 0 deletions apps/gbmusic/ChangeLog
Expand Up @@ -2,3 +2,4 @@
0.02: Increase text brightness, improve controls, (try to) reduce memory usage
0.03: Only auto-start if active app is a clock, auto close after 1 hour of inactivity
0.04: Setting to disable touch controls, minor bugfix
0.05: Setting to disable double/triple press control, remove touch controls setting, reduce fadeout flicker
5 changes: 3 additions & 2 deletions apps/gbmusic/README.md
Expand Up @@ -22,8 +22,9 @@ You can change these under `Settings`->`App/Widget Settings`->`Music Controls`.
Automatically load the app when you play music and close when the music stops.
(If the app opened automatically, it closes after music has been paused for 5 minutes.)

**Touch**:
Enable touch controls?
**Simple button**:
Disable double/triple pressing Button 2: always simply toggle play/pause.
(For music players which handle multiple button presses themselves.)

## Controls

Expand Down
81 changes: 50 additions & 31 deletions apps/gbmusic/app.js
Expand Up @@ -13,10 +13,6 @@ let info = {
};
const POUT = 300000; // auto close timeout when paused: 5 minutes (in ms)
const IOUT = 3600000; // auto close timeout for inactivity: 1 hour (in ms)
// Touch controls? 0: off, 1: when LCD on, 2: always
let s = require("Storage").readJSON("gbmusic.json", 1) || {};
const TCTL = ("touch" in s) ? (s.touch|0)%3 : 1;
delete s;

///////////////////////
// Self-repeating timeouts
Expand All @@ -42,7 +38,7 @@ function fadeOut() {
if (!Bangle.isLCDOn() || !fade) {
return;
}
drawMusic();
drawMusic(false); // don't clear: draw over existing text to prevent flicker
setTimeout(fadeOut, 500);
}
function brightness() {
Expand Down Expand Up @@ -131,7 +127,7 @@ function f2hex(f) {
return ("00"+(Math.round(f*255)).toString(16)).substr(-2);
}
/**
* @param name
* @param {string} name - musicinfo property "num"/"artist"/"album"/"track"
* @return {string} Semi-random color to use for given info
*/
function infoColor(name) {
Expand Down Expand Up @@ -174,7 +170,6 @@ function trackColor() {
////////////////////
/**
* Draw date and time
* @return {*}
*/
function drawDateTime() {
const now = new Date;
Expand Down Expand Up @@ -209,8 +204,9 @@ function drawDateTime() {

/**
* Draw track number and total count
* @param {boolean} clr - Clear area before redrawing?
*/
function drawNum() {
function drawNum(clr) {
let num = "";
if ("n" in info && info.n>0) {
num = "#"+info.n;
Expand All @@ -220,9 +216,11 @@ function drawNum() {
}
g.reset();
g.setFont("Vector", 30)
.setFontAlign(1, -1) // top right
.clearRect(225, 30, 120, 60)
.drawString(num, 225, 30);
.setFontAlign(1, -1); // top right
if (clr) {
g.clearRect(225, 30, 120, 60);
}
g.drawString(num, 225, 30);
}
/**
* Clear rectangle used by track title
Expand All @@ -232,8 +230,9 @@ function clearTrack() {
}
/**
* Draw track title
* @param {boolean} clr - Clear area before redrawing?
*/
function drawTrack() {
function drawTrack(clr) {
let size = fitText(info.track);
if (size<25) {
// the title is too long: start the scroller
Expand All @@ -250,7 +249,9 @@ function drawTrack() {
g.setFont("Vector", size)
.setFontAlign(0, 1) // center bottom
.setColor(trackColor());
clearTrack();
if (clr) {
clearTrack();
}
g.drawString(info.track, 119, 109);
}
/**
Expand All @@ -270,8 +271,9 @@ function drawScroller() {

/**
* Draw track artist and album
* @param {boolean} clr - Clear area before redrawing?
*/
function drawArtistAlbum() {
function drawArtistAlbum(clr) {
// we just use small enough fonts to make these always fit
// calculate stuff before clear+redraw
const aCol = infoColor("artist");
Expand All @@ -285,7 +287,9 @@ function drawArtistAlbum() {
bSiz = 20;
}
g.reset();
g.clearRect(0, 120, 240, 189);
if (clr) {
g.clearRect(0, 120, 240, 189);
}
let top = 124;
if (info.artist) {
g.setFont("Vector", aSiz)
Expand Down Expand Up @@ -347,7 +351,6 @@ function controlColor(ctrl) {
return (ctrl in tCommand) ? "#ff0000" : "#008800";
}
function drawControl(ctrl, x, y) {
if (!TCTL) {return;}
g.setColor(controlColor(ctrl));
const s = 20;
if (stat!==controlState) {
Expand Down Expand Up @@ -379,18 +382,22 @@ function drawControls() {
controlState = stat;
}

function drawMusic() {
drawNum();
drawTrack();
drawArtistAlbum();
/**
* @param {boolean} [clr=true] Clear area before redrawing?
*/
function drawMusic(clr) {
clr = !(clr===false); // undefined means yes
drawNum(clr);
drawTrack(clr);
drawArtistAlbum(clr);
}

////////////////////////
// GB event handlers
///////////////////////
/**
* Update music info
* @param e
* @param {Object} e - Gadgetbridge musicinfo event
*/
function musicInfo(e) {
info = e;
Expand All @@ -410,7 +417,11 @@ function musicInfo(e) {
}
}

let tPxt, tIxt;
let tPxt, tIxt; // Timeouts to eXiT when Paused/Inactive for too long
/**
* Update music state
* @param {Object} e - Gadgetbridge musicstate event
*/
function musicState(e) {
stat = e.state;
// if paused for five minutes, load the clock
Expand Down Expand Up @@ -446,6 +457,7 @@ function musicState(e) {
}
}
if (Bangle.isLCDOn()) {
drawMusic(false); // redraw in case we were fading out but resumed play
drawControls();
}
}
Expand Down Expand Up @@ -473,11 +485,19 @@ function startButtonWatches() {
tPress = setTimeout(() => {Bangle.showLauncher();}, 3000);
}
}, BTN2, {repeat: true, edge: "rising"});
setWatch(() => {
nPress++;
clearTimeout(tPress);
tPress = setTimeout(handleButton2Press, 500);
}, BTN2, {repeat: true, edge: "falling"});
const s = require("Storage").readJSON("gbmusic.json", 1) || {};
if (s.simpleButton) {
setWatch(() => {
clearTimeout(tPress);
togglePlay();
}, BTN2, {repeat: true, edge: "falling"});
} else {
setWatch(() => {
nPress++;
clearTimeout(tPress);
tPress = setTimeout(handleButton2Press, 500);
}, BTN2, {repeat: true, edge: "falling"});
}
}
function handleButton2Press() {
tPress = null;
Expand All @@ -500,7 +520,7 @@ function handleButton2Press() {
let tCommand = {};
/**
* Send command and highlight corresponding control
* @param command "play/pause/next/previous/volumeup/volumedown"
* @param {string} command - "play"/"pause"/"next"/"previous"/"volumeup"/"volumedown"
*/
function sendCommand(command) {
Bluetooth.println(JSON.stringify({t: "music", n: command}));
Expand All @@ -520,9 +540,8 @@ function togglePlay() {
sendCommand(stat==="play" ? "pause" : "play");
}
function startTouchWatches() {
if (!TCTL) {return;}
Bangle.on("touch", side => {
if (TCTL<2 && !Bangle.isLCDOn()) {return;}
if (!Bangle.isLCDOn()) {return;} // for <2v10 firmware
switch(side) {
case 1:
sendCommand(stat==="play" ? "pause" : "previous");
Expand All @@ -535,7 +554,7 @@ function startTouchWatches() {
}
});
Bangle.on("swipe", dir => {
if (TCTL<2 && !Bangle.isLCDOn()) {return;}
if (!Bangle.isLCDOn()) {return;} // for <2v10 firmware
sendCommand(dir===1 ? "previous" : "next");
});
}
Expand Down
26 changes: 14 additions & 12 deletions apps/gbmusic/settings.js
Expand Up @@ -5,12 +5,11 @@
const SETTINGS_FILE = "gbmusic.json",
storage = require("Storage"),
translate = require("locale").translate;
const TOUCH_OPTIONS = ["Off", "When LCD on", "Always"];

// initialize with default settings...
let s = {
autoStart: true,
touch: 1,
simpleButton: false,
};
// ...and overwrite them with any saved values
// This way saved values are preserved if a new version adds more settings
Expand All @@ -19,24 +18,27 @@
s[key] = saved[key];
}

function save(key, value) {
s[key] = value;
storage.write(SETTINGS_FILE, s);
function save(key) {
return function (value) {
s[key] = value;
storage.write(SETTINGS_FILE, s);
}
}

const yesNo = (v) => translate(v ? "Yes" : "No");
let menu = {
"": {"title": "Music Control"},
};
menu[translate("< Back")] = back;
menu[translate("Auto start")] = {
value: s.autoStart,
format: v => translate(v ? "Yes" : "No"),
onchange: v => {save("autoStart", v);},
value: !!s.autoStart,
format: yesNo,
onchange: save("autoStart"),
};
menu[translate("Touch")] = {
value: s.touch|0,
format: v => translate(TOUCH_OPTIONS[(v+3)%3]),
onchange: v => {save("touch", (v+3)%3);},
menu[translate("Simple button")] = {
value: !!s.simpleButton,
format: yesNo,
onchange: save("simpleButton"),
};

E.showMenu(menu);
Expand Down

0 comments on commit 2b356a2

Please sign in to comment.