From 1fa0c32142c0c288adf8784c6c032ae3fa4a6611 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sat, 7 May 2022 10:52:45 +0200 Subject: Multi-Select on 'All episodes' screen --- .../antennapod/fragment/EpisodesListFragment.java | 65 +++++++++++++++++++--- .../danoeh/antennapod/fragment/InboxFragment.java | 3 + app/src/main/res/layout/all_episodes_fragment.xml | 33 ++++++----- 3 files changed, 78 insertions(+), 23 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java index b658e5f08..d47544e6f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -22,12 +22,15 @@ import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; +import com.google.android.material.snackbar.Snackbar; +import com.leinardi.android.speeddial.SpeedDialView; import de.danoeh.antennapod.adapter.EpisodeItemListAdapter; import de.danoeh.antennapod.event.FeedListUpdateEvent; import de.danoeh.antennapod.event.playback.PlaybackPositionEvent; import de.danoeh.antennapod.event.PlayerStatusEvent; import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.core.menuhandler.MenuItemUtils; +import de.danoeh.antennapod.fragment.actions.EpisodeMultiSelectActionHandler; import de.danoeh.antennapod.ui.common.PagedToolbarFragment; import de.danoeh.antennapod.view.EpisodeItemListRecyclerView; import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder; @@ -59,7 +62,7 @@ import io.reactivex.schedulers.Schedulers; /** * Shows unread or recently published episodes */ -public abstract class EpisodesListFragment extends Fragment { +public abstract class EpisodesListFragment extends Fragment implements EpisodeItemListAdapter.OnSelectModeListener { public static final String TAG = "EpisodesListFragment"; protected static final int EPISODES_PER_PAGE = 150; @@ -72,6 +75,7 @@ public abstract class EpisodesListFragment extends Fragment { ProgressBar progLoading; View loadingMoreView; EmptyViewHandler emptyView; + SpeedDialView speedDialView; @NonNull List episodes = new ArrayList<>(); @@ -174,17 +178,13 @@ public abstract class EpisodesListFragment extends Fragment { // The method is called on all fragments in a ViewPager, so this needs to be ignored in invisible ones. // Apparently, none of the visibility check method works reliably on its own, so we just use all. return false; - } - if (item.getItemId() == R.id.share_item) { - return true; // avoids that the position is reset when we need it in the submenu - } - - if (listAdapter.getLongPressedItem() == null) { + } else if (listAdapter.getLongPressedItem() == null) { Log.i(TAG, "Selected item or listAdapter was null, ignoring selection"); return super.onContextItemSelected(item); + } else if (listAdapter.onContextItemSelected(item)) { + return true; } FeedItem selectedItem = listAdapter.getLongPressedItem(); - return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem); } @@ -225,6 +225,31 @@ public abstract class EpisodesListFragment extends Fragment { createRecycleAdapter(recyclerView, emptyView); emptyView.hide(); + speedDialView = root.findViewById(R.id.fabSD); + speedDialView.setOverlayLayout(root.findViewById(R.id.fabSDOverlay)); + speedDialView.inflate(R.menu.episodes_apply_action_speeddial); + speedDialView.setOnChangeListener(new SpeedDialView.OnChangeListener() { + @Override + public boolean onMainActionSelected() { + return false; + } + + @Override + public void onToggleChanged(boolean open) { + if (open && listAdapter.getSelectedCount() == 0) { + ((MainActivity) getActivity()).showSnackbarAbovePlayer(R.string.no_items_selected, + Snackbar.LENGTH_SHORT); + speedDialView.close(); + } + } + }); + speedDialView.setOnActionSelectedListener(actionItem -> { + new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), listAdapter.getSelectedItems()) + .handleAction(actionItem.getId()); + listAdapter.endSelectMode(); + return true; + }); + return root; } @@ -292,14 +317,38 @@ public abstract class EpisodesListFragment extends Fragment { @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); + if (!inActionMode()) { + menu.findItem(R.id.multi_select).setVisible(true); + } MenuItemUtils.setOnClickListeners(menu, EpisodesListFragment.this::onContextItemSelected); } }; + listAdapter.setOnSelectModeListener(this); listAdapter.updateItems(episodes); recyclerView.setAdapter(listAdapter); emptyViewHandler.updateAdapter(listAdapter); } + @Override + public void onDestroyView() { + super.onDestroyView(); + if (listAdapter != null) { + listAdapter.endSelectMode(); + } + listAdapter = null; + } + + @Override + public void onStartSelectMode() { + speedDialView.setVisibility(View.VISIBLE); + } + + @Override + public void onEndSelectMode() { + speedDialView.close(); + speedDialView.setVisibility(View.GONE); + } + @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(FeedItemEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java index abb04b2f3..5063da9a4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java @@ -67,6 +67,9 @@ public class InboxFragment extends EpisodesListFragment implements Toolbar.OnMen SwipeActions swipeActions = new SwipeActions(this, TAG).attachTo(recyclerView); swipeActions.setFilter(new FeedItemFilter(FeedItemFilter.NEW)); + speedDialView.removeActionItemById(R.id.mark_unread_batch); + speedDialView.removeActionItemById(R.id.remove_from_queue_batch); + speedDialView.removeActionItemById(R.id.delete_batch); return inboxContainer; } diff --git a/app/src/main/res/layout/all_episodes_fragment.xml b/app/src/main/res/layout/all_episodes_fragment.xml index 3b560967c..c1e7e6434 100644 --- a/app/src/main/res/layout/all_episodes_fragment.xml +++ b/app/src/main/res/layout/all_episodes_fragment.xml @@ -2,9 +2,9 @@ + android:layout_height="match_parent" + android:orientation="vertical"> - + @@ -45,7 +45,7 @@ android:indeterminateOnly="true" android:visibility="gone" android:layout_centerInParent="true" - tools:background="@android:color/holo_red_light"/> + tools:background="@android:color/holo_red_light" /> - \ No newline at end of file + + + -- cgit v1.2.3 From 7c0d084ffcc6a17ccac043a76f6640c89dea6272 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sun, 8 May 2022 10:21:39 +0200 Subject: Automatically select lazy loaded items if necessary --- .../antennapod/adapter/EpisodeItemListAdapter.java | 2 ++ .../de/danoeh/antennapod/adapter/SelectableAdapter.java | 16 ++++++++++++---- .../danoeh/antennapod/fragment/EpisodesListFragment.java | 3 +++ 3 files changed, 17 insertions(+), 4 deletions(-) (limited to 'app/src/main') 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 53223896f..8fb4a6314 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java @@ -48,6 +48,7 @@ public class EpisodeItemListAdapter extends SelectableAdapter items) { episodes = items; notifyDataSetChanged(); + updateTitle(); } @Override @@ -195,6 +196,7 @@ public class EpisodeItemListAdapter extends SelectableAdapter extends Recy private final HashSet selectedIds = new HashSet<>(); private final Activity activity; private OnSelectModeListener onSelectModeListener; + boolean shouldSelectLazyLoadedItems = false; public SelectableAdapter(Activity activity) { this.activity = activity; @@ -34,6 +35,7 @@ abstract class SelectableAdapter extends Recy onSelectModeListener.onStartSelectMode(); } + shouldSelectLazyLoadedItems = false; selectedIds.clear(); selectedIds.add(getItemId(pos)); notifyDataSetChanged(); @@ -56,9 +58,10 @@ abstract class SelectableAdapter extends Recy @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); + boolean selectAll = selectedIds.size() != getItemCount(); + shouldSelectLazyLoadedItems = selectAll; + setSelected(0, getItemCount(), selectAll); + toggleSelectAllIcon(item, selectAll); updateTitle(); return true; } @@ -69,6 +72,7 @@ abstract class SelectableAdapter extends Recy public void onDestroyActionMode(ActionMode mode) { callOnEndSelectMode(); actionMode = null; + shouldSelectLazyLoadedItems = false; selectedIds.clear(); notifyDataSetChanged(); } @@ -147,7 +151,7 @@ abstract class SelectableAdapter extends Recy } } - private void updateTitle() { + void updateTitle() { if (actionMode == null) { return; } @@ -166,6 +170,10 @@ abstract class SelectableAdapter extends Recy } } + public boolean shouldSelectLazyLoadedItems() { + return shouldSelectLazyLoadedItems; + } + public interface OnSelectModeListener { void onStartSelectMode(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java index d47544e6f..55134cbfa 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -283,6 +283,9 @@ public abstract class EpisodesListFragment extends Fragment implements EpisodeIt } episodes.addAll(data); onFragmentLoaded(episodes); + if (listAdapter.shouldSelectLazyLoadedItems()) { + listAdapter.setSelected(episodes.size() - data.size(), episodes.size(), true); + } }, error -> Log.e(TAG, Log.getStackTraceString(error)), () -> { recyclerView.post(() -> isLoadingMore = false); // Make sure to not always load 2 pages at once -- cgit v1.2.3 From 03220d07748d0908c4cf4e6b260ce828dc5ebaac Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sun, 8 May 2022 10:55:05 +0200 Subject: Show correct total number for lazy loaded items --- .../antennapod/adapter/EpisodeItemListAdapter.java | 2 +- .../antennapod/adapter/SelectableAdapter.java | 22 ++++++++++++++++++++-- .../antennapod/fragment/AllEpisodesFragment.java | 5 +++++ .../antennapod/fragment/EpisodesListFragment.java | 14 ++++++++++++-- .../fragment/FavoriteEpisodesFragment.java | 6 ++++++ .../danoeh/antennapod/fragment/InboxFragment.java | 5 +++++ 6 files changed, 49 insertions(+), 5 deletions(-) (limited to 'app/src/main') 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 8fb4a6314..088caf70a 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java @@ -195,8 +195,8 @@ public class EpisodeItemListAdapter extends SelectableAdapter extends RecyclerView.Adapter { +public abstract class SelectableAdapter extends RecyclerView.Adapter { + public static final int COUNT_AUTOMATICALLY = -1; private ActionMode actionMode; private final HashSet selectedIds = new HashSet<>(); private final Activity activity; private OnSelectModeListener onSelectModeListener; boolean shouldSelectLazyLoadedItems = false; + private int totalNumberOfItems = COUNT_AUTOMATICALLY; public SelectableAdapter(Activity activity) { this.activity = activity; @@ -155,9 +157,17 @@ abstract class SelectableAdapter extends Recy if (actionMode == null) { return; } + int totalCount = getItemCount(); + int selectedCount = selectedIds.size(); + if (totalNumberOfItems != COUNT_AUTOMATICALLY) { + totalCount = totalNumberOfItems; + if (shouldSelectLazyLoadedItems) { + selectedCount += (totalNumberOfItems - getItemCount()); + } + } actionMode.setTitle(activity.getResources() .getQuantityString(R.plurals.num_selected_label, selectedIds.size(), - selectedIds.size(), getItemCount())); + selectedCount, totalCount)); } public void setOnSelectModeListener(OnSelectModeListener onSelectModeListener) { @@ -174,6 +184,14 @@ abstract class SelectableAdapter extends Recy return shouldSelectLazyLoadedItems; } + /** + * Sets the total number of items that could be lazy-loaded. + * Can also be set to {@link #COUNT_AUTOMATICALLY} to simply use {@link #getItemCount} + */ + public void setTotalNumberOfItems(int totalNumberOfItems) { + this.totalNumberOfItems = totalNumberOfItems; + } + public interface OnSelectModeListener { void onStartSelectMode(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index 853e7d6f7..2627da3d9 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -105,4 +105,9 @@ public class AllEpisodesFragment extends EpisodesListFragment { protected List loadMoreData() { return DBReader.getRecentlyPublishedEpisodes((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE, feedItemFilter); } + + @Override + protected int loadTotalItemCount() { + return DBReader.getTotalEpisodeCount(feedItemFilter); + } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java index 55134cbfa..e47599e3b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -5,6 +5,7 @@ import android.os.Bundle; import android.view.ContextMenu; import android.view.KeyEvent; import androidx.annotation.NonNull; +import androidx.core.util.Pair; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.SimpleItemAnimator; @@ -25,6 +26,7 @@ import android.widget.Toast; import com.google.android.material.snackbar.Snackbar; import com.leinardi.android.speeddial.SpeedDialView; import de.danoeh.antennapod.adapter.EpisodeItemListAdapter; +import de.danoeh.antennapod.adapter.SelectableAdapter; import de.danoeh.antennapod.event.FeedListUpdateEvent; import de.danoeh.antennapod.event.playback.PlaybackPositionEvent; import de.danoeh.antennapod.event.PlayerStatusEvent; @@ -447,14 +449,15 @@ public abstract class EpisodesListFragment extends Fragment implements EpisodeIt if (disposable != null) { disposable.dispose(); } - disposable = Observable.fromCallable(this::loadData) + disposable = Observable.fromCallable(() -> new Pair<>(loadData(), loadTotalItemCount())) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(data -> { progLoading.setVisibility(View.GONE); loadingMoreView.setVisibility(View.GONE); hasMoreItems = true; - episodes = data; + episodes = data.first; + listAdapter.setTotalNumberOfItems(data.second); onFragmentLoaded(episodes); if (getParentFragment() instanceof PagedToolbarFragment) { ((PagedToolbarFragment) getParentFragment()).invalidateOptionsMenuIfActive(this); @@ -475,4 +478,11 @@ public abstract class EpisodesListFragment extends Fragment implements EpisodeIt */ @NonNull protected abstract List loadMoreData(); + + /** + * Returns the total number of items that would be returned if {@link #loadMoreData} was called often enough. + */ + protected int loadTotalItemCount() { + return SelectableAdapter.COUNT_AUTOMATICALLY; + } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java index 33aba3b54..56b2b433f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java @@ -12,6 +12,7 @@ import android.view.View; import android.view.ViewGroup; import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.model.feed.FeedItemFilter; import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder; import org.greenrobot.eventbus.Subscribe; @@ -101,4 +102,9 @@ public class FavoriteEpisodesFragment extends EpisodesListFragment { protected List loadMoreData() { return DBReader.getFavoriteItemsList((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE); } + + @Override + protected int loadTotalItemCount() { + return DBReader.getTotalEpisodeCount(new FeedItemFilter(FeedItemFilter.IS_FAVORITE)); + } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java index 5063da9a4..14bfe2951 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java @@ -119,4 +119,9 @@ public class InboxFragment extends EpisodesListFragment implements Toolbar.OnMen protected List loadMoreData() { return DBReader.getNewItemsList((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE); } + + @Override + protected int loadTotalItemCount() { + return DBReader.getTotalEpisodeCount(new FeedItemFilter(FeedItemFilter.NEW)); + } } -- cgit v1.2.3 From 072e172996ea82af8ac56880ad56714197d3fe7c Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sun, 8 May 2022 11:08:34 +0200 Subject: Switch around arguments in MultiSelectActionHandler --- .../fragment/CompletedDownloadsFragment.java | 4 +- .../antennapod/fragment/EpisodesListFragment.java | 4 +- .../antennapod/fragment/FeedItemlistFragment.java | 4 +- .../danoeh/antennapod/fragment/QueueFragment.java | 4 +- .../actions/EpisodeMultiSelectActionHandler.java | 79 ++++++++++++---------- 5 files changed, 51 insertions(+), 44 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java index 22a7a877c..cab7ba452 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java @@ -115,8 +115,8 @@ public class CompletedDownloadsFragment extends Fragment } }); speedDialView.setOnActionSelectedListener(actionItem -> { - new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), adapter.getSelectedItems()) - .handleAction(actionItem.getId()); + new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), actionItem.getId()) + .handleAction(adapter.getSelectedItems()); adapter.endSelectMode(); return true; }); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java index e47599e3b..06c8148e5 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -246,8 +246,8 @@ public abstract class EpisodesListFragment extends Fragment implements EpisodeIt } }); speedDialView.setOnActionSelectedListener(actionItem -> { - new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), listAdapter.getSelectedItems()) - .handleAction(actionItem.getId()); + new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), actionItem.getId()) + .handleAction(listAdapter.getSelectedItems()); listAdapter.endSelectMode(); return true; }); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java index a42abd724..80a65e518 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java @@ -197,8 +197,8 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem } }); speedDialBinding.fabSD.setOnActionSelectedListener(actionItem -> { - new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), adapter.getSelectedItems()) - .handleAction(actionItem.getId()); + new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), actionItem.getId()) + .handleAction(adapter.getSelectedItems()); adapter.endSelectMode(); return true; }); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java index 2a89519a5..61f45f0f9 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -505,8 +505,8 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi } }); speedDialView.setOnActionSelectedListener(actionItem -> { - new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), recyclerAdapter.getSelectedItems()) - .handleAction(actionItem.getId()); + new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), actionItem.getId()) + .handleAction(recyclerAdapter.getSelectedItems()); recyclerAdapter.endSelectMode(); return true; }); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java b/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java index 17642874e..aad611e5d 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java @@ -21,35 +21,37 @@ import de.danoeh.antennapod.model.feed.FeedItem; public class EpisodeMultiSelectActionHandler { private static final String TAG = "EpisodeSelectHandler"; private final MainActivity activity; - private final List selectedItems; + private final int actionId; + private int totalNumItems = 0; + private Snackbar snackbar = null; - public EpisodeMultiSelectActionHandler(MainActivity activity, List selectedItems) { + public EpisodeMultiSelectActionHandler(MainActivity activity, int actionId) { this.activity = activity; - this.selectedItems = selectedItems; + this.actionId = actionId; } - public void handleAction(int id) { - if (id == R.id.add_to_queue_batch) { - queueChecked(); - } else if (id == R.id.remove_from_queue_batch) { - removeFromQueueChecked(); - } else if (id == R.id.mark_read_batch) { - markedCheckedPlayed(); - } else if (id == R.id.mark_unread_batch) { - markedCheckedUnplayed(); - } else if (id == R.id.download_batch) { - downloadChecked(); - } else if (id == R.id.delete_batch) { - deleteChecked(); + public void handleAction(List items) { + if (actionId == R.id.add_to_queue_batch) { + queueChecked(items); + } else if (actionId == R.id.remove_from_queue_batch) { + removeFromQueueChecked(items); + } else if (actionId == R.id.mark_read_batch) { + markedCheckedPlayed(items); + } else if (actionId == R.id.mark_unread_batch) { + markedCheckedUnplayed(items); + } else if (actionId == R.id.download_batch) { + downloadChecked(items); + } else if (actionId == R.id.delete_batch) { + deleteChecked(items); } else { - Log.e(TAG, "Unrecognized speed dial action item. Do nothing. id=" + id); + Log.e(TAG, "Unrecognized speed dial action item. Do nothing. id=" + actionId); } } - private void queueChecked() { + private void queueChecked(List items) { // Check if an episode actually contains any media files before adding it to queue - LongList toQueue = new LongList(selectedItems.size()); - for (FeedItem episode : selectedItems) { + LongList toQueue = new LongList(items.size()); + for (FeedItem episode : items) { if (episode.hasMedia()) { toQueue.add(episode.getId()); } @@ -58,28 +60,28 @@ public class EpisodeMultiSelectActionHandler { showMessage(R.plurals.added_to_queue_batch_label, toQueue.size()); } - private void removeFromQueueChecked() { - long[] checkedIds = getSelectedIds(); + private void removeFromQueueChecked(List items) { + long[] checkedIds = getSelectedIds(items); DBWriter.removeQueueItem(activity, true, checkedIds); showMessage(R.plurals.removed_from_queue_batch_label, checkedIds.length); } - private void markedCheckedPlayed() { - long[] checkedIds = getSelectedIds(); + private void markedCheckedPlayed(List items) { + long[] checkedIds = getSelectedIds(items); DBWriter.markItemPlayed(FeedItem.PLAYED, checkedIds); showMessage(R.plurals.marked_read_batch_label, checkedIds.length); } - private void markedCheckedUnplayed() { - long[] checkedIds = getSelectedIds(); + private void markedCheckedUnplayed(List items) { + long[] checkedIds = getSelectedIds(items); DBWriter.markItemPlayed(FeedItem.UNPLAYED, checkedIds); showMessage(R.plurals.marked_unread_batch_label, checkedIds.length); } - private void downloadChecked() { + private void downloadChecked(List items) { // download the check episodes in the same order as they are currently displayed List requests = new ArrayList<>(); - for (FeedItem episode : selectedItems) { + for (FeedItem episode : items) { if (episode.hasMedia() && !episode.getFeed().isLocalFeed()) { requests.add(DownloadRequestCreator.create(episode.getMedia()).build()); } @@ -88,9 +90,9 @@ public class EpisodeMultiSelectActionHandler { showMessage(R.plurals.downloading_batch_label, requests.size()); } - private void deleteChecked() { + private void deleteChecked(List items) { int countHasMedia = 0; - for (FeedItem feedItem : selectedItems) { + for (FeedItem feedItem : items) { if (feedItem.hasMedia() && feedItem.getMedia().isDownloaded()) { countHasMedia++; DBWriter.deleteFeedMediaOfItem(activity, feedItem.getMedia().getId()); @@ -100,14 +102,19 @@ public class EpisodeMultiSelectActionHandler { } private void showMessage(@PluralsRes int msgId, int numItems) { - activity.showSnackbarAbovePlayer(activity.getResources() - .getQuantityString(msgId, numItems, numItems), Snackbar.LENGTH_LONG); + totalNumItems += numItems; + String text = activity.getResources().getQuantityString(msgId, totalNumItems, totalNumItems); + if (snackbar != null) { + snackbar.setText(text); + } else { + snackbar = activity.showSnackbarAbovePlayer(text, Snackbar.LENGTH_LONG); + } } - private long[] getSelectedIds() { - long[] checkedIds = new long[selectedItems.size()]; - for (int i = 0; i < selectedItems.size(); ++i) { - checkedIds[i] = selectedItems.get(i).getId(); + private long[] getSelectedIds(List items) { + long[] checkedIds = new long[items.size()]; + for (int i = 0; i < items.size(); ++i) { + checkedIds[i] = items.get(i).getId(); } return checkedIds; } -- cgit v1.2.3 From 2e999aef6271664578f95a9cf2a365857525b4ac Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sun, 8 May 2022 11:18:11 +0200 Subject: Apply action to items that are not loaded yet --- .../antennapod/fragment/AllEpisodesFragment.java | 2 +- .../antennapod/fragment/EpisodesListFragment.java | 27 ++++++++++++++++++---- .../fragment/FavoriteEpisodesFragment.java | 2 +- .../danoeh/antennapod/fragment/InboxFragment.java | 2 +- .../actions/EpisodeMultiSelectActionHandler.java | 15 +++++++----- 5 files changed, 34 insertions(+), 14 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index 2627da3d9..51dcb664c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -102,7 +102,7 @@ public class AllEpisodesFragment extends EpisodesListFragment { @NonNull @Override - protected List loadMoreData() { + protected List loadMoreData(int page) { return DBReader.getRecentlyPublishedEpisodes((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE, feedItemFilter); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java index 06c8148e5..9409c7c3a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -36,6 +36,7 @@ import de.danoeh.antennapod.fragment.actions.EpisodeMultiSelectActionHandler; import de.danoeh.antennapod.ui.common.PagedToolbarFragment; import de.danoeh.antennapod.view.EpisodeItemListRecyclerView; import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder; +import io.reactivex.Completable; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -246,9 +247,25 @@ public abstract class EpisodesListFragment extends Fragment implements EpisodeIt } }); speedDialView.setOnActionSelectedListener(actionItem -> { - new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), actionItem.getId()) - .handleAction(listAdapter.getSelectedItems()); - listAdapter.endSelectMode(); + EpisodeMultiSelectActionHandler handler = + new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), actionItem.getId()); + Completable.fromAction( + () -> { + handler.handleAction(listAdapter.getSelectedItems()); + if (listAdapter.shouldSelectLazyLoadedItems()) { + int applyPage = page + 1; + List nextPage; + do { + nextPage = loadMoreData(applyPage); + handler.handleAction(nextPage); + applyPage++; + } while (nextPage.size() == EPISODES_PER_PAGE); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(() -> listAdapter.endSelectMode(), + error -> Log.e(TAG, Log.getStackTraceString(error))); return true; }); @@ -276,7 +293,7 @@ public abstract class EpisodesListFragment extends Fragment implements EpisodeIt } isLoadingMore = true; loadingMoreView.setVisibility(View.VISIBLE); - disposable = Observable.fromCallable(this::loadMoreData) + disposable = Observable.fromCallable(() -> loadMoreData(page)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(data -> { @@ -477,7 +494,7 @@ public abstract class EpisodesListFragment extends Fragment implements EpisodeIt * @return The items from the next page of data */ @NonNull - protected abstract List loadMoreData(); + protected abstract List loadMoreData(int page); /** * Returns the total number of items that would be returned if {@link #loadMoreData} was called often enough. diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java index 56b2b433f..cc45a7d35 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java @@ -99,7 +99,7 @@ public class FavoriteEpisodesFragment extends EpisodesListFragment { @NonNull @Override - protected List loadMoreData() { + protected List loadMoreData(int page) { return DBReader.getFavoriteItemsList((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java index 14bfe2951..067e7466c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java @@ -116,7 +116,7 @@ public class InboxFragment extends EpisodesListFragment implements Toolbar.OnMen @NonNull @Override - protected List loadMoreData() { + protected List loadMoreData(int page) { return DBReader.getNewItemsList((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java b/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java index aad611e5d..0dc416e0e 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/actions/EpisodeMultiSelectActionHandler.java @@ -103,12 +103,15 @@ public class EpisodeMultiSelectActionHandler { private void showMessage(@PluralsRes int msgId, int numItems) { totalNumItems += numItems; - String text = activity.getResources().getQuantityString(msgId, totalNumItems, totalNumItems); - if (snackbar != null) { - snackbar.setText(text); - } else { - snackbar = activity.showSnackbarAbovePlayer(text, Snackbar.LENGTH_LONG); - } + activity.runOnUiThread(() -> { + String text = activity.getResources().getQuantityString(msgId, totalNumItems, totalNumItems); + if (snackbar != null) { + snackbar.setText(text); + snackbar.show(); // Resets the timeout + } else { + snackbar = activity.showSnackbarAbovePlayer(text, Snackbar.LENGTH_LONG); + } + }); } private long[] getSelectedIds(List items) { -- cgit v1.2.3 From 934e2802f8a6f9552489726ef1a43f1476eda233 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sun, 8 May 2022 11:24:59 +0200 Subject: Remove 'mark all as read' button Now available through multi-select --- .../danoeh/antennapod/fragment/AllEpisodesFragment.java | 1 - .../danoeh/antennapod/fragment/EpisodesListFragment.java | 15 --------------- .../antennapod/fragment/FavoriteEpisodesFragment.java | 1 - .../de/danoeh/antennapod/menuhandler/FeedMenuHandler.java | 15 --------------- app/src/main/res/menu/episodes.xml | 8 -------- 5 files changed, 40 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index 51dcb664c..46a648e57 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -66,7 +66,6 @@ public class AllEpisodesFragment extends EpisodesListFragment { public void onPrepareOptionsMenu(@NonNull Menu menu) { super.onPrepareOptionsMenu(menu); menu.findItem(R.id.filter_items).setVisible(true); - menu.findItem(R.id.mark_all_read_item).setVisible(true); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java index 9409c7c3a..0f75c2fb7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -137,21 +137,6 @@ public abstract class EpisodesListFragment extends Fragment implements EpisodeIt if (itemId == R.id.refresh_item) { AutoUpdateManager.runImmediate(requireContext()); return true; - } else if (itemId == R.id.mark_all_read_item) { - ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(), - R.string.mark_all_read_label, - R.string.mark_all_read_confirmation_msg) { - - @Override - public void onConfirmButtonPressed(DialogInterface dialog) { - dialog.dismiss(); - DBWriter.markAllItemsRead(); - ((MainActivity) getActivity()).showSnackbarAbovePlayer( - R.string.mark_all_read_msg, Toast.LENGTH_SHORT); - } - }; - markAllReadConfirmationDialog.createNewDialog().show(); - return true; } else if (itemId == R.id.remove_all_inbox_item) { ConfirmationDialog removeAllNewFlagsConfirmationDialog = new ConfirmationDialog(getActivity(), R.string.remove_all_inbox_label, diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java index cc45a7d35..30eec8780 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java @@ -48,7 +48,6 @@ public class FavoriteEpisodesFragment extends EpisodesListFragment { public void onPrepareOptionsMenu(@NonNull Menu menu) { super.onPrepareOptionsMenu(menu); menu.findItem(R.id.filter_items).setVisible(false); - menu.findItem(R.id.mark_all_read_item).setVisible(false); } @NonNull diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java index bfe269caa..de6056063 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java @@ -1,13 +1,11 @@ package de.danoeh.antennapod.menuhandler; import android.content.Context; -import android.content.DialogInterface; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import androidx.annotation.NonNull; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.IntentUtils; @@ -56,19 +54,6 @@ public class FeedMenuHandler { DBTasks.forceRefreshCompleteFeed(context, selectedFeed); } else if (itemId == R.id.sort_items) { showSortDialog(context, selectedFeed); - } else if (itemId == R.id.mark_all_read_item) { - ConfirmationDialog conDialog = new ConfirmationDialog(context, - R.string.mark_all_read_label, - R.string.mark_all_read_feed_confirmation_msg) { - - @Override - public void onConfirmButtonPressed( - DialogInterface dialog) { - dialog.dismiss(); - DBWriter.markFeedRead(selectedFeed.getId()); - } - }; - conDialog.createNewDialog().show(); } else if (itemId == R.id.visit_website_item) { IntentUtils.openInBrowser(context, selectedFeed.getLink()); } else if (itemId == R.id.share_item) { diff --git a/app/src/main/res/menu/episodes.xml b/app/src/main/res/menu/episodes.xml index 75f7df861..4e6da923b 100644 --- a/app/src/main/res/menu/episodes.xml +++ b/app/src/main/res/menu/episodes.xml @@ -24,12 +24,4 @@ android:visible="false" custom:showAsAction="ifRoom"/> - - -- cgit v1.2.3 From 850529856a0dfdab701b6720f35f15ee13a090c2 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sun, 8 May 2022 11:46:21 +0200 Subject: Confirm mark as played/unplayed --- .../antennapod/fragment/EpisodesListFragment.java | 60 +++++++++++++++------- 1 file changed, 41 insertions(+), 19 deletions(-) (limited to 'app/src/main') diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java index 0f75c2fb7..03dbc6ae4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -232,31 +232,53 @@ public abstract class EpisodesListFragment extends Fragment implements EpisodeIt } }); speedDialView.setOnActionSelectedListener(actionItem -> { - EpisodeMultiSelectActionHandler handler = - new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), actionItem.getId()); - Completable.fromAction( - () -> { - handler.handleAction(listAdapter.getSelectedItems()); - if (listAdapter.shouldSelectLazyLoadedItems()) { - int applyPage = page + 1; - List nextPage; - do { - nextPage = loadMoreData(applyPage); - handler.handleAction(nextPage); - applyPage++; - } while (nextPage.size() == EPISODES_PER_PAGE); - } - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(() -> listAdapter.endSelectMode(), - error -> Log.e(TAG, Log.getStackTraceString(error))); + int confirmationString = 0; + if (listAdapter.getSelectedItems().size() >= 25 || listAdapter.shouldSelectLazyLoadedItems()) { + // Should ask for confirmation + if (actionItem.getId() == R.id.mark_read_batch) { + confirmationString = R.string.multi_select_mark_played_confirmation; + } else if (actionItem.getId() == R.id.mark_unread_batch) { + confirmationString = R.string.multi_select_mark_unplayed_confirmation; + } + } + if (confirmationString == 0) { + performMultiSelectAction(actionItem.getId()); + } else { + new ConfirmationDialog(getActivity(), R.string.multi_select, confirmationString) { + @Override + public void onConfirmButtonPressed(DialogInterface dialog) { + performMultiSelectAction(actionItem.getId()); + } + }.createNewDialog().show(); + } return true; }); return root; } + private void performMultiSelectAction(int actionItemId) { + EpisodeMultiSelectActionHandler handler = + new EpisodeMultiSelectActionHandler(((MainActivity) getActivity()), actionItemId); + Completable.fromAction( + () -> { + handler.handleAction(listAdapter.getSelectedItems()); + if (listAdapter.shouldSelectLazyLoadedItems()) { + int applyPage = page + 1; + List nextPage; + do { + nextPage = loadMoreData(applyPage); + handler.handleAction(nextPage); + applyPage++; + } while (nextPage.size() == EPISODES_PER_PAGE); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(() -> listAdapter.endSelectMode(), + error -> Log.e(TAG, Log.getStackTraceString(error))); + } + private void setupLoadMoreScrollListener() { recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override -- cgit v1.2.3