diff options
author | Tom Hennen <TomHennen@users.noreply.github.com> | 2015-11-07 09:16:24 -0500 |
---|---|---|
committer | Tom Hennen <TomHennen@users.noreply.github.com> | 2015-11-07 09:16:24 -0500 |
commit | 0aeb520f14763e3f81c6e83d9584b79eac7d19f0 (patch) | |
tree | ecfaf180ff4d8d4a30a1c9581fdd71d514ec6a14 /app/src/main/java/de/danoeh | |
parent | c5d99a84b288d30afd0a2b54bb557fdbcdc9e56b (diff) | |
parent | 8286a76cdf580f4adc1e536cabd3cb3ce3427f15 (diff) | |
download | AntennaPod-0aeb520f14763e3f81c6e83d9584b79eac7d19f0.zip |
Merge pull request #1347 from mfietz/recycler_view
RecyclerView & SnackBar
Diffstat (limited to 'app/src/main/java/de/danoeh')
10 files changed, 811 insertions, 779 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 a92eefc00..bcbabffcf 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -102,7 +102,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity private View navDrawer; private ListView navList; private NavListAdapter navAdapter; - private AdapterView.AdapterContextMenuInfo lastMenuInfo = null; + private int mPosition = -1; private ActionBarDrawerToggle drawerToggle; @@ -139,11 +139,8 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity final FragmentManager fm = getSupportFragmentManager(); - fm.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() { - @Override - public void onBackStackChanged() { - drawerToggle.setDrawerIndicatorEnabled(fm.getBackStackEntryCount() == 0); - } + fm.addOnBackStackChangedListener(() -> { + drawerToggle.setDrawerIndicatorEnabled(fm.getBackStackEntryCount() == 0); }); getSupportActionBar().setDisplayHomeAsUpEnabled(true); @@ -153,6 +150,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity navList.setAdapter(navAdapter); navList.setOnItemClickListener(navListClickListener); navList.setOnItemLongClickListener(newListLongClickListener); + registerForContextMenu(navList); navAdapter.registerDataSetObserver(new DataSetObserver() { @Override @@ -161,12 +159,9 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity } }); - findViewById(R.id.nav_settings).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - drawerLayout.closeDrawer(navDrawer); - startActivity(new Intent(MainActivity.this, PreferenceController.getPreferenceActivity())); - } + findViewById(R.id.nav_settings).setOnClickListener(v -> { + drawerLayout.closeDrawer(navDrawer); + startActivity(new Intent(MainActivity.this, PreferenceController.getPreferenceActivity())); }); FragmentTransaction transaction = fm.beginTransaction(); @@ -218,12 +213,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity private void checkFirstLaunch() { SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); if (prefs.getBoolean(PREF_IS_FIRST_LAUNCH, true)) { - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - drawerLayout.openDrawer(navDrawer); - } - }, 1500); + new Handler().postDelayed(() -> drawerLayout.openDrawer(navDrawer), 1500); SharedPreferences.Editor edit = prefs.edit(); edit.putBoolean(PREF_IS_FIRST_LAUNCH, false); @@ -245,21 +235,15 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setTitle(R.string.drawer_preferences); - builder.setMultiChoiceItems(navLabels, checked, new DialogInterface.OnMultiChoiceClickListener() { - @Override - public void onClick(DialogInterface dialog, int which, boolean isChecked) { - if (isChecked) { - hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]); - } else { - hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]); - } + builder.setMultiChoiceItems(navLabels, checked, (dialog, which, isChecked) -> { + if (isChecked) { + hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]); + } else { + hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]); } }); - builder.setPositiveButton(R.string.confirm_label, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - UserPreferences.setHiddenDrawerItems(hiddenDrawerItems); - } + builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { + UserPreferences.setHiddenDrawerItems(hiddenDrawerItems); }); builder.setNegativeButton(R.string.cancel_label, null); builder.create().show(); @@ -422,6 +406,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity showDrawerPreferencesDialog(); return true; } else { + mPosition = position; return false; } } @@ -521,6 +506,68 @@ 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! + } + + + @Override + public boolean onContextItemSelected(MenuItem item) { + final int position = mPosition; + mPosition = -1; // reset + if(position < 0) { + return false; + } + 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/ActionButtonUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java index d60888a43..74cf6af60 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java @@ -7,6 +7,8 @@ import android.widget.ImageButton; import org.apache.commons.lang3.Validate; +import java.lang.ref.WeakReference; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; @@ -27,7 +29,7 @@ public class ActionButtonUtils { public ActionButtonUtils(Context context) { Validate.notNull(context); - this.context = context; + this.context = context.getApplicationContext(); drawables = context.obtainStyledAttributes(new int[] { R.attr.av_play, R.attr.navigation_cancel, diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java index c72c95feb..b35ff0aa9 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java @@ -5,10 +5,13 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.support.v7.widget.PopupMenu; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.text.format.DateUtils; 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; @@ -22,6 +25,7 @@ import com.bumptech.glide.load.resource.drawable.GlideDrawable; import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; import com.joanzapata.iconify.Iconify; +import com.nineoldandroids.view.ViewHelper; import java.lang.ref.WeakReference; @@ -51,6 +55,8 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR private final boolean showOnlyNewEpisodes; private final WeakReference<MainActivity> mainActivityRef; + private int position = -1; + public AllEpisodesRecycleAdapter(Context context, MainActivity mainActivity, ItemAccess itemAccess, @@ -96,6 +102,10 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR public void onBindViewHolder(final Holder holder, int position) { final FeedItem item = itemAccess.getItem(position); if (item == null) return; + holder.itemView.setOnLongClickListener(v -> { + this.position = position; + return false; + }); holder.item = item; holder.position = position; holder.placeholder.setVisibility(View.VISIBLE); @@ -189,6 +199,12 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR return itemAccess.getItem(position); } + public int getPosition() { + int pos = position; + position = -1; // reset + return pos; + } + private class CoverTarget extends GlideDrawableImageViewTarget { private final WeakReference<Uri> fallback; @@ -235,63 +251,10 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR } }; - 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(); - - // 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{ + implements View.OnClickListener, + View.OnCreateContextMenuListener, + ItemTouchHelperViewHolder { TextView placeholder; TextView title; TextView pubDate; @@ -308,7 +271,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR public Holder(View itemView) { super(itemView); itemView.setOnClickListener(this); - itemView.setOnLongClickListener(this); + itemView.setOnCreateContextMenuListener(this); } @Override @@ -319,14 +282,42 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR } } - public FeedItem getFeedItem() { return item; } + @Override + public void onItemSelected() { + ViewHelper.setAlpha(itemView, 0.5f); + } - public int getItemPosition() { return position; } + @Override + public void onItemClear() { + ViewHelper.setAlpha(itemView, 1.0f); + } + + public FeedItem getFeedItem() { return item; } @Override - public boolean onLongClick(View view) { - return showContextMenu(view); + public void onCreateContextMenu(final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + FeedItem item = itemAccess.getItem(getAdapterPosition()); + + MenuInflater inflater = mainActivityRef.get().getMenuInflater(); + inflater.inflate(R.menu.allepisodes_context, menu); + + if (item != null) { + menu.setHeaderTitle(item.getTitle()); + } + + FeedItemMenuHandler.MenuInterface contextMenuInterface = (id, visible) -> { + if (menu == null) { + return; + } + MenuItem item1 = menu.findItem(id); + if (item1 != null) { + item1.setVisible(visible); + } + }; + FeedItemMenuHandler.onPrepareMenu(mainActivityRef.get(), contextMenuInterface, item, true, + null); } + } public interface ItemAccess { @@ -340,4 +331,26 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR boolean isInQueue(FeedItem item); } + + /** + * Notifies a View Holder of relevant callbacks from + * {@link ItemTouchHelper.Callback}. + */ + public interface ItemTouchHelperViewHolder { + + /** + * Called when the {@link ItemTouchHelper} first registers an + * item as being moved or swiped. + * Implementations should update the item view to indicate + * it's active state. + */ + void onItemSelected(); + + + /** + * Called when the {@link ItemTouchHelper} has completed the + * move or swipe, and the active item state should be cleared. + */ + void onItemClear(); + } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java deleted file mode 100644 index d5fb00b34..000000000 --- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java +++ /dev/null @@ -1,250 +0,0 @@ -package de.danoeh.antennapod.adapter; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.text.format.DateUtils; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.resource.drawable.GlideDrawable; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; -import com.joanzapata.iconify.Iconify; - -import java.lang.ref.WeakReference; - -import de.danoeh.antennapod.R; -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.preferences.UserPreferences; -import de.danoeh.antennapod.core.storage.DownloadRequester; -import de.danoeh.antennapod.core.util.Converter; -import de.danoeh.antennapod.core.util.NetworkUtils; - -/** - * List adapter for the queue. - */ -public class QueueListAdapter extends BaseAdapter { - - private static final String TAG = QueueListAdapter.class.getSimpleName(); - - private final Context context; - private final ItemAccess itemAccess; - private final ActionButtonCallback actionButtonCallback; - private final ActionButtonUtils actionButtonUtils; - - private boolean locked; - - - public QueueListAdapter(Context context, ItemAccess itemAccess, ActionButtonCallback actionButtonCallback) { - super(); - this.context = context; - this.itemAccess = itemAccess; - this.actionButtonUtils = new ActionButtonUtils(context); - this.actionButtonCallback = actionButtonCallback; - locked = UserPreferences.isQueueLocked(); - } - - public void setLocked(boolean locked) { - this.locked = locked; - notifyDataSetChanged(); - } - - @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; - } - - @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.queue_listitem, - parent, false); - holder.dragHandle = (ImageView) convertView.findViewById(R.id.drag_handle); - holder.placeholder = (TextView) convertView.findViewById(R.id.txtvPlaceholder); - holder.cover = (ImageView) convertView.findViewById(R.id.imgvCover); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); - holder.pubDate = (TextView) convertView.findViewById(R.id.txtvPubDate); - holder.progressLeft = (TextView) convertView.findViewById(R.id.txtvProgressLeft); - holder.progressRight = (TextView) convertView - .findViewById(R.id.txtvProgressRight); - holder.butSecondary = (ImageButton) convertView - .findViewById(R.id.butSecondaryAction); - holder.progress = (ProgressBar) convertView - .findViewById(R.id.progressBar); - convertView.setTag(holder); - } else { - holder = (Holder) convertView.getTag(); - } - - if(locked) { - holder.dragHandle.setVisibility(View.GONE); - } else { - holder.dragHandle.setVisibility(View.VISIBLE); - } - - holder.placeholder.setText(item.getFeed().getTitle()); - - holder.title.setText(item.getTitle()); - FeedMedia media = item.getMedia(); - - holder.title.setText(item.getTitle()); - String pubDate = DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL); - holder.pubDate.setText(pubDate.replace(" ", "\n")); - - if (media != null) { - final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media); - FeedItem.State state = item.getState(); - if (isDownloadingMedia) { - holder.progressLeft.setText(Converter.byteToString(itemAccess.getItemDownloadedBytes(item))); - if(itemAccess.getItemDownloadSize(item) > 0) { - holder.progressRight.setText(Converter.byteToString(itemAccess.getItemDownloadSize(item))); - } else { - holder.progressRight.setText(Converter.byteToString(media.getSize())); - } - holder.progress.setProgress(itemAccess.getItemDownloadProgressPercent(item)); - holder.progress.setVisibility(View.VISIBLE); - } else if (state == FeedItem.State.PLAYING - || state == FeedItem.State.IN_PROGRESS) { - if (media.getDuration() > 0) { - int progress = (int) (100.0 * media.getPosition() / media.getDuration()); - holder.progress.setProgress(progress); - holder.progress.setVisibility(View.VISIBLE); - holder.progressLeft.setText(Converter - .getDurationStringLong(media.getPosition())); - holder.progressRight.setText(Converter.getDurationStringLong(media.getDuration())); - } - } else { - if(media.getSize() > 0) { - holder.progressLeft.setText(Converter.byteToString(media.getSize())); - } else if(false == media.checkedOnSizeButUnknown()) { - holder.progressLeft.setText("{fa-spinner}"); - Iconify.addIcons(holder.progressLeft); - NetworkUtils.getFeedMediaSizeObservable(media) - .subscribe( - size -> { - if (size > 0) { - holder.progressLeft.setText(Converter.byteToString(size)); - } else { - holder.progressLeft.setText(""); - } - }, error -> { - holder.progressLeft.setText(""); - Log.e(TAG, Log.getStackTraceString(error)); - }); - } else { - holder.progressLeft.setText(""); - } - holder.progressRight.setText(Converter.getDurationStringLong(media.getDuration())); - holder.progress.setVisibility(View.GONE); - } - } - - actionButtonUtils.configureActionButton(holder.butSecondary, item); - holder.butSecondary.setFocusable(false); - holder.butSecondary.setTag(item); - holder.butSecondary.setOnClickListener(secondaryActionListener); - - Glide.with(context) - .load(item.getImageUri()) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() - .into(new CoverTarget(item.getFeed().getImageUri(), holder.placeholder, holder.cover)); - - return convertView; - } - - private class CoverTarget extends GlideDrawableImageViewTarget { - - private final WeakReference<Uri> fallback; - private final WeakReference<TextView> placeholder; - private final WeakReference<ImageView> cover; - - public CoverTarget(Uri fallbackUri, TextView txtvPlaceholder, ImageView imgvCover) { - super(imgvCover); - fallback = new WeakReference<>(fallbackUri); - placeholder = new WeakReference<>(txtvPlaceholder); - cover = new WeakReference<>(imgvCover); - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - Uri fallbackUri = fallback.get(); - TextView txtvPlaceholder = placeholder.get(); - ImageView imgvCover = cover.get(); - if(fallbackUri != null && txtvPlaceholder != null && imgvCover != null) { - Glide.with(context) - .load(fallbackUri) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() - .into(new CoverTarget(null, txtvPlaceholder, imgvCover)); - } - } - - @Override - public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) { - super.onResourceReady(drawable, anim); - TextView txtvPlaceholder = placeholder.get(); - if(txtvPlaceholder != null) { - txtvPlaceholder.setVisibility(View.INVISIBLE); - } - } - } - - private View.OnClickListener secondaryActionListener = new View.OnClickListener() { - @Override - public void onClick(View v) { - FeedItem item = (FeedItem) v.getTag(); - actionButtonCallback.onActionButtonPressed(item); - } - }; - - static class Holder { - ImageView dragHandle; - ImageView cover; - TextView placeholder; - TextView title; - TextView pubDate; - TextView progressLeft; - TextView progressRight; - ProgressBar progress; - ImageButton butSecondary; - } - - public interface ItemAccess { - FeedItem getItem(int position); - int getCount(); - long getItemDownloadedBytes(FeedItem item); - long getItemDownloadSize(FeedItem item); - int getItemDownloadProgressPercent(FeedItem item); - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java new file mode 100644 index 000000000..062f883bc --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java @@ -0,0 +1,346 @@ +package de.danoeh.antennapod.adapter; + +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.support.v4.view.MotionEventCompat; +import android.support.v7.widget.PopupMenu; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; +import android.text.format.DateUtils; +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.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.load.resource.drawable.GlideDrawable; +import com.bumptech.glide.request.animation.GlideAnimation; +import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; +import com.joanzapata.iconify.Iconify; +import com.nineoldandroids.view.ViewHelper; + +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.preferences.UserPreferences; +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.LongList; +import de.danoeh.antennapod.core.util.NetworkUtils; +import de.danoeh.antennapod.fragment.ItemFragment; +import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; + +/** + * List adapter for the queue. + */ +public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdapter.ViewHolder> { + + private static final String TAG = QueueRecyclerAdapter.class.getSimpleName(); + + private WeakReference<MainActivity> mainActivity; + private final ItemAccess itemAccess; + private final ActionButtonCallback actionButtonCallback; + private final ActionButtonUtils actionButtonUtils; + private final ItemTouchHelper itemTouchHelper; + + private boolean locked; + + private int position = -1; + + public QueueRecyclerAdapter(MainActivity mainActivity, + ItemAccess itemAccess, + ActionButtonCallback actionButtonCallback, + ItemTouchHelper itemTouchHelper) { + super(); + this.mainActivity = new WeakReference<>(mainActivity); + this.itemAccess = itemAccess; + this.actionButtonUtils = new ActionButtonUtils(mainActivity); + this.actionButtonCallback = actionButtonCallback; + this.itemTouchHelper = itemTouchHelper; + locked = UserPreferences.isQueueLocked(); + } + + public void setLocked(boolean locked) { + this.locked = locked; + notifyDataSetChanged(); + } + + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.queue_listitem, parent, false); + return new ViewHolder(view); + } + + public void onBindViewHolder(ViewHolder holder, int pos) { + FeedItem item = itemAccess.getItem(pos); + holder.bind(item); + holder.itemView.setOnLongClickListener(v -> { + position = pos; + return false; + }); + } + + public int getItemCount() { + return itemAccess.getCount(); + } + + public int getPosition() { + return position; + } + + public class ViewHolder extends RecyclerView.ViewHolder + implements View.OnClickListener, + View.OnCreateContextMenuListener, + ItemTouchHelperViewHolder { + + private final ImageView dragHandle; + private final TextView placeholder; + private final ImageView cover; + private final TextView title; + private final TextView pubDate; + private final TextView progressLeft; + private final TextView progressRight; + private final ProgressBar progressBar; + private final ImageButton butSecondary; + + private FeedItem item; + + public ViewHolder(View v) { + super(v); + dragHandle = (ImageView) v.findViewById(R.id.drag_handle); + placeholder = (TextView) v.findViewById(R.id.txtvPlaceholder); + cover = (ImageView) v.findViewById(R.id.imgvCover); + title = (TextView) v.findViewById(R.id.txtvTitle); + pubDate = (TextView) v.findViewById(R.id.txtvPubDate); + progressLeft = (TextView) v.findViewById(R.id.txtvProgressLeft); + progressRight = (TextView) v.findViewById(R.id.txtvProgressRight); + butSecondary = (ImageButton) v.findViewById(R.id.butSecondaryAction); + progressBar = (ProgressBar) v.findViewById(R.id.progressBar); + v.setTag(this); + v.setOnClickListener(this); + v.setOnCreateContextMenuListener(this); + dragHandle.setOnTouchListener((v1, event) -> { + if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { + Log.d(TAG, "startDrag()"); + itemTouchHelper.startDrag(ViewHolder.this); + } + return false; + }); + } + + @Override + public void onClick(View v) { + MainActivity activity = mainActivity.get(); + if (activity != null) { + activity.loadChildFragment(ItemFragment.newInstance(item.getId())); + } + } + + @Override + public void onCreateContextMenu(final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + FeedItem item = itemAccess.getItem(getAdapterPosition()); + + MenuInflater inflater = mainActivity.get().getMenuInflater(); + inflater.inflate(R.menu.queue_context, menu); + + if (item != null) { + menu.setHeaderTitle(item.getTitle()); + } + + FeedItemMenuHandler.MenuInterface contextMenuInterface = (id, visible) -> { + if (menu == null) { + return; + } + MenuItem item1 = menu.findItem(id); + if (item1 != null) { + item1.setVisible(visible); + } + }; + FeedItemMenuHandler.onPrepareMenu(mainActivity.get(), contextMenuInterface, item, true, + itemAccess.getQueueIds()); + } + + @Override + public void onItemSelected() { + ViewHelper.setAlpha(itemView, 0.5f); + } + + @Override + public void onItemClear() { + ViewHelper.setAlpha(itemView, 1.0f); + } + + public void bind(FeedItem item) { + this.item = item; + if(locked) { + dragHandle.setVisibility(View.GONE); + } else { + dragHandle.setVisibility(View.VISIBLE); + } + + placeholder.setText(item.getFeed().getTitle()); + + title.setText(item.getTitle()); + FeedMedia media = item.getMedia(); + + title.setText(item.getTitle()); + String pubDateStr = DateUtils.formatDateTime(mainActivity.get(), + item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL); + pubDate.setText(pubDateStr.replace(" ", "\n")); + + if (media != null) { + final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media); + FeedItem.State state = item.getState(); + if (isDownloadingMedia) { + progressLeft.setText(Converter.byteToString(itemAccess.getItemDownloadedBytes(item))); + if(itemAccess.getItemDownloadSize(item) > 0) { + progressRight.setText(Converter.byteToString(itemAccess.getItemDownloadSize(item))); + } else { + progressRight.setText(Converter.byteToString(media.getSize())); + } + progressBar.setProgress(itemAccess.getItemDownloadProgressPercent(item)); + progressBar.setVisibility(View.VISIBLE); + } else if (state == FeedItem.State.PLAYING + || state == FeedItem.State.IN_PROGRESS) { + if (media.getDuration() > 0) { + int progress = (int) (100.0 * media.getPosition() / media.getDuration()); + progressBar.setProgress(progress); + progressBar.setVisibility(View.VISIBLE); + progressLeft.setText(Converter + .getDurationStringLong(media.getPosition())); + progressRight.setText(Converter.getDurationStringLong(media.getDuration())); + } + } else { + if(media.getSize() > 0) { + progressLeft.setText(Converter.byteToString(media.getSize())); + } else if(false == media.checkedOnSizeButUnknown()) { + progressLeft.setText("{fa-spinner}"); + Iconify.addIcons(progressLeft); + NetworkUtils.getFeedMediaSizeObservable(media) + .subscribe( + size -> { + if (size > 0) { + progressLeft.setText(Converter.byteToString(size)); + } else { + progressLeft.setText(""); + } + }, error -> { + progressLeft.setText(""); + Log.e(TAG, Log.getStackTraceString(error)); + }); + } else { + progressLeft.setText(""); + } + progressRight.setText(Converter.getDurationStringLong(media.getDuration())); + progressBar.setVisibility(View.GONE); + } + } + + actionButtonUtils.configureActionButton(butSecondary, item); + butSecondary.setFocusable(false); + butSecondary.setTag(item); + butSecondary.setOnClickListener(secondaryActionListener); + + Glide.with(mainActivity.get()) + .load(item.getImageUri()) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() + .into(new CoverTarget(item.getFeed().getImageUri(), placeholder, cover)); + } + + + } + + + private class CoverTarget extends GlideDrawableImageViewTarget { + + private final WeakReference<Uri> fallback; + private final WeakReference<TextView> placeholder; + private final WeakReference<ImageView> cover; + + public CoverTarget(Uri fallbackUri, TextView txtvPlaceholder, ImageView imgvCover) { + super(imgvCover); + fallback = new WeakReference<>(fallbackUri); + placeholder = new WeakReference<>(txtvPlaceholder); + cover = new WeakReference<>(imgvCover); + } + + @Override + public void onLoadFailed(Exception e, Drawable errorDrawable) { + Uri fallbackUri = fallback.get(); + TextView txtvPlaceholder = placeholder.get(); + ImageView imgvCover = cover.get(); + if(fallbackUri != null && txtvPlaceholder != null && imgvCover != null) { + Glide.with(mainActivity.get()) + .load(fallbackUri) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() + .into(new CoverTarget(null, txtvPlaceholder, imgvCover)); + } + } + + @Override + public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) { + super.onResourceReady(drawable, anim); + TextView txtvPlaceholder = placeholder.get(); + if(txtvPlaceholder != null) { + txtvPlaceholder.setVisibility(View.INVISIBLE); + } + } + } + + private View.OnClickListener secondaryActionListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + FeedItem item = (FeedItem) v.getTag(); + actionButtonCallback.onActionButtonPressed(item); + } + }; + + + public interface ItemAccess { + FeedItem getItem(int position); + int getCount(); + long getItemDownloadedBytes(FeedItem item); + long getItemDownloadSize(FeedItem item); + int getItemDownloadProgressPercent(FeedItem item); + LongList getQueueIds(); + } + + /** + * Notifies a View Holder of relevant callbacks from + * {@link ItemTouchHelper.Callback}. + */ + public interface ItemTouchHelperViewHolder { + + /** + * Called when the {@link ItemTouchHelper} first registers an + * item as being moved or swiped. + * Implementations should update the item view to indicate + * it's active state. + */ + void onItemSelected(); + + + /** + * Called when the {@link ItemTouchHelper} has completed the + * move or swipe, and the active item state should be cleared. + */ + void onItemClear(); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java index 8e416139e..c5b6ddb65 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java @@ -79,16 +79,14 @@ public class EpisodesApplyActionFragment extends Fragment { mListView = (ListView) view.findViewById(android.R.id.list); mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - public void onItemClick(AdapterView<?> ListView, View view, int position, long rowId) { - long id = episodes.get(position).getId(); - if (checkedIds.contains(id)) { - checkedIds.remove(id); - } else { - checkedIds.add(id); - } - refreshCheckboxes(); + mListView.setOnItemClickListener((ListView, view1, position, rowId) -> { + long id = episodes.get(position).getId(); + if (checkedIds.contains(id)) { + checkedIds.remove(id); + } else { + checkedIds.add(id); } + refreshCheckboxes(); }); for(FeedItem episode : episodes) { @@ -101,40 +99,15 @@ public class EpisodesApplyActionFragment extends Fragment { checkAll(); btnAddToQueue = (Button) view.findViewById(R.id.btnAddToQueue); - btnAddToQueue.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - queueChecked(); - } - }); + btnAddToQueue.setOnClickListener(v -> queueChecked()); btnMarkAsPlayed = (Button) view.findViewById(R.id.btnMarkAsPlayed); - btnMarkAsPlayed.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - markedCheckedPlayed(); - } - }); + btnMarkAsPlayed.setOnClickListener(v -> markedCheckedPlayed()); btnMarkAsUnplayed = (Button) view.findViewById(R.id.btnMarkAsUnplayed); - btnMarkAsUnplayed.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - markedCheckedUnplayed(); - } - }); + btnMarkAsUnplayed.setOnClickListener(v -> markedCheckedUnplayed()); btnDownload = (Button) view.findViewById(R.id.btnDownload); - btnDownload.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - downloadChecked(); - } - }); + btnDownload.setOnClickListener(v -> downloadChecked()); btnDelete = (Button) view.findViewById(R.id.btnDelete); - btnDelete.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - deleteChecked(); - } - }); + btnDelete.setOnClickListener(v -> deleteChecked()); return view; } @@ -153,16 +126,13 @@ public class EpisodesApplyActionFragment extends Fragment { FontAwesomeIcons.fa_sort).color(textColor).actionBarSize()); mSelectToggle = menu.findItem(R.id.select_toggle); - mSelectToggle.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - if (checkedIds.size() == episodes.size()) { - checkNone(); - } else { - checkAll(); - } - return true; + mSelectToggle.setOnMenuItemClickListener(item -> { + if (checkedIds.size() == episodes.size()) { + checkNone(); + } else { + checkAll(); } + return true; }); menu.findItem(R.id.select_options).setIcon(new IconDrawable(getActivity(), @@ -241,14 +211,11 @@ public class EpisodesApplyActionFragment extends Fragment { } private void sortByTitle(final boolean reverse) { - Collections.sort(episodes, new Comparator<FeedItem>() { - @Override - public int compare(FeedItem lhs, FeedItem rhs) { - if (reverse) { - return -1 * lhs.getTitle().compareTo(rhs.getTitle()); - } else { - return lhs.getTitle().compareTo(rhs.getTitle()); - } + Collections.sort(episodes, (lhs, rhs) -> { + if (reverse) { + return -1 * lhs.getTitle().compareTo(rhs.getTitle()); + } else { + return lhs.getTitle().compareTo(rhs.getTitle()); } }); refreshTitles(); @@ -256,20 +223,17 @@ public class EpisodesApplyActionFragment extends Fragment { } private void sortByDate(final boolean reverse) { - Collections.sort(episodes, new Comparator<FeedItem>() { - @Override - public int compare(FeedItem lhs, FeedItem rhs) { - if (lhs.getPubDate() == null) { - return -1; - } else if (rhs.getPubDate() == null) { - return 1; - } - int code = lhs.getPubDate().compareTo(rhs.getPubDate()); - if (reverse) { - return -1 * code; - } else { - return code; - } + Collections.sort(episodes, (lhs, rhs) -> { + if (lhs.getPubDate() == null) { + return -1; + } else if (rhs.getPubDate() == null) { + return 1; + } + int code = lhs.getPubDate().compareTo(rhs.getPubDate()); + if (reverse) { + return -1 * code; + } else { + return code; } }); refreshTitles(); @@ -277,22 +241,19 @@ public class EpisodesApplyActionFragment extends Fragment { } private void sortByDuration(final boolean reverse) { - Collections.sort(episodes, new Comparator<FeedItem>() { - @Override - public int compare(FeedItem lhs, FeedItem rhs) { - int ordering; - if (false == lhs.hasMedia()) { - ordering = 1; - } else if (false == rhs.hasMedia()) { - ordering = -1; - } else { - ordering = lhs.getMedia().getDuration() - rhs.getMedia().getDuration(); - } - if(reverse) { - return -1 * ordering; + Collections.sort(episodes, (lhs, rhs) -> { + int ordering; + if (false == lhs.hasMedia()) { + ordering = 1; + } else if (false == rhs.hasMedia()) { + ordering = -1; } else { - return ordering; + ordering = lhs.getMedia().getDuration() - rhs.getMedia().getDuration(); } + if(reverse) { + return -1 * ordering; + } else { + return ordering; } }); refreshTitles(); @@ -361,7 +322,7 @@ public class EpisodesApplyActionFragment extends Fragment { } private void queueChecked() { - DBWriter.addQueueItem(getActivity(), episodes.toArray(new FeedItem[0])); + DBWriter.addQueueItem(getActivity(), true, checkedIds.toArray()); close(); } 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 c3249771f..cdd6bc265 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -21,6 +21,8 @@ import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.Toast; +import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; + import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -39,9 +41,10 @@ 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.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; @@ -94,12 +97,6 @@ public class AllEpisodesFragment extends Fragment { } @Override - public void onResume() { - super.onResume(); - loadItems(); - } - - @Override public void onStart() { super.onStart(); EventDistributor.getInstance().register(contentUpdate); @@ -114,9 +111,17 @@ public class AllEpisodesFragment extends Fragment { } @Override + public void onResume() { + super.onResume(); + loadItems(); + registerForContextMenu(recyclerView); + } + + @Override public void onPause() { super.onPause(); saveScrollPosition(); + unregisterForContextMenu(recyclerView); } @Override @@ -261,6 +266,31 @@ public class AllEpisodesFragment extends Fragment { } @Override + public boolean onContextItemSelected(MenuItem item) { + if(!isVisible()) { + return false; + } + int pos = listAdapter.getPosition(); + if(pos < 0) { + return false; + } + FeedItem selectedItem = itemAccess.getItem(pos); + + if (selectedItem == null) { + Log.i(TAG, "Selected item at position " + pos + " 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; + } + } + + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return onCreateViewHelper(inflater, container, savedInstanceState, R.layout.all_episodes_fragment); @@ -278,8 +308,7 @@ public class AllEpisodesFragment extends Fragment { layoutManager = new LinearLayoutManager(getActivity()); recyclerView.setLayoutManager(layoutManager); recyclerView.setHasFixedSize(true); - RecyclerView.ItemDecoration itemDecoration = new DividerItemDecoration(getActivity(), null); - recyclerView.addItemDecoration(itemDecoration); + recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build()); progLoading = (ProgressBar) root.findViewById(R.id.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 bbe9b20f3..d684c064c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -14,9 +14,10 @@ import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter; +import de.danoeh.antennapod.adapter.QueueRecyclerAdapter; +import de.danoeh.antennapod.core.event.QueueEvent; 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; @@ -104,6 +105,32 @@ public class NewEpisodesFragment extends AllEpisodesFragment { snackbar.show(); h.postDelayed(r, (int)Math.ceil(snackbar.getDuration() * 1.05f)); } + + @Override + public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, + int actionState) { + // We only want the active item + if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { + if (viewHolder instanceof AllEpisodesRecycleAdapter.ItemTouchHelperViewHolder) { + AllEpisodesRecycleAdapter.ItemTouchHelperViewHolder itemViewHolder = + (AllEpisodesRecycleAdapter.ItemTouchHelperViewHolder) viewHolder; + itemViewHolder.onItemSelected(); + } + } + + super.onSelectedChanged(viewHolder, actionState); + } + @Override + public void clearView(RecyclerView recyclerView, + RecyclerView.ViewHolder viewHolder) { + super.clearView(recyclerView, viewHolder); + + if (viewHolder instanceof AllEpisodesRecycleAdapter.ItemTouchHelperViewHolder) { + AllEpisodesRecycleAdapter.ItemTouchHelperViewHolder itemViewHolder = + (AllEpisodesRecycleAdapter.ItemTouchHelperViewHolder) viewHolder; + itemViewHolder.onItemClear(); + } + } }; ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java index 0ac33f8fb..709d7038a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -1,43 +1,45 @@ package de.danoeh.antennapod.fragment; -import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; +import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.view.MenuItemCompat; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; import android.support.v7.widget.SearchView; +import android.support.v7.widget.helper.ItemTouchHelper; 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 com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; +import java.util.Collections; 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.QueueListAdapter; +import de.danoeh.antennapod.adapter.QueueRecyclerAdapter; import de.danoeh.antennapod.core.asynctask.DownloadObserver; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; +import de.danoeh.antennapod.core.event.FeedItemEvent; +import de.danoeh.antennapod.core.event.QueueEvent; 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.event.QueueEvent; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.Downloader; @@ -47,10 +49,9 @@ 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.Converter; +import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.QueueSorter; -import de.danoeh.antennapod.core.util.gui.FeedItemUndoToken; -import de.danoeh.antennapod.core.util.gui.UndoBarController; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.greenrobot.event.EventBus; @@ -71,28 +72,19 @@ public class QueueFragment extends Fragment { EventDistributor.PLAYER_STATUS_UPDATE; private TextView infoBar; - private DragSortListView listView; - private QueueListAdapter listAdapter; + private RecyclerView recyclerView; + private QueueRecyclerAdapter recyclerAdapter; private TextView txtvEmpty; private ProgressBar progLoading; - private ContextMenu contextMenu; - private AdapterView.AdapterContextMenuInfo lastMenuInfo = null; - - private UndoBarController<FeedItemUndoToken> undoBarController; - private List<FeedItem> queue; private List<Downloader> downloaderList; - private boolean itemsLoaded = false; - private boolean viewsCreated = false; private boolean isUpdatingFeeds = false; private static final String PREFS = "QueueFragment"; - private static final String PREF_KEY_LIST_TOP = "list_top"; - private static final String PREF_KEY_LIST_SELECTION = "list_selection"; - - private AtomicReference<Activity> activity = new AtomicReference<Activity>(); + private static final String PREF_SCROLL_POSITION = "scroll_position"; + private static final String PREF_SCROLL_OFFSET = "scroll_offset"; private DownloadObserver downloadObserver = null; @@ -102,6 +94,8 @@ public class QueueFragment extends Fragment { private boolean blockDownloadObserverUpdate = false; private Subscription subscription; + private LinearLayoutManager layoutManager; + private ItemTouchHelper itemTouchHelper; @Override @@ -112,90 +106,117 @@ public class QueueFragment extends Fragment { } @Override - public void onResume() { - super.onResume(); - loadItems(); - } - - @Override public void onStart() { super.onStart(); - EventDistributor.getInstance().register(contentUpdate); - EventBus.getDefault().register(this); - this.activity.set((MainActivity) getActivity()); if (downloadObserver != null) { downloadObserver.setActivity(getActivity()); downloadObserver.onResume(); } - if (viewsCreated && itemsLoaded) { + if (queue != null) { onFragmentLoaded(); } } @Override - public void onPause() { - super.onPause(); - saveScrollPosition(); + public void onResume() { + super.onResume(); + recyclerView.setAdapter(recyclerAdapter); + loadItems(); + EventDistributor.getInstance().register(contentUpdate); + EventBus.getDefault().register(this); } @Override - public void onStop() { - super.onStop(); + public void onPause() { + super.onPause(); + saveScrollPosition(); EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); if(subscription != null) { subscription.unsubscribe(); } - if(undoBarController.isShowing()) { - undoBarController.close(); - } } - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - this.activity.set((MainActivity) activity); + public void onEventMainThread(QueueEvent event) { + Log.d(TAG, "onEvent(" + event + ")"); + switch(event.action) { + case ADDED: + queue.add(event.position, event.item); + recyclerAdapter.notifyItemInserted(event.position); + break; + case SET_QUEUE: + queue = event.items; + recyclerAdapter.notifyDataSetChanged(); + break; + case REMOVED: + int position = FeedItemUtil.indexOfItemWithId(queue, event.item.getId()); + queue.remove(position); + recyclerAdapter.notifyItemRemoved(position); + break; + case CLEARED: + queue.clear(); + recyclerAdapter.notifyDataSetChanged(); + break; + case SORTED: + queue = event.items; + recyclerAdapter.notifyDataSetChanged(); + break; + case MOVED: + int from = FeedItemUtil.indexOfItemWithId(queue, event.item.getId()); + int to = event.position; + Collections.swap(queue, from, to); + recyclerAdapter.notifyItemMoved(from, to); + break; + } + onFragmentLoaded(); } - public void onEventMainThread(QueueEvent event) { + public void onEventMainThread(FeedItemEvent event) { Log.d(TAG, "onEvent(" + event + ")"); - if(event.action == QueueEvent.Action.REMOVED) { - undoBarController.showUndoBar(false, getString(R.string.removed_from_queue), - new FeedItemUndoToken(event.item, event.position)); + for(int i=0, size = event.items.size(); i < size; i++) { + FeedItem item = event.items.get(i); + int pos = FeedItemUtil.indexOfItemWithId(queue, item.getId()); + if(pos >= 0) { + queue.remove(pos); + queue.add(pos, item); + recyclerAdapter.notifyItemChanged(pos); + } } - loadItems(); } private void saveScrollPosition() { + int firstItem = layoutManager.findFirstVisibleItemPosition(); + View firstItemView = layoutManager.findViewByPosition(firstItem); + float topOffset; + if(firstItemView == null) { + topOffset = 0; + } else { + topOffset = firstItemView.getTop(); + } + SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, 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(PREFS, 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); + 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(); } } private void resetViewState() { - unregisterForContextMenu(listView); - listAdapter = null; - activity.set(null); - undoBarController = null; - viewsCreated = false; + recyclerAdapter = null; blockDownloadObserverUpdate = false; if (downloadObserver != null) { downloadObserver.onPause(); @@ -208,17 +229,14 @@ public class QueueFragment extends Fragment { resetViewState(); } - private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() { - @Override - public boolean isRefreshing() { - return DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds(); - } + private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = () -> { + return DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds(); }; @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); - if (itemsLoaded) { + if (queue != null) { inflater.inflate(R.menu.queue, menu); MenuItem searchItem = menu.findItem(R.id.action_search); @@ -251,14 +269,9 @@ public class QueueFragment extends Fragment { switch (item.getItemId()) { case R.id.queue_lock: boolean locked = !UserPreferences.isQueueLocked(); - if(locked) { - listView.setDragEnabled(false); - } else { - listView.setDragEnabled(true); - } UserPreferences.setQueueLocked(locked); getActivity().supportInvalidateOptionsMenu(); - listAdapter.setLocked(locked); + recyclerAdapter.setLocked(locked); return true; case R.id.refresh_item: List<Feed> feeds = ((MainActivity) getActivity()).getFeeds(); @@ -305,54 +318,19 @@ public class QueueFragment extends Fragment { } else { return true; } - } - 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.queue_context, menu); - - if (item != null) { - menu.setHeaderTitle(item.getTitle()); - } - - contextMenu = menu; - lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; - LongList queueIds = new LongList(queue.size()); - for(FeedItem queueItem : queue) { - queueIds.add(queueItem.getId()); - } - FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queueIds); - } @Override public boolean onContextItemSelected(MenuItem item) { - AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); - if(menuInfo == null) { - menuInfo = lastMenuInfo; + if(!isVisible()) { + return false; } - FeedItem selectedItem = itemAccess.getItem(menuInfo.position); + int pos = recyclerAdapter.getPosition(); + FeedItem selectedItem = itemAccess.getItem(pos); if (selectedItem == null) { - Log.i(TAG, "Selected item at position " + menuInfo.position + " was null, ignoring selection"); + Log.i(TAG, "Selected item at position " + pos + " was null, ignoring selection"); return super.onContextItemSelected(item); } @@ -373,112 +351,111 @@ public class QueueFragment extends Fragment { View root = inflater.inflate(R.layout.queue_fragment, container, false); infoBar = (TextView) root.findViewById(R.id.info_bar); - listView = (DragSortListView) root.findViewById(android.R.id.list); - txtvEmpty = (TextView) root.findViewById(android.R.id.empty); - progLoading = (ProgressBar) root.findViewById(R.id.progLoading); - listView.setEmptyView(txtvEmpty); + recyclerView = (RecyclerView) root.findViewById(R.id.recyclerView); + layoutManager = new LinearLayoutManager(getActivity()); + recyclerView.setLayoutManager(layoutManager); + recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build()); + recyclerView.setHasFixedSize(true); + registerForContextMenu(recyclerView); - if(UserPreferences.isQueueLocked()) { - listView.setDragEnabled(false); - } else { - listView.setDragEnabled(true); - } + itemTouchHelper = new ItemTouchHelper( + new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) { - 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())); + @Override + public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + int from = viewHolder.getAdapterPosition(); + int to = target.getAdapterPosition(); + Log.d(TAG, "move(" + from + ", " + to + ")"); + Collections.swap(queue, from, to); + recyclerAdapter.notifyItemMoved(from, to); + DBWriter.moveQueueItem(from, to, false); + return true; } - } - }); - - listView.setDragSortListener(new DragSortListView.DragSortListener() { - @Override - public void drag(int from, int to) { - Log.d(TAG, "drag"); - blockDownloadObserverUpdate = true; - } - @Override - public void drop(int from, int to) { - Log.d(TAG, "drop"); - blockDownloadObserverUpdate = false; - if(subscription != null) { - subscription.unsubscribe(); + @Override + public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { + if(subscription != null) { + subscription.unsubscribe(); + } + final int position = viewHolder.getAdapterPosition(); + Log.d(TAG, "remove(" + position + ")"); + final FeedItem item = queue.get(position); + final boolean isRead = item.isPlayed(); + DBWriter.markItemPlayed(FeedItem.PLAYED, item.getId()); + DBWriter.removeQueueItem(getActivity(), item, true); + Snackbar snackbar = Snackbar.make(root, getString(R.string.marked_as_read_label), Snackbar.LENGTH_LONG); + snackbar.setAction(getString(R.string.undo), v -> { + DBWriter.addQueueItemAt(getActivity(), item.getId(), position, false); + if(false == isRead) { + DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId()); + } + }); + snackbar.show(); } - final FeedItem item = queue.remove(from); - queue.add(to, item); - listAdapter.notifyDataSetChanged(); - DBWriter.moveQueueItem(from, to, true); - } - @Override - public void remove(int which) { - Log.d(TAG, "remove(" + which + ")"); - if(subscription != null) { - subscription.unsubscribe(); + @Override + public boolean isLongPressDragEnabled() { + return false == UserPreferences.isQueueLocked(); } - FeedItem item = (FeedItem) listView.getAdapter().getItem(which); - DBWriter.removeQueueItem(getActivity(), item, true); - } - }); - - 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(); - int position = token.getPosition(); - DBWriter.addQueueItemAt(context, itemId, position, false); + @Override + public boolean isItemViewSwipeEnabled() { + return false == UserPreferences.isQueueLocked(); } - } - @Override - public void onHide(FeedItemUndoToken token) { - if (token != null && context != null) { - long itemId = token.getFeedItemId(); - FeedItem item = DBReader.getFeedItem(itemId); - if(item != null) { - FeedMedia media = item.getMedia(); - if (media != null && media.hasAlmostEnded() && item.getFeed().getPreferences().getCurrentAutoDelete()) { - DBWriter.deleteFeedMediaOfItem(context, media.getId()); + @Override + public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, + int actionState) { + // We only want the active item + if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { + if (viewHolder instanceof QueueRecyclerAdapter.ItemTouchHelperViewHolder) { + QueueRecyclerAdapter.ItemTouchHelperViewHolder itemViewHolder = + (QueueRecyclerAdapter.ItemTouchHelperViewHolder) viewHolder; + itemViewHolder.onItemSelected(); } } + + super.onSelectedChanged(viewHolder, actionState); + } + @Override + public void clearView(RecyclerView recyclerView, + RecyclerView.ViewHolder viewHolder) { + super.clearView(recyclerView, viewHolder); + + if (viewHolder instanceof QueueRecyclerAdapter.ItemTouchHelperViewHolder) { + QueueRecyclerAdapter.ItemTouchHelperViewHolder itemViewHolder = + (QueueRecyclerAdapter.ItemTouchHelperViewHolder) viewHolder; + itemViewHolder.onItemClear(); + } } } + ); + itemTouchHelper.attachToRecyclerView(recyclerView); - }); - - registerForContextMenu(listView); - - if (!itemsLoaded) { - progLoading.setVisibility(View.VISIBLE); - txtvEmpty.setVisibility(View.GONE); - } - - viewsCreated = true; - - if (itemsLoaded && activity.get() != null) { - onFragmentLoaded(); - } + txtvEmpty = (TextView) root.findViewById(android.R.id.empty); + txtvEmpty.setVisibility(View.GONE); + progLoading = (ProgressBar) root.findViewById(R.id.progLoading); + progLoading.setVisibility(View.VISIBLE); return root; } private void onFragmentLoaded() { - if (listAdapter == null) { - listAdapter = new QueueListAdapter(activity.get(), itemAccess, new DefaultActionButtonCallback(activity.get())); - listView.setAdapter(listAdapter); - downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback); + if (recyclerAdapter == null) { + MainActivity activity = (MainActivity) getActivity(); + recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess, + new DefaultActionButtonCallback(activity), itemTouchHelper); + recyclerView.setAdapter(recyclerAdapter); + downloadObserver = new DownloadObserver(activity, new Handler(), downloadObserverCallback); downloadObserver.onResume(); } - listAdapter.notifyDataSetChanged(); + if(queue == null || queue.size() == 0) { + recyclerView.setVisibility(View.GONE); + txtvEmpty.setVisibility(View.VISIBLE); + } else { + txtvEmpty.setVisibility(View.GONE); + recyclerView.setVisibility(View.VISIBLE); + } restoreScrollPosition(); @@ -486,7 +463,10 @@ public class QueueFragment extends Fragment { // needs data that may have just been loaded. getActivity().supportInvalidateOptionsMenu(); - // refresh information bar + refreshInfoBar(); + } + + private void refreshInfoBar() { String info = queue.size() + getString(R.string.episodes_suffix); if(queue.size() > 0) { long duration = 0; @@ -505,21 +485,21 @@ public class QueueFragment extends Fragment { @Override public void onContentChanged(List<Downloader> downloaderList) { QueueFragment.this.downloaderList = downloaderList; - if (listAdapter != null && !blockDownloadObserverUpdate) { - listAdapter.notifyDataSetChanged(); + if (recyclerAdapter != null && !blockDownloadObserverUpdate) { + recyclerAdapter.notifyDataSetChanged(); } } }; - private QueueListAdapter.ItemAccess itemAccess = new QueueListAdapter.ItemAccess() { + private QueueRecyclerAdapter.ItemAccess itemAccess = new QueueRecyclerAdapter.ItemAccess() { @Override public int getCount() { - return (itemsLoaded) ? queue.size() : 0; + return queue != null ? queue.size() : 0; } @Override public FeedItem getItem(int position) { - return (itemsLoaded) ? queue.get(position) : null; + return queue != null ? queue.get(position) : null; } @Override @@ -561,6 +541,11 @@ public class QueueFragment extends Fragment { } return 0; } + + @Override + public LongList getQueueIds() { + return queue != null ? LongList.of(FeedItemUtil.getIds(queue)) : new LongList(0); + } }; private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @@ -579,23 +564,19 @@ public class QueueFragment extends Fragment { if(subscription != null) { subscription.unsubscribe(); } - if (viewsCreated && !itemsLoaded) { - listView.setVisibility(View.GONE); + if (queue == null) { + recyclerView.setVisibility(View.GONE); txtvEmpty.setVisibility(View.GONE); progLoading.setVisibility(View.VISIBLE); } subscription = Observable.defer(() -> Observable.just(DBReader.getQueue())) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> { - listView.setVisibility(View.VISIBLE); - progLoading.setVisibility(View.GONE); - if(result != null) { - queue = result; - itemsLoaded = true; - if (viewsCreated && activity.get() != null) { - onFragmentLoaded(); - } + .subscribe(items -> { + if(items != null) { + progLoading.setVisibility(View.GONE); + queue = items; + onFragmentLoaded(); } }, error -> { Log.e(TAG, Log.getStackTraceString(error)); diff --git a/app/src/main/java/de/danoeh/antennapod/view/DividerItemDecoration.java b/app/src/main/java/de/danoeh/antennapod/view/DividerItemDecoration.java deleted file mode 100644 index c32f7e702..000000000 --- a/app/src/main/java/de/danoeh/antennapod/view/DividerItemDecoration.java +++ /dev/null @@ -1,124 +0,0 @@ -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."); - } - } -} |