summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--README.md6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java56
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java52
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java92
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java2
-rw-r--r--app/src/main/res/layout/cover_fragment.xml2
-rw-r--r--app/src/main/res/layout/feeditem_fragment_header.xml25
-rw-r--r--app/src/main/res/layout/queue_fragment.xml19
-rw-r--r--app/src/main/res/layout/queue_listitem.xml50
-rw-r--r--app/src/main/res/xml/preferences.xml12
-rw-r--r--core/build.gradle3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java33
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java24
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java37
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndDateUtils.java27
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/gui/UndoBarController.java121
-rw-r--r--core/src/main/res/values/strings.xml2
-rwxr-xr-xdescription/en.txt23
22 files changed, 557 insertions, 48 deletions
diff --git a/.gitignore b/.gitignore
index 12a398578..153ef778f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,7 +33,6 @@ gen-external-apklibs
out
#transifex downloads
changelog
-description
# other
*.odg#
diff --git a/README.md b/README.md
index 85aadcc65..aaff4426a 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# AntennaPod
-```
-Please note that AntennaPod will not receive any future updates until further notice.
-```
+
+### Please note that AntennaPod will not receive any future updates until further notice (see issue [#604](https://github.com/antennapod/antennapod/issues/604) for more information).
+
This is the official repository of AntennaPod, a podcast manager for Android.
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java
index d5b85575b..77b2b4d49 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
+import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -16,6 +17,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.Converter;
/**
* List adapter for the queue.
@@ -64,12 +66,16 @@ public class QueueListAdapter extends BaseAdapter {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.queue_listitem,
parent, false);
+ holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
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.position = (TextView) convertView.findViewById(R.id.txtvPosition);
holder.progress = (ProgressBar) convertView
- .findViewById(R.id.pbar_download_progress);
+ .findViewById(R.id.progressBar);
holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
convertView.setTag(holder);
} else {
@@ -77,19 +83,39 @@ public class QueueListAdapter extends BaseAdapter {
}
holder.title.setText(item.getTitle());
+ FeedMedia media = item.getMedia();
- AdapterUtils.updateEpisodePlaybackProgress(item, context.getResources(), holder.position, holder.progress);
- 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);
-
- if (!media.isDownloaded()) {
- if (isDownloadingMedia) {
- // item is being downloaded
+ 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.progress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
+ holder.progressLeft.setText(Converter
+ .getDurationStringLong(media.getPosition()));
+ holder.progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
}
+ } else {
+ holder.progressLeft.setText(Converter.byteToString(media.getSize()));
+ holder.progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
+ holder.progress.setVisibility(View.GONE);
}
}
@@ -116,18 +142,20 @@ public class QueueListAdapter extends BaseAdapter {
static class Holder {
- TextView title;
ImageView imageView;
- TextView position;
+ TextView title;
+ TextView pubDate;
+ TextView progressLeft;
+ TextView progressRight;
ProgressBar progress;
ImageButton butSecondary;
}
public interface ItemAccess {
- int getCount();
-
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/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
index c40fce351..0f6f7d53c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -1,25 +1,35 @@
package de.danoeh.antennapod.fragment;
import android.content.Context;
+import android.content.res.TypedArray;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
+import android.support.v4.view.MenuItemCompat;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
+import java.util.List;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadLogAdapter;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
-
-import java.util.List;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
/**
* Shows the download log
*/
public class DownloadLogFragment extends ListFragment {
+ private static final String TAG = "DownloadLogFragment";
+
private List<DownloadStatus> downloadLog;
private DownloadLogAdapter adapter;
@@ -29,6 +39,7 @@ public class DownloadLogFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
+ setHasOptionsMenu(true);
EventDistributor.getInstance().register(contentUpdate);
startItemLoader();
}
@@ -63,7 +74,7 @@ public class DownloadLogFragment extends ListFragment {
}
setListShown(true);
adapter.notifyDataSetChanged();
-
+ getActivity().supportInvalidateOptionsMenu();
}
private DownloadLogAdapter.ItemAccess itemAccess = new DownloadLogAdapter.ItemAccess() {
@@ -105,6 +116,41 @@ public class DownloadLogFragment extends ListFragment {
}
}
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
+ MenuItemCompat.setShowAsAction(clearHistory, MenuItemCompat.SHOW_AS_ACTION_IF_ROOM);
+ TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.content_discard});
+ clearHistory.setIcon(drawables.getDrawable(0));
+ drawables.recycle();
+ }
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ if (itemsLoaded && !MenuItemUtils.isActivityDrawerOpen((NavDrawerActivity) getActivity())) {
+ menu.findItem(R.id.clear_history_item).setVisible(downloadLog != null && !downloadLog.isEmpty());
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (!super.onOptionsItemSelected(item)) {
+ switch (item.getItemId()) {
+ case R.id.clear_history_item:
+ DBWriter.clearDownloadLog(getActivity());
+ return true;
+ default:
+ return false;
+ }
+ } else {
+ return true;
+ }
+ }
+
private class ItemLoader extends AsyncTask<Void, Void, List<DownloadStatus>> {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index ac9e744ed..e80bf5f14 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -16,6 +16,7 @@ import android.support.v4.util.Pair;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
+import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
@@ -49,6 +50,7 @@ import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.core.util.playback.Timeline;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
@@ -91,6 +93,8 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
private View header;
private WebView webvDescription;
private TextView txtvTitle;
+ private TextView txtvDuration;
+ private TextView txtvPublished;
private ImageView imgvCover;
private ProgressBar progbarDownload;
private ProgressBar progbarLoading;
@@ -166,6 +170,8 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
header = inflater.inflate(R.layout.feeditem_fragment_header, toolbar, false);
root = (ViewGroup) layout.findViewById(R.id.content_root);
txtvTitle = (TextView) header.findViewById(R.id.txtvTitle);
+ txtvDuration = (TextView) header.findViewById(R.id.txtvDuration);
+ txtvPublished = (TextView) header.findViewById(R.id.txtvPublished);
if (Build.VERSION.SDK_INT >= 14) { // ellipsize is causing problems on old versions, see #448
txtvTitle.setEllipsize(TextUtils.TruncateAt.END);
}
@@ -313,6 +319,8 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
private void updateAppearance() {
txtvTitle.setText(item.getTitle());
+ txtvPublished.setText(DateUtils.formatDateTime(getActivity(), item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
+
Picasso.with(getActivity()).load(item.getImageUri())
.fit()
.into(imgvCover);
@@ -348,7 +356,10 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
}
drawables.recycle();
- } else {
+ } else {if(media.getDuration() > 0) {
+ txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
+ }
+
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.av_play,
R.attr.av_download, R.attr.action_stream, R.attr.content_discard, R.attr.navigation_cancel});
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 913b544a5..da33c6ea3 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -3,9 +3,11 @@ package de.danoeh.antennapod.fragment;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Parcelable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.SearchView;
import android.util.Log;
@@ -42,6 +44,8 @@ import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.QueueSorter;
+import de.danoeh.antennapod.core.util.gui.FeedItemUndoToken;
+import de.danoeh.antennapod.core.util.gui.UndoBarController;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
@@ -59,6 +63,8 @@ public class QueueFragment extends Fragment {
private TextView txtvEmpty;
private ProgressBar progLoading;
+ private UndoBarController undoBarController;
+
private List<FeedItem> queue;
private List<Downloader> downloaderList;
@@ -66,6 +72,10 @@ public class QueueFragment extends Fragment {
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 DownloadObserver downloadObserver = null;
@@ -104,6 +114,12 @@ public class QueueFragment extends Fragment {
}
@Override
+ public void onPause() {
+ super.onPause();
+ saveScrollPosition();
+ }
+
+ @Override
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
@@ -116,10 +132,35 @@ public class QueueFragment extends Fragment {
this.activity.set((MainActivity) activity);
}
+ private void saveScrollPosition() {
+ 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.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);
+ // restore once, then forget
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(PREF_KEY_LIST_SELECTION, 0);
+ editor.putInt(PREF_KEY_LIST_TOP, 0);
+ editor.commit();
+ }
+ }
+
private void resetViewState() {
unregisterForContextMenu(listView);
listAdapter = null;
activity.set(null);
+ undoBarController = null;
viewsCreated = false;
blockDownloadObserverUpdate = false;
if (downloadObserver != null) {
@@ -301,9 +342,31 @@ public class QueueFragment extends Fragment {
@Override
public void remove(int which) {
+ Log.d(TAG, "remove("+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);
+ }
+ }
+ });
+
+
registerForContextMenu(listView);
if (!itemsLoaded) {
@@ -329,6 +392,8 @@ public class QueueFragment extends Fragment {
}
listAdapter.notifyDataSetChanged();
+ restoreScrollPosition();
+
// we need to refresh the options menu because it sometimes
// needs data that may have just been loaded.
getActivity().supportInvalidateOptionsMenu();
@@ -363,6 +428,33 @@ public class QueueFragment extends Fragment {
}
@Override
+ public long getItemDownloadedBytes(FeedItem item) {
+ if (downloaderList != null) {
+ for (Downloader downloader : downloaderList) {
+ if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
+ && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
+ Log.d(TAG, "downloaded bytes: " + downloader.getDownloadRequest().getSoFar());
+ return downloader.getDownloadRequest().getSoFar();
+ }
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public long getItemDownloadSize(FeedItem item) {
+ if (downloaderList != null) {
+ for (Downloader downloader : downloaderList) {
+ if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
+ && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
+ Log.d(TAG, "downloaded size: " + downloader.getDownloadRequest().getSize());
+ return downloader.getDownloadRequest().getSize();
+ }
+ }
+ }
+ return 0;
+ }
+ @Override
public int getItemDownloadProgressPercent(FeedItem item) {
if (downloaderList != null) {
for (Downloader downloader : downloaderList) {
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
index ffac05321..43f942308 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
@@ -59,8 +59,6 @@ public class PreferenceController {
public static final String PREF_GPODNET_LOGOUT = "pref_gpodnet_logout";
public static final String PREF_GPODNET_HOSTNAME = "pref_gpodnet_hostname";
public static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify";
- private static final String PREF_PERSISTENT_NOTIFICATION = "prefPersistNotify";
-
private final PreferenceUI ui;
diff --git a/app/src/main/res/layout/cover_fragment.xml b/app/src/main/res/layout/cover_fragment.xml
index 7d86346e3..18540aa1f 100644
--- a/app/src/main/res/layout/cover_fragment.xml
+++ b/app/src/main/res/layout/cover_fragment.xml
@@ -13,7 +13,7 @@
android:layout_height="match_parent"
android:layout_gravity="center"
android:adjustViewBounds="true"
- android:scaleType="centerCrop"
+ android:scaleType="centerInside"
tools:src="@android:drawable/sym_def_app_icon" />
</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/feeditem_fragment_header.xml b/app/src/main/res/layout/feeditem_fragment_header.xml
index 5956ae062..a21488306 100644
--- a/app/src/main/res/layout/feeditem_fragment_header.xml
+++ b/app/src/main/res/layout/feeditem_fragment_header.xml
@@ -16,7 +16,7 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="horizontal"
- android:paddingBottom="8dp">
+ android:paddingBottom="0dp">
<ImageView
android:id="@+id/imgvCover"
@@ -59,6 +59,29 @@
android:maxLines="5"
tools:text="Podcast title"
tools:background="@android:color/holo_green_dark" />
+
+ <TextView
+ android:id="@+id/txtvDuration"
+ style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_below="@id/txtvTitle"
+ android:layout_marginLeft="16dp"
+ tools:text="00:42:23"
+ tools:background="@android:color/holo_green_dark"/>
+
+ <TextView
+ android:id="@+id/txtvPublished"
+ style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toLeftOf="@id/butMoreActions"
+ android:layout_marginRight="8dp"
+ tools:text="Jan 23"
+ tools:background="@android:color/holo_green_dark"
+ android:layout_below="@+id/txtvTitle"/>
+
</RelativeLayout>
<ProgressBar
diff --git a/app/src/main/res/layout/queue_fragment.xml b/app/src/main/res/layout/queue_fragment.xml
index d184eb28d..307d95a8d 100644
--- a/app/src/main/res/layout/queue_fragment.xml
+++ b/app/src/main/res/layout/queue_fragment.xml
@@ -20,7 +20,8 @@
dslv:float_alpha="0.6"
dslv:float_background_color="?attr/dragview_float_background"
dslv:max_drag_scroll_speed="0.5"
- dslv:remove_enabled="false"
+ dslv:remove_enabled="true"
+ dslv:remove_mode="flingRemove"
dslv:slide_shuffle_speed="0.3"
dslv:sort_enabled="true"
dslv:track_drag_sort="true"
@@ -42,4 +43,18 @@
android:indeterminateOnly="true"
android:visibility="gone" />
-</FrameLayout> \ No newline at end of file
+ <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>
diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml
index 74c6ed785..5d41c52cd 100644
--- a/app/src/main/res/layout/queue_listitem.xml
+++ b/app/src/main/res/layout/queue_listitem.xml
@@ -32,7 +32,7 @@
<RelativeLayout
android:layout_width="0dp"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
@@ -40,46 +40,72 @@
android:layout_weight="1"
tools:background="@android:color/holo_red_dark">
+ <!-- order is important, pubDate first! -->
+ <TextView
+ android:id="@+id/txtvPubDate"
+ style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:lines="2"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="8dp"
+ android:gravity="right|bottom"
+ android:text="Feb\n12"
+ tools:background="@android:color/holo_blue_light" />
+
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_toLeftOf="@id/txtvPubDate"
android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:text="Queue item title"
+ android:ellipsize="end"
tools:background="@android:color/holo_blue_light" />
<RelativeLayout
android:id="@+id/bottom_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_below="@id/txtvTitle"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
- android:layout_marginTop="16dp">
+ android:layout_alignParentRight="true">
<TextView
- android:id="@+id/txtvPosition"
+ android:id="@+id/txtvProgressLeft"
style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_marginBottom="0dp"
android:text="00:42:23"
- tools:background="@android:color/holo_blue_light" />
+ tools:background="@android:color/holo_blue_light"/>
+
+ <TextView
+ android:id="@+id/txtvProgressRight"
+ style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_marginBottom="0dp"
+ tools:text="Jan 23"
+ tools:background="@android:color/holo_green_dark" />
<ProgressBar
- android:id="@+id/pbar_download_progress"
+ android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
- android:layout_width="0dp"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_marginLeft="8dp"
- android:layout_toRightOf="@id/txtvPosition"
+ android:layout_below="@id/txtvProgressLeft"
+ android:layout_marginTop="-2dp"
android:max="100"
tools:background="@android:color/holo_blue_light" />
+
</RelativeLayout>
</RelativeLayout>
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 5169eac5a..cf1be1a74 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -22,6 +22,17 @@
android:summary="@string/pref_persistNotify_sum"
android:title="@string/pref_persistNotify_title"/>
</PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/queue_label">
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:enabled="true"
+ android:key="prefQueueAddToFront"
+ android:summary="@string/pref_queueAddToFront_sum"
+ android:title="@string/pref_queueAddToFront_title"/>
+ />
+ </PreferenceCategory>
+
<PreferenceCategory android:title="@string/playback_pref">
<CheckBoxPreference
android:defaultValue="true"
@@ -111,6 +122,7 @@
</PreferenceScreen>
</PreferenceCategory>
+
<PreferenceCategory android:title="@string/services_label">
<PreferenceScreen
android:key="prefFlattrSettings"
diff --git a/core/build.gradle b/core/build.gradle
index dfe0fb133..710378a18 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -44,4 +44,5 @@ dependencies {
compile 'com.squareup.okhttp:okhttp:2.2.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.2.0'
compile 'com.squareup.okio:okio:1.2.0'
-} \ No newline at end of file
+ compile 'com.nineoldandroids:library:2.4.0'
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
index a3b9f6049..7cbb69a7f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
@@ -60,6 +60,7 @@ public class UserPreferences implements
private static final String PREF_SEEK_DELTA_SECS = "prefSeekDeltaSecs";
private static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify";
private static final String PREF_PERSISTENT_NOTIFICATION = "prefPersistNotify";
+ public static final String PREF_QUEUE_ADD_TO_FRONT = "prefQueueAddToFront";
// TODO: Make this value configurable
private static final float PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT = 0.8f;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
index 87bbdf455..c5bf89533 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
@@ -7,7 +7,6 @@ import android.content.SharedPreferences;
import android.database.Cursor;
import android.preference.PreferenceManager;
import android.util.Log;
-
import org.shredzone.flattr4j.model.Flattr;
import java.io.File;
@@ -35,6 +34,7 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.QueueAccess;
@@ -239,6 +239,26 @@ public class DBWriter {
}
/**
+ * Deletes the entire download log.
+ *
+ * @param context A context that is used for opening a database connection.
+ */
+ public static Future<?> clearDownloadLog(final Context context) {
+ return dbExec.submit(new Runnable() {
+ @Override
+ public void run() {
+ PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+ adapter.clearDownloadLog();
+ adapter.close();
+ EventDistributor.getInstance()
+ .sendDownloadLogUpdateBroadcast();
+ }
+ });
+ }
+
+
+ /**
* Adds a FeedMedia object to the playback history. A FeedMedia object is in the playback history if
* its playback completion date is set to a non-null value. This method will set the playback completion date to the
* current date regardless of the current value.
@@ -386,7 +406,16 @@ public class DBWriter {
context, itemIds[i]);
if (item != null) {
- queue.add(item);
+ // add item to either front ot back of queue
+ boolean addToFront = PreferenceManager.getDefaultSharedPreferences(context)
+ .getBoolean(UserPreferences.PREF_QUEUE_ADD_TO_FRONT, false);
+
+ if(addToFront){
+ queue.add(0, item);
+ }else{
+ queue.add(item);
+ }
+
queueModified = true;
if (!item.isRead()) {
item.setRead(true);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
index ce41147e1..f72858adc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
@@ -889,6 +889,10 @@ public class PodDBAdapter {
db.update(TABLE_NAME_FEED_MEDIA, values, null, null);
}
+ public void clearDownloadLog() {
+ db.delete(TABLE_NAME_DOWNLOAD_LOG, null, null);
+ }
+
/**
* Get all Feeds from the Feed Table.
*
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java
index 1dda24944..47503dee4 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java
@@ -1,14 +1,23 @@
package de.danoeh.antennapod.core.syndication.handler;
import android.util.Log;
-import de.danoeh.antennapod.core.BuildConfig;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.syndication.namespace.*;
-import de.danoeh.antennapod.core.syndication.namespace.atom.NSAtom;
+
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
+import de.danoeh.antennapod.core.BuildConfig;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.syndication.namespace.NSContent;
+import de.danoeh.antennapod.core.syndication.namespace.NSDublinCore;
+import de.danoeh.antennapod.core.syndication.namespace.NSITunes;
+import de.danoeh.antennapod.core.syndication.namespace.NSMedia;
+import de.danoeh.antennapod.core.syndication.namespace.NSRSS20;
+import de.danoeh.antennapod.core.syndication.namespace.NSSimpleChapters;
+import de.danoeh.antennapod.core.syndication.namespace.Namespace;
+import de.danoeh.antennapod.core.syndication.namespace.SyndElement;
+import de.danoeh.antennapod.core.syndication.namespace.atom.NSAtom;
+
/** Superclass for all SAX Handlers which process Syndication formats */
public class SyndHandler extends DefaultHandler {
private static final String TAG = "SyndHandler";
@@ -100,7 +109,12 @@ public class SyndHandler extends DefaultHandler {
state.namespaces.put(uri, new NSMedia());
if (BuildConfig.DEBUG)
Log.d(TAG, "Recognized media namespace");
- }
+ } else if (uri.equals(NSDublinCore.NSURI)
+ && prefix.equals(NSDublinCore.NSTAG)) {
+ state.namespaces.put(uri, new NSDublinCore());
+ if (BuildConfig.DEBUG)
+ Log.d(TAG, "Recognized DublinCore namespace");
+ }
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java
new file mode 100644
index 000000000..099593eed
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java
@@ -0,0 +1,37 @@
+package de.danoeh.antennapod.core.syndication.namespace;
+
+import org.xml.sax.Attributes;
+
+import de.danoeh.antennapod.core.syndication.handler.HandlerState;
+import de.danoeh.antennapod.core.syndication.util.SyndDateUtils;
+
+public class NSDublinCore extends Namespace {
+ private static final String TAG = "NSDublinCore";
+ public static final String NSTAG = "dc";
+ public static final String NSURI = "http://purl.org/dc/elements/1.1/";
+
+ private static final String ITEM = "item";
+ private static final String DATE = "date";
+
+ @Override
+ public SyndElement handleElementStart(String localName, HandlerState state,
+ Attributes attributes) {
+ return new SyndElement(localName, this);
+ }
+
+ @Override
+ public void handleElementEnd(String localName, HandlerState state) {
+ if(state.getTagstack().size() >= 2
+ && state.getContentBuf() != null) {
+ String content = state.getContentBuf().toString();
+ SyndElement topElement = state.getTagstack().peek();
+ String top = topElement.getName();
+ SyndElement secondElement = state.getSecondTag();
+ String second = secondElement.getName();
+ if (top.equals(DATE) && second.equals(ITEM)) {
+ state.getCurrentItem().setPubDate(
+ SyndDateUtils.parseISO8601Date(content));
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndDateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndDateUtils.java
index 1ac389f08..a9929d7b1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndDateUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndDateUtils.java
@@ -28,6 +28,8 @@ public class SyndDateUtils {
*/
public static final String RFC3339LOCAL = "yyyy-MM-dd'T'HH:mm:ssZ";
+ public static final String ISO8601_SHORT = "yyyy-MM-dd";
+
private static ThreadLocal<SimpleDateFormat> RFC822Formatter = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
@@ -44,6 +46,14 @@ public class SyndDateUtils {
};
+ private static ThreadLocal<SimpleDateFormat> ISO8601ShortFormatter = new ThreadLocal<SimpleDateFormat>() {
+ @Override
+ protected SimpleDateFormat initialValue() {
+ return new SimpleDateFormat(ISO8601_SHORT, Locale.US);
+ }
+
+ };
+
public static Date parseRFC822Date(String date) {
Date result = null;
if (date.contains("PDT")) {
@@ -123,6 +133,23 @@ public class SyndDateUtils {
}
+ public static Date parseISO8601Date(String date) {
+ if(date.length() > ISO8601_SHORT.length()) {
+ return parseRFC3339Date(date);
+ }
+ Date result = null;
+ if(date.length() == "YYYYMMDD".length()) {
+ date = date.substring(0, 4) + "-" + date.substring(4, 6) + "-" + date.substring(6,8);
+ }
+ SimpleDateFormat format = ISO8601ShortFormatter.get();
+ try {
+ result = format.parse(date);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
/**
* Takes a string of the form [HH:]MM:SS[.mmm] and converts it to
* milliseconds.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/UndoBarController.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/UndoBarController.java
new file mode 100644
index 000000000..0e03bc8b4
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/gui/UndoBarController.java
@@ -0,0 +1,121 @@
+package de.danoeh.antennapod.core.util.gui;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+
+import com.nineoldandroids.animation.Animator;
+import com.nineoldandroids.animation.AnimatorListenerAdapter;
+import com.nineoldandroids.view.ViewHelper;
+import com.nineoldandroids.view.ViewPropertyAnimator;
+
+import de.danoeh.antennapod.core.R;
+
+import static com.nineoldandroids.view.ViewPropertyAnimator.animate;
+
+public class UndoBarController {
+ private View mBarView;
+ private TextView mMessageView;
+ private ViewPropertyAnimator mBarAnimator;
+ private Handler mHideHandler = new Handler();
+
+ private UndoListener mUndoListener;
+
+ // State objects
+ private Parcelable mUndoToken;
+ private CharSequence mUndoMessage;
+
+ public interface UndoListener {
+ void onUndo(Parcelable token);
+ }
+
+ public UndoBarController(View undoBarView, UndoListener undoListener) {
+ mBarView = undoBarView;
+ mBarAnimator = animate(mBarView);
+ mUndoListener = undoListener;
+
+ mMessageView = (TextView) mBarView.findViewById(R.id.undobar_message);
+ mBarView.findViewById(R.id.undobar_button)
+ .setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ hideUndoBar(false);
+ mUndoListener.onUndo(mUndoToken);
+ }
+ });
+
+ hideUndoBar(true);
+ }
+
+ public void showUndoBar(boolean immediate, CharSequence message, Parcelable undoToken) {
+ mUndoToken = undoToken;
+ mUndoMessage = message;
+ mMessageView.setText(mUndoMessage);
+
+ mHideHandler.removeCallbacks(mHideRunnable);
+ mHideHandler.postDelayed(mHideRunnable,
+ mBarView.getResources().getInteger(R.integer.undobar_hide_delay));
+
+ mBarView.setVisibility(View.VISIBLE);
+ if (immediate) {
+ ViewHelper.setAlpha(mBarView, 1);
+ } else {
+ mBarAnimator.cancel();
+ mBarAnimator
+ .alpha(1)
+ .setDuration(
+ mBarView.getResources()
+ .getInteger(android.R.integer.config_shortAnimTime))
+ .setListener(null);
+ }
+ }
+
+ public void hideUndoBar(boolean immediate) {
+ mHideHandler.removeCallbacks(mHideRunnable);
+ if (immediate) {
+ mBarView.setVisibility(View.GONE);
+ ViewHelper.setAlpha(mBarView, 0);
+ mUndoMessage = null;
+ } else {
+ mBarAnimator.cancel();
+ mBarAnimator
+ .alpha(0)
+ .setDuration(mBarView.getResources()
+ .getInteger(android.R.integer.config_shortAnimTime))
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mBarView.setVisibility(View.GONE);
+ mUndoMessage = null;
+ mUndoToken = null;
+ }
+ });
+ }
+ }
+
+ public void onSaveInstanceState(Bundle outState) {
+ outState.putCharSequence("undo_message", mUndoMessage);
+ outState.putParcelable("undo_token", mUndoToken);
+ }
+
+ public void onRestoreInstanceState(Bundle savedInstanceState) {
+ if (savedInstanceState != null) {
+ mUndoMessage = savedInstanceState.getCharSequence("undo_message");
+ mUndoToken = savedInstanceState.getParcelable("undo_token");
+
+ if (mUndoToken != null || !TextUtils.isEmpty(mUndoMessage)) {
+ showUndoBar(true, mUndoMessage, mUndoToken);
+ }
+ }
+ }
+
+ private Runnable mHideRunnable = new Runnable() {
+ @Override
+ public void run() {
+ hideUndoBar(false);
+ }
+ };
+}
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 9b7cb3585..e8c3408b2 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -272,6 +272,8 @@
<string name="pref_persistNotify_title">Persistent playback controls</string>
<string name="pref_persistNotify_sum">Keep notification and lockscreen controls when playback is paused.</string>
<string name="pref_expand_notify_unsupport_toast">Android versions before 4.1 do not support expanded notifications.</string>
+ <string name="pref_queueAddToFront_sum">Add new episodes to the front of the queue.</string>
+ <string name="pref_queueAddToFront_title">Enqueue at front.</string>
<!-- Auto-Flattr dialog -->
<string name="auto_flattr_enable">Enable automatic flattring</string>
diff --git a/description/en.txt b/description/en.txt
new file mode 100755
index 000000000..7796c4898
--- /dev/null
+++ b/description/en.txt
@@ -0,0 +1,23 @@
+An open-source podcast manager for Android (CURRENTLY UNMAINTAINED).
+
+AntennaPod is an open-source podcast manager for Android 2.3.3 and above. It offers all the basic features you expect from a podcatcher, like streaming and downloading episodes, refreshing all feeds automatically or adding them to a queue to listen to them later. Moreover, AntennaPod lets you flattr podcasts and episodes from within the app.
+
+So far the following features are implemented:
+
+* Downloading and Streaming of episodes
+* Variable speed playback (requires Presto Sound Library or Prestissimo)
+* Support for Atom and RSS feeds
+* Support for password-protected feeds and episodes
+* OPML import and export
+* Flattr integration including automatic flattring
+* Player homescreen widget
+* Search
+* Automatic feed updates
+* Automatic download of new episodes
+* Sleep timer
+* Access to the gpodder.net podcast directory
+* Subscription syncing with the gpodder.net service
+* Supports MP3 chapters, VorbisComment chapters and Podlove Simple Chapters
+* Supports paged feeds (http://podlove.org/paged-feeds/)
+
+Please note that this app is no longer maintained until further notice.