Merge pull request #3351 from ByteHamster/lazy-load-episodes
More episodes on all episodes
This commit is contained in:
commit
8a3e4f8765
|
@ -56,14 +56,13 @@ import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||||
import de.danoeh.antennapod.core.util.Flavors;
|
import de.danoeh.antennapod.core.util.Flavors;
|
||||||
import de.danoeh.antennapod.core.util.IntentUtils;
|
import de.danoeh.antennapod.core.util.IntentUtils;
|
||||||
import de.danoeh.antennapod.core.util.StorageUtils;
|
import de.danoeh.antennapod.core.util.StorageUtils;
|
||||||
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
|
|
||||||
import de.danoeh.antennapod.dialog.RatingDialog;
|
import de.danoeh.antennapod.dialog.RatingDialog;
|
||||||
import de.danoeh.antennapod.dialog.RenameFeedDialog;
|
import de.danoeh.antennapod.dialog.RenameFeedDialog;
|
||||||
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
||||||
import de.danoeh.antennapod.fragment.DownloadsFragment;
|
import de.danoeh.antennapod.fragment.DownloadsFragment;
|
||||||
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
import de.danoeh.antennapod.fragment.EpisodesFragment;
|
||||||
import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
|
import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
|
||||||
import de.danoeh.antennapod.fragment.ItemlistFragment;
|
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
|
||||||
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
|
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
|
||||||
import de.danoeh.antennapod.fragment.QueueFragment;
|
import de.danoeh.antennapod.fragment.QueueFragment;
|
||||||
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
import de.danoeh.antennapod.fragment.SubscriptionFragment;
|
||||||
|
@ -338,7 +337,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadFeedFragmentById(long feedId, Bundle args) {
|
public void loadFeedFragmentById(long feedId, Bundle args) {
|
||||||
Fragment fragment = ItemlistFragment.newInstance(feedId);
|
Fragment fragment = FeedItemlistFragment.newInstance(feedId);
|
||||||
if(args != null) {
|
if(args != null) {
|
||||||
fragment.setArguments(args);
|
fragment.setArguments(args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,11 @@ import com.bumptech.glide.Glide;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
import com.bumptech.glide.request.RequestOptions;
|
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
import de.danoeh.antennapod.core.feed.Feed;
|
import de.danoeh.antennapod.core.feed.Feed;
|
||||||
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
|
||||||
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
import de.danoeh.antennapod.fragment.AddFeedFragment;
|
||||||
import de.danoeh.antennapod.fragment.ItemlistFragment;
|
import de.danoeh.antennapod.fragment.FeedItemlistFragment;
|
||||||
import jp.shts.android.library.TriangleLabelView;
|
import jp.shts.android.library.TriangleLabelView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -142,7 +140,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
|
||||||
if (position == getAddTilePosition()) {
|
if (position == getAddTilePosition()) {
|
||||||
mainActivityRef.get().loadChildFragment(new AddFeedFragment());
|
mainActivityRef.get().loadChildFragment(new AddFeedFragment());
|
||||||
} else {
|
} else {
|
||||||
Fragment fragment = ItemlistFragment.newInstance(getItemId(position));
|
Fragment fragment = FeedItemlistFragment.newInstance(getItemId(position));
|
||||||
mainActivityRef.get().loadChildFragment(fragment);
|
mainActivityRef.get().loadChildFragment(fragment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,9 @@
|
||||||
package de.danoeh.antennapod.fragment;
|
package de.danoeh.antennapod.fragment;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.view.MenuItemCompat;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.support.v7.widget.SearchView;
|
|
||||||
import android.support.v7.widget.SimpleItemAnimator;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
@ -20,240 +11,48 @@ import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.joanzapata.iconify.Iconify;
|
import com.joanzapata.iconify.Iconify;
|
||||||
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
|
import de.danoeh.antennapod.R;
|
||||||
|
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||||
|
import de.danoeh.antennapod.core.feed.FeedItemFilter;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
|
import de.danoeh.antennapod.dialog.FilterDialog;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
|
||||||
import org.greenrobot.eventbus.EventBus;
|
|
||||||
import org.greenrobot.eventbus.Subscribe;
|
|
||||||
import org.greenrobot.eventbus.ThreadMode;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import de.danoeh.antennapod.R;
|
|
||||||
import de.danoeh.antennapod.activity.MainActivity;
|
|
||||||
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
|
|
||||||
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.core.event.FeedItemEvent;
|
|
||||||
import de.danoeh.antennapod.core.feed.EventDistributor;
|
|
||||||
import de.danoeh.antennapod.core.feed.Feed;
|
|
||||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
|
||||||
import de.danoeh.antennapod.core.feed.FeedItemFilter;
|
|
||||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
|
||||||
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
|
||||||
import de.danoeh.antennapod.core.service.download.DownloadService;
|
|
||||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
|
||||||
import de.danoeh.antennapod.core.storage.DBReader;
|
|
||||||
import de.danoeh.antennapod.core.storage.DBTasks;
|
|
||||||
import de.danoeh.antennapod.core.storage.DBWriter;
|
|
||||||
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
|
||||||
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
|
||||||
import de.danoeh.antennapod.core.util.LongList;
|
|
||||||
import de.danoeh.antennapod.dialog.FilterDialog;
|
|
||||||
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
|
||||||
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
|
||||||
import de.danoeh.antennapod.view.EmptyViewHandler;
|
|
||||||
import io.reactivex.Observable;
|
|
||||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
|
||||||
import io.reactivex.disposables.Disposable;
|
|
||||||
import io.reactivex.schedulers.Schedulers;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows unread or recently published episodes
|
* Like 'EpisodesFragment' except that it only shows new episodes and
|
||||||
|
* supports swiping to mark as read.
|
||||||
*/
|
*/
|
||||||
public class AllEpisodesFragment extends Fragment {
|
public class AllEpisodesFragment extends EpisodesListFragment {
|
||||||
|
|
||||||
public static final String TAG = "AllEpisodesFragment";
|
public static final String TAG = "AllEpisodesFragment";
|
||||||
|
private static final String PREF_NAME = "PrefAllEpisodesFragment";
|
||||||
|
|
||||||
private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE |
|
private static final int EPISODES_PER_PAGE = 150;
|
||||||
EventDistributor.UNREAD_ITEMS_UPDATE |
|
private static final int VISIBLE_EPISODES_SCROLL_THRESHOLD = 5;
|
||||||
EventDistributor.PLAYER_STATUS_UPDATE;
|
private static int page = 1;
|
||||||
|
|
||||||
private static final int RECENT_EPISODES_LIMIT = 150;
|
|
||||||
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";
|
|
||||||
|
|
||||||
RecyclerView recyclerView;
|
|
||||||
AllEpisodesRecycleAdapter listAdapter;
|
|
||||||
private ProgressBar progLoading;
|
|
||||||
EmptyViewHandler emptyView;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
List<FeedItem> episodes = new ArrayList<>();
|
|
||||||
@NonNull
|
|
||||||
private List<Downloader> downloaderList = new ArrayList<>();
|
|
||||||
|
|
||||||
private boolean isUpdatingFeeds;
|
|
||||||
boolean isMenuInvalidationAllowed = false;
|
|
||||||
|
|
||||||
Disposable disposable;
|
|
||||||
private LinearLayoutManager layoutManager;
|
|
||||||
|
|
||||||
protected TextView txtvInformation;
|
|
||||||
private static FeedItemFilter feedItemFilter = new FeedItemFilter("");
|
private static FeedItemFilter feedItemFilter = new FeedItemFilter("");
|
||||||
|
|
||||||
boolean showOnlyNewEpisodes() {
|
@Override
|
||||||
|
protected boolean showOnlyNewEpisodes() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getPrefName() {
|
|
||||||
return DEFAULT_PREF_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
protected String getPrefName() {
|
||||||
super.onStart();
|
return PREF_NAME;
|
||||||
setHasOptionsMenu(true);
|
|
||||||
EventDistributor.getInstance().register(contentUpdate);
|
|
||||||
EventBus.getDefault().register(this);
|
|
||||||
loadItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
registerForContextMenu(recyclerView);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
saveScrollPosition();
|
|
||||||
unregisterForContextMenu(recyclerView);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStop() {
|
|
||||||
super.onStop();
|
|
||||||
EventBus.getDefault().unregister(this);
|
|
||||||
EventDistributor.getInstance().unregister(contentUpdate);
|
|
||||||
if (disposable != null) {
|
|
||||||
disposable.dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
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.commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
|
|
||||||
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
||||||
if (!isAdded()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.onCreateOptionsMenu(menu, inflater);
|
|
||||||
inflater.inflate(R.menu.episodes, menu);
|
|
||||||
|
|
||||||
MenuItem searchItem = menu.findItem(R.id.action_search);
|
|
||||||
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
|
|
||||||
MenuItemUtils.adjustTextColor(getActivity(), sv);
|
|
||||||
sv.setQueryHint(getString(R.string.search_hint));
|
|
||||||
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onQueryTextSubmit(String s) {
|
|
||||||
sv.clearFocus();
|
|
||||||
((MainActivity) requireActivity()).loadChildFragment(SearchFragment.newInstance(s));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onQueryTextChange(String s) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPrepareOptionsMenu(Menu menu) {
|
|
||||||
super.onPrepareOptionsMenu(menu);
|
|
||||||
MenuItem markAllRead = menu.findItem(R.id.mark_all_read_item);
|
|
||||||
if (markAllRead != null) {
|
|
||||||
markAllRead.setVisible(!showOnlyNewEpisodes() && !episodes.isEmpty());
|
|
||||||
}
|
|
||||||
MenuItem removeAllNewFlags = menu.findItem(R.id.remove_all_new_flags_item);
|
|
||||||
if (removeAllNewFlags != null) {
|
|
||||||
removeAllNewFlags.setVisible(showOnlyNewEpisodes() && !episodes.isEmpty());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
if (!super.onOptionsItemSelected(item)) {
|
if (!super.onOptionsItemSelected(item)) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.refresh_item:
|
|
||||||
List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
|
|
||||||
if (feeds != null) {
|
|
||||||
DBTasks.refreshAllFeeds(getActivity(), feeds);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case 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();
|
|
||||||
Toast.makeText(getActivity(), R.string.mark_all_read_msg, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
markAllReadConfirmationDialog.createNewDialog().show();
|
|
||||||
return true;
|
|
||||||
case R.id.remove_all_new_flags_item:
|
|
||||||
ConfirmationDialog removeAllNewFlagsConfirmationDialog = new ConfirmationDialog(getActivity(),
|
|
||||||
R.string.remove_all_new_flags_label,
|
|
||||||
R.string.remove_all_new_flags_confirmation_msg) {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfirmButtonPressed(DialogInterface dialog) {
|
|
||||||
dialog.dismiss();
|
|
||||||
DBWriter.removeAllNewFlags();
|
|
||||||
Toast.makeText(getActivity(), R.string.removed_all_new_flags_msg, Toast.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
removeAllNewFlagsConfirmationDialog.createNewDialog().show();
|
|
||||||
return true;
|
|
||||||
case R.id.filter_items:
|
case R.id.filter_items:
|
||||||
showFilterDialog();
|
showFilterDialog();
|
||||||
return true;
|
return true;
|
||||||
|
@ -263,81 +62,62 @@ public class AllEpisodesFragment extends Fragment {
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onContextItemSelected(MenuItem item) {
|
|
||||||
Log.d(TAG, "onContextItemSelected() called with: " + "item = [" + item + "]");
|
|
||||||
if (!getUserVisibleHint()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!isVisible()) {
|
|
||||||
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.getSelectedItem() == null) {
|
|
||||||
Log.i(TAG, "Selected item or listAdapter was null, ignoring selection");
|
|
||||||
return super.onContextItemSelected(item);
|
|
||||||
}
|
|
||||||
FeedItem selectedItem = listAdapter.getSelectedItem();
|
|
||||||
|
|
||||||
// Remove new flag contains UI logic specific to All/New/FavoriteSegments,
|
|
||||||
// e.g., Undo with Snackbar,
|
|
||||||
// and is handled by this class rather than the generic FeedItemMenuHandler
|
|
||||||
// Undo is useful for Remove new flag, given there is no UI to undo it otherwise,
|
|
||||||
// i.e., there is context menu item for Mark as new
|
|
||||||
if (R.id.remove_new_flag_item == item.getItemId()) {
|
|
||||||
removeNewFlagWithUndo(selectedItem);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
super.onCreateView(inflater, container, savedInstanceState);
|
View root = super.onCreateView(inflater, container, savedInstanceState);
|
||||||
View root = inflater.inflate(R.layout.all_episodes_fragment, container, false);
|
|
||||||
txtvInformation = root.findViewById(R.id.txtvInformation);
|
|
||||||
|
|
||||||
layoutManager = new LinearLayoutManager(getActivity());
|
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
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.ItemAnimator animator = recyclerView.getItemAnimator();
|
/* Total number of episodes after last load */
|
||||||
if (animator instanceof SimpleItemAnimator) {
|
private int previousTotalEpisodes = 0;
|
||||||
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
progLoading = root.findViewById(R.id.progLoading);
|
/* True if loading more episodes is still in progress */
|
||||||
progLoading.setVisibility(View.VISIBLE);
|
private boolean isLoadingMore = true;
|
||||||
|
|
||||||
emptyView = new EmptyViewHandler(getContext());
|
@Override
|
||||||
emptyView.attachToRecyclerView(recyclerView);
|
public void onScrolled(RecyclerView recyclerView, int deltaX, int deltaY) {
|
||||||
emptyView.setIcon(R.attr.feed);
|
super.onScrolled(recyclerView, deltaX, deltaY);
|
||||||
emptyView.setTitle(R.string.no_all_episodes_head_label);
|
|
||||||
emptyView.setMessage(R.string.no_all_episodes_label);
|
|
||||||
|
|
||||||
createRecycleAdapter(recyclerView, emptyView);
|
int visibleEpisodeCount = recyclerView.getChildCount();
|
||||||
emptyView.hide();
|
int totalEpisodeCount = recyclerView.getLayoutManager().getItemCount();
|
||||||
|
int firstVisibleEpisode = ((LinearLayoutManager) recyclerView.getLayoutManager()).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)) {
|
||||||
|
|
||||||
|
/* The end of the list has been reached. Load more data. */
|
||||||
|
page++;
|
||||||
|
loadMoreItems();
|
||||||
|
isLoadingMore = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onFragmentLoaded(List<FeedItem> episodes) {
|
@Override
|
||||||
listAdapter.notifyDataSetChanged();
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
menu.findItem(R.id.filter_items).setVisible(true);
|
||||||
|
menu.findItem(R.id.mark_all_read_item).setVisible(!episodes.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
if (episodes.size() == 0) {
|
@Override
|
||||||
createRecycleAdapter(recyclerView, emptyView);
|
protected void onFragmentLoaded(List<FeedItem> episodes) {
|
||||||
}
|
super.onFragmentLoaded(episodes);
|
||||||
|
|
||||||
if (feedItemFilter.getValues().length > 0) {
|
if (feedItemFilter.getValues().length > 0) {
|
||||||
txtvInformation.setText("{fa-info-circle} " + this.getString(R.string.filtered_label));
|
txtvInformation.setText("{fa-info-circle} " + this.getString(R.string.filtered_label));
|
||||||
|
@ -346,179 +126,22 @@ public class AllEpisodesFragment extends Fragment {
|
||||||
} else {
|
} else {
|
||||||
txtvInformation.setVisibility(View.GONE);
|
txtvInformation.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
restoreScrollPosition();
|
|
||||||
requireActivity().invalidateOptionsMenu();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void loadMoreItems() {
|
||||||
* 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 AllEpisodesRecycleAdapter(mainActivity, itemAccess, showOnlyNewEpisodes());
|
|
||||||
listAdapter.setHasStableIds(true);
|
|
||||||
recyclerView.setAdapter(listAdapter);
|
|
||||||
emptyViewHandler.updateAdapter(listAdapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
return episodes.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FeedItem getItem(int position) {
|
|
||||||
if (0 <= position && position < episodes.size()) {
|
|
||||||
return episodes.get(position);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LongList getItemsIds() {
|
|
||||||
LongList ids = new LongList(episodes.size());
|
|
||||||
for (FeedItem episode : episodes) {
|
|
||||||
ids.add(episode.getId());
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemDownloadProgressPercent(FeedItem item) {
|
|
||||||
for (Downloader downloader : downloaderList) {
|
|
||||||
DownloadRequest downloadRequest = downloader.getDownloadRequest();
|
|
||||||
if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
|
|
||||||
&& downloadRequest.getFeedfileId() == item.getMedia().getId()) {
|
|
||||||
return downloadRequest.getProgressPercent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInQueue(FeedItem item) {
|
|
||||||
return item != null && item.isTagged(FeedItem.TAG_QUEUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LongList getQueueIds() {
|
|
||||||
LongList queueIds = new LongList();
|
|
||||||
for (FeedItem item : episodes) {
|
|
||||||
if (item.isTagged(FeedItem.TAG_QUEUE)) {
|
|
||||||
queueIds.add(item.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return queueIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
|
||||||
public void onEventMainThread(FeedItemEvent event) {
|
|
||||||
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
|
||||||
for (FeedItem item : event.items) {
|
|
||||||
int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
|
|
||||||
if (pos >= 0) {
|
|
||||||
episodes.remove(pos);
|
|
||||||
if (shouldUpdatedItemRemainInList(item)) {
|
|
||||||
episodes.add(pos, item);
|
|
||||||
listAdapter.notifyItemChanged(pos);
|
|
||||||
} else {
|
|
||||||
listAdapter.notifyItemRemoved(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean shouldUpdatedItemRemainInList(FeedItem item) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
|
||||||
public void onEventMainThread(DownloadEvent event) {
|
|
||||||
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
|
||||||
DownloaderUpdate update = event.update;
|
|
||||||
downloaderList = update.downloaders;
|
|
||||||
if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) {
|
|
||||||
requireActivity().invalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
if (update.mediaIds.length > 0) {
|
|
||||||
for (long mediaId : update.mediaIds) {
|
|
||||||
int pos = FeedItemUtil.indexOfItemWithMediaId(episodes, mediaId);
|
|
||||||
if (pos >= 0) {
|
|
||||||
listAdapter.notifyItemChanged(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
|
|
||||||
@Override
|
|
||||||
public void update(EventDistributor eventDistributor, Integer arg) {
|
|
||||||
if ((arg & EVENTS) != 0) {
|
|
||||||
loadItems();
|
|
||||||
if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
|
|
||||||
requireActivity().invalidateOptionsMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void loadItems() {
|
|
||||||
if (disposable != null) {
|
if (disposable != null) {
|
||||||
disposable.dispose();
|
disposable.dispose();
|
||||||
}
|
}
|
||||||
disposable = Observable.fromCallable(this::loadData)
|
disposable = Observable.fromCallable(this::loadMoreData)
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(data -> {
|
.subscribe(data -> {
|
||||||
progLoading.setVisibility(View.GONE);
|
progLoading.setVisibility(View.GONE);
|
||||||
episodes = data;
|
episodes.addAll(data);
|
||||||
onFragmentLoaded(episodes);
|
onFragmentLoaded(episodes);
|
||||||
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
List<FeedItem> loadData() {
|
|
||||||
return feedItemFilter.filter( DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT) );
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeNewFlagWithUndo(FeedItem item) {
|
|
||||||
if (item == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.d(TAG, "removeNewFlagWithUndo(" + item.getId() + ")");
|
|
||||||
if (disposable != null) {
|
|
||||||
disposable.dispose();
|
|
||||||
}
|
|
||||||
// we're marking it as unplayed since the user didn't actually play it
|
|
||||||
// but they don't want it considered 'NEW' anymore
|
|
||||||
DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
|
|
||||||
|
|
||||||
final Handler h = new Handler(getActivity().getMainLooper());
|
|
||||||
final Runnable r = () -> {
|
|
||||||
FeedMedia media = item.getMedia();
|
|
||||||
if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
|
|
||||||
DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Snackbar snackbar = Snackbar.make(getView(), getString(R.string.removed_new_flag_label),
|
|
||||||
Snackbar.LENGTH_LONG);
|
|
||||||
snackbar.setAction(getString(R.string.undo), v -> {
|
|
||||||
DBWriter.markItemPlayed(FeedItem.NEW, item.getId());
|
|
||||||
// don't forget to cancel the thing that's going to remove the media
|
|
||||||
h.removeCallbacks(r);
|
|
||||||
});
|
|
||||||
snackbar.show();
|
|
||||||
h.postDelayed(r, (int) Math.ceil(snackbar.getDuration() * 1.05f));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showFilterDialog() {
|
private void showFilterDialog() {
|
||||||
FilterDialog filterDialog = new FilterDialog(getContext(), feedItemFilter) {
|
FilterDialog filterDialog = new FilterDialog(getContext(), feedItemFilter) {
|
||||||
@Override
|
@Override
|
||||||
|
@ -530,4 +153,14 @@ public class AllEpisodesFragment extends Fragment {
|
||||||
|
|
||||||
filterDialog.openDialog();
|
filterDialog.openDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
protected List<FeedItem> loadData() {
|
||||||
|
return feedItemFilter.filter( DBReader.getRecentlyPublishedEpisodes(0, page * EPISODES_PER_PAGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<FeedItem> loadMoreData() {
|
||||||
|
return feedItemFilter.filter( DBReader.getRecentlyPublishedEpisodes((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,7 +79,7 @@ public class EpisodesFragment extends Fragment {
|
||||||
public static class EpisodesPagerAdapter extends FragmentPagerAdapter {
|
public static class EpisodesPagerAdapter extends FragmentPagerAdapter {
|
||||||
|
|
||||||
private final Resources resources;
|
private final Resources resources;
|
||||||
private final AllEpisodesFragment[] fragments = {
|
private final EpisodesListFragment[] fragments = {
|
||||||
new NewEpisodesFragment(),
|
new NewEpisodesFragment(),
|
||||||
new AllEpisodesFragment(),
|
new AllEpisodesFragment(),
|
||||||
new FavoriteEpisodesFragment()
|
new FavoriteEpisodesFragment()
|
||||||
|
|
|
@ -0,0 +1,488 @@
|
||||||
|
package de.danoeh.antennapod.fragment;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.design.widget.Snackbar;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v4.view.MenuItemCompat;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.support.v7.widget.SearchView;
|
||||||
|
import android.support.v7.widget.SimpleItemAnimator;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ProgressBar;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
import com.joanzapata.iconify.Iconify;
|
||||||
|
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
|
||||||
|
import de.danoeh.antennapod.R;
|
||||||
|
import de.danoeh.antennapod.activity.MainActivity;
|
||||||
|
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
|
||||||
|
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.core.event.FeedItemEvent;
|
||||||
|
import de.danoeh.antennapod.core.feed.EventDistributor;
|
||||||
|
import de.danoeh.antennapod.core.feed.Feed;
|
||||||
|
import de.danoeh.antennapod.core.feed.FeedItem;
|
||||||
|
import de.danoeh.antennapod.core.feed.FeedItemFilter;
|
||||||
|
import de.danoeh.antennapod.core.feed.FeedMedia;
|
||||||
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
|
import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
||||||
|
import de.danoeh.antennapod.core.service.download.DownloadService;
|
||||||
|
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBTasks;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBWriter;
|
||||||
|
import de.danoeh.antennapod.core.storage.DownloadRequester;
|
||||||
|
import de.danoeh.antennapod.core.util.FeedItemUtil;
|
||||||
|
import de.danoeh.antennapod.core.util.LongList;
|
||||||
|
import de.danoeh.antennapod.dialog.FilterDialog;
|
||||||
|
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
|
||||||
|
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
|
||||||
|
import de.danoeh.antennapod.view.EmptyViewHandler;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||||
|
import io.reactivex.disposables.Disposable;
|
||||||
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import org.greenrobot.eventbus.EventBus;
|
||||||
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows unread or recently published episodes
|
||||||
|
*/
|
||||||
|
public abstract class EpisodesListFragment extends Fragment {
|
||||||
|
|
||||||
|
public static final String TAG = "EpisodesListFragment";
|
||||||
|
|
||||||
|
private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE |
|
||||||
|
EventDistributor.UNREAD_ITEMS_UPDATE |
|
||||||
|
EventDistributor.PLAYER_STATUS_UPDATE;
|
||||||
|
|
||||||
|
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";
|
||||||
|
|
||||||
|
RecyclerView recyclerView;
|
||||||
|
AllEpisodesRecycleAdapter listAdapter;
|
||||||
|
ProgressBar progLoading;
|
||||||
|
EmptyViewHandler emptyView;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
List<FeedItem> episodes = new ArrayList<>();
|
||||||
|
@NonNull
|
||||||
|
private List<Downloader> downloaderList = new ArrayList<>();
|
||||||
|
|
||||||
|
private boolean isUpdatingFeeds;
|
||||||
|
boolean isMenuInvalidationAllowed = false;
|
||||||
|
|
||||||
|
protected Disposable disposable;
|
||||||
|
private LinearLayoutManager layoutManager;
|
||||||
|
protected TextView txtvInformation;
|
||||||
|
|
||||||
|
boolean showOnlyNewEpisodes() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPrefName() {
|
||||||
|
return DEFAULT_PREF_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
EventDistributor.getInstance().register(contentUpdate);
|
||||||
|
EventBus.getDefault().register(this);
|
||||||
|
loadItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
registerForContextMenu(recyclerView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
saveScrollPosition();
|
||||||
|
unregisterForContextMenu(recyclerView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
EventBus.getDefault().unregister(this);
|
||||||
|
EventDistributor.getInstance().unregister(contentUpdate);
|
||||||
|
if (disposable != null) {
|
||||||
|
disposable.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
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.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
|
||||||
|
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
if (!isAdded()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
inflater.inflate(R.menu.episodes, menu);
|
||||||
|
|
||||||
|
MenuItem searchItem = menu.findItem(R.id.action_search);
|
||||||
|
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
|
||||||
|
MenuItemUtils.adjustTextColor(getActivity(), sv);
|
||||||
|
sv.setQueryHint(getString(R.string.search_hint));
|
||||||
|
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextSubmit(String s) {
|
||||||
|
sv.clearFocus();
|
||||||
|
((MainActivity) requireActivity()).loadChildFragment(SearchFragment.newInstance(s));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onQueryTextChange(String s) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
if (!super.onOptionsItemSelected(item)) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.refresh_item:
|
||||||
|
List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
|
||||||
|
if (feeds != null) {
|
||||||
|
DBTasks.refreshAllFeeds(getActivity(), feeds);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
case 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();
|
||||||
|
Toast.makeText(getActivity(), R.string.mark_all_read_msg, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
markAllReadConfirmationDialog.createNewDialog().show();
|
||||||
|
return true;
|
||||||
|
case R.id.remove_all_new_flags_item:
|
||||||
|
ConfirmationDialog removeAllNewFlagsConfirmationDialog = new ConfirmationDialog(getActivity(),
|
||||||
|
R.string.remove_all_new_flags_label,
|
||||||
|
R.string.remove_all_new_flags_confirmation_msg) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfirmButtonPressed(DialogInterface dialog) {
|
||||||
|
dialog.dismiss();
|
||||||
|
DBWriter.removeAllNewFlags();
|
||||||
|
Toast.makeText(getActivity(), R.string.removed_all_new_flags_msg, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
removeAllNewFlagsConfirmationDialog.createNewDialog().show();
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onContextItemSelected(MenuItem item) {
|
||||||
|
Log.d(TAG, "onContextItemSelected() called with: " + "item = [" + item + "]");
|
||||||
|
if (!getUserVisibleHint()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!isVisible()) {
|
||||||
|
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.getSelectedItem() == null) {
|
||||||
|
Log.i(TAG, "Selected item or listAdapter was null, ignoring selection");
|
||||||
|
return super.onContextItemSelected(item);
|
||||||
|
}
|
||||||
|
FeedItem selectedItem = listAdapter.getSelectedItem();
|
||||||
|
|
||||||
|
// Remove new flag contains UI logic specific to All/New/FavoriteSegments,
|
||||||
|
// e.g., Undo with Snackbar,
|
||||||
|
// and is handled by this class rather than the generic FeedItemMenuHandler
|
||||||
|
// Undo is useful for Remove new flag, given there is no UI to undo it otherwise,
|
||||||
|
// i.e., there is context menu item for Mark as new
|
||||||
|
if (R.id.remove_new_flag_item == item.getItemId()) {
|
||||||
|
removeNewFlagWithUndo(selectedItem);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
super.onCreateView(inflater, container, savedInstanceState);
|
||||||
|
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.ItemAnimator animator = recyclerView.getItemAnimator();
|
||||||
|
if (animator instanceof SimpleItemAnimator) {
|
||||||
|
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
progLoading = root.findViewById(R.id.progLoading);
|
||||||
|
progLoading.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
emptyView = new EmptyViewHandler(getContext());
|
||||||
|
emptyView.attachToRecyclerView(recyclerView);
|
||||||
|
emptyView.setIcon(R.attr.feed);
|
||||||
|
emptyView.setTitle(R.string.no_all_episodes_head_label);
|
||||||
|
emptyView.setMessage(R.string.no_all_episodes_label);
|
||||||
|
|
||||||
|
createRecycleAdapter(recyclerView, emptyView);
|
||||||
|
emptyView.hide();
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onFragmentLoaded(List<FeedItem> episodes) {
|
||||||
|
listAdapter.notifyDataSetChanged();
|
||||||
|
|
||||||
|
if (episodes.size() == 0) {
|
||||||
|
createRecycleAdapter(recyclerView, emptyView);
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreScrollPosition();
|
||||||
|
requireActivity().invalidateOptionsMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 AllEpisodesRecycleAdapter(mainActivity, itemAccess, showOnlyNewEpisodes());
|
||||||
|
listAdapter.setHasStableIds(true);
|
||||||
|
recyclerView.setAdapter(listAdapter);
|
||||||
|
emptyViewHandler.updateAdapter(listAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return episodes.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FeedItem getItem(int position) {
|
||||||
|
if (0 <= position && position < episodes.size()) {
|
||||||
|
return episodes.get(position);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LongList getItemsIds() {
|
||||||
|
LongList ids = new LongList(episodes.size());
|
||||||
|
for (FeedItem episode : episodes) {
|
||||||
|
ids.add(episode.getId());
|
||||||
|
}
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemDownloadProgressPercent(FeedItem item) {
|
||||||
|
for (Downloader downloader : downloaderList) {
|
||||||
|
DownloadRequest downloadRequest = downloader.getDownloadRequest();
|
||||||
|
if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
|
||||||
|
&& downloadRequest.getFeedfileId() == item.getMedia().getId()) {
|
||||||
|
return downloadRequest.getProgressPercent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInQueue(FeedItem item) {
|
||||||
|
return item != null && item.isTagged(FeedItem.TAG_QUEUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LongList getQueueIds() {
|
||||||
|
LongList queueIds = new LongList();
|
||||||
|
for (FeedItem item : episodes) {
|
||||||
|
if (item.isTagged(FeedItem.TAG_QUEUE)) {
|
||||||
|
queueIds.add(item.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return queueIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
public void onEventMainThread(FeedItemEvent event) {
|
||||||
|
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
||||||
|
for (FeedItem item : event.items) {
|
||||||
|
int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
|
||||||
|
if (pos >= 0) {
|
||||||
|
episodes.remove(pos);
|
||||||
|
if (shouldUpdatedItemRemainInList(item)) {
|
||||||
|
episodes.add(pos, item);
|
||||||
|
listAdapter.notifyItemChanged(pos);
|
||||||
|
} else {
|
||||||
|
listAdapter.notifyItemRemoved(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean shouldUpdatedItemRemainInList(FeedItem item) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
|
||||||
|
public void onEventMainThread(DownloadEvent event) {
|
||||||
|
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
|
||||||
|
DownloaderUpdate update = event.update;
|
||||||
|
downloaderList = update.downloaders;
|
||||||
|
if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) {
|
||||||
|
requireActivity().invalidateOptionsMenu();
|
||||||
|
}
|
||||||
|
if (update.mediaIds.length > 0) {
|
||||||
|
for (long mediaId : update.mediaIds) {
|
||||||
|
int pos = FeedItemUtil.indexOfItemWithMediaId(episodes, mediaId);
|
||||||
|
if (pos >= 0) {
|
||||||
|
listAdapter.notifyItemChanged(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
|
||||||
|
@Override
|
||||||
|
public void update(EventDistributor eventDistributor, Integer arg) {
|
||||||
|
if ((arg & EVENTS) != 0) {
|
||||||
|
loadItems();
|
||||||
|
if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
|
||||||
|
requireActivity().invalidateOptionsMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void loadItems() {
|
||||||
|
if (disposable != null) {
|
||||||
|
disposable.dispose();
|
||||||
|
}
|
||||||
|
disposable = Observable.fromCallable(this::loadData)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(data -> {
|
||||||
|
progLoading.setVisibility(View.GONE);
|
||||||
|
episodes = data;
|
||||||
|
onFragmentLoaded(episodes);
|
||||||
|
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
protected abstract List<FeedItem> loadData();
|
||||||
|
|
||||||
|
void removeNewFlagWithUndo(FeedItem item) {
|
||||||
|
if (item == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "removeNewFlagWithUndo(" + item.getId() + ")");
|
||||||
|
if (disposable != null) {
|
||||||
|
disposable.dispose();
|
||||||
|
}
|
||||||
|
// we're marking it as unplayed since the user didn't actually play it
|
||||||
|
// but they don't want it considered 'NEW' anymore
|
||||||
|
DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
|
||||||
|
|
||||||
|
final Handler h = new Handler(getActivity().getMainLooper());
|
||||||
|
final Runnable r = () -> {
|
||||||
|
FeedMedia media = item.getMedia();
|
||||||
|
if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
|
||||||
|
DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Snackbar snackbar = Snackbar.make(getView(), getString(R.string.removed_new_flag_label),
|
||||||
|
Snackbar.LENGTH_LONG);
|
||||||
|
snackbar.setAction(getString(R.string.undo), v -> {
|
||||||
|
DBWriter.markItemPlayed(FeedItem.NEW, item.getId());
|
||||||
|
// don't forget to cancel the thing that's going to remove the media
|
||||||
|
h.removeCallbacks(r);
|
||||||
|
});
|
||||||
|
snackbar.show();
|
||||||
|
h.postDelayed(r, (int) Math.ceil(snackbar.getDuration() * 1.05f));
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
|
||||||
* Like 'EpisodesFragment' except that it only shows favorite episodes and
|
* Like 'EpisodesFragment' except that it only shows favorite episodes and
|
||||||
* supports swiping to remove from favorites.
|
* supports swiping to remove from favorites.
|
||||||
*/
|
*/
|
||||||
public class FavoriteEpisodesFragment extends AllEpisodesFragment {
|
public class FavoriteEpisodesFragment extends EpisodesListFragment {
|
||||||
|
|
||||||
private static final String TAG = "FavoriteEpisodesFrag";
|
private static final String TAG = "FavoriteEpisodesFrag";
|
||||||
private static final String PREF_NAME = "PrefFavoriteEpisodesFragment";
|
private static final String PREF_NAME = "PrefFavoriteEpisodesFragment";
|
||||||
|
@ -85,19 +85,6 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPrepareOptionsMenu(Menu menu) {
|
|
||||||
super.onPrepareOptionsMenu(menu);
|
|
||||||
|
|
||||||
menu.removeItem(R.id.filter_items);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onFragmentLoaded(List<FeedItem> episodes) {
|
|
||||||
super.onFragmentLoaded(episodes);
|
|
||||||
txtvInformation.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
protected List<FeedItem> loadData() {
|
protected List<FeedItem> loadData() {
|
||||||
|
|
|
@ -79,7 +79,7 @@ import io.reactivex.schedulers.Schedulers;
|
||||||
* Displays a list of FeedItems.
|
* Displays a list of FeedItems.
|
||||||
*/
|
*/
|
||||||
@SuppressLint("ValidFragment")
|
@SuppressLint("ValidFragment")
|
||||||
public class ItemlistFragment extends ListFragment {
|
public class FeedItemlistFragment extends ListFragment {
|
||||||
private static final String TAG = "ItemlistFragment";
|
private static final String TAG = "ItemlistFragment";
|
||||||
|
|
||||||
private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE
|
private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE
|
||||||
|
@ -120,8 +120,8 @@ public class ItemlistFragment extends ListFragment {
|
||||||
* @param feedId The id of the feed to show
|
* @param feedId The id of the feed to show
|
||||||
* @return the newly created instance of an ItemlistFragment
|
* @return the newly created instance of an ItemlistFragment
|
||||||
*/
|
*/
|
||||||
public static ItemlistFragment newInstance(long feedId) {
|
public static FeedItemlistFragment newInstance(long feedId) {
|
||||||
ItemlistFragment i = new ItemlistFragment();
|
FeedItemlistFragment i = new FeedItemlistFragment();
|
||||||
Bundle b = new Bundle();
|
Bundle b = new Bundle();
|
||||||
b.putLong(ARGUMENT_FEED_ID, feedId);
|
b.putLong(ARGUMENT_FEED_ID, feedId);
|
||||||
i.setArguments(b);
|
i.setArguments(b);
|
|
@ -534,7 +534,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void openPodcast() {
|
private void openPodcast() {
|
||||||
Fragment fragment = ItemlistFragment.newInstance(item.getFeedId());
|
Fragment fragment = FeedItemlistFragment.newInstance(item.getFeedId());
|
||||||
((MainActivity)getActivity()).loadChildFragment(fragment);
|
((MainActivity)getActivity()).loadChildFragment(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import android.support.v7.widget.RecyclerView;
|
||||||
import android.support.v7.widget.helper.ItemTouchHelper;
|
import android.support.v7.widget.helper.ItemTouchHelper;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
* Like 'EpisodesFragment' except that it only shows new episodes and
|
* Like 'EpisodesFragment' except that it only shows new episodes and
|
||||||
* supports swiping to mark as read.
|
* supports swiping to mark as read.
|
||||||
*/
|
*/
|
||||||
public class NewEpisodesFragment extends AllEpisodesFragment {
|
public class NewEpisodesFragment extends EpisodesListFragment {
|
||||||
|
|
||||||
public static final String TAG = "NewEpisodesFragment";
|
public static final String TAG = "NewEpisodesFragment";
|
||||||
private static final String PREF_NAME = "PrefNewEpisodesFragment";
|
private static final String PREF_NAME = "PrefNewEpisodesFragment";
|
||||||
|
@ -40,6 +41,12 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
|
||||||
return item.isNew();
|
return item.isNew();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepareOptionsMenu(Menu menu) {
|
||||||
|
super.onPrepareOptionsMenu(menu);
|
||||||
|
menu.findItem(R.id.remove_all_new_flags_item).setVisible(!episodes.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
@ -93,19 +100,6 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
|
||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPrepareOptionsMenu(Menu menu) {
|
|
||||||
super.onPrepareOptionsMenu(menu);
|
|
||||||
|
|
||||||
menu.removeItem(R.id.filter_items);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onFragmentLoaded(List<FeedItem> episodes) {
|
|
||||||
super.onFragmentLoaded(episodes);
|
|
||||||
txtvInformation.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
protected List<FeedItem> loadData() {
|
protected List<FeedItem> loadData() {
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
android:icon="?attr/ic_filter"
|
android:icon="?attr/ic_filter"
|
||||||
android:menuCategory="container"
|
android:menuCategory="container"
|
||||||
android:title="@string/filter"
|
android:title="@string/filter"
|
||||||
|
android:visible="false"
|
||||||
custom:showAsAction="ifRoom"/>
|
custom:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
|
@ -29,6 +30,7 @@
|
||||||
android:title="@string/mark_all_read_label"
|
android:title="@string/mark_all_read_label"
|
||||||
android:menuCategory="container"
|
android:menuCategory="container"
|
||||||
custom:showAsAction="collapseActionView"
|
custom:showAsAction="collapseActionView"
|
||||||
|
android:visible="false"
|
||||||
android:icon="?attr/navigation_accept"/>
|
android:icon="?attr/navigation_accept"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
|
@ -36,6 +38,7 @@
|
||||||
android:title="@string/remove_all_new_flags_label"
|
android:title="@string/remove_all_new_flags_label"
|
||||||
android:menuCategory="container"
|
android:menuCategory="container"
|
||||||
custom:showAsAction="collapseActionView"
|
custom:showAsAction="collapseActionView"
|
||||||
|
android:visible="false"
|
||||||
android:icon="?attr/navigation_accept"/>
|
android:icon="?attr/navigation_accept"/>
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|
|
@ -418,17 +418,18 @@ public final class DBReader {
|
||||||
/**
|
/**
|
||||||
* Loads a list of FeedItems sorted by pubDate in descending order.
|
* Loads a list of FeedItems sorted by pubDate in descending order.
|
||||||
*
|
*
|
||||||
|
* @param offset The first episode that should be loaded.
|
||||||
* @param limit The maximum number of episodes that should be loaded.
|
* @param limit The maximum number of episodes that should be loaded.
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public static List<FeedItem> getRecentlyPublishedEpisodes(int limit) {
|
public static List<FeedItem> getRecentlyPublishedEpisodes(int offset, int limit) {
|
||||||
Log.d(TAG, "getRecentlyPublishedEpisodes() called with: " + "limit = [" + limit + "]");
|
Log.d(TAG, "getRecentlyPublishedEpisodes() called with: " + "offset = [" + offset + "]" + " limit = [" + limit + "]" );
|
||||||
|
|
||||||
PodDBAdapter adapter = PodDBAdapter.getInstance();
|
PodDBAdapter adapter = PodDBAdapter.getInstance();
|
||||||
adapter.open();
|
adapter.open();
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
try {
|
try {
|
||||||
cursor = adapter.getRecentlyPublishedItemsCursor(limit);
|
cursor = adapter.getRecentlyPublishedItemsCursor(offset, limit);
|
||||||
List<FeedItem> items = extractItemlistFromCursor(adapter, cursor);
|
List<FeedItem> items = extractItemlistFromCursor(adapter, cursor);
|
||||||
loadAdditionalFeedItemListData(items);
|
loadAdditionalFeedItemListData(items);
|
||||||
return items;
|
return items;
|
||||||
|
|
|
@ -1020,8 +1020,8 @@ public class PodDBAdapter {
|
||||||
return db.rawQuery(query, null);
|
return db.rawQuery(query, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Cursor getRecentlyPublishedItemsCursor(int limit) {
|
public final Cursor getRecentlyPublishedItemsCursor(int offset, int limit) {
|
||||||
return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, null, null, null, null, KEY_PUBDATE + " DESC LIMIT " + limit);
|
return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, null, null, null, null, KEY_PUBDATE + " DESC LIMIT " + offset + ", " + limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getDownloadedItemsCursor() {
|
public Cursor getDownloadedItemsCursor() {
|
||||||
|
|
Loading…
Reference in New Issue