Added shuffle and repeat functionality.

This commit is contained in:
Felix Ableitner 2013-12-29 19:31:42 +01:00
parent b86bf827f0
commit e31747e44b
42 changed files with 129 additions and 22 deletions

View file

@ -16,6 +16,10 @@ Android Support Library and [Cling](http://4thline.org/projects/cling/) are requ
Binaries for Cling are included in the libs folder. Binaries for Cling are included in the libs folder.
## Icons
All icons are taken from the Android project.
## License ## License
[BSD 3-Clause License](LICENSE.md) [BSD 3-Clause License](LICENSE.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 651 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 599 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 843 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 897 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 837 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 774 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 685 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -51,6 +51,13 @@
android:gravity="right" android:gravity="right"
android:minEms="2" /> android:minEms="2" />
<ImageButton
android:id="@+id/shuffle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/shuffle"
android:layout_toLeftOf="@+id/previous" />
<ImageButton <ImageButton
android:id="@+id/previous" android:id="@+id/previous"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -72,6 +79,13 @@
android:contentDescription="@string/next" android:contentDescription="@string/next"
android:layout_toRightOf="@id/playpause" /> android:layout_toRightOf="@id/playpause" />
<ImageButton
android:id="@+id/repeat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/repeat"
android:layout_toRightOf="@id/next" />
<TextView <TextView
android:id="@+id/total_time" android:id="@+id/total_time"
android:layout_width="wrap_content" android:layout_width="wrap_content"

View file

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<color name="currently_playing_background">#BB99CC00</color> <color name="currently_playing_background">#BB99CC00</color>
<color name="button_highlight">#ff33b5e5</color>
</resources> </resources>

View file

@ -9,6 +9,8 @@
<string name="exit_renderer">Do you really want to exit the renderer? Playback will be stopped.</string> <string name="exit_renderer">Do you really want to exit the renderer? Playback will be stopped.</string>
<string name="previous">Previous</string> <string name="previous">Previous</string>
<string name="next">Next</string> <string name="next">Next</string>
<string name="shuffle">Shuffle</string>
<string name="repeat">Repeat</string>
<string name="select_route">Please select a route</string> <string name="select_route">Please select a route</string>
<string name="album_art">Album Art</string> <string name="album_art">Album Art</string>
<string name="route_description">DLNA Playback</string> <string name="route_description">DLNA Playback</string>

View file

@ -86,6 +86,8 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
private View mControls; private View mControls;
private SeekBar mProgressBar; private SeekBar mProgressBar;
private ImageButton mPlayPause; private ImageButton mPlayPause;
private ImageButton mShuffle;
private ImageButton mRepeat;
private View mCurrentTrackView; private View mCurrentTrackView;
@ -111,6 +113,7 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
mMediaRouterPlayService.getService().setRouterFragment(RouteFragment.this); mMediaRouterPlayService.getService().setRouterFragment(RouteFragment.this);
mPlaylistAdapter.addAll(mMediaRouterPlayService.getService().getPlaylist()); mPlaylistAdapter.addAll(mMediaRouterPlayService.getService().getPlaylist());
receiveIsPlaying(mMediaRouterPlayService.getService().getCurrentTrack()); receiveIsPlaying(mMediaRouterPlayService.getService().getCurrentTrack());
applyColors();
} }
public void onServiceDisconnected(ComponentName className) { public void onServiceDisconnected(ComponentName className) {
@ -157,17 +160,25 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
mProgressBar = (SeekBar) getView().findViewById(R.id.progressBar); mProgressBar = (SeekBar) getView().findViewById(R.id.progressBar);
mProgressBar.setOnSeekBarChangeListener(this); mProgressBar.setOnSeekBarChangeListener(this);
mShuffle = (ImageButton) getView().findViewById(R.id.shuffle);
mShuffle.setImageResource(R.drawable.ic_action_shuffle);
mShuffle.setOnClickListener(this);
ImageButton previous = (ImageButton) getView().findViewById(R.id.previous); ImageButton previous = (ImageButton) getView().findViewById(R.id.previous);
previous.setImageResource(R.drawable.ic_media_previous); previous.setImageResource(R.drawable.ic_action_previous);
getView().findViewById(R.id.previous).setOnClickListener(this); previous.setOnClickListener(this);
ImageButton next = (ImageButton) getView().findViewById(R.id.next); ImageButton next = (ImageButton) getView().findViewById(R.id.next);
next.setImageResource(R.drawable.ic_media_next); next.setImageResource(R.drawable.ic_action_next);
getView().findViewById(R.id.next).setOnClickListener(this); next.setOnClickListener(this);
mRepeat = (ImageButton) getView().findViewById(R.id.repeat);
mRepeat.setImageResource(R.drawable.ic_action_repeat);
mRepeat.setOnClickListener(this);
mPlayPause = (ImageButton) getView().findViewById(R.id.playpause); mPlayPause = (ImageButton) getView().findViewById(R.id.playpause);
mPlayPause.setOnClickListener(this); mPlayPause.setOnClickListener(this);
mPlayPause.setImageResource(R.drawable.ic_media_play); mPlayPause.setImageResource(R.drawable.ic_action_play);
getActivity().getApplicationContext().startService( getActivity().getApplicationContext().startService(
new Intent(getActivity(), MediaRouterPlayService.class)); new Intent(getActivity(), MediaRouterPlayService.class));
@ -361,22 +372,48 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
*/ */
@Override @Override
public void onClick(View v) { public void onClick(View v) {
MediaRouterPlayService s = mMediaRouterPlayService.getService();
switch (v.getId()) { switch (v.getId()) {
case R.id.playpause: case R.id.playpause:
if (mPlaying) if (mPlaying)
mMediaRouterPlayService.getService().pause(); s.pause();
else else
mMediaRouterPlayService.getService().resume(); s.resume();
break;
case R.id.shuffle:
s.toggleShuffleEnabled();
applyColors();
break; break;
case R.id.previous: case R.id.previous:
mMediaRouterPlayService.getService().playPrevious(); s.playPrevious();
break; break;
case R.id.next: case R.id.next:
mMediaRouterPlayService.getService().playNext(); s.playNext();
break;
case R.id.repeat:
s.toggleRepeatEnabled();
applyColors();
break; break;
} }
} }
/**
* Enables or disables highlighting on shuffle/repeat buttons (depending
* if they are enabled or disabled).
*/
private void applyColors() {
MediaRouterPlayService s = mMediaRouterPlayService.getService();
int highlight = getResources().getColor(R.color.button_highlight);
int transparent = getResources().getColor(android.R.color.transparent);
mShuffle.setColorFilter((s.getShuffleEnabled())
? highlight
: transparent);
mRepeat.setColorFilter((s.getRepeatEnabled())
? highlight
: transparent);
}
/** /**
* Sends manual seek on progress bar to renderer. * Sends manual seek on progress bar to renderer.
*/ */
@ -484,11 +521,11 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_BUFFERING || status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_BUFFERING ||
status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_PENDING) { status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_PENDING) {
mPlaying = true; mPlaying = true;
mPlayPause.setImageResource(R.drawable.ic_media_pause); mPlayPause.setImageResource(R.drawable.ic_action_pause);
} }
else { else {
mPlaying = false; mPlaying = false;
mPlayPause.setImageResource(R.drawable.ic_media_play); mPlayPause.setImageResource(R.drawable.ic_action_play);
} }
if (mListView.getAdapter() == mPlaylistAdapter) if (mListView.getAdapter() == mPlaylistAdapter)

