undo delete client

This commit is contained in:
badaix 2016-01-17 14:16:18 +01:00
parent d014e79ad0
commit dd020b0b6d
8 changed files with 133 additions and 85 deletions

View file

@ -41,10 +41,7 @@ public class ClientInfoItem extends LinearLayout implements SeekBar.OnSeekBarCha
} }
private void update() { private void update() {
if (!clientInfo.getName().isEmpty()) title.setText(clientInfo.getVisibleName());
title.setText(clientInfo.getName());
else
title.setText(clientInfo.getHost());
title.setEnabled(clientInfo.isConnected()); title.setEnabled(clientInfo.isConnected());
volumeSeekBar.setProgress(clientInfo.getVolume().getPercent()); volumeSeekBar.setProgress(clientInfo.getVolume().getPercent());
if (clientInfo.getVolume().isMuted()) if (clientInfo.getVolume().isMuted())

View file

@ -5,8 +5,6 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.ServiceConnection; import android.content.ServiceConnection;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.media.AudioManager; import android.media.AudioManager;
import android.net.Uri; import android.net.Uri;
import android.net.nsd.NsdManager; import android.net.nsd.NsdManager;
@ -14,6 +12,9 @@ import android.net.nsd.NsdServiceInfo;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.support.design.widget.Snackbar;
import android.support.design.widget.Snackbar.Callback;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
@ -26,7 +27,6 @@ import android.widget.CheckBox;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.appindexing.Action; import com.google.android.gms.appindexing.Action;
import com.google.android.gms.appindexing.AppIndex; import com.google.android.gms.appindexing.AppIndex;
@ -40,9 +40,6 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
private static final String TAG = "Main"; private static final String TAG = "Main";
boolean bound = false; boolean bound = false;
private TextView tvInfo;
private CheckBox cbScreenWakelock;
private ListView lvClient;
private MenuItem miStartStop = null; private MenuItem miStartStop = null;
private NsdManager.DiscoveryListener mDiscoveryListener; private NsdManager.DiscoveryListener mDiscoveryListener;
private NsdManager mNsdManager = null; private NsdManager mNsdManager = null;
@ -82,8 +79,8 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
tvInfo = (TextView) findViewById(R.id.tvInfo); TextView tvInfo = (TextView) findViewById(R.id.tvInfo);
cbScreenWakelock = (CheckBox) findViewById(R.id.cbScreenWakelock); CheckBox cbScreenWakelock = (CheckBox) findViewById(R.id.cbScreenWakelock);
cbScreenWakelock.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { cbScreenWakelock.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override @Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) { public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
@ -94,8 +91,6 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
} }
}); });
lvClient = (ListView) findViewById(R.id.lvClient);
AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE); AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
String rate = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); String rate = audioManager.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
@ -104,6 +99,7 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
} }
clientInfoAdapter = new ClientInfoAdapter(this, this); clientInfoAdapter = new ClientInfoAdapter(this, this);
ListView lvClient = (ListView) findViewById(R.id.lvClient);
lvClient.setAdapter(clientInfoAdapter); lvClient.setAdapter(clientInfoAdapter);
getSupportActionBar().setSubtitle("Host: no Snapserver found"); getSupportActionBar().setSubtitle("Host: no Snapserver found");
@ -175,6 +171,7 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
i.putExtra(SnapclientService.EXTRA_HOST, host); i.putExtra(SnapclientService.EXTRA_HOST, host);
i.putExtra(SnapclientService.EXTRA_PORT, port); i.putExtra(SnapclientService.EXTRA_PORT, port);
i.setAction(SnapclientService.ACTION_START);
startService(i); startService(i);
} }
@ -209,7 +206,9 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
MainActivity.this.runOnUiThread(new Runnable() { MainActivity.this.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
getSupportActionBar().setSubtitle(text); ActionBar actionBar = getSupportActionBar();
if (actionBar != null)
actionBar.setSubtitle(text);
} }
}); });
} }
@ -361,9 +360,31 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
} }
@Override @Override
public void onDeleteClicked(ClientInfoItem clientInfoItem) { public void onDeleteClicked(final ClientInfoItem clientInfoItem) {
remoteControl.delete(clientInfoItem.getClientInfo()); final ClientInfo clientInfo = clientInfoItem.getClientInfo();
clientInfoAdapter.removeClient(clientInfoItem.getClientInfo()); clientInfo.setDeleted(true);
clientInfoAdapter.update();
Snackbar mySnackbar = Snackbar.make(findViewById(R.id.myCoordinatorLayout),
getString(R.string.client_deleted, clientInfo.getVisibleName()),
Snackbar.LENGTH_SHORT);
mySnackbar.setAction(R.string.undo_string, new View.OnClickListener() {
@Override
public void onClick(View v) {
clientInfo.setDeleted(false);
clientInfoAdapter.update();
}
});
mySnackbar.setCallback(new Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
super.onDismissed(snackbar, event);
if (event != DISMISS_EVENT_ACTION) {
remoteControl.delete(clientInfo);
clientInfoAdapter.removeClient(clientInfo);
}
}
});
mySnackbar.show();
} }
@Override @Override
@ -409,7 +430,7 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
@Override @Override
public void onServerInfo(RemoteControl remoteControl, ServerInfo serverInfo) { public void onServerInfo(RemoteControl remoteControl, ServerInfo serverInfo) {
clientInfoAdapter.update(serverInfo); clientInfoAdapter.updateServer(serverInfo);
} }
private class ClientInfoAdapter extends ArrayAdapter<ClientInfo> { private class ClientInfoAdapter extends ArrayAdapter<ClientInfo> {
@ -440,29 +461,36 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
return clientInfoItem; return clientInfoItem;
} }
public void updateClient(final ClientInfo clientInfo) {
if (serverInfo.addClient(clientInfo))
update(null);
}
public void removeClient(final ClientInfo clientInfo) { public void removeClient(final ClientInfo clientInfo) {
Log.d(TAG, "removeClient: " + clientInfo.getMac()); if ((clientInfo != null) && serverInfo.removeClient(clientInfo)) {
if (serverInfo.removeClient(clientInfo)) {
Log.d(TAG, "removeClient 1: " + clientInfo.getMac()); Log.d(TAG, "removeClient 1: " + clientInfo.getMac());
update(null); update();
} }
} }
public void update(final ServerInfo serverInfo) { public void updateClient(final ClientInfo clientInfo) {
if (serverInfo != null) Log.d(TAG, "updateClient: " + clientInfo.getHost() + " " + clientInfo.isDeleted());
if (serverInfo.updateClient(clientInfo)) {
update();
}
}
public void updateServer(final ServerInfo serverInfo) {
if (serverInfo != null) {
ClientInfoAdapter.this.serverInfo = serverInfo; ClientInfoAdapter.this.serverInfo = serverInfo;
update();
}
}
public void update() {
MainActivity.this.runOnUiThread(new Runnable() { MainActivity.this.runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
clear(); clear();
for (ClientInfo clientInfo : ClientInfoAdapter.this.serverInfo.getClientInfos()) { for (ClientInfo clientInfo : ClientInfoAdapter.this.serverInfo.getClientInfos()) {
if ((clientInfo != null) && (!hideOffline || clientInfo.isConnected())) if ((clientInfo != null) && (!hideOffline || clientInfo.isConnected()) && !clientInfo.isDeleted())
add(clientInfo); add(clientInfo);
} }
notifyDataSetChanged(); notifyDataSetChanged();
@ -478,7 +506,7 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
if (this.hideOffline == hideOffline) if (this.hideOffline == hideOffline)
return; return;
this.hideOffline = hideOffline; this.hideOffline = hideOffline;
update(null); update();
} }
} }
} }

