diff --git a/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.java b/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.java --- a/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.java +++ b/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisActivity.java @@ -51,7 +51,7 @@ private String deviceId; private final Handler positionSeekUpdateHandler = new Handler(); private Runnable positionSeekUpdateRunnable = null; - private String targetPlayer = null; + private MprisPlugin.MprisPlayer targetPlayer = null; private static String milisToProgress(long milis) { int length = (int)(milis / 1000); //From milis to seconds @@ -69,7 +69,7 @@ text.append(seconds); return text.toString(); } - protected void connectToPlugin() { + protected void connectToPlugin(final String targetPlayerName) { BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() { @Override @@ -81,56 +81,15 @@ Log.e("MprisActivity", "device has no mpris plugin!"); return; } + targetPlayer = mpris.getPlayerStatus(targetPlayerName); mpris.setPlayerStatusUpdatedHandler("activity", new Handler() { @Override public void handleMessage(Message msg) { runOnUiThread(new Runnable() { @Override public void run() { - String song = mpris.getCurrentSong(); - - TextView nowPlaying = (TextView) findViewById(R.id.now_playing_textview); - if (!nowPlaying.getText().toString().equals(song)) { - nowPlaying.setText(song); - } - - //Hacks for Spotify because it reports incorrect info about what it supports - boolean isSpotify = "spotify".equals(mpris.getPlayer().toLowerCase()); - - if (mpris.getLength() > -1 && mpris.getPosition() > -1 && !isSpotify) { - ((TextView) findViewById(R.id.time_textview)).setText(milisToProgress(mpris.getLength())); - SeekBar positionSeek = (SeekBar)findViewById(R.id.positionSeek); - positionSeek.setMax((int)(mpris.getLength())); - positionSeek.setProgress((int)(mpris.getPosition())); - findViewById(R.id.progress_slider).setVisibility(View.VISIBLE); - } else { - findViewById(R.id.progress_slider).setVisibility(View.GONE); - } - - int volume = mpris.getVolume(); - ((SeekBar) findViewById(R.id.volume_seek)).setProgress(volume); - - boolean isPlaying = mpris.isPlaying(); - if (isPlaying) { - ((ImageButton) findViewById(R.id.play_button)).setImageResource(android.R.drawable.ic_media_pause); - findViewById(R.id.play_button).setVisibility(mpris.isPauseAllowed() ? View.VISIBLE : View.GONE); - } else { - ((ImageButton) findViewById(R.id.play_button)).setImageResource(android.R.drawable.ic_media_play); - findViewById(R.id.play_button).setVisibility(mpris.isPlayAllowed() ? View.VISIBLE : View.GONE); - } - - if (isSpotify) { - findViewById(R.id.volume_layout).setVisibility(View.INVISIBLE); - findViewById(R.id.rew_button).setVisibility(View.GONE); - findViewById(R.id.ff_button).setVisibility(View.GONE); - } else { - findViewById(R.id.volume_layout).setVisibility(View.VISIBLE); - findViewById(R.id.rew_button).setVisibility(mpris.isSeekAllowed() ? View.VISIBLE : View.GONE); - findViewById(R.id.ff_button).setVisibility(mpris.isSeekAllowed() ? View.VISIBLE : View.GONE); - } - findViewById(R.id.next_button).setVisibility(mpris.isGoNextAllowed() ? View.VISIBLE : View.GONE); - findViewById(R.id.prev_button).setVisibility(mpris.isGoPreviousAllowed() ? View.VISIBLE : View.GONE); + updatePlayerStatus(mpris); } }); } @@ -169,36 +128,28 @@ if (pos >= playerList.size()) return; String player = playerList.get(pos); - if (player.equals(mpris.getPlayer())) { + if (targetPlayer != null && player.equals(targetPlayer.getPlayer())) { return; //Player hasn't actually changed } - mpris.setPlayer(player); - - //Clear values from previous player - ((TextView) findViewById(R.id.now_playing_textview)).setText(""); - ((TextView) findViewById(R.id.time_textview)).setText(milisToProgress(0)); - ((SeekBar)findViewById(R.id.positionSeek)).setMax(0); + targetPlayer = mpris.getPlayerStatus(player); + updatePlayerStatus(mpris); } @Override public void onNothingSelected(AdapterView arg0) { - mpris.setPlayer(null); + targetPlayer = null; } }); if (targetPlayer != null) { - int targetIndex = adapter.getPosition(targetPlayer); + int targetIndex = adapter.getPosition(targetPlayer.getPlayer()); if (targetIndex >= 0) { spinner.setSelection(targetIndex); - } - targetPlayer = null; - } else { - // restore last selected player - int position = adapter.getPosition(mpris.getPlayer()); - if (position >= 0) { - spinner.setSelection(position); + } else { + targetPlayer = null; } } + updatePlayerStatus(mpris); } }); } @@ -212,7 +163,7 @@ private final BaseLinkProvider.ConnectionReceiver connectionReceiver = new BaseLinkProvider.ConnectionReceiver() { @Override public void onConnectionReceived(NetworkPackage identityPackage, BaseLink link) { - connectToPlugin(); + connectToPlugin(null); } @Override @@ -232,49 +183,78 @@ }); } + private void updatePlayerStatus(MprisPlugin mpris) { + MprisPlugin.MprisPlayer playerStatus = targetPlayer; + if (playerStatus == null) { + //No player with that name found, just display "empty" data + playerStatus = mpris.getEmptyPlayer(); + } + String song = playerStatus.getCurrentSong(); + + TextView nowPlaying = (TextView) findViewById(R.id.now_playing_textview); + if (!nowPlaying.getText().toString().equals(song)) { + nowPlaying.setText(song); + } + + if (playerStatus.isSeekAllowed()) { + ((TextView) findViewById(R.id.time_textview)).setText(milisToProgress(playerStatus.getLength())); + SeekBar positionSeek = (SeekBar)findViewById(R.id.positionSeek); + positionSeek.setMax((int)(playerStatus.getLength())); + positionSeek.setProgress((int)(playerStatus.getPosition())); + findViewById(R.id.progress_slider).setVisibility(View.VISIBLE); + } else { + findViewById(R.id.progress_slider).setVisibility(View.GONE); + } + + int volume = playerStatus.getVolume(); + ((SeekBar) findViewById(R.id.volume_seek)).setProgress(volume); + + boolean isPlaying = playerStatus.isPlaying(); + if (isPlaying) { + ((ImageButton) findViewById(R.id.play_button)).setImageResource(android.R.drawable.ic_media_pause); + findViewById(R.id.play_button).setVisibility(playerStatus.isPauseAllowed() ? View.VISIBLE : View.GONE); + } else { + ((ImageButton) findViewById(R.id.play_button)).setImageResource(android.R.drawable.ic_media_play); + findViewById(R.id.play_button).setVisibility(playerStatus.isPlayAllowed() ? View.VISIBLE : View.GONE); + } + + findViewById(R.id.volume_layout).setVisibility(playerStatus.isSetVolumeAllowed() ? View.VISIBLE : View.INVISIBLE); + findViewById(R.id.rew_button).setVisibility(playerStatus.isSeekAllowed() ? View.VISIBLE : View.GONE); + findViewById(R.id.ff_button).setVisibility(playerStatus.isSeekAllowed() ? View.VISIBLE : View.GONE); + findViewById(R.id.next_button).setVisibility(playerStatus.isGoNextAllowed() ? View.VISIBLE : View.GONE); + findViewById(R.id.prev_button).setVisibility(playerStatus.isGoPreviousAllowed() ? View.VISIBLE : View.GONE); + } + /** * Change current volume with provided step. * - * @param mpris multimedia controller * @param step step size volume change */ - private void updateVolume(MprisPlugin mpris, int step) { - final int currentVolume = mpris.getVolume(); + private void updateVolume(int step) { + if (targetPlayer == null) { + return; + } + final int currentVolume = targetPlayer.getVolume(); + if(currentVolume < 100 || currentVolume > 0) { int newVolume = currentVolume + step; if(newVolume > 100) { newVolume = 100; } else if (newVolume <0 ) { newVolume = 0; } - mpris.setVolume(newVolume); + targetPlayer.setVolume(newVolume); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: - BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() { - @Override - public void onServiceStart(BackgroundService service) { - Device device = service.getDevice(deviceId); - MprisPlugin mpris = device.getPlugin(MprisPlugin.class); - if (mpris == null) return; - updateVolume(mpris, 5); - } - }); + updateVolume(5); return true; case KeyEvent.KEYCODE_VOLUME_DOWN: - BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() { - @Override - public void onServiceStart(BackgroundService service) { - Device device = service.getDevice(deviceId); - MprisPlugin mpris = device.getPlugin(MprisPlugin.class); - if (mpris == null) return; - updateVolume(mpris, -5); - } - }); + updateVolume(-5); return true; default: return super.onKeyDown(keyCode, event); @@ -298,7 +278,7 @@ super.onCreate(savedInstanceState); setContentView(R.layout.mpris_control); - targetPlayer = getIntent().getStringExtra("player"); + final String targetPlayerName = getIntent().getStringExtra("player"); getIntent().removeExtra("player"); deviceId = getIntent().getStringExtra("deviceId"); @@ -313,18 +293,16 @@ service.addConnectionListener(connectionReceiver); } }); - connectToPlugin(); + connectToPlugin(targetPlayerName); findViewById(R.id.play_button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() { @Override public void onServiceStart(BackgroundService service) { - Device device = service.getDevice(deviceId); - MprisPlugin mpris = device.getPlugin(MprisPlugin.class); - if (mpris == null) return; - mpris.sendAction("PlayPause"); + if (targetPlayer == null) return; + targetPlayer.sendAction("PlayPause"); } }); } @@ -336,10 +314,8 @@ BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() { @Override public void onServiceStart(BackgroundService service) { - Device device = service.getDevice(deviceId); - MprisPlugin mpris = device.getPlugin(MprisPlugin.class); - if (mpris == null) return; - mpris.sendAction("Previous"); + if (targetPlayer == null) return; + targetPlayer.sendAction("Previous"); } }); } @@ -351,10 +327,8 @@ BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() { @Override public void onServiceStart(BackgroundService service) { - Device device = service.getDevice(deviceId); - MprisPlugin mpris = device.getPlugin(MprisPlugin.class); - if (mpris == null) return; - mpris.Seek(interval_time * -1); + if (targetPlayer == null) return; + targetPlayer.Seek(interval_time * -1); } }); } @@ -366,10 +340,8 @@ BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() { @Override public void onServiceStart(BackgroundService service) { - Device device = service.getDevice(deviceId); - MprisPlugin mpris = device.getPlugin(MprisPlugin.class); - if (mpris == null) return; - mpris.Seek(interval_time); + if (targetPlayer == null) return; + targetPlayer.Seek(interval_time); } }); } @@ -381,10 +353,8 @@ BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() { @Override public void onServiceStart(BackgroundService service) { - Device device = service.getDevice(deviceId); - MprisPlugin mpris = device.getPlugin(MprisPlugin.class); - if (mpris == null) return; - mpris.sendAction("Next"); + if (targetPlayer == null) return; + targetPlayer.sendAction("Next"); } }); } @@ -404,10 +374,8 @@ BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() { @Override public void onServiceStart(BackgroundService service) { - Device device = service.getDevice(deviceId); - MprisPlugin mpris = device.getPlugin(MprisPlugin.class); - if (mpris == null) return; - mpris.setVolume(seekBar.getProgress()); + if (targetPlayer == null) return; + targetPlayer.setVolume(seekBar.getProgress()); } }); } @@ -421,12 +389,8 @@ BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() { @Override public void onServiceStart(BackgroundService service) { - Device device = service.getDevice(deviceId); - if (device != null) { - MprisPlugin mpris = device.getPlugin(MprisPlugin.class); - if (mpris != null) { - positionSeek.setProgress((int) (mpris.getPosition())); - } + if (targetPlayer != null) { + positionSeek.setProgress((int) (targetPlayer.getPosition())); } positionSeekUpdateHandler.removeCallbacks(positionSeekUpdateRunnable); positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 1000); @@ -453,10 +417,8 @@ BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() { @Override public void onServiceStart(BackgroundService service) { - Device device = service.getDevice(deviceId); - MprisPlugin mpris = device.getPlugin(MprisPlugin.class); - if (mpris != null) { - mpris.setPosition(seekBar.getProgress()); + if (targetPlayer != null) { + targetPlayer.setPosition(seekBar.getProgress()); } positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 200); } diff --git a/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisPlugin.java b/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisPlugin.java --- a/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisPlugin.java +++ b/src/org/kde/kdeconnect/Plugins/MprisPlugin/MprisPlugin.java @@ -33,29 +33,111 @@ import org.kde.kdeconnect_tp.R; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; +import java.util.Map; public class MprisPlugin extends Plugin { + public class MprisPlayer { + private String player = ""; + private boolean playing = false; + private String currentSong = ""; + private int volume = 50; + private long length = -1; + private long lastPosition = 0; + private long lastPositionTime; + private boolean playAllowed = true; + private boolean pauseAllowed = true; + private boolean goNextAllowed = true; + private boolean goPreviousAllowed = true; + private boolean seekAllowed = true; + + public MprisPlayer() { + lastPositionTime = System.currentTimeMillis(); + } + + public String getCurrentSong() { + return currentSong; + } + + public String getPlayer() { + return player; + } + + private boolean isSpotify() { + return getPlayer().toLowerCase().equals("spotify"); + } + + public int getVolume() { + return volume; + } + + public long getLength(){ return length; } + + public boolean isPlaying() { + return playing; + } + + public boolean isPlayAllowed() { + return playAllowed; + } + + public boolean isPauseAllowed() { + return pauseAllowed; + } + + public boolean isGoNextAllowed() { + return goNextAllowed; + } + + public boolean isGoPreviousAllowed() { + return goPreviousAllowed; + } + + public boolean isSeekAllowed() { + return seekAllowed && getLength() >= 0 && getPosition() >= 0 && !isSpotify(); + } + + public boolean isSetVolumeAllowed() { + return !isSpotify(); + } + + public long getPosition(){ + if(playing) { + return lastPosition + (System.currentTimeMillis() - lastPositionTime); + } else { + return lastPosition; + } + } + + public void sendAction(String action) { + MprisPlugin.this.sendAction(getPlayer(), action); + } + + public void setVolume(int volume) { + MprisPlugin.this.setVolume(getPlayer(), volume); + } + + public void setPosition(int position) { + MprisPlugin.this.setPosition(getPlayer(), position); + + lastPosition = position; + lastPositionTime = System.currentTimeMillis(); + } + + public void Seek(int offset) { + MprisPlugin.this.Seek(getPlayer(), offset); + } + } public final static String PACKAGE_TYPE_MPRIS = "kdeconnect.mpris"; public final static String PACKAGE_TYPE_MPRIS_REQUEST = "kdeconnect.mpris.request"; - private String player = ""; - private boolean playing = false; - private String currentSong = ""; - private int volume = 50; - private long length = -1; - private long lastPosition; - private long lastPositionTime; - private boolean playAllowed = true; - private boolean pauseAllowed = true; - private boolean goNextAllowed = true; - private boolean goPreviousAllowed = true; - private boolean seekAllowed = true; + private HashMap players = new HashMap<>(); private HashMap playerStatusUpdated = new HashMap<>(); - private List playerList = new ArrayList<>(); private HashMap playerListUpdated = new HashMap<>(); @Override @@ -81,66 +163,60 @@ @Override public boolean onCreate() { requestPlayerList(); - lastPositionTime = System.currentTimeMillis(); return true; } @Override public void onDestroy() { - playerList.clear(); + players.clear(); } - public void sendAction(String player, String action) { + private void sendAction(String player, String action) { NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); np.set("player", player); np.set("action", action); device.sendPackage(np); } - public void sendAction(String action) { - sendAction(player, action); - } - public void setVolume(int volume) { + private void setVolume(String player, int volume) { NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); np.set("player", player); np.set("setVolume",volume); device.sendPackage(np); } - public void setPosition(int position) { + private void setPosition(String player, int position) { NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); np.set("player", player); np.set("SetPosition", position); device.sendPackage(np); - this.lastPosition = position; - this.lastPositionTime = System.currentTimeMillis(); } - public void Seek(int offset) { + private void Seek(String player, int offset) { NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); np.set("player", player); np.set("Seek", offset); device.sendPackage(np); } @Override public boolean onPackageReceived(NetworkPackage np) { - if (np.has("nowPlaying") || np.has("volume") || np.has("isPlaying") || np.has("length") || np.has("pos")) { - if (np.getString("player").equals(player)) { - currentSong = np.getString("nowPlaying", currentSong); - volume = np.getInt("volume", volume); - length = np.getLong("length", length); + MprisPlayer playerStatus = players.get(np.getString("player")); + if (playerStatus != null) { + playerStatus.currentSong = np.getString("nowPlaying", playerStatus.currentSong); + playerStatus.volume = np.getInt("volume", playerStatus.volume); + playerStatus.length = np.getLong("length", playerStatus.length); if(np.has("pos")){ - lastPosition = np.getLong("pos", lastPosition); - lastPositionTime = System.currentTimeMillis(); + playerStatus.lastPosition = np.getLong("pos", playerStatus.lastPosition); + playerStatus.lastPositionTime = System.currentTimeMillis(); } - playing = np.getBoolean("isPlaying", playing); - playAllowed = np.getBoolean("canPlay", playAllowed); - pauseAllowed = np.getBoolean("canPause", pauseAllowed); - goNextAllowed = np.getBoolean("canGoNext", goNextAllowed); - goPreviousAllowed = np.getBoolean("canGoPrevious", goPreviousAllowed); - seekAllowed = np.getBoolean("canSeek", seekAllowed); + playerStatus.playing = np.getBoolean("isPlaying", playerStatus.playing); + playerStatus.playAllowed = np.getBoolean("canPlay", playerStatus.playAllowed); + playerStatus.pauseAllowed = np.getBoolean("canPause", playerStatus.pauseAllowed); + playerStatus.goNextAllowed = np.getBoolean("canGoNext", playerStatus.goNextAllowed); + playerStatus.goPreviousAllowed = np.getBoolean("canGoPrevious", playerStatus.goPreviousAllowed); + playerStatus.seekAllowed = np.getBoolean("canSeek", playerStatus.seekAllowed); for (String key : playerStatusUpdated.keySet()) { try { playerStatusUpdated.get(key).dispatchMessage(new Message()); @@ -155,18 +231,37 @@ List newPlayerList = np.getStringList("playerList"); if (newPlayerList != null) { - boolean equals = false; - if (newPlayerList.size() == playerList.size()) { - equals = true; - for (int i=0; i> iter = players.entrySet().iterator(); + while (iter.hasNext()) { + String oldPlayer = iter.next().getKey(); + + boolean found = false; + for (String newPlayer : newPlayerList) { + if (newPlayer.equals(oldPlayer)) { + found = true; break; } } + + if (!found) { + iter.remove(); + equals = false; + } } if (!equals) { - playerList = newPlayerList; for (String key : playerListUpdated.keySet()) { try { playerListUpdated.get(key).dispatchMessage(new Message()); @@ -198,8 +293,8 @@ h.dispatchMessage(new Message()); //Get the status if this is the first handler we have - if (playerListUpdated.size() == 1) { - requestPlayerStatus(); + if (playerListUpdated.size() == 1 && !players.isEmpty()) { + requestPlayerStatus(getPlayerList().get(0)); } } @@ -214,88 +309,29 @@ } } - public void setPlayer(String player) { - if (player == null || player.equals(this.player)) return; - this.player = player; - currentSong = ""; - volume = 50; - playing = false; - playAllowed = true; - pauseAllowed = true; - goNextAllowed = true; - goPreviousAllowed = true; - seekAllowed = true; - for (String key : playerStatusUpdated.keySet()) { - try { - playerStatusUpdated.get(key).dispatchMessage(new Message()); - } catch(Exception e) { - e.printStackTrace(); - Log.e("MprisControl","Exception"); - playerStatusUpdated.remove(key); - } - } - requestPlayerStatus(); - } - public List getPlayerList() { - return playerList; - } - - public String getCurrentSong() { - return currentSong; + List playerlist = new ArrayList<>(players.keySet()); + Collections.sort(playerlist); + return playerlist; } - public String getPlayer() { - return player; + public MprisPlayer getPlayerStatus(String player) { + return players.get(player); } - public int getVolume() { - return volume; - } - - public long getLength(){ return length; } - - public boolean isPlaying() { - return playing; - } - - public boolean isPlayAllowed() { - return playAllowed; - } - - public boolean isPauseAllowed() { - return pauseAllowed; - } - - public boolean isGoNextAllowed() { - return goNextAllowed; - } - - public boolean isGoPreviousAllowed() { - return goPreviousAllowed; - } - - public boolean isSeekAllowed() { - return seekAllowed; - } - - public long getPosition(){ - if(playing) { - return lastPosition + (System.currentTimeMillis() - lastPositionTime); - } else { - return lastPosition; - } + public MprisPlayer getEmptyPlayer() { + return new MprisPlayer(); } private void requestPlayerList() { NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); np.set("requestPlayerList",true); device.sendPackage(np); } - private void requestPlayerStatus() { + private void requestPlayerStatus(String player) { NetworkPackage np = new NetworkPackage(PACKAGE_TYPE_MPRIS_REQUEST); - np.set("player",player); + np.set("player", player); np.set("requestNowPlaying",true); np.set("requestVolume",true); device.sendPackage(np);