diff options
author | Kurt Hardin <kurthardin.dev@gmail.com> | 2012-08-29 13:25:20 -0700 |
---|---|---|
committer | Kurt Hardin <kurthardin.dev@gmail.com> | 2012-08-29 13:25:20 -0700 |
commit | 9d2b9d39a1b43dc26d5598dab801198adbe42009 (patch) | |
tree | eba153912e3f1821597916ef7e2896154bc349b2 | |
parent | d0e1ef7dca6fefa2815c1e633901cd369f938b63 (diff) | |
download | dsub-9d2b9d39a1b43dc26d5598dab801198adbe42009.tar.gz dsub-9d2b9d39a1b43dc26d5598dab801198adbe42009.tar.bz2 dsub-9d2b9d39a1b43dc26d5598dab801198adbe42009.zip |
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.
15 files changed, 170 insertions, 5 deletions
diff --git a/subsonic-android/res/layout-land/download.xml b/subsonic-android/res/layout-land/download.xml index 01a9b68d..b0303e52 100644 --- a/subsonic-android/res/layout-land/download.xml +++ b/subsonic-android/res/layout-land/download.xml @@ -14,15 +14,15 @@ android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentRight="true"
- android:background="@color/mediaControlBackground"
- >
+ android:background="@color/mediaControlBackground">
<LinearLayout
+ android:id="@+id/download_play_controls_layout"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true">
+ android:layout_centerHorizontal="true">
<ImageButton
android:id="@+id/download_previous"
@@ -87,7 +87,6 @@ android:id="@+id/download_song_title"
android:layout_width="150dip"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
android:layout_above="@+id/download_status"
android:layout_centerHorizontal="true"
android:layout_marginLeft="12dip"
@@ -99,6 +98,7 @@ android:textColor="@color/mediaControlForeground"/>
<LinearLayout
+ android:id="@+id/download_other_controls_layout"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -111,6 +111,7 @@ android:background="@android:color/transparent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_below="@+id/download_play_controls_layout"
android:padding="9dip"/>
<ImageButton
android:id="@+id/download_repeat"
@@ -120,6 +121,13 @@ android:layout_height="wrap_content"
android:padding="9dip"/>
<ImageButton
+ android:id="@+id/download_star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:background="@null"
+ android:src="@android:drawable/star_big_off"/>
+ <ImageButton
android:id="@+id/download_toggle_list"
android:src="@drawable/media_toggle_list"
android:background="@android:color/transparent"
diff --git a/subsonic-android/res/layout-port/download.xml b/subsonic-android/res/layout-port/download.xml index e2a198a2..e6a2358a 100644 --- a/subsonic-android/res/layout-port/download.xml +++ b/subsonic-android/res/layout-port/download.xml @@ -72,6 +72,15 @@ android:paddingTop="12dip"
android:paddingRight="12dip"
android:paddingBottom="12dip"/>
+
+ <ImageButton
+ android:id="@+id/download_star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignRight="@+id/download_repeat"
+ android:layout_below="@+id/download_jukebox"
+ android:background="@null"
+ android:src="@android:drawable/star_big_off"/>
<ImageView
android:id="@+id/download_album_art_image"
diff --git a/subsonic-android/res/layout/album_list_item.xml b/subsonic-android/res/layout/album_list_item.xml index 15d999c3..aa2ac803 100644 --- a/subsonic-android/res/layout/album_list_item.xml +++ b/subsonic-android/res/layout/album_list_item.xml @@ -36,6 +36,15 @@ android:singleLine="true"/>
</LinearLayout>
+
+ <ImageButton
+ android:id="@+id/album_star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:background="@null"
+ android:focusable="false"
+ android:src="@android:drawable/star_big_off" />
<ImageView
android:src="@drawable/list_item_artist"
diff --git a/subsonic-android/res/layout/song_list_item.xml b/subsonic-android/res/layout/song_list_item.xml index 2163ef72..87f56a22 100644 --- a/subsonic-android/res/layout/song_list_item.xml +++ b/subsonic-android/res/layout/song_list_item.xml @@ -73,4 +73,14 @@ </LinearLayout>
</LinearLayout>
+
+ <ImageButton
+ android:id="@+id/song_star"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="right|center_vertical"
+ android:background="@null"
+ android:focusable="false"
+ android:src="@android:drawable/btn_star_big_off" />
+
</LinearLayout>
diff --git a/subsonic-android/res/values/strings.xml b/subsonic-android/res/values/strings.xml index 7f9f6728..eef510e4 100644 --- a/subsonic-android/res/values/strings.xml +++ b/subsonic-android/res/values/strings.xml @@ -110,6 +110,8 @@ <string name="download.jukebox_server_too_old">Remote control is not supported. Please upgrade your Subsonic server.</string>
<string name="download.jukebox_offline">Remote control is not available in offline mode.</string>
<string name="download.jukebox_not_authorized">Remote control is not allowed. Please enable jukebox mode in <b>Users > Settings</b> on your Subsonic server.</string>
+
+ <string name="starring_content_error">Failed to update \"%s\", please try later.</string>
<string name="song_details.all">%1$s %2$s</string>
<string name="song_details.kbps">%d kbps</string>
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<Void>(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.<String>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<SongView, ?> INSTANCES = new WeakHashMap<SongView, Object>(); 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(); } |