Changeset View
Changeset View
Standalone View
Standalone View
src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisPlugin.java
Show All 27 Lines | |||||
28 | import android.support.v4.content.ContextCompat; | 28 | import android.support.v4.content.ContextCompat; | ||
29 | import android.util.Log; | 29 | import android.util.Log; | ||
30 | 30 | | |||
31 | import org.kde.kdeconnect.NetworkPackage; | 31 | import org.kde.kdeconnect.NetworkPackage; | ||
32 | import org.kde.kdeconnect.Plugins.Plugin; | 32 | import org.kde.kdeconnect.Plugins.Plugin; | ||
33 | import org.kde.kdeconnect_tp.R; | 33 | import org.kde.kdeconnect_tp.R; | ||
34 | 34 | | |||
35 | import java.util.ArrayList; | 35 | import java.util.ArrayList; | ||
36 | import java.util.Collections; | ||||
36 | import java.util.HashMap; | 37 | import java.util.HashMap; | ||
38 | import java.util.Iterator; | ||||
37 | import java.util.List; | 39 | import java.util.List; | ||
38 | 40 | | |||
39 | public class MprisPlugin extends Plugin { | 41 | public class MprisPlugin extends Plugin { | ||
40 | 42 | public class MprisPlayer { | |||
41 | public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris"; | | |||
42 | public final static String PACKAGE_TYPE_MPRIS_REQUEST = "kdeconnect.mpris.request"; | | |||
43 | | ||||
44 | private String player = ""; | 43 | private String player = ""; | ||
45 | private boolean playing = false; | 44 | private boolean playing = false; | ||
46 | private String currentSong = ""; | 45 | private String currentSong = ""; | ||
47 | private int volume = 50; | 46 | private int volume = 50; | ||
48 | private long length = -1; | 47 | private long length = -1; | ||
49 | private long lastPosition; | 48 | private long lastPosition = 0; | ||
50 | private long lastPositionTime; | 49 | private long lastPositionTime; | ||
51 | private boolean playAllowed = true; | 50 | private boolean playAllowed = true; | ||
52 | private boolean pauseAllowed = true; | 51 | private boolean pauseAllowed = true; | ||
53 | private boolean goNextAllowed = true; | 52 | private boolean goNextAllowed = true; | ||
54 | private boolean goPreviousAllowed = true; | 53 | private boolean goPreviousAllowed = true; | ||
55 | private boolean seekAllowed = true; | 54 | private boolean seekAllowed = true; | ||
55 | | ||||
56 | public MprisPlayer() { | ||||
57 | lastPositionTime = System.currentTimeMillis(); | ||||
58 | } | ||||
59 | | ||||
60 | public String getCurrentSong() { | ||||
61 | return currentSong; | ||||
62 | } | ||||
63 | | ||||
64 | public String getPlayer() { | ||||
65 | return player; | ||||
66 | } | ||||
67 | | ||||
68 | private boolean isSpotify() { | ||||
69 | return getPlayer().toLowerCase().equals("spotify"); | ||||
70 | } | ||||
71 | | ||||
72 | public int getVolume() { | ||||
73 | return volume; | ||||
74 | } | ||||
75 | | ||||
76 | public long getLength(){ return length; } | ||||
77 | | ||||
78 | public boolean isPlaying() { | ||||
79 | return playing; | ||||
80 | } | ||||
81 | | ||||
82 | public boolean isPlayAllowed() { | ||||
83 | return playAllowed; | ||||
84 | } | ||||
85 | | ||||
86 | public boolean isPauseAllowed() { | ||||
87 | return pauseAllowed; | ||||
88 | } | ||||
89 | | ||||
90 | public boolean isGoNextAllowed() { | ||||
91 | return goNextAllowed; | ||||
92 | } | ||||
93 | | ||||
94 | public boolean isGoPreviousAllowed() { | ||||
95 | return goPreviousAllowed; | ||||
96 | } | ||||
97 | | ||||
98 | public boolean isSeekAllowed() { | ||||
99 | return seekAllowed && getLength() >= 0 && getPosition() >= 0 && !isSpotify(); | ||||
100 | } | ||||
101 | | ||||
102 | public boolean isSetVolumeAllowed() { | ||||
103 | return !isSpotify(); | ||||
104 | } | ||||
105 | | ||||
106 | public long getPosition(){ | ||||
107 | if(playing) { | ||||
108 | return lastPosition + (System.currentTimeMillis() - lastPositionTime); | ||||
109 | } else { | ||||
110 | return lastPosition; | ||||
111 | } | ||||
112 | } | ||||
113 | | ||||
114 | public void playPause() { | ||||
115 | if (isPauseAllowed() || isPlayAllowed()) { | ||||
116 | MprisPlugin.this.sendCommand(getPlayer(), "action", "PlayPause"); | ||||
117 | } | ||||
118 | } | ||||
119 | | ||||
120 | public void play() { | ||||
121 | if (isPlayAllowed()) { | ||||
122 | MprisPlugin.this.sendCommand(getPlayer(), "action", "Play"); | ||||
123 | } | ||||
124 | } | ||||
125 | | ||||
126 | public void pause() { | ||||
127 | if (isPauseAllowed()) { | ||||
128 | MprisPlugin.this.sendCommand(getPlayer(), "action", "Pause"); | ||||
129 | } | ||||
130 | } | ||||
131 | | ||||
132 | public void stop() { | ||||
133 | MprisPlugin.this.sendCommand(getPlayer(), "action", "Stop"); | ||||
134 | } | ||||
135 | | ||||
136 | public void previous() { | ||||
137 | if (isGoPreviousAllowed()) { | ||||
138 | MprisPlugin.this.sendCommand(getPlayer(), "action", "Previous"); | ||||
139 | } | ||||
140 | } | ||||
141 | | ||||
142 | public void next() { | ||||
143 | if (isGoNextAllowed()) { | ||||
144 | MprisPlugin.this.sendCommand(getPlayer(), "action", "Next"); | ||||
145 | } | ||||
146 | } | ||||
147 | | ||||
148 | public void setVolume(int volume) { | ||||
149 | if (isSetVolumeAllowed()) { | ||||
150 | MprisPlugin.this.sendCommand(getPlayer(), "setVolume", volume); | ||||
151 | } | ||||
152 | } | ||||
153 | | ||||
154 | public void setPosition(int position) { | ||||
155 | if (isSeekAllowed()) { | ||||
156 | MprisPlugin.this.sendCommand(getPlayer(), "SetPosition", position); | ||||
157 | | ||||
158 | lastPosition = position; | ||||
159 | lastPositionTime = System.currentTimeMillis(); | ||||
160 | } | ||||
161 | } | ||||
162 | | ||||
163 | public void seek(int offset) { | ||||
164 | if (isSeekAllowed()) { | ||||
165 | MprisPlugin.this.sendCommand(getPlayer(), "Seek", offset); | ||||
166 | } | ||||
167 | } | ||||
168 | } | ||||
169 | | ||||
170 | public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris"; | ||||
171 | public final static String PACKAGE_TYPE_MPRIS_REQUEST = "kdeconnect.mpris.request"; | ||||
172 | | ||||
173 | private HashMap<String, MprisPlayer> players = new HashMap<>(); | ||||
56 | private HashMap<String,Handler> playerStatusUpdated = new HashMap<>(); | 174 | private HashMap<String,Handler> playerStatusUpdated = new HashMap<>(); | ||
57 | 175 | | |||
58 | private List<String> playerList = new ArrayList<>(); | | |||
59 | private HashMap<String,Handler> playerListUpdated = new HashMap<>(); | 176 | private HashMap<String,Handler> playerListUpdated = new HashMap<>(); | ||
60 | 177 | | |||
61 | @Override | 178 | @Override | ||
62 | public String getDisplayName() { | 179 | public String getDisplayName() { | ||
63 | return context.getResources().getString(R.string.pref_plugin_mpris); | 180 | return context.getResources().getString(R.string.pref_plugin_mpris); | ||
64 | } | 181 | } | ||
65 | 182 | | |||
66 | @Override | 183 | @Override | ||
Show All 9 Lines | |||||
76 | @Override | 193 | @Override | ||
77 | public boolean hasSettings() { | 194 | public boolean hasSettings() { | ||
78 | return true; | 195 | return true; | ||
79 | } | 196 | } | ||
80 | 197 | | |||
81 | @Override | 198 | @Override | ||
82 | public boolean onCreate() { | 199 | public boolean onCreate() { | ||
83 | requestPlayerList(); | 200 | requestPlayerList(); | ||
84 | lastPositionTime = System.currentTimeMillis(); | | |||
85 | return true; | 201 | return true; | ||
86 | } | 202 | } | ||
87 | 203 | | |||
88 | @Override | 204 | @Override | ||
89 | public void onDestroy() { | 205 | public void onDestroy() { | ||
90 | playerList.clear(); | 206 | players.clear(); | ||
91 | } | 207 | } | ||
92 | 208 | | |||
93 | public void sendAction(String player, String action) { | 209 | private void sendCommand(String player, String method, String value) { | ||
94 | NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); | 210 | NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); | ||
95 | np.set("player", player); | 211 | np.set("player", player); | ||
96 | np.set("action", action); | 212 | np.set(method, value); | ||
97 | device.sendPackage(np); | 213 | device.sendPackage(np); | ||
98 | } | 214 | } | ||
99 | public void sendAction(String action) { | | |||
100 | sendAction(player, action); | | |||
101 | } | | |||
102 | 215 | | |||
103 | public void setVolume(int volume) { | 216 | private void sendCommand(String player, String method, int value) { | ||
104 | NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); | | |||
105 | np.set("player", player); | | |||
106 | np.set("setVolume",volume); | | |||
107 | device.sendPackage(np); | | |||
108 | } | | |||
109 | | ||||
110 | public void setPosition(int position) { | | |||
111 | NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); | 217 | NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); | ||
112 | np.set("player", player); | 218 | np.set("player", player); | ||
113 | np.set("SetPosition", position); | 219 | np.set(method, value); | ||
114 | device.sendPackage(np); | | |||
115 | this.lastPosition = position; | | |||
116 | this.lastPositionTime = System.currentTimeMillis(); | | |||
117 | } | | |||
118 | | ||||
119 | public void Seek(int offset) { | | |||
120 | NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); | | |||
121 | np.set("player", player); | | |||
122 | np.set("Seek", offset); | | |||
123 | device.sendPackage(np); | 220 | device.sendPackage(np); | ||
124 | } | 221 | } | ||
125 | 222 | | |||
126 | @Override | 223 | @Override | ||
127 | public boolean onPackageReceived(NetworkPackage np) { | 224 | public boolean onPackageReceived(NetworkPackage np) { | ||
128 | | ||||
129 | if (np.has("nowPlaying") || np.has("volume") || np.has("isPlaying") || np.has("length") || np.has("pos")) { | 225 | if (np.has("nowPlaying") || np.has("volume") || np.has("isPlaying") || np.has("length") || np.has("pos")) { | ||
130 | if (np.getString("player").equals(player)) { | 226 | MprisPlayer playerStatus = players.get(np.getString("player")); | ||
131 | currentSong = np.getString("nowPlaying", currentSong); | 227 | if (playerStatus != null) { | ||
132 | volume = np.getInt("volume", volume); | 228 | playerStatus.currentSong = np.getString("nowPlaying", playerStatus.currentSong); | ||
133 | length = np.getLong("length", length); | 229 | playerStatus.volume = np.getInt("volume", playerStatus.volume); | ||
230 | playerStatus.length = np.getLong("length", playerStatus.length); | ||||
134 | if(np.has("pos")){ | 231 | if(np.has("pos")){ | ||
135 | lastPosition = np.getLong("pos", lastPosition); | 232 | playerStatus.lastPosition = np.getLong("pos", playerStatus.lastPosition); | ||
136 | lastPositionTime = System.currentTimeMillis(); | 233 | playerStatus.lastPositionTime = System.currentTimeMillis(); | ||
137 | } | 234 | } | ||
138 | playing = np.getBoolean("isPlaying", playing); | 235 | playerStatus.playing = np.getBoolean("isPlaying", playerStatus.playing); | ||
139 | playAllowed = np.getBoolean("canPlay", playAllowed); | 236 | playerStatus.playAllowed = np.getBoolean("canPlay", playerStatus.playAllowed); | ||
140 | pauseAllowed = np.getBoolean("canPause", pauseAllowed); | 237 | playerStatus.pauseAllowed = np.getBoolean("canPause", playerStatus.pauseAllowed); | ||
141 | goNextAllowed = np.getBoolean("canGoNext", goNextAllowed); | 238 | playerStatus.goNextAllowed = np.getBoolean("canGoNext", playerStatus.goNextAllowed); | ||
142 | goPreviousAllowed = np.getBoolean("canGoPrevious", goPreviousAllowed); | 239 | playerStatus.goPreviousAllowed = np.getBoolean("canGoPrevious", playerStatus.goPreviousAllowed); | ||
143 | seekAllowed = np.getBoolean("canSeek", seekAllowed); | 240 | playerStatus.seekAllowed = np.getBoolean("canSeek", playerStatus.seekAllowed); | ||
144 | for (String key : playerStatusUpdated.keySet()) { | 241 | for (String key : playerStatusUpdated.keySet()) { | ||
145 | try { | 242 | try { | ||
146 | playerStatusUpdated.get(key).dispatchMessage(new Message()); | 243 | playerStatusUpdated.get(key).dispatchMessage(new Message()); | ||
147 | } catch(Exception e) { | 244 | } catch(Exception e) { | ||
148 | e.printStackTrace(); | 245 | e.printStackTrace(); | ||
149 | Log.e("MprisControl","Exception"); | 246 | Log.e("MprisControl","Exception"); | ||
150 | playerStatusUpdated.remove(key); | 247 | playerStatusUpdated.remove(key); | ||
151 | } | 248 | } | ||
152 | } | 249 | } | ||
153 | } | 250 | } | ||
154 | } | 251 | } | ||
155 | 252 | | |||
156 | List<String> newPlayerList = np.getStringList("playerList"); | 253 | List<String> newPlayerList = np.getStringList("playerList"); | ||
157 | if (newPlayerList != null) { | 254 | if (newPlayerList != null) { | ||
158 | boolean equals = false; | 255 | boolean equals = true; | ||
159 | if (newPlayerList.size() == playerList.size()) { | 256 | for (String newPlayer : newPlayerList) { | ||
160 | equals = true; | 257 | if (!players.containsKey(newPlayer)) { | ||
161 | for (int i=0; i<newPlayerList.size(); i++) { | | |||
162 | if (!newPlayerList.get(i).equals(playerList.get(i))) { | | |||
163 | equals = false; | 258 | equals = false; | ||
259 | | ||||
260 | MprisPlayer player = new MprisPlayer(); | ||||
261 | player.player = newPlayer; | ||||
262 | players.put(newPlayer, player); | ||||
263 | | ||||
264 | //Immediately ask for the data of this player | ||||
265 | requestPlayerStatus(newPlayer); | ||||
266 | } | ||||
267 | } | ||||
268 | Iterator<HashMap.Entry<String, MprisPlayer>> iter = players.entrySet().iterator(); | ||||
269 | while (iter.hasNext()) { | ||||
270 | String oldPlayer = iter.next().getKey(); | ||||
271 | | ||||
272 | boolean found = false; | ||||
273 | for (String newPlayer : newPlayerList) { | ||||
274 | if (newPlayer.equals(oldPlayer)) { | ||||
275 | found = true; | ||||
164 | break; | 276 | break; | ||
165 | } | 277 | } | ||
166 | } | 278 | } | ||
279 | | ||||
280 | if (!found) { | ||||
281 | iter.remove(); | ||||
282 | equals = false; | ||||
283 | } | ||||
167 | } | 284 | } | ||
168 | if (!equals) { | 285 | if (!equals) { | ||
169 | playerList = newPlayerList; | | |||
170 | for (String key : playerListUpdated.keySet()) { | 286 | for (String key : playerListUpdated.keySet()) { | ||
171 | try { | 287 | try { | ||
172 | playerListUpdated.get(key).dispatchMessage(new Message()); | 288 | playerListUpdated.get(key).dispatchMessage(new Message()); | ||
173 | } catch(Exception e) { | 289 | } catch(Exception e) { | ||
174 | e.printStackTrace(); | 290 | e.printStackTrace(); | ||
175 | Log.e("MprisControl","Exception"); | 291 | Log.e("MprisControl","Exception"); | ||
176 | playerListUpdated.remove(key); | 292 | playerListUpdated.remove(key); | ||
177 | } | 293 | } | ||
Show All 15 Lines | |||||
193 | } | 309 | } | ||
194 | 310 | | |||
195 | public void setPlayerStatusUpdatedHandler(String id, Handler h) { | 311 | public void setPlayerStatusUpdatedHandler(String id, Handler h) { | ||
196 | playerStatusUpdated.put(id, h); | 312 | playerStatusUpdated.put(id, h); | ||
197 | 313 | | |||
198 | h.dispatchMessage(new Message()); | 314 | h.dispatchMessage(new Message()); | ||
199 | 315 | | |||
200 | //Get the status if this is the first handler we have | 316 | //Get the status if this is the first handler we have | ||
201 | if (playerListUpdated.size() == 1) { | 317 | if (playerListUpdated.size() == 1 && !players.isEmpty()) { | ||
202 | requestPlayerStatus(); | 318 | requestPlayerStatus(getPlayerList().get(0)); | ||
203 | } | 319 | } | ||
204 | } | 320 | } | ||
205 | 321 | | |||
206 | public void setPlayerListUpdatedHandler(String id, Handler h) { | 322 | public void setPlayerListUpdatedHandler(String id, Handler h) { | ||
207 | playerListUpdated.put(id,h); | 323 | playerListUpdated.put(id,h); | ||
208 | 324 | | |||
209 | h.dispatchMessage(new Message()); | 325 | h.dispatchMessage(new Message()); | ||
210 | 326 | | |||
211 | //Get the status if this is the first handler we have | 327 | //Get the status if this is the first handler we have | ||
212 | if (playerListUpdated.size() == 1) { | 328 | if (playerListUpdated.size() == 1) { | ||
213 | requestPlayerList(); | 329 | requestPlayerList(); | ||
214 | } | 330 | } | ||
215 | } | 331 | } | ||
216 | 332 | | |||
217 | public void setPlayer(String player) { | | |||
218 | if (player == null || player.equals(this.player)) return; | | |||
219 | this.player = player; | | |||
220 | currentSong = ""; | | |||
221 | volume = 50; | | |||
222 | playing = false; | | |||
223 | playAllowed = true; | | |||
224 | pauseAllowed = true; | | |||
225 | goNextAllowed = true; | | |||
226 | goPreviousAllowed = true; | | |||
227 | seekAllowed = true; | | |||
228 | for (String key : playerStatusUpdated.keySet()) { | | |||
229 | try { | | |||
230 | playerStatusUpdated.get(key).dispatchMessage(new Message()); | | |||
231 | } catch(Exception e) { | | |||
232 | e.printStackTrace(); | | |||
233 | Log.e("MprisControl","Exception"); | | |||
234 | playerStatusUpdated.remove(key); | | |||
235 | } | | |||
236 | } | | |||
237 | requestPlayerStatus(); | | |||
238 | } | | |||
239 | | ||||
240 | public List<String> getPlayerList() { | 333 | public List<String> getPlayerList() { | ||
241 | return playerList; | 334 | List<String> playerlist = new ArrayList<>(players.keySet()); | ||
335 | Collections.sort(playerlist); | ||||
336 | return playerlist; | ||||
242 | } | 337 | } | ||
243 | 338 | | |||
244 | public String getCurrentSong() { | 339 | public MprisPlayer getPlayerStatus(String player) { | ||
245 | return currentSong; | 340 | return players.get(player); | ||
246 | } | | |||
247 | | ||||
248 | public String getPlayer() { | | |||
249 | return player; | | |||
250 | } | 341 | } | ||
251 | 342 | | |||
252 | public int getVolume() { | 343 | public MprisPlayer getEmptyPlayer() { | ||
253 | return volume; | 344 | return new MprisPlayer(); | ||
254 | } | | |||
255 | | ||||
256 | public long getLength(){ return length; } | | |||
257 | | ||||
258 | public boolean isPlaying() { | | |||
259 | return playing; | | |||
260 | } | | |||
261 | | ||||
262 | public boolean isPlayAllowed() { | | |||
263 | return playAllowed; | | |||
264 | } | | |||
265 | | ||||
266 | public boolean isPauseAllowed() { | | |||
267 | return pauseAllowed; | | |||
268 | } | | |||
269 | | ||||
270 | public boolean isGoNextAllowed() { | | |||
271 | return goNextAllowed; | | |||
272 | } | | |||
273 | | ||||
274 | public boolean isGoPreviousAllowed() { | | |||
275 | return goPreviousAllowed; | | |||
276 | } | | |||
277 | | ||||
278 | public boolean isSeekAllowed() { | | |||
279 | return seekAllowed; | | |||
280 | } | | |||
281 | | ||||
282 | public long getPosition(){ | | |||
283 | if(playing) { | | |||
284 | return lastPosition + (System.currentTimeMillis() - lastPositionTime); | | |||
285 | } else { | | |||
286 | return lastPosition; | | |||
287 | } | | |||
288 | } | 345 | } | ||
289 | 346 | | |||
290 | private void requestPlayerList() { | 347 | private void requestPlayerList() { | ||
291 | NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); | 348 | NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); | ||
292 | np.set("requestPlayerList",true); | 349 | np.set("requestPlayerList",true); | ||
293 | device.sendPackage(np); | 350 | device.sendPackage(np); | ||
294 | } | 351 | } | ||
295 | 352 | | |||
296 | private void requestPlayerStatus() { | 353 | private void requestPlayerStatus(String player) { | ||
297 | NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); | 354 | NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); | ||
298 | np.set("player",player); | 355 | np.set("player", player); | ||
299 | np.set("requestNowPlaying",true); | 356 | np.set("requestNowPlaying",true); | ||
300 | np.set("requestVolume",true); | 357 | np.set("requestVolume",true); | ||
301 | device.sendPackage(np); | 358 | device.sendPackage(np); | ||
302 | } | 359 | } | ||
303 | 360 | | |||
304 | @Override | 361 | @Override | ||
305 | public boolean hasMainActivity() { | 362 | public boolean hasMainActivity() { | ||
306 | return true; | 363 | return true; | ||
Show All 15 Lines |