diff options
Diffstat (limited to 'app/src/main/java/de/danoeh/antennapod/adapter')
3 files changed, 239 insertions, 20 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java index 774b97454..674b5f86e 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java @@ -3,11 +3,20 @@ package de.danoeh.antennapod.adapter; import android.app.Activity; import android.view.ContextMenu; import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; + +import org.apache.commons.lang3.ArrayUtils; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.model.feed.FeedItem; @@ -15,24 +24,20 @@ 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. */ -public class EpisodeItemListAdapter extends RecyclerView.Adapter<EpisodeItemViewHolder> +public class EpisodeItemListAdapter extends SelectableAdapter<EpisodeItemViewHolder> implements View.OnCreateContextMenuListener { private final WeakReference<MainActivity> mainActivityRef; private List<FeedItem> episodes = new ArrayList<>(); - private FeedItem selectedItem; + private FeedItem longPressedItem; + int longPressedPosition = 0; // used to init actionMode public EpisodeItemListAdapter(MainActivity mainActivity) { - super(); + super(mainActivity); this.mainActivityRef = new WeakReference<>(mainActivity); setHasStableIds(true); } @@ -63,19 +68,35 @@ public class EpisodeItemListAdapter extends RecyclerView.Adapter<EpisodeItemView FeedItem item = episodes.get(pos); holder.bind(item); - holder.itemView.setOnLongClickListener(v -> { - selectedItem = item; - return false; - }); + holder.itemView.setOnClickListener(v -> { MainActivity activity = mainActivityRef.get(); - if (activity != null) { + if (activity != null && !inActionMode()) { long[] ids = FeedItemUtil.getIds(episodes); int position = ArrayUtils.indexOf(ids, item.getId()); activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position)); + } else { + toggleSelection(pos); } }); holder.itemView.setOnCreateContextMenuListener(this); + holder.itemView.setOnLongClickListener(v -> { + longPressedItem = item; + longPressedPosition = pos; + return false; + }); + + if (inActionMode()) { + holder.secondaryActionButton.setVisibility(View.GONE); + holder.selectCheckBox.setOnClickListener(v -> { + toggleSelection(pos); + }); + holder.selectCheckBox.setChecked(isSelected(pos)); + holder.selectCheckBox.setVisibility(View.VISIBLE); + } else { + holder.selectCheckBox.setVisibility(View.GONE); + } + afterBindViewHolder(holder, pos); holder.hideSeparatorIfNecessary(); } @@ -106,6 +127,7 @@ public class EpisodeItemListAdapter extends RecyclerView.Adapter<EpisodeItemView * Instead, we tell the adapter to use partial binding by calling {@link #notifyItemChanged(int, Object)}. * We actually ignore the payload and always do a full bind but calling the partial bind method ensures * that ViewHolders are always re-used. + * * @param position Position of the item that has changed */ public void notifyItemChangedCompat(int position) { @@ -113,8 +135,8 @@ public class EpisodeItemListAdapter extends RecyclerView.Adapter<EpisodeItemView } @Nullable - public FeedItem getSelectedItem() { - return selectedItem; + public FeedItem getLongPressedItem() { + return longPressedItem; } @Override @@ -139,8 +161,37 @@ public class EpisodeItemListAdapter extends RecyclerView.Adapter<EpisodeItemView @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); + if (inActionMode()) { + inflater.inflate(R.menu.multi_select_context_popup, menu); + } else { + inflater.inflate(R.menu.feeditemlist_context, menu); + menu.setHeaderTitle(longPressedItem.getTitle()); + FeedItemMenuHandler.onPrepareMenu(menu, longPressedItem, R.id.skip_episode_item); + } + } + + public boolean onContextItemSelected(MenuItem item) { + if (item.getItemId() == R.id.multi_select) { + startSelectMode(longPressedPosition); + return true; + } else if (item.getItemId() == R.id.select_all_above) { + setSelected(0, longPressedPosition, true); + return true; + } else if (item.getItemId() == R.id.select_all_below) { + setSelected(longPressedPosition + 1, getItemCount(), true); + return true; + } + return false; } + + public List<FeedItem> getSelectedItems() { + List<FeedItem> items = new ArrayList<>(); + for (int i = 0; i < getItemCount(); i++) { + if (isSelected(i)) { + items.add(getItem(i)); + } + } + return items; + } + } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java index 01712ea29..117ba3258 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java @@ -64,10 +64,10 @@ public class QueueRecyclerAdapter extends EpisodeItemListAdapter { super.onCreateContextMenu(menu, v, menuInfo); final boolean keepSorted = UserPreferences.isQueueKeepSorted(); - if (getItem(0).getId() == getSelectedItem().getId() || keepSorted) { + if (getItem(0).getId() == getLongPressedItem().getId() || keepSorted) { menu.findItem(R.id.move_to_top_item).setVisible(false); } - if (getItem(getItemCount() - 1).getId() == getSelectedItem().getId() || keepSorted) { + if (getItem(getItemCount() - 1).getId() == getLongPressedItem().getId() || keepSorted) { menu.findItem(R.id.move_to_bottom_item).setVisible(false); } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SelectableAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SelectableAdapter.java new file mode 100644 index 000000000..670357d16 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SelectableAdapter.java @@ -0,0 +1,168 @@ +package de.danoeh.antennapod.adapter; + +import android.app.Activity; +import android.view.ActionMode; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; + +import androidx.recyclerview.widget.RecyclerView; + +import de.danoeh.antennapod.R; + +import java.util.HashSet; + +/** + * Used by Recyclerviews that need to provide ability to select items. + */ +abstract class SelectableAdapter<T extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<T> { + private ActionMode actionMode; + private final HashSet<Long> selectedIds = new HashSet<>(); + private final Activity activity; + private OnEndSelectModeListener onEndSelectModeListener; + + public SelectableAdapter(Activity activity) { + this.activity = activity; + } + + public void startSelectMode(int pos) { + if (inActionMode()) { + endSelectMode(); + } + + selectedIds.clear(); + selectedIds.add(getItemId(pos)); + notifyDataSetChanged(); + + actionMode = activity.startActionMode(new ActionMode.Callback() { + @Override + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + MenuInflater inflater = mode.getMenuInflater(); + inflater.inflate(R.menu.multi_select_options, menu); + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + updateTitle(); + toggleSelectAllIcon(menu.findItem(R.id.select_toggle), false); + return false; + } + + @Override + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + if (item.getItemId() == R.id.select_toggle) { + boolean allSelected = selectedIds.size() == getItemCount(); + setSelected(0, getItemCount(), !allSelected); + toggleSelectAllIcon(item, !allSelected); + updateTitle(); + return true; + } + return false; + } + + @Override + public void onDestroyActionMode(ActionMode mode) { + callOnEndSelectMode(); + actionMode = null; + selectedIds.clear(); + notifyDataSetChanged(); + } + }); + updateTitle(); + } + + /** + * End action mode if currently in select mode, otherwise do nothing + */ + public void endSelectMode() { + if (inActionMode()) { + callOnEndSelectMode(); + actionMode.finish(); + } + } + + public boolean isSelected(int pos) { + return selectedIds.contains(getItemId(pos)); + } + + /** + * Set the selected state of item at given position + * + * @param pos the position to select + * @param selected true for selected state and false for unselected + */ + public void setSelected(int pos, boolean selected) { + if (selected) { + selectedIds.add(getItemId(pos)); + } else { + selectedIds.remove(getItemId(pos)); + } + updateTitle(); + } + + /** + * Set the selected state of item for a given range + * + * @param startPos start position of range, inclusive + * @param endPos end position of range, inclusive + * @param selected indicates the selection state + * @throws IllegalArgumentException if start and end positions are not valid + */ + public void setSelected(int startPos, int endPos, boolean selected) throws IllegalArgumentException { + for (int i = startPos; i < endPos && i < getItemCount(); i++) { + setSelected(i, selected); + } + notifyItemRangeChanged(startPos, (endPos - startPos)); + } + + protected void toggleSelection(int pos) { + setSelected(pos, !isSelected(pos)); + notifyItemChanged(pos); + + if (selectedIds.size() == 0) { + endSelectMode(); + } + } + + public boolean inActionMode() { + return actionMode != null; + } + + public int getSelectedCount() { + return selectedIds.size(); + } + + private void toggleSelectAllIcon(MenuItem selectAllItem, boolean allSelected) { + if (allSelected) { + selectAllItem.setIcon(R.drawable.ic_select_none); + selectAllItem.setTitle(R.string.deselect_all_label); + } else { + selectAllItem.setIcon(R.drawable.ic_select_all); + selectAllItem.setTitle(R.string.select_all_label); + } + } + + private void updateTitle() { + if (actionMode == null) { + return; + } + actionMode.setTitle(activity.getResources() + .getQuantityString(R.plurals.num_selected_label, selectedIds.size(), + selectedIds.size(), getItemCount())); + } + + public void setOnEndSelectModeListener(OnEndSelectModeListener onEndSelectModeListener) { + this.onEndSelectModeListener = onEndSelectModeListener; + } + + private void callOnEndSelectMode() { + if (onEndSelectModeListener != null) { + onEndSelectModeListener.onEndSelectMode(); + } + } + + public interface OnEndSelectModeListener { + void onEndSelectMode(); + } +} |