diff options
author | Tom Hennen <TomHennen@users.noreply.github.com> | 2015-11-05 06:52:24 -0500 |
---|---|---|
committer | Tom Hennen <TomHennen@users.noreply.github.com> | 2015-11-05 06:52:24 -0500 |
commit | 3b003c60abb7ccf461ea5f4eae35e69cdda2ecd2 (patch) | |
tree | bea911f0dab76ae175b879bb324315b9f172ab70 /app/src/main/java/de/danoeh/antennapod | |
parent | 58d1ba69f00df2519102d5c4a7f0f9f258ff3f33 (diff) | |
parent | e03ef16558a3d604860f7f14a895c1b7f022f425 (diff) | |
download | AntennaPod-3b003c60abb7ccf461ea5f4eae35e69cdda2ecd2.zip |
Merge pull request #1336 from TomHennen/recycler_view
Recycler view for Episodes Fragment
Diffstat (limited to 'app/src/main/java/de/danoeh/antennapod')
-rw-r--r-- | app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java | 73 | ||||
-rw-r--r-- | app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java (renamed from app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java) | 196 | ||||
-rw-r--r-- | app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java | 198 | ||||
-rw-r--r-- | app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java | 77 | ||||
-rw-r--r-- | app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java | 6 | ||||
-rw-r--r-- | app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java | 92 | ||||
-rw-r--r-- | app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java | 4 | ||||
-rw-r--r-- | app/src/main/java/de/danoeh/antennapod/view/DividerItemDecoration.java | 124 |
8 files changed, 399 insertions, 371 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java index e92599561..a92eefc00 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -153,7 +153,6 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity navList.setAdapter(navAdapter); navList.setOnItemClickListener(navListClickListener); navList.setOnItemLongClickListener(newListLongClickListener); - registerForContextMenu(navList); navAdapter.registerDataSetObserver(new DataSetObserver() { @Override @@ -522,78 +521,6 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity } } - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - if(v.getId() != R.id.nav_list) { - return; - } - AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; - int position = adapterInfo.position; - if(position < navAdapter.getSubscriptionOffset()) { - return; - } - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.nav_feed_context, menu); - Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); - menu.setHeaderTitle(feed.getTitle()); - // episodes are not loaded, so we cannot check if the podcast has new or unplayed ones! - - // we may need to reference this elsewhere... - lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); - - if(menuInfo == null) { - menuInfo = lastMenuInfo; - } - - if(menuInfo == null - || menuInfo.targetView == null - || menuInfo.targetView.getParent() == null - || menuInfo.targetView.getParent() instanceof ListView == false - || ((ListView)menuInfo.targetView.getParent()).getId() != R.id.nav_list) { - return false; - } - final int position = menuInfo.position; - Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); - switch(item.getItemId()) { - case R.id.mark_all_seen_item: - DBWriter.markFeedSeen(feed.getId()); - return true; - case R.id.mark_all_read_item: - DBWriter.markFeedRead(feed.getId()); - return true; - case R.id.remove_item: - final FeedRemover remover = new FeedRemover(this, feed) { - @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); - if(getSelectedNavListIndex() == position) { - loadFragment(EpisodesFragment.TAG, null); - } - } - }; - ConfirmationDialog conDialog = new ConfirmationDialog(this, - R.string.remove_feed_label, - R.string.feed_delete_confirmation_msg) { - @Override - public void onConfirmButtonPressed( - DialogInterface dialog) { - dialog.dismiss(); - remover.executeAsync(); - } - }; - conDialog.createNewDialog().show(); - return true; - default: - return super.onContextItemSelected(item); - } - } - private DBReader.NavDrawerData navDrawerData; private AsyncTask<Void, Void, DBReader.NavDrawerData> loadTask; private int selectedNavListIndex = 0; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java index b1b85da9f..c72c95feb 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java @@ -3,12 +3,15 @@ package de.danoeh.antennapod.adapter; import android.content.Context; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.support.v7.widget.PopupMenu; +import android.support.v7.widget.RecyclerView; import android.text.format.DateUtils; import android.util.Log; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.BaseAdapter; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ProgressBar; @@ -23,29 +26,38 @@ import com.joanzapata.iconify.Iconify; import java.lang.ref.WeakReference; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.glide.ApGlideSettings; +import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.NetworkUtils; +import de.danoeh.antennapod.fragment.ItemFragment; +import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; /** * List adapter for the list of new episodes */ -public class AllEpisodesListAdapter extends BaseAdapter { +public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesRecycleAdapter.Holder> { - private static final String TAG = AllEpisodesListAdapter.class.getSimpleName(); + private static final String TAG = AllEpisodesRecycleAdapter.class.getSimpleName(); private final Context context; private final ItemAccess itemAccess; private final ActionButtonCallback actionButtonCallback; private final ActionButtonUtils actionButtonUtils; private final boolean showOnlyNewEpisodes; + private final WeakReference<MainActivity> mainActivityRef; - public AllEpisodesListAdapter(Context context, ItemAccess itemAccess, ActionButtonCallback actionButtonCallback, - boolean showOnlyNewEpisodes) { + public AllEpisodesRecycleAdapter(Context context, + MainActivity mainActivity, + ItemAccess itemAccess, + ActionButtonCallback actionButtonCallback, + boolean showOnlyNewEpisodes) { super(); + this.mainActivityRef = new WeakReference<>(mainActivity); this.context = context; this.itemAccess = itemAccess; this.actionButtonUtils = new ActionButtonUtils(context); @@ -54,55 +66,38 @@ public class AllEpisodesListAdapter extends BaseAdapter { } @Override - public int getCount() { - return itemAccess.getCount(); - } - - @Override - public Object getItem(int position) { - return itemAccess.getItem(position); - } - - @Override - public long getItemId(int position) { - return position; + public Holder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.new_episodes_listitem, parent, false); + Holder holder = new Holder(view); + holder.placeholder = (TextView) view.findViewById(R.id.txtvPlaceholder); + holder.title = (TextView) view.findViewById(R.id.txtvTitle); + holder.pubDate = (TextView) view + .findViewById(R.id.txtvPublished); + holder.statusUnread = view.findViewById(R.id.statusUnread); + holder.butSecondary = (ImageButton) view + .findViewById(R.id.butSecondaryAction); + holder.queueStatus = (ImageView) view + .findViewById(R.id.imgvInPlaylist); + holder.progress = (ProgressBar) view + .findViewById(R.id.pbar_progress); + holder.cover = (ImageView) view.findViewById(R.id.imgvCover); + holder.txtvDuration = (TextView) view.findViewById(R.id.txtvDuration); + holder.item = null; + holder.mainActivityRef = mainActivityRef; + holder.position = -1; + // so we can grab this later + view.setTag(holder); + + return holder; } @Override - public int getViewTypeCount() { - return 1; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - Holder holder; - final FeedItem item = (FeedItem) getItem(position); - if (item == null) return null; - - if (convertView == null) { - holder = new Holder(); - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - convertView = inflater.inflate(R.layout.new_episodes_listitem, - parent, false); - holder.placeholder = (TextView) convertView.findViewById(R.id.txtvPlaceholder); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); - holder.pubDate = (TextView) convertView - .findViewById(R.id.txtvPublished); - holder.statusUnread = convertView.findViewById(R.id.statusUnread); - holder.butSecondary = (ImageButton) convertView - .findViewById(R.id.butSecondaryAction); - holder.queueStatus = (ImageView) convertView - .findViewById(R.id.imgvInPlaylist); - holder.progress = (ProgressBar) convertView - .findViewById(R.id.pbar_progress); - holder.cover = (ImageView) convertView.findViewById(R.id.imgvCover); - holder.txtvDuration = (TextView) convertView.findViewById(R.id.txtvDuration); - convertView.setTag(holder); - } else { - holder = (Holder) convertView.getTag(); - } - + public void onBindViewHolder(final Holder holder, int position) { + final FeedItem item = itemAccess.getItem(position); + if (item == null) return; + holder.item = item; + holder.position = position; holder.placeholder.setVisibility(View.VISIBLE); holder.placeholder.setText(item.getFeed().getTitle()); holder.title.setText(item.getTitle()); @@ -146,7 +141,7 @@ public class AllEpisodesListAdapter extends BaseAdapter { // item is being downloaded holder.progress.setProgress(itemAccess.getItemDownloadProgressPercent(item)); } else if (state == FeedItem.State.PLAYING - || state == FeedItem.State.IN_PROGRESS) { + || state == FeedItem.State.IN_PROGRESS) { if (media.getDuration() > 0) { int progress = (int) (100.0 * media.getPosition() / media.getDuration()); holder.progress.setProgress(progress); @@ -178,8 +173,20 @@ public class AllEpisodesListAdapter extends BaseAdapter { .fitCenter() .dontAnimate() .into(new CoverTarget(item.getFeed().getImageUri(), holder.placeholder, holder.cover)); + } - return convertView; + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemCount() { + return itemAccess.getCount(); + } + + public FeedItem getItem(int position) { + return itemAccess.getItem(position); } private class CoverTarget extends GlideDrawableImageViewTarget { @@ -228,8 +235,63 @@ public class AllEpisodesListAdapter extends BaseAdapter { } }; + private Menu popupMenu; + private final FeedItemMenuHandler.MenuInterface contextMenuInterface = new FeedItemMenuHandler.MenuInterface() { + @Override + public void setItemVisibility(int id, boolean visible) { + if(popupMenu == null) { + return; + } + MenuItem item = popupMenu.findItem(id); + if (item != null) { + item.setVisible(visible); + } + } + }; + + private final boolean showContextMenu(View view) { + // Create a PopupMenu, giving it the clicked view for an anchor + MainActivity mainActivity = this.mainActivityRef.get(); + if (mainActivity == null) { + Log.d(TAG, "mainActivity is null"); + return false; + } + PopupMenu popup = new PopupMenu(mainActivity, view); + Menu menu = popup.getMenu(); - static class Holder { + // Inflate our menu resource into the PopupMenu's Menu + popup.getMenuInflater().inflate(R.menu.allepisodes_context, popup.getMenu()); + + Holder holder = (Holder) view.getTag(); + FeedItem item = holder.item; + if (item == null) { + return false; + } + + popupMenu = menu; + FeedItemMenuHandler.onPrepareMenu(context, contextMenuInterface, item, true, null); + + // Set a listener so we are notified if a menu item is clicked + popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem menuItem) { + try { + FeedItemMenuHandler.onMenuItemClicked(context, menuItem.getItemId(), item); + return true; + } catch (DownloadRequestException e) { + e.printStackTrace(); + } + return false; + } + }); + + popup.show(); + return true; + } + + + public class Holder extends RecyclerView.ViewHolder + implements View.OnClickListener, View.OnLongClickListener{ TextView placeholder; TextView title; TextView pubDate; @@ -239,6 +301,32 @@ public class AllEpisodesListAdapter extends BaseAdapter { ProgressBar progress; TextView txtvDuration; ImageButton butSecondary; + FeedItem item; + WeakReference<MainActivity> mainActivityRef; + int position; + + public Holder(View itemView) { + super(itemView); + itemView.setOnClickListener(this); + itemView.setOnLongClickListener(this); + } + + @Override + public void onClick(View v) { + MainActivity mainActivity = mainActivityRef.get(); + if (mainActivity != null) { + mainActivity.loadChildFragment(ItemFragment.newInstance(item.getId())); + } + } + + public FeedItem getFeedItem() { return item; } + + public int getItemPosition() { return position; } + + @Override + public boolean onLongClick(View view) { + return showContextMenu(view); + } } public interface ItemAccess { 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 31b24773f..c3249771f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -7,30 +7,26 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.support.v4.app.Fragment; -import android.support.v4.util.Pair; import android.support.v4.view.MenuItemCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.support.v7.widget.SearchView; import android.util.Log; -import android.view.ContextMenu; 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.AllEpisodesListAdapter; +import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter; import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; import de.danoeh.antennapod.core.asynctask.DownloadObserver; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; @@ -43,11 +39,9 @@ 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.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; -import de.danoeh.antennapod.core.util.LongList; -import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; +import de.danoeh.antennapod.view.DividerItemDecoration; import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; @@ -68,24 +62,18 @@ public class AllEpisodesFragment extends Fragment { 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 static final String PREF_SCROLL_POSITION = "scroll_position"; + private static final String PREF_SCROLL_OFFSET = "scroll_offset"; - private String prefName; - protected DragSortListView listView; - private AllEpisodesListAdapter listAdapter; - private TextView txtvEmpty; + protected RecyclerView recyclerView; + private AllEpisodesRecycleAdapter listAdapter; private ProgressBar progLoading; - private ContextMenu contextMenu; - private AdapterView.AdapterContextMenuInfo lastMenuInfo = null; private List<FeedItem> episodes; - private LongList queuedItemsIds; private List<Downloader> downloaderList; private boolean itemsLoaded = false; private boolean viewsCreated = false; - private final boolean showOnlyNewEpisodes; private AtomicReference<MainActivity> activity = new AtomicReference<MainActivity>(); @@ -94,20 +82,10 @@ public class AllEpisodesFragment extends Fragment { private boolean isUpdatingFeeds; protected Subscription subscription; + private LinearLayoutManager layoutManager; - 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; - } + protected boolean showOnlyNewEpisodes() { return false; } + protected String getPrefName() { return DEFAULT_PREF_NAME; } @Override public void onCreate(Bundle savedInstanceState) { @@ -163,25 +141,32 @@ public class AllEpisodesFragment extends Fragment { } private void saveScrollPosition() { - SharedPreferences prefs = getActivity().getSharedPreferences(prefName, Context.MODE_PRIVATE); + int firstItem = layoutManager.findFirstVisibleItemPosition(); + View firstItemView = layoutManager.findViewByPosition(firstItem); + float topOffset; + if(firstItemView == null) { + topOffset = 0; + } else { + topOffset = firstItemView.getTop(); + } + + SharedPreferences prefs = getActivity().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); - 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.putInt(PREF_SCROLL_POSITION, firstItem); + editor.putFloat(PREF_SCROLL_OFFSET, topOffset); 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); + SharedPreferences prefs = getActivity().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE); + int position = prefs.getInt(PREF_SCROLL_POSITION, 0); + float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f); + if (position > 0 || offset > 0) { + layoutManager.scrollToPositionWithOffset(position, (int) offset); // restore once, then forget SharedPreferences.Editor editor = prefs.edit(); - editor.putInt(PREF_KEY_LIST_SELECTION, 0); - editor.putInt(PREF_KEY_LIST_TOP, 0); + editor.putInt(PREF_SCROLL_POSITION, 0); + editor.putFloat(PREF_SCROLL_OFFSET, 0.0f); editor.commit(); } } @@ -289,26 +274,17 @@ public class AllEpisodesFragment extends Fragment { 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())); - } - - } - }); + recyclerView = (RecyclerView) root.findViewById(android.R.id.list); + layoutManager = new LinearLayoutManager(getActivity()); + recyclerView.setLayoutManager(layoutManager); + recyclerView.setHasFixedSize(true); + RecyclerView.ItemDecoration itemDecoration = new DividerItemDecoration(getActivity(), null); + recyclerView.addItemDecoration(itemDecoration); - registerForContextMenu(listView); + progLoading = (ProgressBar) root.findViewById(R.id.progLoading); if (!itemsLoaded) { progLoading.setVisibility(View.VISIBLE); - txtvEmpty.setVisibility(View.GONE); } viewsCreated = true; @@ -320,80 +296,11 @@ public class AllEpisodesFragment extends Fragment { return root; } - private final FeedItemMenuHandler.MenuInterface contextMenuInterface = new FeedItemMenuHandler.MenuInterface() { - @Override - public void setItemVisibility(int id, boolean visible) { - if(contextMenu == null) { - return; - } - MenuItem item = contextMenu.findItem(id); - if (item != null) { - item.setVisible(visible); - } - } - }; - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; - FeedItem item = itemAccess.getItem(adapterInfo.position); - MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.allepisodes_context, menu); - - if (item != null) { - menu.setHeaderTitle(item.getTitle()); - } - - contextMenu = menu; - lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; - FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queuedItemsIds); - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - if (!getUserVisibleHint()) { - // we're not visible, don't do anything. - return false; - } - AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); - if (menuInfo == null) { - menuInfo = lastMenuInfo; - } - if (menuInfo == null) { - Log.e(TAG, "menuInfo is null, not doing anything"); - return false; - } - - FeedItem selectedItem = null; - - // make sure the item still makes sense - if (menuInfo.position >= 0 && menuInfo.position < itemAccess.getCount()) { - selectedItem = itemAccess.getItem(menuInfo.position); - } else { - Log.d(TAG, "Selected item at position " + menuInfo.position + " does not exist, only " + itemAccess.getCount() + " items available"); - } - - if (selectedItem == null) { - Log.i(TAG, "Selected item at position " + menuInfo.position + " was null, ignoring selection"); - return super.onContextItemSelected(item); - } - - try { - return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem); - } catch (DownloadRequestException e) { - e.printStackTrace(); - Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show(); - return true; - } - } - private void onFragmentLoaded() { if (listAdapter == null) { - listAdapter = new AllEpisodesListAdapter(activity.get(), itemAccess, - new DefaultActionButtonCallback(activity.get()), showOnlyNewEpisodes); - listView.setAdapter(listAdapter); - listView.setEmptyView(txtvEmpty); + listAdapter = new AllEpisodesRecycleAdapter(activity.get(), activity.get(), itemAccess, + new DefaultActionButtonCallback(activity.get()), showOnlyNewEpisodes()); + recyclerView.setAdapter(listAdapter); downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback); downloadObserver.onResume(); } @@ -413,7 +320,7 @@ public class AllEpisodesFragment extends Fragment { } }; - private AllEpisodesListAdapter.ItemAccess itemAccess = new AllEpisodesListAdapter.ItemAccess() { + protected AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() { @Override public int getCount() { @@ -446,11 +353,10 @@ public class AllEpisodesFragment extends Fragment { @Override public boolean isInQueue(FeedItem item) { - if (itemsLoaded) { - return queuedItemsIds.contains(item.getId()); - } else { - return false; + if (item != null) { + return item.isTagged(FeedItem.TAG_QUEUE); } + return false; } }; @@ -467,7 +373,6 @@ public class AllEpisodesFragment extends Fragment { }; private void updateShowOnlyEpisodesListViewState() { - listView.setEmptyView(txtvEmpty); } protected void loadItems() { @@ -475,19 +380,17 @@ public class AllEpisodesFragment extends Fragment { subscription.unsubscribe(); } if (viewsCreated && !itemsLoaded) { - listView.setVisibility(View.GONE); - txtvEmpty.setVisibility(View.GONE); + recyclerView.setVisibility(View.GONE); progLoading.setVisibility(View.VISIBLE); } subscription = Observable.defer(() -> Observable.just(loadData())) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(data -> { - listView.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.VISIBLE); progLoading.setVisibility(View.GONE); if (data != null) { - episodes = data.first; - queuedItemsIds = data.second; + episodes = data; itemsLoaded = true; if (viewsCreated && activity.get() != null) { onFragmentLoaded(); @@ -498,11 +401,8 @@ public class AllEpisodesFragment extends Fragment { }); } - protected Pair<List<FeedItem>,LongList> loadData() { - List<FeedItem> items; - items = DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT); - LongList queuedIds = DBReader.getQueueIDList(); - return Pair.create(items, queuedIds); + protected List<FeedItem> loadData() { + return DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java index 95f7cfcc1..532516dda 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java @@ -1,8 +1,9 @@ package de.danoeh.antennapod.fragment; -import android.content.Context; import android.os.Bundle; -import android.support.v4.util.Pair; +import android.support.design.widget.Snackbar; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -11,13 +12,11 @@ import android.view.ViewGroup; import java.util.List; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter; import de.danoeh.antennapod.core.event.FavoritesEvent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; -import de.danoeh.antennapod.core.util.LongList; -import de.danoeh.antennapod.core.util.gui.FeedItemUndoToken; -import de.danoeh.antennapod.core.util.gui.UndoBarController; import de.greenrobot.event.EventBus; @@ -32,11 +31,11 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment { private static final String PREF_NAME = "PrefFavoriteEpisodesFragment"; - private UndoBarController undoBarController; + @Override + protected boolean showOnlyNewEpisodes() { return true; } - public FavoriteEpisodesFragment() { - super(false, PREF_NAME); - } + @Override + protected String getPrefName() { return PREF_NAME; } public void onEvent(FavoritesEvent event) { Log.d(TAG, "onEvent(" + event + ")"); @@ -58,54 +57,48 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment { @Override protected void resetViewState() { super.resetViewState(); - undoBarController = null; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = super.onCreateViewHelper(inflater, container, savedInstanceState, - R.layout.episodes_fragment_with_undo); + R.layout.all_episodes_fragment); - listView.setRemoveListener(which -> { - Log.d(TAG, "remove(" + which + ")"); - if (subscription != null) { - subscription.unsubscribe(); + ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + return false; } - FeedItem item = (FeedItem) listView.getAdapter().getItem(which); - - DBWriter.removeFavoriteItem(item); - - undoBarController.showUndoBar(false, - getString(R.string.removed_item), new FeedItemUndoToken(item, - which) - ); - }); - - undoBarController = new UndoBarController<FeedItemUndoToken>(root.findViewById(R.id.undobar), new UndoBarController.UndoListener<FeedItemUndoToken>() { - - private final Context context = getActivity(); @Override - public void onUndo(FeedItemUndoToken token) { - if (token != null) { - long itemId = token.getFeedItemId(); - DBWriter.addFavoriteItemById(itemId); + public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) { + AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder; + Log.d(TAG, "remove(" + holder.getItemId() + ")"); + + if (subscription != null) { + subscription.unsubscribe(); + } + FeedItem item = holder.getFeedItem(); + if (item != null) { + DBWriter.removeFavoriteItem(item); + + Snackbar snackbar = Snackbar.make(root, getString(R.string.removed_item), + Snackbar.LENGTH_LONG); + snackbar.setAction(getString(R.string.undo), v -> { + DBWriter.addFavoriteItem(item); + }); + snackbar.show(); } } + }; - @Override - public void onHide(FeedItemUndoToken token) { - // nothing to do - } - }); + ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback); + itemTouchHelper.attachToRecyclerView(recyclerView); return root; } @Override - protected Pair<List<FeedItem>,LongList> loadData() { - List<FeedItem> items; - items = DBReader.getFavoriteItemsList(); - LongList queuedIds = DBReader.getQueueIDList(); - return Pair.create(items, queuedIds); + protected List<FeedItem> loadData() { + return DBReader.getFavoriteItemsList(); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java index d17788dde..8505cf143 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java @@ -463,7 +463,7 @@ public class ItemlistFragment extends ListFragment { private void refreshHeaderView() { if (getListView() == null || feed == null) { - Log.e(TAG, "Unable to setup listview: listView = null or feed = null"); + Log.e(TAG, "Unable to setup listview: recyclerView = null or feed = null"); return; } if(feed.hasLastUpdateFailed()) { @@ -503,7 +503,7 @@ public class ItemlistFragment extends ListFragment { private void setupHeaderView() { if (getListView() == null || feed == null) { - Log.e(TAG, "Unable to setup listview: listView = null or feed = null"); + Log.e(TAG, "Unable to setup listview: recyclerView = null or feed = null"); return; } ListView lv = getListView(); @@ -561,7 +561,7 @@ public class ItemlistFragment extends ListFragment { private void setupFooterView() { if (getListView() == null || feed == null) { - Log.e(TAG, "Unable to setup listview: listView = null or feed = null"); + Log.e(TAG, "Unable to setup listview: recyclerView = null or feed = null"); return; } if (feed.isPaged() && feed.getNextPageLink() != null) { 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 60d0161b2..bbe9b20f3 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -1,26 +1,25 @@ package de.danoeh.antennapod.fragment; -import android.content.Context; import android.os.Bundle; -import android.support.v4.util.Pair; +import android.os.Handler; +import android.support.design.widget.Snackbar; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import com.mobeta.android.dslv.DragSortListView; - import java.util.List; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.event.QueueEvent; +import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; -import de.danoeh.antennapod.core.util.LongList; -import de.danoeh.antennapod.core.util.gui.FeedItemUndoToken; -import de.danoeh.antennapod.core.util.gui.UndoBarController; import de.greenrobot.event.EventBus; @@ -35,11 +34,11 @@ public class NewEpisodesFragment extends AllEpisodesFragment { private static final String PREF_NAME = "PrefNewEpisodesFragment"; - private UndoBarController undoBarController; + @Override + protected boolean showOnlyNewEpisodes() { return true; } - public NewEpisodesFragment() { - super(true, PREF_NAME); - } + @Override + protected String getPrefName() { return PREF_NAME; } public void onEvent(QueueEvent event) { Log.d(TAG, "onEvent(" + event + ")"); @@ -61,64 +60,61 @@ public class NewEpisodesFragment extends AllEpisodesFragment { @Override protected void resetViewState() { super.resetViewState(); - undoBarController = null; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = super.onCreateViewHelper(inflater, container, savedInstanceState, - R.layout.episodes_fragment_with_undo); + R.layout.all_episodes_fragment); - listView.setRemoveListener(new DragSortListView.RemoveListener() { + ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { @Override - public void remove(int which) { - Log.d(TAG, "remove(" + which + ")"); + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + return false; + } + + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) { + AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder; + + Log.d(TAG, "remove(" + holder.getItemId() + ")"); if (subscription != null) { subscription.unsubscribe(); } - FeedItem item = (FeedItem) listView.getAdapter().getItem(which); + FeedItem item = holder.getFeedItem(); // we're marking it as unplayed since the user didn't actually play it // but they don't want it considered 'NEW' anymore DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId()); - undoBarController.showUndoBar(false, - getString(R.string.marked_as_read_label), new FeedItemUndoToken(item, - which) - ); - } - }); - undoBarController = new UndoBarController<FeedItemUndoToken>(root.findViewById(R.id.undobar), new UndoBarController.UndoListener<FeedItemUndoToken>() { - - private final Context context = getActivity(); - - @Override - public void onUndo(FeedItemUndoToken token) { - if (token != null) { - long itemId = token.getFeedItemId(); - DBWriter.markItemPlayed(FeedItem.NEW, itemId); - } - } - @Override - public void onHide(FeedItemUndoToken token) { - if (token != null && context != null) { - long itemId = token.getFeedItemId(); - FeedItem item = DBReader.getFeedItem(itemId); + final Handler h = new Handler(getActivity().getMainLooper()); + final Runnable r = () -> { FeedMedia media = item.getMedia(); - if(media != null && media.hasAlmostEnded() && item.getFeed().getPreferences().getCurrentAutoDelete()) { - DBWriter.deleteFeedMediaOfItem(context, media.getId()); + if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) { + DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId()); } - } + }; + + Snackbar snackbar = Snackbar.make(root, getString(R.string.marked_as_read_label), + Snackbar.LENGTH_LONG); + snackbar.setAction(getString(R.string.undo), v -> { + DBWriter.markItemPlayed(FeedItem.NEW, item.getId()); + // don't forget to cancel the thing that's going to remove the media + h.removeCallbacks(r); + }); + snackbar.show(); + h.postDelayed(r, (int)Math.ceil(snackbar.getDuration() * 1.05f)); } - }); + }; + + ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback); + itemTouchHelper.attachToRecyclerView(recyclerView); + return root; } @Override - protected Pair<List<FeedItem>,LongList> loadData() { - List<FeedItem> items; - items = DBReader.getNewItemsList(); - LongList queuedIds = DBReader.getQueueIDList(); - return Pair.create(items, queuedIds); + protected List<FeedItem> loadData() { + return DBReader.getNewItemsList(); } } diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java index b24779934..3fa1048c0 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java @@ -73,10 +73,10 @@ public class FeedItemMenuHandler { } boolean isInQueue = selectedItem.isTagged(FeedItem.TAG_QUEUE); - if(queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId()) { + if(queueAccess == null || queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId()) { mi.setItemVisibility(R.id.move_to_top_item, false); } - if(queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId()) { + if(queueAccess == null || queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId()) { mi.setItemVisibility(R.id.move_to_bottom_item, false); } if (!isInQueue || isPlaying) { diff --git a/app/src/main/java/de/danoeh/antennapod/view/DividerItemDecoration.java b/app/src/main/java/de/danoeh/antennapod/view/DividerItemDecoration.java new file mode 100644 index 000000000..c32f7e702 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/DividerItemDecoration.java @@ -0,0 +1,124 @@ +package de.danoeh.antennapod.view; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.util.AttributeSet; +import android.view.View; + +public class DividerItemDecoration extends RecyclerView.ItemDecoration { + + private Drawable mDivider; + private boolean mShowFirstDivider = false; + private boolean mShowLastDivider = false; + + + public DividerItemDecoration(Context context, AttributeSet attrs) { + final TypedArray a = context.obtainStyledAttributes(attrs, new int[] { android.R.attr.listDivider }); + mDivider = a.getDrawable(0); + a.recycle(); + } + + public DividerItemDecoration(Context context, AttributeSet attrs, boolean showFirstDivider, + boolean showLastDivider) { + this(context, attrs); + mShowFirstDivider = showFirstDivider; + mShowLastDivider = showLastDivider; + } + + public DividerItemDecoration(Drawable divider) { + mDivider = divider; + } + + public DividerItemDecoration(Drawable divider, boolean showFirstDivider, + boolean showLastDivider) { + this(divider); + mShowFirstDivider = showFirstDivider; + mShowLastDivider = showLastDivider; + } + + @Override + public void getItemOffsets(Rect outRect, View view, RecyclerView parent, + RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + if (mDivider == null) { + return; + } + if (parent.getChildPosition(view) < 1) { + return; + } + + if (getOrientation(parent) == LinearLayoutManager.VERTICAL) { + outRect.top = mDivider.getIntrinsicHeight(); + } else { + outRect.left = mDivider.getIntrinsicWidth(); + } + } + + @Override + public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { + if (mDivider == null) { + super.onDrawOver(c, parent, state); + return; + } + + // Initialization needed to avoid compiler warning + int left = 0, right = 0, top = 0, bottom = 0, size; + int orientation = getOrientation(parent); + int childCount = parent.getChildCount(); + + if (orientation == LinearLayoutManager.VERTICAL) { + size = mDivider.getIntrinsicHeight(); + left = parent.getPaddingLeft(); + right = parent.getWidth() - parent.getPaddingRight(); + } else { //horizontal + size = mDivider.getIntrinsicWidth(); + top = parent.getPaddingTop(); + bottom = parent.getHeight() - parent.getPaddingBottom(); + } + + for (int i = mShowFirstDivider ? 0 : 1; i < childCount; i++) { + View child = parent.getChildAt(i); + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + + if (orientation == LinearLayoutManager.VERTICAL) { + top = child.getTop() - params.topMargin; + bottom = top + size; + } else { //horizontal + left = child.getLeft() - params.leftMargin; + right = left + size; + } + mDivider.setBounds(left, top, right, bottom); + mDivider.draw(c); + } + + // show last divider + if (mShowLastDivider && childCount > 0) { + View child = parent.getChildAt(childCount - 1); + RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams(); + if (orientation == LinearLayoutManager.VERTICAL) { + top = child.getBottom() + params.bottomMargin; + bottom = top + size; + } else { // horizontal + left = child.getRight() + params.rightMargin; + right = left + size; + } + mDivider.setBounds(left, top, right, bottom); + mDivider.draw(c); + } + } + + private int getOrientation(RecyclerView parent) { + if (parent.getLayoutManager() instanceof LinearLayoutManager) { + LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager(); + return layoutManager.getOrientation(); + } else { + throw new IllegalStateException( + "DividerItemDecoration can only be used with a LinearLayoutManager."); + } + } +} |