Set play/paused state only through events.

This commit is contained in:
Felix Ableitner 2013-06-06 10:44:03 +02:00
parent a44f88729c
commit d8660afaeb
3 changed files with 127 additions and 70 deletions

View file

@ -1,5 +1,7 @@
package com.github.nutomic.controldlna; package com.github.nutomic.controldlna;
import org.teleal.cling.support.model.item.Item;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
@ -133,9 +135,12 @@ public class MainActivity extends SherlockFragmentActivity implements
super.onBackPressed(); super.onBackPressed();
} }
public void play(String uri) { /**
* Utility function to call RendererFragment.play from ServerFragment.
*/
public void play(Item[] playlist, int start) {
getSupportActionBar().selectTab(getSupportActionBar().getTabAt(0)); getSupportActionBar().selectTab(getSupportActionBar().getTabAt(0));
mRendererFragment.play(uri); mRendererFragment.play(playlist, start);
} }
} }

View file

@ -1,19 +1,29 @@
package com.github.nutomic.controldlna; package com.github.nutomic.controldlna;
import java.util.Map;
import org.teleal.cling.android.AndroidUpnpService; import org.teleal.cling.android.AndroidUpnpService;
import org.teleal.cling.android.AndroidUpnpServiceImpl; import org.teleal.cling.android.AndroidUpnpServiceImpl;
import org.teleal.cling.controlpoint.SubscriptionCallback;
import org.teleal.cling.model.action.ActionInvocation; import org.teleal.cling.model.action.ActionInvocation;
import org.teleal.cling.model.gena.CancelReason;
import org.teleal.cling.model.gena.GENASubscription;
import org.teleal.cling.model.message.UpnpResponse; import org.teleal.cling.model.message.UpnpResponse;
import org.teleal.cling.model.meta.Device; import org.teleal.cling.model.meta.Device;
import org.teleal.cling.model.meta.LocalDevice; import org.teleal.cling.model.meta.LocalDevice;
import org.teleal.cling.model.meta.RemoteDevice; import org.teleal.cling.model.meta.RemoteDevice;
import org.teleal.cling.model.meta.Service; import org.teleal.cling.model.meta.Service;
import org.teleal.cling.model.state.StateVariableValue;
import org.teleal.cling.model.types.ServiceType; import org.teleal.cling.model.types.ServiceType;
import org.teleal.cling.registry.Registry; import org.teleal.cling.registry.Registry;
import org.teleal.cling.registry.RegistryListener; import org.teleal.cling.registry.RegistryListener;
import org.teleal.cling.support.avtransport.callback.Play; import org.teleal.cling.support.avtransport.callback.Play;
import org.teleal.cling.support.avtransport.callback.SetAVTransportURI; import org.teleal.cling.support.avtransport.callback.SetAVTransportURI;
import org.teleal.cling.support.avtransport.callback.Stop; import org.teleal.cling.support.avtransport.callback.Stop;
import org.teleal.cling.support.avtransport.lastchange.AVTransportLastChangeParser;
import org.teleal.cling.support.avtransport.lastchange.AVTransportVariable;
import org.teleal.cling.support.lastchange.LastChange;
import org.teleal.cling.support.model.item.Item;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
@ -52,21 +62,26 @@ public class RendererFragment extends Fragment implements
private boolean mPlaying = false; private boolean mPlaying = false;
private Item[] mPlaylist;
/** /**
* ListView adapter of media renderers. * ListView adapter of media renderers.
*/ */
private DeviceArrayAdapter mRendererAdapter; private DeviceArrayAdapter mRendererAdapter;
private FileArrayAdapter mPlaylistAdapter;
/** /**
* The media renderer that is currently active. * The media renderer that is currently active.
*/ */
@SuppressWarnings("rawtypes") private Device<?, ?, ?> mCurrentRenderer;
private Device mCurrentRenderer;
/** /**
* Stores uri that is to be played if no renderer is selected. * First track to be played when a renderer is selected (-1 for none).
*/ */
private String mCachedPlayUri = ""; private int mCachedStart = -1;
private SubscriptionCallback mSubscriptionCallback;
/** /**
* Cling UPNP service. * Cling UPNP service.
@ -199,7 +214,7 @@ public class RendererFragment extends Fragment implements
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
mCachedPlayUri = ""; mCachedStart = -1;
} }
/** /**
@ -214,16 +229,20 @@ public class RendererFragment extends Fragment implements
} }
/** /**
* Plays an URI to a media renderer. If none is selected, the URI is * Plays the URIs in playlist to the current renderer, or caches parameters
* cached and played after selecting one. * until a renderer is selected.
*
* @param playlist Array of URIs to play.
* @param start Index of the URI which should be played first.
*/ */
void play(String uri) { public void play(Item[] playlist, final int start) {
Log.d(TAG, uri); mPlaylist = playlist;
if (mCurrentRenderer != null) { if (mCurrentRenderer != null) {
mListView.setAdapter(mPlaylistAdapter);
final Service<?, ?> service = mCurrentRenderer.findService( final Service<?, ?> service = mCurrentRenderer.findService(
new ServiceType("schemas-upnp-org", "AVTransport")); new ServiceType("schemas-upnp-org", "AVTransport"));
mUpnpService.getControlPoint().execute(new SetAVTransportURI(service, mUpnpService.getControlPoint().execute(new SetAVTransportURI(service,
uri, "NO METADATA") { playlist[start].getFirstResource().getValue(), "NO METADATA") {
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@Override @Override
public void failure(ActionInvocation invocation, public void failure(ActionInvocation invocation,
@ -235,15 +254,12 @@ public class RendererFragment extends Fragment implements
@Override @Override
public void success(ActionInvocation invocation) { public void success(ActionInvocation invocation) {
mUpnpService.getControlPoint().execute(new Play(service) { mUpnpService.getControlPoint().execute(new Play(service) {
@Override @Override
public void failure(ActionInvocation invocation, public void failure(ActionInvocation invocation,
UpnpResponse operation, String defaultMsg) { UpnpResponse operation, String defaultMsg) {
Log.w(TAG, "Playback failed: " + defaultMsg); Log.w(TAG, "Playback failed: " + defaultMsg);
} }
@Override
public void success(ActionInvocation invocation) {
playbackStarted();
}
}); });
} }
}); });
@ -251,7 +267,7 @@ public class RendererFragment extends Fragment implements
else { else {
Toast.makeText(getActivity(), "Please select a renderer.", Toast.makeText(getActivity(), "Please select a renderer.",
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();
mCachedPlayUri = uri; mCachedStart = start;
} }
} }
@ -262,12 +278,85 @@ public class RendererFragment extends Fragment implements
public void onItemClick(AdapterView<?> a, View v, int position, long id) { public void onItemClick(AdapterView<?> a, View v, int position, long id) {
if (mCurrentRenderer == null) { if (mCurrentRenderer == null) {
mCurrentRenderer = mRendererAdapter.getItem(position); mCurrentRenderer = mRendererAdapter.getItem(position);
mListView.setAdapter(null); Service<?, ?> service = mCurrentRenderer.findService(
mPlayPause.setVisibility(View.VISIBLE); new ServiceType("schemas-upnp-org", "AVTransport"));
if (!mCachedPlayUri.equals("")) { mSubscriptionCallback = new SubscriptionCallback(service, 600) {
play(mCachedPlayUri);
mCachedPlayUri = ""; @SuppressWarnings("rawtypes")
@Override
protected void established(GENASubscription sub) {
} }
@SuppressWarnings("rawtypes")
@Override
protected void ended(GENASubscription sub, CancelReason reason,
UpnpResponse response) {
}
@SuppressWarnings("rawtypes")
@Override
protected void eventReceived(GENASubscription sub) {
@SuppressWarnings("unchecked")
Map<String, StateVariableValue> m = sub.getCurrentValues();
try {
LastChange lastChange = new LastChange(
new AVTransportLastChangeParser(),
m.get("LastChange").toString());
switch (lastChange.getEventedValue(0,
AVTransportVariable.TransportState.class)
.getValue()) {
case PLAYING:
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
mPlayPause.setText(R.string.pause);
mPlaying = true;
Log.d(TAG, "play");
}
});
break;
case PAUSED_PLAYBACK:
// fallthrough
case STOPPED:
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
mPlayPause.setText(R.string.play);
mPlaying = false;
Log.d(TAG, "stop");
}
});
break;
default:
}
} catch (Exception e) {
e.printStackTrace();
}
}
@SuppressWarnings("rawtypes")
@Override
protected void eventsMissed(GENASubscription sub, int numberOfMissedEvents) {
}
@SuppressWarnings("rawtypes")
@Override
protected void failed(GENASubscription sub, UpnpResponse responseStatus,
Exception exception, String defaultMsg) {
Log.d(TAG, defaultMsg);
}
};
mUpnpService.getControlPoint().execute(mSubscriptionCallback);
if (mCachedStart != -1) {
play(mPlaylist, mCachedStart);
mCachedStart = -1;
}
mListView.setAdapter(mPlaylistAdapter);
mPlayPause.setVisibility(View.VISIBLE);
} }
} }
@ -277,8 +366,10 @@ public class RendererFragment extends Fragment implements
@Override @Override
public boolean onBackPressed() { public boolean onBackPressed() {
if (mCurrentRenderer != null) { if (mCurrentRenderer != null) {
mListView.setAdapter(mRendererAdapter);
mCurrentRenderer = null; mCurrentRenderer = null;
mSubscriptionCallback.end();
mListView.setAdapter(mRendererAdapter);
mPlayPause.setVisibility(View.GONE); mPlayPause.setVisibility(View.GONE);
return true; return true;
} }
@ -310,17 +401,8 @@ public class RendererFragment extends Fragment implements
UpnpResponse operation, String defaultMessage) { UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Stop failed: " + defaultMessage); Log.w(TAG, "Stop failed: " + defaultMessage);
} }
@Override
public void success(ActionInvocation invocation) {
playbackPaused();
};
}); });
} }
@SuppressWarnings("rawtypes")
@Override
public void success(ActionInvocation invocation) {
playbackPaused();
};
}); });
} else { } else {
mUpnpService.getControlPoint().execute(new Play(service) { mUpnpService.getControlPoint().execute(new Play(service) {
@ -331,44 +413,9 @@ public class RendererFragment extends Fragment implements
UpnpResponse operation, String defaultMessage) { UpnpResponse operation, String defaultMessage) {
Log.w(TAG, "Play failed: " + defaultMessage); Log.w(TAG, "Play failed: " + defaultMessage);
} }
@SuppressWarnings("rawtypes")
@Override
public void success(ActionInvocation invocation) {
playbackStarted();
};
}); });
} }
} }
} }
/**
* Sets button text and playing attribute.
* Call this after pausing/stopping playback.
*/
private void playbackPaused() {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
mPlaying = false;
mPlayPause.setText(R.string.play);
}
});
}
/**
* Sets button text and playing attribute.
* Call this after starting playback.
*/
private void playbackStarted() {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
mPlaying = true;
mPlayPause.setText(R.string.pause);
}
});
}
} }

View file

@ -211,9 +211,14 @@ public class ServerFragment extends ListFragment implements OnBackPressedListene
getFiles(((Container) mFileAdapter.getItem(position)).getId()); getFiles(((Container) mFileAdapter.getItem(position)).getId());
} }
else { else {
Item[] playlist = new Item[mFileAdapter.getCount()];
for (int i = 0; i < mFileAdapter.getCount(); i++) {
if (mFileAdapter.getItem(i) instanceof Item) {
playlist[i] = (Item) mFileAdapter.getItem(position);
}
}
MainActivity activity = (MainActivity) getActivity(); MainActivity activity = (MainActivity) getActivity();
activity.play(mFileAdapter.getItem(position) activity.play(playlist, position);
.getFirstResource().getValue());
} }
} }
} }