gbmusic: improve controls

Using BTN3-rising for `volumedown` meant that everytime you hold BTN3 to
reload the watch it first messes with your volume. So now we listen for
falling edge only.
Also add double/triple pressing BTN2 for next/previous.
And fix a bug with the scroller interval.
pull/717/head
Richard de Boer 2021-04-10 18:32:20 +02:00
parent 2de7a2dea0
commit 66c5284d23
3 changed files with 87 additions and 89 deletions

View File

@ -1,2 +1,2 @@
0.01: Initial version
0.02: Increase text brightness, try to improve memory usage
0.02: Increase text brightness, improve controls, (try to) reduce memory usage

View File

@ -23,9 +23,13 @@ You can change this under `Settings`->`App/Widget Settings`->`Music Controls`.
## Controls
### Buttons
* Button 1: Volume up (hold to repeat)
* Button 2: Toggle play/pause, long-press for menu
* Button 3: Volume down (hold to repeat, but remember that holding for too long resets your watch)
* Button 1: Volume up
* Button 2:
- Single press: toggle play/pause
- Double press: next song
- Triple press: previous song
- Long-press: open application launcher
* Button 3: Volume down
### Touch
* Left: pause/previous song

View File

@ -50,7 +50,7 @@ function brightness() {
// Scroll long track names
// use an interval to get smooth movement
let offset = null, // scroll Offset: null = no scrolling
scrollI;
iScroll;
function scroll() {
offset += 10;
drawScroller();
@ -61,16 +61,16 @@ function scrollStart() {
}
offset = 0;
if (Bangle.isLCDOn()) {
if (!scrollI) {
scrollI = setInterval(scroll, 200);
if (!iScroll) {
iScroll = setInterval(scroll, 200);
}
drawScroller();
}
}
function scrollStop() {
if (scrollI) {
clearInterval(scrollI);
scrollI = null;
if (iScroll) {
clearInterval(iScroll);
iScroll = null;
}
offset = null;
}
@ -342,10 +342,6 @@ function drawIcon(icon, x, y, s) {
})[icon](x, y, s);
}
function controlColor(ctrl) {
if (vCmd && ctrl===vCmd) {
// volume button kept pressed down
return "#ff0000";
}
return (ctrl in tCommand) ? "#ff0000" : "#008800";
}
function drawControl(ctrl, x, y) {
@ -431,26 +427,48 @@ function musicState(e) {
// Events
////////////////////
let tLauncher;
// we put starting of watches inside a function, so we can defer it until we
// asked the user about autoStart
function startLauncherWatch() {
// long-press: launcher
// short-press: toggle play/pause
setWatch(function() {
if (tLauncher) {
clearTimeout(tLauncher);
// we put starting of watches inside a function, so we can defer it until
// we asked the user about autoStart
/**
* Start watching for BTN2 presses
*/
let tPress, nPress = 0;
function startButtonWatches() {
// BTN1/3: volume control
// Wait for falling edge to avoid messing with volume while long-pressing BTN3
// to reload the watch (and same for BTN2 for consistency)
setWatch(() => { sendCommand("volumeup"); }, BTN1, {repeat: true, edge: "falling"});
setWatch(() => { sendCommand("volumedown"); }, BTN3, {repeat: true, edge: "falling"});
// BTN2: long-press for launcher, otherwise depends on number of presses
setWatch(() => {
if (nPress===0) {
tPress = setTimeout(() => {Bangle.showLauncher();}, 3000);
}
tLauncher = setTimeout(Bangle.showLauncher, 1000);
}, BTN2, {repeat: true, edge: "rising"});
setWatch(function() {
if (tLauncher) {
clearTimeout(tLauncher);
tLauncher = null;
}
togglePlay();
setWatch(() => {
nPress++;
clearTimeout(tPress);
tPress = setTimeout(handleButton2Press, 500);
}, BTN2, {repeat: true, edge: "falling"});
}
function handleButton2Press() {
tPress = null;
switch(nPress) {
case 1:
togglePlay();
break;
case 2:
sendCommand("next");
break;
case 3:
sendCommand("previous");
break;
default: // invalid
Bangle.buzz(50);
}
nPress = 0;
}
let tCommand = {};
/**
@ -470,46 +488,12 @@ function sendCommand(command) {
drawControls();
}
// BTN1/3: volume control (with repeat after long-press)
let tVol, vCmd;
function volUp() {
volStart("up");
}
function volDown() {
volStart("down");
}
function volStart(dir) {
const command = "volume"+dir;
stopVol();
sendCommand(command);
vCmd = command;
tVol = setTimeout(repeatVol, 500);
}
function repeatVol() {
sendCommand(vCmd);
tVol = setTimeout(repeatVol, 100);
}
function stopVol() {
if (tVol) {
clearTimeout(tVol);
tVol = null;
}
vCmd = null;
drawControls();
}
function startVolWatches() {
setWatch(volUp, BTN1, {repeat: true, edge: "rising"});
setWatch(stopVol, BTN1, {repeat: true, edge: "falling"});
setWatch(volDown, BTN3, {repeat: true, edge: "rising"});
setWatch(stopVol, BTN3, {repeat: true, edge: "falling"});
}
// touch/swipe: navigation
function togglePlay() {
sendCommand(stat==="play" ? "pause" : "play");
}
function startTouchWatches() {
Bangle.on("touch", function(side) {
Bangle.on("touch", side => {
switch(side) {
case 1:
sendCommand(stat==="play" ? "pause" : "previous");
@ -521,10 +505,34 @@ function startTouchWatches() {
togglePlay();
}
});
Bangle.on("swipe", function(dir) {
Bangle.on("swipe", dir => {
sendCommand(dir===1 ? "previous" : "next");
});
}
function startLCDWatch() {
Bangle.on("lcdPower", (on) => {
if (on) {
// redraw and resume scrolling
tick();
drawMusic();
drawControls();
fadeOut();
if (offset!==null) {
drawScroller();
if (!iScroll) {
iScroll = setInterval(scroll, 200);
}
}
} else {
// pause scrolling
if (iScroll) {
clearInterval(iScroll);
iScroll = null;
}
}
});
}
/////////////////////
// Startup
/////////////////////
@ -546,9 +554,9 @@ function startEmulator() {
}
}
function startWatches() {
startVolWatches();
startLauncherWatch();
startButtonWatches();
startTouchWatches();
startLCDWatch();
}
function start() {
@ -576,23 +584,6 @@ function start() {
startWatches();
tick();
startEmulator();
Bangle.on("lcdPower", (on) => {
if (on) {
tick();
drawMusic();
drawControls();
fadeOut();
if (offset!==null) {
drawScroller();
scrollI = setInterval(scroll, 200);
}
} else {
if (scrollI) {
clearInterval(scrollI);
scrollI = null;
}
}
});
}
function init() {
@ -602,23 +593,26 @@ function init() {
// autoloaded: load state was saved by widget
info = saved.info;
stat = saved.state;
delete (saved);
delete saved;
auto = true;
start();
} else {
const s = require("Storage").readJSON("gbmusic.json", 1) || {};
delete saved;
let s = require("Storage").readJSON("gbmusic.json", 1) || {};
if (!("autoStart" in s)) {
// user opened the app, but has not picked a setting yet
// ask them about autoloading now
E.showPrompt(
"Automatically load\n"+
"when playing music?\n",
).then(function(autoStart) {
s.autoStart = autoStart;
).then(choice => {
s.autoStart = choice;
require("Storage").writeJSON("gbmusic.json", s);
delete s;
setTimeout(start, 0);
});
} else {
delete s;
start();
}
}