aboutsummaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorScott Jackson <daneren2005@gmail.com>2015-10-09 10:01:32 -0700
committerScott Jackson <daneren2005@gmail.com>2015-10-09 10:01:32 -0700
commit3da246de2ca75afaa03985a8a5a73c12bf1995c4 (patch)
treeadebb9094b4a8db6247a44ac88e12043933c5f1a /app
parente08a49b2ea562f7d845108c73f9ff22987e34f64 (diff)
downloaddsub-3da246de2ca75afaa03985a8a5a73c12bf1995c4.tar.gz
dsub-3da246de2ca75afaa03985a8a5a73c12bf1995c4.tar.bz2
dsub-3da246de2ca75afaa03985a8a5a73c12bf1995c4.zip
Start of Auto support
Diffstat (limited to 'app')
-rw-r--r--app/src/main/AndroidManifest.xml16
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/AutoMediaBrowserService.java94
-rw-r--r--app/src/main/java/github/daneren2005/dsub/service/DownloadService.java10
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientBase.java59
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientHelper.java32
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientICS.java2
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/compat/RemoteControlClientLP.java205
-rw-r--r--app/src/main/res/xml/auto_app_description.xml4
8 files changed, 350 insertions, 72 deletions
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 @@
<service android:name=".service.DownloadService"
android:label="DSub Playback Service"/>
+
+ <service android:name=".service.AutoMediaBrowserService"
+ android:exported="true">
+
+ <intent-filter>
+ <action android:name="android.media.browse.MediaBrowserService"/>
+ </intent-filter>
+ </service>
+
<service android:name="org.fourthline.cling.android.AndroidUpnpServiceImpl"/>
<service android:name="github.daneren2005.dsub.service.sync.AuthenticatorService">
<intent-filter>
@@ -241,6 +250,11 @@
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
- </application>
+
+ <meta-data android:name="com.google.android.gms.car.application"
+ android:resource="@xml/auto_app_description"/>
+ <meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
+ android:resource="@drawable/stat_notify_playing" />
+ </application>
</manifest>
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 <http://www.gnu.org/licenses/>.
+
+ 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<List<MediaBrowser.MediaItem>> result) {
+ if(BROWSER_ROOT.equals(parentId)) {
+ getRootFolders(result);
+ } else {
+
+ }
+ }
+
+ private void getRootFolders(Result<List<MediaBrowser.MediaItem>> result) {
+ List<MediaBrowser.MediaItem> 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<DownloadService>(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 <http://www.gnu.org/licenses/>.
+
+ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<automotiveApp>
+ <uses name="media"/>
+</automotiveApp> \ No newline at end of file