Queue: Replace DSVL with RecyclerView

This commit is contained in:
Martin Fietz 2015-11-05 17:12:18 +01:00
parent 3b003c60ab
commit 67d2287323
7 changed files with 492 additions and 488 deletions

View File

@ -1,250 +0,0 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.text.format.DateUtils;
import android.util.Log;
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.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 java.lang.ref.WeakReference;
import de.danoeh.antennapod.R;
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.NetworkUtils;
/**
* List adapter for the queue.
*/
public class QueueListAdapter extends BaseAdapter {
private static final String TAG = QueueListAdapter.class.getSimpleName();
private final Context context;
private final ItemAccess itemAccess;
private final ActionButtonCallback actionButtonCallback;
private final ActionButtonUtils actionButtonUtils;
private boolean locked;
public QueueListAdapter(Context context, ItemAccess itemAccess, ActionButtonCallback actionButtonCallback) {
super();
this.context = context;
this.itemAccess = itemAccess;
this.actionButtonUtils = new ActionButtonUtils(context);
this.actionButtonCallback = actionButtonCallback;
locked = UserPreferences.isQueueLocked();
}
public void setLocked(boolean locked) {
this.locked = locked;
notifyDataSetChanged();
}
@Override
public int getCount() {
return itemAccess.getCount();
}
@Override
public Object getItem(int position) {
return itemAccess.getItem(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
final FeedItem item = (FeedItem) getItem(position);
if (item == null) return null;
if (convertView == null) {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.queue_listitem,
parent, false);
holder.dragHandle = (ImageView) convertView.findViewById(R.id.drag_handle);
holder.placeholder = (TextView) convertView.findViewById(R.id.txtvPlaceholder);
holder.cover = (ImageView) convertView.findViewById(R.id.imgvCover);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.pubDate = (TextView) convertView.findViewById(R.id.txtvPubDate);
holder.progressLeft = (TextView) convertView.findViewById(R.id.txtvProgressLeft);
holder.progressRight = (TextView) convertView
.findViewById(R.id.txtvProgressRight);
holder.butSecondary = (ImageButton) convertView
.findViewById(R.id.butSecondaryAction);
holder.progress = (ProgressBar) convertView
.findViewById(R.id.progressBar);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
if(locked) {
holder.dragHandle.setVisibility(View.GONE);
} else {
holder.dragHandle.setVisibility(View.VISIBLE);
}
holder.placeholder.setText(item.getFeed().getTitle());
holder.title.setText(item.getTitle());
FeedMedia media = item.getMedia();
holder.title.setText(item.getTitle());
String pubDate = DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL);
holder.pubDate.setText(pubDate.replace(" ", "\n"));
if (media != null) {
final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
FeedItem.State state = item.getState();
if (isDownloadingMedia) {
holder.progressLeft.setText(Converter.byteToString(itemAccess.getItemDownloadedBytes(item)));
if(itemAccess.getItemDownloadSize(item) > 0) {
holder.progressRight.setText(Converter.byteToString(itemAccess.getItemDownloadSize(item)));
} else {
holder.progressRight.setText(Converter.byteToString(media.getSize()));
}
holder.progress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
holder.progress.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());
holder.progress.setProgress(progress);
holder.progress.setVisibility(View.VISIBLE);
holder.progressLeft.setText(Converter
.getDurationStringLong(media.getPosition()));
holder.progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
}
} else {
if(media.getSize() > 0) {
holder.progressLeft.setText(Converter.byteToString(media.getSize()));
} else if(false == media.checkedOnSizeButUnknown()) {
holder.progressLeft.setText("{fa-spinner}");
Iconify.addIcons(holder.progressLeft);
NetworkUtils.getFeedMediaSizeObservable(media)
.subscribe(
size -> {
if (size > 0) {
holder.progressLeft.setText(Converter.byteToString(size));
} else {
holder.progressLeft.setText("");
}
}, error -> {
holder.progressLeft.setText("");
Log.e(TAG, Log.getStackTraceString(error));
});
} else {
holder.progressLeft.setText("");
}
holder.progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
holder.progress.setVisibility(View.GONE);
}
}
actionButtonUtils.configureActionButton(holder.butSecondary, item);
holder.butSecondary.setFocusable(false);
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
Glide.with(context)
.load(item.getImageUri())
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
.dontAnimate()
.into(new CoverTarget(item.getFeed().getImageUri(), holder.placeholder, holder.cover));
return convertView;
}
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(context)
.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);
}
};
static class Holder {
ImageView dragHandle;
ImageView cover;
TextView placeholder;
TextView title;
TextView pubDate;
TextView progressLeft;
TextView progressRight;
ProgressBar progress;
ImageButton butSecondary;
}
public interface ItemAccess {
FeedItem getItem(int position);
int getCount();
long getItemDownloadedBytes(FeedItem item);
long getItemDownloadSize(FeedItem item);
int getItemDownloadProgressPercent(FeedItem item);
}
}

