Merge pull request #748 from TomHennen/new_and_all_episodes

New and all episodes fixes AntennaPod/AntennaPod#741 and fixes AntennaPod/AntennaPod#742
This commit is contained in:
Tom Hennen 2015-04-16 21:08:41 -04:00
commit beabcf4302
11 changed files with 573 additions and 467 deletions

View File

@ -73,6 +73,12 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
// all episodes
openNavDrawer();
solo.clickOnText(solo.getString(R.string.all_episodes_label));
solo.waitForView(android.R.id.list);
assertEquals(solo.getString(R.string.all_episodes_label), getActionbarTitle());
// new episodes
openNavDrawer();
solo.clickOnText(solo.getString(R.string.new_episodes_label));
solo.waitForView(android.R.id.list);
assertEquals(solo.getString(R.string.new_episodes_label), getActionbarTitle());

View File

@ -79,7 +79,7 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
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));

View File

@ -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;

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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<FeedItem> unreadItems;
private List<FeedItem> recentItems;
private QueueAccess queueAccess;
private List<Downloader> downloaderList;
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
private boolean showOnlyNewEpisodes = false;
private AtomicReference<MainActivity> activity = new AtomicReference<MainActivity>();
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<Feed> 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<Downloader> 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<Void, Void, Object[]> {
@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<FeedItem>) lists[0];
recentItems = (List<FeedItem>) lists[1];
queueAccess = (QueueAccess) lists[2];
itemsLoaded = true;
if (viewsCreated && activity.get() != null) {
onFragmentLoaded();
}
}
}
}
}

View File

@ -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<FeedItem> unreadItems;
private List<FeedItem> recentItems;
private QueueAccess queueAccess;
private List<Downloader> downloaderList;
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
private boolean showOnlyNewEpisodes = false;
private AtomicReference<MainActivity> activity = new AtomicReference<MainActivity>();
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<Feed> 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<Downloader> 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<Void, Void, Object[]> {
@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<FeedItem>) lists[0];
recentItems = (List<FeedItem>) lists[1];
queueAccess = (QueueAccess) lists[2];
itemsLoaded = true;
if (viewsCreated && activity.get() != null) {
onFragmentLoaded();
}
}
}
}
}

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dslv="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.mobeta.android.dslv.DragSortListView
android:id="@android:id/list"
android:scrollbarStyle="outsideOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="@dimen/list_vertical_padding"
android:paddingBottom="@dimen/list_vertical_padding"
android:clipToPadding="false"
dslv:collapsed_height="2dp"
dslv:drag_enabled="false"
dslv:drag_scroll_start="0.33"
dslv:float_alpha="0.6"
dslv:max_drag_scroll_speed="0.5"
dslv:remove_enabled="true"
dslv:remove_mode="flingRemove"
dslv:slide_shuffle_speed="0.3"
dslv:sort_enabled="false"
dslv:track_drag_sort="false"
dslv:float_background_color="?attr/dragview_float_background"
dslv:use_default_controller="true"
tools:background="@android:color/holo_green_dark"/>
<TextView
android:id="@id/android:empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/no_items_label"/>
<ProgressBar
android:id="@+id/progLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminateOnly="true"
android:visibility="gone"
tools:visibility="visible"
tools:layout_width="match_parent"
tools:layout_height="64dp"
tools:background="@android:color/holo_red_light"/>
</FrameLayout>

View File

@ -17,11 +17,4 @@
custom:showAsAction="collapseActionView"
android:icon="?attr/navigation_accept"/>
<item
android:id="@+id/episode_filter_item"
android:title="@string/episode_filter_label"
android:menuCategory="container"
android:checkable="true"
custom:showAsAction="collapseActionView"/>
</menu>

View File

@ -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;

View File

@ -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.
* <p/>
* This method can update multiple feeds at once. Submitting a feed twice in the same method call can result in undefined behavior.
* <p/>
@ -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 {