diff --git a/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java b/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java
index 02720e42e..916b13a38 100644
--- a/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java
+++ b/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java
@@ -1,14 +1,11 @@
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.dialog.CustomMRControllerDialog;
import de.danoeh.antennapod.core.CastCallbacks;
+import de.danoeh.antennapod.fragment.CustomMRControllerDialogFragment;
public class CastCallbackImpl implements CastCallbacks {
@Override
@@ -17,12 +14,7 @@ public class CastCallbackImpl implements CastCallbacks {
@NonNull
@Override
public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
- return new MediaRouteControllerDialogFragment() {
- @Override
- public MediaRouteControllerDialog onCreateControllerDialog(Context context, Bundle savedInstanceState) {
- return new CustomMRControllerDialog(context);
- }
- };
+ return new CustomMRControllerDialogFragment();
}
};
}
diff --git a/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java b/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java
index 009a8c254..93ec4e3f0 100644
--- a/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java
+++ b/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.dialog;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -14,6 +15,7 @@ import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.support.v4.util.Pair;
+import android.support.v4.view.MarginLayoutParamsCompat;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v7.app.MediaRouteControllerDialog;
import android.support.v7.graphics.Palette;
@@ -104,10 +106,70 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
@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);
+ boolean landscape = getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
+ if (landscape) {
+ /*
+ * When a horizontal LinearLayout measures itself, it first measures its children and
+ * settles their widths on the first pass, and only then figures out its height, never
+ * revisiting the widths measurements.
+ * When one has a child view that imposes a certain aspect ratio (such as an ImageView),
+ * then its width and height are related to each other, and so if one allows for a large
+ * height, then it will request for itself a large width as well. However, on the first
+ * child measurement, the LinearLayout imposes a very relaxed height bound, that the
+ * child uses to tell the width it wants, a value which the LinearLayout will interpret
+ * as final, even though the child will want to change it once a more restrictive height
+ * bound is imposed later.
+ *
+ * Our solution is, given that the heights of the children do not depend on their widths
+ * in this case, we first figure out the layout's height and only then perform the
+ * usual sequence of measurements.
+ *
+ * Note: this solution does not take into account any vertical paddings nor children's
+ * vertical margins in determining the height, as this View as well as its children are
+ * defined in code and no paddings/margins that would influence these computations are
+ * introduced.
+ *
+ * There were no resources online for this type of issue as far as I could gather.
+ */
+ rootView = new LinearLayout(getContext()) {
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // We'd like to find the overall height before adjusting the widths within the LinearLayout
+ int maxHeight = Integer.MIN_VALUE;
+ if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) {
+ for (int i = 0; i < getChildCount(); i++) {
+ int height = Integer.MIN_VALUE;
+ View child = getChildAt(i);
+ ViewGroup.LayoutParams lp = child.getLayoutParams();
+ // we only measure children whose layout_height is not MATCH_PARENT
+ if (lp.height >= 0) {
+ height = lp.height;
+ } else if (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+ child.measure(widthMeasureSpec, heightMeasureSpec);
+ height = child.getMeasuredHeight();
+ }
+ maxHeight = Math.max(maxHeight, height);
+ }
+ }
+ if (maxHeight > 0) {
+ super.onMeasure(widthMeasureSpec,
+ MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY));
+ } else {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+ };
+ rootView.setOrientation(LinearLayout.HORIZONTAL);
+ } else {
+ rootView = new LinearLayout(getContext());
+ rootView.setOrientation(LinearLayout.VERTICAL);
+ }
+ FrameLayout.LayoutParams rootParams = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ rootParams.setMargins(0, 0, 0,
+ getContext().getResources().getDimensionPixelSize(R.dimen.media_router_controller_bottom_margin));
+ rootView.setLayoutParams(rootParams);
// Start the session activity when a content item (album art, title or subtitle) is clicked.
View.OnClickListener onClickListener = v -> {
@@ -124,38 +186,107 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
}
};
- artView = new ImageView(getContext()) {
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- 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;
+ LinearLayout.LayoutParams artParams;
+ /*
+ * On portrait orientation, we want to limit the artView's height to 9/16 of the available
+ * width. Reason is that we need to choose the height wisely otherwise we risk the dialog
+ * being much larger than the screen, and there doesn't seem to be a good way to know the
+ * available height beforehand.
+ *
+ * On landscape orientation, we want to limit the artView's width to its available height.
+ * Otherwise, horizontal images would take too much space and severely restrict the space
+ * for episode title and play/pause button.
+ *
+ * Internal implementation of ImageView only uses the source image's aspect ratio, but we
+ * want to impose our own and fallback to the source image's when it is more favorable.
+ * Solutions were inspired, among other similar sources, on
+ * http://stackoverflow.com/questions/18077325/scale-image-to-fill-imageview-width-and-keep-aspect-ratio
+ */
+ if (landscape) {
+ artView = new ImageView(getContext()) {
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ int desiredWidth = widthMeasureSpec;
+ int desiredMeasureMode = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY ?
+ MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
+ if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) {
+ Drawable drawable = getDrawable();
+ if (drawable != null) {
+ int intrHeight = drawable.getIntrinsicHeight();
+ int intrWidth = drawable.getIntrinsicWidth();
+ int originalHeight = MeasureSpec.getSize(heightMeasureSpec);
+ if (intrHeight < intrWidth) {
+ desiredWidth = MeasureSpec.makeMeasureSpec(
+ originalHeight, desiredMeasureMode);
+ } else {
+ desiredWidth = MeasureSpec.makeMeasureSpec(
+ Math.round((float) originalHeight * intrWidth / intrHeight),
+ desiredMeasureMode);
+ }
}
- desiredHeight = MeasureSpec.makeMeasureSpec((int) (intrHeight * scale + 0.5f), MeasureSpec.EXACTLY);
}
+ super.onMeasure(desiredWidth, heightMeasureSpec);
}
+ };
+ artParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ MarginLayoutParamsCompat.setMarginStart(artParams,
+ getContext().getResources().getDimensionPixelSize(R.dimen.media_router_controller_playback_control_horizontal_spacing));
+ } else {
+ artView = new ImageView(getContext()) {
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ 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(
+ Math.round(intrHeight * scale),
+ MeasureSpec.EXACTLY);
+ }
+ }
+ super.onMeasure(widthMeasureSpec, desiredHeight);
+ }
+ };
+ artParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+ // When we fetch the bitmap, we want to know if we should set a background color or not.
+ artView.setTag(landscape);
- super.onMeasure(widthMeasureSpec, desiredHeight);
- }
- };
- artView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT));
artView.setScaleType(ImageView.ScaleType.FIT_CENTER);
artView.setOnClickListener(onClickListener);
+ artView.setLayoutParams(artParams);
rootView.addView(artView);
- View playbackControlLayout = View.inflate(getContext(), R.layout.media_router_controller, rootView);
+
+ ViewGroup wrapper = rootView;
+
+ if (landscape) {
+ // Here we wrap with a frame layout because we want to set different layout parameters
+ // for landscape orientation.
+ wrapper = new FrameLayout(getContext());
+ wrapper.setLayoutParams(new LinearLayout.LayoutParams(
+ 0,
+ ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
+ rootView.addView(wrapper);
+ rootView.setWeightSum(1f);
+ }
+
+ View playbackControlLayout = View.inflate(getContext(), R.layout.media_router_controller, wrapper);
titleView = (TextView) playbackControlLayout.findViewById(R.id.mrc_control_title);
subtitleView = (TextView) playbackControlLayout.findViewById(R.id.mrc_control_subtitle);
@@ -179,7 +310,8 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
event.setPackageName(getContext().getPackageName());
event.setClassName(getClass().getName());
int resId = isPlaying ?
- android.support.v7.mediarouter.R.string.mr_controller_pause : android.support.v7.mediarouter.R.string.mr_controller_play;
+ android.support.v7.mediarouter.R.string.mr_controller_pause :
+ android.support.v7.mediarouter.R.string.mr_controller_play;
event.getText().add(getContext().getString(resId));
accessibilityManager.sendAccessibilityEvent(event);
}
@@ -270,14 +402,20 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
fetchArtSubscription = null;
+ if (artView == null) {
+ return;
+ }
if (result.first != null) {
- artView.setBackgroundColor(result.second);
+ if (!((Boolean) artView.getTag())) {
+ artView.setBackgroundColor(result.second);
+ }
artView.setImageBitmap(result.first);
artView.setVisibility(View.VISIBLE);
} else {
artView.setVisibility(View.GONE);
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
+
}
private void updateState() {
diff --git a/app/src/play/java/de/danoeh/antennapod/fragment/CustomMRControllerDialogFragment.java b/app/src/play/java/de/danoeh/antennapod/fragment/CustomMRControllerDialogFragment.java
new file mode 100644
index 000000000..a960ec998
--- /dev/null
+++ b/app/src/play/java/de/danoeh/antennapod/fragment/CustomMRControllerDialogFragment.java
@@ -0,0 +1,15 @@
+package de.danoeh.antennapod.fragment;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v7.app.MediaRouteControllerDialog;
+import android.support.v7.app.MediaRouteControllerDialogFragment;
+
+import de.danoeh.antennapod.dialog.CustomMRControllerDialog;
+
+public class CustomMRControllerDialogFragment extends 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
index 7d3889db2..659ac0a78 100644
--- a/app/src/play/res/layout/media_router_controller.xml
+++ b/app/src/play/res/layout/media_router_controller.xml
@@ -1,50 +1,41 @@
-
-
-
+
-
+
+
+
-
-
+
-
-
-
-
-
-
-
+ android:textAppearance="?attr/mediaRouteControllerSecondaryTextStyle"
+ android:singleLine="true" />
+
+
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 5e1057716..e3557f5f8 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
@@ -29,6 +29,7 @@ import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
+import android.support.v4.view.InputDeviceCompat;
import android.support.v7.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
@@ -41,8 +42,8 @@ import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.Target;
-import java.util.List;
import java.util.ArrayList;
+import java.util.List;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
@@ -302,7 +303,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
List queueItems = new ArrayList<>();
try {
- for (FeedItem feedItem: taskManager.getQueue()) {
+ for (FeedItem feedItem : taskManager.getQueue()) {
queueItems.add(new MediaSessionCompat.QueueItem(feedItem.getMedia().getMediaItem().getDescription(), feedItem.getId()));
}
mediaSession.setQueue(queueItems);
@@ -345,7 +346,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
@Override
- public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+ public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) {
Log.d(TAG, "OnGetRoot: clientPackageName=" + clientPackageName +
"; clientUid=" + clientUid + " ; rootHints=" + rootHints);
return new BrowserRoot(
@@ -363,10 +364,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
@Override
- public void onLoadChildren(String parentId,
- Result> result) {
+ public void onLoadChildren(@NonNull String parentId,
+ @NonNull Result> result) {
Log.d(TAG, "OnLoadChildren: parentMediaId=" + parentId);
- List mediaItems = new ArrayList();
+ List mediaItems = new ArrayList<>();
if (parentId.equals(getResources().getString(R.string.app_name))) {
// Root List
mediaItems.add(createBrowsableMediaItemForRoot());
@@ -415,7 +416,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (keycode != -1) {
Log.d(TAG, "Received media button event");
handleKeycode(keycode, intent.getIntExtra(MediaButtonReceiver.EXTRA_SOURCE,
- InputDevice.SOURCE_CLASS_NONE));
+ InputDeviceCompat.SOURCE_CLASS_NONE));
} else if (!flavorHelper.castDisconnect(castDisconnect)) {
started = true;
boolean stream = intent.getBooleanExtra(EXTRA_SHOULD_STREAM,
@@ -1030,6 +1031,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
}
if (!Thread.currentThread().isInterrupted() && started) {
+ mediaSession.setSessionActivity(PendingIntent.getActivity(this, 0,
+ PlaybackService.getPlayerActivityIntent(this),
+ PendingIntent.FLAG_UPDATE_CURRENT));
mediaSession.setMetadata(builder.build());
}
};
@@ -1176,8 +1180,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
.setShowActionsInCompactView(compactActionList.toArray())
.setShowCancelButton(true)
.setCancelButtonIntent(stopButtonPendingIntent))
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setColor(Notification.COLOR_DEFAULT);
+ .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
+ .setColor(NotificationCompat.COLOR_DEFAULT);
notification = notificationBuilder.build();
diff --git a/core/src/main/res/values-land/dimens.xml b/core/src/main/res/values-land/dimens.xml
new file mode 100644
index 000000000..73b2b2e98
--- /dev/null
+++ b/core/src/main/res/values-land/dimens.xml
@@ -0,0 +1,4 @@
+
+
+ @dimen/media_router_controller_playback_control_horizontal_spacing
+
diff --git a/core/src/main/res/values/dimens.xml b/core/src/main/res/values/dimens.xml
index 4f549efd7..01dce6a1c 100644
--- a/core/src/main/res/values/dimens.xml
+++ b/core/src/main/res/values/dimens.xml
@@ -36,4 +36,9 @@
48dp
+ 16dp
+ 12dp
+ 24dp
+ 8dp
+
diff --git a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
index aef3e3c2b..93431d466 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
@@ -75,9 +75,8 @@ public class PlaybackServiceFlavorHelper {
boolean castDisconnect(boolean castDisconnect) {
if (castDisconnect) {
castManager.disconnect();
- return true;
}
- return false;
+ return castDisconnect;
}
boolean onMediaPlayerInfo(Context context, int code, @StringRes int resourceId) {