summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDomingos Lopes <domingos86lopes@gmail.com>2016-03-19 01:32:55 -0400
committerDomingos Lopes <domingos86lopes+github@gmail.com>2016-04-23 21:39:52 -0400
commit8061d94c1b2dcdc99fc0a3c4554a00f01ca4941f (patch)
treecdc3f9ca984e174ffcf517f1f2434f59e79990ee
parent1b6459c8ee2dfcb40a4909aa87ff2ea100b8221e (diff)
downloadAntennaPod-8061d94c1b2dcdc99fc0a3c4554a00f01ca4941f.zip
add chromecast support initialization logic and introduce chromecast preference
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/CastCallbacksImpl.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java1
-rw-r--r--app/src/main/res/xml/preferences.xml9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/CastCallbacks.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/CastUtils.java164
-rw-r--r--core/src/main/res/values/strings.xml2
8 files changed, 228 insertions, 4 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/config/CastCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/CastCallbacksImpl.java
new file mode 100644
index 000000000..b92544201
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/config/CastCallbacksImpl.java
@@ -0,0 +1,20 @@
+package de.danoeh.antennapod.config;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+
+import de.danoeh.antennapod.activity.AudioplayerActivity;
+import de.danoeh.antennapod.core.CastCallbacks;
+
+public class CastCallbacksImpl implements CastCallbacks {
+ @Override
+ public Class<? extends Activity> getCastActivity() {
+ return AudioplayerActivity.class;
+ }
+
+ @Override
+ public Intent getCastActivityIntent(Context context) {
+ return new Intent(context, getCastActivity());
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
index 932b9d22f..154b10cb3 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
@@ -16,5 +16,6 @@ public class ClientConfigurator {
ClientConfig.playbackServiceCallbacks = new PlaybackServiceCallbacksImpl();
ClientConfig.flattrCallbacks = new FlattrCallbacksImpl();
ClientConfig.dbTasksCallbacks = new DBTasksCallbacksImpl();
+ ClientConfig.castCallbacks = new CastCallbacksImpl();
}
}
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index ecdcd3517..57829e3e1 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -293,5 +293,14 @@
android:key="prefAbout"
android:title="@string/about_pref"/>
</PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/experimental_pref">
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
+ android:defaultValue="false"
+ android:enabled="true"
+ android:key="prefCast"
+ android:summary="@string/pref_cast_message"
+ android:title="@string/pref_cast_title"/>
+ </PreferenceCategory>
</PreferenceScreen>
diff --git a/core/src/main/java/de/danoeh/antennapod/core/CastCallbacks.java b/core/src/main/java/de/danoeh/antennapod/core/CastCallbacks.java
new file mode 100644
index 000000000..a2f17ba93
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/CastCallbacks.java
@@ -0,0 +1,16 @@
+package de.danoeh.antennapod.core;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Callbacks for the Cast features on the core module.
+ */
+public interface CastCallbacks {
+
+ Class<? extends Activity> getCastActivity();
+
+ Intent getCastActivityIntent(Context context);
+
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java
index a96affb23..d7735b34e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java
@@ -5,6 +5,7 @@ import android.content.Context;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.core.util.CastUtils;
import de.danoeh.antennapod.core.util.NetworkUtils;
/**
@@ -30,6 +31,8 @@ public class ClientConfig {
public static DBTasksCallbacks dbTasksCallbacks;
+ public static CastCallbacks castCallbacks;
+
private static boolean initialized = false;
public static synchronized void initialize(Context context) {
@@ -41,6 +44,7 @@ public class ClientConfig {
UpdateManager.init(context);
PlaybackPreferences.init(context);
NetworkUtils.init(context);
+ CastUtils.initializeCastManager(context);
initialized = true;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
index 569dfd2c4..879861a3f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
@@ -111,6 +111,7 @@ public class UserPreferences {
public static final String PREF_SONIC = "prefSonic";
public static final String PREF_STEREO_TO_MONO = "PrefStereoToMono";
public static final String PREF_NORMALIZER = "prefNormalizer";
+ public static final String PREF_CAST_ENABLED = "prefCast"; //Used for enabling Chromecast support
public static final int EPISODE_CLEANUP_QUEUE = -1;
public static final int EPISODE_CLEANUP_NULL = -2;
public static final int EPISODE_CLEANUP_DEFAULT = 0;
@@ -493,16 +494,16 @@ public class UserPreferences {
public static void setVolume(int leftVolume, int rightVolume) {
assert(0 <= leftVolume && leftVolume <= 100);
- assert(0 <= rightVolume && rightVolume <= 100);
+ assert (0 <= rightVolume && rightVolume <= 100);
prefs.edit()
- .putInt(PREF_LEFT_VOLUME, leftVolume)
+ .putInt(PREF_LEFT_VOLUME, leftVolume)
.putInt(PREF_RIGHT_VOLUME, rightVolume)
.apply();
}
public static void setAutodownloadSelectedNetworks(String[] value) {
prefs.edit()
- .putString(PREF_AUTODL_SELECTED_NETWORKS, TextUtils.join(",", value))
+ .putString(PREF_AUTODL_SELECTED_NETWORKS, TextUtils.join(",", value))
.apply();
}
@@ -540,7 +541,7 @@ public class UserPreferences {
throw new IllegalArgumentException("Flattr threshold must be in range [0.0, 1.0]");
}
prefs.edit()
- .putBoolean(PREF_AUTO_FLATTR, enabled)
+ .putBoolean(PREF_AUTO_FLATTR, enabled)
.putFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD, autoFlattrThreshold)
.apply();
}
@@ -800,4 +801,11 @@ public class UserPreferences {
public static int readEpisodeCacheSize(String valueFromPrefs) {
return readEpisodeCacheSizeInternal(valueFromPrefs);
}
+
+ /**
+ * Evaluates whether Cast support (Chromecast, Audio Cast, etc) is enabled on the preferences.
+ */
+ public static boolean isCastEnabled() {
+ return prefs.getBoolean(PREF_CAST_ENABLED, false);
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/CastUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/CastUtils.java
new file mode 100644
index 000000000..95103fb6c
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/CastUtils.java
@@ -0,0 +1,164 @@
+package de.danoeh.antennapod.core.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.preference.PreferenceManager;
+import android.support.v7.media.MediaRouter;
+import android.util.Log;
+
+import com.google.android.gms.cast.CastDevice;
+import com.google.android.gms.cast.CastMediaControlIntent;
+import com.google.android.gms.cast.MediaInfo;
+import com.google.android.gms.cast.MediaMetadata;
+import com.google.android.gms.common.images.WebImage;
+import com.google.android.libraries.cast.companionlibrary.cast.CastConfiguration;
+import com.google.android.libraries.cast.companionlibrary.cast.VideoCastManager;
+import com.google.android.libraries.cast.companionlibrary.cast.callbacks.VideoCastConsumer;
+import com.google.android.libraries.cast.companionlibrary.cast.callbacks.VideoCastConsumerImpl;
+
+import java.util.Calendar;
+
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.feed.FeedImage;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.playback.ExternalMedia;
+import de.danoeh.antennapod.core.util.playback.Playable;
+
+/**
+ * Helper functions for Cast support.
+ */
+public class CastUtils {
+ private static final String TAG = "CastUtils";
+
+ public static final String CAST_APP_ID = CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID;
+
+ public static final String KEY_MEDIA_ID = "CastUtils.Id";
+
+ public static void initializeCastManager(Context context){
+ // TODO check for cast support enabled
+ VideoCastManager.initialize(context, new CastConfiguration.Builder(CastUtils.CAST_APP_ID)
+ .enableDebug()
+ .enableLockScreen()
+ .enableNotification()
+ .enableWifiReconnection()
+ .enableAutoReconnect()
+ .setTargetActivity(ClientConfig.castCallbacks.getCastActivity())
+ .build());
+ VideoCastManager.getInstance().addVideoCastConsumer(castConsumer);
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .registerOnSharedPreferenceChangeListener(changeListener);
+ }
+
+ public static boolean isCastable(Playable media){
+ if (media == null || media instanceof ExternalMedia) {
+ return false;
+ }
+ if (media instanceof FeedMedia){
+ String url = media.getStreamUrl();
+ if(url == null || url.isEmpty()){
+ return false;
+ }
+ switch (media.getMediaType()) {
+ case UNKNOWN:
+ return false;
+ case AUDIO:
+ return audioCapable;
+ case VIDEO:
+ return videoCapable;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Converts {@link FeedMedia} objects into a format suitable for sending to a Cast Device.
+ * Before using this method, one should make sure {@link #isCastable(Playable)} returns
+ * {@code true}.
+ *
+ * Unless media.{@link FeedMedia#loadMetadata() loadMetadata()} has already been called,
+ * this method should not run on the main thread.
+ *
+ * @param media The {@link FeedMedia} object to be converted.
+ * @return {@link MediaInfo} object in a format proper for casting.
+ */
+ public static MediaInfo convertFromFeedMedia(FeedMedia media){
+ if(media == null) {
+ return null;
+ }
+ MediaMetadata metadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_GENERIC);
+ try{
+ media.loadMetadata();
+ } catch (Playable.PlayableException e) {
+ Log.e(TAG, "Unable to load FeedMedia metadata", e);
+ }
+ FeedItem feedItem = media.getItem();
+ if (feedItem != null) {
+ metadata.putString(MediaMetadata.KEY_TITLE, media.getEpisodeTitle());
+ String subtitle = media.getFeedTitle();
+ if (subtitle != null) {
+ metadata.putString(MediaMetadata.KEY_SUBTITLE, subtitle);
+ }
+ FeedImage image = feedItem.getImage();
+ if (image != null && image.getDownload_url() != null &&
+ !image.getDownload_url().isEmpty()) {
+ metadata.addImage(new WebImage(Uri.parse(image.getDownload_url())));
+ }
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(media.getItem().getPubDate());
+ metadata.putDate(MediaMetadata.KEY_RELEASE_DATE, calendar);
+
+ }
+ //metadata.putString(MediaMetadata.KEY_ARTIST, null);
+ metadata.putString(KEY_MEDIA_ID, media.getIdentifier().toString());
+
+ return new MediaInfo.Builder(media.getStreamUrl())
+ .setContentType(media.getMime_type())
+ .setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
+ .setMetadata(metadata)
+ .build();
+ }
+
+
+ private static SharedPreferences.OnSharedPreferenceChangeListener changeListener =
+ (preference, key) -> {
+ if (UserPreferences.PREF_CAST_ENABLED.equals(key)){
+ if (UserPreferences.isCastEnabled()){
+ // TODO enable all cast-related features
+ } else {
+ // TODO disable all cast-related features
+ }
+ }
+ };
+
+ // Ideally, all these fields and methods should be part of the CastManager implementation
+ private static boolean videoCapable = true;
+ private static boolean audioCapable = true;
+
+ public static boolean isVideoCapable(CastDevice device, boolean defaultValue){
+ if (device == null) {
+ return defaultValue;
+ }
+ return device.hasCapability(CastDevice.CAPABILITY_VIDEO_OUT);
+ }
+
+ public static boolean isAudioCapable(CastDevice device, boolean defaultValue){
+ if (device == null) {
+ return defaultValue;
+ }
+ return device.hasCapability(CastDevice.CAPABILITY_AUDIO_OUT);
+ }
+
+ private static VideoCastConsumer castConsumer = new VideoCastConsumerImpl() {
+ @Override
+ public void onDeviceSelected(CastDevice device, MediaRouter.RouteInfo routeInfo) {
+ // If no device is selected, we assume both audio and video are castable
+ videoCapable = isVideoCapable(device, true);
+ audioCapable = isAudioCapable(device, true);
+ }
+ };
+
+ //TODO Queue handling perhaps
+}
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 9026e2129..8a3f32b5f 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -403,6 +403,8 @@
<string name="pref_faq">FAQ</string>
<string name="pref_known_issues">Known issues</string>
<string name="pref_no_browser_found">No web browser found.</string>
+ <string name="pref_cast_title">Cast support</string>
+ <string name="pref_cast_message">Enable support for remote media playback on Cast devices (such as Chromecast, Audio Speakers or Android TV)</string>
<!-- Auto-Flattr dialog -->
<string name="auto_flattr_enable">Enable automatic flattring</string>