diff --git a/android/Snapcast/src/main/java/de/badaix/snapcast/ClientInfoItem.java b/android/Snapcast/src/main/java/de/badaix/snapcast/ClientInfoItem.java index 6b8c06bf..6bbba9f7 100644 --- a/android/Snapcast/src/main/java/de/badaix/snapcast/ClientInfoItem.java +++ b/android/Snapcast/src/main/java/de/badaix/snapcast/ClientInfoItem.java @@ -10,17 +10,18 @@ import android.widget.TextView; import de.badaix.snapcast.control.ClientInfo; +import de.badaix.snapcast.control.Volume; -public class ClientInfoItem extends LinearLayout { +public class ClientInfoItem extends LinearLayout implements SeekBar.OnSeekBarChangeListener { - public static interface OnAppItemChangedListener { - abstract void onChanged(ClientInfo clientInfo, boolean enabled); + public interface ClientInfoItemListener { + void onVolumeChanged(ClientInfoItem clientInfoItem, Volume volume); } private TextView title; - private TextView summary; private SeekBar volumeSeekBar; private ClientInfo clientInfo; + private ClientInfoItemListener listener = null; public ClientInfoItem(Context context, ClientInfo clientInfo) { super(context); @@ -28,10 +29,10 @@ public class ClientInfoItem extends LinearLayout { .getSystemService(Context.LAYOUT_INFLATER_SERVICE); vi.inflate(R.layout.client_info, this); title = (TextView) findViewById(R.id.title); - summary = (TextView) findViewById(R.id.summary); volumeSeekBar = (SeekBar) findViewById(R.id.volumeSeekBar); volumeSeekBar.setMax(100); setClientInfo(clientInfo); + volumeSeekBar.setOnSeekBarChangeListener(this); } public void setClientInfo(final ClientInfo clientInfo) { @@ -40,7 +41,6 @@ public class ClientInfoItem extends LinearLayout { title.setText(clientInfo.getName()); else title.setText(clientInfo.getHost()); - summary.setText(clientInfo.getMac()); volumeSeekBar.setProgress(clientInfo.getVolume().getPercent()); } @@ -48,4 +48,27 @@ public class ClientInfoItem extends LinearLayout { return clientInfo; } + public void setListener(ClientInfoItemListener listener) { + this.listener = listener; + } + + @Override + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + if (fromUser && (listener != null)) { + Volume volume = new Volume(progress, false); + clientInfo.setVolume(volume); + listener.onVolumeChanged(this, volume); + } + } + + @Override + public void onStartTrackingTouch(SeekBar seekBar) { + + } + + @Override + public void onStopTrackingTouch(SeekBar seekBar) { + + } + } diff --git a/android/Snapcast/src/main/java/de/badaix/snapcast/MainActivity.java b/android/Snapcast/src/main/java/de/badaix/snapcast/MainActivity.java index 87c81506..0ab66d25 100644 --- a/android/Snapcast/src/main/java/de/badaix/snapcast/MainActivity.java +++ b/android/Snapcast/src/main/java/de/badaix/snapcast/MainActivity.java @@ -38,8 +38,9 @@ import java.io.OutputStream; import de.badaix.snapcast.control.ClientInfo; import de.badaix.snapcast.control.ServerInfo; +import de.badaix.snapcast.control.Volume; -public class MainActivity extends ActionBarActivity implements View.OnClickListener, TcpClient.TcpClientListener { +public class MainActivity extends ActionBarActivity implements View.OnClickListener, TcpClient.TcpClientListener, ClientInfoItem.ClientInfoItemListener { private static final String TAG = "Main"; @@ -101,7 +102,7 @@ public class MainActivity extends ActionBarActivity implements View.OnClickListe } serverInfo = new ServerInfo(); - clientInfoAdapter = new ClientInfoAdapter(this, 0); + clientInfoAdapter = new ClientInfoAdapter(this, this); lvClient.setAdapter(clientInfoAdapter); copyAssets(); @@ -113,12 +114,15 @@ public class MainActivity extends ActionBarActivity implements View.OnClickListe private class ClientInfoAdapter extends ArrayAdapter { private Context context; + private ClientInfoItem.ClientInfoItemListener listener; - public ClientInfoAdapter(Context context, int textViewResourceId) { - super(context, textViewResourceId); + public ClientInfoAdapter(Context context, ClientInfoItem.ClientInfoItemListener listener) { + super(context, 0); this.context = context; + this.listener = listener; } + @Override public View getView(int position, View convertView, ViewGroup parent) { ClientInfo clientInfo = getItem(position); @@ -130,11 +134,32 @@ public class MainActivity extends ActionBarActivity implements View.OnClickListe } else { clientInfoItem = new ClientInfoItem(context, clientInfo); } - + clientInfoItem.setListener(listener); return clientInfoItem; } + + public void addClient(ClientInfo clientInfo) { + if (clientInfo == null) + return; + + for (int i = 0; i < getCount(); i++) { + ClientInfo client = getItem(i); + if (client.getMac().equals(clientInfo.getMac())) { + insert(clientInfo, i); + remove(client); + return; + } + } + add(clientInfo); + } } + @Override + protected void onPause() { + if ((tcpClient != null) && (tcpClient.isConnected())) + tcpClient.stop(); + super.onPause(); + } private void copyAssets() { AssetManager assetManager = getAssets(); @@ -211,9 +236,13 @@ public class MainActivity extends ActionBarActivity implements View.OnClickListe Toast.makeText(this, "Scan", Toast.LENGTH_SHORT).show(); initializeDiscoveryListener(); } else if (view == button) { - Toast.makeText(this, "Connecting", Toast.LENGTH_SHORT).show(); - tcpClient = new TcpClient(this); - tcpClient.start(host, port + 1); + if ((tcpClient == null) || !tcpClient.isConnected()) { + Toast.makeText(this, "Connecting", Toast.LENGTH_SHORT).show(); + tcpClient = new TcpClient(this); + tcpClient.start(host, port + 1); + } else { + tcpClient.sendMessage("{\"jsonrpc\": \"2.0\", \"method\": \"System.GetStatus\", \"id\": 1}"); + } } } @@ -336,7 +365,7 @@ public class MainActivity extends ActionBarActivity implements View.OnClickListe JSONObject json = new JSONObject(message); if (json.has("id")) { Log.d(TAG, "ID: " + json.getString("id")); - if (json.getJSONObject("result").has("clients")) { + if ((json.get("result") instanceof JSONObject) && json.getJSONObject("result").has("clients")) { JSONArray clients = json.getJSONObject("result").getJSONArray("clients"); for (int i = 0; i < clients.length(); i++) { final ClientInfo clientInfo = new ClientInfo(clients.getJSONObject(i)); @@ -346,14 +375,14 @@ public class MainActivity extends ActionBarActivity implements View.OnClickListe @Override public void run() { if (clientInfo.isConnected()) - clientInfoAdapter.add(clientInfo); + clientInfoAdapter.addClient(clientInfo); } }); } } } else { Log.d(TAG, "Notification: " + json.getString("method")); - if (json.getString("method").equals("Client.OnUpdate")) { + if (json.getString("method").equals("Client.OnUpdate") || json.getString("method").equals("Client.OnConnect")) { final ClientInfo clientInfo = new ClientInfo(json.getJSONObject("params").getJSONObject("data")); Log.d(TAG, "ClientInfo: " + clientInfo); Log.d(TAG, "Changed: " + serverInfo.addClient(clientInfo)); @@ -362,7 +391,7 @@ public class MainActivity extends ActionBarActivity implements View.OnClickListe @Override public void run() { if (clientInfo.isConnected()) - clientInfoAdapter.add(clientInfo); + clientInfoAdapter.addClient(clientInfo); } }); } @@ -382,5 +411,13 @@ public class MainActivity extends ActionBarActivity implements View.OnClickListe public void onDisconnected(TcpClient tcpClient) { Log.d(TAG, "onDisconnected"); } + + @Override + public void onVolumeChanged(ClientInfoItem clientInfoItem, Volume volume) { + ClientInfo client = clientInfoItem.getClientInfo(); + tcpClient.sendMessage("{\"jsonrpc\": \"2.0\", \"method\": \"Client.SetVolume\", \"params\": {\"client\": \"" + client.getMac() + "\", \"volume\": " + client.getVolume().getPercent() + "}, \"id\": 3}"); + } + } + diff --git a/android/Snapcast/src/main/java/de/badaix/snapcast/TcpClient.java b/android/Snapcast/src/main/java/de/badaix/snapcast/TcpClient.java index 36ff7e3c..003c5383 100644 --- a/android/Snapcast/src/main/java/de/badaix/snapcast/TcpClient.java +++ b/android/Snapcast/src/main/java/de/badaix/snapcast/TcpClient.java @@ -22,7 +22,9 @@ public class TcpClient { // class at on asynckTask doInBackground public interface TcpClientListener { void onMessageReceived(TcpClient tcpClient, String message); + void onConnected(TcpClient tcpClient); + void onDisconnected(TcpClient tcpClient); } @@ -36,6 +38,7 @@ public class TcpClient { // used to read messages from the server private BufferedReader mBufferIn; private Thread worker = null; + private Socket socket = null; private String uid; @@ -47,6 +50,12 @@ public class TcpClient { mMessageListener = listener; } + public boolean isConnected() { + if (socket == null) + return false; + return socket.isConnected(); + } + /** * Sends the message entered by client to the server * @@ -90,7 +99,7 @@ public class TcpClient { Log.d(TAG, "Connecting to " + serverAddr.getCanonicalHostName() + ":" + port); // create a socket to make the connection with the server - Socket socket = new Socket(serverAddr, port); + socket = new Socket(serverAddr, port); try { diff --git a/android/Snapcast/src/main/res/drawable/big_card.xml b/android/Snapcast/src/main/res/drawable/big_card.xml new file mode 100644 index 00000000..c085a937 --- /dev/null +++ b/android/Snapcast/src/main/res/drawable/big_card.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/Snapcast/src/main/res/layout/client_info.xml b/android/Snapcast/src/main/res/layout/client_info.xml index a4800395..be49c8fb 100644 --- a/android/Snapcast/src/main/res/layout/client_info.xml +++ b/android/Snapcast/src/main/res/layout/client_info.xml @@ -2,11 +2,12 @@ + - - + android:textAppearance="?android:attr/textAppearanceMedium" /> + + #ff999999 + #ffffffff + #8c666666 + \ No newline at end of file diff --git a/control/testClient.py b/control/testClient.py index 751f7ae5..6e53e991 100755 --- a/control/testClient.py +++ b/control/testClient.py @@ -49,8 +49,8 @@ doRequest("{\"jsonrpc\": \"2.0\", \"method\": \"Client.SetMute\", \"params\": {\ doRequest("{\"jsonrpc\": \"2.0\", \"method\": \"Client.SetMute\", \"params\": {\"client\": \"80:1f:02:ed:fd:e0\", \"mute\": false}, \"id\": 9}\r\n") doRequest("{\"jsonrpc\": \"2.0\", \"method\": \"Client.SetMute\", \"params\": {\"client\": \"80:1f:02:ed:fd:e0\", \"mute\": true}, \"id\": 9}\r\n") doRequest("{\"jsonrpc\": \"2.0\", \"method\": \"Client.SetMute\", \"params\": {\"client\": \"80:1f:02:ed:fd:e0\", \"mute\": false}, \"id\": 9}\r\n") -doRequest("{\"jsonrpc\": \"2.0\", \"method\": \"Client.SetLatency\", \"params\": {\"client\": \"80:1f:02:ed:fd:e0\", \"latency\": 10}, \"id\": 7}\r\n") -doRequest("{\"jsonrpc\": \"2.0\", \"method\": \"Client.SetName\", \"params\": {\"client\": \"80:1f:02:ed:fd:e0\", \"name\": \"living room\"}, \"id\": 8}\r\n") +doRequest("{\"jsonrpc\": \"2.0\", \"method\": \"Client.SetLatency\", \"params\": {\"client\": \"80:1f:02:ed:fd:e0\", \"latency\": 0}, \"id\": 7}\r\n") +doRequest("{\"jsonrpc\": \"2.0\", \"method\": \"Client.SetName\", \"params\": {\"client\": \"80:1f:02:ed:fd:e0\", \"name\": \"T400\"}, \"id\": 8}\r\n") #invalid json doRequest("some message to test invalid requests\r\n") #missing id