-Added play queue dropdown to channel info items.

-Added play queue dropdown to playlist info items.
-Added Channel Play Queue.
-Renamed External Play Queue to Playlist Play Queue.
-Modified Playlist Play Queue to allow loading from initial state.
This commit is contained in:
John Zhen Mo 2017-11-01 23:38:18 -07:00
parent 87febf8679
commit b8a17580c5
12 changed files with 431 additions and 47 deletions

View File

@ -1,6 +1,5 @@
package org.schabi.newpipe.fragments.list.playlist;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
@ -24,10 +23,7 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.fragments.list.BaseListInfoFragment;
import org.schabi.newpipe.player.BackgroundPlayer;
import org.schabi.newpipe.player.MainVideoPlayer;
import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.playlist.ExternalPlayQueue;
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.report.UserAction;
import org.schabi.newpipe.util.ExtractorHelper;
@ -185,7 +181,7 @@ public class PlaylistFragment extends BaseListInfoFragment<PlaylistInfo> {
}
private PlayQueue getPlayQueue() {
return new ExternalPlayQueue(
return new PlaylistPlayQueue(
currentInfo.service_id,
currentInfo.url,
currentInfo.next_streams_url,

View File

@ -1,7 +1,11 @@
package org.schabi.newpipe.info_list.holder;
import android.content.Context;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.PopupMenu;
import android.widget.TextView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
@ -10,7 +14,9 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.playlist.ChannelPlayQueue;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
import de.hdodenhof.circleimageview.CircleImageView;
@ -18,6 +24,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
public final CircleImageView itemThumbnailView;
public final TextView itemTitleView;
public final TextView itemAdditionalDetailView;
public final ImageButton itemActionDropdown;
ChannelMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, int layoutId, ViewGroup parent) {
super(infoItemBuilder, layoutId, parent);
@ -25,6 +32,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
itemThumbnailView = itemView.findViewById(R.id.itemThumbnailView);
itemTitleView = itemView.findViewById(R.id.itemTitleView);
itemAdditionalDetailView = itemView.findViewById(R.id.itemAdditionalDetails);
itemActionDropdown = itemView.findViewById(R.id.itemActionDropdown);
}
public ChannelMiniInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
@ -50,6 +58,55 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder {
}
}
});
enableActionDropdown(item);
}
private void enableActionDropdown(final ChannelInfoItem item) {
itemActionDropdown.setVisibility(View.VISIBLE);
itemActionDropdown.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final PopupMenu actionMenu = getStreamDropdown(itemBuilder.getContext(), itemActionDropdown, item);
if (itemBuilder.getOnChannelSelectedListener() != null) {
itemBuilder.getOnChannelSelectedListener().dropdownClicked(item, actionMenu);
}
actionMenu.show();
}
});
}
private PopupMenu getStreamDropdown(final Context context, final View anchor, final ChannelInfoItem infoItem) {
PopupMenu actionMenu = new PopupMenu(context, anchor);
final MenuItem mainPlay = actionMenu.getMenu().add(R.string.play_all);
mainPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
NavigationHelper.playOnMainPlayer(context, new ChannelPlayQueue(infoItem));
return true;
}
});
final MenuItem popupPlay = actionMenu.getMenu().add(R.string.controls_popup_title);
popupPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
NavigationHelper.playOnPopupPlayer(context, new ChannelPlayQueue(infoItem));
return true;
}
});
final MenuItem backgroundPlay = actionMenu.getMenu().add(R.string.controls_background_title);
backgroundPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
NavigationHelper.playOnBackgroundPlayer(context, new ChannelPlayQueue(infoItem));
return true;
}
});
return actionMenu;
}
protected String getDetailLine(final ChannelInfoItem item) {

View File

@ -1,8 +1,12 @@
package org.schabi.newpipe.info_list.holder;
import android.content.Context;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
@ -11,12 +15,15 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.playlist.PlaylistPlayQueue;
import org.schabi.newpipe.util.NavigationHelper;
public class PlaylistInfoItemHolder extends InfoItemHolder {
public final ImageView itemThumbnailView;
public final TextView itemStreamCountView;
public final TextView itemTitleView;
public final TextView itemUploaderView;
public final ImageButton itemActionDropdown;
public PlaylistInfoItemHolder(InfoItemBuilder infoItemBuilder, ViewGroup parent) {
super(infoItemBuilder, R.layout.list_playlist_item, parent);
@ -25,6 +32,7 @@ public class PlaylistInfoItemHolder extends InfoItemHolder {
itemTitleView = itemView.findViewById(R.id.itemTitleView);
itemStreamCountView = itemView.findViewById(R.id.itemStreamCountView);
itemUploaderView = itemView.findViewById(R.id.itemUploaderView);
itemActionDropdown = itemView.findViewById(R.id.itemActionDropdown);
}
@Override
@ -47,8 +55,56 @@ public class PlaylistInfoItemHolder extends InfoItemHolder {
}
}
});
enableActionDropdown(item);
}
private void enableActionDropdown(final PlaylistInfoItem item) {
itemActionDropdown.setVisibility(View.VISIBLE);
itemActionDropdown.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
final PopupMenu actionMenu = getStreamDropdown(itemBuilder.getContext(), itemActionDropdown, item);
if (itemBuilder.getOnPlaylistSelectedListener() != null) {
itemBuilder.getOnPlaylistSelectedListener().dropdownClicked(item, actionMenu);
}
actionMenu.show();
}
});
}
private PopupMenu getStreamDropdown(final Context context, final View anchor, final PlaylistInfoItem infoItem) {
PopupMenu actionMenu = new PopupMenu(context, anchor);
final MenuItem mainPlay = actionMenu.getMenu().add(R.string.play_all);
mainPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
NavigationHelper.playOnMainPlayer(context, new PlaylistPlayQueue(infoItem));
return true;
}
});
final MenuItem popupPlay = actionMenu.getMenu().add(R.string.controls_popup_title);
popupPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
NavigationHelper.playOnPopupPlayer(context, new PlaylistPlayQueue(infoItem));
return true;
}
});
final MenuItem backgroundPlay = actionMenu.getMenu().add(R.string.controls_background_title);
backgroundPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
NavigationHelper.playOnBackgroundPlayer(context, new PlaylistPlayQueue(infoItem));
return true;
}
});
return actionMenu;
}
/**
* Display options for playlist thumbnails
*/

