diff --git a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java index b092264cd..bbcc4ce5c 100644 --- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java +++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java @@ -73,6 +73,12 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2 private void startLocalPlayback() { assertTrue(solo.waitForActivity(MainActivity.class)); openNavDrawer(); - solo.clickOnText(solo.getString(R.string.new_episodes_label)); + solo.clickOnText(solo.getString(R.string.all_episodes_label)); solo.waitForView(android.R.id.list); solo.clickOnView(solo.getView(R.id.butSecondaryAction)); assertTrue(solo.waitForActivity(AudioplayerActivity.class)); 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 b749788da..6f6b68579 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -34,6 +34,7 @@ import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.util.StorageUtils; import de.danoeh.antennapod.fragment.AddFeedFragment; +import de.danoeh.antennapod.fragment.AllEpisodesFragment; import de.danoeh.antennapod.fragment.DownloadsFragment; import de.danoeh.antennapod.fragment.ExternalPlayerFragment; import de.danoeh.antennapod.fragment.ItemlistFragment; @@ -69,9 +70,10 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity public static final int POS_QUEUE = 0, POS_NEW = 1, - POS_DOWNLOADS = 2, - POS_HISTORY = 3, - POS_ADD = 4; + POS_ALL_EPISODES = 2, + POS_DOWNLOADS = 3, + POS_HISTORY = 4, + POS_ADD = 5; private Toolbar toolbar; private ExternalPlayerFragment externalPlayerFragment; @@ -224,6 +226,9 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity case POS_NEW: fragment = new NewEpisodesFragment(); break; + case POS_ALL_EPISODES: + fragment = new AllEpisodesFragment(); + break; case POS_QUEUE: fragment = new QueueFragment(); break; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java index 05783e3ee..17cd71f86 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -25,7 +25,13 @@ public class NavListAdapter extends BaseAdapter { public static final int VIEW_TYPE_SECTION_DIVIDER = 1; public static final int VIEW_TYPE_SUBSCRIPTION = 2; - public static final int[] NAV_TITLES = {R.string.queue_label, R.string.new_episodes_label, R.string.downloads_label, R.string.playback_history_label, R.string.add_feed_label}; + public static final int[] NAV_TITLES = { + R.string.queue_label, + R.string.new_episodes_label, + R.string.all_episodes_label, + R.string.downloads_label, + R.string.playback_history_label, + R.string.add_feed_label}; private final Drawable[] drawables; @@ -38,10 +44,15 @@ public class NavListAdapter extends BaseAdapter { this.itemAccess = itemAccess; this.context = context; - TypedArray ta = context.obtainStyledAttributes(new int[]{R.attr.stat_playlist, R.attr.ic_new, - R.attr.av_download, R.attr.ic_history, R.attr.content_new}); + TypedArray ta = context.obtainStyledAttributes(new int[]{ + R.attr.stat_playlist, + R.attr.ic_new, + R.attr.feed, + R.attr.av_download, + R.attr.ic_history, + R.attr.content_new}); drawables = new Drawable[]{ta.getDrawable(0), ta.getDrawable(1), ta.getDrawable(2), - ta.getDrawable(3), ta.getDrawable(4)}; + ta.getDrawable(3), ta.getDrawable(4), ta.getDrawable(5)}; ta.recycle(); } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java index 2d481a7ef..1f98ec158 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java @@ -1,7 +1,6 @@ package de.danoeh.antennapod.adapter; import android.content.Context; -import android.graphics.Color; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; @@ -12,11 +11,9 @@ import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; -import com.nineoldandroids.view.ViewHelper; import com.squareup.picasso.Picasso; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.storage.DownloadRequester; @@ -142,13 +139,6 @@ public class NewEpisodesListAdapter extends BaseAdapter { .fit() .into(holder.imageView); - if (item.isRead()) { - // grey it out - ViewHelper.setAlpha(convertView, .2f); - } else { - ViewHelper.setAlpha(convertView, 1.0f); - } - return convertView; } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java new file mode 100644 index 000000000..58b6f734f --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -0,0 +1,457 @@ +package de.danoeh.antennapod.fragment; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.Fragment; +import android.support.v7.widget.SearchView; +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.AdapterView; +import android.widget.ProgressBar; +import android.widget.TextView; +import android.widget.Toast; + +import com.mobeta.android.dslv.DragSortListView; + +import java.util.List; +import java.util.concurrent.atomic.AtomicReference; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; +import de.danoeh.antennapod.adapter.NewEpisodesListAdapter; +import de.danoeh.antennapod.core.asynctask.DownloadObserver; +import de.danoeh.antennapod.core.dialog.ConfirmationDialog; +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.FeedMedia; +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.QueueAccess; +import de.danoeh.antennapod.menuhandler.MenuItemUtils; +import de.danoeh.antennapod.menuhandler.NavDrawerActivity; + +/** + * Shows unread or recently published episodes + */ +public class AllEpisodesFragment extends Fragment { + private static final String TAG = "AllEpisodesFragment"; + private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | + EventDistributor.DOWNLOAD_QUEUED | + EventDistributor.QUEUE_UPDATE | + EventDistributor.UNREAD_ITEMS_UPDATE | + EventDistributor.PLAYER_STATUS_UPDATE; + + private static final int RECENT_EPISODES_LIMIT = 150; + private static final String DEFAULT_PREF_NAME = "PrefAllEpisodesFragment"; + private static final String PREF_KEY_LIST_TOP = "list_top"; + private static final String PREF_KEY_LIST_SELECTION = "list_selection"; + + private String prefName; + protected DragSortListView listView; + private NewEpisodesListAdapter listAdapter; + private TextView txtvEmpty; + private ProgressBar progLoading; + + private List unreadItems; + private List recentItems; + private QueueAccess queueAccess; + private List downloaderList; + + private boolean itemsLoaded = false; + private boolean viewsCreated = false; + private boolean showOnlyNewEpisodes = false; + + private AtomicReference activity = new AtomicReference(); + + private DownloadObserver downloadObserver = null; + + private boolean isUpdatingFeeds; + + public AllEpisodesFragment() { + // by default we show all the episodes + this(false, DEFAULT_PREF_NAME); + } + + // this is only going to be called by our sub-class. + // The Android docs say to avoid non-default constructors + // but I think this will be OK since it will only be invoked + // from a fragment via a default constructor + protected AllEpisodesFragment(boolean showOnlyNewEpisodes, String prefName) { + this.showOnlyNewEpisodes = showOnlyNewEpisodes; + this.prefName = prefName; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + setHasOptionsMenu(true); + } + + @Override + public void onResume() { + super.onResume(); + startItemLoader(); + } + + @Override + public void onStart() { + super.onStart(); + EventDistributor.getInstance().register(contentUpdate); + this.activity.set((MainActivity) getActivity()); + if (downloadObserver != null) { + downloadObserver.setActivity(getActivity()); + downloadObserver.onResume(); + } + if (viewsCreated && itemsLoaded) { + onFragmentLoaded(); + } + } + + @Override + public void onPause() { + super.onPause(); + saveScrollPosition(); + } + + @Override + public void onStop() { + super.onStop(); + EventDistributor.getInstance().unregister(contentUpdate); + stopItemLoader(); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + this.activity.set((MainActivity) getActivity()); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + resetViewState(); + } + + private void saveScrollPosition() { + SharedPreferences prefs = getActivity().getSharedPreferences(prefName, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = prefs.edit(); + View v = listView.getChildAt(0); + int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop()); + editor.putInt(PREF_KEY_LIST_SELECTION, listView.getFirstVisiblePosition()); + editor.putInt(PREF_KEY_LIST_TOP, top); + editor.commit(); + } + + private void restoreScrollPosition() { + SharedPreferences prefs = getActivity().getSharedPreferences(prefName, Context.MODE_PRIVATE); + int listSelection = prefs.getInt(PREF_KEY_LIST_SELECTION, 0); + int top = prefs.getInt(PREF_KEY_LIST_TOP, 0); + if (listSelection > 0 || top > 0) { + listView.setSelectionFromTop(listSelection, top); + // restore once, then forget + SharedPreferences.Editor editor = prefs.edit(); + editor.putInt(PREF_KEY_LIST_SELECTION, 0); + editor.putInt(PREF_KEY_LIST_TOP, 0); + editor.commit(); + } + } + + protected void resetViewState() { + listAdapter = null; + activity.set(null); + viewsCreated = false; + if (downloadObserver != null) { + downloadObserver.onPause(); + } + } + + + private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() { + @Override + public boolean isRefreshing() { + return DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds(); + } + }; + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) { + inflater.inflate(R.menu.new_episodes, menu); + + final SearchView sv = new SearchView(getActivity()); + MenuItemUtils.addSearchItem(menu, sv); + sv.setQueryHint(getString(R.string.search_hint)); + sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(String s) { + sv.clearFocus(); + ((MainActivity) getActivity()).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); + if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) { + menu.findItem(R.id.mark_all_read_item).setVisible(unreadItems != null && !unreadItems.isEmpty()); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (!super.onOptionsItemSelected(item)) { + switch (item.getItemId()) { + case R.id.refresh_item: + List feeds = ((MainActivity) getActivity()).getFeeds(); + if (feeds != null) { + DBTasks.refreshAllFeeds(getActivity(), feeds); + } + return true; + case R.id.mark_all_read_item: + ConfirmationDialog conDialog = 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(getActivity()); + Toast.makeText(getActivity(), R.string.mark_all_read_msg, Toast.LENGTH_SHORT).show(); + } + }; + conDialog.createNewDialog().show(); + return true; + default: + return false; + } + } else { + return true; + } + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return onCreateViewHelper(inflater, container, savedInstanceState, + R.layout.all_episodes_fragment, R.string.all_episodes_label); + } + + protected View onCreateViewHelper(LayoutInflater inflater, + ViewGroup container, + Bundle savedInstanceState, + int fragmentResource, + int titleString) { + super.onCreateView(inflater, container, savedInstanceState); + ((MainActivity) getActivity()).getSupportActionBar().setTitle(titleString); + + View root = inflater.inflate(fragmentResource, container, false); + + listView = (DragSortListView) root.findViewById(android.R.id.list); + txtvEmpty = (TextView) root.findViewById(android.R.id.empty); + progLoading = (ProgressBar) root.findViewById(R.id.progLoading); + + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + FeedItem item = (FeedItem) listAdapter.getItem(position - listView.getHeaderViewsCount()); + if (item != null) { + ((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(item.getId())); + } + + } + }); + + if (!itemsLoaded) { + progLoading.setVisibility(View.VISIBLE); + txtvEmpty.setVisibility(View.GONE); + } + + viewsCreated = true; + + if (itemsLoaded && activity.get() != null) { + onFragmentLoaded(); + } + + return root; + } + + private void onFragmentLoaded() { + if (listAdapter == null) { + listAdapter = new NewEpisodesListAdapter(activity.get(), itemAccess, new DefaultActionButtonCallback(activity.get())); + listView.setAdapter(listAdapter); + listView.setEmptyView(txtvEmpty); + downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback); + downloadObserver.onResume(); + } + listAdapter.notifyDataSetChanged(); + restoreScrollPosition(); + getActivity().supportInvalidateOptionsMenu(); + updateShowOnlyEpisodesListViewState(); + } + + private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { + @Override + public void onContentChanged() { + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + + @Override + public void onDownloadDataAvailable(List downloaderList) { + AllEpisodesFragment.this.downloaderList = downloaderList; + if (listAdapter != null) { + listAdapter.notifyDataSetChanged(); + } + } + }; + + private NewEpisodesListAdapter.ItemAccess itemAccess = new NewEpisodesListAdapter.ItemAccess() { + + @Override + public int getCount() { + if (itemsLoaded) { + return (showOnlyNewEpisodes) ? unreadItems.size() : recentItems.size(); + } + return 0; + } + + @Override + public FeedItem getItem(int position) { + if (itemsLoaded) { + return (showOnlyNewEpisodes) ? unreadItems.get(position) : recentItems.get(position); + } + return null; + } + + @Override + public int getItemDownloadProgressPercent(FeedItem item) { + if (downloaderList != null) { + for (Downloader downloader : downloaderList) { + if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA + && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) { + return downloader.getDownloadRequest().getProgressPercent(); + } + } + } + return 0; + } + + @Override + public boolean isInQueue(FeedItem item) { + if (itemsLoaded) { + return queueAccess.contains(item.getId()); + } else { + return false; + } + } + + + }; + + private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + @Override + public void update(EventDistributor eventDistributor, Integer arg) { + if ((arg & EVENTS) != 0) { + startItemLoader(); + if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) { + getActivity().supportInvalidateOptionsMenu(); + } + } + } + }; + + private void updateShowOnlyEpisodesListViewState() { + if (showOnlyNewEpisodes) { + listView.setEmptyView(null); + txtvEmpty.setVisibility(View.GONE); + } else { + listView.setEmptyView(txtvEmpty); + } + } + + private ItemLoader itemLoader; + + private void startItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + itemLoader = new ItemLoader(); + itemLoader.execute(); + } + + protected void stopItemLoader() { + if (itemLoader != null) { + itemLoader.cancel(true); + } + } + + private class ItemLoader extends AsyncTask { + + @Override + protected void onPreExecute() { + super.onPreExecute(); + if (viewsCreated && !itemsLoaded) { + listView.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.GONE); + progLoading.setVisibility(View.VISIBLE); + } + } + + @Override + protected Object[] doInBackground(Void... params) { + Context context = activity.get(); + if (context != null) { + return new Object[]{DBReader.getUnreadItemsList(context), + DBReader.getRecentlyPublishedEpisodes(context, RECENT_EPISODES_LIMIT), + QueueAccess.IDListAccess(DBReader.getQueueIDList(context))}; + } else { + return null; + } + } + + @Override + protected void onPostExecute(Object[] lists) { + super.onPostExecute(lists); + listView.setVisibility(View.VISIBLE); + progLoading.setVisibility(View.GONE); + + if (lists != null) { + unreadItems = (List) lists[0]; + recentItems = (List) lists[1]; + queueAccess = (QueueAccess) lists[2]; + itemsLoaded = true; + if (viewsCreated && activity.get() != null) { + onFragmentLoaded(); + } + } + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java index 8bc4099a9..c2bbae42d 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -1,290 +1,50 @@ package de.danoeh.antennapod.fragment; -import android.app.Activity; -import android.content.Context; -import android.content.DialogInterface; -import android.content.SharedPreferences; -import android.os.AsyncTask; import android.os.Bundle; -import android.os.Handler; import android.os.Parcelable; -import android.support.v4.app.Fragment; -import android.support.v7.widget.SearchView; 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.AdapterView; -import android.widget.ProgressBar; -import android.widget.TextView; -import android.widget.Toast; import com.mobeta.android.dslv.DragSortListView; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; -import de.danoeh.antennapod.adapter.NewEpisodesListAdapter; -import de.danoeh.antennapod.core.asynctask.DownloadObserver; -import de.danoeh.antennapod.core.dialog.ConfirmationDialog; -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.FeedMedia; -import de.danoeh.antennapod.core.preferences.UserPreferences; -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.QueueAccess; import de.danoeh.antennapod.core.util.gui.FeedItemUndoToken; import de.danoeh.antennapod.core.util.gui.UndoBarController; -import de.danoeh.antennapod.menuhandler.MenuItemUtils; -import de.danoeh.antennapod.menuhandler.NavDrawerActivity; /** - * Shows unread or recently published episodes + * Like 'EpisodesFragment' except that it only shows new episodes and + * supports swiping to mark as read. */ -public class NewEpisodesFragment extends Fragment { +public class NewEpisodesFragment extends AllEpisodesFragment { + private static final String TAG = "NewEpisodesFragment"; - private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | - EventDistributor.DOWNLOAD_QUEUED | - EventDistributor.QUEUE_UPDATE | - EventDistributor.UNREAD_ITEMS_UPDATE | - EventDistributor.PLAYER_STATUS_UPDATE; - - private static final int RECENT_EPISODES_LIMIT = 150; private static final String PREF_NAME = "PrefNewEpisodesFragment"; - private static final String PREF_EPISODE_FILTER_BOOL = "newEpisodeFilterEnabled"; - private static final String PREF_KEY_LIST_TOP = "list_top"; - private static final String PREF_KEY_LIST_SELECTION = "list_selection"; - - private DragSortListView listView; - private NewEpisodesListAdapter listAdapter; - private TextView txtvEmpty; - private ProgressBar progLoading; private UndoBarController undoBarController; - private List unreadItems; - private List recentItems; - private QueueAccess queueAccess; - private List downloaderList; - - private boolean itemsLoaded = false; - private boolean viewsCreated = false; - private boolean showOnlyNewEpisodes = false; - - private AtomicReference activity = new AtomicReference(); - - private DownloadObserver downloadObserver = null; - - private boolean isUpdatingFeeds; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setRetainInstance(true); - setHasOptionsMenu(true); - - updateShowOnlyEpisodes(); + public NewEpisodesFragment() { + super(true, PREF_NAME); } @Override - public void onResume() { - super.onResume(); - startItemLoader(); - } - - @Override - public void onStart() { - super.onStart(); - EventDistributor.getInstance().register(contentUpdate); - this.activity.set((MainActivity) getActivity()); - if (downloadObserver != null) { - downloadObserver.setActivity(getActivity()); - downloadObserver.onResume(); - } - if (viewsCreated && itemsLoaded) { - onFragmentLoaded(); - } - } - - @Override - public void onPause() { - super.onPause(); - saveScrollPosition(); - } - - @Override - public void onStop() { - super.onStop(); - EventDistributor.getInstance().unregister(contentUpdate); - stopItemLoader(); - } - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - this.activity.set((MainActivity) getActivity()); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - resetViewState(); - } - - private void saveScrollPosition() { - SharedPreferences prefs = getActivity().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = prefs.edit(); - View v = listView.getChildAt(0); - int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop()); - editor.putInt(PREF_KEY_LIST_SELECTION, listView.getFirstVisiblePosition()); - editor.putInt(PREF_KEY_LIST_TOP, top); - editor.commit(); - } - - private void restoreScrollPosition() { - SharedPreferences prefs = getActivity().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); - int listSelection = prefs.getInt(PREF_KEY_LIST_SELECTION, 0); - int top = prefs.getInt(PREF_KEY_LIST_TOP, 0); - if(listSelection > 0 || top > 0) { - listView.setSelectionFromTop(listSelection, top); - // restore once, then forget - SharedPreferences.Editor editor = prefs.edit(); - editor.putInt(PREF_KEY_LIST_SELECTION, 0); - editor.putInt(PREF_KEY_LIST_TOP, 0); - editor.commit(); - } - } - - private void resetViewState() { - listAdapter = null; - activity.set(null); - viewsCreated = false; + protected void resetViewState() { + super.resetViewState(); undoBarController = null; - if (downloadObserver != null) { - downloadObserver.onPause(); - } - } - - - private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() { - @Override - public boolean isRefreshing() { - return DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds(); - } - }; - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) { - inflater.inflate(R.menu.new_episodes, menu); - - final SearchView sv = new SearchView(getActivity()); - MenuItemUtils.addSearchItem(menu, sv); - sv.setQueryHint(getString(R.string.search_hint)); - sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { - @Override - public boolean onQueryTextSubmit(String s) { - sv.clearFocus(); - ((MainActivity) getActivity()).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); - if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) { - menu.findItem(R.id.mark_all_read_item).setVisible(unreadItems != null && !unreadItems.isEmpty()); - menu.findItem(R.id.episode_filter_item).setChecked(showOnlyNewEpisodes); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (!super.onOptionsItemSelected(item)) { - switch (item.getItemId()) { - case R.id.refresh_item: - List feeds = ((MainActivity) getActivity()).getFeeds(); - if (feeds != null) { - DBTasks.refreshAllFeeds(getActivity(), feeds); - } - return true; - case R.id.mark_all_read_item: - ConfirmationDialog conDialog = 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(getActivity()); - Toast.makeText(getActivity(), R.string.mark_all_read_msg, Toast.LENGTH_SHORT).show(); - } - }; - conDialog.createNewDialog().show(); - return true; - case R.id.episode_filter_item: - boolean newVal = !item.isChecked(); - setShowOnlyNewEpisodes(newVal); - item.setChecked(newVal); - return true; - default: - return false; - } - } else { - return true; - } - } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - ((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.new_episodes_label); - - View root = inflater.inflate(R.layout.new_episodes_fragment, container, false); - - listView = (DragSortListView) root.findViewById(android.R.id.list); - txtvEmpty = (TextView) root.findViewById(android.R.id.empty); - progLoading = (ProgressBar) root.findViewById(R.id.progLoading); - - listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - FeedItem item = (FeedItem) listAdapter.getItem(position - listView.getHeaderViewsCount()); - if (item != null) { - ((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(item.getId())); - } - - } - }); + View root = super.onCreateViewHelper(inflater, container, savedInstanceState, + R.layout.new_episodes_fragment, R.string.new_episodes_label); listView.setRemoveListener(new DragSortListView.RemoveListener() { @Override public void remove(int which) { - Log.d(TAG, "remove("+which+")"); + Log.d(TAG, "remove(" + which + ")"); stopItemLoader(); FeedItem item = (FeedItem) listView.getAdapter().getItem(which); DBWriter.markItemRead(getActivity(), item.getId(), true); @@ -307,191 +67,6 @@ public class NewEpisodesFragment extends Fragment { } } }); - - final int secondColor = (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) ? R.color.swipe_refresh_secondary_color_dark : R.color.swipe_refresh_secondary_color_light; - - if (!itemsLoaded) { - progLoading.setVisibility(View.VISIBLE); - txtvEmpty.setVisibility(View.GONE); - } - - viewsCreated = true; - - if (itemsLoaded && activity.get() != null) { - onFragmentLoaded(); - } - return root; } - - private void onFragmentLoaded() { - if (listAdapter == null) { - listAdapter = new NewEpisodesListAdapter(activity.get(), itemAccess, new DefaultActionButtonCallback(activity.get())); - listView.setAdapter(listAdapter); - listView.setEmptyView(txtvEmpty); - downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback); - downloadObserver.onResume(); - } - listAdapter.notifyDataSetChanged(); - restoreScrollPosition(); - getActivity().supportInvalidateOptionsMenu(); - updateShowOnlyEpisodesListViewState(); - } - - private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() { - @Override - public void onContentChanged() { - if (listAdapter != null) { - listAdapter.notifyDataSetChanged(); - } - } - - @Override - public void onDownloadDataAvailable(List downloaderList) { - NewEpisodesFragment.this.downloaderList = downloaderList; - if (listAdapter != null) { - listAdapter.notifyDataSetChanged(); - } - } - }; - - private NewEpisodesListAdapter.ItemAccess itemAccess = new NewEpisodesListAdapter.ItemAccess() { - - @Override - public int getCount() { - if (itemsLoaded) { - return (showOnlyNewEpisodes) ? unreadItems.size() : recentItems.size(); - } - return 0; - } - - @Override - public FeedItem getItem(int position) { - if (itemsLoaded) { - return (showOnlyNewEpisodes) ? unreadItems.get(position) : recentItems.get(position); - } - return null; - } - - @Override - public int getItemDownloadProgressPercent(FeedItem item) { - if (downloaderList != null) { - for (Downloader downloader : downloaderList) { - if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA - && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) { - return downloader.getDownloadRequest().getProgressPercent(); - } - } - } - return 0; - } - - @Override - public boolean isInQueue(FeedItem item) { - if (itemsLoaded) { - return queueAccess.contains(item.getId()); - } else { - return false; - } - } - - - }; - - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { - @Override - public void update(EventDistributor eventDistributor, Integer arg) { - if ((arg & EVENTS) != 0) { - startItemLoader(); - if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) { - getActivity().supportInvalidateOptionsMenu(); - } - } - } - }; - - private void updateShowOnlyEpisodes() { - SharedPreferences prefs = getActivity().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); - showOnlyNewEpisodes = prefs.getBoolean(PREF_EPISODE_FILTER_BOOL, true); - } - - private void setShowOnlyNewEpisodes(boolean newVal) { - showOnlyNewEpisodes = newVal; - SharedPreferences prefs = getActivity().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(PREF_EPISODE_FILTER_BOOL, showOnlyNewEpisodes); - editor.commit(); - if (itemsLoaded && viewsCreated) { - listAdapter.notifyDataSetChanged(); - activity.get().supportInvalidateOptionsMenu(); - updateShowOnlyEpisodesListViewState(); - } - } - - private void updateShowOnlyEpisodesListViewState() { - if (showOnlyNewEpisodes) { - listView.setEmptyView(null); - txtvEmpty.setVisibility(View.GONE); - } else { - listView.setEmptyView(txtvEmpty); - } - } - - private ItemLoader itemLoader; - - private void startItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); - } - itemLoader = new ItemLoader(); - itemLoader.execute(); - } - - private void stopItemLoader() { - if (itemLoader != null) { - itemLoader.cancel(true); - } - } - - private class ItemLoader extends AsyncTask { - - @Override - protected void onPreExecute() { - super.onPreExecute(); - if (viewsCreated && !itemsLoaded) { - listView.setVisibility(View.GONE); - txtvEmpty.setVisibility(View.GONE); - progLoading.setVisibility(View.VISIBLE); - } - } - - @Override - protected Object[] doInBackground(Void... params) { - Context context = activity.get(); - if (context != null) { - return new Object[]{DBReader.getUnreadItemsList(context), - DBReader.getRecentlyPublishedEpisodes(context, RECENT_EPISODES_LIMIT), - QueueAccess.IDListAccess(DBReader.getQueueIDList(context))}; - } else { - return null; - } - } - - @Override - protected void onPostExecute(Object[] lists) { - super.onPostExecute(lists); - listView.setVisibility(View.VISIBLE); - progLoading.setVisibility(View.GONE); - - if (lists != null) { - unreadItems = (List) lists[0]; - recentItems = (List) lists[1]; - queueAccess = (QueueAccess) lists[2]; - itemsLoaded = true; - if (viewsCreated && activity.get() != null) { - onFragmentLoaded(); - } - } - } - } } diff --git a/app/src/main/res/layout/all_episodes_fragment.xml b/app/src/main/res/layout/all_episodes_fragment.xml new file mode 100644 index 000000000..19db02f1d --- /dev/null +++ b/app/src/main/res/layout/all_episodes_fragment.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/new_episodes.xml b/app/src/main/res/menu/new_episodes.xml index 72661a17e..d7bcdf0f6 100644 --- a/app/src/main/res/menu/new_episodes.xml +++ b/app/src/main/res/menu/new_episodes.xml @@ -17,11 +17,4 @@ custom:showAsAction="collapseActionView" android:icon="?attr/navigation_accept"/> - - \ No newline at end of file diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java index c0f71ed55..8860653a1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java @@ -350,6 +350,19 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource return false; } + public FeedItem getMostRecentItem() { + // we could sort, but we don't need to, a simple search is fine... + Date mostRecentDate = new Date(0); + FeedItem mostRecentItem = null; + for (FeedItem item : items) { + if (item.getPubDate().after(mostRecentDate)) { + mostRecentDate = item.getPubDate(); + mostRecentItem = item; + } + } + return mostRecentItem; + } + @Override public int getTypeAsInt() { return FEEDFILETYPE_FEED; diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java index e0e370b0d..8dd6ddea7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java @@ -560,7 +560,7 @@ public final class DBTasks { /** * Adds new Feeds to the database or updates the old versions if they already exists. If another Feed with the same * identifying value already exists, this method will add new FeedItems from the new Feed to the existing Feed. - * These FeedItems will be marked as unread. + * These FeedItems will be marked as unread with the exception of the most recent FeedItem. *

* This method can update multiple feeds at once. Submitting a feed twice in the same method call can result in undefined behavior. *

@@ -586,12 +586,16 @@ public final class DBTasks { final Feed savedFeed = searchFeedByIdentifyingValueOrID(context, adapter, newFeed); if (savedFeed == null) { - if (BuildConfig.DEBUG) - Log.d(TAG, - "Found no existing Feed with title " - + newFeed.getTitle() + ". Adding as new one." - ); + Log.d(TAG, "Found no existing Feed with title " + + newFeed.getTitle() + ". Adding as new one."); + // Add a new Feed + // all new feeds will have the most recent item marked as unplayed + FeedItem mostRecent = newFeed.getMostRecentItem(); + if (mostRecent != null) { + mostRecent.setRead(false); + } + newFeedsList.add(newFeed); resultFeeds[feedIdx] = newFeed; } else {