summaryrefslogtreecommitdiff
path: root/app/src/main/java/de/danoeh/antennapod/adapter
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/de/danoeh/antennapod/adapter')
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java87
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SelectableAdapter.java168
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();
+ }
+}