View File

@ -2,7 +2,6 @@ package org.schabi.newpipe.info_list.holder;
import android.content.Context;
import android.support.v4.content.ContextCompat;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@ -10,7 +9,6 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
@ -19,9 +17,6 @@ import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamType;
import org.schabi.newpipe.info_list.InfoItemBuilder;
import org.schabi.newpipe.player.BackgroundPlayer;
import org.schabi.newpipe.player.PopupVideoPlayer;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.util.Localization;
import org.schabi.newpipe.util.NavigationHelper;
@ -91,11 +86,13 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
case AUDIO_LIVE_STREAM:
case NONE:
default:
disableActionDropdown();
break;
}
}
private void enableActionDropdown(final StreamInfoItem item) {
itemActionDropdown.setClickable(true);
itemActionDropdown.setVisibility(View.VISIBLE);
itemActionDropdown.setOnClickListener(new View.OnClickListener() {
@Override
@ -109,10 +106,34 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
});
}
private void disableActionDropdown() {
itemActionDropdown.setVisibility(View.GONE);
itemActionDropdown.setClickable(false);
itemActionDropdown.setOnClickListener(null);
}
private PopupMenu getStreamDropdown(final Context context, final View anchor, final StreamInfoItem infoItem) {
PopupMenu actionMenu = new PopupMenu(context, anchor);
final MenuItem mainPlay = actionMenu.getMenu().add(R.string.play_btn_text);
final MenuItem backgroundEnqueue = actionMenu.getMenu().add(R.string.enqueue_on_background);
backgroundEnqueue.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(infoItem));
return true;
}
});
final MenuItem popupEnqueue = actionMenu.getMenu().add(R.string.enqueue_on_popup);
popupEnqueue.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(infoItem));
return true;
}
});
final MenuItem mainPlay = actionMenu.getMenu().add(R.string.play_all);
mainPlay.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
@ -139,24 +160,6 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder {
}
});
final MenuItem backgroundEnqueue = actionMenu.getMenu().add(R.string.enqueue_on_background);
backgroundEnqueue.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
NavigationHelper.enqueueOnBackgroundPlayer(context, new SinglePlayQueue(infoItem));
return true;
}
});
final MenuItem popupEnqueue = actionMenu.getMenu().add(R.string.enqueue_on_popup);
popupEnqueue.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem menuItem) {
NavigationHelper.enqueueOnPopupPlayer(context, new SinglePlayQueue(infoItem));
return true;
}
});
return actionMenu;
}

View File

