Replaced all episodes list with new adapter

This commit is contained in:
ByteHamster 2020-02-04 23:13:15 +01:00
parent e14ad4d859
commit 17aae8c2db
7 changed files with 54 additions and 425 deletions

View File

@ -1,54 +1,29 @@
package de.danoeh.antennapod.adapter;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.ItemTouchHelper;
import android.text.Layout;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.joanzapata.iconify.Iconify;
import java.lang.ref.WeakReference;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.fragment.ItemFragment;
import de.danoeh.antennapod.fragment.ItemPagerFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.view.EpisodeItemViewHolder;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference;
/**
* List adapter for the list of new episodes
* List adapter for the list of new episodes.
*/
public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesRecycleAdapter.Holder> {
private static final String TAG = AllEpisodesRecycleAdapter.class.getSimpleName();
public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<EpisodeItemViewHolder>
implements View.OnCreateContextMenuListener {
private final WeakReference<MainActivity> mainActivityRef;
private final ItemAccess itemAccess;
@ -56,149 +31,38 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
private FeedItem selectedItem;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
public AllEpisodesRecycleAdapter(MainActivity mainActivity,
ItemAccess itemAccess,
boolean showOnlyNewEpisodes) {
public AllEpisodesRecycleAdapter(MainActivity mainActivity, ItemAccess itemAccess, boolean showOnlyNewEpisodes) {
super();
this.mainActivityRef = new WeakReference<>(mainActivity);
this.itemAccess = itemAccess;
this.showOnlyNewEpisodes = showOnlyNewEpisodes;
}
playingBackGroundColor = ThemeUtils.getColorFromAttr(mainActivity, R.attr.currently_playing_background);
normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent);
@NonNull
@Override
public EpisodeItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
EpisodeItemViewHolder viewHolder = new EpisodeItemViewHolder(mainActivityRef.get(), parent);
viewHolder.dragHandle.setVisibility(View.GONE);
return viewHolder;
}
@Override
public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.new_episodes_listitem, parent, false);
Holder holder = new Holder(view);
holder.container = view.findViewById(R.id.container);
holder.content = view.findViewById(R.id.content);
holder.placeholder = view.findViewById(R.id.txtvPlaceholder);
holder.title = view.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
holder.pubDate = view
.findViewById(R.id.txtvPublished);
holder.statusUnread = view.findViewById(R.id.statusUnread);
holder.butSecondary = view
.findViewById(R.id.butSecondaryAction);
holder.queueStatus = view
.findViewById(R.id.imgvInPlaylist);
holder.progress = view
.findViewById(R.id.pbar_progress);
holder.cover = view.findViewById(R.id.imgvCover);
holder.txtvDuration = view.findViewById(R.id.txtvDuration);
holder.item = null;
holder.mainActivityRef = mainActivityRef;
// so we can grab this later
view.setTag(holder);
return holder;
}
@Override
public void onBindViewHolder(final Holder holder, int position) {
final FeedItem item = itemAccess.getItem(position);
if (item == null) return;
public void onBindViewHolder(EpisodeItemViewHolder holder, int pos) {
FeedItem item = itemAccess.getItem(pos);
holder.bind(item);
holder.itemView.setOnLongClickListener(v -> {
this.selectedItem = item;
selectedItem = item;
return false;
});
holder.item = item;
holder.placeholder.setVisibility(View.VISIBLE);
holder.placeholder.setText(item.getFeed().getTitle());
holder.title.setText(item.getTitle());
String pubDateStr = DateUtils.formatAbbrev(mainActivityRef.get(), item.getPubDate());
holder.pubDate.setText(pubDateStr);
if (showOnlyNewEpisodes || !item.isNew()) {
holder.statusUnread.setVisibility(View.INVISIBLE);
} else {
holder.statusUnread.setVisibility(View.VISIBLE);
}
if(item.isPlayed()) {
holder.content.setAlpha(0.5f);
} else {
holder.content.setAlpha(1.0f);
}
FeedMedia media = item.getMedia();
if (media != null) {
final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
if (media.getDuration() > 0) {
holder.txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
} else if (media.getSize() > 0) {
holder.txtvDuration.setText(Converter.byteToString(media.getSize()));
} else if(NetworkUtils.isEpisodeHeadDownloadAllowed() && !media.checkedOnSizeButUnknown()) {
holder.txtvDuration.setText("{fa-spinner}");
Iconify.addIcons(holder.txtvDuration);
NetworkUtils.getFeedMediaSizeObservable(media)
.subscribe(
size -> {
if (size > 0) {
holder.txtvDuration.setText(Converter.byteToString(size));
} else {
holder.txtvDuration.setText("");
}
}, error -> {
holder.txtvDuration.setText("");
Log.e(TAG, Log.getStackTraceString(error));
});
} else {
holder.txtvDuration.setText("");
holder.itemView.setOnClickListener(v -> {
MainActivity activity = mainActivityRef.get();
if (activity != null) {
long[] ids = itemAccess.getQueueIds().toArray();
int position = ArrayUtils.indexOf(ids, item.getId());
activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
FeedItem.State state = item.getState();
if (isDownloadingMedia) {
holder.progress.setVisibility(View.VISIBLE);
// item is being downloaded
holder.progress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
} else if (state == FeedItem.State.PLAYING
|| state == FeedItem.State.IN_PROGRESS) {
if (media.getDuration() > 0) {
int progress = (int) (100.0 * media.getPosition() / media.getDuration());
holder.progress.setProgress(progress);
holder.progress.setVisibility(View.VISIBLE);
}
} else {
holder.progress.setVisibility(View.INVISIBLE);
}
if (media.isCurrentlyPlaying()) {
holder.container.setBackgroundColor(playingBackGroundColor);
} else {
holder.container.setBackgroundColor(normalBackGroundColor);
}
} else {
holder.progress.setVisibility(View.INVISIBLE);
holder.txtvDuration.setVisibility(View.GONE);
}
boolean isInQueue = itemAccess.isInQueue(item);
if (isInQueue) {
holder.queueStatus.setVisibility(View.VISIBLE);
} else {
holder.queueStatus.setVisibility(View.INVISIBLE);
}
ItemActionButton actionButton = ItemActionButton.forItem(item, isInQueue);
actionButton.configure(holder.butSecondary, mainActivityRef.get());
holder.butSecondary.setFocusable(false);
holder.butSecondary.setTag(item);
new CoverLoader(mainActivityRef.get())
.withUri(ImageResourceUtils.getImageLocation(item))
.withFallbackUri(item.getFeed().getImageLocation())
.withPlaceholderView(holder.placeholder)
.withCoverView(holder.cover)
.load();
});
holder.itemView.setOnCreateContextMenuListener(this);
}
@Nullable
@ -217,73 +81,12 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
return itemAccess.getCount();
}
public class Holder extends RecyclerView.ViewHolder
implements View.OnClickListener,
View.OnCreateContextMenuListener,
ItemTouchHelperViewHolder {
LinearLayout content;
FrameLayout container;
TextView placeholder;
TextView title;
TextView pubDate;
View statusUnread;
ImageView queueStatus;
ImageView cover;
ProgressBar progress;
TextView txtvDuration;
ImageButton butSecondary;
FeedItem item;
WeakReference<MainActivity> mainActivityRef;
public Holder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
itemView.setOnCreateContextMenuListener(this);
}
@Override
public void onClick(View v) {
MainActivity mainActivity = mainActivityRef.get();
if (mainActivity != null) {
LongList itemIds = itemAccess.getItemsIds();
long[] ids = itemIds.toArray();
mainActivity.loadChildFragment(ItemPagerFragment.newInstance(ids, itemIds.indexOf(item.getId())));
}
}
@Override
public void onItemSelected() {
itemView.setAlpha(0.5f);
}
@Override
public void onItemClear() {
itemView.setAlpha(1.0f);
}
public FeedItem getFeedItem() { return item; }
@Override
public void onCreateContextMenu(final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
FeedItem item = itemAccess.getItem(getAdapterPosition());
MenuInflater inflater = mainActivityRef.get().getMenuInflater();
inflater.inflate(R.menu.feeditemlist_context, menu);
if (item != null) {
menu.setHeaderTitle(item.getTitle());
}
FeedItemMenuHandler.onPrepareMenu(menu, item);
}
public boolean isCurrentlyPlayingItem() {
return item.getMedia() != null && item.getMedia().isCurrentlyPlaying();
}
public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
progress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
}
@Override
public void onCreateContextMenu(final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
MenuInflater inflater = mainActivityRef.get().getMenuInflater();
inflater.inflate(R.menu.feeditemlist_context, menu);
menu.setHeaderTitle(selectedItem.getTitle());
FeedItemMenuHandler.onPrepareMenu(menu, selectedItem, R.id.skip_episode_item);
}
public interface ItemAccess {
@ -292,12 +95,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
FeedItem getItem(int position);
LongList getItemsIds();
int getItemDownloadProgressPercent(FeedItem item);
boolean isInQueue(FeedItem item);
LongList getQueueIds();
}

View File

@ -28,6 +28,7 @@ import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.view.EpisodeItemViewHolder;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@ -384,32 +385,6 @@ public abstract class EpisodesListFragment extends Fragment {
return null;
}
@Override
public LongList getItemsIds() {
LongList ids = new LongList(episodes.size());
for (FeedItem episode : episodes) {
ids.add(episode.getId());
}
return ids;
}
@Override
public int getItemDownloadProgressPercent(FeedItem item) {
for (Downloader downloader : downloaderList) {
DownloadRequest downloadRequest = downloader.getDownloadRequest();
if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
&& downloadRequest.getFeedfileId() == item.getMedia().getId()) {
return downloadRequest.getProgressPercent();
}
}
return 0;
}
@Override
public boolean isInQueue(FeedItem item) {
return item != null && item.isTagged(FeedItem.TAG_QUEUE);
}
@Override
public LongList getQueueIds() {
LongList queueIds = new LongList();
@ -444,8 +419,7 @@ public abstract class EpisodesListFragment extends Fragment {
public void onEventMainThread(PlaybackPositionEvent event) {
if (listAdapter != null) {
for (int i = 0; i < listAdapter.getItemCount(); i++) {
AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)
recyclerView.findViewHolderForAdapterPosition(i);
EpisodeItemViewHolder holder = (EpisodeItemViewHolder) recyclerView.findViewHolderForAdapterPosition(i);
if (holder != null && holder.isCurrentlyPlayingItem()) {
holder.notifyPlaybackPositionUpdated(event);
break;

View File

@ -10,6 +10,7 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import de.danoeh.antennapod.view.EpisodeItemViewHolder;
import org.greenrobot.eventbus.Subscribe;
import java.util.List;
@ -63,8 +64,8 @@ public class FavoriteEpisodesFragment extends EpisodesListFragment {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder) viewHolder;
Log.d(TAG, String.format("remove(%s)", holder.getItemId()));
EpisodeItemViewHolder holder = (EpisodeItemViewHolder) viewHolder;
Log.d(TAG, String.format("remove(%s)", holder.getFeedItem().getId()));
if (disposable != null) {
disposable.dispose();

View File

@ -16,6 +16,7 @@ import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.view.EpisodeItemViewHolder;
/**
* Like 'EpisodesFragment' except that it only shows new episodes and
@ -63,7 +64,7 @@ public class NewEpisodesFragment extends EpisodesListFragment {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder) viewHolder;
EpisodeItemViewHolder holder = (EpisodeItemViewHolder) viewHolder;
FeedItemMenuHandler.removeNewFlagWithUndo(NewEpisodesFragment.this, holder.getFeedItem());
}

View File

@ -176,6 +176,10 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder
}
}
public FeedItem getFeedItem() {
return item;
}
public boolean isCurrentlyPlayingItem() {
return item.getMedia() != null && item.getMedia().isCurrentlyPlaying();
}

View File

@ -7,7 +7,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:layout_height="150dp"
android:gravity="center_vertical"
android:baselineAligned="false">
@ -48,8 +47,7 @@
android:layout_height="@dimen/thumbnail_length_queue_item"
android:layout_centerVertical="true"
android:contentDescription="@string/cover_label"
tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
tools:src="@tools:sample/avatars" />
</RelativeLayout>
@ -80,7 +78,8 @@
android:layout_height="wrap_content"
android:id="@+id/statusUnread"
android:layout_marginRight="4dp"
android:layout_marginEnd="4dp"/>
android:layout_marginEnd="4dp"
tools:text="@sample/episodes.json/data/status_label"/>
<ImageView
android:layout_width="16dp"
@ -121,7 +120,7 @@
android:layout_height="wrap_content"
android:layout_marginRight="4dp"
android:layout_marginEnd="4dp"
tools:text="Feb 12"/>
tools:text="@sample/episodes.json/data/published_at"/>
<TextView
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
@ -147,7 +146,7 @@
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="Item title"
tools:text="@sample/episodes.json/data/title"
android:ellipsize="end"
tools:background="@android:color/holo_blue_light"/>
@ -183,7 +182,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
tools:text="01:23:23"
tools:text="@sample/episodes.json/data/duration"
tools:background="@android:color/holo_blue_light"/>
</LinearLayout>

View File

@ -1,147 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="@+id/container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
android:gravity="center_vertical">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/txtvPlaceholder"
android:layout_width="@dimen/thumbnail_length_itemlist"
android:layout_height="@dimen/thumbnail_length_itemlist"
android:layout_gravity="center_vertical"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:background="@color/light_gray"
android:ellipsize="end"
android:maxLines="3"
android:gravity="center"/>
<ImageView
android:id="@+id/imgvCover"
android:layout_height="64dp"
android:layout_width="64dp"
android:layout_alignLeft="@id/txtvPlaceholder"
android:layout_alignStart="@id/txtvPlaceholder"
android:layout_alignTop="@id/txtvPlaceholder"
android:layout_alignRight="@id/txtvPlaceholder"
android:layout_alignEnd="@id/txtvPlaceholder"
android:layout_alignBottom="@id/txtvPlaceholder"
android:contentDescription="@string/cover_label"
tools:src="@tools:sample/avatars" />
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
android:layout_marginStart="@dimen/listitem_threeline_textleftpadding"
android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
android:layout_marginEnd="@dimen/listitem_threeline_textrightpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_weight="1">
<TextView
android:id="@+id/statusUnread"
style="@style/AntennaPod.TextView.UnreadIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
tools:text="@sample/episodes.json/data/status_label"/>
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@id/statusUnread"
android:layout_toStartOf="@id/statusUnread"
tools:text="@sample/episodes.json/data/title" />
<RelativeLayout
android:id="@+id/bottom_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="@id/txtvTitle"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true">
<TextView
android:id="@+id/txtvDuration"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
tools:text="@sample/episodes.json/data/duration" />
<ImageView
android:id="@+id/imgvInPlaylist"
android:layout_width="@dimen/enc_icons_size"
android:layout_height="@dimen/enc_icons_size"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:contentDescription="@string/in_queue_label"
android:src="?attr/stat_playlist"
tools:src="@sample/inplaylist" />
<TextView
android:id="@+id/txtvPublished"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@id/imgvInPlaylist"
android:layout_toStartOf="@id/imgvInPlaylist"
android:ellipsize="end"
tools:text="@sample/episodes.json/data/published_at" />
<ProgressBar
android:id="@+id/pbar_progress"
style="?attr/progressBarTheme"
android:layout_width="match_parent"
android:layout_height="4dp"
android:layout_below="@id/txtvDuration"
android:max="100" />
</RelativeLayout>
</RelativeLayout>
<include layout="@layout/vertical_list_divider"/>
<include layout="@layout/secondary_action" />
</LinearLayout>
</FrameLayout>