add chromecast support initialization logic and introduce chromecast preference

This commit is contained in:
Domingos Lopes 2016-03-19 01:32:55 -04:00 committed by Domingos Lopes
parent 1b6459c8ee
commit 8061d94c1b
8 changed files with 228 additions and 4 deletions

View File

@ -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());
}
}

View File

@ -16,5 +16,6 @@ public class ClientConfigurator {
ClientConfig.playbackServiceCallbacks = new PlaybackServiceCallbacksImpl();
ClientConfig.flattrCallbacks = new FlattrCallbacksImpl();
ClientConfig.dbTasksCallbacks = new DBTasksCallbacksImpl();
ClientConfig.castCallbacks = new CastCallbacksImpl();
}
}

View File

@ -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>

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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
}

View File

@ -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>