Skip to content

Commit 7360e96

Browse files
author
Ferenc Nánási
committed
Experimental feature added: music controls
1 parent cc882b8 commit 7360e96

12 files changed

+151
-96
lines changed

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ Configure IP & Port in your application and run mpv with --idle flag on your hos
2727

2828
The app not available on Google Play yet. I want improve my application before putting it to wider public.
2929

30+
## Experimental features
31+
32+
- Android notification by using `cordova-plugin-music-controls2` (disabled by default, it has issues) check out [TODO file](https://github.com/husudosu/mpv-remote-app/blob/master/TODO.md)
33+
3034
## App available on IzzyOnDroid
3135

3236
<p align="center">

TODO.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,7 @@ ionic capacitor run android -l --host=host
2222

2323
- By some reasons routing not working properly, when not using modals on Settings -> Servers and Media collections
2424
- Better handling for playlist
25-
- Need someone who can design a proper icon for the app
25+
- Need someone who can design a proper icon for the app (currently using Ionic default app icon),
26+
- Need some help with [cordova-plugin-music-controls2](https://github.com/Arzio/cordova-plugin-music-controls2) plugin:
27+
- When the app is not active, playback status not refreshing for notification,
28+
- I'd like remove album cover from notification.

android/app/capacitor.build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ dependencies {
1616
implementation project(':capacitor-storage')
1717
implementation project(':capacitor-community-sqlite')
1818
implementation "androidx.core:core:1.1.0"
19+
implementation "com.android.support:support-v4:25.1.1"
1920
}
2021

2122

android/app/src/main/res/xml/config.xml

+4
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,9 @@
77
<param name="onload" value="true"/>
88
</feature>
99

10+
<feature name="MusicControls">
11+
<param name="android-package" value="com.homerours.musiccontrols.MusicControls"/>
12+
</feature>
13+
1014

1115
</widget>

package-lock.json

+18-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"@ionic/vue-router": "^6.0.3",
2424
"axios": "^0.24.0",
2525
"com-darryncampbell-cordova-plugin-intent": "^2.2.0",
26+
"cordova-plugin-music-controls2": "github:Arzio/cordova-plugin-music-controls2",
2627
"core-js": "^3.6.5",
2728
"vue": "^3.2.28",
2829
"vue-router": "^4.0.12",

src/Container.vue

+11-1
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ export default defineComponent({
159159
window.plugins.intentShim.onIntent((intent) => {
160160
handleIntent(intent);
161161
});
162+
// await store.dispatch("notificationController/createController");
162163
}
163164
};
164165
@@ -240,6 +241,11 @@ export default defineComponent({
240241
store.commit("app/setScreenOrinetation", screen.orientation.type);
241242
apiInstance.get("/status").then((response) => {
242243
store.commit("simpleapi/setPlayerData", response.data);
244+
if (
245+
platforms.includes("hybrid") &&
246+
store.getters["settings/androidNotificationEnabled"] === true
247+
)
248+
store.dispatch("simpleapi/handleMusicControls");
243249
});
244250
245251
if (!store.state.simpleapi.playbackRefreshInterval)
@@ -250,7 +256,11 @@ export default defineComponent({
250256
if (platforms.includes("hybrid")) registerBroadcastReceiver();
251257
} else {
252258
// Battery saving stuff
253-
store.commit("simpleapi/clearPlaybackRefreshInterval");
259+
260+
// Clear update interval only if notiifcations disabled
261+
if (store.getters["settings/androidNotificationEnabled"] === false) {
262+
store.commit("simpleapi/clearPlaybackRefreshInterval");
263+
}
254264
await store.dispatch("settings/closeDbConnection");
255265
if (platforms.includes("hybrid")) unregisterBroadcastReceiver();
256266
}

src/components/playerController.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
<script>
4848
import { computed } from "vue";
4949
import { useStore } from "vuex";
50-
50+
import musicControls from "cordova-plugin-music-controls2/www/MusicControls.js";
5151
import { IonFooter, IonRow, IonCol, IonIcon, IonButton } from "@ionic/vue";
5252
import {
5353
playOutline,
@@ -70,6 +70,7 @@ export default {
7070
key: "pause",
7171
value: !playerData.value.pause,
7272
});
73+
musicControls.updateIsPlaying(!playerData.value.pause);
7374
}
7475
});
7576
};

src/store/index.js

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { createStore } from "vuex";
22
import { settings } from "./settings.module";
33
import { app } from "./app.module";
44
import { simpleapi } from "./simpleapi.module";
5-
65
export const store = createStore({
76
state: {},
87
mutations: {},

src/store/settings.module.js

+15
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ export const settings = {
4646
getServerHistory: (state) => {
4747
return state.history;
4848
},
49+
androidNotificationEnabled: (state) => {
50+
return state.settings.androidNotificationEnabled;
51+
},
4952
},
5053
mutations: {
5154
setAppSettings(state, value) {
@@ -105,6 +108,17 @@ export const settings = {
105108
await Storage.remove({ key: "server_port" });
106109
}
107110

111+
const androidNotificationEnabled = await Storage.get({
112+
key: "androidNotificationEnabled",
113+
});
114+
115+
if (androidNotificationEnabled.value == null) {
116+
await dispatch("setSetting", {
117+
key: "androidNotificationEnabled",
118+
value: false,
119+
});
120+
}
121+
108122
const servers = await getServer(state.dbSession);
109123
const currentServerId = await Storage.get({ key: "currentServerId" });
110124
if (servers.length > 0) {
@@ -121,6 +135,7 @@ export const settings = {
121135
commit("setAppSettings", {
122136
servers,
123137
currentServerId: parseInt(currentServerId.value),
138+
androidNotificationEnabled: androidNotificationEnabled.value,
124139
});
125140
},
126141
setSetting: async function ({ commit }, payload) {

src/store/simpleapi.module.js

+63-88
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { apiInstance } from "../api";
2-
// import musicControls from "cordova-plugin-music-controls2/www/MusicControls.js";
2+
import musicControls from "cordova-plugin-music-controls2/www/MusicControls.js";
33
const initialState = {
44
playerData: {
55
"audio-delay": 0, // <-- milliseconds
@@ -42,57 +42,31 @@ const initialState = {
4242
musicControlsActive: false,
4343
musicControlStatus: {
4444
artist: "",
45-
isPlaying: false,
46-
filename: "",
47-
"media-title": "",
4845
},
4946
};
5047

51-
// async function postData(url = "", data = {}) {
52-
// // Default options are marked with *
53-
// const response = await fetch(url, {
54-
// method: "POST", // *GET, POST, PUT, DELETE, etc.
55-
// mode: "cors", // no-cors, *cors, same-origin
56-
// cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
57-
// credentials: "same-origin", // include, *same-origin, omit
58-
// headers: {
59-
// "Content-Type": "application/json",
60-
// // 'Content-Type': 'application/x-www-form-urlencoded',
61-
// },
62-
// redirect: "follow", // manual, *follow, error
63-
// referrerPolicy: "no-referrer", // no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url
64-
// body: JSON.stringify(data), // body data type must match "Content-Type" header
65-
// });
66-
// return response.json(); // parses JSON response into native JavaScript objects
67-
// }
68-
69-
// function events(action) {
70-
// const message = JSON.parse(action).message;
71-
// switch (message) {
72-
// case "music-controls-next":
73-
// apiInstance.post("controls/next");
74-
// break;
75-
// case "music-controls-previous":
76-
// apiInstance.post("controls/prev");
77-
// break;
78-
// case "music-controls-pause":
79-
// // Do something
80-
// console.log("MusicController: Pause");
81-
// musicControls.updateIsPlaying(false);
82-
// break;
83-
// case "music-controls-play":
84-
// console.log("MusicController: Play");
85-
// musicControls.updateIsPlaying(true);
86-
// break;
87-
// case "music-controls-destroy":
88-
// // Do something
89-
// console.log("Destroy music controls");
90-
// break;
91-
// default:
92-
// break;
93-
// }
94-
// }
48+
const musicControlsSettings = {
49+
hasPrev: false,
50+
hasNext: false,
51+
cover: null,
52+
};
9553

54+
const musicEventHandler = async (action) => {
55+
const message = JSON.parse(action).message;
56+
console.log(`Message from controller: ${message}`);
57+
switch (message) {
58+
case "music-controls-pause":
59+
await apiInstance
60+
.post("/controls/play-pause")
61+
.then(() => musicControls.updateIsPlaying(false));
62+
break;
63+
case "music-controls-play":
64+
await apiInstance
65+
.post("/controls/play-pause")
66+
.then(() => musicControls.updateIsPlaying(true));
67+
break;
68+
}
69+
};
9670
export const simpleapi = {
9771
namespaced: true,
9872
state: { ...initialState },
@@ -130,43 +104,6 @@ export const simpleapi = {
130104
mutations: {
131105
setPlayerData(state, value) {
132106
state.playerData = value;
133-
134-
// If music controls enabled
135-
// if (state.connected && state.playerData.filename) {
136-
// if (!state.musicControlsActive) {
137-
// musicControls.create(
138-
// {
139-
// artist:
140-
// state.playerData["media-title"] || state.playerData.filename,
141-
// dismissable: false,
142-
// },
143-
// () => {
144-
// state.musicControlsActive = true;
145-
// }
146-
// );
147-
// state.musicControlStatus["media-title"] =
148-
// state.playerData["media-title"];
149-
// state.musicControlStatus.filename = state.playerData.filename;
150-
151-
// musicControls.updateIsPlaying(!state.playerData.pause);
152-
// musicControls.subscribe(events);
153-
// musicControls.listen();
154-
// } else {
155-
// // Media have changed.
156-
// if (
157-
// state.musicControlStatus["media-title"] !=
158-
// state.playerData["media-title"] ||
159-
// state.musicControlStatus.filename != state.playerData.filename
160-
// ) {
161-
// musicControls.destroy();
162-
// state.musicControlsActive = false;
163-
// // } else {
164-
// // console.log("T");
165-
// // // musicControls.updateIsPlaying(!state.playerData.pause);
166-
// // }
167-
// }
168-
// }
169-
// }
170107
},
171108
setPlayerDataProperty(state, value) {
172109
state.playerData[value.key] = value.value;
@@ -178,26 +115,64 @@ export const simpleapi = {
178115
state.MPVInfo = value;
179116
},
180117
clearPlaybackRefreshInterval(state) {
118+
console.log("clear playback refresh interval");
181119
if (state.playbackRefreshInterval != null) {
182-
console.log("Store playbackRefreshInterval should be cleared.");
183120
clearInterval(state.playbackRefreshInterval);
184121
state.playbackRefreshInterval = null;
185122
}
186123
},
187124
clearPlayerData(state) {
188125
state.playerData = { ...initialState.playerData };
189126
},
127+
setMusicControlsActive(state, value) {
128+
state.musicControlsActive = value;
129+
},
130+
setMusicControlsTitle(state, value) {
131+
state.musicControlStatus.artist = value;
132+
},
190133
},
191134
actions: {
192-
setPlaybackRefreshInterval({ commit, state }) {
135+
setPlaybackRefreshInterval({ commit, state, dispatch, rootGetters }) {
193136
if (state.playbackRefreshInterval == null) {
194137
state.playbackRefreshInterval = setInterval(() => {
195138
apiInstance.get("/status").then((response) => {
196139
commit("setPlayerData", response.data);
140+
if (rootGetters["settings/androidNotificationEnabled"] === true) {
141+
dispatch("handleMusicControls");
142+
}
197143
});
198144
}, 1500);
199145
}
200-
// Also setup local notifcations
146+
},
147+
handleMusicControls({ commit, state }) {
148+
if (state.playerData.filename) {
149+
const title =
150+
state.playerData["media-title"] || state.playerData.filename;
151+
152+
if (title != state.musicControlStatus.artist) {
153+
console.log("Have to change title");
154+
155+
if (state.musicControlsActive) {
156+
musicControls.destroy();
157+
musicControls.create({ ...musicControlsSettings, track: title });
158+
musicControls.subscribe(musicEventHandler);
159+
musicControls.listen();
160+
} else {
161+
musicControls.create({ ...musicControlsSettings, track: title });
162+
musicControls.subscribe(musicEventHandler);
163+
musicControls.listen();
164+
}
165+
commit("setMusicControlsTitle", title);
166+
commit("setMusicControlsActive", true);
167+
musicControls.updateIsPlaying(!state.playerData.pause);
168+
} else {
169+
musicControls.updateIsPlaying(!state.playerData.pause);
170+
}
171+
} else if (state.musicControlsActive) {
172+
console.log("Music controls should be destroyed");
173+
musicControls.destroy();
174+
commit("setMusicControlsActive", false);
175+
}
201176
},
202177
},
203178
};

0 commit comments

Comments
 (0)