1
0
Fork 0

Merge pull request #741 from rigrig/gbmusic

gbmusic: Setting to disable double/triple press control, remove touch controls setting, reduce fadeout flicker
master
Gordon Williams 2021-05-13 15:56:03 +01:00 committed by GitHub
commit 2b356a22f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 69 additions and 46 deletions

View File

@ -3044,7 +3044,7 @@
"name": "Gadgetbridge Music Controls", "name": "Gadgetbridge Music Controls",
"shortName":"Music Controls", "shortName":"Music Controls",
"icon": "icon.png", "icon": "icon.png",
"version":"0.04", "version":"0.05",
"description": "Control the music on your Gadgetbridge-connected phone", "description": "Control the music on your Gadgetbridge-connected phone",
"tags": "tools,bluetooth,gadgetbridge,music", "tags": "tools,bluetooth,gadgetbridge,music",
"type":"app", "type":"app",

View File

@ -2,3 +2,4 @@
0.02: Increase text brightness, improve controls, (try to) reduce memory usage 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.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.04: Setting to disable touch controls, minor bugfix
0.05: Setting to disable double/triple press control, remove touch controls setting, reduce fadeout flicker

View File

@ -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. 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.) (If the app opened automatically, it closes after music has been paused for 5 minutes.)
**Touch**: **Simple button**:
Enable touch controls? Disable double/triple pressing Button 2: always simply toggle play/pause.
(For music players which handle multiple button presses themselves.)
## Controls ## Controls

View File

