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.
## Icons
All icons are taken from the Android project.
## License
[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: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
android:id="@+id/previous"
android:layout_width="wrap_content"
@ -72,6 +79,13 @@
android:contentDescription="@string/next"
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
android:id="@+id/total_time"
android:layout_width="wrap_content"

View file

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="currently_playing_background">#BB99CC00</color>
<color name="button_highlight">#ff33b5e5</color>
</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="previous">Previous</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="album_art">Album Art</string>
<string name="route_description">DLNA Playback</string>

View file

@ -86,6 +86,8 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
private View mControls;
private SeekBar mProgressBar;
private ImageButton mPlayPause;
private ImageButton mShuffle;
private ImageButton mRepeat;
private View mCurrentTrackView;
@ -111,6 +113,7 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
mMediaRouterPlayService.getService().setRouterFragment(RouteFragment.this);
mPlaylistAdapter.addAll(mMediaRouterPlayService.getService().getPlaylist());
receiveIsPlaying(mMediaRouterPlayService.getService().getCurrentTrack());
applyColors();
}
public void onServiceDisconnected(ComponentName className) {
@ -157,17 +160,25 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
mProgressBar = (SeekBar) getView().findViewById(R.id.progressBar);
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);
previous.setImageResource(R.drawable.ic_media_previous);
getView().findViewById(R.id.previous).setOnClickListener(this);
previous.setImageResource(R.drawable.ic_action_previous);
previous.setOnClickListener(this);
ImageButton next = (ImageButton) getView().findViewById(R.id.next);
next.setImageResource(R.drawable.ic_media_next);
getView().findViewById(R.id.next).setOnClickListener(this);
next.setImageResource(R.drawable.ic_action_next);
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.setOnClickListener(this);
mPlayPause.setImageResource(R.drawable.ic_media_play);
mPlayPause.setImageResource(R.drawable.ic_action_play);
getActivity().getApplicationContext().startService(
new Intent(getActivity(), MediaRouterPlayService.class));
@ -361,22 +372,48 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
*/
@Override
public void onClick(View v) {
MediaRouterPlayService s = mMediaRouterPlayService.getService();
switch (v.getId()) {
case R.id.playpause:
if (mPlaying)
mMediaRouterPlayService.getService().pause();
s.pause();
else
mMediaRouterPlayService.getService().resume();
s.resume();
break;
case R.id.shuffle:
s.toggleShuffleEnabled();
applyColors();
break;
case R.id.previous:
mMediaRouterPlayService.getService().playPrevious();
s.playPrevious();
break;
case R.id.next:
mMediaRouterPlayService.getService().playNext();
s.playNext();
break;
case R.id.repeat:
s.toggleRepeatEnabled();
applyColors();
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.
*/
@ -484,11 +521,11 @@ public class RouteFragment extends MediaRouteDiscoveryFragment implements
status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_BUFFERING ||
status.getPlaybackState() == MediaItemStatus.PLAYBACK_STATE_PENDING) {
mPlaying = true;
mPlayPause.setImageResource(R.drawable.ic_media_pause);
mPlayPause.setImageResource(R.drawable.ic_action_pause);
}
else {
mPlaying = false;
mPlayPause.setImageResource(R.drawable.ic_media_play);
mPlayPause.setImageResource(R.drawable.ic_action_play);
}
if (mListView.getAdapter() == mPlaylistAdapter)

View file

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