2021-03-14 21:28:44 +00:00
|
|
|
/* jshint esversion: 6 */
|
|
|
|
/**
|
|
|
|
* Control the music on your Gadgetbridge-connected phone
|
|
|
|
**/
|
2021-03-31 01:22:58 +00:00
|
|
|
let auto = false; // auto close if opened automatically
|
|
|
|
let stat = "";
|
|
|
|
let info = {
|
|
|
|
artist: "",
|
|
|
|
album: "",
|
|
|
|
track: "",
|
|
|
|
n: 0,
|
|
|
|
c: 0,
|
|
|
|
};
|
|
|
|
const TOUT = 300000; // auto close timeout: 5 minutes (in ms)
|
2021-03-14 21:28:44 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
///////////////////////
|
|
|
|
// Self-repeating timeouts
|
|
|
|
///////////////////////
|
2021-03-14 21:28:44 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
// Clock
|
|
|
|
let tock = -1;
|
|
|
|
function tick() {
|
|
|
|
if (!Bangle.isLCDOn()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const now = new Date;
|
|
|
|
if (now.getHours()*60+now.getMinutes()!==tock) {
|
|
|
|
drawDateTime();
|
|
|
|
tock = now.getHours()*60+now.getMinutes();
|
|
|
|
}
|
|
|
|
setTimeout(tick, 1000); // we only show minute precision anyway
|
|
|
|
}
|
2021-03-14 21:28:44 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
// Fade out while paused and auto closing
|
|
|
|
let fade = null;
|
|
|
|
function fadeOut() {
|
|
|
|
if (!Bangle.isLCDOn() || !fade) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
drawMusic();
|
|
|
|
setTimeout(fadeOut, 500);
|
|
|
|
}
|
|
|
|
function brightness() {
|
|
|
|
if (!fade) {
|
|
|
|
return 1;
|
2021-03-14 21:28:44 +00:00
|
|
|
}
|
2021-03-31 01:22:58 +00:00
|
|
|
return Math.max(0, 1-((Date.now()-fade)/TOUT));
|
|
|
|
}
|
2021-03-14 21:28:44 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
// Scroll long track names
|
|
|
|
// use an interval to get smooth movement
|
|
|
|
let offset = null, // scroll Offset: null = no scrolling
|
2021-04-10 16:32:20 +00:00
|
|
|
iScroll;
|
2021-03-31 01:22:58 +00:00
|
|
|
function scroll() {
|
|
|
|
offset += 10;
|
|
|
|
drawScroller();
|
|
|
|
}
|
|
|
|
function scrollStart() {
|
|
|
|
if (offset!==null) {
|
|
|
|
return; // already started
|
2021-03-14 21:28:44 +00:00
|
|
|
}
|
2021-03-31 01:22:58 +00:00
|
|
|
offset = 0;
|
|
|
|
if (Bangle.isLCDOn()) {
|
2021-04-10 16:32:20 +00:00
|
|
|
if (!iScroll) {
|
|
|
|
iScroll = setInterval(scroll, 200);
|
2021-03-14 21:28:44 +00:00
|
|
|
}
|
2021-03-31 01:22:58 +00:00
|
|
|
drawScroller();
|
2021-03-14 21:28:44 +00:00
|
|
|
}
|
2021-03-31 01:22:58 +00:00
|
|
|
}
|
|
|
|
function scrollStop() {
|
2021-04-10 16:32:20 +00:00
|
|
|
if (iScroll) {
|
|
|
|
clearInterval(iScroll);
|
|
|
|
iScroll = null;
|
2021-03-30 20:36:04 +00:00
|
|
|
}
|
2021-03-31 01:22:58 +00:00
|
|
|
offset = null;
|
|
|
|
}
|
2021-03-30 20:36:04 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
/**
|
|
|
|
* @param {string} text
|
|
|
|
* @return {number} Maximum font size to make text fit on screen
|
|
|
|
*/
|
|
|
|
function fitText(text) {
|
|
|
|
if (!text.length) {
|
|
|
|
return Infinity;
|
|
|
|
}
|
|
|
|
// make a guess, then shrink/grow until it fits
|
|
|
|
const test = (s) => g.setFont("Vector", s).stringWidth(text);
|
|
|
|
let best = Math.floor(24000/test(100));
|
|
|
|
if (test(best)===240) { // good guess!
|
|
|
|
return best;
|
|
|
|
}
|
|
|
|
if (test(best)<240) {
|
|
|
|
do {
|
|
|
|
best++;
|
|
|
|
} while(test(best)<=240);
|
|
|
|
return best-1;
|
|
|
|
}
|
|
|
|
// width > 240
|
|
|
|
do {
|
|
|
|
best--;
|
|
|
|
} while(test(best)>240);
|
|
|
|
return best;
|
|
|
|
}
|
2021-03-30 20:36:04 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
/**
|
|
|
|
* @param {string} text
|
|
|
|
* @return {number} Randomish but deterministic number from 0-360 for text
|
|
|
|
*/
|
|
|
|
function textCode(text) {
|
|
|
|
"ram";
|
|
|
|
let code = 0;
|
|
|
|
for(let i = 0; i<text.length; i++) {
|
|
|
|
code += text.charCodeAt(i);
|
|
|
|
}
|
|
|
|
return code%360;
|
|
|
|
}
|
|
|
|
// dark magic
|
|
|
|
function hsv2rgb(h, s, v) {
|
|
|
|
const f = (n) => {
|
|
|
|
const k = (n+h/60)%6;
|
|
|
|
return v-v*s*Math.max(Math.min(k, 4-k, 1), 0);
|
|
|
|
};
|
|
|
|
return {r: f(5), g: f(3), b: f(1)};
|
|
|
|
}
|
|
|
|
function f2hex(f) {
|
|
|
|
return ("00"+(Math.round(f*255)).toString(16)).substr(-2);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @param name
|
|
|
|
* @return {string} Semi-random color to use for given info
|
|
|
|
*/
|
|
|
|
function infoColor(name) {
|
|
|
|
let h, s, v;
|
|
|
|
if (name==="num") {
|
|
|
|
// always white
|
|
|
|
h = 0;
|
|
|
|
s = 0;
|
|
|
|
} else {
|
|
|
|
// make color depend deterministically on info
|
|
|
|
let code = 0;
|
|
|
|
switch(name) {
|
|
|
|
case "track":
|
|
|
|
code += textCode(info.track);
|
|
|
|
// fallthrough: also use album+artist
|
|
|
|
case "album":
|
|
|
|
code += textCode(info.album);
|
|
|
|
// fallthrough: also use artist
|
|
|
|
default:
|
|
|
|
code += textCode(info[name]);
|
|
|
|
}
|
|
|
|
h = code%360;
|
|
|
|
s = 0.7;
|
|
|
|
}
|
|
|
|
v = brightness();
|
|
|
|
const rgb = hsv2rgb(h, s, v);
|
|
|
|
return "#"+f2hex(rgb.r)+f2hex(rgb.g)+f2hex(rgb.b);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Remember track color until info changes
|
|
|
|
* Because we need this every time we move the scroller
|
|
|
|
* @return {string}
|
|
|
|
*/
|
|
|
|
function trackColor() {
|
|
|
|
if (!("track_color" in info) || fade) {
|
|
|
|
info.track_color = infoColor("track");
|
|
|
|
}
|
|
|
|
return info.track_color;
|
|
|
|
}
|
2021-03-14 21:28:44 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
////////////////////
|
|
|
|
// Drawing functions
|
|
|
|
////////////////////
|
|
|
|
/**
|
|
|
|
* Draw date and time
|
|
|
|
* @return {*}
|
|
|
|
*/
|
|
|
|
function drawDateTime() {
|
|
|
|
const now = new Date;
|
|
|
|
const l = require("locale");
|
|
|
|
const is12 = (require("Storage").readJSON("setting.json", 1) || {})["12hour"];
|
|
|
|
let time;
|
|
|
|
if (is12) {
|
|
|
|
const d12 = new Date(now.getTime());
|
|
|
|
const hour = d12.getHours();
|
|
|
|
if (hour===0) {
|
|
|
|
d12.setHours(12);
|
|
|
|
} else if (hour>12) {
|
|
|
|
d12.setHours(hour-12);
|
|
|
|
}
|
|
|
|
time = l.time(d12, true)+l.meridian(now);
|
|
|
|
} else {
|
|
|
|
time = l.time(now, true);
|
2021-03-14 21:28:44 +00:00
|
|
|
}
|
2021-03-31 01:22:58 +00:00
|
|
|
g.reset();
|
|
|
|
g.setFont("Vector", 24)
|
|
|
|
.setFontAlign(-1, -1) // top left
|
|
|
|
.clearRect(10, 30, 119, 54)
|
|
|
|
.drawString(time, 10, 30);
|
2021-03-14 21:28:44 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
const date = require("locale").date(now, true);
|
|
|
|
g.setFont("Vector", 16)
|
|
|
|
.setFontAlign(0, 1) // bottom center
|
|
|
|
.setClipRect(35, 198, 199, 214)
|
|
|
|
.clearRect(31, 198, 199, 214)
|
|
|
|
.drawString(date, 119, 240-26);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Draw track number and total count
|
|
|
|
*/
|
|
|
|
function drawNum() {
|
|
|
|
let num = "";
|
|
|
|
if ("n" in info && info.n>0) {
|
|
|
|
num = "#"+info.n;
|
|
|
|
if ("c" in info && info.c>0) { // I've seen { c:-1 }
|
|
|
|
num += "/"+info.c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
g.reset();
|
|
|
|
g.setFont("Vector", 30)
|
|
|
|
.setFontAlign(1, -1) // top right
|
|
|
|
.clearRect(225, 30, 120, 60)
|
|
|
|
.drawString(num, 225, 30);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Clear rectangle used by track title
|
|
|
|
*/
|
|
|
|
function clearTrack() {
|
|
|
|
g.clearRect(0, 60, 239, 119);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Draw track title
|
|
|
|
*/
|
|
|
|
function drawTrack() {
|
|
|
|
let size = fitText(info.track);
|
|
|
|
if (size<25) {
|
|
|
|
// the title is too long: start the scroller
|
|
|
|
scrollStart();
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
scrollStop();
|
|
|
|
}
|
|
|
|
// stationary track
|
|
|
|
if (size>40) {
|
|
|
|
size = 40;
|
|
|
|
}
|
|
|
|
g.reset();
|
|
|
|
g.setFont("Vector", size)
|
|
|
|
.setFontAlign(0, 1) // center bottom
|
|
|
|
.setColor(trackColor());
|
|
|
|
clearTrack();
|
|
|
|
g.drawString(info.track, 119, 109);
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Draw scrolling track title
|
|
|
|
*/
|
|
|
|
function drawScroller() {
|
|
|
|
g.reset();
|
|
|
|
g.setFont("Vector", 40);
|
|
|
|
const w = g.stringWidth(info.track)+40;
|
|
|
|
offset = offset%w;
|
|
|
|
g.setFontAlign(-1, 1) // left bottom
|
|
|
|
.setColor(trackColor());
|
|
|
|
clearTrack();
|
|
|
|
g.drawString(info.track, -offset+40, 109)
|
|
|
|
.drawString(info.track, -offset+40+w, 109);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Draw track artist and album
|
|
|
|
*/
|
|
|
|
function drawArtistAlbum() {
|
|
|
|
// we just use small enough fonts to make these always fit
|
|
|
|
// calculate stuff before clear+redraw
|
|
|
|
const aCol = infoColor("artist");
|
|
|
|
const bCol = infoColor("album");
|
|
|
|
let aSiz = fitText(info.artist);
|
|
|
|
if (aSiz>30) {
|
|
|
|
aSiz = 30;
|
|
|
|
}
|
|
|
|
let bSiz = fitText(info.album);
|
|
|
|
if (bSiz>20) {
|
|
|
|
bSiz = 20;
|
|
|
|
}
|
|
|
|
g.reset();
|
|
|
|
g.clearRect(0, 120, 240, 189);
|
|
|
|
let top = 124;
|
|
|
|
if (info.artist) {
|
|
|
|
g.setFont("Vector", aSiz)
|
|
|
|
.setFontAlign(0, -1) // center top
|
|
|
|
.setColor(aCol)
|
|
|
|
.drawString(info.artist, 119, top);
|
|
|
|
top += aSiz+4; // fit album neatly under artist
|
|
|
|
}
|
|
|
|
if (info.album) {
|
|
|
|
g.setFont("Vector", bSiz)
|
|
|
|
.setFontAlign(0, -1) // center top
|
|
|
|
.setColor(bCol)
|
|
|
|
.drawString(info.album, 119, top);
|
2021-03-14 21:28:44 +00:00
|
|
|
}
|
2021-03-31 01:22:58 +00:00
|
|
|
}
|
2021-03-30 20:36:04 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param {string} icon Icon name
|
|
|
|
* @param {number} x
|
|
|
|
* @param {number} y
|
|
|
|
* @param {number} s Icon size
|
|
|
|
*/
|
|
|
|
function drawIcon(icon, x, y, s) {
|
|
|
|
({
|
2021-03-14 21:28:44 +00:00
|
|
|
pause: function(x, y, s) {
|
2021-03-27 18:17:18 +00:00
|
|
|
const w1 = s/3;
|
|
|
|
g.drawRect(x, y, x+w1, y+s);
|
|
|
|
g.drawRect(x+s-w1, y, x+s, y+s);
|
2021-03-14 21:28:44 +00:00
|
|
|
},
|
|
|
|
play: function(x, y, s) {
|
|
|
|
g.drawPoly([
|
|
|
|
x, y,
|
|
|
|
x+s, y+s/2,
|
|
|
|
x, y+s,
|
2021-03-27 18:17:18 +00:00
|
|
|
], true);
|
2021-03-14 21:28:44 +00:00
|
|
|
},
|
|
|
|
previous: function(x, y, s) {
|
2021-03-27 18:17:18 +00:00
|
|
|
const w2 = s*1/5;
|
2021-03-14 21:28:44 +00:00
|
|
|
g.drawPoly([
|
|
|
|
x+s, y,
|
|
|
|
x+w2, y+s/2,
|
|
|
|
x+s, y+s,
|
2021-03-27 18:17:18 +00:00
|
|
|
], true);
|
|
|
|
g.drawRect(x, y, x+w2, y+s);
|
2021-03-14 21:28:44 +00:00
|
|
|
},
|
|
|
|
next: function(x, y, s) {
|
2021-03-27 18:17:18 +00:00
|
|
|
const w2 = s*4/5;
|
2021-03-14 21:28:44 +00:00
|
|
|
g.drawPoly([
|
|
|
|
x, y,
|
|
|
|
x+w2, y+s/2,
|
|
|
|
x, y+s,
|
2021-03-27 18:17:18 +00:00
|
|
|
], true);
|
|
|
|
g.drawRect(x+w2, y, x+s, y+s);
|
2021-03-14 21:28:44 +00:00
|
|
|
},
|
2021-03-31 01:22:58 +00:00
|
|
|
})[icon](x, y, s);
|
|
|
|
}
|
|
|
|
function controlColor(ctrl) {
|
|
|
|
return (ctrl in tCommand) ? "#ff0000" : "#008800";
|
|
|
|
}
|
|
|
|
function drawControl(ctrl, x, y) {
|
|
|
|
g.setColor(controlColor(ctrl));
|
|
|
|
const s = 20;
|
|
|
|
if (stat!==controlState) {
|
|
|
|
g.clearRect(x, y, x+s, y+s);
|
2021-03-27 18:17:18 +00:00
|
|
|
}
|
2021-03-31 01:22:58 +00:00
|
|
|
drawIcon(ctrl, x, y, s);
|
|
|
|
}
|
|
|
|
let controlState;
|
|
|
|
function drawControls() {
|
|
|
|
g.reset();
|
|
|
|
if (stat==="play") {
|
|
|
|
// left touch
|
|
|
|
drawControl("pause", 10, 190);
|
|
|
|
// right touch
|
|
|
|
drawControl("next", 200, 190);
|
|
|
|
} else {
|
|
|
|
drawControl("previous", 10, 190);
|
|
|
|
drawControl("play", 200, 190);
|
|
|
|
}
|
|
|
|
g.setFont("6x8", 2);
|
|
|
|
// BTN1
|
|
|
|
g.setFontAlign(1, -1);
|
|
|
|
g.setColor(controlColor("volumeup"));
|
|
|
|
g.drawString("+", 240, 30);
|
|
|
|
// BTN2
|
|
|
|
g.setFontAlign(1, 1);
|
|
|
|
g.setColor(controlColor("volumedown"));
|
|
|
|
g.drawString("-", 240, 210);
|
|
|
|
controlState = stat;
|
|
|
|
}
|
2021-03-30 20:36:04 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
function drawMusic() {
|
|
|
|
drawNum();
|
|
|
|
drawTrack();
|
|
|
|
drawArtistAlbum();
|
|
|
|
}
|
2021-03-30 20:36:04 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
////////////////////////
|
|
|
|
// GB event handlers
|
|
|
|
///////////////////////
|
|
|
|
/**
|
|
|
|
* Update music info
|
|
|
|
* @param e
|
|
|
|
*/
|
|
|
|
function musicInfo(e) {
|
|
|
|
info = e;
|
|
|
|
delete (info.t);
|
|
|
|
offset = null;
|
|
|
|
if (Bangle.isLCDOn()) {
|
|
|
|
drawMusic();
|
2021-03-30 20:36:04 +00:00
|
|
|
}
|
2021-03-31 01:22:58 +00:00
|
|
|
}
|
2021-03-30 20:36:04 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
let tXit;
|
|
|
|
function musicState(e) {
|
|
|
|
stat = e.state;
|
|
|
|
// if paused for five minutes, load the clock
|
|
|
|
// (but timeout resets if we get new info, even while paused)
|
|
|
|
if (tXit) {
|
|
|
|
clearTimeout(tXit);
|
|
|
|
}
|
|
|
|
tXit = null;
|
|
|
|
fade = null;
|
|
|
|
delete info.track_color;
|
|
|
|
if (stat!=="play" && auto) {
|
|
|
|
if (stat==="stop") { // never actually happens with my phone :-(
|
|
|
|
load();
|
|
|
|
} else { // also quit when paused for a long time
|
|
|
|
tXit = setTimeout(load, TOUT);
|
|
|
|
fade = Date.now();
|
|
|
|
fadeOut();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Bangle.isLCDOn()) {
|
|
|
|
drawControls();
|
2021-03-14 21:28:44 +00:00
|
|
|
}
|
2021-03-31 01:22:58 +00:00
|
|
|
}
|
2021-03-14 21:28:44 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
////////////////////
|
|
|
|
// Events
|
|
|
|
////////////////////
|
2021-03-14 21:28:44 +00:00
|
|
|
|
2021-04-10 16:32:20 +00:00
|
|
|
// 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);
|
2021-03-31 01:22:58 +00:00
|
|
|
}
|
|
|
|
}, BTN2, {repeat: true, edge: "rising"});
|
2021-04-10 16:32:20 +00:00
|
|
|
setWatch(() => {
|
|
|
|
nPress++;
|
|
|
|
clearTimeout(tPress);
|
|
|
|
tPress = setTimeout(handleButton2Press, 500);
|
2021-03-31 01:22:58 +00:00
|
|
|
}, BTN2, {repeat: true, edge: "falling"});
|
|
|
|
}
|
2021-04-10 16:32:20 +00:00
|
|
|
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;
|
|
|
|
}
|
2021-03-14 21:28:44 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
let tCommand = {};
|
|
|
|
/**
|
|
|
|
* Send command and highlight corresponding control
|
|
|
|
* @param command "play/pause/next/previous/volumeup/volumedown"
|
|
|
|
*/
|
|
|
|
function sendCommand(command) {
|
|
|
|
Bluetooth.println(JSON.stringify({t: "music", n: command}));
|
|
|
|
// for controlColor
|
|
|
|
if (command in tCommand) {
|
|
|
|
clearTimeout(tCommand[command]);
|
|
|
|
}
|
|
|
|
tCommand[command] = setTimeout(function() {
|
|
|
|
delete tCommand[command];
|
2021-03-27 18:17:18 +00:00
|
|
|
drawControls();
|
2021-03-31 01:22:58 +00:00
|
|
|
}, 200);
|
|
|
|
drawControls();
|
|
|
|
}
|
2021-03-14 21:28:44 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
// touch/swipe: navigation
|
|
|
|
function togglePlay() {
|
|
|
|
sendCommand(stat==="play" ? "pause" : "play");
|
|
|
|
}
|
|
|
|
function startTouchWatches() {
|
2021-04-10 16:32:20 +00:00
|
|
|
Bangle.on("touch", side => {
|
2021-03-31 01:22:58 +00:00
|
|
|
switch(side) {
|
|
|
|
case 1:
|
|
|
|
sendCommand(stat==="play" ? "pause" : "previous");
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
sendCommand(stat==="play" ? "next" : "play");
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
togglePlay();
|
|
|
|
}
|
|
|
|
});
|
2021-04-10 16:32:20 +00:00
|
|
|
Bangle.on("swipe", dir => {
|
2021-03-31 01:22:58 +00:00
|
|
|
sendCommand(dir===1 ? "previous" : "next");
|
|
|
|
});
|
|
|
|
}
|
2021-04-10 16:32:20 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
/////////////////////
|
|
|
|
// Startup
|
|
|
|
/////////////////////
|
|
|
|
// check for saved music stat (by widget) to load
|
|
|
|
g.clear();
|
2021-04-25 13:11:26 +00:00
|
|
|
global.gbmusic_active = true; // we don't need our widget (needed for <2.09 devices)
|
2021-03-31 01:22:58 +00:00
|
|
|
Bangle.loadWidgets();
|
|
|
|
Bangle.drawWidgets();
|
|
|
|
delete (global.gbmusic_active);
|
2021-03-14 21:28:44 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
function startEmulator() {
|
|
|
|
if (typeof Bluetooth==="undefined") { // emulator!
|
|
|
|
Bluetooth = {
|
|
|
|
println: (line) => {console.log("Bluetooth:", line);},
|
|
|
|
};
|
|
|
|
// some example info
|
|
|
|
GB({"t": "musicinfo", "artist": "Some Artist Name", "album": "The Album Name", "track": "The Track Title Goes Here", "dur": 241, "c": 2, "n": 2});
|
|
|
|
GB({"t": "musicstate", "state": "play", "position": 0, "shuffle": 1, "repeat": 1});
|
2021-03-14 21:28:44 +00:00
|
|
|
}
|
2021-03-31 01:22:58 +00:00
|
|
|
}
|
|
|
|
function startWatches() {
|
2021-04-10 16:32:20 +00:00
|
|
|
startButtonWatches();
|
2021-03-31 01:22:58 +00:00
|
|
|
startTouchWatches();
|
2021-04-10 16:32:20 +00:00
|
|
|
startLCDWatch();
|
2021-03-31 01:22:58 +00:00
|
|
|
}
|
2021-03-30 20:36:04 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
function start() {
|
|
|
|
// start listening for music updates
|
|
|
|
const _GB = global.GB;
|
|
|
|
global.GB = (event) => {
|
|
|
|
// we eat music events!
|
|
|
|
switch(event.t) {
|
|
|
|
case "musicinfo":
|
|
|
|
musicInfo(event);
|
|
|
|
break;
|
|
|
|
case "musicstate":
|
|
|
|
musicState(event);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// pass on other events
|
|
|
|
if (_GB) {
|
|
|
|
setTimeout(_GB, 0, event);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
drawMusic();
|
|
|
|
drawControls();
|
|
|
|
startWatches();
|
|
|
|
tick();
|
|
|
|
startEmulator();
|
|
|
|
}
|
2021-03-14 21:28:44 +00:00
|
|
|
|
2021-03-31 01:22:58 +00:00
|
|
|
function init() {
|
2021-03-27 18:17:18 +00:00
|
|
|
let saved = require("Storage").readJSON("gbmusic.load.json", true);
|
|
|
|
require("Storage").erase("gbmusic.load.json");
|
2021-03-14 21:28:44 +00:00
|
|
|
if (saved) {
|
|
|
|
// autoloaded: load state was saved by widget
|
2021-03-27 18:17:18 +00:00
|
|
|
info = saved.info;
|
2021-03-31 01:22:58 +00:00
|
|
|
stat = saved.state;
|
2021-04-10 16:32:20 +00:00
|
|
|
delete saved;
|
2021-03-31 01:22:58 +00:00
|
|
|
auto = true;
|
2021-03-27 18:17:18 +00:00
|
|
|
start();
|
2021-03-14 21:28:44 +00:00
|
|
|
} else {
|
2021-04-10 16:32:20 +00:00
|
|
|
delete saved;
|
|
|
|
let s = require("Storage").readJSON("gbmusic.json", 1) || {};
|
2021-03-14 21:28:44 +00:00
|
|
|
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",
|
2021-04-10 16:32:20 +00:00
|
|
|
).then(choice => {
|
|
|
|
s.autoStart = choice;
|
2021-03-27 18:17:18 +00:00
|
|
|
require("Storage").writeJSON("gbmusic.json", s);
|
2021-04-10 16:32:20 +00:00
|
|
|
delete s;
|
2021-03-27 18:17:18 +00:00
|
|
|
setTimeout(start, 0);
|
|
|
|
});
|
2021-03-14 21:28:44 +00:00
|
|
|
} else {
|
2021-04-10 16:32:20 +00:00
|
|
|
delete s;
|
2021-03-27 18:17:18 +00:00
|
|
|
start();
|
2021-03-14 21:28:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-31 01:22:58 +00:00
|
|
|
init();
|
|
|
|
|