blob: aa3368258b4e7100f5b4ef34552bf834805c4644 [file] [log] [blame]
package de.danoeh.antennapod.adapter;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.text.TextUtils;
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.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.animation.GlideAnimation;
import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
import com.joanzapata.iconify.Iconify;
import com.nineoldandroids.view.ViewHelper;
import java.lang.ref.WeakReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
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.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.fragment.ItemFragment;
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();
private WeakReference<MainActivity> mainActivity;
private final ItemAccess itemAccess;
private final ActionButtonCallback actionButtonCallback;
private final ActionButtonUtils actionButtonUtils;
private final ItemTouchHelper itemTouchHelper;
private boolean locked;
private FeedItem selectedItem;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
public QueueRecyclerAdapter(MainActivity mainActivity,
ItemAccess itemAccess,
ActionButtonCallback actionButtonCallback,
ItemTouchHelper itemTouchHelper) {
super();
this.mainActivity = new WeakReference<>(mainActivity);
this.itemAccess = itemAccess;
this.actionButtonUtils = new ActionButtonUtils(mainActivity);
this.actionButtonCallback = actionButtonCallback;
this.itemTouchHelper = itemTouchHelper;
locked = UserPreferences.isQueueLocked();
if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_dark);
} else {
playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_light);
}
normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent);
}
public void setLocked(boolean locked) {
this.locked = locked;
notifyDataSetChanged();
}
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 void onBindViewHolder(ViewHolder holder, int pos) {
FeedItem item = itemAccess.getItem(pos);
holder.bind(item);
holder.itemView.setOnLongClickListener(v -> {
selectedItem = item;
return false;
});
}
@Nullable
public FeedItem getSelectedItem() {
return selectedItem;
}
public int getItemCount() {
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 = (FrameLayout) v.findViewById(R.id.container);
dragHandle = (ImageView) v.findViewById(R.id.drag_handle);
placeholder = (TextView) v.findViewById(R.id.txtvPlaceholder);
cover = (ImageView) v.findViewById(R.id.imgvCover);
title = (TextView) v.findViewById(R.id.txtvTitle);
pubDate = (TextView) v.findViewById(R.id.txtvPubDate);
progressLeft = (TextView) v.findViewById(R.id.txtvProgressLeft);
progressRight = (TextView) v.findViewById(R.id.txtvProgressRight);
butSecondary = (ImageButton) v.findViewById(R.id.butSecondaryAction);
progressBar = (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) {
activity.loadChildFragment(ItemFragment.newInstance(item.getId()));
}
}
@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);
if (item != null) {
menu.setHeaderTitle(item.getTitle());
}
FeedItemMenuHandler.MenuInterface contextMenuInterface = (id, visible) -> {
if (menu == null) {
return;
}
MenuItem item1 = menu.findItem(id);
if (item1 != null) {
item1.setVisible(visible);
}
};
FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true,
itemAccess.getQueueIds(), itemAccess.getFavoritesIds());
}
@Override
public void onItemSelected() {
ViewHelper.setAlpha(itemView, 0.5f);
}
@Override
public void onItemClear() {
ViewHelper.setAlpha(itemView, 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.isDownloadAllowed() && false == 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.GONE);
}
if(media.isCurrentlyPlaying()) {
container.setBackgroundColor(playingBackGroundColor);
} else {
container.setBackgroundColor(normalBackGroundColor);
}
}
actionButtonUtils.configureActionButton(butSecondary, item, true);
butSecondary.setFocusable(false);
butSecondary.setTag(item);
butSecondary.setOnClickListener(secondaryActionListener);
Glide.with(mainActivity.get())
.load(item.getImageUri())
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(new CoverTarget(item.getFeed().getImageUri(), placeholder, cover));
}
}
private class CoverTarget extends GlideDrawableImageViewTarget {
private final WeakReference<Uri> fallback;
private final WeakReference<TextView> placeholder;
private final WeakReference<ImageView> cover;
public CoverTarget(Uri fallbackUri, TextView txtvPlaceholder, ImageView imgvCover) {
super(imgvCover);
fallback = new WeakReference<>(fallbackUri);
placeholder = new WeakReference<>(txtvPlaceholder);
cover = new WeakReference<>(imgvCover);
}
@Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
Uri fallbackUri = fallback.get();
TextView txtvPlaceholder = placeholder.get();
ImageView imgvCover = cover.get();
if(fallbackUri != null && txtvPlaceholder != null && imgvCover != null) {
Glide.with(mainActivity.get())
.load(fallbackUri)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(new CoverTarget(null, txtvPlaceholder, imgvCover));
}
}
@Override
public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) {
super.onResourceReady(drawable, anim);
TextView txtvPlaceholder = placeholder.get();
if(txtvPlaceholder != null) {
txtvPlaceholder.setVisibility(View.INVISIBLE);
}
}
}
private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag();
actionButtonCallback.onActionButtonPressed(item);
}
};
public interface ItemAccess {
FeedItem getItem(int position);
int getCount();
long getItemDownloadedBytes(FeedItem item);
long getItemDownloadSize(FeedItem item);
int getItemDownloadProgressPercent(FeedItem item);
LongList getQueueIds();
LongList getFavoritesIds();
}
/**
* Notifies a View Holder of relevant callbacks from
* {@link ItemTouchHelper.Callback}.
*/
public interface ItemTouchHelperViewHolder {
/**
* Called when the {@link ItemTouchHelper} first registers an
* item as being moved or swiped.
* Implementations should update the item view to indicate
* it's active state.
*/
void onItemSelected();
/**
* Called when the {@link ItemTouchHelper} has completed the
* move or swipe, and the active item state should be cleared.
*/
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;
}
}