From 9d2b9d39a1b43dc26d5598dab801198adbe42009 Mon Sep 17 00:00:00 2001 From: Kurt Hardin Date: Wed, 29 Aug 2012 13:25:20 -0700 Subject: Added support for starring albums and songs. Requires server version 4.7 (currently in beta) for star support. Currently the Subsonic APIs don't provide adequate star support when listing artists. --- .../dsub/activity/DownloadActivity.java | 17 +++++++++ .../dsub/activity/SubsonicTabActivity.java | 42 ++++++++++++++++++++++ .../daneren2005/dsub/domain/MusicDirectory.java | 9 +++++ .../dsub/service/CachedMusicService.java | 5 +++ .../daneren2005/dsub/service/MusicService.java | 2 ++ .../dsub/service/OfflineMusicService.java | 5 +++ .../daneren2005/dsub/service/RESTMusicService.java | 11 ++++++ .../service/parser/MusicDirectoryEntryParser.java | 1 + .../github/daneren2005/dsub/util/AlbumView.java | 19 ++++++++++ .../src/github/daneren2005/dsub/util/SongView.java | 18 +++++++++- 10 files changed, 128 insertions(+), 1 deletion(-) (limited to 'subsonic-android/src/github') diff --git a/subsonic-android/src/github/daneren2005/dsub/activity/DownloadActivity.java b/subsonic-android/src/github/daneren2005/dsub/activity/DownloadActivity.java index a657b7eb..04d710d5 100644 --- a/subsonic-android/src/github/daneren2005/dsub/activity/DownloadActivity.java +++ b/subsonic-android/src/github/daneren2005/dsub/activity/DownloadActivity.java @@ -45,6 +45,7 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.WindowManager; import android.view.animation.AnimationUtils; @@ -106,6 +107,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi private Button visualizerButton; private Button jukeboxButton; private View toggleListButton; + private ImageButton starButton; private ScheduledExecutorService executorService; private DownloadFile currentPlaying; private long currentRevision; @@ -154,6 +156,19 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi LinearLayout visualizerViewLayout = (LinearLayout) findViewById(R.id.download_visualizer_view_layout); toggleListButton = findViewById(R.id.download_toggle_list); + + starButton = (ImageButton) findViewById(R.id.download_star); + starButton.setVisibility(Util.isOffline(this) ? View.GONE : View.VISIBLE); + starButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + DownloadFile currentDownload = getDownloadService().getCurrentPlaying(); + if (currentDownload != null) { + MusicDirectory.Entry currentSong = currentDownload.getSong(); + toggleStarredInBackground(currentSong, starButton); + } + } + }); View.OnTouchListener touchListener = new View.OnTouchListener() { @Override @@ -711,11 +726,13 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi albumTextView.setText(song.getAlbum()); artistTextView.setText(song.getArtist()); getImageLoader().loadImage(albumArtImageView, song, true, true); + starButton.setImageResource(song.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off); } else { songTitleTextView.setText(null); albumTextView.setText(null); artistTextView.setText(null); getImageLoader().loadImage(albumArtImageView, null, true, false); + starButton.setImageResource(android.R.drawable.btn_star_big_off); } } diff --git a/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicTabActivity.java b/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicTabActivity.java index 6afe842b..e3548302 100644 --- a/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicTabActivity.java +++ b/subsonic-android/src/github/daneren2005/dsub/activity/SubsonicTabActivity.java @@ -39,6 +39,7 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.Window; +import android.widget.ImageButton; import android.widget.TextView; import github.daneren2005.dsub.R; import github.daneren2005.dsub.domain.MusicDirectory; @@ -46,9 +47,12 @@ import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.service.DownloadServiceImpl; import github.daneren2005.dsub.service.MusicService; import github.daneren2005.dsub.service.MusicServiceFactory; +import github.daneren2005.dsub.service.OfflineException; +import github.daneren2005.dsub.service.ServerTooOldException; import github.daneren2005.dsub.util.Constants; import github.daneren2005.dsub.util.ImageLoader; import github.daneren2005.dsub.util.ModalBackgroundTask; +import github.daneren2005.dsub.util.SilentBackgroundTask; import github.daneren2005.dsub.util.Util; /** @@ -227,6 +231,44 @@ public class SubsonicTabActivity extends Activity { private void updateButtonVisibility() { int visibility = Util.isOffline(this) ? View.GONE : View.VISIBLE; } + + public void toggleStarredInBackground(final MusicDirectory.Entry entry, final ImageButton button) { + + final boolean starred = !entry.isStarred(); + + button.setImageResource(starred ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off); + entry.setStarred(starred); + + // Util.toast(SubsonicTabActivity.this, getResources().getString(R.string.starring_content, entry.getTitle())); + new SilentBackgroundTask(this) { + @Override + protected Void doInBackground() throws Throwable { + MusicService musicService = MusicServiceFactory.getMusicService(SubsonicTabActivity.this); + musicService.setStarred(entry.getId(), starred, SubsonicTabActivity.this, null); + return null; + } + + @Override + protected void done(Void result) { + // Util.toast(SubsonicTabActivity.this, getResources().getString(R.string.starring_content_done, entry.getTitle())); + } + + @Override + protected void error(Throwable error) { + button.setImageResource(!starred ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off); + entry.setStarred(!starred); + + String msg; + if (error instanceof OfflineException || error instanceof ServerTooOldException) { + msg = getErrorMessage(error); + } else { + msg = getResources().getString(R.string.starring_content_error, entry.getTitle()) + " " + getErrorMessage(error); + } + + Util.toast(SubsonicTabActivity.this, msg, false); + } + }.execute(); + } public void setProgressVisible(boolean visible) { View view = findViewById(R.id.tab_progress); diff --git a/subsonic-android/src/github/daneren2005/dsub/domain/MusicDirectory.java b/subsonic-android/src/github/daneren2005/dsub/domain/MusicDirectory.java index 420ce813..da0d8cbc 100644 --- a/subsonic-android/src/github/daneren2005/dsub/domain/MusicDirectory.java +++ b/subsonic-android/src/github/daneren2005/dsub/domain/MusicDirectory.java @@ -80,6 +80,7 @@ public class MusicDirectory { private Integer bitRate; private String path; private boolean video; + private boolean starred; public String getId() { return id; @@ -232,6 +233,14 @@ public class MusicDirectory { public void setVideo(boolean video) { this.video = video; } + + public boolean isStarred() { + return starred; + } + + public void setStarred(boolean starred) { + this.starred = starred; + } @Override public boolean equals(Object o) { diff --git a/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java index 094c959f..dbd421f2 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java @@ -222,6 +222,11 @@ public class CachedMusicService implements MusicService { public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception { return musicService.setJukeboxGain(gain, context, progressListener); } + + @Override + public void setStarred(String id, boolean starred, Context context, ProgressListener progressListener) throws Exception { + musicService.setStarred(id, starred, context, progressListener); + } private void checkSettingsChanged(Context context) { String newUrl = Util.getRestUrl(context, null); diff --git a/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java index e8fca8f0..a5045b18 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java @@ -88,4 +88,6 @@ public interface MusicService { JukeboxStatus getJukeboxStatus(Context context, ProgressListener progressListener) throws Exception; JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception; + + void setStarred(String id, boolean starred, Context context, ProgressListener progressListener) throws Exception; } \ No newline at end of file diff --git a/subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java index 9c49d550..29992029 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/OfflineMusicService.java @@ -308,6 +308,11 @@ public class OfflineMusicService extends RESTMusicService { public JukeboxStatus setJukeboxGain(float gain, Context context, ProgressListener progressListener) throws Exception { throw new OfflineException("Jukebox not available in offline mode"); } + + @Override + public void setStarred(String id, boolean starred, Context context, ProgressListener progressListener) throws Exception { + throw new OfflineException("Starring not available in offline mode"); + } @Override public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { diff --git a/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java index 748eacee..c24b2c06 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java @@ -602,6 +602,17 @@ public class RESTMusicService implements MusicService { Util.close(reader); } } + + @Override + public void setStarred(String id, boolean starred, Context context, ProgressListener progressListener) throws Exception { + checkServerVersion(context, "1.8", "Starring is not supported."); + Reader reader = getReader(context, progressListener, starred ? "star" : "unstar", null, "id", id); + try { + new ErrorParser(context).parse(reader); + } finally { + Util.close(reader); + } + } private Reader getReader(Context context, ProgressListener progressListener, String method, HttpParams requestParams) throws Exception { return getReader(context, progressListener, method, requestParams, Collections.emptyList(), Collections.emptyList()); diff --git a/subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java b/subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java index 3a1826e5..d724f63c 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/parser/MusicDirectoryEntryParser.java @@ -38,6 +38,7 @@ public class MusicDirectoryEntryParser extends AbstractParser { entry.setDirectory(getBoolean("isDir")); entry.setCoverArt(get("coverArt")); entry.setArtist(get("artist")); + entry.setStarred(get("starred") != null); if (!entry.isDirectory()) { entry.setAlbum(get("album")); diff --git a/subsonic-android/src/github/daneren2005/dsub/util/AlbumView.java b/subsonic-android/src/github/daneren2005/dsub/util/AlbumView.java index 98bd500f..8c799aad 100644 --- a/subsonic-android/src/github/daneren2005/dsub/util/AlbumView.java +++ b/subsonic-android/src/github/daneren2005/dsub/util/AlbumView.java @@ -21,9 +21,11 @@ package github.daneren2005.dsub.util; import android.content.Context; import android.view.LayoutInflater; import android.view.View; +import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.activity.SubsonicTabActivity; import github.daneren2005.dsub.domain.MusicDirectory; /** @@ -32,10 +34,13 @@ import github.daneren2005.dsub.domain.MusicDirectory; * @author Sindre Mehus */ public class AlbumView extends LinearLayout { + + private MusicDirectory.Entry album; private TextView titleView; private TextView artistView; private View coverArtView; + private ImageButton starButton; public AlbumView(Context context) { super(context); @@ -44,12 +49,26 @@ public class AlbumView extends LinearLayout { titleView = (TextView) findViewById(R.id.album_title); artistView = (TextView) findViewById(R.id.album_artist); coverArtView = findViewById(R.id.album_coverart); + starButton = (ImageButton) findViewById(R.id.album_star); + starButton.setVisibility(Util.isOffline(getContext()) ? View.GONE : View.VISIBLE); + starButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + SubsonicTabActivity activity = (SubsonicTabActivity) getContext(); + activity.toggleStarredInBackground(album, starButton); + } + }); } public void setAlbum(MusicDirectory.Entry album, ImageLoader imageLoader) { + this.album = album; + titleView.setText(album.getTitle()); artistView.setText(album.getArtist()); artistView.setVisibility(album.getArtist() == null ? View.GONE : View.VISIBLE); imageLoader.loadImage(coverArtView, album, false, true); + + starButton.setImageResource(album.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off); + starButton.setFocusable(false); } } diff --git a/subsonic-android/src/github/daneren2005/dsub/util/SongView.java b/subsonic-android/src/github/daneren2005/dsub/util/SongView.java index 61846c27..1bc2b9c0 100644 --- a/subsonic-android/src/github/daneren2005/dsub/util/SongView.java +++ b/subsonic-android/src/github/daneren2005/dsub/util/SongView.java @@ -25,9 +25,11 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.Checkable; import android.widget.CheckedTextView; +import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; import github.daneren2005.dsub.R; +import github.daneren2005.dsub.activity.SubsonicTabActivity; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.service.DownloadServiceImpl; @@ -46,13 +48,15 @@ public class SongView extends LinearLayout implements Checkable { private static final String TAG = SongView.class.getSimpleName(); private static final WeakHashMap INSTANCES = new WeakHashMap(); private static Handler handler; + + private MusicDirectory.Entry song; private CheckedTextView checkedTextView; private TextView titleTextView; private TextView artistTextView; private TextView durationTextView; private TextView statusTextView; - private MusicDirectory.Entry song; + private ImageButton starButton; public SongView(Context context) { super(context); @@ -63,6 +67,15 @@ public class SongView extends LinearLayout implements Checkable { artistTextView = (TextView) findViewById(R.id.song_artist); durationTextView = (TextView) findViewById(R.id.song_duration); statusTextView = (TextView) findViewById(R.id.song_status); + starButton = (ImageButton) findViewById(R.id.song_star); + starButton.setVisibility(Util.isOffline(getContext()) ? View.GONE : View.VISIBLE); + starButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + SubsonicTabActivity activity = (SubsonicTabActivity) getContext(); + activity.toggleStarredInBackground(song, starButton); + } + }); INSTANCES.put(this, null); int instanceCount = INSTANCES.size(); @@ -96,6 +109,9 @@ public class SongView extends LinearLayout implements Checkable { artistTextView.setText(artist); durationTextView.setText(Util.formatDuration(song.getDuration())); checkedTextView.setVisibility(checkable && !song.isVideo() ? View.VISIBLE : View.GONE); + + starButton.setImageResource(song.isStarred() ? android.R.drawable.btn_star_big_on : android.R.drawable.btn_star_big_off); + starButton.setFocusable(false); update(); } -- cgit v1.2.3 From 5df4f164d5bf2d983da92e18d6d7c2b66322fc42 Mon Sep 17 00:00:00 2001 From: Kurt Hardin Date: Fri, 7 Sep 2012 16:04:03 -0700 Subject: Moved RemoteControlClient code into Helper classes to isolate ICS specific code. Added call to unregister RemoteControlClient when service is destroyed. --- .../dsub/service/DownloadServiceImpl.java | 118 +++++++-------------- .../dsub/util/compat/RemoteControlClientBase.java | 32 ++++++ .../util/compat/RemoteControlClientHelper.java | 27 +++++ .../dsub/util/compat/RemoteControlClientICS.java | 70 ++++++++++++ 4 files changed, 170 insertions(+), 77 deletions(-) create mode 100644 subsonic-android/src/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java create mode 100644 subsonic-android/src/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java create mode 100644 subsonic-android/src/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java (limited to 'subsonic-android/src/github') diff --git a/subsonic-android/src/github/daneren2005/dsub/service/DownloadServiceImpl.java b/subsonic-android/src/github/daneren2005/dsub/service/DownloadServiceImpl.java index b7f6813e..128fe0f8 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/DownloadServiceImpl.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/DownloadServiceImpl.java @@ -18,22 +18,14 @@ */ package github.daneren2005.dsub.service; -import android.annotation.TargetApi; -import android.app.PendingIntent; -import android.app.Service; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.media.AudioManager; -import android.media.MediaMetadataRetriever; -import android.media.MediaPlayer; -import android.media.RemoteControlClient; -import android.os.Build; -import android.os.Handler; -import android.os.IBinder; -import android.os.PowerManager; -import android.util.Log; +import static github.daneren2005.dsub.domain.PlayerState.COMPLETED; +import static github.daneren2005.dsub.domain.PlayerState.DOWNLOADING; +import static github.daneren2005.dsub.domain.PlayerState.IDLE; +import static github.daneren2005.dsub.domain.PlayerState.PAUSED; +import static github.daneren2005.dsub.domain.PlayerState.PREPARED; +import static github.daneren2005.dsub.domain.PlayerState.PREPARING; +import static github.daneren2005.dsub.domain.PlayerState.STARTED; +import static github.daneren2005.dsub.domain.PlayerState.STOPPED; import github.daneren2005.dsub.audiofx.EqualizerController; import github.daneren2005.dsub.audiofx.VisualizerController; import github.daneren2005.dsub.domain.MusicDirectory; @@ -41,10 +33,12 @@ import github.daneren2005.dsub.domain.PlayerState; import github.daneren2005.dsub.domain.RepeatMode; import github.daneren2005.dsub.receiver.MediaButtonIntentReceiver; import github.daneren2005.dsub.util.CancellableTask; +import github.daneren2005.dsub.util.Constants; import github.daneren2005.dsub.util.LRUCache; import github.daneren2005.dsub.util.ShufflePlayBuffer; import github.daneren2005.dsub.util.SimpleServiceBinder; import github.daneren2005.dsub.util.Util; +import github.daneren2005.dsub.util.compat.RemoteControlClientHelper; import java.io.File; import java.util.ArrayList; @@ -52,8 +46,17 @@ import java.util.Collections; import java.util.Iterator; import java.util.List; -import static github.daneren2005.dsub.domain.PlayerState.*; -import github.daneren2005.dsub.util.*; +import android.app.Service; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.os.Handler; +import android.os.IBinder; +import android.os.PowerManager; +import android.util.Log; /** * @author Sindre Mehus @@ -71,8 +74,7 @@ public class DownloadServiceImpl extends Service implements DownloadService { public static final String CMD_NEXT = "github.daneren2005.dsub.CMD_NEXT"; - private RemoteControlClient mRemoteControlClient; - private ImageLoader imageLoader; + private RemoteControlClientHelper mRemoteControl; private final IBinder binder = new SimpleServiceBinder(this); private MediaPlayer mediaPlayer; @@ -121,12 +123,9 @@ public class DownloadServiceImpl extends Service implements DownloadService { } } - @TargetApi(14) @Override public void onCreate() { super.onCreate(); - - imageLoader = new ImageLoader(this); mediaPlayer = new MediaPlayer(); mediaPlayer.setWakeMode(this, PowerManager.PARTIAL_WAKE_LOCK); @@ -139,36 +138,15 @@ public class DownloadServiceImpl extends Service implements DownloadService { } }); - if (Build.VERSION.SDK_INT >= 14) { - - Util.requestAudioFocus(this); - Util.registerMediaButtonEventReceiver(this); - - // Use the remote control APIs (if available) to set the playback state - if (mRemoteControlClient == null) { - AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - ComponentName mediaButtonReceiverComponent = new ComponentName(getPackageName(), MediaButtonIntentReceiver.class.getName()); - // audioManager.registerMediaButtonEventReceiver(mediaButtonReceiverComponent); - // build the PendingIntent for the remote control client - Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); - mediaButtonIntent.setComponent(mediaButtonReceiverComponent); - PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0); - // create and register the remote control client - mRemoteControlClient = new RemoteControlClient(mediaPendingIntent); - audioManager.registerRemoteControlClient(mRemoteControlClient); - } - - mRemoteControlClient.setPlaybackState( - RemoteControlClient.PLAYSTATE_STOPPED); + Util.requestAudioFocus(this); + Util.registerMediaButtonEventReceiver(this); - mRemoteControlClient.setTransportControlFlags( - RemoteControlClient.FLAG_KEY_MEDIA_PLAY | - RemoteControlClient.FLAG_KEY_MEDIA_PAUSE | - RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE | - RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS | - RemoteControlClient.FLAG_KEY_MEDIA_NEXT | - RemoteControlClient.FLAG_KEY_MEDIA_STOP); - } + if (mRemoteControl == null) { + // Use the remote control APIs (if available) to set the playback state + mRemoteControl = RemoteControlClientHelper.createInstance(); + ComponentName mediaButtonReceiverComponent = new ComponentName(getPackageName(), MediaButtonIntentReceiver.class.getName()); + mRemoteControl.register(this, mediaButtonReceiverComponent); + } if (equalizerAvailable) { equalizerController = new EqualizerController(this, mediaPlayer); @@ -194,12 +172,13 @@ public class DownloadServiceImpl extends Service implements DownloadService { } @Override - public void onStart(Intent intent, int startId) { - super.onStart(intent, startId); + public int onStartCommand(Intent intent, int flags, int startId) { + super.onStartCommand(intent, flags, startId); lifecycleSupport.onStart(intent); + return START_NOT_STICKY; } - @Override + @Override public void onDestroy() { super.onDestroy(); lifecycleSupport.onDestroy(); @@ -211,6 +190,10 @@ public class DownloadServiceImpl extends Service implements DownloadService { if (visualizerController != null) { visualizerController.release(); } + if (mRemoteControl != null) { + mRemoteControl.unregister(this); + mRemoteControl = null; + } instance = null; } @@ -440,7 +423,6 @@ public class DownloadServiceImpl extends Service implements DownloadService { } } - @TargetApi(14) synchronized void setCurrentPlaying(DownloadFile currentPlaying, boolean showNotification) { this.currentPlaying = currentPlaying; @@ -457,23 +439,8 @@ public class DownloadServiceImpl extends Service implements DownloadService { Util.hidePlayingNotification(this, this, handler); } - if (mRemoteControlClient != null) { - MusicDirectory.Entry currentSong = currentPlaying == null ? null: currentPlaying.getSong(); - // Update the remote controls - mRemoteControlClient.editMetadata(true) - .putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, currentSong == null ? null : currentSong.getArtist()) - .putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, currentSong == null ? null : currentSong.getAlbum()) - .putString(MediaMetadataRetriever.METADATA_KEY_TITLE, currentSong == null ? null : currentSong.getTitle()) - .putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, currentSong == null ? 0 : currentSong.getDuration()) - .apply(); - if (currentSong == null) { - mRemoteControlClient.editMetadata(true) - .putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, null) - .apply(); - } else { - imageLoader.loadImage(this, mRemoteControlClient, currentSong); - } - } + MusicDirectory.Entry currentSong = currentPlaying == null ? null: currentPlaying.getSong(); + mRemoteControl.updateMetadata(this, currentSong); } @Override @@ -683,7 +650,6 @@ public class DownloadServiceImpl extends Service implements DownloadService { return playerState; } - @TargetApi(14) synchronized void setPlayerState(PlayerState playerState) { Log.i(TAG, this.playerState.name() + " -> " + playerState.name() + " (" + currentPlaying + ")"); @@ -696,9 +662,7 @@ public class DownloadServiceImpl extends Service implements DownloadService { Util.broadcastPlaybackStatusChange(this, playerState); this.playerState = playerState; - if (mRemoteControlClient != null) { - mRemoteControlClient.setPlaybackState(playerState.getRemoteControlClientPlayState()); - } + mRemoteControl.setPlaybackState(playerState.getRemoteControlClientPlayState()); if (show) { Util.showPlayingNotification(this, this, handler, currentPlaying.getSong()); diff --git a/subsonic-android/src/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java b/subsonic-android/src/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java new file mode 100644 index 00000000..c3f3f70c --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java @@ -0,0 +1,32 @@ +package github.daneren2005.dsub.util.compat; + +import github.daneren2005.dsub.domain.MusicDirectory.Entry; +import android.content.ComponentName; +import android.content.Context; +import android.util.Log; + +public class RemoteControlClientBase extends RemoteControlClientHelper { + + private static final String TAG = RemoteControlClientBase.class.getSimpleName(); + + @Override + public void register(Context context, ComponentName mediaButtonReceiverComponent) { + Log.w(TAG, "RemoteControlClient requires Android API level 14 or higher."); + } + + @Override + public void unregister(Context context) { + Log.w(TAG, "RemoteControlClient requires Android API level 14 or higher."); + } + + @Override + public void setPlaybackState(int state) { + Log.w(TAG, "RemoteControlClient requires Android API level 14 or higher."); + } + + @Override + public void updateMetadata(Context context, Entry currentSong) { + Log.w(TAG, "RemoteControlClient requires Android API level 14 or higher."); + } + +} diff --git a/subsonic-android/src/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java b/subsonic-android/src/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java new file mode 100644 index 00000000..ddaa9f43 --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java @@ -0,0 +1,27 @@ +package github.daneren2005.dsub.util.compat; + +import github.daneren2005.dsub.domain.MusicDirectory; +import android.content.ComponentName; +import android.content.Context; +import android.os.Build; + +public abstract class RemoteControlClientHelper { + + public static RemoteControlClientHelper createInstance() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + return new RemoteControlClientBase(); + } else { + return new RemoteControlClientICS(); + } + } + + protected RemoteControlClientHelper() { + // Avoid instantiation + } + + public abstract void register(final Context context, final ComponentName mediaButtonReceiverComponent); + public abstract void unregister(final Context context); + public abstract void setPlaybackState(final int state); + public abstract void updateMetadata(final Context context, final MusicDirectory.Entry currentSong); + +} diff --git a/subsonic-android/src/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java b/subsonic-android/src/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java new file mode 100644 index 00000000..98c52694 --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java @@ -0,0 +1,70 @@ +package github.daneren2005.dsub.util.compat; + +import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.util.ImageLoader; +import android.annotation.TargetApi; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.media.AudioManager; +import android.media.MediaMetadataRetriever; +import android.media.RemoteControlClient; + +@TargetApi(14) +public class RemoteControlClientICS extends RemoteControlClientHelper { + + private RemoteControlClient mRemoteControl; + + public void register(final Context context, final ComponentName mediaButtonReceiverComponent) { + AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + + // build the PendingIntent for the remote control client + Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); + mediaButtonIntent.setComponent(mediaButtonReceiverComponent); + PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 0, mediaButtonIntent, 0); + + // create and register the remote control client + mRemoteControl = new RemoteControlClient(mediaPendingIntent); + audioManager.registerRemoteControlClient(mRemoteControl); + + mRemoteControl.setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED); + + mRemoteControl.setTransportControlFlags( + RemoteControlClient.FLAG_KEY_MEDIA_PLAY | + RemoteControlClient.FLAG_KEY_MEDIA_PAUSE | + RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE | + RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS | + RemoteControlClient.FLAG_KEY_MEDIA_NEXT | + RemoteControlClient.FLAG_KEY_MEDIA_STOP); + } + + public void unregister(final Context context) { + if (mRemoteControl != null) { + AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + audioManager.unregisterRemoteControlClient(mRemoteControl); + } + } + + public void setPlaybackState(final int state) { + mRemoteControl.setPlaybackState(state); + } + + public void updateMetadata(final Context context, final MusicDirectory.Entry currentSong) { + // Update the remote controls + mRemoteControl.editMetadata(true) + .putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, currentSong == null ? null : currentSong.getArtist()) + .putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, currentSong == null ? null : currentSong.getAlbum()) + .putString(MediaMetadataRetriever.METADATA_KEY_TITLE, currentSong == null ? null : currentSong.getTitle()) + .putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, currentSong == null ? 0 : currentSong.getDuration()) + .apply(); + if (currentSong == null) { + mRemoteControl.editMetadata(true) + .putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, null) + .apply(); + } else { + new ImageLoader(context).loadImage(context, mRemoteControl, currentSong); + } + } + +} -- cgit v1.2.3 From 72c741b2f3dd35c68c8a81f7d786de673e94f434 Mon Sep 17 00:00:00 2001 From: Kurt Hardin Date: Tue, 25 Sep 2012 09:39:52 -0700 Subject: Added 'Starred' section to MainActivity. --- subsonic-android/res/layout/main_buttons.xml | 12 ++++++++++++ subsonic-android/res/values/strings.xml | 1 + .../src/github/daneren2005/dsub/activity/MainActivity.java | 5 ++++- 3 files changed, 17 insertions(+), 1 deletion(-) (limited to 'subsonic-android/src/github') diff --git a/subsonic-android/res/layout/main_buttons.xml b/subsonic-android/res/layout/main_buttons.xml index 969ad99e..a532c233 100644 --- a/subsonic-android/res/layout/main_buttons.xml +++ b/subsonic-android/res/layout/main_buttons.xml @@ -102,6 +102,18 @@ android:paddingLeft="6dip" android:paddingRight="6dip" android:minHeight="50dip"/> + Recently played Most played Top rated + Starred Random Exit diff --git a/subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java b/subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java index e5958e57..ff5c29c1 100644 --- a/subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java +++ b/subsonic-android/src/github/daneren2005/dsub/activity/MainActivity.java @@ -79,6 +79,7 @@ public class MainActivity extends SubsonicTabActivity { final View albumsHighestButton = buttons.findViewById(R.id.main_albums_highest); final View albumsRecentButton = buttons.findViewById(R.id.main_albums_recent); final View albumsFrequentButton = buttons.findViewById(R.id.main_albums_frequent); + final View albumsStarredButton = buttons.findViewById(R.id.main_albums_starred); final View dummyView = findViewById(R.id.main_dummy); @@ -92,7 +93,7 @@ public class MainActivity extends SubsonicTabActivity { adapter.addViews(Arrays.asList(serverButton), true); if (!Util.isOffline(this)) { adapter.addView(albumsTitle, false); - adapter.addViews(Arrays.asList(albumsNewestButton, albumsRandomButton, albumsHighestButton, albumsRecentButton, albumsFrequentButton), true); + adapter.addViews(Arrays.asList(albumsNewestButton, albumsRandomButton, albumsHighestButton, albumsStarredButton, albumsRecentButton, albumsFrequentButton), true); } list.setAdapter(adapter); registerForContextMenu(dummyView); @@ -108,6 +109,8 @@ public class MainActivity extends SubsonicTabActivity { showAlbumList("random"); } else if (view == albumsHighestButton) { showAlbumList("highest"); + } else if (view == albumsStarredButton) { + showAlbumList("starred"); } else if (view == albumsRecentButton) { showAlbumList("recent"); } else if (view == albumsFrequentButton) { -- cgit v1.2.3 From 50cbd5040bfecfbad8b46694be17e6b793094c43 Mon Sep 17 00:00:00 2001 From: Kurt Hardin Date: Tue, 25 Sep 2012 11:46:13 -0700 Subject: Added star support features for original UI. --- .../dsub/activity/SelectAlbumActivity.java | 8 ++- .../dsub/service/CachedMusicService.java | 5 ++ .../daneren2005/dsub/service/MusicService.java | 2 + .../daneren2005/dsub/service/RESTMusicService.java | 11 ++++ .../dsub/service/parser/StarredListParser.java | 62 ++++++++++++++++++++++ 5 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 subsonic-android/src/github/daneren2005/dsub/service/parser/StarredListParser.java (limited to 'subsonic-android/src/github') diff --git a/subsonic-android/src/github/daneren2005/dsub/activity/SelectAlbumActivity.java b/subsonic-android/src/github/daneren2005/dsub/activity/SelectAlbumActivity.java index 108dcf9c..8a473bc7 100644 --- a/subsonic-android/src/github/daneren2005/dsub/activity/SelectAlbumActivity.java +++ b/subsonic-android/src/github/daneren2005/dsub/activity/SelectAlbumActivity.java @@ -336,7 +336,13 @@ public class SelectAlbumActivity extends SubsonicTabActivity { new LoadTask() { @Override protected MusicDirectory load(MusicService service) throws Exception { - return service.getAlbumList(albumListType, size, offset, SelectAlbumActivity.this, this); + MusicDirectory result; + if ("starred".equals(albumListType)) { + result = service.getStarredList(SelectAlbumActivity.this, this); + } else { + result = service.getAlbumList(albumListType, size, offset, SelectAlbumActivity.this, this); + } + return result; } @Override diff --git a/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java index dbd421f2..832b2037 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/CachedMusicService.java @@ -163,6 +163,11 @@ public class CachedMusicService implements MusicService { return musicService.getAlbumList(type, size, offset, context, progressListener); } + @Override + public MusicDirectory getStarredList(Context context, ProgressListener progressListener) throws Exception { + return musicService.getStarredList(context, progressListener); + } + @Override public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { return musicService.getRandomSongs(size, context, progressListener); diff --git a/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java index a5045b18..15abbdf8 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/MusicService.java @@ -53,6 +53,8 @@ public interface MusicService { SearchResult search(SearchCritera criteria, Context context, ProgressListener progressListener) throws Exception; + MusicDirectory getStarredList(Context context, ProgressListener progressListener) throws Exception; + MusicDirectory getPlaylist(String id, String name, Context context, ProgressListener progressListener) throws Exception; List getPlaylists(boolean refresh, Context context, ProgressListener progressListener) throws Exception; diff --git a/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java b/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java index c24b2c06..1ef65bb8 100644 --- a/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java +++ b/subsonic-android/src/github/daneren2005/dsub/service/RESTMusicService.java @@ -93,6 +93,7 @@ import github.daneren2005.dsub.service.parser.PlaylistsParser; import github.daneren2005.dsub.service.parser.RandomSongsParser; import github.daneren2005.dsub.service.parser.SearchResult2Parser; import github.daneren2005.dsub.service.parser.SearchResultParser; +import github.daneren2005.dsub.service.parser.StarredListParser; import github.daneren2005.dsub.service.parser.VersionParser; import github.daneren2005.dsub.service.ssl.SSLSocketFactory; import github.daneren2005.dsub.service.ssl.TrustSelfSignedStrategy; @@ -414,6 +415,16 @@ public class RESTMusicService implements MusicService { } } + @Override + public MusicDirectory getStarredList(Context context, ProgressListener progressListener) throws Exception { + Reader reader = getReader(context, progressListener, "getStarred", null); + try { + return new StarredListParser(context).parse(reader, progressListener); + } finally { + Util.close(reader); + } + } + @Override public MusicDirectory getRandomSongs(int size, Context context, ProgressListener progressListener) throws Exception { HttpParams params = new BasicHttpParams(); diff --git a/subsonic-android/src/github/daneren2005/dsub/service/parser/StarredListParser.java b/subsonic-android/src/github/daneren2005/dsub/service/parser/StarredListParser.java new file mode 100644 index 00000000..c3c16949 --- /dev/null +++ b/subsonic-android/src/github/daneren2005/dsub/service/parser/StarredListParser.java @@ -0,0 +1,62 @@ +/* + This file is part of Subsonic. + + Subsonic 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. + + Subsonic 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 Subsonic. If not, see . + + Copyright 2009 (C) Sindre Mehus + */ +package github.daneren2005.dsub.service.parser; + +import android.content.Context; +import github.daneren2005.dsub.R; +import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.util.ProgressListener; +import org.xmlpull.v1.XmlPullParser; + +import java.io.Reader; + +/** + * @author Kurt Hardin + */ +public class StarredListParser extends MusicDirectoryEntryParser { + + public StarredListParser(Context context) { + super(context); + } + + public MusicDirectory parse(Reader reader, ProgressListener progressListener) throws Exception { + + updateProgress(progressListener, R.string.parser_reading); + init(reader); + + MusicDirectory dir = new MusicDirectory(); + int eventType; + do { + eventType = nextParseEvent(); + if (eventType == XmlPullParser.START_TAG) { + String name = getElementName(); + if ("album".equals(name) || "song".equals(name)) { + dir.addChild(parseEntry()); + } else if ("error".equals(name)) { + handleError(); + } + } + } while (eventType != XmlPullParser.END_DOCUMENT); + + validate(); + updateProgress(progressListener, R.string.parser_reading_done); + + return dir; + } +} \ No newline at end of file -- cgit v1.2.3