summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java22
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java111
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java30
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java43
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/EpisodeItemListRecyclerView.java73
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java1
-rw-r--r--app/src/main/res/layout/all_episodes_fragment.xml7
-rw-r--r--app/src/main/res/layout/feed_item_list_fragment.xml2
-rw-r--r--app/src/main/res/layout/queue_fragment.xml5
-rw-r--r--app/src/main/res/layout/search_fragment.xml4
-rw-r--r--app/src/main/res/layout/simple_list_fragment.xml2
-rw-r--r--core/src/main/res/values/ids.xml1
18 files changed, 174 insertions, 194 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index 7983db393..6b1e5e7d6 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -23,6 +23,7 @@ import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
+import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.snackbar.Snackbar;
@@ -74,6 +75,7 @@ public class MainActivity extends CastEnabledActivity {
private ActionBarDrawerToggle drawerToggle;
private LockableBottomSheetBehavior sheetBehavior;
private long lastBackButtonPressTime = 0;
+ private RecyclerView.RecycledViewPool recycledViewPool = new RecyclerView.RecycledViewPool();
@NonNull
public static Intent getIntentToOpenFeed(@NonNull Context context, long feedId) {
@@ -89,6 +91,7 @@ public class MainActivity extends CastEnabledActivity {
super.onCreate(savedInstanceState);
StorageUtils.checkStorageAvailability(this);
setContentView(R.layout.main);
+ recycledViewPool.setMaxRecycledViews(R.id.episode_item_view_holder, 25);
drawerLayout = findViewById(R.id.drawer_layout);
navDrawer = findViewById(R.id.navDrawerFragment);
@@ -191,6 +194,10 @@ public class MainActivity extends CastEnabledActivity {
findViewById(R.id.audioplayerFragment).setVisibility(visible ? View.VISIBLE : View.GONE);
}
+ public RecyclerView.RecycledViewPool getRecycledViewPool() {
+ return recycledViewPool;
+ }
+
public void loadFragment(String tag, Bundle args) {
Log.d(TAG, "loadFragment(tag: " + tag + ", args: " + args + ")");
Fragment fragment;
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 2f39b599f..d391f1c54 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/EpisodeItemListAdapter.java
@@ -42,14 +42,25 @@ public class EpisodeItemListAdapter extends RecyclerView.Adapter<EpisodeItemView
notifyDataSetChanged();
}
+ @Override
+ public final int getItemViewType(int position) {
+ return R.id.episode_item_view_holder;
+ }
+
@NonNull
@Override
- public EpisodeItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ public final EpisodeItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new EpisodeItemViewHolder(mainActivityRef.get(), parent);
}
@Override
- public void onBindViewHolder(EpisodeItemViewHolder holder, int pos) {
+ public final void onBindViewHolder(EpisodeItemViewHolder holder, int pos) {
+ // Reset state of recycled views
+ holder.coverHolder.setVisibility(View.VISIBLE);
+ holder.dragHandle.setVisibility(View.GONE);
+
+ beforeBindViewHolder(holder, pos);
+
FeedItem item = episodes.get(pos);
holder.bind(item);
holder.itemView.setOnLongClickListener(v -> {
@@ -65,9 +76,16 @@ public class EpisodeItemListAdapter extends RecyclerView.Adapter<EpisodeItemView
}
});
holder.itemView.setOnCreateContextMenuListener(this);
+ afterBindViewHolder(holder, pos);
holder.hideSeparatorIfNecessary();
}
+ protected void beforeBindViewHolder(EpisodeItemViewHolder holder, int pos) {
+ }
+
+ protected void afterBindViewHolder(EpisodeItemViewHolder holder, int pos) {
+ }
+
/**
* {@link #notifyItemChanged(int)} is final, so we can not override.
* Calling {@link #notifyItemChanged(int)} may bind the item to a new ViewHolder and execute a transition.
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 14f537eb0..428a968c6 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -35,8 +35,7 @@ public class QueueRecyclerAdapter extends EpisodeItemListAdapter {
@Override
@SuppressLint("ClickableViewAccessibility")
- public void onBindViewHolder(EpisodeItemViewHolder holder, int pos) {
- super.onBindViewHolder(holder, pos);
+ protected void afterBindViewHolder(EpisodeItemViewHolder holder, int pos) {
View.OnTouchListener startDragTouchListener = (v1, event) -> {
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
Log.d(TAG, "startDrag()");
@@ -56,7 +55,6 @@ public class QueueRecyclerAdapter extends EpisodeItemListAdapter {
}
holder.isInQueue.setVisibility(View.GONE);
- holder.hideSeparatorIfNecessary();
}
@Override
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 d5f8775f1..a3c07721a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -13,9 +13,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
@@ -31,6 +28,7 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
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;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -55,7 +53,7 @@ public class CompletedDownloadsFragment extends Fragment {
private List<FeedItem> items = new ArrayList<>();
private CompletedDownloadsListAdapter adapter;
- private RecyclerView recyclerView;
+ private EpisodeItemListRecyclerView recyclerView;
private ProgressBar progressBar;
private Disposable disposable;
private EmptyViewHandler emptyView;
@@ -68,10 +66,7 @@ public class CompletedDownloadsFragment extends Fragment {
toolbar.setVisibility(View.GONE);
recyclerView = root.findViewById(R.id.recyclerView);
- LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
- recyclerView.setLayoutManager(layoutManager);
- recyclerView.setHasFixedSize(true);
- recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
+ recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool());
recyclerView.setVisibility(View.GONE);
adapter = new CompletedDownloadsListAdapter((MainActivity) getActivity());
recyclerView.setAdapter(adapter);
@@ -215,8 +210,7 @@ public class CompletedDownloadsFragment extends Fragment {
}
@Override
- public void onBindViewHolder(EpisodeItemViewHolder holder, int pos) {
- super.onBindViewHolder(holder, pos);
+ public void afterBindViewHolder(EpisodeItemViewHolder holder, int pos) {
DeleteActionButton actionButton = new DeleteActionButton(getItem(pos));
actionButton.configure(holder.secondaryActionButton, holder.secondaryActionIcon, getActivity());
}
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 2a7c1f2b1..189e026a1 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -1,12 +1,9 @@
package de.danoeh.antennapod.fragment;
-import android.content.Context;
import android.content.DialogInterface;
-import android.content.SharedPreferences;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
-import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;
import android.util.Log;
@@ -20,13 +17,12 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
-
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.view.EpisodeItemListRecyclerView;
import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -61,18 +57,15 @@ import io.reactivex.schedulers.Schedulers;
public abstract class EpisodesListFragment extends Fragment {
public static final String TAG = "EpisodesListFragment";
- private static final String DEFAULT_PREF_NAME = "PrefAllEpisodesFragment";
- private static final String PREF_SCROLL_POSITION = "scroll_position";
- private static final String PREF_SCROLL_OFFSET = "scroll_offset";
-
protected static final int EPISODES_PER_PAGE = 150;
- private static final int VISIBLE_EPISODES_SCROLL_THRESHOLD = 5;
protected int page = 1;
+ protected boolean isLoadingMore = false;
+ protected boolean hasMoreItems = true;
- RecyclerView recyclerView;
+ EpisodeItemListRecyclerView recyclerView;
EpisodeItemListAdapter listAdapter;
ProgressBar progLoading;
- View loadingMore;
+ View loadingMoreView;
EmptyViewHandler emptyView;
@NonNull
@@ -81,11 +74,10 @@ public abstract class EpisodesListFragment extends Fragment {
private volatile boolean isUpdatingFeeds;
private boolean isMenuVisible = true;
protected Disposable disposable;
- private LinearLayoutManager layoutManager;
protected TextView txtvInformation;
String getPrefName() {
- return DEFAULT_PREF_NAME;
+ return TAG;
}
@Override
@@ -105,7 +97,7 @@ public abstract class EpisodesListFragment extends Fragment {
@Override
public void onPause() {
super.onPause();
- saveScrollPosition();
+ recyclerView.saveScrollPosition(getPrefName());
unregisterForContextMenu(recyclerView);
}
@@ -118,37 +110,6 @@ public abstract class EpisodesListFragment extends Fragment {
}
}
- private void saveScrollPosition() {
- int firstItem = layoutManager.findFirstVisibleItemPosition();
- View firstItemView = layoutManager.findViewByPosition(firstItem);
- float topOffset;
- if (firstItemView == null) {
- topOffset = 0;
- } else {
- topOffset = firstItemView.getTop();
- }
-
- SharedPreferences prefs = getActivity().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(PREF_SCROLL_POSITION, firstItem);
- editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
- editor.apply();
- }
-
- private void restoreScrollPosition() {
- SharedPreferences prefs = getActivity().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE);
- int position = prefs.getInt(PREF_SCROLL_POSITION, 0);
- float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
- if (position > 0 || offset > 0) {
- layoutManager.scrollToPositionWithOffset(position, (int) offset);
- // restore once, then forget
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(PREF_SCROLL_POSITION, 0);
- editor.putFloat(PREF_SCROLL_OFFSET, 0.0f);
- editor.apply();
- }
- }
-
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
@@ -241,12 +202,9 @@ public abstract class EpisodesListFragment extends Fragment {
View root = inflater.inflate(R.layout.all_episodes_fragment, container, false);
txtvInformation = root.findViewById(R.id.txtvInformation);
- layoutManager = new LinearLayoutManager(getActivity());
recyclerView = root.findViewById(android.R.id.list);
- recyclerView.setLayoutManager(layoutManager);
- recyclerView.setHasFixedSize(true);
- recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
recyclerView.setVisibility(View.GONE);
+ recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool());
setupLoadMoreScrollListener();
RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator();
@@ -256,7 +214,7 @@ public abstract class EpisodesListFragment extends Fragment {
progLoading = root.findViewById(R.id.progLoading);
progLoading.setVisibility(View.VISIBLE);
- loadingMore = root.findViewById(R.id.loadingMore);
+ loadingMoreView = root.findViewById(R.id.loadingMore);
emptyView = new EmptyViewHandler(getContext());
emptyView.attachToRecyclerView(recyclerView);
@@ -272,33 +230,10 @@ public abstract class EpisodesListFragment extends Fragment {
private void setupLoadMoreScrollListener() {
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
-
- /* Total number of episodes after last load */
- private int previousTotalEpisodes = 0;
-
- /* True if loading more episodes is still in progress */
- private boolean isLoadingMore = true;
-
@Override
- public void onScrolled(@NonNull RecyclerView recyclerView, int deltaX, int deltaY) {
- super.onScrolled(recyclerView, deltaX, deltaY);
-
- int visibleEpisodeCount = recyclerView.getChildCount();
- int totalEpisodeCount = recyclerView.getLayoutManager().getItemCount();
- int firstVisibleEpisode = layoutManager.findFirstVisibleItemPosition();
-
- /* Determine if loading more episodes has finished */
- if (isLoadingMore) {
- if (totalEpisodeCount > previousTotalEpisodes) {
- isLoadingMore = false;
- previousTotalEpisodes = totalEpisodeCount;
- }
- }
-
- /* Determine if the user scrolled to the bottom and loading more episodes is not already in progress */
- if (!isLoadingMore && (totalEpisodeCount - visibleEpisodeCount)
- <= (firstVisibleEpisode + VISIBLE_EPISODES_SCROLL_THRESHOLD)) {
-
+ public void onScrolled(@NonNull RecyclerView view, int deltaX, int deltaY) {
+ super.onScrolled(view, deltaX, deltaY);
+ if (!isLoadingMore && hasMoreItems && recyclerView.isScrolledToBottom()) {
/* The end of the list has been reached. Load more data. */
page++;
loadMoreItems();
@@ -312,26 +247,35 @@ public abstract class EpisodesListFragment extends Fragment {
if (disposable != null) {
disposable.dispose();
}
- loadingMore.setVisibility(View.VISIBLE);
+ isLoadingMore = true;
+ loadingMoreView.setVisibility(View.VISIBLE);
disposable = Observable.fromCallable(this::loadMoreData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
- loadingMore.setVisibility(View.GONE);
- progLoading.setVisibility(View.GONE);
+ if (data.size() < EPISODES_PER_PAGE) {
+ hasMoreItems = false;
+ }
episodes.addAll(data);
onFragmentLoaded(episodes);
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }, 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 onFragmentLoaded(List<FeedItem> episodes) {
+ boolean restoreScrollPosition = listAdapter.getItemCount() == 0;
if (episodes.size() == 0) {
createRecycleAdapter(recyclerView, emptyView);
} else {
listAdapter.updateItems(episodes);
}
-
- restoreScrollPosition();
+ if (restoreScrollPosition) {
+ recyclerView.restoreScrollPosition(getPrefName());
+ }
if (isMenuVisible && isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
requireActivity().invalidateOptionsMenu();
}
@@ -431,6 +375,7 @@ public abstract class EpisodesListFragment extends Fragment {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
progLoading.setVisibility(View.GONE);
+ hasMoreItems = true;
episodes = data;
onFragmentLoaded(episodes);
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
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 c9502ae4c..9bfb4d6dc 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -23,7 +23,6 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
-import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
@@ -31,7 +30,6 @@ import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconTextView;
-import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
@@ -66,6 +64,7 @@ import de.danoeh.antennapod.dialog.RenameFeedDialog;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.view.EpisodeItemListRecyclerView;
import de.danoeh.antennapod.view.ToolbarIconTintManager;
import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder;
import io.reactivex.Observable;
@@ -90,7 +89,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
private MoreContentListFooterUtil nextPageLoader;
private ProgressBar progressBar;
- private RecyclerView recyclerView;
+ private EpisodeItemListRecyclerView recyclerView;
private TextView txtvTitle;
private IconTextView txtvFailure;
private ImageView imgvBackground;
@@ -144,10 +143,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
recyclerView = root.findViewById(R.id.recyclerView);
- LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
- recyclerView.setLayoutManager(layoutManager);
- recyclerView.setHasFixedSize(true);
- recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
+ recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool());
recyclerView.setVisibility(View.GONE);
progressBar = root.findViewById(R.id.progLoading);
@@ -193,16 +189,11 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
});
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
- public void onScrolled(@NonNull RecyclerView recyclerView, int deltaX, int deltaY) {
- super.onScrolled(recyclerView, deltaX, deltaY);
-
- int visibleEpisodeCount = recyclerView.getChildCount();
- int totalEpisodeCount = recyclerView.getLayoutManager().getItemCount();
- int firstVisibleEpisode = layoutManager.findFirstVisibleItemPosition();
-
- boolean isAtBottom = (totalEpisodeCount - visibleEpisodeCount) <= (firstVisibleEpisode + 3);
+ public void onScrolled(@NonNull RecyclerView view, int deltaX, int deltaY) {
+ super.onScrolled(view, deltaX, deltaY);
boolean hasMorePages = feed != null && feed.isPaged() && feed.getNextPageLink() != null;
- nextPageLoader.getRoot().setVisibility((isAtBottom && hasMorePages) ? View.VISIBLE : View.GONE);
+ nextPageLoader.getRoot().setVisibility(
+ (recyclerView.isScrolledToBottom() && hasMorePages) ? View.VISIBLE : View.GONE);
}
});
@@ -556,12 +547,9 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
super(mainActivity);
}
- @NonNull
@Override
- public EpisodeItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- EpisodeItemViewHolder viewHolder = super.onCreateViewHolder(parent, viewType);
- viewHolder.coverHolder.setVisibility(View.GONE);
- return viewHolder;
+ protected void beforeBindViewHolder(EpisodeItemViewHolder holder, int pos) {
+ holder.coverHolder.setVisibility(View.GONE);
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index 4aac34c60..269c33962 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -8,12 +8,13 @@ import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
import de.danoeh.antennapod.view.ShownotesWebView;
+import io.reactivex.Maybe;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -74,7 +75,7 @@ public class ItemDescriptionFragment extends Fragment {
if (webViewLoader != null) {
webViewLoader.dispose();
}
- webViewLoader = Observable.fromCallable(this::loadData)
+ webViewLoader = Maybe.fromCallable(this::loadData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
@@ -84,10 +85,14 @@ public class ItemDescriptionFragment extends Fragment {
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
- @NonNull
+ @Nullable
private String loadData() {
- Timeline timeline = new Timeline(getActivity(), controller.getMedia());
- return timeline.processShownotes();
+ if (controller.getMedia() != null) {
+ Timeline timeline = new Timeline(getActivity(), controller.getMedia());
+ return timeline.processShownotes();
+ } else {
+ return null;
+ }
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
index 4a7b9603d..dabff7269 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -16,9 +16,6 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.core.view.MenuItemCompat;
import androidx.fragment.app.Fragment;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
@@ -34,6 +31,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
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;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -51,7 +49,7 @@ public class PlaybackHistoryFragment extends Fragment {
private List<FeedItem> playbackHistory;
private PlaybackHistoryListAdapter adapter;
private Disposable disposable;
- private RecyclerView recyclerView;
+ private EpisodeItemListRecyclerView recyclerView;
private EmptyViewHandler emptyView;
private ProgressBar progressBar;
@@ -71,10 +69,7 @@ public class PlaybackHistoryFragment extends Fragment {
((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);
recyclerView = root.findViewById(R.id.recyclerView);
- LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
- recyclerView.setLayoutManager(layoutManager);
- recyclerView.setHasFixedSize(true);
- recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
+ recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool());
recyclerView.setVisibility(View.GONE);
adapter = new PlaybackHistoryListAdapter((MainActivity) getActivity());
recyclerView.setAdapter(adapter);
@@ -246,8 +241,7 @@ public class PlaybackHistoryFragment extends Fragment {
}
@Override
- public void onBindViewHolder(EpisodeItemViewHolder holder, int pos) {
- super.onBindViewHolder(holder, pos);
+ protected void afterBindViewHolder(EpisodeItemViewHolder holder, int pos) {
// played items shouldn't be transparent for this fragment since, *all* items
// in this fragment will, by definition, be played. So it serves no purpose and can make
// it harder to read.
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 e4f08acd9..6a2b1abef 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -18,11 +18,9 @@ import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.ItemTouchHelper;
-import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;
import com.google.android.material.snackbar.Snackbar;
-import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.QueueRecyclerAdapter;
@@ -49,6 +47,7 @@ import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.view.EmptyViewHandler;
+import de.danoeh.antennapod.view.EpisodeItemListRecyclerView;
import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -71,7 +70,7 @@ public class QueueFragment extends Fragment {
public static final String TAG = "QueueFragment";
private TextView infoBar;
- private RecyclerView recyclerView;
+ private EpisodeItemListRecyclerView recyclerView;
private QueueRecyclerAdapter recyclerAdapter;
private EmptyViewHandler emptyView;
private ProgressBar progLoading;
@@ -81,16 +80,12 @@ public class QueueFragment extends Fragment {
private boolean isUpdatingFeeds = false;
private static final String PREFS = "QueueFragment";
- private static final String PREF_SCROLL_POSITION = "scroll_position";
- private static final String PREF_SCROLL_OFFSET = "scroll_offset";
private static final String PREF_SHOW_LOCK_WARNING = "show_lock_warning";
private Disposable disposable;
- private LinearLayoutManager layoutManager;
private ItemTouchHelper itemTouchHelper;
private SharedPreferences prefs;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -112,7 +107,7 @@ public class QueueFragment extends Fragment {
@Override
public void onPause() {
super.onPause();
- saveScrollPosition();
+ recyclerView.saveScrollPosition(QueueFragment.TAG);
}
@Override
@@ -159,7 +154,7 @@ public class QueueFragment extends Fragment {
case MOVED:
return;
}
- saveScrollPosition();
+ recyclerView.saveScrollPosition(QueueFragment.TAG);
onFragmentLoaded(false);
}
@@ -232,30 +227,6 @@ public class QueueFragment extends Fragment {
}
}
- private void saveScrollPosition() {
- int firstItem = layoutManager.findFirstVisibleItemPosition();
- View firstItemView = layoutManager.findViewByPosition(firstItem);
- float topOffset;
- if(firstItemView == null) {
- topOffset = 0;
- } else {
- topOffset = firstItemView.getTop();
- }
-
- prefs.edit()
- .putInt(PREF_SCROLL_POSITION, firstItem)
- .putFloat(PREF_SCROLL_OFFSET, topOffset)
- .apply();
- }
-
- private void restoreScrollPosition() {
- int position = prefs.getInt(PREF_SCROLL_POSITION, 0);
- float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
- if (position > 0 || offset > 0) {
- layoutManager.scrollToPositionWithOffset(position, (int) offset);
- }
- }
-
private void resetViewState() {
recyclerAdapter = null;
}
@@ -480,9 +451,7 @@ public class QueueFragment extends Fragment {
if (animator instanceof SimpleItemAnimator) {
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
}
- layoutManager = new LinearLayoutManager(getActivity());
- recyclerView.setLayoutManager(layoutManager);
- recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
+ recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool());
registerForContextMenu(recyclerView);
itemTouchHelper = new ItemTouchHelper(
@@ -598,7 +567,7 @@ public class QueueFragment extends Fragment {
}
if (restoreScrollPosition) {
- restoreScrollPosition();
+ recyclerView.restoreScrollPosition(QueueFragment.TAG);
}
// we need to refresh the options menu because it sometimes
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
index 389996b07..1ebf382fe 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.fragment;
-import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.util.Pair;
@@ -18,7 +17,6 @@ import androidx.appcompat.widget.SearchView;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
@@ -35,6 +33,7 @@ import de.danoeh.antennapod.core.storage.FeedSearcher;
import de.danoeh.antennapod.core.util.FeedItemUtil;
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;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -59,8 +58,7 @@ public class SearchFragment extends Fragment {
private Disposable disposable;
private ProgressBar progressBar;
private EmptyViewHandler emptyViewHandler;
- private RecyclerView recyclerView;
- private RecyclerView recyclerViewFeeds;
+ private EpisodeItemListRecyclerView recyclerView;
private List<FeedItem> results;
/**
@@ -117,15 +115,12 @@ public class SearchFragment extends Fragment {
progressBar = layout.findViewById(R.id.progressBar);
recyclerView = layout.findViewById(R.id.recyclerView);
- LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
- recyclerView.setLayoutManager(layoutManager);
- recyclerView.setHasFixedSize(true);
- recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
+ recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool());
recyclerView.setVisibility(View.GONE);
adapter = new EpisodeItemListAdapter((MainActivity) getActivity());
recyclerView.setAdapter(adapter);
- recyclerViewFeeds = layout.findViewById(R.id.recyclerViewFeeds);
+ RecyclerView recyclerViewFeeds = layout.findViewById(R.id.recyclerViewFeeds);
LinearLayoutManager layoutManagerFeeds = new LinearLayoutManager(getActivity());
layoutManagerFeeds.setOrientation(RecyclerView.HORIZONTAL);
recyclerViewFeeds.setLayoutManager(layoutManagerFeeds);
diff --git a/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemListRecyclerView.java b/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemListRecyclerView.java
new file mode 100644
index 000000000..cbc6ca630
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/EpisodeItemListRecyclerView.java
@@ -0,0 +1,73 @@
+package de.danoeh.antennapod.view;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.AttributeSet;
+import android.view.View;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+import io.reactivex.annotations.Nullable;
+
+public class EpisodeItemListRecyclerView extends RecyclerView {
+ private static final String TAG = "EpisodeItemListRecyclerView";
+ private static final String PREF_PREFIX_SCROLL_POSITION = "scroll_position_";
+ private static final String PREF_PREFIX_SCROLL_OFFSET = "scroll_offset_";
+
+ private LinearLayoutManager layoutManager;
+
+ public EpisodeItemListRecyclerView(Context context) {
+ super(context);
+ setup();
+ }
+
+ public EpisodeItemListRecyclerView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ setup();
+ }
+
+ public EpisodeItemListRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setup();
+ }
+
+ private void setup() {
+ layoutManager = new LinearLayoutManager(getContext());
+ layoutManager.setRecycleChildrenOnDetach(true);
+ setLayoutManager(layoutManager);
+ setHasFixedSize(true);
+ addItemDecoration(new HorizontalDividerItemDecoration.Builder(getContext()).build());
+ }
+
+ public void saveScrollPosition(String tag) {
+ int firstItem = layoutManager.findFirstVisibleItemPosition();
+ View firstItemView = layoutManager.findViewByPosition(firstItem);
+ float topOffset;
+ if (firstItemView == null) {
+ topOffset = 0;
+ } else {
+ topOffset = firstItemView.getTop();
+ }
+
+ getContext().getSharedPreferences(TAG, Context.MODE_PRIVATE).edit()
+ .putInt(PREF_PREFIX_SCROLL_POSITION + tag, firstItem)
+ .putInt(PREF_PREFIX_SCROLL_OFFSET + tag, (int) topOffset)
+ .apply();
+ }
+
+ public void restoreScrollPosition(String tag) {
+ SharedPreferences prefs = getContext().getSharedPreferences(TAG, Context.MODE_PRIVATE);
+ int position = prefs.getInt(PREF_PREFIX_SCROLL_POSITION + tag, 0);
+ int offset = prefs.getInt(PREF_PREFIX_SCROLL_OFFSET + tag, 0);
+ if (position > 0 || offset > 0) {
+ layoutManager.scrollToPositionWithOffset(position, offset);
+ }
+ }
+
+ public boolean isScrolledToBottom() {
+ int visibleEpisodeCount = getChildCount();
+ int totalEpisodeCount = layoutManager.getItemCount();
+ int firstVisibleEpisode = layoutManager.findFirstVisibleItemPosition();
+ return (totalEpisodeCount - visibleEpisodeCount) <= (firstVisibleEpisode + 3);
+ }
+}
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 a6300b50e..506f0f695 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
@@ -15,7 +15,6 @@ import com.joanzapata.iconify.Iconify;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.CoverLoader;
-import de.danoeh.antennapod.adapter.QueueRecyclerAdapter;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
diff --git a/app/src/main/res/layout/all_episodes_fragment.xml b/app/src/main/res/layout/all_episodes_fragment.xml
index 19a4d7827..73f061a0f 100644
--- a/app/src/main/res/layout/all_episodes_fragment.xml
+++ b/app/src/main/res/layout/all_episodes_fragment.xml
@@ -17,7 +17,7 @@
android:visibility="gone"
tools:text="(i) Information" />
- <androidx.recyclerview.widget.RecyclerView
+ <de.danoeh.antennapod.view.EpisodeItemListRecyclerView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -28,11 +28,6 @@
android:paddingTop="@dimen/list_vertical_padding"
android:paddingBottom="@dimen/list_vertical_padding"
android:layout_above="@id/loadingMore"
- app:fastScrollEnabled="true"
- app:fastScrollHorizontalThumbDrawable="@drawable/scrollbar_thumb_drawable"
- app:fastScrollHorizontalTrackDrawable="@drawable/scrollbar_line_drawable"
- app:fastScrollVerticalThumbDrawable="@drawable/scrollbar_thumb_drawable"
- app:fastScrollVerticalTrackDrawable="@drawable/scrollbar_line_drawable"
tools:itemCount="13"
tools:listitem="@layout/feeditemlist_item" />
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 a26bb3531..e607ac216 100644
--- a/app/src/main/res/layout/feed_item_list_fragment.xml
+++ b/app/src/main/res/layout/feed_item_list_fragment.xml
@@ -46,7 +46,7 @@
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
- <androidx.recyclerview.widget.RecyclerView
+ <de.danoeh.antennapod.view.EpisodeItemListRecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/app/src/main/res/layout/queue_fragment.xml b/app/src/main/res/layout/queue_fragment.xml
index a8a81d954..94f929d30 100644
--- a/app/src/main/res/layout/queue_fragment.xml
+++ b/app/src/main/res/layout/queue_fragment.xml
@@ -32,12 +32,11 @@
android:layout_below="@id/info_bar"
android:background="?android:attr/listDivider"/>
- <androidx.recyclerview.widget.RecyclerView
+ <de.danoeh.antennapod.view.EpisodeItemListRecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_below="@id/divider"
- android:scrollbars="vertical"/>
+ android:layout_below="@id/divider" />
<ProgressBar
android:id="@+id/progLoading"
diff --git a/app/src/main/res/layout/search_fragment.xml b/app/src/main/res/layout/search_fragment.xml
index ff40af775..19cd87409 100644
--- a/app/src/main/res/layout/search_fragment.xml
+++ b/app/src/main/res/layout/search_fragment.xml
@@ -31,9 +31,9 @@
android:paddingRight="12dp"
android:clipToPadding="false"/>
- <androidx.recyclerview.widget.RecyclerView
- android:layout_below="@id/recyclerViewFeeds"
+ <de.danoeh.antennapod.view.EpisodeItemListRecyclerView
android:id="@+id/recyclerView"
+ android:layout_below="@id/recyclerViewFeeds"
android:layout_marginTop="-4dp"
android:paddingTop="12dp"
android:clipToPadding="false"
diff --git a/app/src/main/res/layout/simple_list_fragment.xml b/app/src/main/res/layout/simple_list_fragment.xml
index 5bd2925ad..368029932 100644
--- a/app/src/main/res/layout/simple_list_fragment.xml
+++ b/app/src/main/res/layout/simple_list_fragment.xml
@@ -11,7 +11,7 @@
android:layout_alignParentTop="true"
android:id="@+id/toolbar"/>
- <androidx.recyclerview.widget.RecyclerView
+ <de.danoeh.antennapod.view.EpisodeItemListRecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar"
diff --git a/core/src/main/res/values/ids.xml b/core/src/main/res/values/ids.xml
index 90e405fde..1d1777ef1 100644
--- a/core/src/main/res/values/ids.xml
+++ b/core/src/main/res/values/ids.xml
@@ -24,4 +24,5 @@
<item name="notification_gpodnet_sync_autherror" type="id"/>
<item name="undobar_button" type="id"/>
<item name="undobar_message" type="id"/>
+ <item name="episode_item_view_holder" type="id"/>
</resources> \ No newline at end of file