diff --git a/README.md b/README.md index 3232646..751db3c 100644 --- a/README.md +++ b/README.md @@ -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) \ No newline at end of file diff --git a/res/drawable-hdpi/ic_action_next.png b/res/drawable-hdpi/ic_action_next.png new file mode 100644 index 0000000..71076e4 Binary files /dev/null and b/res/drawable-hdpi/ic_action_next.png differ diff --git a/res/drawable-hdpi/ic_action_pause.png b/res/drawable-hdpi/ic_action_pause.png new file mode 100644 index 0000000..535ad58 Binary files /dev/null and b/res/drawable-hdpi/ic_action_pause.png differ diff --git a/res/drawable-hdpi/ic_action_play.png b/res/drawable-hdpi/ic_action_play.png new file mode 100644 index 0000000..e22b099 Binary files /dev/null and b/res/drawable-hdpi/ic_action_play.png differ diff --git a/res/drawable-hdpi/ic_action_previous.png b/res/drawable-hdpi/ic_action_previous.png new file mode 100644 index 0000000..c766ece Binary files /dev/null and b/res/drawable-hdpi/ic_action_previous.png differ diff --git a/res/drawable-hdpi/ic_action_repeat.png b/res/drawable-hdpi/ic_action_repeat.png new file mode 100644 index 0000000..773e2de Binary files /dev/null and b/res/drawable-hdpi/ic_action_repeat.png differ diff --git a/res/drawable-hdpi/ic_action_shuffle.png b/res/drawable-hdpi/ic_action_shuffle.png new file mode 100644 index 0000000..7387b59 Binary files /dev/null and b/res/drawable-hdpi/ic_action_shuffle.png differ diff --git a/res/drawable-hdpi/ic_media_next.png b/res/drawable-hdpi/ic_media_next.png deleted file mode 100644 index 6e27b81..0000000 Binary files a/res/drawable-hdpi/ic_media_next.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_media_pause.png b/res/drawable-hdpi/ic_media_pause.png deleted file mode 100644 index 1d465a4..0000000 Binary files a/res/drawable-hdpi/ic_media_pause.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_media_play.png b/res/drawable-hdpi/ic_media_play.png deleted file mode 100644 index 2746d17..0000000 Binary files a/res/drawable-hdpi/ic_media_play.png and /dev/null differ diff --git a/res/drawable-hdpi/ic_media_previous.png b/res/drawable-hdpi/ic_media_previous.png deleted file mode 100644 index 85b3766..0000000 Binary files a/res/drawable-hdpi/ic_media_previous.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_action_next.png b/res/drawable-mdpi/ic_action_next.png new file mode 100644 index 0000000..5c66996 Binary files /dev/null and b/res/drawable-mdpi/ic_action_next.png differ diff --git a/res/drawable-mdpi/ic_action_pause.png b/res/drawable-mdpi/ic_action_pause.png new file mode 100644 index 0000000..95a39ff Binary files /dev/null and b/res/drawable-mdpi/ic_action_pause.png differ diff --git a/res/drawable-mdpi/ic_action_play.png b/res/drawable-mdpi/ic_action_play.png new file mode 100644 index 0000000..b94fbe6 Binary files /dev/null and b/res/drawable-mdpi/ic_action_play.png differ diff --git a/res/drawable-mdpi/ic_action_previous.png b/res/drawable-mdpi/ic_action_previous.png new file mode 100644 index 0000000..c84e6b1 Binary files /dev/null and b/res/drawable-mdpi/ic_action_previous.png differ diff --git a/res/drawable-mdpi/ic_action_repeat.png b/res/drawable-mdpi/ic_action_repeat.png new file mode 100644 index 0000000..a2e8ec5 Binary files /dev/null and b/res/drawable-mdpi/ic_action_repeat.png differ diff --git a/res/drawable-mdpi/ic_action_shuffle.png b/res/drawable-mdpi/ic_action_shuffle.png new file mode 100644 index 0000000..e605c9c Binary files /dev/null and b/res/drawable-mdpi/ic_action_shuffle.png differ diff --git a/res/drawable-mdpi/ic_media_next.png b/res/drawable-mdpi/ic_media_next.png deleted file mode 100644 index fcd73d9..0000000 Binary files a/res/drawable-mdpi/ic_media_next.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_media_pause.png b/res/drawable-mdpi/ic_media_pause.png deleted file mode 100644 index 3e6b2a1..0000000 Binary files a/res/drawable-mdpi/ic_media_pause.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_media_play.png b/res/drawable-mdpi/ic_media_play.png deleted file mode 100644 index 7966bbc..0000000 Binary files a/res/drawable-mdpi/ic_media_play.png and /dev/null differ diff --git a/res/drawable-mdpi/ic_media_previous.png b/res/drawable-mdpi/ic_media_previous.png deleted file mode 100644 index b653d05..0000000 Binary files a/res/drawable-mdpi/ic_media_previous.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_action_next.png b/res/drawable-xhdpi/ic_action_next.png new file mode 100644 index 0000000..5c030cb Binary files /dev/null and b/res/drawable-xhdpi/ic_action_next.png differ diff --git a/res/drawable-xhdpi/ic_action_pause.png b/res/drawable-xhdpi/ic_action_pause.png new file mode 100644 index 0000000..b02cf90 Binary files /dev/null and b/res/drawable-xhdpi/ic_action_pause.png differ diff --git a/res/drawable-xhdpi/ic_action_play.png b/res/drawable-xhdpi/ic_action_play.png new file mode 100644 index 0000000..e01a844 Binary files /dev/null and b/res/drawable-xhdpi/ic_action_play.png differ diff --git a/res/drawable-xhdpi/ic_action_previous.png b/res/drawable-xhdpi/ic_action_previous.png new file mode 100644 index 0000000..f6eea80 Binary files /dev/null and b/res/drawable-xhdpi/ic_action_previous.png differ diff --git a/res/drawable-xhdpi/ic_action_repeat.png b/res/drawable-xhdpi/ic_action_repeat.png new file mode 100644 index 0000000..61dce04 Binary files /dev/null and b/res/drawable-xhdpi/ic_action_repeat.png differ diff --git a/res/drawable-xhdpi/ic_action_shuffle.png b/res/drawable-xhdpi/ic_action_shuffle.png new file mode 100644 index 0000000..b67295f Binary files /dev/null and b/res/drawable-xhdpi/ic_action_shuffle.png differ diff --git a/res/drawable-xhdpi/ic_media_next.png b/res/drawable-xhdpi/ic_media_next.png deleted file mode 100644 index 4def965..0000000 Binary files a/res/drawable-xhdpi/ic_media_next.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_media_pause.png b/res/drawable-xhdpi/ic_media_pause.png deleted file mode 100644 index 6bd3d48..0000000 Binary files a/res/drawable-xhdpi/ic_media_pause.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_media_play.png b/res/drawable-xhdpi/ic_media_play.png deleted file mode 100644 index ccfef18..0000000 Binary files a/res/drawable-xhdpi/ic_media_play.png and /dev/null differ diff --git a/res/drawable-xhdpi/ic_media_previous.png b/res/drawable-xhdpi/ic_media_previous.png deleted file mode 100644 index c4472ae..0000000 Binary files a/res/drawable-xhdpi/ic_media_previous.png and /dev/null differ diff --git a/res/drawable-xxhdpi/ic_action_next.png b/res/drawable-xxhdpi/ic_action_next.png new file mode 100644 index 0000000..3c93772 Binary files /dev/null and b/res/drawable-xxhdpi/ic_action_next.png differ diff --git a/res/drawable-xxhdpi/ic_action_pause.png b/res/drawable-xxhdpi/ic_action_pause.png new file mode 100644 index 0000000..293f712 Binary files /dev/null and b/res/drawable-xxhdpi/ic_action_pause.png differ diff --git a/res/drawable-xxhdpi/ic_action_play.png b/res/drawable-xxhdpi/ic_action_play.png new file mode 100644 index 0000000..97ff9b0 Binary files /dev/null and b/res/drawable-xxhdpi/ic_action_play.png differ diff --git a/res/drawable-xxhdpi/ic_action_previous.png b/res/drawable-xxhdpi/ic_action_previous.png new file mode 100644 index 0000000..99d6a79 Binary files /dev/null and b/res/drawable-xxhdpi/ic_action_previous.png differ diff --git a/res/drawable-xxhdpi/ic_action_repeat.png b/res/drawable-xxhdpi/ic_action_repeat.png new file mode 100644 index 0000000..c46ffa9 Binary files /dev/null and b/res/drawable-xxhdpi/ic_action_repeat.png differ diff --git a/res/drawable-xxhdpi/ic_action_shuffle.png b/res/drawable-xxhdpi/ic_action_shuffle.png new file mode 100644 index 0000000..a2e1785 Binary files /dev/null and b/res/drawable-xxhdpi/ic_action_shuffle.png differ diff --git a/res/layout/route_fragment.xml b/res/layout/route_fragment.xml index 9682fab..025820e 100644 --- a/res/layout/route_fragment.xml +++ b/res/layout/route_fragment.xml @@ -50,6 +50,13 @@ android:layout_alignParentTop="true" android:gravity="right" android:minEms="2" /> + + + + #BB99CC00 + #ff33b5e5 diff --git a/res/values/strings.xml b/res/values/strings.xml index 3dad884..6a359ea 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9,6 +9,8 @@ Do you really want to exit the renderer? Playback will be stopped. Previous Next + Shuffle + Repeat Please select a route Album Art DLNA Playback diff --git a/src/com/github/nutomic/controldlna/gui/RouteFragment.java b/src/com/github/nutomic/controldlna/gui/RouteFragment.java index aaa2db6..5ac8277 100644 --- a/src/com/github/nutomic/controldlna/gui/RouteFragment.java +++ b/src/com/github/nutomic/controldlna/gui/RouteFragment.java @@ -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,21 +372,47 @@ 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) diff --git a/src/com/github/nutomic/controldlna/mediarouter/MediaRouterPlayService.java b/src/com/github/nutomic/controldlna/mediarouter/MediaRouterPlayService.java index ee39d52..b05ffcc 100644 --- a/src/com/github/nutomic/controldlna/mediarouter/MediaRouterPlayService.java +++ b/src/com/github/nutomic/controldlna/mediarouter/MediaRouterPlayService.java @@ -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(); } }); } @@ -372,5 +405,21 @@ public class MediaRouterPlayService extends Service { public List getPlaylist() { return mPlaylist; } + + public void toggleShuffleEnabled() { + mShuffle = !mShuffle; + } + + public boolean getShuffleEnabled() { + return mShuffle; + } + + public void toggleRepeatEnabled() { + mRepeat = !mRepeat; + } + + public boolean getRepeatEnabled() { + return mRepeat; + } }