@ -13,10 +13,6 @@ let info = {
}; };
const POUT = 300000; // auto close timeout when paused: 5 minutes (in ms) const POUT = 300000; // auto close timeout when paused: 5 minutes (in ms)
const IOUT = 3600000; // auto close timeout for inactivity: 1 hour (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 // Self-repeating timeouts
@ -42,7 +38,7 @@ function fadeOut() {
if (!Bangle.isLCDOn() || !fade) { if (!Bangle.isLCDOn() || !fade) {
return; return;
} }
drawMusic(); drawMusic(false); // don't clear: draw over existing text to prevent flicker
setTimeout(fadeOut, 500); setTimeout(fadeOut, 500);
} }
function brightness() { function brightness() {
@ -131,7 +127,7 @@ function f2hex(f) {
return ("00"+(Math.round(f*255)).toString(16)).substr(-2); 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 * @return {string} Semi-random color to use for given info
*/ */
function infoColor(name) { function infoColor(name) {
@ -174,7 +170,6 @@ function trackColor() {
//////////////////// ////////////////////
/** /**
* Draw date and time * Draw date and time
* @return {*}
*/ */
function drawDateTime() { function drawDateTime() {
const now = new Date; const now = new Date;
@ -209,8 +204,9 @@ function drawDateTime() {
/** /**
* Draw track number and total count * Draw track number and total count
* @param {boolean} clr - Clear area before redrawing?
*/ */
function drawNum() { function drawNum(clr) {
let num = ""; let num = "";
if ("n" in info && info.n>0) { if ("n" in info && info.n>0) {
num = "#"+info.n; num = "#"+info.n;
@ -220,9 +216,11 @@ function drawNum() {
} }
g.reset(); g.reset();
g.setFont("Vector", 30) g.setFont("Vector", 30)
.setFontAlign(1, -1) // top right .setFontAlign(1, -1); // top right
.clearRect(225, 30, 120, 60) if (clr) {
.drawString(num, 225, 30); g.clearRect(225, 30, 120, 60);
}
g.drawString(num, 225, 30);
} }
/** /**
* Clear rectangle used by track title * Clear rectangle used by track title
@ -232,8 +230,9 @@ function clearTrack() {
} }
/** /**
* Draw track title * Draw track title
* @param {boolean} clr - Clear area before redrawing?
*/ */
function drawTrack() { function drawTrack(clr) {
let size = fitText(info.track); let size = fitText(info.track);
if (size<25) { if (size<25) {
// the title is too long: start the scroller // the title is too long: start the scroller
@ -250,7 +249,9 @@ function drawTrack() {
g.setFont("Vector", size) g.setFont("Vector", size)
.setFontAlign(0, 1) // center bottom .setFontAlign(0, 1) // center bottom
.setColor(trackColor()); .setColor(trackColor());
clearTrack(); if (clr) {
clearTrack();
}
g.drawString(info.track, 119, 109); g.drawString(info.track, 119, 109);
} }
/** /**
@ -270,8 +271,9 @@ function drawScroller() {
/** /**
* Draw track artist and album * 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 // we just use small enough fonts to make these always fit
// calculate stuff before clear+redraw // calculate stuff before clear+redraw
const aCol = infoColor("artist"); const aCol = infoColor("artist");
@ -285,7 +287,9 @@ function drawArtistAlbum() {
bSiz = 20; bSiz = 20;
} }
g.reset(); g.reset();
g.clearRect(0, 120, 240, 189); if (clr) {
g.clearRect(0, 120, 240, 189);
}
let top = 124; let top = 124;
if (info.artist) { if (info.artist) {
g.setFont("Vector", aSiz) g.setFont("Vector", aSiz)
@ -347,7 +351,6 @@ function controlColor(ctrl) {
return (ctrl in tCommand) ? "#ff0000" : "#008800"; return (ctrl in tCommand) ? "#ff0000" : "#008800";
} }
function drawControl(ctrl, x, y) { function drawControl(ctrl, x, y) {
if (!TCTL) {return;}
g.setColor(controlColor(ctrl)); g.setColor(controlColor(ctrl));
const s = 20; const s = 20;
if (stat!==controlState) { if (stat!==controlState) {
@ -379,10 +382,14 @@ function drawControls() {
controlState = stat; controlState = stat;
} }
function drawMusic() { /**
drawNum(); * @param {boolean} [clr=true] Clear area before redrawing?
drawTrack(); */
drawArtistAlbum(); function drawMusic(clr) {
clr = !(clr===false); // undefined means yes
drawNum(clr);
drawTrack(clr);
drawArtistAlbum(clr);
} }
//////////////////////// ////////////////////////
@ -390,7 +397,7 @@ function drawMusic() {
/////////////////////// ///////////////////////
/** /**
* Update music info * Update music info
* @param e * @param {Object} e - Gadgetbridge musicinfo event
*/ */
function musicInfo(e) { function musicInfo(e) {
info = e; info = e;
@ -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) { function musicState(e) {
stat = e.state; stat = e.state;
// if paused for five minutes, load the clock // if paused for five minutes, load the clock
@ -446,6 +457,7 @@ function musicState(e) {
} }
} }
if (Bangle.isLCDOn()) { if (Bangle.isLCDOn()) {
drawMusic(false); // redraw in case we were fading out but resumed play
drawControls(); drawControls();
} }
} }
@ -473,11 +485,19 @@ function startButtonWatches() {
tPress = setTimeout(() => {Bangle.showLauncher();}, 3000); tPress = setTimeout(() => {Bangle.showLauncher();}, 3000);
} }
}, BTN2, {repeat: true, edge: "rising"}); }, BTN2, {repeat: true, edge: "rising"});
setWatch(() => { const s = require("Storage").readJSON("gbmusic.json", 1) || {};
nPress++; if (s.simpleButton) {
clearTimeout(tPress); setWatch(() => {
tPress = setTimeout(handleButton2Press, 500); clearTimeout(tPress);
}, BTN2, {repeat: true, edge: "falling"}); togglePlay();
}, BTN2, {repeat: true, edge: "falling"});
} else {
setWatch(() => {
nPress++;
clearTimeout(tPress);
tPress = setTimeout(handleButton2Press, 500);
}, BTN2, {repeat: true, edge: "falling"});
}
} }
function handleButton2Press() { function handleButton2Press() {
tPress = null; tPress = null;
@ -500,7 +520,7 @@ function handleButton2Press() {
let tCommand = {}; let tCommand = {};
/** /**
* Send command and highlight corresponding control * 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) { function sendCommand(command) {
Bluetooth.println(JSON.stringify({t: "music", n: command})); Bluetooth.println(JSON.stringify({t: "music", n: command}));
@ -520,9 +540,8 @@ function togglePlay() {
sendCommand(stat==="play" ? "pause" : "play"); sendCommand(stat==="play" ? "pause" : "play");
} }
function startTouchWatches() { function startTouchWatches() {
if (!TCTL) {return;}
Bangle.on("touch", side => { Bangle.on("touch", side => {
if (TCTL<2 && !Bangle.isLCDOn()) {return;} if (!Bangle.isLCDOn()) {return;} // for <2v10 firmware
switch(side) { switch(side) {
case 1: case 1:
sendCommand(stat==="play" ? "pause" : "previous"); sendCommand(stat==="play" ? "pause" : "previous");
@ -535,7 +554,7 @@ function startTouchWatches() {
} }
}); });
Bangle.on("swipe", dir => { Bangle.on("swipe", dir => {
if (TCTL<2 && !Bangle.isLCDOn()) {return;} if (!Bangle.isLCDOn()) {return;} // for <2v10 firmware
sendCommand(dir===1 ? "previous" : "next"); sendCommand(dir===1 ? "previous" : "next");
}); });
} }

View File

@ -5,12 +5,11 @@
const SETTINGS_FILE = "gbmusic.json", const SETTINGS_FILE = "gbmusic.json",
storage = require("Storage"), storage = require("Storage"),
translate = require("locale").translate; translate = require("locale").translate;
const TOUCH_OPTIONS = ["Off", "When LCD on", "Always"];
// initialize with default settings... // initialize with default settings...
let s = { let s = {
autoStart: true, autoStart: true,
touch: 1, simpleButton: false,
}; };
// ...and overwrite them with any saved values // ...and overwrite them with any saved values
// This way saved values are preserved if a new version adds more settings // This way saved values are preserved if a new version adds more settings
@ -19,24 +18,27 @@
s[key] = saved[key]; s[key] = saved[key];
} }
function save(key, value) { function save(key) {
s[key] = value; return function (value) {
storage.write(SETTINGS_FILE, s); s[key] = value;
storage.write(SETTINGS_FILE, s);
}
} }
const yesNo = (v) => translate(v ? "Yes" : "No");
let menu = { let menu = {
"": {"title": "Music Control"}, "": {"title": "Music Control"},
}; };
menu[translate("< Back")] = back; menu[translate("< Back")] = back;
menu[translate("Auto start")] = { menu[translate("Auto start")] = {
value: s.autoStart, value: !!s.autoStart,
format: v => translate(v ? "Yes" : "No"), format: yesNo,
onchange: v => {save("autoStart", v);}, onchange: save("autoStart"),
}; };
menu[translate("Touch")] = { menu[translate("Simple button")] = {
value: s.touch|0, value: !!s.simpleButton,
format: v => translate(TOUCH_OPTIONS[(v+3)%3]), format: yesNo,
onchange: v => {save("touch", (v+3)%3);}, onchange: save("simpleButton"),
}; };
E.showMenu(menu); E.showMenu(menu);