View file

@ -30,6 +30,7 @@ package com.github.nutomic.controldlna.mediarouter;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Random;
import org.teleal.cling.support.contentdirectory.DIDLParser; import org.teleal.cling.support.contentdirectory.DIDLParser;
import org.teleal.cling.support.model.DIDLContent; import org.teleal.cling.support.model.DIDLContent;
@ -86,6 +87,10 @@ public class MediaRouterPlayService extends Service {
*/ */
private int mCurrentTrack = -1; private int mCurrentTrack = -1;
private boolean mShuffle = false;
private boolean mRepeat = false;
private String mItemId; private String mItemId;
private String mSessionId; private String mSessionId;
@ -243,6 +248,9 @@ public class MediaRouterPlayService extends Service {
* Sends 'pause' signal to current renderer. * Sends 'pause' signal to current renderer.
*/ */
public void pause() { public void pause() {
if (mPlaylist.isEmpty())
return;
Intent intent = new Intent(MediaControlIntent.ACTION_PAUSE); Intent intent = new Intent(MediaControlIntent.ACTION_PAUSE);
intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId); intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId);
@ -253,6 +261,9 @@ public class MediaRouterPlayService extends Service {
* Sends 'resume' signal to current renderer. * Sends 'resume' signal to current renderer.
*/ */
public void resume() { public void resume() {
if (mPlaylist.isEmpty())
return;
Intent intent = new Intent(MediaControlIntent.ACTION_RESUME); Intent intent = new Intent(MediaControlIntent.ACTION_RESUME);
intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId); intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId);
@ -267,6 +278,9 @@ public class MediaRouterPlayService extends Service {
* Sends 'stop' signal to current renderer. * Sends 'stop' signal to current renderer.
*/ */
public void stop() { public void stop() {
if (mPlaylist.isEmpty())
return;
Intent intent = new Intent(MediaControlIntent.ACTION_STOP); Intent intent = new Intent(MediaControlIntent.ACTION_STOP);
intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId); intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId);
@ -274,6 +288,9 @@ public class MediaRouterPlayService extends Service {
} }
public void seek(int seconds) { public void seek(int seconds) {
if (mPlaylist.isEmpty())
return;
Intent intent = new Intent(MediaControlIntent.ACTION_SEEK); Intent intent = new Intent(MediaControlIntent.ACTION_SEEK);
intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK);
intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId); intent.putExtra(MediaControlIntent.EXTRA_SESSION_ID, mSessionId);
@ -296,7 +313,23 @@ public class MediaRouterPlayService extends Service {
* Plays the track after current in the playlist. * Plays the track after current in the playlist.
*/ */
public void playNext() { public void playNext() {
play(mCurrentTrack + 1); if (mCurrentTrack == -1)
return;
if (mShuffle)
// Play random item.
play(new Random().nextInt(mPlaylist.size()));
else if (mCurrentTrack + 1 < mPlaylist.size())
// Playlist not over, play next item.
play(mCurrentTrack + 1);
else if (mRepeat)
// Playlist over, repeat it.
play(0);
else if (!mBound) {
// Playlist over, stop playback.
stopSelf();
mPollingStatus = false;
}
} }
@ -304,7 +337,14 @@ public class MediaRouterPlayService extends Service {
* Plays the track before current in the playlist. * Plays the track before current in the playlist.
*/ */
public void playPrevious() { public void playPrevious() {
play(mCurrentTrack - 1); if (mCurrentTrack == -1)
return;
if (mShuffle)
// Play random item.
play(new Random().nextInt(mPlaylist.size()));
else
play(mCurrentTrack - 1);
} }
/** /**
@ -339,15 +379,8 @@ public class MediaRouterPlayService extends Service {
status.getPlaybackState() != MediaItemStatus.PLAYBACK_STATE_PLAYING) status.getPlaybackState() != MediaItemStatus.PLAYBACK_STATE_PLAYING)
stopForeground(true); stopForeground(true);
if (status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_FINISHED) { if (status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_FINISHED)
if (mCurrentTrack + 1 < mPlaylist.size()) playNext();
playNext();
else {
if (!mBound)
stopSelf();
mPollingStatus = false;
}
}
} }
}); });
} }
@ -373,4 +406,20 @@ public class MediaRouterPlayService extends Service {
return mPlaylist; return mPlaylist;
} }
public void toggleShuffleEnabled() {
mShuffle = !mShuffle;
}
public boolean getShuffleEnabled() {
return mShuffle;
}
public void toggleRepeatEnabled() {
mRepeat = !mRepeat;
}
public boolean getRepeatEnabled() {
return mRepeat;
}
} }