mirror of https://github.com/espruino/BangleApps
Spotify remote initial release
restructure menu Requires Gadgetbridge 71.0 fix textinput->search menu screenshotspull/2170/head
parent
baeaec3694
commit
eece227508
|
@ -0,0 +1,5 @@
|
|||
0.01: New app.
|
||||
0.02: Restructure menu.
|
||||
0.03: change handling of intent extras.
|
||||
0.04: New layout.
|
||||
0.05: Add widgets field. Tweak layout.
|
|
@ -0,0 +1,21 @@
|
|||
Requires Gadgetbridge 71.0 or later. Allow intents in Gadgetbridge in order for this app to work.
|
||||
|
||||
Touch input:
|
||||
|
||||
Press the different ui elements to control Podcast Addict and open menus. Press left or right arrow to go to previous/next track.
|
||||
|
||||
Swipe input:
|
||||
|
||||
Swipe left/right to go to previous/next track. Swipe up/down to change the volume.
|
||||
|
||||
It's possible to start tracks by searching with the remote. Search term without tags will override search with tags.
|
||||
|
||||
To start playing 'from cold' the command for previous/next track via touch or swipe can be used. Pressing just play/pause is not guaranteed to initiate spotify in all circumstances (this will probably change with subsequent releases).
|
||||
|
||||
In order to search to play or start music from the 'Saved' menu the Android device must be awake and unlocked. The remote can wake and unlock the device if the Bangle.js has been added as a 'trusted device' under Android Settings->Security->Smart Lock->Trusted devices.
|
||||
|
||||
The swipe logic was inspired by the implementation in [rigrig](https://git.tubul.net/rigrig/)'s Scrolling Messages.
|
||||
|
||||
Spotify Remote was created by [thyttan](https://github.com/thyttan/).
|
||||
|
||||
<a target="_blank" href="https://icons8.com/icon/63316/spotify">Spotify</a> icon by <a target="_blank" href="https://icons8.com">Icons8</a>
|
|
@ -0,0 +1 @@
|
|||
require("heatshrink").decompress(atob("mEwwhC/AFV3AAQVVDKQWHDB0HC5NwCyoYMCxZJKFxgwKCxowJC6xGOJBAWPGA4MGogXOIwdCmf/AAczkhIKC4VzCogAD+YZEC49PC5AABmgXO+czJYoYDC4gfCuRYGoUjDAZ4GUJlyn4XNukjIwMzmVHBAU/+YXKoZ0GmQLCDgQXIU5IVDC5JVCIwIECDA5HIR4hkBDAX0C5YAHOoIXJa4QRDoUikiOEm7vKE4YADmZ1FC5N/R48nC5tzFQMiokimYYHC4h4KJwX3Ow6QMOwoXGSAoAKIwrBNFxIXZJBxGHGB4WIGBouJDBgWLJJYWMDBIWODIwVRAH4AXA="))
|
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
Bluetooth.println(JSON.stringify({t:"intent", action:"", flags:["flag1", "flag2",...], categories:["category1","category2",...], mimetype:"", data:"", package:"", class:"", target:"", extra:{someKey:"someValueOrString"}}));
|
||||
*/
|
||||
|
||||
var R;
|
||||
var backToMenu = false;
|
||||
var isPaused = true;
|
||||
|
||||
// The main layout of the app
|
||||
function gfx() {
|
||||
//Bangle.drawWidgets();
|
||||
R = Bangle.appRect;
|
||||
marigin = 8;
|
||||
// g.drawString(str, x, y, solid)
|
||||
g.clearRect(R);
|
||||
g.reset();
|
||||
|
||||
g.setFont("4x6:2");
|
||||
g.setFontAlign(1, 0, 0);
|
||||
g.drawString("->", R.x2 - marigin, R.y + R.h/2);
|
||||
|
||||
g.setFontAlign(-1, 0, 0);
|
||||
g.drawString("<-", R.x + marigin, R.y + R.h/2);
|
||||
|
||||
g.setFontAlign(-1, 0, 1);
|
||||
g.drawString("<-", R.x + R.w/2, R.y + marigin);
|
||||
|
||||
g.setFontAlign(1, 0, 1);
|
||||
g.drawString("->", R.x + R.w/2, R.y2 - marigin);
|
||||
|
||||
g.setFontAlign(0, 0, 0);
|
||||
g.drawString("Play\nPause", R.x + R.w/2, R.y + R.h/2);
|
||||
|
||||
g.setFontAlign(-1, -1, 0);
|
||||
g.drawString("Menu", R.x + 2*marigin, R.y + 2*marigin);
|
||||
|
||||
g.setFontAlign(-1, 1, 0);
|
||||
g.drawString("Wake", R.x + 2*marigin, R.y + R.h - 2*marigin);
|
||||
|
||||
g.setFontAlign(1, -1, 0);
|
||||
g.drawString("Srch", R.x + R.w - 2*marigin, R.y + 2*marigin);
|
||||
|
||||
g.setFontAlign(1, 1, 0);
|
||||
g.drawString("Saved", R.x + R.w - 2*marigin, R.y + R.h - 2*marigin);
|
||||
}
|
||||
|
||||
// Touch handler for main layout
|
||||
function touchHandler(_, xy) {
|
||||
x = xy.x;
|
||||
y = xy.y;
|
||||
len = (R.w<R.h+1)?(R.w/3):(R.h/3);
|
||||
|
||||
// doing a<b+1 seemed faster than a<=b, also using a>b-1 instead of a>b.
|
||||
if ((R.x-1<x && x<R.x+len) && (R.y-1<y && y<R.y+len)) {
|
||||
//Menu
|
||||
Bangle.removeAllListeners("touch");
|
||||
Bangle.removeAllListeners("swipe");
|
||||
backToMenu = true;
|
||||
E.showMenu(spotifyMenu);
|
||||
} else if ((R.x-1<x && x<R.x+len) && (R.y2-len<y && y<R.y2+1)) {
|
||||
//Wake
|
||||
gadgetbridgeWake();
|
||||
gadgetbridgeWake();
|
||||
} else if ((R.x2-len<x && x<R.x2+1) && (R.y-1<y && y<R.y+len)) {
|
||||
//Srch
|
||||
Bangle.removeAllListeners("touch");
|
||||
Bangle.removeAllListeners("swipe");
|
||||
E.showMenu(searchMenu);
|
||||
} else if ((R.x2-len<x && x<R.x2+1) && (R.y2-len<y && y<R.y2+1)) {
|
||||
//Saved
|
||||
Bangle.removeAllListeners("touch");
|
||||
Bangle.removeAllListeners("swipe");
|
||||
E.showMenu(savedMenu);
|
||||
} else if ((R.x-1<x && x<R.x+len) && (R.y+R.h/2-len/2<y && y<R.y+R.h/2+len/2)) {
|
||||
//Previous
|
||||
spotifyWidget("PREVIOUS");
|
||||
} else if ((R.x2-len+1<x && x<R.x2+1) && (R.y+R.h/2-len/2<y && y<R.y+R.h/2+len/2)) {
|
||||
//Next
|
||||
spotifyWidget("NEXT");
|
||||
} else if ((R.x-1<x && x<R.x2+1) && (R.y-1<y && y<R.y2+1)){
|
||||
//play/pause
|
||||
playPause = isPaused?"play":"pause";
|
||||
Bangle.musicControl(playPause);
|
||||
isPaused = !isPaused;
|
||||
}
|
||||
}
|
||||
|
||||
// Swipe handler for main layout, used to jump backward and forward within a podcast episode.
|
||||
function swipeHandler(LR, _) {
|
||||
if (LR==-1) {
|
||||
spotifyWidget("NEXT");
|
||||
}
|
||||
if (LR==1) {
|
||||
spotifyWidget("PREVIOUS");
|
||||
}
|
||||
}
|
||||
|
||||
// Navigation input on the main layout
|
||||
function setUI() {
|
||||
// Bangle.setUI code from rigrig's smessages app for volume control: https://git.tubul.net/rigrig/BangleApps/src/branch/personal/apps/smessages/app.js
|
||||
Bangle.setUI(
|
||||
{mode : "updown", back : load},
|
||||
ud => {
|
||||
if (ud) Bangle.musicControl(ud>0 ? "volumedown" : "volumeup");
|
||||
}
|
||||
);
|
||||
Bangle.on("touch", touchHandler);
|
||||
Bangle.on("swipe", swipeHandler);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Get back to the main layout
|
||||
function backToGfx() {
|
||||
E.showMenu();
|
||||
g.clear();
|
||||
g.reset();
|
||||
Bangle.removeAllListeners("touch");
|
||||
Bangle.removeAllListeners("swipe");
|
||||
setUI();
|
||||
gfx();
|
||||
backToMenu = false;
|
||||
}
|
||||
|
||||
/*
|
||||
The functions for interacting with Android and the Spotify app
|
||||
*/
|
||||
|
||||
simpleSearch = "";
|
||||
function simpleSearchTerm() { // input a simple search term without tags, overrides search with tags (artist and track)
|
||||
require("textinput").input({text:simpleSearch}).then(result => {simpleSearch = result;}).then(() => {E.showMenu(searchMenu);});
|
||||
}
|
||||
|
||||
artist = "";
|
||||
function artistSearchTerm() { // input artist to search for
|
||||
require("textinput").input({text:artist}).then(result => {artist = result;}).then(() => {E.showMenu(searchMenu);});
|
||||
}
|
||||
|
||||
track = "";
|
||||
function trackSearchTerm() { // input track to search for
|
||||
require("textinput").input({text:track}).then(result => {track = result;}).then(() => {E.showMenu(searchMenu);});
|
||||
}
|
||||
|
||||
album = "";
|
||||
function albumSearchTerm() { // input album to search for
|
||||
require("textinput").input({text:album}).then(result => {album = result;}).then(() => {E.showMenu(searchMenu);});
|
||||
}
|
||||
|
||||
function searchPlayWOTags() {//make a spotify search and play using entered terms
|
||||
searchString = simpleSearch;
|
||||
Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:searchString}, flags:["FLAG_ACTIVITY_NEW_TASK"]}));
|
||||
}
|
||||
|
||||
function searchPlayWTags() {//make a spotify search and play using entered terms
|
||||
searchString = (artist=="" ? "":("artist:\""+artist+"\"")) + ((artist!="" && track!="") ? " ":"") + (track=="" ? "":("track:\""+track+"\"")) + (((artist!="" && album!="") || (track!="" && album!="")) ? " ":"") + (album=="" ? "":(" album:\""+album+"\""));
|
||||
Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:searchString}, flags:["FLAG_ACTIVITY_NEW_TASK"]}));
|
||||
}
|
||||
|
||||
function playVreden() {//Play the track "Vreden" by Sara Parkman via spotify uri-link
|
||||
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:track:5QEFFJ5tAeRlVquCUNpAJY:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
|
||||
}
|
||||
|
||||
function playVredenAlternate() {//Play the track "Vreden" by Sara Parkman via spotify uri-link
|
||||
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:track:5QEFFJ5tAeRlVquCUNpAJY:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK"]}));
|
||||
}
|
||||
|
||||
function searchPlayVreden() {//Play the track "Vreden" by Sara Parkman via search and play
|
||||
Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:'artist:"Sara Parkman" track:"Vreden"'}, flags:["FLAG_ACTIVITY_NEW_TASK"]}));
|
||||
}
|
||||
|
||||
function openAlbum() {//Play EP "The Blue Room" by Coldplay
|
||||
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:album:3MVb2CWB36x7VwYo5sZmf2", target:"activity", flags:["FLAG_ACTIVITY_NEW_TASK"]}));
|
||||
}
|
||||
|
||||
function searchPlayAlbum() {//Play EP "The Blue Room" by Coldplay via search and play
|
||||
Bluetooth.println(JSON.stringify({t:"intent", action:"android.media.action.MEDIA_PLAY_FROM_SEARCH", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", target:"activity", extra:{query:'album:"The blue room" artist:"Coldplay"', "android.intent.extra.focus":"vnd.android.cursor.item/album"}, flags:["FLAG_ACTIVITY_NEW_TASK"]}));
|
||||
}
|
||||
|
||||
function spotifyWidget(action) {
|
||||
Bluetooth.println(JSON.stringify({t:"intent", action:("com.spotify.mobile.android.ui.widget."+action), package:"com.spotify.music", target:"broadcastreceiver"}));
|
||||
}
|
||||
|
||||
function gadgetbridgeWake() {
|
||||
Bluetooth.println(JSON.stringify({t:"intent", target:"activity", flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_CLEAR_TASK", "FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS", "FLAG_ACTIVITY_NO_ANIMATION"], package:"gadgetbridge", class:"nodomain.freeyourgadget.gadgetbridge.activities.WakeActivity"}));
|
||||
}
|
||||
|
||||
function spotifyPlaylistDW() {
|
||||
Bluetooth.println(JSON.stringify({t:"intent", action:"android.intent.action.VIEW", categories:["android.intent.category.DEFAULT"], package:"com.spotify.music", data:"spotify:user:spotify:playlist:37i9dQZEVXcRfaeEbxXIgb:play", target:"activity" , flags:["FLAG_ACTIVITY_NEW_TASK", "FLAG_ACTIVITY_NO_ANIMATION"/*, "FLAG_ACTIVITY_CLEAR_TOP", "FLAG_ACTIVITY_PREVIOUS_IS_TOP"*/]}));
|
||||
}
|
||||
|
||||
// Spotify Remote Menu
|
||||
var spotifyMenu = {
|
||||
"" : { title : " ",
|
||||
back: backToGfx },
|
||||
"Controls" : ()=>{E.showMenu(controlMenu);},
|
||||
"Search and play" : ()=>{E.showMenu(searchMenu);},
|
||||
"Saved music" : ()=>{E.showMenu(savedMenu);},
|
||||
"Wake the android" : function() {gadgetbridgeWake();gadgetbridgeWake();},
|
||||
"Exit Spotify Remote" : ()=>{load();}
|
||||
};
|
||||
|
||||
|
||||
var controlMenu = {
|
||||
"" : { title : " ",
|
||||
back: () => {if (backToMenu) E.showMenu(spotifyMenu);
|
||||
if (!backToMenu) backToGfx();} },
|
||||
"Play" : ()=>{Bangle.musicControl("play");},
|
||||
"Pause" : ()=>{Bangle.musicControl("pause");},
|
||||
"Previous" : ()=>{spotifyWidget("PREVIOUS");},
|
||||
"Next" : ()=>{spotifyWidget("NEXT");},
|
||||
"Play (widget, next then previous)" : ()=>{spotifyWidget("NEXT"); spotifyWidget("PREVIOUS");},
|
||||
"Messages Music Controls" : ()=>{load("messagesmusic.app.js");},
|
||||
};
|
||||
|
||||
var searchMenu = {
|
||||
"" : { title : " ",
|
||||
back: () => {if (backToMenu) E.showMenu(spotifyMenu);
|
||||
if (!backToMenu) backToGfx();} },
|
||||
"Search term w/o tags" : ()=>{simpleSearchTerm();},
|
||||
"Execute search and play w/o tags" : ()=>{searchPlayWOTags();},
|
||||
"Search term w tag \"artist\"" : ()=>{artistSearchTerm();},
|
||||
"Search term w tag \"track\"" : ()=>{trackSearchTerm();},
|
||||
"Search term w tag \"album\"" : ()=>{albumSearchTerm();},
|
||||
"Execute search and play with tags" : ()=>{searchPlayWTags();},
|
||||
"Play \"Vreden\" by Sara Parkman via uri-link" : ()=>{playVreden();},
|
||||
"Play \"Vreden\" by Sara Parkman via search&play" : ()=>{searchPlayVreden();},
|
||||
"Open \"The Blue Room\" EP (no autoplay)" : ()=>{openAlbum();},
|
||||
"Play \"The Blue Room\" EP via search&play" : ()=>{searchPlayAlbum();},
|
||||
"Play playlist Discover Weekly" : ()=>{spotifyPlaylistDW();},
|
||||
};
|
||||
|
||||
var savedMenu = {
|
||||
"" : { title : " ",
|
||||
back: () => {if (backToMenu) E.showMenu(spotifyMenu);
|
||||
if (!backToMenu) backToGfx();} },
|
||||
"Play \"Vreden\" by Sara Parkman via uri-link" : ()=>{playVreden();},
|
||||
"Open \"The Blue Room\" EP (no autoplay)" : ()=>{openAlbum();},
|
||||
"Play \"The Blue Room\" EP via search&play" : ()=>{searchPlayAlbum();},
|
||||
"Play playlist Discover Weekly" : ()=>{spotifyPlaylistDW();},
|
||||
};
|
||||
|
||||
Bangle.loadWidgets();
|
||||
setUI();
|
||||
gfx();
|
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"id": "spotrem",
|
||||
"name": "Spotify Remote",
|
||||
"version": "0.05",
|
||||
"description": "Control spotify on your android device.",
|
||||
"readme": "README.md",
|
||||
"type": "app",
|
||||
"tags": "spotify,music,player,remote,control,intent,intents,gadgetbridge,spotrem",
|
||||
"icon": "app.png",
|
||||
"screenshots" : [ {"url":"screenshot1.png"}, {"url":"screenshot2.png"} ],
|
||||
"supports": ["BANGLEJS2"],
|
||||
"dependencies": { "textinput":"type"},
|
||||
"storage": [
|
||||
{"name":"spotrem.app.js","url":"app.js"},
|
||||
{"name":"spotrem.img","url":"app-icon.js","evaluate":true}
|
||||
]
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
Loading…
Reference in New Issue