mirror of
https://github.com/badaix/snapcast.git
synced 2025-08-03 16:48:52 +02:00
client list is a fragment
This commit is contained in:
parent
0c87becca0
commit
36360476ac
9 changed files with 507 additions and 208 deletions
|
@ -15,7 +15,8 @@
|
|||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleInstance">
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@style/AppTheme.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
|
|
@ -0,0 +1,206 @@
|
|||
package de.badaix.snapcast;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
|
||||
import de.badaix.snapcast.control.ClientInfo;
|
||||
import de.badaix.snapcast.control.ServerInfo;
|
||||
|
||||
|
||||
/**
|
||||
* A simple {@link Fragment} subclass.
|
||||
* Activities that contain this fragment must implement the
|
||||
* {@link ClientListFragment.OnFragmentInteractionListener} interface
|
||||
* to handle interaction events.
|
||||
* Use the {@link ClientListFragment#newInstance} factory method to
|
||||
* create an instance of this fragment.
|
||||
*/
|
||||
public class ClientListFragment extends Fragment {
|
||||
|
||||
private static final String TAG = "ClientList";
|
||||
|
||||
// TODO: Rename parameter arguments, choose names that match
|
||||
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
|
||||
private static final String ARG_PARAM1 = "param1";
|
||||
private static final String ARG_PARAM2 = "param2";
|
||||
|
||||
// TODO: Rename and change types of parameters
|
||||
private String mParam1;
|
||||
private String mParam2;
|
||||
|
||||
private OnFragmentInteractionListener mListener;
|
||||
private ClientInfoItem.ClientInfoItemListener clientInfoItemListener;
|
||||
private ClientInfoAdapter clientInfoAdapter;
|
||||
private ServerInfo serverInfo = null;
|
||||
private boolean hideOffline = false;
|
||||
|
||||
public ClientListFragment() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this factory method to create a new instance of
|
||||
* this fragment using the provided parameters.
|
||||
*
|
||||
* @param param1 Parameter 1.
|
||||
* @param param2 Parameter 2.
|
||||
* @return A new instance of fragment ClientListFragment.
|
||||
*/
|
||||
// TODO: Rename and change types and number of parameters
|
||||
public static ClientListFragment newInstance(String param1, String param2) {
|
||||
ClientListFragment fragment = new ClientListFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_PARAM1, param1);
|
||||
args.putString(ARG_PARAM2, param2);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (getArguments() != null) {
|
||||
mParam1 = getArguments().getString(ARG_PARAM1);
|
||||
mParam2 = getArguments().getString(ARG_PARAM2);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
// Inflate the layout for this fragment
|
||||
View view = inflater.inflate(R.layout.fragment_client_list, container, false);
|
||||
ListView lvClient = (ListView) view.findViewById(R.id.lvClient);
|
||||
clientInfoAdapter = new ClientInfoAdapter(getContext(), clientInfoItemListener);
|
||||
clientInfoAdapter.setHideOffline(hideOffline);
|
||||
clientInfoAdapter.updateServer(serverInfo);
|
||||
lvClient.setAdapter(clientInfoAdapter);
|
||||
return view;
|
||||
}
|
||||
|
||||
// TODO: Rename method, update argument and hook method into UI event
|
||||
public void onButtonPressed(Uri uri) {
|
||||
if (mListener != null) {
|
||||
mListener.onFragmentInteraction(uri);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnFragmentInteractionListener) {
|
||||
mListener = (OnFragmentInteractionListener) context;
|
||||
} else {
|
||||
throw new RuntimeException(context.toString()
|
||||
+ " must implement OnFragmentInteractionListener");
|
||||
}
|
||||
clientInfoItemListener = (ClientInfoItem.ClientInfoItemListener) context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
public void updateServer(ServerInfo serverInfo) {
|
||||
this.serverInfo = serverInfo;
|
||||
if (clientInfoAdapter != null)
|
||||
clientInfoAdapter.updateServer(this.serverInfo);
|
||||
}
|
||||
|
||||
public void setHideOffline(boolean hide) {
|
||||
this.hideOffline = hide;
|
||||
if (clientInfoAdapter != null)
|
||||
clientInfoAdapter.setHideOffline(hideOffline);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This interface must be implemented by activities that contain this
|
||||
* fragment to allow an interaction in this fragment to be communicated
|
||||
* to the activity and potentially other fragments contained in that
|
||||
* activity.
|
||||
* <p/>
|
||||
* See the Android Training lesson <a href=
|
||||
* "http://developer.android.com/training/basics/fragments/communicating.html"
|
||||
* >Communicating with Other Fragments</a> for more information.
|
||||
*/
|
||||
public interface OnFragmentInteractionListener {
|
||||
// TODO: Update argument type and name
|
||||
void onFragmentInteraction(Uri uri);
|
||||
}
|
||||
|
||||
|
||||
public class ClientInfoAdapter extends ArrayAdapter<ClientInfo> {
|
||||
private Context context;
|
||||
private ClientInfoItem.ClientInfoItemListener listener;
|
||||
private boolean hideOffline = false;
|
||||
private ServerInfo serverInfo = new ServerInfo();
|
||||
|
||||
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);
|
||||
final ClientInfoItem clientInfoItem;
|
||||
|
||||
if (convertView != null) {
|
||||
clientInfoItem = (ClientInfoItem) convertView;
|
||||
clientInfoItem.setClientInfo(clientInfo);
|
||||
} else {
|
||||
clientInfoItem = new ClientInfoItem(context, clientInfo);
|
||||
}
|
||||
clientInfoItem.setListener(listener);
|
||||
return clientInfoItem;
|
||||
}
|
||||
|
||||
public void updateServer(final ServerInfo serverInfo) {
|
||||
if (serverInfo != null) {
|
||||
ClientInfoAdapter.this.serverInfo = serverInfo;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void update() {
|
||||
|
||||
ClientListFragment.this.getActivity().runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clear();
|
||||
for (ClientInfo clientInfo : ClientInfoAdapter.this.serverInfo.getClientInfos()) {
|
||||
if ((clientInfo != null) && (!hideOffline || clientInfo.isConnected()) && !clientInfo.isDeleted())
|
||||
add(clientInfo);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isHideOffline() {
|
||||
return hideOffline;
|
||||
}
|
||||
|
||||
public void setHideOffline(boolean hideOffline) {
|
||||
if (this.hideOffline == hideOffline)
|
||||
return;
|
||||
this.hideOffline = hideOffline;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -10,22 +10,23 @@ import android.net.Uri;
|
|||
import android.net.nsd.NsdServiceInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.design.widget.Snackbar.Callback;
|
||||
import android.support.design.widget.TabLayout;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentPagerAdapter;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.gms.appindexing.Action;
|
||||
|
@ -38,18 +39,24 @@ import de.badaix.snapcast.control.ServerInfo;
|
|||
import de.badaix.snapcast.utils.NsdHelper;
|
||||
import de.badaix.snapcast.utils.Setup;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements ClientInfoItem.ClientInfoItemListener, RemoteControl.RemoteControlListener, SnapclientService.SnapclientListener, NsdHelper.NsdHelperListener {
|
||||
public class MainActivity extends AppCompatActivity implements ClientListFragment.OnFragmentInteractionListener, ClientInfoItem.ClientInfoItemListener, RemoteControl.RemoteControlListener, SnapclientService.SnapclientListener, NsdHelper.NsdHelperListener {
|
||||
|
||||
private static final String TAG = "Main";
|
||||
private static final String SERVICE_NAME = "Snapcast";
|
||||
|
||||
boolean bound = false;
|
||||
private MenuItem miStartStop = null;
|
||||
private String host = "";
|
||||
private int port = 1704;
|
||||
private RemoteControl remoteControl = null;
|
||||
private ClientInfoAdapter clientInfoAdapter;
|
||||
private ServerInfo serverInfo = null;
|
||||
private SnapclientService snapclientService;
|
||||
private Runnable enablePlayButtonRunnable;
|
||||
private Handler handler;
|
||||
private SectionsPagerAdapter sectionsPagerAdapter;
|
||||
|
||||
/**
|
||||
* The {@link ViewPager} that will host the section contents.
|
||||
*/
|
||||
private ViewPager mViewPager;
|
||||
|
||||
/**
|
||||
* ATTENTION: This was auto-generated to implement the App Indexing API.
|
||||
|
@ -102,9 +109,19 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
|
|||
tvInfo.setText("Sample rate: " + rate + ", buffer size: " + size);
|
||||
}
|
||||
|
||||
clientInfoAdapter = new ClientInfoAdapter(this, this);
|
||||
ListView lvClient = (ListView) findViewById(R.id.lvClient);
|
||||
lvClient.setAdapter(clientInfoAdapter);
|
||||
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
// Create the adapter that will return a fragment for each of the three
|
||||
// primary sections of the activity.
|
||||
sectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
|
||||
|
||||
// Set up the ViewPager with the sections adapter.
|
||||
mViewPager = (ViewPager) findViewById(R.id.container);
|
||||
mViewPager.setAdapter(sectionsPagerAdapter);
|
||||
|
||||
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
|
||||
tabLayout.setupWithViewPager(mViewPager);
|
||||
|
||||
getSupportActionBar().setSubtitle("Host: no Snapserver found");
|
||||
|
||||
new Thread(new Runnable() {
|
||||
|
@ -119,16 +136,6 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
|
|||
// ATTENTION: This was auto-generated to implement the App Indexing API.
|
||||
// See https://g.co/AppIndexing/AndroidStudio for more information.
|
||||
client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
|
||||
|
||||
handler = new Handler();
|
||||
enablePlayButtonRunnable =
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (miStartStop != null)
|
||||
miStartStop.setEnabled(true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -141,7 +148,7 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
|
|||
boolean isChecked = settings.getBoolean("hide_offline", false);
|
||||
MenuItem menuItem = menu.findItem(R.id.action_hide_offline);
|
||||
menuItem.setChecked(isChecked);
|
||||
clientInfoAdapter.setHideOffline(isChecked);
|
||||
sectionsPagerAdapter.setHideOffline(isChecked);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -155,15 +162,14 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
|
|||
//noinspection SimplifiableIfStatement
|
||||
if (id == R.id.action_scan) {
|
||||
// NsdHelper.getInstance(this).startListening("*._tcp.", "SnapCast", this);
|
||||
NsdHelper.getInstance(this).startListening("_snapcast._tcp.", "Snapcast", this);
|
||||
NsdHelper.getInstance(this).startListening("_snapcast._tcp.", SERVICE_NAME, this);
|
||||
return true;
|
||||
} else if (id == R.id.action_play_stop) {
|
||||
if (bound && snapclientService.isRunning()) {
|
||||
stopSnapclient();
|
||||
} else {
|
||||
startSnapclient();
|
||||
item.setEnabled(false);
|
||||
handler.postDelayed(enablePlayButtonRunnable, 5000);
|
||||
startSnapclient();
|
||||
}
|
||||
return true;
|
||||
} else if (id == R.id.action_hide_offline) {
|
||||
|
@ -172,11 +178,11 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
|
|||
SharedPreferences.Editor editor = settings.edit();
|
||||
editor.putBoolean("hide_offline", item.isChecked());
|
||||
editor.apply();
|
||||
clientInfoAdapter.setHideOffline(item.isChecked());
|
||||
sectionsPagerAdapter.setHideOffline(item.isChecked());
|
||||
return true;
|
||||
} else if (id == R.id.action_refresh) {
|
||||
if ((remoteControl != null) && remoteControl.isConnected())
|
||||
remoteControl.getServerStatus();
|
||||
startRemoteControl();
|
||||
remoteControl.getServerStatus();
|
||||
} else if (id == R.id.action_about) {
|
||||
Intent intent = new Intent(this, AboutActivity.class);
|
||||
startActivity(intent);
|
||||
|
@ -186,17 +192,22 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
|
|||
}
|
||||
|
||||
private void updateStartStopMenuItem() {
|
||||
if (bound && snapclientService.isRunning()) {
|
||||
Log.d(TAG, "updateStartStopMenuItem: ic_media_stop");
|
||||
miStartStop.setIcon(R.drawable.ic_media_stop);
|
||||
} else {
|
||||
Log.d(TAG, "updateStartStopMenuItem: ic_media_play");
|
||||
miStartStop.setIcon(R.drawable.ic_media_play);
|
||||
}
|
||||
if (miStartStop != null) {
|
||||
handler.removeCallbacks(enablePlayButtonRunnable);
|
||||
miStartStop.setEnabled(true);
|
||||
}
|
||||
MainActivity.this.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (bound && snapclientService.isRunning()) {
|
||||
Log.d(TAG, "updateStartStopMenuItem: ic_media_stop");
|
||||
miStartStop.setIcon(R.drawable.ic_media_stop);
|
||||
} else {
|
||||
Log.d(TAG, "updateStartStopMenuItem: ic_media_play");
|
||||
miStartStop.setIcon(R.drawable.ic_media_play);
|
||||
}
|
||||
if (miStartStop != null) {
|
||||
miStartStop.setEnabled(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void startSnapclient() {
|
||||
|
@ -241,7 +252,7 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
|
|||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
NsdHelper.getInstance(this).startListening("_snapcast._tcp.", "Snapcast", this);
|
||||
NsdHelper.getInstance(this).startListening("_snapcast._tcp.", SERVICE_NAME, this);
|
||||
|
||||
Intent intent = new Intent(this, SnapclientService.class);
|
||||
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
|
||||
|
@ -296,67 +307,27 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStart() {
|
||||
public void onPlayerStart(SnapclientService snapclientService) {
|
||||
Log.d(TAG, "onPlayerStart");
|
||||
updateStartStopMenuItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStop() {
|
||||
public void onPlayerStop(SnapclientService snapclientService) {
|
||||
Log.d(TAG, "onPlayerStop");
|
||||
updateStartStopMenuItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLog(String log) {
|
||||
public void onLog(SnapclientService snapclientService, String log) {
|
||||
Log.d(TAG, log);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onVolumeChanged(ClientInfoItem clientInfoItem, int percent) {
|
||||
remoteControl.setVolume(clientInfoItem.getClientInfo(), percent);
|
||||
public void onError(SnapclientService snapclientService, String msg, Exception exception) {
|
||||
updateStartStopMenuItem();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMute(ClientInfoItem clientInfoItem, boolean mute) {
|
||||
remoteControl.setMute(clientInfoItem.getClientInfo(), mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteClicked(final ClientInfoItem clientInfoItem) {
|
||||
final ClientInfo clientInfo = 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
|
||||
public void onPropertiesClicked(ClientInfoItem clientInfoItem) {
|
||||
Intent intent = new Intent(this, ClientSettingsActivity.class);
|
||||
intent.putExtra("clientInfo", clientInfoItem.getClientInfo());
|
||||
startActivityForResult(intent, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
|
@ -368,7 +339,7 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
|
|||
if (requestCode == 1) {
|
||||
ClientInfo clientInfo = data.getParcelableExtra("clientInfo");
|
||||
remoteControl.setName(clientInfo, clientInfo.getName());
|
||||
clientInfoAdapter.updateClient(clientInfo);
|
||||
//TODO clientInfoAdapter.updateClient(clientInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -386,15 +357,16 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
|
|||
public void onClientEvent(RemoteControl remoteControl, ClientInfo clientInfo, RemoteControl.ClientEvent event) {
|
||||
Log.d(TAG, "onClientEvent: " + event.toString());
|
||||
if (event == RemoteControl.ClientEvent.deleted)
|
||||
clientInfoAdapter.removeClient(clientInfo);
|
||||
serverInfo.removeClient(clientInfo);
|
||||
else
|
||||
clientInfoAdapter.updateClient(clientInfo);
|
||||
|
||||
serverInfo.updateClient(clientInfo);
|
||||
sectionsPagerAdapter.updateServer(serverInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServerInfo(RemoteControl remoteControl, ServerInfo serverInfo) {
|
||||
clientInfoAdapter.updateServer(serverInfo);
|
||||
this.serverInfo = serverInfo;
|
||||
sectionsPagerAdapter.updateServer(serverInfo);
|
||||
}
|
||||
|
||||
private void setActionbarSubtitle(final String subtitle) {
|
||||
|
@ -418,81 +390,112 @@ public class MainActivity extends AppCompatActivity implements ClientInfoItem.Cl
|
|||
NsdHelper.getInstance(this).stopListening();
|
||||
}
|
||||
|
||||
private class ClientInfoAdapter extends ArrayAdapter<ClientInfo> {
|
||||
private Context context;
|
||||
private ClientInfoItem.ClientInfoItemListener listener;
|
||||
private boolean hideOffline = false;
|
||||
private ServerInfo serverInfo = new ServerInfo();
|
||||
|
||||
public ClientInfoAdapter(Context context, ClientInfoItem.ClientInfoItemListener listener) {
|
||||
super(context, 0);
|
||||
this.context = context;
|
||||
this.listener = listener;
|
||||
}
|
||||
@Override
|
||||
public void onFragmentInteraction(Uri uri) {
|
||||
Log.d(TAG, "onFragmentInteraction: " + uri);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ClientInfo clientInfo = getItem(position);
|
||||
final ClientInfoItem clientInfoItem;
|
||||
@Override
|
||||
public void onVolumeChanged(ClientInfoItem clientInfoItem, int percent) {
|
||||
remoteControl.setVolume(clientInfoItem.getClientInfo(), percent);
|
||||
}
|
||||
|
||||
if (convertView != null) {
|
||||
clientInfoItem = (ClientInfoItem) convertView;
|
||||
clientInfoItem.setClientInfo(clientInfo);
|
||||
} else {
|
||||
clientInfoItem = new ClientInfoItem(context, clientInfo);
|
||||
@Override
|
||||
public void onMute(ClientInfoItem clientInfoItem, boolean mute) {
|
||||
remoteControl.setMute(clientInfoItem.getClientInfo(), mute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteClicked(final ClientInfoItem clientInfoItem) {
|
||||
final ClientInfo clientInfo = clientInfoItem.getClientInfo();
|
||||
clientInfo.setDeleted(true);
|
||||
serverInfo.updateClient(clientInfo);
|
||||
// sectionsPagerAdapter.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);
|
||||
serverInfo.updateClient(clientInfo);
|
||||
// sectionsPagerAdapter.update();
|
||||
}
|
||||
clientInfoItem.setListener(listener);
|
||||
return clientInfoItem;
|
||||
}
|
||||
|
||||
public void removeClient(final ClientInfo clientInfo) {
|
||||
if ((clientInfo != null) && serverInfo.removeClient(clientInfo)) {
|
||||
Log.d(TAG, "removeClient 1: " + clientInfo.getMac());
|
||||
update();
|
||||
});
|
||||
mySnackbar.setCallback(new Snackbar.Callback() {
|
||||
@Override
|
||||
public void onDismissed(Snackbar snackbar, int event) {
|
||||
super.onDismissed(snackbar, event);
|
||||
if (event != DISMISS_EVENT_ACTION) {
|
||||
remoteControl.delete(clientInfo);
|
||||
serverInfo.removeClient(clientInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
mySnackbar.show();
|
||||
}
|
||||
|
||||
public void updateClient(final ClientInfo clientInfo) {
|
||||
Log.d(TAG, "updateClient: " + clientInfo.getHost() + " " + clientInfo.isDeleted());
|
||||
if (serverInfo.updateClient(clientInfo)) {
|
||||
update();
|
||||
}
|
||||
@Override
|
||||
public void onPropertiesClicked(ClientInfoItem clientInfoItem) {
|
||||
Intent intent = new Intent(this, ClientSettingsActivity.class);
|
||||
intent.putExtra("clientInfo", clientInfoItem.getClientInfo());
|
||||
startActivityForResult(intent, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
|
||||
* one of the sections/tabs/pages.
|
||||
*/
|
||||
public class SectionsPagerAdapter extends FragmentPagerAdapter {
|
||||
|
||||
private ClientListFragment clientListFragment;
|
||||
|
||||
public SectionsPagerAdapter(FragmentManager fm) {
|
||||
super(fm);
|
||||
clientListFragment = ClientListFragment.newInstance("TODO1", "TODO2");
|
||||
}
|
||||
|
||||
public void updateServer(final ServerInfo serverInfo) {
|
||||
if (serverInfo != null) {
|
||||
ClientInfoAdapter.this.serverInfo = serverInfo;
|
||||
update();
|
||||
clientListFragment.updateServer(serverInfo);
|
||||
}
|
||||
|
||||
public void setHideOffline(boolean hide) {
|
||||
clientListFragment.setHideOffline(hide);
|
||||
}
|
||||
|
||||
// public void update() {
|
||||
// clientListFragment.update();
|
||||
// }
|
||||
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
// getItem is called to instantiate the fragment for the given page.
|
||||
// Return a PlaceholderFragment (defined as a static inner class below).
|
||||
return clientListFragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
// Show 3 total pages.
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getPageTitle(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return "SECTION 1";
|
||||
case 1:
|
||||
return "SECTION 2";
|
||||
case 2:
|
||||
return "SECTION 3";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public void update() {
|
||||
|
||||
MainActivity.this.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
clear();
|
||||
for (ClientInfo clientInfo : ClientInfoAdapter.this.serverInfo.getClientInfos()) {
|
||||
if ((clientInfo != null) && (!hideOffline || clientInfo.isConnected()) && !clientInfo.isDeleted())
|
||||
add(clientInfo);
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isHideOffline() {
|
||||
return hideOffline;
|
||||
}
|
||||
|
||||
public void setHideOffline(boolean hideOffline) {
|
||||
if (this.hideOffline == hideOffline)
|
||||
return;
|
||||
this.hideOffline = hideOffline;
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,15 @@ import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
|
|||
|
||||
public class SnapclientService extends Service {
|
||||
|
||||
public interface SnapclientListener {
|
||||
void onPlayerStart(SnapclientService snapclientService);
|
||||
|
||||
void onPlayerStop(SnapclientService snapclientService);
|
||||
|
||||
void onLog(SnapclientService snapclientService, String log);
|
||||
void onError(SnapclientService snapclientService, String msg, Exception exception);
|
||||
}
|
||||
|
||||
public static final String EXTRA_HOST = "EXTRA_HOST";
|
||||
public static final String EXTRA_PORT = "EXTRA_PORT";
|
||||
public static final String ACTION_START = "ACTION_START";
|
||||
|
@ -40,6 +49,7 @@ public class SnapclientService extends Service {
|
|||
private Thread reader = null;
|
||||
private boolean running = false;
|
||||
private SnapclientListener listener = null;
|
||||
private boolean logReceived;
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
|
@ -156,44 +166,60 @@ public class SnapclientService extends Service {
|
|||
String line;
|
||||
try {
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
if (listener != null)
|
||||
listener.onLog(line);
|
||||
log(line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
logReceived = false;
|
||||
reader.start();
|
||||
running = true;
|
||||
if (listener != null)
|
||||
listener.onPlayerStart();
|
||||
} catch (IOException e) {
|
||||
|
||||
//TODO: wait for started message on stdout
|
||||
/* long now = System.currentTimeMillis();
|
||||
while (!logReceived) {
|
||||
if (System.currentTimeMillis() > now + 1000)
|
||||
throw new Exception("start timeout");
|
||||
Thread.sleep(100, 0);
|
||||
}
|
||||
*/
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
if (listener != null)
|
||||
listener.onError(this, e.getMessage(), e);
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
private void stop() {
|
||||
if (reader != null)
|
||||
reader.interrupt();
|
||||
if (process != null)
|
||||
process.destroy();
|
||||
if ((wakeLock != null) && wakeLock.isHeld())
|
||||
wakeLock.release();
|
||||
if ((wifiWakeLock != null) && wifiWakeLock.isHeld())
|
||||
wifiWakeLock.release();
|
||||
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DEFAULT);
|
||||
running = false;
|
||||
private void log(String msg) {
|
||||
if (!logReceived) {
|
||||
logReceived = true;
|
||||
running = true;
|
||||
if (listener != null)
|
||||
listener.onPlayerStart(this);
|
||||
}
|
||||
if (listener != null)
|
||||
listener.onPlayerStop();
|
||||
listener.onLog(SnapclientService.this, msg);
|
||||
}
|
||||
|
||||
public interface SnapclientListener {
|
||||
void onPlayerStart();
|
||||
|
||||
void onPlayerStop();
|
||||
|
||||
void onLog(String log);
|
||||
private void stop() {
|
||||
try {
|
||||
if (reader != null)
|
||||
reader.interrupt();
|
||||
if (process != null)
|
||||
process.destroy();
|
||||
if ((wakeLock != null) && wakeLock.isHeld())
|
||||
wakeLock.release();
|
||||
if ((wifiWakeLock != null) && wifiWakeLock.isHeld())
|
||||
wifiWakeLock.release();
|
||||
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_DEFAULT);
|
||||
running = false;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (listener != null)
|
||||
listener.onPlayerStop(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,11 +1,43 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/myCoordinatorLayout"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="de.badaix.snapcast.MainActivity">
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context="de.badaix.swipe.MainActivity">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
<!-- android:paddingTop="@dimen/appbar_padding_top" -->
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay">
|
||||
<!-- app:layout_scrollFlags="scroll|enterAlways" -->
|
||||
|
||||
</android.support.v7.widget.Toolbar>
|
||||
|
||||
<android.support.design.widget.TabLayout
|
||||
android:id="@+id/tabs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
|
@ -39,15 +71,6 @@
|
|||
android:visibility="gone"
|
||||
android:id="@+id/button" />
|
||||
|
||||
<ListView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:listSelector="@android:color/transparent"
|
||||
android:cacheColorHint="@android:color/transparent"
|
||||
android:divider="@null"
|
||||
android:dividerHeight="1dp"
|
||||
android:id="@+id/lvClient" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="de.badaix.snapcast.ClientListFragment">
|
||||
|
||||
|
||||
<ListView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:listSelector="@android:color/transparent"
|
||||
android:cacheColorHint="@android:color/transparent"
|
||||
android:divider="@null"
|
||||
android:dividerHeight="1dp"
|
||||
android:id="@+id/lvClient" />
|
||||
|
||||
</FrameLayout>
|
|
@ -1,5 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#3F51B5</color>
|
||||
<color name="colorPrimaryDark">#303F9F</color>
|
||||
<color name="colorAccent">#FF4081</color>
|
||||
<color name="second_grey">#ff999999</color>
|
||||
<color name="card_white">#ffffffff</color>
|
||||
<color name="card_shadow">#8c666666</color>
|
||||
|
|
|
@ -32,5 +32,8 @@
|
|||
<string name="title_activity_about">About</string>
|
||||
<string name="about_file">about.html</string>
|
||||
<string name="about">About</string>
|
||||
|
||||
<!-- TODO: Remove or change this placeholder text -->
|
||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||
</resources>
|
||||
|
||||
|
|
|
@ -1,26 +1,12 @@
|
|||
<resources>
|
||||
<color name="color_background">#EEEEEE</color>
|
||||
<color name="actionbar_background">#37474F</color>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<item name="android:windowBackground">@color/color_background</item>
|
||||
<item name="android:actionBarStyle">@style/MyActionBar</item>
|
||||
<!-- Support library compatibility -->
|
||||
<item name="actionBarStyle">@style/MyActionBar</item>
|
||||
<!-- <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> -->
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
<style name="MyActionBar" parent="@style/Widget.AppCompat.Light.ActionBar.Solid.Inverse">
|
||||
<item name="android:background">@color/actionbar_background</item>
|
||||
<!-- Support library compatibility -->
|
||||
<item name="background">@color/actionbar_background</item>
|
||||
</style>
|
||||
|
||||
<style name="PlayerButton" parent="android:Widget.Holo.ImageButton">
|
||||
<item name="android:background">?android:attr/actionBarItemBackground</item>
|
||||
<!-- <item name="android:minHeight">@dimen/playerbutton_height</item> -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
|
@ -28,8 +14,39 @@
|
|||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
|
||||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
|
||||
|
||||
<!-- Base application theme.
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<item name="android:windowBackground">@color/color_background</item>
|
||||
<item name="android:actionBarStyle">@style/MyActionBar</item>
|
||||
<!- Support library compatibility
|
||||
<item name="actionBarStyle">@style/MyActionBar</item>
|
||||
<!- <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!- Customize your theme here.
|
||||
</style>
|
||||
|
||||
<style name="MyActionBar" parent="@style/Widget.AppCompat.Light.ActionBar.Solid.Inverse">
|
||||
<item name="android:background">@color/actionbar_background</item>
|
||||
<!- Support library compatibility -
|
||||
<item name="background">@color/actionbar_background</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
<style name="PlayerButton" parent="android:Widget.Holo.ImageButton">
|
||||
<item name="android:background">?android:attr/actionBarItemBackground</item>
|
||||
<!- <item name="android:minHeight">@dimen/playerbutton_height</item> -
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
-->
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue