Commit b519bc5e authored by Albert Vaca Cintora's avatar Albert Vaca Cintora

Improvements and bugs fixed in the mpris plugin

parent 816a6f5c
......@@ -102,7 +102,8 @@
android:singleLine="true"
android:gravity="center"
android:padding="8dip"
android:layout_gravity="center" />
android:layout_gravity="center"
android:text="00:00" />
<SeekBar
android:layout_width="wrap_content"
......@@ -119,15 +120,17 @@
android:singleLine="true"
android:gravity="center"
android:padding="8dip"
android:layout_gravity="center" />
android:layout_gravity="center"
android:text="00:00" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="70dip"
android:layout_height="wrap_content"
android:id="@+id/volume_layout"
android:layout_gravity="center">
android:layout_gravity="center"
android:layout_marginTop="8dip">
<ImageView
android:layout_width="30dip"
......
......@@ -2,38 +2,57 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:layout_height="64dp">
<TextView
android:id="@+id/notification_song"
android:layout_width="wrap_content"
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Song Name"
android:layout_gravity="center_vertical"
android:layout_weight="1"
android:singleLine="true"
android:paddingLeft="3dp" />
android:layout_weight="1">
<TextView
android:id="@+id/notification_song"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@android:style/TextAppearance.Medium.Inverse"
android:text="Song Name"
android:layout_weight="1"
android:singleLine="true"/>
<TextView
android:id="@+id/notification_player"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@android:style/TextAppearance.Small.Inverse"
android:text="Player Name"
android:layout_weight="1"
android:singleLine="true"/>
</LinearLayout>
<ImageButton
android:id="@+id/notification_prev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_gravity="center_vertical"
android:src="@android:drawable/ic_media_previous" />
<ImageButton
android:id="@+id/notification_play_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_gravity="center_vertical"
android:src="@android:drawable/ic_media_play" />
<ImageButton
android:id="@+id/notification_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:layout_gravity="center_vertical"
android:src="@android:drawable/ic_media_next" />
......
......@@ -110,6 +110,7 @@
<string name="sftp_camera">Camera pictures</string>
<string name="add_host">Add host/IP</string>
<string name="custom_dev_list_help">Use this option only if your device is not automatically detected. Enter IP address or hostname below and touch the button to add it to the list. Touch an existing item to remove it from the list.</string>
<string name="mpris_player_on_device">%1$s on %2$s</string>
<string-array name="mpris_time_entries">
<item>10 seconds</item>
......
package org.kde.kdeconnect.Helpers;
import java.util.concurrent.atomic.AtomicInteger;
public class NotificationsHelper {
private final static AtomicInteger c = new AtomicInteger((int)System.currentTimeMillis());
public static int getUniqueId() {
return c.incrementAndGet();
}
}
......@@ -47,14 +47,11 @@ import java.util.ArrayList;
public class MprisActivity extends ActionBarActivity {
//TODO: Add a loading spinner at the beginning (to distinguish the loading state from a no-players state).
//TODO 2: Add a message when no players are detected after loading completes
private String deviceId;
private final Handler positionSeekUpdateHandler = new Handler();
private Runnable positionSeekUpdateRunnable;
private boolean positionSeekUpdateScheduled = false;
NotificationPanel nPanel;
private Runnable positionSeekUpdateRunnable = null;
private NotificationPanel nPanel = null;
private String targetPlayer = null;
private static String milisToProgress(long milis) {
int length = (int)(milis / 1000); //From milis to seconds
......@@ -75,27 +72,25 @@ public class MprisActivity extends ActionBarActivity {
protected void connectToPlugin() {
final String deviceId = getIntent().getStringExtra("deviceId");
BackgroundService.RunCommand(this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
final Device device = service.getDevice(deviceId);
final MprisPlugin mpris = (MprisPlugin) device.getPlugin("plugin_mpris");
if (mpris == null) {
Log.e("MprisActivity", "device has no mpris plugin!");
return;
}
mpris.setPlayerStatusUpdatedHandler(new Handler() {
mpris.setPlayerStatusUpdatedHandler("activity", new Handler() {
@Override
public void handleMessage(Message msg) {
runOnUiThread(new Runnable() {
@Override
public void run() {
String s = mpris.getCurrentSong();
((TextView) findViewById(R.id.now_playing_textview)).setText(s);
String song = mpris.getCurrentSong();
((TextView) findViewById(R.id.now_playing_textview)).setText(song);
if (mpris.getLength() > -1 && mpris.getPosition() > -1 && !"Spotify".equals(mpris.getPlayer())) {
((TextView) findViewById(R.id.time_textview)).setText(milisToProgress(mpris.getLength()));
......@@ -115,19 +110,15 @@ public class MprisActivity extends ActionBarActivity {
boolean isPlaying = mpris.isPlaying();
if (isPlaying) {
((ImageButton) findViewById(R.id.play_button)).setImageResource(android.R.drawable.ic_media_pause);
if(!positionSeekUpdateScheduled) {
positionSeekUpdateRunnable.run();
}
} else {
((ImageButton) findViewById(R.id.play_button)).setImageResource(android.R.drawable.ic_media_play);
}
nPanel.updateStatus(s,isPlaying);
}
});
}
});
mpris.setPlayerListUpdatedHandler(new Handler() {
mpris.setPlayerListUpdatedHandler("activity", new Handler() {
@Override
public void handleMessage(Message msg) {
final ArrayList<String> playerList = mpris.getPlayerList();
......@@ -135,6 +126,7 @@ public class MprisActivity extends ActionBarActivity {
android.R.layout.simple_spinner_item,
playerList.toArray(new String[playerList.size()])
);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
runOnUiThread(new Runnable() {
@Override
......@@ -166,6 +158,9 @@ public class MprisActivity extends ActionBarActivity {
findViewById(R.id.ff_button).setVisibility(View.VISIBLE);
findViewById(R.id.positionSeek).setVisibility(View.VISIBLE);
}
//If there was a panel already, this will override it
nPanel = new NotificationPanel(getApplicationContext(), device, player);
}
@Override
......@@ -174,11 +169,18 @@ public class MprisActivity extends ActionBarActivity {
}
});
// restore the selected player
int position = adapter.getPosition(mpris.getPlayer());
if (position >= 0) {
spinner.setSelection(position);
if (targetPlayer != null) {
int targetIndex = adapter.getPosition(targetPlayer);
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);
}
}
}
});
......@@ -279,11 +281,10 @@ public class MprisActivity extends ActionBarActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.mpris_control);
targetPlayer = getIntent().getStringExtra("player");
getIntent().removeExtra("player");
deviceId = getIntent().getStringExtra("deviceId");
nPanel = new NotificationPanel(this, deviceId);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String interval_time_str = prefs.getString(getString(R.string.mpris_time_key),
getString(R.string.mpris_time_default));
......@@ -390,20 +391,13 @@ public class MprisActivity extends ActionBarActivity {
MprisPlugin mpris = (MprisPlugin) device.getPlugin("plugin_mpris");
if (mpris == null) return;
mpris.setVolume(seekBar.getProgress());
positionSeekUpdateRunnable.run();
}
});
}
});
positionSeekUpdateRunnable = new Runnable() {
private long lastTime;
@Override
public void run() {
final SeekBar positionSeek = (SeekBar)findViewById(R.id.positionSeek);
......@@ -411,21 +405,18 @@ public class MprisActivity extends ActionBarActivity {
BackgroundService.RunCommand(MprisActivity.this, new BackgroundService.InstanceCallback() {
@Override
public void onServiceStart(BackgroundService service) {
positionSeekUpdateScheduled = false;
Device device = service.getDevice(deviceId);
MprisPlugin mpris = (MprisPlugin) device.getPlugin("plugin_mpris");
if (mpris == null) return;
positionSeek.setProgress((int)(mpris.getPosition()));
if(!mpris.isPlaying()) return;
positionSeekUpdateHandler.postDelayed(thisRunnable, 1000);
positionSeekUpdateScheduled = true;
if (mpris != null) {
positionSeek.setProgress((int) (mpris.getPosition()));
}
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 1000);
}
});
}
};
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 200);
((SeekBar)findViewById(R.id.positionSeek)).setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
......@@ -436,7 +427,6 @@ public class MprisActivity extends ActionBarActivity {
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
positionSeekUpdateHandler.removeCallbacks(positionSeekUpdateRunnable);
positionSeekUpdateScheduled = false;
}
@Override
......@@ -446,17 +436,22 @@ public class MprisActivity extends ActionBarActivity {
public void onServiceStart(BackgroundService service) {
Device device = service.getDevice(deviceId);
MprisPlugin mpris = (MprisPlugin) device.getPlugin("plugin_mpris");
if (mpris == null) return;
mpris.setPosition(seekBar.getProgress());
if (mpris != null) {
mpris.setPosition(seekBar.getProgress());
}
positionSeekUpdateHandler.postDelayed(positionSeekUpdateRunnable, 200);
}
});
}
});
}
@Override
protected void onPause() {
super.onPause();
positionSeekUpdateHandler.removeCallbacks(positionSeekUpdateRunnable);
}
}
......@@ -36,24 +36,23 @@ import org.kde.kdeconnect.Plugins.Plugin;
import org.kde.kdeconnect_tp.R;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class MprisPlugin extends Plugin {
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 Handler playerStatusUpdated = null;
private HashMap<String,Handler> playerStatusUpdated = new HashMap<String,Handler>();
private ArrayList<String> playerList = new ArrayList<String>();
private Handler playerListUpdated = null;
private String player = "";
private boolean playing = false;
private HashMap<String,Handler> playerListUpdated = new HashMap<String,Handler>();
@Override
public String getPluginName() {
......@@ -97,22 +96,26 @@ public class MprisPlugin extends Plugin {
playerList.clear();
}
public void sendAction(String s) {
public void sendAction(String player, String action) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MPRIS);
np.set("player",player);
np.set("action",s);
np.set("player", player);
np.set("action", action);
device.sendPackage(np);
}
public void sendAction(String action) {
sendAction(player, action);
}
public void setVolume(int volume) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MPRIS);
np.set("player",player);
np.set("player", player);
np.set("setVolume",volume);
device.sendPackage(np);
}
public void setPosition(int position) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MPRIS);
np.set("player",player);
np.set("player", player);
np.set("SetPosition", position);
device.sendPackage(np);
this.lastPosition = position;
......@@ -121,8 +124,8 @@ public class MprisPlugin extends Plugin {
public void Seek(int offset) {
NetworkPackage np = new NetworkPackage(NetworkPackage.PACKAGE_TYPE_MPRIS);
np.set("player",player);
np.set("Seek",offset);
np.set("player", player);
np.set("Seek", offset);
device.sendPackage(np);
}
......@@ -140,13 +143,13 @@ public class MprisPlugin extends Plugin {
lastPositionTime = System.currentTimeMillis();
}
playing = np.getBoolean("isPlaying", playing);
if (playerStatusUpdated != null) {
for (String key : playerStatusUpdated.keySet()) {
try {
playerStatusUpdated.dispatchMessage(new Message());
playerStatusUpdated.get(key).dispatchMessage(new Message());
} catch(Exception e) {
e.printStackTrace();
Log.e("MprisControl","Exception");
playerStatusUpdated = null;
playerStatusUpdated.remove(key);
}
}
}
......@@ -167,13 +170,13 @@ public class MprisPlugin extends Plugin {
}
if (!equals) {
playerList = newPlayerList;
if (playerListUpdated != null) {
for (String key : playerListUpdated.keySet()) {
try {
playerListUpdated.dispatchMessage(new Message());
playerListUpdated.get(key).dispatchMessage(new Message());
} catch(Exception e) {
e.printStackTrace();
Log.e("MprisControl","Exception");
playerListUpdated = null;
playerListUpdated.remove(key);
}
}
}
......@@ -182,43 +185,52 @@ public class MprisPlugin extends Plugin {
return true;
}
public void setPlayerStatusUpdatedHandler(Handler h) {
playerStatusUpdated = h;
if (currentSong.length() > 0) h.dispatchMessage(new Message());
requestPlayerStatus();
}
public void setPlayerStatusUpdatedHandler(String id, Handler h) {
playerStatusUpdated.put(id, h);
if (playerListUpdated.size() == 1) {
requestPlayerStatus();
}
public String getCurrentSong() {
return currentSong;
h.dispatchMessage(new Message());
}
public void setPlayerListUpdatedHandler(Handler h) {
playerListUpdated = h;
if (playerList.size() > 0) h.dispatchMessage(new Message());
requestPlayerList();
}
public void setPlayerListUpdatedHandler(String id, Handler h) {
if (playerList.size() > 0) {
h.dispatchMessage(new Message());
}
public ArrayList<String> getPlayerList() {
return playerList;
playerListUpdated.put(id,h);
if (playerListUpdated.size() == 1) {
requestPlayerList();
}
}
public void setPlayer(String s) {
player = s;
public void setPlayer(String player) {
if (player == null || player.equals(this.player)) return;
this.player = player;
currentSong = "";
volume = 50;
playing = false;
if (playerStatusUpdated != null) {
for (String key : playerStatusUpdated.keySet()) {
try {
playerStatusUpdated.dispatchMessage(new Message());
playerStatusUpdated.get(key).dispatchMessage(new Message());
} catch(Exception e) {
e.printStackTrace();
Log.e("MprisControl","Exception");
playerStatusUpdated = null;
playerStatusUpdated.remove(key);
}
}
requestPlayerStatus();
}
public ArrayList<String> getPlayerList() {
return playerList;
}
public String getCurrentSong() {
return currentSong;
}
public String getPlayer() {
return player;
}
......
......@@ -23,73 +23,113 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import android.widget.RemoteViews;
import org.kde.kdeconnect.Device;
import org.kde.kdeconnect.Helpers.NotificationsHelper;
import org.kde.kdeconnect_tp.R;
public class NotificationPanel {
String deviceId;
private static final int notificationId = 182144338; //Random number, fixed id to make sure we don't produce more than one notification
private String deviceId;
private String player;
private MprisActivity parent;
private NotificationManager nManager;
private NotificationCompat.Builder nBuilder;
private RemoteViews remoteView;
public NotificationPanel(MprisActivity parent, String deviceId) {
this.parent = parent;
this.deviceId = deviceId;
nBuilder = new NotificationCompat.Builder(parent)
.setContentTitle("Mpris Activity")
public NotificationPanel(Context context, Device device, String player) {
this.deviceId = device.getDeviceId();
this.player = player;
//FIXME: When the mpris plugin gets destroyed and recreated, we should add this listener again
final MprisPlugin mpris = (MprisPlugin)device.getPlugin("plugin_mpris");
if (mpris != null) {
mpris.setPlayerStatusUpdatedHandler("notification", new Handler() {
@Override
public void handleMessage(Message msg) {
String song = mpris.getCurrentSong();
boolean isPlaying = mpris.isPlaying();
updateStatus(song, isPlaying);
}
});
}
Intent launch = new Intent(context, MprisActivity.class);
launch.putExtra("deviceId", deviceId);
launch.putExtra("player", player);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MprisActivity.class);
stackBuilder.addNextIntent(launch);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
nManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
remoteView = new RemoteViews(context.getPackageName(), R.layout.mpris_notification);
nBuilder = new NotificationCompat.Builder(context)
.setContentTitle("KDE Connect")
.setLocalOnly(true)
.setSmallIcon(android.R.drawable.ic_media_play)
.setContentIntent(resultPendingIntent)
.setOngoing(true);
remoteView = new RemoteViews(parent.getPackageName(), R.layout.notification_layout);
String deviceName = device.getName();
String playerOnDevice = context.getString(R.string.mpris_player_on_device, player, deviceName);
remoteView.setTextViewText(R.id.notification_player, playerOnDevice);
//set the button listeners
setListeners(remoteView);
nBuilder.setContent(remoteView);
Intent playpause = new Intent(context, NotificationReturnSlot.class);
playpause.putExtra("action", "play");
playpause.putExtra("deviceId", deviceId);
playpause.putExtra("player", player);
PendingIntent btn1 = PendingIntent.getBroadcast(context, NotificationsHelper.getUniqueId(), playpause, 0);
remoteView.setOnClickPendingIntent(R.id.notification_play_pause, btn1);
Intent next = new Intent(context, NotificationReturnSlot.class);
next.putExtra("action", "next");
next.putExtra("deviceId"