Extract queue item holder to new class
First step to use a single item holder in the future
This commit is contained in:
parent
7e2fd0b1d7
commit
cae04b5b13
|
@ -1,56 +1,33 @@
|
||||||
package de.danoeh.antennapod.adapter;
|
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.util.Log;
|
import android.util.Log;
|
||||||
import android.view.ContextMenu;
|
import android.view.ContextMenu;
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.FrameLayout;
|
import androidx.annotation.NonNull;
|
||||||
import android.widget.ImageButton;
|
import androidx.annotation.Nullable;
|
||||||
import android.widget.ImageView;
|
import androidx.core.view.MotionEventCompat;
|
||||||
import android.widget.ProgressBar;
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||||
import android.widget.TextView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import de.danoeh.antennapod.R;
|
||||||
import com.joanzapata.iconify.Iconify;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
|
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||||
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
|
import de.danoeh.antennapod.core.util.LongList;
|
||||||
import de.danoeh.antennapod.fragment.ItemPagerFragment;
|
import de.danoeh.antennapod.fragment.ItemPagerFragment;
|
||||||
|
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||||
|
import de.danoeh.antennapod.view.EpisodeItemViewHolder;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
import org.apache.commons.lang3.ArrayUtils;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
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.
|
* List adapter for the queue.
|
||||||
*/
|
*/
|
||||||
public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdapter.ViewHolder> {
|
public class QueueRecyclerAdapter extends RecyclerView.Adapter<EpisodeItemViewHolder> implements View.OnCreateContextMenuListener {
|
||||||
|
private static final String TAG = "QueueRecyclerAdapter";
|
||||||
private static final String TAG = QueueRecyclerAdapter.class.getSimpleName();
|
|
||||||
|
|
||||||
private final WeakReference<MainActivity> mainActivity;
|
private final WeakReference<MainActivity> mainActivity;
|
||||||
private final ItemAccess itemAccess;
|
private final ItemAccess itemAccess;
|
||||||
|
@ -60,9 +37,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
|
||||||
|
|
||||||
private FeedItem selectedItem;
|
private FeedItem selectedItem;
|
||||||
|
|
||||||
private final int playingBackGroundColor;
|
|
||||||
private final int normalBackGroundColor;
|
|
||||||
|
|
||||||
public QueueRecyclerAdapter(MainActivity mainActivity,
|
public QueueRecyclerAdapter(MainActivity mainActivity,
|
||||||
ItemAccess itemAccess,
|
ItemAccess itemAccess,
|
||||||
ItemTouchHelper itemTouchHelper) {
|
ItemTouchHelper itemTouchHelper) {
|
||||||
|
@ -71,9 +45,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
|
||||||
this.itemAccess = itemAccess;
|
this.itemAccess = itemAccess;
|
||||||
this.itemTouchHelper = itemTouchHelper;
|
this.itemTouchHelper = itemTouchHelper;
|
||||||
locked = UserPreferences.isQueueLocked();
|
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) {
|
public void setLocked(boolean locked) {
|
||||||
|
@ -81,20 +52,38 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
|
||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public EpisodeItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.queue_listitem, parent, false);
|
EpisodeItemViewHolder viewHolder = new EpisodeItemViewHolder(mainActivity.get());
|
||||||
return new ViewHolder(view);
|
viewHolder.dragHandle.setOnTouchListener((v1, event) -> {
|
||||||
|
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
|
||||||
|
Log.d(TAG, "startDrag()");
|
||||||
|
itemTouchHelper.startDrag(viewHolder);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return viewHolder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(ViewHolder holder, int pos) {
|
public void onBindViewHolder(EpisodeItemViewHolder holder, int pos) {
|
||||||
FeedItem item = itemAccess.getItem(pos);
|
FeedItem item = itemAccess.getItem(pos);
|
||||||
holder.bind(item);
|
holder.bind(item);
|
||||||
|
holder.dragHandle.setVisibility(locked ? View.GONE : View.VISIBLE);
|
||||||
holder.itemView.setOnLongClickListener(v -> {
|
holder.itemView.setOnLongClickListener(v -> {
|
||||||
selectedItem = item;
|
selectedItem = item;
|
||||||
return false;
|
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));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
holder.itemView.setOnCreateContextMenuListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -112,211 +101,30 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
|
||||||
return itemAccess.getCount();
|
return itemAccess.getCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ViewHolder extends RecyclerView.ViewHolder
|
|
||||||
implements View.OnClickListener,
|
|
||||||
View.OnCreateContextMenuListener,
|
|
||||||
ItemTouchHelperViewHolder {
|
|
||||||
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@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
|
@Override
|
||||||
public void onCreateContextMenu(final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
public void onCreateContextMenu(final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
|
||||||
FeedItem item = itemAccess.getItem(getAdapterPosition());
|
|
||||||
|
|
||||||
MenuInflater inflater = mainActivity.get().getMenuInflater();
|
MenuInflater inflater = mainActivity.get().getMenuInflater();
|
||||||
inflater.inflate(R.menu.queue_context, menu); // queue-specific menu items
|
inflater.inflate(R.menu.queue_context, menu); // queue-specific menu items
|
||||||
inflater.inflate(R.menu.feeditemlist_context, menu); // generic menu items for item feeds
|
inflater.inflate(R.menu.feeditemlist_context, menu); // generic menu items for item feeds
|
||||||
|
|
||||||
if (item != null) {
|
menu.setHeaderTitle(selectedItem.getTitle());
|
||||||
menu.setHeaderTitle(item.getTitle());
|
FeedItemMenuHandler.onPrepareMenu(menu, selectedItem, R.id.skip_episode_item);
|
||||||
}
|
|
||||||
|
|
||||||
FeedItemMenuHandler.onPrepareMenu(menu, item,
|
|
||||||
R.id.skip_episode_item); // Skip Episode is not useful in Queue, so hide it.
|
|
||||||
// Queue-specific menu preparation
|
// Queue-specific menu preparation
|
||||||
final boolean keepSorted = UserPreferences.isQueueKeepSorted();
|
final boolean keepSorted = UserPreferences.isQueueKeepSorted();
|
||||||
final LongList queueAccess = itemAccess.getQueueIds();
|
final LongList queueAccess = itemAccess.getQueueIds();
|
||||||
if (queueAccess.size() == 0 || queueAccess.get(0) == item.getId() || keepSorted) {
|
if (queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId() || keepSorted) {
|
||||||
menu.findItem(R.id.move_to_top_item).setVisible(false);
|
menu.findItem(R.id.move_to_top_item).setVisible(false);
|
||||||
}
|
}
|
||||||
if (queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == item.getId() || keepSorted) {
|
if (queueAccess.size() == 0 || queueAccess.get(queueAccess.size() - 1) == selectedItem.getId() || keepSorted) {
|
||||||
menu.findItem(R.id.move_to_bottom_item).setVisible(false);
|
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);
|
|
||||||
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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ItemAccess {
|
public interface ItemAccess {
|
||||||
FeedItem getItem(int position);
|
FeedItem getItem(int position);
|
||||||
|
|
||||||
int getCount();
|
int getCount();
|
||||||
long getItemDownloadedBytes(FeedItem item);
|
|
||||||
long getItemDownloadSize(FeedItem item);
|
|
||||||
int getItemDownloadProgressPercent(FeedItem item);
|
|
||||||
LongList getQueueIds();
|
LongList getQueueIds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -341,18 +149,4 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
|
||||||
*/
|
*/
|
||||||
void onItemClear();
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import androidx.recyclerview.widget.SimpleItemAnimator;
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
|
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
|
||||||
|
|
||||||
|
import de.danoeh.antennapod.view.EpisodeItemViewHolder;
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
|
@ -84,7 +85,6 @@ public class QueueFragment extends Fragment {
|
||||||
private ProgressBar progLoading;
|
private ProgressBar progLoading;
|
||||||
|
|
||||||
private List<FeedItem> queue;
|
private List<FeedItem> queue;
|
||||||
private List<Downloader> downloaderList;
|
|
||||||
|
|
||||||
private boolean isUpdatingFeeds = false;
|
private boolean isUpdatingFeeds = false;
|
||||||
|
|
||||||
|
@ -196,7 +196,6 @@ public class QueueFragment extends Fragment {
|
||||||
public void onEventMainThread(DownloadEvent event) {
|
public void onEventMainThread(DownloadEvent event) {
|
||||||
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
||||||
DownloaderUpdate update = event.update;
|
DownloaderUpdate update = event.update;
|
||||||
downloaderList = update.downloaders;
|
|
||||||
if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
|
if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
|
||||||
getActivity().invalidateOptionsMenu();
|
getActivity().invalidateOptionsMenu();
|
||||||
}
|
}
|
||||||
|
@ -214,7 +213,7 @@ public class QueueFragment extends Fragment {
|
||||||
public void onEventMainThread(PlaybackPositionEvent event) {
|
public void onEventMainThread(PlaybackPositionEvent event) {
|
||||||
if (recyclerAdapter != null) {
|
if (recyclerAdapter != null) {
|
||||||
for (int i = 0; i < recyclerAdapter.getItemCount(); i++) {
|
for (int i = 0; i < recyclerAdapter.getItemCount(); i++) {
|
||||||
QueueRecyclerAdapter.ViewHolder holder = (QueueRecyclerAdapter.ViewHolder)
|
EpisodeItemViewHolder holder = (EpisodeItemViewHolder)
|
||||||
recyclerView.findViewHolderForAdapterPosition(i);
|
recyclerView.findViewHolderForAdapterPosition(i);
|
||||||
if (holder != null && holder.isCurrentlyPlayingItem()) {
|
if (holder != null && holder.isCurrentlyPlayingItem()) {
|
||||||
holder.notifyPlaybackPositionUpdated(event);
|
holder.notifyPlaybackPositionUpdated(event);
|
||||||
|
@ -688,46 +687,6 @@ public class QueueFragment extends Fragment {
|
||||||
return null;
|
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
|
@Override
|
||||||
public LongList getQueueIds() {
|
public LongList getQueueIds() {
|
||||||
return queue != null ? LongList.of(FeedItemUtil.getIds(queue)) : new LongList(0);
|
return queue != null ? LongList.of(FeedItemUtil.getIds(queue)) : new LongList(0);
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
package de.danoeh.antennapod.view;
|
||||||
|
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.text.Layout;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
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.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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the view which shows FeedItems.
|
||||||
|
*/
|
||||||
|
public class EpisodeItemViewHolder extends RecyclerView.ViewHolder
|
||||||
|
implements QueueRecyclerAdapter.ItemTouchHelperViewHolder {
|
||||||
|
private static final String TAG = "EpisodeItemViewHolder";
|
||||||
|
|
||||||
|
private final FrameLayout container;
|
||||||
|
public 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 final MainActivity activity;
|
||||||
|
|
||||||
|
private FeedItem item;
|
||||||
|
|
||||||
|
public EpisodeItemViewHolder(MainActivity activity) {
|
||||||
|
super(View.inflate(activity, R.layout.queue_listitem, null));
|
||||||
|
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);
|
||||||
|
progressLeft = itemView.findViewById(R.id.txtvProgressLeft);
|
||||||
|
progressRight = itemView.findViewById(R.id.txtvProgressRight);
|
||||||
|
butSecondary = itemView.findViewById(R.id.butSecondaryAction);
|
||||||
|
progressBar = itemView.findViewById(R.id.progressBar);
|
||||||
|
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());
|
||||||
|
title.setText(item.getTitle());
|
||||||
|
pubDate.setText(formatPubDate());
|
||||||
|
|
||||||
|
FeedMedia media = item.getMedia();
|
||||||
|
if (media != null) {
|
||||||
|
final DownloadRequest downloadRequest = DownloadRequester.getInstance().getRequestFor(media);
|
||||||
|
FeedItem.State state = item.getState();
|
||||||
|
if (downloadRequest != null) {
|
||||||
|
progressLeft.setText(Converter.byteToString(downloadRequest.getSoFar()));
|
||||||
|
if (downloadRequest.getSize() > 0) {
|
||||||
|
progressRight.setText(Converter.byteToString(downloadRequest.getSize()));
|
||||||
|
} else {
|
||||||
|
progressRight.setText(Converter.byteToString(media.getSize()));
|
||||||
|
}
|
||||||
|
progressBar.setProgress(downloadRequest.getProgressPercent());
|
||||||
|
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(ThemeUtils.getColorFromAttr(activity,
|
||||||
|
R.attr.currently_playing_background));
|
||||||
|
} else {
|
||||||
|
container.setBackgroundColor(Color.TRANSPARENT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemActionButton actionButton = ItemActionButton.forItem(item, true);
|
||||||
|
actionButton.configure(butSecondary, activity);
|
||||||
|
butSecondary.setFocusable(false);
|
||||||
|
butSecondary.setTag(item);
|
||||||
|
|
||||||
|
new CoverLoader(activity)
|
||||||
|
.withUri(ImageResourceUtils.getImageLocation(item))
|
||||||
|
.withFallbackUri(item.getFeed().getImageLocation())
|
||||||
|
.withPlaceholderView(placeholder)
|
||||||
|
.withCoverView(cover)
|
||||||
|
.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String formatPubDate() {
|
||||||
|
String pubDateStr = DateUtils.formatAbbrev(activity, 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);
|
||||||
|
}
|
||||||
|
return pubDateStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.service.download.handler;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.event.DownloadEvent;
|
import de.danoeh.antennapod.core.event.DownloadEvent;
|
||||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||||
|
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -23,6 +24,7 @@ public class PostDownloaderTask implements Runnable {
|
||||||
runningDownloads.add(downloader);
|
runningDownloads.add(downloader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DownloadRequester.getInstance().updateProgress(downloads);
|
||||||
List<Downloader> list = Collections.unmodifiableList(runningDownloads);
|
List<Downloader> list = Collections.unmodifiableList(runningDownloads);
|
||||||
EventBus.getDefault().postSticky(DownloadEvent.refresh(list));
|
EventBus.getDefault().postSticky(DownloadEvent.refresh(list));
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||||
import org.apache.commons.io.FilenameUtils;
|
import org.apache.commons.io.FilenameUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -343,6 +344,16 @@ public class DownloadRequester implements DownloadStateProvider {
|
||||||
return item.getDownload_url() != null && downloads.containsKey(item.getDownload_url());
|
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
|
* Checks if feedfile with the given download url is in the downloads list
|
||||||
*/
|
*/
|
||||||
|
@ -428,4 +439,13 @@ public class DownloadRequester implements DownloadStateProvider {
|
||||||
}
|
}
|
||||||
return filename;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue