From d568150b4a371fa0f19530c9ad8f1d191d9ca8af Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Wed, 15 Apr 2015 20:05:59 -0400 Subject: [PATCH 01/11] now saving the last navigation fragment the user had loaded --- .../danoeh/antennapod/activity/MainActivity.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) 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 2efee838d..b749788da 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -56,6 +56,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity public static final String PREF_NAME = "MainActivityPrefs"; public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch"; + public static final String PREF_LAST_FRAGMENT = "prefMainActivityLastFragment"; public static final String EXTRA_NAV_INDEX = "nav_index"; public static final String EXTRA_NAV_TYPE = "nav_type"; @@ -144,7 +145,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity if (mainFragment != null) { transaction.replace(R.id.main_view, mainFragment); } else { - loadFragment(NavListAdapter.VIEW_TYPE_NAV, POS_QUEUE, null); + loadFragment(NavListAdapter.VIEW_TYPE_NAV, getLastNavFragment(), null); } externalPlayerFragment = new ExternalPlayerFragment(); @@ -169,6 +170,18 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity checkFirstLaunch(); } + private void saveLastNavFragment(int relPos) { + SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); + SharedPreferences.Editor edit = prefs.edit(); + edit.putInt(PREF_LAST_FRAGMENT, relPos); + edit.commit(); + } + + private int getLastNavFragment() { + SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); + return prefs.getInt(PREF_LAST_FRAGMENT, POS_QUEUE); + } + private void checkFirstLaunch() { SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); if (prefs.getBoolean(PREF_IS_FIRST_LAUNCH, true)) { @@ -227,6 +240,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity } currentTitle = getString(NavListAdapter.NAV_TITLES[relPos]); selectedNavListIndex = relPos; + saveLastNavFragment(relPos); } else if (viewType == NavListAdapter.VIEW_TYPE_SUBSCRIPTION) { Feed feed = itemAccess.getItem(relPos); From 78768ae9d9e163763607fd04de5c8e1f36a5b9a5 Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Wed, 15 Apr 2015 21:12:19 -0400 Subject: [PATCH 02/11] now we have 'All Episodes' and 'New Episodes' --- .../test/antennapod/ui/MainActivityTest.java | 6 + .../de/test/antennapod/ui/PlaybackTest.java | 2 +- .../antennapod/activity/MainActivity.java | 11 +- .../antennapod/adapter/NavListAdapter.java | 20 +- .../adapter/NewEpisodesListAdapter.java | 10 - .../antennapod/fragment/EpisodesFragment.java | 457 ++++++++++++++++++ .../fragment/NewEpisodesFragment.java | 446 +---------------- app/src/main/res/layout/episodes_fragment.xml | 52 ++ app/src/main/res/menu/new_episodes.xml | 7 - 9 files changed, 551 insertions(+), 460 deletions(-) create mode 100644 app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java create mode 100644 app/src/main/res/layout/episodes_fragment.xml 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 2efee838d..1aeb1de88 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.EpisodesFragment; import de.danoeh.antennapod.fragment.DownloadsFragment; import de.danoeh.antennapod.fragment.ExternalPlayerFragment; import de.danoeh.antennapod.fragment.ItemlistFragment; @@ -68,9 +69,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; @@ -211,6 +213,9 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity case POS_NEW: fragment = new NewEpisodesFragment(); break; + case POS_ALL_EPISODES: + fragment = new EpisodesFragment(); + 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..3f3ee6e23 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,16 @@ 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, + // TODO: wouldn't be bad to have a different icon for all/new episodes + R.attr.ic_new, + R.attr.ic_new, + 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/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java new file mode 100644 index 000000000..4c861dc05 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.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.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 + */ +public class EpisodesFragment extends Fragment { + private static final String TAG = "EpisodesFragment"; + 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 = "PrefEpisodesFragment"; + private static final String PREF_KEY_LIST_TOP = "list_top"; + private static final String PREF_KEY_LIST_SELECTION = "list_selection"; + + private String prefName; + private 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 EpisodesFragment() { + // 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 EpisodesFragment(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.episodes_fragment); + } + + protected View onCreateViewHelper(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState, int fragmentResource) { + super.onCreateView(inflater, container, savedInstanceState); + ((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.new_episodes_label); + + 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) { + EpisodesFragment.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..8bb3f10c8 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,51 @@ 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 EpisodesFragment { + 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 = super.onCreateViewHelper(inflater, container, savedInstanceState, R.layout.new_episodes_fragment); - 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())); - } - - } - }); + final DragSortListView listView = (DragSortListView) root.findViewById(android.R.id.list); 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 +68,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/episodes_fragment.xml b/app/src/main/res/layout/episodes_fragment.xml new file mode 100644 index 000000000..19db02f1d --- /dev/null +++ b/app/src/main/res/layout/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 From 0172951d4edf5554232b4bf6ab3a0b94fc3c63ff Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Wed, 15 Apr 2015 21:26:08 -0400 Subject: [PATCH 03/11] now properly setting the title and changed the icon for 'All Episodes' --- .../de/danoeh/antennapod/adapter/NavListAdapter.java | 4 ++-- .../danoeh/antennapod/fragment/EpisodesFragment.java | 11 ++++++++--- .../antennapod/fragment/NewEpisodesFragment.java | 3 ++- 3 files changed, 12 insertions(+), 6 deletions(-) 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 3f3ee6e23..837c7745f 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -46,9 +46,9 @@ public class NavListAdapter extends BaseAdapter { TypedArray ta = context.obtainStyledAttributes(new int[]{ R.attr.stat_playlist, - // TODO: wouldn't be bad to have a different icon for all/new episodes - R.attr.ic_new, R.attr.ic_new, + // TODO: wouldn't be bad to have a different icon for queue and all episodes + R.attr.stat_playlist, R.attr.av_download, R.attr.ic_history, R.attr.content_new}); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java index 4c861dc05..c13fec407 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java @@ -264,12 +264,17 @@ public class EpisodesFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return onCreateViewHelper(inflater, container, savedInstanceState, R.layout.episodes_fragment); + return onCreateViewHelper(inflater, container, savedInstanceState, + R.layout.episodes_fragment, R.string.all_episodes_label); } - protected View onCreateViewHelper(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState, int fragmentResource) { + protected View onCreateViewHelper(LayoutInflater inflater, + ViewGroup container, + Bundle savedInstanceState, + int fragmentResource, + int titleString) { super.onCreateView(inflater, container, savedInstanceState); - ((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.new_episodes_label); + ((MainActivity) getActivity()).getSupportActionBar().setTitle(titleString); View root = inflater.inflate(fragmentResource, container, false); 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 8bb3f10c8..b3dad1914 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -38,7 +38,8 @@ public class NewEpisodesFragment extends EpisodesFragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View root = super.onCreateViewHelper(inflater, container, savedInstanceState, R.layout.new_episodes_fragment); + View root = super.onCreateViewHelper(inflater, container, savedInstanceState, + R.layout.new_episodes_fragment, R.string.new_episodes_label); final DragSortListView listView = (DragSortListView) root.findViewById(android.R.id.list); From 6d3fe6dd3e0da6ae6cd0892067cf387f88ee2ce8 Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Thu, 16 Apr 2015 19:04:59 -0400 Subject: [PATCH 04/11] using different icon for the 'All Episodes' fragment --- .../main/java/de/danoeh/antennapod/adapter/NavListAdapter.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 837c7745f..17cd71f86 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -47,8 +47,7 @@ public class NavListAdapter extends BaseAdapter { TypedArray ta = context.obtainStyledAttributes(new int[]{ R.attr.stat_playlist, R.attr.ic_new, - // TODO: wouldn't be bad to have a different icon for queue and all episodes - R.attr.stat_playlist, + R.attr.feed, R.attr.av_download, R.attr.ic_history, R.attr.content_new}); From b721a5d35fc40d6022247ad8c01615789ec38814 Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Thu, 16 Apr 2015 19:09:26 -0400 Subject: [PATCH 05/11] renamed EpisodesFragment to AllEpisodesFragment --- .../antennapod/activity/MainActivity.java | 4 ++-- ...Fragment.java => AllEpisodesFragment.java} | 19 +++++++------------ .../fragment/NewEpisodesFragment.java | 2 +- ...fragment.xml => all_episodes_fragment.xml} | 0 4 files changed, 10 insertions(+), 15 deletions(-) rename app/src/main/java/de/danoeh/antennapod/fragment/{EpisodesFragment.java => AllEpisodesFragment.java} (95%) rename app/src/main/res/layout/{episodes_fragment.xml => all_episodes_fragment.xml} (100%) 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 1aeb1de88..81973e3df 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -34,7 +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.EpisodesFragment; +import de.danoeh.antennapod.fragment.AllEpisodesFragment; import de.danoeh.antennapod.fragment.DownloadsFragment; import de.danoeh.antennapod.fragment.ExternalPlayerFragment; import de.danoeh.antennapod.fragment.ItemlistFragment; @@ -214,7 +214,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity fragment = new NewEpisodesFragment(); break; case POS_ALL_EPISODES: - fragment = new EpisodesFragment(); + fragment = new AllEpisodesFragment(); break; case POS_QUEUE: fragment = new QueueFragment(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java similarity index 95% rename from app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java rename to app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index c13fec407..f95672e27 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -7,10 +7,8 @@ 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; @@ -37,7 +35,6 @@ 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; @@ -45,16 +42,14 @@ 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 */ -public class EpisodesFragment extends Fragment { - private static final String TAG = "EpisodesFragment"; +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 | @@ -62,7 +57,7 @@ public class EpisodesFragment extends Fragment { EventDistributor.PLAYER_STATUS_UPDATE; private static final int RECENT_EPISODES_LIMIT = 150; - private static final String DEFAULT_PREF_NAME = "PrefEpisodesFragment"; + 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"; @@ -87,7 +82,7 @@ public class EpisodesFragment extends Fragment { private boolean isUpdatingFeeds; - public EpisodesFragment() { + public AllEpisodesFragment() { // by default we show all the episodes this(false, DEFAULT_PREF_NAME); } @@ -96,7 +91,7 @@ public class EpisodesFragment extends Fragment { // 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 EpisodesFragment(boolean showOnlyNewEpisodes, String prefName) { + protected AllEpisodesFragment(boolean showOnlyNewEpisodes, String prefName) { this.showOnlyNewEpisodes = showOnlyNewEpisodes; this.prefName = prefName; } @@ -265,7 +260,7 @@ public class EpisodesFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return onCreateViewHelper(inflater, container, savedInstanceState, - R.layout.episodes_fragment, R.string.all_episodes_label); + R.layout.all_episodes_fragment, R.string.all_episodes_label); } protected View onCreateViewHelper(LayoutInflater inflater, @@ -331,7 +326,7 @@ public class EpisodesFragment extends Fragment { @Override public void onDownloadDataAvailable(List downloaderList) { - EpisodesFragment.this.downloaderList = downloaderList; + AllEpisodesFragment.this.downloaderList = downloaderList; if (listAdapter != null) { listAdapter.notifyDataSetChanged(); } 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 b3dad1914..1dc5fd51e 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -19,7 +19,7 @@ import de.danoeh.antennapod.core.util.gui.UndoBarController; * Like 'EpisodesFragment' except that it only shows new episodes and * supports swiping to mark as read. */ -public class NewEpisodesFragment extends EpisodesFragment { +public class NewEpisodesFragment extends AllEpisodesFragment { private static final String TAG = "NewEpisodesFragment"; private static final String PREF_NAME = "PrefNewEpisodesFragment"; diff --git a/app/src/main/res/layout/episodes_fragment.xml b/app/src/main/res/layout/all_episodes_fragment.xml similarity index 100% rename from app/src/main/res/layout/episodes_fragment.xml rename to app/src/main/res/layout/all_episodes_fragment.xml From b0b228303c33550624835ecdb5f98c1d7c85dfe5 Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Thu, 16 Apr 2015 19:11:36 -0400 Subject: [PATCH 06/11] reuse listView from AllEpisodesFragment --- .../java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java | 2 +- .../java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index f95672e27..58b6f734f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -62,7 +62,7 @@ public class AllEpisodesFragment extends Fragment { private static final String PREF_KEY_LIST_SELECTION = "list_selection"; private String prefName; - private DragSortListView listView; + protected DragSortListView listView; private NewEpisodesListAdapter listAdapter; private TextView txtvEmpty; private ProgressBar progLoading; 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 1dc5fd51e..c2bbae42d 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -41,8 +41,6 @@ public class NewEpisodesFragment extends AllEpisodesFragment { View root = super.onCreateViewHelper(inflater, container, savedInstanceState, R.layout.new_episodes_fragment, R.string.new_episodes_label); - final DragSortListView listView = (DragSortListView) root.findViewById(android.R.id.list); - listView.setRemoveListener(new DragSortListView.RemoveListener() { @Override public void remove(int which) { From 0420bf47f48380f0b72fe245ac0990bb2cd7368d Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Thu, 16 Apr 2015 20:09:11 -0400 Subject: [PATCH 07/11] the most recent episode in new feeds is marked unplayed --- .../de/danoeh/antennapod/core/feed/Feed.java | 10 ++++++++++ .../danoeh/antennapod/core/storage/DBTasks.java | 16 ++++++++++------ 2 files changed, 20 insertions(+), 6 deletions(-) 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..90edd50bc 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,16 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource return false; } + public FeedItem getMostRecentItem(boolean enableEpisodeFilter) { + // we're going to assume the most recent item is the first one... + // we can sort later if needed + int numItems = getNumOfItems(enableEpisodeFilter); + if (numItems > 0) { + return getItemAtIndex(enableEpisodeFilter, 0); + } + return null; + } + @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..0624b0396 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(false); + if (mostRecent != null) { + mostRecent.setRead(false); + } + newFeedsList.add(newFeed); resultFeeds[feedIdx] = newFeed; } else { From 4e9597a95075e8384c6545b8d6c9a63be3c22e48 Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Thu, 16 Apr 2015 20:17:09 -0400 Subject: [PATCH 08/11] now actually using the date to figure out what is omst recent rather than just assuming it's the first --- .../de/danoeh/antennapod/core/feed/Feed.java | 17 ++++++++++------- .../danoeh/antennapod/core/storage/DBTasks.java | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) 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 90edd50bc..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,14 +350,17 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource return false; } - public FeedItem getMostRecentItem(boolean enableEpisodeFilter) { - // we're going to assume the most recent item is the first one... - // we can sort later if needed - int numItems = getNumOfItems(enableEpisodeFilter); - if (numItems > 0) { - return getItemAtIndex(enableEpisodeFilter, 0); + 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 null; + return mostRecentItem; } @Override 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 0624b0396..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 @@ -591,7 +591,7 @@ public final class DBTasks { // Add a new Feed // all new feeds will have the most recent item marked as unplayed - FeedItem mostRecent = newFeed.getMostRecentItem(false); + FeedItem mostRecent = newFeed.getMostRecentItem(); if (mostRecent != null) { mostRecent.setRead(false); } From c712fcf4e2a688cdec5bb7020429bee5c0afd4b2 Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Thu, 16 Apr 2015 21:18:28 -0400 Subject: [PATCH 09/11] updating build number for 1.1.1 alpha test --- app/src/main/AndroidManifest.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a04b1df07..0ffc4af9e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,8 +1,8 @@ + android:versionCode="50" + android:versionName="1.1.1"> From 91fb7e6c2a3224b6fe643a3f22978b4b8badfeca Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Fri, 17 Apr 2015 17:29:00 -0400 Subject: [PATCH 10/11] Fixed some NullPointerExceptions These were happening on some handsets when onPrepareOptionsMenu was getting called. It is sometimes called before the menu is actually loaded, causing us to not find the MenuItem we're looking for. This solves the symptom, if not the cause. After making this change the number of failures reported on Apkudo dropped from 20 devices to just 6. --- .../de/danoeh/antennapod/fragment/AllEpisodesFragment.java | 5 ++++- .../danoeh/antennapod/fragment/PlaybackHistoryFragment.java | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index 58b6f734f..2eef7b635 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -219,7 +219,10 @@ public class AllEpisodesFragment extends Fragment { 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()); + MenuItem menuItem = menu.findItem(R.id.mark_all_read_item); + if (menuItem != null) { + menuItem.setVisible(unreadItems != null && !unreadItems.isEmpty()); + } } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java index ab38af106..9002227d6 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java @@ -146,7 +146,10 @@ public class PlaybackHistoryFragment extends ListFragment { public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) { - menu.findItem(R.id.clear_history_item).setVisible(playbackHistory != null && !playbackHistory.isEmpty()); + MenuItem menuItem = menu.findItem(R.id.clear_history_item); + if (menuItem != null) { + menuItem.setVisible(playbackHistory != null && !playbackHistory.isEmpty()); + } } } From 6142ed7280e7a96e127cc1f129db78e9abcb0921 Mon Sep 17 00:00:00 2001 From: Tom Hennen Date: Fri, 17 Apr 2015 17:37:10 -0400 Subject: [PATCH 11/11] updated build number for new release candidate --- app/src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0ffc4af9e..7a4c8366e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,7 +1,7 @@