View File

@ -0,0 +1,302 @@
package de.danoeh.antennapod.adapter;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
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 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.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
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;
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();
}
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.position = pos;
}
public int getItemCount() {
return itemAccess.getCount();
}
public class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener, View.OnCreateContextMenuListener {
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;
private int position;
public ViewHolder(View v) {
super(v);
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(mainActivity.get(), contextMenuInterface, item, true,
itemAccess.getQueueIds());
}
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.formatDateTime(mainActivity.get(),
item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL);
pubDate.setText(pubDateStr.replace(" ", "\n"));
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(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);
}
}
actionButtonUtils.configureActionButton(butSecondary, item);
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();
}
}

View File

@ -14,9 +14,9 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
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.event.QueueEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;

View File

@ -1,14 +1,17 @@
package de.danoeh.antennapod.fragment;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@ -22,22 +25,21 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.mobeta.android.dslv.DragSortListView;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.QueueListAdapter;
import de.danoeh.antennapod.adapter.QueueRecyclerAdapter;
import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
@ -47,12 +49,13 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.IntList;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.QueueSorter;
import de.danoeh.antennapod.core.util.gui.FeedItemUndoToken;
import de.danoeh.antennapod.core.util.gui.UndoBarController;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.view.DividerItemDecoration;
import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscription;
@ -71,28 +74,22 @@ public class QueueFragment extends Fragment {
EventDistributor.PLAYER_STATUS_UPDATE;
private TextView infoBar;
private DragSortListView listView;
private QueueListAdapter listAdapter;
private RecyclerView recyclerView;
private QueueRecyclerAdapter recyclerAdapter;
private TextView txtvEmpty;
private ProgressBar progLoading;
private ContextMenu contextMenu;
private AdapterView.AdapterContextMenuInfo lastMenuInfo = null;
private UndoBarController<FeedItemUndoToken> undoBarController;
private List<FeedItem> queue;
private List<Downloader> downloaderList;
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
private boolean isUpdatingFeeds = false;
private static final String PREFS = "QueueFragment";
private static final String PREF_KEY_LIST_TOP = "list_top";
private static final String PREF_KEY_LIST_SELECTION = "list_selection";
private AtomicReference<Activity> activity = new AtomicReference<Activity>();
private static final String PREF_SCROLL_POSITION = "scroll_position";
private static final String PREF_SCROLL_OFFSET = "scroll_offset";
private DownloadObserver downloadObserver = null;
@ -102,6 +99,8 @@ public class QueueFragment extends Fragment {
private boolean blockDownloadObserverUpdate = false;
private Subscription subscription;
private LinearLayoutManager layoutManager;
private ItemTouchHelper itemTouchHelper;
@Override
@ -115,19 +114,18 @@ public class QueueFragment extends Fragment {
public void onResume() {
super.onResume();
loadItems();
EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
}
@Override
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
this.activity.set((MainActivity) getActivity());
if (downloadObserver != null) {
downloadObserver.setActivity(getActivity());
downloadObserver.onResume();
}
if (viewsCreated && itemsLoaded) {
if (queue != null) {
onFragmentLoaded();
}
}
@ -136,66 +134,90 @@ public class QueueFragment extends Fragment {
public void onPause() {
super.onPause();
saveScrollPosition();
}
@Override
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if(subscription != null) {
subscription.unsubscribe();
}
if(undoBarController.isShowing()) {
undoBarController.close();
}
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.activity.set((MainActivity) activity);
}
public void onEventMainThread(QueueEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
if(event.action == QueueEvent.Action.REMOVED) {
undoBarController.showUndoBar(false, getString(R.string.removed_from_queue),
new FeedItemUndoToken(event.item, event.position));
switch(event.action) {
case ADDED:
queue.add(event.position, event.item);
recyclerAdapter.notifyItemInserted(event.position);
break;
case SET_QUEUE:
queue = event.items;
recyclerAdapter.notifyDataSetChanged();
break;
case REMOVED:
int position = FeedItemUtil.indexOfItemWithId(queue, event.item.getId());
queue.remove(position);
recyclerAdapter.notifyItemRemoved(position);
break;
case CLEARED:
queue.clear();
recyclerAdapter.notifyDataSetChanged();
break;
case SORTED:
queue = event.items;
recyclerAdapter.notifyDataSetChanged();
break;
case MOVED:
// ItemTouchHelper already handled everything
break;
}
}
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
IntList positions = new IntList();
for(int i=0, size = event.items.size(); i < size; i++) {
FeedItem item = event.items.get(i);
int pos = FeedItemUtil.indexOfItemWithId(queue, item.getId());
if(pos >= 0) {
queue.remove(pos);
queue.add(pos, item);
recyclerAdapter.notifyItemChanged(pos);
}
}
loadItems();
}
private void saveScrollPosition() {
int firstItem = layoutManager.findFirstVisibleItemPosition();
View firstItemView = layoutManager.findViewByPosition(firstItem);
float topOffset;
if(firstItemView == null) {
topOffset = 0;
} else {
topOffset = firstItemView.getTop();
}
SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
View v = listView.getChildAt(0);
int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop());
editor.putInt(PREF_KEY_LIST_SELECTION, listView.getFirstVisiblePosition());
editor.putInt(PREF_KEY_LIST_TOP, top);
editor.putInt(PREF_SCROLL_POSITION, firstItem);
editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
editor.commit();
}
private void restoreScrollPosition() {
SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
int listSelection = prefs.getInt(PREF_KEY_LIST_SELECTION, 0);
int top = prefs.getInt(PREF_KEY_LIST_TOP, 0);
if(listSelection > 0 || top > 0) {
listView.setSelectionFromTop(listSelection, top);
int position = prefs.getInt(PREF_SCROLL_POSITION, 0);
float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
if (position > 0 || offset > 0) {
layoutManager.scrollToPositionWithOffset(position, (int) offset);
// restore once, then forget
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(PREF_KEY_LIST_SELECTION, 0);
editor.putInt(PREF_KEY_LIST_TOP, 0);
editor.putInt(PREF_SCROLL_POSITION, 0);
editor.putFloat(PREF_SCROLL_OFFSET, 0.0f);
editor.commit();
}
}
private void resetViewState() {
unregisterForContextMenu(listView);
listAdapter = null;
activity.set(null);
undoBarController = null;
viewsCreated = false;
unregisterForContextMenu(recyclerView);
blockDownloadObserverUpdate = false;
if (downloadObserver != null) {
downloadObserver.onPause();
@ -208,17 +230,14 @@ public class QueueFragment extends Fragment {
resetViewState();
}
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() {
@Override
public boolean isRefreshing() {
return DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
}
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = () -> {
return DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
};
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
if (itemsLoaded) {
if (queue != null) {
inflater.inflate(R.menu.queue, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
@ -251,14 +270,9 @@ public class QueueFragment extends Fragment {
switch (item.getItemId()) {
case R.id.queue_lock:
boolean locked = !UserPreferences.isQueueLocked();
if(locked) {
listView.setDragEnabled(false);
} else {
listView.setDragEnabled(true);
}
UserPreferences.setQueueLocked(locked);
getActivity().supportInvalidateOptionsMenu();
listAdapter.setLocked(locked);
recyclerAdapter.setLocked(locked);
return true;
case R.id.refresh_item:
List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
@ -321,28 +335,6 @@ public class QueueFragment extends Fragment {
}
};
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
FeedItem item = itemAccess.getItem(adapterInfo.position);
MenuInflater inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.queue_context, menu);
if (item != null) {
menu.setHeaderTitle(item.getTitle());
}
contextMenu = menu;
lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
LongList queueIds = new LongList(queue.size());
for(FeedItem queueItem : queue) {
queueIds.add(queueItem.getId());
}
FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queueIds);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
@ -373,112 +365,86 @@ public class QueueFragment extends Fragment {
View root = inflater.inflate(R.layout.queue_fragment, container, false);
infoBar = (TextView) root.findViewById(R.id.info_bar);
listView = (DragSortListView) root.findViewById(android.R.id.list);
txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
listView.setEmptyView(txtvEmpty);
recyclerView = (RecyclerView) root.findViewById(R.id.recyclerView);
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
RecyclerView.ItemDecoration itemDecoration = new DividerItemDecoration(getActivity(), null);
recyclerView.addItemDecoration(itemDecoration);
recyclerView.setHasFixedSize(true);
if(UserPreferences.isQueueLocked()) {
listView.setDragEnabled(false);
} else {
listView.setDragEnabled(true);
}
itemTouchHelper = new ItemTouchHelper(
new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
FeedItem item = (FeedItem) listAdapter.getItem(position - listView.getHeaderViewsCount());
if (item != null) {
((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(item.getId()));
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
Log.d(TAG, "move(" + from + ", " + to + ")");
Collections.swap(queue, from, to);
recyclerAdapter.notifyItemMoved(from, to);
DBWriter.moveQueueItem(from, to, true);
return true;
}
}
});
listView.setDragSortListener(new DragSortListView.DragSortListener() {
@Override
public void drag(int from, int to) {
Log.d(TAG, "drag");
blockDownloadObserverUpdate = true;
}
@Override
public void drop(int from, int to) {
Log.d(TAG, "drop");
blockDownloadObserverUpdate = false;
if(subscription != null) {
subscription.unsubscribe();
}
final FeedItem item = queue.remove(from);
queue.add(to, item);
listAdapter.notifyDataSetChanged();
DBWriter.moveQueueItem(from, to, true);
}
@Override
public void remove(int which) {
Log.d(TAG, "remove(" + which + ")");
if(subscription != null) {
subscription.unsubscribe();
}
FeedItem item = (FeedItem) listView.getAdapter().getItem(which);
DBWriter.removeQueueItem(getActivity(), item, true);
}
});
undoBarController = new UndoBarController<FeedItemUndoToken>(root.findViewById(R.id.undobar),
new UndoBarController.UndoListener<FeedItemUndoToken>() {
private final Context context = getActivity();
@Override
public void onUndo(FeedItemUndoToken token) {
if (token != null) {
long itemId = token.getFeedItemId();
int position = token.getPosition();
DBWriter.addQueueItemAt(context, itemId, position, false);
}
}
@Override
public void onHide(FeedItemUndoToken token) {
if (token != null && context != null) {
long itemId = token.getFeedItemId();
FeedItem item = DBReader.getFeedItem(itemId);
if(item != null) {
FeedMedia media = item.getMedia();
if (media != null && media.hasAlmostEnded() && item.getFeed().getPreferences().getCurrentAutoDelete()) {
DBWriter.deleteFeedMediaOfItem(context, media.getId());
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
if(subscription != null) {
subscription.unsubscribe();
}
final int position = viewHolder.getAdapterPosition();
Log.d(TAG, "remove(" + position + ")");
final FeedItem item = queue.get(position);
final boolean isRead = item.isPlayed();
DBWriter.markItemPlayed(FeedItem.PLAYED, item.getId());
DBWriter.removeQueueItem(getActivity(), item, true);
Snackbar snackbar = Snackbar.make(root, getString(R.string.marked_as_read_label), Snackbar.LENGTH_LONG);
snackbar.setAction(getString(R.string.undo), v -> {
DBWriter.addQueueItemAt(getActivity(), item.getId(), position, false);
if(false == isRead) {
DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
}
});
snackbar.show();
}
@Override
public boolean isLongPressDragEnabled() {
return false == UserPreferences.isQueueLocked();
}
@Override
public boolean isItemViewSwipeEnabled() {
return false == UserPreferences.isQueueLocked();
}
}
);
itemTouchHelper.attachToRecyclerView(recyclerView);
});
registerForContextMenu(listView);
if (!itemsLoaded) {
progLoading.setVisibility(View.VISIBLE);
txtvEmpty.setVisibility(View.GONE);
}
viewsCreated = true;
if (itemsLoaded && activity.get() != null) {
onFragmentLoaded();
}
txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
txtvEmpty.setVisibility(View.GONE);
progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
progLoading.setVisibility(View.VISIBLE);
return root;
}
private void onFragmentLoaded() {
if (listAdapter == null) {
listAdapter = new QueueListAdapter(activity.get(), itemAccess, new DefaultActionButtonCallback(activity.get()));
listView.setAdapter(listAdapter);
downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback);
if (recyclerAdapter == null) {
MainActivity activity = (MainActivity) getActivity();
recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess,
new DefaultActionButtonCallback(activity), itemTouchHelper);
recyclerView.setAdapter(recyclerAdapter);
downloadObserver = new DownloadObserver(activity, new Handler(), downloadObserverCallback);
downloadObserver.onResume();
}
listAdapter.notifyDataSetChanged();
if(queue == null || queue.size() == 0) {
recyclerView.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.VISIBLE);
} else {
txtvEmpty.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
recyclerAdapter.notifyDataSetChanged();
restoreScrollPosition();
@ -505,21 +471,21 @@ public class QueueFragment extends Fragment {
@Override
public void onContentChanged(List<Downloader> downloaderList) {
QueueFragment.this.downloaderList = downloaderList;
if (listAdapter != null && !blockDownloadObserverUpdate) {
listAdapter.notifyDataSetChanged();
if (recyclerAdapter != null && !blockDownloadObserverUpdate) {
recyclerAdapter.notifyDataSetChanged();
}
}
};
private QueueListAdapter.ItemAccess itemAccess = new QueueListAdapter.ItemAccess() {
private QueueRecyclerAdapter.ItemAccess itemAccess = new QueueRecyclerAdapter.ItemAccess() {
@Override
public int getCount() {
return (itemsLoaded) ? queue.size() : 0;
return queue != null ? queue.size() : 0;
}
@Override
public FeedItem getItem(int position) {
return (itemsLoaded) ? queue.get(position) : null;
return queue != null ? queue.get(position) : null;
}
@Override
@ -561,6 +527,11 @@ public class QueueFragment extends Fragment {
}
return 0;
}
@Override
public LongList getQueueIds() {
return queue != null ? LongList.of(FeedItemUtil.getIds(queue)) : new LongList(0);
}
};
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@ -579,23 +550,19 @@ public class QueueFragment extends Fragment {
if(subscription != null) {
subscription.unsubscribe();
}
if (viewsCreated && !itemsLoaded) {
listView.setVisibility(View.GONE);
if (queue == null) {
recyclerView.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.GONE);
progLoading.setVisibility(View.VISIBLE);
}
subscription = Observable.defer(() -> Observable.just(DBReader.getQueue()))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
listView.setVisibility(View.VISIBLE);
progLoading.setVisibility(View.GONE);
if(result != null) {
queue = result;
itemsLoaded = true;
if (viewsCreated && activity.get() != null) {
onFragmentLoaded();
}
.subscribe(items -> {
if(items != null) {
progLoading.setVisibility(View.GONE);
queue = items;
onFragmentLoaded();
}
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dslv="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -21,26 +20,12 @@
android:layout_below="@id/info_bar"
android:background="?android:attr/listDivider"/>
<com.mobeta.android.dslv.DragSortListView
android:id="@android:id/list"
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:scrollbarStyle="outsideOverlay"
android:layout_below="@+id/divider"
dslv:collapsed_height="2dp"
dslv:drag_enabled="true"
dslv:drag_handle_id="@id/drag_handle"
dslv:drag_scroll_start="0.33"
dslv:float_alpha="0.6"
dslv:float_background_color="?attr/dragview_float_background"
dslv:max_drag_scroll_speed="0.5"
dslv:remove_enabled="true"
dslv:remove_mode="flingRemove"
dslv:slide_shuffle_speed="0.3"
dslv:sort_enabled="true"
dslv:track_drag_sort="true"
dslv:use_default_controller="true" />
android:layout_below="@id/divider"
android:scrollbars="vertical"/>
<TextView
android:id="@id/android:empty"
@ -58,19 +43,4 @@
android:indeterminateOnly="true"
android:visibility="gone" />
<LinearLayout
android:id="@+id/undobar"
android:layout_alignParentBottom="true"
style="@style/UndoBar">
<TextView
android:id="@+id/undobar_message"
style="@style/UndoBarMessage"/>
<Button
android:id="@+id/undobar_button"
style="@style/UndoBarButton"/>
</LinearLayout>
</RelativeLayout>

View File

@ -6,24 +6,28 @@
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingLeft="8dp"
android:gravity="center_vertical"
android:background="?attr/selectableItemBackground"
tools:background="@android:color/darker_gray" >
<ImageView
android:id="@+id/drag_handle"
android:layout_width="100dp"
android:layout_height="match_parent"
android:layout_marginLeft="-8dp"
android:layout_marginRight="-64dp"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginLeft="-16dp"
android:layout_marginRight="-20dp"
android:gravity="center"
android:contentDescription="@string/drag_handle_content_description"
android:scaleType="fitXY"
android:src="?attr/dragview_background"
tools:src="@drawable/ic_drag_handle"
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_height="wrap_content"
android:layout_marginLeft="8dp">
<TextView
android:id="@+id/txtvPlaceholder"
android:layout_width="@dimen/thumbnail_length_queue_item"

View File

@ -30,6 +30,17 @@ public final class LongList {
size = 0;
}
public static LongList of(long... values) {
if(values == null || values.length == 0) {
return new LongList(0);
}
LongList result = new LongList(values.length);
for(long value : values) {
result.add(value);
}
return result;
}
@Override
public int hashCode() {
int hashCode = 1;