View file

@ -15,7 +15,6 @@ import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.PowerManager; import android.os.PowerManager;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.widget.Toast;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
@ -32,6 +31,8 @@ public class SnapclientService extends Service {
public static final String EXTRA_HOST = "EXTRA_HOST"; public static final String EXTRA_HOST = "EXTRA_HOST";
public static final String EXTRA_PORT = "EXTRA_PORT"; public static final String EXTRA_PORT = "EXTRA_PORT";
public static final String ACTION_START = "ACTION_START";
public static final String ACTION_STOP = "ACTION_STOP";
private final IBinder mBinder = new LocalBinder(); private final IBinder mBinder = new LocalBinder();
private java.lang.Process process = null; private java.lang.Process process = null;
private PowerManager.WakeLock wakeLock = null; private PowerManager.WakeLock wakeLock = null;
@ -50,14 +51,15 @@ public class SnapclientService extends Service {
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction() == "ACTION_STOP") { if (intent == null)
return START_NOT_STICKY;
if (intent.getAction() == ACTION_STOP) {
stopService(); stopService();
return START_NOT_STICKY; return START_NOT_STICKY;
} } else if (intent.getAction() == ACTION_START) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
Intent stopIntent = new Intent(this, SnapclientService.class); Intent stopIntent = new Intent(this, SnapclientService.class);
stopIntent.setAction("ACTION_STOP"); stopIntent.setAction(ACTION_STOP);
PendingIntent piStop = PendingIntent.getService(this, 0, stopIntent, 0); PendingIntent piStop = PendingIntent.getService(this, 0, stopIntent, 0);
NotificationCompat.Builder builder = NotificationCompat.Builder builder =
@ -94,14 +96,13 @@ public class SnapclientService extends Service {
// mNotificationManager.notify(123, notification); // mNotificationManager.notify(123, notification);
startForeground(123, notification); startForeground(123, notification);
String host = intent.getStringExtra(EXTRA_HOST); String host = intent.getStringExtra(EXTRA_HOST);
int port = intent.getIntExtra(EXTRA_PORT, 1704); int port = intent.getIntExtra(EXTRA_PORT, 1704);
start(host, port); start(host, port);
// If we get killed, after returning from here, restart
return START_STICKY; return START_STICKY;
} }
return START_NOT_STICKY;
}
@Override @Override
public void onDestroy() { public void onDestroy() {

View file

@ -29,6 +29,7 @@ public class ClientInfo implements JsonSerialisable {
private Time_t lastSeen; private Time_t lastSeen;
private boolean connected; private boolean connected;
private int latency = 0; private int latency = 0;
private boolean deleted = false;
public ClientInfo(JSONObject json) { public ClientInfo(JSONObject json) {
fromJson(json); fromJson(json);
@ -114,6 +115,12 @@ public class ClientInfo implements JsonSerialisable {
return name; return name;
} }
public String getVisibleName() {
if ((name != null) && !name.isEmpty())
return name;
return getHost();
}
public void setName(String name) { public void setName(String name) {
this.name = name; this.name = name;
} }
@ -134,6 +141,14 @@ public class ClientInfo implements JsonSerialisable {
this.connected = connected; this.connected = connected;
} }
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
@Override @Override
public String toString() { public String toString() {
return "ClientInfo{" + return "ClientInfo{" +
@ -157,6 +172,7 @@ public class ClientInfo implements JsonSerialisable {
ClientInfo that = (ClientInfo) o; ClientInfo that = (ClientInfo) o;
if (connected != that.connected) return false; if (connected != that.connected) return false;
if (deleted != that.deleted) return false;
if (latency != that.latency) return false; if (latency != that.latency) return false;
if (mac != null ? !mac.equals(that.mac) : that.mac != null) return false; if (mac != null ? !mac.equals(that.mac) : that.mac != null) return false;
if (ip != null ? !ip.equals(that.ip) : that.ip != null) return false; if (ip != null ? !ip.equals(that.ip) : that.ip != null) return false;
@ -176,6 +192,7 @@ public class ClientInfo implements JsonSerialisable {
result = 31 * result + (name != null ? name.hashCode() : 0); result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + (volume != null ? volume.hashCode() : 0); result = 31 * result + (volume != null ? volume.hashCode() : 0);
result = 31 * result + (connected ? 1 : 0); result = 31 * result + (connected ? 1 : 0);
result = 31 * result + (deleted ? 1 : 0);
result = 31 * result + latency; result = 31 * result + latency;
return result; return result;
} }

View file

@ -76,7 +76,7 @@ public class RemoteControl implements TcpClient.TcpClientListener {
JSONArray clients = json.getJSONObject("result").getJSONArray("clients"); JSONArray clients = json.getJSONObject("result").getJSONArray("clients");
for (int i = 0; i < clients.length(); i++) { for (int i = 0; i < clients.length(); i++) {
final ClientInfo clientInfo = new ClientInfo(clients.getJSONObject(i)); final ClientInfo clientInfo = new ClientInfo(clients.getJSONObject(i));
serverInfo.addClient(clientInfo); serverInfo.updateClient(clientInfo);
} }
if (listener != null) if (listener != null)
listener.onServerInfo(this, serverInfo); listener.onServerInfo(this, serverInfo);

View file

@ -26,7 +26,7 @@ public class ServerInfo {
return false; return false;
} }
public boolean addClient(ClientInfo client) { public boolean updateClient(ClientInfo client) {
if (client == null) if (client == null)
return false; return false;

View file

@ -1,12 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/myCoordinatorLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="de.badaix.snapcast.MainActivity"> tools:context="de.badaix.snapcast.MainActivity">
<LinearLayout <LinearLayout
@ -14,7 +12,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_centerHorizontal="true"> android:layout_centerHorizontal="true"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -48,4 +50,5 @@
</LinearLayout> </LinearLayout>
</RelativeLayout> </android.support.design.widget.CoordinatorLayout>

View file

@ -26,5 +26,7 @@
<string name="client_latency">Latency</string> <string name="client_latency">Latency</string>
<string name="online">online</string> <string name="online">online</string>
<string name="action_play_stop">Play/Stop</string> <string name="action_play_stop">Play/Stop</string>
<string name="client_deleted">Client %1$s deleted</string>
<string name="undo_string">undo</string>
</resources> </resources>