-Enable background and popup playlists.

This commit is contained in:
John Zhen M 2017-09-05 17:48:48 -07:00 committed by John Zhen Mo
parent b54d18d888
commit 7d7a6f7ccc
10 changed files with 192 additions and 125 deletions

View File

@ -724,7 +724,7 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
.getBoolean(activity.getString(R.string.use_external_audio_player_key), false); .getBoolean(activity.getString(R.string.use_external_audio_player_key), false);
if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) { if (!useExternalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 16) {
openNormalBackgroundPlayer(audioStream); openNormalBackgroundPlayer();
} else { } else {
openExternalBackgroundPlayer(audioStream); openExternalBackgroundPlayer(audioStream);
} }
@ -763,8 +763,8 @@ public class VideoDetailFragment extends BaseStateFragment<StreamInfo> implement
} }
private void openNormalBackgroundPlayer(AudioStream audioStream) { private void openNormalBackgroundPlayer() {
activity.startService(NavigationHelper.getOpenBackgroundPlayerIntent(activity, currentInfo, audioStream)); activity.startService(NavigationHelper.getOpenBackgroundPlayerIntent(activity, currentInfo));
Toast.makeText(activity, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show(); Toast.makeText(activity, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show();
} }

View File

@ -21,7 +21,9 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.player.BackgroundPlayer;
import org.schabi.newpipe.player.MainVideoPlayer; import org.schabi.newpipe.player.MainVideoPlayer;
import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.report.UserAction; import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper; import org.schabi.newpipe.util.ExtractorHelper;
import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.NavigationHelper;
@ -44,6 +46,8 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
private TextView headerStreamCount; private TextView headerStreamCount;
private Button headerPlayAllButton; private Button headerPlayAllButton;
private Button headerPopupButton;
private Button headerBackgroundButton;
public static PlaylistFragment getInstance(int serviceId, String url, String name) { public static PlaylistFragment getInstance(int serviceId, String url, String name) {
PlaylistFragment instance = new PlaylistFragment(); PlaylistFragment instance = new PlaylistFragment();
@ -71,7 +75,10 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
headerUploaderName = headerRootLayout.findViewById(R.id.uploader_name); headerUploaderName = headerRootLayout.findViewById(R.id.uploader_name);
headerUploaderAvatar = headerRootLayout.findViewById(R.id.uploader_avatar_view); headerUploaderAvatar = headerRootLayout.findViewById(R.id.uploader_avatar_view);
headerStreamCount = headerRootLayout.findViewById(R.id.playlist_stream_count); headerStreamCount = headerRootLayout.findViewById(R.id.playlist_stream_count);
headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_play_all_button); headerPlayAllButton = headerRootLayout.findViewById(R.id.playlist_play_all_button);
headerPopupButton = headerRootLayout.findViewById(R.id.playlist_play_popup_button);
headerBackgroundButton = headerRootLayout.findViewById(R.id.playlist_play_bg_button);
return headerRootLayout; return headerRootLayout;
} }
@ -138,7 +145,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
} }
imageLoader.displayImage(result.uploader_avatar_url, headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS); imageLoader.displayImage(result.uploader_avatar_url, headerUploaderAvatar, DISPLAY_AVATAR_OPTIONS);
headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos, (int) result.stream_count)); headerStreamCount.setText(getResources().getQuantityString(R.plurals.videos, (int) result.stream_count, (int) result.stream_count));
if (!result.errors.isEmpty()) { if (!result.errors.isEmpty()) {
showSnackBarError(result.errors, UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.service_id), result.url, 0); showSnackBarError(result.errors, UserAction.REQUESTED_PLAYLIST, NewPipe.getNameOfService(result.service_id), result.url, 0);
@ -147,15 +154,28 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
headerPlayAllButton.setOnClickListener(new View.OnClickListener() { headerPlayAllButton.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
final Intent intent = NavigationHelper.getExternalPlaylistIntent( startActivity(buildPlaylistIntent(MainVideoPlayer.class));
activity, MainVideoPlayer.class, currentInfo, infoListAdapter.getItemsList(), 0 }
); });
headerPopupButton.setOnClickListener(new View.OnClickListener() {
startActivity(intent); @Override
public void onClick(View view) {
activity.startService(buildPlaylistIntent(PopupVideoPlayer.class));
}
});
headerBackgroundButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
activity.startService(buildPlaylistIntent(BackgroundPlayer.class));
} }
}); });
} }
private Intent buildPlaylistIntent(final Class targetClazz) {
return NavigationHelper.getExternalPlaylistIntent(
activity, targetClazz, currentInfo, infoListAdapter.getItemsList(), 0
);
}
@Override @Override
public void handleNextItems(ListExtractor.NextItemsResult result) { public void handleNextItems(ListExtractor.NextItemsResult result) {

View File

@ -36,6 +36,7 @@ import android.util.Log;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.source.MediaSource;
import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.BuildConfig;
import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.MainActivity;
@ -43,11 +44,11 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.util.ThemeHelper;
import java.io.Serializable;
/** /**
* Base players joining the common properties * Base players joining the common properties
@ -65,9 +66,6 @@ public class BackgroundPlayer extends Service {
public static final String ACTION_FAST_REWIND = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_REWIND"; public static final String ACTION_FAST_REWIND = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_REWIND";
public static final String ACTION_FAST_FORWARD = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_FORWARD"; public static final String ACTION_FAST_FORWARD = "org.schabi.newpipe.player.BackgroundPlayer.ACTION_FAST_FORWARD";
public static final String AUDIO_STREAM = "video_only_audio_stream";
private AudioStream audioStream;
private BasePlayerImpl basePlayerImpl; private BasePlayerImpl basePlayerImpl;
private PowerManager powerManager; private PowerManager powerManager;
private WifiManager wifiManager; private WifiManager wifiManager;
@ -177,8 +175,8 @@ public class BackgroundPlayer extends Service {
private void setupNotification(RemoteViews remoteViews) { private void setupNotification(RemoteViews remoteViews) {
//if (videoThumbnail != null) remoteViews.setImageViewBitmap(R.id.notificationCover, videoThumbnail); //if (videoThumbnail != null) remoteViews.setImageViewBitmap(R.id.notificationCover, videoThumbnail);
///else remoteViews.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail); ///else remoteViews.setImageViewResource(R.id.notificationCover, R.drawable.dummy_thumbnail);
remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle()); // remoteViews.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle());
remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName()); // remoteViews.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName());
remoteViews.setOnClickPendingIntent(R.id.notificationPlayPause, remoteViews.setOnClickPendingIntent(R.id.notificationPlayPause,
PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT)); PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT));
@ -340,12 +338,16 @@ public class BackgroundPlayer extends Service {
@Override @Override
public void onFastRewind() { public void onFastRewind() {
if (isPlayerBuffering()) return;
playQueue.setIndex(playQueue.getIndex() - 1); playQueue.setIndex(playQueue.getIndex() - 1);
triggerProgressUpdate(); triggerProgressUpdate();
} }
@Override @Override
public void onFastForward() { public void onFastForward() {
if (isPlayerBuffering()) return;
playQueue.setIndex(playQueue.getIndex() + 1); playQueue.setIndex(playQueue.getIndex() + 1);
triggerProgressUpdate(); triggerProgressUpdate();
} }
@ -376,6 +378,26 @@ public class BackgroundPlayer extends Service {
// Playback Listener // Playback Listener
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/
@Override
public void sync(final StreamInfo info, final int sortedStreamsIndex) {
super.sync(info, sortedStreamsIndex);
basePlayerImpl.setVideoTitle(info.name);
basePlayerImpl.setUploaderName(info.uploader_name);
notRemoteView.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle());
notRemoteView.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName());
bigNotRemoteView.setTextViewText(R.id.notificationSongName, basePlayerImpl.getVideoTitle());
bigNotRemoteView.setTextViewText(R.id.notificationArtist, basePlayerImpl.getUploaderName());
updateNotification(-1);
}
@Override
public MediaSource sourceOf(final StreamInfo info, final int sortedStreamsIndex) {
final AudioStream audio = ListHelper.getHighestQualityAudio(info.audio_streams);
return buildMediaSource(audio.url, MediaFormat.getSuffixById(audio.format));
}
@Override @Override
public void shutdown() { public void shutdown() {
super.shutdown(); super.shutdown();

View File

@ -35,6 +35,7 @@ import android.preference.PreferenceManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.Toast;
import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.DefaultLoadControl;
@ -249,8 +250,6 @@ public abstract class BasePlayer implements Player.EventListener,
default: default:
break; break;
} }
initThumbnail();
} }
@ -586,8 +585,13 @@ public abstract class BasePlayer implements Player.EventListener,
@Override @Override
public void onPositionDiscontinuity() { public void onPositionDiscontinuity() {
// Refresh the playback if there is a transition to the next video
int newIndex = simpleExoPlayer.getCurrentWindowIndex(); int newIndex = simpleExoPlayer.getCurrentWindowIndex();
if (playbackManager.getCurrentSourceIndex() != newIndex) playbackManager.refresh(newIndex); if (DEBUG) Log.d(TAG, "onPositionDiscontinuity() called with: index = [" + newIndex + "]");
if (newIndex == playbackManager.getCurrentSourceIndex() + 1) {
playbackManager.refresh(newIndex);
}
} }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
@ -596,6 +600,7 @@ public abstract class BasePlayer implements Player.EventListener,
@Override @Override
public void block() { public void block() {
if (simpleExoPlayer == null) return;
Log.d(TAG, "Blocking..."); Log.d(TAG, "Blocking...");
simpleExoPlayer.stop(); simpleExoPlayer.stop();
@ -605,6 +610,7 @@ public abstract class BasePlayer implements Player.EventListener,
@Override @Override
public void unblock() { public void unblock() {
if (simpleExoPlayer == null) return;
Log.d(TAG, "Unblocking..."); Log.d(TAG, "Unblocking...");
if (restoreQueueIndex != playQueue.getIndex()) { if (restoreQueueIndex != playQueue.getIndex()) {
@ -619,25 +625,28 @@ public abstract class BasePlayer implements Player.EventListener,
@Override @Override
public void sync(final StreamInfo info, final int sortedStreamsIndex) { public void sync(final StreamInfo info, final int sortedStreamsIndex) {
if (simpleExoPlayer == null) return;
Log.d(TAG, "Syncing..."); Log.d(TAG, "Syncing...");
videoUrl = info.url; videoUrl = info.url;
videoThumbnailUrl = info.thumbnail_url; videoThumbnailUrl = info.thumbnail_url;
videoTitle = info.name; videoTitle = info.name;
initThumbnail();
if (simpleExoPlayer.getCurrentWindowIndex() != playbackManager.getCurrentSourceIndex()) { if (simpleExoPlayer.getCurrentWindowIndex() != playbackManager.getCurrentSourceIndex()) {
Log.w(TAG, "Rewinding to correct window"); Log.w(TAG, "Rewinding to correct window");
simpleExoPlayer.seekTo(playbackManager.getCurrentSourceIndex(), 0L); if (simpleExoPlayer.getCurrentTimeline().getWindowCount() > playbackManager.getCurrentSourceIndex()) {
simpleExoPlayer.seekToDefaultPosition(playbackManager.getCurrentSourceIndex());
} else {
Toast.makeText(context, "Player out of sync", Toast.LENGTH_SHORT).show();
simpleExoPlayer.seekToDefaultPosition();
}
} }
simpleExoPlayer.setPlayWhenReady(true); simpleExoPlayer.setPlayWhenReady(true);
} }
@Override
public MediaSource sourceOf(final StreamInfo info, final int sortedStreamsIndex) {
return null;
}
@Override @Override
public void shutdown() { public void shutdown() {
Log.d(TAG, "Shutting down..."); Log.d(TAG, "Shutting down...");
@ -888,4 +897,8 @@ public abstract class BasePlayer implements Player.EventListener,
public PlayQueue getPlayQueue() { public PlayQueue getPlayQueue() {
return playQueue; return playQueue;
} }
public boolean isPlayerBuffering() {
return currentState == STATE_BUFFERING;
}
} }

View File

@ -471,6 +471,7 @@ public class MainVideoPlayer extends Activity {
public boolean onDoubleTap(MotionEvent e) { public boolean onDoubleTap(MotionEvent e) {
if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
//if (!playerImpl.isPlaying()) return false; //if (!playerImpl.isPlaying()) return false;
if (playerImpl.isPlayerBuffering()) return false;
if (e.getX() > playerImpl.getRootView().getWidth() / 2) if (e.getX() > playerImpl.getRootView().getWidth() / 2)
playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1); playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1);

View File

@ -580,7 +580,8 @@ public class PopupVideoPlayer extends Service {
public boolean onDoubleTap(MotionEvent e) { public boolean onDoubleTap(MotionEvent e) {
if (DEBUG) if (DEBUG)
Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY());
if (!playerImpl.isPlaying()) return false; if (!playerImpl.isPlaying() || playerImpl.isPlayerBuffering()) return false;
if (e.getX() > popupWidth / 2) { if (e.getX() > popupWidth / 2) {
//playerImpl.onFastForward(); //playerImpl.onFastForward();
playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1); playerImpl.playQueue.setIndex(playerImpl.playQueue.getIndex() + 1);

View File

@ -274,7 +274,7 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
video = videos.get(sortedStreamsIndex); video = videos.get(sortedStreamsIndex);
} }
final MediaSource mediaSource = super.buildMediaSource(video.url, MediaFormat.getSuffixById(video.format)); final MediaSource mediaSource = buildMediaSource(video.url, MediaFormat.getSuffixById(video.format));
if (!video.isVideoOnly) return mediaSource; if (!video.isVideoOnly) return mediaSource;
final AudioStream audio = ListHelper.getHighestQualityAudio(info.audio_streams); final AudioStream audio = ListHelper.getHighestQualityAudio(info.audio_streams);
@ -282,15 +282,6 @@ public abstract class VideoPlayer extends BasePlayer implements SimpleExoPlayer.
return new MergingMediaSource(mediaSource, new ExtractorMediaSource(audioUri, cacheDataSourceFactory, extractorsFactory, null, null)); return new MergingMediaSource(mediaSource, new ExtractorMediaSource(audioUri, cacheDataSourceFactory, extractorsFactory, null, null));
} }
@Override
public MediaSource buildMediaSource(String url, String overrideExtension) {
MediaSource mediaSource = super.buildMediaSource(url, overrideExtension);
if (!getSelectedVideoStream().isVideoOnly || videoOnlyAudioStream == null) return mediaSource;
Uri audioUri = Uri.parse(videoOnlyAudioStream.url);
return new MergingMediaSource(mediaSource, new ExtractorMediaSource(audioUri, cacheDataSourceFactory, extractorsFactory, null, null));
}
public void buildQualityMenu(PopupMenu popupMenu) { public void buildQualityMenu(PopupMenu popupMenu) {
for (int i = 0; i < videoStreamsList.size(); i++) { for (int i = 0; i < videoStreamsList.size(); i++) {
VideoStream videoStream = videoStreamsList.get(i); VideoStream videoStream = videoStreamsList.get(i);

View File

@ -28,6 +28,8 @@ public class PlayQueueItem implements Serializable {
private Throwable error; private Throwable error;
private transient Single<StreamInfo> stream;
PlayQueueItem(final StreamInfo streamInfo, final int sortedQualityIndex) { PlayQueueItem(final StreamInfo streamInfo, final int sortedQualityIndex) {
this.title = streamInfo.name; this.title = streamInfo.name;
this.url = streamInfo.url; this.url = streamInfo.url;
@ -80,6 +82,11 @@ public class PlayQueueItem implements Serializable {
@NonNull @NonNull
public Single<StreamInfo> getStream() { public Single<StreamInfo> getStream() {
return stream == null ? stream = getInfo() : stream;
}
@NonNull
private Single<StreamInfo> getInfo() {
final Consumer<Throwable> onError = new Consumer<Throwable>() { final Consumer<Throwable> onError = new Consumer<Throwable>() {
@Override @Override
public void accept(Throwable throwable) throws Exception { public void accept(Throwable throwable) throws Exception {
@ -90,7 +97,6 @@ public class PlayQueueItem implements Serializable {
return ExtractorHelper.getStreamInfo(this.serviceId, this.url, false) return ExtractorHelper.getStreamInfo(this.serviceId, this.url, false)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
.retry(3)
.doOnError(onError); .doOnError(onError);
} }
} }

View File

@ -83,25 +83,6 @@ public class NavigationHelper {
.putExtra(SinglePlayQueue.STREAM, info); .putExtra(SinglePlayQueue.STREAM, info);
} }
public static Intent getOpenBackgroundPlayerIntent(Context context, StreamInfo info, AudioStream audioStream) {
return getOpenBackgroundPlayerIntent(context, info);
}
// public static Intent getOpenBackgroundPlayerIntent(Context context, StreamInfo info) {
// return getOpenBackgroundPlayerIntent(context, info, info.audio_streams.get(ListHelper.getDefaultAudioFormat(context, info.audio_streams)));
// }
//
// public static Intent getOpenBackgroundPlayerIntent(Context context, StreamInfo info, AudioStream audioStream) {
// Intent mIntent = new Intent(context, BackgroundPlayer.class)
// .putExtra(BasePlayer.VIDEO_TITLE, info.name)
// .putExtra(BasePlayer.VIDEO_URL, info.url)
// .putExtra(BasePlayer.VIDEO_THUMBNAIL_URL, info.thumbnail_url)
// .putExtra(BasePlayer.CHANNEL_NAME, info.uploader_name)
// .putExtra(BackgroundPlayer.AUDIO_STREAM, audioStream);
// if (info.start_position > 0) mIntent.putExtra(BasePlayer.START_POSITION, info.start_position * 1000L);
// return mIntent;
// }
/*////////////////////////////////////////////////////////////////////////// /*//////////////////////////////////////////////////////////////////////////
// Through FragmentManager // Through FragmentManager
//////////////////////////////////////////////////////////////////////////*/ //////////////////////////////////////////////////////////////////////////*/

View File

@ -9,10 +9,87 @@
android:background="?attr/contrast_background_color" android:background="?attr/contrast_background_color"
android:paddingBottom="6dp"> android:paddingBottom="6dp">
<TextView
android:id="@+id/playlist_title_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="6dp"
android:ellipsize="end"
android:maxLines="2"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/playlist_detail_title_text_size"
tools:text="Mix musics #23 title Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsum"/>
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/top_control"> android:layout_below="@+id/playlist_title_view"
android:id="@+id/playlist_meta">
<RelativeLayout
android:id="@+id/uploader_layout"
android:layout_width="wrap_content"
android:layout_height="@dimen/playlist_detail_uploader_layout_height"
android:layout_marginLeft="4dp"
android:layout_marginRight="6dp"
android:layout_marginTop="6dp"
android:layout_toLeftOf="@+id/playlist_stream_count"
android:layout_toStartOf="@+id/playlist_stream_count"
android:background="?attr/selectableItemBackground"
android:gravity="left|center_vertical"
android:padding="2dp"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:visibility="visible">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/uploader_avatar_view"
android:layout_width="@dimen/playlist_detail_uploader_image_size"
android:layout_height="@dimen/playlist_detail_uploader_image_size"
android:layout_alignParentLeft="true"
android:layout_margin="1dp"
android:src="@drawable/buddy"
app:civ_border_color="#ffffff"
app:civ_border_width="1dp"/>
<TextView
android:id="@+id/uploader_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="4dp"
android:layout_toRightOf="@+id/uploader_avatar_view"
android:ellipsize="end"
android:gravity="left|center_vertical"
android:maxLines="1"
android:textSize="@dimen/playlist_detail_subtext_size"
tools:ignore="RtlHardcoded"
tools:text="Typical uploader name"/>
</RelativeLayout>
<TextView
android:id="@+id/playlist_stream_count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/uploader_layout"
android:layout_alignTop="@+id/uploader_layout"
android:layout_alignParentRight="true"
android:layout_marginRight="6dp"
android:ellipsize="end"
android:gravity="right|center_vertical"
android:maxLines="1"
android:textSize="@dimen/playlist_detail_subtext_size"
tools:ignore="RtlHardcoded"
tools:text="234 videos"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/play_control"
android:layout_below="@+id/playlist_meta">
<Button <Button
android:id="@+id/playlist_play_all_button" android:id="@+id/playlist_play_all_button"
@ -27,78 +104,33 @@
tools:ignore="RtlHardcoded" tools:ignore="RtlHardcoded"
tools:visibility="visible"/> tools:visibility="visible"/>
<TextView <Button
android:id="@+id/playlist_title_view" android:id="@+id/playlist_play_bg_button"
android:layout_width="match_parent" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="8dp" android:layout_gravity="center_vertical|right"
android:layout_marginRight="8dp" android:layout_marginRight="2dp"
android:layout_marginTop="6dp"
android:layout_toLeftOf="@+id/playlist_play_all_button" android:layout_toLeftOf="@+id/playlist_play_all_button"
android:layout_toStartOf="@+id/playlist_play_all_button" android:layout_toStartOf="@+id/playlist_play_all_button"
android:layout_centerInParent="true" android:text="@string/controls_background_title"
android:ellipsize="end" android:textSize="@dimen/channel_rss_title_size"
android:maxLines="2" android:theme="@style/RedButton"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/playlist_detail_title_text_size"
tools:text="Mix musics #23 title Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsumLorem ipsum"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/uploader_layout"
android:layout_width="match_parent"
android:layout_height="@dimen/playlist_detail_uploader_layout_height"
android:layout_below="@+id/top_control"
android:layout_marginLeft="4dp"
android:layout_marginRight="6dp"
android:layout_marginTop="6dp"
android:layout_toLeftOf="@+id/playlist_stream_count"
android:layout_toStartOf="@+id/playlist_stream_count"
android:background="?attr/selectableItemBackground"
android:gravity="left|center_vertical"
android:padding="2dp"
android:visibility="gone"
tools:ignore="RtlHardcoded"
tools:visibility="visible">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/uploader_avatar_view"
android:layout_width="@dimen/playlist_detail_uploader_image_size"
android:layout_height="@dimen/playlist_detail_uploader_image_size"
android:layout_alignParentLeft="true"
android:layout_margin="1dp"
android:src="@drawable/buddy"
app:civ_border_color="#ffffff"
app:civ_border_width="1dp"/>
<TextView
android:id="@+id/uploader_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="4dp"
android:layout_toRightOf="@+id/uploader_avatar_view"
android:ellipsize="end"
android:gravity="left|center_vertical"
android:maxLines="1"
android:textSize="@dimen/playlist_detail_subtext_size"
tools:ignore="RtlHardcoded" tools:ignore="RtlHardcoded"
tools:text="Typical uploader name"/> tools:visibility="visible" />
</RelativeLayout>
<TextView <Button
android:id="@+id/playlist_stream_count" android:id="@+id/playlist_play_popup_button"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignBottom="@+id/uploader_layout" android:layout_gravity="center_vertical|right"
android:layout_alignEnd="@+id/top_control" android:layout_marginRight="2dp"
android:layout_alignRight="@+id/top_control" android:layout_toLeftOf="@+id/playlist_play_bg_button"
android:layout_alignTop="@+id/uploader_layout" android:layout_toStartOf="@+id/playlist_play_bg_button"
android:layout_marginRight="6dp" android:text="@string/controls_popup_title"
android:ellipsize="end" android:textSize="@dimen/channel_rss_title_size"
android:gravity="right|center_vertical" android:theme="@style/RedButton"
android:maxLines="1" tools:ignore="RtlHardcoded"
android:textSize="@dimen/playlist_detail_subtext_size" tools:visibility="visible" />
tools:ignore="RtlHardcoded" </RelativeLayout>
tools:text="234 videos"/>
</RelativeLayout> </RelativeLayout>