Groups: interims commit

This commit is contained in:
badaix 2016-12-04 21:35:44 +01:00
parent 22f768311d
commit 0af508f6ee
14 changed files with 555 additions and 328 deletions

View file

@ -20,8 +20,8 @@ package de.badaix.snapcast;
import android.content.Context; import android.content.Context;
import android.support.v7.widget.PopupMenu; import android.support.v7.widget.PopupMenu;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.ImageButton; import android.widget.ImageButton;
@ -36,19 +36,21 @@ import de.badaix.snapcast.control.json.Volume;
public class ClientItem extends LinearLayout implements SeekBar.OnSeekBarChangeListener, View.OnClickListener, PopupMenu.OnMenuItemClickListener { public class ClientItem extends LinearLayout implements SeekBar.OnSeekBarChangeListener, View.OnClickListener, PopupMenu.OnMenuItemClickListener {
private static final String TAG = "ClientItem";
private TextView title; private TextView title;
private SeekBar volumeSeekBar; private SeekBar volumeSeekBar;
private ImageButton ibMute; private ImageButton ibMute;
private ImageButton ibOverflow; private ImageButton ibOverflow;
private Client client; private Client client;
private ServerStatus server; private ServerStatus server;
private ClientInfoItemListener listener = null; private ClientItemListener listener = null;
public ClientItem(Context context, ServerStatus server, Client client) { public ClientItem(Context context, ServerStatus server, Client client) {
super(context); super(context);
LayoutInflater vi = (LayoutInflater) context LayoutInflater vi = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE); .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
vi.inflate(R.layout.client_info, this); vi.inflate(R.layout.client_item, this);
title = (TextView) findViewById(R.id.title); title = (TextView) findViewById(R.id.title);
volumeSeekBar = (SeekBar) findViewById(R.id.volumeSeekBar); volumeSeekBar = (SeekBar) findViewById(R.id.volumeSeekBar);
ibMute = (ImageButton) findViewById(R.id.ibMute); ibMute = (ImageButton) findViewById(R.id.ibMute);
@ -59,9 +61,11 @@ public class ClientItem extends LinearLayout implements SeekBar.OnSeekBarChangeL
setClient(client); setClient(client);
volumeSeekBar.setOnSeekBarChangeListener(this); volumeSeekBar.setOnSeekBarChangeListener(this);
this.server = server; this.server = server;
update();
} }
private void update() { private void update() {
Log.d(TAG, "update: " + client.getVisibleName() + ", connected: " + client.isConnected());
title.setText(client.getVisibleName()); title.setText(client.getVisibleName());
title.setEnabled(client.isConnected()); title.setEnabled(client.isConnected());
volumeSeekBar.setProgress(client.getConfig().getVolume().getPercent()); volumeSeekBar.setProgress(client.getConfig().getVolume().getPercent());
@ -80,7 +84,7 @@ public class ClientItem extends LinearLayout implements SeekBar.OnSeekBarChangeL
update(); update();
} }
public void setListener(ClientInfoItemListener listener) { public void setListener(ClientItemListener listener) {
this.listener = listener; this.listener = listener;
} }
@ -95,7 +99,7 @@ public class ClientItem extends LinearLayout implements SeekBar.OnSeekBarChangeL
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (v == ibMute) { /* TODO: group if (v == ibMute) {
Volume volume = client.getConfig().getVolume(); Volume volume = client.getConfig().getVolume();
volume.setMuted(!volume.isMuted()); volume.setMuted(!volume.isMuted());
update(); update();
@ -125,6 +129,7 @@ public class ClientItem extends LinearLayout implements SeekBar.OnSeekBarChangeL
popup.setOnMenuItemClickListener(this); popup.setOnMenuItemClickListener(this);
popup.show(); popup.show();
} }
*/
} }
@Override @Override
@ -151,7 +156,7 @@ public class ClientItem extends LinearLayout implements SeekBar.OnSeekBarChangeL
} }
} }
public interface ClientInfoItemListener { public interface ClientItemListener {
void onVolumeChanged(ClientItem clientItem, int percent); void onVolumeChanged(ClientItem clientItem, int percent);
void onMute(ClientItem clientItem, boolean mute); void onMute(ClientItem clientItem, boolean mute);
@ -159,8 +164,6 @@ public class ClientItem extends LinearLayout implements SeekBar.OnSeekBarChangeL
void onDeleteClicked(ClientItem clientItem); void onDeleteClicked(ClientItem clientItem);
void onPropertiesClicked(ClientItem clientItem); void onPropertiesClicked(ClientItem clientItem);
void onStreamClicked(ClientItem clientItem, Stream stream);
} }
} }

View file

