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.master
parent
2de7a2dea0
commit
66c5284d23
|
|
@ -1,2 +1,2 @@
|
||||||
0.01: Initial version
|
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
|
||||||
|
|
@ -23,9 +23,13 @@ You can change this under `Settings`->`App/Widget Settings`->`Music Controls`.
|
||||||
## Controls
|
## Controls
|
||||||
|
|
||||||
### Buttons
|
### Buttons
|
||||||
* Button 1: Volume up (hold to repeat)
|
* Button 1: Volume up
|
||||||
* Button 2: Toggle play/pause, long-press for menu
|
* Button 2:
|
||||||
* Button 3: Volume down (hold to repeat, but remember that holding for too long resets your watch)
|
- Single press: toggle play/pause
|
||||||
|
- Double press: next song
|
||||||
|
- Triple press: previous song
|
||||||
|
- Long-press: open application launcher
|
||||||
|
* Button 3: Volume down
|
||||||
|
|
||||||
### Touch
|
### Touch
|
||||||
* Left: pause/previous song
|
* Left: pause/previous song
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ function brightness() {
|
||||||
// Scroll long track names
|
// Scroll long track names
|
||||||
// use an interval to get smooth movement
|
// use an interval to get smooth movement
|
||||||
let offset = null, // scroll Offset: null = no scrolling
|
let offset = null, // scroll Offset: null = no scrolling
|
||||||
scrollI;
|
iScroll;
|
||||||
function scroll() {
|
function scroll() {
|
||||||
offset += 10;
|
offset += 10;
|
||||||
drawScroller();
|
drawScroller();
|
||||||
|
|
@ -61,16 +61,16 @@ function scrollStart() {
|
||||||
}
|
}
|
||||||
offset = 0;
|
offset = 0;
|
||||||
if (Bangle.isLCDOn()) {
|
if (Bangle.isLCDOn()) {
|
||||||
if (!scrollI) {
|
if (!iScroll) {
|
||||||
scrollI = setInterval(scroll, 200);
|
iScroll = setInterval(scroll, 200);
|
||||||
}
|
}
|
||||||
drawScroller();
|
drawScroller();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function scrollStop() {
|
function scrollStop() {
|
||||||
if (scrollI) {
|
if (iScroll) {
|
||||||
clearInterval(scrollI);
|
clearInterval(iScroll);
|
||||||
scrollI = null;
|
iScroll = null;
|
||||||
}
|
}
|
||||||
offset = null;
|
offset = null;
|
||||||
}
|
}
|
||||||
|
|
@ -342,10 +342,6 @@ function drawIcon(icon, x, y, s) {
|
||||||
})[icon](x, y, s);
|
})[icon](x, y, s);
|
||||||
}
|
}
|
||||||
function controlColor(ctrl) {
|
function controlColor(ctrl) {
|
||||||
if (vCmd && ctrl===vCmd) {
|
|
||||||
// volume button kept pressed down
|
|
||||||
return "#ff0000";
|
|
||||||
}
|
|
||||||
return (ctrl in tCommand) ? "#ff0000" : "#008800";
|
return (ctrl in tCommand) ? "#ff0000" : "#008800";
|
||||||
}
|
}
|
||||||
function drawControl(ctrl, x, y) {
|
function drawControl(ctrl, x, y) {
|
||||||
|
|
@ -431,26 +427,48 @@ function musicState(e) {
|
||||||
// Events
|
// Events
|
||||||
////////////////////
|
////////////////////
|
||||||
|
|
||||||
let tLauncher;
|
// we put starting of watches inside a function, so we can defer it until
|
||||||
// we put starting of watches inside a function, so we can defer it until we
|
// we asked the user about autoStart
|
||||||
// asked the user about autoStart
|
/**
|
||||||
function startLauncherWatch() {
|
* Start watching for BTN2 presses
|
||||||
// long-press: launcher
|
*/
|
||||||
// short-press: toggle play/pause
|
let tPress, nPress = 0;
|
||||||
setWatch(function() {
|
function startButtonWatches() {
|
||||||
if (tLauncher) {
|
// BTN1/3: volume control
|
||||||
clearTimeout(tLauncher);
|
// 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"});
|
}, BTN2, {repeat: true, edge: "rising"});
|
||||||
setWatch(function() {
|
setWatch(() => {
|
||||||
if (tLauncher) {
|
nPress++;
|
||||||
clearTimeout(tLauncher);
|
clearTimeout(tPress);
|
||||||
tLauncher = null;
|
tPress = setTimeout(handleButton2Press, 500);
|
||||||
}
|
|
||||||
togglePlay();
|
|
||||||
}, BTN2, {repeat: true, edge: "falling"});
|
}, 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 = {};
|
let tCommand = {};
|
||||||
/**
|
/**
|
||||||
|
|
@ -470,46 +488,12 @@ function sendCommand(command) {
|
||||||
drawControls();
|
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
|
// touch/swipe: navigation
|
||||||
function togglePlay() {
|
function togglePlay() {
|
||||||
sendCommand(stat==="play" ? "pause" : "play");
|
sendCommand(stat==="play" ? "pause" : "play");
|
||||||
}
|
}
|
||||||
function startTouchWatches() {
|
function startTouchWatches() {
|
||||||
Bangle.on("touch", function(side) {
|
Bangle.on("touch", side => {
|
||||||
switch(side) {
|
switch(side) {
|
||||||
case 1:
|
case 1:
|
||||||
sendCommand(stat==="play" ? "pause" : "previous");
|
sendCommand(stat==="play" ? "pause" : "previous");
|
||||||
|
|
@ -521,10 +505,34 @@ function startTouchWatches() {
|
||||||
togglePlay();
|
togglePlay();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Bangle.on("swipe", function(dir) {
|
Bangle.on("swipe", dir => {
|
||||||
sendCommand(dir===1 ? "previous" : "next");
|
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
|
// Startup
|
||||||
/////////////////////
|
/////////////////////
|
||||||
|
|
@ -546,9 +554,9 @@ function startEmulator() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function startWatches() {
|
function startWatches() {
|
||||||
startVolWatches();
|
startButtonWatches();
|
||||||
startLauncherWatch();
|
|
||||||
startTouchWatches();
|
startTouchWatches();
|
||||||
|
startLCDWatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
function start() {
|
function start() {
|
||||||
|
|
@ -576,23 +584,6 @@ function start() {
|
||||||
startWatches();
|
startWatches();
|
||||||
tick();
|
tick();
|
||||||
startEmulator();
|
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() {
|
function init() {
|
||||||
|
|
@ -602,23 +593,26 @@ function init() {
|
||||||
// autoloaded: load state was saved by widget
|
// autoloaded: load state was saved by widget
|
||||||
info = saved.info;
|
info = saved.info;
|
||||||
stat = saved.state;
|
stat = saved.state;
|
||||||
delete (saved);
|
delete saved;
|
||||||
auto = true;
|
auto = true;
|
||||||
start();
|
start();
|
||||||
} else {
|
} else {
|
||||||
const s = require("Storage").readJSON("gbmusic.json", 1) || {};
|
delete saved;
|
||||||
|
let s = require("Storage").readJSON("gbmusic.json", 1) || {};
|
||||||
if (!("autoStart" in s)) {
|
if (!("autoStart" in s)) {
|
||||||
// user opened the app, but has not picked a setting yet
|
// user opened the app, but has not picked a setting yet
|
||||||
// ask them about autoloading now
|
// ask them about autoloading now
|
||||||
E.showPrompt(
|
E.showPrompt(
|
||||||
"Automatically load\n"+
|
"Automatically load\n"+
|
||||||
"when playing music?\n",
|
"when playing music?\n",
|
||||||
).then(function(autoStart) {
|
).then(choice => {
|
||||||
s.autoStart = autoStart;
|
s.autoStart = choice;
|
||||||
require("Storage").writeJSON("gbmusic.json", s);
|
require("Storage").writeJSON("gbmusic.json", s);
|
||||||
|
delete s;
|
||||||
setTimeout(start, 0);
|
setTimeout(start, 0);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
delete s;
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue