summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDomingos Lopes <domingos86lopes+github@gmail.com>2016-06-28 02:07:43 -0400
committerDomingos Lopes <domingos86lopes+github@gmail.com>2016-06-28 02:18:11 -0400
commit6d0debfd4d0c19ebe0287b136072b05ae6d8e65f (patch)
tree5537d380200a1980484c517a28af46d2ab39581c
parent926f3912cfc774ebf89db9d668ec3d31c6634f70 (diff)
downloadAntennaPod-6d0debfd4d0c19ebe0287b136072b05ae6d8e65f.zip
implement custom mediaroute playback controller
-rw-r--r--app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java1
-rw-r--r--app/src/play/java/de/danoeh/antennapod/cast/CustomMRControllerDialog.java226
-rw-r--r--app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java29
-rw-r--r--app/src/play/res/layout/media_router_controller.xml59
-rw-r--r--core/src/free/java/de/danoeh/antennapod/core/CastCallbacks.java7
-rw-r--r--core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java33
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/CastCallbacks.java12
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java2
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java3
11 files changed, 363 insertions, 18 deletions
diff --git a/app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java b/app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java
new file mode 100644
index 000000000..5e714f02c
--- /dev/null
+++ b/app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java
@@ -0,0 +1,7 @@
+package de.danoeh.antennapod.config;
+
+import de.danoeh.antennapod.core.CastCallbacks;
+
+public class CastCallbackImpl implements CastCallbacks {
+
+}
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..f26f2ea76 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 CastCallbackImpl();
}
}
diff --git a/app/src/play/java/de/danoeh/antennapod/cast/CustomMRControllerDialog.java b/app/src/play/java/de/danoeh/antennapod/cast/CustomMRControllerDialog.java
new file mode 100644
index 000000000..7469e830a
--- /dev/null
+++ b/app/src/play/java/de/danoeh/antennapod/cast/CustomMRControllerDialog.java
@@ -0,0 +1,226 @@
+package de.danoeh.antennapod.cast;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.support.v4.media.MediaDescriptionCompat;
+import android.support.v4.media.MediaMetadataCompat;
+import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
+import android.support.v7.app.MediaRouteControllerDialog;
+import android.support.v7.media.MediaRouter;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import de.danoeh.antennapod.R;
+
+public class CustomMRControllerDialog extends MediaRouteControllerDialog {
+ public static final String TAG = "CustomMRContrDialog";
+
+ private MediaRouter mediaRouter;
+ private MediaSessionCompat.Token token;
+
+ private ImageView artView;
+ private TextView titleView;
+ private TextView subtitleView;
+ private ImageButton playPauseButton;
+ private LinearLayout rootView;
+
+ private MediaControllerCompat mediaController;
+ private MediaControllerCompat.Callback mediaControllerCallback;
+
+ public CustomMRControllerDialog(Context context) {
+ this(context, 0);
+ }
+
+ public CustomMRControllerDialog(Context context, int theme) {
+ super(context, theme);
+ mediaRouter = MediaRouter.getInstance(getContext());
+ token = mediaRouter.getMediaSessionToken();
+ try {
+ if (token != null) {
+ mediaController = new MediaControllerCompat(getContext(), token);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error creating media controller", e);
+ }
+
+ if (mediaController != null) {
+ mediaControllerCallback = new MediaControllerCompat.Callback() {
+ @Override
+ public void onSessionDestroyed() {
+ if (mediaController != null) {
+ mediaController.unregisterCallback(mediaControllerCallback);
+ mediaController = null;
+ }
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadataCompat metadata) {
+ updateViews();
+ }
+
+ @Override
+ public void onPlaybackStateChanged(PlaybackStateCompat state) {
+ updateState();
+ }
+ };
+ mediaController.registerCallback(mediaControllerCallback);
+ }
+ }
+
+ @Override
+ public View onCreateMediaControlView(Bundle savedInstanceState) {
+ rootView = new LinearLayout(getContext());
+ rootView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ rootView.setOrientation(LinearLayout.VERTICAL);
+
+ artView = new ImageView(getContext()) {
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int desiredWidth = widthMeasureSpec;
+ int desiredHeight = heightMeasureSpec;
+ if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) {
+ Drawable drawable = getDrawable();
+ if (drawable != null) {
+ int originalWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int intrHeight = drawable.getIntrinsicHeight();
+ int intrWidth = drawable.getIntrinsicWidth();
+ float scale;
+ if (intrHeight*16 > intrWidth*9) {
+ // image is taller than 16:9
+ scale = (float) originalWidth * 9 / 16 / intrHeight;
+ } else {
+ // image is more horizontal than 16:9
+ scale = (float) originalWidth / intrWidth;
+ }
+ desiredHeight = MeasureSpec.makeMeasureSpec((int) (intrHeight * scale + 0.5f), MeasureSpec.EXACTLY);
+ }
+ }
+
+ super.onMeasure(desiredWidth, desiredHeight);
+ }
+ };
+ artView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ artView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+
+ rootView.addView(artView);
+ View playbackControlLayout = View.inflate(getContext(), R.layout.media_router_controller, rootView);
+
+ titleView = (TextView) playbackControlLayout.findViewById(R.id.mrc_control_title);
+ subtitleView = (TextView) playbackControlLayout.findViewById(R.id.mrc_control_subtitle);
+ playPauseButton = (ImageButton) playbackControlLayout.findViewById(R.id.mrc_control_play_pause);
+
+ updateViews();
+ return rootView;
+ }
+
+ private void updateViews() {
+ if (token == null || artView == null || mediaController == null) {
+ rootView.setVisibility(View.GONE);
+ return;
+ }
+ MediaMetadataCompat metadata = mediaController.getMetadata();
+ MediaDescriptionCompat description = metadata == null ? null : metadata.getDescription();
+ if (description == null) {
+ rootView.setVisibility(View.GONE);
+ return;
+ }
+
+ PlaybackStateCompat state = mediaController.getPlaybackState();
+ MediaRouter.RouteInfo route = MediaRouter.getInstance(getContext()).getSelectedRoute();
+
+ CharSequence title = description.getTitle();
+ boolean hasTitle = !TextUtils.isEmpty(title);
+ CharSequence subtitle = description.getSubtitle();
+ boolean hasSubtitle = !TextUtils.isEmpty(subtitle);
+
+ boolean showTitle = false;
+ boolean showSubtitle = false;
+ if (route.getPresentationDisplayId() != MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE) {
+ // The user is currently casting screen.
+ titleView.setText(android.support.v7.mediarouter.R.string.mr_controller_casting_screen);
+ showTitle = true;
+ } else if (state == null || state.getState() == PlaybackStateCompat.STATE_NONE) {
+ // Show "No media selected" as we don't yet know the playback state.
+ // (Only exception is bluetooth where we don't show anything.)
+ if (!route.isDeviceTypeBluetooth()) {
+ titleView.setText(android.support.v7.mediarouter.R.string.mr_controller_no_media_selected);
+ showTitle = true;
+ }
+ } else if (!hasTitle && !hasSubtitle) {
+ titleView.setText(android.support.v7.mediarouter.R.string.mr_controller_no_info_available);
+ showTitle = true;
+ } else {
+ if (hasTitle) {
+ titleView.setText(title);
+ showTitle = true;
+ }
+ if (hasSubtitle) {
+ subtitleView.setText(subtitle);
+ showSubtitle = true;
+ }
+ }
+ titleView.setVisibility(showTitle ? View.VISIBLE : View.GONE);
+ subtitleView.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
+
+ updateState();
+
+ Bitmap art = metadata.getBitmap(MediaMetadataCompat.METADATA_KEY_ART);
+ if (art == null) {
+ artView.setVisibility(View.GONE);
+ return;
+ }
+ artView.setImageBitmap(art);
+ artView.setVisibility(View.VISIBLE);
+ rootView.setVisibility(View.VISIBLE);
+ }
+
+ private void updateState() {
+ if (mediaController == null) {
+ return;
+ }
+ PlaybackStateCompat state = mediaController.getPlaybackState();
+ if (state != null) {
+ boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_BUFFERING
+ || state.getState() == PlaybackStateCompat.STATE_PLAYING;
+ boolean supportsPlay = (state.getActions() & (PlaybackStateCompat.ACTION_PLAY
+ | PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0;
+ boolean supportsPause = (state.getActions() & (PlaybackStateCompat.ACTION_PAUSE
+ | PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0;
+ if (isPlaying && supportsPause) {
+ playPauseButton.setVisibility(View.VISIBLE);
+ playPauseButton.setImageResource(getThemeResource(getContext(),
+ android.support.v7.mediarouter.R.attr.mediaRoutePauseDrawable));
+ playPauseButton.setContentDescription(getContext().getResources()
+ .getText(android.support.v7.mediarouter.R.string.mr_controller_pause));
+ } else if (!isPlaying && supportsPlay) {
+ playPauseButton.setVisibility(View.VISIBLE);
+ playPauseButton.setImageResource(getThemeResource(getContext(),
+ android.support.v7.mediarouter.R.attr.mediaRoutePlayDrawable));
+ playPauseButton.setContentDescription(getContext().getResources()
+ .getText(android.support.v7.mediarouter.R.string.mr_controller_play));
+ } else {
+ playPauseButton.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ private static int getThemeResource(Context context, int attr) {
+ TypedValue value = new TypedValue();
+ return context.getTheme().resolveAttribute(attr, value, true) ? value.resourceId : 0;
+ }
+}
diff --git a/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java b/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java
new file mode 100644
index 000000000..ca3758351
--- /dev/null
+++ b/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java
@@ -0,0 +1,29 @@
+package de.danoeh.antennapod.config;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v7.app.MediaRouteControllerDialog;
+import android.support.v7.app.MediaRouteControllerDialogFragment;
+import android.support.v7.app.MediaRouteDialogFactory;
+
+import de.danoeh.antennapod.cast.CustomMRControllerDialog;
+import de.danoeh.antennapod.core.CastCallbacks;
+
+public class CastCallbackImpl implements CastCallbacks {
+ @Override
+ public MediaRouteDialogFactory getMediaRouterDialogFactory() {
+ return new MediaRouteDialogFactory() {
+ @NonNull
+ @Override
+ public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
+ return new MediaRouteControllerDialogFragment() {
+ @Override
+ public MediaRouteControllerDialog onCreateControllerDialog(Context context, Bundle savedInstanceState) {
+ return new CustomMRControllerDialog(context);
+ }
+ };
+ }
+ };
+ }
+}
diff --git a/app/src/play/res/layout/media_router_controller.xml b/app/src/play/res/layout/media_router_controller.xml
new file mode 100644
index 000000000..15c2f9731
--- /dev/null
+++ b/app/src/play/res/layout/media_router_controller.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <!--<ImageView android:id="@+id/mrc_art"-->
+ <!--android:layout_width="match_parent"-->
+ <!--android:layout_height="wrap_content"-->
+ <!--android:adjustViewBounds="true"-->
+ <!--android:scaleType="fitCenter"-->
+ <!--android:background="?attr/colorPrimary"-->
+ <!--android:layout_gravity="top"-->
+ <!--android:visibility="gone"/>-->
+
+ <RelativeLayout android:id="@+id/mrc_playback_control"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="16dp"
+ android:paddingBottom="16dp"
+ android:paddingLeft="24dp"
+ android:paddingStart="24dp"
+ android:paddingRight="12dp"
+ android:paddingEnd="12dp">
+ <ImageButton android:id="@+id/mrc_control_play_pause"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="12dp"
+ android:layout_marginStart="12dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:contentDescription="@string/mr_controller_play"
+ android:background="?attr/selectableItemBackgroundBorderless"/>
+
+ <LinearLayout android:id="@+id/mrc_control_title_container"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_toLeftOf="@id/mrc_control_play_pause"
+ android:layout_toStartOf="@id/mrc_control_play_pause">
+ <TextView android:id="@+id/mrc_control_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/mediaRouteControllerPrimaryTextStyle"
+ android:singleLine="true" />
+ <TextView android:id="@+id/mrc_control_subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?attr/mediaRouteControllerSecondaryTextStyle"
+ android:singleLine="true" />
+ </LinearLayout>
+ </RelativeLayout>
+
+ <View android:id="@+id/mrc_control_divider"
+ android:layout_width="fill_parent"
+ android:layout_height="8dp"/>
+</LinearLayout> \ No newline at end of file
diff --git a/core/src/free/java/de/danoeh/antennapod/core/CastCallbacks.java b/core/src/free/java/de/danoeh/antennapod/core/CastCallbacks.java
new file mode 100644
index 000000000..2e266c736
--- /dev/null
+++ b/core/src/free/java/de/danoeh/antennapod/core/CastCallbacks.java
@@ -0,0 +1,7 @@
+package de.danoeh.antennapod.core;
+
+/**
+ * Callbacks for Chromecast support on the core module
+ */
+public interface CastCallbacks {
+}
diff --git a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java
index d1c93d782..eb2e6fc9e 100644
--- a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java
+++ b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java
@@ -30,6 +30,8 @@ public class ClientConfig {
public static DBTasksCallbacks dbTasksCallbacks;
+ public static CastCallbacks castCallbacks;
+
private static boolean initialized = false;
public static synchronized void initialize(Context context) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index 76c960607..c79d662cb 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -28,11 +28,9 @@ import android.support.v7.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import android.view.Display;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
-import android.view.WindowManager;
import android.widget.Toast;
import com.bumptech.glide.Glide;
@@ -945,31 +943,32 @@ public class PlaybackService extends Service {
builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, p.getEpisodeTitle());
builder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, p.getFeedTitle());
- if (p.getImageLocation() != null && UserPreferences.setLockscreenBackground()) {
- builder.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, p.getImageLocation().toString());
- try {
- if (isCasting) {
+ String imageLocation = p.getImageLocation();
+
+ if (!TextUtils.isEmpty(imageLocation)) {
+ if (isCasting || UserPreferences.setLockscreenBackground()) {
+ builder.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, imageLocation);
+ builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, imageLocation);
+ try {
Bitmap art = Glide.with(this)
- .load(p.getImageLocation())
+ .load(imageLocation)
.asBitmap()
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.get();
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, art);
- } else {
- WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- Bitmap art = Glide.with(this)
- .load(p.getImageLocation())
+ // Icon is useful for MediaDescription,
+ Bitmap icon = Glide.with(this)
+ .load(imageLocation)
.asBitmap()
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .centerCrop()
- .into(display.getWidth(), display.getHeight())
+ .fitCenter()
+ .into(128, 128)
.get();
- builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, art);
+ builder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON, icon);
+ } catch (Throwable tr) {
+ Log.e(TAG, Log.getStackTraceString(tr));
}
- } catch (Throwable tr) {
- Log.e(TAG, Log.getStackTraceString(tr));
}
}
if (!Thread.currentThread().isInterrupted() && started) {
diff --git a/core/src/play/java/de/danoeh/antennapod/core/CastCallbacks.java b/core/src/play/java/de/danoeh/antennapod/core/CastCallbacks.java
new file mode 100644
index 000000000..770fee9b9
--- /dev/null
+++ b/core/src/play/java/de/danoeh/antennapod/core/CastCallbacks.java
@@ -0,0 +1,12 @@
+package de.danoeh.antennapod.core;
+
+import android.support.annotation.Nullable;
+import android.support.v7.app.MediaRouteDialogFactory;
+
+/**
+ * Callbacks for Chromecast support on the core module
+ */
+public interface CastCallbacks {
+
+ @Nullable MediaRouteDialogFactory getMediaRouterDialogFactory();
+}
diff --git a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
index 9bbccbb82..3dfd6ea65 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
@@ -31,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) {
diff --git a/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java b/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
index 66b4558be..0674990f1 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
@@ -62,6 +62,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.TimeUnit;
+import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
import static com.google.android.gms.cast.RemoteMediaPlayer.RESUME_STATE_PLAY;
@@ -130,12 +131,12 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
public static synchronized CastManager init(Context context) {
if (INSTANCE == null) {
- //TODO also setup dialog factory if necessary
CastConfiguration castConfiguration = new CastConfiguration.Builder(CAST_APP_ID)
.enableDebug()
.enableAutoReconnect()
.enableWifiReconnection()
.setLaunchOptions(true, Locale.getDefault())
+ .setMediaRouteDialogFactory(ClientConfig.castCallbacks.getMediaRouterDialogFactory())
.build();
Log.d(TAG, "New instance of CastManager is created");
if (ConnectionResult.SUCCESS != GoogleApiAvailability.getInstance()