@ -157,7 +157,7 @@ public class MediaSourceManager {
}
private void onPlayQueueChanged(final PlayQueueEvent event) {
if (playQueue.isEmpty()) {
if (playQueue.isEmpty() && playQueue.isComplete()) {
playbackListener.shutdown();
return;
}

View File

@ -0,0 +1,151 @@
package org.schabi.newpipe.playlist;
import android.util.Log;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.util.ExtractorHelper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import io.reactivex.SingleObserver;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public final class ChannelPlayQueue extends PlayQueue {
private final String TAG = "ChannelPlayQueue@" + Integer.toHexString(hashCode());
private boolean isInitial;
private boolean isComplete;
private int serviceId;
private String baseUrl;
private String nextUrl;
private transient Disposable fetchReactor;
public ChannelPlayQueue(final ChannelInfoItem item) {
this(item.service_id, item.url, item.url, Collections.<InfoItem>emptyList(), 0);
}
public ChannelPlayQueue(final int serviceId,
final String url,
final String nextPageUrl,
final List<InfoItem> streams,
final int index) {
super(index, extractChannelItems(streams));
this.baseUrl = url;
this.nextUrl = nextPageUrl;
this.serviceId = serviceId;
this.isInitial = streams.isEmpty();
this.isComplete = !isInitial && (nextPageUrl == null || nextPageUrl.isEmpty());
}
@Override
public boolean isComplete() {
return isComplete;
}
@Override
public void fetch() {
if (isInitial) {
ExtractorHelper.getChannelInfo(this.serviceId, this.baseUrl, false)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getChannelInitialObserver());
} else {
ExtractorHelper.getMoreChannelItems(this.serviceId, this.baseUrl, this.nextUrl)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getChannelNextItemsObserver());
}
}
private SingleObserver<ChannelInfo> getChannelInitialObserver() {
return new SingleObserver<ChannelInfo>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
if (isComplete || (fetchReactor != null && !fetchReactor.isDisposed())) {
d.dispose();
} else {
fetchReactor = d;
}
}
@Override
public void onSuccess(@NonNull ChannelInfo result) {
if (!result.has_more_streams) isComplete = true;
nextUrl = result.next_streams_url;
append(extractChannelItems(result.related_streams));
isInitial = false;
fetchReactor.dispose();
fetchReactor = null;
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "Error fetching more playlist, marking playlist as complete.", e);
isComplete = true;
append(); // Notify change
}
};
}
private SingleObserver<ListExtractor.NextItemsResult> getChannelNextItemsObserver() {
return new SingleObserver<ListExtractor.NextItemsResult>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
if (isComplete || (fetchReactor != null && !fetchReactor.isDisposed())) {
d.dispose();
} else {
fetchReactor = d;
}
}
@Override
public void onSuccess(@NonNull ListExtractor.NextItemsResult result) {
if (!result.hasMoreStreams()) isComplete = true;
nextUrl = result.nextItemsUrl;
append(extractChannelItems(result.nextItemsList));
fetchReactor.dispose();
fetchReactor = null;
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "Error fetching more playlist, marking playlist as complete.", e);
isComplete = true;
append(); // Notify change
}
};
}
@Override
public void dispose() {
super.dispose();
if (fetchReactor != null) fetchReactor.dispose();
}
private static List<PlayQueueItem> extractChannelItems(final List<InfoItem> infos) {
List<PlayQueueItem> result = new ArrayList<>();
for (final InfoItem stream : infos) {
if (stream instanceof StreamInfoItem) {
result.add(new PlayQueueItem((StreamInfoItem) stream));
}
}
return result;
}
}

View File

