summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xres/drawable-hdpi/ic_drag_handle.pngbin0 -> 220 bytes
-rwxr-xr-xres/drawable-hdpi/ic_drag_handle_dark.pngbin0 -> 204 bytes
-rwxr-xr-xres/drawable-mdpi/ic_drag_handle.pngbin0 -> 175 bytes
-rwxr-xr-xres/drawable-mdpi/ic_drag_handle_dark.pngbin0 -> 159 bytes
-rwxr-xr-xres/drawable-xhdpi/ic_drag_handle.pngbin0 -> 234 bytes
-rwxr-xr-xres/drawable-xhdpi/ic_drag_handle_dark.pngbin0 -> 216 bytes
-rwxr-xr-xres/drawable-xxhdpi/ic_drag_handle.pngbin0 -> 290 bytes
-rwxr-xr-xres/drawable-xxhdpi/ic_drag_handle_dark.pngbin0 -> 265 bytes
-rw-r--r--res/layout/new_episodes_fragment.xml19
-rw-r--r--res/layout/queue_fragment.xml58
-rw-r--r--res/layout/queue_listitem.xml130
-rw-r--r--res/values/strings.xml1
-rw-r--r--res/values/styles.xml4
-rw-r--r--src/de/danoeh/antennapod/activity/MainActivity.java20
-rw-r--r--src/de/danoeh/antennapod/adapter/QueueListAdapter.java176
-rw-r--r--src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java4
-rw-r--r--src/de/danoeh/antennapod/fragment/QueueFragment.java282
-rw-r--r--src/de/danoeh/antennapod/util/gui/FeedItemUndoToken.java55
18 files changed, 731 insertions, 18 deletions
diff --git a/res/drawable-hdpi/ic_drag_handle.png b/res/drawable-hdpi/ic_drag_handle.png
new file mode 100755
index 000000000..38ec201de
--- /dev/null
+++ b/res/drawable-hdpi/ic_drag_handle.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_drag_handle_dark.png b/res/drawable-hdpi/ic_drag_handle_dark.png
new file mode 100755
index 000000000..e96d23252
--- /dev/null
+++ b/res/drawable-hdpi/ic_drag_handle_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_drag_handle.png b/res/drawable-mdpi/ic_drag_handle.png
new file mode 100755
index 000000000..4afbdc67d
--- /dev/null
+++ b/res/drawable-mdpi/ic_drag_handle.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_drag_handle_dark.png b/res/drawable-mdpi/ic_drag_handle_dark.png
new file mode 100755
index 000000000..2b25c4101
--- /dev/null
+++ b/res/drawable-mdpi/ic_drag_handle_dark.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_drag_handle.png b/res/drawable-xhdpi/ic_drag_handle.png
new file mode 100755
index 000000000..5bdcac342
--- /dev/null
+++ b/res/drawable-xhdpi/ic_drag_handle.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_drag_handle_dark.png b/res/drawable-xhdpi/ic_drag_handle_dark.png
new file mode 100755
index 000000000..d341c7c82
--- /dev/null
+++ b/res/drawable-xhdpi/ic_drag_handle_dark.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_drag_handle.png b/res/drawable-xxhdpi/ic_drag_handle.png
new file mode 100755
index 000000000..f834699c6
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_drag_handle.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_drag_handle_dark.png b/res/drawable-xxhdpi/ic_drag_handle_dark.png
new file mode 100755
index 000000000..a9408bc9d
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_drag_handle_dark.png
Binary files differ
diff --git a/res/layout/new_episodes_fragment.xml b/res/layout/new_episodes_fragment.xml
index d8276cf38..460fe73d0 100644
--- a/res/layout/new_episodes_fragment.xml
+++ b/res/layout/new_episodes_fragment.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:dslv="http://schemas.android.com/apk/res-auto"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:dslv="http://schemas.android.com/apk/res-auto"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
<com.mobeta.android.dslv.DragSortListView
android:id="@android:id/list"
@@ -24,7 +24,7 @@
dslv:sort_enabled="false"
dslv:track_drag_sort="false"
dslv:float_background_color="?attr/dragview_float_background"
- dslv:use_default_controller="true" />
+ dslv:use_default_controller="true"/>
<TextView
android:id="@id/android:empty"
@@ -32,13 +32,14 @@
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
- android:text="@string/no_items_label" />
+ android:text="@string/no_items_label"/>
<ProgressBar
android:id="@+id/progLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:indeterminateOnly="true" />
+ android:indeterminateOnly="true"
+ android:visibility="gone"/>
-</LinearLayout> \ No newline at end of file
+</FrameLayout> \ No newline at end of file
diff --git a/res/layout/queue_fragment.xml b/res/layout/queue_fragment.xml
new file mode 100644
index 000000000..1c48768a2
--- /dev/null
+++ b/res/layout/queue_fragment.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:dslv="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.mobeta.android.dslv.DragSortListView
+ android:id="@android:id/list"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:scrollbarStyle="outsideOverlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ 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: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:float_background_color="?attr/dragview_float_background"
+ dslv:use_default_controller="true"/>
+
+ <TextView
+ android:id="@id/android:empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:text="@string/no_items_label"/>
+
+ <ProgressBar
+ android:id="@+id/progLoading"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:indeterminateOnly="true"
+ android:visibility="gone"/>
+
+ <LinearLayout
+ android:id="@+id/undobar"
+ style="@style/UndoBar">
+
+ <TextView
+ android:id="@+id/undobar_message"
+ style="@style/UndoBarMessage"/>
+
+ <Button
+ android:id="@+id/undobar_button"
+ style="@style/UndoBarButton"/>
+ </LinearLayout>
+
+</FrameLayout> \ No newline at end of file
diff --git a/res/layout/queue_listitem.xml b/res/layout/queue_listitem.xml
new file mode 100644
index 000000000..5bac726f3
--- /dev/null
+++ b/res/layout/queue_listitem.xml
@@ -0,0 +1,130 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:layout_width="24dp"
+ android:layout_height="match_parent"
+ android:id="@+id/drag_handle"
+ android:src="?attr/dragview_background"
+ android:scaleType="center"
+ android:layout_margin="8dp"
+ android:contentDescription="@string/drag_handle_content_description"/>
+
+ <RelativeLayout
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:layout_marginRight="16dp">
+
+ <ImageView
+ android:id="@+id/imgvImage"
+ android:contentDescription="@string/cover_label"
+ android:layout_width="@dimen/thumbnail_length_itemlist"
+ android:layout_height="@dimen/thumbnail_length_itemlist"
+ android:layout_alignParentLeft="true"
+ android:scaleType="centerCrop"/>
+
+ <ImageView
+ android:id="@+id/statusPlaying"
+ android:contentDescription="@string/status_playing_label"
+ android:layout_width="@dimen/status_indicator_width"
+ android:layout_height="18dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp"
+ android:background="@color/status_playing"
+ android:gravity="center"
+ android:padding="2dp"
+ android:src="@drawable/av_play_dark"/>
+
+ <TextView
+ android:id="@+id/txtvPublished"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/imgvImage"
+ android:layout_toLeftOf="@id/statusPlaying"
+ android:ellipsize="end"
+ android:maxLines="1"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textSize="@dimen/text_size_micro"/>
+
+ <TextView
+ android:id="@+id/txtvTitle"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/txtvPublished"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="4dp"
+ android:layout_marginTop="2dp"
+ android:layout_toRightOf="@id/imgvImage"
+ android:layout_toLeftOf="@id/statusPlaying"
+ android:ellipsize="end"
+ android:lines="2"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/text_size_small"/>
+
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:layout_toRightOf="@id/imgvImage"
+ android:orientation="vertical">
+
+ <RelativeLayout
+ android:id="@+id/bottom_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <ProgressBar
+ android:id="@+id/pbar_download_progress"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:max="100"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_alignParentLeft="true"/>
+
+ <TextView
+ android:id="@+id/txtvDuration"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentRight="true"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textSize="@dimen/text_size_micro"/>
+ </RelativeLayout>
+ </LinearLayout>
+ </RelativeLayout>
+
+ <View
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:background="@drawable/vertical_divider"
+ android:layout_marginTop="8dp"
+ android:layout_marginBottom="8dp"/>
+
+ <ImageButton
+ android:id="@+id/butSecondaryAction"
+ android:focusable="false"
+ android:clickable="false"
+ android:focusableInTouchMode="false"
+ android:layout_width="@dimen/listview_secondary_button_width"
+ android:layout_height="match_parent"
+ android:background="?attr/borderless_button"
+ tools:ignore="ContentDescription"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 2f64c88b0..ca448f8a8 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -362,6 +362,7 @@
<string name="in_queue_label">Episode is in the queue</string>
<string name="new_episodes_count_label">Number of new episodes</string>
<string name="in_progress_episodes_count_label">Number of episodes you have started listening to</string>
+ <string name="drag_handle_content_description">Drag to change the position of this item</string>
<!-- AntennaPodSP -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 769630046..e29329c78 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -33,7 +33,7 @@
<item name="attr/overlay_background">@color/overlay_light</item>
<item name="attr/spinner_button">@drawable/spinner_button</item>
<item name="attr/overlay_drawable">@drawable/overlay_drawable</item>
- <item name="attr/dragview_background">@drawable/dragview_background</item>
+ <item name="attr/dragview_background">@drawable/ic_drag_handle</item>
<item name="attr/dragview_float_background">@color/white</item>
<item name="attr/nav_drawer_background">@color/white</item>
<item name="attr/nav_drawer_toggle">@drawable/ic_drawer</item>
@@ -71,7 +71,7 @@
<item name="attr/overlay_background">@color/overlay_dark</item>
<item name="attr/spinner_button">@drawable/spinner_button_dark</item>
<item name="attr/overlay_drawable">@drawable/overlay_drawable_dark</item>
- <item name="attr/dragview_background">@drawable/dragview_background_dark</item>
+ <item name="attr/dragview_background">@drawable/ic_drag_handle_dark</item>
<item name="attr/dragview_float_background">@color/black</item>
<item name="attr/nav_drawer_background">#3B3B3B</item>
<item name="attr/nav_drawer_toggle">@drawable/ic_drawer_dark</item>
diff --git a/src/de/danoeh/antennapod/activity/MainActivity.java b/src/de/danoeh/antennapod/activity/MainActivity.java
index 03f44a97d..3b2cc52e9 100644
--- a/src/de/danoeh/antennapod/activity/MainActivity.java
+++ b/src/de/danoeh/antennapod/activity/MainActivity.java
@@ -26,10 +26,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.feed.EventDistributor;
import de.danoeh.antennapod.feed.Feed;
-import de.danoeh.antennapod.fragment.EpisodesFragment;
-import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
-import de.danoeh.antennapod.fragment.ItemlistFragment;
-import de.danoeh.antennapod.fragment.NewEpisodesFragment;
+import de.danoeh.antennapod.fragment.*;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.util.StorageUtils;
@@ -126,8 +123,15 @@ public class MainActivity extends ActionBarActivity {
FragmentTransaction fT = fragmentManager.beginTransaction();
Fragment fragment = null;
if (viewType == NavListAdapter.VIEW_TYPE_NAV) {
+ switch (relPos) {
+ case 0:
+ fragment = new NewEpisodesFragment();
+ break;
+ case 1:
+ fragment = new QueueFragment();
+ break;
+ }
currentTitle = getString(NavListAdapter.NAV_TITLES[relPos]);
- fragment = new NewEpisodesFragment();
} else if (viewType == NavListAdapter.VIEW_TYPE_SUBSCRIPTION) {
Feed feed = itemAccess.getItem(relPos);
@@ -145,13 +149,13 @@ public class MainActivity extends ActionBarActivity {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int viewType = parent.getAdapter().getItemViewType(position);
- if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER) {
+ if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER && position != selectedNavListIndex) {
int relPos = (viewType == NavListAdapter.VIEW_TYPE_NAV) ? position : position - NavListAdapter.SUBSCRIPTION_OFFSET;
loadFragment(viewType, relPos);
- drawerLayout.closeDrawer(navList);
selectedNavListIndex = position;
navAdapter.notifyDataSetChanged();
}
+ drawerLayout.closeDrawer(navList);
}
};
@@ -164,6 +168,7 @@ public class MainActivity extends ActionBarActivity {
if (!drawerLayout.isDrawerOpen(navList)) {
getSupportActionBar().setTitle(currentTitle);
}
+ selectedNavListIndex = savedInstanceState.getInt("selectedNavIndex");
}
}
@@ -177,6 +182,7 @@ public class MainActivity extends ActionBarActivity {
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("title", currentTitle.toString());
+ outState.putInt("selectedNavIndex", selectedNavListIndex);
}
diff --git a/src/de/danoeh/antennapod/adapter/QueueListAdapter.java b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java
new file mode 100644
index 000000000..ae53f6837
--- /dev/null
+++ b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java
@@ -0,0 +1,176 @@
+package de.danoeh.antennapod.adapter;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.text.format.DateUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.*;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.feed.FeedItem;
+import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.storage.DownloadRequester;
+import de.danoeh.antennapod.util.Converter;
+
+/**
+ * List adapter for the queue.
+ */
+public class QueueListAdapter extends BaseAdapter {
+
+
+ private final Context context;
+ private final ItemAccess itemAccess;
+ private final TypedArray drawables;
+ private final int[] labels;
+
+ public QueueListAdapter(Context context, ItemAccess itemAccess) {
+ super();
+ this.context = context;
+ this.itemAccess = itemAccess;
+ drawables = context.obtainStyledAttributes(new int[]{
+ R.attr.av_play, R.attr.navigation_cancel, R.attr.av_download});
+ labels = new int[]{R.string.play_label, R.string.cancel_download_label, R.string.download_label};
+ }
+
+ @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,
+ null);
+ holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ holder.pubDate = (TextView) convertView
+ .findViewById(R.id.txtvPublished);
+ holder.butSecondary = (ImageButton) convertView
+ .findViewById(R.id.butSecondaryAction);
+ holder.statusPlaying = (ImageView) convertView
+ .findViewById(R.id.statusPlaying);
+ holder.downloadProgress = (ProgressBar) convertView
+ .findViewById(R.id.pbar_download_progress);
+ holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
+ holder.txtvDuration = (TextView) convertView.findViewById(R.id.txtvDuration);
+ convertView.setTag(holder);
+ } else {
+ holder = (Holder) convertView.getTag();
+ }
+
+ holder.title.setText(item.getTitle());
+ holder.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_SHOW_DATE));
+ FeedItem.State state = item.getState();
+
+ if (state == FeedItem.State.PLAYING) {
+ holder.statusPlaying.setVisibility(View.VISIBLE);
+ } else {
+ holder.statusPlaying.setVisibility(View.INVISIBLE);
+ }
+
+ FeedMedia media = item.getMedia();
+ if (media != null) {
+ final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
+
+ if (media.getDuration() > 0) {
+ holder.txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
+ } else {
+ holder.txtvDuration.setText("");
+ }
+
+ if (isDownloadingMedia) {
+ holder.downloadProgress.setVisibility(View.VISIBLE);
+ holder.txtvDuration.setVisibility(View.GONE);
+ } else {
+ holder.txtvDuration.setVisibility(View.VISIBLE);
+ holder.downloadProgress.setVisibility(View.GONE);
+ }
+
+ if (!media.isDownloaded()) {
+ if (isDownloadingMedia) {
+ // item is being downloaded
+ holder.butSecondary.setVisibility(View.VISIBLE);
+ holder.butSecondary.setImageDrawable(drawables
+ .getDrawable(1));
+ holder.butSecondary.setContentDescription(context.getString(labels[1]));
+
+ holder.downloadProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
+ } else {
+ // item is not downloaded and not being downloaded
+ holder.butSecondary.setVisibility(View.VISIBLE);
+ holder.butSecondary.setImageDrawable(drawables.getDrawable(2));
+ holder.butSecondary.setContentDescription(context.getString(labels[2]));
+ }
+ } else {
+ // item is not being downloaded
+ holder.butSecondary.setVisibility(View.VISIBLE);
+ holder.butSecondary
+ .setImageDrawable(drawables.getDrawable(0));
+ holder.butSecondary.setContentDescription(context.getString(labels[0]));
+ }
+ } else {
+ holder.butSecondary.setVisibility(View.INVISIBLE);
+ }
+
+ holder.butSecondary.setFocusable(false);
+ holder.butSecondary.setTag(item);
+ holder.butSecondary.setOnClickListener(secondaryActionListener);
+
+
+ ImageLoader.getInstance().loadThumbnailBitmap(
+ item,
+ holder.imageView,
+ (int) convertView.getResources().getDimension(
+ R.dimen.thumbnail_length)
+ );
+ return convertView;
+ }
+
+ private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ FeedItem item = (FeedItem) v.getTag();
+ itemAccess.onFeedItemSecondaryAction(item);
+ }
+ };
+
+
+ static class Holder {
+ TextView title;
+ TextView pubDate;
+ ImageView imageView;
+ ImageView statusPlaying;
+ ProgressBar downloadProgress;
+ TextView txtvDuration;
+ ImageButton butSecondary;
+ }
+
+ public interface ItemAccess {
+ int getCount();
+
+ FeedItem getItem(int position);
+
+ int getItemDownloadProgressPercent(FeedItem item);
+
+ void onFeedItemSecondaryAction(FeedItem item);
+ }
+}
diff --git a/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
index aa3f9ab65..cb5632d05 100644
--- a/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/src/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -111,6 +111,10 @@ public class NewEpisodesFragment extends Fragment {
txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
+ if (!itemsLoaded) {
+ progLoading.setVisibility(View.VISIBLE);
+ txtvEmpty.setVisibility(View.GONE);
+ }
viewsCreated = true;
diff --git a/src/de/danoeh/antennapod/fragment/QueueFragment.java b/src/de/danoeh/antennapod/fragment/QueueFragment.java
new file mode 100644
index 000000000..a18994af5
--- /dev/null
+++ b/src/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -0,0 +1,282 @@
+package de.danoeh.antennapod.fragment;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import com.mobeta.android.dslv.DragSortListView;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.adapter.QueueListAdapter;
+import de.danoeh.antennapod.asynctask.DownloadObserver;
+import de.danoeh.antennapod.feed.EventDistributor;
+import de.danoeh.antennapod.feed.FeedItem;
+import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.service.download.Downloader;
+import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.storage.DBWriter;
+import de.danoeh.antennapod.util.UndoBarController;
+import de.danoeh.antennapod.util.gui.FeedItemUndoToken;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Shows all items in the queue
+ */
+public class QueueFragment extends Fragment {
+ private static final String TAG = "QueueFragment";
+ private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
+ EventDistributor.DOWNLOAD_QUEUED |
+ EventDistributor.QUEUE_UPDATE;
+
+ private DragSortListView listView;
+ private QueueListAdapter listAdapter;
+ private TextView txtvEmpty;
+ private ProgressBar progLoading;
+ private UndoBarController undoBarController;
+
+ private List<FeedItem> queue;
+ private List<Downloader> downloaderList;
+
+ private boolean itemsLoaded = false;
+ private boolean viewsCreated = false;
+
+ private AtomicReference<Activity> activity = new AtomicReference<Activity>();
+
+ private DownloadObserver downloadObserver = null;
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+
+ startItemLoader();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ EventDistributor.getInstance().register(contentUpdate);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ EventDistributor.getInstance().unregister(contentUpdate);
+ stopItemLoader();
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ this.activity.set((MainActivity) activity);
+ if (downloadObserver != null) {
+ downloadObserver.setActivity(activity);
+ downloadObserver.onResume();
+ }
+ if (viewsCreated && itemsLoaded) {
+ onFragmentLoaded();
+ }
+
+
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ listAdapter = null;
+ undoBarController = null;
+ activity.set(null);
+ viewsCreated = false;
+ if (downloadObserver != null) {
+ downloadObserver.onPause();
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ View root = inflater.inflate(R.layout.queue_fragment, container, false);
+ listView = (DragSortListView) root.findViewById(android.R.id.list);
+ txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
+ progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
+ listView.setEmptyView(txtvEmpty);
+
+ listView.setDropListener(new DragSortListView.DropListener() {
+ @Override
+ public void drop(int from, int to) {
+ stopItemLoader();
+ final FeedItem item = queue.remove(from);
+ queue.add(to, item);
+ listAdapter.notifyDataSetChanged();
+ DBWriter.moveQueueItem(getActivity(), from, to, true);
+ }
+ });
+
+ listView.setRemoveListener(new DragSortListView.RemoveListener() {
+ @Override
+ public void remove(int which) {
+ stopItemLoader();
+ FeedItem item = (FeedItem) listView.getAdapter().getItem(which);
+ DBWriter.removeQueueItem(getActivity(), item.getId(), true);
+ undoBarController.showUndoBar(false,
+ getString(R.string.removed_from_queue), new FeedItemUndoToken(item,
+ which));
+ }
+ });
+
+ undoBarController = new UndoBarController(root.findViewById(R.id.undobar), new UndoBarController.UndoListener() {
+ @Override
+ public void onUndo(Parcelable token) {
+ // Perform the undo
+ FeedItemUndoToken undoToken = (FeedItemUndoToken) token;
+ if (token != null) {
+ long itemId = undoToken.getFeedItemId();
+ int position = undoToken.getPosition();
+ DBWriter.addQueueItemAt(getActivity(), itemId, position, false);
+ }
+ }
+ });
+
+ if (!itemsLoaded) {
+ progLoading.setVisibility(View.VISIBLE);
+ txtvEmpty.setVisibility(View.GONE);
+ }
+
+ viewsCreated = true;
+
+ if (itemsLoaded && activity.get() != null) {
+ onFragmentLoaded();
+ }
+
+ return root;
+ }
+
+ private void onFragmentLoaded() {
+ if (listAdapter == null) {
+ listAdapter = new QueueListAdapter(activity.get(), itemAccess);
+ listView.setAdapter(listAdapter);
+ downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback);
+ downloadObserver.onResume();
+ }
+ listAdapter.notifyDataSetChanged();
+ }
+
+ private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
+ @Override
+ public void onContentChanged() {
+ if (listAdapter != null) {
+ listAdapter.notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void onDownloadDataAvailable(List<Downloader> downloaderList) {
+ QueueFragment.this.downloaderList = downloaderList;
+ if (listAdapter != null) {
+ listAdapter.notifyDataSetChanged();
+ }
+ }
+ };
+
+ private QueueListAdapter.ItemAccess itemAccess = new QueueListAdapter.ItemAccess() {
+ @Override
+ public int getCount() {
+ return (itemsLoaded) ? queue.size() : 0;
+ }
+
+ @Override
+ public FeedItem getItem(int position) {
+ return (itemsLoaded) ? queue.get(position) : null;
+ }
+
+ @Override
+ public int getItemDownloadProgressPercent(FeedItem item) {
+ if (downloaderList != null) {
+ for (Downloader downloader : downloaderList) {
+ if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
+ && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
+ return downloader.getDownloadRequest().getProgressPercent();
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public void onFeedItemSecondaryAction(FeedItem item) {
+
+ }
+ };
+
+ private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ @Override
+ public void update(EventDistributor eventDistributor, Integer arg) {
+ if ((arg & EVENTS) != 0) {
+ startItemLoader();
+ }
+ }
+ };
+
+ private ItemLoader itemLoader;
+
+ private void startItemLoader() {
+ if (itemLoader != null) {
+ itemLoader.cancel(true);
+ }
+ itemLoader = new ItemLoader();
+ itemLoader.execute();
+ }
+
+ private void stopItemLoader() {
+ if (itemLoader != null) {
+ itemLoader.cancel(true);
+ }
+ }
+
+ private class ItemLoader extends AsyncTask<Void, Void, List<FeedItem>> {
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ if (viewsCreated && !itemsLoaded) {
+ listView.setVisibility(View.GONE);
+ txtvEmpty.setVisibility(View.GONE);
+ progLoading.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ protected void onPostExecute(List<FeedItem> feedItems) {
+ super.onPostExecute(feedItems);
+ listView.setVisibility(View.VISIBLE);
+ progLoading.setVisibility(View.GONE);
+
+ if (feedItems != null) {
+ queue = feedItems;
+ itemsLoaded = true;
+ if (viewsCreated && activity.get() != null) {
+ onFragmentLoaded();
+ }
+ }
+ }
+
+ @Override
+ protected List<FeedItem> doInBackground(Void... params) {
+ Context context = activity.get();
+ if (context != null) {
+ return DBReader.getQueue(context);
+ }
+ return null;
+ }
+ }
+}
diff --git a/src/de/danoeh/antennapod/util/gui/FeedItemUndoToken.java b/src/de/danoeh/antennapod/util/gui/FeedItemUndoToken.java
new file mode 100644
index 000000000..b920559db
--- /dev/null
+++ b/src/de/danoeh/antennapod/util/gui/FeedItemUndoToken.java
@@ -0,0 +1,55 @@
+package de.danoeh.antennapod.util.gui;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import de.danoeh.antennapod.feed.FeedItem;
+
+/**
+ * Used by an UndoBarController for saving a removed FeedItem
+ */
+public class FeedItemUndoToken implements Parcelable {
+ private long itemId;
+ private long feedId;
+ private int position;
+
+ public FeedItemUndoToken(FeedItem item, int position) {
+ this.itemId = item.getId();
+ this.feedId = item.getFeed().getId();
+ this.position = position;
+ }
+
+ private FeedItemUndoToken(Parcel in) {
+ itemId = in.readLong();
+ feedId = in.readLong();
+ position = in.readInt();
+ }
+
+ public static final Parcelable.Creator<FeedItemUndoToken> CREATOR = new Parcelable.Creator<FeedItemUndoToken>() {
+ public FeedItemUndoToken createFromParcel(Parcel in) {
+ return new FeedItemUndoToken(in);
+ }
+
+ public FeedItemUndoToken[] newArray(int size) {
+ return new FeedItemUndoToken[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(itemId);
+ out.writeLong(feedId);
+ out.writeInt(position);
+ }
+
+ public long getFeedItemId() {
+ return itemId;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+}
+