summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java250
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java302
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java351
-rw-r--r--app/src/main/res/layout/queue_fragment.xml38
-rw-r--r--app/src/main/res/layout/queue_listitem.xml18
6 files changed, 477 insertions, 484 deletions
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..539c2786f
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -0,0 +1,302 @@
+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 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;
+
+ 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.position = pos;
+ }
+
+ public int getItemCount() {
+ return itemAccess.getCount();
+ }
+
+
+ public class ViewHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener, View.OnCreateContextMenuListener {
+
+ 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;
+ private int position;
+
+ 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());
+ }
+
+ 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();
+ }
+}
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..00ed99444 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,9 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
+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;
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..cf159b4a1 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -1,14 +1,17 @@
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;
@@ -22,22 +25,21 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-import com.mobeta.android.dslv.DragSortListView;
-
+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,12 +49,13 @@ 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.IntList;
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.danoeh.antennapod.view.DividerItemDecoration;
import de.greenrobot.event.EventBus;
import rx.Observable;
import rx.Subscription;
@@ -71,28 +74,22 @@ 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 +99,8 @@ public class QueueFragment extends Fragment {
private boolean blockDownloadObserverUpdate = false;
private Subscription subscription;
+ private LinearLayoutManager layoutManager;
+ private ItemTouchHelper itemTouchHelper;
@Override
@@ -115,19 +114,18 @@ public class QueueFragment extends Fragment {
public void onResume() {
super.onResume();
loadItems();
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().register(this);
}
@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();
}
}
@@ -136,66 +134,90 @@ public class QueueFragment extends Fragment {
public void onPause() {
super.onPause();
saveScrollPosition();
- }
-
- @Override
- public void onStop() {
- super.onStop();
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:
+ // ItemTouchHelper already handled everything
+ break;
+ }
}
- 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));
+ IntList positions = new IntList();
+ 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;
+ unregisterForContextMenu(recyclerView);
blockDownloadObserverUpdate = false;
if (downloadObserver != null) {
downloadObserver.onPause();
@@ -208,17 +230,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 +270,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();
@@ -322,28 +336,6 @@ public class QueueFragment extends Fragment {
};
@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) {
@@ -373,112 +365,86 @@ 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.ItemDecoration itemDecoration = new DividerItemDecoration(getActivity(), null);
+ recyclerView.addItemDecoration(itemDecoration);
+ recyclerView.setHasFixedSize(true);
- 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()));
- }
- }
- });
-
- 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 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, true);
+ return true;
}
- 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 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();
}
- 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 isLongPressDragEnabled() {
+ 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 boolean isItemViewSwipeEnabled() {
+ return false == UserPreferences.isQueueLocked();
}
}
+ );
+ 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);
+ }
+ recyclerAdapter.notifyDataSetChanged();
restoreScrollPosition();
@@ -505,21 +471,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 +527,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 +550,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/res/layout/queue_fragment.xml b/app/src/main/res/layout/queue_fragment.xml
index 339369971..ba2cdd110 100644
--- a/app/src/main/res/layout/queue_fragment.xml
+++ b/app/src/main/res/layout/queue_fragment.xml
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:dslv="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -21,26 +20,12 @@
android:layout_below="@id/info_bar"
android:background="?android:attr/listDivider"/>
- <com.mobeta.android.dslv.DragSortListView
- android:id="@android:id/list"
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
- android:layout_below="@+id/divider"
- dslv:collapsed_height="2dp"
- dslv:drag_enabled="true"
- dslv:drag_handle_id="@id/drag_handle"
- dslv:drag_scroll_start="0.33"
- dslv:float_alpha="0.6"
- dslv:float_background_color="?attr/dragview_float_background"
- dslv:max_drag_scroll_speed="0.5"
- dslv:remove_enabled="true"
- dslv:remove_mode="flingRemove"
- dslv:slide_shuffle_speed="0.3"
- dslv:sort_enabled="true"
- dslv:track_drag_sort="true"
- dslv:use_default_controller="true" />
+ android:layout_below="@id/divider"
+ android:scrollbars="vertical"/>
<TextView
android:id="@id/android:empty"
@@ -58,19 +43,4 @@
android:indeterminateOnly="true"
android:visibility="gone" />
- <LinearLayout
- android:id="@+id/undobar"
- android:layout_alignParentBottom="true"
- style="@style/UndoBar">
-
- <TextView
- android:id="@+id/undobar_message"
- style="@style/UndoBarMessage"/>
-
- <Button
- android:id="@+id/undobar_button"
- style="@style/UndoBarButton"/>
-
- </LinearLayout>
-
</RelativeLayout>
diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml
index 38076ff51..8572f5e05 100644
--- a/app/src/main/res/layout/queue_listitem.xml
+++ b/app/src/main/res/layout/queue_listitem.xml
@@ -6,24 +6,28 @@
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
android:orientation="horizontal"
- android:paddingLeft="16dp"
+ android:paddingLeft="8dp"
+ android:gravity="center_vertical"
+ android:background="?attr/selectableItemBackground"
tools:background="@android:color/darker_gray" >
<ImageView
android:id="@+id/drag_handle"
- android:layout_width="100dp"
- android:layout_height="match_parent"
- android:layout_marginLeft="-8dp"
- android:layout_marginRight="-64dp"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginLeft="-16dp"
+ android:layout_marginRight="-20dp"
+ android:gravity="center"
android:contentDescription="@string/drag_handle_content_description"
android:scaleType="fitXY"
android:src="?attr/dragview_background"
- tools:src="@drawable/ic_drag_handle"
+ tools:src="@drawable/ic_drag_vertical_grey600_48dp"
tools:background="@android:color/holo_green_dark" />
<RelativeLayout
android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp">
<TextView
android:id="@+id/txtvPlaceholder"
android:layout_width="@dimen/thumbnail_length_queue_item"