@ -4,6 +4,8 @@ import android.util.Log;
import org.schabi.newpipe.extractor.InfoItem;
import org.schabi.newpipe.extractor.ListExtractor;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.util.ExtractorHelper;
@ -17,9 +19,10 @@ import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public final class ExternalPlayQueue extends PlayQueue {
private final String TAG = "ExternalPlayQueue@" + Integer.toHexString(hashCode());
public final class PlaylistPlayQueue extends PlayQueue {
private final String TAG = "PlaylistPlayQueue@" + Integer.toHexString(hashCode());
private boolean isInitial;
private boolean isComplete;
private int serviceId;
@ -28,7 +31,11 @@ public final class ExternalPlayQueue extends PlayQueue {
private transient Disposable fetchReactor;
public ExternalPlayQueue(final int serviceId,
public PlaylistPlayQueue(final PlaylistInfoItem item) {
this(item.service_id, item.url, item.url, Collections.<InfoItem>emptyList(), 0);
}
public PlaylistPlayQueue(final int serviceId,
final String url,
final String nextPageUrl,
final List<InfoItem> streams,
@ -39,7 +46,8 @@ public final class ExternalPlayQueue extends PlayQueue {
this.nextUrl = nextPageUrl;
this.serviceId = serviceId;
this.isComplete = nextPageUrl == null || nextPageUrl.isEmpty();
this.isInitial = streams.isEmpty();
this.isComplete = !isInitial && (nextPageUrl == null || nextPageUrl.isEmpty());
}
@Override
@ -49,13 +57,51 @@ public final class ExternalPlayQueue extends PlayQueue {
@Override
public void fetch() {
ExtractorHelper.getMorePlaylistItems(this.serviceId, this.baseUrl, this.nextUrl)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getPlaylistObserver());
if (isInitial) {
ExtractorHelper.getPlaylistInfo(this.serviceId, this.baseUrl, false)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getInitialPlaylistObserver());
} else {
ExtractorHelper.getMorePlaylistItems(this.serviceId, this.baseUrl, this.nextUrl)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getPlaylistNextItemsObserver());
}
}
private SingleObserver<ListExtractor.NextItemsResult> getPlaylistObserver() {
private SingleObserver<PlaylistInfo> getInitialPlaylistObserver() {
return new SingleObserver<PlaylistInfo>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
if (isComplete || (fetchReactor != null && !fetchReactor.isDisposed())) {
d.dispose();
} else {
fetchReactor = d;
}
}
@Override
public void onSuccess(@NonNull PlaylistInfo result) {
if (!result.has_more_streams) isComplete = true;
nextUrl = result.next_streams_url;
append(extractPlaylistItems(result.related_streams));
fetchReactor.dispose();
fetchReactor = null;
}
@Override
public void onError(@NonNull Throwable e) {
Log.e(TAG, "Error fetching more playlist, marking playlist as complete.", e);
isComplete = true;
append(); // Notify change
}
};
}
private SingleObserver<ListExtractor.NextItemsResult> getPlaylistNextItemsObserver() {
return new SingleObserver<ListExtractor.NextItemsResult>() {
@Override
public void onSubscribe(@NonNull Disposable d) {

View File

@ -7,7 +7,9 @@
android:layout_height="@dimen/video_item_search_height"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:padding="@dimen/video_item_search_padding">
android:focusable="true"
android:paddingTop="@dimen/video_item_search_padding"
android:paddingBottom="@dimen/video_item_search_padding">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/itemThumbnailView"
@ -16,23 +18,42 @@
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
android:contentDescription="@string/list_thumbnail_view_description"
android:src="@drawable/buddy"
tools:ignore="RtlHardcoded"/>
<ImageButton
android:id="@+id/itemActionDropdown"
android:layout_width="wrap_content"
android:layout_height="@dimen/video_item_search_thumbnail_image_height"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
android:layout_marginStart="@dimen/video_item_search_image_right_margin"
android:background="?attr/selectableItemBackground"
android:src="?attr/more_vertical"
android:visibility="gone"
tools:ignore="ContentDescription"
tools:visibility="visible" />
<TextView
android:id="@+id/itemTitleView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginBottom="@dimen/video_item_search_image_right_margin"
android:layout_toLeftOf="@id/itemActionDropdown"
android:layout_toStartOf="@id/itemActionDropdown"
android:layout_toRightOf="@+id/itemThumbnailView"
android:layout_toEndOf="@+id/itemThumbnailView"
android:ellipsize="end"
android:lines="1"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="@dimen/video_item_search_title_text_size"
tools:text="Channel Title, Lorem ipsum"/>
tools:text="Channel Title, Lorem ipsum" />
<TextView
android:id="@+id/itemChannelDescriptionView"
@ -40,19 +61,25 @@
android:layout_height="wrap_content"
android:layout_above="@+id/itemAdditionalDetails"
android:layout_marginBottom="@dimen/channel_item_description_to_details_margin"
android:layout_toLeftOf="@id/itemActionDropdown"
android:layout_toStartOf="@id/itemActionDropdown"
android:layout_toRightOf="@+id/itemThumbnailView"
android:layout_toEndOf="@+id/itemThumbnailView"
android:ellipsize="end"
android:lines="2"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_uploader_text_size"
tools:text="Channel description, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit"/>
tools:text="Channel description, Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blanditLorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tristique vitae sem vitae blandit" />
<TextView
android:id="@+id/itemAdditionalDetails"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_toLeftOf="@id/itemActionDropdown"
android:layout_toStartOf="@id/itemActionDropdown"
android:layout_toRightOf="@+id/itemThumbnailView"
android:layout_toEndOf="@+id/itemThumbnailView"
android:lines="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"

View File

@ -7,23 +7,43 @@
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:padding="@dimen/video_item_search_padding">
android:focusable="true"
android:paddingTop="@dimen/video_item_search_padding"
android:paddingBottom="@dimen/video_item_search_padding">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/itemThumbnailView"
android:layout_width="48dp"
android:layout_height="42dp"
android:layout_centerVertical="true"
android:layout_marginRight="12dp"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
android:contentDescription="@string/list_thumbnail_view_description"
android:src="@drawable/buddy_channel_item"
tools:ignore="RtlHardcoded"/>
<ImageButton
android:id="@+id/itemActionDropdown"
android:layout_width="wrap_content"
android:layout_height="42dp"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
android:layout_marginStart="@dimen/video_item_search_image_right_margin"
android:background="?attr/selectableItemBackground"
android:src="?attr/more_vertical"
android:visibility="gone"
tools:visibility="visible"
tools:ignore="ContentDescription"/>
<TextView
android:id="@+id/itemTitleView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@id/itemActionDropdown"
android:layout_toStartOf="@id/itemActionDropdown"
android:layout_marginBottom="2dp"
android:layout_marginTop="3dp"
android:layout_toRightOf="@+id/itemThumbnailView"
@ -40,6 +60,8 @@
android:layout_height="wrap_content"
android:layout_below="@+id/itemTitleView"
android:layout_toRightOf="@+id/itemThumbnailView"
android:layout_toLeftOf="@id/itemActionDropdown"
android:layout_toStartOf="@id/itemActionDropdown"
android:lines="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textSize="@dimen/video_item_search_upload_date_text_size"

View File

@ -7,7 +7,9 @@
android:layout_height="@dimen/video_item_search_height"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:padding="@dimen/video_item_search_padding">
android:focusable="true"
android:paddingTop="@dimen/video_item_search_padding"
android:paddingBottom="@dimen/video_item_search_padding">
<ImageView
android:id="@+id/itemThumbnailView"
@ -16,17 +18,35 @@
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
android:contentDescription="@string/list_thumbnail_view_description"
android:scaleType="fitEnd"
android:src="@drawable/dummy_thumbnail_playlist"
tools:ignore="RtlHardcoded"/>
<ImageButton
android:id="@+id/itemActionDropdown"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
android:layout_marginStart="@dimen/video_item_search_image_right_margin"
android:background="?attr/selectableItemBackground"
android:src="?attr/more_vertical"
android:visibility="gone"
tools:visibility="visible"
tools:ignore="ContentDescription"/>
<TextView
android:id="@+id/itemStreamCountView"
android:layout_width="@dimen/playlist_item_thumbnail_stream_count_width"
android:layout_height="match_parent"
android:layout_alignBottom="@id/itemThumbnailView"
android:layout_toLeftOf="@id/itemActionDropdown"
android:layout_toStartOf="@id/itemActionDropdown"
android:layout_alignRight="@id/itemThumbnailView"
android:layout_alignTop="@id/itemThumbnailView"
android:background="@color/playlist_stream_count_background_color"
@ -46,6 +66,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@id/itemActionDropdown"
android:layout_toStartOf="@id/itemActionDropdown"
android:layout_toRightOf="@+id/itemThumbnailView"
android:ellipsize="end"
android:maxLines="2"
@ -59,6 +81,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/itemTitleView"
android:layout_toLeftOf="@id/itemActionDropdown"
android:layout_toStartOf="@id/itemActionDropdown"
android:layout_toRightOf="@+id/itemThumbnailView"
android:lines="1"
android:textAppearance="?android:attr/textAppearanceSmall"

View File

@ -18,7 +18,7 @@
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="@dimen/video_item_search_padding"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
android:layout_marginRight="@dimen/video_item_search_image_right_margin"
android:contentDescription="@string/list_thumbnail_view_description"
android:scaleType="centerCrop"

View File

@ -51,6 +51,8 @@
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginLeft="@dimen/video_item_search_image_right_margin"
android:layout_marginStart="@dimen/video_item_search_image_right_margin"
android:background="?attr/selectableItemBackground"
android:src="?attr/more_vertical"
android:visibility="gone"