diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java index 4ebfcff5b..385360790 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java @@ -52,6 +52,7 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter listItems; private NavDrawerData.DrawerItem selectedItem = null; int longPressedPosition = 0; // used to init actionMode + private int dummyViews = 0; public SubscriptionsRecyclerAdapter(MainActivity mainActivity) { super(mainActivity); @@ -95,6 +96,11 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter= listItems.size()) { + holder.selectView.setVisibility(View.GONE); + holder.bindDummy(); + return; + } NavDrawerData.DrawerItem drawerItem = listItems.get(position); boolean isFeed = drawerItem.type == NavDrawerData.DrawerItem.Type.FEED; holder.bind(drawerItem); @@ -157,11 +163,14 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter= listItems.size()) { + return RecyclerView.NO_ID; // Dummy views + } return listItems.get(position).id; } @@ -202,6 +211,10 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter listItems) { this.listItems = listItems; } @@ -270,6 +283,17 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter items = new ArrayList<>(); private CompletedDownloadsListAdapter adapter; private EpisodeItemListRecyclerView recyclerView; - private ProgressBar progressBar; private Disposable disposable; private EmptyViewHandler emptyView; private boolean displayUpArrow; @@ -91,11 +92,12 @@ public class CompletedDownloadsFragment extends Fragment recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool()); adapter = new CompletedDownloadsListAdapter((MainActivity) getActivity()); adapter.setOnSelectModeListener(this); + int previousEpisodesCount = getContext().getSharedPreferences(TAG, Context.MODE_PRIVATE) + .getInt(PREF_PREVIOUS_EPISODE_COUNT, 5); + adapter.setDummyViews(Math.max(1, previousEpisodesCount)); recyclerView.setAdapter(adapter); swipeActions = new SwipeActions(this, TAG).attachTo(recyclerView); swipeActions.setFilter(new FeedItemFilter(FeedItemFilter.DOWNLOADED)); - progressBar = root.findViewById(R.id.progLoading); - progressBar.setVisibility(View.VISIBLE); speedDialView = root.findViewById(R.id.fabSD); speedDialView.setOverlayLayout(root.findViewById(R.id.fabSDOverlay)); @@ -159,6 +161,9 @@ public class CompletedDownloadsFragment extends Fragment if (disposable != null) { disposable.dispose(); } + getContext().getSharedPreferences(TAG, Context.MODE_PRIVATE).edit() + .putInt(PREF_PREVIOUS_EPISODE_COUNT, adapter.getItemCount()) + .apply(); } @Override @@ -291,11 +296,16 @@ public class CompletedDownloadsFragment extends Fragment }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> { - items = result; - adapter.updateItems(result); - progressBar.setVisibility(View.GONE); - }, error -> Log.e(TAG, Log.getStackTraceString(error))); + .subscribe( + result -> { + items = result; + adapter.setDummyViews(0); + adapter.updateItems(result); + }, error -> { + adapter.setDummyViews(0); + adapter.updateItems(Collections.emptyList()); + Log.e(TAG, Log.getStackTraceString(error)); + }); } @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 03764f61c..d8b05d207 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.fragment; +import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.os.Handler; @@ -11,7 +12,6 @@ import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.ProgressBar; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.widget.Toolbar; @@ -56,6 +56,7 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -65,16 +66,15 @@ public abstract class EpisodesListFragment extends Fragment implements EpisodeItemListAdapter.OnSelectModeListener, Toolbar.OnMenuItemClickListener { public static final String TAG = "EpisodesListFragment"; private static final String KEY_UP_ARROW = "up_arrow"; + private static final String PREF_PREVIOUS_EPISODE_COUNT = "episodeCount"; protected static final int EPISODES_PER_PAGE = 150; protected int page = 1; protected boolean isLoadingMore = false; - protected boolean hasMoreItems = true; + protected boolean hasMoreItems = false; private boolean displayUpArrow; EpisodeItemListRecyclerView recyclerView; EpisodeItemListAdapter listAdapter; - ProgressBar progLoading; - View loadingMoreView; EmptyViewHandler emptyView; SpeedDialView speedDialView; Toolbar toolbar; @@ -113,6 +113,9 @@ public abstract class EpisodesListFragment extends Fragment if (disposable != null) { disposable.dispose(); } + getContext().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE).edit() + .putInt(PREF_PREVIOUS_EPISODE_COUNT, episodes.size()) + .apply(); } @Override @@ -187,17 +190,28 @@ public abstract class EpisodesListFragment extends Fragment getResources().getInteger(R.integer.swipe_to_refresh_duration_in_ms)); }); - progLoading = root.findViewById(R.id.progLoading); - progLoading.setVisibility(View.VISIBLE); - loadingMoreView = root.findViewById(R.id.loadingMore); + listAdapter = new EpisodeItemListAdapter((MainActivity) getActivity()) { + @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); + int previousEpisodesCount = getContext().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE) + .getInt(PREF_PREVIOUS_EPISODE_COUNT, 5); + listAdapter.setDummyViews(Math.max(1, previousEpisodesCount)); + recyclerView.setAdapter(listAdapter); emptyView = new EmptyViewHandler(getContext()); emptyView.attachToRecyclerView(recyclerView); emptyView.setIcon(R.drawable.ic_feed); emptyView.setTitle(R.string.no_all_episodes_head_label); emptyView.setMessage(R.string.no_all_episodes_label); - - createRecycleAdapter(recyclerView, emptyView); + emptyView.updateAdapter(listAdapter); emptyView.hide(); speedDialView = root.findViewById(R.id.fabSD); @@ -286,68 +300,36 @@ public abstract class EpisodesListFragment extends Fragment disposable.dispose(); } isLoadingMore = true; - loadingMoreView.setVisibility(View.VISIBLE); + listAdapter.setDummyViews(1); + listAdapter.notifyItemInserted(listAdapter.getItemCount() - 1); disposable = Observable.fromCallable(() -> loadMoreData(page)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(data -> { - if (data.size() < EPISODES_PER_PAGE) { - hasMoreItems = false; - } - episodes.addAll(data); - updateAdapterWithNewItems(); - 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 - progLoading.setVisibility(View.GONE); - loadingMoreView.setVisibility(View.GONE); - }); - } - - protected void updateAdapterWithNewItems() { - boolean restoreScrollPosition = listAdapter.getItemCount() == 0; - if (episodes.size() == 0) { - createRecycleAdapter(recyclerView, emptyView); - } else { - listAdapter.updateItems(episodes); - } - if (restoreScrollPosition) { - recyclerView.restoreScrollPosition(getPrefName()); - } - } - - /** - * Currently, we need to recreate the list adapter in order to be able to undo last item via the - * snackbar. See #3084 for details. - */ - private void createRecycleAdapter(RecyclerView recyclerView, EmptyViewHandler emptyViewHandler) { - MainActivity mainActivity = (MainActivity) getActivity(); - listAdapter = new EpisodeItemListAdapter(mainActivity) { - @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); + .subscribe( + data -> { + if (data.size() < EPISODES_PER_PAGE) { + hasMoreItems = false; + } + episodes.addAll(data); + listAdapter.setDummyViews(0); + listAdapter.updateItems(episodes); + if (listAdapter.shouldSelectLazyLoadedItems()) { + listAdapter.setSelected(episodes.size() - data.size(), episodes.size(), true); + } + }, error -> { + listAdapter.setDummyViews(0); + listAdapter.updateItems(Collections.emptyList()); + Log.e(TAG, Log.getStackTraceString(error)); + }, () -> { + // Make sure to not always load 2 pages at once + recyclerView.post(() -> isLoadingMore = false); + }); } @Override public void onDestroyView() { super.onDestroyView(); - if (listAdapter != null) { - listAdapter.endSelectMode(); - } - listAdapter = null; + listAdapter.endSelectMode(); } @Override @@ -380,13 +362,11 @@ public abstract class EpisodesListFragment extends Fragment @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(PlaybackPositionEvent event) { - if (listAdapter != null) { - for (int i = 0; i < listAdapter.getItemCount(); i++) { - EpisodeItemViewHolder holder = (EpisodeItemViewHolder) recyclerView.findViewHolderForAdapterPosition(i); - if (holder != null && holder.isCurrentlyPlayingItem()) { - holder.notifyPlaybackPositionUpdated(event); - break; - } + for (int i = 0; i < listAdapter.getItemCount(); i++) { + EpisodeItemViewHolder holder = (EpisodeItemViewHolder) recyclerView.findViewHolderForAdapterPosition(i); + if (holder != null && holder.isCurrentlyPlayingItem()) { + holder.notifyPlaybackPositionUpdated(event); + break; } } } @@ -445,16 +425,20 @@ public abstract class EpisodesListFragment extends Fragment 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.first; - listAdapter.notifyDataSetChanged(); - listAdapter.setTotalNumberOfItems(data.second); - updateAdapterWithNewItems(); - updateToolbar(); - }, error -> Log.e(TAG, Log.getStackTraceString(error))); + .subscribe( + data -> { + episodes = data.first; + hasMoreItems = !(page == 1 && episodes.size() < EPISODES_PER_PAGE); + listAdapter.setDummyViews(0); + listAdapter.updateItems(episodes); + listAdapter.setTotalNumberOfItems(data.second); + recyclerView.restoreScrollPosition(getPrefName()); + updateToolbar(); + }, error -> { + listAdapter.setDummyViews(0); + listAdapter.updateItems(Collections.emptyList()); + Log.e(TAG, Log.getStackTraceString(error)); + }); } @NonNull 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 dfae22491..bf8a3e958 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java @@ -74,6 +74,7 @@ import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import java.util.Collections; import java.util.List; /** @@ -142,7 +143,12 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem updateToolbar(); viewBinding.recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool()); - viewBinding.progLoading.setVisibility(View.VISIBLE); + adapter = new FeedItemListAdapter((MainActivity) getActivity()); + adapter.setOnSelectModeListener(this); + adapter.setDummyViews(10); + viewBinding.recyclerView.setAdapter(adapter); + swipeActions = new SwipeActions(this, TAG).attachTo(viewBinding.recyclerView); + ToolbarIconTintManager iconTintManager = new ToolbarIconTintManager( getContext(), viewBinding.toolbar, viewBinding.collapsingToolbar) { @Override @@ -221,10 +227,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem if (disposable != null) { disposable.dispose(); } - if (adapter != null) { - adapter.endSelectMode(); - } - adapter = null; + adapter.endSelectMode(); } @Override @@ -294,9 +297,6 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem @Override public void onItemClick(AdapterView parent, View view, int position, long id) { - if (adapter == null) { - return; - } MainActivity activity = (MainActivity) getActivity(); long[] ids = FeedItemUtil.getIds(feed.getItems()); activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position)); @@ -315,9 +315,6 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); if (feed == null || feed.getItems() == null) { return; - } else if (adapter == null) { - loadItems(); - return; } for (int i = 0, size = event.items.size(); i < size; i++) { FeedItem item = event.items.get(i); @@ -335,7 +332,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); DownloaderUpdate update = event.update; updateToolbar(); - if (adapter != null && update.mediaIds.length > 0 && feed != null) { + if (update.mediaIds.length > 0 && feed != null) { for (long mediaId : update.mediaIds) { int pos = FeedItemUtil.indexOfItemWithMediaId(feed.getItems(), mediaId); if (pos >= 0) { @@ -347,14 +344,12 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(PlaybackPositionEvent event) { - if (adapter != null) { - for (int i = 0; i < adapter.getItemCount(); i++) { - EpisodeItemViewHolder holder = (EpisodeItemViewHolder) - viewBinding.recyclerView.findViewHolderForAdapterPosition(i); - if (holder != null && holder.isCurrentlyPlayingItem()) { - holder.notifyPlaybackPositionUpdated(event); - break; - } + for (int i = 0; i < adapter.getItemCount(); i++) { + EpisodeItemViewHolder holder = (EpisodeItemViewHolder) + viewBinding.recyclerView.findViewHolderForAdapterPosition(i); + if (holder != null && holder.isCurrentlyPlayingItem()) { + holder.notifyPlaybackPositionUpdated(event); + break; } } } @@ -417,28 +412,6 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem nextPageLoader.setLoadingState(DownloadService.isDownloadingFeeds()); } - private void displayList() { - if (getView() == null) { - Log.e(TAG, "Required root view is not yet created. Stop binding data to UI."); - return; - } - if (adapter == null) { - viewBinding.recyclerView.setAdapter(null); - adapter = new FeedItemListAdapter((MainActivity) getActivity()); - adapter.setOnSelectModeListener(this); - viewBinding.recyclerView.setAdapter(adapter); - swipeActions = new SwipeActions(this, TAG).attachTo(viewBinding.recyclerView); - } - viewBinding.progLoading.setVisibility(View.GONE); - if (feed != null) { - adapter.updateItems(feed.getItems()); - swipeActions.setFilter(feed.getItemFilter()); - } - - updateToolbar(); - updateSyncProgressBarVisibility(); - } - private void refreshHeaderView() { setupHeaderView(); if (viewBinding == null || feed == null) { @@ -557,12 +530,19 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem .subscribe( result -> { feed = result; + swipeActions.setFilter(feed.getItemFilter()); refreshHeaderView(); - displayList(); + adapter.setDummyViews(0); + adapter.updateItems(feed.getItems()); + updateToolbar(); + updateSyncProgressBarVisibility(); }, error -> { feed = null; refreshHeaderView(); - displayList(); + adapter.setDummyViews(0); + adapter.updateItems(Collections.emptyList()); + updateToolbar(); + updateSyncProgressBarVisibility(); Log.e(TAG, Log.getStackTraceString(error)); }); } 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 467299e4c..713cb594f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -14,7 +14,6 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; -import android.widget.ProgressBar; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; @@ -24,10 +23,8 @@ import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.SimpleItemAnimator; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; - import com.google.android.material.snackbar.Snackbar; import com.leinardi.android.speeddial.SpeedDialView; - import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.EpisodeItemListAdapter; @@ -35,26 +32,26 @@ import de.danoeh.antennapod.adapter.QueueRecyclerAdapter; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.core.event.DownloaderUpdate; -import de.danoeh.antennapod.event.FeedItemEvent; -import de.danoeh.antennapod.event.playback.PlaybackPositionEvent; -import de.danoeh.antennapod.event.PlayerStatusEvent; -import de.danoeh.antennapod.event.QueueEvent; -import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; -import de.danoeh.antennapod.core.menuhandler.MenuItemUtils; -import de.danoeh.antennapod.fragment.actions.EpisodeMultiSelectActionHandler; -import de.danoeh.antennapod.fragment.swipeactions.SwipeActions; -import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils; +import de.danoeh.antennapod.core.menuhandler.MenuItemUtils; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.FeedItemUtil; +import de.danoeh.antennapod.core.util.download.AutoUpdateManager; +import de.danoeh.antennapod.event.FeedItemEvent; +import de.danoeh.antennapod.event.PlayerStatusEvent; +import de.danoeh.antennapod.event.QueueEvent; +import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; +import de.danoeh.antennapod.event.playback.PlaybackPositionEvent; +import de.danoeh.antennapod.fragment.actions.EpisodeMultiSelectActionHandler; +import de.danoeh.antennapod.fragment.swipeactions.SwipeActions; +import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; +import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedItemFilter; import de.danoeh.antennapod.model.feed.SortOrder; -import de.danoeh.antennapod.core.util.download.AutoUpdateManager; -import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.view.EmptyViewHandler; import de.danoeh.antennapod.view.EpisodeItemListRecyclerView; import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder; @@ -81,7 +78,6 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi private EpisodeItemListRecyclerView recyclerView; private QueueRecyclerAdapter recyclerAdapter; private EmptyViewHandler emptyView; - private ProgressBar progLoading; private Toolbar toolbar; private boolean displayUpArrow; @@ -89,6 +85,7 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi private static final String PREFS = "QueueFragment"; private static final String PREF_SHOW_LOCK_WARNING = "show_lock_warning"; + private static final String PREF_PREVIOUS_EPISODE_COUNT = "episodeCount"; private Disposable disposable; private SwipeActions swipeActions; @@ -107,7 +104,7 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi public void onStart() { super.onStart(); if (queue != null) { - onFragmentLoaded(true); + recyclerView.restoreScrollPosition(QueueFragment.TAG); } loadItems(true); EventBus.getDefault().register(this); @@ -126,6 +123,7 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi if (disposable != null) { disposable.dispose(); } + prefs.edit().putInt(PREF_PREVIOUS_EPISODE_COUNT, queue.size()).apply(); } @Subscribe(threadMode = ThreadMode.MAIN) @@ -161,7 +159,7 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi return; } recyclerView.saveScrollPosition(QueueFragment.TAG); - onFragmentLoaded(false); + refreshInfoBar(); } @Subscribe(threadMode = ThreadMode.MAIN) @@ -456,6 +454,17 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool()); registerForContextMenu(recyclerView); + recyclerAdapter = new QueueRecyclerAdapter((MainActivity) getActivity(), swipeActions) { + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + MenuItemUtils.setOnClickListeners(menu, QueueFragment.this::onContextItemSelected); + } + }; + recyclerAdapter.setOnSelectModeListener(this); + recyclerAdapter.setDummyViews(Math.max(1, prefs.getInt(PREF_PREVIOUS_EPISODE_COUNT, 5))); + recyclerView.setAdapter(recyclerAdapter); + SwipeRefreshLayout swipeRefreshLayout = root.findViewById(R.id.swipeRefresh); swipeRefreshLayout.setDistanceToTriggerSync(getResources().getInteger(R.integer.swipe_refresh_distance)); swipeRefreshLayout.setOnRefreshListener(() -> { @@ -473,9 +482,7 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi emptyView.setIcon(R.drawable.ic_playlist_play); emptyView.setTitle(R.string.no_items_header_label); emptyView.setMessage(R.string.no_items_label); - - progLoading = root.findViewById(R.id.progLoading); - progLoading.setVisibility(View.VISIBLE); + emptyView.updateAdapter(recyclerAdapter); speedDialView = root.findViewById(R.id.fabSD); speedDialView.setOverlayLayout(root.findViewById(R.id.fabSDOverlay)); @@ -513,38 +520,6 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi super.onSaveInstanceState(outState); } - private void onFragmentLoaded(final boolean restoreScrollPosition) { - if (queue != null) { - if (recyclerAdapter == null) { - MainActivity activity = (MainActivity) getActivity(); - recyclerAdapter = new QueueRecyclerAdapter(activity, swipeActions) { - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - MenuItemUtils.setOnClickListeners(menu, QueueFragment.this::onContextItemSelected); - } - }; - recyclerAdapter.setOnSelectModeListener(this); - recyclerView.setAdapter(recyclerAdapter); - emptyView.updateAdapter(recyclerAdapter); - } - recyclerAdapter.updateItems(queue); - } else { - recyclerAdapter = null; - emptyView.updateAdapter(null); - } - - if (restoreScrollPosition) { - recyclerView.restoreScrollPosition(QueueFragment.TAG); - } - - // we need to refresh the options menu because it sometimes - // needs data that may have just been loaded. - refreshToolbarState(); - - refreshInfoBar(); - } - private void refreshInfoBar() { String info = String.format(Locale.getDefault(), "%d%s", queue.size(), getString(R.string.episodes_suffix)); @@ -574,18 +549,18 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi } if (queue == null) { emptyView.hide(); - progLoading.setVisibility(View.VISIBLE); } disposable = Observable.fromCallable(DBReader::getQueue) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(items -> { - progLoading.setVisibility(View.GONE); queue = items; - onFragmentLoaded(restoreScrollPosition); - if (recyclerAdapter != null) { - recyclerAdapter.notifyDataSetChanged(); + recyclerAdapter.setDummyViews(0); + recyclerAdapter.updateItems(queue); + if (restoreScrollPosition) { + recyclerView.restoreScrollPosition(QueueFragment.TAG); } + refreshInfoBar(); }, error -> Log.e(TAG, Log.getStackTraceString(error))); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java index 439f8a2cc..f5bfed324 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; import android.content.SharedPreferences; import android.os.Bundle; +import android.text.TextUtils; import android.util.DisplayMetrics; import androidx.fragment.app.Fragment; @@ -14,7 +15,6 @@ import android.widget.AdapterView; import android.widget.Button; import android.widget.GridView; import android.widget.LinearLayout; -import android.widget.ProgressBar; import android.widget.TextView; import de.danoeh.antennapod.net.discovery.ItunesTopListLoader; @@ -41,7 +41,6 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. private static final String TAG = "FeedDiscoveryFragment"; private static final int NUM_SUGGESTIONS = 12; - private ProgressBar progressBar; private Disposable disposable; private FeedDiscoverAdapter adapter; private GridView discoverGridLayout; @@ -59,7 +58,6 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. ((MainActivity) getActivity()).loadChildFragment(new DiscoveryFragment())); discoverGridLayout = root.findViewById(R.id.discover_grid); - progressBar = root.findViewById(R.id.discover_progress_bar); errorView = root.findViewById(R.id.discover_error); errorTextView = root.findViewById(R.id.discover_error_txtV); errorRetry = root.findViewById(R.id.discover_error_retry_btn); @@ -108,8 +106,6 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. } private void loadToplist() { - progressBar.setVisibility(View.VISIBLE); - discoverGridLayout.setVisibility(View.INVISIBLE); errorView.setVisibility(View.GONE); errorRetry.setVisibility(View.INVISIBLE); poweredByTextView.setVisibility(View.VISIBLE); @@ -121,7 +117,6 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. if (countryCode.equals(ItunesTopListLoader.DISCOVER_HIDE_FAKE_COUNTRY_CODE)) { errorTextView.setText(R.string.discover_is_hidden); errorView.setVisibility(View.VISIBLE); - progressBar.setVisibility(View.GONE); discoverGridLayout.setVisibility(View.GONE); errorRetry.setVisibility(View.GONE); poweredByTextView.setVisibility(View.GONE); @@ -132,20 +127,18 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. .subscribe( podcasts -> { errorView.setVisibility(View.GONE); - progressBar.setVisibility(View.GONE); - discoverGridLayout.setVisibility(View.VISIBLE); if (podcasts.size() == 0) { errorTextView.setText(getResources().getText(R.string.search_status_no_results)); errorView.setVisibility(View.VISIBLE); discoverGridLayout.setVisibility(View.INVISIBLE); } else { + discoverGridLayout.setVisibility(View.VISIBLE); adapter.updateData(podcasts); } }, error -> { Log.e(TAG, Log.getStackTraceString(error)); errorTextView.setText(error.getLocalizedMessage()); errorView.setVisibility(View.VISIBLE); - progressBar.setVisibility(View.GONE); discoverGridLayout.setVisibility(View.INVISIBLE); errorRetry.setVisibility(View.VISIBLE); }); @@ -154,7 +147,7 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. @Override public void onItemClick(AdapterView parent, final View view, int position, long id) { PodcastSearchResult podcast = adapter.getItem(position); - if (podcast.feedUrl == null) { + if (TextUtils.isEmpty(podcast.feedUrl)) { return; } Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java index bb7d9ff30..252ef8269 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java @@ -13,7 +13,6 @@ import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.ProgressBar; import android.widget.TextView; import androidx.annotation.NonNull; @@ -75,6 +74,7 @@ public class SubscriptionFragment extends Fragment public static final String TAG = "SubscriptionFragment"; private static final String PREFS = "SubscriptionFragment"; private static final String PREF_NUM_COLUMNS = "columns"; + private static final String PREF_PREVIOUS_EPISODE_COUNT = "episodeCount"; private static final String KEY_UP_ARROW = "up_arrow"; private static final String ARGUMENT_FOLDER = "folder"; @@ -88,7 +88,6 @@ public class SubscriptionFragment extends Fragment private RecyclerView subscriptionRecycler; private SubscriptionsRecyclerAdapter subscriptionAdapter; private FloatingActionButton subscriptionAddButton; - private ProgressBar progressBar; private EmptyViewHandler emptyView; private TextView feedsFilteredMsg; private Toolbar toolbar; @@ -153,8 +152,24 @@ public class SubscriptionFragment extends Fragment setColumnNumber(prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns())); subscriptionRecycler.addItemDecoration(new SubscriptionsRecyclerAdapter.GridDividerItemDecorator()); registerForContextMenu(subscriptionRecycler); + subscriptionAdapter = new SubscriptionsRecyclerAdapter((MainActivity) getActivity()) { + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + MenuItemUtils.setOnClickListeners(menu, SubscriptionFragment.this::onContextItemSelected); + } + }; + subscriptionAdapter.setOnSelectModeListener(this); + subscriptionAdapter.setDummyViews(Math.max(1, prefs.getInt(PREF_PREVIOUS_EPISODE_COUNT, 5))); + subscriptionRecycler.setAdapter(subscriptionAdapter); + setupEmptyView(); + subscriptionAddButton = root.findViewById(R.id.subscriptions_add); - progressBar = root.findViewById(R.id.progLoading); + subscriptionAddButton.setOnClickListener(view -> { + if (getActivity() instanceof MainActivity) { + ((MainActivity) getActivity()).loadChildFragment(new AddFeedFragment()); + } + }); feedsFilteredMsg = root.findViewById(R.id.feeds_filtered_message); feedsFilteredMsg.setOnClickListener((l) -> SubscriptionsFilterDialog.showDialog(requireContext())); @@ -253,26 +268,6 @@ public class SubscriptionFragment extends Fragment emptyView.attachToRecyclerView(subscriptionRecycler); } - @Override - public void onViewCreated(@NonNull View v, Bundle savedInstanceState) { - super.onViewCreated(v, savedInstanceState); - subscriptionAdapter = new SubscriptionsRecyclerAdapter((MainActivity) getActivity()) { - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - MenuItemUtils.setOnClickListeners(menu, SubscriptionFragment.this::onContextItemSelected); - } - }; - subscriptionAdapter.setOnSelectModeListener(this); - subscriptionRecycler.setAdapter(subscriptionAdapter); - setupEmptyView(); - subscriptionAddButton.setOnClickListener(view -> { - if (getActivity() instanceof MainActivity) { - ((MainActivity) getActivity()).loadChildFragment(new AddFeedFragment()); - } - }); - } - @Override public void onStart() { super.onStart(); @@ -287,6 +282,7 @@ public class SubscriptionFragment extends Fragment if (disposable != null) { disposable.dispose(); } + prefs.edit().putInt(PREF_PREVIOUS_EPISODE_COUNT, subscriptionAdapter.getItemCount()).apply(); if (subscriptionAdapter != null) { subscriptionAdapter.endSelectMode(); @@ -319,13 +315,12 @@ public class SubscriptionFragment extends Fragment subscriptionAdapter.endSelectMode(); } listItems = result; + subscriptionAdapter.setDummyViews(0); subscriptionAdapter.setItems(result); subscriptionAdapter.notifyDataSetChanged(); emptyView.updateVisibility(); - progressBar.setVisibility(View.GONE); // Keep hidden to avoid flickering while refreshing }, error -> { Log.e(TAG, Log.getStackTraceString(error)); - progressBar.setVisibility(View.GONE); }); if (UserPreferences.getSubscriptionsFilter().isEnabled()) { diff --git a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java index bc29740b0..b4a01ed5a 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java +++ b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java @@ -220,6 +220,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder { .withPlaceholderView(placeholder) .withCoverView(cover) .load(); + hideSeparatorIfNecessary(); } private void updateDuration(PlaybackPositionEvent event) { diff --git a/app/src/main/res/layout/episodes_list_fragment.xml b/app/src/main/res/layout/episodes_list_fragment.xml index 629b7ab0e..343d530fd 100644 --- a/app/src/main/res/layout/episodes_list_fragment.xml +++ b/app/src/main/res/layout/episodes_list_fragment.xml @@ -32,8 +32,7 @@ android:id="@+id/swipeRefresh" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_below="@+id/txtvInformation" - android:layout_above="@id/loadingMore"> + android:layout_below="@+id/txtvInformation"> - - - - - - - - - - diff --git a/app/src/main/res/layout/feed_item_list_fragment.xml b/app/src/main/res/layout/feed_item_list_fragment.xml index 2f175770f..2cc2b8214 100644 --- a/app/src/main/res/layout/feed_item_list_fragment.xml +++ b/app/src/main/res/layout/feed_item_list_fragment.xml @@ -61,14 +61,6 @@ - - - - - - diff --git a/app/src/main/res/layout/quick_feed_discovery.xml b/app/src/main/res/layout/quick_feed_discovery.xml index 9ef3db180..0fb5fdb8c 100644 --- a/app/src/main/res/layout/quick_feed_discovery.xml +++ b/app/src/main/res/layout/quick_feed_discovery.xml @@ -1,98 +1,92 @@ + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal"> + + android:layout_width="0dip" + android:layout_height="wrap_content" + android:text="@string/discover" + android:textSize="18sp" + android:layout_marginBottom="8dp" + android:layout_weight="1" + android:textColor="?android:attr/textColorPrimary" />