Merge pull request #2057 from domingos86/castdialogimage-issue1910
fix horizontal layout (MediaRouteControllerDialog)
This commit is contained in:
commit
e6e37e9558
|
@ -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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
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.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
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,6 +186,54 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
|
|||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
|
@ -142,20 +252,41 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
|
|||
// image is more horizontal than 16:9
|
||||
scale = (float) originalWidth / intrWidth;
|
||||
}
|
||||
desiredHeight = MeasureSpec.makeMeasureSpec((int) (intrHeight * scale + 0.5f), MeasureSpec.EXACTLY);
|
||||
desiredHeight = MeasureSpec.makeMeasureSpec(
|
||||
Math.round(intrHeight * scale),
|
||||
MeasureSpec.EXACTLY);
|
||||
}
|
||||
}
|
||||
|
||||
super.onMeasure(widthMeasureSpec, desiredHeight);
|
||||
}
|
||||
};
|
||||
artView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
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);
|
||||
|
||||
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) {
|
||||
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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,23 +1,19 @@
|
|||
<?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">
|
||||
|
||||
<RelativeLayout android:id="@+id/mrc_playback_control"
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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">
|
||||
android:paddingTop="@dimen/media_router_controller_playback_control_vertical_padding"
|
||||
android:paddingBottom="@dimen/media_router_controller_playback_control_vertical_padding"
|
||||
android:paddingLeft="@dimen/media_router_controller_playback_control_start_padding"
|
||||
android:paddingStart="@dimen/media_router_controller_playback_control_start_padding"
|
||||
android:paddingRight="@dimen/media_router_controller_playback_control_horizontal_spacing"
|
||||
android:paddingEnd="@dimen/media_router_controller_playback_control_horizontal_spacing">
|
||||
<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_marginLeft="@dimen/media_router_controller_playback_control_horizontal_spacing"
|
||||
android:layout_marginStart="@dimen/media_router_controller_playback_control_horizontal_spacing"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:contentDescription="@string/mr_controller_play"
|
||||
|
@ -42,9 +38,4 @@
|
|||
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>
|
||||
</RelativeLayout>
|
||||
|
|
|
@ -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<MediaSessionCompat.QueueItem> 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<List<MediaBrowserCompat.MediaItem>> result) {
|
||||
public void onLoadChildren(@NonNull String parentId,
|
||||
@NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
|
||||
Log.d(TAG, "OnLoadChildren: parentMediaId=" + parentId);
|
||||
List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<MediaBrowserCompat.MediaItem>();
|
||||
List<MediaBrowserCompat.MediaItem> 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();
|
||||
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="media_router_controller_playback_control_start_padding">@dimen/media_router_controller_playback_control_horizontal_spacing</dimen>
|
||||
</resources>
|
|
@ -36,4 +36,9 @@
|
|||
|
||||
<dimen name="audioplayer_playercontrols_length">48dp</dimen>
|
||||
|
||||
<dimen name="media_router_controller_playback_control_vertical_padding">16dp</dimen>
|
||||
<dimen name="media_router_controller_playback_control_horizontal_spacing">12dp</dimen>
|
||||
<dimen name="media_router_controller_playback_control_start_padding">24dp</dimen>
|
||||
<dimen name="media_router_controller_bottom_margin">8dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue