Merge pull request #3827 from ByteHamster/extract-queue-item-view

Rewrite list item display
This commit is contained in:
H. Lehmann 2020-02-12 11:09:11 +01:00 committed by GitHub
commit 25fbff6afd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 1244 additions and 2225 deletions

View File

@ -228,7 +228,7 @@ public class PlaybackTest {
final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
Matcher<View> allEpisodesMatcher = allOf(withId(android.R.id.list), isDisplayed(), hasMinimumChildCount(2));
onView(isRoot()).perform(waitForView(allEpisodesMatcher, 1000));
onView(allEpisodesMatcher).perform(actionOnItemAtPosition(0, clickChildViewWithId(R.id.butSecondaryAction)));
onView(allEpisodesMatcher).perform(actionOnItemAtPosition(0, clickChildViewWithId(R.id.secondaryActionButton)));
FeedMedia media = episodes.get(0).getMedia();
Awaitility.await().atMost(1, TimeUnit.SECONDS).until(
@ -244,7 +244,7 @@ public class PlaybackTest {
Matcher<View> queueMatcher = allOf(withId(R.id.recyclerView), isDisplayed(), hasMinimumChildCount(2));
onView(isRoot()).perform(waitForView(queueMatcher, 1000));
onView(queueMatcher).perform(actionOnItemAtPosition(itemIdx, clickChildViewWithId(R.id.butSecondaryAction)));
onView(queueMatcher).perform(actionOnItemAtPosition(itemIdx, clickChildViewWithId(R.id.secondaryActionButton)));
FeedMedia media = queue.get(itemIdx).getMedia();
Awaitility.await().atMost(1, TimeUnit.SECONDS).until(

View File

@ -1,74 +0,0 @@
package de.danoeh.antennapod.adapter;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.joanzapata.iconify.Iconify;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.NetworkUtils;
/**
* Utility methods for adapters
*/
class AdapterUtils {
private static final String TAG = AdapterUtils.class.getSimpleName();
private AdapterUtils() {
}
/**
* Updates the contents of the TextView that shows the current playback position and the ProgressBar.
*/
static void updateEpisodePlaybackProgress(FeedItem item, TextView txtvPos, ProgressBar episodeProgress) {
FeedMedia media = item.getMedia();
episodeProgress.setVisibility(View.GONE);
if (media == null) {
txtvPos.setVisibility(View.GONE);
return;
} else {
txtvPos.setVisibility(View.VISIBLE);
}
FeedItem.State state = item.getState();
if (state == FeedItem.State.PLAYING
|| state == FeedItem.State.IN_PROGRESS) {
if (media.getDuration() > 0) {
episodeProgress.setVisibility(View.VISIBLE);
episodeProgress.setProgress((int) (((double) media
.getPosition()) / media.getDuration() * 100));
txtvPos.setText(Converter.getDurationStringLong(media.getDuration()
- media.getPosition()));
}
} else if (!media.isDownloaded()) {
if (media.getSize() > 0) {
txtvPos.setText(Converter.byteToString(media.getSize()));
} else if(NetworkUtils.isEpisodeHeadDownloadAllowed() && !media.checkedOnSizeButUnknown()) {
txtvPos.setText("{fa-spinner}");
Iconify.addIcons(txtvPos);
NetworkUtils.getFeedMediaSizeObservable(media)
.subscribe(
size -> {
if (size > 0) {
txtvPos.setText(Converter.byteToString(size));
} else {
txtvPos.setText("");
}
}, error -> {
txtvPos.setText("");
Log.e(TAG, Log.getStackTraceString(error));
});
} else {
txtvPos.setText("");
}
} else {
txtvPos.setText(Converter.getDurationStringLong(media.getDuration()));
}
}
}

View File

@ -1,204 +1,73 @@
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.core.util.FeedItemUtil;
import de.danoeh.antennapod.fragment.ItemPagerFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
/**
* 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;
private final boolean showOnlyNewEpisodes;
private List<FeedItem> episodes = new ArrayList<>();
private FeedItem selectedItem;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
public AllEpisodesRecycleAdapter(MainActivity mainActivity,
ItemAccess itemAccess,
boolean showOnlyNewEpisodes) {
public AllEpisodesRecycleAdapter(MainActivity mainActivity) {
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);
public void updateItems(List<FeedItem> items) {
episodes = items;
notifyDataSetChanged();
}
@NonNull
@Override
public EpisodeItemViewHolder onCreateViewHolder(@NonNull 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 = episodes.get(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 = FeedItemUtil.getIds(episodes);
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, true);
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);
holder.hideSeparatorIfNecessary();
}
@Nullable
@ -208,98 +77,21 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
@Override
public long getItemId(int position) {
FeedItem item = itemAccess.getItem(position);
FeedItem item = episodes.get(position);
return item != null ? item.getId() : RecyclerView.NO_POSITION;
}
@Override
public int getItemCount() {
return itemAccess.getCount();
return episodes.size();
}
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()));
}
}
public interface ItemAccess {
int getCount();
FeedItem getItem(int position);
LongList getItemsIds();
int getItemDownloadProgressPercent(FeedItem item);
boolean isInQueue(FeedItem item);
LongList getQueueIds();
@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);
}
/**

View File

@ -1,36 +1,26 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
import android.text.Spanned;
import android.text.style.ClickableSpan;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
private static final String TAG = "ChapterListAdapter";
private Playable media;
private int defaultTextColor;
private final Callback callback;
private int currentChapterIndex = -1;
@ -59,11 +49,11 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
convertView = inflater.inflate(R.layout.simplechapter_item, parent, false);
holder.view = convertView;
holder.title = convertView.findViewById(R.id.txtvTitle);
defaultTextColor = holder.title.getTextColors().getDefaultColor();
holder.start = convertView.findViewById(R.id.txtvStart);
holder.link = convertView.findViewById(R.id.txtvLink);
holder.duration = convertView.findViewById(R.id.txtvDuration);
holder.butPlayChapter = convertView.findViewById(R.id.butPlayChapter);
holder.secondaryActionButton = convertView.findViewById(R.id.secondaryActionButton);
holder.secondaryActionIcon = convertView.findViewById(R.id.secondaryActionIcon);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
@ -83,60 +73,15 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
holder.duration.setText(getContext().getString(R.string.chapter_duration,
Converter.getDurationStringLong((int) duration)));
if (sc.getLink() != null) {
if (sc.getLink() == null) {
holder.link.setVisibility(View.GONE);
} else {
holder.link.setVisibility(View.VISIBLE);
holder.link.setText(sc.getLink());
Linkify.addLinks(holder.link, Linkify.WEB_URLS);
} else {
holder.link.setVisibility(View.GONE);
holder.link.setOnClickListener(v -> IntentUtils.openInBrowser(getContext(), sc.getLink()));
}
holder.link.setMovementMethod(null);
holder.link.setOnTouchListener((v, event) -> {
// from
// http://stackoverflow.com/questions/7236840/android-textview-linkify-intercepts-with-parent-view-gestures
TextView widget = (TextView) v;
Object text = widget.getText();
if (text instanceof Spanned) {
Spannable buffer = (Spannable) text;
int action = event.getAction();
if (action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= widget.getTotalPaddingLeft();
y -= widget.getTotalPaddingTop();
x += widget.getScrollX();
y += widget.getScrollY();
Layout layout = widget.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = buffer.getSpans(off, off,
ClickableSpan.class);
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
}
return true;
}
}
}
return false;
});
holder.butPlayChapter.setOnClickListener(v -> {
holder.secondaryActionIcon.setImageResource(ThemeUtils.getDrawableFromAttr(getContext(), R.attr.av_play));
holder.secondaryActionButton.setOnClickListener(v -> {
if (callback != null) {
callback.onPlayChapterButtonClicked(position);
}
@ -147,8 +92,6 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
holder.view.setBackgroundColor(playingBackGroundColor);
} else {
holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
holder.title.setTextColor(defaultTextColor);
holder.start.setTextColor(defaultTextColor);
}
return convertView;
@ -160,7 +103,8 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
TextView start;
TextView link;
TextView duration;
ImageButton butPlayChapter;
View secondaryActionButton;
ImageView secondaryActionIcon;
}
@Override

View File

@ -26,171 +26,139 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.view.viewholder.DownloadItemViewHolder;
import de.danoeh.antennapod.view.viewholder.FeedViewHolder;
/** Displays a list of DownloadStatus entries. */
/**
* Displays a list of DownloadStatus entries.
*/
public class DownloadLogAdapter extends BaseAdapter {
private static final String TAG = "DownloadLogAdapter";
private static final String TAG = "DownloadLogAdapter";
private final Context context;
private final Context context;
private final ItemAccess itemAccess;
public DownloadLogAdapter(Context context, ItemAccess itemAccess) {
super();
public DownloadLogAdapter(Context context, ItemAccess itemAccess) {
super();
this.itemAccess = itemAccess;
this.context = context;
}
this.context = context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
DownloadStatus status = getItem(position);
if (convertView == null) {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloadlog_item, parent, false);
holder.icon = convertView.findViewById(R.id.txtvIcon);
holder.retry = convertView.findViewById(R.id.btnRetry);
holder.date = convertView.findViewById(R.id.txtvDate);
holder.title = convertView.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
holder.type = convertView.findViewById(R.id.txtvType);
holder.reason = convertView.findViewById(R.id.txtvReason);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
holder.type.setText(R.string.download_type_feed);
} else if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
holder.type.setText(R.string.download_type_media);
}
if (status.getTitle() != null) {
holder.title.setText(status.getTitle());
} else {
holder.title.setText(R.string.download_log_title_unknown);
}
holder.date.setText(DateUtils.getRelativeTimeSpanString(
status.getCompletionDate().getTime(),
System.currentTimeMillis(), 0, 0));
if (status.isSuccessful()) {
holder.icon.setTextColor(ContextCompat.getColor(convertView.getContext(),
R.color.download_success_green));
holder.icon.setText("{fa-check-circle}");
holder.retry.setVisibility(View.GONE);
holder.reason.setVisibility(View.GONE);
} else {
holder.icon.setTextColor(ContextCompat.getColor(convertView.getContext(),
R.color.download_failed_red));
holder.icon.setText("{fa-times-circle}");
String reasonText = status.getReason().getErrorString(context);
if (status.getReasonDetailed() != null) {
reasonText += ": " + status.getReasonDetailed();
}
holder.reason.setText(reasonText);
holder.reason.setVisibility(View.VISIBLE);
if(!newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
holder.retry.setVisibility(View.VISIBLE);
holder.retry.setOnClickListener(clickListener);
ButtonHolder btnHolder;
if(holder.retry.getTag() != null) {
btnHolder = (ButtonHolder) holder.retry.getTag();
} else {
btnHolder = new ButtonHolder();
}
btnHolder.typeId = status.getFeedfileType();
btnHolder.id = status.getFeedfileId();
holder.retry.setTag(btnHolder);
} else {
holder.retry.setVisibility(View.GONE);
holder.retry.setOnClickListener(null);
holder.retry.setTag(null);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
DownloadItemViewHolder holder;
if (convertView == null) {
holder = new DownloadItemViewHolder(context, parent);
} else {
holder = (DownloadItemViewHolder) convertView.getTag();
}
return convertView;
}
DownloadStatus status = getItem(position);
if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
holder.type.setText(R.string.download_type_feed);
} else if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
holder.type.setText(R.string.download_type_media);
}
private final View.OnClickListener clickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
ButtonHolder holder = (ButtonHolder) v.getTag();
if(holder.typeId == Feed.FEEDFILETYPE_FEED) {
Feed feed = DBReader.getFeed(holder.id);
if (feed != null) {
try {
DBTasks.forceRefreshFeed(context, feed);
} catch (DownloadRequestException e) {
e.printStackTrace();
}
} else {
Log.wtf(TAG, "Could not find feed for feed id: " + holder.id);
}
} else if(holder.typeId == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
FeedMedia media = DBReader.getFeedMedia(holder.id);
if (media != null) {
try {
DownloadRequester.getInstance().downloadMedia(context, media.getItem());
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
} catch (DownloadRequestException e) {
e.printStackTrace();
DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
}
} else {
Log.wtf(TAG, "Could not find media for id: " + holder.id);
}
} else {
Log.wtf(TAG, "Unexpected type id: " + holder.typeId);
}
v.setVisibility(View.GONE);
}
};
if (status.getTitle() != null) {
holder.title.setText(status.getTitle());
} else {
holder.title.setText(R.string.download_log_title_unknown);
}
holder.date.setText(DateUtils.getRelativeTimeSpanString(status.getCompletionDate().getTime(),
System.currentTimeMillis(), 0, 0));
private boolean newerWasSuccessful(int position, int feedTypeId, long id) {
for (int i = 0; i < position; i++) {
DownloadStatus status = getItem(i);
if (status.getFeedfileType() == feedTypeId && status.getFeedfileId() == id &&
status.isSuccessful()) return true;
}
return false;
}
if (status.isSuccessful()) {
holder.icon.setTextColor(ContextCompat.getColor(context, R.color.download_success_green));
holder.icon.setText("{fa-check-circle}");
holder.secondaryActionButton.setVisibility(View.INVISIBLE);
holder.reason.setVisibility(View.GONE);
} else {
holder.icon.setTextColor(ContextCompat.getColor(context, R.color.download_failed_red));
holder.icon.setText("{fa-times-circle}");
String reasonText = status.getReason().getErrorString(context);
if (status.getReasonDetailed() != null) {
reasonText += ": " + status.getReasonDetailed();
}
holder.reason.setText(reasonText);
holder.reason.setVisibility(View.VISIBLE);
static class Holder {
IconTextView icon;
IconButton retry;
TextView title;
TextView type;
TextView date;
TextView reason;
}
if (newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
holder.secondaryActionButton.setVisibility(View.INVISIBLE);
holder.secondaryActionButton.setOnClickListener(null);
holder.secondaryActionButton.setTag(null);
} else {
holder.secondaryActionIcon.setImageResource(
ThemeUtils.getDrawableFromAttr(context, R.attr.navigation_refresh));
holder.secondaryActionButton.setVisibility(View.VISIBLE);
static class ButtonHolder {
int typeId;
long id;
}
if (status.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
holder.secondaryActionButton.setOnClickListener(v -> {
holder.secondaryActionButton.setVisibility(View.INVISIBLE);
Feed feed = DBReader.getFeed(status.getFeedfileId());
if (feed == null) {
Log.e(TAG, "Could not find feed for feed id: " + status.getFeedfileId());
return;
}
try {
DBTasks.forceRefreshFeed(context, feed);
} catch (DownloadRequestException e) {
e.printStackTrace();
}
});
} else if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
holder.secondaryActionButton.setOnClickListener(v -> {
holder.secondaryActionButton.setVisibility(View.INVISIBLE);
FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
if (media == null) {
Log.e(TAG, "Could not find feed media for feed id: " + status.getFeedfileId());
return;
}
try {
DownloadRequester.getInstance().downloadMedia(context, media.getItem());
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
} catch (DownloadRequestException e) {
e.printStackTrace();
DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
}
});
}
}
}
@Override
public int getCount() {
return holder.itemView;
}
private boolean newerWasSuccessful(int position, int feedTypeId, long id) {
for (int i = 0; i < position; i++) {
DownloadStatus status = getItem(i);
if (status.getFeedfileType() == feedTypeId && status.getFeedfileId() == id && status.isSuccessful()) {
return true;
}
}
return false;
}
@Override
public int getCount() {
return itemAccess.getCount();
}
}
@Override
public DownloadStatus getItem(int position) {
@Override
public DownloadStatus getItem(int position) {
return itemAccess.getItem(position);
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
public interface ItemAccess {
int getCount();
DownloadStatus getItem(int position);
int getCount();
DownloadStatus getItem(int position);
}
}

View File

@ -1,37 +1,25 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.os.Build;
import android.text.Layout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
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.ThemeUtils;
import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder;
/**
* Shows a list of downloaded episodes
* Shows a list of downloaded episodes.
*/
public class DownloadedEpisodesListAdapter extends BaseAdapter {
private final Context context;
private final MainActivity activity;
private final ItemAccess itemAccess;
public DownloadedEpisodesListAdapter(Context context, ItemAccess itemAccess) {
public DownloadedEpisodesListAdapter(MainActivity activity, ItemAccess itemAccess) {
super();
this.context = context;
this.activity = activity;
this.itemAccess = itemAccess;
}
@ -52,77 +40,21 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
final FeedItem item = getItem(position);
if (item == null) return null;
EpisodeItemViewHolder holder;
if (convertView == null) {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloaded_episodeslist_item,
parent, false);
holder.imageView = convertView.findViewById(R.id.imgvImage);
holder.title = convertView.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
holder.txtvSize = convertView.findViewById(R.id.txtvSize);
holder.queueStatus = convertView.findViewById(R.id.imgvInPlaylist);
holder.pubDate = convertView
.findViewById(R.id.txtvPublished);
holder.butSecondary = convertView
.findViewById(R.id.butSecondaryAction);
convertView.setTag(holder);
holder = new EpisodeItemViewHolder(activity, parent);
} else {
holder = (Holder) convertView.getTag();
holder = (EpisodeItemViewHolder) convertView.getTag();
}
Glide.with(context)
.load(ImageResourceUtils.getImageLocation(item))
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(holder.imageView);
final FeedItem item = getItem(position);
holder.bind(item);
holder.dragHandle.setVisibility(View.GONE);
holder.secondaryActionIcon.setImageResource(ThemeUtils.getDrawableFromAttr(activity, R.attr.content_discard));
holder.secondaryActionButton.setOnClickListener(v -> itemAccess.onFeedItemSecondaryAction(item));
holder.hideSeparatorIfNecessary();
if(item.isPlayed()) {
convertView.setAlpha(0.5f);
} else {
convertView.setAlpha(1.0f);
}
holder.title.setText(item.getTitle());
holder.txtvSize.setText(Converter.byteToString(item.getMedia().getSize()));
holder.queueStatus.setVisibility(item.isTagged(FeedItem.TAG_QUEUE) ? View.VISIBLE : View.GONE);
String pubDateStr = DateUtils.formatAbbrev(context, item.getPubDate());
holder.pubDate.setText(pubDateStr);
holder.butSecondary.setFocusable(false);
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
return convertView;
}
private final View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag();
itemAccess.onFeedItemSecondaryAction(item);
}
};
static class Holder {
ImageView imageView;
TextView title;
TextView txtvSize;
ImageView queueStatus;
TextView pubDate;
ImageButton butSecondary;
return holder.itemView;
}
public interface ItemAccess {

View File

@ -5,23 +5,25 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.ImageView;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.view.CircularProgressBar;
public class DownloadlistAdapter extends BaseAdapter {
private final ItemAccess itemAccess;
private final Context context;
public DownloadlistAdapter(Context context,
ItemAccess itemAccess) {
public DownloadlistAdapter(Context context, ItemAccess itemAccess) {
super();
this.context = context;
this.itemAccess = itemAccess;
@ -47,47 +49,44 @@ public class DownloadlistAdapter extends BaseAdapter {
Holder holder;
Downloader downloader = getItem(position);
DownloadRequest request = downloader.getDownloadRequest();
// Inflate layout
if (convertView == null) {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloadlist_item, parent, false);
holder.title = convertView.findViewById(R.id.txtvTitle);
holder.downloaded = convertView
.findViewById(R.id.txtvDownloaded);
holder.percent = convertView
.findViewById(R.id.txtvPercent);
holder.progbar = convertView
.findViewById(R.id.progProgress);
holder.butSecondary = convertView
.findViewById(R.id.butSecondaryAction);
holder.status = convertView.findViewById(R.id.txtvStatus);
holder.secondaryActionButton = convertView.findViewById(R.id.secondaryActionButton);
holder.secondaryActionIcon = convertView.findViewById(R.id.secondaryActionIcon);
holder.secondaryActionProgress = convertView.findViewById(R.id.secondaryActionProgress);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
holder.title.setText(request.getTitle());
holder.secondaryActionIcon.setImageResource(ThemeUtils.getDrawableFromAttr(context, R.attr.navigation_cancel));
holder.secondaryActionButton.setTag(downloader);
holder.secondaryActionButton.setOnClickListener(butSecondaryListener);
holder.secondaryActionProgress.setPercentage(0, request);
holder.progbar.setIndeterminate(request.getSoFar() <= 0);
String strDownloaded = Converter.byteToString(request.getSoFar());
if (request.getSize() != DownloadStatus.SIZE_UNKNOWN) {
strDownloaded += " / " + Converter.byteToString(request.getSize());
holder.percent.setText(request.getProgressPercent() + "%");
holder.progbar.setProgress(request.getProgressPercent());
holder.percent.setVisibility(View.VISIBLE);
} else {
holder.progbar.setProgress(0);
holder.percent.setVisibility(View.INVISIBLE);
String status = "";
if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
status += context.getString(R.string.download_type_feed);
} else if (request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
status += context.getString(R.string.download_type_media);
}
holder.downloaded.setText(strDownloaded);
holder.butSecondary.setFocusable(false);
holder.butSecondary.setTag(downloader);
holder.butSecondary.setOnClickListener(butSecondaryListener);
status += " · ";
if (request.getSoFar() <= 0) {
status += context.getString(R.string.download_queued);
} else {
status += Converter.byteToString(request.getSoFar());
if (request.getSize() != DownloadStatus.SIZE_UNKNOWN) {
status += " / " + Converter.byteToString(request.getSize());
holder.secondaryActionProgress.setPercentage(
0.01f * Math.max(1, request.getProgressPercent()), request);
}
}
holder.status.setText(status);
return convertView;
}
@ -102,10 +101,10 @@ public class DownloadlistAdapter extends BaseAdapter {
static class Holder {
TextView title;
TextView downloaded;
TextView percent;
ProgressBar progbar;
ImageButton butSecondary;
TextView status;
View secondaryActionButton;
ImageView secondaryActionIcon;
CircularProgressBar secondaryActionProgress;
}
public interface ItemAccess {

View File

@ -1,33 +1,17 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import androidx.core.content.ContextCompat;
import android.text.Layout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Adapter;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
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.util.LongList;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder;
import de.danoeh.antennapod.view.viewholder.FeedComponentViewHolder;
import de.danoeh.antennapod.view.viewholder.FeedViewHolder;
/**
* List adapter for items of feeds that the user has already subscribed to.
@ -35,27 +19,19 @@ import de.danoeh.antennapod.core.util.ThemeUtils;
public class FeedItemlistAdapter extends BaseAdapter {
private final ItemAccess itemAccess;
private final Context context;
private final boolean showFeedtitle;
/** true if played items should be made partially transparent */
private final MainActivity activity;
private final boolean makePlayedItemsTransparent;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
private final boolean showIcons;
private int currentlyPlayingItem = -1;
public FeedItemlistAdapter(Context context,
ItemAccess itemAccess,
boolean showFeedtitle,
boolean makePlayedItemsTransparent) {
public FeedItemlistAdapter(MainActivity activity, ItemAccess itemAccess,
boolean showIcons, boolean makePlayedItemsTransparent) {
super();
this.context = context;
this.activity = activity;
this.itemAccess = itemAccess;
this.showFeedtitle = showFeedtitle;
this.showIcons = showIcons;
this.makePlayedItemsTransparent = makePlayedItemsTransparent;
playingBackGroundColor = ThemeUtils.getColorFromAttr(context, R.attr.currently_playing_background);
normalBackGroundColor = ContextCompat.getColor(context, android.R.color.transparent);
}
@Override
@ -70,135 +46,56 @@ public class FeedItemlistAdapter extends BaseAdapter {
}
@Override
public FeedItem getItem(int position) {
public FeedComponent getItem(int position) {
return itemAccess.getItem(position);
}
@Override
@SuppressWarnings("ResourceType")
public View getView(final int position, View convertView, ViewGroup parent) {
Holder holder;
final FeedItem item = getItem(position);
if (convertView == null) {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.feeditemlist_item, parent, false);
holder.container = convertView
.findViewById(R.id.container);
holder.title = convertView.findViewById(R.id.txtvItemname);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
holder.lenSize = convertView
.findViewById(R.id.txtvLenSize);
holder.butAction = convertView
.findViewById(R.id.butSecondaryAction);
holder.published = convertView
.findViewById(R.id.txtvPublished);
holder.inPlaylist = convertView
.findViewById(R.id.imgvInPlaylist);
holder.type = convertView.findViewById(R.id.imgvType);
holder.statusUnread = convertView
.findViewById(R.id.statusUnread);
holder.episodeProgress = convertView
.findViewById(R.id.pbar_episode_progress);
convertView.setTag(holder);
final FeedComponent item = getItem(position);
if (item instanceof Feed) {
return getView((Feed) item, convertView, parent);
} else {
holder = (Holder) convertView.getTag();
final FeedItem feeditem = (FeedItem) item;
if (feeditem.getMedia() != null && feeditem.getMedia().isCurrentlyPlaying()) {
currentlyPlayingItem = position;
}
return getView(feeditem, convertView, parent);
}
}
private View getView(Feed item, View convertView, ViewGroup parent) {
FeedViewHolder holder;
if (convertView == null || !(convertView.getTag() instanceof FeedViewHolder)) {
holder = new FeedViewHolder(activity, parent);
} else {
holder = (FeedViewHolder) convertView.getTag();
}
holder.bind(item);
return holder.itemView;
}
private View getView(final FeedItem item, View convertView, ViewGroup parent) {
EpisodeItemViewHolder holder;
if (convertView == null || !(convertView.getTag() instanceof EpisodeItemViewHolder)) {
holder = new EpisodeItemViewHolder(activity, parent);
} else {
holder = (EpisodeItemViewHolder) convertView.getTag();
}
if (!(getItemViewType(position) == Adapter.IGNORE_ITEM_VIEW_TYPE)) {
convertView.setVisibility(View.VISIBLE);
StringBuilder buffer = new StringBuilder(item.getTitle());
if (showFeedtitle) {
buffer.append(" (");
buffer.append(item.getFeed().getTitle());
buffer.append(")");
}
holder.title.setText(buffer.toString());
if(item.isNew()) {
holder.statusUnread.setVisibility(View.VISIBLE);
} else {
holder.statusUnread.setVisibility(View.INVISIBLE);
}
if(item.isPlayed() && makePlayedItemsTransparent) {
convertView.setAlpha(0.5f);
} else {
convertView.setAlpha(1.0f);
}
String pubDateStr = DateUtils.formatAbbrev(context, item.getPubDate());
holder.published.setText(pubDateStr);
boolean isInQueue = item.isTagged(FeedItem.TAG_QUEUE);
FeedMedia media = item.getMedia();
if (media == null) {
holder.episodeProgress.setVisibility(View.INVISIBLE);
holder.inPlaylist.setVisibility(View.INVISIBLE);
holder.type.setVisibility(View.INVISIBLE);
holder.lenSize.setVisibility(View.INVISIBLE);
} else {
AdapterUtils.updateEpisodePlaybackProgress(item, holder.lenSize, holder.episodeProgress);
if (isInQueue) {
holder.inPlaylist.setVisibility(View.VISIBLE);
} else {
holder.inPlaylist.setVisibility(View.INVISIBLE);
}
if (DownloadRequester.getInstance().isDownloadingFile(item.getMedia())) {
holder.episodeProgress.setVisibility(View.VISIBLE);
holder.episodeProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
} else {
if(media.getPosition() == 0) {
holder.episodeProgress.setVisibility(View.INVISIBLE);
}
}
TypedArray typeDrawables = context.obtainStyledAttributes(
new int[]{R.attr.type_audio, R.attr.type_video});
final int[] labels = new int[]{R.string.media_type_audio_label, R.string.media_type_video_label};
MediaType mediaType = item.getMedia().getMediaType();
if (mediaType == MediaType.AUDIO) {
holder.type.setImageDrawable(typeDrawables.getDrawable(0));
holder.type.setContentDescription(context.getString(labels[0]));
holder.type.setVisibility(View.VISIBLE);
} else if (mediaType == MediaType.VIDEO) {
holder.type.setImageDrawable(typeDrawables.getDrawable(1));
holder.type.setContentDescription(context.getString(labels[1]));
holder.type.setVisibility(View.VISIBLE);
} else {
holder.type.setImageBitmap(null);
holder.type.setVisibility(View.GONE);
}
typeDrawables.recycle();
if (media.isCurrentlyPlaying()) {
holder.container.setBackgroundColor(playingBackGroundColor);
currentlyPlayingItem = position;
} else {
holder.container.setBackgroundColor(normalBackGroundColor);
}
}
ItemActionButton actionButton = ItemActionButton.forItem(item, isInQueue, true);
actionButton.configure(holder.butAction, context);
holder.butAction.setFocusable(false);
holder.butAction.setTag(item);
} else {
convertView.setVisibility(View.GONE);
if (!showIcons) {
holder.coverHolder.setVisibility(View.GONE);
}
return convertView;
holder.bind(item);
holder.dragHandle.setVisibility(View.GONE);
if (!makePlayedItemsTransparent) {
holder.itemView.setAlpha(1.0f);
}
holder.hideSeparatorIfNecessary();
return holder.itemView;
}
public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event, ListView listView) {
@ -208,35 +105,15 @@ public class FeedItemlistAdapter extends BaseAdapter {
if (view == null) {
return;
}
Holder holder = (Holder) view.getTag();
holder.episodeProgress.setVisibility(View.VISIBLE);
holder.episodeProgress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
holder.lenSize.setText(Converter.getDurationStringLong(event.getDuration() - event.getPosition()));
EpisodeItemViewHolder holder = (EpisodeItemViewHolder) view.getTag();
holder.notifyPlaybackPositionUpdated(event);
}
}
static class Holder {
LinearLayout container;
TextView title;
TextView published;
TextView lenSize;
ImageView type;
ImageView inPlaylist;
ImageButton butAction;
View statusUnread;
ProgressBar episodeProgress;
}
public interface ItemAccess {
int getItemDownloadProgressPercent(FeedItem item);
int getCount();
FeedItem getItem(int position);
LongList getQueueIds();
FeedComponent getItem(int position);
}
}

View File

@ -1,56 +1,34 @@
package de.danoeh.antennapod.adapter;
import android.os.Build;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.core.view.MotionEventCompat;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.ItemTouchHelper;
import android.text.Layout;
import android.text.TextUtils;
import android.annotation.SuppressLint;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.joanzapata.iconify.Iconify;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.MotionEventCompat;
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.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.fragment.ItemPagerFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
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.menuhandler.FeedItemMenuHandler;
/**
* List adapter for the queue.
*/
public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdapter.ViewHolder> {
private static final String TAG = QueueRecyclerAdapter.class.getSimpleName();
public class QueueRecyclerAdapter extends RecyclerView.Adapter<EpisodeItemViewHolder> implements View.OnCreateContextMenuListener {
private static final String TAG = "QueueRecyclerAdapter";
private final WeakReference<MainActivity> mainActivity;
private final ItemAccess itemAccess;
@ -60,9 +38,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
private FeedItem selectedItem;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
public QueueRecyclerAdapter(MainActivity mainActivity,
ItemAccess itemAccess,
ItemTouchHelper itemTouchHelper) {
@ -71,9 +46,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
this.itemAccess = itemAccess;
this.itemTouchHelper = itemTouchHelper;
locked = UserPreferences.isQueueLocked();
playingBackGroundColor = ThemeUtils.getColorFromAttr(mainActivity, R.attr.currently_playing_background);
normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent);
}
public void setLocked(boolean locked) {
@ -81,20 +53,49 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.queue_listitem, parent, false);
return new ViewHolder(view);
public EpisodeItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new EpisodeItemViewHolder(mainActivity.get(), parent);
}
@Override
public void onBindViewHolder(ViewHolder holder, int pos) {
@SuppressLint("ClickableViewAccessibility")
public void onBindViewHolder(EpisodeItemViewHolder holder, int pos) {
FeedItem item = itemAccess.getItem(pos);
holder.bind(item);
holder.dragHandle.setVisibility(locked ? View.GONE : View.VISIBLE);
holder.itemView.setOnLongClickListener(v -> {
selectedItem = item;
return false;
});
holder.itemView.setOnClickListener(v -> {
MainActivity activity = mainActivity.get();
if (activity != null) {
long[] ids = itemAccess.getQueueIds().toArray();
int position = ArrayUtils.indexOf(ids, item.getId());
activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
});
View.OnTouchListener startDragTouchListener = (v1, event) -> {
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
Log.d(TAG, "startDrag()");
itemTouchHelper.startDrag(holder);
}
return false;
};
if (!locked) {
holder.dragHandle.setOnTouchListener(startDragTouchListener);
holder.coverHolder.setOnTouchListener(startDragTouchListener);
} else {
holder.dragHandle.setOnTouchListener(null);
holder.coverHolder.setOnTouchListener(null);
}
holder.itemView.setOnCreateContextMenuListener(this);
holder.isInQueue.setVisibility(View.GONE);
holder.hideSeparatorIfNecessary();
}
@Nullable
@ -112,211 +113,30 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
return itemAccess.getCount();
}
public class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener,
View.OnCreateContextMenuListener,
ItemTouchHelperViewHolder {
@Override
public void onCreateContextMenu(final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
MenuInflater inflater = mainActivity.get().getMenuInflater();
inflater.inflate(R.menu.queue_context, menu); // queue-specific menu items
inflater.inflate(R.menu.feeditemlist_context, menu); // generic menu items for item feeds
private final FrameLayout container;
private final ImageView dragHandle;
private final TextView placeholder;
private final ImageView cover;
private final TextView title;
private final TextView pubDate;
private final TextView progressLeft;
private final TextView progressRight;
private final ProgressBar progressBar;
private final ImageButton butSecondary;
private FeedItem item;
public ViewHolder(View v) {
super(v);
container = v.findViewById(R.id.container);
dragHandle = v.findViewById(R.id.drag_handle);
placeholder = v.findViewById(R.id.txtvPlaceholder);
cover = v.findViewById(R.id.imgvCover);
title = v.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
pubDate = v.findViewById(R.id.txtvPubDate);
progressLeft = v.findViewById(R.id.txtvProgressLeft);
progressRight = v.findViewById(R.id.txtvProgressRight);
butSecondary = v.findViewById(R.id.butSecondaryAction);
progressBar = v.findViewById(R.id.progressBar);
v.setTag(this);
v.setOnClickListener(this);
v.setOnCreateContextMenuListener(this);
dragHandle.setOnTouchListener((v1, event) -> {
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
Log.d(TAG, "startDrag()");
itemTouchHelper.startDrag(ViewHolder.this);
}
return false;
});
menu.setHeaderTitle(selectedItem.getTitle());
FeedItemMenuHandler.onPrepareMenu(menu, selectedItem, R.id.skip_episode_item);
// Queue-specific menu preparation
final boolean keepSorted = UserPreferences.isQueueKeepSorted();
final LongList queueAccess = itemAccess.getQueueIds();
if (queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId() || keepSorted) {
menu.findItem(R.id.move_to_top_item).setVisible(false);
}
@Override
public void onClick(View v) {
MainActivity activity = mainActivity.get();
if (activity != null) {
long[] ids = itemAccess.getQueueIds().toArray();
int position = ArrayUtils.indexOf(ids, item.getId());
activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
}
@Override
public void onCreateContextMenu(final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
FeedItem item = itemAccess.getItem(getAdapterPosition());
MenuInflater inflater = mainActivity.get().getMenuInflater();
inflater.inflate(R.menu.queue_context, menu); // queue-specific menu items
inflater.inflate(R.menu.feeditemlist_context, menu); // generic menu items for item feeds
if (item != null) {
menu.setHeaderTitle(item.getTitle());
}
FeedItemMenuHandler.onPrepareMenu(menu, item,
R.id.skip_episode_item); // Skip Episode is not useful in Queue, so hide it.
// Queue-specific menu preparation
final boolean keepSorted = UserPreferences.isQueueKeepSorted();
final LongList queueAccess = itemAccess.getQueueIds();
if (queueAccess.size() == 0 || queueAccess.get(0) == item.getId() || keepSorted) {
menu.findItem(R.id.move_to_top_item).setVisible(false);
}
if (queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == item.getId() || keepSorted) {
menu.findItem(R.id.move_to_bottom_item).setVisible(false);
}
}
@Override
public void onItemSelected() {
itemView.setAlpha(0.5f);
}
@Override
public void onItemClear() {
itemView.setAlpha(1.0f);
}
public void bind(FeedItem item) {
this.item = item;
if(locked) {
dragHandle.setVisibility(View.GONE);
} else {
dragHandle.setVisibility(View.VISIBLE);
}
placeholder.setText(item.getFeed().getTitle());
title.setText(item.getTitle());
FeedMedia media = item.getMedia();
title.setText(item.getTitle());
String pubDateStr = DateUtils.formatAbbrev(mainActivity.get(), item.getPubDate());
int index = 0;
if(countMatches(pubDateStr, ' ') == 1 || countMatches(pubDateStr, ' ') == 2) {
index = pubDateStr.lastIndexOf(' ');
} else if(countMatches(pubDateStr, '.') == 2) {
index = pubDateStr.lastIndexOf('.');
} else if(countMatches(pubDateStr, '-') == 2) {
index = pubDateStr.lastIndexOf('-');
} else if(countMatches(pubDateStr, '/') == 2) {
index = pubDateStr.lastIndexOf('/');
}
if(index > 0) {
pubDateStr = pubDateStr.substring(0, index+1).trim() + "\n" + pubDateStr.substring(index+1);
}
pubDate.setText(pubDateStr);
if (media != null) {
final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
FeedItem.State state = item.getState();
if (isDownloadingMedia) {
progressLeft.setText(Converter.byteToString(itemAccess.getItemDownloadedBytes(item)));
if(itemAccess.getItemDownloadSize(item) > 0) {
progressRight.setText(Converter.byteToString(itemAccess.getItemDownloadSize(item)));
} else {
progressRight.setText(Converter.byteToString(media.getSize()));
}
progressBar.setProgress(itemAccess.getItemDownloadProgressPercent(item));
progressBar.setVisibility(View.VISIBLE);
} else if (state == FeedItem.State.PLAYING
|| state == FeedItem.State.IN_PROGRESS) {
if (media.getDuration() > 0) {
int progress = (int) (100.0 * media.getPosition() / media.getDuration());
progressBar.setProgress(progress);
progressBar.setVisibility(View.VISIBLE);
progressLeft.setText(Converter
.getDurationStringLong(media.getPosition()));
progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
}
} else {
if(media.getSize() > 0) {
progressLeft.setText(Converter.byteToString(media.getSize()));
} else if(NetworkUtils.isEpisodeHeadDownloadAllowed() && !media.checkedOnSizeButUnknown()) {
progressLeft.setText("{fa-spinner}");
Iconify.addIcons(progressLeft);
NetworkUtils.getFeedMediaSizeObservable(media)
.subscribe(
size -> {
if (size > 0) {
progressLeft.setText(Converter.byteToString(size));
} else {
progressLeft.setText("");
}
}, error -> {
progressLeft.setText("");
Log.e(TAG, Log.getStackTraceString(error));
});
} else {
progressLeft.setText("");
}
progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
progressBar.setVisibility(View.INVISIBLE);
}
if(media.isCurrentlyPlaying()) {
container.setBackgroundColor(playingBackGroundColor);
} else {
container.setBackgroundColor(normalBackGroundColor);
}
}
ItemActionButton actionButton = ItemActionButton.forItem(item, true, true);
actionButton.configure(butSecondary, mainActivity.get());
butSecondary.setFocusable(false);
butSecondary.setTag(item);
new CoverLoader(mainActivity.get())
.withUri(ImageResourceUtils.getImageLocation(item))
.withFallbackUri(item.getFeed().getImageLocation())
.withPlaceholderView(placeholder)
.withCoverView(cover)
.load();
}
public boolean isCurrentlyPlayingItem() {
return item.getMedia() != null && item.getMedia().isCurrentlyPlaying();
}
public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
progressLeft.setText(Converter.getDurationStringLong(event.getPosition()));
progressRight.setText(Converter.getDurationStringLong(event.getDuration()));
if (queueAccess.size() == 0 || queueAccess.get(queueAccess.size() - 1) == selectedItem.getId() || keepSorted) {
menu.findItem(R.id.move_to_bottom_item).setVisible(false);
}
}
public interface ItemAccess {
FeedItem getItem(int position);
int getCount();
long getItemDownloadedBytes(FeedItem item);
long getItemDownloadSize(FeedItem item);
int getItemDownloadProgressPercent(FeedItem item);
LongList getQueueIds();
}
@ -341,18 +161,4 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
*/
void onItemClear();
}
// Oh Xiaomi, I hate you so much. How did you manage to fuck this up?
private static int countMatches(final CharSequence str, final char ch) {
if (TextUtils.isEmpty(str)) {
return 0;
}
int count = 0;
for (int i = 0; i < str.length(); i++) {
if (ch == str.charAt(i)) {
count++;
}
}
return count;
}
}

View File

@ -1,123 +0,0 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.os.Build;
import android.text.Layout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
/**
* List adapter for search activity.
*/
public class SearchlistAdapter extends BaseAdapter {
private final Context context;
private final ItemAccess itemAccess;
public SearchlistAdapter(Context context, ItemAccess itemAccess) {
this.context = context;
this.itemAccess = itemAccess;
}
@Override
public int getCount() {
return itemAccess.getCount();
}
@Override
public FeedComponent getItem(int position) {
return itemAccess.getItem(position);
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final Holder holder;
FeedComponent component = getItem(position);
// Inflate Layout
if (convertView == null) {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.searchlist_item, parent, false);
holder.title = convertView.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
holder.cover = convertView
.findViewById(R.id.imgvFeedimage);
holder.subtitle = convertView
.findViewById(R.id.txtvSubtitle);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
if (component.getClass() == Feed.class) {
final Feed feed = (Feed) component;
holder.title.setText(feed.getTitle());
holder.subtitle.setVisibility(View.GONE);
Glide.with(context)
.load(feed.getImageLocation())
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(holder.cover);
} else if (component.getClass() == FeedItem.class) {
final FeedItem item = (FeedItem) component;
holder.title.setText(item.getTitle());
holder.subtitle.setVisibility(View.VISIBLE);
convertView.setAlpha(item.isPlayed() ? 0.5f : 1.0f);
Glide.with(context)
.load(item.getFeed().getImageLocation())
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate())
.into(holder.cover);
}
return convertView;
}
static class Holder {
ImageView cover;
TextView title;
TextView subtitle;
}
public interface ItemAccess {
int getCount();
FeedComponent getItem(int position);
}
}

View File

@ -2,6 +2,7 @@ package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
import android.content.res.TypedArray;
import android.widget.ImageView;
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
@ -54,14 +55,13 @@ public abstract class ItemActionButton {
}
}
public void configure(@NonNull ImageButton button, Context context) {
TypedArray drawables = context.obtainStyledAttributes(new int[]{getDrawable()});
public void configure(@NonNull View button, @NonNull ImageView icon, Context context) {
button.setVisibility(getVisibility());
button.setContentDescription(context.getString(getLabel()));
button.setImageDrawable(drawables.getDrawable(0));
button.setOnClickListener((view) -> onClick(context));
TypedArray drawables = context.obtainStyledAttributes(new int[]{getDrawable()});
icon.setImageDrawable(drawables.getDrawable(0));
drawables.recycle();
}
}

View File

@ -38,11 +38,6 @@ public class AllEpisodesFragment extends EpisodesListFragment {
feedItemFilter = new FeedItemFilter(prefs.getString(PREF_FILTER, ""));
}
@Override
protected boolean showOnlyNewEpisodes() {
return false;
}
@Override
protected String getPrefName() {
return PREF_NAME;

View File

@ -52,7 +52,7 @@ public class CompletedDownloadsFragment extends ListFragment {
addVerticalPadding();
addEmptyView();
listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess);
listAdapter = new DownloadedEpisodesListAdapter((MainActivity) getActivity(), itemAccess);
setListAdapter(listAdapter);
setListShown(false);
EventBus.getDefault().register(this);

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.viewholder.EpisodeItemViewHolder;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@ -43,14 +44,11 @@ import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
@ -82,8 +80,6 @@ public abstract class EpisodesListFragment extends Fragment {
@NonNull
List<FeedItem> episodes = new ArrayList<>();
@NonNull
private List<Downloader> downloaderList = new ArrayList<>();
private boolean isUpdatingFeeds;
boolean isMenuInvalidationAllowed = false;
@ -92,10 +88,6 @@ public abstract class EpisodesListFragment extends Fragment {
private LinearLayoutManager layoutManager;
protected TextView txtvInformation;
boolean showOnlyNewEpisodes() {
return false;
}
String getPrefName() {
return DEFAULT_PREF_NAME;
}
@ -347,10 +339,10 @@ public abstract class EpisodesListFragment extends Fragment {
}
protected void onFragmentLoaded(List<FeedItem> episodes) {
listAdapter.notifyDataSetChanged();
if (episodes.size() == 0) {
createRecycleAdapter(recyclerView, emptyView);
} else {
listAdapter.updateItems(episodes);
}
restoreScrollPosition();
@ -363,66 +355,13 @@ public abstract class EpisodesListFragment extends Fragment {
*/
private void createRecycleAdapter(RecyclerView recyclerView, EmptyViewHandler emptyViewHandler) {
MainActivity mainActivity = (MainActivity) getActivity();
listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess, showOnlyNewEpisodes());
listAdapter = new AllEpisodesRecycleAdapter(mainActivity);
listAdapter.setHasStableIds(true);
listAdapter.updateItems(episodes);
recyclerView.setAdapter(listAdapter);
emptyViewHandler.updateAdapter(listAdapter);
}
private final AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
@Override
public int getCount() {
return episodes.size();
}
@Override
public FeedItem getItem(int position) {
if (0 <= position && position < episodes.size()) {
return episodes.get(position);
}
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();
for (FeedItem item : episodes) {
if (item.isTagged(FeedItem.TAG_QUEUE)) {
queueIds.add(item.getId());
}
}
return queueIds;
}
};
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
@ -444,8 +383,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;
@ -462,7 +400,7 @@ public abstract class EpisodesListFragment extends Fragment {
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
List<Downloader> downloaderList = update.downloaders;
if (isMenuInvalidationAllowed && event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
requireActivity().invalidateOptionsMenu();
}

View File

@ -10,12 +10,12 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder;
import org.greenrobot.eventbus.Subscribe;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
import de.danoeh.antennapod.core.event.FavoritesEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
@ -30,11 +30,6 @@ public class FavoriteEpisodesFragment extends EpisodesListFragment {
private static final String TAG = "FavoriteEpisodesFrag";
private static final String PREF_NAME = "PrefFavoriteEpisodesFragment";
@Override
protected boolean showOnlyNewEpisodes() {
return true;
}
@Override
protected String getPrefName() {
return PREF_NAME;
@ -63,8 +58,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

@ -89,25 +89,18 @@ public class FeedItemlistFragment extends ListFragment {
private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id";
private FeedItemlistAdapter adapter;
private ContextMenu contextMenu;
private AdapterView.AdapterContextMenuInfo lastMenuInfo = null;
private MoreContentListFooterUtil listFooter;
private long feedID;
private Feed feed;
private boolean headerCreated = false;
private List<Downloader> downloaderList;
private MoreContentListFooterUtil listFooter;
private boolean isUpdatingFeed;
private TextView txtvTitle;
private IconTextView txtvFailure;
private ImageView imgvBackground;
private ImageView imgvCover;
private TextView txtvInformation;
private Disposable disposable;
@ -296,7 +289,7 @@ public class FeedItemlistFragment extends ListFragment {
AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
// because of addHeaderView(), positions are increased by 1!
FeedItem item = itemAccess.getItem(adapterInfo.position-1);
FeedItem item = (FeedItem) itemAccess.getItem(adapterInfo.position - 1);
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.feeditemlist_context, menu);
@ -305,7 +298,6 @@ public class FeedItemlistFragment extends ListFragment {
menu.setHeaderTitle(item.getTitle());
}
contextMenu = menu;
lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
FeedItemMenuHandler.onPrepareMenu(menu, item);
}
@ -313,11 +305,11 @@ public class FeedItemlistFragment extends ListFragment {
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
if(menuInfo == null) {
if (menuInfo == null) {
menuInfo = lastMenuInfo;
}
// because of addHeaderView(), positions are increased by 1!
FeedItem selectedItem = itemAccess.getItem(menuInfo.position-1);
FeedItem selectedItem = feed.getItemAtIndex(menuInfo.position - 1);
if (selectedItem == null) {
Log.i(TAG, "Selected item at position " + menuInfo.position + " was null, ignoring selection");
@ -366,7 +358,6 @@ public class FeedItemlistFragment extends ListFragment {
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
if (event.hasChangedFeedUpdateStatus(isUpdatingFeed)) {
updateProgressBarVisibility();
}
@ -424,7 +415,7 @@ public class FeedItemlistFragment extends ListFragment {
setListAdapter(null);
setupHeaderView();
setupFooterView();
adapter = new FeedItemlistAdapter(getActivity(), itemAccess, false, true);
adapter = new FeedItemlistAdapter((MainActivity) getActivity(), itemAccess, false, true);
setListAdapter(adapter);
}
refreshHeaderView();
@ -574,40 +565,13 @@ public class FeedItemlistFragment extends ListFragment {
}
}
@Override
public LongList getQueueIds() {
LongList queueIds = new LongList();
if(feed == null) {
return queueIds;
}
for(FeedItem item : feed.getItems()) {
if(item.isTagged(FeedItem.TAG_QUEUE)) {
queueIds.add(item.getId());
}
}
return queueIds;
}
@Override
public int getCount() {
return (feed != null) ? feed.getNumOfItems() : 0;
}
@Override
public int getItemDownloadProgressPercent(FeedItem item) {
if (downloaderList != null) {
for (Downloader downloader : downloaderList) {
if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
&& downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
return downloader.getDownloadRequest().getProgressPercent();
}
}
}
return 0;
}
};
private void loadItems() {
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.viewholder.EpisodeItemViewHolder;
/**
* Like 'EpisodesFragment' except that it only shows new episodes and
@ -26,11 +27,6 @@ public class NewEpisodesFragment extends EpisodesListFragment {
public static final String TAG = "NewEpisodesFragment";
private static final String PREF_NAME = "PrefNewEpisodesFragment";
@Override
protected boolean showOnlyNewEpisodes() {
return true;
}
@Override
protected String getPrefName() {
return PREF_NAME;
@ -63,7 +59,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

@ -73,7 +73,7 @@ public class PlaybackHistoryFragment extends ListFragment {
// played items shoudln't be transparent for this fragment since, *all* items
// in this fragment will, by definition, be played. So it serves no purpose and can make
// it harder to read.
adapter = new FeedItemlistAdapter(getActivity(), itemAccess, true, false);
adapter = new FeedItemlistAdapter((MainActivity) getActivity(), itemAccess, true, false);
setListAdapter(adapter);
}
@ -93,7 +93,7 @@ public class PlaybackHistoryFragment extends ListFragment {
}
}
@Subscribe(sticky = true)
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(DownloadEvent event) {
Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
@ -180,19 +180,6 @@ public class PlaybackHistoryFragment extends ListFragment {
private final FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() {
@Override
public int getItemDownloadProgressPercent(FeedItem item) {
if (downloaderList != null) {
for (Downloader downloader : downloaderList) {
if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
&& downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
return downloader.getDownloadRequest().getProgressPercent();
}
}
}
return 0;
}
@Override
public int getCount() {
return (playbackHistory != null) ? playbackHistory.size() : 0;
@ -206,20 +193,6 @@ public class PlaybackHistoryFragment extends ListFragment {
return null;
}
}
@Override
public LongList getQueueIds() {
LongList queueIds = new LongList();
if(playbackHistory == null) {
return queueIds;
}
for (FeedItem item : playbackHistory) {
if (item.isTagged(FeedItem.TAG_QUEUE)) {
queueIds.add(item.getId());
}
}
return queueIds;
}
};
private void loadItems() {

View File

@ -27,6 +27,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator;
import com.google.android.material.snackbar.Snackbar;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@ -45,11 +46,9 @@ import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@ -84,7 +83,6 @@ public class QueueFragment extends Fragment {
private ProgressBar progLoading;
private List<FeedItem> queue;
private List<Downloader> downloaderList;
private boolean isUpdatingFeeds = false;
@ -196,7 +194,6 @@ public class QueueFragment extends Fragment {
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
getActivity().invalidateOptionsMenu();
}
@ -214,7 +211,7 @@ public class QueueFragment extends Fragment {
public void onEventMainThread(PlaybackPositionEvent event) {
if (recyclerAdapter != null) {
for (int i = 0; i < recyclerAdapter.getItemCount(); i++) {
QueueRecyclerAdapter.ViewHolder holder = (QueueRecyclerAdapter.ViewHolder)
EpisodeItemViewHolder holder = (EpisodeItemViewHolder)
recyclerView.findViewHolderForAdapterPosition(i);
if (holder != null && holder.isCurrentlyPlayingItem()) {
holder.notifyPlaybackPositionUpdated(event);
@ -510,7 +507,6 @@ public class QueueFragment extends Fragment {
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
recyclerView.setHasFixedSize(true);
registerForContextMenu(recyclerView);
itemTouchHelper = new ItemTouchHelper(
@ -692,46 +688,6 @@ public class QueueFragment extends Fragment {
return null;
}
@Override
public long getItemDownloadedBytes(FeedItem item) {
if (downloaderList != null) {
for (Downloader downloader : downloaderList) {
if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
&& downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
Log.d(TAG, "downloaded bytes: " + downloader.getDownloadRequest().getSoFar());
return downloader.getDownloadRequest().getSoFar();
}
}
}
return 0;
}
@Override
public long getItemDownloadSize(FeedItem item) {
if (downloaderList != null) {
for (Downloader downloader : downloaderList) {
if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
&& downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
Log.d(TAG, "downloaded size: " + downloader.getDownloadRequest().getSize());
return downloader.getDownloadRequest().getSize();
}
}
}
return 0;
}
@Override
public int getItemDownloadProgressPercent(FeedItem item) {
if (downloaderList != null) {
for (Downloader downloader : downloaderList) {
if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
&& downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
return downloader.getDownloadRequest().getProgressPercent();
}
}
}
return 0;
}
@Override
public LongList getQueueIds() {
return queue != null ? LongList.of(FeedItemUtil.getIds(queue)) : new LongList(0);

View File

@ -104,11 +104,12 @@ public class RunningDownloadsFragment extends ListFragment {
DownloadRequest downloadRequest = downloader.getDownloadRequest();
DownloadRequester.getInstance().cancelDownload(getActivity(), downloadRequest.getSource());
if(downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA &&
UserPreferences.isEnableAutodownload()) {
if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
&& UserPreferences.isEnableAutodownload()) {
FeedMedia media = DBReader.getFeedMedia(downloadRequest.getFeedfileId());
DBWriter.setFeedItemAutoDownload(media.getItem(), false);
Toast.makeText(getActivity(), R.string.download_canceled_autodownload_enabled_msg, Toast.LENGTH_SHORT).show();
Toast.makeText(getActivity(), R.string.download_canceled_autodownload_enabled_msg,
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), R.string.download_canceled_msg, Toast.LENGTH_SHORT).show();
}

View File

@ -20,7 +20,8 @@ import androidx.core.view.MenuItemCompat;
import androidx.fragment.app.Fragment;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.SearchlistAdapter;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
@ -33,8 +34,10 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import java.util.ArrayList;
import java.util.List;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
/**
* Performs a search operation on all feeds or one specific feed and displays the search result.
@ -45,7 +48,7 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
private static final String ARG_QUERY = "query";
private static final String ARG_FEED = "feed";
private SearchlistAdapter searchAdapter;
private FeedItemlistAdapter searchAdapter;
private List<FeedComponent> searchResults = new ArrayList<>();
private Disposable disposable;
private ProgressBar progressBar;
@ -105,7 +108,7 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
View layout = inflater.inflate(R.layout.search_fragment, container, false);
ListView listView = layout.findViewById(R.id.listview);
progressBar = layout.findViewById(R.id.progressBar);
searchAdapter = new SearchlistAdapter(getActivity(), itemAccess);
searchAdapter = new FeedItemlistAdapter((MainActivity) getActivity(), itemAccess, true, true);
listView.setAdapter(searchAdapter);
listView.setOnItemClickListener(this);
@ -164,6 +167,14 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
search();
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(DownloadEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
if (searchAdapter != null) {
searchAdapter.notifyDataSetChanged();
}
}
private void onSearchResults(List<FeedComponent> results) {
progressBar.setVisibility(View.GONE);
searchResults = results;
@ -172,7 +183,7 @@ public class SearchFragment extends Fragment implements AdapterView.OnItemClickL
emptyViewHandler.setMessage(getString(R.string.no_results_for_query, query));
}
private final SearchlistAdapter.ItemAccess itemAccess = new SearchlistAdapter.ItemAccess() {
private final FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() {
@Override
public int getCount() {
return searchResults.size();

View File

@ -0,0 +1,87 @@
package de.danoeh.antennapod.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
public class CircularProgressBar extends View {
private static final float EPSILON = 0.005f;
private final Paint paintBackground = new Paint();
private final Paint paintProgress = new Paint();
private float percentage = 0;
private float targetPercentage = 0;
private Object tag = null;
public CircularProgressBar(Context context) {
super(context);
setup();
}
public CircularProgressBar(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setup();
}
public CircularProgressBar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setup();
}
private void setup() {
paintBackground.setAntiAlias(true);
paintBackground.setStyle(Paint.Style.STROKE);
paintProgress.setAntiAlias(true);
paintProgress.setStyle(Paint.Style.STROKE);
paintProgress.setStrokeCap(Paint.Cap.ROUND);
int[] colorAttrs = new int[] { android.R.attr.textColorPrimary, android.R.attr.textColorSecondary };
TypedArray a = getContext().obtainStyledAttributes(colorAttrs);
paintProgress.setColor(a.getColor(0, 0xffffffff));
paintBackground.setColor(a.getColor(1, 0xffffffff));
a.recycle();
}
/**
* Sets the percentage to be displayed.
* @param percentage Number from 0 to 1
* @param tag When the tag is the same as last time calling setPercentage, the update is animated
*/
public void setPercentage(float percentage, Object tag) {
targetPercentage = percentage;
if (tag == null || !tag.equals(this.tag)) {
// Do not animate
this.percentage = percentage;
this.tag = tag;
}
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float padding = getHeight() * 0.06f;
paintBackground.setStrokeWidth(getHeight() * 0.02f);
paintProgress.setStrokeWidth(padding);
RectF bounds = new RectF(padding, padding, getWidth() - padding, getHeight() - padding);
canvas.drawArc(bounds, 0, 360, false, paintBackground);
if (percentage > EPSILON && 1 - percentage > EPSILON) {
canvas.drawArc(bounds, -90, percentage * 360, false, paintProgress);
}
if (Math.abs(percentage - targetPercentage) > EPSILON) {
float delta = Math.min(0.02f, Math.abs(targetPercentage - percentage));
percentage += delta * ((targetPercentage - percentage) > 0 ? 1f : -1f);
invalidate();
}
}
}

View File

@ -0,0 +1,41 @@
package de.danoeh.antennapod.view.viewholder;
import android.content.Context;
import android.os.Build;
import android.text.Layout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.joanzapata.iconify.widget.IconTextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.view.CircularProgressBar;
public class DownloadItemViewHolder extends RecyclerView.ViewHolder {
public final View secondaryActionButton;
public final ImageView secondaryActionIcon;
public final CircularProgressBar secondaryActionProgress;
public final IconTextView icon;
public final TextView title;
public final TextView type;
public final TextView date;
public final TextView reason;
public DownloadItemViewHolder(Context context, ViewGroup parent) {
super(LayoutInflater.from(context).inflate(R.layout.downloadlog_item, parent, false));
date = itemView.findViewById(R.id.txtvDate);
type = itemView.findViewById(R.id.txtvType);
icon = itemView.findViewById(R.id.txtvIcon);
reason = itemView.findViewById(R.id.txtvReason);
secondaryActionProgress = itemView.findViewById(R.id.secondaryActionProgress);
secondaryActionButton = itemView.findViewById(R.id.secondaryActionButton);
secondaryActionIcon = itemView.findViewById(R.id.secondaryActionIcon);
title = itemView.findViewById(R.id.txtvTitle);
if (Build.VERSION.SDK_INT >= 23) {
title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
itemView.setTag(this);
}
}

View File

@ -0,0 +1,213 @@
package de.danoeh.antennapod.view.viewholder;
import android.graphics.Color;
import android.os.Build;
import android.text.Layout;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import com.joanzapata.iconify.Iconify;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.CoverLoader;
import de.danoeh.antennapod.adapter.QueueRecyclerAdapter;
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.feed.MediaType;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
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.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.view.CircularProgressBar;
/**
* Holds the view which shows FeedItems.
*/
public class EpisodeItemViewHolder extends FeedComponentViewHolder
implements QueueRecyclerAdapter.ItemTouchHelperViewHolder {
private static final String TAG = "EpisodeItemViewHolder";
private final View container;
public final ImageView dragHandle;
private final TextView placeholder;
private final ImageView cover;
private final TextView title;
private final TextView pubDate;
private final TextView position;
private final TextView duration;
private final TextView size;
public final TextView isNew;
public final ImageView isInQueue;
private final ImageView isVideo;
public final ImageView isFavorite;
private final ProgressBar progressBar;
public final View secondaryActionButton;
public final ImageView secondaryActionIcon;
private final CircularProgressBar secondaryActionProgress;
private final TextView separatorIcons;
public final CardView coverHolder;
private final MainActivity activity;
private FeedItem item;
public EpisodeItemViewHolder(MainActivity activity, ViewGroup parent) {
super(LayoutInflater.from(activity).inflate(R.layout.feeditemlist_item, parent, false));
this.activity = activity;
container = itemView.findViewById(R.id.container);
dragHandle = itemView.findViewById(R.id.drag_handle);
placeholder = itemView.findViewById(R.id.txtvPlaceholder);
cover = itemView.findViewById(R.id.imgvCover);
title = itemView.findViewById(R.id.txtvTitle);
if (Build.VERSION.SDK_INT >= 23) {
title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
pubDate = itemView.findViewById(R.id.txtvPubDate);
position = itemView.findViewById(R.id.txtvPosition);
duration = itemView.findViewById(R.id.txtvDuration);
progressBar = itemView.findViewById(R.id.progressBar);
isInQueue = itemView.findViewById(R.id.ivInPlaylist);
isVideo = itemView.findViewById(R.id.ivIsVideo);
isNew = itemView.findViewById(R.id.statusUnread);
isFavorite = itemView.findViewById(R.id.isFavorite);
size = itemView.findViewById(R.id.size);
separatorIcons = itemView.findViewById(R.id.separatorIcons);
secondaryActionProgress = itemView.findViewById(R.id.secondaryActionProgress);
secondaryActionButton = itemView.findViewById(R.id.secondaryActionButton);
secondaryActionIcon = itemView.findViewById(R.id.secondaryActionIcon);
coverHolder = itemView.findViewById(R.id.coverHolder);
itemView.setTag(this);
}
@Override
public void onItemSelected() {
itemView.setAlpha(0.5f);
}
@Override
public void onItemClear() {
itemView.setAlpha(1.0f);
}
public void bind(FeedItem item) {
this.item = item;
placeholder.setText(item.getFeed().getTitle());
title.setText(item.getTitle());
pubDate.setText(DateUtils.formatAbbrev(activity, item.getPubDate()));
isNew.setVisibility(item.isNew() ? View.VISIBLE : View.GONE);
isFavorite.setVisibility(item.isTagged(FeedItem.TAG_FAVORITE) ? View.VISIBLE : View.GONE);
isInQueue.setVisibility(item.isTagged(FeedItem.TAG_QUEUE) ? View.VISIBLE : View.GONE);
itemView.setAlpha(item.isPlayed() ? 0.5f : 1.0f);
ItemActionButton actionButton = ItemActionButton.forItem(item, true, true);
actionButton.configure(secondaryActionButton, secondaryActionIcon, activity);
secondaryActionButton.setFocusable(false);
if (item.getMedia() != null) {
bind(item.getMedia());
} else {
secondaryActionProgress.setPercentage(0, item);
}
if (coverHolder.getVisibility() == View.VISIBLE) {
new CoverLoader(activity)
.withUri(ImageResourceUtils.getImageLocation(item))
.withFallbackUri(item.getFeed().getImageLocation())
.withPlaceholderView(placeholder)
.withCoverView(cover)
.load();
}
}
private void bind(FeedMedia media) {
isVideo.setVisibility(media.getMediaType() == MediaType.VIDEO ? View.VISIBLE : View.GONE);
duration.setText(Converter.getDurationStringLong(media.getDuration()));
if (media.isCurrentlyPlaying()) {
container.setBackgroundColor(ThemeUtils.getColorFromAttr(activity, R.attr.currently_playing_background));
} else {
container.setBackgroundResource(ThemeUtils.getDrawableFromAttr(activity, R.attr.selectableItemBackground));
}
if (DownloadRequester.getInstance().isDownloadingFile(media)) {
final DownloadRequest downloadRequest = DownloadRequester.getInstance().getRequestFor(media);
float percent = 0.01f * downloadRequest.getProgressPercent();
secondaryActionProgress.setPercentage(Math.max(percent, 0.01f), item);
} else if (media.isDownloaded()) {
secondaryActionProgress.setPercentage(1, item); // Do not animate 100% -> 0%
} else {
secondaryActionProgress.setPercentage(0, item); // Animate X% -> 0%
}
if (media.getDuration() > 0
&& (item.getState() == FeedItem.State.PLAYING || item.getState() == FeedItem.State.IN_PROGRESS)) {
int progress = (int) (100.0 * media.getPosition() / media.getDuration());
progressBar.setProgress(progress);
position.setText(Converter.getDurationStringLong(media.getPosition()));
duration.setText(Converter.getDurationStringLong(media.getDuration()));
progressBar.setVisibility(View.VISIBLE);
position.setVisibility(View.VISIBLE);
} else {
progressBar.setVisibility(View.GONE);
position.setVisibility(View.GONE);
}
if (media.getSize() > 0) {
size.setText(Converter.byteToString(media.getSize()));
} else if (NetworkUtils.isEpisodeHeadDownloadAllowed() && !media.checkedOnSizeButUnknown()) {
size.setText("{fa-spinner}");
Iconify.addIcons(size);
NetworkUtils.getFeedMediaSizeObservable(media).subscribe(
sizeValue -> {
if (sizeValue > 0) {
size.setText(Converter.byteToString(sizeValue));
} else {
size.setText("");
}
}, error -> {
size.setText("");
Log.e(TAG, Log.getStackTraceString(error));
});
} else {
size.setText("");
}
}
public FeedItem getFeedItem() {
return item;
}
public boolean isCurrentlyPlayingItem() {
return item.getMedia() != null && item.getMedia().isCurrentlyPlaying();
}
public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
position.setText(Converter.getDurationStringLong(event.getPosition()));
duration.setText(Converter.getDurationStringLong(event.getDuration()));
}
/**
* Hides the separator dot between icons and text if there are no icons.
*/
public void hideSeparatorIfNecessary() {
boolean hasIcons = isNew.getVisibility() == View.VISIBLE
|| isInQueue.getVisibility() == View.VISIBLE
|| isVideo.getVisibility() == View.VISIBLE
|| isFavorite.getVisibility() == View.VISIBLE
|| isNew.getVisibility() == View.VISIBLE;
separatorIcons.setVisibility(hasIcons ? View.VISIBLE : View.GONE);
}
}

View File

@ -0,0 +1,15 @@
package de.danoeh.antennapod.view.viewholder;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
/**
* Holds the view which shows FeedComponents.
*/
public class FeedComponentViewHolder extends RecyclerView.ViewHolder {
public FeedComponentViewHolder(@NonNull View itemView) {
super(itemView);
}
}

View File

@ -0,0 +1,62 @@
package de.danoeh.antennapod.view.viewholder;
import android.os.Build;
import android.text.Layout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.cardview.widget.CardView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.CoverLoader;
import de.danoeh.antennapod.core.feed.Feed;
/**
* Holds the view which shows feeds.
*/
public class FeedViewHolder extends FeedComponentViewHolder {
private static final String TAG = "FeedViewHolder";
private final TextView placeholder;
private final ImageView cover;
private final TextView title;
public final CardView coverHolder;
private final MainActivity activity;
private Feed feed;
public FeedViewHolder(MainActivity activity, ViewGroup parent) {
super(LayoutInflater.from(activity).inflate(R.layout.feeditemlist_item, parent, false));
this.activity = activity;
placeholder = itemView.findViewById(R.id.txtvPlaceholder);
cover = itemView.findViewById(R.id.imgvCover);
coverHolder = itemView.findViewById(R.id.coverHolder);
title = itemView.findViewById(R.id.txtvTitle);
if (Build.VERSION.SDK_INT >= 23) {
title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
itemView.findViewById(R.id.secondaryActionButton).setVisibility(View.GONE);
itemView.findViewById(R.id.status).setVisibility(View.GONE);
itemView.findViewById(R.id.progress).setVisibility(View.GONE);
itemView.findViewById(R.id.drag_handle).setVisibility(View.GONE);
itemView.setTag(this);
}
public void bind(Feed feed) {
this.feed = feed;
placeholder.setText(feed.getTitle());
title.setText(feed.getTitle());
if (coverHolder.getVisibility() == View.VISIBLE) {
new CoverLoader(activity)
.withUri(feed.getImageLocation())
.withPlaceholderView(placeholder)
.withCoverView(cover)
.load();
}
}
}

View File

@ -34,7 +34,7 @@
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable"
app:fastScrollVerticalTrackDrawable="@drawable/line_drawable"
tools:itemCount="13"
tools:listitem="@layout/new_episodes_listitem" />
tools:listitem="@layout/feeditemlist_item" />
<ProgressBar
android:id="@+id/progLoading"

View File

@ -1,102 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
<ImageView
android:id="@+id/imgvImage"
android:layout_width="@dimen/thumbnail_length_downloaded_item"
android:layout_height="@dimen/thumbnail_length_downloaded_item"
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:contentDescription="@string/cover_label"
android:scaleType="centerCrop"
tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
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_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_weight="1"
android:orientation="vertical"
tools:background="@android:color/holo_red_dark">
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
tools:text="Downloaded episode title"
tools:background="@android:color/holo_green_dark"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:background="@android:color/holo_red_dark" >
<TextView
android:id="@+id/txtvSize"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="23 MB"
tools:background="@android:color/holo_green_dark"/>
<View
android:layout_width="0dip"
android:layout_height="1dip"
android:layout_weight="1" />
<ImageView
android:id="@+id/imgvInPlaylist"
android:layout_width="@dimen/enc_icons_size"
android:layout_height="@dimen/enc_icons_size"
android:contentDescription="@string/in_queue_label"
android:src="?attr/stat_playlist"
android:visibility="visible"
tools:src="@drawable/ic_list_white_24dp"
tools:background="@android:color/holo_red_light"/>
<TextView
android:id="@+id/txtvPublished"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
tools:text="Jan 23"
tools:background="@android:color/holo_green_dark"/>
</LinearLayout>
</LinearLayout>
<include layout="@layout/vertical_list_divider"/>
<ImageButton
android:id="@+id/butSecondaryAction"
android:layout_width="@dimen/listview_secondary_button_width"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:clickable="false"
android:contentDescription="@string/delete_episode_label"
android:focusable="false"
android:focusableInTouchMode="false"
android:src="?attr/content_discard"
tools:src="@drawable/ic_delete_white_24dp"
tools:background="@android:color/holo_green_dark" />
</LinearLayout>

View File

@ -1,90 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:baselineAligned="false"
android:descendantFocusability="blocksDescendants">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/txtvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:ellipsize="end"
android:lines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
tools:text="Download item title"
tools:background="@android:color/holo_green_dark" />
<ProgressBar
android:id="@+id/progProgress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="16dp"
android:layout_marginBottom="4dp"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="4dp"
tools:background="@android:color/holo_blue_light" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding">
<TextView
android:id="@+id/txtvDownloaded"
android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
android:layout_marginEnd="@dimen/listitem_threeline_textrightpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="@sample/episodes.json/data/title"
android:ellipsize="end"/>
<TextView
android:id="@+id/txtvStatus"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:ellipsize="end"
android:lines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small"
tools:text="21 MB / 42 MB"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvPercent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:ellipsize="end"
android:lines="1"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small"
tools:text="50%"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
tools:text="Media file · 10MB / 20MB"/>
</LinearLayout>
<include layout="@layout/vertical_list_divider"/>
<ImageButton
android:id="@+id/butSecondaryAction"
android:layout_width="@dimen/listview_secondary_button_width"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:clickable="false"
android:contentDescription="@string/cancel_download_label"
android:focusable="false"
android:focusableInTouchMode="false"
android:src="?attr/navigation_cancel"
tools:src="@drawable/ic_cancel_white_24dp"
tools:background="@android:color/holo_green_dark" />
<include layout="@layout/secondary_action"/>
</LinearLayout>

View File

@ -1,97 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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"
android:paddingTop="8dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingBottom="8dp"
android:descendantFocusability="blocksDescendants"
tools:background="@android:color/darker_gray">
<LinearLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:baselineAligned="false"
android:descendantFocusability="blocksDescendants">
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/txtvIcon"
android:layout_width="48sp"
android:layout_height="48sp"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:textSize="48sp"
android:gravity="center" />
android:id="@+id/txtvIcon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
android:layout_marginStart="@dimen/listitem_threeline_textleftpadding"
android:textSize="40dp"
android:gravity="center"
tools:text="X"/>
<com.joanzapata.iconify.widget.IconButton
android:id="@+id/btnRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/txtvIcon"
android:layout_alignLeft="@id/txtvIcon"
android:layout_alignStart="@id/txtvIcon"
android:layout_alignRight="@id/txtvIcon"
android:layout_alignEnd="@id/txtvIcon"
android:layout_marginTop="8dp"
android:text="{fa-repeat}"
tools:text="↻" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
android:layout_marginEnd="@dimen/listitem_threeline_textrightpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/txtvType"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
tools:text="Media file"
tools:background="@android:color/holo_green_dark" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/status"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/txtvIcon"
android:layout_toEndOf="@id/txtvIcon"
android:layout_toLeftOf="@id/txtvType"
android:layout_toStartOf="@id/txtvType"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:minLines="1"
android:maxLines="2"
tools:text="Download item title"
tools:background="@android:color/holo_blue_light" />
<TextView
android:id="@+id/txtvType"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Media file"/>
<TextView
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="4dp"
android:layout_marginEnd="4dp"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:text="·"
tools:background="@android:color/holo_blue_light"/>
<TextView
android:id="@+id/txtvDate"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="January 23"/>
<TextView
android:id="@+id/txtvDate"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/txtvIcon"
android:layout_toEndOf="@id/txtvIcon"
android:layout_below="@id/txtvTitle"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
tools:text="January 23"
tools:background="@android:color/holo_green_dark" />
</LinearLayout>
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="@sample/episodes.json/data/title"
android:ellipsize="end"
tools:background="@android:color/holo_blue_light"/>
<TextView
android:id="@+id/txtvReason"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/txtvDate"
android:layout_toRightOf="@id/txtvIcon"
android:layout_toEndOf="@id/txtvIcon"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:textColor="?android:attr/textColorTertiary"
android:textSize="@dimen/text_size_micro"
tools:text="@string/design_time_downloaded_log_failure_reason"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvReason"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
tools:text="@string/design_time_downloaded_log_failure_reason"/>
</RelativeLayout>
</LinearLayout>
<include layout="@layout/secondary_action"/>
</LinearLayout>

View File

@ -1,124 +1,203 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:baselineAligned="false">
<RelativeLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:layout_weight="1"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
tools:background="@android:color/holo_orange_dark">
<TextView
android:id="@+id/statusUnread"
style="@style/AntennaPod.TextView.UnreadIndicator"
<LinearLayout
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_marginRight="8dp"
tools:text="NEW"
tools:background="@android:color/white" />
android:layout_height="match_parent"
android:minWidth="16dp">
<ImageView
android:id="@+id/drag_handle"
android:layout_width="36dp"
android:layout_height="match_parent"
android:contentDescription="@string/drag_handle_content_description"
android:scaleType="center"
android:src="?attr/dragview_background"
tools:src="@drawable/ic_drag_vertical_grey600_48dp"
tools:background="@android:color/holo_green_dark"/>
<TextView
android:id="@+id/txtvItemname"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
</LinearLayout>
<androidx.cardview.widget.CardView
android:layout_width="@dimen/thumbnail_length_queue_item"
android:layout_height="@dimen/thumbnail_length_queue_item"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_marginRight="@dimen/listitem_threeline_textleftpadding"
android:layout_marginEnd="@dimen/listitem_threeline_textleftpadding"
android:id="@+id/coverHolder"
app:cardCornerRadius="4dp"
app:cardElevation="0dp">
<RelativeLayout
android:layout_width="@dimen/thumbnail_length_queue_item"
android:layout_height="@dimen/thumbnail_length_queue_item">
<TextView
android:id="@+id/txtvPlaceholder"
android:layout_width="@dimen/thumbnail_length_queue_item"
android:layout_height="@dimen/thumbnail_length_queue_item"
android:layout_centerVertical="true"
android:gravity="center"
android:background="@color/light_gray"
android:maxLines="3"
android:padding="2dp"
android:ellipsize="end"/>
<ImageView
android:id="@+id/imgvCover"
android:layout_width="@dimen/thumbnail_length_queue_item"
android:layout_height="@dimen/thumbnail_length_queue_item"
android:layout_centerVertical="true"
android:contentDescription="@string/cover_label"
tools:src="@tools:sample/avatars"/>
</RelativeLayout>
</androidx.cardview.widget.CardView>
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="4dp"
android:layout_toLeftOf="@id/statusUnread"
android:layout_toStartOf="@id/statusUnread"
tools:text="Episode title"
tools:background="@android:color/holo_green_dark" />
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
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"
tools:background="@android:color/holo_red_dark"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/status"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:text="@string/new_episodes_label"
style="@style/AntennaPod.TextView.UnreadIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/statusUnread"
android:layout_marginRight="4dp"
android:layout_marginEnd="4dp"
tools:text="@sample/episodes.json/data/status_label"/>
<ImageView
android:layout_width="14sp"
android:layout_height="14sp"
app:srcCompat="?attr/type_video"
tools:srcCompat="@drawable/ic_videocam_grey600_24dp"
android:id="@+id/ivIsVideo"/>
<ImageView
android:layout_width="14sp"
android:layout_height="14sp"
app:srcCompat="?attr/ic_unfav"
tools:srcCompat="@drawable/ic_star_grey600_24dp"
android:id="@+id/isFavorite"/>
<ImageView
android:layout_width="14sp"
android:layout_height="14sp"
app:srcCompat="?attr/stat_playlist"
tools:srcCompat="@drawable/ic_list_grey600_24dp"
android:id="@+id/ivInPlaylist"/>
<TextView
android:id="@+id/separatorIcons"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="4dp"
android:layout_marginStart="4dp"
android:layout_marginRight="4dp"
android:layout_marginEnd="4dp"
android:text="·"
tools:background="@android:color/holo_blue_light"/>
<TextView
android:id="@+id/txtvPubDate"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="4dp"
android:layout_marginEnd="4dp"
tools:text="@sample/episodes.json/data/published_at"/>
<TextView
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="4dp"
android:layout_marginEnd="4dp"
android:text="·"
tools:background="@android:color/holo_blue_light"/>
<TextView
android:id="@+id/size"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_marginRight="4dp"
android:layout_marginEnd="4dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="10 MB"/>
</LinearLayout>
<TextView
android:id="@+id/txtvLenSize"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@id/txtvItemname"
tools:text="00:42:23"
tools:background="@android:color/holo_green_dark" />
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="@sample/episodes.json/data/title"
android:ellipsize="end"
tools:background="@android:color/holo_blue_light"/>
<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_below="@id/txtvItemname"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:contentDescription="@string/in_queue_label"
android:src="?attr/stat_playlist"
android:visibility="visible"
tools:src="@drawable/ic_list_white_24dp"
tools:background="@android:color/holo_red_light" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/progress"
android:orientation="horizontal"
android:gravity="center_vertical">
<ImageView
android:id="@+id/imgvType"
android:layout_width="@dimen/enc_icons_size"
android:layout_height="@dimen/enc_icons_size"
android:layout_below="@id/txtvItemname"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:layout_toLeftOf="@id/imgvInPlaylist"
android:layout_toStartOf="@id/imgvInPlaylist"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_hearing_white_18dp"
tools:background="@android:color/holo_red_light" />
<TextView
android:id="@+id/txtvPosition"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
tools:text="00:42:23"
tools:background="@android:color/holo_blue_light"/>
<TextView
android:id="@+id/txtvPublished"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/txtvItemname"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"
android:layout_toLeftOf="@id/imgvType"
android:layout_toStartOf="@id/imgvType"
tools:text="Jan 23"
tools:background="@android:color/holo_green_dark" />
<ProgressBar
android:id="@+id/progressBar"
style="?attr/progressBarTheme"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="4dp"
android:max="100"
android:layout_margin="4dp"
tools:background="@android:color/holo_blue_light"/>
<ProgressBar
android:id="@+id/pbar_episode_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/txtvPublished"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_toStartOf="@id/txtvPublished"
android:layout_toLeftOf="@id/txtvPublished"
android:layout_toEndOf="@id/txtvLenSize"
android:layout_toRightOf="@id/txtvLenSize"
android:layoutDirection="ltr"
android:indeterminate="false"
android:max="100"
android:progress="42"
tools:background="@android:color/holo_blue_light" />
<TextView
android:id="@+id/txtvDuration"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="0dp"
tools:text="@sample/episodes.json/data/duration"
tools:background="@android:color/holo_blue_light"/>
</RelativeLayout>
</LinearLayout>
<include layout="@layout/vertical_list_divider"/>
</LinearLayout>
<include layout="@layout/secondary_action"/>

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>

View File

@ -1,153 +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:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
tools:background="@android:color/darker_gray" >
<ImageView
android:id="@+id/drag_handle"
android:layout_width="104dp"
android:layout_height="64dp"
android:layout_marginLeft="-16dp"
android:layout_marginStart="-16dp"
android:layout_marginRight="-72dp"
android:layout_marginEnd="-72dp"
android:contentDescription="@string/drag_handle_content_description"
android:scaleType="fitXY"
android:src="?attr/dragview_background"
tools:src="@drawable/ic_drag_vertical_grey600_48dp"
tools:background="@android:color/holo_green_dark" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp">
<TextView
android:id="@+id/txtvPlaceholder"
android:layout_width="@dimen/thumbnail_length_queue_item"
android:layout_height="@dimen/thumbnail_length_queue_item"
android:layout_centerVertical="true"
android:gravity="center"
android:background="@color/light_gray"
android:maxLines="3"
android:ellipsize="end"/>
<ImageView
android:id="@+id/imgvCover"
android:layout_width="@dimen/thumbnail_length_queue_item"
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"/>
</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"
tools:background="@android:color/holo_red_dark">
<!-- order is important, pubDate first! -->
<TextView
android:id="@+id/txtvPubDate"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:lines="2"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:gravity="end|top"
android:text="Feb\n12"
tools:background="@android:color/holo_blue_light" />
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/txtvPubDate"
android:layout_toStartOf="@id/txtvPubDate"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:text="Queue item title"
android:ellipsize="end"
tools:background="@android:color/holo_blue_light" />
<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/txtvProgressLeft"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginBottom="0dp"
android:text="00:42:23"
tools:background="@android:color/holo_blue_light"/>
<TextView
android:id="@+id/txtvProgressRight"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginBottom="0dp"
tools:text="Jan 23"
tools:background="@android:color/holo_green_dark" />
<ProgressBar
android:id="@+id/progressBar"
style="?attr/progressBarTheme"
android:layout_width="match_parent"
android:layout_height="4dp"
android:layout_below="@id/txtvProgressLeft"
android:layoutDirection="ltr"
android:max="100"
tools:background="@android:color/holo_blue_light" />
</RelativeLayout>
</RelativeLayout>
<include layout="@layout/vertical_list_divider"/>
<include layout="@layout/secondary_action"/>
</LinearLayout>
</FrameLayout>

View File

@ -13,8 +13,5 @@
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:paddingLeft="@dimen/list_vertical_padding"
android:paddingRight="@dimen/list_vertical_padding" />
android:layout_height="match_parent" />
</FrameLayout>

View File

@ -1,12 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/butSecondaryAction"
android:layout_width="@dimen/listview_secondary_button_width"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
tools:ignore="ContentDescription"
tools:src="@sample/secondaryaction" />
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginRight="12dp"
android:layout_marginEnd="12dp"
android:id="@+id/secondaryActionButton"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="false"
android:focusableInTouchMode="false" >
<ImageView
android:id="@+id/secondaryActionIcon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
tools:ignore="ContentDescription"
tools:src="@sample/secondaryaction"/>
<de.danoeh.antennapod.view.CircularProgressBar
android:id="@+id/secondaryActionProgress"
android:layout_width="40dp"
android:layout_gravity="center"
android:layout_height="40dp"/>
</FrameLayout>

View File

@ -1,86 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
<TextView
android:id="@+id/txtvStart"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
tools:text="Start"
tools:background="@android:color/holo_green_dark" />
android:baselineAligned="false"
android:descendantFocusability="blocksDescendants">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginEnd="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical"
tools:background="@android:color/holo_red_dark">
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
android:layout_marginEnd="@dimen/listitem_threeline_textrightpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/txtvTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:ellipsize="end"
android:maxLines="2"
android:textColor="?android:attr/textColorPrimary"
android:textSize="16sp"
tools:text="Chapter title"
tools:background="@android:color/holo_green_dark" />
android:id="@+id/txtvStart"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="00:00:00"/>
<TextView
android:id="@+id/txtvLink"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:focusable="false"
android:focusableInTouchMode="false"
android:maxLines="1"
android:visibility="gone"
tools:visibility="visible"
tools:text="Link"
tools:background="@android:color/holo_green_dark" />
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="@sample/episodes.json/data/title"
android:ellipsize="end"/>
<TextView
android:id="@+id/txtvLink"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:focusable="false"
android:focusableInTouchMode="false"
android:visibility="gone"
android:background="?attr/selectableItemBackground"
tools:visibility="visible"
tools:text="https://example.com"/>
<TextView
android:id="@+id/txtvDuration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:focusable="false"
android:focusableInTouchMode="false"
android:maxLines="1"
tools:text="Duration"
tools:background="@android:color/holo_green_dark" />
tools:text="Duration: 00:00:00"/>
</LinearLayout>
<include layout="@layout/vertical_list_divider" />
<include layout="@layout/secondary_action"/>
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/butPlayChapter"
android:layout_width="@dimen/listview_secondary_button_width"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:clickable="false"
android:contentDescription="@string/chapters_label"
android:focusable="false"
android:focusableInTouchMode="false"
android:src="?attr/av_play"
tools:src="@drawable/ic_play_arrow_white_36dp"
tools:background="@android:color/holo_green_dark" />
</LinearLayout>
</LinearLayout>

View File

@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.service.download.handler;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import org.greenrobot.eventbus.EventBus;
import java.util.ArrayList;
@ -23,6 +24,7 @@ public class PostDownloaderTask implements Runnable {
runningDownloads.add(downloader);
}
}
DownloadRequester.getInstance().updateProgress(downloads);
List<Downloader> list = Collections.unmodifiableList(runningDownloads);
EventBus.getDefault().postSticky(DownloadEvent.refresh(list));
}

View File

@ -12,6 +12,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.content.ContextCompat;
import de.danoeh.antennapod.core.service.download.Downloader;
import org.apache.commons.io.FilenameUtils;
import java.io.File;
@ -343,6 +344,16 @@ public class DownloadRequester implements DownloadStateProvider {
return item.getDownload_url() != null && downloads.containsKey(item.getDownload_url());
}
/**
* Get the downloader for this item.
*/
public synchronized DownloadRequest getRequestFor(FeedFile item) {
if (isDownloadingFile(item)) {
return downloads.get(item.getDownload_url());
}
return null;
}
/**
* Checks if feedfile with the given download url is in the downloads list
*/
@ -428,4 +439,13 @@ public class DownloadRequester implements DownloadStateProvider {
}
return filename;
}
public void updateProgress(List<Downloader> newDownloads) {
for (Downloader downloader : newDownloads) {
DownloadRequest request = downloader.getDownloadRequest();
if (downloads.containsKey(request.getSource())) {
downloads.put(request.getSource(), request);
}
}
}
}

View File

@ -35,11 +35,12 @@ public class FeedSearcher {
final List<FeedComponent> result = new ArrayList<>();
try {
FutureTask<List<FeedItem>> itemSearchTask = DBTasks.searchFeedItems(context, selectedFeed, query);
FutureTask<List<Feed>> feedSearchTask = DBTasks.searchFeeds(context, query);
itemSearchTask.run();
feedSearchTask.run();
result.addAll(feedSearchTask.get());
if (selectedFeed == 0) {
FutureTask<List<Feed>> feedSearchTask = DBTasks.searchFeeds(context, query);
feedSearchTask.run();
result.addAll(feedSearchTask.get());
}
result.addAll(itemSearchTask.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();

View File

@ -4,6 +4,7 @@ import android.content.Context;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
import android.util.TypedValue;
import androidx.annotation.DrawableRes;
public class ThemeUtils {
private ThemeUtils() {
@ -15,4 +16,10 @@ public class ThemeUtils {
context.getTheme().resolveAttribute(attr, typedValue, true);
return typedValue.data;
}
public static @DrawableRes int getDrawableFromAttr(Context context, @AttrRes int attr) {
TypedValue typedValue = new TypedValue();
context.getTheme().resolveAttribute(attr, typedValue, true);
return typedValue.resourceId;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 478 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 621 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 885 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 697 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 B

View File

@ -0,0 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path android:fillColor="#FF757575"
android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
</vector>

View File

@ -0,0 +1,8 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path android:fillColor="#FFFFFFFF"
android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
</vector>

View File

@ -2,6 +2,4 @@
<resources>
<dimen name="thumbnail_length">170dp</dimen>
<dimen name="thumbnail_length_queue_item">64dp</dimen>
<dimen name="thumbnail_length_downloaded_item">64dp</dimen>
</resources>

View File

@ -26,7 +26,6 @@
<attr name="social_share" format="reference"/>
<attr name="stat_playlist" format="reference"/>
<attr name="ic_folder" format="reference"/>
<attr name="type_audio" format="reference"/>
<attr name="type_video" format="reference"/>
<attr name="overlay_drawable" format="reference"/>
<attr name="dragview_background" format="reference"/>

View File

@ -7,8 +7,8 @@
<color name="black">#000000</color>
<color name="holo_blue_light">#33B5E5</color>
<color name="holo_blue_dark">#0099CC</color>
<color name="download_success_green">#669900</color>
<color name="download_failed_red">#CC0000</color>
<color name="download_success_green">#248800</color>
<color name="download_failed_red">#B00020</color>
<color name="status_progress">#E033B5E5</color>
<color name="overlay_dark">#2C2C2C</color>
<color name="overlay_light">#FFFFFF</color>

View File

@ -10,8 +10,8 @@
<dimen name="text_size_navdrawer">16sp</dimen>
<dimen name="text_size_medium">18sp</dimen>
<dimen name="text_size_large">22sp</dimen>
<dimen name="thumbnail_length_itemlist">64dp</dimen>
<dimen name="thumbnail_length_queue_item">64dp</dimen>
<dimen name="thumbnail_length_itemlist">56dp</dimen>
<dimen name="thumbnail_length_queue_item">56dp</dimen>
<dimen name="thumbnail_length_downloaded_item">64dp</dimen>
<dimen name="thumbnail_length_onlinefeedview">100dp</dimen>
<dimen name="feeditemlist_header_height">132dp</dimen>
@ -23,7 +23,7 @@
<dimen name="listitem_threeline_textleftpadding">16dp</dimen>
<dimen name="listitem_threeline_textrightpadding">8dp</dimen>
<dimen name="listitem_threeline_verticalpadding">16dp</dimen>
<dimen name="listitem_threeline_verticalpadding">8dp</dimen>
<dimen name="listitem_threeline_horizontalpadding">16dp</dimen>
<dimen name="list_vertical_padding">8dp</dimen>

View File

@ -102,6 +102,7 @@
<string name="feed_volume_reduction_light">Light</string>
<string name="feed_volume_reduction_heavy">Heavy</string>
<string name="parallel_downloads_suffix">\u0020parallel downloads</string>
<string name="download_queued">Download queued</string>
<string name="feed_auto_download_global">Global default</string>
<string name="feed_auto_download_always">Always</string>
<string name="feed_auto_download_never">Never</string>

View File

@ -38,8 +38,7 @@
<item name="navigation_up">@drawable/navigation_up</item>
<item name="social_share">@drawable/ic_share_grey600_24dp</item>
<item name="stat_playlist">@drawable/ic_list_grey600_24dp</item>
<item name="type_audio">@drawable/ic_hearing_grey600_18dp</item>
<item name="type_video">@drawable/ic_remove_red_eye_grey600_18dp</item>
<item name="type_video">@drawable/ic_videocam_grey600_24dp</item>
<item name="non_transparent_background">@color/white</item>
<item name="overlay_background">@color/overlay_light</item>
<item name="overlay_drawable">@drawable/overlay_drawable</item>
@ -127,8 +126,7 @@
<item name="navigation_up">@drawable/navigation_up_dark</item>
<item name="social_share">@drawable/ic_share_white_24dp</item>
<item name="stat_playlist">@drawable/ic_list_white_24dp</item>
<item name="type_audio">@drawable/ic_hearing_white_18dp</item>
<item name="type_video">@drawable/ic_remove_red_eye_white_18dp</item>
<item name="type_video">@drawable/ic_videocam_white_24dp</item>
<item name="non_transparent_background">@color/black</item>
<item name="overlay_background">@color/overlay_dark</item>
<item name="overlay_drawable">@drawable/overlay_drawable_dark</item>
@ -291,7 +289,7 @@
<style name="AntennaPod.TextView.ListItemPrimaryTitle" parent="@android:style/TextAppearance.Small">
<item name="android:textSize">16sp</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:lines">2</item>
<item name="android:maxLines">2</item>
<item name="android:ellipsize">end</item>
</style>