From 3da246de2ca75afaa03985a8a5a73c12bf1995c4 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Fri, 9 Oct 2015 10:01:32 -0700 Subject: Start of Auto support --- app/src/main/AndroidManifest.xml | 16 +- .../dsub/service/AutoMediaBrowserService.java | 94 ++++++++++ .../daneren2005/dsub/service/DownloadService.java | 10 +- .../dsub/util/compat/RemoteControlClientBase.java | 59 +++--- .../util/compat/RemoteControlClientHelper.java | 32 ---- .../dsub/util/compat/RemoteControlClientICS.java | 2 +- .../dsub/util/compat/RemoteControlClientLP.java | 205 +++++++++++++++++++++ app/src/main/res/xml/auto_app_description.xml | 4 + 8 files changed, 350 insertions(+), 72 deletions(-) create mode 100644 app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java delete mode 100644 app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java create mode 100644 app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java create mode 100644 app/src/main/res/xml/auto_app_description.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 152375a4..efaf4ecc 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -90,6 +90,15 @@ + + + + + + + + @@ -241,6 +250,11 @@ - + + + + diff --git a/app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java b/app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java new file mode 100644 index 00000000..4d21d793 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java @@ -0,0 +1,94 @@ +/* + 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 2015 (C) Scott Jackson +*/ +package github.daneren2005.dsub.service; + +import android.annotation.TargetApi; +import android.content.Intent; +import android.media.MediaDescription; +import android.media.browse.MediaBrowser; +import android.media.session.MediaSession; +import android.os.Build; +import android.os.Bundle; +import android.service.media.MediaBrowserService; +import android.support.annotation.Nullable; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +import github.daneren2005.dsub.util.Util; +import github.daneren2005.dsub.util.compat.RemoteControlClientLP; + +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +public class AutoMediaBrowserService extends MediaBrowserService { + private static final String TAG = AutoMediaBrowserService.class.getSimpleName(); + private static final String BROWSER_ROOT = "root"; + MediaSession mediaSession; + + @Override + public void onCreate() { + super.onCreate(); + + DownloadService downloadService = getDownloadService(); + RemoteControlClientLP remoteControlClient = (RemoteControlClientLP) downloadService.getRemoteControlClient(); + setSessionToken(remoteControlClient.getMediaSession().getSessionToken()); + } + + @Nullable + @Override + public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) { + BrowserRoot root = new BrowserRoot(BROWSER_ROOT, null); + return root; + } + + @Override + public void onLoadChildren(String parentId, Result> result) { + if(BROWSER_ROOT.equals(parentId)) { + getRootFolders(result); + } else { + + } + } + + private void getRootFolders(Result> result) { + List mediaItems = new ArrayList<>(); + + MediaDescription.Builder library = new MediaDescription.Builder(); + library.setDescription("Library") + .setMediaId("library"); + mediaItems.add(new MediaBrowser.MediaItem(library.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE)); + + result.sendResult(mediaItems); + } + + public DownloadService getDownloadService() { + // If service is not available, request it to start and wait for it. + for (int i = 0; i < 5; i++) { + DownloadService downloadService = DownloadService.getInstance(); + if (downloadService != null) { + break; + } + Log.w(TAG, "DownloadService not running. Attempting to start it."); + startService(new Intent(this, DownloadService.class)); + Util.sleepQuietly(50L); + } + + return DownloadService.getInstance(); + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java index 7af21831..6dedca29 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java +++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java @@ -49,7 +49,7 @@ import github.daneren2005.dsub.util.MediaRouteManager; 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 github.daneren2005.dsub.util.compat.RemoteControlClientBase; import github.daneren2005.dsub.util.tags.BastpUtil; import github.daneren2005.dsub.view.UpdateView; import github.daneren2005.serverproxy.BufferProxy; @@ -106,7 +106,7 @@ public class DownloadService extends Service { private static final int SHUFFLE_MODE_ALL = 1; private static final int SHUFFLE_MODE_ARTIST = 2; - private RemoteControlClientHelper mRemoteControl; + private RemoteControlClientBase mRemoteControl; private final IBinder binder = new SimpleServiceBinder(this); private Looper mediaPlayerLooper; @@ -238,7 +238,7 @@ public class DownloadService extends Service { if (mRemoteControl == null) { // Use the remote control APIs (if available) to set the playback state - mRemoteControl = RemoteControlClientHelper.createInstance(); + mRemoteControl = RemoteControlClientBase.createInstance(); ComponentName mediaButtonReceiverComponent = new ComponentName(getPackageName(), MediaButtonIntentReceiver.class.getName()); mRemoteControl.register(this, mediaButtonReceiverComponent); } @@ -2232,6 +2232,10 @@ public class DownloadService extends Service { } clearCurrentBookmark(downloadFile.getSong(), true); } + + public RemoteControlClientBase getRemoteControlClient() { + return mRemoteControl; + } private boolean isPastCutoff() { return isPastCutoff(getPlayerPosition(), getPlayerDuration()); diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java index 320092e9..357e2585 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java @@ -1,43 +1,32 @@ package github.daneren2005.dsub.util.compat; -import github.daneren2005.dsub.domain.MusicDirectory.Entry; +import github.daneren2005.dsub.domain.MusicDirectory; import android.content.ComponentName; import android.content.Context; import android.support.v7.media.MediaRouter; -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) { - - } - - @Override - public void unregister(Context context) { - +import android.os.Build; + +public abstract class RemoteControlClientBase { + + public static RemoteControlClientBase createInstance() { + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return new RemoteControlClientLP(); + } else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + return new RemoteControlClientJB(); + } else { + return new RemoteControlClientICS(); + } } - - @Override - public void setPlaybackState(int state) { - + + protected RemoteControlClientBase() { + // Avoid instantiation } - - @Override - public void updateMetadata(Context context, Entry currentSong) { - - } - - @Override - public void registerRoute(MediaRouter router) { - - } - - @Override - public void unregisterRoute(MediaRouter router) { - - } - + + 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); + public abstract void registerRoute(MediaRouter router); + public abstract void unregisterRoute(MediaRouter router); + } diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java deleted file mode 100644 index 93075a28..00000000 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java +++ /dev/null @@ -1,32 +0,0 @@ -package github.daneren2005.dsub.util.compat; - -import github.daneren2005.dsub.domain.MusicDirectory; -import android.content.ComponentName; -import android.content.Context; -import android.support.v7.media.MediaRouter; -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 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { - return new RemoteControlClientJB(); - } 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); - public abstract void registerRoute(MediaRouter router); - public abstract void unregisterRoute(MediaRouter router); - -} diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java index 46b6b47d..9b4725bd 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java @@ -16,7 +16,7 @@ import android.support.v7.media.MediaRouter; import github.daneren2005.dsub.activity.SubsonicActivity; @TargetApi(14) -public class RemoteControlClientICS extends RemoteControlClientHelper { +public class RemoteControlClientICS extends RemoteControlClientBase { private static String TAG = RemoteControlClientICS.class.getSimpleName(); protected RemoteControlClient mRemoteControl; diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java new file mode 100644 index 00000000..2cf2dd20 --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java @@ -0,0 +1,205 @@ +/* + 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 2015 (C) Scott Jackson +*/ +package github.daneren2005.dsub.util.compat; + +import android.annotation.TargetApi; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.media.AudioAttributes; +import android.media.MediaMetadata; +import android.media.Rating; +import android.media.RemoteControlClient; +import android.media.session.MediaSession; +import android.media.session.PlaybackState; +import android.os.Build; +import android.support.v7.media.MediaRouter; + +import github.daneren2005.dsub.activity.SubsonicFragmentActivity; +import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.service.DownloadService; +import github.daneren2005.dsub.util.Constants; + +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +public class RemoteControlClientLP extends RemoteControlClientBase { + private MediaSession mediaSession; + private DownloadService downloadService; + + private PlaybackState previousState; + + @Override + public void register(Context context, ComponentName mediaButtonReceiverComponent) { + downloadService = (DownloadService) context; + mediaSession = new MediaSession(downloadService, "DSub MediaSession"); + + Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); + mediaButtonIntent.setComponent(mediaButtonReceiverComponent); + PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), 0, mediaButtonIntent, 0); + mediaSession.setMediaButtonReceiver(mediaPendingIntent); + + Intent activityIntent = new Intent(context, SubsonicFragmentActivity.class); + activityIntent.putExtra(Constants.INTENT_EXTRA_NAME_DOWNLOAD, true); + activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + PendingIntent activityPendingIntent = PendingIntent.getActivity(context, 0, activityIntent, 0); + mediaSession.setSessionActivity(activityPendingIntent); + + mediaSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS); + mediaSession.setCallback(new EventCallback()); + + if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { + mediaSession.setRatingType(Rating.RATING_THUMB_UP_DOWN); + } + + AudioAttributes.Builder audioAttributesBuilder = new AudioAttributes.Builder(); + audioAttributesBuilder.setUsage(AudioAttributes.USAGE_MEDIA) + .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC); + mediaSession.setPlaybackToLocal(audioAttributesBuilder.build()); + mediaSession.setActive(true); + } + + @Override + public void unregister(Context context) { + mediaSession.release(); + } + + @Override + public void setPlaybackState(int state) { + PlaybackState.Builder builder; + if(previousState == null) { + builder = new PlaybackState.Builder(); + } else { + builder = new PlaybackState.Builder(previousState); + } + + int newState = PlaybackState.STATE_NONE; + switch(state) { + case RemoteControlClient.PLAYSTATE_PLAYING: + newState = PlaybackState.STATE_PLAYING; + break; + case RemoteControlClient.PLAYSTATE_STOPPED: + newState = PlaybackState.STATE_STOPPED; + break; + case RemoteControlClient.PLAYSTATE_PAUSED: + newState = PlaybackState.STATE_PAUSED; + break; + case RemoteControlClient.PLAYSTATE_BUFFERING: + newState = PlaybackState.STATE_BUFFERING; + break; + } + + long position = -1; + if(state == RemoteControlClient.PLAYSTATE_PLAYING || state == RemoteControlClient.PLAYSTATE_PAUSED) { + position = downloadService.getPlayerPosition(); + } + builder.setState(newState, position, 1.0f); + builder.setActions(getPlaybackActions()); + + PlaybackState playbackState = builder.build(); + mediaSession.setPlaybackState(playbackState); + previousState = playbackState; + } + + @Override + public void updateMetadata(Context context, MusicDirectory.Entry currentSong) { + MediaMetadata.Builder builder = new MediaMetadata.Builder(); + builder.putString(MediaMetadata.METADATA_KEY_ARTIST, (currentSong == null) ? null : currentSong.getArtist()) + .putString(MediaMetadata.METADATA_KEY_ALBUM, (currentSong == null) ? null : currentSong.getAlbum()) + .putString(MediaMetadata.METADATA_KEY_ALBUM_ARTIST, (currentSong == null) ? null : currentSong.getArtist()) + .putString(MediaMetadata.METADATA_KEY_TITLE, (currentSong) == null ? null : currentSong.getTitle()) + .putString(MediaMetadata.METADATA_KEY_GENRE, (currentSong) == null ? null : currentSong.getGenre()) + .putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, (currentSong == null) ? + 0 : ((currentSong.getTrack() == null) ? 0 : currentSong.getTrack())) + .putLong(MediaMetadata.METADATA_KEY_DURATION, (currentSong == null) ? + 0 : ((currentSong.getDuration() == null) ? 0 : (currentSong.getDuration() * 1000))); + + int userRating = currentSong.getRating(); + Rating rating; + if(userRating == 1) { + rating = Rating.newThumbRating(false); + } else if(userRating == 5) { + rating = Rating.newThumbRating(true); + } else { + rating = Rating.newUnratedRating(Rating.RATING_THUMB_UP_DOWN); + } + builder.putRating(MediaMetadata.METADATA_KEY_USER_RATING, rating); + + mediaSession.setMetadata(builder.build()); + } + + @Override + public void registerRoute(MediaRouter router) { + router.setMediaSession(mediaSession); + } + + @Override + public void unregisterRoute(MediaRouter router) { + router.setMediaSession(null); + } + + public MediaSession getMediaSession() { + return mediaSession; + } + + protected long getPlaybackActions() { + return PlaybackState.ACTION_PLAY | + PlaybackState.ACTION_PAUSE | + PlaybackState.ACTION_SEEK_TO | + PlaybackState.ACTION_SET_RATING; + } + + private class EventCallback extends MediaSession.Callback { + @Override + public void onPlay() { + downloadService.start(); + } + + @Override + public void onStop() { + downloadService.pause(); + } + + @Override + public void onPause() { + downloadService.pause(); + } + + @Override + public void onSeekTo(long position) { + downloadService.seekTo((int) position); + } + + @Override + public void onSetRating(Rating rating) { + if(rating.getRatingStyle() != Rating.RATING_THUMB_UP_DOWN) { + return; + } + + if(rating.isRated()) { + if(rating.isThumbUp()) { + + } else { + + } + } else { + + } + } + } +} diff --git a/app/src/main/res/xml/auto_app_description.xml b/app/src/main/res/xml/auto_app_description.xml new file mode 100644 index 00000000..48316457 --- /dev/null +++ b/app/src/main/res/xml/auto_app_description.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file -- cgit v1.2.3 From 8fed2b7596b2f0b2ba0e7d650f454267f3b1e351 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Sat, 10 Oct 2015 15:35:02 -0700 Subject: #480 Add support for back/forward + album artwork for Auto --- .../github/daneren2005/dsub/util/ImageLoader.java | 27 +++++++------- .../dsub/util/compat/RemoteControlClientBase.java | 2 ++ .../dsub/util/compat/RemoteControlClientICS.java | 14 +++++--- .../dsub/util/compat/RemoteControlClientLP.java | 41 ++++++++++++++++++++-- 4 files changed, 64 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java b/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java index 8b027d70..470d72fb 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java +++ b/app/src/main/java/github/daneren2005/dsub/util/ImageLoader.java @@ -29,7 +29,6 @@ import android.graphics.Shader; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; -import android.media.RemoteControlClient; import android.os.Build; import android.os.Handler; import android.os.Looper; @@ -46,6 +45,7 @@ import github.daneren2005.dsub.domain.Playlist; import github.daneren2005.dsub.domain.ServerInfo; import github.daneren2005.dsub.service.MusicService; import github.daneren2005.dsub.service.MusicServiceFactory; +import github.daneren2005.dsub.util.compat.RemoteControlClientBase; /** * Asynchronous loading of images, with caching. @@ -257,22 +257,22 @@ public class ImageLoader { return task; } - public SilentBackgroundTask loadImage(Context context, RemoteControlClient remoteControl, MusicDirectory.Entry entry) { + public SilentBackgroundTask loadImage(Context context, RemoteControlClientBase remoteControl, MusicDirectory.Entry entry) { Bitmap bitmap; if (entry == null || entry.getCoverArt() == null) { bitmap = getUnknownImage(entry, imageSizeLarge); - setImage(remoteControl, Util.createDrawableFromBitmap(context, bitmap)); + setImage(entry, remoteControl, Util.createDrawableFromBitmap(context, bitmap)); return null; } bitmap = cache.get(getKey(entry.getCoverArt(), imageSizeLarge)); if (bitmap != null && !bitmap.isRecycled()) { Drawable drawable = Util.createDrawableFromBitmap(this.context, bitmap); - setImage(remoteControl, drawable); + setImage(entry, remoteControl, drawable); return null; } - setImage(remoteControl, Util.createDrawableFromBitmap(context, null)); + setImage(entry, remoteControl, Util.createDrawableFromBitmap(context, null)); ImageTask task = new RemoteControlClientImageTask(context, entry, imageSizeLarge, imageSizeLarge, false, remoteControl); task.execute(); return task; @@ -367,28 +367,27 @@ public class ImageLoader { } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) - private void setImage(RemoteControlClient remoteControl, Drawable drawable) { + private void setImage(MusicDirectory.Entry entry, RemoteControlClientBase remoteControl, Drawable drawable) { if(remoteControl != null && drawable != null) { Bitmap origBitmap = ((BitmapDrawable)drawable).getBitmap(); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2 && origBitmap != null) { origBitmap = origBitmap.copy(origBitmap.getConfig(), false); } if ( origBitmap != null && !origBitmap.isRecycled()) { - remoteControl.editMetadata(false).putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, origBitmap).apply(); + remoteControl.updateAlbumArt(entry, origBitmap); } else { if(origBitmap != null) { Log.e(TAG, "Tried to load a recycled bitmap."); } - remoteControl.editMetadata(false) - .putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, null) - .apply(); + + remoteControl.updateAlbumArt(entry, null); } } } public abstract class ImageTask extends SilentBackgroundTask { private final Context mContext; - private final MusicDirectory.Entry mEntry; + protected final MusicDirectory.Entry mEntry; private final int mSize; private final int mSaveSize; private final boolean mIsNowPlaying; @@ -448,9 +447,9 @@ public class ImageLoader { } private class RemoteControlClientImageTask extends ImageTask { - private RemoteControlClient mRemoteControl; + private RemoteControlClientBase mRemoteControl; - public RemoteControlClientImageTask(Context context, MusicDirectory.Entry entry, int size, int saveSize, boolean isNowPlaying, RemoteControlClient remoteControl) { + public RemoteControlClientImageTask(Context context, MusicDirectory.Entry entry, int size, int saveSize, boolean isNowPlaying, RemoteControlClientBase remoteControl) { super(context, entry, size, saveSize, isNowPlaying); mRemoteControl = remoteControl; @@ -458,7 +457,7 @@ public class ImageLoader { @Override protected void done(Void result) { - setImage(mRemoteControl, mDrawable); + setImage(mEntry, mRemoteControl, mDrawable); } } diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java index 357e2585..b5987270 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java @@ -3,6 +3,7 @@ package github.daneren2005.dsub.util.compat; import github.daneren2005.dsub.domain.MusicDirectory; import android.content.ComponentName; import android.content.Context; +import android.graphics.Bitmap; import android.support.v7.media.MediaRouter; import android.os.Build; @@ -26,6 +27,7 @@ public abstract class RemoteControlClientBase { 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); + public abstract void updateAlbumArt(MusicDirectory.Entry currentSong, Bitmap bitmap); public abstract void registerRoute(MediaRouter router); public abstract void unregisterRoute(MediaRouter router); diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java index 9b4725bd..994c23a6 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java @@ -8,6 +8,7 @@ import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; import android.media.AudioManager; import android.media.MediaMetadataRetriever; import android.media.RemoteControlClient; @@ -72,14 +73,19 @@ public class RemoteControlClientICS extends RemoteControlClientBase { updateMetadata(currentSong, editor); editor.apply(); if (currentSong == null || imageLoader == null) { - mRemoteControl.editMetadata(true) - .putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, null) - .apply(); + updateAlbumArt(currentSong, null); } else { - imageLoader.loadImage(context, mRemoteControl, currentSong); + imageLoader.loadImage(context, this, currentSong); } } + @Override + public void updateAlbumArt(MusicDirectory.Entry currentSong, Bitmap bitmap) { + mRemoteControl.editMetadata(true) + .putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, bitmap). + apply(); + } + @Override public void registerRoute(MediaRouter router) { if(mRemoteControl == null) { diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java index 2cf2dd20..8ebbc9de 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java @@ -23,6 +23,7 @@ import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; import android.media.AudioAttributes; import android.media.MediaMetadata; import android.media.Rating; @@ -31,16 +32,22 @@ import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.os.Build; import android.support.v7.media.MediaRouter; +import android.util.Log; +import github.daneren2005.dsub.activity.SubsonicActivity; import github.daneren2005.dsub.activity.SubsonicFragmentActivity; import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.util.Constants; +import github.daneren2005.dsub.util.ImageLoader; @TargetApi(Build.VERSION_CODES.LOLLIPOP) public class RemoteControlClientLP extends RemoteControlClientBase { - private MediaSession mediaSession; - private DownloadService downloadService; + private static final String TAG = RemoteControlClientLP.class.getSimpleName(); + + protected MediaSession mediaSession; + protected DownloadService downloadService; + protected ImageLoader imageLoader; private PlaybackState previousState; @@ -72,6 +79,8 @@ public class RemoteControlClientLP extends RemoteControlClientBase { .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC); mediaSession.setPlaybackToLocal(audioAttributesBuilder.build()); mediaSession.setActive(true); + + imageLoader = SubsonicActivity.getStaticImageLoader(context); } @Override @@ -118,6 +127,14 @@ public class RemoteControlClientLP extends RemoteControlClientBase { @Override public void updateMetadata(Context context, MusicDirectory.Entry currentSong) { + setMetadata(currentSong, null); + + if(currentSong != null && imageLoader != null) { + imageLoader.loadImage(context, this, currentSong); + } + } + + public void setMetadata(MusicDirectory.Entry currentSong, Bitmap bitmap) { MediaMetadata.Builder builder = new MediaMetadata.Builder(); builder.putString(MediaMetadata.METADATA_KEY_ARTIST, (currentSong == null) ? null : currentSong.getArtist()) .putString(MediaMetadata.METADATA_KEY_ALBUM, (currentSong == null) ? null : currentSong.getAlbum()) @@ -140,9 +157,18 @@ public class RemoteControlClientLP extends RemoteControlClientBase { } builder.putRating(MediaMetadata.METADATA_KEY_USER_RATING, rating); + if(bitmap != null) { + builder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap); + } + mediaSession.setMetadata(builder.build()); } + @Override + public void updateAlbumArt(MusicDirectory.Entry currentSong, Bitmap bitmap) { + setMetadata(currentSong, bitmap); + } + @Override public void registerRoute(MediaRouter router) { router.setMediaSession(mediaSession); @@ -161,6 +187,8 @@ public class RemoteControlClientLP extends RemoteControlClientBase { return PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_SEEK_TO | + PlaybackState.ACTION_SKIP_TO_NEXT | + PlaybackState.ACTION_SKIP_TO_PREVIOUS | PlaybackState.ACTION_SET_RATING; } @@ -185,6 +213,15 @@ public class RemoteControlClientLP extends RemoteControlClientBase { downloadService.seekTo((int) position); } + @Override + public void onSkipToNext() { + downloadService.next(); + } + @Override + public void onSkipToPrevious() { + downloadService.previous(); + } + @Override public void onSetRating(Rating rating) { if(rating.getRatingStyle() != Rating.RATING_THUMB_UP_DOWN) { -- cgit v1.2.3 From abe43fa2eb6f5a8fb6734af4cba40dbba5650158 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Mon, 12 Oct 2015 18:07:51 -0700 Subject: #480 Add support for starring/rating from auto --- .../dsub/activity/SubsonicFragmentActivity.java | 8 +- .../dsub/fragments/NowPlayingFragment.java | 179 +++++--------- .../dsub/fragments/SelectDirectoryFragment.java | 13 +- .../dsub/fragments/SubsonicFragment.java | 226 +----------------- .../daneren2005/dsub/service/DownloadService.java | 73 ++++++ .../github/daneren2005/dsub/util/UpdateHelper.java | 263 +++++++++++++++++++++ .../dsub/util/compat/RemoteControlClientLP.java | 60 +++-- app/src/main/res/values/strings.xml | 2 + 8 files changed, 449 insertions(+), 375 deletions(-) create mode 100644 app/src/main/java/github/daneren2005/dsub/util/UpdateHelper.java diff --git a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java index 8d3bf099..f32c5255 100644 --- a/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java +++ b/app/src/main/java/github/daneren2005/dsub/activity/SubsonicFragmentActivity.java @@ -819,8 +819,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo @Override public void onSongsChanged(List songs, DownloadFile currentPlaying, int currentPlayingIndex) { - if(this.currentPlaying != currentPlaying || currentPlaying == null) { + if(this.currentPlaying != currentPlaying || this.currentPlaying == null) { onSongChanged(currentPlaying, currentPlayingIndex); + onMetadataUpdate(currentPlaying != null ? currentPlaying.getSong() : null, DownloadService.METADATA_UPDATED_ALL); } } @@ -836,4 +837,9 @@ public class SubsonicFragmentActivity extends SubsonicActivity implements Downlo startButton.setImageResource(typedArray.getResourceId(0, 0)); typedArray.recycle(); } + + @Override + public void onMetadataUpdate(MusicDirectory.Entry entry, int fieldChange) { + + } } diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java index 892891d2..a6517bf6 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java @@ -61,6 +61,7 @@ import github.daneren2005.dsub.activity.SubsonicFragmentActivity; import github.daneren2005.dsub.adapter.SectionAdapter; import github.daneren2005.dsub.audiofx.EqualizerController; import github.daneren2005.dsub.domain.Bookmark; +import github.daneren2005.dsub.domain.MusicDirectory; import github.daneren2005.dsub.domain.PlayerState; import github.daneren2005.dsub.domain.RepeatMode; import github.daneren2005.dsub.domain.ServerInfo; @@ -196,24 +197,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis starButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - DownloadFile currentDownload = getDownloadService().getCurrentPlaying(); - if (currentDownload != null) { - final Entry currentSong = currentDownload.getSong(); - toggleStarred(currentSong, new OnStarChange() { - @Override - void starChange(boolean starred) { - if(currentSong.isStarred()) { - starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star)); - } else { - if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { - starButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.star_outline)); - } else { - starButton.setImageResource(R.drawable.ic_toggle_star_outline_dark); - } - } - } - }); - } + getDownloadService().toggleStarred(); } }); } else { @@ -360,37 +344,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis if(downloadService == null) { return; } - - DownloadFile downloadFile = downloadService.getCurrentPlaying(); - if(downloadFile == null) { - return; - } - Entry entry = downloadFile.getSong(); - - // If rating == 1, already set so unset - if(entry.getRating() == 1) { - setRating(entry, 0); - - if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - rateBadButton.setImageResource(R.drawable.ic_action_rating_bad_dark); - } else { - rateBadButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.rating_bad)); - } - } else { - // Immediately skip to the next song - downloadService.next(true); - - // Otherwise set rating to 1 - setRating(entry, 1); - rateBadButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_action_rating_bad_selected)); - - // Make sure good rating is blank - if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - rateGoodButton.setImageResource(R.drawable.ic_action_rating_good_dark); - } else { - rateGoodButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.rating_good)); - } - } + downloadService.toggleRating(1); } }); rateGoodButton.setOnClickListener(new View.OnClickListener() { @@ -400,34 +354,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis if(downloadService == null) { return; } - - DownloadFile downloadFile = downloadService.getCurrentPlaying(); - if(downloadFile == null) { - return; - } - Entry entry = downloadFile.getSong(); - - // If rating == 5, already set so unset - if(entry.getRating() == 5) { - setRating(entry, 0); - - if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - rateGoodButton.setImageResource(R.drawable.ic_action_rating_good_dark); - } else { - rateGoodButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.rating_good)); - } - } else { - // Otherwise set rating to maximum - setRating(entry, 5); - rateGoodButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_action_rating_good_selected)); - - // Make sure bad rating is blank - if (context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - rateBadButton.setImageResource(R.drawable.ic_action_rating_bad_dark); - } else { - rateBadButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.rating_bad)); - } - } + downloadService.toggleRating(5); } }); @@ -694,10 +621,10 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis createNewPlaylist(entries, true); return true; case R.id.menu_star: - toggleStarred(song.getSong()); + UpdateHelper.toggleStarred(context, song.getSong()); return true; case R.id.menu_rate: - setRating(song.getSong()); + UpdateHelper.setRating(context, song.getSong()); return true; case R.id.menu_toggle_timer: if(getDownloadService().getSleepTimer()) { @@ -1093,7 +1020,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis MusicService musicService = MusicServiceFactory.getMusicService(context); musicService.createBookmark(currentSong, position, comment, context, null); - new EntryInstanceUpdater(currentSong) { + new UpdateHelper.EntryInstanceUpdater(currentSong) { @Override public void update(Entry found) { found.setBookmark(new Bookmark(position)); @@ -1235,54 +1162,10 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis Entry song = currentPlaying.getSong(); songTitleTextView.setText(song.getTitle()); getImageLoader().loadImage(albumArtImageView, song, true, true); - if(song.isStarred()) { - starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star)); - } else { - if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { - starButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.star_outline)); - } else { - starButton.setImageResource(R.drawable.ic_toggle_star_outline_dark); - } - } setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex + 1, currentPlayingSize)); - - int badRating, goodRating, bookmark; - if(song.getRating() == 1) { - rateBadButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_action_rating_bad_selected)); - } else { - if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - badRating = R.drawable.ic_action_rating_bad_dark; - } else { - badRating = DrawableTint.getDrawableRes(context, R.attr.rating_bad); - } - rateBadButton.setImageResource(badRating); - } - - if(song.getRating() == 5) { - rateGoodButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_action_rating_good_selected)); - } else { - if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - goodRating = R.drawable.ic_action_rating_good_dark; - } else { - goodRating = DrawableTint.getDrawableRes(context, R.attr.rating_good); - } - rateGoodButton.setImageResource(goodRating); - } - - if(song.getBookmark() != null) { - bookmarkButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_menu_bookmark_selected)); - } else { - if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - bookmark = R.drawable.ic_menu_bookmark_dark; - } else { - bookmark = DrawableTint.getDrawableRes(context, R.attr.bookmark); - } - bookmarkButton.setImageResource(bookmark); - } } else { songTitleTextView.setText(null); getImageLoader().loadImage(albumArtImageView, (Entry) null, true, false); - starButton.setImageResource(R.drawable.ic_toggle_star_outline_dark); setSubtitle(null); } } @@ -1319,6 +1202,7 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis setSubtitle(context.getResources().getString(R.string.download_playing_out_of, currentPlayingIndex + 1, currentPlayingSize)); if(this.currentPlaying != currentPlaying) { onSongChanged(currentPlaying, currentPlayingIndex); + onMetadataUpdate(currentPlaying != null ? currentPlaying.getSong() : null, DownloadService.METADATA_UPDATED_ALL); } } @@ -1401,6 +1285,53 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis } } + @Override + public void onMetadataUpdate(Entry song, int fieldChange) { + if(song != null && song.isStarred()) { + starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star)); + } else { + if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { + starButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.star_outline)); + } else { + starButton.setImageResource(R.drawable.ic_toggle_star_outline_dark); + } + } + + int badRating, goodRating, bookmark; + if(song != null && song.getRating() == 1) { + rateBadButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_action_rating_bad_selected)); + } else { + if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { + badRating = R.drawable.ic_action_rating_bad_dark; + } else { + badRating = DrawableTint.getDrawableRes(context, R.attr.rating_bad); + } + rateBadButton.setImageResource(badRating); + } + + if(song != null && song.getRating() == 5) { + rateGoodButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_action_rating_good_selected)); + } else { + if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { + goodRating = R.drawable.ic_action_rating_good_dark; + } else { + goodRating = DrawableTint.getDrawableRes(context, R.attr.rating_good); + } + rateGoodButton.setImageResource(goodRating); + } + + if(song != null && song.getBookmark() != null) { + bookmarkButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_menu_bookmark_selected)); + } else { + if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { + bookmark = R.drawable.ic_menu_bookmark_dark; + } else { + bookmark = DrawableTint.getDrawableRes(context, R.attr.bookmark); + } + bookmarkButton.setImageResource(bookmark); + } + } + public void updateRepeatButton() { DownloadService downloadService = getDownloadService(); switch (downloadService.getRepeatMode()) { diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java index 2db10c2e..9e6fec45 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SelectDirectoryFragment.java @@ -57,6 +57,7 @@ import github.daneren2005.dsub.util.LoadingTask; import github.daneren2005.dsub.util.Pair; import github.daneren2005.dsub.util.SilentBackgroundTask; import github.daneren2005.dsub.util.TabBackgroundTask; +import github.daneren2005.dsub.util.UpdateHelper; import github.daneren2005.dsub.util.UserUtil; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.view.FastScroller; @@ -1037,7 +1038,7 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section musicService.setStarred(entries, artists, albums, false, this, context); for(Entry entry: unstar) { - new EntryInstanceUpdater(entry) { + new UpdateHelper.EntryInstanceUpdater(entry) { @Override public void update(Entry found) { found.setStarred(false); @@ -1382,10 +1383,10 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section starButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - toggleStarred(directory, new OnStarChange() { + UpdateHelper.toggleStarred(context, directory, new UpdateHelper.OnStarChange() { @Override - void starChange(boolean starred) { - if(directory.isStarred()) { + public void starChange(boolean starred) { + if (directory.isStarred()) { starButton.setImageResource(DrawableTint.getDrawableRes(context, R.attr.star_outline)); starButton.setImageDrawable(DrawableTint.getTintedDrawable(context, R.drawable.ic_toggle_star)); } else { @@ -1406,9 +1407,9 @@ public class SelectDirectoryFragment extends SubsonicFragment implements Section ratingBarWrapper.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - setRating(directory, new OnRatingChange() { + UpdateHelper.setRating(context, directory, new UpdateHelper.OnRatingChange() { @Override - void ratingChange(int rating) { + public void ratingChange(int rating) { ratingBar.setRating(directory.getRating()); } }); diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java index 8672f534..7baf9009 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/SubsonicFragment.java @@ -77,6 +77,7 @@ import github.daneren2005.dsub.util.MenuUtil; import github.daneren2005.dsub.util.ProgressListener; import github.daneren2005.dsub.util.SilentBackgroundTask; import github.daneren2005.dsub.util.LoadingTask; +import github.daneren2005.dsub.util.UpdateHelper; import github.daneren2005.dsub.util.UserUtil; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.view.AlbumView; @@ -327,7 +328,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR deleteRecursively(artist); break; case R.id.artist_menu_star: - toggleStarred(artist); + UpdateHelper.toggleStarred(context, artist); break; case R.id.album_menu_play_now: artistOverride = true; @@ -354,7 +355,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR downloadRecursively(entry.getId(), true, true, false, false, true); break; case R.id.album_menu_star: - toggleStarred(entry); + UpdateHelper.toggleStarred(context, entry); break; case R.id.album_menu_delete: deleteRecursively(entry); @@ -387,7 +388,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR addToPlaylist(songs); break; case R.id.song_menu_star: - toggleStarred(entry); + UpdateHelper.toggleStarred(context, entry); break; case R.id.song_menu_play_external: playExternalPlayer(entry); @@ -411,7 +412,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR deleteBookmark(entry, null); break; case R.id.menu_rate: - setRating(entry); + UpdateHelper.setRating(context, entry); break; default: return false; @@ -790,106 +791,6 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR dialog.show(); } - public void toggleStarred(Entry entry) { - toggleStarred(entry, null); - } - - public void toggleStarred(final Entry entry, final OnStarChange onStarChange) { - final boolean starred = !entry.isStarred(); - entry.setStarred(starred); - if(onStarChange != null) { - onStarChange.starChange(starred); - } - - new SilentBackgroundTask(context) { - @Override - protected Void doInBackground() throws Throwable { - MusicService musicService = MusicServiceFactory.getMusicService(context); - if(entry.isDirectory() && Util.isTagBrowsing(context) && !Util.isOffline(context)) { - if(entry.isAlbum()) { - musicService.setStarred(null, null, Arrays.asList(entry), starred, null, context); - } else { - musicService.setStarred(null, Arrays.asList(entry), null, starred, null, context); - } - } else { - musicService.setStarred(Arrays.asList(entry), null, null, starred, null, context); - } - - new EntryInstanceUpdater(entry) { - @Override - public void update(Entry found) { - found.setStarred(starred); - } - }.execute(); - - return null; - } - - @Override - protected void done(Void result) { - // UpdateView - Util.toast(context, context.getResources().getString(starred ? R.string.starring_content_starred : R.string.starring_content_unstarred, entry.getTitle())); - } - - @Override - protected void error(Throwable error) { - Log.w(TAG, "Failed to star", error); - entry.setStarred(!starred); - if(onStarChange != null) { - onStarChange.starChange(!starred); - } - - String msg; - if (error instanceof OfflineException || error instanceof ServerTooOldException) { - msg = getErrorMessage(error); - } else { - msg = context.getResources().getString(R.string.starring_content_error, entry.getTitle()) + " " + getErrorMessage(error); - } - - Util.toast(context, msg, false); - } - }.execute(); - } - - public void toggleStarred(final Artist entry) { - final boolean starred = !entry.isStarred(); - entry.setStarred(starred); - - new SilentBackgroundTask(context) { - @Override - protected Void doInBackground() throws Throwable { - MusicService musicService = MusicServiceFactory.getMusicService(context); - if(Util.isTagBrowsing(context) && !Util.isOffline(context)) { - musicService.setStarred(null, Arrays.asList(new Entry(entry)), null, starred, null, context); - } else { - musicService.setStarred(Arrays.asList(new Entry(entry)), null, null, starred, null, context); - } - return null; - } - - @Override - protected void done(Void result) { - // UpdateView - Util.toast(context, context.getResources().getString(starred ? R.string.starring_content_starred : R.string.starring_content_unstarred, entry.getName())); - } - - @Override - protected void error(Throwable error) { - Log.w(TAG, "Failed to star", error); - entry.setStarred(!starred); - - String msg; - if (error instanceof OfflineException || error instanceof ServerTooOldException) { - msg = getErrorMessage(error); - } else { - msg = context.getResources().getString(R.string.starring_content_error, entry.getName()) + " " + getErrorMessage(error); - } - - Util.toast(context, msg, false); - } - }.execute(); - } - protected void downloadRecursively(final String id, final boolean save, final boolean append, final boolean autoplay, final boolean shuffle, final boolean background) { downloadRecursively(id, "", true, save, append, autoplay, shuffle, background); } @@ -1737,7 +1638,7 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR MusicService musicService = MusicServiceFactory.getMusicService(context); musicService.deleteBookmark(entry, context, null); - new EntryInstanceUpdater(entry) { + new UpdateHelper.EntryInstanceUpdater(entry) { @Override public void update(Entry found) { found.setBookmark(null); @@ -1773,80 +1674,6 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR }); } - protected void setRating(Entry entry) { - setRating(entry, null); - } - protected void setRating(final Entry entry, final OnRatingChange onRatingChange) { - View layout = context.getLayoutInflater().inflate(R.layout.rating, null); - final RatingBar ratingBar = (RatingBar) layout.findViewById(R.id.rating_bar); - ratingBar.setRating((float) entry.getRating()); - - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(context.getResources().getString(R.string.rating_title, entry.getTitle())) - .setView(layout) - .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int id) { - int rating = (int) ratingBar.getRating(); - setRating(entry, rating, onRatingChange); - } - }) - .setNegativeButton(R.string.common_cancel, null); - - AlertDialog dialog = builder.create(); - dialog.show(); - } - - protected void setRating(Entry entry, int rating) { - setRating(entry, rating, null); - } - protected void setRating(final Entry entry, final int rating, final OnRatingChange onRatingChange) { - final int oldRating = entry.getRating(); - entry.setRating(rating); - - if(onRatingChange != null) { - onRatingChange.ratingChange(rating); - } - - new SilentBackgroundTask(context) { - @Override - protected Void doInBackground() throws Throwable { - MusicService musicService = MusicServiceFactory.getMusicService(context); - musicService.setRating(entry, rating, context, null); - - new EntryInstanceUpdater(entry) { - @Override - public void update(Entry found) { - found.setRating(rating); - } - }.execute(); - return null; - } - - @Override - protected void done(Void result) { - Util.toast(context, context.getResources().getString(rating > 0 ? R.string.rating_set_rating : R.string.rating_remove_rating, entry.getTitle())); - } - - @Override - protected void error(Throwable error) { - entry.setRating(oldRating); - if(onRatingChange != null) { - onRatingChange.ratingChange(oldRating); - } - - String msg; - if (error instanceof OfflineException || error instanceof ServerTooOldException) { - msg = getErrorMessage(error); - } else { - msg = context.getResources().getString(rating > 0 ? R.string.rating_set_rating_failed : R.string.rating_remove_rating_failed, entry.getTitle()) + " " + getErrorMessage(error); - } - - Util.toast(context, msg, false); - } - }.execute(); - } - public SectionAdapter getCurrentAdapter() { return null; } public void stopActionMode() { SectionAdapter adapter = getCurrentAdapter(); @@ -1965,47 +1792,6 @@ public class SubsonicFragment extends Fragment implements SwipeRefreshLayout.OnR } } - protected abstract class EntryInstanceUpdater { - private Entry entry; - - public EntryInstanceUpdater(Entry entry) { - this.entry = entry; - } - - public abstract void update(Entry found); - - public void execute() { - DownloadService downloadService = getDownloadService(); - if(downloadService != null && !entry.isDirectory()) { - boolean serializeChanges = false; - List downloadFiles = downloadService.getDownloads(); - for(DownloadFile file: downloadFiles) { - Entry check = file.getSong(); - if(entry.getId().equals(check.getId())) { - update(entry); - serializeChanges = true; - } - } - - if(serializeChanges) { - downloadService.serializeQueue(); - } - } - - Entry find = UpdateView.findEntry(entry); - if(find != null) { - update(find); - } - } - } - - public abstract class OnRatingChange { - abstract void ratingChange(int rating); - } - public abstract class OnStarChange { - abstract void starChange(boolean starred); - } - public abstract class RecursiveLoader extends LoadingTask { protected MusicService musicService; protected static final int MAX_SONGS = 500; diff --git a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java index 6dedca29..576791b2 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java +++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java @@ -48,6 +48,7 @@ import github.daneren2005.dsub.util.Constants; import github.daneren2005.dsub.util.MediaRouteManager; import github.daneren2005.dsub.util.ShufflePlayBuffer; import github.daneren2005.dsub.util.SimpleServiceBinder; +import github.daneren2005.dsub.util.UpdateHelper; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.util.compat.RemoteControlClientBase; import github.daneren2005.dsub.util.tags.BastpUtil; @@ -106,6 +107,11 @@ public class DownloadService extends Service { private static final int SHUFFLE_MODE_ALL = 1; private static final int SHUFFLE_MODE_ARTIST = 2; + public static final int METADATA_UPDATED_ALL = 0; + public static final int METADATA_UPDATED_STAR = 1; + public static final int METADATA_UPDATED_RATING = 2; + public static final int METADATA_UPDATED_BOOKMARK = 4; + private RemoteControlClientBase mRemoteControl; private final IBinder binder = new SimpleServiceBinder(this); @@ -2465,6 +2471,55 @@ public class DownloadService extends Service { } } + public void toggleStarred() { + final DownloadFile currentPlaying = this.currentPlaying; + if(currentPlaying == null) { + return; + } + + UpdateHelper.toggleStarred(this, currentPlaying.getSong(), new UpdateHelper.OnStarChange() { + @Override + public void starChange(boolean starred) { + if(currentPlaying == DownloadService.this.currentPlaying) { + onMetadataUpdate(METADATA_UPDATED_STAR); + } + } + }); + } + public void toggleRating(int rating) { + if(currentPlaying == null) { + return; + } + + MusicDirectory.Entry entry = currentPlaying.getSong(); + if(entry.getRating() == rating) { + setRating(0); + } else { + setRating(rating); + } + } + public void setRating(int rating) { + final DownloadFile currentPlaying = this.currentPlaying; + if(currentPlaying == null) { + return; + } + MusicDirectory.Entry entry = currentPlaying.getSong(); + + // Immediately skip to the next song if down thumbed + if(rating == 1) { + next(true); + } + + UpdateHelper.setRating(this, entry, rating, new UpdateHelper.OnRatingChange() { + @Override + public void ratingChange(int rating) { + if(currentPlaying == DownloadService.this.currentPlaying) { + onMetadataUpdate(METADATA_UPDATED_RATING); + } + } + }); + } + public void addOnSongChangedListener(OnSongChangedListener listener) { addOnSongChangedListener(listener, false); } @@ -2504,6 +2559,9 @@ public class DownloadService extends Service { public void run() { if(revision == atRevision) { listener.onSongChanged(currentPlaying, currentPlayingIndex); + + MusicDirectory.Entry entry = currentPlaying != null ? currentPlaying.getSong() : null; + listener.onMetadataUpdate(entry, METADATA_UPDATED_ALL); } } }); @@ -2560,6 +2618,20 @@ public class DownloadService extends Service { }); } } + private synchronized void onMetadataUpdate() { + onMetadataUpdate(METADATA_UPDATED_ALL); + } + private synchronized void onMetadataUpdate(final int updateType) { + for(final OnSongChangedListener listener: onSongChangedListeners) { + handler.post(new Runnable() { + @Override + public void run() { + MusicDirectory.Entry entry = currentPlaying != null ? currentPlaying.getSong() : null; + listener.onMetadataUpdate(entry, updateType); + } + }); + } + } private class BufferTask extends SilentBackgroundTask { private final DownloadFile downloadFile; @@ -2672,5 +2744,6 @@ public class DownloadService extends Service { void onSongsChanged(List songs, DownloadFile currentPlaying, int currentPlayingIndex); void onSongProgress(DownloadFile currentPlaying, int millisPlayed, Integer duration, boolean isSeekable); void onStateUpdate(DownloadFile downloadFile, PlayerState playerState); + void onMetadataUpdate(MusicDirectory.Entry entry, int fieldChange); } } diff --git a/app/src/main/java/github/daneren2005/dsub/util/UpdateHelper.java b/app/src/main/java/github/daneren2005/dsub/util/UpdateHelper.java new file mode 100644 index 00000000..c7e0a04b --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/util/UpdateHelper.java @@ -0,0 +1,263 @@ +/* + 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 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.util; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.support.v7.app.AlertDialog; +import android.util.Log; +import android.view.View; +import android.widget.RatingBar; + +import java.util.Arrays; +import java.util.List; + +import github.daneren2005.dsub.R; +import github.daneren2005.dsub.domain.Artist; +import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.domain.MusicDirectory.Entry; +import github.daneren2005.dsub.fragments.SubsonicFragment; +import github.daneren2005.dsub.service.DownloadFile; +import github.daneren2005.dsub.service.DownloadService; +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.view.UpdateView; + +public final class UpdateHelper { + private static final String TAG = UpdateHelper.class.getSimpleName(); + + public static void toggleStarred(Context context, Entry entry) { + toggleStarred(context, entry, null); + } + + public static void toggleStarred(final Context context, final Entry entry, final OnStarChange onStarChange) { + final boolean starred = !entry.isStarred(); + entry.setStarred(starred); + if(onStarChange != null) { + onStarChange.starChange(starred); + } + + new SilentBackgroundTask(context) { + @Override + protected Void doInBackground() throws Throwable { + MusicService musicService = MusicServiceFactory.getMusicService(context); + if(entry.isDirectory() && Util.isTagBrowsing(context) && !Util.isOffline(context)) { + if(entry.isAlbum()) { + musicService.setStarred(null, null, Arrays.asList(entry), starred, null, context); + } else { + musicService.setStarred(null, Arrays.asList(entry), null, starred, null, context); + } + } else { + musicService.setStarred(Arrays.asList(entry), null, null, starred, null, context); + } + + new EntryInstanceUpdater(entry) { + @Override + public void update(Entry found) { + found.setStarred(starred); + } + }.execute(); + + return null; + } + + @Override + protected void done(Void result) { + // UpdateView + Util.toast(context, context.getResources().getString(starred ? R.string.starring_content_starred : R.string.starring_content_unstarred, entry.getTitle())); + } + + @Override + protected void error(Throwable error) { + Log.w(TAG, "Failed to star", error); + entry.setStarred(!starred); + if(onStarChange != null) { + onStarChange.starChange(!starred); + } + + String msg; + if (error instanceof OfflineException || error instanceof ServerTooOldException) { + msg = getErrorMessage(error); + } else { + msg = context.getResources().getString(R.string.starring_content_error, entry.getTitle()) + " " + getErrorMessage(error); + } + + Util.toast(context, msg, false); + } + }.execute(); + } + + public static void toggleStarred(final Context context, final Artist entry) { + final boolean starred = !entry.isStarred(); + entry.setStarred(starred); + + new SilentBackgroundTask(context) { + @Override + protected Void doInBackground() throws Throwable { + MusicService musicService = MusicServiceFactory.getMusicService(context); + if(Util.isTagBrowsing(context) && !Util.isOffline(context)) { + musicService.setStarred(null, Arrays.asList(new Entry(entry)), null, starred, null, context); + } else { + musicService.setStarred(Arrays.asList(new Entry(entry)), null, null, starred, null, context); + } + return null; + } + + @Override + protected void done(Void result) { + // UpdateView + Util.toast(context, context.getResources().getString(starred ? R.string.starring_content_starred : R.string.starring_content_unstarred, entry.getName())); + } + + @Override + protected void error(Throwable error) { + Log.w(TAG, "Failed to star", error); + entry.setStarred(!starred); + + String msg; + if (error instanceof OfflineException || error instanceof ServerTooOldException) { + msg = getErrorMessage(error); + } else { + msg = context.getResources().getString(R.string.starring_content_error, entry.getName()) + " " + getErrorMessage(error); + } + + Util.toast(context, msg, false); + } + }.execute(); + } + + public static void setRating(Activity context, Entry entry) { + setRating(context, entry, null); + } + public static void setRating(final Activity context, final Entry entry, final OnRatingChange onRatingChange) { + View layout = context.getLayoutInflater().inflate(R.layout.rating, null); + final RatingBar ratingBar = (RatingBar) layout.findViewById(R.id.rating_bar); + ratingBar.setRating((float) entry.getRating()); + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(context.getResources().getString(R.string.rating_title, entry.getTitle())) + .setView(layout) + .setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int id) { + int rating = (int) ratingBar.getRating(); + setRating(context, entry, rating, onRatingChange); + } + }) + .setNegativeButton(R.string.common_cancel, null); + + AlertDialog dialog = builder.create(); + dialog.show(); + } + + public static void setRating(Context context, Entry entry, int rating) { + setRating(context, entry, rating, null); + } + public static void setRating(final Context context, final Entry entry, final int rating, final OnRatingChange onRatingChange) { + final int oldRating = entry.getRating(); + entry.setRating(rating); + + if(onRatingChange != null) { + onRatingChange.ratingChange(rating); + } + + new SilentBackgroundTask(context) { + @Override + protected Void doInBackground() throws Throwable { + MusicService musicService = MusicServiceFactory.getMusicService(context); + musicService.setRating(entry, rating, context, null); + + new EntryInstanceUpdater(entry) { + @Override + public void update(Entry found) { + found.setRating(rating); + } + }.execute(); + return null; + } + + @Override + protected void done(Void result) { + Util.toast(context, context.getResources().getString(rating > 0 ? R.string.rating_set_rating : R.string.rating_remove_rating, entry.getTitle())); + } + + @Override + protected void error(Throwable error) { + entry.setRating(oldRating); + if(onRatingChange != null) { + onRatingChange.ratingChange(oldRating); + } + + String msg; + if (error instanceof OfflineException || error instanceof ServerTooOldException) { + msg = getErrorMessage(error); + } else { + msg = context.getResources().getString(rating > 0 ? R.string.rating_set_rating_failed : R.string.rating_remove_rating_failed, entry.getTitle()) + " " + getErrorMessage(error); + } + + Util.toast(context, msg, false); + } + }.execute(); + } + + public static abstract class EntryInstanceUpdater { + private Entry entry; + + public EntryInstanceUpdater(Entry entry) { + this.entry = entry; + } + + public abstract void update(Entry found); + + public void execute() { + DownloadService downloadService = DownloadService.getInstance(); + if(downloadService != null && !entry.isDirectory()) { + boolean serializeChanges = false; + List downloadFiles = downloadService.getDownloads(); + for(DownloadFile file: downloadFiles) { + Entry check = file.getSong(); + if(entry.getId().equals(check.getId())) { + update(entry); + serializeChanges = true; + } + } + + if(serializeChanges) { + downloadService.serializeQueue(); + } + } + + Entry find = UpdateView.findEntry(entry); + if(find != null) { + update(find); + } + } + } + + public static abstract class OnStarChange { + public abstract void starChange(boolean starred); + } + public static abstract class OnRatingChange { + public abstract void ratingChange(int rating); + } +} diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java index 8ebbc9de..e2cbb307 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java @@ -31,26 +31,31 @@ import android.media.RemoteControlClient; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.os.Build; +import android.os.Bundle; import android.support.v7.media.MediaRouter; import android.util.Log; +import github.daneren2005.dsub.R; import github.daneren2005.dsub.activity.SubsonicActivity; import github.daneren2005.dsub.activity.SubsonicFragmentActivity; import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.service.DownloadFile; import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.util.Constants; import github.daneren2005.dsub.util.ImageLoader; +import github.daneren2005.dsub.util.UpdateHelper; @TargetApi(Build.VERSION_CODES.LOLLIPOP) public class RemoteControlClientLP extends RemoteControlClientBase { private static final String TAG = RemoteControlClientLP.class.getSimpleName(); + private static final String CUSTOM_ACTION_THUMBS_UP = "github.daneren2005.dsub.THUMBS_UP"; + private static final String CUSTOM_ACTION_THUMBS_DOWN = "github.daneren2005.dsub.THUMBS_DOWN"; + private static final String CUSTOM_ACTION_STAR = "github.daneren2005.dsub.STAR"; protected MediaSession mediaSession; protected DownloadService downloadService; protected ImageLoader imageLoader; - private PlaybackState previousState; - @Override public void register(Context context, ComponentName mediaButtonReceiverComponent) { downloadService = (DownloadService) context; @@ -90,12 +95,7 @@ public class RemoteControlClientLP extends RemoteControlClientBase { @Override public void setPlaybackState(int state) { - PlaybackState.Builder builder; - if(previousState == null) { - builder = new PlaybackState.Builder(); - } else { - builder = new PlaybackState.Builder(previousState); - } + PlaybackState.Builder builder = new PlaybackState.Builder(); int newState = PlaybackState.STATE_NONE; switch(state) { @@ -120,9 +120,13 @@ public class RemoteControlClientLP extends RemoteControlClientBase { builder.setState(newState, position, 1.0f); builder.setActions(getPlaybackActions()); + DownloadFile downloadFile = downloadService.getCurrentPlaying(); + if(downloadFile != null) { + addCustomActions(downloadFile.getSong(), builder); + } + PlaybackState playbackState = builder.build(); mediaSession.setPlaybackState(playbackState); - previousState = playbackState; } @Override @@ -188,8 +192,22 @@ public class RemoteControlClientLP extends RemoteControlClientBase { PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_SEEK_TO | PlaybackState.ACTION_SKIP_TO_NEXT | - PlaybackState.ACTION_SKIP_TO_PREVIOUS | - PlaybackState.ACTION_SET_RATING; + PlaybackState.ACTION_SKIP_TO_PREVIOUS; + } + protected void addCustomActions(MusicDirectory.Entry currentSong, PlaybackState.Builder builder) { + PlaybackState.CustomAction thumbsUp = new PlaybackState.CustomAction.Builder(CUSTOM_ACTION_THUMBS_UP, + downloadService.getString(R.string.download_thumbs_up), + R.drawable.ic_action_rating_good_selected).build(); + + PlaybackState.CustomAction thumbsDown = new PlaybackState.CustomAction.Builder(CUSTOM_ACTION_THUMBS_DOWN, + downloadService.getString(R.string.download_thumbs_down), + R.drawable.ic_action_rating_bad_selected).build(); + + PlaybackState.CustomAction star = new PlaybackState.CustomAction.Builder(CUSTOM_ACTION_STAR, + downloadService.getString(R.string.common_star), + R.drawable.ic_toggle_star).build(); + + builder.addCustomAction(star).addCustomAction(thumbsDown).addCustomAction(thumbsUp); } private class EventCallback extends MediaSession.Callback { @@ -223,19 +241,13 @@ public class RemoteControlClientLP extends RemoteControlClientBase { } @Override - public void onSetRating(Rating rating) { - if(rating.getRatingStyle() != Rating.RATING_THUMB_UP_DOWN) { - return; - } - - if(rating.isRated()) { - if(rating.isThumbUp()) { - - } else { - - } - } else { - + public void onCustomAction(String action, Bundle extras) { + if(CUSTOM_ACTION_THUMBS_UP.equals(action)) { + downloadService.toggleRating(5); + } else if(CUSTOM_ACTION_THUMBS_DOWN.equals(action)) { + downloadService.toggleRating(1); + } else if(CUSTOM_ACTION_STAR.equals(action)) { + downloadService.toggleStarred(); } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7c682930..6d000734 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -223,6 +223,8 @@ \nEstimated Size: %2$s Failed to load continue from where you left off on another device at + Thumbs Up + Thumbs Down New podcasts available New songs in playlists -- cgit v1.2.3 From 89e248e22c98b127d6afe0a448a74641250eb7d2 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Tue, 13 Oct 2015 17:16:11 -0700 Subject: Update playlist toggle --- .../res/drawable-hdpi/action_toggle_list_dark.png | Bin 601 -> 1031 bytes .../res/drawable-hdpi/action_toggle_list_light.png | Bin 616 -> 1165 bytes .../res/drawable-mdpi/action_toggle_list_dark.png | Bin 381 -> 669 bytes .../res/drawable-mdpi/action_toggle_list_light.png | Bin 414 -> 745 bytes .../res/drawable-xhdpi/action_toggle_list_dark.png | Bin 931 -> 1448 bytes .../res/drawable-xhdpi/action_toggle_list_light.png | Bin 958 -> 1556 bytes .../res/drawable-xxhdpi/action_toggle_list_dark.png | Bin 1545 -> 2436 bytes .../res/drawable-xxhdpi/action_toggle_list_light.png | Bin 1577 -> 2659 bytes .../res/drawable-xxxhdpi/action_toggle_list_dark.png | Bin 2390 -> 3602 bytes .../res/drawable-xxxhdpi/action_toggle_list_light.png | Bin 2397 -> 3848 bytes 10 files changed, 0 insertions(+), 0 deletions(-) diff --git a/app/src/main/res/drawable-hdpi/action_toggle_list_dark.png b/app/src/main/res/drawable-hdpi/action_toggle_list_dark.png index 74b11687..75960936 100644 Binary files a/app/src/main/res/drawable-hdpi/action_toggle_list_dark.png and b/app/src/main/res/drawable-hdpi/action_toggle_list_dark.png differ diff --git a/app/src/main/res/drawable-hdpi/action_toggle_list_light.png b/app/src/main/res/drawable-hdpi/action_toggle_list_light.png index 36a56aac..0b4b552d 100644 Binary files a/app/src/main/res/drawable-hdpi/action_toggle_list_light.png and b/app/src/main/res/drawable-hdpi/action_toggle_list_light.png differ diff --git a/app/src/main/res/drawable-mdpi/action_toggle_list_dark.png b/app/src/main/res/drawable-mdpi/action_toggle_list_dark.png index 442db2fb..19fcae8f 100644 Binary files a/app/src/main/res/drawable-mdpi/action_toggle_list_dark.png and b/app/src/main/res/drawable-mdpi/action_toggle_list_dark.png differ diff --git a/app/src/main/res/drawable-mdpi/action_toggle_list_light.png b/app/src/main/res/drawable-mdpi/action_toggle_list_light.png index f078c702..12ecc9a0 100644 Binary files a/app/src/main/res/drawable-mdpi/action_toggle_list_light.png and b/app/src/main/res/drawable-mdpi/action_toggle_list_light.png differ diff --git a/app/src/main/res/drawable-xhdpi/action_toggle_list_dark.png b/app/src/main/res/drawable-xhdpi/action_toggle_list_dark.png index 1c973af3..7b05a06e 100644 Binary files a/app/src/main/res/drawable-xhdpi/action_toggle_list_dark.png and b/app/src/main/res/drawable-xhdpi/action_toggle_list_dark.png differ diff --git a/app/src/main/res/drawable-xhdpi/action_toggle_list_light.png b/app/src/main/res/drawable-xhdpi/action_toggle_list_light.png index 34946bee..4b890155 100644 Binary files a/app/src/main/res/drawable-xhdpi/action_toggle_list_light.png and b/app/src/main/res/drawable-xhdpi/action_toggle_list_light.png differ diff --git a/app/src/main/res/drawable-xxhdpi/action_toggle_list_dark.png b/app/src/main/res/drawable-xxhdpi/action_toggle_list_dark.png index 409cbb50..c6c78b1c 100644 Binary files a/app/src/main/res/drawable-xxhdpi/action_toggle_list_dark.png and b/app/src/main/res/drawable-xxhdpi/action_toggle_list_dark.png differ diff --git a/app/src/main/res/drawable-xxhdpi/action_toggle_list_light.png b/app/src/main/res/drawable-xxhdpi/action_toggle_list_light.png index b04a1e6b..9898e130 100644 Binary files a/app/src/main/res/drawable-xxhdpi/action_toggle_list_light.png and b/app/src/main/res/drawable-xxhdpi/action_toggle_list_light.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/action_toggle_list_dark.png b/app/src/main/res/drawable-xxxhdpi/action_toggle_list_dark.png index 5efdb642..93369e24 100644 Binary files a/app/src/main/res/drawable-xxxhdpi/action_toggle_list_dark.png and b/app/src/main/res/drawable-xxxhdpi/action_toggle_list_dark.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/action_toggle_list_light.png b/app/src/main/res/drawable-xxxhdpi/action_toggle_list_light.png index 957a3ac6..b0989dde 100644 Binary files a/app/src/main/res/drawable-xxxhdpi/action_toggle_list_light.png and b/app/src/main/res/drawable-xxxhdpi/action_toggle_list_light.png differ -- cgit v1.2.3 From 4b6a015b10da86cc5b9bae5f81b642cae48e1fe8 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Tue, 13 Oct 2015 17:16:32 -0700 Subject: Update README for changed Asset Studio commands --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 23324cd7..e7d83ca4 100644 --- a/README.md +++ b/README.md @@ -29,5 +29,5 @@ Media Icons are double standard size. On https://romannurik.github.io/AndroidAs ``` PARAM_RESOURCES.iconSize = {w: 64, h: 64} PARAM_RESOURCES['targetRect-clipart'] = { x: 12, y: 12, w: 40, h: 40 } -form.fields[0].params.maxFinalSize = {w: 512, h: 512} +form.fields_[0].params_.maxFinalSize = {w: 512, h: 512} ``` -- cgit v1.2.3 From c9df036805f9d4c02cc706bd8a29f4dc7de19834 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Tue, 13 Oct 2015 18:00:26 -0700 Subject: #480, #379 Don't show prev/next when not useful, display queue on Auto, and improve custom actions/show them on Wear --- .../daneren2005/dsub/service/DownloadService.java | 31 +++++- .../dsub/util/compat/RemoteControlClientBase.java | 11 +- .../dsub/util/compat/RemoteControlClientICS.java | 13 +++ .../dsub/util/compat/RemoteControlClientLP.java | 116 ++++++++++++++++----- .../res/drawable-hdpi/ic_action_rating_bad.png | Bin 0 -> 817 bytes .../ic_action_rating_bad_selected.png | Bin 559 -> 584 bytes .../res/drawable-hdpi/ic_action_rating_good.png | Bin 0 -> 835 bytes .../ic_action_rating_good_selected.png | Bin 550 -> 577 bytes app/src/main/res/drawable-hdpi/ic_toggle_star.png | Bin 904 -> 969 bytes .../res/drawable-hdpi/ic_toggle_star_outline.png | Bin 0 -> 1322 bytes .../res/drawable-mdpi/ic_action_rating_bad.png | Bin 0 -> 469 bytes .../ic_action_rating_bad_selected.png | Bin 345 -> 374 bytes .../res/drawable-mdpi/ic_action_rating_good.png | Bin 0 -> 459 bytes .../ic_action_rating_good_selected.png | Bin 326 -> 357 bytes app/src/main/res/drawable-mdpi/ic_toggle_star.png | Bin 612 -> 664 bytes .../res/drawable-mdpi/ic_toggle_star_outline.png | Bin 0 -> 844 bytes .../res/drawable-xhdpi/ic_action_rating_bad.png | Bin 0 -> 898 bytes .../ic_action_rating_bad_selected.png | Bin 655 -> 681 bytes .../res/drawable-xhdpi/ic_action_rating_good.png | Bin 0 -> 900 bytes .../ic_action_rating_good_selected.png | Bin 648 -> 665 bytes app/src/main/res/drawable-xhdpi/ic_toggle_star.png | Bin 1260 -> 1367 bytes .../res/drawable-xhdpi/ic_toggle_star_outline.png | Bin 0 -> 1864 bytes .../res/drawable-xxhdpi/ic_action_rating_bad.png | Bin 0 -> 1662 bytes .../ic_action_rating_bad_selected.png | Bin 1018 -> 1074 bytes .../res/drawable-xxhdpi/ic_action_rating_good.png | Bin 0 -> 1681 bytes .../ic_action_rating_good_selected.png | Bin 1085 -> 1116 bytes .../main/res/drawable-xxhdpi/ic_toggle_star.png | Bin 1930 -> 2060 bytes .../res/drawable-xxhdpi/ic_toggle_star_outline.png | Bin 0 -> 2882 bytes .../res/drawable-xxxhdpi/ic_action_rating_bad.png | Bin 0 -> 1730 bytes .../ic_action_rating_bad_selected.png | Bin 1558 -> 1603 bytes .../res/drawable-xxxhdpi/ic_action_rating_good.png | Bin 0 -> 1731 bytes .../ic_action_rating_good_selected.png | Bin 1695 -> 1650 bytes .../main/res/drawable-xxxhdpi/ic_toggle_star.png | Bin 2798 -> 2986 bytes .../drawable-xxxhdpi/ic_toggle_star_outline.png | Bin 0 -> 4166 bytes 34 files changed, 139 insertions(+), 32 deletions(-) create mode 100644 app/src/main/res/drawable-hdpi/ic_action_rating_bad.png create mode 100644 app/src/main/res/drawable-hdpi/ic_action_rating_good.png create mode 100644 app/src/main/res/drawable-hdpi/ic_toggle_star_outline.png create mode 100644 app/src/main/res/drawable-mdpi/ic_action_rating_bad.png create mode 100644 app/src/main/res/drawable-mdpi/ic_action_rating_good.png create mode 100644 app/src/main/res/drawable-mdpi/ic_toggle_star_outline.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_action_rating_bad.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_action_rating_good.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_toggle_star_outline.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_action_rating_bad.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_action_rating_good.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_toggle_star_outline.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_action_rating_good.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_toggle_star_outline.png diff --git a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java index 576791b2..d33fa527 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java +++ b/app/src/main/java/github/daneren2005/dsub/service/DownloadService.java @@ -103,6 +103,7 @@ public class DownloadService extends Service { public static final int REWIND = 10000; private static final double DELETE_CUTOFF = 0.84; private static final int REQUIRED_ALBUM_MATCHES = 4; + private static final int REMOTE_PLAYLIST_TOTAL = 3; private static final int SHUFFLE_MODE_NONE = 0; private static final int SHUFFLE_MODE_ALL = 1; private static final int SHUFFLE_MODE_ARTIST = 2; @@ -448,10 +449,21 @@ public class DownloadService extends Service { lifecycleSupport.serializeDownloadQueue(); } - private void updateRemotePlaylist() { + private synchronized void updateRemotePlaylist() { + List playlist = new ArrayList<>(); + if(currentPlaying != null) { + int index = downloadList.indexOf(currentPlaying); + int size = size(); + int end = index + REMOTE_PLAYLIST_TOTAL; + for(int i = index; i < size && i < end; i++) { + playlist.add(downloadList.get(i)); + } + } + if (remoteState != LOCAL && remoteController != null) { remoteController.updatePlaylist(); } + mRemoteControl.updatePlaylist(playlist); } public synchronized void restore(List songs, List toDelete, int currentPlayingIndex, int currentPlayingPosition) { @@ -1026,6 +1038,7 @@ public class DownloadService extends Service { proxy = null; } checkDownloads(); + updateRemotePlaylist(); } /** Plays or resumes the playback, depending on the current player state. */ @@ -1762,6 +1775,8 @@ public class DownloadService extends Service { setPlayerState(PAUSED); onSongProgress(); } + + updateRemotePlaylist(); } // Only call when starting, setPlayerState(PAUSED) already calls this @@ -2604,6 +2619,13 @@ public class DownloadService extends Service { } }); } + + handler.post(new Runnable() { + @Override + public void run() { + mRemoteControl.setPlaybackState(playerState.getRemoteControlClientPlayState()); + } + }); } private void onStateUpdate() { final long atRevision = revision; @@ -2631,6 +2653,13 @@ public class DownloadService extends Service { } }); } + + handler.post(new Runnable() { + @Override + public void run() { + mRemoteControl.metadataChanged(currentPlaying.getSong()); + } + }); } private class BufferTask extends SilentBackgroundTask { diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java index b5987270..1f7035dc 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java @@ -1,12 +1,16 @@ package github.daneren2005.dsub.util.compat; import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.service.DownloadFile; + import android.content.ComponentName; import android.content.Context; import android.graphics.Bitmap; import android.support.v7.media.MediaRouter; import android.os.Build; +import java.util.List; + public abstract class RemoteControlClientBase { public static RemoteControlClientBase createInstance() { @@ -25,10 +29,11 @@ public abstract class RemoteControlClientBase { 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); + public abstract void setPlaybackState(int state); + public abstract void updateMetadata(Context context, MusicDirectory.Entry currentSong); + public abstract void metadataChanged(MusicDirectory.Entry currentSong); public abstract void updateAlbumArt(MusicDirectory.Entry currentSong, Bitmap bitmap); public abstract void registerRoute(MediaRouter router); public abstract void unregisterRoute(MediaRouter router); - + public abstract void updatePlaylist(List playlist); } diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java index 994c23a6..55002c4b 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java @@ -1,6 +1,7 @@ package github.daneren2005.dsub.util.compat; import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.service.DownloadFile; import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.util.ImageLoader; import android.annotation.TargetApi; @@ -14,6 +15,8 @@ import android.media.MediaMetadataRetriever; import android.media.RemoteControlClient; import android.support.v7.media.MediaRouter; +import java.util.List; + import github.daneren2005.dsub.activity.SubsonicActivity; @TargetApi(14) @@ -79,6 +82,11 @@ public class RemoteControlClientICS extends RemoteControlClientBase { } } + @Override + public void metadataChanged(MusicDirectory.Entry currentSong) { + + } + @Override public void updateAlbumArt(MusicDirectory.Entry currentSong, Bitmap bitmap) { mRemoteControl.editMetadata(true) @@ -104,6 +112,11 @@ public class RemoteControlClientICS extends RemoteControlClientBase { router.removeRemoteControlClient(mRemoteControl); } + @Override + public void updatePlaylist(List playlist) { + + } + protected void updateMetadata(final MusicDirectory.Entry currentSong, final RemoteControlClient.MetadataEditor editor) { editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, (currentSong == null) ? null : currentSong.getArtist()) .putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, (currentSong == null) ? null : currentSong.getAlbum()) diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java index e2cbb307..b6a38199 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.media.AudioAttributes; +import android.media.MediaDescription; import android.media.MediaMetadata; import android.media.Rating; import android.media.RemoteControlClient; @@ -33,7 +34,9 @@ import android.media.session.PlaybackState; import android.os.Build; import android.os.Bundle; import android.support.v7.media.MediaRouter; -import android.util.Log; + +import java.util.ArrayList; +import java.util.List; import github.daneren2005.dsub.R; import github.daneren2005.dsub.activity.SubsonicActivity; @@ -43,7 +46,6 @@ import github.daneren2005.dsub.service.DownloadFile; import github.daneren2005.dsub.service.DownloadService; import github.daneren2005.dsub.util.Constants; import github.daneren2005.dsub.util.ImageLoader; -import github.daneren2005.dsub.util.UpdateHelper; @TargetApi(Build.VERSION_CODES.LOLLIPOP) public class RemoteControlClientLP extends RemoteControlClientBase { @@ -51,10 +53,19 @@ public class RemoteControlClientLP extends RemoteControlClientBase { private static final String CUSTOM_ACTION_THUMBS_UP = "github.daneren2005.dsub.THUMBS_UP"; private static final String CUSTOM_ACTION_THUMBS_DOWN = "github.daneren2005.dsub.THUMBS_DOWN"; private static final String CUSTOM_ACTION_STAR = "github.daneren2005.dsub.STAR"; + // Copied from MediaControlConstants so I did not have to include the entire Wear SDK just for these constant + private static final String SHOW_ON_WEAR = "android.support.wearable.media.extra.CUSTOM_ACTION_SHOW_ON_WEAR"; + private static final String WEAR_RESERVE_SKIP_TO_NEXT = "android.support.wearable.media.extra.RESERVE_SLOT_SKIP_TO_NEXT"; + private static final String WEAR_RESERVE_SKIP_TO_PREVIOUS = "android.support.wearable.media.extra.RESERVE_SLOT_SKIP_TO_PREVIOUS"; + // These constants don't seem to exist anywhere in the SDK. Grabbed from Google's sample media player app + private static final String AUTO_RESERVE_SKIP_TO_NEXT = "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_NEXT"; + private static final String AUTO_RESERVE_SKIP_TO_PREVIOUS = "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS"; protected MediaSession mediaSession; protected DownloadService downloadService; protected ImageLoader imageLoader; + protected List currentQueue; + protected int previousState; @Override public void register(Context context, ComponentName mediaButtonReceiverComponent) { @@ -75,16 +86,19 @@ public class RemoteControlClientLP extends RemoteControlClientBase { mediaSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS); mediaSession.setCallback(new EventCallback()); - if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) { - mediaSession.setRatingType(Rating.RATING_THUMB_UP_DOWN); - } - AudioAttributes.Builder audioAttributesBuilder = new AudioAttributes.Builder(); audioAttributesBuilder.setUsage(AudioAttributes.USAGE_MEDIA) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC); mediaSession.setPlaybackToLocal(audioAttributesBuilder.build()); mediaSession.setActive(true); + Bundle sessionExtras = new Bundle(); + sessionExtras.putBoolean(WEAR_RESERVE_SKIP_TO_PREVIOUS, true); + sessionExtras.putBoolean(WEAR_RESERVE_SKIP_TO_NEXT, true); + sessionExtras.putBoolean(AUTO_RESERVE_SKIP_TO_PREVIOUS, true); + sessionExtras.putBoolean(AUTO_RESERVE_SKIP_TO_NEXT, true); + mediaSession.setExtras(sessionExtras); + imageLoader = SubsonicActivity.getStaticImageLoader(context); } @@ -122,11 +136,14 @@ public class RemoteControlClientLP extends RemoteControlClientBase { DownloadFile downloadFile = downloadService.getCurrentPlaying(); if(downloadFile != null) { - addCustomActions(downloadFile.getSong(), builder); + MusicDirectory.Entry entry = downloadFile.getSong(); + addCustomActions(entry, builder); + builder.setActiveQueueItemId(entry.getId().hashCode()); } PlaybackState playbackState = builder.build(); mediaSession.setPlaybackState(playbackState); + previousState = state; } @Override @@ -138,6 +155,11 @@ public class RemoteControlClientLP extends RemoteControlClientBase { } } + @Override + public void metadataChanged(MusicDirectory.Entry currentSong) { + setPlaybackState(previousState); + } + public void setMetadata(MusicDirectory.Entry currentSong, Bitmap bitmap) { MediaMetadata.Builder builder = new MediaMetadata.Builder(); builder.putString(MediaMetadata.METADATA_KEY_ARTIST, (currentSong == null) ? null : currentSong.getArtist()) @@ -150,17 +172,6 @@ public class RemoteControlClientLP extends RemoteControlClientBase { .putLong(MediaMetadata.METADATA_KEY_DURATION, (currentSong == null) ? 0 : ((currentSong.getDuration() == null) ? 0 : (currentSong.getDuration() * 1000))); - int userRating = currentSong.getRating(); - Rating rating; - if(userRating == 1) { - rating = Rating.newThumbRating(false); - } else if(userRating == 5) { - rating = Rating.newThumbRating(true); - } else { - rating = Rating.newUnratedRating(Rating.RATING_THUMB_UP_DOWN); - } - builder.putRating(MediaMetadata.METADATA_KEY_USER_RATING, rating); - if(bitmap != null) { builder.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap); } @@ -183,31 +194,68 @@ public class RemoteControlClientLP extends RemoteControlClientBase { router.setMediaSession(null); } + @Override + public void updatePlaylist(List playlist) { + List queue = new ArrayList<>(); + + for(DownloadFile file: playlist) { + MusicDirectory.Entry entry = file.getSong(); + + MediaDescription description = new MediaDescription.Builder() + .setMediaId(entry.getId()) + .setTitle(entry.getTitle()) + .setSubtitle(entry.getAlbumDisplay()) + .build(); + MediaSession.QueueItem item = new MediaSession.QueueItem(description, entry.getId().hashCode()); + queue.add(item); + } + + mediaSession.setQueue(queue); + currentQueue = playlist; + } + public MediaSession getMediaSession() { return mediaSession; } protected long getPlaybackActions() { - return PlaybackState.ACTION_PLAY | + long actions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_SEEK_TO | - PlaybackState.ACTION_SKIP_TO_NEXT | - PlaybackState.ACTION_SKIP_TO_PREVIOUS; + PlaybackState.ACTION_SKIP_TO_QUEUE_ITEM; + + int currentIndex = downloadService.getCurrentPlayingIndex(); + int size = downloadService.size(); + if(currentIndex > 0) { + actions |= PlaybackState.ACTION_SKIP_TO_PREVIOUS; + } + if(currentIndex < size - 1) { + actions |= PlaybackState.ACTION_SKIP_TO_NEXT; + } + + return actions; } protected void addCustomActions(MusicDirectory.Entry currentSong, PlaybackState.Builder builder) { + Bundle showOnWearExtras = new Bundle(); + showOnWearExtras.putBoolean(SHOW_ON_WEAR, true); + + int rating = currentSong.getRating(); PlaybackState.CustomAction thumbsUp = new PlaybackState.CustomAction.Builder(CUSTOM_ACTION_THUMBS_UP, - downloadService.getString(R.string.download_thumbs_up), - R.drawable.ic_action_rating_good_selected).build(); + downloadService.getString(R.string.download_thumbs_up), + rating == 5 ? R.drawable.ic_action_rating_good_selected : R.drawable.ic_action_rating_good) + .setExtras(showOnWearExtras).build(); PlaybackState.CustomAction thumbsDown = new PlaybackState.CustomAction.Builder(CUSTOM_ACTION_THUMBS_DOWN, - downloadService.getString(R.string.download_thumbs_down), - R.drawable.ic_action_rating_bad_selected).build(); + downloadService.getString(R.string.download_thumbs_down), + rating == 1 ? R.drawable.ic_action_rating_bad_selected : R.drawable.ic_action_rating_bad) + .setExtras(showOnWearExtras).build(); PlaybackState.CustomAction star = new PlaybackState.CustomAction.Builder(CUSTOM_ACTION_STAR, - downloadService.getString(R.string.common_star), - R.drawable.ic_toggle_star).build(); + downloadService.getString(R.string.common_star), + currentSong.isStarred() ? R.drawable.ic_toggle_star : R.drawable.ic_toggle_star_outline) + .setExtras(showOnWearExtras).build(); - builder.addCustomAction(star).addCustomAction(thumbsDown).addCustomAction(thumbsUp); + builder.addCustomAction(thumbsDown).addCustomAction(star).addCustomAction(thumbsUp); } private class EventCallback extends MediaSession.Callback { @@ -240,6 +288,18 @@ public class RemoteControlClientLP extends RemoteControlClientBase { downloadService.previous(); } + @Override + public void onSkipToQueueItem(long queueId) { + if(currentQueue != null) { + for(DownloadFile file: currentQueue) { + if(file.getSong().getId().hashCode() == queueId) { + downloadService.play(file); + return; + } + } + } + } + @Override public void onCustomAction(String action, Bundle extras) { if(CUSTOM_ACTION_THUMBS_UP.equals(action)) { diff --git a/app/src/main/res/drawable-hdpi/ic_action_rating_bad.png b/app/src/main/res/drawable-hdpi/ic_action_rating_bad.png new file mode 100644 index 00000000..7f06020b Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_rating_bad.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-hdpi/ic_action_rating_bad_selected.png index 911eea25..b144900f 100644 Binary files a/app/src/main/res/drawable-hdpi/ic_action_rating_bad_selected.png and b/app/src/main/res/drawable-hdpi/ic_action_rating_bad_selected.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_rating_good.png b/app/src/main/res/drawable-hdpi/ic_action_rating_good.png new file mode 100644 index 00000000..42640dbb Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_rating_good.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-hdpi/ic_action_rating_good_selected.png index 09417791..7096c8c2 100644 Binary files a/app/src/main/res/drawable-hdpi/ic_action_rating_good_selected.png and b/app/src/main/res/drawable-hdpi/ic_action_rating_good_selected.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_toggle_star.png b/app/src/main/res/drawable-hdpi/ic_toggle_star.png index f9ef8772..e62e5803 100644 Binary files a/app/src/main/res/drawable-hdpi/ic_toggle_star.png and b/app/src/main/res/drawable-hdpi/ic_toggle_star.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_toggle_star_outline.png b/app/src/main/res/drawable-hdpi/ic_toggle_star_outline.png new file mode 100644 index 00000000..fead34b3 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_toggle_star_outline.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_rating_bad.png b/app/src/main/res/drawable-mdpi/ic_action_rating_bad.png new file mode 100644 index 00000000..6cb87ce9 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_rating_bad.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-mdpi/ic_action_rating_bad_selected.png index ac563748..e96fc42e 100644 Binary files a/app/src/main/res/drawable-mdpi/ic_action_rating_bad_selected.png and b/app/src/main/res/drawable-mdpi/ic_action_rating_bad_selected.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_rating_good.png b/app/src/main/res/drawable-mdpi/ic_action_rating_good.png new file mode 100644 index 00000000..21ca0f92 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_rating_good.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-mdpi/ic_action_rating_good_selected.png index 197aa106..9340e80f 100644 Binary files a/app/src/main/res/drawable-mdpi/ic_action_rating_good_selected.png and b/app/src/main/res/drawable-mdpi/ic_action_rating_good_selected.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_toggle_star.png b/app/src/main/res/drawable-mdpi/ic_toggle_star.png index 6d949fd0..570104dc 100644 Binary files a/app/src/main/res/drawable-mdpi/ic_toggle_star.png and b/app/src/main/res/drawable-mdpi/ic_toggle_star.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_toggle_star_outline.png b/app/src/main/res/drawable-mdpi/ic_toggle_star_outline.png new file mode 100644 index 00000000..656336d6 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_toggle_star_outline.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_rating_bad.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad.png new file mode 100644 index 00000000..8d36c8e7 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_selected.png index 98494b1f..a88e9483 100644 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_selected.png and b/app/src/main/res/drawable-xhdpi/ic_action_rating_bad_selected.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_rating_good.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_good.png new file mode 100644 index 00000000..88f6a6cb Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_rating_good.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-xhdpi/ic_action_rating_good_selected.png index f5065d18..5e30e89e 100644 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_rating_good_selected.png and b/app/src/main/res/drawable-xhdpi/ic_action_rating_good_selected.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_toggle_star.png b/app/src/main/res/drawable-xhdpi/ic_toggle_star.png index 5c54298c..7b628888 100644 Binary files a/app/src/main/res/drawable-xhdpi/ic_toggle_star.png and b/app/src/main/res/drawable-xhdpi/ic_toggle_star.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_toggle_star_outline.png b/app/src/main/res/drawable-xhdpi/ic_toggle_star_outline.png new file mode 100644 index 00000000..55a2d40b Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_toggle_star_outline.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad.png new file mode 100644 index 00000000..a09751b7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_selected.png index 945bb594..5adde418 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_selected.png and b/app/src/main/res/drawable-xxhdpi/ic_action_rating_bad_selected.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_rating_good.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good.png new file mode 100644 index 00000000..7adc2c24 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_selected.png index 273615ea..de797727 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_selected.png and b/app/src/main/res/drawable-xxhdpi/ic_action_rating_good_selected.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_toggle_star.png b/app/src/main/res/drawable-xxhdpi/ic_toggle_star.png index 4db2a37c..00e7c347 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_toggle_star.png and b/app/src/main/res/drawable-xxhdpi/ic_toggle_star.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_toggle_star_outline.png b/app/src/main/res/drawable-xxhdpi/ic_toggle_star_outline.png new file mode 100644 index 00000000..096ebba7 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_toggle_star_outline.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad.png b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad.png new file mode 100644 index 00000000..6c358480 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_selected.png b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_selected.png index f585ba00..0088b4ef 100644 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_selected.png and b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_bad_selected.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good.png b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good.png new file mode 100644 index 00000000..b949b53b Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_selected.png b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_selected.png index 9ee267f7..3fca95ef 100644 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_selected.png and b/app/src/main/res/drawable-xxxhdpi/ic_action_rating_good_selected.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_toggle_star.png b/app/src/main/res/drawable-xxxhdpi/ic_toggle_star.png index 6116210d..ce14f384 100644 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_toggle_star.png and b/app/src/main/res/drawable-xxxhdpi/ic_toggle_star.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_toggle_star_outline.png b/app/src/main/res/drawable-xxxhdpi/ic_toggle_star_outline.png new file mode 100644 index 00000000..c39bbcf5 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_toggle_star_outline.png differ -- cgit v1.2.3 From b2645d6de60eec05c4435681655b6647c639d13a Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Tue, 13 Oct 2015 18:04:45 -0700 Subject: Apply fix to timer not resetting from https://github.com/popeen/Popeens-DSub/commit/992ebba143119b00447687cf3d3e13ae2ac6783f --- .../daneren2005/dsub/fragments/NowPlayingFragment.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java index a6517bf6..5db79e99 100644 --- a/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java +++ b/app/src/main/java/github/daneren2005/dsub/fragments/NowPlayingFragment.java @@ -428,7 +428,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis if(downloadService != null && downloadService.getSleepTimer()) { int timeRemaining = downloadService.getSleepTimeRemaining(); timerMenu = menu.findItem(R.id.menu_toggle_timer); - timerMenu.setTitle(context.getResources().getString(R.string.download_stop_time_remaining, Util.formatDuration(timeRemaining))); + if(timeRemaining > 1){ + timerMenu.setTitle(context.getResources().getString(R.string.download_stop_time_remaining, Util.formatDuration(timeRemaining))); + } else { + timerMenu.setTitle(R.string.menu_set_timer); + } } if(downloadService != null && downloadService.getKeepScreenOn()) { menu.findItem(R.id.menu_screen_on_off).setChecked(true); @@ -1232,7 +1236,11 @@ public class NowPlayingFragment extends SubsonicFragment implements OnGestureLis DownloadService downloadService = getDownloadService(); if(downloadService.getSleepTimer() && timerMenu != null) { int timeRemaining = downloadService.getSleepTimeRemaining(); - timerMenu.setTitle(context.getResources().getString(R.string.download_stop_time_remaining, Util.formatDuration(timeRemaining))); + if(timeRemaining > 1){ + timerMenu.setTitle(context.getResources().getString(R.string.download_stop_time_remaining, Util.formatDuration(timeRemaining))); + } else { + timerMenu.setTitle(R.string.menu_set_timer); + } } } -- cgit v1.2.3 From 374b737332e44b59e70892578881c59ef33d9e8d Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Wed, 14 Oct 2015 17:01:14 -0700 Subject: Make wear notification controls match theme color --- .../java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java index b6a38199..7a54983d 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java @@ -57,6 +57,7 @@ public class RemoteControlClientLP extends RemoteControlClientBase { private static final String SHOW_ON_WEAR = "android.support.wearable.media.extra.CUSTOM_ACTION_SHOW_ON_WEAR"; private static final String WEAR_RESERVE_SKIP_TO_NEXT = "android.support.wearable.media.extra.RESERVE_SLOT_SKIP_TO_NEXT"; private static final String WEAR_RESERVE_SKIP_TO_PREVIOUS = "android.support.wearable.media.extra.RESERVE_SLOT_SKIP_TO_PREVIOUS"; + private static final String WEAR_BACKGROUND_THEME = "android.support.wearable.media.extra.BACKGROUND_COLOR_FROM_THEME"; // These constants don't seem to exist anywhere in the SDK. Grabbed from Google's sample media player app private static final String AUTO_RESERVE_SKIP_TO_NEXT = "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_NEXT"; private static final String AUTO_RESERVE_SKIP_TO_PREVIOUS = "com.google.android.gms.car.media.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS"; @@ -93,6 +94,7 @@ public class RemoteControlClientLP extends RemoteControlClientBase { mediaSession.setActive(true); Bundle sessionExtras = new Bundle(); + sessionExtras.putBoolean(WEAR_BACKGROUND_THEME, true); sessionExtras.putBoolean(WEAR_RESERVE_SKIP_TO_PREVIOUS, true); sessionExtras.putBoolean(WEAR_RESERVE_SKIP_TO_NEXT, true); sessionExtras.putBoolean(AUTO_RESERVE_SKIP_TO_PREVIOUS, true); -- cgit v1.2.3 From 15e0bf9982b5235b3934c05093ff6b517524d86b Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Mon, 19 Oct 2015 17:08:50 -0700 Subject: #480 Add support for searching within Auto --- .../daneren2005/dsub/domain/SearchResult.java | 10 ++ .../daneren2005/dsub/util/BackgroundTask.java | 3 + .../daneren2005/dsub/util/SilentServiceTask.java | 41 ++++++ .../dsub/util/compat/RemoteControlClientLP.java | 150 ++++++++++++++++++++- 4 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/github/daneren2005/dsub/util/SilentServiceTask.java diff --git a/app/src/main/java/github/daneren2005/dsub/domain/SearchResult.java b/app/src/main/java/github/daneren2005/dsub/domain/SearchResult.java index 3427f2ca..4d761b02 100644 --- a/app/src/main/java/github/daneren2005/dsub/domain/SearchResult.java +++ b/app/src/main/java/github/daneren2005/dsub/domain/SearchResult.java @@ -49,4 +49,14 @@ public class SearchResult implements Serializable { public List getSongs() { return songs; } + + public boolean hasArtists() { + return !artists.isEmpty(); + } + public boolean hasAlbums() { + return !albums.isEmpty(); + } + public boolean hasSongs() { + return !songs.isEmpty(); + } } \ No newline at end of file diff --git a/app/src/main/java/github/daneren2005/dsub/util/BackgroundTask.java b/app/src/main/java/github/daneren2005/dsub/util/BackgroundTask.java index 30081d7d..18f245d5 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/BackgroundTask.java +++ b/app/src/main/java/github/daneren2005/dsub/util/BackgroundTask.java @@ -93,6 +93,9 @@ public abstract class BackgroundTask implements ProgressListener { return (context instanceof Activity) ? ((Activity) context) : null; } + protected Context getContext() { + return context; + } protected Handler getHandler() { return handler; } diff --git a/app/src/main/java/github/daneren2005/dsub/util/SilentServiceTask.java b/app/src/main/java/github/daneren2005/dsub/util/SilentServiceTask.java new file mode 100644 index 00000000..9fdfe66f --- /dev/null +++ b/app/src/main/java/github/daneren2005/dsub/util/SilentServiceTask.java @@ -0,0 +1,41 @@ +/* + 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 2015 (C) Scott Jackson +*/ + +package github.daneren2005.dsub.util; + +import android.content.Context; + +import github.daneren2005.dsub.service.MusicService; +import github.daneren2005.dsub.service.MusicServiceFactory; + +public abstract class SilentServiceTask extends SilentBackgroundTask { + protected MusicService musicService; + + public SilentServiceTask(Context context) { + super(context); + } + + @Override + protected T doInBackground() throws Throwable { + musicService = MusicServiceFactory.getMusicService(getContext()); + return doInBackground(musicService); + } + + protected abstract T doInBackground(MusicService musicService) throws Throwable; +} diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java index 7a54983d..c9f8cab8 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java @@ -23,16 +23,17 @@ import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.graphics.Bitmap; import android.media.AudioAttributes; import android.media.MediaDescription; import android.media.MediaMetadata; -import android.media.Rating; import android.media.RemoteControlClient; import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.os.Build; import android.os.Bundle; +import android.provider.MediaStore; import android.support.v7.media.MediaRouter; import java.util.ArrayList; @@ -42,10 +43,16 @@ import github.daneren2005.dsub.R; import github.daneren2005.dsub.activity.SubsonicActivity; import github.daneren2005.dsub.activity.SubsonicFragmentActivity; import github.daneren2005.dsub.domain.MusicDirectory; +import github.daneren2005.dsub.domain.Playlist; +import github.daneren2005.dsub.domain.SearchCritera; +import github.daneren2005.dsub.domain.SearchResult; import github.daneren2005.dsub.service.DownloadFile; import github.daneren2005.dsub.service.DownloadService; +import github.daneren2005.dsub.service.MusicService; import github.daneren2005.dsub.util.Constants; import github.daneren2005.dsub.util.ImageLoader; +import github.daneren2005.dsub.util.SilentServiceTask; +import github.daneren2005.dsub.util.Util; @TargetApi(Build.VERSION_CODES.LOLLIPOP) public class RemoteControlClientLP extends RemoteControlClientBase { @@ -260,6 +267,91 @@ public class RemoteControlClientLP extends RemoteControlClientBase { builder.addCustomAction(thumbsDown).addCustomAction(star).addCustomAction(thumbsUp); } + private void searchPlaylist(final String name) { + new SilentServiceTask(downloadService) { + @Override + protected Void doInBackground(MusicService musicService) throws Throwable { + List playlists = musicService.getPlaylists(false, downloadService, null); + for(Playlist playlist: playlists) { + if(playlist.getName().equals(name)) { + getPlaylist(playlist); + return null; + } + } + + noResults(); + return null; + } + + private void getPlaylist(Playlist playlist) throws Exception { + MusicDirectory musicDirectory = musicService.getPlaylist(false, playlist.getId(), playlist.getName(), downloadService, null); + playSongs(musicDirectory.getChildren()); + } + }.execute(); + } + private void searchCriteria(final SearchCritera searchCritera) { + new SilentServiceTask(downloadService) { + @Override + protected Void doInBackground(MusicService musicService) throws Throwable { + SearchResult results = musicService.search(searchCritera, downloadService, null); + + if(results.hasArtists()) { + playFromParent(new MusicDirectory.Entry(results.getArtists().get(0))); + } else if(results.hasAlbums()) { + playFromParent(results.getAlbums().get(0)); + } else if(results.hasSongs()) { + playSong(results.getSongs().get(0)); + } else { + noResults(); + } + + return null; + } + + private void playFromParent(MusicDirectory.Entry parent) throws Exception { + List songs = new ArrayList<>(); + getSongsRecursively(parent, songs); + playSongs(songs); + } + private void getSongsRecursively(MusicDirectory.Entry parent, List songs) throws Exception { + MusicDirectory musicDirectory; + if(Util.isTagBrowsing(downloadService) && !Util.isOffline(downloadService)) { + musicDirectory = musicService.getAlbum(parent.getId(), parent.getTitle(), false, downloadService, this); + } else { + musicDirectory = musicService.getMusicDirectory(parent.getId(), parent.getTitle(), false, downloadService, this); + } + + for (MusicDirectory.Entry dir : musicDirectory.getChildren(true, false)) { + if (dir.getRating() == 1) { + continue; + } + + getSongsRecursively(dir, songs); + } + + for (MusicDirectory.Entry song : musicDirectory.getChildren(false, true)) { + if (!song.isVideo() && song.getRating() != 1) { + songs.add(song); + } + } + } + }.execute(); + } + + private void playSong(MusicDirectory.Entry entry) { + List entries = new ArrayList<>(); + entries.add(entry); + playSongs(entries); + } + private void playSongs(List entries) { + downloadService.clear(); + downloadService.download(entries, false, true, false, false); + } + + private void noResults() { + + } + private class EventCallback extends MediaSession.Callback { @Override public void onPlay() { @@ -302,6 +394,62 @@ public class RemoteControlClientLP extends RemoteControlClientBase { } } + @Override + public void onPlayFromSearch (String query, Bundle extras) { + // User just asked to playing something + if("".equals(query)) { + downloadService.setShufflePlayEnabled(true); + } else { + String mediaFocus = extras.getString(MediaStore.EXTRA_MEDIA_FOCUS); + + + // Play a specific playlist + if (MediaStore.Audio.Playlists.ENTRY_CONTENT_TYPE.equals(mediaFocus)) { + String playlist = extras.getString(MediaStore.EXTRA_MEDIA_PLAYLIST); + searchPlaylist(playlist); + } + // Play a specific genre + else if (MediaStore.Audio.Genres.ENTRY_CONTENT_TYPE.equals(mediaFocus)) { + String genre = extras.getString(MediaStore.EXTRA_MEDIA_GENRE); + + SharedPreferences.Editor editor = Util.getPreferences(downloadService).edit(); + editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_START_YEAR, null); + editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_END_YEAR, null); + editor.putString(Constants.PREFERENCES_KEY_SHUFFLE_GENRE, genre); + editor.commit(); + + downloadService.setShufflePlayEnabled(true); + } + else { + int artists = 10; + int albums = 10; + int songs = 10; + + // Play a specific artist + if (MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE.equals(mediaFocus)) { + query = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST); + albums = 0; + songs = 0; + } + // Play a specific album + else if (MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE.equals(mediaFocus)) { + query = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM); + artists = 0; + songs = 0 ; + } + // Play a specific song + else if (MediaStore.Audio.Media.ENTRY_CONTENT_TYPE.equals(mediaFocus)) { + query = extras.getString(MediaStore.EXTRA_MEDIA_TITLE); + artists = 0; + albums = 0; + } + + SearchCritera criteria = new SearchCritera(query, artists, albums, songs); + searchCriteria(criteria); + } + } + } + @Override public void onCustomAction(String action, Bundle extras) { if(CUSTOM_ACTION_THUMBS_UP.equals(action)) { -- cgit v1.2.3 From 105e63e74994004c510fdada63d03086bbb56752 Mon Sep 17 00:00:00 2001 From: Scott Jackson Date: Thu, 22 Oct 2015 17:52:02 -0700 Subject: #480 Add playlists browsing from Auto --- .../dsub/service/AutoMediaBrowserService.java | 177 ++++++++++++++++++--- .../github/daneren2005/dsub/util/Constants.java | 1 + .../dsub/util/compat/RemoteControlClientLP.java | 35 +++- 3 files changed, 191 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java b/app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java index 4d21d793..d579ef54 100644 --- a/app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java +++ b/app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java @@ -22,9 +22,9 @@ import android.annotation.TargetApi; import android.content.Intent; import android.media.MediaDescription; import android.media.browse.MediaBrowser; -import android.media.session.MediaSession; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.service.media.MediaBrowserService; import android.support.annotation.Nullable; import android.util.Log; @@ -32,6 +32,12 @@ import android.util.Log; import java.util.ArrayList; import java.util.List; +import github.daneren2005.dsub.R; +import github.daneren2005.dsub.domain.Playlist; +import github.daneren2005.dsub.domain.ServerInfo; +import github.daneren2005.dsub.util.Constants; +import github.daneren2005.dsub.util.SilentBackgroundTask; +import github.daneren2005.dsub.util.SilentServiceTask; import github.daneren2005.dsub.util.Util; import github.daneren2005.dsub.util.compat.RemoteControlClientLP; @@ -39,18 +45,22 @@ import github.daneren2005.dsub.util.compat.RemoteControlClientLP; public class AutoMediaBrowserService extends MediaBrowserService { private static final String TAG = AutoMediaBrowserService.class.getSimpleName(); private static final String BROWSER_ROOT = "root"; - MediaSession mediaSession; + private static final String BROWSER_ALBUM_LISTS = "albumLists"; + private static final String BROWSER_LIBRARY = "library"; + private static final String BROWSER_PLAYLISTS = "playlists"; + private static final String PLAYLIST_PREFIX = "pl-"; + private static final String ALBUM_TYPE_PREFIX = "ty-"; + + private DownloadService downloadService; + private Handler handler = new Handler(); @Override public void onCreate() { super.onCreate(); - - DownloadService downloadService = getDownloadService(); - RemoteControlClientLP remoteControlClient = (RemoteControlClientLP) downloadService.getRemoteControlClient(); - setSessionToken(remoteControlClient.getMediaSession().getSessionToken()); + getDownloadService(); } - @Nullable + @Nullable @Override public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) { BrowserRoot root = new BrowserRoot(BROWSER_ROOT, null); @@ -61,34 +71,161 @@ public class AutoMediaBrowserService extends MediaBrowserService { public void onLoadChildren(String parentId, Result> result) { if(BROWSER_ROOT.equals(parentId)) { getRootFolders(result); + } else if(BROWSER_ALBUM_LISTS.equals(parentId)) { + getAlbumLists(result); + } else if(parentId.startsWith(ALBUM_TYPE_PREFIX)) { + int id = Integer.valueOf(parentId.substring(ALBUM_TYPE_PREFIX.length())); + getAlbumList(result, id); + } else if(BROWSER_LIBRARY.equals(parentId)) { + getLibrary(result); + } else if(BROWSER_PLAYLISTS.equals(parentId)) { + getPlaylists(result); + } else if(parentId.startsWith(PLAYLIST_PREFIX)) { + getPlayOptions(result, parentId.substring(PLAYLIST_PREFIX.length()), Constants.INTENT_EXTRA_NAME_PLAYLIST_ID); } else { - + // No idea what it is, send empty result + result.sendResult(new ArrayList()); } } private void getRootFolders(Result> result) { List mediaItems = new ArrayList<>(); + /*MediaDescription.Builder albumLists = new MediaDescription.Builder(); + albumLists.setTitle(downloadService.getString(R.string.main_albums_title)) + .setMediaId(BROWSER_ALBUM_LISTS); + mediaItems.add(new MediaBrowser.MediaItem(albumLists.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE)); + MediaDescription.Builder library = new MediaDescription.Builder(); - library.setDescription("Library") - .setMediaId("library"); - mediaItems.add(new MediaBrowser.MediaItem(library.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE)); + library.setTitle(downloadService.getString(R.string.button_bar_browse)) + .setMediaId(BROWSER_LIBRARY); + mediaItems.add(new MediaBrowser.MediaItem(library.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE));*/ + + MediaDescription.Builder playlists = new MediaDescription.Builder(); + playlists.setTitle(downloadService.getString(R.string.button_bar_playlists)) + .setMediaId(BROWSER_PLAYLISTS); + mediaItems.add(new MediaBrowser.MediaItem(playlists.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE)); result.sendResult(mediaItems); } - public DownloadService getDownloadService() { - // If service is not available, request it to start and wait for it. - for (int i = 0; i < 5; i++) { - DownloadService downloadService = DownloadService.getInstance(); - if (downloadService != null) { - break; + private void getAlbumLists(Result> result) { + List albums = new ArrayList<>(); + albums.add(R.string.main_albums_newest); + albums.add(R.string.main_albums_random); + if(ServerInfo.checkServerVersion(downloadService, "1.8")) { + albums.add(R.string.main_albums_alphabetical); + } + if(!Util.isTagBrowsing(downloadService)) { + albums.add(R.string.main_albums_highest); + } + // albums.add(R.string.main_albums_starred); + // albums.add(R.string.main_albums_genres); + // albums.add(R.string.main_albums_year); + albums.add(R.string.main_albums_recent); + albums.add(R.string.main_albums_frequent); + + List mediaItems = new ArrayList<>(); + + for(Integer id: albums) { + MediaDescription description = new MediaDescription.Builder() + .setTitle(downloadService.getResources().getString(id)) + .setMediaId(ALBUM_TYPE_PREFIX + id) + .build(); + + mediaItems.add(new MediaBrowser.MediaItem(description, MediaBrowser.MediaItem.FLAG_BROWSABLE)); + } + + result.sendResult(mediaItems); + } + private void getAlbumList(Result> result, int id) { + + } + + private void getLibrary(Result> result) { + + } + + private void getPlaylists(final Result> result) { + new SilentServiceTask>(downloadService) { + @Override + protected List doInBackground(MusicService musicService) throws Throwable { + return musicService.getPlaylists(false, downloadService, null); } - Log.w(TAG, "DownloadService not running. Attempting to start it."); + + @Override + protected void done(List playlists) { + List mediaItems = new ArrayList<>(); + + for(Playlist playlist: playlists) { + MediaDescription description = new MediaDescription.Builder() + .setTitle(playlist.getName()) + .setMediaId(PLAYLIST_PREFIX + playlist.getId()) + .build(); + + mediaItems.add(new MediaBrowser.MediaItem(description, MediaBrowser.MediaItem.FLAG_BROWSABLE)); + } + + result.sendResult(mediaItems); + } + }.execute(); + + result.detach(); + } + private void getPlayOptions(Result> result, String id, String idConstant) { + List mediaItems = new ArrayList<>(); + + Bundle playAllExtras = new Bundle(); + playAllExtras.putString(idConstant, id); + + MediaDescription.Builder playAll = new MediaDescription.Builder(); + playAll.setTitle(downloadService.getString(R.string.menu_play)) + .setMediaId("play-" + id) + .setExtras(playAllExtras); + mediaItems.add(new MediaBrowser.MediaItem(playAll.build(), MediaBrowser.MediaItem.FLAG_PLAYABLE)); + + Bundle shuffleExtras = new Bundle(); + shuffleExtras.putString(idConstant, id); + shuffleExtras.putBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, true); + + MediaDescription.Builder shuffle = new MediaDescription.Builder(); + shuffle.setTitle(downloadService.getString(R.string.menu_shuffle)) + .setMediaId("shuffle-" + id) + .setExtras(shuffleExtras); + mediaItems.add(new MediaBrowser.MediaItem(shuffle.build(), MediaBrowser.MediaItem.FLAG_PLAYABLE)); + + /*Bundle playLastExtras = new Bundle(); + playLastExtras.putString(idConstant, id); + playLastExtras.putBoolean(Constants.INTENT_EXTRA_PLAY_LAST, true); + + MediaDescription.Builder playLast = new MediaDescription.Builder(); + playLast.setTitle(downloadService.getString(R.string.menu_play_last)) + .setMediaId("playLast-" + id) + .setExtras(playLastExtras); + mediaItems.add(new MediaBrowser.MediaItem(playLast.build(), MediaBrowser.MediaItem.FLAG_PLAYABLE));*/ + + result.sendResult(mediaItems); + } + + public void getDownloadService() { + if(DownloadService.getInstance() == null) { startService(new Intent(this, DownloadService.class)); - Util.sleepQuietly(50L); } - return DownloadService.getInstance(); + waitForDownloadService(); + } + public void waitForDownloadService() { + downloadService = DownloadService.getInstance(); + if(downloadService == null) { + handler.postDelayed(new Runnable() { + @Override + public void run() { + waitForDownloadService(); + } + }, 100); + } else { + RemoteControlClientLP remoteControlClient = (RemoteControlClientLP) downloadService.getRemoteControlClient(); + setSessionToken(remoteControlClient.getMediaSession().getSessionToken()); + } } } diff --git a/app/src/main/java/github/daneren2005/dsub/util/Constants.java b/app/src/main/java/github/daneren2005/dsub/util/Constants.java index 22463d9d..34cfac45 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/Constants.java +++ b/app/src/main/java/github/daneren2005/dsub/util/Constants.java @@ -66,6 +66,7 @@ public final class Constants { public static final String INTENT_EXTRA_SEARCH_SONG = "searchSong"; public static final String INTENT_EXTRA_TOP_TRACKS = "topTracks"; public static final String INTENT_EXTRA_SHOW_ALL = "showAll"; + public static final String INTENT_EXTRA_PLAY_LAST = "playLast"; // Preferences keys. public static final String PREFERENCES_KEY_SERVER_KEY = "server"; diff --git a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java index c9f8cab8..456446f3 100644 --- a/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java +++ b/app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java @@ -338,14 +338,31 @@ public class RemoteControlClientLP extends RemoteControlClientBase { }.execute(); } + private void playPlaylist(final Playlist playlist, final boolean shuffle, final boolean append) { + new SilentServiceTask(downloadService) { + @Override + protected Void doInBackground(MusicService musicService) throws Throwable { + MusicDirectory musicDirectory = musicService.getPlaylist(false, playlist.getId(), playlist.getName(), downloadService, null); + playSongs(musicDirectory.getChildren(), shuffle, append); + + return null; + } + }.execute(); + } + private void playSong(MusicDirectory.Entry entry) { List entries = new ArrayList<>(); entries.add(entry); playSongs(entries); } private void playSongs(List entries) { - downloadService.clear(); - downloadService.download(entries, false, true, false, false); + playSongs(entries, false, false); + } + private void playSongs(List entries, boolean shuffle, boolean append) { + if(!append) { + downloadService.clear(); + } + downloadService.download(entries, false, true, false, shuffle); } private void noResults() { @@ -450,6 +467,20 @@ public class RemoteControlClientLP extends RemoteControlClientBase { } } + public void onPlayFromMediaId (String mediaId, Bundle extras) { + if(extras == null) { + return; + } + + boolean shuffle = extras.getBoolean(Constants.INTENT_EXTRA_NAME_SHUFFLE, false); + boolean playLast = extras.getBoolean(Constants.INTENT_EXTRA_PLAY_LAST, false); + String playlistId = extras.getString(Constants.INTENT_EXTRA_NAME_PLAYLIST_ID, null); + if(playlistId != null) { + Playlist playlist = new Playlist(playlistId, null); + playPlaylist(playlist, shuffle, playLast); + } + } + @Override public void onCustomAction(String action, Bundle extras) { if(CUSTOM_ACTION_THUMBS_UP.equals(action)) { -- cgit v1.2.3