@ -27,17 +27,15 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView;
import de.badaix.snapcast.control.json.Client; import de.badaix.snapcast.control.json.Group;
import de.badaix.snapcast.control.json.ServerStatus; import de.badaix.snapcast.control.json.ServerStatus;
import de.badaix.snapcast.control.json.Stream;
/** /**
* A simple {@link Fragment} subclass. * A simple {@link Fragment} subclass.
* Activities that contain this fragment must implement the * Activities that contain this fragment must implement the
* {@link ClientItem.ClientInfoItemListener} interface * {@link GroupItem.GroupItemListener} interface
* to handle interaction events. * to handle interaction events.
*/ */
public class ClientListFragment extends Fragment { public class ClientListFragment extends Fragment {
@ -49,14 +47,10 @@ public class ClientListFragment extends Fragment {
private static final String ARG_PARAM1 = "param1"; private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2"; private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters private GroupItem.GroupItemListener groupItemListener;
private Stream stream; private GroupAdapter groupAdapter;
private ClientItem.ClientInfoItemListener clientInfoItemListener;
private ClientInfoAdapter clientInfoAdapter;
private ServerStatus serverStatus = null; private ServerStatus serverStatus = null;
private boolean hideOffline = false; private boolean hideOffline = false;
private TextView tvStreamState = null;
public ClientListFragment() { public ClientListFragment() {
// Required empty public constructor // Required empty public constructor
@ -93,35 +87,27 @@ public class ClientListFragment extends Fragment {
// Inflate the layout for this fragment // Inflate the layout for this fragment
Log.d(TAG, "onCreateView: " + this.toString()); Log.d(TAG, "onCreateView: " + this.toString());
View view = inflater.inflate(R.layout.fragment_client_list, container, false); View view = inflater.inflate(R.layout.fragment_client_list, container, false);
tvStreamState = (TextView) view.findViewById(R.id.tvStreamState); ListView lvGroup = (ListView) view.findViewById(R.id.lvGroup);
ListView lvClient = (ListView) view.findViewById(R.id.lvClient); groupAdapter = new GroupAdapter(getContext(), groupItemListener);
clientInfoAdapter = new ClientInfoAdapter(getContext(), clientInfoItemListener); groupAdapter.setHideOffline(hideOffline);
clientInfoAdapter.setHideOffline(hideOffline); groupAdapter.updateServer(serverStatus);
clientInfoAdapter.updateServer(serverStatus); lvGroup.setAdapter(groupAdapter);
lvClient.setAdapter(clientInfoAdapter);
updateGui(); updateGui();
return view; return view;
} }
private void updateGui() { private void updateGui() {
Log.d(TAG, "(tvStreamState == null): " + (tvStreamState == null) + " " + this.toString());
if ((tvStreamState == null) || (stream == null))
return;
String codec = stream.getUri().getQuery().get("codec");
if (codec.contains(":"))
codec = codec.split(":")[0];
tvStreamState.setText(stream.getUri().getQuery().get("sampleformat") + " - " + codec + " - " + stream.getStatus().toString());
} }
@Override @Override
public void onAttach(Context context) { public void onAttach(Context context) {
super.onAttach(context); super.onAttach(context);
if (context instanceof ClientItem.ClientInfoItemListener) { if (context instanceof GroupItem.GroupItemListener) {
clientInfoItemListener = (ClientItem.ClientInfoItemListener) context; groupItemListener = (GroupItem.GroupItemListener) context;
} else { } else {
throw new RuntimeException(context.toString() throw new RuntimeException(context.toString()
+ " must implement ClientInfoItemListener"); + " must implement GroupItemListener");
} }
updateGui(); updateGui();
} }
@ -129,39 +115,28 @@ public class ClientListFragment extends Fragment {
@Override @Override
public void onDetach() { public void onDetach() {
super.onDetach(); super.onDetach();
clientInfoItemListener = null; groupItemListener = null;
} }
public void updateServer(ServerStatus serverStatus) { public void updateServer(ServerStatus serverStatus) {
this.serverStatus = serverStatus; this.serverStatus = serverStatus;
if (clientInfoAdapter != null) if (groupAdapter != null)
clientInfoAdapter.updateServer(this.serverStatus); groupAdapter.updateServer(this.serverStatus);
} }
public void setHideOffline(boolean hide) { public void setHideOffline(boolean hide) {
this.hideOffline = hide; this.hideOffline = hide;
if (clientInfoAdapter != null) if (groupAdapter != null)
clientInfoAdapter.setHideOffline(hideOffline); groupAdapter.setHideOffline(hideOffline);
} }
public String getName() { public class GroupAdapter extends ArrayAdapter<Group> {
return stream.getName();
}
public void setStream(Stream stream) {
Log.d(TAG, "setStream: " + stream.getName() + ", status: " + stream.getStatus());
this.stream = stream;
updateGui();
}
public class ClientInfoAdapter extends ArrayAdapter<Client> {
private Context context; private Context context;
private ClientItem.ClientInfoItemListener listener; private GroupItem.GroupItemListener listener;
private boolean hideOffline = false; private boolean hideOffline = false;
private ServerStatus serverStatus = new ServerStatus(); private ServerStatus serverStatus = new ServerStatus();
public ClientInfoAdapter(Context context, ClientItem.ClientInfoItemListener listener) { public GroupAdapter(Context context, GroupItem.GroupItemListener listener) {
super(context, 0); super(context, 0);
this.context = context; this.context = context;
this.listener = listener; this.listener = listener;
@ -169,22 +144,22 @@ public class ClientListFragment extends Fragment {
@Override @Override
public View getView(int position, View convertView, ViewGroup parent) { public View getView(int position, View convertView, ViewGroup parent) {
Client client = getItem(position); Group group = getItem(position);
final ClientItem clientItem; final GroupItem groupItem;
if (convertView != null) { if (convertView != null) {
clientItem = (ClientItem) convertView; groupItem = (GroupItem) convertView;
clientItem.setClient(client); groupItem.setGroup(group);
} else { } else {
clientItem = new ClientItem(context, serverStatus, client); groupItem = new GroupItem(context, serverStatus, group);
} }
clientItem.setListener(listener); groupItem.setListener(listener);
return clientItem; return groupItem;
} }
public void updateServer(final ServerStatus serverStatus) { public void updateServer(final ServerStatus serverStatus) {
if (serverStatus != null) { if (serverStatus != null) {
ClientInfoAdapter.this.serverStatus = serverStatus; GroupAdapter.this.serverStatus = serverStatus;
update(); update();
} }
} }
@ -192,10 +167,16 @@ public class ClientListFragment extends Fragment {
public void update() { public void update() {
clear(); clear();
for (Client client : ClientInfoAdapter.this.serverStatus.getClientInfos()) { // TODO: group
if ((client != null) && (!hideOffline || client.isConnected()) && !client.isDeleted() && client.getConfig().getStream().equals(ClientListFragment.this.stream.getId())) for (Group group : GroupAdapter.this.serverStatus.getGroups()) {
add(group);
/* for (Client client : group.getClients()) {
if ((client != null) && (!hideOffline || client.isConnected()) && !client.isDeleted())// && client.getConfig().getStream().equals(ClientListFragment.this.stream.getId()))
add(client); add(client);
} }
*/
}
if (getActivity() != null) { if (getActivity() != null) {
ClientListFragment.this.getActivity().runOnUiThread(new Runnable() { ClientListFragment.this.getActivity().runOnUiThread(new Runnable() {
@Override @Override

View file

@ -94,6 +94,7 @@ public class ClientSettingsFragment extends PreferenceFragment {
prefStream = (ListPreference) findPreference("pref_client_stream"); prefStream = (ListPreference) findPreference("pref_client_stream");
prefStream.setEntries(streamNames); prefStream.setEntries(streamNames);
prefStream.setEntryValues(streamIds); prefStream.setEntryValues(streamIds);
/* TODO: group
for (int i = 0; i < streams.size(); ++i) { for (int i = 0; i < streams.size(); ++i) {
if (streamIds[i].equals(client.getConfig().getStream())) { if (streamIds[i].equals(client.getConfig().getStream())) {
prefStream.setSummary(streamNames[i]); prefStream.setSummary(streamNames[i]);
@ -116,6 +117,7 @@ public class ClientSettingsFragment extends PreferenceFragment {
return false; return false;
} }
}); });
*/
prefMac = (Preference) findPreference("pref_client_mac"); prefMac = (Preference) findPreference("pref_client_mac");
prefId = (Preference) findPreference("pref_client_id"); prefId = (Preference) findPreference("pref_client_id");
prefIp = (Preference) findPreference("pref_client_ip"); prefIp = (Preference) findPreference("pref_client_ip");

View file

@ -0,0 +1,237 @@
/*
* This file is part of snapcast
* Copyright (C) 2014-2016 Johannes Pohl
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.badaix.snapcast;
import android.content.Context;
import android.support.v7.widget.PopupMenu;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import de.badaix.snapcast.control.json.Client;
import de.badaix.snapcast.control.json.Group;
import de.badaix.snapcast.control.json.ServerStatus;
import de.badaix.snapcast.control.json.Stream;
/**
* Created by johannes on 04.12.16.
*/
public class GroupItem extends LinearLayout implements SeekBar.OnSeekBarChangeListener, View.OnClickListener, PopupMenu.OnMenuItemClickListener, ClientItem.ClientItemListener {
private static final String TAG = "GroupItem";
// private TextView title;
private SeekBar volumeSeekBar;
private ImageButton ibMute;
private ImageButton ibOverflow;
private LinearLayout llClient;
private Group group;
private ServerStatus server;
private TextView tvStreamState = null;
private GroupItemListener listener = null;
private Stream stream = null;
private LinearLayout llVolume;
public GroupItem(Context context, ServerStatus server, Group group) {
super(context);
LayoutInflater vi = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
vi.inflate(R.layout.group_item, this);
// title = (TextView) findViewById(R.id.title);
volumeSeekBar = (SeekBar) findViewById(R.id.volumeSeekBar);
ibMute = (ImageButton) findViewById(R.id.ibMute);
ibMute.setImageResource(R.drawable.ic_speaker_icon);
ibMute.setOnClickListener(this);
ibOverflow = (ImageButton) findViewById(R.id.ibOverflow);
ibOverflow.setOnClickListener(this);
llVolume = (LinearLayout) findViewById(R.id.llVolume);
llVolume.setVisibility(GONE);
llClient = (LinearLayout) findViewById(R.id.llClient);
llClient.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
tvStreamState = (TextView) findViewById(R.id.tvStreamState);
setGroup(group);
volumeSeekBar.setOnSeekBarChangeListener(this);
this.server = server;
stream = server.getStream(group.getStreamId());
update();
}
private void update() {
// title.setText(group.getName());
llClient.removeAllViews();
for (Client client : group.getClients()) {
ClientItem clientItem = new ClientItem(this.getContext(), server, client);
clientItem.setListener(this);
llClient.addView(clientItem);
}
Log.d(TAG, "(tvStreamState == null): " + (tvStreamState == null) + " " + this.toString());
if ((tvStreamState == null) || (stream == null))
return;
tvStreamState.setText(stream.getName());
/* String codec = stream.getUri().getQuery().get("codec");
if (codec.contains(":"))
codec = codec.split(":")[0];
tvStreamState.setText(stream.getUri().getQuery().get("sampleformat") + " - " + codec + " - " + stream.getStatus().toString());
*/
/* title.setEnabled(group.isConnected());
volumeSeekBar.setProgress(group.getConfig().getVolume().getPercent());
if (client.getConfig().getVolume().isMuted())
ibMute.setImageResource(R.drawable.ic_mute_icon);
else
ibMute.setImageResource(R.drawable.ic_speaker_icon);
*/
}
public Group getGroup() {
return group;
}
public void setGroup(final Group group) {
this.group = group;
update();
}
public void setStream(Stream stream) {
Log.d(TAG, "setStream: " + stream.getName() + ", status: " + stream.getStatus());
this.stream = stream;
update();
}
public void setListener(GroupItemListener listener) {
this.listener = listener;
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
/* if (fromUser && (listener != null)) {
Volume volume = new Volume(progress, false);
client.setVolume(volume);
listener.onVolumeChanged(this, volume.getPercent());
}
*/
}
@Override
public void onClick(View v) {
/* TODO: group if (v == ibMute) {
Volume volume = client.getConfig().getVolume();
volume.setMuted(!volume.isMuted());
update();
listener.onMute(this, volume.isMuted());
} else if (v == ibOverflow) {
PopupMenu popup = new PopupMenu(v.getContext(), v);
popup.getMenu().add(Menu.NONE, R.id.menu_details, 0, R.string.menu_details);
if (!client.isConnected())
popup.getMenu().add(Menu.NONE, R.id.menu_delete, 1, R.string.menu_delete);
if ((server != null) && (server.getStreams().size() > 1)) {
int pos = 2;
for (final Stream stream : server.getStreams()) {
if (client.getConfig().getStream().equals(stream.getId()))
continue;
final MenuItem menuItem = popup.getMenu().add(Menu.NONE, Menu.NONE, pos, stream.getName());
menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
listener.onStreamClicked(ClientItem.this, stream);
return true;
}
});
++pos;
}
}
popup.setOnMenuItemClickListener(this);
popup.show();
}
*/
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
@Override
public boolean onMenuItemClick(MenuItem item) {
/* switch (item.getItemId()) {
case R.id.menu_details:
listener.onPropertiesClicked(this);
return true;
case R.id.menu_delete:
listener.onDeleteClicked(this);
return true;
default:
return false;
}
*/
return false;
}
@Override
public void onVolumeChanged(ClientItem clientItem, int percent) {
if (listener != null)
listener.onVolumeChanged(this, clientItem, percent);
}
@Override
public void onMute(ClientItem clientItem, boolean mute) {
if (listener != null)
listener.onMute(this, clientItem, mute);
}
@Override
public void onDeleteClicked(ClientItem clientItem) {
if (listener != null)
listener.onDeleteClicked(this, clientItem);
}
@Override
public void onPropertiesClicked(ClientItem clientItem) {
if (listener != null)
listener.onPropertiesClicked(this, clientItem);
}
public interface GroupItemListener {
void onVolumeChanged(GroupItem group, ClientItem clientItem, int percent);
void onMute(GroupItem group, ClientItem clientItem, boolean mute);
void onDeleteClicked(GroupItem group, ClientItem clientItem);
void onPropertiesClicked(GroupItem group, ClientItem clientItem);
void onStreamClicked(GroupItem group, Stream stream);
}
}

View file

@ -35,11 +35,6 @@ import android.os.IBinder;
import android.support.design.widget.CoordinatorLayout; import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.design.widget.TabLayout; 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.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
@ -48,24 +43,20 @@ import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager; import android.view.WindowManager;
import org.json.JSONException;
import org.json.JSONObject;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Vector;
import de.badaix.snapcast.control.RemoteControl; import de.badaix.snapcast.control.RemoteControl;
import de.badaix.snapcast.control.json.Client; import de.badaix.snapcast.control.json.Client;
import de.badaix.snapcast.control.json.Group;
import de.badaix.snapcast.control.json.ServerStatus; import de.badaix.snapcast.control.json.ServerStatus;
import de.badaix.snapcast.control.json.Stream; import de.badaix.snapcast.control.json.Stream;
import de.badaix.snapcast.utils.NsdHelper; import de.badaix.snapcast.utils.NsdHelper;
import de.badaix.snapcast.utils.Settings; import de.badaix.snapcast.utils.Settings;
import de.badaix.snapcast.utils.Setup; import de.badaix.snapcast.utils.Setup;
public class MainActivity extends AppCompatActivity implements ClientItem.ClientInfoItemListener, RemoteControl.RemoteControlListener, SnapclientService.SnapclientListener, NsdHelper.NsdHelperListener { public class MainActivity extends AppCompatActivity implements GroupItem.GroupItemListener, RemoteControl.RemoteControlListener, SnapclientService.SnapclientListener, NsdHelper.NsdHelperListener {
static final int CLIENT_PROPERTIES_REQUEST = 1; static final int CLIENT_PROPERTIES_REQUEST = 1;
private static final String TAG = "Main"; private static final String TAG = "Main";
@ -80,17 +71,12 @@ public class MainActivity extends AppCompatActivity implements ClientItem.Client
private RemoteControl remoteControl = null; private RemoteControl remoteControl = null;
private ServerStatus serverStatus = null; private ServerStatus serverStatus = null;
private SnapclientService snapclientService; private SnapclientService snapclientService;
private SectionsPagerAdapter sectionsPagerAdapter; private ClientListFragment clientListFragment;
private TabLayout tabLayout; private TabLayout tabLayout;
private Snackbar warningSamplerateSnackbar = null; private Snackbar warningSamplerateSnackbar = null;
private int nativeSampleRate = 0; private int nativeSampleRate = 0;
private CoordinatorLayout coordinatorLayout; private CoordinatorLayout coordinatorLayout;
/**
* The {@link ViewPager} that will host the section contents.
*/
private ViewPager mViewPager;
/** /**
* Defines callbacks for service binding, passed to bindService() * Defines callbacks for service binding, passed to bindService()
@ -139,15 +125,8 @@ public class MainActivity extends AppCompatActivity implements ClientItem.Client
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
// Create the adapter that will return a fragment for each of the three // Create the adapter that will return a fragment for each of the three
// primary sections of the activity. // primary sections of the activity.
sectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter. clientListFragment = (ClientListFragment) getSupportFragmentManager().findFragmentById(R.id.clientListFragment);
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(sectionsPagerAdapter);
tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
mViewPager.setVisibility(View.GONE);
setActionbarSubtitle("Host: no Snapserver found"); setActionbarSubtitle("Host: no Snapserver found");
@ -159,8 +138,6 @@ public class MainActivity extends AppCompatActivity implements ClientItem.Client
Log.d(TAG, "done copying snapclient"); Log.d(TAG, "done copying snapclient");
} }
}).start(); }).start();
sectionsPagerAdapter.setHideOffline(Settings.getInstance(this).getBoolean("hide_offline", false));
} }
public void checkFirstRun() { public void checkFirstRun() {
@ -195,7 +172,6 @@ public class MainActivity extends AppCompatActivity implements ClientItem.Client
boolean isChecked = Settings.getInstance(this).getBoolean("hide_offline", false); boolean isChecked = Settings.getInstance(this).getBoolean("hide_offline", false);
MenuItem menuItem = menu.findItem(R.id.action_hide_offline); MenuItem menuItem = menu.findItem(R.id.action_hide_offline);
menuItem.setChecked(isChecked); menuItem.setChecked(isChecked);
sectionsPagerAdapter.setHideOffline(isChecked);
// setHost(host, port, controlPort); // setHost(host, port, controlPort);
if (remoteControl != null) { if (remoteControl != null) {
updateMenuItems(remoteControl.isConnected()); updateMenuItems(remoteControl.isConnected());
@ -241,7 +217,7 @@ public class MainActivity extends AppCompatActivity implements ClientItem.Client
} else if (id == R.id.action_hide_offline) { } else if (id == R.id.action_hide_offline) {
item.setChecked(!item.isChecked()); item.setChecked(!item.isChecked());
Settings.getInstance(this).put("hide_offline", item.isChecked()); Settings.getInstance(this).put("hide_offline", item.isChecked());
sectionsPagerAdapter.setHideOffline(item.isChecked()); //TODO: group sectionsPagerAdapter.setHideOffline(item.isChecked());
return true; return true;
} else if (id == R.id.action_refresh) { } else if (id == R.id.action_refresh) {
startRemoteControl(); startRemoteControl();
@ -407,7 +383,7 @@ public class MainActivity extends AppCompatActivity implements ClientItem.Client
return; return;
} }
if (requestCode == CLIENT_PROPERTIES_REQUEST) { if (requestCode == CLIENT_PROPERTIES_REQUEST) {
Client client = null; /* TODO: group Client client = null;
try { try {
client = new Client(new JSONObject(data.getStringExtra("client"))); client = new Client(new JSONObject(data.getStringExtra("client")));
} catch (JSONException e) { } catch (JSONException e) {
@ -432,17 +408,12 @@ public class MainActivity extends AppCompatActivity implements ClientItem.Client
remoteControl.setLatency(client, client.getConfig().getLatency()); remoteControl.setLatency(client, client.getConfig().getLatency());
serverStatus.updateClient(client); serverStatus.updateClient(client);
sectionsPagerAdapter.updateServer(serverStatus); sectionsPagerAdapter.updateServer(serverStatus);
*/
} }
} }
@Override @Override
public void onConnected(RemoteControl remoteControl) { public void onConnected(RemoteControl remoteControl) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mViewPager.setVisibility(View.VISIBLE);
}
});
setActionbarSubtitle(remoteControl.getHost()); setActionbarSubtitle(remoteControl.getHost());
remoteControl.getServerStatus(); remoteControl.getServerStatus();
updateMenuItems(true); updateMenuItems(true);
@ -457,7 +428,7 @@ public class MainActivity extends AppCompatActivity implements ClientItem.Client
public void onDisconnected(RemoteControl remoteControl, Exception e) { public void onDisconnected(RemoteControl remoteControl, Exception e) {
Log.d(TAG, "onDisconnected"); Log.d(TAG, "onDisconnected");
serverStatus = new ServerStatus(); serverStatus = new ServerStatus();
sectionsPagerAdapter.updateServer(serverStatus); //TODO: group sectionsPagerAdapter.updateServer(serverStatus);
if (e != null) { if (e != null) {
if (e instanceof UnknownHostException) if (e instanceof UnknownHostException)
setActionbarSubtitle("error: unknown host"); setActionbarSubtitle("error: unknown host");
@ -466,38 +437,38 @@ public class MainActivity extends AppCompatActivity implements ClientItem.Client
} else { } else {
setActionbarSubtitle("not connected"); setActionbarSubtitle("not connected");
} }
runOnUiThread(new Runnable() {
@Override
public void run() {
mViewPager.setVisibility(View.GONE);
}
});
updateMenuItems(false); updateMenuItems(false);
} }
@Override @Override
public void onClientEvent(RemoteControl remoteControl, Client client, RemoteControl.ClientEvent event) { public void onClientEvent(RemoteControl remoteControl, Client client, RemoteControl.ClientEvent event) {
Log.d(TAG, "onClientEvent: " + event.toString()); Log.d(TAG, "onClientEvent: " + event.toString());
remoteControl.getServerStatus();
/* TODO: group
if (event == RemoteControl.ClientEvent.deleted) if (event == RemoteControl.ClientEvent.deleted)
serverStatus.removeClient(client); serverStatus.removeClient(client);
else else
serverStatus.updateClient(client); serverStatus.updateClient(client);
sectionsPagerAdapter.updateServer(serverStatus); sectionsPagerAdapter.updateServer(serverStatus);
*/
} }
@Override @Override
public void onServerStatus(RemoteControl remoteControl, ServerStatus serverStatus) { public void onServerStatus(RemoteControl remoteControl, ServerStatus serverStatus) {
this.serverStatus = serverStatus; this.serverStatus = serverStatus;
for (Stream s : serverStatus.getStreams()) MainActivity.this.runOnUiThread(new Runnable() {
Log.d(TAG, s.toString()); @Override
sectionsPagerAdapter.updateServer(serverStatus); public void run() {
clientListFragment.updateServer(MainActivity.this.serverStatus);
}
});
// TODO: group sectionsPagerAdapter.updateServer(serverStatus);
} }
@Override @Override
public void onStreamUpdate(RemoteControl remoteControl, Stream stream) { public void onStreamUpdate(RemoteControl remoteControl, Stream stream) {
serverStatus.updateStream(stream); serverStatus.updateStream(stream);
sectionsPagerAdapter.updateServer(serverStatus); // TODO: group sectionsPagerAdapter.updateServer(serverStatus);
} }
@ -558,19 +529,20 @@ public class MainActivity extends AppCompatActivity implements ClientItem.Client
@Override @Override
public void onVolumeChanged(ClientItem clientItem, int percent) { public void onVolumeChanged(GroupItem groupItem, ClientItem clientItem, int percent) {
remoteControl.setVolume(clientItem.getClient(), percent); remoteControl.setVolume(clientItem.getClient(), percent);
} }
@Override @Override
public void onMute(ClientItem clientItem, boolean mute) { public void onMute(GroupItem groupItem, ClientItem clientItem, boolean mute) {
remoteControl.setMute(clientItem.getClient(), mute); remoteControl.setMute(clientItem.getClient(), mute);
} }
@Override @Override
public void onDeleteClicked(final ClientItem clientItem) { public void onDeleteClicked(GroupItem groupItem, final ClientItem clientItem) {
final Client client = clientItem.getClient(); final Client client = clientItem.getClient();
client.setDeleted(true); client.setDeleted(true);
/* TODO: group
serverStatus.updateClient(client); serverStatus.updateClient(client);
sectionsPagerAdapter.updateServer(serverStatus); sectionsPagerAdapter.updateServer(serverStatus);
Snackbar mySnackbar = Snackbar.make(findViewById(R.id.myCoordinatorLayout), Snackbar mySnackbar = Snackbar.make(findViewById(R.id.myCoordinatorLayout),
@ -595,10 +567,11 @@ public class MainActivity extends AppCompatActivity implements ClientItem.Client
} }
}); });
mySnackbar.show(); mySnackbar.show();
*/
} }
@Override @Override
public void onPropertiesClicked(ClientItem clientItem) { public void onPropertiesClicked(GroupItem groupItem, ClientItem clientItem) {
Intent intent = new Intent(this, ClientSettingsActivity.class); Intent intent = new Intent(this, ClientSettingsActivity.class);
intent.putExtra("client", clientItem.getClient().toJson().toString()); intent.putExtra("client", clientItem.getClient().toJson().toString());
intent.putExtra("streams", serverStatus.getJsonStreams().toString()); intent.putExtra("streams", serverStatus.getJsonStreams().toString());
@ -607,78 +580,15 @@ public class MainActivity extends AppCompatActivity implements ClientItem.Client
} }
@Override @Override
public void onStreamClicked(ClientItem clientItem, Stream stream) { public void onStreamClicked(GroupItem groupItem, Stream stream) {
/* TODO: group
Client client = clientItem.getClient(); Client client = clientItem.getClient();
client.getConfig().setStream(stream.getId()); client.getConfig().setStream(stream.getId());
remoteControl.setStream(client, client.getConfig().getStream()); remoteControl.setStream(client, client.getConfig().getStream());
serverStatus.updateClient(client); serverStatus.updateClient(client);
sectionsPagerAdapter.updateServer(serverStatus); sectionsPagerAdapter.updateServer(serverStatus);
*/
} }
/**
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
private Vector<ClientListFragment> fragments = new Vector<>();
private int streamCount = 0;
private boolean hideOffline = false;
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
public void updateServer(final ServerStatus serverStatus) {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Log.d(TAG, "updateServer: " + serverStatus.getStreams().size());
boolean changed = (serverStatus.getStreams().size() != streamCount);
while (serverStatus.getStreams().size() > fragments.size())
fragments.add(new ClientListFragment());
for (int i = 0; i < serverStatus.getStreams().size(); ++i) {
fragments.get(i).setStream(serverStatus.getStreams().get(i));
fragments.get(i).updateServer(serverStatus);
}
if (changed) {
streamCount = serverStatus.getStreams().size();
notifyDataSetChanged();
tabLayout.setTabsFromPagerAdapter(SectionsPagerAdapter.this);
}
setHideOffline(hideOffline);
}
});
}
public void setHideOffline(boolean hide) {
this.hideOffline = hide;
for (ClientListFragment clientListFragment : fragments)
clientListFragment.setHideOffline(hide);
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return streamCount;
}
@Override
public CharSequence getPageTitle(int position) {
return fragments.get(position).getName();
}
}
} }

View file

@ -88,27 +88,28 @@ public class ServerStatus implements JsonSerialisable {
} }
return false; return false;
} }
*/
public boolean updateClient(Client client) { public boolean updateClient(Client client) {
if (client == null) if (client == null)
return false; return false;
for (int i = 0; i < clients.size(); ++i) { for (Group group : groups) {
Client clientInfo = clients.get(i); for (int i = 0; i < group.getClients().size(); ++i) {
if (clientInfo == null) Client c = group.getClients().get(i);
if (c == null)
continue; continue;
if (client.getId().equals(clientInfo.getId())) { if (client.getId().equals(c.getId())) {
if (clientInfo.equals(client)) if (client.equals(c))
return true;
group.getClients().set(i, client);
return true;
}
}
}
return false; return false;
clients.set(i, client);
return true;
} }
}
clients.add(client);
return true;
}
*/
public boolean updateStream(Stream stream) { public boolean updateStream(Stream stream) {
if (stream == null) if (stream == null)
return false; return false;
@ -137,6 +138,13 @@ public class ServerStatus implements JsonSerialisable {
return streams; return streams;
} }
public Stream getStream(String id) {
for (Stream s : streams)
if ((s != null) && (s.getId().equals(id)))
return s;
return null;
}
public JSONArray getJsonStreams() { public JSONArray getJsonStreams() {
JSONArray jsonArray = new JSONArray(); JSONArray jsonArray = new JSONArray();
for (Stream stream : streams) for (Stream stream : streams)

View file

@ -44,53 +44,15 @@
</android.support.v7.widget.Toolbar> </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.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager <fragment
android:id="@+id/container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/> android:layout_marginTop="?attr/actionBarSize"
android:name="de.badaix.snapcast.ClientListFragment"
<LinearLayout android:id="@+id/clientListFragment"
android:layout_width="match_parent" tools:layout="@layout/fragment_client_list"/>
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:orientation="vertical"
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
android:id="@+id/tvInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:text="Info: this is a prototype"
android:textAppearance="?android:attr/textAppearanceSmall"/>
<CheckBox
android:id="@+id/cbScreenWakelock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Keep screen on"/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Button"
android:visibility="gone"/>
-->
</LinearLayout>
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>

View file

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ This file is part of snapcast
~ Copyright (C) 2014-2016 Johannes Pohl
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- android:descendantFocusability="afterDescendants"-->
<!-- android:paddingRight="?android:attr/scrollbarSize"-->
<!-- android:background="@drawable/big_card"-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageButton
android:id="@+id/ibOverflow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@null"
android:src="@drawable/abc_ic_menu_moreoverflow_mtrl_alpha"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/ibOverflow"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:paddingBottom="1dp"
android:paddingLeft="5dp"
android:paddingTop="2dp"
android:singleLine="true"
android:text="Title"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingBottom="4dp"
android:paddingLeft="3dp">
<ImageButton
android:id="@+id/ibMute"
android:layout_width="60dp"
android:layout_height="match_parent"
android:background="@null"
android:src="@drawable/ic_speaker_icon"/>
<SeekBar
android:id="@+id/volumeSeekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:max="100"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</FrameLayout>

View file

@ -23,13 +23,8 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context="de.badaix.snapcast.ClientListFragment"> tools:context="de.badaix.snapcast.ClientListFragment">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView <ListView
android:id="@+id/lvClient" android:id="@+id/lvGroup"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_above="@+id/tvStreamState" android:layout_above="@+id/tvStreamState"
@ -43,18 +38,4 @@
android:dividerHeight="1dp" android:dividerHeight="1dp"
android:listSelector="@android:color/transparent"/> android:listSelector="@android:color/transparent"/>
<TextView
android:id="@+id/tvStreamState"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentStart="true"
android:padding="5dp"
android:text="Stream state"
android:textAppearance="?android:attr/textAppearanceMedium"/>
</RelativeLayout>
</FrameLayout> </FrameLayout>

View file

@ -20,7 +20,8 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto" xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
>
<!-- android:descendantFocusability="afterDescendants"--> <!-- android:descendantFocusability="afterDescendants"-->
<!-- android:paddingRight="?android:attr/scrollbarSize"--> <!-- android:paddingRight="?android:attr/scrollbarSize"-->
<!-- android:background="@drawable/big_card"--> <!-- android:background="@drawable/big_card"-->
@ -32,21 +33,20 @@
card_view:cardUseCompatPadding="true" card_view:cardUseCompatPadding="true"
card_view:contentPadding="2dp"> card_view:contentPadding="2dp">
<RelativeLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageButton
android:id="@+id/ibOverflow"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentEnd="true" android:orientation="vertical">
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@null"
android:src="@drawable/abc_ic_menu_moreoverflow_mtrl_alpha"/>
<LinearLayout <LinearLayout
android:id="@+id/llClient"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"/>
<LinearLayout
android:id="@+id/llVolume"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
@ -54,6 +54,7 @@
android:orientation="vertical"> android:orientation="vertical">
<!--
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -66,7 +67,7 @@
android:singleLine="true" android:singleLine="true"
android:text="Title" android:text="Title"
android:textAppearance="?android:attr/textAppearanceMedium"/> android:textAppearance="?android:attr/textAppearanceMedium"/>
-->
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -90,7 +91,33 @@
android:max="100"/> android:max="100"/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageButton
android:id="@+id/ibOverflow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="@null"
android:src="@drawable/abc_ic_menu_moreoverflow_mtrl_alpha"/>
<TextView
android:id="@+id/tvStreamState"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentStart="true"
android:padding="5dp"
android:text="Stream state"
android:textAppearance="?android:attr/textAppearanceMedium"/>
</RelativeLayout> </RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView> </android.support.v7.widget.CardView>
</FrameLayout> </FrameLayout>

View file

@ -93,11 +93,14 @@ ClientInfoPtr Config::getClientInfo(const std::string& clientId) const
if (clientId.empty()) if (clientId.empty())
return nullptr; return nullptr;
for (auto client: clients) for (auto group: groups)
{
for (auto client: group->clients)
{ {
if (client->id == clientId) if (client->id == clientId)
return client; return client;
} }
}
return nullptr; return nullptr;
} }
@ -109,13 +112,26 @@ ClientInfoPtr Config::addClientInfo(const std::string& clientId)
if (!client) if (!client)
{ {
client = make_shared<ClientInfo>(clientId); client = make_shared<ClientInfo>(clientId);
clients.push_back(client); //TODO: strange contruct
getGroup(client);
} }
return client; return client;
} }
GroupPtr Config::getGroup(const std::string& groupId) const
{
for (auto group: groups)
{
if (group->id == groupId)
return group;
}
return nullptr;
}
GroupPtr Config::getGroup(ClientInfoPtr client) GroupPtr Config::getGroup(ClientInfoPtr client)
{ {
for (auto group: groups) for (auto group: groups)
@ -127,8 +143,7 @@ GroupPtr Config::getGroup(ClientInfoPtr client)
} }
} }
GroupPtr group = std::make_shared<Group>(); GroupPtr group = std::make_shared<Group>();//client);
group->id = generateUUID();
group->clients.push_back(client); group->clients.push_back(client);
groups.push_back(group); groups.push_back(group);
@ -136,9 +151,9 @@ GroupPtr Config::getGroup(ClientInfoPtr client)
} }
json Config::getServerStatus(const std::string& clientId, const json& streams) const json Config::getServerStatus(const json& streams) const
{ {
json jClient = json::array(); /* json jClient = json::array();
if (clientId != "") if (clientId != "")
{ {
ClientInfoPtr client = getClientInfo(clientId); ClientInfoPtr client = getClientInfo(clientId);
@ -147,7 +162,7 @@ json Config::getServerStatus(const std::string& clientId, const json& streams) c
} }
else else
jClient = getClientInfos(); jClient = getClientInfos();
*/
Host host; Host host;
host.update(); host.update();
//TODO: Set MAC and IP //TODO: Set MAC and IP
@ -157,7 +172,7 @@ json Config::getServerStatus(const std::string& clientId, const json& streams) c
{"host", host.toJson()},//getHostName()}, {"host", host.toJson()},//getHostName()},
{"snapserver", snapserver.toJson()} {"snapserver", snapserver.toJson()}
}}, }},
{"clients", jClient}, {"groups", getGroups()},
{"streams", streams} {"streams", streams}
}; };
@ -165,14 +180,14 @@ json Config::getServerStatus(const std::string& clientId, const json& streams) c
} }
json Config::getClientInfos() const /*json Config::getClientInfos() const
{ {
json result = json::array(); json result = json::array();
for (auto client: clients) for (auto client: clients)
result.push_back(client->toJson()); result.push_back(client->toJson());
return result; return result;
} }
*/
json Config::getGroups() const json Config::getGroups() const
{ {
@ -186,9 +201,9 @@ json Config::getGroups() const
void Config::remove(ClientInfoPtr client) void Config::remove(ClientInfoPtr client)
{ {
auto group = getGroup(client); auto group = getGroup(client);
if (group->clients.size() == 1) auto clients = group->clients;
groups.erase(std::remove(groups.begin(), groups.end(), group), groups.end());
clients.erase(std::remove(clients.begin(), clients.end(), client), clients.end()); clients.erase(std::remove(clients.begin(), clients.end(), client), clients.end());
if (group->clients.empty())
groups.erase(std::remove(groups.begin(), groups.end(), group), groups.end());
} }

View file

@ -257,8 +257,11 @@ struct ClientInfo
struct Group struct Group
{ {
Group() Group(const ClientInfoPtr client = nullptr)
{ {
if (client)
id = client->id;
id = generateUUID();
} }
void fromJson(const json& j) void fromJson(const json& j)
@ -314,15 +317,16 @@ public:
void remove(ClientInfoPtr client); void remove(ClientInfoPtr client);
GroupPtr getGroup(ClientInfoPtr client); GroupPtr getGroup(ClientInfoPtr client);
GroupPtr getGroup(const std::string& groupId) const;
json getClientInfos() const; // json getClientInfos() const;
json getGroups() const; json getGroups() const;
json getServerStatus(const std::string& clientId, const json& streams) const; json getServerStatus(const json& streams) const;
void save(); void save();
std::vector<GroupPtr> groups; std::vector<GroupPtr> groups;
std::vector<ClientInfoPtr> clients; // std::vector<ClientInfoPtr> clients;
private: private:
Config(); Config();

View file

@ -115,9 +115,17 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
json response; json response;
ClientInfoPtr clientInfo = nullptr; ClientInfoPtr clientInfo = nullptr;
GroupPtr group = nullptr;
msg::ServerSettings serverSettings; msg::ServerSettings serverSettings;
serverSettings.setBufferMs(settings_.bufferMs); serverSettings.setBufferMs(settings_.bufferMs);
if (request.method.find("Group.Set") == 0)
{
group = Config::instance().getGroup(request.getParam("group").get<string>());
if (group == nullptr)
throw JsonInternalErrorException("Group not found", request.id);
}
if (request.method.find("Client.Set") == 0) if (request.method.find("Client.Set") == 0)
{ {
clientInfo = Config::instance().getClientInfo(request.getParam("client").get<string>()); clientInfo = Config::instance().getClientInfo(request.getParam("client").get<string>());
@ -127,8 +135,9 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
if (request.method == "Server.GetStatus") if (request.method == "Server.GetStatus")
{ {
/// TODO: rpc
string clientId = request.hasParam("client") ? request.getParam("client").get<string>() : ""; string clientId = request.hasParam("client") ? request.getParam("client").get<string>() : "";
response = Config::instance().getServerStatus(clientId, streamManager_->toJson()); response = Config::instance().getServerStatus(/*clientId,*/ streamManager_->toJson());
// logO << response.dump(4); // logO << response.dump(4);
} }
else if (request.method == "Server.DeleteClient") else if (request.method == "Server.DeleteClient")
@ -153,24 +162,25 @@ void StreamServer::onMessageReceived(ControlSession* controlSession, const std::
clientInfo->config.volume.muted = request.getParam<bool>("mute", false, true); clientInfo->config.volume.muted = request.getParam<bool>("mute", false, true);
response = clientInfo->config.volume.muted; response = clientInfo->config.volume.muted;
} }
else if (request.method == "Client.SetStream") else if (request.method == "Group.SetStream")
{ {
/* TODO: Group.SetStream
string streamId = request.getParam("id").get<string>(); string streamId = request.getParam("id").get<string>();
PcmStreamPtr stream = streamManager_->getStream(streamId); PcmStreamPtr stream = streamManager_->getStream(streamId);
if (stream == nullptr) if (stream == nullptr)
throw JsonInternalErrorException("Stream not found", request.id); throw JsonInternalErrorException("Stream not found", request.id);
clientInfo->config.streamId = streamId; group->streamId = streamId;
response = clientInfo->config.streamId; response = group->streamId;
session_ptr session = getStreamSession(request.getParam("client").get<string>()); for (auto client: group->clients)
{
session_ptr session = getStreamSession(client->id);
if (session != nullptr) if (session != nullptr)
{ {
session->sendAsync(stream->getHeader()); session->sendAsync(stream->getHeader());
session->setPcmStream(stream); session->setPcmStream(stream);
} }
*/ }
} }
else if (request.method == "Client.SetLatency") else if (request.method == "Client.SetLatency")
{ {
@ -247,7 +257,6 @@ void StreamServer::onMessageReceived(StreamSession* connection, const msg::BaseM
logD << "request kServerSettings: " << connection->clientId << "\n"; logD << "request kServerSettings: " << connection->clientId << "\n";
// std::lock_guard<std::mutex> mlock(mutex_); // std::lock_guard<std::mutex> mlock(mutex_);
ClientInfoPtr client = Config::instance().addClientInfo(connection->clientId); ClientInfoPtr client = Config::instance().addClientInfo(connection->clientId);
GroupPtr group = Config::instance().getGroup(client); GroupPtr group = Config::instance().getGroup(client);
logD << "request kServerSettings\n"; logD << "request kServerSettings\n";
@ -289,6 +298,8 @@ void StreamServer::onMessageReceived(StreamSession* connection, const msg::BaseM
json notification = JsonNotification::getJson("Client.OnConnect", client->toJson()); json notification = JsonNotification::getJson("Client.OnConnect", client->toJson());
// logO << notification.dump(4) << "\n"; // logO << notification.dump(4) << "\n";
controlServer_->send(notification.dump()); controlServer_->send(notification.dump());
// cout << Config::instance().getServerStatus(streamManager_->toJson()).dump(4) << "\n";
// cout << group->toJson().dump(4) << "\n";
} }
} }