summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.tx/config1
-rw-r--r--CHANGELOG.md12
-rw-r--r--README.md15
-rw-r--r--app/build.gradle4
-rw-r--r--app/proguard.cfg7
-rw-r--r--app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java25
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java24
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java48
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java56
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java34
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java38
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java5
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java2
-rw-r--r--app/src/main/AndroidManifest.xml14
-rw-r--r--app/src/main/assets/LICENSE_GLIDE.txt94
-rw-r--r--app/src/main/assets/LICENSE_PICASSO.txt13
-rw-r--r--app/src/main/assets/LICENSE_STACKBLUR.txt71
-rw-r--r--app/src/main/java/de/danoeh/antennapod/AppConfig.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/UpdateManager.java88
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java82
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java101
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java152
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java53
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java69
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java76
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java68
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java153
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java414
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java35
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java65
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java129
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java87
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java19
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java68
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java134
-rw-r--r--app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java2
-rw-r--r--app/src/main/res/layout-v14/authentication_dialog.xml10
-rw-r--r--app/src/main/res/layout-v14/download_authentication_activity.xml10
-rw-r--r--app/src/main/res/layout/addfeed.xml53
-rw-r--r--app/src/main/res/layout/authentication_dialog.xml10
-rw-r--r--app/src/main/res/layout/cover_fragment.xml4
-rw-r--r--app/src/main/res/layout/download_authentication_activity.xml10
-rw-r--r--app/src/main/res/layout/episodes_apply_action_fragment.xml120
-rw-r--r--app/src/main/res/layout/feedinfo.xml57
-rw-r--r--app/src/main/res/layout/feeditem_fragment_header.xml3
-rw-r--r--app/src/main/res/layout/fragment_itunes_search.xml2
-rw-r--r--app/src/main/res/layout/gpodnet_podcast_list.xml2
-rw-r--r--app/src/main/res/layout/gpodnetauth_credentials.xml10
-rw-r--r--app/src/main/res/layout/new_episodes_listitem.xml42
-rw-r--r--app/src/main/res/layout/queue_fragment.xml30
-rw-r--r--app/src/main/res/layout/queue_listitem.xml37
-rw-r--r--app/src/main/res/menu/allepisodes_context.xml27
-rw-r--r--app/src/main/res/menu/episodes_apply_action_options.xml55
-rw-r--r--app/src/main/res/menu/feedinfo.xml6
-rw-r--r--app/src/main/res/menu/feeditem_options.xml29
-rw-r--r--app/src/main/res/menu/feeditemlist_context.xml28
-rw-r--r--app/src/main/res/menu/feedlist.xml34
-rw-r--r--app/src/main/res/menu/mediaplayer.xml35
-rw-r--r--app/src/main/res/menu/nav_feed_context.xml19
-rw-r--r--app/src/main/res/menu/queue_context.xml29
-rw-r--r--app/src/main/res/xml/preferences.xml55
-rw-r--r--app/src/main/templates/about.html10
-rw-r--r--core/build.gradle3
-rw-r--r--core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java27
-rw-r--r--core/src/main/AndroidManifest.xml3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/ApplicationCallbacks.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/StorageCallbacks.java27
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/DownloadObserver.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/ImageResource.java (renamed from core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoImageResource.java)2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java527
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java26
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java24
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java36
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java25
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java55
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java48
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java36
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/QueueEvent.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java33
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideSettings.java11
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java141
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java267
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java19
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java193
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java665
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/AlarmUpdateReceiver.java19
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/FeedMediaSizeService.java76
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java76
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java81
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java26
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APSPCleanupAlgorithm.java139
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APSPDownloadAlgorithm.java72
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java154
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java82
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java132
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java24
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java355
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Converter.java21
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java40
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java31
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java49
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java49
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java2
-rw-r--r--core/src/main/res/values-az/strings.xml4
-rw-r--r--core/src/main/res/values-ca/strings.xml9
-rw-r--r--core/src/main/res/values-cs-rCZ/strings.xml88
-rw-r--r--core/src/main/res/values-da/strings.xml9
-rw-r--r--core/src/main/res/values-de/strings.xml84
-rw-r--r--core/src/main/res/values-es-rES/strings.xml4
-rw-r--r--core/src/main/res/values-es/strings.xml84
-rw-r--r--core/src/main/res/values-fr/strings.xml103
-rw-r--r--core/src/main/res/values-hi-rIN/strings.xml4
-rw-r--r--core/src/main/res/values-it-rIT/strings.xml55
-rw-r--r--core/src/main/res/values-iw-rIL/strings.xml15
-rw-r--r--core/src/main/res/values-ja/strings.xml81
-rw-r--r--core/src/main/res/values-ko/strings.xml81
-rw-r--r--core/src/main/res/values-nl/strings.xml4
-rw-r--r--core/src/main/res/values-no/strings.xml31
-rw-r--r--core/src/main/res/values-pl-rPL/strings.xml9
-rw-r--r--core/src/main/res/values-pt-rBR/strings.xml4
-rw-r--r--core/src/main/res/values-pt/strings.xml106
-rw-r--r--core/src/main/res/values-ro-rRO/strings.xml4
-rw-r--r--core/src/main/res/values-ru/strings.xml31
-rw-r--r--core/src/main/res/values-sv-rSE/strings.xml190
-rw-r--r--core/src/main/res/values-tr/strings.xml55
-rw-r--r--core/src/main/res/values-uk-rUA/strings.xml87
-rw-r--r--core/src/main/res/values-zh-rCN/strings.xml7
-rw-r--r--core/src/main/res/values/arrays.xml77
-rw-r--r--core/src/main/res/values/colors.xml1
-rw-r--r--core/src/main/res/values/strings.xml202
-rw-r--r--core/src/main/res/values/styles.xml4
174 files changed, 5360 insertions, 3089 deletions
diff --git a/.tx/config b/.tx/config
index b0bb46170..c0d81ea94 100644
--- a/.tx/config
+++ b/.tx/config
@@ -18,6 +18,7 @@ trans.it_IT = core/src/main/res/values-it-rIT/strings.xml
trans.ja = core/src/main/res/values-ja/strings.xml
trans.ko = core/src/main/res/values-ko/strings.xml
trans.nl = core/src/main/res/values-nl/strings.xml
+#trans.no = core/src/main/res/values-no/strings.xml
trans.pl_PL = core/src/main/res/values-pl-rPL/strings.xml
trans.pt = core/src/main/res/values-pt/strings.xml
trans.pt_BR = core/src/main/res/values-pt-rBR/strings.xml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f00a7a7bf..bb3986671 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,17 @@
Change Log
==========
+Version 1.3
+-----------
+* Bulk actions on feed episodes (download, queue, delete)
+* Reduced space used by images
+* Automatic refresh at a certain time of day
+* Customizable indicators and sorting for feeds
+* Ability to share feeds
+* Improved auto download
+* Many fixes and usability improvements
+
Version 1.2
-------------------
+-----------
* Optionally disable swiping and dragging in the queue
* Resume playback after phone call
* Filter episodes in the Podcast feed
diff --git a/README.md b/README.md
index 9b6ba3000..daf218c67 100644
--- a/README.md
+++ b/README.md
@@ -10,8 +10,11 @@ You can use the [AntennaPod Google Group](https://groups.google.com/forum/#!foru
Bug reports and feature requests can be submitted [here](https://github.com/danieloeh/AntennaPod/issues/new) (please read the [instructions](https://github.com/danieloeh/AntennaPod/blob/master/CONTRIBUTING.md) on how to report a bug and how to submit a feature request first!).
+## Beta Testing
+You can participate in beta testing by joining the Google Plus [AntennaPod Beta](https://plus.google.com/communities/105484485757508984859) community and then going to [this url](https://play.google.com/apps/testing/de.danoeh.antennapod)
+
## Alpha Testing
-You can participate in alpha testing by joining the Google Plus [AntennaPod Alpha](https://plus.google.com/communities/117446128636140585144) community.
+You can participate in alpha testing by joining the Google Plus [AntennaPod Alpha](https://plus.google.com/communities/117446128636140585144) community and then going to [this url](https://play.google.com/apps/testing/de.danoeh.antennapod)
## Nightly Builds
@@ -31,13 +34,3 @@ If you want to translate AntennaPod into another language, you can visit the [Tr
Information on how to build AntennaPod can be found in the [Wiki](https://github.com/danieloeh/AntennaPod/wiki/Building-AntennaPod).
-## Donate
-
-[![Flattr Button](http://api.flattr.com/button/button-static-50x60.png "Flattr This!")](https://flattr.com/thing/745609/Antennapod "AntennaPod")
-
-![Donate Bitcoin](https://en.bitcoin.it/w/images/en/7/74/BC_Rnd_64px.png)
-Bitcoin address: `1DzvtuvdW8VhDsq9GUytMyALmsHeaHEKbg`
-
-[![Bountysource](https://www.bountysource.com/badge/tracker?tracker_id=370084)](https://www.bountysource.com/trackers/370084-antennapod?utm_source=370084&utm_medium=shield&utm_campaign=TRACKER_BADGE)
-
-[![Support via Gratipay](https://cdn.rawgit.com/gratipay/gratipay-badge/2.3.0/dist/gratipay.png)](https://gratipay.com/danieloeh/)
diff --git a/app/build.gradle b/app/build.gradle
index 0f6fe06aa..cd6fbd354 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -16,7 +16,7 @@ dependencies {
}
compile 'commons-io:commons-io:2.4'
compile 'org.jsoup:jsoup:1.7.3'
- compile 'com.squareup.picasso:picasso:2.5.2'
+ compile 'com.github.bumptech.glide:glide:3.6.1'
compile 'com.squareup.okhttp:okhttp:2.3.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.3.0'
compile 'com.squareup.okio:okio:1.2.0'
@@ -121,7 +121,7 @@ task filterAbout {
from 'src/main/templates/about.html'
into 'src/main/assets'
filter(ReplaceTokens, tokens: [versionname: android.defaultConfig.versionName,
- versioncode: android.defaultConfig.versionCode.toString()])
+ commit: "git rev-parse --short HEAD".execute().text])
}
}
diff --git a/app/proguard.cfg b/app/proguard.cfg
index 536f54eca..6020b5751 100644
--- a/app/proguard.cfg
+++ b/app/proguard.cfg
@@ -89,3 +89,10 @@
# android-iconify
-keep class com.joanzapata.** { *; }
+
+# Glide
+-keep public class * implements com.bumptech.glide.module.GlideModule
+-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
+ **[] $VALUES;
+ public *;
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
index 52067c971..13a0e3f78 100644
--- a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
@@ -2,15 +2,9 @@ package de.test.antennapod.handler;
import android.content.Context;
import android.test.InstrumentationTestCase;
-import de.danoeh.antennapod.core.feed.*;
-import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
-import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException;
-import de.test.antennapod.util.syndication.feedgenerator.AtomGenerator;
-import de.test.antennapod.util.syndication.feedgenerator.FeedGenerator;
-import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
+
import org.xml.sax.SAXException;
-import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -19,6 +13,19 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import javax.xml.parsers.ParserConfigurationException;
+
+import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedImage;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
+import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException;
+import de.test.antennapod.util.syndication.feedgenerator.AtomGenerator;
+import de.test.antennapod.util.syndication.feedgenerator.FeedGenerator;
+import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
+
/**
* Tests for FeedHandler
*/
@@ -162,10 +169,10 @@ public class FeedHandlerTest extends InstrumentationTestCase {
for (int i = 0; i < numItems; i++) {
FeedItem item = new FeedItem(0, "item-" + i, "http://example.com/item-" + i,
- "http://example.com/items/" + i, new Date(i*60000), false, feed);
+ "http://example.com/items/" + i, new Date(i*60000), FeedItem.UNPLAYED, feed);
feed.getItems().add(item);
if (withFeedMedia) {
- item.setMedia(new FeedMedia(0, item, 4711, 0, 100, "audio/mp3", null, "http://example.com/media-" + i,
+ item.setMedia(new FeedMedia(0, item, 4711, 0, 1024*1024, "audio/mp3", null, "http://example.com/media-" + i,
false, null, 0));
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
index 443fbed7e..4a5883c64 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
@@ -43,7 +43,7 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
- UserPreferences.createInstance(getInstrumentation().getTargetContext());
+ UserPreferences.init(getInstrumentation().getTargetContext());
destDir = getInstrumentation().getTargetContext().getExternalFilesDir(DOWNLOAD_DIR);
assertNotNull(destDir);
assertTrue(destDir.exists());
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
index aac4c245a..42fa84d20 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
@@ -3,15 +3,9 @@ package de.test.antennapod.service.playback;
import android.content.Context;
import android.media.RemoteControlClient;
import android.test.InstrumentationTestCase;
-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.service.playback.PlaybackServiceMediaPlayer;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import de.danoeh.antennapod.core.util.playback.Playable;
-import de.test.antennapod.util.service.download.HTTPBin;
+
import junit.framework.AssertionFailedError;
+
import org.apache.commons.io.IOUtils;
import java.io.File;
@@ -23,6 +17,16 @@ import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+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.feed.FeedPreferences;
+import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
+import de.danoeh.antennapod.core.service.playback.PlayerStatus;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import de.test.antennapod.util.service.download.HTTPBin;
+
/**
* Test class for PlaybackServiceMediaPlayer
*/
@@ -113,8 +117,10 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
private Playable writeTestPlayable(String downloadUrl, String fileUrl) {
final Context c = getInstrumentation().getTargetContext();
Feed f = new Feed(0, new Date(), "f", "l", "d", null, null, null, null, "i", null, null, "l", false);
+ FeedPreferences prefs = new FeedPreferences(f.getId(), false, FeedPreferences.AutoDeleteAction.NO, null, null);
+ f.setPreferences(prefs);
f.setItems(new ArrayList<FeedItem>());
- FeedItem i = new FeedItem(0, "t", "i", "l", new Date(), false, f);
+ FeedItem i = new FeedItem(0, "t", "i", "l", new Date(), FeedItem.UNPLAYED, f);
f.getItems().add(i);
FeedMedia media = new FeedMedia(0, i, 0, 0, 0, "audio/wav", fileUrl, downloadUrl, fileUrl != null, null, 0);
i.setMedia(media);
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
index d74128c2f..edb576249 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
@@ -51,7 +51,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
Feed f = new Feed(0, new Date(), "title", "link", "d", null, null, null, null, "id", null, "null", "url", false);
f.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS; i++) {
- f.getItems().add(new FeedItem(0, pref + i, pref + i, "link", new Date(), true, f));
+ f.getItems().add(new FeedItem(0, pref + i, pref + i, "link", new Date(), FeedItem.PLAYED, f));
}
PodDBAdapter adapter = new PodDBAdapter(c);
adapter.open();
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
index 95d2ce58a..11a34813a 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
@@ -42,38 +42,6 @@ public class DBReaderTest extends InstrumentationTestCase {
adapter.close();
}
- private void expiredFeedListTestHelper(long lastUpdate, long expirationTime, boolean shouldReturn) {
- final Context context = getInstrumentation().getTargetContext();
- Feed feed = new Feed(0, new Date(lastUpdate), "feed", "link", "descr", null,
- null, null, null, "feed", null, null, "url", false, new FlattrStatus(), false, null, null, false);
- feed.setItems(new ArrayList<FeedItem>());
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- adapter.setCompleteFeed(feed);
- adapter.close();
-
- assertTrue(feed.getId() != 0);
- List<Feed> expiredFeeds = DBReader.getExpiredFeedsList(context, expirationTime);
- assertNotNull(expiredFeeds);
- if (shouldReturn) {
- assertTrue(expiredFeeds.size() == 1);
- assertTrue(expiredFeeds.get(0).getId() == feed.getId());
- } else {
- assertTrue(expiredFeeds.isEmpty());
- }
- }
-
- public void testGetExpiredFeedsListShouldReturnFeed() {
- final long expirationTime = 1000 * 60 * 60; // 1 hour
- expiredFeedListTestHelper(System.currentTimeMillis() - expirationTime - 1, expirationTime, true);
- }
-
- public void testGetExpiredFeedsListShouldNotReturnFeed() {
- final long expirationTime = 1000 * 60 * 60; // 1 hour
- expiredFeedListTestHelper(System.currentTimeMillis() - expirationTime / 2, expirationTime, false);
- }
-
-
public void testGetFeedList() {
final Context context = getInstrumentation().getTargetContext();
List<Feed> feeds = saveFeedlist(context, 10, 0, false);
@@ -277,7 +245,7 @@ public class DBReaderTest extends InstrumentationTestCase {
int i = random.nextInt(numItems);
if (!unread.contains(items.get(i))) {
FeedItem item = items.get(i);
- item.setRead(false);
+ item.setPlayed(false);
unread.add(item);
}
}
@@ -297,7 +265,7 @@ public class DBReaderTest extends InstrumentationTestCase {
assertNotNull(unreadSaved);
assertTrue(unread.size() == unreadSaved.size());
for (FeedItem item : unreadSaved) {
- assertFalse(item.isRead());
+ assertFalse(item.isPlayed());
}
}
@@ -310,11 +278,11 @@ public class DBReaderTest extends InstrumentationTestCase {
for (int i = 0; i < unread.size(); i++) {
unreadIds[i] = unread.get(i).getId();
}
- LongList unreadSaved = DBReader.getNewItemIds(context);
+ List<FeedItem> unreadSaved = DBReader.getUnreadItemsList(context);
assertNotNull(unreadSaved);
assertTrue(unread.size() == unreadSaved.size());
for(int i=0; i < unreadSaved.size(); i++) {
- long savedId = unreadSaved.get(i);
+ long savedId = unreadSaved.get(i).getId();
boolean found = false;
for (long id : unreadIds) {
if (id == savedId) {
@@ -385,13 +353,13 @@ public class DBReaderTest extends InstrumentationTestCase {
final int NUM_FEEDS = 10;
final int NUM_ITEMS = 10;
final int NUM_QUEUE = 1;
- final int NUM_UNREAD = 2;
+ final int NUM_NEW = 2;
List<Feed> feeds = DBTestUtils.saveFeedlist(context, NUM_FEEDS, NUM_ITEMS, true);
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
- for (int i = 0; i < NUM_UNREAD; i++) {
+ for (int i = 0; i < NUM_NEW; i++) {
FeedItem item = feeds.get(0).getItems().get(i);
- item.setRead(false);
+ item.setNew();
adapter.setSingleFeedItem(item);
}
List<FeedItem> queue = new ArrayList<FeedItem>();
@@ -405,7 +373,7 @@ public class DBReaderTest extends InstrumentationTestCase {
DBReader.NavDrawerData navDrawerData = DBReader.getNavDrawerData(context);
assertEquals(NUM_FEEDS, navDrawerData.feeds.size());
- assertEquals(NUM_UNREAD, navDrawerData.numNewItems);
+ assertEquals(NUM_NEW, navDrawerData.numNewItems);
assertEquals(NUM_QUEUE, navDrawerData.queueSize);
}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
index 16f50cf28..944fc7792 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
@@ -12,6 +12,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -66,6 +67,8 @@ public class DBTasksTest extends InstrumentationTestCase {
SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()).edit();
prefEdit.putString(UserPreferences.PREF_EPISODE_CACHE_SIZE, Integer.toString(EPISODE_CACHE_SIZE));
prefEdit.commit();
+
+ UserPreferences.init(context);
}
@FlakyTest(tolerance = 3)
@@ -77,7 +80,7 @@ public class DBTasksTest extends InstrumentationTestCase {
feed.setItems(items);
List<File> files = new ArrayList<File>();
for (int i = 0; i < NUM_ITEMS; i++) {
- FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
File f = new File(destFolder, "file " + i);
assertTrue(f.createNewFile());
@@ -115,7 +118,7 @@ public class DBTasksTest extends InstrumentationTestCase {
feed.setItems(items);
List<File> files = new ArrayList<File>();
for (int i = 0; i < NUM_ITEMS; i++) {
- FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), false, feed);
+ FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.UNPLAYED, feed);
File f = new File(destFolder, "file " + i);
assertTrue(f.createNewFile());
@@ -150,7 +153,7 @@ public class DBTasksTest extends InstrumentationTestCase {
feed.setItems(items);
List<File> files = new ArrayList<File>();
for (int i = 0; i < NUM_ITEMS; i++) {
- FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
File f = new File(destFolder, "file " + i);
assertTrue(f.createNewFile());
@@ -207,14 +210,14 @@ public class DBTasksTest extends InstrumentationTestCase {
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS; i++) {
- feed.getItems().add(new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(), false, feed));
+ feed.getItems().add(new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(), FeedItem.UNPLAYED, feed));
}
Feed newFeed = DBTasks.updateFeed(context, feed)[0];
assertTrue(newFeed == feed);
assertTrue(feed.getId() != 0);
for (FeedItem item : feed.getItems()) {
- assertFalse(item.isRead());
+ assertFalse(item.isPlayed());
assertTrue(item.getId() != 0);
}
}
@@ -241,7 +244,7 @@ public class DBTasksTest extends InstrumentationTestCase {
final Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS_OLD; i++) {
- feed.getItems().add(new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(i), true, feed));
+ feed.getItems().add(new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(i), FeedItem.PLAYED, feed));
}
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
@@ -260,7 +263,7 @@ public class DBTasksTest extends InstrumentationTestCase {
}
for (int i = NUM_ITEMS_OLD; i < NUM_ITEMS_NEW + NUM_ITEMS_OLD; i++) {
- feed.getItems().add(0, new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(i), true, feed));
+ feed.getItems().add(0, new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(i), FeedItem.UNPLAYED, feed));
}
final Feed newFeed = DBTasks.updateFeed(context, feed)[0];
@@ -274,7 +277,6 @@ public class DBTasksTest extends InstrumentationTestCase {
updatedFeedTest(feedFromDB, feedID, itemIDs, NUM_ITEMS_OLD, NUM_ITEMS_NEW);
}
- @FlakyTest(tolerance = 3)
private void updatedFeedTest(final Feed newFeed, long feedID, List<Long> itemIDs, final int NUM_ITEMS_OLD, final int NUM_ITEMS_NEW) {
assertTrue(newFeed.getId() == feedID);
assertTrue(newFeed.getItems().size() == NUM_ITEMS_NEW + NUM_ITEMS_OLD);
@@ -284,7 +286,7 @@ public class DBTasksTest extends InstrumentationTestCase {
FeedItem item = newFeed.getItems().get(i);
assertTrue(item.getFeed() == newFeed);
assertTrue(item.getId() == itemIDs.get(i));
- assertTrue(item.isRead());
+ assertTrue(item.isPlayed());
assertTrue(item.getPubDate().getTime() >= lastDate.getTime());
lastDate = item.getPubDate();
}
@@ -292,43 +294,9 @@ public class DBTasksTest extends InstrumentationTestCase {
FeedItem item = newFeed.getItems().get(i);
assertTrue(item.getFeed() == newFeed);
assertTrue(item.getId() != 0);
- assertFalse(item.isRead());
+ assertFalse(item.isPlayed());
assertTrue(item.getPubDate().getTime() >= lastDate.getTime());
lastDate = item.getPubDate();
}
}
-
- @FlakyTest(tolerance = 3)
- private void expiredFeedListTestHelper(long lastUpdate, long expirationTime, boolean shouldReturn) {
- UserPreferences.setUpdateInterval(context, expirationTime);
- Feed feed = new Feed(0, new Date(lastUpdate), "feed", "link", "descr", null,
- null, null, null, "feed", null, null, "url", false, new FlattrStatus(), false, null, null, false);
- feed.setItems(new ArrayList<FeedItem>());
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- adapter.setCompleteFeed(feed);
- adapter.close();
-
- assertTrue(feed.getId() != 0);
- List<Feed> expiredFeeds = DBTasks.getExpiredFeeds(context);
- assertNotNull(expiredFeeds);
- if (shouldReturn) {
- assertTrue(expiredFeeds.size() == 1);
- assertTrue(expiredFeeds.get(0).getId() == feed.getId());
- } else {
- assertTrue(expiredFeeds.isEmpty());
- }
- }
-
- @FlakyTest(tolerance = 3)
- public void testGetExpiredFeedsTestShouldReturn() {
- final long expirationTime = 1000 * 60 * 60;
- expiredFeedListTestHelper(System.currentTimeMillis() - expirationTime - 1, expirationTime, true);
- }
-
- @FlakyTest(tolerance = 3)
- public void testGetExpiredFeedsTestShouldNotReturn() {
- final long expirationTime = 1000 * 60 * 60;
- expiredFeedListTestHelper(System.currentTimeMillis() - expirationTime / 2, expirationTime, false);
- }
}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
index 4f33cd798..c6f702f3d 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
@@ -51,7 +51,7 @@ public class DBTestUtils {
f.setItems(new ArrayList<FeedItem>());
for (int j = 0; j < numItems; j++) {
FeedItem item = new FeedItem(0, "item " + j, "id" + j, "link" + j, new Date(),
- true, f, withChapters);
+ FeedItem.PLAYED, f, withChapters);
if (withMedia) {
FeedMedia media = new FeedMedia(item, "url" + j, 1, "audio/mp3");
item.setMedia(media);
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
index f3b0c9165..c5fa2f131 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
@@ -65,7 +65,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", new Date(), "title");
List<FeedItem> items = new ArrayList<FeedItem>();
feed.setItems(items);
- FeedItem item = new FeedItem(0, "Item", "Item", "url", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "Item", "Item", "url", new Date(), FeedItem.PLAYED, feed);
FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", dest.getAbsolutePath(), "download_url", true, null, 0);
item.setMedia(media);
@@ -104,7 +104,7 @@ public class DBWriterTest extends InstrumentationTestCase {
List<File> itemFiles = new ArrayList<File>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
- FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed, true);
+ FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed, true);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
@@ -171,7 +171,7 @@ public class DBWriterTest extends InstrumentationTestCase {
List<File> itemFiles = new ArrayList<File>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
- FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
@@ -268,7 +268,7 @@ public class DBWriterTest extends InstrumentationTestCase {
// create items
for (int i = 0; i < 10; i++) {
- FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
}
@@ -320,7 +320,7 @@ public class DBWriterTest extends InstrumentationTestCase {
// create items with images
for (int i = 0; i < 10; i++) {
- FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
File itemImageFile = new File(destFolder, "item-image-" + i);
FeedImage itemImage = new FeedImage(0, "item-image" + i, itemImageFile.getAbsolutePath(), "url", true);
@@ -378,7 +378,7 @@ public class DBWriterTest extends InstrumentationTestCase {
List<File> itemFiles = new ArrayList<File>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
- FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
@@ -450,7 +450,7 @@ public class DBWriterTest extends InstrumentationTestCase {
List<File> itemFiles = new ArrayList<File>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
- FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
@@ -496,7 +496,7 @@ public class DBWriterTest extends InstrumentationTestCase {
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
- FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
FeedMedia media = new FeedMedia(0, item, 10, 0, 1, "mime", null, "url", false, playbackCompletionDate, 0);
feed.getItems().add(item);
item.setMedia(media);
@@ -543,7 +543,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS; i++) {
- FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
}
@@ -569,7 +569,7 @@ public class DBWriterTest extends InstrumentationTestCase {
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
- FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
PodDBAdapter adapter = new PodDBAdapter(context);
@@ -593,7 +593,7 @@ public class DBWriterTest extends InstrumentationTestCase {
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
- FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
PodDBAdapter adapter = new PodDBAdapter(context);
@@ -661,7 +661,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS; i++) {
- FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
}
@@ -707,7 +707,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS; i++) {
- FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
}
@@ -754,7 +754,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS; i++) {
- FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), false, feed);
+ FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.UNPLAYED, feed);
feed.getItems().add(item);
}
@@ -771,7 +771,7 @@ public class DBWriterTest extends InstrumentationTestCase {
DBWriter.markFeedRead(context, feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
List<FeedItem> loadedItems = DBReader.getFeedItemList(context, feed);
for (FeedItem item : loadedItems) {
- assertTrue(item.isRead());
+ assertTrue(item.isPlayed());
}
}
@@ -781,7 +781,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS; i++) {
- FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), false, feed);
+ FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.UNPLAYED, feed);
feed.getItems().add(item);
}
@@ -798,7 +798,7 @@ public class DBWriterTest extends InstrumentationTestCase {
DBWriter.markAllItemsRead(context).get(TIMEOUT, TimeUnit.SECONDS);
List<FeedItem> loadedItems = DBReader.getFeedItemList(context, feed);
for (FeedItem item : loadedItems) {
- assertTrue(item.isRead());
+ assertTrue(item.isPlayed());
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
index 0326174e3..65c962f01 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
@@ -78,7 +78,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
openNavDrawer();
solo.clickOnText(solo.getString(R.string.add_feed_label));
solo.enterText(0, feed.getDownload_url());
- solo.clickOnButton(0);
+ solo.clickOnButton(solo.getString(R.string.confirm_label));
solo.waitForActivity(DefaultOnlineFeedViewActivity.class);
solo.waitForView(R.id.butSubscribe);
assertEquals(solo.getString(R.string.subscribe_label), solo.getButton(0).getText().toString());
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
index eb1cb9c71..a6af6c544 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -39,7 +39,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
Timeout.setLargeTimeout(1000);
context = getInstrumentation().getTargetContext();
res = getActivity().getResources();
- UserPreferences.createInstance(context);
+ UserPreferences.init(context);
}
@Override
@@ -231,7 +231,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
return pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss();
}
}, Timeout.getLargeTimeout());
- solo.clickOnText(solo.getString(R.string.pref_auto_delete_title));
+ solo.clickOnText(solo.getString(R.string.pref_pausePlaybackForFocusLoss_title));
solo.waitForCondition(new Condition() {
@Override
public boolean isSatisfied() {
@@ -241,9 +241,9 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
}
public void testDisableUpdateInterval() {
- solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervall_title));
+ solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_sum));
solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.pref_update_interval_hours_manual));
+ solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_Disable));
solo.waitForCondition(new Condition() {
@Override
public boolean isSatisfied() {
@@ -253,7 +253,9 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
}
public void testSetUpdateInterval() {
- solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervall_title));
+ solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_title));
+ solo.waitForDialogToOpen();
+ solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
solo.waitForDialogToOpen();
String search = "12 " + solo.getString(R.string.pref_update_interval_hours_plural);
solo.clickOnText(search);
@@ -424,30 +426,4 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
}
}, Timeout.getLargeTimeout());
}
-
- @FlakyTest(tolerance = 3)
- public void testAbout() throws IOException {
- int numViews = 0, numLinks = 0;
- InputStream input = getActivity().getResources().getAssets().open("about.html");
- List<String> lines = IOUtils.readLines(input);
- input.close();
- for(String line : lines) {
- if(line.contains("(View)")) {
- numViews++;
- } else if(line.contains("(Link)")) {
- numLinks++;
- }
- }
- for(int i=0; i < numViews; i++) {
- solo.clickOnText(solo.getString(R.string.about_pref));
- solo.clickOnText("(View)", i);
- solo.goBack();
- }
- for(int i=0; i < numLinks; i++) {
- solo.clickOnText(solo.getString(R.string.about_pref));
- solo.clickOnText("(Link)", i);
- solo.goBack();
- }
- }
-
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
index 613826932..7bbc6e462 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
@@ -111,6 +111,9 @@ public class UITestUtils {
private File newMediaFile(String name) throws IOException {
File mediaFile = new File(hostedMediaDir, name);
+ if(mediaFile.exists()) {
+ mediaFile.delete();
+ }
Assert.assertFalse(mediaFile.exists());
InputStream in = context.getAssets().open("testfile.mp3");
@@ -142,7 +145,7 @@ public class UITestUtils {
List<FeedItem> items = new ArrayList<FeedItem>();
for (int j = 0; j < NUM_ITEMS_PER_FEED; j++) {
FeedItem item = new FeedItem(j, "Feed " + (i+1) + ": Item " + (j+1), "item" + j,
- "http://example.com/feed" + i + "/item/" + j, new Date(), false, feed);
+ "http://example.com/feed" + i + "/item/" + j, new Date(), FeedItem.UNPLAYED, feed);
items.add(item);
File mediaFile = newMediaFile("feed-" + i + "-episode-" + j + ".mp3");
diff --git a/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java b/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
index 2c56b71cc..7e535e12c 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
@@ -31,7 +31,7 @@ public class TimelineTest extends InstrumentationTestCase {
}
private Playable newTestPlayable(List<Chapter> chapters, String shownotes) {
- FeedItem item = new FeedItem(0, "Item", "item-id", "http://example.com/item", new Date(), true, null);
+ FeedItem item = new FeedItem(0, "Item", "item-id", "http://example.com/item", new Date(), FeedItem.PLAYED, null);
item.setChapters(chapters);
item.setContentEncoded(shownotes);
FeedMedia media = new FeedMedia(item, "http://example.com/episode", 100, "audio/mp3");
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 25b350aba..5d630ca06 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,9 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.danoeh.antennapod"
- android:versionCode="62"
- android:versionName="1.2.2">
-
+ android:versionCode="1030006"
+ android:versionName="1.3">
+ <!--
+ Version code schema:
+ "1.2.3-SNAPSHOT" -> 1020300
+ "1.2.3-RC4" -> 1020304
+ -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
@@ -322,6 +326,10 @@
android:scheme="package"/>
</intent-filter>
</receiver>
+
+ <meta-data
+ android:name="de.danoeh.antennapod.core.glide.ApGlideModule"
+ android:value="GlideModule" />
</application>
</manifest>
diff --git a/app/src/main/assets/LICENSE_GLIDE.txt b/app/src/main/assets/LICENSE_GLIDE.txt
new file mode 100644
index 000000000..f5111eeab
--- /dev/null
+++ b/app/src/main/assets/LICENSE_GLIDE.txt
@@ -0,0 +1,94 @@
+License for everything not in third_party and not otherwise marked:
+
+Copyright 2014 Google, Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY GOOGLE, INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Google, Inc.
+---------------------------------------------------------------------------------------------
+License for third_party/disklrucache:
+
+Copyright 2012 Jake Wharton
+Copyright 2011 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+---------------------------------------------------------------------------------------------
+License for third_party/gif_decoder:
+
+Copyright (c) 2013 Xcellent Creations, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+---------------------------------------------------------------------------------------------
+License for third_party/gif_encoder/AnimatedGifEncoder.java and
+third_party/gif_encoder/LZWEncoder.java:
+
+No copyright asserted on the source code of this class. May be used for any
+purpose, however, refer to the Unisys LZW patent for restrictions on use of
+the associated LZWEncoder class. Please forward any corrections to
+kweiner@fmsware.com.
+
+-----------------------------------------------------------------------------
+License for third_party/gif_encoder/NeuQuant.java
+
+Copyright (c) 1994 Anthony Dekker
+
+NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
+"Kohonen neural networks for optimal colour quantization" in "Network:
+Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
+the algorithm.
+
+Any party obtaining a copy of these files from the author, directly or
+indirectly, is granted, free of charge, a full and unrestricted irrevocable,
+world-wide, paid up, royalty-free, nonexclusive right and license to deal in
+this software and documentation files (the "Software"), including without
+limitation the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Software, and to permit persons who
+receive copies from any such party to do so, with the only requirement being
+that this copyright notice remain intact.
diff --git a/app/src/main/assets/LICENSE_PICASSO.txt b/app/src/main/assets/LICENSE_PICASSO.txt
deleted file mode 100644
index 0bf6b9f8e..000000000
--- a/app/src/main/assets/LICENSE_PICASSO.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright 2013 Square, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/app/src/main/assets/LICENSE_STACKBLUR.txt b/app/src/main/assets/LICENSE_STACKBLUR.txt
new file mode 100644
index 000000000..b66736ccb
--- /dev/null
+++ b/app/src/main/assets/LICENSE_STACKBLUR.txt
@@ -0,0 +1,71 @@
+Apache License, Version 2.0
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
+
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of this License; and
+You must cause any modified files to carry prominent notices stating that You changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work
+To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
+
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/app/src/main/java/de/danoeh/antennapod/AppConfig.java b/app/src/main/java/de/danoeh/antennapod/AppConfig.java
deleted file mode 100644
index 24f13d4a3..000000000
--- a/app/src/main/java/de/danoeh/antennapod/AppConfig.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package de.danoeh.antennapod;
-
-public final class AppConfig {
- /** Should be used when setting User-Agent header for HTTP-requests. */
- public final static String USER_AGENT = "AntennaPod/0.9.9.4";
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index 451094909..f47fe3944 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -1,12 +1,14 @@
package de.danoeh.antennapod;
import android.app.Application;
+import android.content.Intent;
import android.content.res.Configuration;
-import de.danoeh.antennapod.core.asynctask.PicassoProvider;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.FeedMediaSizeService;
+import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.spa.SPAUtil;
/** Main application class. */
@@ -37,12 +39,15 @@ public class PodcastApp extends Application {
singleton = this;
LOGICAL_DENSITY = getResources().getDisplayMetrics().density;
- PicassoProvider.setupPicassoInstance(this);
- UserPreferences.createInstance(this);
- PlaybackPreferences.createInstance(this);
+ UpdateManager.init(this);
+ UserPreferences.init(this);
+ PlaybackPreferences.init(this);
+ NetworkUtils.init(this);
EventDistributor.getInstance();
SPAUtil.sendSPAppsQueryFeedsIntent(this);
+
+ startService(new Intent(this, FeedMediaSizeService.class));
}
public static float getLogicalDensity() {
diff --git a/app/src/main/java/de/danoeh/antennapod/UpdateManager.java b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java
new file mode 100644
index 000000000..2f6bb1b50
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java
@@ -0,0 +1,88 @@
+package de.danoeh.antennapod;
+
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import java.io.File;
+import java.util.List;
+
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedImage;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+
+/*
+ * This class's job is do perform maintenance tasks whenever the app has been updated
+ */
+public class UpdateManager {
+
+ public static final String TAG = UpdateManager.class.getSimpleName();
+
+ private static final String PREF_NAME = "app_version";
+ private static final String KEY_VERSION_CODE = "version_code";
+
+ private static int currentVersionCode;
+
+ private static Context context;
+ private static SharedPreferences prefs;
+
+ public static void init(Context context) {
+ UpdateManager.context = context;
+ prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+ PackageManager pm = context.getPackageManager();
+ try {
+ PackageInfo info = pm.getPackageInfo(context.getPackageName(), 0);
+ currentVersionCode = info.versionCode;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Failed to obtain package info for package name: " + context.getPackageName(), e);
+ currentVersionCode = 0;
+ return;
+ }
+ final int oldVersionCode = getStoredVersionCode();
+ Log.d(TAG, "old: " + oldVersionCode + ", current: " + currentVersionCode);
+ if(oldVersionCode < currentVersionCode) {
+ onUpgrade(oldVersionCode, currentVersionCode);
+ setCurrentVersionCode();
+ }
+ }
+
+ public static int getStoredVersionCode() {
+ return prefs.getInt(KEY_VERSION_CODE, -1);
+ }
+
+ public static void setCurrentVersionCode() {
+ prefs.edit().putInt(KEY_VERSION_CODE, currentVersionCode).apply();
+ }
+
+ private static void onUpgrade(final int oldVersionCode, final int newVersionCode) {
+ if(oldVersionCode < 1030099) {
+ // delete the now obsolete image cache
+ // from now on, Glide will handle caching images
+ new Thread() {
+ public void run() {
+ List<Feed> feeds = DBReader.getFeedList(context);
+ for (Feed podcast : feeds) {
+ List<FeedItem> episodes = DBReader.getFeedItemList(context, podcast);
+ for (FeedItem episode : episodes) {
+ FeedImage image = episode.getImage();
+ if (image != null && image.isDownloaded() && image.getFile_url() != null) {
+ File imageFile = new File(image.getFile_url());
+ if (imageFile.exists()) {
+ imageFile.delete();
+ }
+ image.setFile_url(null); // calls setDownloaded(false)
+ DBWriter.setFeedImage(context, image);
+ }
+ }
+ }
+ }
+ }.start();
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
index b59f6ba35..294acd427 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.activity;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
@@ -13,6 +14,8 @@ import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.util.Log;
+import android.view.ContextMenu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
@@ -24,21 +27,26 @@ import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.apache.commons.lang3.StringUtils;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.ChapterListAdapter;
import de.danoeh.antennapod.adapter.NavListAdapter;
+import de.danoeh.antennapod.core.asynctask.FeedRemover;
+import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.feed.SimpleChapter;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
@@ -430,6 +438,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
drawerLayout.closeDrawer(navDrawer);
}
});
+ registerForContextMenu(navList);
drawerToggle.syncState();
findViewById(R.id.nav_settings).setOnClickListener(new View.OnClickListener() {
@@ -549,9 +558,13 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
}
txtvTitle.setText(media.getEpisodeTitle());
getSupportActionBar().setTitle("");
- Picasso.with(this)
+ Glide.with(this)
.load(media.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(butShowCover);
setNavButtonVisibility();
@@ -634,6 +647,65 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
}
}
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ if(v.getId() != R.id.nav_list) {
+ return;
+ }
+ AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ int position = adapterInfo.position;
+ if(position < navAdapter.getSubscriptionOffset()) {
+ return;
+ }
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.nav_feed_context, menu);
+ Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
+ menu.setHeaderTitle(feed.getTitle());
+ // episodes are not loaded, so we cannot check if the podcast has new or unplayed ones!
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+ if(menuInfo.targetView.getParent() instanceof ListView == false
+ || ((ListView)menuInfo.targetView.getParent()).getId() != R.id.nav_list) {
+ return false;
+ }
+ int position = menuInfo.position;
+ Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
+ switch(item.getItemId()) {
+ case R.id.mark_all_seen_item:
+ DBWriter.markFeedSeen(this, feed.getId());
+ return true;
+ case R.id.mark_all_read_item:
+ DBWriter.markFeedRead(this, feed.getId());
+ return true;
+ case R.id.remove_item:
+ final FeedRemover remover = new FeedRemover(this, feed) {
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ }
+ };
+ ConfirmationDialog conDialog = new ConfirmationDialog(this,
+ R.string.remove_feed_label,
+ R.string.feed_delete_confirmation_msg) {
+ @Override
+ public void onConfirmButtonPressed(
+ DialogInterface dialog) {
+ dialog.dismiss();
+ remover.executeAsync();
+ }
+ };
+ conDialog.createNewDialog().show();
+ return true;
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+
private DBReader.NavDrawerData navDrawerData;
private AsyncTask<Void, Void, DBReader.NavDrawerData> loadTask;
@@ -708,8 +780,8 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
}
@Override
- public int getNumberOfUnreadFeedItems(long feedId) {
- return (navDrawerData != null) ? navDrawerData.numUnreadFeedItems.get(feedId) : 0;
+ public int getFeedCounter(long feedId) {
+ return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0;
}
};
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
index 287ae3568..0993ed6d6 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
@@ -17,7 +17,8 @@ import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
@@ -36,6 +37,7 @@ import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
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.glide.ApGlideSettings;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -124,9 +126,13 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
subscribeButton = (Button) header.findViewById(R.id.butSubscribe);
if (feed.getImage() != null && StringUtils.isNotBlank(feed.getImage().getDownload_url())) {
- Picasso.with(this)
+ Glide.with(this)
.load(feed.getImage().getDownload_url())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(cover);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
index 5f76a20a8..9cdaf2fc6 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
@@ -2,6 +2,9 @@ package de.danoeh.antennapod.activity;
import android.content.ClipData;
import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
@@ -12,24 +15,31 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageView;
+import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.joanzapata.android.iconify.Iconify;
-import com.squareup.picasso.Picasso;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LangUtils;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
@@ -38,6 +48,7 @@ import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
*/
public class FeedInfoActivity extends ActionBarActivity {
private static final String TAG = "FeedInfoActivity";
+ private boolean autoDeleteChanged = false;
public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
@@ -52,6 +63,7 @@ public class FeedInfoActivity extends ActionBarActivity {
private EditText etxtUsername;
private EditText etxtPassword;
private CheckBox cbxAutoDownload;
+ private Spinner spnAutoDelete;
private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() {
@Override
@@ -89,6 +101,7 @@ public class FeedInfoActivity extends ActionBarActivity {
txtvAuthor = (TextView) findViewById(R.id.txtvAuthor);
txtvUrl = (TextView) findViewById(R.id.txtvUrl);
cbxAutoDownload = (CheckBox) findViewById(R.id.cbxAutoDownload);
+ spnAutoDelete = (Spinner) findViewById(R.id.spnAutoDelete);
etxtUsername = (EditText) findViewById(R.id.etxtUsername);
etxtPassword = (EditText) findViewById(R.id.etxtPassword);
@@ -108,13 +121,18 @@ public class FeedInfoActivity extends ActionBarActivity {
Log.d(TAG, "Language is " + feed.getLanguage());
Log.d(TAG, "Author is " + feed.getAuthor());
Log.d(TAG, "URL is " + feed.getDownload_url());
+ FeedPreferences prefs = feed.getPreferences();
imgvCover.post(new Runnable() {
@Override
public void run() {
- Picasso.with(FeedInfoActivity.this)
+ Glide.with(FeedInfoActivity.this)
.load(feed.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(imgvCover);
}
});
@@ -133,17 +151,49 @@ public class FeedInfoActivity extends ActionBarActivity {
Iconify.addIcons(txtvUrl);
cbxAutoDownload.setEnabled(UserPreferences.isEnableAutodownload());
- cbxAutoDownload.setChecked(feed.getPreferences().getAutoDownload());
+ cbxAutoDownload.setChecked(prefs.getAutoDownload());
cbxAutoDownload.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
feed.getPreferences().setAutoDownload(checked);
feed.savePreferences(FeedInfoActivity.this);
+ ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedInfoActivity.this,
+ feed, checked);
+ dialog.createNewDialog().show();
}
});
+ spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+ FeedPreferences.AutoDeleteAction auto_delete_action;
+ switch (parent.getSelectedItemPosition()) {
+ case 0:
+ auto_delete_action = FeedPreferences.AutoDeleteAction.GLOBAL;
+ break;
+
+ case 1:
+ auto_delete_action = FeedPreferences.AutoDeleteAction.YES;
+ break;
+
+ case 2:
+ auto_delete_action = FeedPreferences.AutoDeleteAction.NO;
+ break;
+
+ default: // TODO - add exceptions here
+ return;
+ }
+ feed.getPreferences().setAutoDeleteAction(auto_delete_action);// p
+ autoDeleteChanged = true;
+ }
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Another interface callback
+ }
+ });
+ spnAutoDelete.setSelection(prefs.getAutoDeleteAction().ordinal());
- etxtUsername.setText(feed.getPreferences().getUsername());
- etxtPassword.setText(feed.getPreferences().getPassword());
+ etxtUsername.setText(prefs.getUsername());
+ etxtPassword.setText(prefs.getPassword());
etxtUsername.addTextChangedListener(authTextWatcher);
etxtPassword.addTextChangedListener(authTextWatcher);
@@ -181,13 +231,18 @@ public class FeedInfoActivity extends ActionBarActivity {
@Override
protected void onPause() {
super.onPause();
- if (feed != null && authInfoChanged) {
- Log.d(TAG, "Auth info changed, saving credentials");
+ if (feed != null) {
FeedPreferences prefs = feed.getPreferences();
- prefs.setUsername(etxtUsername.getText().toString());
- prefs.setPassword(etxtPassword.getText().toString());
- DBWriter.setFeedPreferences(this, prefs);
+ if (authInfoChanged) {
+ Log.d(TAG, "Auth info changed, saving credentials");
+ prefs.setUsername(etxtUsername.getText().toString());
+ prefs.setPassword(etxtPassword.getText().toString());
+ }
+ if (authInfoChanged || autoDeleteChanged) {
+ DBWriter.setFeedPreferences(this, prefs);
+ }
authInfoChanged = false;
+ autoDeleteChanged = false;
}
}
@@ -205,7 +260,8 @@ public class FeedInfoActivity extends ActionBarActivity {
menu.findItem(R.id.support_item).setVisible(
feed != null && feed.getPaymentLink() != null);
menu.findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null);
- menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null);
+ menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null &&
+ IntentUtils.isCallable(this, new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink()))));
return true;
}
@@ -226,4 +282,25 @@ public class FeedInfoActivity extends ActionBarActivity {
return super.onOptionsItemSelected(item);
}
}
+
+ private class ApplyToEpisodesDialog extends ConfirmationDialog {
+
+ private final Feed feed;
+ private final boolean autoDownload;
+
+ public ApplyToEpisodesDialog(Context context, Feed feed, boolean autoDownload) {
+ super(context, R.string.auto_download_apply_to_items_title,
+ R.string.auto_download_apply_to_items_message);
+ this.feed = feed;
+ this.autoDownload = autoDownload;
+ setPositiveText(R.string.yes);
+ setNegativeText(R.string.no);
+ }
+
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ DBWriter.setFeedsItemsAutoDownload(context, feed, autoDownload);
+ }
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index 254749cd6..7eee1558b 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.activity;
import android.app.AlertDialog;
+import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -19,6 +20,8 @@ import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.util.Log;
+import android.view.ContextMenu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
@@ -31,11 +34,15 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.NavListAdapter;
+import de.danoeh.antennapod.core.asynctask.FeedRemover;
+import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.ProgressEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.QueueEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.fragment.AddFeedFragment;
import de.danoeh.antennapod.fragment.AllEpisodesFragment;
@@ -92,9 +99,10 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
private ActionBarDrawerToggle drawerToggle;
- private CharSequence drawerTitle;
private CharSequence currentTitle;
+ private ProgressDialog pd;
+
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getNoTitleTheme());
@@ -107,7 +115,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
setSupportActionBar(toolbar);
getSupportActionBar().setElevation(3.0f);
- drawerTitle = currentTitle = getTitle();
+ currentTitle = getTitle();
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
navList = (ListView) findViewById(R.id.nav_list);
@@ -136,6 +144,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
navList.setAdapter(navAdapter);
navList.setOnItemClickListener(navListClickListener);
navList.setOnItemLongClickListener(newListLongClickListener);
+ registerForContextMenu(navList);
navAdapter.registerDataSetObserver(new DataSetObserver() {
@Override
@@ -161,9 +170,9 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
String lastFragment = getLastNavFragment();
if(ArrayUtils.contains(NAV_DRAWER_TAGS, lastFragment)) {
loadFragment(lastFragment, null);
+ } else {
+ loadFeedFragmentById(Integer.valueOf(lastFragment), null);
}
- // else: lastFragment contains feed id - drawer data is not loaded yet,
- // so loading is postponed until then
}
externalPlayerFragment = new ExternalPlayerFragment();
transaction.replace(R.id.playerFragment, externalPlayerFragment);
@@ -173,6 +182,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
private void saveLastNavFragment(String tag) {
+ Log.d(TAG, "saveLastNavFragment(tag: " + tag +")");
SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
SharedPreferences.Editor edit = prefs.edit();
if(tag != null) {
@@ -185,7 +195,9 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
private String getLastNavFragment() {
SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
- return prefs.getString(PREF_LAST_FRAGMENT_TAG, QueueFragment.TAG);
+ String lastFragment = prefs.getString(PREF_LAST_FRAGMENT_TAG, QueueFragment.TAG);
+ Log.d(TAG, "getLastNavFragment() -> " + lastFragment);
+ return lastFragment;
}
private void checkFirstLaunch() {
@@ -251,6 +263,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
public void loadFragment(int index, Bundle args) {
+ Log.d(TAG, "loadFragment(index: " + index + ", args: " + args +")");
if (index < navAdapter.getSubscriptionOffset()) {
String tag = navAdapter.getTags().get(index);
loadFragment(tag, args);
@@ -261,7 +274,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
public void loadFragment(final String tag, Bundle args) {
- Log.d(TAG, "loadFragment(\"" + tag + "\", " + args + ")");
+ Log.d(TAG, "loadFragment(tag: " + tag + ", args: " + args + ")");
Fragment fragment = null;
switch (tag) {
case QueueFragment.TAG:
@@ -297,32 +310,20 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
return;
}
Feed feed = itemAccess.getItem(relPos);
- long feedId = feed.getId();
+ loadFeedFragmentById(feed.getId(), args);
+ }
+
+ public void loadFeedFragmentById(long feedId, Bundle args) {
Fragment fragment = ItemlistFragment.newInstance(feedId);
if(args != null) {
fragment.setArguments(args);
}
- saveLastNavFragment(String.valueOf(feed.getId()));
+ saveLastNavFragment(String.valueOf(feedId));
currentTitle = "";
getSupportActionBar().setTitle(currentTitle);
loadFragment(fragment);
}
- public void loadFeedFragmentById(long feedId) {
- if (navDrawerData != null) {
- int relPos = -1;
- List<Feed> feeds = navDrawerData.feeds;
- for (int i = 0; relPos < 0 && i < feeds.size(); i++) {
- if (feeds.get(i).getId() == feedId) {
- relPos = i;
- }
- }
- if(relPos >= 0) {
- loadFeedFragmentByPosition(relPos, null);
- }
- }
- }
-
private void loadFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
// clear back stack
@@ -362,15 +363,19 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
private int getSelectedNavListIndex() {
- String lastFragment = getLastNavFragment();
- int tagIndex = navAdapter.getTags().indexOf(lastFragment);
+ String currentFragment = getLastNavFragment();
+ if(currentFragment == null) {
+ // should not happen, but better safe than sorry
+ return -1;
+ }
+ int tagIndex = navAdapter.getTags().indexOf(currentFragment);
if(tagIndex >= 0) {
return tagIndex;
- } else if(ArrayUtils.contains(NAV_DRAWER_TAGS, lastFragment)) {
+ } else if(ArrayUtils.contains(NAV_DRAWER_TAGS, currentFragment)) {
// the fragment was just hidden
return -1;
} else { // last fragment was not a list, but a feed
- long feedId = Long.parseLong(lastFragment);
+ long feedId = Long.parseLong(currentFragment);
if (navDrawerData != null) {
List<Feed> feeds = navDrawerData.feeds;
for (int i = 0; i < feeds.size(); i++) {
@@ -465,6 +470,9 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
cancelLoadTask();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
+ if(pd != null) {
+ pd.dismiss();
+ }
}
@Override
@@ -481,6 +489,67 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
}
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ if(v.getId() != R.id.nav_list) {
+ return;
+ }
+ AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ int position = adapterInfo.position;
+ if(position < navAdapter.getSubscriptionOffset()) {
+ return;
+ }
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.nav_feed_context, menu);
+ Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
+ menu.setHeaderTitle(feed.getTitle());
+ // episodes are not loaded, so we cannot check if the podcast has new or unplayed ones!
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+ if(menuInfo.targetView.getParent() instanceof ListView == false
+ || ((ListView)menuInfo.targetView.getParent()).getId() != R.id.nav_list) {
+ return false;
+ }
+ final int position = menuInfo.position;
+ Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
+ switch(item.getItemId()) {
+ case R.id.mark_all_seen_item:
+ DBWriter.markFeedSeen(this, feed.getId());
+ return true;
+ case R.id.mark_all_read_item:
+ DBWriter.markFeedRead(this, feed.getId());
+ return true;
+ case R.id.remove_item:
+ final FeedRemover remover = new FeedRemover(this, feed) {
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ if(getSelectedNavListIndex() == position) {
+ loadFragment(NewEpisodesFragment.TAG, null);
+ }
+ }
+ };
+ ConfirmationDialog conDialog = new ConfirmationDialog(this,
+ R.string.remove_feed_label,
+ R.string.feed_delete_confirmation_msg) {
+ @Override
+ public void onConfirmButtonPressed(
+ DialogInterface dialog) {
+ dialog.dismiss();
+ remover.executeAsync();
+ }
+ };
+ conDialog.createNewDialog().show();
+ return true;
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
private DBReader.NavDrawerData navDrawerData;
private AsyncTask<Void, Void, DBReader.NavDrawerData> loadTask;
private int selectedNavListIndex = 0;
@@ -520,8 +589,8 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
@Override
- public int getNumberOfUnreadFeedItems(long feedId) {
- return (navDrawerData != null) ? navDrawerData.numUnreadFeedItems.get(feedId) : 0;
+ public int getFeedCounter(long feedId) {
+ return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0;
}
};
@@ -542,13 +611,6 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
navDrawerData = result;
navAdapter.notifyDataSetChanged();
- String lastFragment = getLastNavFragment();
- if(!ArrayUtils.contains(NAV_DRAWER_TAGS, lastFragment)) {
- long feedId = Long.valueOf(lastFragment);
- loadFeedFragmentById(feedId);
- saveLastNavFragment(null);
- }
-
if (handleIntent) {
handleNavIntent();
}
@@ -568,6 +630,24 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
loadData();
}
+ public void onEventMainThread(ProgressEvent event) {
+ Log.d(TAG, "onEvent(" + event + ")");
+ switch(event.action) {
+ case START:
+ pd = new ProgressDialog(this);
+ pd.setMessage(event.message);
+ pd.setIndeterminate(true);
+ pd.setCancelable(false);
+ pd.show();
+ break;
+ case END:
+ if(pd != null) {
+ pd.dismiss();
+ }
+ break;
+ }
+ }
+
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index 0cd388b9d..b83e9bc15 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -278,6 +278,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
startActivity(intent);
return true;
} else if (media != null) {
+ FeedItem feedItem = ((FeedMedia) media).getItem();
switch (item.getItemId()) {
case R.id.disable_sleeptimer_item:
if (controller.serviceAvailable()) {
@@ -333,12 +334,20 @@ public abstract class MediaplayerActivity extends ActionBarActivity
break;
case R.id.support_item:
if (media instanceof FeedMedia) {
- FeedItem feedItem = ((FeedMedia) media).getItem();
DBTasks.flattrItemIfLoggedIn(this, feedItem);
}
break;
case R.id.share_link_item:
- ShareUtils.shareLink(this, media.getWebsiteLink());
+ ShareUtils.shareFeedItemLink(this, feedItem);
+ break;
+ case R.id.share_download_url_item:
+ ShareUtils.shareFeedItemDownloadLink(this, feedItem);
+ break;
+ case R.id.share_link_with_position_item:
+ ShareUtils.shareFeedItemLink(this, feedItem, true);
+ break;
+ case R.id.share_download_url_with_position_item:
+ ShareUtils.shareFeedItemDownloadLink(this, feedItem, true);
break;
case R.id.skip_episode_item:
sendBroadcast(new Intent(
@@ -471,7 +480,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
checked = i;
}
choices[i] = String.valueOf(values[i]) + " "
- + getString(R.string.time_unit_seconds);
+ + getString(R.string.time_seconds);
}
choice = values[checked];
AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
@@ -519,7 +528,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
checked = i;
}
choices[i] = String.valueOf(values[i]) + " "
- + getString(R.string.time_unit_seconds);
+ + getString(R.string.time_seconds);
}
choice = values[checked];
AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
index 2b1b13ae6..3ab384012 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -180,7 +180,7 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
url = URLChecker.prepareURL(url);
feed = new Feed(url, new Date(0));
if (username != null && password != null) {
- feed.setPreferences(new FeedPreferences(0, false, username, password));
+ feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, username, password));
}
String fileUrl = new File(getExternalCacheDir(),
FileNameGenerator.generateFileName(feed.getDownload_url())).toString();
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
index feb3989bc..2ec987d1a 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
@@ -2,7 +2,6 @@ package de.danoeh.antennapod.activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
@@ -18,11 +17,10 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.Reader;
-import java.util.List;
-import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LangUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
@@ -72,38 +70,33 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
int nextOption = 1;
intentPickAction = new Intent(Intent.ACTION_PICK);
intentPickAction.setData(Uri.parse("file://"));
- List<ResolveInfo> intentActivities = getPackageManager()
- .queryIntentActivities(intentPickAction, CHOOSE_OPML_FILE);
- if(intentActivities.size() == 0) {
- intentPickAction.setData(null);
- intentActivities = getPackageManager()
- .queryIntentActivities(intentPickAction, CHOOSE_OPML_FILE);
- if(intentActivities.size() == 0) {
- txtvHeaderExplanation1.setVisibility(View.GONE);
- txtvExplanation1.setVisibility(View.GONE);
- findViewById(R.id.divider1).setVisibility(View.GONE);
- butChooseFilesystem.setVisibility(View.GONE);
- }
+
+ if(false == IntentUtils.isCallable(getApplicationContext(), intentPickAction)) {
+ intentPickAction.setData(null);
+ if(false == IntentUtils.isCallable(getApplicationContext(), intentPickAction)) {
+ txtvHeaderExplanation1.setVisibility(View.GONE);
+ txtvExplanation1.setVisibility(View.GONE);
+ findViewById(R.id.divider1).setVisibility(View.GONE);
+ butChooseFilesystem.setVisibility(View.GONE);
}
+ }
if(txtvExplanation1.getVisibility() == View.VISIBLE) {
- txtvHeaderExplanation1.setText("Option " + nextOption);
- nextOption++;
- }
+ txtvHeaderExplanation1.setText("Option " + nextOption);
+ nextOption++;
+ }
intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT);
intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
intentGetContentAction.setType("*/*");
- intentActivities = getPackageManager()
- .queryIntentActivities(intentGetContentAction, CHOOSE_OPML_FILE);
- if(intentActivities.size() == 0) {
- txtvHeaderExplanation2.setVisibility(View.GONE);
- txtvExplanation2.setVisibility(View.GONE);
- findViewById(R.id.divider2).setVisibility(View.GONE);
- butChooseExternal.setVisibility(View.GONE);
- } else {
- txtvHeaderExplanation2.setText("Option " + nextOption);
- nextOption++;
- }
+ if(false == IntentUtils.isCallable(getApplicationContext(), intentGetContentAction)) {
+ txtvHeaderExplanation2.setVisibility(View.GONE);
+ txtvExplanation2.setVisibility(View.GONE);
+ findViewById(R.id.divider2).setVisibility(View.GONE);
+ butChooseExternal.setVisibility(View.GONE);
+ } else {
+ txtvHeaderExplanation2.setText("Option " + nextOption);
+ nextOption++;
+ }
txtvHeaderExplanation3.setText("Option " + nextOption);
}
@@ -137,7 +130,7 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
try {
mReader = new InputStreamReader(new FileInputStream(file),
LangUtils.UTF_8);
- if (BuildConfig.DEBUG) Log.d(TAG, "Parsing " + file.toString());
+ Log.d(TAG, "Parsing " + file.toString());
startImport(mReader);
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found which really should be there");
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
index 3802de2a6..94970d833 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
@@ -60,10 +60,13 @@ public class PreferenceActivity extends ActionBarActivity {
root.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(root);
- prefFragment = new MainFragment();
- getFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit();
+ // we need to create the PreferenceController before the MainFragment
+ // since the MainFragment depends on the preferenceController already being created
preferenceController = new PreferenceController(preferenceUI);
+
+ prefFragment = new MainFragment();
+ getFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit();
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
index 8e347a819..aca2b359a 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
@@ -88,7 +88,7 @@ public class ActionButtonUtils {
butSecondary.setContentDescription(context.getString(labels[0]));
}
} else {
- if (item.isRead()) {
+ if (item.isPlayed()) {
butSecondary.setVisibility(View.INVISIBLE);
} else {
butSecondary.setVisibility(View.VISIBLE);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
index e22b31361..d3ee08546 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
@@ -36,18 +36,19 @@ public class AdapterUtils {
|| state == FeedItem.State.IN_PROGRESS) {
if (media.getDuration() > 0) {
episodeProgress.setVisibility(View.VISIBLE);
- episodeProgress
- .setProgress((int) (((double) media
+ episodeProgress.setProgress((int) (((double) media
.getPosition()) / media.getDuration() * 100));
- txtvPos.setText(Converter
- .getDurationStringLong(media.getDuration()
+ txtvPos.setText(Converter.getDurationStringLong(media.getDuration()
- media.getPosition()));
}
} else if (!media.isDownloaded()) {
- txtvPos.setText(Converter.byteToString(media.getSize()));
+ if(media.getSize() > 0) {
+ txtvPos.setText(Converter.byteToString(media.getSize()));
+ } else {
+ txtvPos.setText("");
+ }
} else {
- txtvPos.setText(Converter.getDurationStringLong(media
- .getDuration()));
+ txtvPos.setText(Converter.getDurationStringLong(media.getDuration()));
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java
index d96326053..4b6ee8f91 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java
@@ -1,6 +1,8 @@
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.view.LayoutInflater;
import android.view.View;
@@ -11,11 +13,18 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.load.resource.drawable.GlideDrawable;
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
+
+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.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
@@ -72,6 +81,7 @@ public class AllEpisodesListAdapter extends BaseAdapter {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.new_episodes_listitem,
parent, false);
+ holder.placeholder = (TextView) convertView.findViewById(R.id.txtvPlaceholder);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.pubDate = (TextView) convertView
.findViewById(R.id.txtvPublished);
@@ -82,16 +92,18 @@ public class AllEpisodesListAdapter extends BaseAdapter {
.findViewById(R.id.imgvInPlaylist);
holder.progress = (ProgressBar) convertView
.findViewById(R.id.pbar_progress);
- holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
+ holder.cover = (ImageView) convertView.findViewById(R.id.imgvCover);
holder.txtvDuration = (TextView) convertView.findViewById(R.id.txtvDuration);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
+ holder.placeholder.setVisibility(View.VISIBLE);
+ holder.placeholder.setText(item.getFeed().getTitle());
holder.title.setText(item.getTitle());
holder.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
- if (showOnlyNewEpisodes || item.isRead() || false == itemAccess.isNew(item)) {
+ if (showOnlyNewEpisodes || false == item.isNew()) {
holder.statusUnread.setVisibility(View.INVISIBLE);
} else {
holder.statusUnread.setVisibility(View.VISIBLE);
@@ -141,14 +153,54 @@ public class AllEpisodesListAdapter extends BaseAdapter {
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
- Picasso.with(context)
+ Glide.with(context)
.load(item.getImageUri())
- .fit()
- .into(holder.imageView);
+ .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) {
@@ -159,11 +211,12 @@ public class AllEpisodesListAdapter extends BaseAdapter {
static class Holder {
+ TextView placeholder;
TextView title;
TextView pubDate;
View statusUnread;
ImageView queueStatus;
- ImageView imageView;
+ ImageView cover;
ProgressBar progress;
TextView txtvDuration;
ImageButton butSecondary;
@@ -179,7 +232,5 @@ public class AllEpisodesListAdapter extends BaseAdapter {
boolean isInQueue(FeedItem item);
- boolean isNew(FeedItem item);
-
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
index 223fe17f6..c3486f2f2 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
@@ -12,11 +12,8 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -61,7 +58,7 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
if (!isDownloading && !media.isDownloaded()) {
LongList queueIds = DBReader.getQueueIDList(context);
- if (NetworkUtils.isDownloadAllowed(context) || userAllowedMobileDownloads()) {
+ if (NetworkUtils.isDownloadAllowed() || userAllowedMobileDownloads()) {
try {
DBTasks.downloadFeedItems(context, item);
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
@@ -95,21 +92,8 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
}
}
} else {
- if (!item.isRead()) {
+ if (!item.isPlayed()) {
DBWriter.markItemRead(context, item, true, true);
-
- if(GpodnetPreferences.loggedIn()) {
- // gpodder: send played action
- FeedMedia media = item.getMedia();
- GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, GpodnetEpisodeAction.Action.PLAY)
- .currentDeviceId()
- .currentTimestamp()
- .started(media.getDuration() / 1000)
- .position(media.getDuration() / 1000)
- .total(media.getDuration() / 1000)
- .build();
- GpodnetPreferences.enqueueEpisodeAction(action);
- }
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
index f29cfdf2f..0eb15da8c 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -124,11 +124,15 @@ public class DownloadLogAdapter extends BaseAdapter {
ButtonHolder holder = (ButtonHolder) v.getTag();
if(holder.typeId == Feed.FEEDFILETYPE_FEED) {
Feed feed = DBReader.getFeed(context, holder.id);
- feed.setLastUpdate(new Date(0)); // force refresh
- try {
- DBTasks.refreshFeed(context, feed);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
+ if (feed != null) {
+ feed.setLastUpdate(new Date(0)); // force refresh
+ try {
+ DBTasks.refreshFeed(context, feed);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ }
+ } else {
+ Log.wtf(TAG, "Could not find feed for feed id: " + holder.id);
}
} else if(holder.typeId == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
FeedMedia media = DBReader.getFeedMedia(context, holder.id);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
index 15e0a7a33..2b1eccea5 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
@@ -10,10 +10,12 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.Converter;
/**
@@ -88,9 +90,13 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
holder.butSecondary.setOnClickListener(secondaryActionListener);
- Picasso.with(context)
+ Glide.with(context)
.load(item.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(holder.imageView);
return convertView;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
index b39e23d42..56e2bb1bd 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
@@ -33,13 +33,17 @@ public class FeedItemlistAdapter extends BaseAdapter {
private final Context context;
private boolean showFeedtitle;
private int selectedItemIndex;
+ /** true if played items should be made partially transparent */
+ private boolean makePlayedItemsTransparent;
private final ActionButtonUtils actionButtonUtils;
public static final int SELECTION_NONE = -1;
public FeedItemlistAdapter(Context context,
ItemAccess itemAccess,
- ActionButtonCallback callback, boolean showFeedtitle) {
+ ActionButtonCallback callback,
+ boolean showFeedtitle,
+ boolean makePlayedItemsTransparent) {
super();
this.callback = callback;
this.context = context;
@@ -47,6 +51,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
this.showFeedtitle = showFeedtitle;
this.selectedItemIndex = SELECTION_NONE;
this.actionButtonUtils = new ActionButtonUtils(context);
+ this.makePlayedItemsTransparent = makePlayedItemsTransparent;
}
@Override
@@ -106,18 +111,18 @@ public class FeedItemlistAdapter extends BaseAdapter {
StringBuilder buffer = new StringBuilder(item.getTitle());
if (showFeedtitle) {
- buffer.append("(");
+ buffer.append(" (");
buffer.append(item.getFeed().getTitle());
buffer.append(")");
}
holder.title.setText(buffer.toString());
- if(false == item.isRead() && itemAccess.isNew(item)) {
+ if(item.isNew()) {
holder.statusUnread.setVisibility(View.VISIBLE);
} else {
holder.statusUnread.setVisibility(View.INVISIBLE);
}
- if(item.isRead()) {
+ if(item.isPlayed() && makePlayedItemsTransparent) {
ViewHelper.setAlpha(convertView, 0.5f);
} else {
ViewHelper.setAlpha(convertView, 1.0f);
@@ -180,7 +185,6 @@ public class FeedItemlistAdapter extends BaseAdapter {
convertView.setVisibility(View.GONE);
}
return convertView;
-
}
private final OnClickListener butActionListener = new OnClickListener() {
@@ -221,8 +225,6 @@ public class FeedItemlistAdapter extends BaseAdapter {
FeedItem getItem(int position);
- boolean isNew(FeedItem item);
-
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
index 0d2d5cfa0..3c91cbbbb 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -15,7 +15,8 @@ import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.apache.commons.lang3.ArrayUtils;
@@ -27,6 +28,7 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.fragment.AddFeedFragment;
import de.danoeh.antennapod.fragment.AllEpisodesFragment;
@@ -259,9 +261,13 @@ public class NavListAdapter extends BaseAdapter
holder = (FeedHolder) convertView.getTag();
}
- Picasso.with(context)
+ Glide.with(context)
.load(feed.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(holder.image);
holder.title.setText(feed.getTitle());
@@ -276,10 +282,10 @@ public class NavListAdapter extends BaseAdapter
p.addRule(RelativeLayout.LEFT_OF, R.id.txtvCount);
holder.failure.setVisibility(View.GONE);
}
- int feedUnreadItems = itemAccess.getNumberOfUnreadFeedItems(feed.getId());
- if(feedUnreadItems > 0) {
+ int counter = itemAccess.getFeedCounter(feed.getId());
+ if(counter > 0) {
holder.count.setVisibility(View.VISIBLE);
- holder.count.setText(String.valueOf(feedUnreadItems));
+ holder.count.setText(String.valueOf(counter));
holder.count.setTypeface(holder.title.getTypeface());
} else {
holder.count.setVisibility(View.GONE);
@@ -306,7 +312,7 @@ public class NavListAdapter extends BaseAdapter
int getSelectedItemIndex();
int getQueueSize();
int getNumberOfNewItems();
- int getNumberOfUnreadFeedItems(long feedId);
+ int getFeedCounter(long feedId);
}
}
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 bba5a00a9..60c125fda 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java
@@ -1,7 +1,10 @@
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;
@@ -11,11 +14,18 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.load.resource.drawable.GlideDrawable;
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
+
+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;
@@ -25,6 +35,7 @@ import de.danoeh.antennapod.core.util.Converter;
*/
public class QueueListAdapter extends BaseAdapter {
+ private static final String TAG = QueueListAdapter.class.getSimpleName();
private final Context context;
private final ItemAccess itemAccess;
@@ -76,7 +87,8 @@ public class QueueListAdapter extends BaseAdapter {
convertView = inflater.inflate(R.layout.queue_listitem,
parent, false);
holder.dragHandle = (ImageView) convertView.findViewById(R.id.drag_handle);
- holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
+ 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);
@@ -86,7 +98,6 @@ public class QueueListAdapter extends BaseAdapter {
.findViewById(R.id.butSecondaryAction);
holder.progress = (ProgressBar) convertView
.findViewById(R.id.progressBar);
- holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
@@ -98,10 +109,11 @@ public class QueueListAdapter extends BaseAdapter {
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"));
@@ -129,7 +141,11 @@ public class QueueListAdapter extends BaseAdapter {
holder.progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
}
} else {
- holder.progressLeft.setText(Converter.byteToString(media.getSize()));
+ if(media.getSize() > 0) {
+ holder.progressLeft.setText(Converter.byteToString(media.getSize()));
+ } else {
+ holder.progressLeft.setText("");
+ }
holder.progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
holder.progress.setVisibility(View.GONE);
}
@@ -140,14 +156,54 @@ public class QueueListAdapter extends BaseAdapter {
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
- Picasso.with(context)
+ Glide.with(context)
.load(item.getImageUri())
- .fit()
- .into(holder.imageView);
+ .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) {
@@ -156,10 +212,10 @@ public class QueueListAdapter extends BaseAdapter {
}
};
-
static class Holder {
ImageView dragHandle;
- ImageView imageView;
+ ImageView cover;
+ TextView placeholder;
TextView title;
TextView pubDate;
TextView progressLeft;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
index cedce7903..83f5dcb4d 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
@@ -8,13 +8,15 @@ import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.SearchResult;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
/**
* List adapter for search activity.
@@ -73,9 +75,13 @@ public class SearchlistAdapter extends BaseAdapter {
holder.title.setText(feed.getTitle());
holder.subtitle.setVisibility(View.GONE);
- Picasso.with(context)
+ Glide.with(context)
.load(feed.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(holder.cover);
} else if (component.getClass() == FeedItem.class) {
@@ -86,9 +92,13 @@ public class SearchlistAdapter extends BaseAdapter {
holder.subtitle.setText(result.getSubtitle());
}
- Picasso.with(context)
+ Glide.with(context)
.load(item.getFeed().getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(holder.cover);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
index b85709c5e..743f9fc86 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
@@ -8,13 +8,15 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetPodcast;
/**
@@ -49,9 +51,13 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
}
if (StringUtils.isNotBlank(podcast.getLogoUrl())) {
- Picasso.with(convertView.getContext())
+ Glide.with(convertView.getContext())
.load(podcast.getLogoUrl())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(holder.image);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
index 4fc2838b7..08ffdd197 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
@@ -1,23 +1,18 @@
package de.danoeh.antennapod.adapter.itunes;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.AsyncTask;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+
import org.json.JSONException;
import org.json.JSONObject;
-import java.io.IOException;
import java.util.List;
import de.danoeh.antennapod.R;
@@ -46,55 +41,6 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
this.context = context;
}
- /**
- * Updates the given ImageView with the image in the given Podcast's imageUrl
- */
- class FetchImageTask extends AsyncTask<Void,Void,Bitmap>{
- /**
- * Current podcast
- */
- private final Podcast podcast;
-
- /**
- * ImageView to be updated
- */
- private final ImageView imageView;
-
- /**
- * Constructor
- *
- * @param podcast Podcast that has the image
- * @param imageView UI image to be updated
- */
- FetchImageTask(Podcast podcast, ImageView imageView){
- this.podcast = podcast;
- this.imageView = imageView;
- }
-
- //Get the image from the url
- @Override
- protected Bitmap doInBackground(Void... params) {
- HttpClient client = new DefaultHttpClient();
- HttpGet get = new HttpGet(podcast.imageUrl);
- try {
- HttpResponse response = client.execute(get);
- return BitmapFactory.decodeStream(response.getEntity().getContent());
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- //Set the background image for the podcast
- @Override
- protected void onPostExecute(Bitmap img) {
- super.onPostExecute(img);
- if(img!=null) {
- imageView.setImageBitmap(img);
- }
- }
- }
-
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//Current podcast
@@ -121,7 +67,13 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
viewHolder.titleView.setText(podcast.title);
//Update the empty imageView with the image from the feed
- new FetchImageTask(podcast,viewHolder.coverView).execute();
+ Glide.with(context)
+ .load(podcast.imageUrl)
+ .placeholder(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .fitCenter()
+ .dontAnimate()
+ .into(viewHolder.coverView);
//Feed the grid view
return view;
diff --git a/app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java
index 4d9be5d78..008aacfa5 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java
@@ -8,7 +8,6 @@ import android.content.Intent;
import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.activity.StorageErrorActivity;
import de.danoeh.antennapod.core.ApplicationCallbacks;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
public class ApplicationCallbacksImpl implements ApplicationCallbacks {
@@ -22,8 +21,4 @@ public class ApplicationCallbacksImpl implements ApplicationCallbacks {
return new Intent(context, StorageErrorActivity.class);
}
- @Override
- public void setUpdateInterval(long updateInterval) {
- UserPreferences.restartUpdateAlarm(updateInterval, updateInterval);
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
index 10666aa36..932b9d22f 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
@@ -14,7 +14,6 @@ public class ClientConfigurator {
ClientConfig.downloadServiceCallbacks = new DownloadServiceCallbacksImpl();
ClientConfig.gpodnetCallbacks = new GpodnetCallbacksImpl();
ClientConfig.playbackServiceCallbacks = new PlaybackServiceCallbacksImpl();
- ClientConfig.storageCallbacks = new StorageCallbacksImpl();
ClientConfig.flattrCallbacks = new FlattrCallbacksImpl();
ClientConfig.dbTasksCallbacks = new DBTasksCallbacksImpl();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java
deleted file mode 100644
index 943e05690..000000000
--- a/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java
+++ /dev/null
@@ -1,153 +0,0 @@
-package de.danoeh.antennapod.config;
-
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.util.Log;
-
-import de.danoeh.antennapod.core.StorageCallbacks;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-
-public class StorageCallbacksImpl implements StorageCallbacks {
-
- @Override
- public int getDatabaseVersion() {
- return 15;
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- Log.w("DBAdapter", "Upgrading from version " + oldVersion + " to "
- + newVersion + ".");
- if (oldVersion <= 1) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN "
- + PodDBAdapter.KEY_TYPE + " TEXT");
- }
- if (oldVersion <= 2) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS
- + " ADD COLUMN " + PodDBAdapter.KEY_LINK + " TEXT");
- }
- if (oldVersion <= 3) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + PodDBAdapter.KEY_ITEM_IDENTIFIER + " TEXT");
- }
- if (oldVersion <= 4) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN "
- + PodDBAdapter.KEY_FEED_IDENTIFIER + " TEXT");
- }
- if (oldVersion <= 5) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG
- + " ADD COLUMN " + PodDBAdapter.KEY_REASON_DETAILED + " TEXT");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG
- + " ADD COLUMN " + PodDBAdapter.KEY_DOWNLOADSTATUS_TITLE + " TEXT");
- }
- if (oldVersion <= 6) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS
- + " ADD COLUMN " + PodDBAdapter.KEY_CHAPTER_TYPE + " INTEGER");
- }
- if (oldVersion <= 7) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " ADD COLUMN " + PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE
- + " INTEGER");
- }
- if (oldVersion <= 8) {
- final int KEY_ID_POSITION = 0;
- final int KEY_MEDIA_POSITION = 1;
-
- // Add feeditem column to feedmedia table
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " ADD COLUMN " + PodDBAdapter.KEY_FEEDITEM
- + " INTEGER");
- Cursor feeditemCursor = db.query(PodDBAdapter.TABLE_NAME_FEED_ITEMS,
- new String[]{PodDBAdapter.KEY_ID, PodDBAdapter.KEY_MEDIA}, "? > 0",
- new String[]{PodDBAdapter.KEY_MEDIA}, null, null, null);
- if (feeditemCursor.moveToFirst()) {
- db.beginTransaction();
- ContentValues contentValues = new ContentValues();
- do {
- long mediaId = feeditemCursor.getLong(KEY_MEDIA_POSITION);
- contentValues.put(PodDBAdapter.KEY_FEEDITEM, feeditemCursor.getLong(KEY_ID_POSITION));
- db.update(PodDBAdapter.TABLE_NAME_FEED_MEDIA, contentValues, PodDBAdapter.KEY_ID + "=?", new String[]{String.valueOf(mediaId)});
- contentValues.clear();
- } while (feeditemCursor.moveToNext());
- db.setTransactionSuccessful();
- db.endTransaction();
- }
- feeditemCursor.close();
- }
- if (oldVersion <= 9) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD
- + " INTEGER DEFAULT 1");
- }
- if (oldVersion <= 10) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_FLATTR_STATUS
- + " INTEGER");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + PodDBAdapter.KEY_FLATTR_STATUS
- + " INTEGER");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " ADD COLUMN " + PodDBAdapter.KEY_PLAYED_DURATION
- + " INTEGER");
- }
- if (oldVersion <= 11) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_USERNAME
- + " TEXT");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_PASSWORD
- + " TEXT");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + PodDBAdapter.KEY_IMAGE
- + " INTEGER");
- }
- if (oldVersion <= 12) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_IS_PAGED + " INTEGER DEFAULT 0");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_NEXT_PAGE_LINK + " TEXT");
- }
- if (oldVersion <= 13) {
- // remove duplicate rows in "Chapters" table that were created because of a bug.
- db.execSQL(String.format("DELETE FROM %s WHERE %s NOT IN " +
- "(SELECT MIN(%s) as %s FROM %s GROUP BY %s,%s,%s,%s,%s)",
- PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS,
- PodDBAdapter.KEY_ID,
- PodDBAdapter.KEY_ID,
- PodDBAdapter.KEY_ID,
- PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS,
- PodDBAdapter.KEY_TITLE,
- PodDBAdapter.KEY_START,
- PodDBAdapter.KEY_FEEDITEM,
- PodDBAdapter.KEY_LINK,
- PodDBAdapter.KEY_CHAPTER_TYPE));
- }
- if(oldVersion <= 14) {
-
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD + " INTEGER");
- db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " SET " + PodDBAdapter.KEY_AUTO_DOWNLOAD + " = "
- + "(SELECT " + PodDBAdapter.KEY_AUTO_DOWNLOAD
- + " FROM " + PodDBAdapter.TABLE_NAME_FEEDS
- + " WHERE " + PodDBAdapter.TABLE_NAME_FEEDS + "." + PodDBAdapter.KEY_ID
- + " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_FEED + ")");
-
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_HIDE + " TEXT");
-
-
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0");
-
- // create indexes
- db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_FEED);
- db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_IMAGE);
- db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDMEDIA_FEEDITEM);
- db.execSQL(PodDBAdapter.CREATE_INDEX_QUEUE_FEEDITEM);
- db.execSQL(PodDBAdapter.CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM);
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
new file mode 100644
index 000000000..8a4a4efbf
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -0,0 +1,414 @@
+package de.danoeh.antennapod.dialog;
+
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.joanzapata.android.iconify.IconDrawable;
+import com.joanzapata.android.iconify.Iconify;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
+import de.danoeh.antennapod.core.feed.FeedItem;
+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.util.LongList;
+
+public class EpisodesApplyActionFragment extends Fragment {
+
+ public String TAG = "EpisodeActionFragment";
+
+ private ListView mListView;
+ private ArrayAdapter<String> mAdapter;
+
+ private Button btnAddToQueue;
+ private Button btnMarkAsPlayed;
+ private Button btnMarkAsUnplayed;
+ private Button btnDownload;
+ private Button btnDelete;
+
+ private final Map<Long,FeedItem> idMap;
+ private final List<FeedItem> episodes;
+ private final List<String> titles = new ArrayList();
+ private final LongList checkedIds = new LongList();
+
+ private MenuItem mSelectToggle;
+
+ private int textColor;
+
+ public EpisodesApplyActionFragment(List<FeedItem> episodes) {
+ this.episodes = episodes;
+ this.idMap = new HashMap<>(episodes.size());
+ for(FeedItem episode : episodes) {
+ this.idMap.put(episode.getId(), episode);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.episodes_apply_action_fragment, container, false);
+
+ mListView = (ListView) view.findViewById(android.R.id.list);
+ mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ public void onItemClick(AdapterView<?> ListView, View view, int position, long rowId) {
+ long id = episodes.get(position).getId();
+ if (checkedIds.contains(id)) {
+ checkedIds.remove(id);
+ } else {
+ checkedIds.add(id);
+ }
+ refreshCheckboxes();
+ }
+ });
+
+ for(FeedItem episode : episodes) {
+ titles.add(episode.getTitle());
+ }
+
+ mAdapter = new ArrayAdapter<>(getActivity(),
+ android.R.layout.simple_list_item_multiple_choice, titles);
+ mListView.setAdapter(mAdapter);
+ checkAll();
+
+ btnAddToQueue = (Button) view.findViewById(R.id.btnAddToQueue);
+ btnAddToQueue.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ queueChecked();
+ }
+ });
+ btnMarkAsPlayed = (Button) view.findViewById(R.id.btnMarkAsPlayed);
+ btnMarkAsPlayed.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ markedCheckedPlayed();
+ }
+ });
+ btnMarkAsUnplayed = (Button) view.findViewById(R.id.btnMarkAsUnplayed);
+ btnMarkAsUnplayed.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ markedCheckedUnplayed();
+ }
+ });
+ btnDownload = (Button) view.findViewById(R.id.btnDownload);
+ btnDownload.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ downloadChecked();
+ }
+ });
+ btnDelete = (Button) view.findViewById(R.id.btnDelete);
+ btnDelete.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ deleteChecked();
+ }
+ });
+
+ return view;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.episodes_apply_action_options, menu);
+
+ int[] attrs = { android.R.attr.textColor };
+ TypedArray ta = getActivity().obtainStyledAttributes(attrs);
+ textColor = ta.getColor(0, Color.GRAY);
+ ta.recycle();
+
+ menu.findItem(R.id.sort).setIcon(new IconDrawable(getActivity(),
+ Iconify.IconValue.fa_sort).color(textColor).actionBarSize());
+
+ mSelectToggle = menu.findItem(R.id.select_toggle);
+ mSelectToggle.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ if (checkedIds.size() == episodes.size()) {
+ checkNone();
+ } else {
+ checkAll();
+ }
+ return true;
+ }
+ });
+
+ menu.findItem(R.id.select_options).setIcon(new IconDrawable(getActivity(),
+ Iconify.IconValue.fa_caret_down).color(textColor).actionBarSize());
+ }
+
+ @Override
+ public void onPrepareOptionsMenu (Menu menu) {
+ Iconify.IconValue iVal;
+ if(checkedIds.size() == episodes.size()) {
+ iVal = Iconify.IconValue.fa_check_square_o;
+ } else if(checkedIds.size() == 0) {
+ iVal = Iconify.IconValue.fa_square_o;
+ } else {
+ iVal = Iconify.IconValue.fa_minus_square_o;
+ }
+ mSelectToggle.setIcon(new IconDrawable(getActivity(), iVal).color(textColor).actionBarSize());
+
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int resId = 0;
+ switch(item.getItemId()) {
+ case R.id.select_options:
+ return true;
+ case R.id.check_all:
+ checkAll();
+ resId = R.string.selected_all_label;
+ break;
+ case R.id.check_none:
+ checkNone();
+ resId = R.string.deselected_all_label;
+ break;
+ case R.id.check_played:
+ checkPlayed(true);
+ resId = R.string.selected_played_label;
+ break;
+ case R.id.check_unplayed:
+ checkPlayed(false);
+ resId = R.string.selected_unplayed_label;
+ break;
+ case R.id.check_downloaded:
+ checkDownloaded(true);
+ resId = R.string.selected_downloaded_label;
+ break;
+ case R.id.check_not_downloaded:
+ checkDownloaded(false);
+ resId = R.string.selected_not_downloaded_label;
+ break;
+ case R.id.sort_title_a_z:
+ sortByTitle(false);
+ return true;
+ case R.id.sort_title_z_a:
+ sortByTitle(true);
+ return true;
+ case R.id.sort_date_new_old:
+ sortByDate(true);
+ return true;
+ case R.id.sort_date_old_new:
+ sortByDate(false);
+ return true;
+ case R.id.sort_duration_long_short:
+ sortByDuration(true);
+ return true;
+ case R.id.sort_duration_short_long:
+ sortByDuration(false);
+ return true;
+ }
+ if(resId != 0) {
+ Toast.makeText(getActivity(), resId, Toast.LENGTH_SHORT).show();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private void sortByTitle(final boolean reverse) {
+ Collections.sort(episodes, new Comparator<FeedItem>() {
+ @Override
+ public int compare(FeedItem lhs, FeedItem rhs) {
+ if (reverse) {
+ return -1 * lhs.getTitle().compareTo(rhs.getTitle());
+ } else {
+ return lhs.getTitle().compareTo(rhs.getTitle());
+ }
+ }
+ });
+ refreshTitles();
+ refreshCheckboxes();
+ }
+
+ private void sortByDate(final boolean reverse) {
+ Collections.sort(episodes, new Comparator<FeedItem>() {
+ @Override
+ public int compare(FeedItem lhs, FeedItem rhs) {
+ if (lhs.getPubDate() == null) {
+ return -1;
+ } else if (rhs.getPubDate() == null) {
+ return 1;
+ }
+ int code = lhs.getPubDate().compareTo(rhs.getPubDate());
+ if (reverse) {
+ return -1 * code;
+ } else {
+ return code;
+ }
+ }
+ });
+ refreshTitles();
+ refreshCheckboxes();
+ }
+
+ private void sortByDuration(final boolean reverse) {
+ Collections.sort(episodes, new Comparator<FeedItem>() {
+ @Override
+ public int compare(FeedItem lhs, FeedItem rhs) {
+ int ordering;
+ if (false == lhs.hasMedia()) {
+ ordering = 1;
+ } else if (false == rhs.hasMedia()) {
+ ordering = -1;
+ } else {
+ ordering = lhs.getMedia().getDuration() - rhs.getMedia().getDuration();
+ }
+ if(reverse) {
+ return -1 * ordering;
+ } else {
+ return ordering;
+ }
+ }
+ });
+ refreshTitles();
+ refreshCheckboxes();
+ }
+
+ private void checkAll() {
+ for (FeedItem episode : episodes) {
+ if(false == checkedIds.contains(episode.getId())) {
+ checkedIds.add(episode.getId());
+ }
+ }
+ refreshCheckboxes();
+ }
+
+ private void checkNone() {
+ checkedIds.clear();
+ refreshCheckboxes();
+ }
+
+ private void checkPlayed(boolean isPlayed) {
+ for (FeedItem episode : episodes) {
+ if(episode.isPlayed() == isPlayed) {
+ if(!checkedIds.contains(episode.getId())) {
+ checkedIds.add(episode.getId());
+ }
+ } else {
+ if(checkedIds.contains(episode.getId())) {
+ checkedIds.remove(episode.getId());
+ }
+ }
+ }
+ refreshCheckboxes();
+ }
+
+ private void checkDownloaded(boolean isDownloaded) {
+ for (FeedItem episode : episodes) {
+ if(episode.hasMedia() && episode.getMedia().isDownloaded() == isDownloaded) {
+ if(!checkedIds.contains(episode.getId())) {
+ checkedIds.add(episode.getId());
+ }
+ } else {
+ if(checkedIds.contains(episode.getId())) {
+ checkedIds.remove(episode.getId());
+ }
+ }
+ }
+ refreshCheckboxes();
+ }
+
+ private void refreshTitles() {
+ titles.clear();
+ for(FeedItem episode : episodes) {
+ titles.add(episode.getTitle());
+ }
+ mAdapter.notifyDataSetChanged();
+ }
+
+ private void refreshCheckboxes() {
+ for (int i = 0; i < episodes.size(); i++) {
+ FeedItem episode = episodes.get(i);
+ boolean checked = checkedIds.contains(episode.getId());
+ mListView.setItemChecked(i, checked);
+ }
+ ActivityCompat.invalidateOptionsMenu(EpisodesApplyActionFragment.this.getActivity());
+ }
+
+ private void queueChecked() {
+ LongList orderedIds = new LongList();
+ for(FeedItem episode : episodes) {
+ if(checkedIds.contains(episode.getId())) {
+ orderedIds.add((episode.getId()));
+ }
+ }
+ DBWriter.addQueueItem(getActivity(), false, orderedIds.toArray());
+ close();
+ }
+
+ private void markedCheckedPlayed() {
+ DBWriter.markItemRead(getActivity(), true, checkedIds.toArray());
+ close();
+ }
+
+ private void markedCheckedUnplayed() {
+ DBWriter.markItemRead(getActivity(), false, checkedIds.toArray());
+ close();
+ }
+
+ private void downloadChecked() {
+ // download the check episodes in the same order as they are currently displayed
+ List<FeedItem> toDownload = new ArrayList<FeedItem>(checkedIds.size());
+ for(FeedItem episode : episodes) {
+ if(checkedIds.contains(episode.getId())) {
+ toDownload.add(episode);
+ }
+ }
+ try {
+ DBTasks.downloadFeedItems(getActivity(), toDownload.toArray(new FeedItem[0]));
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage());
+ }
+ close();
+ }
+
+ private void deleteChecked() {
+ for(long id : checkedIds.toArray()) {
+ FeedItem episode = idMap.get(id);
+ if(episode.hasMedia()) {
+ DBWriter.deleteFeedMediaOfItem(getActivity(), episode.getMedia().getId());
+ }
+ }
+ close();
+ }
+
+ private void close() {
+ getActivity().getSupportFragmentManager().popBackStack();
+ }
+
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java
index 6561d501e..5c4d4c430 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java
@@ -39,9 +39,9 @@ public abstract class TimeDialog extends Dialog {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
- String[] spinnerContent = new String[]{context.getString(R.string.time_unit_seconds),
- context.getString(R.string.time_unit_minutes),
- context.getString(R.string.time_unit_hours)};
+ String[] spinnerContent = new String[]{context.getString(R.string.time_seconds),
+ context.getString(R.string.time_minutes),
+ context.getString(R.string.time_hours)};
setContentView(R.layout.time_dialog);
etxtTime = (EditText) findViewById(R.id.etxtTime);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
index ff5485251..b4c4f1822 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -30,8 +30,8 @@ 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.AllEpisodesListAdapter;
+import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.feed.EventDistributor;
@@ -72,10 +72,10 @@ public class AllEpisodesFragment extends Fragment {
private TextView txtvEmpty;
private ProgressBar progLoading;
private ContextMenu contextMenu;
+ private AdapterView.AdapterContextMenuInfo lastMenuInfo = null;
private List<FeedItem> episodes;
private LongList queuedItemsIds;
- private LongList newItemsIds;
private List<Downloader> downloaderList;
private boolean itemsLoaded = false;
@@ -341,12 +341,16 @@ public class AllEpisodesFragment extends Fragment {
}
contextMenu = menu;
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, queuedItemsIds);
+ lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queuedItemsIds);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+ if(menuInfo == null) {
+ menuInfo = lastMenuInfo;
+ }
FeedItem selectedItem = itemAccess.getItem(menuInfo.position);
if (selectedItem == null) {
@@ -380,14 +384,7 @@ public class AllEpisodesFragment extends Fragment {
private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
@Override
- public void onContentChanged() {
- if (listAdapter != null) {
- listAdapter.notifyDataSetChanged();
- }
- }
-
- @Override
- public void onDownloadDataAvailable(List<Downloader> downloaderList) {
+ public void onContentChanged(List<Downloader> downloaderList) {
AllEpisodesFragment.this.downloaderList = downloaderList;
if (listAdapter != null) {
listAdapter.notifyDataSetChanged();
@@ -434,18 +431,6 @@ public class AllEpisodesFragment extends Fragment {
return false;
}
}
-
- @Override
- public boolean isNew(FeedItem item) {
- if (itemsLoaded) {
- // should actually never be called in NewEpisodesFragment, but better safe than sorry
- return showOnlyNewEpisodes || newItemsIds.contains(item.getId());
- } else {
- return false;
- }
- }
-
-
};
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@@ -510,8 +495,7 @@ public class AllEpisodesFragment extends Fragment {
} else {
return new Object[]{
DBReader.getRecentlyPublishedEpisodes(context, RECENT_EPISODES_LIMIT),
- DBReader.getQueueIDList(context),
- DBReader.getNewItemIds(context)
+ DBReader.getQueueIDList(context)
};
}
} else {
@@ -528,7 +512,6 @@ public class AllEpisodesFragment extends Fragment {
if (lists != null) {
episodes = (List<FeedItem>) lists[0];
queuedItemsIds = (LongList) lists[1];
- newItemsIds = (LongList) lists[2];
itemsLoaded = true;
if (viewsCreated && activity.get() != null) {
onFragmentLoaded();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
index 3076f8136..a1667cce0 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -10,12 +10,14 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AudioplayerActivity;
import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.playback.Playable;
/**
@@ -80,8 +82,12 @@ public class CoverFragment extends Fragment implements
public void run() {
Context c = getActivity();
if (c != null) {
- Picasso.with(c)
+ Glide.with(c)
.load(media.getImageUri())
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .dontAnimate()
.into(imgvCover);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
index fdb128f03..634c3c546 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -11,9 +11,11 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.playback.Playable;
@@ -55,7 +57,7 @@ public class ExternalPlayerFragment extends Fragment {
public void onClick(View v) {
Log.d(TAG, "layoutInfo was clicked");
- if (controller.getMedia() != null) {
+ if (controller != null && controller.getMedia() != null) {
startActivity(PlaybackService.getPlayerActivityIntent(
getActivity(), controller.getMedia()));
}
@@ -192,14 +194,18 @@ public class ExternalPlayerFragment extends Fragment {
private boolean loadMediaInfo() {
Log.d(TAG, "Loading media info");
- if (controller.serviceAvailable()) {
+ if (controller != null && controller.serviceAvailable()) {
Playable media = controller.getMedia();
if (media != null) {
txtvTitle.setText(media.getEpisodeTitle());
- Picasso.with(getActivity())
+ Glide.with(getActivity())
.load(media.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(imgvCover);
fragmentLayout.setVisibility(View.VISIBLE);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index a7c6d62e6..9693e6886 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -25,12 +25,12 @@ import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
-import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.ShownotesProvider;
import de.danoeh.antennapod.core.util.playback.Playable;
@@ -104,8 +104,7 @@ public class ItemDescriptionFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Creating view");
+ Log.d(TAG, "Creating view");
webvDescription = new WebView(getActivity());
if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
if (Build.VERSION.SDK_INT >= 11
@@ -141,8 +140,7 @@ public class ItemDescriptionFragment extends Fragment {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Page finished");
+ Log.d(TAG, "Page finished");
// Restoring the scroll position might not always work
view.postDelayed(new Runnable() {
@@ -163,15 +161,13 @@ public class ItemDescriptionFragment extends Fragment {
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Fragment attached");
+ Log.d(TAG, "Fragment attached");
}
@Override
public void onDetach() {
super.onDetach();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Fragment detached");
+ Log.d(TAG, "Fragment detached");
if (webViewLoader != null) {
webViewLoader.cancel(true);
}
@@ -180,8 +176,7 @@ public class ItemDescriptionFragment extends Fragment {
@Override
public void onDestroy() {
super.onDestroy();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Fragment destroyed");
+ Log.d(TAG, "Fragment destroyed");
if (webViewLoader != null) {
webViewLoader.cancel(true);
}
@@ -195,8 +190,7 @@ public class ItemDescriptionFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Creating fragment");
+ Log.d(TAG, "Creating fragment");
Bundle args = getArguments();
saveState = args.getBoolean(ARG_SAVE_STATE, false);
highlightTimecodes = args.getBoolean(ARG_HIGHLIGHT_TIMECODES, false);
@@ -258,11 +252,7 @@ public class ItemDescriptionFragment extends Fragment {
WebView.HitTestResult r = webvDescription.getHitTestResult();
if (r != null
&& r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Link of webview was long-pressed. Extra: "
- + r.getExtra()
- );
+ Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.getExtra());
selectedURL = r.getExtra();
webvDescription.showContextMenu();
return true;
@@ -281,8 +271,10 @@ public class ItemDescriptionFragment extends Fragment {
switch (item.getItemId()) {
case R.id.open_in_browser_item:
Uri uri = Uri.parse(selectedURL);
- getActivity()
- .startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ getActivity().startActivity(intent);
+ }
break;
case R.id.share_url_item:
ShareUtils.shareLink(getActivity(), selectedURL);
@@ -331,8 +323,12 @@ public class ItemDescriptionFragment extends Fragment {
R.string.go_to_position_label);
menu.setHeaderTitle(Converter.getDurationStringLong(Timeline.getTimecodeLinkTime(selectedURL)));
} else {
- menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
- R.string.open_in_browser_label);
+ Uri uri = Uri.parse(selectedURL);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
+ R.string.open_in_browser_label);
+ }
menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
R.string.copy_url_label);
menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
@@ -358,8 +354,7 @@ public class ItemDescriptionFragment extends Fragment {
// /webvDescription.loadData(url, "text/html", "utf-8");
webvDescription.loadDataWithBaseURL(null, data, "text/html",
"utf-8", "about:blank");
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Webview loaded");
+ Log.d(TAG, "Webview loaded");
webViewLoader = null;
}
@@ -370,8 +365,7 @@ public class ItemDescriptionFragment extends Fragment {
@Override
protected Void doInBackground(Void... params) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Loading Webview");
+ Log.d(TAG, "Loading Webview");
try {
Activity activity = getActivity();
if (activity != null) {
@@ -397,24 +391,17 @@ public class ItemDescriptionFragment extends Fragment {
private void savePreference() {
if (saveState) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Saving preferences");
+ Log.d(TAG, "Saving preferences");
SharedPreferences prefs = getActivity().getSharedPreferences(PREF,
Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
if (media != null && webvDescription != null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Saving scroll position: "
- + webvDescription.getScrollY()
- );
+ Log.d(TAG, "Saving scroll position: " + webvDescription.getScrollY());
editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY());
editor.putString(PREF_PLAYABLE_ID, media.getIdentifier()
.toString());
} else {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "savePreferences was called while media or webview was null");
+ Log.d(TAG, "savePreferences was called while media or webview was null");
editor.putInt(PREF_SCROLL_Y, -1);
editor.putString(PREF_PLAYABLE_ID, "");
}
@@ -424,8 +411,7 @@ public class ItemDescriptionFragment extends Fragment {
private boolean restoreFromPreference() {
if (saveState) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Restoring from preferences");
+ Log.d(TAG, "Restoring from preferences");
Activity activity = getActivity();
if (activity != null) {
SharedPreferences prefs = activity.getSharedPreferences(
@@ -435,8 +421,7 @@ public class ItemDescriptionFragment extends Fragment {
if (scrollY != -1 && media != null
&& id.equals(media.getIdentifier().toString())
&& webvDescription != null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Restored scroll Position: " + scrollY);
+ Log.d(TAG, "Restored scroll Position: " + scrollY);
webvDescription.scrollTo(webvDescription.getScrollX(),
scrollY);
return true;
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 51a1e2252..4edb7f36f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -1,7 +1,8 @@
package de.danoeh.antennapod.fragment;
import android.annotation.TargetApi;
-import android.content.ActivityNotFoundException;
+import android.content.ClipData;
+import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray;
import android.net.Uri;
@@ -18,7 +19,9 @@ import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
+import android.view.ContextMenu;
import android.view.LayoutInflater;
+import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -32,7 +35,8 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import java.util.List;
@@ -45,6 +49,7 @@ import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.QueueEvent;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -53,7 +58,9 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
+import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.playback.Timeline;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.greenrobot.event.EventBus;
@@ -107,6 +114,11 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
private ImageButton butMore;
private PopupMenu popupMenu;
+ /**
+ * URL that was selected via long-press.
+ */
+ private String selectedURL;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -194,20 +206,19 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
webvDescription.getSettings().setLayoutAlgorithm(
WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webvDescription.getSettings().setLoadWithOverviewMode(true);
+ webvDescription.setOnLongClickListener(webViewLongClickListener);
webvDescription.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- try {
+ if(IntentUtils.isCallable(getActivity(), intent)) {
startActivity(intent);
- } catch (ActivityNotFoundException e) {
- e.printStackTrace();
- return true;
}
return true;
}
});
+ registerForContextMenu(webvDescription);
imgvCover = (ImageView) header.findViewById(R.id.imgvCover);
progbarDownload = (ProgressBar) header.findViewById(R.id.progbarDownload);
@@ -272,10 +283,10 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
popupMenu.getMenu().clear();
popupMenu.inflate(R.menu.feeditem_options);
if (item.hasMedia()) {
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue);
+ FeedItemMenuHandler.onPrepareMenu(getActivity(), popupMenuInterface, item, true, queue);
} else {
// these are already available via button1 and button2
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue,
+ FeedItemMenuHandler.onPrepareMenu(getActivity(), popupMenuInterface, item, true, queue,
R.id.mark_read_item, R.id.visit_website_item);
}
popupMenu.show();
@@ -313,7 +324,7 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
private void onFragmentLoaded() {
- progbarLoading.setVisibility(View.GONE);
+ progbarLoading.setVisibility(View.INVISIBLE);
if (webviewData != null) {
webvDescription.loadDataWithBaseURL(null, webviewData, "text/html",
"utf-8", "about:blank");
@@ -327,10 +338,16 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
txtvTitle.setText(item.getTitle());
txtvPublished.setText(DateUtils.formatDateTime(getActivity(), item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
- Picasso.with(getActivity()).load(item.getImageUri())
- .fit()
+ Glide.with(getActivity())
+ .load(item.getImageUri())
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(imgvCover);
- progbarDownload.setVisibility(View.GONE);
+
+ progbarDownload.setVisibility(View.INVISIBLE);
if (item.hasMedia() && downloaderList != null) {
for (Downloader downloader : downloaderList) {
if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
@@ -346,7 +363,7 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.navigation_accept,
R.attr.location_web_site});
- if (!item.isRead()) {
+ if (!item.isPlayed()) {
butAction1.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(0), null, null, null);
butAction1.setText(getActivity().getString(R.string.mark_read_label));
butAction1.setVisibility(View.VISIBLE);
@@ -398,6 +415,83 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
getLoaderManager().restartLoader(0, null, ItemFragment.this);
}
+ private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ WebView.HitTestResult r = webvDescription.getHitTestResult();
+ if (r != null
+ && r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
+ Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.getExtra());
+ selectedURL = r.getExtra();
+ webvDescription.showContextMenu();
+ return true;
+ }
+ selectedURL = null;
+ return false;
+ }
+ };
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ boolean handled = selectedURL != null;
+ if (selectedURL != null) {
+ switch (item.getItemId()) {
+ case R.id.open_in_browser_item:
+ Uri uri = Uri.parse(selectedURL);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ getActivity().startActivity(intent);
+ }
+ break;
+ case R.id.share_url_item:
+ ShareUtils.shareLink(getActivity(), selectedURL);
+ break;
+ case R.id.copy_url_item:
+ if (android.os.Build.VERSION.SDK_INT >= 11) {
+ ClipData clipData = ClipData.newPlainText(selectedURL,
+ selectedURL);
+ android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(clipData);
+ } else {
+ android.text.ClipboardManager cm = (android.text.ClipboardManager) getActivity()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setText(selectedURL);
+ }
+ Toast t = Toast.makeText(getActivity(),
+ R.string.copied_url_msg, Toast.LENGTH_SHORT);
+ t.show();
+ break;
+ default:
+ handled = false;
+ break;
+
+ }
+ selectedURL = null;
+ }
+ return handled;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenu.ContextMenuInfo menuInfo) {
+ if (selectedURL != null) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ Uri uri = Uri.parse(selectedURL);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
+ R.string.open_in_browser_label);
+ }
+ menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
+ R.string.copy_url_label);
+ menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
+ R.string.share_url_label);
+ menu.setHeaderTitle(selectedURL);
+ }
+ }
+
@Override
public Loader<Pair<FeedItem,LongList>> onCreateLoader(int id, Bundle args) {
return new DBTaskLoader<Pair<FeedItem,LongList>>(getActivity()) {
@@ -445,14 +539,7 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
private final DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
@Override
- public void onContentChanged() {
- if (itemsLoaded && getActivity() != null) {
- updateAppearance();
- }
- }
-
- @Override
- public void onDownloadDataAvailable(List<Downloader> downloaderList) {
+ public void onContentChanged(List<Downloader> downloaderList) {
ItemFragment.this.downloaderList = downloaderList;
if (itemsLoaded && getActivity() != null) {
updateAppearance();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
index a9cbe8291..63ebf234e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -4,13 +4,16 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.LightingColorFilter;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.support.v4.app.Fragment;
import android.support.v4.app.ListFragment;
import android.support.v4.view.MenuItemCompat;
-
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.util.Log;
@@ -29,8 +32,10 @@ import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.joanzapata.android.iconify.IconDrawable;
import com.joanzapata.android.iconify.Iconify;
-import com.squareup.picasso.Picasso;
import org.apache.commons.lang3.Validate;
@@ -43,7 +48,6 @@ import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
-import de.danoeh.antennapod.core.asynctask.PicassoProvider;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.EventDistributor;
@@ -53,6 +57,8 @@ import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedItemFilter;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.QueueEvent;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -61,6 +67,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
+import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
@@ -83,12 +90,11 @@ public class ItemlistFragment extends ListFragment {
protected FeedItemlistAdapter adapter;
private ContextMenu contextMenu;
+ private AdapterView.AdapterContextMenuInfo lastMenuInfo = null;
private long feedID;
private Feed feed;
private LongList queuedItemsIds;
- private LongList newItemsIds;
-
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
@@ -155,6 +161,7 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onResume() {
super.onResume();
+ Log.d(TAG, "onResume()");
updateProgressBarVisibility();
startItemLoader();
}
@@ -217,6 +224,18 @@ public class ItemlistFragment extends ListFragment {
return false;
}
});
+ if(feed == null || feed.getLink() == null) {
+ menu.findItem(R.id.share_link_item).setVisible(false);
+ menu.findItem(R.id.visit_website_item).setVisible(false);
+ }
+ int[] attrs = { android.R.attr.textColor };
+ TypedArray ta = getActivity().obtainStyledAttributes(attrs);
+ int textColor = ta.getColor(0, Color.GRAY);
+ ta.recycle();
+
+ menu.findItem(R.id.episode_actions).setIcon(new IconDrawable(getActivity(),
+ Iconify.IconValue.fa_gears).color(textColor).actionBarSize());
+
isUpdatingFeed = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
}
@@ -234,6 +253,10 @@ public class ItemlistFragment extends ListFragment {
try {
if (!FeedMenuHandler.onOptionsItemClicked(getActivity(), item, feed)) {
switch (item.getItemId()) {
+ case R.id.episode_actions:
+ Fragment fragment = new EpisodesApplyActionFragment(feed.getItems());
+ ((MainActivity)getActivity()).loadChildFragment(fragment);
+ return true;
case R.id.remove_item:
final FeedRemover remover = new FeedRemover(
getActivity(), feed) {
@@ -302,12 +325,16 @@ public class ItemlistFragment extends ListFragment {
}
contextMenu = menu;
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, queuedItemsIds);
+ lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queuedItemsIds);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+ if(menuInfo == null) {
+ menuInfo = lastMenuInfo;
+ }
// because of addHeaderView(), positions are increased by 1!
FeedItem selectedItem = itemAccess.getItem(menuInfo.position-1);
@@ -396,12 +423,15 @@ public class ItemlistFragment extends ListFragment {
private boolean insideOnFragmentLoaded = false;
private void onFragmentLoaded() {
+ if(!isVisible()) {
+ return;
+ }
insideOnFragmentLoaded = true;
if (adapter == null) {
setListAdapter(null);
setupHeaderView();
setupFooterView();
- adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false);
+ adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false, true);
setListAdapter(adapter);
downloadObserver = new DownloadObserver(getActivity(), new Handler(), downloadObserverCallback);
downloadObserver.onResume();
@@ -421,6 +451,10 @@ public class ItemlistFragment extends ListFragment {
}
private void refreshHeaderView() {
+ if (getListView() == null || feed == null) {
+ Log.e(TAG, "Unable to setup listview: listView = null or feed = null");
+ return;
+ }
if(feed.hasLastUpdateFailed()) {
txtvFailure.setVisibility(View.VISIBLE);
} else {
@@ -448,14 +482,7 @@ public class ItemlistFragment extends ListFragment {
private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
@Override
- public void onContentChanged() {
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
- }
-
- @Override
- public void onDownloadDataAvailable(List<Downloader> downloaderList) {
+ public void onContentChanged(List<Downloader> downloaderList) {
ItemlistFragment.this.downloaderList = downloaderList;
if (adapter != null) {
adapter.notifyDataSetChanged();
@@ -485,17 +512,26 @@ public class ItemlistFragment extends ListFragment {
txtvTitle.setText(feed.getTitle());
txtvAuthor.setText(feed.getAuthor());
- Picasso.with(getActivity())
+
+ // https://github.com/bumptech/glide/issues/529
+ imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
+
+ Glide.with(getActivity())
.load(feed.getImageUri())
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
- .transform(PicassoProvider.blurTransformation)
- .resize(PicassoProvider.BLUR_IMAGE_SIZE, PicassoProvider.BLUR_IMAGE_SIZE)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .transform(new FastBlurTransformation(getActivity()))
+ .dontAnimate()
.into(imgvBackground);
- Picasso.with(getActivity())
+ Glide.with(getActivity())
.load(feed.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(imgvCover);
butShowInfo.setOnClickListener(new View.OnClickListener() {
@@ -558,11 +594,6 @@ public class ItemlistFragment extends ListFragment {
}
@Override
- public boolean isNew(FeedItem item) {
- return (newItemsIds != null) && newItemsIds.contains(item.getId());
- }
-
- @Override
public int getItemDownloadProgressPercent(FeedItem item) {
if (downloaderList != null) {
for (Downloader downloader : downloaderList) {
@@ -599,13 +630,12 @@ public class ItemlistFragment extends ListFragment {
Context context = getActivity();
if (context != null) {
Feed feed = DBReader.getFeed(context, feedID);
- if(feed.getItemFilter() != null) {
+ if(feed != null && feed.getItemFilter() != null) {
FeedItemFilter filter = feed.getItemFilter();
feed.setItems(filter.filter(context, feed.getItems()));
}
LongList queuedItemsIds = DBReader.getQueueIDList(context);
- LongList newItemsIds = DBReader.getNewItemIds(context);
- return new Object[] { feed, queuedItemsIds, newItemsIds };
+ return new Object[] { feed, queuedItemsIds };
} else {
return null;
}
@@ -617,7 +647,6 @@ public class ItemlistFragment extends ListFragment {
if (res != null) {
feed = (Feed) res[0];
queuedItemsIds = (LongList) res[1];
- newItemsIds = res[2] == null ? null : (LongList) res[2];
itemsLoaded = true;
if (viewsCreated) {
onFragmentLoaded();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
index 16789d694..edd4da7fe 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
@@ -22,6 +22,8 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
@@ -31,7 +33,7 @@ import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.adapter.itunes.ItunesAdapter;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.*;
+import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast;
//Searches iTunes store for given string and displays results in a list
public class ItunesSearchFragment extends Fragment {
@@ -160,8 +162,18 @@ public class ItunesSearchFragment extends Fragment {
*
* @param query Search string
*/
- public SearchTask(String query){
- this.query = query;
+ public SearchTask(String query) {
+ String encodedQuery = null;
+ try {
+ encodedQuery = URLEncoder.encode(query, "UTF-8");
+ } catch(UnsupportedEncodingException e) {
+ // this won't ever be thrown
+ }
+ if(encodedQuery != null) {
+ this.query = encodedQuery;
+ } else {
+ this.query = query; // failsafe
+ }
}
//Get the podcast data
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 4bce3c7ba..9a25674b6 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -72,7 +72,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
Log.d(TAG, "remove(" + which + ")");
stopItemLoader();
FeedItem item = (FeedItem) listView.getAdapter().getItem(which);
- DBWriter.markItemRead(getActivity(), item.getId(), true);
+ DBWriter.markItemRead(getActivity(), true, item.getId());
undoBarController.showUndoBar(false,
getString(R.string.marked_as_read_label), new FeedItemUndoToken(item,
which)
@@ -88,7 +88,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
public void onUndo(FeedItemUndoToken token) {
if (token != null) {
long itemId = token.getFeedItemId();
- DBWriter.markItemRead(context, itemId, false);
+ DBWriter.markItemRead(context, false, itemId);
}
}
@Override
@@ -97,7 +97,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
long itemId = token.getFeedItemId();
FeedItem item = DBReader.getFeedItem(context, itemId);
FeedMedia media = item.getMedia();
- if(media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
+ if(media != null && media.hasAlmostEnded() && item.getFeed().getPreferences().getCurrentAutoDelete()) {
DBWriter.deleteFeedMediaOfItem(context, media.getId());
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
index 9099829d8..b094133d3 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -192,7 +192,10 @@ public class PlaybackHistoryFragment extends ListFragment {
private void onFragmentLoaded() {
if (adapter == null) {
- adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(activity.get()), true);
+ // played items shoudln't be transparent for this fragment since, *all* items
+ // in this fragment will, by definition, be played. So it serves no purpose and can make
+ // it harder to read.
+ adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(activity.get()), true, false);
setListAdapter(adapter);
downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback);
downloadObserver.onResume();
@@ -204,14 +207,7 @@ public class PlaybackHistoryFragment extends ListFragment {
private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
@Override
- public void onContentChanged() {
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
- }
-
- @Override
- public void onDownloadDataAvailable(List<Downloader> downloaderList) {
+ public void onContentChanged(List<Downloader> downloaderList) {
PlaybackHistoryFragment.this.downloaderList = downloaderList;
if (adapter != null) {
adapter.notifyDataSetChanged();
@@ -226,11 +222,6 @@ public class PlaybackHistoryFragment extends ListFragment {
}
@Override
- public boolean isNew(FeedItem item) {
- return false;
- }
-
- @Override
public int getItemDownloadProgressPercent(FeedItem item) {
if (downloaderList != null) {
for (Downloader downloader : downloaderList) {
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 d82c7b8f7..24c9fc425 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -47,6 +47,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.LongList;
import de.danoeh.antennapod.core.util.QueueSorter;
import de.danoeh.antennapod.core.util.gui.FeedItemUndoToken;
@@ -66,12 +67,14 @@ public class QueueFragment extends Fragment {
EventDistributor.DOWNLOAD_QUEUED |
EventDistributor.PLAYER_STATUS_UPDATE;
+ private TextView infoBar;
private DragSortListView listView;
private QueueListAdapter listAdapter;
private TextView txtvEmpty;
private ProgressBar progLoading;
private ContextMenu contextMenu;
+ private AdapterView.AdapterContextMenuInfo lastMenuInfo = null;
private UndoBarController<FeedItemUndoToken> undoBarController;
@@ -325,16 +328,20 @@ public class QueueFragment extends Fragment {
}
contextMenu = menu;
+ lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
LongList queueIds = new LongList(queue.size());
for(FeedItem queueItem : queue) {
queueIds.add(queueItem.getId());
}
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, queueIds);
+ FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queueIds);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+ if(menuInfo == null) {
+ menuInfo = lastMenuInfo;
+ }
FeedItem selectedItem = itemAccess.getItem(menuInfo.position);
if (selectedItem == null) {
@@ -358,6 +365,7 @@ public class QueueFragment extends Fragment {
((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.queue_label);
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);
@@ -426,7 +434,7 @@ public class QueueFragment extends Fragment {
long itemId = token.getFeedItemId();
FeedItem item = DBReader.getFeedItem(context, itemId);
FeedMedia media = item.getMedia();
- if(media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
+ if(media != null && media.hasAlmostEnded() && item.getFeed().getPreferences().getCurrentAutoDelete()) {
DBWriter.deleteFeedMediaOfItem(context, media.getId());
}
}
@@ -464,18 +472,25 @@ public class QueueFragment extends Fragment {
// we need to refresh the options menu because it sometimes
// needs data that may have just been loaded.
getActivity().supportInvalidateOptionsMenu();
- }
- private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
- @Override
- public void onContentChanged() {
- if (listAdapter != null && !blockDownloadObserverUpdate) {
- listAdapter.notifyDataSetChanged();
+ // refresh information bar
+ String info = queue.size() + getString(R.string.episodes_suffix);
+ if(queue.size() > 0) {
+ long duration = 0;
+ for(FeedItem item : queue) {
+ if(item.getMedia() != null) {
+ duration += item.getMedia().getDuration();
+ }
}
+ info += " \u2022 ";
+ info += Converter.getDurationStringLocalized(getActivity(), duration);
}
+ infoBar.setText(info);
+ }
+ private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
@Override
- public void onDownloadDataAvailable(List<Downloader> downloaderList) {
+ public void onContentChanged(List<Downloader> downloaderList) {
QueueFragment.this.downloaderList = downloaderList;
if (listAdapter != null && !blockDownloadObserverUpdate) {
listAdapter.notifyDataSetChanged();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
index b1b61f74b..eb4d18328 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.fragment;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.ListFragment;
+import android.util.Log;
import android.view.View;
import android.widget.ListView;
import android.widget.Toast;
@@ -24,7 +25,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequester;
* Displays all running downloads and provides actions to cancel them
*/
public class RunningDownloadsFragment extends ListFragment {
- private static final String TAG = "RunningDownloadsFragment";
+ private static final String TAG = "RunningDownloadsFrag";
private DownloadObserver downloadObserver;
private List<Downloader> downloaderList;
@@ -53,12 +54,8 @@ public class RunningDownloadsFragment extends ListFragment {
downloadObserver = new DownloadObserver(getActivity(), new Handler(), new DownloadObserver.Callback() {
@Override
- public void onContentChanged() {
- downloadlistAdapter.notifyDataSetChanged();
- }
-
- @Override
- public void onDownloadDataAvailable(List<Downloader> downloaderList) {
+ public void onContentChanged(List<Downloader> downloaderList) {
+ Log.d(TAG, "onContentChanged: downloaderList.size() == " + downloaderList.size());
RunningDownloadsFragment.this.downloaderList = downloaderList;
downloadlistAdapter.notifyDataSetChanged();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
index fc6225409..975493ce9 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -120,7 +120,7 @@ public class SearchFragment extends ListFragment {
SearchResult result = (SearchResult) l.getAdapter().getItem(position);
FeedComponent comp = result.getComponent();
if (comp.getClass() == Feed.class) {
- ((MainActivity) getActivity()).loadFeedFragmentById(comp.getId());
+ ((MainActivity) getActivity()).loadFeedFragmentById(comp.getId(), null);
} else {
if (comp.getClass() == FeedItem.class) {
FeedItem item = (FeedItem) comp;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
index 6139a4901..623c6faa7 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
@@ -62,7 +62,10 @@ public abstract class PodcastListFragment extends Fragment {
@Override
public boolean onQueryTextSubmit(String s) {
sv.clearFocus();
- ((MainActivity) getActivity()).loadChildFragment(SearchListFragment.newInstance(s));
+ MainActivity activity = (MainActivity)getActivity();
+ if (activity != null) {
+ activity.loadChildFragment(SearchListFragment.newInstance(s));
+ }
return true;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
index fe1a09149..3e2fdf24f 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -4,6 +4,7 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
+import android.widget.Toast;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -16,7 +17,7 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
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.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.ShareUtils;
@@ -56,7 +57,7 @@ public class FeedItemMenuHandler {
* @param queueAccess Used for testing if the queue contains the selected item
* @return Returns true if selectedItem is not null.
*/
- public static boolean onPrepareMenu(MenuInterface mi, FeedItem selectedItem,
+ public static boolean onPrepareMenu(Context context, MenuInterface mi, FeedItem selectedItem,
boolean showExtendedMenu, LongList queueAccess) {
if (selectedItem == null) {
return false;
@@ -84,8 +85,19 @@ public class FeedItemMenuHandler {
if (!(!isInQueue && selectedItem.getMedia() != null)) {
mi.setItemVisibility(R.id.add_to_queue_item, false);
}
+
if (!showExtendedMenu || selectedItem.getLink() == null) {
+ mi.setItemVisibility(R.id.visit_website_item, false);
mi.setItemVisibility(R.id.share_link_item, false);
+ mi.setItemVisibility(R.id.share_link_with_position_item, false);
+ }
+ if (!showExtendedMenu || !hasMedia || selectedItem.getMedia().getDownload_url() == null) {
+ mi.setItemVisibility(R.id.share_download_url_item, false);
+ mi.setItemVisibility(R.id.share_download_url_with_position_item, false);
+ }
+ if(false == hasMedia || selectedItem.getMedia().getPosition() <= 0) {
+ mi.setItemVisibility(R.id.share_link_with_position_item, false);
+ mi.setItemVisibility(R.id.share_download_url_with_position_item, false);
}
if (!(state == FeedItem.State.UNREAD || state == FeedItem.State.IN_PROGRESS)) {
@@ -108,10 +120,6 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.deactivate_auto_download, false);
}
- if (!showExtendedMenu || selectedItem.getLink() == null) {
- mi.setItemVisibility(R.id.visit_website_item, false);
- }
-
if (selectedItem.getPaymentLink() == null || !selectedItem.getFlattrStatus().flattrable()) {
mi.setItemVisibility(R.id.support_item, false);
}
@@ -125,21 +133,19 @@ public class FeedItemMenuHandler {
* @param excludeIds Menu item that should be excluded
* @return true if selectedItem is not null.
*/
- public static boolean onPrepareMenu(MenuInterface mi,
- FeedItem selectedItem, boolean showExtendedMenu, LongList queueAccess, int... excludeIds) {
- boolean rc = onPrepareMenu(mi, selectedItem, showExtendedMenu, queueAccess);
+ public static boolean onPrepareMenu(Context context, MenuInterface mi, FeedItem selectedItem,
+ boolean showExtendedMenu, LongList queueAccess, int... excludeIds) {
+ boolean rc = onPrepareMenu(context, mi, selectedItem, showExtendedMenu, queueAccess);
if (rc && excludeIds != null) {
for (int id : excludeIds) {
mi.setItemVisibility(id, false);
}
}
-
return rc;
}
public static boolean onMenuItemClicked(Context context, int menuItemId,
FeedItem selectedItem) throws DownloadRequestException {
- DownloadRequester requester = DownloadRequester.getInstance();
switch (menuItemId) {
case R.id.skip_episode_item:
context.sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
@@ -148,22 +154,25 @@ public class FeedItemMenuHandler {
DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId());
break;
case R.id.mark_read_item:
- selectedItem.setRead(true);
+ selectedItem.setPlayed(true);
DBWriter.markItemRead(context, selectedItem, true, false);
if(GpodnetPreferences.loggedIn()) {
FeedMedia media = selectedItem.getMedia();
- GpodnetEpisodeAction actionPlay = new GpodnetEpisodeAction.Builder(selectedItem, Action.PLAY)
- .currentDeviceId()
- .currentTimestamp()
- .started(media.getDuration() / 1000)
- .position(media.getDuration() / 1000)
- .total(media.getDuration() / 1000)
- .build();
- GpodnetPreferences.enqueueEpisodeAction(actionPlay);
+ // not all items have media, Gpodder only cares about those that do
+ if (media != null) {
+ GpodnetEpisodeAction actionPlay = new GpodnetEpisodeAction.Builder(selectedItem, Action.PLAY)
+ .currentDeviceId()
+ .currentTimestamp()
+ .started(media.getDuration() / 1000)
+ .position(media.getDuration() / 1000)
+ .total(media.getDuration() / 1000)
+ .build();
+ GpodnetPreferences.enqueueEpisodeAction(actionPlay);
+ }
}
break;
case R.id.mark_unread_item:
- selectedItem.setRead(false);
+ selectedItem.setPlayed(false);
DBWriter.markItemRead(context, selectedItem, false, false);
if(GpodnetPreferences.loggedIn()) {
GpodnetEpisodeAction actionNew = new GpodnetEpisodeAction.Builder(selectedItem, Action.NEW)
@@ -198,7 +207,13 @@ public class FeedItemMenuHandler {
break;
case R.id.visit_website_item:
Uri uri = Uri.parse(selectedItem.getLink());
- context.startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(context, intent)) {
+ context.startActivity(intent);
+ } else {
+ Toast.makeText(context, context.getString(R.string.download_error_malformed_url),
+ Toast.LENGTH_SHORT);
+ }
break;
case R.id.support_item:
DBTasks.flattrItemIfLoggedIn(context, selectedItem);
@@ -206,6 +221,15 @@ public class FeedItemMenuHandler {
case R.id.share_link_item:
ShareUtils.shareFeedItemLink(context, selectedItem);
break;
+ case R.id.share_download_url_item:
+ ShareUtils.shareFeedItemDownloadLink(context, selectedItem);
+ break;
+ case R.id.share_link_with_position_item:
+ ShareUtils.shareFeedItemLink(context, selectedItem, true);
+ break;
+ case R.id.share_download_url_with_position_item:
+ ShareUtils.shareFeedItemDownloadLink(context, selectedItem, true);
+ break;
default:
Log.d(TAG, "Unknown menuItemId: " + menuItemId);
return false;
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
index 7bd8fedc9..3df59724d 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -9,6 +9,7 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.widget.Toast;
import java.util.ArrayList;
import java.util.Arrays;
@@ -20,6 +21,7 @@ import de.danoeh.antennapod.core.feed.Feed;
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.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
/**
@@ -39,11 +41,11 @@ public class FeedMenuHandler {
}
Log.d(TAG, "Preparing options menu");
- menu.findItem(R.id.mark_all_read_item).setVisible(selectedFeed.hasNewItems());
- if (selectedFeed.getPaymentLink() != null && selectedFeed.getFlattrStatus().flattrable())
+ if (selectedFeed.getPaymentLink() != null && selectedFeed.getFlattrStatus().flattrable()) {
menu.findItem(R.id.support_item).setVisible(true);
- else
+ } else {
menu.findItem(R.id.support_item).setVisible(false);
+ }
menu.findItem(R.id.refresh_complete_item).setVisible(selectedFeed.isPaged());
@@ -83,7 +85,13 @@ public class FeedMenuHandler {
break;
case R.id.visit_website_item:
Uri uri = Uri.parse(selectedFeed.getLink());
- context.startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(context, intent)) {
+ context.startActivity(intent);
+ } else {
+ Toast.makeText(context, context.getString(R.string.download_error_malformed_url),
+ Toast.LENGTH_SHORT);
+ }
break;
case R.id.support_item:
DBTasks.flattrFeedIfLoggedIn(context, selectedFeed);
@@ -91,7 +99,7 @@ public class FeedMenuHandler {
case R.id.share_link_item:
ShareUtils.shareFeedlink(context, selectedFeed);
break;
- case R.id.share_source_item:
+ case R.id.share_download_url_item:
ShareUtils.shareFeedDownloadLink(context, selectedFeed);
break;
default:
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 f387b7524..39edfe582 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.preferences;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -16,8 +17,10 @@ import android.preference.Preference;
import android.preference.PreferenceScreen;
import android.text.Editable;
import android.text.TextWatcher;
+import android.text.format.DateFormat;
import android.util.Log;
import android.widget.EditText;
+import android.widget.TimePicker;
import android.widget.Toast;
import java.io.File;
@@ -33,12 +36,9 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.activity.PreferenceActivityGingerbread;
import de.danoeh.antennapod.asynctask.OpmlExportWorker;
-import de.danoeh.antennapod.core.asynctask.FlattrClickWorker;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
-import de.danoeh.antennapod.core.util.flattr.SimpleFlattrThing;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog;
import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
@@ -49,7 +49,6 @@ import de.danoeh.antennapod.dialog.VariableSpeedDialog;
*/
public class PreferenceController {
private static final String TAG = "PreferenceController";
- public static final String PREF_FLATTR_THIS_APP = "prefFlattrThisApp";
public static final String PREF_FLATTR_SETTINGS = "prefFlattrSettings";
public static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate";
public static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
@@ -105,23 +104,6 @@ public class PreferenceController {
);
}
- ui.findPreference(PreferenceController.PREF_FLATTR_THIS_APP).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- new FlattrClickWorker(activity,
- new SimpleFlattrThing(activity.getString(R.string.app_name),
- FlattrUtils.APP_URL,
- new FlattrStatus(FlattrStatus.STATUS_QUEUE)
- )
- ).executeAsync();
-
- return true;
- }
- }
- );
-
ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() {
@@ -200,6 +182,15 @@ public class PreferenceController {
}
});
+ ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL)
+ .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ showUpdateIntervalTimePreferencesDialog();
+ return true;
+ }
+ });
+
ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL)
.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
@@ -345,14 +336,33 @@ public class PreferenceController {
@Override
public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) {
- UserPreferences.setAutoFlattrSettings(activity, autoFlattrEnabled, autoFlattrValue);
+ UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue);
checkItemVisibility();
}
});
return true;
}
});
- buildUpdateIntervalPreference();
+ ui.findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE)
+ .setOnPreferenceChangeListener(
+ new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object o) {
+ if (o instanceof String) {
+ int newValue = Integer.valueOf((String) o) * 1024 * 1024;
+ if(newValue != UserPreferences.getImageCacheSize()) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(ui.getActivity());
+ dialog.setTitle(android.R.string.dialog_alert_title);
+ dialog.setMessage(R.string.pref_restart_required);
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.show();
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+ );
buildSmartMarkAsPlayedPreference();
buildAutodownloadSelectedNetworsPreference();
setSelectedNetworksEnabled(UserPreferences
@@ -385,12 +395,8 @@ public class PreferenceController {
ui.findPreference(PreferenceController.PREF_GPODNET_HOSTNAME).setSummary(GpodnetPreferences.getHostname());
}
- private void buildUpdateIntervalPreference() {
+ private String[] getUpdateIntervalEntries(final String[] values) {
final Resources res = ui.getActivity().getResources();
-
- ListPreference pref = (ListPreference) ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL);
- String[] values = res.getStringArray(
- R.array.update_intervall_values);
String[] entries = new String[values.length];
for (int x = 0; x < values.length; x++) {
Integer v = Integer.parseInt(values[x]);
@@ -399,19 +405,15 @@ public class PreferenceController {
entries[x] = res.getString(R.string.pref_update_interval_hours_manual);
break;
case 1:
- entries[x] = v
- + " "
- + res.getString(R.string.pref_update_interval_hours_singular);
+ entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_singular);
break;
default:
- entries[x] = v + " "
- + res.getString(R.string.pref_update_interval_hours_plural);
+ entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_plural);
break;
}
}
- pref.setEntries(entries);
-
+ return entries;
}
private void buildSmartMarkAsPlayedPreference() {
@@ -426,7 +428,7 @@ public class PreferenceController {
entries[x] = res.getString(R.string.pref_smart_mark_as_played_disabled);
} else {
Integer v = Integer.parseInt(values[x]);
- entries[x] = v + " " + res.getString(R.string.time_unit_seconds);
+ entries[x] = res.getQuantityString(R.plurals.time_seconds_quantified, v);
}
}
pref.setEntries(entries);
@@ -529,9 +531,7 @@ public class PreferenceController {
}
UserPreferences.setAutodownloadSelectedNetworks(
- activity, prefValuesList
- .toArray(new String[prefValuesList
- .size()])
+ prefValuesList.toArray(new String[prefValuesList.size()])
);
return true;
} else {
@@ -606,7 +606,63 @@ public class PreferenceController {
builder.create().show();
}
+ private void showUpdateIntervalTimePreferencesDialog() {
+ final Context context = ui.getActivity();
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.pref_autoUpdateIntervallOrTime_title);
+ builder.setMessage(R.string.pref_autoUpdateIntervallOrTime_message);
+ builder.setNegativeButton(R.string.pref_autoUpdateIntervallOrTime_Disable, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ UserPreferences.setUpdateInterval(0);
+ }
+ });
+ builder.setNeutralButton(R.string.pref_autoUpdateIntervallOrTime_Interval, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
+ final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
+ final String[] entries = getUpdateIntervalEntries(values);
+ builder.setSingleChoiceItems(entries, -1, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int hours = Integer.valueOf(values[which]);
+ UserPreferences.setUpdateInterval(hours);
+ dialog.dismiss();
+ }
+ });
+ builder.setNegativeButton(context.getString(R.string.cancel_label), null);
+ builder.show();
+ }
+ });
+ builder.setPositiveButton(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int hourOfDay = 7, minute = 0;
+ int[] updateTime = UserPreferences.getUpdateTimeOfDay();
+ if (updateTime.length == 2) {
+ hourOfDay = updateTime[0];
+ minute = updateTime[1];
+ }
+ TimePickerDialog timePickerDialog = new TimePickerDialog(context, new TimePickerDialog.OnTimeSetListener() {
+ @Override
+ public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
+ if (view.getTag() == null) { // onTimeSet() may get called twice!
+ view.setTag("TAGGED");
+ UserPreferences.setUpdateTimeOfDay(hourOfDay, minute);
+ }
+ }
+ }, hourOfDay, minute, DateFormat.is24HourFormat(context));
+ timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
+ timePickerDialog.show();
+ }
+ }
+ );
+ builder.show();
+ }
public static interface PreferenceUI {
diff --git a/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java b/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
index f55a7603f..2615ec5c8 100644
--- a/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
+++ b/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
@@ -9,24 +9,20 @@ import android.util.Log;
import org.apache.commons.lang3.StringUtils;
-import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.NetworkUtils;
public class ConnectivityActionReceiver extends BroadcastReceiver {
- private static final String TAG = "ConnectivityActionReceiver";
+ private static final String TAG = "ConnectivityActionRecvr";
@Override
public void onReceive(final Context context, Intent intent) {
if (StringUtils.equals(intent.getAction(), ConnectivityManager.CONNECTIVITY_ACTION)) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Received intent");
+ Log.d(TAG, "Received intent");
- if (NetworkUtils.autodownloadNetworkAvailable(context)) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "auto-dl network available, starting auto-download");
+ if (NetworkUtils.autodownloadNetworkAvailable()) {
+ Log.d(TAG, "auto-dl network available, starting auto-download");
DBTasks.autodownloadUndownloadedItems(context);
} else { // if new network is Wi-Fi, finish ongoing downloads,
// otherwise cancel all downloads
@@ -34,12 +30,9 @@ public class ConnectivityActionReceiver extends BroadcastReceiver {
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
if (ni == null || ni.getType() != ConnectivityManager.TYPE_WIFI) {
- if (BuildConfig.DEBUG)
- Log.i(TAG,
- "Device is no longer connected to Wi-Fi. Cancelling ongoing downloads");
+ Log.i(TAG, "Device is no longer connected to Wi-Fi. Cancelling ongoing downloads");
DownloadRequester.getInstance().cancelAllDownloads(context);
}
-
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
index 1fe9e2cf9..990b3bd54 100644
--- a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
+++ b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
@@ -62,7 +62,7 @@ public class PlayerWidgetService extends Service {
DBWriter.markItemRead(this, item, true, false);
DBWriter.removeQueueItem(this, item, false);
DBWriter.addItemToPlaybackHistory(this, media);
- if (UserPreferences.isAutoDelete()) {
+ if (item.getFeed().getPreferences().getCurrentAutoDelete()) {
Log.d(TAG, "Delete " + media.toString());
DBWriter.deleteFeedMediaOfItem(this, media.getId());
}
diff --git a/app/src/main/res/layout-v14/authentication_dialog.xml b/app/src/main/res/layout-v14/authentication_dialog.xml
index ed05dab1c..00e74c9e1 100644
--- a/app/src/main/res/layout-v14/authentication_dialog.xml
+++ b/app/src/main/res/layout-v14/authentication_dialog.xml
@@ -15,7 +15,10 @@
android:layout_height="0dp"
android:layout_weight="1"
android:layout_margin="16dp"
- android:hint="@string/username_label"/>
+ android:hint="@string/username_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<EditText
android:id="@+id/etxtPassword"
@@ -24,7 +27,10 @@
android:layout_weight="1"
android:layout_margin="16dp"
android:inputType="textPassword"
- android:hint="@string/password_label"/>
+ android:hint="@string/password_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<CheckBox
android:id="@+id/chkSaveUsernamePassword"
diff --git a/app/src/main/res/layout-v14/download_authentication_activity.xml b/app/src/main/res/layout-v14/download_authentication_activity.xml
index c1fe55ceb..8d2c7fb17 100644
--- a/app/src/main/res/layout-v14/download_authentication_activity.xml
+++ b/app/src/main/res/layout-v14/download_authentication_activity.xml
@@ -31,7 +31,10 @@
android:layout_margin="16dp"
android:id="@+id/etxtUsername"
android:hint="@string/username_label"
- android:layout_below="@id/txtvDescription"/>
+ android:layout_below="@id/txtvDescription"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<EditText
android:layout_width="match_parent"
@@ -40,7 +43,10 @@
android:id="@+id/etxtPassword"
android:hint="@string/password_label"
android:inputType="textPassword"
- android:layout_below="@id/etxtUsername"/>
+ android:layout_below="@id/etxtUsername"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<RelativeLayout
android:id="@+id/footer"
diff --git a/app/src/main/res/layout/addfeed.xml b/app/src/main/res/layout/addfeed.xml
index b7babbafa..dff24c650 100644
--- a/app/src/main/res/layout/addfeed.xml
+++ b/app/src/main/res/layout/addfeed.xml
@@ -15,26 +15,33 @@
android:orientation="vertical">
<TextView
- android:id="@+id/txtvFeedurl"
+ android:id="@+id/txtvPodcastDirectories"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/AntennaPod.TextView.Heading"
- android:text="@string/txtvfeedurl_label"/>
+ android:text="@string/podcastdirectories_label"/>
- <EditText
- android:id="@+id/etxtFeedurl"
+ <TextView
+ android:id="@+id/txtvPodcastDirectoriesDescr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
- android:hint="@string/etxtFeedurlHint"
- android:inputType="textUri"/>
+ android:text="@string/podcastdirectories_descr"
+ android:textSize="@dimen/text_size_medium"
+ android:layout_marginTop="4dp"/>
<Button
- android:id="@+id/butConfirm"
+ android:id="@+id/butSearchItunes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- android:text="@string/confirm_label"/>
+ android:text="@string/search_itunes_label"/>
+
+ <Button
+ android:id="@+id/butBrowseGpoddernet"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/browse_gpoddernet_label"/>
<View
android:id="@+id/divider1"
@@ -44,34 +51,30 @@
android:background="?android:attr/listDivider"/>
<TextView
- android:id="@+id/txtvPodcastDirectories"
+ android:id="@+id/txtvFeedurl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/divider1"
style="@style/AntennaPod.TextView.Heading"
- android:text="@string/podcastdirectories_label"/>
-
- <TextView
- android:id="@+id/txtvPodcastDirectoriesDescr"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/podcastdirectories_descr"
- android:textSize="@dimen/text_size_medium"
- android:layout_marginTop="4dp"/>
+ android:text="@string/txtvfeedurl_label"/>
- <Button
- android:id="@+id/butBrowseGpoddernet"
+ <EditText
+ android:id="@+id/etxtFeedurl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="@string/browse_gpoddernet_label"/>
+ android:layout_marginTop="4dp"
+ android:hint="@string/etxtFeedurlHint"
+ android:inputType="textUri"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<Button
- android:id="@+id/butSearchItunes"
+ android:id="@+id/butConfirm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- android:text="@string/search_itunes_label"/>
+ android:text="@string/confirm_label"/>
<View
android:id="@+id/divider2"
diff --git a/app/src/main/res/layout/authentication_dialog.xml b/app/src/main/res/layout/authentication_dialog.xml
index 82260eb43..e18ab42eb 100644
--- a/app/src/main/res/layout/authentication_dialog.xml
+++ b/app/src/main/res/layout/authentication_dialog.xml
@@ -16,7 +16,10 @@
android:layout_height="0dp"
android:layout_weight="1"
android:layout_margin="16dp"
- android:hint="@string/username_label"/>
+ android:hint="@string/username_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<EditText
android:id="@+id/etxtPassword"
@@ -25,7 +28,10 @@
android:layout_weight="1"
android:layout_margin="16dp"
android:inputType="textPassword"
- android:hint="@string/password_label"/>
+ android:hint="@string/password_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<CheckBox
android:id="@+id/chkSaveUsernamePassword"
diff --git a/app/src/main/res/layout/cover_fragment.xml b/app/src/main/res/layout/cover_fragment.xml
index 18540aa1f..4bbdeae06 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="centerInside"
+ android:scaleType="fitCenter"
tools:src="@android:drawable/sym_def_app_icon" />
-</RelativeLayout> \ No newline at end of file
+</RelativeLayout>
diff --git a/app/src/main/res/layout/download_authentication_activity.xml b/app/src/main/res/layout/download_authentication_activity.xml
index 69106c9b3..b035f2516 100644
--- a/app/src/main/res/layout/download_authentication_activity.xml
+++ b/app/src/main/res/layout/download_authentication_activity.xml
@@ -31,7 +31,10 @@
android:layout_margin="16dp"
android:id="@+id/etxtUsername"
android:hint="@string/username_label"
- android:layout_below="@id/txtvDescription"/>
+ android:layout_below="@id/txtvDescription"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<EditText
android:layout_width="match_parent"
@@ -40,7 +43,10 @@
android:id="@+id/etxtPassword"
android:hint="@string/password_label"
android:inputType="textPassword"
- android:layout_below="@id/etxtUsername"/>
+ android:layout_below="@id/etxtUsername"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<LinearLayout
android:id="@+id/footer"
diff --git a/app/src/main/res/layout/episodes_apply_action_fragment.xml b/app/src/main/res/layout/episodes_apply_action_fragment.xml
new file mode 100644
index 000000000..d63088662
--- /dev/null
+++ b/app/src/main/res/layout/episodes_apply_action_fragment.xml
@@ -0,0 +1,120 @@
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+
+ <LinearLayout
+ android:id="@+id/bottomBar"
+ android:layout_width="wrap_content"
+ android:layout_height="68dp"
+ android:layout_alignParentBottom="true"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:padding="4dp">
+
+ <Button
+ android:id="@+id/btnAddToQueue"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableTop="?attr/content_new"
+ android:text="@string/add_to_queue_label"
+ android:textSize="10sp"
+ android:background="@android:color/transparent"/>
+
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_margin="4dp"
+ android:background="?android:attr/listDivider"
+ tools:background="@android:color/holo_red_dark" />
+
+ <Button
+ android:id="@+id/btnMarkAsPlayed"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableTop="?attr/navigation_accept"
+ android:text="@string/mark_read_label"
+ android:textSize="10sp"
+ android:background="@android:color/transparent"/>
+
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_margin="4dp"
+ android:background="?android:attr/listDivider"
+ tools:background="@android:color/holo_red_dark" />
+
+ <Button
+ android:id="@+id/btnMarkAsUnplayed"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableTop="?attr/navigation_cancel"
+ android:text="@string/mark_unread_label"
+ android:textSize="10sp"
+ android:background="@android:color/transparent"/>
+
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_margin="4dp"
+ android:background="?android:attr/listDivider"
+ tools:background="@android:color/holo_red_dark" />
+
+ <Button
+ android:id="@+id/btnDownload"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableTop="?attr/av_download"
+ android:text="@string/download_label"
+ android:textSize="10sp"
+ android:background="@android:color/transparent"/>
+
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_margin="4dp"
+ android:background="?android:attr/listDivider"
+ tools:background="@android:color/holo_red_dark" />
+
+ <Button
+ android:id="@+id/btnDelete"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableTop="?attr/content_discard"
+ android:text="@string/remove_episode_lable"
+ android:textSize="10sp"
+ android:background="@android:color/transparent"/>
+
+ </LinearLayout>
+
+ <View
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/listDivider"
+ android:paddingBottom="4dp"
+ android:layout_above="@id/bottomBar"
+ tools:background="@android:color/holo_red_dark" />
+
+ <ListView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_above="@id/divider">
+
+ </ListView>
+
+
+</RelativeLayout>
diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml
index db897865c..edae51847 100644
--- a/app/src/main/res/layout/feedinfo.xml
+++ b/app/src/main/res/layout/feedinfo.xml
@@ -20,8 +20,8 @@
<ImageView
android:id="@+id/imgvCover"
android:contentDescription="@string/cover_label"
- android:layout_width="70dp"
- android:layout_height="70dp"
+ android:layout_width="80dp"
+ android:layout_height="80dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
tools:src="@drawable/ic_stat_antenna_default"
@@ -33,7 +33,9 @@
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
+ android:layout_alignTop="@id/imgvCover"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_alignBottom="@id/imgvCover"
style="@style/AntennaPod.TextView.Heading"
tools:text="Feed title"
tools:background="@android:color/holo_green_dark"/>
@@ -52,7 +54,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:scrollbarStyle="outsideInset"
+ android:scrollbarStyle="outsideOverlay"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingBottom="8dp">
@@ -119,7 +121,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
- android:layout_marginBottom="8dp"
app:layout_row="2"
app:layout_column="0"
android:lines="1"
@@ -129,10 +130,13 @@
<TextView
android:id="@+id/txtvUrl"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:paddingBottom="4dp"
app:layout_row="2"
app:layout_column="1"
+ app:layout_gravity="fill"
+ android:maxLines="4"
tools:text="http://www.example.com/feed"
tools:background="@android:color/holo_green_dark"/>
@@ -155,7 +159,38 @@
android:enabled="false"
android:textColor="?android:attr/textColorPrimary"
tools:background="@android:color/holo_red_light"
- android:checked="false"/>
+ android:checked="false" />
+
+ <android.support.v7.widget.GridLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ app:columnCount="2"
+ app:rowCount="1">
+
+ <TextView
+ android:id="@+id/txtvFeedAutoDelete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/auto_delete_label"
+ app:layout_row="0"
+ app:layout_column="0"
+ app:layout_gravity="center_vertical"
+ android:layout_marginRight="10dp" />
+
+ <Spinner
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:id="@+id/spnAutoDelete"
+ android:entries="@array/spnAutoDeleteItems"
+ android:layout_marginTop="8dp"
+ app:layout_row="0"
+ app:layout_column="1"
+ android:spinnerMode="dropdown"
+ app:layout_gravity="center"
+ android:dropDownWidth="wrap_content"
+ android:clickable="true" />
+ </android.support.v7.widget.GridLayout>
<TextView
android:id="@+id/txtvAuthentication"
@@ -200,7 +235,10 @@
android:layout_height="wrap_content"
app:layout_row="0"
app:layout_column="1"
- android:hint="@string/username_label"/>
+ android:hint="@string/username_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<TextView
android:id="@+id/txtvPassword"
@@ -211,7 +249,10 @@
app:layout_row="1"
app:layout_column="0"
android:text="@string/password_label"
- android:textColor="?android:attr/textColorPrimary"/>
+ android:textColor="?android:attr/textColorPrimary"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<EditText
android:id="@+id/etxtPassword"
diff --git a/app/src/main/res/layout/feeditem_fragment_header.xml b/app/src/main/res/layout/feeditem_fragment_header.xml
index a21488306..2534dddbe 100644
--- a/app/src/main/res/layout/feeditem_fragment_header.xml
+++ b/app/src/main/res/layout/feeditem_fragment_header.xml
@@ -89,10 +89,9 @@
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
- android:visibility="gone" />
+ android:visibility="invisible" />
<LinearLayout
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/fragment_itunes_search.xml b/app/src/main/res/layout/fragment_itunes_search.xml
index 17ffe349b..e57c59554 100644
--- a/app/src/main/res/layout/fragment_itunes_search.xml
+++ b/app/src/main/res/layout/fragment_itunes_search.xml
@@ -14,7 +14,7 @@ tools:context="de.danoeh.antennapod.activity.ITunesSearchActivity">
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
- android:columnWidth="200dp"
+ android:columnWidth="400dp"
android:gravity="center"
android:horizontalSpacing="8dp"
android:numColumns="auto_fit"
diff --git a/app/src/main/res/layout/gpodnet_podcast_list.xml b/app/src/main/res/layout/gpodnet_podcast_list.xml
index 05df70f31..d8b06e4f7 100644
--- a/app/src/main/res/layout/gpodnet_podcast_list.xml
+++ b/app/src/main/res/layout/gpodnet_podcast_list.xml
@@ -10,7 +10,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
- android:columnWidth="200dp"
+ android:columnWidth="400dp"
android:gravity="center"
android:horizontalSpacing="8dp"
android:numColumns="auto_fit"
diff --git a/app/src/main/res/layout/gpodnetauth_credentials.xml b/app/src/main/res/layout/gpodnetauth_credentials.xml
index 8436570fc..13c5977f8 100644
--- a/app/src/main/res/layout/gpodnetauth_credentials.xml
+++ b/app/src/main/res/layout/gpodnetauth_credentials.xml
@@ -30,7 +30,10 @@
android:layout_height="wrap_content"
android:hint="@string/username_label"
android:layout_below="@id/txtvDescription"
- android:layout_margin="8dp"/>
+ android:layout_margin="8dp"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<EditText
android:id="@+id/etxtPassword"
@@ -39,7 +42,10 @@
android:hint="@string/password_label"
android:layout_below="@id/etxtUsername"
android:inputType="textPassword"
- android:layout_margin="8dp"/>
+ android:layout_margin="8dp"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<Button
android:id="@+id/butLogin"
diff --git a/app/src/main/res/layout/new_episodes_listitem.xml b/app/src/main/res/layout/new_episodes_listitem.xml
index ff1318fc8..ec5ab1e6a 100644
--- a/app/src/main/res/layout/new_episodes_listitem.xml
+++ b/app/src/main/res/layout/new_episodes_listitem.xml
@@ -7,18 +7,36 @@
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
- <ImageView
- android:id="@+id/imgvImage"
- android:layout_width="@dimen/thumbnail_length_itemlist"
- android:layout_height="@dimen/thumbnail_length_itemlist"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
- android:contentDescription="@string/cover_label"
- android:scaleType="centerCrop"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark" />
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/txtvPlaceholder"
+ android:layout_width="@dimen/thumbnail_length_itemlist"
+ android:layout_height="@dimen/thumbnail_length_itemlist"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
+ android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
+ android:background="@color/light_gray"
+ android:ellipsize="end"
+ android:maxLines="3"
+ android:gravity="center"/>
+
+ <ImageView
+ android:id="@+id/imgvCover"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_alignLeft="@id/txtvPlaceholder"
+ android:layout_alignTop="@id/txtvPlaceholder"
+ android:layout_alignRight="@id/txtvPlaceholder"
+ android:layout_alignBottom="@id/txtvPlaceholder"
+ android:contentDescription="@string/cover_label"
+ tools:src="@drawable/ic_stat_antenna_default"
+ tools:background="@android:color/holo_green_dark" />
+
+ </RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
diff --git a/app/src/main/res/layout/queue_fragment.xml b/app/src/main/res/layout/queue_fragment.xml
index 307d95a8d..339369971 100644
--- a/app/src/main/res/layout/queue_fragment.xml
+++ b/app/src/main/res/layout/queue_fragment.xml
@@ -1,18 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<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">
+ <TextView
+ android:id="@+id/info_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:gravity="center"
+ android:textSize="12sp"
+ android:text="42 episodes \u2022 5 hours 17 minutes"/>
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_below="@id/info_bar"
+ android:background="?android:attr/listDivider"/>
+
<com.mobeta.android.dslv.DragSortListView
android:id="@android:id/list"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:clipToPadding="false"
- android:paddingBottom="@dimen/list_vertical_padding"
- android:paddingTop="@dimen/list_vertical_padding"
android:scrollbarStyle="outsideOverlay"
+ android:layout_below="@+id/divider"
dslv:collapsed_height="2dp"
dslv:drag_enabled="true"
dslv:drag_handle_id="@id/drag_handle"
@@ -31,7 +46,7 @@
android:id="@id/android:empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center"
+ android:layout_centerInParent="true"
android:gravity="center"
android:text="@string/no_items_label" />
@@ -39,12 +54,13 @@
android:id="@+id/progLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center"
+ android:layout_centerInParent="true"
android:indeterminateOnly="true"
android:visibility="gone" />
<LinearLayout
android:id="@+id/undobar"
+ android:layout_alignParentBottom="true"
style="@style/UndoBar">
<TextView
@@ -57,4 +73,4 @@
</LinearLayout>
-</FrameLayout>
+</RelativeLayout>
diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml
index 39e9c72a5..38076ff51 100644
--- a/app/src/main/res/layout/queue_listitem.xml
+++ b/app/src/main/res/layout/queue_listitem.xml
@@ -21,17 +21,32 @@
tools:src="@drawable/ic_drag_handle"
tools:background="@android:color/holo_green_dark" />
- <ImageView
- android:id="@+id/imgvImage"
- android:layout_width="@dimen/thumbnail_length_queue_item"
- android:layout_height="@dimen/thumbnail_length_queue_item"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
- android:contentDescription="@string/cover_label"
- android:scaleType="centerCrop"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark"/>
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView
+ android:id="@+id/txtvPlaceholder"
+ android:layout_width="@dimen/thumbnail_length_queue_item"
+ android:layout_height="@dimen/thumbnail_length_queue_item"
+ android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
+ android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
+ android:layout_gravity="center_vertical"
+ android:gravity="center"
+ android:background="@color/light_gray"
+ android:maxLines="3"
+ android:ellipsize="end"/>
+ <ImageView
+ android:id="@+id/imgvCover"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignLeft="@id/txtvPlaceholder"
+ android:layout_alignTop="@id/txtvPlaceholder"
+ android:layout_alignRight="@id/txtvPlaceholder"
+ android:layout_alignBottom="@id/txtvPlaceholder"
+ android:contentDescription="@string/cover_label"
+ tools:src="@drawable/ic_stat_antenna_default"
+ tools:background="@android:color/holo_green_dark"/>
+ </RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
diff --git a/app/src/main/res/menu/allepisodes_context.xml b/app/src/main/res/menu/allepisodes_context.xml
index f89ad5065..171e509a8 100644
--- a/app/src/main/res/menu/allepisodes_context.xml
+++ b/app/src/main/res/menu/allepisodes_context.xml
@@ -40,13 +40,32 @@
android:title="@string/deactivate_auto_download" />
<item
- android:id="@+id/share_link_item"
- android:menuCategory="container"
- android:title="@string/share_link_label" />
- <item
android:id="@+id/visit_website_item"
android:menuCategory="container"
android:title="@string/visit_website_label" />
+ <item
+ android:id="@+id/share_item"
+ android:menuCategory="container"
+ android:title="@string/share_label">
+ <menu>
+ <item
+ android:id="@+id/share_link_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_label" />
+ <item
+ android:id="@+id/share_link_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_with_position_label" />
+ <item
+ android:id="@+id/share_download_url_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_label" />
+ <item
+ android:id="@+id/share_download_url_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_with_position_label" />
+ </menu>
+ </item>
<item
android:id="@+id/support_item"
diff --git a/app/src/main/res/menu/episodes_apply_action_options.xml b/app/src/main/res/menu/episodes_apply_action_options.xml
new file mode 100644
index 000000000..88bef8d1f
--- /dev/null
+++ b/app/src/main/res/menu/episodes_apply_action_options.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/sort"
+ android:title="@string/sort"
+ app:showAsAction="always">
+ <menu>
+ <item android:id="@+id/sort_title"
+ android:title="@string/sort_title"/>
+ <item android:id="@+id/sort_title_a_z"
+ android:title="@string/sort_title_a_z"/>
+ <item android:id="@+id/sort_title_z_a"
+ android:title="@string/sort_title_z_a"/>
+ <item android:id="@+id/sort_date_new_old"
+ android:title="@string/sort_date_new_old"/>
+ <item android:id="@+id/sort_date_old_new"
+ android:title="@string/sort_date_old_new"/>
+ <item android:id="@+id/sort_duration_short_long"
+ android:title="@string/sort_duration_short_long"/>
+ <item android:id="@+id/sort_duration_long_short"
+ android:title="@string/sort_duration_long_short"/>
+ </menu>
+ </item>
+
+ <item
+ android:id="@+id/select_toggle"
+ android:title="@string/select_all_label"
+ app:showAsAction="always"/>
+
+ <item
+ android:id="@+id/select_options"
+ android:title="@string/all_label"
+ app:showAsAction="always">
+
+ <menu>
+ <item android:id="@+id/select_label"
+ android:title="@string/select_label"/>
+ <item android:id="@+id/check_all"
+ android:title="@string/all_label"/>
+ <item android:id="@+id/check_none"
+ android:title="@string/none_label"/>
+ <item android:id="@+id/check_played"
+ android:title="@string/played_label"/>
+ <item android:id="@+id/check_unplayed"
+ android:title="@string/unplayed_label"/>
+ <item android:id="@+id/check_downloaded"
+ android:title="@string/downloaded_label"/>
+ <item android:id="@+id/check_not_downloaded"
+ android:title="@string/not_downloaded_label"/>
+ </menu>
+ </item>
+
+</menu>
diff --git a/app/src/main/res/menu/feedinfo.xml b/app/src/main/res/menu/feedinfo.xml
index be50cb87d..9fdd56b6c 100644
--- a/app/src/main/res/menu/feedinfo.xml
+++ b/app/src/main/res/menu/feedinfo.xml
@@ -20,9 +20,9 @@
android:title="@string/share_link_label">
</item>
<item
- android:id="@+id/share_source_item"
+ android:id="@+id/share_download_url_item"
custom:showAsAction="collapseActionView"
- android:title="@string/share_source_label">
+ android:title="@string/share_feed_url_label">
</item>
-</menu> \ No newline at end of file
+</menu>
diff --git a/app/src/main/res/menu/feeditem_options.xml b/app/src/main/res/menu/feeditem_options.xml
index f8e9b9c75..650912ea2 100644
--- a/app/src/main/res/menu/feeditem_options.xml
+++ b/app/src/main/res/menu/feeditem_options.xml
@@ -48,17 +48,36 @@
</item>
<item
- android:id="@+id/share_link_item"
- custom:showAsAction="collapseActionView"
- android:title="@string/share_link_label">
- </item>
- <item
android:id="@+id/visit_website_item"
android:icon="?attr/location_web_site"
custom:showAsAction="ifRoom|collapseActionView"
android:title="@string/visit_website_label">
</item>
<item
+ android:id="@+id/share_item"
+ android:menuCategory="container"
+ android:title="@string/share_label">
+ <menu>
+ <item
+ android:id="@+id/share_link_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_label" />
+ <item
+ android:id="@+id/share_link_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_with_position_label" />
+ <item
+ android:id="@+id/share_download_url_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_label" />
+ <item
+ android:id="@+id/share_download_url_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_with_position_label" />
+ </menu>
+ </item>
+
+ <item
android:id="@+id/support_item"
custom:showAsAction="collapseActionView"
android:title="@string/support_label">
diff --git a/app/src/main/res/menu/feeditemlist_context.xml b/app/src/main/res/menu/feeditemlist_context.xml
index f89ad5065..7b10e5cce 100644
--- a/app/src/main/res/menu/feeditemlist_context.xml
+++ b/app/src/main/res/menu/feeditemlist_context.xml
@@ -40,13 +40,33 @@
android:title="@string/deactivate_auto_download" />
<item
- android:id="@+id/share_link_item"
- android:menuCategory="container"
- android:title="@string/share_link_label" />
- <item
android:id="@+id/visit_website_item"
android:menuCategory="container"
android:title="@string/visit_website_label" />
+ <item
+ android:id="@+id/share_item"
+ android:menuCategory="container"
+ android:title="@string/share_label">
+ <menu>
+ <item
+ android:id="@+id/share_link_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_label" />
+ <item
+ android:id="@+id/share_link_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_with_position_label" />
+ <item
+ android:id="@+id/share_download_url_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_label" />
+ <item
+ android:id="@+id/share_download_url_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_with_position_label" />
+ </menu>
+ </item>
+
<item
android:id="@+id/support_item"
diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml
index e0da72667..54b90f5c6 100644
--- a/app/src/main/res/menu/feedlist.xml
+++ b/app/src/main/res/menu/feedlist.xml
@@ -10,6 +10,12 @@
custom:showAsAction="always">
</item>
<item
+ android:id="@+id/episode_actions"
+ android:menuCategory="container"
+ android:title="@string/episode_actions"
+ custom:showAsAction="always">
+ </item>
+ <item
android:id="@+id/refresh_item"
android:icon="?attr/navigation_refresh"
android:menuCategory="container"
@@ -31,18 +37,34 @@
android:title="@string/search_label"/>
<item
- android:id="@+id/mark_all_read_item"
- android:menuCategory="container"
- android:title="@string/mark_all_read_label"
- custom:showAsAction="collapseActionView">
- </item>
- <item
android:id="@+id/support_item"
android:menuCategory="container"
android:title="@string/support_label"
android:visible="false"
custom:showAsAction="collapseActionView">
</item>
+
+ <item
+ android:id="@+id/visit_website_item"
+ android:icon="?attr/location_web_site"
+ android:menuCategory="container"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/visit_website_label"
+ android:visible="true">
+ </item>
+ <item
+ android:id="@+id/share_link_item"
+ android:menuCategory="container"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/share_link_label">
+ </item>
+ <item
+ android:id="@+id/share_download_url_item"
+ android:menuCategory="container"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/share_feed_url_label">
+ </item>
+
<item
android:id="@+id/remove_item"
android:icon="?attr/content_discard"
diff --git a/app/src/main/res/menu/mediaplayer.xml b/app/src/main/res/menu/mediaplayer.xml
index 288e44401..053e68552 100644
--- a/app/src/main/res/menu/mediaplayer.xml
+++ b/app/src/main/res/menu/mediaplayer.xml
@@ -13,11 +13,14 @@
custom:showAsAction="collapseActionView"
android:title="@string/set_sleeptimer_label">
</item>
+
<item
- android:id="@+id/share_link_item"
+ android:id="@+id/skip_episode_item"
custom:showAsAction="collapseActionView"
- android:title="@string/share_link_label">
+ android:title="@string/skip_episode_label"
+ android:visible="true">
</item>
+
<item
android:id="@+id/visit_website_item"
android:icon="?attr/location_web_site"
@@ -26,15 +29,33 @@
android:visible="false">
</item>
<item
+ android:id="@+id/share_item"
+ android:menuCategory="container"
+ android:title="@string/share_label">
+ <menu>
+ <item
+ android:id="@+id/share_link_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_label" />
+ <item
+ android:id="@+id/share_link_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_with_position_label" />
+ <item
+ android:id="@+id/share_download_url_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_label" />
+ <item
+ android:id="@+id/share_download_url_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_with_position_label" />
+ </menu>
+ </item>
+ <item
android:id="@+id/support_item"
custom:showAsAction="collapseActionView"
android:title="@string/support_label"
android:visible="false">
</item>
- <item
- android:id="@id/skip_episode_item"
- custom:showAsAction="collapseActionView"
- android:title="@string/skip_episode_label"
- android:visible="true"/>
</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/nav_feed_context.xml b/app/src/main/res/menu/nav_feed_context.xml
new file mode 100644
index 000000000..4bf067d25
--- /dev/null
+++ b/app/src/main/res/menu/nav_feed_context.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/mark_all_seen_item"
+ android:menuCategory="container"
+ android:title="@string/mark_all_seen_label" />
+
+ <item
+ android:id="@+id/mark_all_read_item"
+ android:menuCategory="container"
+ android:title="@string/mark_all_read_label" />
+
+ <item
+ android:id="@+id/remove_item"
+ android:menuCategory="container"
+ android:title="@string/remove_feed_label" />
+
+</menu>
diff --git a/app/src/main/res/menu/queue_context.xml b/app/src/main/res/menu/queue_context.xml
index 6ab2daabf..d09f3c84c 100644
--- a/app/src/main/res/menu/queue_context.xml
+++ b/app/src/main/res/menu/queue_context.xml
@@ -36,21 +36,38 @@
android:id="@+id/activate_auto_download"
android:menuCategory="container"
android:title="@string/activate_auto_download" />
-
<item
android:id="@+id/deactivate_auto_download"
android:menuCategory="container"
android:title="@string/deactivate_auto_download" />
<item
- android:id="@+id/share_link_item"
- android:menuCategory="container"
- android:title="@string/share_link_label" />
- <item
android:id="@+id/visit_website_item"
android:menuCategory="container"
android:title="@string/visit_website_label" />
-
+ <item
+ android:id="@+id/share_item"
+ android:menuCategory="container"
+ android:title="@string/share_label">
+ <menu>
+ <item
+ android:id="@+id/share_link_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_label" />
+ <item
+ android:id="@+id/share_link_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_with_position_label" />
+ <item
+ android:id="@+id/share_download_url_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_label" />
+ <item
+ android:id="@+id/share_download_url_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_with_position_label" />
+ </menu>
+ </item>
<item
android:id="@+id/support_item"
android:menuCategory="container"
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index e848915cf..35fb60c58 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -9,10 +9,29 @@
android:key="prefTheme"
android:summary="@string/pref_set_theme_sum"
android:defaultValue="0"/>
- <Preference
- android:key="prefHiddenDrawerItems"
- android:summary="@string/pref_nav_drawer_items_sum"
- android:title="@string/pref_nav_drawer_items_title" />
+ <PreferenceScreen
+ android:key="prefDrawerSettings"
+ android:summary="@string/pref_nav_drawer_sum"
+ android:title="@string/pref_nav_drawer_title">
+ <Preference
+ android:key="prefHiddenDrawerItems"
+ android:summary="@string/pref_nav_drawer_items_sum"
+ android:title="@string/pref_nav_drawer_items_title" />
+ <ListPreference
+ android:entryValues="@array/nav_drawer_feed_order_values"
+ android:entries="@array/nav_drawer_feed_order_options"
+ android:title="@string/pref_nav_drawer_feed_order_title"
+ android:key="prefDrawerFeedOrder"
+ android:summary="@string/pref_nav_drawer_feed_order_sum"
+ android:defaultValue="0"/>
+ <ListPreference
+ android:entryValues="@array/nav_drawer_feed_counter_values"
+ android:entries="@array/nav_drawer_feed_counter_options"
+ android:title="@string/pref_nav_drawer_feed_counter_title"
+ android:key="prefDrawerFeedIndicator"
+ android:summary="@string/pref_nav_drawer_feed_counter_sum"
+ android:defaultValue="0"/>
+ </PreferenceScreen>
<CheckBoxPreference
android:defaultValue="false"
android:enabled="true"
@@ -25,6 +44,12 @@
android:key="prefPersistNotify"
android:summary="@string/pref_persistNotify_sum"
android:title="@string/pref_persistNotify_title"/>
+ <CheckBoxPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ android:key="prefShowDownloadReport"
+ android:summary="@string/pref_showDownloadReport_sum"
+ android:title="@string/pref_showDownloadReport_title"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/queue_label">
@@ -90,13 +115,10 @@
</PreferenceCategory>
<PreferenceCategory android:title="@string/network_pref">
- <ListPreference
- android:defaultValue="0"
- android:entries="@array/update_intervall_values"
- android:entryValues="@array/update_intervall_values"
+ <Preference
android:key="prefAutoUpdateIntervall"
- android:summary="@string/pref_autoUpdateIntervall_sum"
- android:title="@string/pref_autoUpdateIntervall_title"/>
+ android:summary="@string/pref_autoUpdateIntervallOrTime_sum"
+ android:title="@string/pref_autoUpdateIntervallOrTime_title"/>
<CheckBoxPreference
android:defaultValue="false"
android:enabled="true"
@@ -182,11 +204,13 @@
<Preference
android:title="@string/choose_data_directory"
android:key="prefChooseDataDir"/>
- <Preference
- android:key="prefFlattrThisApp"
- android:summary="@string/pref_flattr_this_app_sum"
- android:title="@string/pref_flattr_this_app_title">
- </Preference>
+ <ListPreference
+ android:entryValues="@array/image_cache_size_values"
+ android:entries="@array/image_cache_size_options"
+ android:title="@string/pref_image_cache_size_title"
+ android:key="prefImageCacheSize"
+ android:summary="@string/pref_image_cache_size_sum"
+ android:defaultValue="100"/>
<Preference
android:key="prefOpmlExport"
android:title="@string/opml_export_label"/>
@@ -194,7 +218,6 @@
android:key="prefAbout"
android:title="@string/about_pref"/>
-
</PreferenceCategory>
</PreferenceScreen>
diff --git a/app/src/main/templates/about.html b/app/src/main/templates/about.html
index 3313fa12f..2aca7ca4d 100644
--- a/app/src/main/templates/about.html
+++ b/app/src/main/templates/about.html
@@ -41,7 +41,8 @@
<div id="header" align="center">
<img src="logo.png" alt="Logo" width="100px" height="100px"/>
- <p>AntennaPod, Version @versionname@, Build @versioncode@</p>
+ <p>AntennaPod, Version @versionname@</p>
+ <p>Commit: @commit@</p>
<p>Created by Daniel Oeh</p>
@@ -67,8 +68,11 @@ licensed under the Apache 2.0 license <a href="LICENSE_PRESTO.txt">(View)</a>
<h2>jsoup <a href="http://jsoup.org/">(Link)</a></h2>
licensed under the MIT license <a href="LICENSE_JSOUP.txt">(View)</a>
-<h2>Picasso <a href="https://github.com/square/picasso">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_PICASSO.txt">(View)</a>
+<h2>Glide <a href="https://github.com/bumptech/glide/">(Link)</a></h2>
+licensed under the Simplified BSD license <a href="LICENSE_GLIDE.txt">(View)</a>
+
+<h2>StackBlur <a href="https://github.com/kikoso/android-stackblur">(Link)</a></h2>
+licensed under the Apache 2.0 license <a href="LICENSE_STACKBLUR.txt">(View)</a>
<h2>OkHttp <a href="https://github.com/square/okhttp">(Link)</a></h2>
licensed under the Apache 2.0 license <a href="LICENSE_OKHTTP.txt">(View)</a>
diff --git a/core/build.gradle b/core/build.gradle
index ae2c11070..5e721bbc3 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -42,7 +42,8 @@ dependencies {
compile 'commons-io:commons-io:2.4'
compile 'com.jayway.android.robotium:robotium-solo:5.2.1'
compile 'org.jsoup:jsoup:1.7.3'
- compile 'com.squareup.picasso:picasso:2.5.2'
+ compile 'com.github.bumptech.glide:glide:3.6.1'
+ compile 'com.github.bumptech.glide:okhttp-integration:1.3.0'
compile 'com.squareup.okhttp:okhttp:2.3.0'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.3.0'
compile 'com.squareup.okio:okio:1.2.0'
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java b/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java
index 2a2d6414a..2727b1447 100644
--- a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java
+++ b/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java
@@ -13,6 +13,7 @@ public class DateUtilsTest extends AndroidTestCase {
public void testParseDateWithMicroseconds() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4);
+ exp.setTimeZone(TimeZone.getTimeZone("UTC"));
Date expected = new Date(exp.getTimeInMillis() + 963);
Date actual = DateUtils.parse("2015-03-28T13:31:04.963870");
assertEquals(expected, actual);
@@ -20,6 +21,7 @@ public class DateUtilsTest extends AndroidTestCase {
public void testParseDateWithCentiseconds() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4);
+ exp.setTimeZone(TimeZone.getTimeZone("UTC"));
Date expected = new Date(exp.getTimeInMillis() + 960);
Date actual = DateUtils.parse("2015-03-28T13:31:04.96");
assertEquals(expected, actual);
@@ -27,6 +29,7 @@ public class DateUtilsTest extends AndroidTestCase {
public void testParseDateWithDeciseconds() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 13, 31, 4);
+ exp.setTimeZone(TimeZone.getTimeZone("UTC"));
Date expected = new Date(exp.getTimeInMillis() + 900);
Date actual = DateUtils.parse("2015-03-28T13:31:04.9");
assertEquals(expected.getTime()/1000, actual.getTime()/1000);
@@ -66,6 +69,14 @@ public class DateUtilsTest extends AndroidTestCase {
assertEquals(expected, actual);
}
+ public void testParseDateWithTimezoneName2() throws Exception {
+ GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 6, 31, 0);
+ exp.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Date expected = new Date(exp.getTimeInMillis());
+ Date actual = DateUtils.parse("Sat, 28 Mar 2015 01:31 EST");
+ assertEquals(expected, actual);
+ }
+
public void testParseDateWithTimeZoneOffset() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2015, 2, 28, 12, 16, 12);
exp.setTimeZone(TimeZone.getTimeZone("UTC"));
@@ -74,4 +85,20 @@ public class DateUtilsTest extends AndroidTestCase {
assertEquals(expected, actual);
}
+ public void testAsctime() throws Exception {
+ GregorianCalendar exp = new GregorianCalendar(2011, 4, 25, 12, 33, 00);
+ exp.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Date expected = new Date(exp.getTimeInMillis());
+ Date actual = DateUtils.parse("Wed, 25 May 2011 12:33:00");
+ assertEquals(expected, actual);
+ }
+
+ public void testMultipleConsecutiveSpaces() throws Exception {
+ GregorianCalendar exp = new GregorianCalendar(2010, 2, 23, 6, 6, 26);
+ exp.setTimeZone(TimeZone.getTimeZone("UTC"));
+ Date expected = new Date(exp.getTimeInMillis());
+ Date actual = DateUtils.parse("Tue, 23 Mar 2010 01:06:26 -0500");
+ assertEquals(expected, actual);
+ }
+
}
diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml
index 3ec519844..17dcb4ad8 100644
--- a/core/src/main/AndroidManifest.xml
+++ b/core/src/main/AndroidManifest.xml
@@ -23,6 +23,9 @@
<service
android:name=".service.GpodnetSyncService"
android:enabled="true" />
+ <service
+ android:name=".service.FeedMediaSizeService"
+ android:enabled="true" />
<receiver
android:name=".receiver.MediaButtonReceiver"
diff --git a/core/src/main/java/de/danoeh/antennapod/core/ApplicationCallbacks.java b/core/src/main/java/de/danoeh/antennapod/core/ApplicationCallbacks.java
index 3bc1ce4eb..1064e98ac 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/ApplicationCallbacks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/ApplicationCallbacks.java
@@ -20,5 +20,4 @@ public interface ApplicationCallbacks {
*/
public Intent getStorageErrorActivity(Context context);
- public void setUpdateInterval(long updateInterval);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java
index 1a2671555..6619e706b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/ClientConfig.java
@@ -21,7 +21,5 @@ public class ClientConfig {
public static FlattrCallbacks flattrCallbacks;
- public static StorageCallbacks storageCallbacks;
-
public static DBTasksCallbacks dbTasksCallbacks;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/StorageCallbacks.java b/core/src/main/java/de/danoeh/antennapod/core/StorageCallbacks.java
deleted file mode 100644
index 5d1a0fffc..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/StorageCallbacks.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package de.danoeh.antennapod.core;
-
-import android.database.sqlite.SQLiteDatabase;
-
-/**
- * Callbacks for the classes in the storage package of the core module.
- */
-public interface StorageCallbacks {
-
- /**
- * Returns the current version of the database.
- *
- * @return The non-negative version number of the database.
- */
- public int getDatabaseVersion();
-
- /**
- * Upgrades the given database from an old version to a newer version.
- *
- * @param db The database that is supposed to be upgraded.
- * @param oldVersion The old version of the database.
- * @param newVersion The version that the database is supposed to be upgraded to.
- */
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
-
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/DownloadObserver.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/DownloadObserver.java
index a13130082..732fa2d27 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/DownloadObserver.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/DownloadObserver.java
@@ -12,6 +12,7 @@ import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -85,15 +86,18 @@ public class DownloadObserver {
if (downloadService == null) {
connectToDownloadService();
}
- callback.onContentChanged();
+ if (downloadService != null) {
+ callback.onContentChanged(downloadService.getDownloads());
+ } else {
+ // the service is gone, there are no more downloads.
+ callback.onContentChanged(new ArrayList<Downloader>());
+ }
startRefresher();
}
};
public interface Callback {
- void onContentChanged();
-
- void onDownloadDataAvailable(List<Downloader> downloaderList);
+ void onContentChanged(List<Downloader> downloaderList);
}
private void connectToDownloadService() {
@@ -116,7 +120,7 @@ public class DownloadObserver {
Log.d(TAG, "Connection to service established");
List<Downloader> downloaderList = downloadService.getDownloads();
if (downloaderList != null && !downloaderList.isEmpty()) {
- callback.onDownloadDataAvailable(downloaderList);
+ callback.onContentChanged(downloaderList);
startRefresher();
}
}
@@ -156,12 +160,14 @@ public class DownloadObserver {
handler.post(new Runnable() {
@Override
public void run() {
- callback.onContentChanged();
if (downloadService != null) {
List<Downloader> downloaderList = downloadService.getDownloads();
+ callback.onContentChanged(downloaderList);
if (downloaderList == null || downloaderList.isEmpty()) {
Thread.currentThread().interrupt();
}
+ } else {
+ callback.onContentChanged(new ArrayList<Downloader>());
}
}
});
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java
index 5d2d5d441..f91d4557e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java
@@ -90,7 +90,7 @@ public class FlattrClickWorker extends AsyncTask<Void, Integer, FlattrClickWorke
return ExitCode.NO_TOKEN;
}
- if (!NetworkUtils.networkAvailable(context)) {
+ if (!NetworkUtils.networkAvailable()) {
return ExitCode.NO_NETWORK;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoImageResource.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/ImageResource.java
index c0d8049db..edd69f15b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoImageResource.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/ImageResource.java
@@ -6,7 +6,7 @@ import android.net.Uri;
* Classes that implement this interface provide access to an image resource that can
* be loaded by the Picasso library.
*/
-public interface PicassoImageResource {
+public interface ImageResource {
/**
* This scheme should be used by PicassoImageResources to
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java
deleted file mode 100644
index 09fe0d654..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java
+++ /dev/null
@@ -1,527 +0,0 @@
-package de.danoeh.antennapod.core.asynctask;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.media.MediaMetadataRetriever;
-import android.net.Uri;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.squareup.okhttp.Interceptor;
-import com.squareup.okhttp.OkHttpClient;
-import com.squareup.okhttp.Response;
-import com.squareup.picasso.Cache;
-import com.squareup.picasso.LruCache;
-import com.squareup.picasso.OkHttpDownloader;
-import com.squareup.picasso.Picasso;
-import com.squareup.picasso.Request;
-import com.squareup.picasso.RequestHandler;
-import com.squareup.picasso.Transformation;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-import de.danoeh.antennapod.core.service.download.HttpDownloader;
-import de.danoeh.antennapod.core.storage.DBReader;
-
-/**
- * Provides access to Picasso instances.
- */
-public class PicassoProvider {
-
- private static final String TAG = "PicassoProvider";
-
- private static final boolean DEBUG = false;
-
- private static ExecutorService executorService;
- private static Cache memoryCache;
-
- private static synchronized ExecutorService getExecutorService() {
- if (executorService == null) {
- executorService = Executors.newFixedThreadPool(3);
- }
- return executorService;
- }
-
- private static synchronized Cache getMemoryCache(Context context) {
- if (memoryCache == null) {
- memoryCache = new LruCache(context);
- }
- return memoryCache;
- }
-
- private static volatile boolean picassoSetup = false;
-
- public static synchronized void setupPicassoInstance(Context appContext) {
- if (picassoSetup) {
- return;
- }
- OkHttpClient client = new OkHttpClient();
- client.interceptors().add(new BasicAuthenticationInterceptor(appContext));
- Picasso picasso = new Picasso.Builder(appContext)
- .indicatorsEnabled(DEBUG)
- .loggingEnabled(DEBUG)
- .downloader(new OkHttpDownloader(client))
- .addRequestHandler(new MediaRequestHandler(appContext))
- .executor(getExecutorService())
- .memoryCache(getMemoryCache(appContext))
- .listener(new Picasso.Listener() {
- @Override
- public void onImageLoadFailed(Picasso picasso, Uri uri, Exception e) {
- Log.e(TAG, "Failed to load Uri:" + uri.toString());
- e.printStackTrace();
- }
- })
- .build();
- Picasso.setSingletonInstance(picasso);
- picassoSetup = true;
- }
-
- private static class BasicAuthenticationInterceptor implements Interceptor {
-
- private final Context context;
-
- public BasicAuthenticationInterceptor(Context context) {
- this.context = context;
- }
-
- @Override
- public Response intercept(Chain chain) throws IOException {
- com.squareup.okhttp.Request request = chain.request();
- String url = request.urlString();
- String authentication = DBReader.getImageAuthentication(context, url);
-
- if(TextUtils.isEmpty(authentication)) {
- Log.d(TAG, "no credentials for '" + url + "'");
- return chain.proceed(request);
- }
-
- // add authentication
- String[] auth = authentication.split(":");
- String credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "ISO-8859-1");
- com.squareup.okhttp.Request newRequest = request
- .newBuilder()
- .addHeader("Authorization", credentials)
- .build();
- Log.d(TAG, "Basic authentication with ISO-8859-1 encoding");
- Response response = chain.proceed(newRequest);
- if (!response.isSuccessful() && response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
- credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "UTF-8");
- newRequest = request
- .newBuilder()
- .addHeader("Authorization", credentials)
- .build();
- Log.d(TAG, "Basic authentication with UTF-8 encoding");
- return chain.proceed(newRequest);
- } else {
- return response;
- }
- }
- }
-
- private static class MediaRequestHandler extends RequestHandler {
-
- final Context context;
-
- public MediaRequestHandler(Context context) {
- super();
- this.context = context;
- }
-
- @Override
- public boolean canHandleRequest(Request data) {
- return StringUtils.equals(data.uri.getScheme(), PicassoImageResource.SCHEME_MEDIA);
- }
-
- @Override
- public Result load(Request data, int networkPolicy) throws IOException {
- Bitmap bitmap = null;
- MediaMetadataRetriever mmr = null;
- try {
- mmr = new MediaMetadataRetriever();
- mmr.setDataSource(data.uri.getPath());
- byte[] image = mmr.getEmbeddedPicture();
- if (image != null) {
- bitmap = decodeStreamFromByteArray(data, image);
- }
- } catch (RuntimeException e) {
- Log.e(TAG, "Failed to decode image in media file", e);
- } finally {
- if (mmr != null) {
- mmr.release();
- }
- }
-
- if (bitmap == null) {
- // this should never, happen, but sometimes it does, so fallback
- // check for fallback Uri
- String fallbackParam = data.uri.getQueryParameter(PicassoImageResource.PARAM_FALLBACK);
- if (fallbackParam != null) {
- Uri fallback = Uri.parse(fallbackParam);
- bitmap = decodeStreamFromFile(data, fallback);
- }
- }
- return new Result(bitmap, Picasso.LoadedFrom.DISK);
-
- }
-
- /* Copied/Adapted from Picasso RequestHandler classes */
-
- private Bitmap decodeStreamFromByteArray(Request data, byte[] bytes) throws IOException {
-
- final BitmapFactory.Options options = createBitmapOptions(data);
- final ByteArrayInputStream in = new ByteArrayInputStream(bytes);
- in.mark(0);
- if (requiresInSampleSize(options)) {
- try {
- BitmapFactory.decodeStream(in, null, options);
- } finally {
- in.reset();
- }
- calculateInSampleSize(data.targetWidth, data.targetHeight, options, data);
- }
- try {
- return BitmapFactory.decodeStream(in, null, options);
- } finally {
- IOUtils.closeQuietly(in);
- }
- }
-
- private Bitmap decodeStreamFromFile(Request data, Uri uri) throws IOException {
- ContentResolver contentResolver = context.getContentResolver();
- final BitmapFactory.Options options = createBitmapOptions(data);
- if (requiresInSampleSize(options)) {
- InputStream is = null;
- try {
- is = contentResolver.openInputStream(uri);
- BitmapFactory.decodeStream(is, null, options);
- } finally {
- IOUtils.closeQuietly(is);
- }
- calculateInSampleSize(data.targetWidth, data.targetHeight, options, data);
- }
- InputStream is = contentResolver.openInputStream(uri);
- try {
- return BitmapFactory.decodeStream(is, null, options);
- } finally {
- IOUtils.closeQuietly(is);
- }
- }
-
- private BitmapFactory.Options createBitmapOptions(Request data) {
- final boolean justBounds = data.hasSize();
- final boolean hasConfig = data.config != null;
- BitmapFactory.Options options = null;
- if (justBounds || hasConfig) {
- options = new BitmapFactory.Options();
- options.inJustDecodeBounds = justBounds;
- if (hasConfig) {
- options.inPreferredConfig = data.config;
- }
- }
- return options;
- }
-
- private static boolean requiresInSampleSize(BitmapFactory.Options options) {
- return options != null && options.inJustDecodeBounds;
- }
-
- private static void calculateInSampleSize(int reqWidth, int reqHeight, BitmapFactory.Options options,
- Request request) {
- calculateInSampleSize(reqWidth, reqHeight, options.outWidth, options.outHeight, options,
- request);
- }
-
- private static void calculateInSampleSize(int reqWidth, int reqHeight, int width, int height,
- BitmapFactory.Options options, Request request) {
- int sampleSize = 1;
- if (height > reqHeight || width > reqWidth) {
- final int heightRatio;
- final int widthRatio;
- if (reqHeight == 0) {
- sampleSize = (int) Math.floor((float) width / (float) reqWidth);
- } else if (reqWidth == 0) {
- sampleSize = (int) Math.floor((float) height / (float) reqHeight);
- } else {
- heightRatio = (int) Math.floor((float) height / (float) reqHeight);
- widthRatio = (int) Math.floor((float) width / (float) reqWidth);
- sampleSize = request.centerInside
- ? Math.max(heightRatio, widthRatio)
- : Math.min(heightRatio, widthRatio);
- }
- }
- options.inSampleSize = sampleSize;
- options.inJustDecodeBounds = false;
- }
- }
-
- public static final int BLUR_RADIUS = 1;
- public static final int BLUR_IMAGE_SIZE = 100;
- public static final String BLUR_KEY = "blur";
-
- public static final Transformation blurTransformation = new Transformation() {
- @Override
- public Bitmap transform(Bitmap source) {
- Bitmap result = fastblur(source, BLUR_RADIUS);
- if (result == null) {
- // just return the original
- // for some reason we couldn't transform it.
- return source;
- }
- source.recycle();
- return result;
- }
-
- @Override
- public String key() {
- return BLUR_KEY;
- }
- };
-
- public static Bitmap fastblur(Bitmap sentBitmap, int radius) {
-
- // Stack Blur v1.0 from
- // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
- //
- // Java Author: Mario Klingemann <mario at quasimondo.com>
- // http://incubator.quasimondo.com
- // created Feburary 29, 2004
- // Android port : Yahel Bouaziz <yahel at kayenko.com>
- // http://www.kayenko.com
- // ported april 5th, 2012
-
- // This is a compromise between Gaussian Blur and Box blur
- // It creates much better looking blurs than Box Blur, but is
- // 7x faster than my Gaussian Blur implementation.
- //
- // I called it Stack Blur because this describes best how this
- // filter works internally: it creates a kind of moving stack
- // of colors whilst scanning through the image. Thereby it
- // just has to add one new block of color to the right side
- // of the stack and remove the leftmost color. The remaining
- // colors on the topmost layer of the stack are either added on
- // or reduced by one, depending on if they are on the right or
- // on the left side of the stack.
- //
- // If you are using this algorithm in your code please add
- // the following line:
- //
- // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
- Bitmap.Config config = sentBitmap.getConfig();
- if (config == null) {
- // Sometimes the config can be null, in those cases
- // we don't do a transform.
- return null;
- }
-
- Bitmap bitmap = sentBitmap.copy(config, true);
-
- if (radius < 1) {
- return (null);
- }
-
- int w = bitmap.getWidth();
- int h = bitmap.getHeight();
-
- int[] pix = new int[w * h];
- Log.e("pix", w + " " + h + " " + pix.length);
- bitmap.getPixels(pix, 0, w, 0, 0, w, h);
-
- int wm = w - 1;
- int hm = h - 1;
- int wh = w * h;
- int div = radius + radius + 1;
-
- int r[] = new int[wh];
- int g[] = new int[wh];
- int b[] = new int[wh];
- int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
- int vmin[] = new int[Math.max(w, h)];
-
- int divsum = (div + 1) >> 1;
- divsum *= divsum;
- int dv[] = new int[256 * divsum];
- for (i = 0; i < 256 * divsum; i++) {
- dv[i] = (i / divsum);
- }
-
- yw = yi = 0;
-
- int[][] stack = new int[div][3];
- int stackpointer;
- int stackstart;
- int[] sir;
- int rbs;
- int r1 = radius + 1;
- int routsum, goutsum, boutsum;
- int rinsum, ginsum, binsum;
-
- for (y = 0; y < h; y++) {
- rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
- for (i = -radius; i <= radius; i++) {
- p = pix[yi + Math.min(wm, Math.max(i, 0))];
- sir = stack[i + radius];
- sir[0] = (p & 0xff0000) >> 16;
- sir[1] = (p & 0x00ff00) >> 8;
- sir[2] = (p & 0x0000ff);
- rbs = r1 - Math.abs(i);
- rsum += sir[0] * rbs;
- gsum += sir[1] * rbs;
- bsum += sir[2] * rbs;
- if (i > 0) {
- rinsum += sir[0];
- ginsum += sir[1];
- binsum += sir[2];
- } else {
- routsum += sir[0];
- goutsum += sir[1];
- boutsum += sir[2];
- }
- }
- stackpointer = radius;
-
- for (x = 0; x < w; x++) {
-
- r[yi] = dv[rsum];
- g[yi] = dv[gsum];
- b[yi] = dv[bsum];
-
- rsum -= routsum;
- gsum -= goutsum;
- bsum -= boutsum;
-
- stackstart = stackpointer - radius + div;
- sir = stack[stackstart % div];
-
- routsum -= sir[0];
- goutsum -= sir[1];
- boutsum -= sir[2];
-
- if (y == 0) {
- vmin[x] = Math.min(x + radius + 1, wm);
- }
- p = pix[yw + vmin[x]];
-
- sir[0] = (p & 0xff0000) >> 16;
- sir[1] = (p & 0x00ff00) >> 8;
- sir[2] = (p & 0x0000ff);
-
- rinsum += sir[0];
- ginsum += sir[1];
- binsum += sir[2];
-
- rsum += rinsum;
- gsum += ginsum;
- bsum += binsum;
-
- stackpointer = (stackpointer + 1) % div;
- sir = stack[(stackpointer) % div];
-
- routsum += sir[0];
- goutsum += sir[1];
- boutsum += sir[2];
-
- rinsum -= sir[0];
- ginsum -= sir[1];
- binsum -= sir[2];
-
- yi++;
- }
- yw += w;
- }
- for (x = 0; x < w; x++) {
- rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
- yp = -radius * w;
- for (i = -radius; i <= radius; i++) {
- yi = Math.max(0, yp) + x;
-
- sir = stack[i + radius];
-
- sir[0] = r[yi];
- sir[1] = g[yi];
- sir[2] = b[yi];
-
- rbs = r1 - Math.abs(i);
-
- rsum += r[yi] * rbs;
- gsum += g[yi] * rbs;
- bsum += b[yi] * rbs;
-
- if (i > 0) {
- rinsum += sir[0];
- ginsum += sir[1];
- binsum += sir[2];
- } else {
- routsum += sir[0];
- goutsum += sir[1];
- boutsum += sir[2];
- }
-
- if (i < hm) {
- yp += w;
- }
- }
- yi = x;
- stackpointer = radius;
- for (y = 0; y < h; y++) {
- // Preserve alpha channel: ( 0xff000000 & pix[yi] )
- pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
-
- rsum -= routsum;
- gsum -= goutsum;
- bsum -= boutsum;
-
- stackstart = stackpointer - radius + div;
- sir = stack[stackstart % div];
-
- routsum -= sir[0];
- goutsum -= sir[1];
- boutsum -= sir[2];
-
- if (x == 0) {
- vmin[y] = Math.min(y + r1, hm) * w;
- }
- p = x + vmin[y];
-
- sir[0] = r[p];
- sir[1] = g[p];
- sir[2] = b[p];
-
- rinsum += sir[0];
- ginsum += sir[1];
- binsum += sir[2];
-
- rsum += rinsum;
- gsum += ginsum;
- bsum += binsum;
-
- stackpointer = (stackpointer + 1) % div;
- sir = stack[stackpointer];
-
- routsum += sir[0];
- goutsum += sir[1];
- boutsum += sir[2];
-
- rinsum -= sir[0];
- ginsum -= sir[1];
- binsum -= sir[2];
-
- yi += w;
- }
- }
-
- Log.e("pix", w + " " + h + " " + pix.length);
- bitmap.setPixels(pix, 0, w, 0, 0, w, h);
-
- return (bitmap);
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java b/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java
index ba1add895..fea2bbb2b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java
@@ -4,7 +4,7 @@ import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.util.Log;
-import de.danoeh.antennapod.core.BuildConfig;
+
import de.danoeh.antennapod.core.R;
/**
@@ -12,12 +12,16 @@ import de.danoeh.antennapod.core.R;
* classes can handle events like confirmation or cancellation.
*/
public abstract class ConfirmationDialog {
- private static final String TAG = "ConfirmationDialog";
- Context context;
+ private static final String TAG = ConfirmationDialog.class.getSimpleName();
+
+ protected Context context;
int titleId;
int messageId;
+ int positiveText;
+ int negativeText;
+
public ConfirmationDialog(Context context, int titleId, int messageId) {
this.context = context;
this.titleId = titleId;
@@ -25,18 +29,26 @@ public abstract class ConfirmationDialog {
}
public void onCancelButtonPressed(DialogInterface dialog) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Dialog was cancelled");
+ Log.d(TAG, "Dialog was cancelled");
dialog.dismiss();
}
+ public void setPositiveText(int id) {
+ this.positiveText = id;
+ }
+
+ public void setNegativeText(int id) {
+ this.negativeText = id;
+ }
+
+
public abstract void onConfirmButtonPressed(DialogInterface dialog);
public final AlertDialog createNewDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(titleId);
builder.setMessage(messageId);
- builder.setPositiveButton(R.string.confirm_label,
+ builder.setPositiveButton(positiveText != 0 ? positiveText : R.string.confirm_label,
new DialogInterface.OnClickListener() {
@Override
@@ -44,7 +56,7 @@ public abstract class ConfirmationDialog {
onConfirmButtonPressed(dialog);
}
});
- builder.setNegativeButton(R.string.cancel_label,
+ builder.setNegativeButton(negativeText != 0 ? negativeText : R.string.cancel_label,
new DialogInterface.OnClickListener() {
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java
new file mode 100644
index 000000000..94e186b7a
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java
@@ -0,0 +1,24 @@
+package de.danoeh.antennapod.core.event;
+
+import de.danoeh.antennapod.core.feed.FeedMedia;
+
+public class FeedMediaEvent {
+
+ public enum Action {
+ UPDATE
+ }
+
+ public final Action action;
+ public final FeedMedia media;
+
+ private FeedMediaEvent(Action action, FeedMedia media) {
+ this.action = action;
+ this.media = media;
+ }
+
+ public static FeedMediaEvent update(FeedMedia media) {
+ return new FeedMediaEvent(Action.UPDATE, media);
+ }
+
+
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java
new file mode 100644
index 000000000..3769d6bb1
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/ProgressEvent.java
@@ -0,0 +1,36 @@
+package de.danoeh.antennapod.core.event;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+public class ProgressEvent {
+
+ public enum Action {
+ START, END
+ }
+
+ public final Action action;
+ public final String message;
+
+ private ProgressEvent(Action action, String message) {
+ this.action = action;
+ this.message = message;
+ }
+
+ public static ProgressEvent start(String message) {
+ return new ProgressEvent(Action.START, message);
+ }
+
+ public static ProgressEvent end() {
+ return new ProgressEvent(Action.END, null);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
+ .append("action", action)
+ .append("message", message)
+ .toString();
+ }
+
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
index 29ba721fe..2a483ca9b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
@@ -10,7 +10,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
-import de.danoeh.antennapod.core.asynctask.PicassoImageResource;
+import de.danoeh.antennapod.core.asynctask.ImageResource;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
import de.danoeh.antennapod.core.util.flattr.FlattrThing;
@@ -20,7 +20,7 @@ import de.danoeh.antennapod.core.util.flattr.FlattrThing;
*
* @author daniel
*/
-public class Feed extends FeedFile implements FlattrThing, PicassoImageResource {
+public class Feed extends FeedFile implements FlattrThing, ImageResource {
public static final int FEEDFILETYPE_FEED = 0;
public static final String TYPE_RSS2 = "rss";
public static final String TYPE_RSS091 = "rss";
@@ -167,7 +167,7 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
*/
public Feed(String url, Date lastUpdate, String title, String username, String password) {
this(url, lastUpdate, title);
- preferences = new FeedPreferences(0, true, username, password);
+ preferences = new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL, username, password);
}
@@ -177,10 +177,21 @@ public class Feed extends FeedFile implements FlattrThing, PicassoImageResource
*/
public boolean hasNewItems() {
for (FeedItem item : items) {
- if (item.getState() == FeedItem.State.UNREAD) {
- if (item.getMedia() != null) {
- return true;
- }
+ if (item.isNew()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if at least one item in the itemlist is unread.
+ *
+ */
+ public boolean hasUnplayedItems() {
+ for (FeedItem item : items) {
+ if (false == item.isNew() && false == item.isPlayed()) {
+ return true;
}
}
return false;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java
index c6f24367e..f77a78721 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java
@@ -4,10 +4,10 @@ import android.net.Uri;
import java.io.File;
-import de.danoeh.antennapod.core.asynctask.PicassoImageResource;
+import de.danoeh.antennapod.core.asynctask.ImageResource;
-public class FeedImage extends FeedFile implements PicassoImageResource {
+public class FeedImage extends FeedFile implements ImageResource {
public static final int FEEDFILETYPE_FEEDIMAGE = 1;
protected String title;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
index 1168c60e4..9229172f0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
@@ -10,7 +10,7 @@ import java.util.List;
import java.util.concurrent.Callable;
import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.asynctask.PicassoImageResource;
+import de.danoeh.antennapod.core.asynctask.ImageResource;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.ShownotesProvider;
import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
@@ -21,7 +21,7 @@ import de.danoeh.antennapod.core.util.flattr.FlattrThing;
*
* @author daniel
*/
-public class FeedItem extends FeedComponent implements ShownotesProvider, FlattrThing, PicassoImageResource {
+public class FeedItem extends FeedComponent implements ShownotesProvider, FlattrThing, ImageResource {
/**
* The id/guid that can be found in the rss/atom feed. Might not be set.
@@ -44,7 +44,11 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
private Feed feed;
private long feedId;
- private boolean read;
+ private int state;
+ public final static int NEW = -1;
+ public final static int UNPLAYED = 0;
+ public final static int PLAYED = 1;
+
private String paymentLink;
private FlattrStatus flattrStatus;
@@ -66,7 +70,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
private boolean autoDownload = true;
public FeedItem() {
- this.read = true;
+ this.state = UNPLAYED;
this.flattrStatus = new FlattrStatus();
this.hasChapters = false;
}
@@ -75,7 +79,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
* This constructor is used by DBReader.
* */
public FeedItem(long id, String title, String link, Date pubDate, String paymentLink, long feedId,
- FlattrStatus flattrStatus, boolean hasChapters, FeedImage image, boolean read,
+ FlattrStatus flattrStatus, boolean hasChapters, FeedImage image, int state,
String itemIdentifier, boolean autoDownload) {
this.id = id;
this.title = title;
@@ -86,7 +90,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
this.flattrStatus = flattrStatus;
this.hasChapters = hasChapters;
this.image = image;
- this.read = read;
+ this.state = state;
this.itemIdentifier = itemIdentifier;
this.autoDownload = autoDownload;
}
@@ -94,13 +98,13 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
/**
* This constructor should be used for creating test objects.
*/
- public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, boolean read, Feed feed) {
+ public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, int state, Feed feed) {
this.id = id;
this.title = title;
this.itemIdentifier = itemIdentifier;
this.link = link;
this.pubDate = (pubDate != null) ? (Date) pubDate.clone() : null;
- this.read = read;
+ this.state = state;
this.feed = feed;
this.flattrStatus = new FlattrStatus();
this.hasChapters = false;
@@ -109,13 +113,13 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
/**
* This constructor should be used for creating test objects involving chapter marks.
*/
- public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, boolean read, Feed feed, boolean hasChapters) {
+ public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, int state, Feed feed, boolean hasChapters) {
this.id = id;
this.title = title;
this.itemIdentifier = itemIdentifier;
this.link = link;
this.pubDate = (pubDate != null) ? (Date) pubDate.clone() : null;
- this.read = read;
+ this.state = state;
this.feed = feed;
this.flattrStatus = new FlattrStatus();
this.hasChapters = hasChapters;
@@ -238,12 +242,25 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
this.feed = feed;
}
- public boolean isRead() {
- return read;
+ public boolean isNew() {
+ return state == NEW;
+ }
+
+
+ public void setNew() {
+ state = NEW;
}
- public void setRead(boolean read) {
- this.read = read;
+ public boolean isPlayed() {
+ return state == PLAYED;
+ }
+
+ public void setPlayed(boolean played) {
+ if(played) {
+ state = PLAYED;
+ } else {
+ state = UNPLAYED;
+ }
}
private boolean isInProgress() {
@@ -257,11 +274,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
public void setContentEncoded(String contentEncoded) {
this.contentEncoded = contentEncoded;
}
-
- public void setFlattrStatus(FlattrStatus status) {
- this.flattrStatus = status;
- }
-
+
public FlattrStatus getFlattrStatus() {
return flattrStatus;
}
@@ -320,7 +333,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
public Uri getImageUri() {
if(media != null && media.hasEmbeddedPicture()) {
return media.getImageUri();
- } else if (hasItemImageDownloaded()) {
+ } else if (image != null) {
return image.getImageUri();
} else if (feed != null) {
return feed.getImageUri();
@@ -342,7 +355,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
return State.IN_PROGRESS;
}
}
- return (isRead() ? State.READ : State.UNREAD);
+ return (isPlayed() ? State.READ : State.UNREAD);
}
public long getFeedId() {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
index 4ad084b39..2fd5666c8 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
@@ -61,9 +61,9 @@ public class FeedItemFilter {
}
List<FeedItem> result = new ArrayList<FeedItem>();
for(FeedItem item : items) {
- if(hideUnplayed && false == item.isRead()) continue;
+ if(hideUnplayed && false == item.isPlayed()) continue;
if(hidePaused && item.getState() == FeedItem.State.IN_PROGRESS) continue;
- if(hidePlayed && item.isRead()) continue;
+ if(hidePlayed && item.isPlayed()) continue;
boolean isQueued = DBReader.getQueueIDList(context).contains(item.getId());
if(hideQueued && isQueued) continue;
if(hideNotQueued && false == isQueued) continue;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
index ee05020cc..9dbf6cc61 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
@@ -28,6 +28,15 @@ public class FeedMedia extends FeedFile implements Playable {
public static final String PREF_MEDIA_ID = "FeedMedia.PrefMediaId";
public static final String PREF_FEED_ID = "FeedMedia.PrefFeedId";
+ /**
+ * Indicates we've checked on the size of the item via the network
+ * and got an invalid response. Using Integer.MIN_VALUE because
+ * 1) we'll still check on it in case it gets downloaded (it's <= 0)
+ * 2) By default all FeedMedia have a size of 0 if we don't know it,
+ * so this won't conflict with existing practice.
+ */
+ private static final int CHECKED_ON_SIZE_BUT_UNKNOWN = Integer.MIN_VALUE;
+
private int duration;
private int position; // Current position in file
private int played_duration; // How many ms of this file have been played (for autoflattring)
@@ -35,6 +44,8 @@ public class FeedMedia extends FeedFile implements Playable {
private String mime_type;
private volatile FeedItem item;
private Date playbackCompletionDate;
+
+ // if null: unknown, will be checked
private Boolean hasEmbeddedPicture;
/* Used for loading item when restoring from parcel. */
@@ -63,6 +74,15 @@ public class FeedMedia extends FeedFile implements Playable {
? null : (Date) playbackCompletionDate.clone();
}
+ public FeedMedia(long id, FeedItem item, int duration, int position,
+ long size, String mime_type, String file_url, String download_url,
+ boolean downloaded, Date playbackCompletionDate, int played_duration,
+ Boolean hasEmbeddedPicture) {
+ this(id, item, duration, position, size, mime_type, file_url, download_url, downloaded,
+ playbackCompletionDate, played_duration);
+ this.hasEmbeddedPicture = hasEmbeddedPicture;
+ }
+
@Override
public String getHumanReadableIdentifier() {
if (item != null && item.getTitle() != null) {
@@ -175,6 +195,9 @@ public class FeedMedia extends FeedFile implements Playable {
public void setPosition(int position) {
this.position = position;
+ if(position > 0 && item.isNew()) {
+ this.item.setPlayed(false);
+ }
}
public long getSize() {
@@ -185,6 +208,18 @@ public class FeedMedia extends FeedFile implements Playable {
this.size = size;
}
+ /**
+ * Indicates we asked the service what the size was, but didn't
+ * get a valid answer and we shoudln't check using the network again.
+ */
+ public void setCheckedOnSizeButUnknown() {
+ this.size = CHECKED_ON_SIZE_BUT_UNKNOWN;
+ }
+
+ public boolean checkedOnSizeButUnknown() {
+ return (CHECKED_ON_SIZE_BUT_UNKNOWN == this.size);
+ }
+
public String getMime_type() {
return mime_type;
}
@@ -354,14 +389,16 @@ public class FeedMedia extends FeedFile implements Playable {
@Override
public void saveCurrentPosition(SharedPreferences pref, int newPosition) {
- setPosition(newPosition);
DBWriter.setFeedMediaPlaybackInformation(ClientConfig.applicationCallbacks.getApplicationInstance(), this);
+ if(item.isNew()) {
+ DBWriter.markItemRead(ClientConfig.applicationCallbacks.getApplicationInstance(), false, item.getId());
+ }
+ setPosition(newPosition);
}
@Override
public void onPlaybackStart() {
}
-
@Override
public void onPlaybackCompleted() {
@@ -429,9 +466,16 @@ public class FeedMedia extends FeedFile implements Playable {
}
}
+ public void setHasEmbeddedPicture(Boolean hasEmbeddedPicture) {
+ this.hasEmbeddedPicture = hasEmbeddedPicture;
+ }
+
@Override
public void setDownloaded(boolean downloaded) {
super.setDownloaded(downloaded);
+ if(downloaded) {
+ item.setPlayed(false);
+ }
}
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
index 2f0304182..88da865cc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.feed;
import android.content.Context;
import de.danoeh.antennapod.core.storage.DBWriter;
import org.apache.commons.lang3.StringUtils;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
/**
* Contains preferences for a single feed.
@@ -11,19 +12,26 @@ public class FeedPreferences {
private long feedID;
private boolean autoDownload;
+ public enum AutoDeleteAction {
+ GLOBAL,
+ YES,
+ NO
+ }
+ private AutoDeleteAction auto_delete_action;
private String username;
private String password;
- public FeedPreferences(long feedID, boolean autoDownload, String username, String password) {
+ public FeedPreferences(long feedID, boolean autoDownload, AutoDeleteAction auto_delete_action, String username, String password) {
this.feedID = feedID;
this.autoDownload = autoDownload;
+ this.auto_delete_action = auto_delete_action;
this.username = username;
this.password = password;
}
/**
- * Compare another FeedPreferences with this one. The feedID and autoDownload attribute are excluded from the
+ * Compare another FeedPreferences with this one. The feedID, autoDownload and AutoDeleteAction attribute are excluded from the
* comparison.
*
* @return True if the two objects are different.
@@ -41,7 +49,7 @@ public class FeedPreferences {
}
/**
- * Update this FeedPreferences object from another one. The feedID and autoDownload attributes are excluded
+ * Update this FeedPreferences object from another one. The feedID, autoDownload and AutoDeleteAction attributes are excluded
* from the update.
*/
public void updateFromOther(FeedPreferences other) {
@@ -67,6 +75,28 @@ public class FeedPreferences {
this.autoDownload = autoDownload;
}
+ public AutoDeleteAction getAutoDeleteAction() {
+ return auto_delete_action;
+ }
+
+ public void setAutoDeleteAction(AutoDeleteAction auto_delete_action) {
+ this.auto_delete_action = auto_delete_action;
+ }
+
+ public boolean getCurrentAutoDelete() {
+ switch (auto_delete_action) {
+ case GLOBAL:
+ return UserPreferences.isAutoDelete();
+
+ case YES:
+ return true;
+
+ case NO:
+ return false;
+ }
+ return false; // TODO - add exceptions here
+ }
+
public void save(Context context) {
DBWriter.setFeedPreferences(context, this);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/QueueEvent.java b/core/src/main/java/de/danoeh/antennapod/core/feed/QueueEvent.java
index c8497f509..97d086e5b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/QueueEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/QueueEvent.java
@@ -8,7 +8,7 @@ import java.util.List;
public class QueueEvent {
public enum Action {
- ADDED, ADDED_ITEMS, REMOVED, CLEARED, DELETED_MEDIA, SORTED, MOVED
+ ADDED, ADDED_ITEMS, REMOVED, IRREVERSIBLE_REMOVED, CLEARED, DELETED_MEDIA, SORTED, MOVED
}
public final Action action;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
new file mode 100644
index 000000000..0baff9723
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
@@ -0,0 +1,33 @@
+package de.danoeh.antennapod.core.glide;
+
+import android.content.Context;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.GlideBuilder;
+import com.bumptech.glide.load.DecodeFormat;
+import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.module.GlideModule;
+
+import java.io.InputStream;
+
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
+/**
+ * {@see com.bumptech.glide.integration.okhttp.OkHttpGlideModule}
+ */
+public class ApGlideModule implements GlideModule {
+
+ @Override
+ public void applyOptions(Context context, GlideBuilder builder) {
+ builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
+ builder.setDiskCache(new InternalCacheDiskCacheFactory(context,
+ UserPreferences.getImageCacheSize()));
+ }
+
+ @Override
+ public void registerComponents(Context context, Glide glide) {
+ glide.register(GlideUrl.class, InputStream.class, new ApOkHttpUrlLoader.Factory());
+ }
+
+} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideSettings.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideSettings.java
new file mode 100644
index 000000000..fc1acd0e1
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideSettings.java
@@ -0,0 +1,11 @@
+package de.danoeh.antennapod.core.glide;
+
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+
+/**
+ * The settings that AntennaPod will use for various Glide options
+ */
+public class ApGlideSettings {
+
+ public static final DiskCacheStrategy AP_DISK_CACHE_STRATEGY = DiskCacheStrategy.ALL;
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
new file mode 100644
index 000000000..c7ac146b5
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
@@ -0,0 +1,141 @@
+package de.danoeh.antennapod.core.glide;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.bumptech.glide.integration.okhttp.OkHttpStreamFetcher;
+import com.bumptech.glide.load.data.DataFetcher;
+import com.bumptech.glide.load.model.GenericLoaderFactory;
+import com.bumptech.glide.load.model.GlideUrl;
+import com.bumptech.glide.load.model.ModelLoader;
+import com.bumptech.glide.load.model.ModelLoaderFactory;
+import com.squareup.okhttp.Interceptor;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.Response;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
+import de.danoeh.antennapod.core.service.download.HttpDownloader;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.util.NetworkUtils;
+
+/**
+ * @see com.bumptech.glide.integration.okhttp.OkHttpUrlLoader
+ */
+public class ApOkHttpUrlLoader implements ModelLoader<GlideUrl, InputStream> {
+
+ private static final String TAG = ApOkHttpUrlLoader.class.getSimpleName();
+
+ /**
+ * The default factory for {@link ApOkHttpUrlLoader}s.
+ */
+ public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {
+
+ private static volatile OkHttpClient internalClient;
+ private OkHttpClient client;
+
+ private static OkHttpClient getInternalClient() {
+ if (internalClient == null) {
+ synchronized (Factory.class) {
+ if (internalClient == null) {
+ internalClient = AntennapodHttpClient.getHttpClient();
+ internalClient.interceptors().add(new NetworkAllowanceInterceptor());
+ internalClient.interceptors().add(new BasicAuthenticationInterceptor());
+ }
+ }
+ }
+ return internalClient;
+ }
+
+ /**
+ * Constructor for a new Factory that runs requests using a static singleton client.
+ */
+ public Factory() {
+ this(getInternalClient());
+ }
+
+ /**
+ * Constructor for a new Factory that runs requests using given client.
+ */
+ public Factory(OkHttpClient client) {
+ this.client = client;
+ }
+
+ @Override
+ public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
+ return new ApOkHttpUrlLoader(client);
+ }
+
+ @Override
+ public void teardown() {
+ // Do nothing, this instance doesn't own the client.
+ }
+ }
+
+ private final OkHttpClient client;
+
+ public ApOkHttpUrlLoader(OkHttpClient client) {
+ this.client = client;
+ }
+
+ @Override
+ public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
+ return new OkHttpStreamFetcher(client, model);
+ }
+
+ private static class NetworkAllowanceInterceptor implements Interceptor {
+
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ if (NetworkUtils.isDownloadAllowed()) {
+ return chain.proceed(chain.request());
+ } else {
+ return null;
+ }
+ }
+
+ }
+
+ private static class BasicAuthenticationInterceptor implements Interceptor {
+
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ com.squareup.okhttp.Request request = chain.request();
+ String url = request.urlString();
+ Context context = ClientConfig.applicationCallbacks.getApplicationInstance();
+ String authentication = DBReader.getImageAuthentication(context, url);
+
+ if(TextUtils.isEmpty(authentication)) {
+ Log.d(TAG, "no credentials for '" + url + "'");
+ return chain.proceed(request);
+ }
+
+ // add authentication
+ String[] auth = authentication.split(":");
+ String credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "ISO-8859-1");
+ com.squareup.okhttp.Request newRequest = request
+ .newBuilder()
+ .addHeader("Authorization", credentials)
+ .build();
+ Log.d(TAG, "Basic authentication with ISO-8859-1 encoding");
+ Response response = chain.proceed(newRequest);
+ if (!response.isSuccessful() && response.code() == HttpURLConnection.HTTP_UNAUTHORIZED) {
+ credentials = HttpDownloader.encodeCredentials(auth[0], auth[1], "UTF-8");
+ newRequest = request
+ .newBuilder()
+ .addHeader("Authorization", credentials)
+ .build();
+ Log.d(TAG, "Basic authentication with UTF-8 encoding");
+ return chain.proceed(newRequest);
+ } else {
+ return response;
+ }
+ }
+ }
+
+} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java
new file mode 100644
index 000000000..ee58c2f39
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java
@@ -0,0 +1,267 @@
+package de.danoeh.antennapod.core.glide;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.media.ThumbnailUtils;
+import android.util.Log;
+
+import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
+import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
+
+public class FastBlurTransformation extends BitmapTransformation {
+
+ private static final String TAG = FastBlurTransformation.class.getSimpleName();
+
+ private static final int STACK_BLUR_RADIUS = 1;
+ private static final int BLUR_IMAGE_WIDTH = 150;
+
+ public FastBlurTransformation(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected Bitmap transform(BitmapPool pool, Bitmap source,
+ int outWidth, int outHeight) {
+ int targetWidth = BLUR_IMAGE_WIDTH;
+ int targetHeight = (int) (1.0 * outHeight * targetWidth / outWidth);
+ Bitmap resized = ThumbnailUtils.extractThumbnail(source, targetWidth, targetHeight);
+ Bitmap result = fastBlur(resized, STACK_BLUR_RADIUS);
+ if (result == null) {
+ Log.w(TAG, "result was null");
+ return source;
+ }
+ return result;
+ }
+
+ @Override
+ public String getId() {
+ return "FastBlurTransformation[width=" + BLUR_IMAGE_WIDTH + "px,radius=" + STACK_BLUR_RADIUS +"]";
+ }
+
+ private static Bitmap fastBlur(Bitmap bitmap, int radius) {
+
+ // Stack Blur v1.0 from
+ // http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html
+ //
+ // Java Author: Mario Klingemann <mario at quasimondo.com>
+ // http://incubator.quasimondo.com
+ // created Feburary 29, 2004
+ // Android port : Yahel Bouaziz <yahel at kayenko.com>
+ // http://www.kayenko.com
+ // ported april 5th, 2012
+
+ // This is a compromise between Gaussian Blur and Box blur
+ // It creates much better looking blurs than Box Blur, but is
+ // 7x faster than my Gaussian Blur implementation.
+ //
+ // I called it Stack Blur because this describes best how this
+ // filter works internally: it creates a kind of moving stack
+ // of colors whilst scanning through the image. Thereby it
+ // just has to add one new block of color to the right side
+ // of the stack and remove the leftmost color. The remaining
+ // colors on the topmost layer of the stack are either added on
+ // or reduced by one, depending on if they are on the right or
+ // on the left side of the stack.
+ //
+ // If you are using this algorithm in your code please add
+ // the following line:
+ //
+ // Stack Blur Algorithm by Mario Klingemann <mario@quasimondo.com>
+
+ if (radius < 1) {
+ return null;
+ }
+
+ int w = bitmap.getWidth();
+ int h = bitmap.getHeight();
+
+ int[] pix = new int[w * h];
+ bitmap.getPixels(pix, 0, w, 0, 0, w, h);
+
+ int wm = w - 1;
+ int hm = h - 1;
+ int wh = w * h;
+ int div = radius + radius + 1;
+
+ int r[] = new int[wh];
+ int g[] = new int[wh];
+ int b[] = new int[wh];
+ int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;
+ int vmin[] = new int[Math.max(w, h)];
+
+ int divsum = (div + 1) >> 1;
+ divsum *= divsum;
+ int dv[] = new int[256 * divsum];
+ for (i = 0; i < 256 * divsum; i++) {
+ dv[i] = (i / divsum);
+ }
+
+ yw = yi = 0;
+
+ int[][] stack = new int[div][3];
+ int stackpointer;
+ int stackstart;
+ int[] sir;
+ int rbs;
+ int r1 = radius + 1;
+ int routsum, goutsum, boutsum;
+ int rinsum, ginsum, binsum;
+
+ for (y = 0; y < h; y++) {
+ rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
+ for (i = -radius; i <= radius; i++) {
+ p = pix[yi + Math.min(wm, Math.max(i, 0))];
+ sir = stack[i + radius];
+ sir[0] = (p & 0xff0000) >> 16;
+ sir[1] = (p & 0x00ff00) >> 8;
+ sir[2] = (p & 0x0000ff);
+ rbs = r1 - Math.abs(i);
+ rsum += sir[0] * rbs;
+ gsum += sir[1] * rbs;
+ bsum += sir[2] * rbs;
+ if (i > 0) {
+ rinsum += sir[0];
+ ginsum += sir[1];
+ binsum += sir[2];
+ } else {
+ routsum += sir[0];
+ goutsum += sir[1];
+ boutsum += sir[2];
+ }
+ }
+ stackpointer = radius;
+
+ for (x = 0; x < w; x++) {
+
+ r[yi] = dv[rsum];
+ g[yi] = dv[gsum];
+ b[yi] = dv[bsum];
+
+ rsum -= routsum;
+ gsum -= goutsum;
+ bsum -= boutsum;
+
+ stackstart = stackpointer - radius + div;
+ sir = stack[stackstart % div];
+
+ routsum -= sir[0];
+ goutsum -= sir[1];
+ boutsum -= sir[2];
+
+ if (y == 0) {
+ vmin[x] = Math.min(x + radius + 1, wm);
+ }
+ p = pix[yw + vmin[x]];
+
+ sir[0] = (p & 0xff0000) >> 16;
+ sir[1] = (p & 0x00ff00) >> 8;
+ sir[2] = (p & 0x0000ff);
+
+ rinsum += sir[0];
+ ginsum += sir[1];
+ binsum += sir[2];
+
+ rsum += rinsum;
+ gsum += ginsum;
+ bsum += binsum;
+
+ stackpointer = (stackpointer + 1) % div;
+ sir = stack[(stackpointer) % div];
+
+ routsum += sir[0];
+ goutsum += sir[1];
+ boutsum += sir[2];
+
+ rinsum -= sir[0];
+ ginsum -= sir[1];
+ binsum -= sir[2];
+
+ yi++;
+ }
+ yw += w;
+ }
+ for (x = 0; x < w; x++) {
+ rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;
+ yp = -radius * w;
+ for (i = -radius; i <= radius; i++) {
+ yi = Math.max(0, yp) + x;
+
+ sir = stack[i + radius];
+
+ sir[0] = r[yi];
+ sir[1] = g[yi];
+ sir[2] = b[yi];
+
+ rbs = r1 - Math.abs(i);
+
+ rsum += r[yi] * rbs;
+ gsum += g[yi] * rbs;
+ bsum += b[yi] * rbs;
+
+ if (i > 0) {
+ rinsum += sir[0];
+ ginsum += sir[1];
+ binsum += sir[2];
+ } else {
+ routsum += sir[0];
+ goutsum += sir[1];
+ boutsum += sir[2];
+ }
+
+ if (i < hm) {
+ yp += w;
+ }
+ }
+ yi = x;
+ stackpointer = radius;
+ for (y = 0; y < h; y++) {
+ // Preserve alpha channel: ( 0xff000000 & pix[yi] )
+ pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];
+
+ rsum -= routsum;
+ gsum -= goutsum;
+ bsum -= boutsum;
+
+ stackstart = stackpointer - radius + div;
+ sir = stack[stackstart % div];
+
+ routsum -= sir[0];
+ goutsum -= sir[1];
+ boutsum -= sir[2];
+
+ if (x == 0) {
+ vmin[y] = Math.min(y + r1, hm) * w;
+ }
+ p = x + vmin[y];
+
+ sir[0] = r[p];
+ sir[1] = g[p];
+ sir[2] = b[p];
+
+ rinsum += sir[0];
+ ginsum += sir[1];
+ binsum += sir[2];
+
+ rsum += rinsum;
+ gsum += ginsum;
+ bsum += binsum;
+
+ stackpointer = (stackpointer + 1) % div;
+ sir = stack[stackpointer];
+
+ routsum += sir[0];
+ goutsum += sir[1];
+ boutsum += sir[2];
+
+ rinsum -= sir[0];
+ ginsum -= sir[1];
+ binsum -= sir[2];
+
+ yi += w;
+ }
+ }
+ bitmap.setPixels(pix, 0, w, 0, 0, w, h);
+ return bitmap;
+ }
+
+} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java
index bd6210d13..efe39485e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java
@@ -93,7 +93,12 @@ public class GpodnetEpisodeAction {
if(StringUtils.isEmpty(podcast) || StringUtils.isEmpty(episode) || StringUtils.isEmpty(actionString)) {
return null;
}
- GpodnetEpisodeAction.Action action = GpodnetEpisodeAction.Action.valueOf(actionString.toUpperCase());
+ GpodnetEpisodeAction.Action action;
+ try {
+ action = GpodnetEpisodeAction.Action.valueOf(actionString.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
String deviceId = object.optString("device", "");
GpodnetEpisodeAction.Builder builder = new GpodnetEpisodeAction.Builder(podcast, episode, action)
.deviceId(deviceId);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java
index c3c6ce8c5..1401d5f39 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java
@@ -8,7 +8,6 @@ import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -217,26 +216,36 @@ public class GpodnetPreferences {
public static void removeRemovedFeeds(Collection<String> removed) {
ensurePreferencesLoaded();
+ feedListLock.lock();
removedFeeds.removeAll(removed);
writePreference(PREF_SYNC_REMOVED, removedFeeds);
+ feedListLock.unlock();
}
- public static synchronized void enqueueEpisodeAction(GpodnetEpisodeAction action) {
+ public static void enqueueEpisodeAction(GpodnetEpisodeAction action) {
ensurePreferencesLoaded();
+ feedListLock.lock();
queuedEpisodeActions.add(action);
writePreference(PREF_SYNC_EPISODE_ACTIONS, writeEpisodeActionsToString(queuedEpisodeActions));
+ feedListLock.unlock();
GpodnetSyncService.sendSyncActionsIntent(ClientConfig.applicationCallbacks.getApplicationInstance());
}
public static List<GpodnetEpisodeAction> getQueuedEpisodeActions() {
ensurePreferencesLoaded();
- return Collections.unmodifiableList(queuedEpisodeActions);
+ List<GpodnetEpisodeAction> copy = new ArrayList();
+ feedListLock.lock();
+ copy.addAll(queuedEpisodeActions);
+ feedListLock.unlock();
+ return copy;
}
- public static synchronized void removeQueuedEpisodeActions(Collection<GpodnetEpisodeAction> queued) {
+ public static void removeQueuedEpisodeActions(Collection<GpodnetEpisodeAction> queued) {
ensurePreferencesLoaded();
+ feedListLock.lock();
queuedEpisodeActions.removeAll(queued);
writePreference(PREF_SYNC_EPISODE_ACTIONS, writeEpisodeActionsToString(queuedEpisodeActions));
+ feedListLock.unlock();
}
/**
@@ -252,12 +261,14 @@ public class GpodnetPreferences {
setUsername(null);
setPassword(null);
setDeviceID(null);
+ feedListLock.lock();
addedFeeds.clear();
writePreference(PREF_SYNC_ADDED, addedFeeds);
removedFeeds.clear();
writePreference(PREF_SYNC_REMOVED, removedFeeds);
queuedEpisodeActions.clear();
writePreference(PREF_SYNC_EPISODE_ACTIONS, writeEpisodeActionsToString(queuedEpisodeActions));
+ feedListLock.unlock();
setLastSubscriptionSyncTimestamp(0);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
index 714f1b051..dfe056f14 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
@@ -3,11 +3,7 @@ package de.danoeh.antennapod.core.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
-import android.util.Log;
-import org.apache.commons.lang3.Validate;
-
-import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.EventDistributor;
/**
@@ -15,159 +11,104 @@ import de.danoeh.antennapod.core.feed.EventDistributor;
* instance of this class must first be instantiated via createInstance() or
* otherwise every public method will throw an Exception when called.
*/
-public class PlaybackPreferences implements
- SharedPreferences.OnSharedPreferenceChangeListener {
- private static final String TAG = "PlaybackPreferences";
-
- /**
- * Contains the feed id of the currently playing item if it is a FeedMedia
- * object.
- */
- public static final String PREF_CURRENTLY_PLAYING_FEED_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedId";
-
- /**
- * Contains the id of the currently playing FeedMedia object or
- * NO_MEDIA_PLAYING if the currently playing media is no FeedMedia object.
- */
- public static final String PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedMediaId";
-
- /**
- * Type of the media object that is currently being played. This preference
- * is set to NO_MEDIA_PLAYING after playback has been completed and is set
- * as soon as the 'play' button is pressed.
- */
- public static final String PREF_CURRENTLY_PLAYING_MEDIA = "de.danoeh.antennapod.preferences.currentlyPlayingMedia";
-
- /** True if last played media was streamed. */
- public static final String PREF_CURRENT_EPISODE_IS_STREAM = "de.danoeh.antennapod.preferences.lastIsStream";
-
- /** True if last played media was a video. */
- public static final String PREF_CURRENT_EPISODE_IS_VIDEO = "de.danoeh.antennapod.preferences.lastIsVideo";
-
- /** The current player status as int. */
+public class PlaybackPreferences implements SharedPreferences.OnSharedPreferenceChangeListener {
+
+ private static final String TAG = "PlaybackPreferences";
+
+ /**
+ * Contains the feed id of the currently playing item if it is a FeedMedia
+ * object.
+ */
+ public static final String PREF_CURRENTLY_PLAYING_FEED_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedId";
+
+ /**
+ * Contains the id of the currently playing FeedMedia object or
+ * NO_MEDIA_PLAYING if the currently playing media is no FeedMedia object.
+ */
+ public static final String PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID = "de.danoeh.antennapod.preferences.lastPlayedFeedMediaId";
+
+ /**
+ * Type of the media object that is currently being played. This preference
+ * is set to NO_MEDIA_PLAYING after playback has been completed and is set
+ * as soon as the 'play' button is pressed.
+ */
+ public static final String PREF_CURRENTLY_PLAYING_MEDIA = "de.danoeh.antennapod.preferences.currentlyPlayingMedia";
+
+ /**
+ * True if last played media was streamed.
+ */
+ public static final String PREF_CURRENT_EPISODE_IS_STREAM = "de.danoeh.antennapod.preferences.lastIsStream";
+
+ /**
+ * True if last played media was a video.
+ */
+ public static final String PREF_CURRENT_EPISODE_IS_VIDEO = "de.danoeh.antennapod.preferences.lastIsVideo";
+
+ /**
+ * The current player status as int.
+ */
public static final String PREF_CURRENT_PLAYER_STATUS = "de.danoeh.antennapod.preferences.currentPlayerStatus";
- /** Value of PREF_CURRENTLY_PLAYING_MEDIA if no media is playing. */
- public static final long NO_MEDIA_PLAYING = -1;
+ /**
+ * Value of PREF_CURRENTLY_PLAYING_MEDIA if no media is playing.
+ */
+ public static final long NO_MEDIA_PLAYING = -1;
- /** Value of PREF_CURRENT_PLAYER_STATUS if media player status is playing. */
+ /**
+ * Value of PREF_CURRENT_PLAYER_STATUS if media player status is playing.
+ */
public static final int PLAYER_STATUS_PLAYING = 1;
- /** Value of PREF_CURRENT_PLAYER_STATUS if media player status is paused. */
+ /**
+ * Value of PREF_CURRENT_PLAYER_STATUS if media player status is paused.
+ */
public static final int PLAYER_STATUS_PAUSED = 2;
- /** Value of PREF_CURRENT_PLAYER_STATUS if media player status is neither playing nor paused. */
+ /**
+ * Value of PREF_CURRENT_PLAYER_STATUS if media player status is neither playing nor paused.
+ */
public static final int PLAYER_STATUS_OTHER = 3;
- private long currentlyPlayingFeedId;
- private long currentlyPlayingFeedMediaId;
- private long currentlyPlayingMedia;
- private boolean currentEpisodeIsStream;
- private boolean currentEpisodeIsVideo;
- private int currentPlayerStatus;
-
- private static PlaybackPreferences instance;
- private Context context;
-
- private PlaybackPreferences(Context context) {
- this.context = context;
- loadPreferences();
- }
+ private static PlaybackPreferences instance;
+ private static SharedPreferences prefs;
- /**
- * Sets up the UserPreferences class.
- *
- * @throws IllegalArgumentException
- * if context is null
- * */
- public static void createInstance(Context context) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Creating new instance of UserPreferences");
- Validate.notNull(context);
-
- instance = new PlaybackPreferences(context);
-
- PreferenceManager.getDefaultSharedPreferences(context)
- .registerOnSharedPreferenceChangeListener(instance);
- }
+ private PlaybackPreferences() {
+ }
- private void loadPreferences() {
- SharedPreferences sp = PreferenceManager
- .getDefaultSharedPreferences(context);
- currentlyPlayingFeedId = sp.getLong(PREF_CURRENTLY_PLAYING_FEED_ID, -1);
- currentlyPlayingFeedMediaId = sp.getLong(
- PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING);
- currentlyPlayingMedia = sp.getLong(PREF_CURRENTLY_PLAYING_MEDIA,
- NO_MEDIA_PLAYING);
- currentEpisodeIsStream = sp.getBoolean(PREF_CURRENT_EPISODE_IS_STREAM, true);
- currentEpisodeIsVideo = sp.getBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, false);
- currentPlayerStatus = sp.getInt(PREF_CURRENT_PLAYER_STATUS,
- PLAYER_STATUS_OTHER);
- }
+ public static void init(Context context) {
+ instance = new PlaybackPreferences();
+ prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.registerOnSharedPreferenceChangeListener(instance);
+ }
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
- if (key.equals(PREF_CURRENTLY_PLAYING_FEED_ID)) {
- currentlyPlayingFeedId = sp.getLong(PREF_CURRENTLY_PLAYING_FEED_ID,
- -1);
-
- } else if (key.equals(PREF_CURRENTLY_PLAYING_MEDIA)) {
- currentlyPlayingMedia = sp
- .getLong(PREF_CURRENTLY_PLAYING_MEDIA, -1);
-
- } else if (key.equals(PREF_CURRENT_EPISODE_IS_STREAM)) {
- currentEpisodeIsStream = sp.getBoolean(PREF_CURRENT_EPISODE_IS_STREAM, true);
-
- } else if (key.equals(PREF_CURRENT_EPISODE_IS_VIDEO)) {
- currentEpisodeIsVideo = sp.getBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, false);
-
- } else if (key.equals(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID)) {
- currentlyPlayingFeedMediaId = sp.getLong(
- PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING);
- }
- else if (key.equals(PREF_CURRENT_PLAYER_STATUS)) {
- currentPlayerStatus = sp.getInt(PREF_CURRENT_PLAYER_STATUS,
- PLAYER_STATUS_OTHER);
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (key.equals(PREF_CURRENT_PLAYER_STATUS)) {
EventDistributor.getInstance().sendPlayerStatusUpdateBroadcast();
}
- }
-
- private static void instanceAvailable() {
- if (instance == null) {
- throw new IllegalStateException(
- "UserPreferences was used before being set up");
- }
- }
-
+ }
public static long getLastPlayedFeedId() {
- instanceAvailable();
- return instance.currentlyPlayingFeedId;
+ return prefs.getLong(PREF_CURRENTLY_PLAYING_FEED_ID, -1);
}
public static long getCurrentlyPlayingMedia() {
- instanceAvailable();
- return instance.currentlyPlayingMedia;
+ return prefs.getLong(PREF_CURRENTLY_PLAYING_MEDIA, NO_MEDIA_PLAYING);
}
public static long getCurrentlyPlayingFeedMediaId() {
- return instance.currentlyPlayingFeedMediaId;
+ return prefs.getLong(PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID, NO_MEDIA_PLAYING);
}
public static boolean getCurrentEpisodeIsStream() {
- instanceAvailable();
- return instance.currentEpisodeIsStream;
+ return prefs.getBoolean(PREF_CURRENT_EPISODE_IS_STREAM, true);
}
public static boolean getCurrentEpisodeIsVideo() {
- instanceAvailable();
- return instance.currentEpisodeIsVideo;
+ return prefs.getBoolean(PREF_CURRENT_EPISODE_IS_VIDEO, false);
}
public static int getCurrentPlayerStatus() {
- instanceAvailable();
- return instance.currentPlayerStatus;
+ return prefs.getInt(PREF_CURRENT_PLAYER_STATUS, PLAYER_STATUS_OTHER);
}
-
}
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 594241573..d56829c70 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
@@ -5,6 +5,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
@@ -18,6 +19,7 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -29,21 +31,23 @@ import de.danoeh.antennapod.core.receiver.FeedUpdateReceiver;
/**
* Provides access to preferences set by the user in the settings screen. A
* private instance of this class must first be instantiated via
- * createInstance() or otherwise every public method will throw an Exception
+ * init() or otherwise every public method will throw an Exception
* when called.
*/
-public class UserPreferences implements
- SharedPreferences.OnSharedPreferenceChangeListener {
+public class UserPreferences {
public static final String IMPORT_DIR = "import/";
private static final String TAG = "UserPreferences";
- // User Infercasce
+ // User Interface
public static final String PREF_THEME = "prefTheme";
public static final String PREF_HIDDEN_DRAWER_ITEMS = "prefHiddenDrawerItems";
+ public static final String PREF_DRAWER_FEED_ORDER = "prefDrawerFeedOrder";
+ public static final String PREF_DRAWER_FEED_COUNTER = "prefDrawerFeedCounter";
public static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify";
public static final String PREF_PERSISTENT_NOTIFICATION = "prefPersistNotify";
+ public static final String PREF_SHOW_DOWNLOAD_REPORT = "prefShowDownloadReport";
// Queue
public static final String PREF_QUEUE_ADD_TO_FRONT = "prefQueueAddToFront";
@@ -54,7 +58,7 @@ public class UserPreferences implements
public static final String PREF_FOLLOW_QUEUE = "prefFollowQueue";
public static final String PREF_AUTO_DELETE = "prefAutoDelete";
public static final String PREF_SMART_MARK_AS_PLAYED_SECS = "prefSmartMarkAsPlayedSecs";
- private static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray";
+ public static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray";
public static final String PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS = "prefPauseForFocusLoss";
public static final String PREF_RESUME_AFTER_CALL = "prefResumeAfterCall";
@@ -74,193 +78,42 @@ public class UserPreferences implements
// Other
public static final String PREF_DATA_FOLDER = "prefDataFolder";
+ public static final String PREF_IMAGE_CACHE_SIZE = "prefImageCacheSize";
// Mediaplayer
public static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
private static final String PREF_FAST_FORWARD_SECS = "prefFastForwardSecs";
private static final String PREF_REWIND_SECS = "prefRewindSecs";
public static final String PREF_QUEUE_LOCKED = "prefQueueLocked";
+ public static final String IMAGE_CACHE_DEFAULT_VALUE = "100";
+ public static final int IMAGE_CACHE_SIZE_MINIMUM = 20;
- // TODO: Make this value configurable
- private static final float PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT = 0.8f;
-
+ // Constants
private static int EPISODE_CACHE_SIZE_UNLIMITED = -1;
+ public static int FEED_ORDER_UNPLAYED_EPISODES = 0;
+ public static int FEED_ORDER_ALPHABETICAL = 1;
+ public static int FEED_COUNTER_SHOW_NEW_UNPLAYED_SUM = 0;
+ public static int FEED_COUNTER_SHOW_NEW = 1;
+ public static int FEED_COUNTER_SHOW_UNPLAYED = 2;
+ public static int FEED_COUNTER_SHOW_NONE = 3;
- private static UserPreferences instance;
- private final Context context;
-
- // User Interface
- private int theme;
- private List<String> hiddenDrawerItems;
- private int notifyPriority;
- private boolean persistNotify;
-
- // Queue
- private boolean enqueueAtFront;
-
- // Playback
- private boolean pauseOnHeadsetDisconnect;
- private boolean unpauseOnHeadsetReconnect;
- private boolean followQueue;
- private boolean autoDelete;
- private int smartMarkAsPlayedSecs;
- private String[] playbackSpeedArray;
- private boolean pauseForFocusLoss;
- private boolean resumeAfterCall;
-
- // Network
- private long updateInterval;
- private boolean allowMobileUpdate;
- private int parallelDownloads;
- private int episodeCacheSize;
- private boolean enableAutodownload;
- private boolean enableAutodownloadOnBattery;
- private boolean enableAutodownloadWifiFilter;
- private String[] autodownloadSelectedNetworks;
-
- // Services
- private boolean autoFlattr;
- private float autoFlattrPlayedDurationThreshold;
-
- // Settings somewhere in the GUI
- private String playbackSpeed;
- private int fastForwardSecs;
- private int rewindSecs;
- private boolean queueLocked;
-
-
- private UserPreferences(Context context) {
- this.context = context;
- loadPreferences();
- }
+ private static Context context;
+ private static SharedPreferences prefs;
/**
* Sets up the UserPreferences class.
*
* @throws IllegalArgumentException if context is null
*/
- public static void createInstance(Context context) {
+ public static void init(Context context) {
Log.d(TAG, "Creating new instance of UserPreferences");
Validate.notNull(context);
- instance = new UserPreferences(context);
+ UserPreferences.context = context;
+ UserPreferences.prefs = PreferenceManager.getDefaultSharedPreferences(context);
createImportDirectory();
createNoMediaFile();
- PreferenceManager.getDefaultSharedPreferences(context)
- .registerOnSharedPreferenceChangeListener(instance);
-
- }
-
- private void loadPreferences() {
- SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
-
- // User Interface
- theme = readThemeValue(sp.getString(PREF_THEME, "0"));
- if (sp.getBoolean(PREF_EXPANDED_NOTIFICATION, false)) {
- notifyPriority = NotificationCompat.PRIORITY_MAX;
- } else {
- notifyPriority = NotificationCompat.PRIORITY_DEFAULT;
- }
- hiddenDrawerItems = Arrays.asList(StringUtils.split(sp.getString(PREF_HIDDEN_DRAWER_ITEMS, ""), ','));
- persistNotify = sp.getBoolean(PREF_PERSISTENT_NOTIFICATION, false);
-
- // Queue
- enqueueAtFront = sp.getBoolean(PREF_QUEUE_ADD_TO_FRONT, false);
-
- // Playback
- pauseOnHeadsetDisconnect = sp.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
- unpauseOnHeadsetReconnect = sp.getBoolean(PREF_UNPAUSE_ON_HEADSET_RECONNECT, true);
- followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
- autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
- smartMarkAsPlayedSecs = Integer.valueOf(sp.getString(PREF_SMART_MARK_AS_PLAYED_SECS, "30"));
- playbackSpeedArray = readPlaybackSpeedArray(sp.getString(
- PREF_PLAYBACK_SPEED_ARRAY, null));
- pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
-
- // Network
- updateInterval = readUpdateInterval(sp.getString(PREF_UPDATE_INTERVAL, "0"));
- allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
- parallelDownloads = Integer.valueOf(sp.getString(PREF_PARALLEL_DOWNLOADS, "6"));
- EPISODE_CACHE_SIZE_UNLIMITED = context.getResources().getInteger(
- R.integer.episode_cache_size_unlimited);
- episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(PREF_EPISODE_CACHE_SIZE, "20"));
- enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
- enableAutodownloadOnBattery = sp.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true);
- enableAutodownloadWifiFilter = sp.getBoolean(PREF_ENABLE_AUTODL_WIFI_FILTER, false);
- autodownloadSelectedNetworks = StringUtils.split(
- sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ',');
-
- // Services
- autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false);
- autoFlattrPlayedDurationThreshold = sp.getFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD,
- PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT);
-
- // MediaPlayer
- playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
- fastForwardSecs = sp.getInt(PREF_FAST_FORWARD_SECS, 30);
- rewindSecs = sp.getInt(PREF_REWIND_SECS, 30);
- queueLocked = sp.getBoolean(PREF_QUEUE_LOCKED, false);
- }
-
- private int readThemeValue(String valueFromPrefs) {
- switch (Integer.parseInt(valueFromPrefs)) {
- case 0:
- return R.style.Theme_AntennaPod_Light;
- case 1:
- return R.style.Theme_AntennaPod_Dark;
- default:
- return R.style.Theme_AntennaPod_Light;
- }
- }
-
- private long readUpdateInterval(String valueFromPrefs) {
- int hours = Integer.parseInt(valueFromPrefs);
- return TimeUnit.HOURS.toMillis(hours);
- }
-
- private int readEpisodeCacheSizeInternal(String valueFromPrefs) {
- if (valueFromPrefs.equals(context
- .getString(R.string.pref_episode_cache_unlimited))) {
- return EPISODE_CACHE_SIZE_UNLIMITED;
- } else {
- return Integer.valueOf(valueFromPrefs);
- }
- }
-
- private String[] readPlaybackSpeedArray(String valueFromPrefs) {
- String[] selectedSpeeds = null;
- // If this preference hasn't been set yet, return the default options
- if (valueFromPrefs == null) {
- String[] allSpeeds = context.getResources().getStringArray(
- R.array.playback_speed_values);
- List<String> speedList = new LinkedList<String>();
- for (String speedStr : allSpeeds) {
- float speed = Float.parseFloat(speedStr);
- if (speed < 2.0001 && speed * 10 % 1 == 0) {
- speedList.add(speedStr);
- }
- }
- selectedSpeeds = speedList.toArray(new String[speedList.size()]);
- } else {
- try {
- JSONArray jsonArray = new JSONArray(valueFromPrefs);
- selectedSpeeds = new String[jsonArray.length()];
- for (int i = 0; i < jsonArray.length(); i++) {
- selectedSpeeds[i] = jsonArray.getString(i);
- }
- } catch (JSONException e) {
- Log.e(TAG, "Got JSON error when trying to get speeds from JSONArray");
- e.printStackTrace();
- }
- }
- return selectedSpeeds;
- }
-
- private static void instanceAvailable() {
- if (instance == null) {
- throw new IllegalStateException("UserPreferences was used before being set up");
- }
}
/**
@@ -269,8 +122,7 @@ public class UserPreferences implements
* @return R.style.Theme_AntennaPod_Light or R.style.Theme_AntennaPod_Dark
*/
public static int getTheme() {
- instanceAvailable();
- return instance.theme;
+ return readThemeValue(prefs.getString(PREF_THEME, "0"));
}
public static int getNoTitleTheme() {
@@ -283,8 +135,18 @@ public class UserPreferences implements
}
public static List<String> getHiddenDrawerItems() {
- instanceAvailable();
- return new ArrayList<String>(instance.hiddenDrawerItems);
+ String hiddenItems = prefs.getString(PREF_HIDDEN_DRAWER_ITEMS, "");
+ return new ArrayList<String>(Arrays.asList(StringUtils.split(hiddenItems, ',')));
+ }
+
+ public static int getFeedOrder() {
+ String value = prefs.getString(PREF_DRAWER_FEED_ORDER, "0");
+ return Integer.valueOf(value);
+ }
+
+ public static int getFeedCounter() {
+ String value = prefs.getString(PREF_DRAWER_FEED_COUNTER, "0");
+ return Integer.valueOf(value);
}
/**
@@ -293,8 +155,11 @@ public class UserPreferences implements
* @return NotificationCompat.PRIORITY_MAX or NotificationCompat.PRIORITY_DEFAULT
*/
public static int getNotifyPriority() {
- instanceAvailable();
- return instance.notifyPriority;
+ if (prefs.getBoolean(PREF_EXPANDED_NOTIFICATION, false)) {
+ return NotificationCompat.PRIORITY_MAX;
+ } else {
+ return NotificationCompat.PRIORITY_DEFAULT;
+ }
}
/**
@@ -303,8 +168,16 @@ public class UserPreferences implements
* @return {@code true} if notifications are persistent, {@code false} otherwise
*/
public static boolean isPersistNotify() {
- instanceAvailable();
- return instance.persistNotify;
+ return prefs.getBoolean(PREF_PERSISTENT_NOTIFICATION, false);
+ }
+
+ /**
+ * Returns true if download reports are shown
+ *
+ * @return {@code true} if download reports are shown, {@code false} otherwise
+ */
+ public static boolean showDownloadReport() {
+ return prefs.getBoolean(PREF_SHOW_DOWNLOAD_REPORT, true);
}
/**
@@ -313,73 +186,77 @@ public class UserPreferences implements
* @return {@code true} if new queue elements are added to the front; {@code false} otherwise
*/
public static boolean enqueueAtFront() {
- instanceAvailable();
- return instance.enqueueAtFront;
+ return prefs.getBoolean(PREF_QUEUE_ADD_TO_FRONT, false);
}
public static boolean isPauseOnHeadsetDisconnect() {
- instanceAvailable();
- return instance.pauseOnHeadsetDisconnect;
+ return prefs.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
}
public static boolean isUnpauseOnHeadsetReconnect() {
- instanceAvailable();
- return instance.unpauseOnHeadsetReconnect;
+ return prefs.getBoolean(PREF_UNPAUSE_ON_HEADSET_RECONNECT, true);
}
public static boolean isFollowQueue() {
- instanceAvailable();
- return instance.followQueue;
+ return prefs.getBoolean(PREF_FOLLOW_QUEUE, false);
}
public static boolean isAutoDelete() {
- instanceAvailable();
- return instance.autoDelete;
+ return prefs.getBoolean(PREF_AUTO_DELETE, false);
}
public static int getSmartMarkAsPlayedSecs() {
- instanceAvailable();
- return instance.smartMarkAsPlayedSecs;
+ return Integer.valueOf(prefs.getString(PREF_SMART_MARK_AS_PLAYED_SECS, "30"));
}
public static boolean isAutoFlattr() {
- instanceAvailable();
- return instance.autoFlattr;
+ return prefs.getBoolean(PREF_AUTO_FLATTR, false);
}
public static String getPlaybackSpeed() {
- instanceAvailable();
- return instance.playbackSpeed;
+ return prefs.getString(PREF_PLAYBACK_SPEED, "1.0");
}
public static String[] getPlaybackSpeedArray() {
- instanceAvailable();
- return instance.playbackSpeedArray;
+ return readPlaybackSpeedArray(prefs.getString(PREF_PLAYBACK_SPEED_ARRAY, null));
}
public static boolean shouldPauseForFocusLoss() {
- instanceAvailable();
- return instance.pauseForFocusLoss;
+ return prefs.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
}
public static long getUpdateInterval() {
- instanceAvailable();
- return instance.updateInterval;
+ String updateInterval = prefs.getString(PREF_UPDATE_INTERVAL, "0");
+ if(false == updateInterval.contains(":")) {
+ return readUpdateInterval(updateInterval);
+ } else {
+ return 0;
+ }
+ }
+
+ public static int[] getUpdateTimeOfDay() {
+ String datetime = prefs.getString(PREF_UPDATE_INTERVAL, "");
+ if(datetime.length() >= 3 && datetime.contains(":")) {
+ String[] parts = datetime.split(":");
+ int hourOfDay = Integer.valueOf(parts[0]);
+ int minute = Integer.valueOf(parts[1]);
+ return new int[] { hourOfDay, minute };
+ } else {
+ return new int[0];
+ }
}
public static boolean isAllowMobileUpdate() {
- instanceAvailable();
- return instance.allowMobileUpdate;
+ return prefs.getBoolean(PREF_MOBILE_UPDATE, false);
}
public static int getParallelDownloads() {
- instanceAvailable();
- return instance.parallelDownloads;
+ return Integer.valueOf(prefs.getString(PREF_PARALLEL_DOWNLOADS, "4"));
}
public static int getEpisodeCacheSizeUnlimited() {
- return EPISODE_CACHE_SIZE_UNLIMITED;
+ return context.getResources().getInteger(R.integer.episode_cache_size_unlimited);
}
/**
@@ -388,33 +265,40 @@ public class UserPreferences implements
* 'unlimited'.
*/
public static int getEpisodeCacheSize() {
- instanceAvailable();
- return instance.episodeCacheSize;
+ return readEpisodeCacheSizeInternal(prefs.getString(PREF_EPISODE_CACHE_SIZE, "20"));
}
public static boolean isEnableAutodownload() {
- instanceAvailable();
- return instance.enableAutodownload;
+ return prefs.getBoolean(PREF_ENABLE_AUTODL, false);
}
public static boolean isEnableAutodownloadOnBattery() {
- instanceAvailable();
- return instance.enableAutodownloadOnBattery;
+ return prefs.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true);
}
public static boolean isEnableAutodownloadWifiFilter() {
- instanceAvailable();
- return instance.enableAutodownloadWifiFilter;
+ return prefs.getBoolean(PREF_ENABLE_AUTODL_WIFI_FILTER, false);
+ }
+
+ public static int getImageCacheSize() {
+ String cacheSizeString = prefs.getString(PREF_IMAGE_CACHE_SIZE, IMAGE_CACHE_DEFAULT_VALUE);
+ int cacheSizeInt = Integer.valueOf(cacheSizeString);
+ // if the cache size is too small the user won't get any images at all
+ // that's bad, force it back to the default.
+ if (cacheSizeInt < IMAGE_CACHE_SIZE_MINIMUM) {
+ prefs.edit().putString(PREF_IMAGE_CACHE_SIZE, IMAGE_CACHE_DEFAULT_VALUE).apply();
+ cacheSizeInt = Integer.valueOf(IMAGE_CACHE_DEFAULT_VALUE);
+ }
+ int cacheSizeMB = cacheSizeInt * 1024 * 1024;
+ return cacheSizeMB;
}
public static int getFastFowardSecs() {
- instanceAvailable();
- return instance.fastForwardSecs;
+ return prefs.getInt(PREF_FAST_FORWARD_SECS, 30);
}
public static int getRewindSecs() {
- instanceAvailable();
- return instance.rewindSecs;
+ return prefs.getInt(PREF_REWIND_SECS, 30);
}
@@ -423,145 +307,38 @@ public class UserPreferences implements
* duration.
*/
public static float getAutoFlattrPlayedDurationThreshold() {
- instanceAvailable();
- return instance.autoFlattrPlayedDurationThreshold;
+ return prefs.getFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD, 0.8f);
}
public static String[] getAutodownloadSelectedNetworks() {
- instanceAvailable();
- return instance.autodownloadSelectedNetworks;
+ String selectedNetWorks = prefs.getString(PREF_AUTODL_SELECTED_NETWORKS, "");
+ return StringUtils.split(selectedNetWorks, ',');
}
public static boolean shouldResumeAfterCall() {
- instanceAvailable();
- return instance.resumeAfterCall;
+ return prefs.getBoolean(PREF_RESUME_AFTER_CALL, true);
}
public static boolean isQueueLocked() {
- instanceAvailable();
- return instance.queueLocked;
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sp, String key) {
- Log.d(TAG, "Registered change of user preferences. Key: " + key);
- switch(key) {
- // User Interface
- case PREF_THEME:
- theme = readThemeValue(sp.getString(PREF_THEME, ""));
- break;
- case PREF_HIDDEN_DRAWER_ITEMS:
- hiddenDrawerItems = Arrays.asList(StringUtils.split(sp.getString(PREF_HIDDEN_DRAWER_ITEMS, ""), ','));
- break;
- case PREF_EXPANDED_NOTIFICATION:
- if (sp.getBoolean(PREF_EXPANDED_NOTIFICATION, false)) {
- notifyPriority = NotificationCompat.PRIORITY_MAX;
- } else {
- notifyPriority = NotificationCompat.PRIORITY_DEFAULT;
- }
- break;
- case PREF_PERSISTENT_NOTIFICATION:
- persistNotify = sp.getBoolean(PREF_PERSISTENT_NOTIFICATION, false);
- break;
- // Queue
- case PREF_QUEUE_ADD_TO_FRONT:
- enqueueAtFront = sp.getBoolean(PREF_QUEUE_ADD_TO_FRONT, false);
- break;
- // Playback
- case PREF_PAUSE_ON_HEADSET_DISCONNECT:
- pauseOnHeadsetDisconnect = sp.getBoolean(PREF_PAUSE_ON_HEADSET_DISCONNECT, true);
- break;
- case PREF_UNPAUSE_ON_HEADSET_RECONNECT:
- unpauseOnHeadsetReconnect = sp.getBoolean(PREF_UNPAUSE_ON_HEADSET_RECONNECT, true);
- break;
- case PREF_FOLLOW_QUEUE:
- followQueue = sp.getBoolean(PREF_FOLLOW_QUEUE, false);
- break;
- case PREF_AUTO_DELETE:
- autoDelete = sp.getBoolean(PREF_AUTO_DELETE, false);
- break;
- case PREF_SMART_MARK_AS_PLAYED_SECS:
- smartMarkAsPlayedSecs = Integer.valueOf(sp.getString(PREF_SMART_MARK_AS_PLAYED_SECS, "30"));
- break;
- case PREF_PLAYBACK_SPEED_ARRAY:
- playbackSpeedArray = readPlaybackSpeedArray(sp.getString(PREF_PLAYBACK_SPEED_ARRAY, null));
- break;
- case PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS:
- pauseForFocusLoss = sp.getBoolean(PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, false);
- break;
- case PREF_RESUME_AFTER_CALL:
- resumeAfterCall = sp.getBoolean(PREF_RESUME_AFTER_CALL, true);
- break;
- // Network
- case PREF_UPDATE_INTERVAL:
- updateInterval = readUpdateInterval(sp.getString(PREF_UPDATE_INTERVAL, "0"));
- ClientConfig.applicationCallbacks.setUpdateInterval(updateInterval);
- break;
- case PREF_MOBILE_UPDATE:
- allowMobileUpdate = sp.getBoolean(PREF_MOBILE_UPDATE, false);
- break;
- case PREF_PARALLEL_DOWNLOADS:
- parallelDownloads = Integer.valueOf(sp.getString(PREF_PARALLEL_DOWNLOADS, "6"));
- break;
- case PREF_EPISODE_CACHE_SIZE:
- episodeCacheSize = readEpisodeCacheSizeInternal(sp.getString(PREF_EPISODE_CACHE_SIZE, "20"));
- break;
- case PREF_ENABLE_AUTODL:
- enableAutodownload = sp.getBoolean(PREF_ENABLE_AUTODL, false);
- break;
- case PREF_ENABLE_AUTODL_ON_BATTERY:
- enableAutodownloadOnBattery = sp.getBoolean(PREF_ENABLE_AUTODL_ON_BATTERY, true);
- break;
- case PREF_ENABLE_AUTODL_WIFI_FILTER:
- enableAutodownloadWifiFilter = sp.getBoolean(PREF_ENABLE_AUTODL_WIFI_FILTER, false);
- break;
- case PREF_AUTODL_SELECTED_NETWORKS:
- autodownloadSelectedNetworks = StringUtils.split(
- sp.getString(PREF_AUTODL_SELECTED_NETWORKS, ""), ',');
- break;
- // Services
- case PREF_AUTO_FLATTR:
- autoFlattr = sp.getBoolean(PREF_AUTO_FLATTR, false);
- break;
- case PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD:
- autoFlattrPlayedDurationThreshold = sp.getFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD,
- PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD_DEFAULT);
- break;
- // Mediaplayer
- case PREF_PLAYBACK_SPEED:
- playbackSpeed = sp.getString(PREF_PLAYBACK_SPEED, "1.0");
- break;
- case PREF_FAST_FORWARD_SECS:
- fastForwardSecs = sp.getInt(PREF_FAST_FORWARD_SECS, 30);
- break;
- case PREF_REWIND_SECS:
- rewindSecs = sp.getInt(PREF_REWIND_SECS, 30);
- break;
- case PREF_QUEUE_LOCKED:
- queueLocked = sp.getBoolean(PREF_QUEUE_LOCKED, false);
- break;
- default:
- Log.w(TAG, "Unhandled key: " + key);
- }
+ return prefs.getBoolean(PREF_QUEUE_LOCKED, false);
}
public static void setPrefFastForwardSecs(int secs) {
- Log.d(TAG, "setPrefFastForwardSecs(" + secs +")");
- SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(instance.context).edit();
- editor.putInt(PREF_FAST_FORWARD_SECS, secs);
- editor.commit();
+ prefs.edit()
+ .putInt(PREF_FAST_FORWARD_SECS, secs)
+ .apply();
}
public static void setPrefRewindSecs(int secs) {
- Log.d(TAG, "setPrefRewindSecs(" + secs +")");
- SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(instance.context).edit();
- editor.putInt(PREF_REWIND_SECS, secs);
- editor.commit();
+ prefs.edit()
+ .putInt(PREF_REWIND_SECS, secs)
+ .apply();
}
public static void setPlaybackSpeed(String speed) {
- PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
- .putString(PREF_PLAYBACK_SPEED, speed).apply();
+ prefs.edit()
+ .putString(PREF_PLAYBACK_SPEED, speed)
+ .apply();
}
public static void setPlaybackSpeedArray(String[] speeds) {
@@ -569,72 +346,117 @@ public class UserPreferences implements
for (String speed : speeds) {
jsonArray.put(speed);
}
- PreferenceManager.getDefaultSharedPreferences(instance.context).edit()
- .putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString())
- .apply();
+ prefs.edit()
+ .putString(PREF_PLAYBACK_SPEED_ARRAY, jsonArray.toString())
+ .apply();
+ }
+
+ public static void setAutodownloadSelectedNetworks(String[] value) {
+ prefs.edit()
+ .putString(PREF_AUTODL_SELECTED_NETWORKS, StringUtils.join(value, ','))
+ .apply();
}
- public static void setAutodownloadSelectedNetworks(Context context,
- String[] value) {
- SharedPreferences.Editor editor = PreferenceManager
- .getDefaultSharedPreferences(context.getApplicationContext())
- .edit();
- editor.putString(PREF_AUTODL_SELECTED_NETWORKS,
- StringUtils.join(value, ','));
- editor.commit();
+ /**
+ * Sets the update interval value.
+ */
+ public static void setUpdateInterval(long hours) {
+ prefs.edit()
+ .putString(PREF_UPDATE_INTERVAL, String.valueOf(hours))
+ .apply();
+ // when updating with an interval, we assume the user wants
+ // to update *now* and then every 'hours' interval thereafter.
+ restartUpdateAlarm(true);
}
/**
- * Sets the update interval value. Should only be used for testing purposes!
+ * Sets the update interval value.
*/
- public static void setUpdateInterval(Context context, long newValue) {
- instanceAvailable();
- SharedPreferences.Editor editor = PreferenceManager
- .getDefaultSharedPreferences(context.getApplicationContext())
- .edit();
- editor.putString(PREF_UPDATE_INTERVAL,
- String.valueOf(newValue));
- editor.commit();
- instance.updateInterval = newValue;
+ public static void setUpdateTimeOfDay(int hourOfDay, int minute) {
+ prefs.edit()
+ .putString(PREF_UPDATE_INTERVAL, hourOfDay + ":" + minute)
+ .apply();
+ restartUpdateAlarm(false);
}
/**
* Change the auto-flattr settings
*
- * @param context For accessing the shared preferences
* @param enabled Whether automatic flattring should be enabled at all
* @param autoFlattrThreshold The percentage of playback time after which an episode should be
* flattrd. Must be a value between 0 and 1 (inclusive)
* */
- public static void setAutoFlattrSettings(Context context, boolean enabled, float autoFlattrThreshold) {
- instanceAvailable();
+ public static void setAutoFlattrSettings( boolean enabled, float autoFlattrThreshold) {
Validate.inclusiveBetween(0.0, 1.0, autoFlattrThreshold);
- PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext())
- .edit()
- .putBoolean(PREF_AUTO_FLATTR, enabled)
- .putFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD, autoFlattrThreshold)
- .commit();
- instance.autoFlattr = enabled;
- instance.autoFlattrPlayedDurationThreshold = autoFlattrThreshold;
+ prefs.edit()
+ .putBoolean(PREF_AUTO_FLATTR, enabled)
+ .putFloat(PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD, autoFlattrThreshold)
+ .apply();
}
public static void setHiddenDrawerItems(Context context, List<String> items) {
- instanceAvailable();
- instance.hiddenDrawerItems = items;
String str = StringUtils.join(items, ',');
- PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext())
- .edit()
- .putString(PREF_HIDDEN_DRAWER_ITEMS, str)
- .commit();
+ prefs.edit()
+ .putString(PREF_HIDDEN_DRAWER_ITEMS, str)
+ .apply();
}
public static void setQueueLocked(boolean locked) {
- instanceAvailable();
- instance.queueLocked = locked;
- PreferenceManager.getDefaultSharedPreferences(instance.context)
- .edit()
- .putBoolean(PREF_QUEUE_LOCKED, locked)
- .commit();
+ prefs.edit()
+ .putBoolean(PREF_QUEUE_LOCKED, locked)
+ .apply();
+ }
+
+ private static int readThemeValue(String valueFromPrefs) {
+ switch (Integer.parseInt(valueFromPrefs)) {
+ case 0:
+ return R.style.Theme_AntennaPod_Light;
+ case 1:
+ return R.style.Theme_AntennaPod_Dark;
+ default:
+ return R.style.Theme_AntennaPod_Light;
+ }
+ }
+
+ private static long readUpdateInterval(String valueFromPrefs) {
+ int hours = Integer.parseInt(valueFromPrefs);
+ return TimeUnit.HOURS.toMillis(hours);
+ }
+
+ private static int readEpisodeCacheSizeInternal(String valueFromPrefs) {
+ if (valueFromPrefs.equals(context.getString(R.string.pref_episode_cache_unlimited))) {
+ return EPISODE_CACHE_SIZE_UNLIMITED;
+ } else {
+ return Integer.valueOf(valueFromPrefs);
+ }
+ }
+
+ private static String[] readPlaybackSpeedArray(String valueFromPrefs) {
+ String[] selectedSpeeds = null;
+ // If this preference hasn't been set yet, return the default options
+ if (valueFromPrefs == null) {
+ String[] allSpeeds = context.getResources().getStringArray(R.array.playback_speed_values);
+ List<String> speedList = new LinkedList<String>();
+ for (String speedStr : allSpeeds) {
+ float speed = Float.parseFloat(speedStr);
+ if (speed < 2.0001 && speed * 10 % 1 == 0) {
+ speedList.add(speedStr);
+ }
+ }
+ selectedSpeeds = speedList.toArray(new String[speedList.size()]);
+ } else {
+ try {
+ JSONArray jsonArray = new JSONArray(valueFromPrefs);
+ selectedSpeeds = new String[jsonArray.length()];
+ for (int i = 0; i < jsonArray.length(); i++) {
+ selectedSpeeds[i] = jsonArray.getString(i);
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "Got JSON error when trying to get speeds from JSONArray");
+ e.printStackTrace();
+ }
+ }
+ return selectedSpeeds;
}
@@ -648,9 +470,6 @@ public class UserPreferences implements
* could not be created.
*/
public static File getDataFolder(Context context, String type) {
- instanceAvailable();
- SharedPreferences prefs = PreferenceManager
- .getDefaultSharedPreferences(context.getApplicationContext());
String strDir = prefs.getString(PREF_DATA_FOLDER, null);
if (strDir == null) {
Log.d(TAG, "Using default data folder");
@@ -696,12 +515,9 @@ public class UserPreferences implements
public static void setDataFolder(String dir) {
Log.d(TAG, "Result from DirectoryChooser: " + dir);
- instanceAvailable();
- SharedPreferences prefs = PreferenceManager
- .getDefaultSharedPreferences(instance.context);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putString(PREF_DATA_FOLDER, dir);
- editor.commit();
+ prefs.edit()
+ .putString(PREF_DATA_FOLDER, dir)
+ .apply();
createImportDirectory();
}
@@ -709,8 +525,7 @@ public class UserPreferences implements
* Create a .nomedia file to prevent scanning by the media scanner.
*/
private static void createNoMediaFile() {
- File f = new File(instance.context.getExternalFilesDir(null),
- ".nomedia");
+ File f = new File(context.getExternalFilesDir(null), ".nomedia");
if (!f.exists()) {
try {
f.createNewFile();
@@ -727,8 +542,7 @@ public class UserPreferences implements
* available
*/
private static void createImportDirectory() {
- File importDir = getDataFolder(instance.context,
- IMPORT_DIR);
+ File importDir = getDataFolder(context, IMPORT_DIR);
if (importDir != null) {
if (importDir.exists()) {
Log.d(TAG, "Import directory already exists");
@@ -741,32 +555,69 @@ public class UserPreferences implements
}
}
+ public static void restartUpdateAlarm(boolean now) {
+ int[] timeOfDay = getUpdateTimeOfDay();
+ Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay));
+ if (timeOfDay.length == 2) {
+ restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]);
+ } else {
+ long hours = getUpdateInterval();
+ long startTrigger = hours;
+ if (now) {
+ startTrigger = TimeUnit.SECONDS.toMillis(10);
+ }
+ restartUpdateIntervalAlarm(startTrigger, hours);
+ }
+ }
+
/**
- * Updates alarm registered with the AlarmManager service or deactivates it.
+ * Sets the interval in which the feeds are refreshed automatically
*/
- public static void restartUpdateAlarm(long triggerAtMillis, long intervalMillis) {
- instanceAvailable();
+ public static void restartUpdateIntervalAlarm(long triggerAtMillis, long intervalMillis) {
Log.d(TAG, "Restarting update alarm.");
- AlarmManager alarmManager = (AlarmManager) instance.context
- .getSystemService(Context.ALARM_SERVICE);
- PendingIntent updateIntent = PendingIntent.getBroadcast(
- instance.context, 0, new Intent(ClientConfig.applicationCallbacks.getApplicationInstance(), FeedUpdateReceiver.class), 0);
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ PendingIntent updateIntent = PendingIntent.getBroadcast(context, 0,
+ new Intent(ClientConfig.applicationCallbacks.getApplicationInstance(), FeedUpdateReceiver.class), 0);
alarmManager.cancel(updateIntent);
- if (intervalMillis != 0) {
- alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, intervalMillis,
+ if (intervalMillis > 0) {
+ alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + triggerAtMillis,
updateIntent);
- Log.d(TAG, "Changed alarm to new interval");
+ Log.d(TAG, "Changed alarm to new interval " + TimeUnit.MILLISECONDS.toHours(intervalMillis) + " h");
} else {
Log.d(TAG, "Automatic update was deactivated");
}
}
+ /**
+ * Sets time of day the feeds are refreshed automatically
+ */
+ public static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute) {
+ Log.d(TAG, "Restarting update alarm.");
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ PendingIntent updateIntent = PendingIntent.getBroadcast(context, 0,
+ new Intent(ClientConfig.applicationCallbacks.getApplicationInstance(), FeedUpdateReceiver.class), 0);
+ alarmManager.cancel(updateIntent);
+
+ Calendar now = Calendar.getInstance();
+ Calendar alarm = (Calendar)now.clone();
+ alarm.set(Calendar.HOUR_OF_DAY, hoursOfDay);
+ alarm.set(Calendar.MINUTE, minute);
+ if (alarm.before(now) || alarm.equals(now)) {
+ alarm.add(Calendar.DATE, 1);
+ }
+ Log.d(TAG, "Alarm set for: " + alarm.toString() + " : " + alarm.getTimeInMillis());
+ alarmManager.set(AlarmManager.RTC_WAKEUP,
+ alarm.getTimeInMillis(),
+ updateIntent);
+ Log.d(TAG, "Changed alarm to new time of day " + hoursOfDay + ":" + minute);
+ }
/**
* Reads episode cache size as it is saved in the episode_cache_size_values array.
*/
public static int readEpisodeCacheSize(String valueFromPrefs) {
- instanceAvailable();
- return instance.readEpisodeCacheSizeInternal(valueFromPrefs);
+ return readEpisodeCacheSizeInternal(valueFromPrefs);
}
+
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/AlarmUpdateReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/AlarmUpdateReceiver.java
index 84277b6d5..7fa92f30c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/receiver/AlarmUpdateReceiver.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/AlarmUpdateReceiver.java
@@ -7,28 +7,25 @@ import android.util.Log;
import org.apache.commons.lang3.StringUtils;
-import de.danoeh.antennapod.core.BuildConfig;
-import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
/** Listens for events that make it necessary to reset the update alarm. */
public class AlarmUpdateReceiver extends BroadcastReceiver {
+
private static final String TAG = "AlarmUpdateReceiver";
@Override
public void onReceive(Context context, Intent intent) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Received intent");
+ Log.d(TAG, "Received intent");
if (StringUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED)) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Resetting update alarm after reboot");
+ Log.d(TAG, "Resetting update alarm after reboot");
} else if (StringUtils.equals(intent.getAction(), Intent.ACTION_PACKAGE_REPLACED)) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Resetting update alarm after app upgrade");
+ Log.d(TAG, "Resetting update alarm after app upgrade");
}
-
- ClientConfig.applicationCallbacks.setUpdateInterval(UserPreferences.getUpdateInterval());
-
+ PlaybackPreferences.init(context);
+ UserPreferences.init(context);
+ UserPreferences.restartUpdateAlarm(false);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
index d37f97a5f..b959c7301 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
@@ -5,6 +5,7 @@ import android.content.Context;
import android.content.Intent;
import android.util.Log;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.util.NetworkUtils;
@@ -18,11 +19,12 @@ public class FeedUpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Received intent");
- if (NetworkUtils.isDownloadAllowed(context)) {
- DBTasks.refreshExpiredFeeds(context);
+ if (NetworkUtils.isDownloadAllowed()) {
+ DBTasks.refreshAllFeeds(context, null);
} else {
Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed");
}
+ UserPreferences.restartUpdateAlarm(false);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/FeedMediaSizeService.java b/core/src/main/java/de/danoeh/antennapod/core/service/FeedMediaSizeService.java
new file mode 100644
index 000000000..2f6e67a28
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/FeedMediaSizeService.java
@@ -0,0 +1,76 @@
+package de.danoeh.antennapod.core.service;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+
+import de.danoeh.antennapod.core.event.FeedMediaEvent;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.util.NetworkUtils;
+import de.greenrobot.event.EventBus;
+
+public class FeedMediaSizeService extends IntentService {
+
+ private final static String TAG = "FeedMediaSizeService";
+
+ public FeedMediaSizeService() {
+ super(TAG);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ Log.d(TAG, "onHandleIntent()");
+ if(false == NetworkUtils.isDownloadAllowed()) {
+ return;
+ }
+ List<FeedMedia> list = DBReader.getFeedMediaUnknownSize(this);
+ for (FeedMedia media : list) {
+ Log.d(TAG, "Getting size currently " + media.getSize() + " for " + media.getDownload_url());
+ if(false == NetworkUtils.isDownloadAllowed()) {
+ return;
+ }
+ long size = Integer.MIN_VALUE;
+ if (media.isDownloaded()) {
+ File mediaFile = new File(media.getLocalMediaUrl());
+ if(mediaFile.exists()) {
+ size = mediaFile.length();
+ }
+ } else if (false == media.checkedOnSizeButUnknown()) {
+ // only query the network if we haven't already checked
+ HttpURLConnection conn = null;
+ try {
+ URL url = new URL(media.getDownload_url());
+ conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestProperty("Accept-Encoding", "");
+ conn.setRequestMethod("HEAD");
+ size = conn.getContentLength();
+ } catch (IOException e) {
+ Log.d(TAG, media.getDownload_url());
+ e.printStackTrace();
+ } finally {
+ if (conn != null) {
+ conn.disconnect();
+ }
+ }
+ }
+ if (size <= 0) {
+ // they didn't tell us the size, but we don't want to keep querying on it
+ media.setCheckedOnSizeButUnknown();
+ } else {
+ media.setSize(size);
+ }
+ Log.d(TAG, "Size now: " + media.getSize());
+ DBWriter.setFeedMedia(this, media);
+ EventBus.getDefault().post(FeedMediaEvent.update(media));
+ }
+ }
+
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java
index 3f2222f42..b80b4303f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java
@@ -108,7 +108,7 @@ public class GpodnetSyncService extends Service {
private synchronized void sync() {
- if (GpodnetPreferences.loggedIn() == false || NetworkUtils.networkAvailable(this) == false) {
+ if (GpodnetPreferences.loggedIn() == false || NetworkUtils.networkAvailable() == false) {
stopSelf();
return;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
index e7b226eca..4fdaf6843 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
@@ -243,7 +243,7 @@ public class DownloadService extends Service {
handler = new Handler();
newMediaFiles = Collections.synchronizedList(new ArrayList<Long>());
reportQueue = Collections.synchronizedList(new ArrayList<DownloadStatus>());
- downloads = new ArrayList<Downloader>();
+ downloads = Collections.synchronizedList(new ArrayList<Downloader>());
numberOfDownloads = new AtomicInteger(0);
IntentFilter cancelDownloadReceiverFilter = new IntentFilter();
@@ -309,7 +309,8 @@ public class DownloadService extends Service {
Log.d(TAG, "Service shutting down");
isRunning = false;
- if (ClientConfig.downloadServiceCallbacks.shouldCreateReport()) {
+ if (ClientConfig.downloadServiceCallbacks.shouldCreateReport() &&
+ UserPreferences.showDownloadReport()) {
updateReport();
}
@@ -324,7 +325,13 @@ public class DownloadService extends Service {
cancelNotificationUpdater();
unregisterReceiver(cancelDownloadReceiver);
+ // TODO: I'm not sure this is actually needed.
+ // We could just invoke the autodownloadUndownloadeditems method
+ // and it would get everything it's supposed to. By sending it the
+ // items in newMediaFiles we're overriding the download algorithm,
+ // which is not something we should probably do.
if (!newMediaFiles.isEmpty()) {
+ Log.d(TAG, "newMediaFiles exist, autodownload them");
DBTasks.autodownloadUndownloadedItems(getApplicationContext(),
ArrayUtils.toPrimitive(newMediaFiles.toArray(new Long[newMediaFiles.size()])));
}
@@ -434,6 +441,7 @@ public class DownloadService extends Service {
} else {
Log.e(TAG, "Could not cancel download with url " + url);
}
+ sendBroadcast(new Intent(ACTION_DOWNLOADS_CONTENT_CHANGED));
} else if (StringUtils.equals(intent.getAction(), ACTION_CANCEL_ALL_DOWNLOADS)) {
for (Downloader d : downloads) {
@@ -774,52 +782,18 @@ public class DownloadService extends Service {
for (int i = 0; i < savedFeeds.length; i++) {
Feed savedFeed = savedFeeds[i];
- // Download Feed Image if provided and not downloaded
- if (savedFeed.getImage() != null
- && savedFeed.getImage().isDownloaded() == false) {
- Log.d(TAG, "Feed has image; Downloading....");
- savedFeed.getImage().setOwner(savedFeed);
- final Feed savedFeedRef = savedFeed;
- try {
- requester.downloadImage(DownloadService.this,
- savedFeedRef.getImage());
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DBWriter.addDownloadStatus(
- DownloadService.this,
- new DownloadStatus(
- savedFeedRef.getImage(),
- savedFeedRef
- .getImage()
- .getHumanReadableIdentifier(),
- DownloadError.ERROR_REQUEST_ERROR,
- false, e.getMessage()
- )
- );
- }
- }
// queue new media files for automatic download
for (FeedItem item : savedFeed.getItems()) {
if(item.getPubDate() == null) {
Log.d(TAG, item.toString());
}
- if(item.getImage() != null && item.getImage().isDownloaded() == false) {
- item.getImage().setOwner(item);
- try {
- requester.downloadImage(DownloadService.this,
- item.getImage());
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- }
- }
- if (!item.isRead() && item.hasMedia() && !item.getMedia().isDownloaded()) {
+ if (item.isNew() && item.hasMedia() && !item.getMedia().isDownloaded()) {
newMediaFiles.add(item.getMedia().getId());
}
}
// If loadAllPages=true, check if another page is available and queue it for download
-
final boolean loadAllPages = results.get(i).first.getArguments().getBoolean(DownloadRequester.REQUEST_ARG_LOAD_ALL_PAGES);
final Feed feed = results.get(i).second.feed;
if (loadAllPages && feed.getNextPageLink() != null) {
@@ -889,7 +863,7 @@ public class DownloadService extends Service {
feed.setFile_url(request.getDestination());
feed.setId(request.getFeedfileId());
feed.setDownloaded(true);
- feed.setPreferences(new FeedPreferences(0, true,
+ feed.setPreferences(new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL,
request.getUsername(), request.getPassword()));
feed.setPageNr(request.getArguments().getInt(DownloadRequester.REQUEST_ARG_PAGE_NR, 0));
@@ -1143,6 +1117,7 @@ public class DownloadService extends Service {
boolean chaptersRead = false;
media.setDownloaded(true);
media.setFile_url(request.getDestination());
+ media.setHasEmbeddedPicture(null);
// Get duration
MediaMetadataRetriever mmr = null;
@@ -1170,12 +1145,16 @@ public class DownloadService extends Service {
}
try {
- if (chaptersRead) {
- DBWriter.setFeedItem(DownloadService.this, media.getItem()).get();
- }
+ // we've received the media, we don't want to autodownload it again
+ FeedItem item = media.getItem();
+ item.setAutoDownload(false);
+
+ // update the db
+ DBWriter.setFeedItem(DownloadService.this, item).get();
+
DBWriter.setFeedMedia(DownloadService.this, media).get();
- if (!DBTasks.isInQueue(DownloadService.this, media.getItem().getId())) {
- DBWriter.addQueueItem(DownloadService.this, media.getItem().getId()).get();
+ if (!DBTasks.isInQueue(DownloadService.this, item.getId())) {
+ DBWriter.addQueueItem(DownloadService.this, item.getId()).get();
}
} catch (ExecutionException e) {
e.printStackTrace();
@@ -1240,7 +1219,16 @@ public class DownloadService extends Service {
}
public List<Downloader> getDownloads() {
- return downloads;
+ if (downloads == null) {
+ // this is unusual, but it should be OK, we'll return
+ // an empty list to make it easy for people
+ return new ArrayList<Downloader>();
+ }
+
+ // return a copy of downloads, but the copy doesn't need to be synchronized.
+ synchronized (downloads) {
+ return new ArrayList<Downloader>(downloads);
+ }
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index 3f6769ee4..7e3f27856 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -29,12 +29,16 @@ import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.widget.Toast;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.SimpleTarget;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.List;
+import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
@@ -42,6 +46,7 @@ import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
@@ -442,7 +447,7 @@ public class PlaybackService extends Service {
}
writePlayerStatusPlaybackPreferences();
- final Playable playable = mediaPlayer.getPSMPInfo().playable;
+ final Playable playable = newInfo.playable;
// Gpodder: send play action
if(GpodnetPreferences.loggedIn() && playable instanceof FeedMedia) {
@@ -525,7 +530,7 @@ public class PlaybackService extends Service {
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
final String TAG = "PlaybackService.onErrorListener";
Log.w(TAG, "An error has occured: " + what + " " + extra);
- if (mediaPlayer.getPSMPInfo().playerStatus == PlayerStatus.PLAYING) {
+ if (mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING) {
mediaPlayer.pause(true, false);
}
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
@@ -549,7 +554,7 @@ public class PlaybackService extends Service {
private void endPlayback(boolean playNextEpisode) {
Log.d(TAG, "Playback ended");
- final Playable playable = mediaPlayer.getPSMPInfo().playable;
+ final Playable playable = mediaPlayer.getPlayable();
if (playable == null) {
Log.e(TAG, "Cannot end playback: media was null");
return;
@@ -584,7 +589,7 @@ public class PlaybackService extends Service {
}
// Delete episode if enabled
- if(UserPreferences.isAutoDelete()) {
+ if(item.getFeed().getPreferences().getCurrentAutoDelete()) {
DBWriter.deleteFeedMediaOfItem(PlaybackService.this, media.getId());
Log.d(TAG, "Episode Deleted");
}
@@ -634,7 +639,7 @@ public class PlaybackService extends Service {
writePlaybackPreferencesNoMediaPlaying();
if (nextMedia != null) {
- stream = !playable.localFileAvailable();
+ stream = !nextMedia.localFileAvailable();
mediaPlayer.playMediaObject(nextMedia, stream, startWhenPrepared, prepareImmediately);
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
(nextMedia.getMediaType() == MediaType.VIDEO) ? EXTRA_CODE_VIDEO : EXTRA_CODE_AUDIO);
@@ -744,8 +749,7 @@ public class PlaybackService extends Service {
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(getApplicationContext()).edit();
- PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
- int playerStatus = getCurrentPlayerStatusAsInt(info.playerStatus);
+ int playerStatus = getCurrentPlayerStatusAsInt(mediaPlayer.getPlayerStatus());
editor.putInt(
PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
@@ -770,7 +774,7 @@ public class PlaybackService extends Service {
/**
* Used by setupNotification to load notification data in another thread.
*/
- private AsyncTask<Void, Void, Void> notificationSetupTask;
+ private Thread notificationSetupThread;
/**
* Prepares notification and starts the service in the foreground.
@@ -781,50 +785,47 @@ public class PlaybackService extends Service {
PlaybackService.getPlayerActivityIntent(this),
PendingIntent.FLAG_UPDATE_CURRENT);
- if (notificationSetupTask != null) {
- notificationSetupTask.cancel(true);
+ if (notificationSetupThread != null) {
+ notificationSetupThread.interrupt();
}
- notificationSetupTask = new AsyncTask<Void, Void, Void>() {
+ Runnable notificationSetupTask = new Runnable() {
Bitmap icon = null;
@Override
- protected Void doInBackground(Void... params) {
+ public void run() {
Log.d(TAG, "Starting background work");
if (android.os.Build.VERSION.SDK_INT >= 11) {
if (info.playable != null) {
+ int iconSize = getResources().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_width);
try {
- int iconSize = getResources().getDimensionPixelSize(
- android.R.dimen.notification_large_icon_width);
- icon = Picasso.with(PlaybackService.this)
+ icon = Glide.with(PlaybackService.this)
.load(info.playable.getImageUri())
- .resize(iconSize, iconSize)
+ .asBitmap()
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .into(-1, -1) // this resizing would not be exact, so we have
+ // scale the bitmap ourselves
.get();
- } catch (IOException e) {
+ icon = Bitmap.createScaledBitmap(icon, iconSize, iconSize, true);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (ExecutionException e) {
e.printStackTrace();
}
}
-
}
if (icon == null) {
icon = BitmapFactory.decodeResource(getApplicationContext().getResources(),
ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext()));
}
- return null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
if (mediaPlayer == null) {
return;
}
- PlaybackServiceMediaPlayer.PSMPInfo newInfo = mediaPlayer.getPSMPInfo();
+ PlayerStatus playerStatus = mediaPlayer.getPlayerStatus();
final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext());
- if (!isCancelled() &&
- started &&
- info.playable != null) {
+ if (!Thread.currentThread().isInterrupted() && started && info.playable != null) {
String contentText = info.playable.getFeedTitle();
String contentTitle = info.playable.getEpisodeTitle();
Notification notification = null;
@@ -865,7 +866,7 @@ public class PlaybackService extends Service {
.setLargeIcon(icon)
.setSmallIcon(smallIcon)
.setPriority(UserPreferences.getNotifyPriority()); // set notification priority
- if (newInfo.playerStatus == PlayerStatus.PLAYING) {
+ if (playerStatus == PlayerStatus.PLAYING) {
notificationBuilder.addAction(android.R.drawable.ic_media_pause, //pause action
getString(R.string.pause_label),
pauseButtonPendingIntent);
@@ -902,15 +903,9 @@ public class PlaybackService extends Service {
Log.d(TAG, "Notification set up");
}
}
-
};
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- notificationSetupTask
- .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- notificationSetupTask.execute();
- }
-
+ notificationSetupThread = new Thread(notificationSetupTask);
+ notificationSetupThread.start();
}
/**
@@ -923,7 +918,7 @@ public class PlaybackService extends Service {
int position = getCurrentPosition();
int duration = getDuration();
float playbackSpeed = getCurrentPlaybackSpeed();
- final Playable playable = mediaPlayer.getPSMPInfo().playable;
+ final Playable playable = mediaPlayer.getPlayable();
if (position != INVALID_TIME && duration != INVALID_TIME && playable != null) {
Log.d(TAG, "Saving current position to " + position);
if (updatePlayedDuration && playable instanceof FeedMedia) {
@@ -1200,12 +1195,10 @@ public class PlaybackService extends Service {
}
public PlayerStatus getStatus() {
- return mediaPlayer.getPSMPInfo().playerStatus;
+ return mediaPlayer.getPlayerStatus();
}
- public Playable getPlayable() {
- return mediaPlayer.getPSMPInfo().playable;
- }
+ public Playable getPlayable() { return mediaPlayer.getPlayable(); }
public void setSpeed(float speed) {
mediaPlayer.setSpeed(speed);
@@ -1231,7 +1224,7 @@ public class PlaybackService extends Service {
public void seekTo(final int t) {
if(mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING
&& GpodnetPreferences.loggedIn()) {
- final Playable playable = mediaPlayer.getPSMPInfo().playable;
+ final Playable playable = mediaPlayer.getPlayable();
if (playable instanceof FeedMedia) {
FeedMedia media = (FeedMedia) playable;
FeedItem item = media.getItem();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
index 7a8e38c59..835a8c1d1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
@@ -196,7 +196,7 @@ public class PlaybackServiceMediaPlayer {
DBWriter.markItemRead(context, item, true, false);
DBWriter.removeQueueItem(context, item, false);
DBWriter.addItemToPlaybackHistory(context, oldMedia);
- if (UserPreferences.isAutoDelete()) {
+ if (item.getFeed().getPreferences().getCurrentAutoDelete()) {
Log.d(TAG, "Delete " + oldMedia.toString());
DBWriter.deleteFeedMediaOfItem(context, oldMedia.getId());
}
@@ -623,10 +623,6 @@ public class PlaybackServiceMediaPlayer {
return mediaType;
}
- public PlayerStatus getPlayerStatus() {
- return playerStatus;
- }
-
public boolean isStreaming() {
return stream;
}
@@ -706,6 +702,26 @@ public class PlaybackServiceMediaPlayer {
}
/**
+ * Returns the current status, if you need the media and the player status together, you should
+ * use getPSMPInfo() to make sure they're properly synchronized. Otherwise a race condition
+ * could result in nonsensical results (like a status of PLAYING, but a null playable)
+ * @return the current player status
+ */
+ public PlayerStatus getPlayerStatus() {
+ return playerStatus;
+ }
+
+ /**
+ * Returns the current media, if you need the media and the player status together, you should
+ * use getPSMPInfo() to make sure they're properly synchronized. Otherwise a race condition
+ * could result in nonsensical results (like a status of PLAYING, but a null playable)
+ * @return the current media. May be null
+ */
+ public Playable getPlayable() {
+ return media;
+ }
+
+ /**
* Returns a token to this object's MediaSession. The MediaSession should only be used for notifications
* at the moment.
*
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java
index f647fd537..a07705e69 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java
@@ -28,7 +28,7 @@ public class APCleanupAlgorithm implements EpisodeCleanupAlgorithm<Integer> {
List<FeedItem> delete;
for (FeedItem item : downloadedItems) {
if (item.hasMedia() && item.getMedia().isDownloaded()
- && !queue.contains(item.getId()) && item.isRead()) {
+ && !queue.contains(item.getId()) && item.isPlayed()) {
candidates.add(item);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
index 92de1eee7..c2e971dce 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APDownloadAlgorithm.java
@@ -22,7 +22,7 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
private final APCleanupAlgorithm cleanupAlgorithm = new APCleanupAlgorithm();
/**
- * Looks for undownloaded episodes in the queue or list of unread items and request a download if
+ * Looks for undownloaded episodes in the queue or list of new items and request a download if
* 1. Network is available
* 2. The device is charging or the user allows auto download on battery
* 3. There is free space in the episode cache
@@ -40,7 +40,7 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
public void run() {
// true if we should auto download based on network status
- boolean networkShouldAutoDl = NetworkUtils.autodownloadNetworkAvailable(context)
+ boolean networkShouldAutoDl = NetworkUtils.autodownloadNetworkAvailable()
&& UserPreferences.isEnableAutodownload();
// true if we should auto download based on power status
@@ -57,12 +57,12 @@ public class APDownloadAlgorithm implements AutomaticDownloadAlgorithm {
candidates = DBReader.getFeedItems(context, mediaIds);
} else {
final List<FeedItem> queue = DBReader.getQueue(context);
- final List<FeedItem> unreadItems = DBReader.getUnreadItemsList(context);
- candidates = new ArrayList<FeedItem>(queue.size() + unreadItems.size());
+ final List<FeedItem> newItems = DBReader.getNewItemsList(context);
+ candidates = new ArrayList<FeedItem>(queue.size() + newItems.size());
candidates.addAll(queue);
- for(FeedItem unreadItem : unreadItems) {
- if(candidates.contains(unreadItem) == false) {
- candidates.add(unreadItem);
+ for(FeedItem newItem : newItems) {
+ if(candidates.contains(newItem) == false) {
+ candidates.add(newItem);
}
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APSPCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APSPCleanupAlgorithm.java
deleted file mode 100644
index 420bbc09d..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APSPCleanupAlgorithm.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package de.danoeh.antennapod.core.storage;
-
-import android.content.Context;
-import android.util.Log;
-
-import org.apache.commons.io.FileUtils;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.concurrent.ExecutionException;
-
-import de.danoeh.antennapod.core.feed.FeedItem;
-
-/**
- * Implementation of the EpisodeCleanupAlgorithm interface used by AntennaPodSP apps.
- */
-public class APSPCleanupAlgorithm implements EpisodeCleanupAlgorithm<Integer> {
- private static final String TAG = "APSPCleanupAlgorithm";
-
- final int numberOfNewAutomaticallyDownloadedEpisodes;
-
- public APSPCleanupAlgorithm(int numberOfNewAutomaticallyDownloadedEpisodes) {
- this.numberOfNewAutomaticallyDownloadedEpisodes = numberOfNewAutomaticallyDownloadedEpisodes;
- }
-
- /**
- * Performs an automatic cleanup. Episodes that have been downloaded first will also be deleted first.
- * The episode that is currently playing as well as the n most recent episodes (the exact value is determined
- * by AppPreferences.numberOfNewAutomaticallyDownloadedEpisodes) will never be deleted.
- *
- * @param context
- * @param episodeSize The maximum amount of space that should be freed by this method
- * @return The number of episodes that have been deleted
- */
- @Override
- public int performCleanup(Context context, Integer episodeSize) {
- Log.i(TAG, String.format("performAutoCleanup(%d)", episodeSize));
- if (episodeSize <= 0) {
- return 0;
- }
-
- List<FeedItem> candidates = getAutoCleanupCandidates(context);
- List<FeedItem> deleteList = new ArrayList<FeedItem>();
- long deletedEpisodesSize = 0;
- Collections.sort(candidates, new Comparator<FeedItem>() {
- @Override
- public int compare(FeedItem lhs, FeedItem rhs) {
- File lFile = new File(lhs.getMedia().getFile_url());
- File rFile = new File(rhs.getMedia().getFile_url());
- if (!lFile.exists() || !rFile.exists()) {
- return 0;
- }
- if (FileUtils.isFileOlder(lFile, rFile)) {
- return -1;
- } else {
- return 1;
- }
- }
- });
- // listened episodes will be deleted first
- Iterator<FeedItem> it = candidates.iterator();
- if (it.hasNext()) {
- for (FeedItem i = it.next(); it.hasNext() && deletedEpisodesSize <= episodeSize; i = it.next()) {
- if (!i.getMedia().isPlaying() && i.getMedia().getPlaybackCompletionDate() != null) {
- it.remove();
- deleteList.add(i);
- deletedEpisodesSize += i.getMedia().getSize();
- }
- }
- }
-
- // delete unlistened old episodes if necessary
- it = candidates.iterator();
- if (it.hasNext()) {
- for (FeedItem i = it.next(); it.hasNext() && deletedEpisodesSize <= episodeSize; i = it.next()) {
- if (!i.getMedia().isPlaying()) {
- it.remove();
- deleteList.add(i);
- deletedEpisodesSize += i.getMedia().getSize();
- }
- }
- }
- for (FeedItem item : deleteList) {
- try {
- DBWriter.deleteFeedMediaOfItem(context, item.getMedia().getId()).get();
- } catch (InterruptedException e) {
- e.printStackTrace();
- } catch (ExecutionException e) {
- e.printStackTrace();
- }
- }
- Log.i(TAG, String.format("performAutoCleanup(%d) deleted %d episodes and freed %d bytes of memory",
- episodeSize, deleteList.size(), deletedEpisodesSize));
- return deleteList.size();
- }
-
- @Override
- public Integer getDefaultCleanupParameter(Context context) {
- return 0;
- }
-
- @Override
- public Integer getPerformCleanupParameter(Context context, List<FeedItem> items) {
- int episodeSize = 0;
- for (FeedItem item : items) {
- if (item.hasMedia() && !item.getMedia().isDownloaded()) {
- episodeSize += item.getMedia().getSize();
- }
- }
- return episodeSize;
- }
-
- /**
- * Returns list of FeedItems that have been downloaded, but are not one of the
- * [numberOfNewAutomaticallyDownloadedEpisodes] most recent items.
- */
- private List<FeedItem> getAutoCleanupCandidates(Context context) {
- List<FeedItem> downloaded = new ArrayList<FeedItem>(DBReader.getDownloadedItems(context));
- List<FeedItem> recent = new ArrayList<FeedItem>(DBReader.getRecentlyPublishedEpisodes(context,
- numberOfNewAutomaticallyDownloadedEpisodes));
- for (FeedItem r : recent) {
- if (r.hasMedia() && r.getMedia().isDownloaded()) {
- for (int i = 0; i < downloaded.size(); i++) {
- if (downloaded.get(i).getId() == r.getId()) {
- downloaded.remove(i);
- break;
- }
- }
- }
- }
-
- return downloaded;
-
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APSPDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APSPDownloadAlgorithm.java
deleted file mode 100644
index f760ec0ce..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APSPDownloadAlgorithm.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package de.danoeh.antennapod.core.storage;
-
-import android.content.Context;
-import android.util.Log;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-
-import de.danoeh.antennapod.core.BuildConfig;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.NetworkUtils;
-
-/**
- * Implements the automatic download algorithm used by AntennaPodSP apps.
- */
-public class APSPDownloadAlgorithm implements AutomaticDownloadAlgorithm {
- private static final String TAG = "APSPDownloadAlgorithm";
-
- private final int numberOfNewAutomaticallyDownloadedEpisodes;
-
- public APSPDownloadAlgorithm(int numberOfNewAutomaticallyDownloadedEpisodes) {
- this.numberOfNewAutomaticallyDownloadedEpisodes = numberOfNewAutomaticallyDownloadedEpisodes;
- }
-
- /**
- * Downloads the most recent episodes automatically. The exact number of
- * episodes that will be downloaded can be set in the AppPreferences.
- *
- * @param context Used for accessing the DB.
- * @return A Runnable that will be submitted to an ExecutorService.
- */
- @Override
- public Runnable autoDownloadUndownloadedItems(final Context context, final long... mediaIds) {
- return new Runnable() {
- @Override
- public void run() {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Performing auto-dl of undownloaded episodes");
- if (NetworkUtils.autodownloadNetworkAvailable(context)
- && UserPreferences.isEnableAutodownload()) {
-
- Arrays.sort(mediaIds);
- List<FeedItem> itemsToDownload = DBReader.getRecentlyPublishedEpisodes(context,
- numberOfNewAutomaticallyDownloadedEpisodes);
- Iterator<FeedItem> it = itemsToDownload.iterator();
-
- for (FeedItem item = it.next(); it.hasNext(); item = it.next()) {
- if (!item.hasMedia()
- || item.getMedia().isDownloaded()
- || Arrays.binarySearch(mediaIds, item.getMedia().getId()) < 0) {
- it.remove();
- }
- }
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Enqueueing " + itemsToDownload.size()
- + " items for automatic download");
- if (!itemsToDownload.isEmpty()) {
- try {
- DBTasks.downloadFeedItems(false, context,
- itemsToDownload.toArray(new FeedItem[itemsToDownload
- .size()]));
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- }
- }
- }
- }
- };
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
index dc24c5784..5ed0a6ee3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
@@ -22,6 +22,7 @@ import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.feed.ID3Chapter;
import de.danoeh.antennapod.core.feed.SimpleChapter;
import de.danoeh.antennapod.core.feed.VorbisCommentChapter;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.LongIntMap;
@@ -114,36 +115,6 @@ public final class DBReader {
}
/**
- * Returns a list of 'expired Feeds', i.e. Feeds that have not been updated for a certain amount of time.
- *
- * @param context A context that is used for opening a database connection.
- * @param expirationTime Time that is used for determining whether a feed is outdated or not.
- * A Feed is considered expired if 'lastUpdate < (currentTime - expirationTime)' evaluates to true.
- * @return A list of Feeds, sorted alphabetically by their title. A Feed-object
- * of the returned list does NOT have its list of FeedItems yet. The FeedItem-list
- * can be loaded separately with {@link #getFeedItemList(android.content.Context, de.danoeh.antennapod.core.feed.Feed)}.
- */
- public static List<Feed> getExpiredFeedsList(final Context context, final long expirationTime) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, String.format("getExpiredFeedsList(%d)", expirationTime));
-
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
-
- Cursor feedlistCursor = adapter.getExpiredFeedsCursor(expirationTime);
- List<Feed> feeds = new ArrayList<Feed>(feedlistCursor.getCount());
-
- if (feedlistCursor.moveToFirst()) {
- do {
- Feed feed = extractFeedFromCursorRow(adapter, feedlistCursor);
- feeds.add(feed);
- } while (feedlistCursor.moveToNext());
- }
- feedlistCursor.close();
- return feeds;
- }
-
- /**
* Takes a list of FeedItems and loads their corresponding Feed-objects from the database.
* The feedID-attribute of a FeedItem must be set to the ID of its feed or the method will
* not find the correct feed of an item.
@@ -230,7 +201,7 @@ public final class DBReader {
new FlattrStatus(itemlistCursor.getLong(PodDBAdapter.IDX_FI_SMALL_FLATTR_STATUS)),
itemlistCursor.getInt(PodDBAdapter.IDX_FI_SMALL_HAS_CHAPTERS) > 0,
image,
- (itemlistCursor.getInt(PodDBAdapter.IDX_FI_SMALL_READ) > 0),
+ itemlistCursor.getInt(PodDBAdapter.IDX_FI_SMALL_READ),
itemlistCursor.getString(PodDBAdapter.IDX_FI_SMALL_ITEM_IDENTIFIER),
itemlistCursor.getInt(itemlistCursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DOWNLOAD)) > 0
);
@@ -274,6 +245,18 @@ public final class DBReader {
playbackCompletionDate = new Date(
playbackCompletionTime);
}
+ Boolean hasEmbeddedPicture;
+ switch(cursor.getInt(cursor.getColumnIndex(PodDBAdapter.KEY_HAS_EMBEDDED_PICTURE))) {
+ case 1:
+ hasEmbeddedPicture = Boolean.TRUE;
+ break;
+ case 0:
+ hasEmbeddedPicture = Boolean.FALSE;
+ break;
+ default:
+ hasEmbeddedPicture = null;
+ break;
+ }
return new FeedMedia(
mediaId,
@@ -286,7 +269,8 @@ public final class DBReader {
cursor.getString(PodDBAdapter.KEY_DOWNLOAD_URL_INDEX),
cursor.getInt(PodDBAdapter.KEY_DOWNLOADED_INDEX) > 0,
playbackCompletionDate,
- cursor.getInt(PodDBAdapter.KEY_PLAYED_DURATION_INDEX));
+ cursor.getInt(PodDBAdapter.KEY_PLAYED_DURATION_INDEX),
+ hasEmbeddedPicture);
}
private static Feed extractFeedFromCursorRow(PodDBAdapter adapter,
@@ -325,9 +309,9 @@ public final class DBReader {
if (image != null) {
image.setOwner(feed);
}
-
FeedPreferences preferences = new FeedPreferences(cursor.getLong(PodDBAdapter.IDX_FEED_SEL_STD_ID),
cursor.getInt(PodDBAdapter.IDX_FEED_SEL_PREFERENCES_AUTO_DOWNLOAD) > 0,
+ FeedPreferences.AutoDeleteAction.values()[cursor.getInt(PodDBAdapter.IDX_FEED_SEL_PREFERENCES_AUTO_DELETE_ACTION)],
cursor.getString(PodDBAdapter.IDX_FEED_SEL_PREFERENCES_USERNAME),
cursor.getString(PodDBAdapter.IDX_FEED_SEL_PREFERENCES_PASSWORD));
@@ -469,8 +453,7 @@ public final class DBReader {
* Loads a list of FeedItems whose 'read'-attribute is set to false.
*
* @param context A context that is used for opening a database connection.
- * @return A list of FeedItems whose 'read'-attribute it set to false. If the FeedItems in the list are not used,
- * consider using {@link #getUnreadItemIds(android.content.Context)} instead.
+ * @return A list of FeedItems whose 'read'-attribute it set to false.
*/
public static List<FeedItem> getUnreadItemsList(Context context) {
if (BuildConfig.DEBUG)
@@ -538,6 +521,28 @@ public final class DBReader {
return itemIds;
}
+ /**
+ * Loads FeedMedia whose file size is unknown
+ *
+ * @param context A context that is used for opening a database connection.
+ * @return A list of FeedMedia items whose size is 0 (unknown and never tried to
+ * determine the correct size)
+ */
+ public static List<FeedMedia> getFeedMediaUnknownSize(Context context) {
+ PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+ Cursor cursor = adapter.getFeedMediaUnknownSizeCursor();
+ List<FeedMedia> result = new ArrayList<>(cursor.getCount());
+ if (cursor.moveToFirst()) {
+ do {
+ FeedMedia media = extractFeedMediaFromCursorRow(cursor);
+ result.add(media);
+ } while (cursor.moveToNext());
+ }
+ cursor.close();
+ return result;
+ }
+
/**
* Loads a list of FeedItems sorted by pubDate in descending order.
@@ -866,7 +871,11 @@ public final class DBReader {
if (cursor.moveToFirst()) {
String username = cursor.getString(0);
String password = cursor.getString(1);
- return username + ":" + password;
+ if(username != null && password != null) {
+ return username + ":" + password;
+ } else {
+ return "";
+ }
}
return "";
} finally {
@@ -1001,20 +1010,6 @@ public final class DBReader {
}
/**
- * Returns a map containing the number of unread items per feed
- *
- * @param context A context that is used for opening a database connection.
- * @return The number of unread items per feed.
- */
- public static LongIntMap getNumberOfUnreadFeedItems(final Context context, long... feedIds) {
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- final LongIntMap result = adapter.getNumberOfUnreadFeedItems(feedIds);
- adapter.close();
- return result;
- }
-
- /**
* Searches the DB for a FeedImage of the given id.
*
* @param context A context that is used for opening a database connection.
@@ -1142,27 +1137,42 @@ public final class DBReader {
for(int i=0; i < feeds.size(); i++) {
feedIds[i] = feeds.get(i).getId();
}
- final LongIntMap numUnreadFeedItems = adapter.getNumberOfUnreadFeedItems(feedIds);
- Collections.sort(feeds, new Comparator<Feed>() {
- @Override
- public int compare(Feed lhs, Feed rhs) {
- long numUnreadLhs = numUnreadFeedItems.get(lhs.getId());
- Log.d(TAG, "feed with id " + lhs.getId() + " has " + numUnreadLhs + " unread items");
- long numUnreadRhs = numUnreadFeedItems.get(rhs.getId());
- Log.d(TAG, "feed with id " + rhs.getId() + " has " + numUnreadRhs + " unread items");
- if(numUnreadLhs > numUnreadRhs) {
- // reverse natural order: podcast with most unplayed episodes first
- return -1;
- } else if(numUnreadLhs == numUnreadRhs) {
+ final LongIntMap feedCounters = adapter.getFeedCounters(feedIds);
+
+ Comparator<Feed> comparator;
+ int feedOrder = UserPreferences.getFeedOrder();
+ if(feedOrder == UserPreferences.FEED_ORDER_UNPLAYED_EPISODES) {
+ comparator = new Comparator<Feed>() {
+ @Override
+ public int compare(Feed lhs, Feed rhs) {
+ long counterLhs = feedCounters.get(lhs.getId());
+ long counterRhs = feedCounters.get(rhs.getId());
+ if(counterLhs > counterRhs) {
+ // reverse natural order: podcast with most unplayed episodes first
+ return -1;
+ } else if(counterLhs == counterRhs) {
+ return lhs.getTitle().compareTo(rhs.getTitle());
+ } else {
+ return 1;
+ }
+ }
+ };
+ } else {
+ comparator = new Comparator<Feed>() {
+ @Override
+ public int compare(Feed lhs, Feed rhs) {
+ if(lhs.getTitle() == null) {
+ return 1;
+ }
return lhs.getTitle().compareTo(rhs.getTitle());
- } else {
- return 1;
}
- }
- });
+ };
+ }
+
+ Collections.sort(feeds, comparator);
int queueSize = adapter.getQueueSize();
int numNewItems = adapter.getNumberOfNewItems();
- NavDrawerData result = new NavDrawerData(feeds, queueSize, numNewItems, numUnreadFeedItems);
+ NavDrawerData result = new NavDrawerData(feeds, queueSize, numNewItems, feedCounters);
adapter.close();
return result;
}
@@ -1171,14 +1181,16 @@ public final class DBReader {
public List<Feed> feeds;
public int queueSize;
public int numNewItems;
- public LongIntMap numUnreadFeedItems;
+ public LongIntMap feedCounters;
- public NavDrawerData(List<Feed> feeds, int queueSize, int numNewItems,
- LongIntMap numUnreadFeedItems) {
+ public NavDrawerData(List<Feed> feeds,
+ int queueSize,
+ int numNewItems,
+ LongIntMap feedIndicatorValues) {
this.feeds = feeds;
this.queueSize = queueSize;
this.numNewItems = numNewItems;
- this.numUnreadFeedItems = numUnreadFeedItems;
+ this.feedCounters = feedIndicatorValues;
}
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
index defce5930..770c4f7d7 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
@@ -5,6 +5,9 @@ import android.content.Intent;
import android.database.Cursor;
import android.util.Log;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -29,6 +32,7 @@ import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.FeedMediaSizeService;
import de.danoeh.antennapod.core.service.GpodnetSyncService;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
@@ -180,6 +184,7 @@ public final class DBTasks {
if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) {
GpodnetSyncService.sendSyncIntent(context);
}
+ Log.d(TAG, "refreshAllFeeds autodownload");
autodownloadUndownloadedItems(context);
}
}.start();
@@ -188,47 +193,6 @@ public final class DBTasks {
}
}
- /**
- * Used by refreshExpiredFeeds to determine which feeds should be refreshed.
- * This method will use the value specified in the UserPreferences as the
- * expiration time.
- *
- * @param context Used for DB access.
- * @return A list of expired feeds. An empty list will be returned if there
- * are no expired feeds.
- */
- public static List<Feed> getExpiredFeeds(final Context context) {
- long millis = UserPreferences.getUpdateInterval();
-
- if (millis > 0) {
-
- List<Feed> feedList = DBReader.getExpiredFeedsList(context,
- millis);
- if (feedList.size() > 0) {
- refreshFeeds(context, feedList);
- }
- return feedList;
- } else {
- return new ArrayList<Feed>();
- }
- }
-
- /**
- * Refreshes expired Feeds in the list returned by the getExpiredFeedsList(Context, long) method in DBReader.
- * The expiration date parameter is determined by the update interval specified in {@link UserPreferences}.
- *
- * @param context Used for DB access.
- */
- public static void refreshExpiredFeeds(final Context context) {
- Log.d(TAG, "Refreshing expired feeds");
-
- new Thread() {
- public void run() {
- refreshFeeds(context, getExpiredFeeds(context));
- }
- }.start();
- }
-
private static void refreshFeeds(final Context context,
final List<Feed> feedList) {
@@ -320,24 +284,6 @@ public final class DBTasks {
}
/**
- * Notifies the database about a missing FeedImage file. This method will attempt to re-download the file.
- *
- * @param context Used for requesting the download.
- * @param image The FeedImage object.
- */
- public static void notifyInvalidImageFile(final Context context,
- final FeedImage image) {
- Log.i(TAG,
- "The DB was notified about an invalid image download. It will now try to re-download the image file");
- try {
- DownloadRequester.getInstance().downloadImage(context, image);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- Log.w(TAG, "Failed to download invalid feed image");
- }
- }
-
- /**
* Notifies the database about a missing FeedMedia file. This method will correct the FeedMedia object's values in the
* DB and send a FeedUpdateBroadcast.
*/
@@ -439,6 +385,7 @@ public final class DBTasks {
* @return A Future that can be used for waiting for the methods completion.
*/
public static Future<?> autodownloadUndownloadedItems(final Context context, final long... mediaIds) {
+ Log.d(TAG, "autodownloadUndownloadedItems");
return autodownloadExec.submit(ClientConfig.dbTasksCallbacks.getAutomaticDownloadAlgorithm()
.autoDownloadUndownloadedItems(context, mediaIds));
@@ -564,7 +511,7 @@ public final class DBTasks {
// all new feeds will have the most recent item marked as unplayed
FeedItem mostRecent = newFeed.getMostRecentItem();
if (mostRecent != null) {
- mostRecent.setRead(false);
+ mostRecent.setNew();
}
newFeedsList.add(newFeed);
@@ -575,16 +522,16 @@ public final class DBTasks {
Collections.sort(newFeed.getItems(), new FeedItemPubdateComparator());
- final boolean markNewItemsAsUnread;
+ final boolean markNewItems;
if (newFeed.getPageNr() == savedFeed.getPageNr()) {
if (savedFeed.compareWithOther(newFeed)) {
Log.d(TAG, "Feed has updated attribute values. Updating old feed's attributes");
savedFeed.updateFromOther(newFeed);
}
- markNewItemsAsUnread = true;
+ markNewItems = true;
} else {
Log.d(TAG, "New feed has a higher page number. Merging without marking as unread");
- markNewItemsAsUnread = false;
+ markNewItems = false;
savedFeed.setNextPageLink(newFeed.getNextPageLink());
}
if (savedFeed.getPreferences().compareWithOther(newFeed.getPreferences())) {
@@ -598,12 +545,11 @@ public final class DBTasks {
item.getIdentifyingValue());
if (oldItem == null) {
// item is new
- final int i = idx;
item.setFeed(savedFeed);
item.setAutoDownload(savedFeed.getPreferences().getAutoDownload());
- savedFeed.getItems().add(i, item);
- if (markNewItemsAsUnread) {
- item.setRead(false);
+ savedFeed.getItems().add(idx, item);
+ if (markNewItems) {
+ item.setNew();
}
} else {
oldItem.updateFromOther(item);
@@ -632,6 +578,8 @@ public final class DBTasks {
EventDistributor.getInstance().sendFeedUpdateBroadcast();
+ context.startService(new Intent(context, FeedMediaSizeService.class));
+
return resultFeeds;
}
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 fe5d0dfd3..12bc208a6 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
@@ -13,10 +13,10 @@ import org.shredzone.flattr4j.model.Flattr;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
@@ -100,6 +100,7 @@ public class DBWriter {
}
media.setDownloaded(false);
media.setFile_url(null);
+ media.setHasEmbeddedPicture(false);
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
adapter.setMedia(media);
@@ -184,13 +185,15 @@ public class DBWriter {
}
// delete stored media files and mark them as read
List<FeedItem> queue = DBReader.getQueue(context);
- boolean queueWasModified = false;
+ List<FeedItem> removed = new ArrayList<>();
if (feed.getItems() == null) {
DBReader.getFeedItemList(context, feed);
}
for (FeedItem item : feed.getItems()) {
- queueWasModified |= queue.remove(item);
+ if(queue.remove(item)) {
+ removed.add(item);
+ }
if (item.getMedia() != null
&& item.getMedia().isDownloaded()) {
File mediaFile = new File(item.getMedia()
@@ -213,8 +216,10 @@ public class DBWriter {
}
PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
- if (queueWasModified) {
+ if (removed.size() > 0) {
adapter.setQueue(queue);
+ EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.IRREVERSIBLE_REMOVED,
+ removed));
}
adapter.removeFeed(feed);
adapter.close();
@@ -351,7 +356,7 @@ public class DBWriter {
final PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
final List<FeedItem> queue = DBReader.getQueue(context, adapter);
- FeedItem item = null;
+ FeedItem item;
if (queue != null) {
if (!itemListContains(queue, itemId)) {
@@ -360,6 +365,9 @@ public class DBWriter {
queue.add(index, item);
adapter.setQueue(queue);
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED, item, index));
+ if(item.isNew()) {
+ DBWriter.markItemRead(context, false, item.getId());
+ }
}
}
}
@@ -374,14 +382,20 @@ public class DBWriter {
}
+ public static Future<?> addQueueItem(final Context context,
+ final long... itemIds) {
+ return addQueueItem(context, false, itemIds);
+ }
+
/**
* Appends FeedItem objects to the end of the queue. The 'read'-attribute of all items will be set to true.
* If a FeedItem is already in the queue, the FeedItem will not change its position in the queue.
*
* @param context A context that is used for opening a database connection.
+ * @param performAutoDownload true if an auto-download process should be started after the operation.
* @param itemIds IDs of the FeedItem objects that should be added to the queue.
*/
- public static Future<?> addQueueItem(final Context context,
+ public static Future<?> addQueueItem(final Context context, final boolean performAutoDownload,
final long... itemIds) {
return dbExec.submit(new Runnable() {
@@ -390,43 +404,45 @@ public class DBWriter {
if (itemIds.length > 0) {
final PodDBAdapter adapter = new PodDBAdapter(context);
adapter.open();
- final List<FeedItem> queue = DBReader.getQueue(context,
- adapter);
+ final List<FeedItem> queue = DBReader.getQueue(context, adapter);
if (queue != null) {
boolean queueModified = false;
- boolean unreadItemsModified = false;
- List<FeedItem> itemsToSave = new LinkedList<FeedItem>();
+ LongList markAsUnplayedIds = new LongList();
for (int i = 0; i < itemIds.length; i++) {
if (!itemListContains(queue, itemIds[i])) {
- final FeedItem item = DBReader.getFeedItem(
- context, itemIds[i]);
+ final FeedItem item = DBReader.getFeedItem(context, itemIds[i]);
if (item != null) {
// add item to either front ot back of queue
boolean addToFront = UserPreferences.enqueueAtFront();
-
- if(addToFront){
- queue.add(0, item);
+ if (addToFront) {
+ queue.add(0 + i, item);
} else {
queue.add(item);
}
-
queueModified = true;
+ if(item.isNew()) {
+ markAsUnplayedIds.add(item.getId());
+ }
}
}
}
if (queueModified) {
adapter.setQueue(queue);
EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED_ITEMS, queue));
+ if(markAsUnplayedIds.size() > 0) {
+ DBWriter.markItemRead(context, false, markAsUnplayedIds.toArray());
+ }
}
}
adapter.close();
- DBTasks.autodownloadUndownloadedItems(context);
+ if (performAutoDownload) {
+ DBTasks.autodownloadUndownloadedItems(context);
+ }
}
}
});
-
}
/**
@@ -594,16 +610,25 @@ public class DBWriter {
adapter.close();
}
- /**
- * Sets the 'read'-attribute of a FeedItem to the specified value.
+ /*
+ * Sets the 'read'-attribute of all specified FeedItems
*
* @param context A context that is used for opening a database connection.
- * @param itemId ID of the FeedItem
* @param read New value of the 'read'-attribute
+ * @param itemIds IDs of the FeedItems.
*/
- public static Future<?> markItemRead(final Context context, final long itemId,
- final boolean read) {
- return markItemRead(context, itemId, read, 0, false);
+ public static Future<?> markItemRead(final Context context, final boolean read, final long... itemIds) {
+ return dbExec.submit(new Runnable() {
+ @Override
+ public void run() {
+ final PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+ int played = read ? FeedItem.PLAYED : FeedItem.UNPLAYED;
+ adapter.setFeedItemRead(played, itemIds);
+ adapter.close();
+ EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ }
+ });
}
@@ -645,6 +670,35 @@ public class DBWriter {
* @param context A context that is used for opening a database connection.
* @param feedId ID of the Feed.
*/
+ public static Future<?> markFeedSeen(final Context context, final long feedId) {
+ return dbExec.submit(new Runnable() {
+
+ @Override
+ public void run() {
+ final PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+ Cursor itemCursor = adapter.getNewItemsIdsCursor(feedId);
+ long[] ids = new long[itemCursor.getCount()];
+ itemCursor.moveToFirst();
+ for (int i = 0; i < ids.length; i++) {
+ ids[i] = itemCursor.getLong(0);
+ itemCursor.moveToNext();
+ }
+ itemCursor.close();
+ adapter.setFeedItemRead(FeedItem.UNPLAYED, ids);
+ adapter.close();
+
+ EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ }
+ });
+ }
+
+ /**
+ * Sets the 'read'-attribute of all FeedItems of a specific Feed to true.
+ *
+ * @param context A context that is used for opening a database connection.
+ * @param feedId ID of the Feed.
+ */
public static Future<?> markFeedRead(final Context context, final long feedId) {
return dbExec.submit(new Runnable() {
@@ -660,13 +714,12 @@ public class DBWriter {
itemCursor.moveToNext();
}
itemCursor.close();
- adapter.setFeedItemRead(true, itemIds);
+ adapter.setFeedItemRead(FeedItem.PLAYED, itemIds);
adapter.close();
EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
}
});
-
}
/**
@@ -689,7 +742,7 @@ public class DBWriter {
itemCursor.moveToNext();
}
itemCursor.close();
- adapter.setFeedItemRead(true, itemIds);
+ adapter.setFeedItemRead(FeedItem.PLAYED, itemIds);
adapter.close();
EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
@@ -1057,9 +1110,34 @@ public class DBWriter {
EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
}
});
+ }
+
+ /**
+ * Sets the 'auto_download'-attribute of specific FeedItem.
+ *
+ * @param context A context that is used for opening a database connection.
+ * @param feed This feed's episodes will be processed.
+ * @param autoDownload If true, auto download will be enabled for the feed's episodes. Else,
+ * it will be disabled.
+ */
+ public static Future<?> setFeedsItemsAutoDownload(final Context context, final Feed feed,
+ final boolean autoDownload) {
+ Log.d(TAG, (autoDownload ? "Enabling" : "Disabling") + " auto download for items of feed " + feed.getId());
+ return dbExec.submit(new Runnable() {
+ @Override
+ public void run() {
+ final PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+ adapter.setFeedsItemsAutoDownload(feed, autoDownload);
+ adapter.close();
+
+ EventDistributor.getInstance().sendUnreadItemsUpdateBroadcast();
+ }
+ });
}
+
/**
* Set filter of the feed
*
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
index 386304530..bc34523cc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
@@ -18,7 +18,6 @@ import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedFile;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
@@ -186,15 +185,6 @@ public class DownloadRequester {
downloadFeed(context, feed, false);
}
- public synchronized void downloadImage(Context context, FeedImage image)
- throws DownloadRequestException {
- if (feedFileValid(image)) {
- FeedFile container = (image.getOwner() instanceof FeedFile) ? (FeedFile) image.getOwner() : null;
- download(context, image, container, new File(getImagefilePath(context),
- getImagefileName(image)), false, null, null, 0, false, null);
- }
- }
-
public synchronized void downloadMedia(Context context, FeedMedia feedmedia)
throws DownloadRequestException {
if (feedFileValid(feedmedia)) {
@@ -332,20 +322,6 @@ public class DownloadRequester {
return "feed-" + FileNameGenerator.generateFileName(filename);
}
- public synchronized String getImagefilePath(Context context)
- throws DownloadRequestException {
- return getExternalFilesDirOrThrowException(context, IMAGE_DOWNLOADPATH)
- .toString() + "/";
- }
-
- public synchronized String getImagefileName(FeedImage image) {
- String filename = image.getDownload_url();
- if (image.getOwner() != null && image.getOwner().getHumanReadableIdentifier() != null) {
- filename = image.getOwner().getHumanReadableIdentifier();
- }
- return "image-" + FileNameGenerator.generateFileName(filename);
- }
-
public synchronized String getMediafilePath(Context context, FeedMedia media)
throws DownloadRequestException {
File externalStorage = getExternalFilesDirOrThrowException(
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 6fabf9005..edb7598ab 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
@@ -9,6 +9,7 @@ import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
+import android.media.MediaMetadataRetriever;
import android.text.TextUtils;
import android.util.Log;
@@ -18,7 +19,8 @@ import java.util.Arrays;
import java.util.List;
import de.danoeh.antennapod.core.BuildConfig;
-import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.R;
+import de.danoeh.antennapod.core.event.ProgressEvent;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
@@ -26,11 +28,11 @@ import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.util.LongIntMap;
import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
-
-;
+import de.greenrobot.event.EventBus;
// TODO Remove media column from feeditem table
@@ -148,6 +150,7 @@ public class PodDBAdapter {
public static final String KEY_CHAPTER_TYPE = "type";
public static final String KEY_PLAYBACK_COMPLETION_DATE = "playback_completion_date";
public static final String KEY_AUTO_DOWNLOAD = "auto_download";
+ public static final String KEY_AUTO_DELETE_ACTION = "auto_delete_action";
public static final String KEY_PLAYED_DURATION = "played_duration";
public static final String KEY_USERNAME = "username";
public static final String KEY_PASSWORD = "password";
@@ -155,6 +158,8 @@ public class PodDBAdapter {
public static final String KEY_NEXT_PAGE_LINK = "next_page_link";
public static final String KEY_HIDE = "hide";
public static final String KEY_LAST_UPDATE_FAILED = "last_update_failed";
+ public static final String KEY_HAS_EMBEDDED_PICTURE = "has_embedded_picture";
+
// Table names
public static final String TABLE_NAME_FEEDS = "Feeds";
@@ -183,7 +188,8 @@ public class PodDBAdapter {
+ KEY_IS_PAGED + " INTEGER DEFAULT 0,"
+ KEY_NEXT_PAGE_LINK + " TEXT,"
+ KEY_HIDE + " TEXT,"
- + KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0)";
+ + KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0,"
+ + KEY_AUTO_DELETE_ACTION + " INTEGER DEFAULT 0)";
public static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE "
+ TABLE_NAME_FEED_ITEMS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
@@ -209,7 +215,7 @@ public class PodDBAdapter {
+ KEY_PLAYBACK_COMPLETION_DATE + " INTEGER,"
+ KEY_FEEDITEM + " INTEGER,"
+ KEY_PLAYED_DURATION + " INTEGER,"
- + KEY_AUTO_DOWNLOAD + " INTEGER)";
+ + KEY_HAS_EMBEDDED_PICTURE + " INTEGER)";
public static final String CREATE_TABLE_DOWNLOAD_LOG = "CREATE TABLE "
+ TABLE_NAME_DOWNLOAD_LOG + " (" + TABLE_PRIMARY_KEY + KEY_FEEDFILE
@@ -279,6 +285,7 @@ public class PodDBAdapter {
TABLE_NAME_FEEDS + "." + KEY_PASSWORD,
TABLE_NAME_FEEDS + "." + KEY_HIDE,
TABLE_NAME_FEEDS + "." + KEY_LAST_UPDATE_FAILED,
+ TABLE_NAME_FEEDS + "." + KEY_AUTO_DELETE_ACTION,
};
// column indices for FEED_SEL_STD
@@ -302,6 +309,7 @@ public class PodDBAdapter {
public static final int IDX_FEED_SEL_STD_NEXT_PAGE_LINK = 17;
public static final int IDX_FEED_SEL_PREFERENCES_USERNAME = 18;
public static final int IDX_FEED_SEL_PREFERENCES_PASSWORD = 19;
+ public static final int IDX_FEED_SEL_PREFERENCES_AUTO_DELETE_ACTION = 22;
/**
* Select all columns from the feeditems-table except description and
@@ -364,8 +372,7 @@ public class PodDBAdapter {
private static synchronized PodDBHelper getDbHelperSingleton(Context appContext) {
if (dbHelperSingleton == null) {
- dbHelperSingleton = new PodDBHelper(appContext, DATABASE_NAME, null,
- ClientConfig.storageCallbacks.getDatabaseVersion());
+ dbHelperSingleton = new PodDBHelper(appContext, DATABASE_NAME, null);
}
return dbHelperSingleton;
}
@@ -458,6 +465,7 @@ public class PodDBAdapter {
}
ContentValues values = new ContentValues();
values.put(KEY_AUTO_DOWNLOAD, prefs.getAutoDownload());
+ values.put(KEY_AUTO_DELETE_ACTION,prefs.getAutoDeleteAction().ordinal());
values.put(KEY_USERNAME, prefs.getUsername());
values.put(KEY_PASSWORD, prefs.getPassword());
db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?", new String[]{String.valueOf(prefs.getFeedID())});
@@ -516,6 +524,7 @@ public class PodDBAdapter {
values.put(KEY_DOWNLOAD_URL, media.getDownload_url());
values.put(KEY_DOWNLOADED, media.isDownloaded());
values.put(KEY_FILE_URL, media.getFile_url());
+ values.put(KEY_HAS_EMBEDDED_PICTURE, media.hasEmbeddedPicture());
if (media.getPlaybackCompletionDate() != null) {
values.put(KEY_PLAYBACK_COMPLETION_DATE, media
@@ -736,7 +745,13 @@ public class PodDBAdapter {
setFeed(item.getFeed());
}
values.put(KEY_FEED, item.getFeed().getId());
- values.put(KEY_READ, item.isRead());
+ if(item.isNew()) {
+ values.put(KEY_READ, FeedItem.NEW);
+ } else if(item.isPlayed()) {
+ values.put(KEY_READ, FeedItem.PLAYED);
+ } else {
+ values.put(KEY_READ, FeedItem.UNPLAYED);
+ }
values.put(KEY_HAS_CHAPTERS, item.getChapters() != null || item.hasChapters());
values.put(KEY_ITEM_IDENTIFIER, item.getItemIdentifier());
values.put(KEY_FLATTR_STATUS, item.getFlattrStatus().toLong());
@@ -768,7 +783,7 @@ public class PodDBAdapter {
db.beginTransaction();
ContentValues values = new ContentValues();
- values.put(KEY_READ, read);
+ values.put(KEY_READ, read ? FeedItem.PLAYED : FeedItem.UNPLAYED);
db.update(TABLE_NAME_FEED_ITEMS, values, KEY_ID + "=?", new String[]{String.valueOf(itemId)});
if (resetMediaPosition) {
@@ -781,7 +796,7 @@ public class PodDBAdapter {
db.endTransaction();
}
- public void setFeedItemRead(boolean read, long... itemIds) {
+ public void setFeedItemRead(int read, long... itemIds) {
db.beginTransaction();
ContentValues values = new ContentValues();
for (long id : itemIds) {
@@ -846,6 +861,13 @@ public class PodDBAdapter {
new String[]{String.valueOf(feedItem.getId())});
}
+ public void setFeedsItemsAutoDownload(Feed feed, boolean autoDownload) {
+ final String sql = "UPDATE " + TABLE_NAME_FEED_ITEMS
+ + " SET " + KEY_AUTO_DOWNLOAD + "="+ (autoDownload ? "1" : "0")
+ + " WHERE " + KEY_FEED + "=" + feed.getId();
+ db.execSQL(sql);
+ }
+
public long getDownloadLogSize() {
final String query = String.format("SELECT COUNT(%s) FROM %s", KEY_ID, TABLE_NAME_DOWNLOAD_LOG);
Cursor result = db.rawQuery(query, null);
@@ -874,8 +896,7 @@ public class PodDBAdapter {
values.put(KEY_ID, i);
values.put(KEY_FEEDITEM, item.getId());
values.put(KEY_FEED, item.getFeed().getId());
- db.insertWithOnConflict(TABLE_NAME_QUEUE, null, values,
- SQLiteDatabase.CONFLICT_REPLACE);
+ db.insertWithOnConflict(TABLE_NAME_QUEUE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
}
db.setTransactionSuccessful();
db.endTransaction();
@@ -967,13 +988,6 @@ public class PodDBAdapter {
return db.query(TABLE_NAME_FEEDS, new String[]{KEY_ID, KEY_DOWNLOAD_URL}, null, null, null, null, null);
}
- public final Cursor getExpiredFeedsCursor(long expirationTime) {
- Cursor c = db.query(TABLE_NAME_FEEDS, FEED_SEL_STD, KEY_LASTUPDATE + " < " + String.valueOf(System.currentTimeMillis() - expirationTime),
- null, null, null,
- null);
- return c;
- }
-
/**
* Returns a cursor with all FeedItems of a Feed. Uses FEEDITEM_SEL_FI_SMALL
*
@@ -1053,6 +1067,7 @@ public class PodDBAdapter {
/**
* Returns a cursor which contains all feed items in the queue. The returned
* cursor uses the FEEDITEM_SEL_FI_SMALL selection.
+ * cursor uses the FEEDITEM_SEL_FI_SMALL selection.
*/
public final Cursor getQueueCursor() {
Object[] args = (Object[]) new String[]{
@@ -1084,44 +1099,46 @@ public class PodDBAdapter {
*/
public final Cursor getUnreadItemsCursor() {
Cursor c = db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, KEY_READ
- + "=0", null, null, null, KEY_PUBDATE + " DESC");
+ + "<" + FeedItem.PLAYED, null, null, null, KEY_PUBDATE + " DESC");
return c;
}
public final Cursor getNewItemIdsCursor() {
- final String query = "SELECT " + TABLE_NAME_FEED_ITEMS + "." + KEY_ID
+ final String query = "SELECT " + KEY_ID
+ " FROM " + TABLE_NAME_FEED_ITEMS
- + " INNER JOIN " + TABLE_NAME_FEED_MEDIA + " ON "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
- + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM
- + " LEFT OUTER JOIN " + TABLE_NAME_QUEUE + " ON "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
- + TABLE_NAME_QUEUE + "." + KEY_FEEDITEM
- + " WHERE "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_READ + " = 0 AND " // unplayed
- + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + " = 0 AND " // undownloaded
- + TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + " = 0 AND " // not partially played
- + TABLE_NAME_QUEUE + "." + KEY_ID + " IS NULL"; // not in queue
+ + " WHERE " + KEY_READ + "=" + FeedItem.NEW;
+ return db.rawQuery(query, null);
+ }
+
+ public final Cursor getFeedMediaUnknownSizeCursor() {
+ final String query = "SELECT * "
+ + " FROM " + TABLE_NAME_FEED_MEDIA
+ + " WHERE " + KEY_SIZE + "<= 0";
return db.rawQuery(query, null);
}
/**
+ * Returns a cursor which contains all items of a feed that are considered new.
+ * The returned cursor uses the FEEDITEM_SEL_FI_SMALL selection.
+ */
+ public final Cursor getNewItemsIdsCursor(long feedId) {
+ final String query = "SELECT " + KEY_ID
+ + " FROM " + TABLE_NAME_FEED_ITEMS
+ + " WHERE " + KEY_FEED + "=" + feedId
+ + " AND " + KEY_READ + "=" + FeedItem.NEW
+ + " ORDER BY " + KEY_PUBDATE + " DESC";
+ Cursor c = db.rawQuery(query, null);
+ return c;
+ }
+
+ /**
* Returns a cursor which contains all feed items that are considered new.
* The returned cursor uses the FEEDITEM_SEL_FI_SMALL selection.
*/
public final Cursor getNewItemsCursor() {
- final String query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS
- + " INNER JOIN " + TABLE_NAME_FEED_MEDIA + " ON "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
- + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM
- + " LEFT OUTER JOIN " + TABLE_NAME_QUEUE + " ON "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
- + TABLE_NAME_QUEUE + "." + KEY_FEEDITEM
- + " WHERE "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_READ + " = 0 AND " // unplayed
- + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + " = 0 AND " // undownloaded
- + TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + " = 0 AND " // not partially played
- + TABLE_NAME_QUEUE + "." + KEY_ID + " IS NULL" // not in queue
+ final String query = "SELECT " + SEL_FI_SMALL_STR
+ + " FROM " + TABLE_NAME_FEED_ITEMS
+ + " WHERE " + KEY_READ + "=" + FeedItem.NEW
+ " ORDER BY " + KEY_PUBDATE + " DESC";
Cursor c = db.rawQuery(query, null);
return c;
@@ -1133,11 +1150,11 @@ public class PodDBAdapter {
}
public Cursor getDownloadedItemsCursor() {
- final String query = "SELECT " + SEL_FI_SMALL_STR + " FROM " + TABLE_NAME_FEED_ITEMS
- + " INNER JOIN " + TABLE_NAME_FEED_MEDIA + " ON "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
- + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM + " WHERE "
- + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + ">0";
+ final String query = "SELECT " + SEL_FI_SMALL_STR
+ + " FROM " + TABLE_NAME_FEED_ITEMS
+ + " INNER JOIN " + TABLE_NAME_FEED_MEDIA
+ + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "=" + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM
+ + " WHERE " + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + ">0";
Cursor c = db.rawQuery(query, null);
return c;
}
@@ -1271,19 +1288,9 @@ public class PodDBAdapter {
}
public final int getNumberOfNewItems() {
- final String query = "SELECT COUNT(" + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + ")"
- +" FROM " + TABLE_NAME_FEED_ITEMS
- + " LEFT JOIN " + TABLE_NAME_FEED_MEDIA + " ON "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
- + TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM
- + " LEFT JOIN " + TABLE_NAME_QUEUE + " ON "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
- + TABLE_NAME_QUEUE + "." + KEY_FEEDITEM
- + " WHERE "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_READ + " = 0 AND " // unplayed
- + TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + " = 0 AND " // undownloaded
- + TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + " = 0 AND " // not partially played
- + TABLE_NAME_QUEUE + "." + KEY_ID + " IS NULL"; // not in queue
+ final String query = "SELECT COUNT(" + KEY_ID + ")"
+ + " FROM " + TABLE_NAME_FEED_ITEMS
+ + " WHERE " + KEY_READ + "=" + FeedItem.NEW;
Cursor c = db.rawQuery(query, null);
int result = 0;
if (c.moveToFirst()) {
@@ -1293,7 +1300,20 @@ public class PodDBAdapter {
return result;
}
- public final LongIntMap getNumberOfUnreadFeedItems(long... feedIds) {
+ public final LongIntMap getFeedCounters(long... feedIds) {
+ int counter = UserPreferences.getFeedCounter();
+ String whereRead;
+ if(counter == UserPreferences.FEED_COUNTER_SHOW_NEW_UNPLAYED_SUM) {
+ whereRead = "(" + KEY_READ + "=" + FeedItem.NEW
+ + " OR " + KEY_READ + "=" + FeedItem.UNPLAYED + ")";
+ } else if(counter == UserPreferences.FEED_COUNTER_SHOW_NEW) {
+ whereRead = KEY_READ + "=" + FeedItem.NEW;
+ } else if(counter == UserPreferences.FEED_COUNTER_SHOW_UNPLAYED) {
+ whereRead = KEY_READ + "=" + FeedItem.UNPLAYED;
+ } else {
+ return new LongIntMap(0);
+ }
+
// work around TextUtils.join wanting only boxed items
// and StringUtils.join() causing NoSuchMethodErrors on MIUI
StringBuilder builder = new StringBuilder();
@@ -1309,8 +1329,8 @@ public class PodDBAdapter {
final String query = "SELECT " + KEY_FEED + ", COUNT(" + KEY_ID + ") AS count "
+ " FROM " + TABLE_NAME_FEED_ITEMS
+ " WHERE " + KEY_FEED + " IN (" + builder.toString() + ") "
- + " AND " + KEY_READ + " = 0"
- + " GROUP BY " + KEY_FEED;
+ + " AND " + whereRead + " GROUP BY " + KEY_FEED;
+
Cursor c = db.rawQuery(query, null);
LongIntMap result = new LongIntMap(c.getCount());
if (c.moveToFirst()) {
@@ -1463,17 +1483,22 @@ public class PodDBAdapter {
* Helper class for opening the Antennapod database.
*/
private static class PodDBHelper extends SQLiteOpenHelper {
+
+ private final static int VERSION = 1030002;
+
+ private Context context;
+
/**
* Constructor.
*
* @param context Context to use
* @param name Name of the database
* @param factory to use for creating cursor objects
- * @param version number of the database
*/
public PodDBHelper(final Context context, final String name,
- final CursorFactory factory, final int version) {
- super(context, name, factory, version);
+ final CursorFactory factory) {
+ super(context, name, factory, VERSION);
+ this.context = context;
}
@Override
@@ -1497,7 +1522,199 @@ public class PodDBAdapter {
@Override
public void onUpgrade(final SQLiteDatabase db, final int oldVersion,
final int newVersion) {
- ClientConfig.storageCallbacks.onUpgrade(db, oldVersion, newVersion);
+ EventBus.getDefault().post(ProgressEvent.start(context.getString(R.string.progress_upgrading_database)));
+ Log.w("DBAdapter", "Upgrading from version " + oldVersion + " to "
+ + newVersion + ".");
+ if (oldVersion <= 1) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN "
+ + KEY_TYPE + " TEXT");
+ }
+ if (oldVersion <= 2) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS
+ + " ADD COLUMN " + KEY_LINK + " TEXT");
+ }
+ if (oldVersion <= 3) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " ADD COLUMN " + KEY_ITEM_IDENTIFIER + " TEXT");
+ }
+ if (oldVersion <= 4) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN "
+ + KEY_FEED_IDENTIFIER + " TEXT");
+ }
+ if (oldVersion <= 5) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG
+ + " ADD COLUMN " + KEY_REASON_DETAILED + " TEXT");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG
+ + " ADD COLUMN " + KEY_DOWNLOADSTATUS_TITLE + " TEXT");
+ }
+ if (oldVersion <= 6) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS
+ + " ADD COLUMN " + KEY_CHAPTER_TYPE + " INTEGER");
+ }
+ if (oldVersion <= 7) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " ADD COLUMN " + KEY_PLAYBACK_COMPLETION_DATE
+ + " INTEGER");
+ }
+ if (oldVersion <= 8) {
+ final int KEY_ID_POSITION = 0;
+ final int KEY_MEDIA_POSITION = 1;
+
+ // Add feeditem column to feedmedia table
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " ADD COLUMN " + KEY_FEEDITEM
+ + " INTEGER");
+ Cursor feeditemCursor = db.query(PodDBAdapter.TABLE_NAME_FEED_ITEMS,
+ new String[]{KEY_ID, KEY_MEDIA}, "? > 0",
+ new String[]{KEY_MEDIA}, null, null, null);
+ if (feeditemCursor.moveToFirst()) {
+ db.beginTransaction();
+ ContentValues contentValues = new ContentValues();
+ do {
+ long mediaId = feeditemCursor.getLong(KEY_MEDIA_POSITION);
+ contentValues.put(KEY_FEEDITEM, feeditemCursor.getLong(KEY_ID_POSITION));
+ db.update(PodDBAdapter.TABLE_NAME_FEED_MEDIA, contentValues, KEY_ID + "=?", new String[]{String.valueOf(mediaId)});
+ contentValues.clear();
+ } while (feeditemCursor.moveToNext());
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ }
+ feeditemCursor.close();
+ }
+ if (oldVersion <= 9) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + KEY_AUTO_DOWNLOAD
+ + " INTEGER DEFAULT 1");
+ }
+ if (oldVersion <= 10) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + KEY_FLATTR_STATUS
+ + " INTEGER");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " ADD COLUMN " + KEY_FLATTR_STATUS
+ + " INTEGER");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " ADD COLUMN " + KEY_PLAYED_DURATION
+ + " INTEGER");
+ }
+ if (oldVersion <= 11) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + KEY_USERNAME
+ + " TEXT");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + KEY_PASSWORD
+ + " TEXT");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " ADD COLUMN " + KEY_IMAGE
+ + " INTEGER");
+ }
+ if (oldVersion <= 12) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + KEY_IS_PAGED + " INTEGER DEFAULT 0");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + KEY_NEXT_PAGE_LINK + " TEXT");
+ }
+ if (oldVersion <= 13) {
+ // remove duplicate rows in "Chapters" table that were created because of a bug.
+ db.execSQL(String.format("DELETE FROM %s WHERE %s NOT IN " +
+ "(SELECT MIN(%s) as %s FROM %s GROUP BY %s,%s,%s,%s,%s)",
+ PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS,
+ KEY_ID,
+ KEY_ID,
+ KEY_ID,
+ PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS,
+ KEY_TITLE,
+ KEY_START,
+ KEY_FEEDITEM,
+ KEY_LINK,
+ KEY_CHAPTER_TYPE));
+ }
+ if(oldVersion <= 14) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " ADD COLUMN " + KEY_AUTO_DOWNLOAD + " INTEGER");
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " SET " + KEY_AUTO_DOWNLOAD + " = "
+ + "(SELECT " + KEY_AUTO_DOWNLOAD
+ + " FROM " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " WHERE " + PodDBAdapter.TABLE_NAME_FEEDS + "." + KEY_ID
+ + " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + ")");
+
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + KEY_HIDE + " TEXT");
+
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0");
+
+ // create indexes
+ db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_FEED);
+ db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_IMAGE);
+ db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDMEDIA_FEEDITEM);
+ db.execSQL(PodDBAdapter.CREATE_INDEX_QUEUE_FEEDITEM);
+ db.execSQL(PodDBAdapter.CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM);
+ }
+ if(oldVersion <= 15) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " ADD COLUMN " + KEY_HAS_EMBEDDED_PICTURE + " INTEGER DEFAULT -1");
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " SET " + KEY_HAS_EMBEDDED_PICTURE + "=0"
+ + " WHERE " + KEY_DOWNLOADED + "=0");
+ Cursor c = db.rawQuery("SELECT " + KEY_FILE_URL
+ + " FROM " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " WHERE " + KEY_DOWNLOADED + "=1 "
+ + " AND " + KEY_HAS_EMBEDDED_PICTURE + "=-1", null);
+ if(c.moveToFirst()) {
+ MediaMetadataRetriever mmr = new MediaMetadataRetriever();
+ do {
+ String fileUrl = c.getString(0);
+ try {
+ mmr.setDataSource(fileUrl);
+ byte[] image = mmr.getEmbeddedPicture();
+ if (image != null) {
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " SET " + KEY_HAS_EMBEDDED_PICTURE + "=1"
+ + " WHERE " + KEY_FILE_URL + "='"+ fileUrl + "'");
+ } else {
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " SET " + KEY_HAS_EMBEDDED_PICTURE + "=0"
+ + " WHERE " + KEY_FILE_URL + "='"+ fileUrl + "'");
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ } while(c.moveToNext());
+ }
+ c.close();
+ }
+ if(oldVersion <= 16) {
+ String selectNew = "SELECT " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_ID
+ + " FROM " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " INNER JOIN " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + " ON "
+ + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
+ + PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM
+ + " LEFT OUTER JOIN " + PodDBAdapter.TABLE_NAME_QUEUE + " ON "
+ + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
+ + PodDBAdapter.TABLE_NAME_QUEUE + "." + KEY_FEEDITEM
+ + " WHERE "
+ + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_READ + " = 0 AND " // unplayed
+ + PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + " = 0 AND " // undownloaded
+ + PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + " = 0 AND " // not partially played
+ + PodDBAdapter.TABLE_NAME_QUEUE + "." + KEY_ID + " IS NULL"; // not in queue
+ String sql = "UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " SET " + KEY_READ + "=" + FeedItem.NEW
+ + " WHERE " + KEY_ID + " IN (" + selectNew + ")";
+ Log.d("Migration", "SQL: " + sql);
+ db.execSQL(sql);
+ }
+ if(oldVersion <= 17) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DELETE_ACTION + " INTEGER DEFAULT 0");
+ }
+ if(oldVersion < 1030005) {
+ db.execSQL("UPDATE FeedItems SET auto_download=0 WHERE " +
+ "(read=1 OR id IN (SELECT feeditem FROM FeedMedia WHERE position>0 OR downloaded=1)) " +
+ "AND id NOT IN (SELECT feeditem FROM Queue)");
+ }
+ EventBus.getDefault().post(ProgressEvent.end());
}
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java
index 6455332be..31eb2efd6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java
@@ -57,6 +57,10 @@ public class NSRSS20 extends Namespace {
long size = 0;
try {
size = Long.parseLong(attributes.getValue(ENC_LEN));
+ if(size < 16384) {
+ // less than 16kb is suspicious, check manually
+ size = 0;
+ }
} catch (NumberFormatException e) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Length attribute could not be parsed.");
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
index a0b514bd6..1b929b214 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
@@ -1,7 +1,10 @@
package de.danoeh.antennapod.core.util;
+import android.content.Context;
import android.util.Log;
+import de.danoeh.antennapod.core.R;
+
/** Provides methods for converting various units. */
public final class Converter {
/** Class shall not be instantiated. */
@@ -23,7 +26,7 @@ public final class Converter {
/** Determines the length of the number for best readability.*/
private static final int NUM_LENGTH = 1024;
-
+ private static final int DAYS_MIL = 86400000;
private static final int HOURS_MIL = 3600000;
private static final int MINUTES_MIL = 60000;
private static final int SECONDS_MIL = 1000;
@@ -99,5 +102,21 @@ public final class Converter {
return Integer.valueOf(parts[0]) * 3600 * 1000 +
Integer.valueOf(parts[1]) * 1000 * 60;
}
+
+ /** Converts milliseconds to a localized string containing hours and minutes */
+ public static String getDurationStringLocalized(Context context, long duration) {
+ int h = (int)(duration / HOURS_MIL);
+ int rest = (int)(duration - h * HOURS_MIL);
+ int m = rest / MINUTES_MIL;
+
+ String result = "";
+ if(h > 0) {
+ String hours = context.getResources().getQuantityString(R.plurals.time_hours_quantified, h, h);
+ result += hours + " ";
+ }
+ String minutes = context.getResources().getQuantityString(R.plurals.time_minutes_quantified, m, m);
+ result += minutes;
+ return result;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
index b6df2dc85..c0233f684 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
@@ -8,6 +8,7 @@ import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
+import java.util.TimeZone;
/**
* Parses several date formats.
@@ -16,49 +17,73 @@ public class DateUtils {
private static final String TAG = "DateUtils";
+ private static final SimpleDateFormat parser = new SimpleDateFormat("", Locale.US);
+ private static final TimeZone defaultTimezone = TimeZone.getTimeZone("GMT");
+
+ static {
+ parser.setLenient(false);
+ parser.setTimeZone(defaultTimezone);
+ }
+
public static Date parse(final String input) {
if(input == null) {
- throw new IllegalArgumentException("Date most not be null");
+ throw new IllegalArgumentException("Date must not be null");
}
- String date = input.replace('/', '-');
+ String date = input.trim().replace('/', '-').replaceAll("( ){2,}+", " ");
+
+ // if datetime is more precise than seconds, make sure the value is in ms
if(date.contains(".")) {
int start = date.indexOf('.');
int current = start+1;
while(current < date.length() && Character.isDigit(date.charAt(current))) {
current++;
}
+ // even more precise than microseconds: discard further decimal places
if(current - start > 4) {
if(current < date.length()-1) {
date = date.substring(0, start + 4) + date.substring(current);
} else {
date = date.substring(0, start + 4);
}
+ // less than 4 decimal places: pad to have a consistent format for the parser
} else if(current - start < 4) {
if(current < date.length()-1) {
date = date.substring(0, current) + StringUtils.repeat("0", 4-(current-start)) + date.substring(current);
} else {
date = date.substring(0, current) + StringUtils.repeat("0", 4-(current-start));
}
-
}
}
String[] patterns = {
"dd MMM yy HH:mm:ss Z",
"dd MMM yy HH:mm Z",
"EEE, dd MMM yyyy HH:mm:ss Z",
+ "EEE, dd MMM yyyy HH:mm:ss",
"EEE, dd MMMM yyyy HH:mm:ss Z",
+ "EEE, dd MMMM yyyy HH:mm:ss",
+ "EEEE, dd MMM yyyy HH:mm:ss Z",
"EEEE, dd MMM yy HH:mm:ss Z",
+ "EEEE, dd MMM yyyy HH:mm:ss",
+ "EEEE, dd MMM yy HH:mm:ss",
"EEE MMM d HH:mm:ss yyyy",
+ "EEE, dd MMM yyyy HH:mm Z",
+ "EEE, dd MMM yyyy HH:mm",
+ "EEE, dd MMMM yyyy HH:mm Z",
+ "EEE, dd MMMM yyyy HH:mm",
+ "EEEE, dd MMM yyyy HH:mm Z",
+ "EEEE, dd MMM yy HH:mm Z",
+ "EEEE, dd MMM yyyy HH:mm",
+ "EEEE, dd MMM yy HH:mm",
+ "EEE MMM d HH:mm yyyy",
"yyyy-MM-dd'T'HH:mm:ss",
- "yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ss.SSS Z",
+ "yyyy-MM-dd'T'HH:mm:ss.SSS",
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss'Z'",
"yyyy-MM-ddZ",
"yyyy-MM-dd"
};
- SimpleDateFormat parser = new SimpleDateFormat("", Locale.US);
- parser.setLenient(false);
+
ParsePosition pos = new ParsePosition(0);
for(String pattern : patterns) {
parser.applyPattern(pattern);
@@ -69,7 +94,7 @@ public class DateUtils {
}
}
- Log.d(TAG, "Could not parse date string \"" + input + "\"");
+ Log.d(TAG, "Could not parse date string \"" + input + "\" [" + date + "]");
return null;
}
@@ -109,6 +134,7 @@ public class DateUtils {
public static String formatRFC3339UTC(Date date) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
+ format.setTimeZone(defaultTimezone);
return format.format(date);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
new file mode 100644
index 000000000..2d5a6e5a1
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
@@ -0,0 +1,18 @@
+package de.danoeh.antennapod.core.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+
+import java.util.List;
+
+public class IntentUtils {
+
+ public static boolean isCallable(final Context context, final Intent intent) {
+ List<ResolveInfo> list = context.getPackageManager().queryIntentActivities(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ return list.size() > 0;
+ }
+
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
index 3a349e221..9296039f0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
@@ -10,14 +10,16 @@ import android.util.Log;
import java.util.Arrays;
import java.util.List;
-import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.preferences.UserPreferences;
public class NetworkUtils {
- private static final String TAG = "NetworkUtils";
- private NetworkUtils() {
+ private static final String TAG = NetworkUtils.class.getSimpleName();
+ private static Context context;
+
+ public static void init(Context context) {
+ NetworkUtils.context = context;
}
/**
@@ -26,18 +28,16 @@ public class NetworkUtils {
* network that is on the 'selected networks' list of the Wi-Fi filter for
* automatic downloads and false otherwise.
* */
- public static boolean autodownloadNetworkAvailable(Context context) {
+ public static boolean autodownloadNetworkAvailable() {
ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
if (networkInfo != null) {
if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Device is connected to Wi-Fi");
+ Log.d(TAG, "Device is connected to Wi-Fi");
if (networkInfo.isConnected()) {
if (!UserPreferences.isEnableAutodownloadWifiFilter()) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Auto-dl filter is disabled");
+ Log.d(TAG, "Auto-dl filter is disabled");
return true;
} else {
WifiManager wm = (WifiManager) context
@@ -48,31 +48,28 @@ public class NetworkUtils {
.getAutodownloadSelectedNetworks());
if (selectedNetworks.contains(Integer.toString(wifiInfo
.getNetworkId()))) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Current network is on the selected networks list");
+ Log.d(TAG, "Current network is on the selected networks list");
return true;
}
}
}
}
}
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Network for auto-dl is not available");
+ Log.d(TAG, "Network for auto-dl is not available");
return false;
}
- public static boolean networkAvailable(Context context) {
+ public static boolean networkAvailable() {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = cm.getActiveNetworkInfo();
return info != null && info.isConnected();
}
- public static boolean isDownloadAllowed(Context context) {
- return UserPreferences.isAllowMobileUpdate() || NetworkUtils.connectedToWifi(context);
+ public static boolean isDownloadAllowed() {
+ return UserPreferences.isAllowMobileUpdate() || NetworkUtils.connectedToWifi();
}
- public static boolean connectedToWifi(Context context) {
+ public static boolean connectedToWifi() {
ConnectivityManager connManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mWifi = connManager
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
index 85f32ed50..35916a604 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
@@ -2,6 +2,8 @@ package de.danoeh.antennapod.core.util;
import android.content.Context;
import android.content.Intent;
+
+import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -11,24 +13,49 @@ public class ShareUtils {
private ShareUtils() {}
- public static void shareLink(Context context, String link) {
+ public static void shareLink(Context context, String text) {
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
- i.putExtra(Intent.EXTRA_SUBJECT, "Sharing URL");
- i.putExtra(Intent.EXTRA_TEXT, link);
- context.startActivity(Intent.createChooser(i, "Share URL"));
+ i.putExtra(Intent.EXTRA_TEXT, text);
+ context.startActivity(Intent.createChooser(i, context.getString(R.string.share_url_label)));
}
-
- public static void shareFeedItemLink(Context context, FeedItem item) {
- shareLink(context, item.getLink());
+
+ public static void shareFeedlink(Context context, Feed feed) {
+ shareLink(context, feed.getTitle() + ": " + feed.getLink());
}
public static void shareFeedDownloadLink(Context context, Feed feed) {
- shareLink(context, feed.getDownload_url());
+ shareLink(context, feed.getTitle() + ": " + feed.getDownload_url());
}
-
- public static void shareFeedlink(Context context, Feed feed) {
- shareLink(context, feed.getLink());
+
+ public static void shareFeedItemLink(Context context, FeedItem item) {
+ shareFeedItemLink(context, item, false);
+ }
+
+ public static void shareFeedItemDownloadLink(Context context, FeedItem item) {
+ shareFeedItemDownloadLink(context, item, false);
+ }
+
+ private static String getItemShareText(FeedItem item) {
+ return item.getFeed().getTitle() + ": " + item.getTitle();
+ }
+
+ public static void shareFeedItemLink(Context context, FeedItem item, boolean withPosition) {
+ String text = getItemShareText(item) + " " + item.getLink();
+ if(withPosition) {
+ int pos = item.getMedia().getPosition();
+ text = item.getLink() + " [" + Converter.getDurationStringLong(pos) + "]";
+ }
+ shareLink(context, text);
+ }
+
+ public static void shareFeedItemDownloadLink(Context context, FeedItem item, boolean withPosition) {
+ String text = getItemShareText(item) + " " + item.getMedia().getDownload_url();
+ if(withPosition) {
+ int pos = item.getMedia().getPosition();
+ text += " [" + Converter.getDurationStringLong(pos) + "]";
+ }
+ shareLink(context, text);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java
index 50792ae26..3b9e6120c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java
@@ -42,12 +42,6 @@ public class FlattrUtils {
private static final String PREF_ACCESS_TOKEN = "de.danoeh.antennapod.preference.flattrAccessToken";
- // Flattr URL for this app.
- public static final String APP_URL = "http://antennapod.com";
- // Human-readable flattr-page.
- public static final String APP_LINK = "https://flattr.com/thing/745609/";
- public static final String APP_THING_ID = "745609";
-
private static volatile AccessToken cachedToken;
private static AndroidAuthenticator createAuthenticator() {
@@ -110,18 +104,6 @@ public class FlattrUtils {
storeToken(null);
}
- public static Thing getAppThing(Context context) {
- FlattrService fs = FlattrServiceCreator.getService(retrieveToken());
- try {
- Thing thing = fs.getThing(Thing.withId(APP_THING_ID));
- return thing;
- } catch (FlattrException e) {
- e.printStackTrace();
- showErrorDialog(context, e.getMessage());
- return null;
- }
- }
-
public static void clickUrl(Context context, String url)
throws FlattrException {
if (hasToken()) {
@@ -245,37 +227,6 @@ public class FlattrUtils {
}
}
- public static void showForbiddenDialog(final Context context,
- final String url) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.action_forbidden_title);
- builder.setMessage(R.string.action_forbidden_msg);
- builder.setPositiveButton(R.string.authenticate_now_label,
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- context.startActivity(
- ClientConfig.flattrCallbacks.getFlattrAuthenticationActivityIntent(context));
- }
-
- }
- );
- builder.setNegativeButton(R.string.visit_website_label,
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Uri uri = Uri.parse(url);
- context.startActivity(new Intent(Intent.ACTION_VIEW,
- uri));
- }
-
- }
- );
- builder.create().show();
- }
-
public static void showErrorDialog(final Context context, final String msg) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.error_label);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
index 7ebd580f7..752e95985 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
@@ -7,7 +7,7 @@ import android.util.Log;
import java.util.List;
-import de.danoeh.antennapod.core.asynctask.PicassoImageResource;
+import de.danoeh.antennapod.core.asynctask.ImageResource;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
@@ -18,7 +18,7 @@ import de.danoeh.antennapod.core.util.ShownotesProvider;
* Interface for objects that can be played by the PlaybackService.
*/
public interface Playable extends Parcelable,
- ShownotesProvider, PicassoImageResource {
+ ShownotesProvider, ImageResource {
/**
* Save information about the playable in a preference so that it can be
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
index a0d12d3e7..ba5428b81 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
@@ -555,7 +555,7 @@ public abstract class PlaybackController {
* Should be used by classes which implement the OnSeekBarChanged interface.
*/
public void onSeekBarStopTrackingTouch(SeekBar seekBar, float prog) {
- if (playbackService != null) {
+ if (playbackService != null && media != null) {
playbackService.seekTo((int) (prog * media.getDuration()));
setupPositionObserver();
}
diff --git a/core/src/main/res/values-az/strings.xml b/core/src/main/res/values-az/strings.xml
index b52ecf4a4..3610f837e 100644
--- a/core/src/main/res/values-az/strings.xml
+++ b/core/src/main/res/values-az/strings.xml
@@ -46,7 +46,6 @@
<string name="mark_all_read_label">Hamısını oxunmuş kimi işarələ</string>
<string name="show_info_label">Məlumatı göstər</string>
<string name="share_link_label">Web-səhifəyi paylaş</string>
- <string name="share_source_label">Kanalı paylaş</string>
<string name="feed_delete_confirmation_msg">Bütün kanallar və epizodlar silinəçək.</string>
<!--actions on feeditems-->
<string name="download_label">Yüklə</string>
@@ -134,8 +133,6 @@
<string name="pref_followQueue_sum">Oynatma başa çatanda növbədə irəlidəki epizodu oynat</string>
<string name="playback_pref">Oynatma</string>
<string name="network_pref">Şəbəkə</string>
- <string name="pref_autoUpdateIntervall_title">Təzələmə intervalı</string>
- <string name="pref_autoUpdateIntervall_sum">Kanalın avtomatik təzələməsinin intervalını seç ya da keçir onu</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Təkçə Wi-Fi vasitəsiilə yüklə</string>
<string name="pref_followQueue_title">Fasiləsiz oynatma</string>
<string name="pref_downloadMediaOnWifiOnly_title">Wi-Fi vasitəsiilə yükləmə</string>
@@ -210,5 +207,6 @@
<string name="downloading_label">Yükləmə...</string>
<!--Content descriptions for image buttons-->
<!--Feed information screen-->
+ <!--Progress information-->
<!--AntennaPodSP-->
</resources>
diff --git a/core/src/main/res/values-ca/strings.xml b/core/src/main/res/values-ca/strings.xml
index be7a73e6d..c4887e512 100644
--- a/core/src/main/res/values-ca/strings.xml
+++ b/core/src/main/res/values-ca/strings.xml
@@ -71,7 +71,6 @@
<string name="show_info_label">Mostra informació</string>
<string name="remove_feed_label">Esborra podcast</string>
<string name="share_link_label">Comparteix l\'enllaç de la plana</string>
- <string name="share_source_label">Comparteix l\'enllaç del canal</string>
<string name="feed_delete_confirmation_msg">Confirmeu que, efectivament, voleu suprimir aquest canal i tots els episodis que us n\'heu baixat.</string>
<string name="feed_remover_msg">S\'està esborrant el canal</string>
<string name="load_complete_feed">S\'ha actualitzat el canal</string>
@@ -197,8 +196,6 @@
<string name="pref_auto_delete_title">Esborrat automàtic</string>
<string name="playback_pref">Reproducció</string>
<string name="network_pref">Xarxa</string>
- <string name="pref_autoUpdateIntervall_title">Interval d\'actualització</string>
- <string name="pref_autoUpdateIntervall_sum">Especifiqueu l\'interval en què els canals s\'actualitzen de forma automàtica, o deshabiliteu la funcionalitat.</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Només baixa fitxers a través d\'una xarxa sense fils</string>
<string name="pref_followQueue_title">Reproducció continuada</string>
<string name="pref_downloadMediaOnWifiOnly_title">Baixa a través de xarxes sense fils</string>
@@ -240,8 +237,6 @@
<string name="pref_gpodnet_setlogin_information_sum">Canvia les dades d\'inici de sessió del vostre compte de gpodder.net</string>
<string name="pref_playback_speed_title">Velocitats de reproducció</string>
<string name="pref_playback_speed_sum">Personalitzeu les velocitats disponibles per a una velocitat de reproducció d\'àudio variable</string>
- <string name="pref_seek_delta_title">Salta a l\'instant</string>
- <string name="pref_seek_delta_sum">Salta aquesta quantitat de segons en rebobinar o en avançar ràpidament</string>
<string name="pref_gpodnet_sethostname_title">Definex nom del servidor</string>
<string name="pref_gpodnet_sethostname_use_default_host">Utilitza el servidor per defecte</string>
<string name="pref_expandNotify_title">Amplia la notificació</string>
@@ -283,9 +278,6 @@
<string name="sleep_timer_label">Temporitzador</string>
<string name="time_left_label">Temps restant:\u0020</string>
<string name="time_dialog_invalid_input">L\'entrada no és vàlida, ja que el temps ha de ser un nombre i no ho és</string>
- <string name="time_unit_seconds">segons</string>
- <string name="time_unit_minutes">minuts</string>
- <string name="time_unit_hours">hores</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">CATEGORIES</string>
<string name="gpodnet_toplist_header">TOP PODCASTS</string>
@@ -354,6 +346,7 @@
<!--Feed information screen-->
<string name="authentication_label">Autenticació</string>
<string name="authentication_descr">Canvieu el nom d\'usuari i contrasenya per a aquest podcast i els seus episodis.</string>
+ <!--Progress information-->
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">S\'estan important les subscripcions des de les apps de propòsit únic...</string>
</resources>
diff --git a/core/src/main/res/values-cs-rCZ/strings.xml b/core/src/main/res/values-cs-rCZ/strings.xml
index 90304a404..4a89a7d8c 100644
--- a/core/src/main/res/values-cs-rCZ/strings.xml
+++ b/core/src/main/res/values-cs-rCZ/strings.xml
@@ -27,6 +27,12 @@
<string name="drawer_open">Otevřít menu</string>
<string name="drawer_close">Zavřít menu</string>
<string name="drawer_preferences">Nastavení panelu</string>
+ <string name="drawer_feed_order_unplayed_episodes">Řadit dle počtu</string>
+ <string name="drawer_feed_order_alphabetical">Řadit abecedně</string>
+ <string name="drawer_feed_counter_new_unplayed">Počet nových a nepřehraných epizod</string>
+ <string name="drawer_feed_counter_new">Počet nových epizod</string>
+ <string name="drawer_feed_counter_unplayed">Počet nepřehraných epizod</string>
+ <string name="drawer_feed_counter_none">Žádné</string>
<!--Webview actions-->
<string name="open_in_browser_label">Otevřít v prohlížeči</string>
<string name="copy_url_label">Kopírovat URL</string>
@@ -38,6 +44,9 @@
<!--Other-->
<string name="confirm_label">Potvrdit</string>
<string name="cancel_label">Zrušit</string>
+ <string name="yes">Ano</string>
+ <string name="no">Ne
+</string>
<string name="author_label">Autor</string>
<string name="language_label">Jazyk</string>
<string name="url_label">URL</string>
@@ -60,7 +69,13 @@
<string name="close_label">Zavřít</string>
<string name="retry_label">Zkusit znovu</string>
<string name="auto_download_label">Zahrnout do automaticky stahovaných</string>
+ <string name="auto_download_apply_to_items_title">Použít na předchozí epizody</string>
+ <string name="auto_download_apply_to_items_message">Nová funkce <i>auto stahování</i> bude automaticky použita na nové epizody.\nChcete ji také použít na předchozí epizody?</string>
+ <string name="auto_delete_label">Automaticky smazat epizodu\n(přeskočit globální nastavení)</string>
<string name="parallel_downloads_suffix">\u0020paralelních stahování</string>
+ <string name="feed_auto_download_global">Globální</string>
+ <string name="feed_auto_download_always">Vždy</string>
+ <string name="feed_auto_download_never">Nikdy</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">URL kanálu</string>
<string name="etxtFeedurlHint">URL nebo webová stránka kanálu</string>
@@ -73,14 +88,20 @@
<string name="mark_all_read_msg">Všechny epizody označeny jako poslechnuté</string>
<string name="mark_all_read_confirmation_msg">Prosím potvrďte, že chcete označit všechny vybrané epizody jako poslechnuté.</string>
<string name="mark_all_read_feed_confirmation_msg">Prosím potvrďte, že chcete označit všechny epizody z tohoto zdroje jako poslechnuté.</string>
+ <string name="mark_all_seen_label">Označit vše jako zobrazené</string>
<string name="show_info_label">Informace o zdroji</string>
<string name="remove_feed_label">Odstranit podcast</string>
+ <string name="share_label">Sdílet...</string>
<string name="share_link_label">Sdílet odkaz</string>
- <string name="share_source_label">Sdílet adresu kanálu</string>
+ <string name="share_link_with_position_label">Sdílet odkaz s pozicí</string>
+ <string name="share_feed_url_label">Sdílet URL kanálu</string>
+ <string name="share_item_url_label">Sdílet URL epizody</string>
+ <string name="share_item_url_with_position_label">Sdílet URL epizody s pozicí</string>
<string name="feed_delete_confirmation_msg">Prosím potvrďte, že chcete smazat tento kanál včetně všech stažených epizod.</string>
<string name="feed_remover_msg">Odstranit kanál</string>
<string name="load_complete_feed">Obnovit kompletní kanál</string>
<string name="hide_episodes_title">Skrýt epizody</string>
+ <string name="episode_actions">Provést akce</string>
<string name="hide_unplayed_episodes_label">Neposlechnuté</string>
<string name="hide_paused_episodes_label">Pozastavené</string>
<string name="hide_played_episodes_label">Poslechnuté</string>
@@ -99,8 +120,8 @@
<string name="remove_label">Odstranit</string>
<string name="remove_episode_lable">Odstranit epizodu</string>
<string name="mark_read_label">Označit jako poslechnuté</string>
- <string name="mark_unread_label">Označit jako neposlechnuté</string>
<string name="marked_as_read_label">Označeno jako poslechnuté</string>
+ <string name="mark_unread_label">Označit jako neposlechnuté</string>
<string name="add_to_queue_label">Přidat do fronty</string>
<string name="added_to_queue_label">Přidáno do fronty</string>
<string name="remove_from_queue_label">Odebrat z fronty</string>
@@ -229,8 +250,12 @@
<string name="pref_smart_mark_as_played_title">Chytré označování jako poslechnuté</string>
<string name="playback_pref">Přehrávání</string>
<string name="network_pref">Síť</string>
- <string name="pref_autoUpdateIntervall_title">Interval aktualizace zdrojů</string>
- <string name="pref_autoUpdateIntervall_sum">Udává interval, ve kterém se kanály automaticky aktualizují nebo tuto funkci deaktivuje</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">Aktualizovat interval nebo čas v průběhu dne</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Udat interval nebo přesný čas v průběhu dne pro automatickou aktualizaci kanálů</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Můžete nastavit <i>interval</i> jako třeba \"každé 2 hodiny\", nastavit specifický <i>čas v průběhu dne</i> jako \"7:00\" nebo úplně <i>vypnout</i> automatické aktualizace.\n\n<small>Mějte na paměti: Časy aktualizací nejsou přesné. Možná zaznamenáte krátká zpoždění.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Vypnout</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Nastavit interval</string>
+ <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Nastavit čas v průběhu dne</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Stahovat soubory pouze pomocí WiFi</string>
<string name="pref_followQueue_title">Kontinuální přehrávání</string>
<string name="pref_downloadMediaOnWifiOnly_title">WiFi stahování</string>
@@ -250,8 +275,14 @@
<string name="pref_auto_flattr_sum">Nastavit automatické flattrování</string>
<string name="user_interface_label">Uživatelské rozhraní</string>
<string name="pref_set_theme_title">Vybrat motiv</string>
+ <string name="pref_nav_drawer_title">Upravit navigační panel</string>
+ <string name="pref_nav_drawer_sum">Upravit vzhled navigačního panelu.</string>
<string name="pref_nav_drawer_items_title">Změnit navigační panel</string>
<string name="pref_nav_drawer_items_sum">Upravit zobrazení položek v navigačním panelu.</string>
+ <string name="pref_nav_drawer_feed_order_title">Nastavit pořadí sbírek</string>
+ <string name="pref_nav_drawer_feed_order_sum">Upravit pořadí vašich sbírek</string>
+ <string name="pref_nav_drawer_feed_counter_title">Nastavit počítadlo sbírek</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Upravit informaci zobrazovanou počítadlem sbírek</string>
<string name="pref_set_theme_sum">Změnit vzhled AntennaPod.</string>
<string name="pref_automatic_download_title">Automatické stahování</string>
<string name="pref_automatic_download_sum">Nastavení automatického stahování epizod.</string>
@@ -283,10 +314,14 @@
<string name="pref_expandNotify_sum">Vždy zobrazovat tlačítka pro přehrávání v upozornění.</string>
<string name="pref_persistNotify_title">Pevné ovládání přehrávání</string>
<string name="pref_persistNotify_sum">Zachovat upozornění a ovládání na obrazovce uzamčení i při pozastaveném přehrávání.</string>
+ <string name="pref_showDownloadReport_title">Zobrazit report stahování</string>
+ <string name="pref_showDownloadReport_sum">Pokud selže stahování, vygenerovat report zobrazující detaily o chybě.</string>
<string name="pref_expand_notify_unsupport_toast">Verze Androidu nižší než 4.1 nepodporují rozšířená upozornění.</string>
<string name="pref_queueAddToFront_sum">Přidávat nové epizody na začátek fronty.</string>
<string name="pref_queueAddToFront_title">Přidat na začátek.</string>
<string name="pref_smart_mark_as_played_disabled">Vypnuto</string>
+ <string name="pref_image_cache_size_title">Velikost odkládací paměti obrázků</string>
+ <string name="pref_image_cache_size_sum">Velikost diskové paměti pro obrázky.</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Povolit automatické flattrování</string>
<string name="auto_flattr_after_percent">Flattrovat díl jakmile bude odehráno %d procent</string>
@@ -312,6 +347,7 @@
<string name="opml_import_error_dir_empty">Adresář importu je prázdný.</string>
<string name="select_all_label">Označit vše</string>
<string name="deselect_all_label">Zrušit výběr</string>
+ <string name="select_options_label">Vybrat ...</string>
<string name="choose_file_from_filesystem">Z místního souborového systému</string>
<string name="choose_file_from_external_application">Použít externí aplikaci</string>
<string name="opml_export_label">OPML export</string>
@@ -326,9 +362,24 @@
<string name="sleep_timer_label">Časovač vypnutí</string>
<string name="time_left_label">Zbývající čas:\u0020</string>
<string name="time_dialog_invalid_input">Neplatný vstup, musí být zadáno celé číslo</string>
- <string name="time_unit_seconds">sekund</string>
- <string name="time_unit_minutes">minut</string>
- <string name="time_unit_hours">hodin</string>
+ <string name="time_seconds">sekund</string>
+ <string name="time_minutes">minut</string>
+ <string name="time_hours">hodin</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">1 sekunda</item>
+ <item quantity="few">%d sekundy</item>
+ <item quantity="other">%d sekund</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 minuta</item>
+ <item quantity="few">%d minuty</item>
+ <item quantity="other">%d minut</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 hodina</item>
+ <item quantity="few">%d hodiny</item>
+ <item quantity="other">%d hodin</item>
+ </plurals>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">KATEGORIE</string>
<string name="gpodnet_toplist_header">TOP PODCASTY</string>
@@ -373,6 +424,7 @@
<string name="pref_pausePlaybackForFocusLoss_title">Automatické pozastavení přehrávání</string>
<string name="pref_resumeAfterCall_sum">Pokračovat v přehrávání po ukončení telefonního hovoru</string>
<string name="pref_resumeAfterCall_title">Pokračovat po telefonátu</string>
+ <string name="pref_restart_required">Pro aktivování změn nastavení bylo třeba restartovat aplikaci AntennaPod.</string>
<!--Online feed view-->
<string name="subscribe_label">Odebírat</string>
<string name="subscribed_label">Odebíráno</string>
@@ -399,7 +451,29 @@
<!--Feed information screen-->
<string name="authentication_label">Ověření</string>
<string name="authentication_descr">Změnit uživatelské jméno a heslo pro tento podcast a jeho epizody.</string>
+ <!--Progress information-->
+ <string name="progress_upgrading_database">Probíhá aktualizace databáze</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importuji odběry z jednoúčelových aplikací...</string>
<string name="search_itunes_label">Prohledat iTunes</string>
+ <string name="select_label"><b>Vybrat ...</b></string>
+ <string name="all_label">Vše</string>
+ <string name="selected_all_label">Vybrány všechny epizody</string>
+ <string name="none_label">Žádné</string>
+ <string name="deselected_all_label">Odebrány všechny epizody</string>
+ <string name="played_label">Přehrány</string>
+ <string name="selected_played_label">Vybrány přehrané epizody</string>
+ <string name="unplayed_label">Nepřehrány</string>
+ <string name="selected_unplayed_label">Odebrány nepřehrané epizody</string>
+ <string name="downloaded_label">Stažené</string>
+ <string name="selected_downloaded_label">Vybrány stažené epizody</string>
+ <string name="not_downloaded_label">Nestažené</string>
+ <string name="selected_not_downloaded_label">Vybrány nestažené epizody</string>
+ <string name="sort_title"><b>Řadit podle ...</b></string>
+ <string name="sort_title_a_z">Názvu (A \u2192 Z)</string>
+ <string name="sort_title_z_a">Názvu (Z \u2192 A)</string>
+ <string name="sort_date_new_old">Data (Nové \u2192 Staré)</string>
+ <string name="sort_date_old_new">Data (Staré \u2192 Nové)</string>
+ <string name="sort_duration_short_long">Délka (Krátké \u2192 Dlouhé)</string>
+ <string name="sort_duration_long_short">Délka (Dlouhé \u2192 Krátké)</string>
</resources>
diff --git a/core/src/main/res/values-da/strings.xml b/core/src/main/res/values-da/strings.xml
index ba7fafca6..cc18185ec 100644
--- a/core/src/main/res/values-da/strings.xml
+++ b/core/src/main/res/values-da/strings.xml
@@ -71,7 +71,6 @@
<string name="show_info_label">Vis information</string>
<string name="remove_feed_label">Fjern podcast</string>
<string name="share_link_label">Del webside link</string>
- <string name="share_source_label">Del feed link</string>
<string name="feed_delete_confirmation_msg">Bekræft venligst at du vil fjerne dette feed og ALLE episoder du har downloadet fra dette feed.</string>
<string name="feed_remover_msg">Fjerner feed</string>
<string name="load_complete_feed">Opdater hele feed\'et</string>
@@ -197,8 +196,6 @@
<string name="pref_auto_delete_title">Slet Automatisk</string>
<string name="playback_pref">Afspilning</string>
<string name="network_pref">Netværk</string>
- <string name="pref_autoUpdateIntervall_title">Opdaterings interval</string>
- <string name="pref_autoUpdateIntervall_sum">Specificer et interval indenfor hvilket feeds opdaterer automatisk eller deaktiver det</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Download kun medie filer over WiFi</string>
<string name="pref_followQueue_title">Kontinuerlig afspilning</string>
<string name="pref_downloadMediaOnWifiOnly_title">WiFi medie download</string>
@@ -240,8 +237,6 @@
<string name="pref_gpodnet_setlogin_information_sum">Skift din gpodder.net kontos login information.</string>
<string name="pref_playback_speed_title">Afspilningshastigheder</string>
<string name="pref_playback_speed_sum">Tilpas tilgængelige hastigheder for variabelt afspilningshastigheds plugin</string>
- <string name="pref_seek_delta_title">Søg tid</string>
- <string name="pref_seek_delta_sum">Søg så mange sekunder når der spoles tilbage eller frem</string>
<string name="pref_gpodnet_sethostname_title">Indstil værtsnavn</string>
<string name="pref_gpodnet_sethostname_use_default_host">Brug standard vært</string>
<string name="pref_expandNotify_title">Udvid notifikation</string>
@@ -283,9 +278,6 @@
<string name="sleep_timer_label">Søvn timer</string>
<string name="time_left_label">Tid tilbage:\u0020</string>
<string name="time_dialog_invalid_input">Ugyldig indtastning, tid skal være et heltal</string>
- <string name="time_unit_seconds">sekunder</string>
- <string name="time_unit_minutes">minutter</string>
- <string name="time_unit_hours">timer</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">KATEGORIER </string>
<string name="gpodnet_toplist_header">TOP PODCASTS</string>
@@ -354,6 +346,7 @@
<!--Feed information screen-->
<string name="authentication_label">Godkendelse</string>
<string name="authentication_descr">Skift dit brugernavn og kodeord for denne podcast og dets episoder.</string>
+ <!--Progress information-->
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importerer abonnementer fra single-purpose apps…</string>
</resources>
diff --git a/core/src/main/res/values-de/strings.xml b/core/src/main/res/values-de/strings.xml
index 93e52acb6..47a0d7877 100644
--- a/core/src/main/res/values-de/strings.xml
+++ b/core/src/main/res/values-de/strings.xml
@@ -27,6 +27,12 @@
<string name="drawer_open">Menü öffnen</string>
<string name="drawer_close">Menü schließen</string>
<string name="drawer_preferences">Seitenleisten-Einstellungen</string>
+ <string name="drawer_feed_order_unplayed_episodes">Sortieren nach Zähler</string>
+ <string name="drawer_feed_order_alphabetical">Alphabetisch sortieren</string>
+ <string name="drawer_feed_counter_new_unplayed">Anzahl neuer und ungespielter Episoden</string>
+ <string name="drawer_feed_counter_new">Anzahl neuer Episoden</string>
+ <string name="drawer_feed_counter_unplayed">Anzahl ungespielter Episoden</string>
+ <string name="drawer_feed_counter_none">Keine</string>
<!--Webview actions-->
<string name="open_in_browser_label">Im Browser öffnen</string>
<string name="copy_url_label">URL kopieren</string>
@@ -38,6 +44,8 @@
<!--Other-->
<string name="confirm_label">Bestätigen</string>
<string name="cancel_label">Abbrechen</string>
+ <string name="yes">Ja</string>
+ <string name="no">Nein</string>
<string name="author_label">Autor</string>
<string name="language_label">Sprache</string>
<string name="url_label">URL</string>
@@ -60,7 +68,13 @@
<string name="close_label">Schließen</string>
<string name="retry_label">Erneut versuchen</string>
<string name="auto_download_label">Automatisch herunterladen</string>
+ <string name="auto_download_apply_to_items_title">Auf bisherige Episoden anwenden</string>
+ <string name="auto_download_apply_to_items_message">Die neue Einstellung zum <i>Automatischen Download</i> wird automatisch auf neue Episoden angewandt.\nMöchtest du sie auch auf die bisherige Episoden anwenden?</string>
+ <string name="auto_delete_label">Episoden automatisch löschen\n(überschreibt globale Vorgabe)</string>
<string name="parallel_downloads_suffix">\u0020gleichzeitige Downloads</string>
+ <string name="feed_auto_download_global">Global</string>
+ <string name="feed_auto_download_always">Immer</string>
+ <string name="feed_auto_download_never">Nie</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">Feed URL</string>
<string name="etxtFeedurlHint">URL des Feeds oder der Webseite</string>
@@ -73,14 +87,20 @@
<string name="mark_all_read_msg">Alle Episoden als gespielt markieren</string>
<string name="mark_all_read_confirmation_msg">Bitte bestätige, dass alle Episoden als gespielt markiert werden sollen.</string>
<string name="mark_all_read_feed_confirmation_msg">Bitte bestätige, dass alle Episoden in diesem Feed als gespielt markiert werden sollen.</string>
+ <string name="mark_all_seen_label">Alle als gesehen markieren</string>
<string name="show_info_label">Informationen anzeigen</string>
<string name="remove_feed_label">Podcast entfernen</string>
+ <string name="share_label">Teile...</string>
<string name="share_link_label">Webseiten-Link teilen</string>
- <string name="share_source_label">Feed-Link teilen</string>
+ <string name="share_link_with_position_label">Teile Link mit Zeitmarke</string>
+ <string name="share_feed_url_label">Teile URL des Podcasts</string>
+ <string name="share_item_url_label">Teile URL der Episode</string>
+ <string name="share_item_url_with_position_label">Teile URL der Episode mit Zeitmarke</string>
<string name="feed_delete_confirmation_msg">Bitte bestätige, dass du diesen Feed und ALLE heruntergeladenen Episoden dieses Feeds entfernen möchtest.</string>
<string name="feed_remover_msg">Entferne Feed</string>
<string name="load_complete_feed">Kompletten Feed aktualisieren</string>
<string name="hide_episodes_title">Episoden verbergen</string>
+ <string name="episode_actions">Aktionen anwenden</string>
<string name="hide_unplayed_episodes_label">Ungespielt</string>
<string name="hide_paused_episodes_label">Pausiert</string>
<string name="hide_played_episodes_label">Gespielt</string>
@@ -99,8 +119,8 @@
<string name="remove_label">Entfernen</string>
<string name="remove_episode_lable">Episode entfernen</string>
<string name="mark_read_label">Als gespielt markieren</string>
- <string name="mark_unread_label">Als ungespielt markieren</string>
<string name="marked_as_read_label">Als gespielt markiert</string>
+ <string name="mark_unread_label">Als ungespielt markieren</string>
<string name="add_to_queue_label">Zur Abspielliste hinzufügen</string>
<string name="added_to_queue_label">Zur Abspielliste hinzugefügt</string>
<string name="remove_from_queue_label">Aus der Abspielliste entfernen</string>
@@ -229,8 +249,12 @@
<string name="pref_smart_mark_as_played_title">Schlaues als gespielt markieren</string>
<string name="playback_pref">Wiedergabe</string>
<string name="network_pref">Netzwerk</string>
- <string name="pref_autoUpdateIntervall_title">Aktualisierungsintervall</string>
- <string name="pref_autoUpdateIntervall_sum">Lege ein Intervall fest, in dem Feeds automatisch aktualisiert werden oder deaktiviere es</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">Aktualisierungsintervall oder -tageszeit</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Lege ein Intervall oder eine Tageszeit zur automatischen Aktualisierung der Podcasts fest</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Du kannst ein festes <i>Intervall</i> wie \"alle 2 Stunden\", eine bestimmte <i>Tageszeit</i> wie \"7 Uhr morgens\" festlegen oder die automatische Aktualisierung komplett <i>deaktivieren</i>.\n\n<small>Bitte beachte: Der Zeitpunkt der Aktualisierung ist ungenau. Du wirst vielleicht eine kurze Verzögerung bemerken.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Deaktivieren</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Intervall einstellen</string>
+ <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Tageszeit festlegen</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Lade Mediendateien nur über WiFi</string>
<string name="pref_followQueue_title">Durchgehendes Abspielen</string>
<string name="pref_downloadMediaOnWifiOnly_title">WiFi Medien-Download</string>
@@ -250,8 +274,14 @@
<string name="pref_auto_flattr_sum">Automatisches Flattrn konfigurieren</string>
<string name="user_interface_label">Benutzeroberfläche</string>
<string name="pref_set_theme_title">Theme auswählen</string>
+ <string name="pref_nav_drawer_title">Seitenleiste anpassen</string>
+ <string name="pref_nav_drawer_sum">Passe das Aussehen der Seitenleiste an.</string>
<string name="pref_nav_drawer_items_title">Seitenleiste ändern</string>
<string name="pref_nav_drawer_items_sum">Ändere, welche Listen in der Seitenleiste erscheinen</string>
+ <string name="pref_nav_drawer_feed_order_title">Reihenfolge der Abonnements einstellen</string>
+ <string name="pref_nav_drawer_feed_order_sum">Ändere die Reihenfolge deiner Abonnements</string>
+ <string name="pref_nav_drawer_feed_counter_title">Abonnement-Zähler einstellen</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Ändere, welche Information der Abonnement-Zähler anzeigt</string>
<string name="pref_set_theme_sum">Ändere das Aussehen von AntennaPod.</string>
<string name="pref_automatic_download_title">Automatisches Herunterladen</string>
<string name="pref_automatic_download_sum">Konfiguriere das automatische Herunterladen von Episoden.</string>
@@ -283,10 +313,14 @@
<string name="pref_expandNotify_sum">Erweiterte Wiedergabebenachrichtigung mit Abspiel-, Pause- und Stop-Knöpfen anzeigen.</string>
<string name="pref_persistNotify_title">Persistente Wiedergabesteurung</string>
<string name="pref_persistNotify_sum">Zeige Wiedergabebedienelemente in der Benachrichtigung und im Lockscreen an, während die Wiedergabe pausiert ist.</string>
+ <string name="pref_showDownloadReport_title">Zeige Download-Bericht</string>
+ <string name="pref_showDownloadReport_sum">Wenn Downloads fehlschlagen, erstelle einen Bericht, der die Details des Fehlschlages beschreibt.</string>
<string name="pref_expand_notify_unsupport_toast">Android-Versionen vor 4.1 unterstützen keine erweiterten Benachrichtigungen.</string>
<string name="pref_queueAddToFront_sum">Fügen Sie neue Folgen auf den Anfang der Warteschlange.</string>
<string name="pref_queueAddToFront_title">Vorne in Abspielliste einreihen</string>
<string name="pref_smart_mark_as_played_disabled">Deaktiviert</string>
+ <string name="pref_image_cache_size_title">Größe des Bilder-Zwischenspeichers</string>
+ <string name="pref_image_cache_size_sum">Größe des Zwischenspeichers für Bilder</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Automatisches Flattrn aktivieren</string>
<string name="auto_flattr_after_percent">Flattr eine Episode, sobald %d Prozent gespielt worden sind</string>
@@ -312,6 +346,7 @@
<string name="opml_import_error_dir_empty">Der Import-Ordner ist leer.</string>
<string name="select_all_label">Alle auswählen</string>
<string name="deselect_all_label">Auswahl zurücksetzen</string>
+ <string name="select_options_label">Wähle aus ...</string>
<string name="choose_file_from_filesystem">Vom lokalen Dateisystem</string>
<string name="choose_file_from_external_application">Verwende externe Anwendung</string>
<string name="opml_export_label">OPML Export</string>
@@ -326,9 +361,21 @@
<string name="sleep_timer_label">Schlummerfunktion</string>
<string name="time_left_label">Zeit übrig:\u0020</string>
<string name="time_dialog_invalid_input">Ungültige Eingabe, Zeit muss eine Ganzzahl sein</string>
- <string name="time_unit_seconds">Sekunden</string>
- <string name="time_unit_minutes">Minuten</string>
- <string name="time_unit_hours">Stunden</string>
+ <string name="time_seconds">Sekunden</string>
+ <string name="time_minutes">Minuten</string>
+ <string name="time_hours">Stunden</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">1 Sekunde</item>
+ <item quantity="other">%d Sekunden</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 Minute</item>
+ <item quantity="other">%d Minuten</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 Stunde</item>
+ <item quantity="other">%d Stunden</item>
+ </plurals>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">KATEGORIEN</string>
<string name="gpodnet_toplist_header">BESTE PODCASTS</string>
@@ -373,6 +420,7 @@
<string name="pref_pausePlaybackForFocusLoss_title">Bei Unterbrechungen pausieren</string>
<string name="pref_resumeAfterCall_sum">Wiedergabe fortsetzen, wenn Anruf beendet ist</string>
<string name="pref_resumeAfterCall_title">Nach Anruf fortsetzen</string>
+ <string name="pref_restart_required">AntennaPod muss neu gestartet werden, damit die Änderungen wirksam werden.</string>
<!--Online feed view-->
<string name="subscribe_label">Abonnieren</string>
<string name="subscribed_label">Abonniert</string>
@@ -399,7 +447,29 @@
<!--Feed information screen-->
<string name="authentication_label">Authentifizierung</string>
<string name="authentication_descr">Ändere den Benutzernamen und das Passwort für diesen Podcast und dessen Episoden.</string>
+ <!--Progress information-->
+ <string name="progress_upgrading_database">Datenbank wird aktualisiert</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importiere Abonnements aus Single-Purpose Apps</string>
<string name="search_itunes_label">iTunes durchsuchen</string>
+ <string name="select_label"><b>Wähle aus ...</b></string>
+ <string name="all_label">Alle</string>
+ <string name="selected_all_label">Alle Episoden ausgewählt</string>
+ <string name="none_label">Keine</string>
+ <string name="deselected_all_label">Alle Episoden abgewählt</string>
+ <string name="played_label">Gespielt</string>
+ <string name="selected_played_label">Gespielte Episoden ausgewählt</string>
+ <string name="unplayed_label">Ungespielt</string>
+ <string name="selected_unplayed_label">Ungespielte Episoden ausgewählt</string>
+ <string name="downloaded_label">Heruntergeladen</string>
+ <string name="selected_downloaded_label">Heruntergeladene Episoden ausgewählt</string>
+ <string name="not_downloaded_label">Nicht heruntergeladen</string>
+ <string name="selected_not_downloaded_label">Nicht heruntergeladene Episoden ausgewählt</string>
+ <string name="sort_title"><b>Sortieren nach ...</b></string>
+ <string name="sort_title_a_z">Titel (A \u2192 Z)</string>
+ <string name="sort_title_z_a">Titel (Z \u2192 A)</string>
+ <string name="sort_date_new_old">Datum (neu \u2192 alt)</string>
+ <string name="sort_date_old_new">Datum (alt \u2192 neu)</string>
+ <string name="sort_duration_short_long">Dauer (kurz \u2192 lang)</string>
+ <string name="sort_duration_long_short">Dauer (lang \u2192 kurz)</string>
</resources>
diff --git a/core/src/main/res/values-es-rES/strings.xml b/core/src/main/res/values-es-rES/strings.xml
index d05c34876..2607d1794 100644
--- a/core/src/main/res/values-es-rES/strings.xml
+++ b/core/src/main/res/values-es-rES/strings.xml
@@ -42,7 +42,6 @@
<string name="mark_all_read_label">Marcar todo como leído</string>
<string name="show_info_label">Información del programa</string>
<string name="share_link_label">Compartir el enlace de la web</string>
- <string name="share_source_label">Compartir el enlace del canal</string>
<string name="feed_delete_confirmation_msg">Confirme que quiere eliminar este canal y TODOS los episodios descargados del mismo.</string>
<!--actions on feeditems-->
<string name="download_label">Descargar</string>
@@ -125,8 +124,6 @@
<string name="pref_followQueue_sum">Saltar al siguiente elemento de la cola al acabar la reproducción</string>
<string name="playback_pref">Reproducción</string>
<string name="network_pref">Red</string>
- <string name="pref_autoUpdateIntervall_title">Intervalo de actualización</string>
- <string name="pref_autoUpdateIntervall_sum">Especificar el intervalo en que se actualizarán automáticamente los canales, o desactivarlo</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Solo descargar los contenidos por WiFi</string>
<string name="pref_followQueue_title">Reproducción continua</string>
<string name="pref_downloadMediaOnWifiOnly_title">Descarga de contenidos por WiFi</string>
@@ -193,5 +190,6 @@
<!--Online feed view-->
<!--Content descriptions for image buttons-->
<!--Feed information screen-->
+ <!--Progress information-->
<!--AntennaPodSP-->
</resources>
diff --git a/core/src/main/res/values-es/strings.xml b/core/src/main/res/values-es/strings.xml
index d5255a589..6670836db 100644
--- a/core/src/main/res/values-es/strings.xml
+++ b/core/src/main/res/values-es/strings.xml
@@ -27,6 +27,12 @@
<string name="drawer_open">Abrir menú</string>
<string name="drawer_close">Cerrar menú</string>
<string name="drawer_preferences">Preferencias del cajón</string>
+ <string name="drawer_feed_order_unplayed_episodes">Ordenar por cuenta</string>
+ <string name="drawer_feed_order_alphabetical">Ordenar alfabéticamente</string>
+ <string name="drawer_feed_counter_new_unplayed">Cantidad de episodios nuevos y no escuchados</string>
+ <string name="drawer_feed_counter_new">Cantidad de episodios nuevos</string>
+ <string name="drawer_feed_counter_unplayed">Cantidad de episodios no escuchados</string>
+ <string name="drawer_feed_counter_none">Ninguno</string>
<!--Webview actions-->
<string name="open_in_browser_label">Abrir en el navegador</string>
<string name="copy_url_label">Copiar URL</string>
@@ -38,6 +44,8 @@
<!--Other-->
<string name="confirm_label">Confirmar</string>
<string name="cancel_label">Cancelar</string>
+ <string name="yes">Sí</string>
+ <string name="no">No</string>
<string name="author_label">Autor</string>
<string name="language_label">Idioma</string>
<string name="url_label">URL</string>
@@ -60,7 +68,13 @@
<string name="close_label">Cerrar</string>
<string name="retry_label">Reintentar</string>
<string name="auto_download_label">Incluir en descargas automáticas</string>
+ <string name="auto_download_apply_to_items_title">Aplicar a episodios anteriores</string>
+ <string name="auto_download_apply_to_items_message">La nueva opción <i>Auto Descarga</i> se aplicará automáticamente a episodios nuevos.\n¿También desea aplicarlo a episodios anteriores?</string>
+ <string name="auto_delete_label">Auto borrar episodio\n(Ignorando los ajustes globales)</string>
<string name="parallel_downloads_suffix">\u0020descargas paralelas</string>
+ <string name="feed_auto_download_global">Global</string>
+ <string name="feed_auto_download_always">Siempre</string>
+ <string name="feed_auto_download_never">Nunca</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">URL del canal</string>
<string name="etxtFeedurlHint">URL del canal o del sitio web</string>
@@ -73,14 +87,20 @@
<string name="mark_all_read_msg">Se marcaron todos los episodios como escuchados</string>
<string name="mark_all_read_confirmation_msg">Por favor, confirme que desea marcar todos los episodios como escuchados.</string>
<string name="mark_all_read_feed_confirmation_msg">Por favor, confirme que desea marcar todos los episodios de este feed como escuchados.</string>
+ <string name="mark_all_seen_label">Marcar todos como vistos</string>
<string name="show_info_label">Información del programa</string>
<string name="remove_feed_label">Eliminar podcast</string>
+ <string name="share_label">Compartir...</string>
<string name="share_link_label">Compartir el enlace de la web</string>
- <string name="share_source_label">Compartir el enlace del canal</string>
+ <string name="share_link_with_position_label">Compartir enlace con posición</string>
+ <string name="share_feed_url_label">Compartir URL del canal</string>
+ <string name="share_item_url_label">Compartir URL del episodio</string>
+ <string name="share_item_url_with_position_label">Compartir URL del episodio con posición</string>
<string name="feed_delete_confirmation_msg">Confirme que quiere eliminar este canal y TODOS los episodios descargados del mismo.</string>
<string name="feed_remover_msg">Quitando el canal</string>
<string name="load_complete_feed">Actualizar el canal completo</string>
<string name="hide_episodes_title">Ocultar episodios</string>
+ <string name="episode_actions">Aplicar acciones</string>
<string name="hide_unplayed_episodes_label">No escuchados</string>
<string name="hide_paused_episodes_label">Pausados</string>
<string name="hide_played_episodes_label">Escuchados</string>
@@ -99,8 +119,8 @@
<string name="remove_label">Quitar</string>
<string name="remove_episode_lable">Quitar episodio</string>
<string name="mark_read_label">Marcar como escuchado</string>
- <string name="mark_unread_label">Marcar como no escuchado</string>
<string name="marked_as_read_label">Marcado como escuchado</string>
+ <string name="mark_unread_label">Marcar como no escuchado</string>
<string name="add_to_queue_label">Añadir a la cola</string>
<string name="added_to_queue_label">Añadido a la cola</string>
<string name="remove_from_queue_label">Quitar de la cola</string>
@@ -229,8 +249,12 @@
<string name="pref_smart_mark_as_played_title">Marcar como escuchado inteligente</string>
<string name="playback_pref">Reproducción</string>
<string name="network_pref">Red</string>
- <string name="pref_autoUpdateIntervall_title">Intervalo de actualización</string>
- <string name="pref_autoUpdateIntervall_sum">Especificar el intervalo en que se actualizarán automáticamente los canales, o desactivarlo</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">Intervalo de actualización u hora del día</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Especificar el intervalo o la hora del día en que se actualizarán automáticamente los canales</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Se puede ajustar un <i>intervalo</i> como \"cada 2 horas\", especificar una <i>hora del día</i> como \"7:00 AM\" o <i>deshabilitar</i> las actualizaciones automáticas.\n\n<small>Nota: Las horas de actualización no son exactas. Puede haber un ligero retraso.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Deshabilitar</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Ajustar intervalo</string>
+ <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Ajustar hora del día</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Solo descargar los contenidos por WiFi</string>
<string name="pref_followQueue_title">Reproducción continua</string>
<string name="pref_downloadMediaOnWifiOnly_title">Descarga de contenidos por WiFi</string>
@@ -250,8 +274,14 @@
<string name="pref_auto_flattr_sum">Configurar flattr automático</string>
<string name="user_interface_label">Interfaz de usuario</string>
<string name="pref_set_theme_title">Elegir un tema</string>
+ <string name="pref_nav_drawer_title">Personalizar el cajón de navegación</string>
+ <string name="pref_nav_drawer_sum">Personalizar la apariencia del cajón de navegación</string>
<string name="pref_nav_drawer_items_title">Cambiar el cajón de navegación</string>
<string name="pref_nav_drawer_items_sum">Cambiar los ítems que aparecen en el cajón de navegación</string>
+ <string name="pref_nav_drawer_feed_order_title">Ajustar orden de suscripción</string>
+ <string name="pref_nav_drawer_feed_order_sum">Cambiar el orden de las suscripciones</string>
+ <string name="pref_nav_drawer_feed_counter_title">Ajustar contador de suscripción</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Cambiar la información mostrada en el contador de suscripción</string>
<string name="pref_set_theme_sum">Cambiar la apariencia de AntennaPod.</string>
<string name="pref_automatic_download_title">Descarga automática</string>
<string name="pref_automatic_download_sum">Configurar la descarga automática de episodios.</string>
@@ -283,10 +313,14 @@
<string name="pref_expandNotify_sum">Expandir siempre la notificación para mostrar los botones de reproducción</string>
<string name="pref_persistNotify_title">Controles de reproducción persistentes</string>
<string name="pref_persistNotify_sum">Mantener la notificación y controles en pantalla de bloqueo cuando se pausa.</string>
+ <string name="pref_showDownloadReport_title">Mostrar informe de descarga</string>
+ <string name="pref_showDownloadReport_sum">Si la descarga falla, generar un informe con los detalles del fallo</string>
<string name="pref_expand_notify_unsupport_toast">Las versiones de Android anteriores a la 4.1 no soportan notificaciones expandidas</string>
<string name="pref_queueAddToFront_sum">Agregar nuevos episodios al principio de la cola.</string>
<string name="pref_queueAddToFront_title">Poner al principio de la cola.</string>
<string name="pref_smart_mark_as_played_disabled">Deshabilitado</string>
+ <string name="pref_image_cache_size_title">Tamaño de la caché de imágenes</string>
+ <string name="pref_image_cache_size_sum">Tamaño de la caché en disco para imágenes.</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Habilitar Flattr automático</string>
<string name="auto_flattr_after_percent">Hacer Flattr del episodio en cuanto se haya reproducido el %d por ciento</string>
@@ -312,6 +346,7 @@
<string name="opml_import_error_dir_empty">El directorio de importación está vacío.</string>
<string name="select_all_label">Seleccionar todo</string>
<string name="deselect_all_label">Deseleccionar todo</string>
+ <string name="select_options_label">Seleccionar...</string>
<string name="choose_file_from_filesystem">Desde el sistema de ficheros local</string>
<string name="choose_file_from_external_application">Usar aplicación externa</string>
<string name="opml_export_label">Exportar a OPML</string>
@@ -326,9 +361,21 @@
<string name="sleep_timer_label">Temporizador</string>
<string name="time_left_label">Tiempo restante:\u0020</string>
<string name="time_dialog_invalid_input">Entrada no válida, el tiempo debe ser un entero</string>
- <string name="time_unit_seconds">segundos</string>
- <string name="time_unit_minutes">minutos</string>
- <string name="time_unit_hours">horas</string>
+ <string name="time_seconds">segundos</string>
+ <string name="time_minutes">minutos</string>
+ <string name="time_hours">horas</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">1 segundo</item>
+ <item quantity="other">%d segundos</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 minuto</item>
+ <item quantity="other">%d minutos</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 hora</item>
+ <item quantity="other">%d horas</item>
+ </plurals>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">CATEGORÍAS</string>
<string name="gpodnet_toplist_header">MEJORES PODCASTS</string>
@@ -373,6 +420,7 @@
<string name="pref_pausePlaybackForFocusLoss_title">Pausar durante las interrupciones</string>
<string name="pref_resumeAfterCall_sum">Reanudar reproducción tras una llamada</string>
<string name="pref_resumeAfterCall_title">Reanudar tras una llamada</string>
+ <string name="pref_restart_required">Es necesario reiniciar AntennaPod para aplicar los cambios.</string>
<!--Online feed view-->
<string name="subscribe_label">Suscribirse</string>
<string name="subscribed_label">Suscrito</string>
@@ -399,7 +447,29 @@
<!--Feed information screen-->
<string name="authentication_label">Autenticación</string>
<string name="authentication_descr">Cambiar nombre y contraseña de este podcast y sus episodios</string>
+ <!--Progress information-->
+ <string name="progress_upgrading_database">Actualizando la base de datos</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importando subscripciones de aplicaciones de uso específico...</string>
<string name="search_itunes_label">Buscar en iTunes</string>
+ <string name="select_label"><b>Seleccionar...</b></string>
+ <string name="all_label">Todo</string>
+ <string name="selected_all_label">Seleccionados todos los episodios</string>
+ <string name="none_label">Ninguno</string>
+ <string name="deselected_all_label">Deseleccionados todos los episodios</string>
+ <string name="played_label">Reproducido</string>
+ <string name="selected_played_label">Seleccionados episodios reproducidos</string>
+ <string name="unplayed_label">No reproducidos</string>
+ <string name="selected_unplayed_label">Seleccionados episodios no reproducidos</string>
+ <string name="downloaded_label">Descargado</string>
+ <string name="selected_downloaded_label">Seleccionados episodios descargados</string>
+ <string name="not_downloaded_label">No descargado</string>
+ <string name="selected_not_downloaded_label">Seleccionados episodios no descargados</string>
+ <string name="sort_title"><b>Ordenar por...</b></string>
+ <string name="sort_title_a_z">Título (A \u2192 Z)</string>
+ <string name="sort_title_z_a">Título (Z \u2192 A)</string>
+ <string name="sort_date_new_old">Fecha (Nuevo \u2192 Antiguo)</string>
+ <string name="sort_date_old_new">Fecha (Antiguo \u2192 Nuevo)</string>
+ <string name="sort_duration_short_long">Duración (Corto \u2192 Largo)</string>
+ <string name="sort_duration_long_short">Duración (Largo \u2192 Corto)</string>
</resources>
diff --git a/core/src/main/res/values-fr/strings.xml b/core/src/main/res/values-fr/strings.xml
index 2f59e139d..10d3fe9a4 100644
--- a/core/src/main/res/values-fr/strings.xml
+++ b/core/src/main/res/values-fr/strings.xml
@@ -26,6 +26,12 @@
<!--Main activity-->
<string name="drawer_open">Ouvrir le menu</string>
<string name="drawer_close">Fermer le menu</string>
+ <string name="drawer_feed_order_unplayed_episodes">Trier par compteur</string>
+ <string name="drawer_feed_order_alphabetical">Trier alphabétiquement</string>
+ <string name="drawer_feed_counter_new_unplayed">Nombre de nouveaux épisodes non-lus</string>
+ <string name="drawer_feed_counter_new">Nombre de nouveaux épisodes</string>
+ <string name="drawer_feed_counter_unplayed">Nombre d\'épisodes non-lus</string>
+ <string name="drawer_feed_counter_none">Aucun</string>
<!--Webview actions-->
<string name="open_in_browser_label">Ouvrir dans le navigateur</string>
<string name="copy_url_label">Copier l\'URL</string>
@@ -37,8 +43,11 @@
<!--Other-->
<string name="confirm_label">Confirmer</string>
<string name="cancel_label">Annuler</string>
+ <string name="yes">Oui</string>
+ <string name="no">Non</string>
<string name="author_label">Auteur</string>
<string name="language_label">Langue</string>
+ <string name="url_label">URL</string>
<string name="podcast_settings_label">Préférences</string>
<string name="cover_label">Image</string>
<string name="error_label">Erreur</string>
@@ -58,7 +67,11 @@
<string name="close_label">Fermer</string>
<string name="retry_label">Réessayer</string>
<string name="auto_download_label">Télécharger automatiquement à l\'avenir</string>
+ <string name="auto_download_apply_to_items_title">Appliquer aux épisodes précédents</string>
<string name="parallel_downloads_suffix">\u0020téléchargements parallèles</string>
+ <string name="feed_auto_download_global">Global</string>
+ <string name="feed_auto_download_always">Toujours</string>
+ <string name="feed_auto_download_never">Jamais</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">URL du flux</string>
<string name="etxtFeedurlHint">URL ou flux ou site web</string>
@@ -71,13 +84,29 @@
<string name="mark_all_read_msg">Tous les épisodes ont été marqués comme lus</string>
<string name="mark_all_read_confirmation_msg">Veuillez confirmer que vous voulez bien marquer tous les épisodes comme lus</string>
<string name="mark_all_read_feed_confirmation_msg">Veuillez confirmer que vous voulez bien marquer tous les épisode de ce flux comme lus</string>
+ <string name="mark_all_seen_label">Marquer tout comme vu</string>
<string name="show_info_label">Voir les détails</string>
<string name="remove_feed_label">Supprimer le podcast</string>
+ <string name="share_label">Partager...</string>
<string name="share_link_label">Partager un lien vers le site</string>
- <string name="share_source_label">Partager le flux</string>
+ <string name="share_link_with_position_label">Partager lien avec position</string>
+ <string name="share_feed_url_label">Partager lien du flux</string>
+ <string name="share_item_url_label">Partager lien de l\'épisode</string>
+ <string name="share_item_url_with_position_label">Partager lien de l\'épisode avec position</string>
<string name="feed_delete_confirmation_msg">Veuillez confirmer que vous voulez bien supprimer ce flux et TOUS ses épisodes que vous avez téléchargés.</string>
<string name="feed_remover_msg">Flux en cours de suppression</string>
<string name="load_complete_feed">Mettre à jour tout le flux</string>
+ <string name="hide_episodes_title">Cacher épisodes</string>
+ <string name="episode_actions">Appliquer les actions</string>
+ <string name="hide_unplayed_episodes_label">Non joués</string>
+ <string name="hide_paused_episodes_label">En pause</string>
+ <string name="hide_played_episodes_label">Joués</string>
+ <string name="hide_queued_episodes_label">Rajouté à la liste</string>
+ <string name="hide_not_queued_episodes_label">Pas rajouté à la liste</string>
+ <string name="hide_downloaded_episodes_label">Téléchargé</string>
+ <string name="hide_not_downloaded_episodes_label">Non téléchargé</string>
+ <string name="filtered_label">Filtré</string>
+ <string name="refresh_failed_msg">{fa-exclamation-circle} Dernière mise à jour échouée</string>
<!--actions on feeditems-->
<string name="download_label">Télécharger</string>
<string name="play_label">Lire</string>
@@ -87,15 +116,18 @@
<string name="remove_label">Supprimer</string>
<string name="remove_episode_lable">Supprimer cet épisode</string>
<string name="mark_read_label">Marquer comme lu</string>
- <string name="mark_unread_label">Marquer comme non lu</string>
<string name="marked_as_read_label">Les épisodes ont été marqués comme lus</string>
+ <string name="mark_unread_label">Marquer comme non lu</string>
<string name="add_to_queue_label">Ajouter à la liste</string>
+ <string name="added_to_queue_label">Ajouté à la liste</string>
<string name="remove_from_queue_label">Supprimer de la liste</string>
<string name="visit_website_label">Visiter le site</string>
<string name="support_label">Flattr ça!</string>
<string name="enqueue_all_new">Ajouter tous à la liste</string>
<string name="download_all">Tous télécharger</string>
<string name="skip_episode_label">Passer cet épisode</string>
+ <string name="activate_auto_download">Activer téléchargement automatique</string>
+ <string name="deactivate_auto_download">Désactiver téléchargement automatique</string>
<!--Download messages and labels-->
<string name="download_successful">terminé</string>
<string name="download_failed">échoué</string>
@@ -114,6 +146,7 @@
<string name="cancel_all_downloads_label">Annuler tous les téléchargements</string>
<string name="download_canceled_msg">Téléchargement annulé</string>
<string name="download_report_title">Téléchargements terminés</string>
+ <string name="download_report_content_title">Rapport des téléchargements</string>
<string name="download_error_malformed_url">URL incorrecte</string>
<string name="download_error_io_error">Erreur d\'E/S</string>
<string name="download_error_request_error">Erreur de requête</string>
@@ -129,6 +162,9 @@
<string name="download_request_error_dialog_message_prefix">Une erreur s\'est produite durant le téléchargement du fichier :\u0020</string>
<string name="authentication_notification_title">Authentification requise</string>
<string name="authentication_notification_msg">La ressource que vous avez demandé nécessite un nom d\'utilisateur et un mot de passe</string>
+ <string name="confirm_mobile_download_dialog_title">Confirmer téléchargement mobile</string>
+ <string name="confirm_mobile_download_dialog_only_add_to_queue">Rajouter à la liste</string>
+ <string name="confirm_mobile_download_dialog_enable_temporarily">Permettre temporairement</string>
<!--Mediaplayer messages-->
<string name="player_error_msg">Erreur !</string>
<string name="player_stopped_msg">Pas de lecture en cours</string>
@@ -143,6 +179,8 @@
<string name="playbackservice_notification_title">Lecture de podcast en cours</string>
<string name="unknown_media_key">AntennaPod - Touche média inconnue : %1$d</string>
<!--Queue operations-->
+ <string name="lock_queue">Bloquer la liste</string>
+ <string name="unlock_queue">Débloquer la liste</string>
<string name="clear_queue_label">Effacer la liste</string>
<string name="undo">Annuler</string>
<string name="removed_from_queue">Élément retiré</string>
@@ -200,10 +238,12 @@
<string name="pref_followQueue_sum">Après la fin d\'un épisode, passer au suivant</string>
<string name="pref_auto_delete_sum">Supprimer l\'épisode quand la lecture est finie</string>
<string name="pref_auto_delete_title">Supression automatique</string>
+ <string name="pref_smart_mark_as_played_sum">Marquer les épisodes comme lus même s\'il reste encore moins d\'un certain intervalle de temps</string>
+ <string name="pref_smart_mark_as_played_title">Marquer comme lu intelligent</string>
<string name="playback_pref">Lecture</string>
<string name="network_pref">Réseau</string>
- <string name="pref_autoUpdateIntervall_title">Intervalle de mise à jour</string>
- <string name="pref_autoUpdateIntervall_sum">Indiquer un intervalle de mise à jour automatique des flux, ou le désactiver</string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Désactiver</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Définir intervalle</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Ne télécharger les épisodes que par Wi-Fi</string>
<string name="pref_followQueue_title">Lecture continue</string>
<string name="pref_downloadMediaOnWifiOnly_title">Téléchargement en Wi-Fi</string>
@@ -223,6 +263,10 @@
<string name="pref_auto_flattr_sum">Configurer les paiements flattr automatiques</string>
<string name="user_interface_label">Interface utilisateur</string>
<string name="pref_set_theme_title">Choisir un thème</string>
+ <string name="pref_nav_drawer_feed_order_title">Définir ordre des abonnements</string>
+ <string name="pref_nav_drawer_feed_order_sum">Change l\'ordre de vos abonnements</string>
+ <string name="pref_nav_drawer_feed_counter_title">Définir compteur des abonnements</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Change l\'information affichée à côté du compteur des abonnements</string>
<string name="pref_set_theme_sum">Modifier l\'apparence d\'AntennaPod.</string>
<string name="pref_automatic_download_title">Téléchargement automatique</string>
<string name="pref_automatic_download_sum">Configurer le téléchargement automatique des épisodes.</string>
@@ -246,15 +290,22 @@
<string name="pref_gpodnet_setlogin_information_sum">Modifier les information de connexion pour votre compte gpodder.net</string>
<string name="pref_playback_speed_title">Vitesses de lecture</string>
<string name="pref_playback_speed_sum">Modifier la liste des vitesses disponibles pour la lecture audio</string>
+ <string name="pref_fast_forward">Avance rapide</string>
+ <string name="pref_rewind">Retour en arrière</string>
<string name="pref_gpodnet_sethostname_title">Choisir un nom de domaine</string>
<string name="pref_gpodnet_sethostname_use_default_host">Utiliser le nom de domaine par défaut</string>
<string name="pref_expandNotify_title">Etendre la notification</string>
<string name="pref_expandNotify_sum">Toujours étendre les notifications pour montrer les boutons de lecture</string>
<string name="pref_persistNotify_title">Boutons de lecture permanents</string>
<string name="pref_persistNotify_sum">Garder les notifications et les boutons de lecture sur l\'écran de verouillage quand la lecture est en pause</string>
+ <string name="pref_showDownloadReport_title">Afficher rapport des téléchargements</string>
+ <string name="pref_showDownloadReport_sum">Si les téléchargements échouent, générer un rapport des détails des échecs.</string>
<string name="pref_expand_notify_unsupport_toast">Les versions d\'Android antérieures à 4.1 ne sont pas compatibles avec les notifications élargies</string>
<string name="pref_queueAddToFront_sum">Ajouter de nouveaux épisodes en tête de file</string>
<string name="pref_queueAddToFront_title">Mettre au début de la file d\'attente</string>
+ <string name="pref_smart_mark_as_played_disabled">Désactivé</string>
+ <string name="pref_image_cache_size_title">Taille de la cache d\'image</string>
+ <string name="pref_image_cache_size_sum">Taille de l’espace disque pour stocker temporairement les images.</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Activer le paiement flattr automatique</string>
<string name="auto_flattr_after_percent">Lancer un paiement flattr pour un épisode dès que %d de l\'épisode a été joué</string>
@@ -280,6 +331,7 @@
<string name="opml_import_error_dir_empty">Le répertoire d\'importation est vide.</string>
<string name="select_all_label">Tout choisir</string>
<string name="deselect_all_label">Ne rien choisir</string>
+ <string name="select_options_label">Sélectionner...</string>
<string name="choose_file_from_filesystem">Depuis le système de fichier local</string>
<string name="choose_file_from_external_application">Utiliser une application tierce</string>
<string name="opml_export_label">Exportation OPML</string>
@@ -294,9 +346,21 @@
<string name="sleep_timer_label">Arrêt automatique</string>
<string name="time_left_label">Durée restante :\u0020</string>
<string name="time_dialog_invalid_input">Entrée invalide, la durée doit être un nombre entier</string>
- <string name="time_unit_seconds">secondes</string>
- <string name="time_unit_minutes">minutes</string>
- <string name="time_unit_hours">heures</string>
+ <string name="time_seconds">secondes</string>
+ <string name="time_minutes">minutes</string>
+ <string name="time_hours">heures</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">1 seconde</item>
+ <item quantity="other">%d secondes</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 minute</item>
+ <item quantity="other">%d minutes</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 heure</item>
+ <item quantity="other">%d heures</item>
+ </plurals>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">CATEGORIES</string>
<string name="gpodnet_toplist_header">PODCASTS POPULAIRES</string>
@@ -339,6 +403,9 @@
<string name="set_to_default_folder">Choisir le répertoire par défaut</string>
<string name="pref_pausePlaybackForFocusLoss_sum">Mettre la lecture en pause au lieu de baisser le volume quand une autre application veut jouer un son</string>
<string name="pref_pausePlaybackForFocusLoss_title">Mettre en pause lors des interruptions</string>
+ <string name="pref_resumeAfterCall_sum">Reprendre la lecture après un appel téléphonique</string>
+ <string name="pref_resumeAfterCall_title">Reprendre après appel</string>
+ <string name="pref_restart_required">AntennaPod doit être redémarré afin que ce changement prenne effet</string>
<!--Online feed view-->
<string name="subscribe_label">S\'abonner</string>
<string name="subscribed_label">Abonné</string>
@@ -365,7 +432,29 @@
<!--Feed information screen-->
<string name="authentication_label">Authentification</string>
<string name="authentication_descr">Modifier votre identifiant et mot de passe pour ce podcast et tous ses épisodes</string>
+ <!--Progress information-->
+ <string name="progress_upgrading_database">Mise à jour de la base de données</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importation des abonnements à partir d\'applications à usage unique...</string>
<string name="search_itunes_label">Chercher sur iTunes</string>
+ <string name="select_label"><b>Sélectionner ...</b></string>
+ <string name="all_label">Tout</string>
+ <string name="selected_all_label">Sélectionné tous les épisodes</string>
+ <string name="none_label">Aucun</string>
+ <string name="deselected_all_label">Désélectionné tous les épisodes</string>
+ <string name="played_label">Joués</string>
+ <string name="selected_played_label">Sélectionné tous les épisodes joués</string>
+ <string name="unplayed_label">Non joués</string>
+ <string name="selected_unplayed_label">Sélectionné tous les épisodes non joués</string>
+ <string name="downloaded_label">Téléchargés</string>
+ <string name="selected_downloaded_label">Sélectionné tous les épisodes téléchargés</string>
+ <string name="not_downloaded_label">Non téléchargés</string>
+ <string name="selected_not_downloaded_label">Sélectionné tous les épisodes non téléchargés</string>
+ <string name="sort_title"><b>Trier par ...</b></string>
+ <string name="sort_title_a_z">Titre (A \u2192 Z)</string>
+ <string name="sort_title_z_a">Titre (Z \u2192 A)</string>
+ <string name="sort_date_new_old">Date (Nouveau \u2192 Ancien)</string>
+ <string name="sort_date_old_new">Date (Ancien \u2192 Nouveau)</string>
+ <string name="sort_duration_short_long">Durée (Courte \u2192 Longue)</string>
+ <string name="sort_duration_long_short">Durée (Longue \u2192 Courte)</string>
</resources>
diff --git a/core/src/main/res/values-hi-rIN/strings.xml b/core/src/main/res/values-hi-rIN/strings.xml
index f32c7c02f..13ef6d489 100644
--- a/core/src/main/res/values-hi-rIN/strings.xml
+++ b/core/src/main/res/values-hi-rIN/strings.xml
@@ -57,7 +57,6 @@
<string name="remove_feed_label">पॉडकास्ट हटाएँ
</string>
<string name="share_link_label">शेयर वेबसाइट लिंक</string>
- <string name="share_source_label">शेयर फ़ीड लिंक</string>
<string name="feed_delete_confirmation_msg">इसकी पुष्टि करें कि आप इस फ़ीड और इस फ़ीड के सभी प्रकरणों को हटाना चाहते हैं जिन्हें आपने डाउनलोड किया है.</string>
<string name="feed_remover_msg">फ़ीड निकाल रहा है</string>
<!--actions on feeditems-->
@@ -158,8 +157,6 @@
<string name="pref_followQueue_sum">प्लेबैक के पूरा होने पर अगली पंक्ति आइटम के लिए जाएँ</string>
<string name="playback_pref">प्लेबैक</string>
<string name="network_pref">संजाल</string>
- <string name="pref_autoUpdateIntervall_title">अंतराल अद्यतन</string>
- <string name="pref_autoUpdateIntervall_sum">फ़ीड स्वचालित रूप से ताजा कर रहे हैं जिसमें एक अंतराल निर्दिष्ट करें या उसे निष्क्रिय करें </string>
<string name="pref_downloadMediaOnWifiOnly_sum">केवल वाईफ़ाई पर मीडिया फ़ाइलें डाउनलोड करें</string>
<string name="pref_followQueue_title">सतत प्लेबैक</string>
<string name="pref_downloadMediaOnWifiOnly_title">वाईफाई मीडिया डाउनलोड करें</string>
@@ -274,5 +271,6 @@
<string name="downloading_label">डाउनलोड कर रहा है ...</string>
<!--Content descriptions for image buttons-->
<!--Feed information screen-->
+ <!--Progress information-->
<!--AntennaPodSP-->
</resources>
diff --git a/core/src/main/res/values-it-rIT/strings.xml b/core/src/main/res/values-it-rIT/strings.xml
index b81e3f2ce..a7ddf81d3 100644
--- a/core/src/main/res/values-it-rIT/strings.xml
+++ b/core/src/main/res/values-it-rIT/strings.xml
@@ -26,6 +26,11 @@
<!--Main activity-->
<string name="drawer_open">Apri il menù</string>
<string name="drawer_close">Chiudi il menù</string>
+ <string name="drawer_feed_order_alphabetical">Ordina alfabeticamente</string>
+ <string name="drawer_feed_counter_new_unplayed">Numero di episodi nuovi e non riprodotti</string>
+ <string name="drawer_feed_counter_new">Numero di episodi nuovi</string>
+ <string name="drawer_feed_counter_unplayed">Numero di episodi non riprodotti</string>
+ <string name="drawer_feed_counter_none">Nessuno</string>
<!--Webview actions-->
<string name="open_in_browser_label">Apri nel browser</string>
<string name="copy_url_label">Copia URL</string>
@@ -37,6 +42,8 @@
<!--Other-->
<string name="confirm_label">Conferma</string>
<string name="cancel_label">Annulla</string>
+ <string name="yes">Sì</string>
+ <string name="no">No</string>
<string name="author_label">Autore</string>
<string name="language_label">Lingua</string>
<string name="url_label">URL</string>
@@ -59,7 +66,11 @@
<string name="close_label">Chiudi</string>
<string name="retry_label">Riprova</string>
<string name="auto_download_label">Includi nei download automatici</string>
+ <string name="auto_download_apply_to_items_title">Applica ai Precedenti Episodi</string>
<string name="parallel_downloads_suffix">\u0020download paralleli</string>
+ <string name="feed_auto_download_global">Globale</string>
+ <string name="feed_auto_download_always">Sempre</string>
+ <string name="feed_auto_download_never">Mai</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">URL del feed</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
@@ -70,10 +81,13 @@
<!--Actions on feeds-->
<string name="mark_all_read_label">Segna tutti come riprodotti</string>
<string name="mark_all_read_msg">Segnati tutti gli episodi come riprodotti</string>
+ <string name="mark_all_seen_label">Segna tutti come visti</string>
<string name="show_info_label">Informazioni</string>
<string name="remove_feed_label">Rimuovi un podcast</string>
+ <string name="share_label">Condividi...</string>
<string name="share_link_label">Condividi il link al sito</string>
- <string name="share_source_label">Condividi il link al feed</string>
+ <string name="share_feed_url_label">Condividi URL del Feed</string>
+ <string name="share_item_url_label">Condividi URL dell\'Episodio</string>
<string name="feed_delete_confirmation_msg">Per favore conferma la cancellazione di questo feed e di TUTTI gli episodi collegati che sono stati precedentemente scaricati.</string>
<string name="feed_remover_msg">Rimozione feed</string>
<string name="load_complete_feed">Ricarica il feed completo</string>
@@ -96,8 +110,8 @@
<string name="remove_label">Rimuovi</string>
<string name="remove_episode_lable">Rimuovi l\'episodio</string>
<string name="mark_read_label">Segna come riprodotto</string>
- <string name="mark_unread_label">Segna come non riprodotto</string>
<string name="marked_as_read_label">Segnato come riprodotto</string>
+ <string name="mark_unread_label">Segna come non riprodotto</string>
<string name="add_to_queue_label">Aggiungi alla coda</string>
<string name="added_to_queue_label">Aggiunto alla coda</string>
<string name="remove_from_queue_label">Rimuovi dalla coda</string>
@@ -126,6 +140,7 @@
<string name="download_error_unauthorized">Errore di autenticazione</string>
<string name="cancel_all_downloads_label">Annulla tutti i download</string>
<string name="download_canceled_msg">Download annullato</string>
+ <string name="download_report_title">Download completato con un errore (o errori)</string>
<string name="download_report_content_title">Rapporto del downoad</string>
<string name="download_error_malformed_url">URL malformato</string>
<string name="download_error_io_error">Errore IO</string>
@@ -219,8 +234,7 @@
<string name="pref_auto_delete_title">Elimina automaticamente</string>
<string name="playback_pref">Riproduzione</string>
<string name="network_pref">Rete</string>
- <string name="pref_autoUpdateIntervall_title">Intervallo di update</string>
- <string name="pref_autoUpdateIntervall_sum">Specifica un intervallo per l\'aggiornamento automatico dei feed o disabilitalo</string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Disabilita</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Abilita il download dei media solo tramite WiFi</string>
<string name="pref_followQueue_title">Playback continuo</string>
<string name="pref_downloadMediaOnWifiOnly_title">Download dei media su WiFi</string>
@@ -269,6 +283,7 @@
<string name="pref_expandNotify_sum">Espandi sempre le notifiche per mostrare i pulsanti di riproduzione.</string>
<string name="pref_persistNotify_title">Controlli di riproduzione persistenti</string>
<string name="pref_persistNotify_sum">Mantieni le notifiche e i controlli del blocco dello schermo quando la riproduzione è in pausa.</string>
+ <string name="pref_showDownloadReport_title">Mostra il Rapporto del Download</string>
<string name="pref_expand_notify_unsupport_toast">Le versioni di Android prima della 4.1 non supportano le notifiche estese.</string>
<string name="pref_smart_mark_as_played_disabled">Disabilitato</string>
<!--Auto-Flattr dialog-->
@@ -307,9 +322,21 @@
<string name="sleep_timer_label">Timer di spegnimento</string>
<string name="time_left_label">Tempo residuo:\u0020</string>
<string name="time_dialog_invalid_input">Input non valido, il campo deve essere un numero intero.</string>
- <string name="time_unit_seconds">secondi</string>
- <string name="time_unit_minutes">minuti</string>
- <string name="time_unit_hours">ore</string>
+ <string name="time_seconds">secondi</string>
+ <string name="time_minutes">minuti</string>
+ <string name="time_hours">ore</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">1 secondo</item>
+ <item quantity="other">%d secondi</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 minuto</item>
+ <item quantity="other">%d minuti</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 ora</item>
+ <item quantity="other">%d ore</item>
+ </plurals>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">CATEGORIE</string>
<string name="gpodnet_toplist_header">TOP PODCAST</string>
@@ -379,7 +406,21 @@
<!--Feed information screen-->
<string name="authentication_label">Autenticazione</string>
<string name="authentication_descr">Cambia il tuo nome utente e la tua password per questo podcast e i suoi episodi.</string>
+ <!--Progress information-->
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importazione di sottoscrizioni da applicazioni monouso in corso...</string>
<string name="search_itunes_label">Cerca su iTunes</string>
+ <string name="select_label"><b>Seleziona ...</b></string>
+ <string name="all_label">Tutti</string>
+ <string name="selected_all_label">Tutti gli Episodi Selezionati</string>
+ <string name="none_label">Nessuno</string>
+ <string name="deselected_all_label">Tutti gli Episodi Deselezionati</string>
+ <string name="played_label">Riprodotto</string>
+ <string name="unplayed_label">Non riprodotto</string>
+ <string name="sort_title_a_z">Titolo (A \u2192 Z)</string>
+ <string name="sort_title_z_a">Titolo (Z \u2192 A)</string>
+ <string name="sort_date_new_old">Data (New \u2192 Old)</string>
+ <string name="sort_date_old_new">Data (Old \u2192 New)</string>
+ <string name="sort_duration_short_long">Durata (Short \u2192 Long)</string>
+ <string name="sort_duration_long_short">Durata (Long \u2192 Short)</string>
</resources>
diff --git a/core/src/main/res/values-iw-rIL/strings.xml b/core/src/main/res/values-iw-rIL/strings.xml
index 9e9c0e6bc..07f3602ff 100644
--- a/core/src/main/res/values-iw-rIL/strings.xml
+++ b/core/src/main/res/values-iw-rIL/strings.xml
@@ -58,7 +58,11 @@
<string name="close_label">סגור</string>
<string name="retry_label">נסה שוב</string>
<string name="auto_download_label">כלול בהורדות אוטומטיות</string>
+ <string name="auto_delete_label">מחק לאחר ההשמעה\n(גובר על ההגדרה הגלובלית)</string>
<string name="parallel_downloads_suffix">\u0020הורדות במקביל</string>
+ <string name="feed_auto_download_global">לפי הגדרה גלובלית</string>
+ <string name="feed_auto_download_always">תמיד</string>
+ <string name="feed_auto_download_never">אף פעם</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">כתובת הזנה</string>
<string name="etxtFeedurlHint">כתובת של הזנה או אתר אינטרנט</string>
@@ -74,7 +78,6 @@
<string name="show_info_label">הצג מידע</string>
<string name="remove_feed_label">הסר פודקאסט</string>
<string name="share_link_label">שתף קישור אתר</string>
- <string name="share_source_label">שתף קישור הזנה</string>
<string name="feed_delete_confirmation_msg">אשר מחיקת הזנה זו ואת כל פרקי ההזנה שהורדת.</string>
<string name="feed_remover_msg">הסר הזנה</string>
<string name="load_complete_feed">רענן את כל ההזנה</string>
@@ -87,8 +90,8 @@
<string name="remove_label">הסר</string>
<string name="remove_episode_lable">הסר פרק</string>
<string name="mark_read_label">סמן כנקרא</string>
- <string name="mark_unread_label">סמן כלא נקרא</string>
<string name="marked_as_read_label">סומן כנקרא</string>
+ <string name="mark_unread_label">סמן כלא נקרא</string>
<string name="add_to_queue_label">הוסף לתור</string>
<string name="remove_from_queue_label">הסר מהתור</string>
<string name="visit_website_label">בקר באתר</string>
@@ -203,8 +206,6 @@
<string name="pref_auto_delete_title">מחיקה אוטומטית</string>
<string name="playback_pref">ניגון</string>
<string name="network_pref">רשת</string>
- <string name="pref_autoUpdateIntervall_title">זמן בין עידכונים</string>
- <string name="pref_autoUpdateIntervall_sum">ציין פרק זמן שבו ההזנות עוברות רענון באופן אוטומטי או לבטל ריענון</string>
<string name="pref_downloadMediaOnWifiOnly_sum">הורד קבצי מדיה רק דרך חיבור אינטרנט אלחוטי</string>
<string name="pref_followQueue_title">ניגון מתמשך</string>
<string name="pref_downloadMediaOnWifiOnly_title">הורדת מדיה דרך אינטרנט אלחוטי</string>
@@ -247,8 +248,6 @@
<string name="pref_gpodnet_setlogin_information_sum">שנה פרטי התחברות של חשבון gpodder.net.</string>
<string name="pref_playback_speed_title">מהירויות ניגון</string>
<string name="pref_playback_speed_sum">התאמת המהיריות הזמינות לניגון במהירות משתנה</string>
- <string name="pref_seek_delta_title">זמן דילוג</string>
- <string name="pref_seek_delta_sum">דלג מספר שניות זה בדילוג לאחור או קדימה</string>
<string name="pref_gpodnet_sethostname_title">הגדר שם שרת</string>
<string name="pref_gpodnet_sethostname_use_default_host">השתמש בשרת ברירת מידל</string>
<string name="pref_expandNotify_title">הרחב הודעה</string>
@@ -297,9 +296,6 @@
<string name="sleep_timer_label">טיימר שינה</string>
<string name="time_left_label">זמן נותר:\u0020</string>
<string name="time_dialog_invalid_input">קלט לא חוקי, זמן חייב להיות מספר שלם</string>
- <string name="time_unit_seconds">שניות</string>
- <string name="time_unit_minutes">דקות</string>
- <string name="time_unit_hours">שעות</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">קטגוריות</string>
<string name="gpodnet_toplist_header">פודקאסטים בכירים</string>
@@ -368,6 +364,7 @@
<!--Feed information screen-->
<string name="authentication_label">אימות</string>
<string name="authentication_descr">שנה את שם המשתמש והסיסמה שלך לפודקאסט ופרקים שלו.</string>
+ <!--Progress information-->
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">מייבא רישום מאפליקציות יעודיות...</string>
<string name="search_itunes_label">חפש בiTunes</string>
diff --git a/core/src/main/res/values-ja/strings.xml b/core/src/main/res/values-ja/strings.xml
index 79411ffc1..3a9e58d9a 100644
--- a/core/src/main/res/values-ja/strings.xml
+++ b/core/src/main/res/values-ja/strings.xml
@@ -27,6 +27,12 @@
<string name="drawer_open">メニューを開く</string>
<string name="drawer_close">メニューを閉じる</string>
<string name="drawer_preferences">ドロワー設定</string>
+ <string name="drawer_feed_order_unplayed_episodes">カウンターで並び替え</string>
+ <string name="drawer_feed_order_alphabetical">アルファベット順に並び替え</string>
+ <string name="drawer_feed_counter_new_unplayed">新しい未再生のエピソードの数</string>
+ <string name="drawer_feed_counter_new">新しいエピソードの数</string>
+ <string name="drawer_feed_counter_unplayed">未再生のエピソードの数</string>
+ <string name="drawer_feed_counter_none">なし</string>
<!--Webview actions-->
<string name="open_in_browser_label">ブラウザーで開く</string>
<string name="copy_url_label">URLをコピー</string>
@@ -38,6 +44,8 @@
<!--Other-->
<string name="confirm_label">確認</string>
<string name="cancel_label">キャンセル</string>
+ <string name="yes">はい</string>
+ <string name="no">いいえ</string>
<string name="author_label">作者</string>
<string name="language_label">言語</string>
<string name="url_label">URL</string>
@@ -60,7 +68,13 @@
<string name="close_label">閉じる</string>
<string name="retry_label">再試行</string>
<string name="auto_download_label">自動ダウンロードに含む</string>
+ <string name="auto_download_apply_to_items_title">前のエピソードに適用</string>
+ <string name="auto_download_apply_to_items_message">新しい <i>自動ダウンロード</i> の設定は、新しいエピソードに自動的に適用されます。\n前のエピソードにも適用しますか?</string>
+ <string name="auto_delete_label">エピソードの自動削除\n(全般のデフォルトを上書きします)</string>
<string name="parallel_downloads_suffix">\u0020パラレル ダウンロード</string>
+ <string name="feed_auto_download_global">全般</string>
+ <string name="feed_auto_download_always">常に</string>
+ <string name="feed_auto_download_never">しない</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">フィードURL</string>
<string name="etxtFeedurlHint">フィードまたはWebサイトのURL</string>
@@ -73,14 +87,20 @@
<string name="mark_all_read_msg">すべてのエピソードを再生済にしました</string>
<string name="mark_all_read_confirmation_msg">再生済としてマークするすべてのエピソードを確認してください。</string>
<string name="mark_all_read_feed_confirmation_msg">再生済としてマークするこのフィードのすべてのエピソードを確認してください。</string>
+ <string name="mark_all_seen_label">参照済としてマーク</string>
<string name="show_info_label">情報を表示</string>
<string name="remove_feed_label">ポッドキャストを削除</string>
+ <string name="share_label">共有...</string>
<string name="share_link_label">Webサイトのリンクを共有</string>
- <string name="share_source_label">フィードのリンクを共有</string>
+ <string name="share_link_with_position_label">場所とリンクを共有</string>
+ <string name="share_feed_url_label">フィード URLを共有</string>
+ <string name="share_item_url_label">エピソード URLを共有</string>
+ <string name="share_item_url_with_position_label">場所とエピソード URL を共有</string>
<string name="feed_delete_confirmation_msg">このフィードと、このフィードのダウンロードしたすべてのエピソードを削除することを確認してください。</string>
<string name="feed_remover_msg">フィードの削除中</string>
<string name="load_complete_feed">フィードをすべて更新</string>
<string name="hide_episodes_title">エピソードを非表示にする</string>
+ <string name="episode_actions">操作を適用</string>
<string name="hide_unplayed_episodes_label">未再生</string>
<string name="hide_paused_episodes_label">一時停止しました</string>
<string name="hide_played_episodes_label">再生しました</string>
@@ -99,8 +119,8 @@
<string name="remove_label">削除</string>
<string name="remove_episode_lable">エピソードを削除</string>
<string name="mark_read_label">再生済としてマーク</string>
- <string name="mark_unread_label">未再生としてマーク</string>
<string name="marked_as_read_label">再生済としてマークしました</string>
+ <string name="mark_unread_label">未再生としてマーク</string>
<string name="add_to_queue_label">キューに追加</string>
<string name="added_to_queue_label">キューに追加しました</string>
<string name="remove_from_queue_label">キューから削除</string>
@@ -229,8 +249,12 @@
<string name="pref_smart_mark_as_played_title">再生済としてスマートマーク</string>
<string name="playback_pref">再生</string>
<string name="network_pref">ネットワーク</string>
- <string name="pref_autoUpdateIntervall_title">更新間隔</string>
- <string name="pref_autoUpdateIntervall_sum">フィードが自動的に更新される間隔を指定するか、または無効にしてください</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">間隔または時間を更新</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">自動的にフィードを更新する間隔または時間を指定してください</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">\"2 時間ごと\" のような <i>間隔</i> 、\"7:00 AM\" のような特定の <i>時間</i> 、または自動更新を完全に <i>無効</i> にセットすることができます。\n\n<small>ご注意ください: 更新時間は正確ではありません。少し遅延が発生する可能性があります。</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">無効</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">間隔をセット</string>
+ <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">時間をセット</string>
<string name="pref_downloadMediaOnWifiOnly_sum">WiFi接続時のみメディアファイルをダウンロードします</string>
<string name="pref_followQueue_title">連続再生</string>
<string name="pref_downloadMediaOnWifiOnly_title">WiFiメディアダウンロード</string>
@@ -250,8 +274,14 @@
<string name="pref_auto_flattr_sum">自動Flattrを構成</string>
<string name="user_interface_label">インターフェース</string>
<string name="pref_set_theme_title">テーマを選択</string>
+ <string name="pref_nav_drawer_title">ナビゲーションドロワーをカスタマイズ</string>
+ <string name="pref_nav_drawer_sum">ナビゲーションドロワーの外観をカスタマイズします。</string>
<string name="pref_nav_drawer_items_title">ナビゲーションドロワーを変更</string>
<string name="pref_nav_drawer_items_sum">ナビゲーションドロワーに表示するアイテムを変更します。</string>
+ <string name="pref_nav_drawer_feed_order_title">購読注文をセット</string>
+ <string name="pref_nav_drawer_feed_order_sum">購読の注文を変更します</string>
+ <string name="pref_nav_drawer_feed_counter_title">購読カウンターをセット</string>
+ <string name="pref_nav_drawer_feed_counter_sum">購読カウンターで表示される情報を変更します</string>
<string name="pref_set_theme_sum">AntennaPodの外観を変更します。</string>
<string name="pref_automatic_download_title">自動ダウンロード</string>
<string name="pref_automatic_download_sum">エピソードの自動ダウンロードを構成します。</string>
@@ -283,10 +313,14 @@
<string name="pref_expandNotify_sum">常に再生ボタンを表示するように通知を展開します。</string>
<string name="pref_persistNotify_title">永続再生コントロール</string>
<string name="pref_persistNotify_sum">再生が一時停止された時に、通知およびロック画面のコントロールを保持します。</string>
+ <string name="pref_showDownloadReport_title">ダウンロード レポートを表示</string>
+ <string name="pref_showDownloadReport_sum">ダウンロードが失敗した場合、失敗の詳細を表示するレポートを生成します。</string>
<string name="pref_expand_notify_unsupport_toast">Androidバージョン4.1以前では、拡張通知をサポートしていません。</string>
<string name="pref_queueAddToFront_sum">新しいエピソードをキューの先頭に追加します。</string>
<string name="pref_queueAddToFront_title">キューの先頭に入れる</string>
<string name="pref_smart_mark_as_played_disabled">無効</string>
+ <string name="pref_image_cache_size_title">画像キャッシュサイズ</string>
+ <string name="pref_image_cache_size_sum">画像のディスクキャッシュのサイズ。</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">自動Flattrを有効にする</string>
<string name="auto_flattr_after_percent">%d %再生したらエピソードをFlattr </string>
@@ -312,6 +346,7 @@
<string name="opml_import_error_dir_empty">インポートディレクトリが空です。</string>
<string name="select_all_label">すべてを選択</string>
<string name="deselect_all_label">選択解除</string>
+ <string name="select_options_label">選択 ...</string>
<string name="choose_file_from_filesystem">ローカル ファイルシステムから</string>
<string name="choose_file_from_external_application">外部アプリケーションを使用する</string>
<string name="opml_export_label">OPMLエクスポート</string>
@@ -326,9 +361,18 @@
<string name="sleep_timer_label">スリープタイマー</string>
<string name="time_left_label">残り時間:\u0020</string>
<string name="time_dialog_invalid_input">入力が正しくありません、時間は数字で入力してください</string>
- <string name="time_unit_seconds">秒</string>
- <string name="time_unit_minutes">分</string>
- <string name="time_unit_hours">時</string>
+ <string name="time_seconds">秒</string>
+ <string name="time_minutes">分</string>
+ <string name="time_hours">時間</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="other">%d 秒</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="other">%d 分</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="other">%d 時間</item>
+ </plurals>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">カテゴリー</string>
<string name="gpodnet_toplist_header">トップ ボッドキャスト</string>
@@ -373,6 +417,7 @@
<string name="pref_pausePlaybackForFocusLoss_title">割り込み時に一時停止</string>
<string name="pref_resumeAfterCall_sum">着信が完了した後に再生を再開します</string>
<string name="pref_resumeAfterCall_title">着信後に再開</string>
+ <string name="pref_restart_required">この変更を有効にするには AntennaPod を再起動する必要があります。</string>
<!--Online feed view-->
<string name="subscribe_label">購読</string>
<string name="subscribed_label">購読しました</string>
@@ -399,7 +444,29 @@
<!--Feed information screen-->
<string name="authentication_label">認証</string>
<string name="authentication_descr">このポッドキャストとそのエピソード用のあなたのユーザー名とパスワードを変更します。</string>
+ <!--Progress information-->
+ <string name="progress_upgrading_database">データベースをアップグレードしています</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">単一目的のアプリから購読をインポート中…</string>
<string name="search_itunes_label">iTunes を検索</string>
+ <string name="select_label"><b>選択 ...</b></string>
+ <string name="all_label">すべて</string>
+ <string name="selected_all_label">すべてのエピソードを選択しました</string>
+ <string name="none_label">なし</string>
+ <string name="deselected_all_label">すべてのエピソードの選択を解除しました</string>
+ <string name="played_label">再生しました</string>
+ <string name="selected_played_label">再生済のエピソードを選択しました</string>
+ <string name="unplayed_label">未再生</string>
+ <string name="selected_unplayed_label">未再生のエピソードを選択しました</string>
+ <string name="downloaded_label">ダウンロードしました</string>
+ <string name="selected_downloaded_label">ダウンロード済のエピソードを選択しました</string>
+ <string name="not_downloaded_label">ダウンロードしていません</string>
+ <string name="selected_not_downloaded_label">ダウンロードしていないエピソードを選択しました</string>
+ <string name="sort_title"><b>並び替え順 ...</b></string>
+ <string name="sort_title_a_z">タイトル (A \u2192 Z)</string>
+ <string name="sort_title_z_a">タイトル (Z \u2192 A)</string>
+ <string name="sort_date_new_old">日付 (新 \u2192 旧)</string>
+ <string name="sort_date_old_new">日付 (旧 \u2192 新)</string>
+ <string name="sort_duration_short_long">期間 (短 \u2192 長)</string>
+ <string name="sort_duration_long_short">期間 (長 \u2192 短)</string>
</resources>
diff --git a/core/src/main/res/values-ko/strings.xml b/core/src/main/res/values-ko/strings.xml
index 148010050..7c9f76fcf 100644
--- a/core/src/main/res/values-ko/strings.xml
+++ b/core/src/main/res/values-ko/strings.xml
@@ -27,6 +27,12 @@
<string name="drawer_open">메뉴 열기</string>
<string name="drawer_close">메뉴 닫기</string>
<string name="drawer_preferences">드로어 기본 설정</string>
+ <string name="drawer_feed_order_unplayed_episodes">카운터로 정렬</string>
+ <string name="drawer_feed_order_alphabetical">사전 순서로 정렬</string>
+ <string name="drawer_feed_counter_new_unplayed">새로운 에피소드와 재생하지 않은 에피소드 수</string>
+ <string name="drawer_feed_counter_new">새로운 에피소드 수</string>
+ <string name="drawer_feed_counter_unplayed">재생하지 않은 에피소드 수</string>
+ <string name="drawer_feed_counter_none">없음</string>
<!--Webview actions-->
<string name="open_in_browser_label">브라우저에서 열기</string>
<string name="copy_url_label">URL 복사</string>
@@ -38,6 +44,8 @@
<!--Other-->
<string name="confirm_label">확인</string>
<string name="cancel_label">취소</string>
+ <string name="yes">예</string>
+ <string name="no">아니요</string>
<string name="author_label">저자</string>
<string name="language_label">언어</string>
<string name="url_label">URL</string>
@@ -60,7 +68,13 @@
<string name="close_label">닫기</string>
<string name="retry_label">다시 시도</string>
<string name="auto_download_label">자동 다운로드에 포함</string>
+ <string name="auto_download_apply_to_items_title">예전 에피소드에 적용</string>
+ <string name="auto_download_apply_to_items_message">새로운 &lt;i&gt;자동 다운로드&lt;/I&gt; 설정은 자동으로 새로운 에피소드에 적용됩니다. 예전 에피소드에도 적용하시겠습니까?</string>
+ <string name="auto_delete_label">에피소드 자동 삭제\n(전체 설정보다 우선)</string>
<string name="parallel_downloads_suffix">\u0020동시 다운로드</string>
+ <string name="feed_auto_download_global">전체 설정</string>
+ <string name="feed_auto_download_always">항상</string>
+ <string name="feed_auto_download_never">안 함</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">피드 URL</string>
<string name="etxtFeedurlHint">피드의 URL 또는 홈페이지</string>
@@ -73,14 +87,20 @@
<string name="mark_all_read_msg">모든 에피소드를 재생했다고 표시했습니다</string>
<string name="mark_all_read_confirmation_msg">모든 에피소드를 재생했다고 표시할지 확인하십시오.</string>
<string name="mark_all_read_feed_confirmation_msg">이 피드에 들어 있는 모든 에피소드를 재생했다고 표시할지 확인하십시오.</string>
+ <string name="mark_all_seen_label">모두 봤다고 표시</string>
<string name="show_info_label">정보 표시</string>
<string name="remove_feed_label">팟캐스트 제거</string>
+ <string name="share_label">공유...</string>
<string name="share_link_label">홈페이지 링크 공유</string>
- <string name="share_source_label">피드 링크 공유</string>
+ <string name="share_link_with_position_label">위치와 같이 링크 공유</string>
+ <string name="share_feed_url_label">피드 URL 공유</string>
+ <string name="share_item_url_label">에피소드 URL 공유</string>
+ <string name="share_item_url_with_position_label">위치와 같이 에피소드 URL 공유</string>
<string name="feed_delete_confirmation_msg">이 피드와 이 피드에서 다운로드한 모든 에피소드를 삭제하시려면 확인을 누르십시오.</string>
<string name="feed_remover_msg">피드 삭제하는 중</string>
<string name="load_complete_feed">전체 피드 새로고침</string>
<string name="hide_episodes_title">에피소드 감추기</string>
+ <string name="episode_actions">동작 적용</string>
<string name="hide_unplayed_episodes_label">재생 안 함</string>
<string name="hide_paused_episodes_label">일시 중지</string>
<string name="hide_played_episodes_label">재생함</string>
@@ -99,8 +119,8 @@
<string name="remove_label">제거</string>
<string name="remove_episode_lable">에피소드 제거</string>
<string name="mark_read_label">재생했다고 표시</string>
- <string name="mark_unread_label">재생하지 않음으로 표시</string>
<string name="marked_as_read_label">재생했다고 표시함</string>
+ <string name="mark_unread_label">재생하지 않음으로 표시</string>
<string name="add_to_queue_label">대기열에 추가</string>
<string name="added_to_queue_label">대기열에 추가함</string>
<string name="remove_from_queue_label">대기열에서 제거</string>
@@ -229,8 +249,12 @@
<string name="pref_smart_mark_as_played_title">똑똑하게 재생한 것으로 표시</string>
<string name="playback_pref">재생</string>
<string name="network_pref">네트워크</string>
- <string name="pref_autoUpdateIntervall_title">업데이트 주기</string>
- <string name="pref_autoUpdateIntervall_sum">피드를 새로 고칠 주기를 지정하거나 새로 고침을 하지 않음</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">업데이트 주기 또는 하루 중 시각</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">피드를 자동으로 새로 고칠 주기 또는 하루 중 특정 시각을 지정하십시오</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">\"매 2시간\"과 같이 <i>주기</i>를 지정할 수도 있고, \"오전 7:00\"와 같이 <i>하루 중 시각</i>을 지정할 수도 있고, 자동 업데이트를 모두 <i>사용 안 할</i> 수도 있습니다.\n\n<small>안내: 업데이트 시간은 정확하지 않습니다. 약간 늦게 업데이트할 수 있습니다.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">사용 안 함</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">주기 지정</string>
+ <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">하루 중 시각 지정</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Wi-Fi를 통해서만 미디어 파일 다운로드</string>
<string name="pref_followQueue_title">연속 재생</string>
<string name="pref_downloadMediaOnWifiOnly_title">Wi-Fi 미디어 다운로드</string>
@@ -250,8 +274,14 @@
<string name="pref_auto_flattr_sum">자동 flattr 설정</string>
<string name="user_interface_label">사용자 인터페이스</string>
<string name="pref_set_theme_title">테마 선택</string>
+ <string name="pref_nav_drawer_title">네비게이션 드로어 사용자 설정</string>
+ <string name="pref_nav_drawer_sum">네비게이션 드로어의 모양을 사용자 설정합니다.</string>
<string name="pref_nav_drawer_items_title">네비게이션 드로어 바꾸기</string>
<string name="pref_nav_drawer_items_sum">네비게이션 드로어에 어떤 항목을 표시할지 바꿉니다.</string>
+ <string name="pref_nav_drawer_feed_order_title">구독 순서 설정</string>
+ <string name="pref_nav_drawer_feed_order_sum">구독 순서를 바꿉니다</string>
+ <string name="pref_nav_drawer_feed_counter_title">구독 카운터 설정</string>
+ <string name="pref_nav_drawer_feed_counter_sum">구독 카운터에 따라 표시되는 정보를 바꿉니다</string>
<string name="pref_set_theme_sum">안테나팟의 겉모양을 바꿉니다.</string>
<string name="pref_automatic_download_title">자동 다운로드</string>
<string name="pref_automatic_download_sum">에피소드 자동 다운로드를 설정합니다.</string>
@@ -283,10 +313,14 @@
<string name="pref_expandNotify_sum">항상 알림에서 재생 버튼이 표시되도록 확장</string>
<string name="pref_persistNotify_title">재생 조작 고정</string>
<string name="pref_persistNotify_sum">재생이 일시 중지했을 때에도 알림과 잠금 화면의 조작 기능 유지</string>
+ <string name="pref_showDownloadReport_title">다운로드 보고서 보기</string>
+ <string name="pref_showDownloadReport_sum">다운로드가 실패하면, 실패를 자세히 표시하는 보고서를 만듭니다.</string>
<string name="pref_expand_notify_unsupport_toast">안드로이드 4.1 전 버전에서는 알림 확장을 지원하지 않습니다.</string>
<string name="pref_queueAddToFront_sum">새 에피소드를 대기열 앞에 추가합니다.</string>
<string name="pref_queueAddToFront_title">대기열 앞에 추가</string>
<string name="pref_smart_mark_as_played_disabled">사용 안 함</string>
+ <string name="pref_image_cache_size_title">이미지 캐시 크기</string>
+ <string name="pref_image_cache_size_sum">이미지에 사용할 디스크 캐시 크기</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">자동 flattr 사용</string>
<string name="auto_flattr_after_percent">%d 퍼센트를 재생하면 에피소드에 flattr합니다</string>
@@ -312,6 +346,7 @@
<string name="opml_import_error_dir_empty">가져오기 디렉터리가 비어 있습니다.</string>
<string name="select_all_label">모두 선택</string>
<string name="deselect_all_label">모두 선택 해제</string>
+ <string name="select_options_label">선택...</string>
<string name="choose_file_from_filesystem">로컬 파일시스템에서</string>
<string name="choose_file_from_external_application">외부 앱 사용</string>
<string name="opml_export_label">OPML 내보내기</string>
@@ -326,9 +361,18 @@
<string name="sleep_timer_label">취침 타이머</string>
<string name="time_left_label">남은 시간:\u0020</string>
<string name="time_dialog_invalid_input">입력이 잘못되었습니다. 시간으로 숫자를 입력해야 합니다.</string>
- <string name="time_unit_seconds">초</string>
- <string name="time_unit_minutes">분</string>
- <string name="time_unit_hours">시간</string>
+ <string name="time_seconds">초</string>
+ <string name="time_minutes">분</string>
+ <string name="time_hours">시간</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="other">%d초</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="other">%d분</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="other">%d시간</item>
+ </plurals>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">분류</string>
<string name="gpodnet_toplist_header">상위 팟캐스트</string>
@@ -373,6 +417,7 @@
<string name="pref_pausePlaybackForFocusLoss_title">끼어들면 일시 중지</string>
<string name="pref_resumeAfterCall_sum">전화 통화가 끝난 후에 재생 다시 시작</string>
<string name="pref_resumeAfterCall_title">통화 후에 다시 시작</string>
+ <string name="pref_restart_required">이 변경 사항을 적용하려면 안테나팟을 다시 시작해야 합니다.</string>
<!--Online feed view-->
<string name="subscribe_label">구독</string>
<string name="subscribed_label">구독함</string>
@@ -399,7 +444,29 @@
<!--Feed information screen-->
<string name="authentication_label">인증</string>
<string name="authentication_descr">이 팟캐스트와 에피소드에 대한 사용자 이름과 비밀번호를 바꿉니다.</string>
+ <!--Progress information-->
+ <string name="progress_upgrading_database">데이터베이스 업그레이드 중</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">단일 용도 앱에서 구독 정보를 가져옵니다...</string>
<string name="search_itunes_label">iTunes 검색</string>
+ <string name="select_label"><b>선택 ...</b></string>
+ <string name="all_label">모두</string>
+ <string name="selected_all_label">모든 에피소드 선택</string>
+ <string name="none_label">없음</string>
+ <string name="deselected_all_label">모든 에피소드 선택 해제</string>
+ <string name="played_label">재생함</string>
+ <string name="selected_played_label">재생 에피소드 선택</string>
+ <string name="unplayed_label">재생 안 함</string>
+ <string name="selected_unplayed_label">재생 안 한 에피소드 선택</string>
+ <string name="downloaded_label">다운로드함</string>
+ <string name="selected_downloaded_label">다운로드한 에피소드 선택</string>
+ <string name="not_downloaded_label">다운로드 안 함</string>
+ <string name="selected_not_downloaded_label">다운로드 안 한 에피소드 선택</string>
+ <string name="sort_title"><b>정렬 ...</b></string>
+ <string name="sort_title_a_z">제목 (A \u2192 Z)</string>
+ <string name="sort_title_z_a">제목 (Z \u2192 A)</string>
+ <string name="sort_date_new_old">시각 (최근 \u2192 과거)</string>
+ <string name="sort_date_old_new">시각 (과거 \u2192 최근)</string>
+ <string name="sort_duration_short_long">길이 (짧은 \u2192 긴)</string>
+ <string name="sort_duration_long_short">길이 (긴 \u2192 짧은)</string>
</resources>
diff --git a/core/src/main/res/values-nl/strings.xml b/core/src/main/res/values-nl/strings.xml
index 0f447d54a..4ef43ecaf 100644
--- a/core/src/main/res/values-nl/strings.xml
+++ b/core/src/main/res/values-nl/strings.xml
@@ -54,7 +54,6 @@
<string name="mark_all_read_label">Alles als gelezen markeren</string>
<string name="show_info_label">Toon informatie</string>
<string name="share_link_label">Website link delen</string>
- <string name="share_source_label">Feed link delen</string>
<string name="feed_delete_confirmation_msg">Bevestig dat u deze feed en ALLE afleveringen van deze feed die u hebt gedownload wilt verwijderen.</string>
<string name="feed_remover_msg">Feed verwijderen</string>
<!--actions on feeditems-->
@@ -162,8 +161,6 @@
<string name="pref_followQueue_sum">Volgende wachtrij item afspelen als de episode voltooid is</string>
<string name="playback_pref">Afspelen</string>
<string name="network_pref">Netwerk</string>
- <string name="pref_autoUpdateIntervall_title">Update interval</string>
- <string name="pref_autoUpdateIntervall_sum">Voer een tijdsinterval in waarin de feeds automatisch worden vernieuwd, of schakel het uit</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Download mediabestanden alleen via WiFi</string>
<string name="pref_followQueue_title">Continu afspelen</string>
<string name="pref_downloadMediaOnWifiOnly_title">WiFi download van media</string>
@@ -295,6 +292,7 @@
<string name="new_episodes_count_label">Aantal nieuwe afleveringen</string>
<string name="in_progress_episodes_count_label">Aantal afleveringen dat begonnen te luisteren zijn</string>
<!--Feed information screen-->
+ <!--Progress information-->
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Abonnementen aan het importeren vanuit single-purpose apps...</string>
</resources>
diff --git a/core/src/main/res/values-no/strings.xml b/core/src/main/res/values-no/strings.xml
new file mode 100644
index 000000000..645d576a4
--- /dev/null
+++ b/core/src/main/res/values-no/strings.xml
@@ -0,0 +1,31 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!--Activitiy and fragment titles-->
+ <!--New episodes fragment-->
+ <!--Main activity-->
+ <!--Webview actions-->
+ <!--Playback history-->
+ <!--Other-->
+ <!--'Add Feed' Activity labels-->
+ <!--Actions on feeds-->
+ <!--actions on feeditems-->
+ <!--Download messages and labels-->
+ <!--Mediaplayer messages-->
+ <!--Queue operations-->
+ <!--Flattr-->
+ <!--Flattr-->
+ <!--Variable Speed-->
+ <!--Empty list labels-->
+ <!--Preferences-->
+ <!--Auto-Flattr dialog-->
+ <!--Search-->
+ <!--OPML import and export-->
+ <!--Sleep timer-->
+ <!--gpodder.net-->
+ <!--Directory chooser-->
+ <!--Online feed view-->
+ <!--Content descriptions for image buttons-->
+ <!--Feed information screen-->
+ <!--Progress information-->
+ <!--AntennaPodSP-->
+</resources>
diff --git a/core/src/main/res/values-pl-rPL/strings.xml b/core/src/main/res/values-pl-rPL/strings.xml
index ba1a0bb91..a43ce7722 100644
--- a/core/src/main/res/values-pl-rPL/strings.xml
+++ b/core/src/main/res/values-pl-rPL/strings.xml
@@ -71,7 +71,6 @@
<string name="show_info_label">Pokaż informacje</string>
<string name="remove_feed_label">Usuń podcast</string>
<string name="share_link_label">Udostępnij stronę</string>
- <string name="share_source_label">Udostępnij kanał</string>
<string name="feed_delete_confirmation_msg">Potwierdź chęć usunięcia tego kanału wraz ze WSZYSTKIMI odcinkami, które zostały pobrane.</string>
<string name="feed_remover_msg">Usuwanie kanału</string>
<string name="load_complete_feed">Odśwież cały kanał</string>
@@ -197,8 +196,6 @@
<string name="pref_auto_delete_title">Automatyczne usuwanie</string>
<string name="playback_pref">Odtwarzanie</string>
<string name="network_pref">Sieć</string>
- <string name="pref_autoUpdateIntervall_title">Częstość aktualizacji</string>
- <string name="pref_autoUpdateIntervall_sum">Określ częstotliwość automatycznego odświeżania lub je wyłącz</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Pobieraj pliki tylko przez WiFi</string>
<string name="pref_followQueue_title">Odtwarzanie ciągłe</string>
<string name="pref_downloadMediaOnWifiOnly_title">WiFi media pobrane</string>
@@ -238,8 +235,6 @@
<string name="pref_gpodnet_setlogin_information_sum">Zmień dane logowania konta gpodder.net.</string>
<string name="pref_playback_speed_title">Prędkość odtwarzania</string>
<string name="pref_playback_speed_sum">Dostosuj prędkości dostępne dla odtwarzania audio o zmiennej prędkości</string>
- <string name="pref_seek_delta_title">Seek time</string>
- <string name="pref_seek_delta_sum">Przeskocz o tyle sekund przewijając</string>
<string name="pref_gpodnet_sethostname_title">Ustaw nazwę hosta</string>
<string name="pref_gpodnet_sethostname_use_default_host">Użyj domyślnego hosta</string>
<string name="pref_expandNotify_title">Rozwiń Powiadomienia</string>
@@ -281,9 +276,6 @@
<string name="sleep_timer_label">Wyłącznik czasowy</string>
<string name="time_left_label">Pozostały czas:\u0020</string>
<string name="time_dialog_invalid_input">Błąd wpisu, czas musi być liczbą całkowitą</string>
- <string name="time_unit_seconds">sekundy</string>
- <string name="time_unit_minutes">minuty</string>
- <string name="time_unit_hours">godziny</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">KATEGORIE</string>
<string name="gpodnet_toplist_header">TOP PODCASTY</string>
@@ -353,6 +345,7 @@ https://gpodder.net/register/</string>
<!--Feed information screen-->
<string name="authentication_label">Autoryzacja</string>
<string name="authentication_descr">Zmień swoją nazwę użytkownika oraz hasło dla tego podcastu i jego odcinków</string>
+ <!--Progress information-->
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importowanie subskrybcji z jednozadaniowych aplikacji</string>
</resources>
diff --git a/core/src/main/res/values-pt-rBR/strings.xml b/core/src/main/res/values-pt-rBR/strings.xml
index c3523acfb..3d421fdb2 100644
--- a/core/src/main/res/values-pt-rBR/strings.xml
+++ b/core/src/main/res/values-pt-rBR/strings.xml
@@ -54,7 +54,6 @@
<string name="mark_all_read_label">Marcar todos como lido</string>
<string name="show_info_label">Mostrar informação</string>
<string name="share_link_label">Compartilhar link do site</string>
- <string name="share_source_label">Compartilhar link do feed</string>
<string name="feed_delete_confirmation_msg">Por favor confirme que você deseja apagar este feed e TODOS os episódios que você fez download deste feed.</string>
<string name="feed_remover_msg">Removendo feed</string>
<!--actions on feeditems-->
@@ -148,8 +147,6 @@
<string name="pref_followQueue_sum">Pular para próximo item da fila quando a reprodução terminar</string>
<string name="playback_pref">Reprodução</string>
<string name="network_pref">Rede</string>
- <string name="pref_autoUpdateIntervall_title">Intervalo de atualização</string>
- <string name="pref_autoUpdateIntervall_sum">Especifica o intervalo com que os feeds serão atualizados automaticamente ou desabilita esta funcionalidade</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Fazer download dos arquivos apenas via rede WiFi</string>
<string name="pref_followQueue_title">Reprodução contínua</string>
<string name="pref_downloadMediaOnWifiOnly_title">Download de mídia via WiFi</string>
@@ -271,5 +268,6 @@
<string name="in_queue_label">Episódio está na fila</string>
<string name="new_episodes_count_label">Numero de novos episódios</string>
<!--Feed information screen-->
+ <!--Progress information-->
<!--AntennaPodSP-->
</resources>
diff --git a/core/src/main/res/values-pt/strings.xml b/core/src/main/res/values-pt/strings.xml
index abbf97de6..f02cd629e 100644
--- a/core/src/main/res/values-pt/strings.xml
+++ b/core/src/main/res/values-pt/strings.xml
@@ -16,7 +16,7 @@
<string name="downloads_running_label">Em curso</string>
<string name="downloads_completed_label">Terminadas</string>
<string name="downloads_log_label">Registo</string>
- <string name="cancel_download_label">Cancelar transferência</string>
+ <string name="cancel_download_label">Cancelar\ntransferência</string>
<string name="playback_history_label">Histórico de reprodução</string>
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_auth_label">Dados gpodder.net</string>
@@ -27,6 +27,12 @@
<string name="drawer_open">Abrir menu</string>
<string name="drawer_close">Fechar menu</string>
<string name="drawer_preferences">Preferências do menu</string>
+ <string name="drawer_feed_order_unplayed_episodes">Ordenar por contador</string>
+ <string name="drawer_feed_order_alphabetical">Ordenar alfabeticamente</string>
+ <string name="drawer_feed_counter_new_unplayed">Número de episódios novos ou por reproduzir</string>
+ <string name="drawer_feed_counter_new">Número de novos episódios</string>
+ <string name="drawer_feed_counter_unplayed">Número de episódios por reproduzir</string>
+ <string name="drawer_feed_counter_none">Nenhum</string>
<!--Webview actions-->
<string name="open_in_browser_label">Abrir no navegador</string>
<string name="copy_url_label">Copiar URL</string>
@@ -38,6 +44,8 @@
<!--Other-->
<string name="confirm_label">Confirmar</string>
<string name="cancel_label">Cancelar</string>
+ <string name="yes">Sim</string>
+ <string name="no">Não</string>
<string name="author_label">Autor</string>
<string name="language_label">Idioma</string>
<string name="url_label">URL</string>
@@ -60,7 +68,13 @@
<string name="close_label">Fechar</string>
<string name="retry_label">Tentar novamente</string>
<string name="auto_download_label">Incluir nas transferências automáticas</string>
+ <string name="auto_download_apply_to_items_title">Aplicar aos episódios anteriores</string>
+ <string name="auto_download_apply_to_items_message">A definição <i>Transferência automática</i> será aplicada a todos os novos episódios.\nGostaria de também a aplicar aos episódios anteriores?</string>
+ <string name="auto_delete_label">Apagar episódio automáticamente\n(altera a definição global)</string>
<string name="parallel_downloads_suffix">\u0020transferências simultâneas</string>
+ <string name="feed_auto_download_global">Global</string>
+ <string name="feed_auto_download_always">Sempre</string>
+ <string name="feed_auto_download_never">Nunca</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">URL da fonte</string>
<string name="etxtFeedurlHint">URL da fonte ou sítio web</string>
@@ -73,14 +87,20 @@
<string name="mark_all_read_msg">Marcar todos os episódios como reproduzidos</string>
<string name="mark_all_read_confirmation_msg">Por favor confirme que deseja marcar todos os episódios como reproduzidos</string>
<string name="mark_all_read_feed_confirmation_msg">Por favor confirme que deseja marcar todos os episódios desta fonte como reproduzidos</string>
+ <string name="mark_all_seen_label">Marcar tudo como visto</string>
<string name="show_info_label">Mostrar informações</string>
<string name="remove_feed_label">Remover podcast</string>
- <string name="share_link_label">Partilhar ligação do sítio web</string>
- <string name="share_source_label">Partilhar ligação da fonte</string>
- <string name="feed_delete_confirmation_msg">Confirme a eliminação desta fonte e de todos os episódios a ela pertencentes</string>
+ <string name="share_label">Partilhar...</string>
+ <string name="share_link_label">Partilhar ligação</string>
+ <string name="share_link_with_position_label">Partilhar ligação com posição</string>
+ <string name="share_feed_url_label">Partilhar URL da fonte</string>
+ <string name="share_item_url_label">Partilhar URL do episódio</string>
+ <string name="share_item_url_with_position_label">Partilhar URL do episódio com posição</string>
+ <string name="feed_delete_confirmation_msg">Por favor confirme que deseja apagar esta fonte e todos os episódios transferidos</string>
<string name="feed_remover_msg">Remover fonte</string>
<string name="load_complete_feed">Atualizar todas as páginas da fonte</string>
<string name="hide_episodes_title">Ocultar episódios</string>
+ <string name="episode_actions">Aplicar ações</string>
<string name="hide_unplayed_episodes_label">Não reproduzidos</string>
<string name="hide_paused_episodes_label">Em pausa</string>
<string name="hide_played_episodes_label">Reproduzidos</string>
@@ -99,8 +119,8 @@
<string name="remove_label">Remover</string>
<string name="remove_episode_lable">Remover episódio</string>
<string name="mark_read_label">Marcar como reproduzido</string>
- <string name="mark_unread_label">Marcar como não reproduzido</string>
<string name="marked_as_read_label">Marcado como reproduzido</string>
+ <string name="mark_unread_label">Marcar como não reproduzido</string>
<string name="add_to_queue_label">Adicionar à fila</string>
<string name="added_to_queue_label">Adicionado à fila</string>
<string name="remove_from_queue_label">Remover da fila</string>
@@ -138,7 +158,7 @@
<string name="download_error_db_access">Erro de acesso à base de dados</string>
<string name="downloads_left">\u0020Transferências em falta</string>
<string name="downloads_processing">Processamento de transferências</string>
- <string name="download_notification_title">A transferir dados...</string>
+ <string name="download_notification_title">A transferir dados do podcast</string>
<string name="download_report_content">%1$d transferências efetuadas, %2$d falhadas</string>
<string name="download_log_title_unknown">Título desconhecido</string>
<string name="download_type_feed">Fonte</string>
@@ -209,7 +229,7 @@
<!--Variable Speed-->
<string name="download_plugin_label">Transferir extra</string>
<string name="no_playback_plugin_title">Extra não instalado</string>
- <string name="no_playback_plugin_msg">Para que a velocidade de reprodução variável funcione, tem que instalar um biblioteca de terceiros.\n\nClique em Transferir extra para a transferir no Google Play.\n\nQuaisquer problemas que ocorram na utilização do extra devem ser reportados diretamente ao seu programador.</string>
+ <string name="no_playback_plugin_msg">Para que a velocidade variável de reprodução funcione, tem que instalar um biblioteca de terceiros.\n\nClique em Transferir extra para a transferir no Google Play.\n\nQuaisquer erros que ocorram na utilização do extra devem ser reportados diretamente ao seu programador.</string>
<string name="set_playback_speed_label">Velocidades de reprodução</string>
<!--Empty list labels-->
<string name="no_items_label">Não existem itens nesta lista</string>
@@ -223,19 +243,23 @@
<string name="pref_pauseOnHeadsetDisconnect_sum">Pausa na reprodução ao remover os auscultadores</string>
<string name="pref_unpauseOnHeadsetReconnect_sum">Continuar reprodução ao ligar os auscultadores</string>
<string name="pref_followQueue_sum">Ir para a faixa seguinte ao terminar a reprodução</string>
- <string name="pref_auto_delete_sum">Eliminar episódio ao terminar a reprodução</string>
+ <string name="pref_auto_delete_sum">Apagar episódio ao terminar a reprodução</string>
<string name="pref_auto_delete_title">Eliminação automática</string>
<string name="pref_smart_mark_as_played_sum">Marcar episódios como reproduzidos mesmo que restem alguns segundos de reprodução</string>
<string name="pref_smart_mark_as_played_title">Marcar como reproduzido (inteligente)</string>
<string name="playback_pref">Reprodução</string>
<string name="network_pref">Rede</string>
- <string name="pref_autoUpdateIntervall_title">Intervalo entre atualizações</string>
- <string name="pref_autoUpdateIntervall_sum">Indique o intervalo de tempo entre as atualizações de fontes ou desative a opção</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">Apenas transferir pelas redes sem fios</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">Intervalo de atualização ou hora do dia</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Define um intervalo de atualização ou hora para atualizar automaticamente a fonte</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Pode definir um <i>intervalo</i>, ex.: \"a cada 2 horas\", definir uma <i>hora do dia</i>, ex.: \"7:00 AM\" ou <i>desativar</i> as atualizações automáticas.\n\n<small>Tenha em conta que a hora de atualização não é precisa. Pode existir um pouco de atraso.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Desativar</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Definir intervalo</string>
+ <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Definir hora do dia</string>
+ <string name="pref_downloadMediaOnWifiOnly_sum">Apenas transferir através de redes sem fios</string>
<string name="pref_followQueue_title">Reprodução contínua</string>
- <string name="pref_downloadMediaOnWifiOnly_title">Transferência Wi-Fi</string>
+ <string name="pref_downloadMediaOnWifiOnly_title">Transferir por Wi-Fi</string>
<string name="pref_pauseOnHeadsetDisconnect_title">Auscultadores removidos</string>
- <string name="pref_unpauseOnHeadsetReconnect_title">Auscultadores ligados</string>
+ <string name="pref_unpauseOnHeadsetReconnect_title">Auscultadores inseridos</string>
<string name="pref_mobileUpdate_title">Atualizações móveis</string>
<string name="pref_mobileUpdate_sum">Permitir atualizações através da rede de dados móveis</string>
<string name="refreshing_label">A atualizar</string>
@@ -250,14 +274,20 @@
<string name="pref_auto_flattr_sum">Configurar flattr automático</string>
<string name="user_interface_label">Interface</string>
<string name="pref_set_theme_title">Tema</string>
- <string name="pref_nav_drawer_items_title">Alterar itens do menu</string>
+ <string name="pref_nav_drawer_title">Personalizar menu de navegação</string>
+ <string name="pref_nav_drawer_sum">Personaliza a aparência do menu de navegação</string>
+ <string name="pref_nav_drawer_items_title">Alterar elementos do menu</string>
<string name="pref_nav_drawer_items_sum">Alterar os itens que aparecem no menu de navegação</string>
+ <string name="pref_nav_drawer_feed_order_title">Definir ordem de subscrição</string>
+ <string name="pref_nav_drawer_feed_order_sum">Alterar a ordem das suas subscrições</string>
+ <string name="pref_nav_drawer_feed_counter_title">Definir contador de subsrições</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Mudar informação mostrada no contador de subscrições</string>
<string name="pref_set_theme_sum">Mudar o aspeto do AntennaPod</string>
<string name="pref_automatic_download_title">Transferência automática</string>
<string name="pref_automatic_download_sum">Configure a transferência automática dos episódios</string>
<string name="pref_autodl_wifi_filter_title">Ativar filtro Wi-Fi</string>
<string name="pref_autodl_wifi_filter_sum">Apenas permitir transferências automáticas através de redes sem fios</string>
- <string name="pref_automatic_download_on_battery_title">Transferência se não estiver a carregar</string>
+ <string name="pref_automatic_download_on_battery_title">Transferir se não estiver a carregar</string>
<string name="pref_automatic_download_on_battery_sum">Permitir transferência automática se a bateria não estiver a ser carregada</string>
<string name="pref_parallel_downloads_title">Transferências simultâneas</string>
<string name="pref_episode_cache_title">Cache de episódios</string>
@@ -283,10 +313,14 @@
<string name="pref_expandNotify_sum">Expandir sempre a notificação para mostrar os botões de reprodução</string>
<string name="pref_persistNotify_title">Controlos de reprodução persistentes</string>
<string name="pref_persistNotify_sum">Manter controlos de notificação e ecrã de bloqueio ao colocar a reprodução em pausa</string>
+ <string name="pref_showDownloadReport_title">Mostrar relatório de erros</string>
+ <string name="pref_showDownloadReport_sum">Se a transferência falhar, gera um relatório que mostra os detalhes do erro</string>
<string name="pref_expand_notify_unsupport_toast">As versões Android anteriores à 4.1 não possuem suporte à expansão de notificações</string>
<string name="pref_queueAddToFront_sum">Colocar novos episódios no inicio da fila</string>
<string name="pref_queueAddToFront_title">Novos episódios no inicio</string>
<string name="pref_smart_mark_as_played_disabled">Desativada</string>
+ <string name="pref_image_cache_size_title">Cache de imagens</string>
+ <string name="pref_image_cache_size_sum">O tamanho da cache de imagens</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Ativar flattr automático</string>
<string name="auto_flattr_after_percent">Flattr de episódios ao atingir %d porcento de reprodução</string>
@@ -312,6 +346,7 @@
<string name="opml_import_error_dir_empty">O diretório de importação está vazio</string>
<string name="select_all_label">Marcar tudo</string>
<string name="deselect_all_label">Desmarcar tudo</string>
+ <string name="select_options_label">Selecionar...</string>
<string name="choose_file_from_filesystem">Do sistema local de ficheiros</string>
<string name="choose_file_from_external_application">Utilizar aplicação externa</string>
<string name="opml_export_label">Exportação OPML</string>
@@ -326,9 +361,21 @@
<string name="sleep_timer_label">Temporizador</string>
<string name="time_left_label">Tempo restante:\u0020</string>
<string name="time_dialog_invalid_input">Tem que introduzir um número inteiro</string>
- <string name="time_unit_seconds">segundos</string>
- <string name="time_unit_minutes">minutos</string>
- <string name="time_unit_hours">horas</string>
+ <string name="time_seconds">segundos</string>
+ <string name="time_minutes">minutos</string>
+ <string name="time_hours">horas</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">1 segundo</item>
+ <item quantity="other">%d segundos</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 minuto</item>
+ <item quantity="other">%d minutos</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 hora</item>
+ <item quantity="other">%d horas</item>
+ </plurals>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">Categorias</string>
<string name="gpodnet_toplist_header">Melhores</string>
@@ -373,6 +420,7 @@
<string name="pref_pausePlaybackForFocusLoss_title">Pausa nas interrupções</string>
<string name="pref_resumeAfterCall_sum">Continuar reprodução ao terminar a chamada</string>
<string name="pref_resumeAfterCall_title">Continuar após a chamada</string>
+ <string name="pref_restart_required">Tem que reiniciar o AntennaPod para aplicar as alterações</string>
<!--Online feed view-->
<string name="subscribe_label">Subscrever</string>
<string name="subscribed_label">Subscrito</string>
@@ -399,7 +447,29 @@
<!--Feed information screen-->
<string name="authentication_label">Autenticação</string>
<string name="authentication_descr">Altere o seu nome de utilizador e senha para este podcast e seus episódios</string>
+ <!--Progress information-->
+ <string name="progress_upgrading_database">Atualizando base de dados</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importar subscrições de aplicações single-purpose...</string>
<string name="search_itunes_label">Procurar no iTunes</string>
+ <string name="select_label"><b>Selecionar...</b></string>
+ <string name="all_label">Todos</string>
+ <string name="selected_all_label">Marcar todos os episódios</string>
+ <string name="none_label">Nenhum</string>
+ <string name="deselected_all_label">Desmarcar todos os episódios</string>
+ <string name="played_label">Reproduzidos</string>
+ <string name="selected_played_label">Selecionar episódios reproduzidos</string>
+ <string name="unplayed_label">Não reproduzidos</string>
+ <string name="selected_unplayed_label">Selecionar episódios não reproduzidos</string>
+ <string name="downloaded_label">Transferidos</string>
+ <string name="selected_downloaded_label">Selecionar episódios transferidos</string>
+ <string name="not_downloaded_label">Não transferidos</string>
+ <string name="selected_not_downloaded_label">Selecionar episódios não transferidos</string>
+ <string name="sort_title"><b>Ordenar por...</b></string>
+ <string name="sort_title_a_z">Título (A \u2192 Z)</string>
+ <string name="sort_title_z_a">Título (Z \u2192 A)</string>
+ <string name="sort_date_new_old">Data (Recente \u2192 Antiga)</string>
+ <string name="sort_date_old_new">Data (Antiga \u2192 Recente)</string>
+ <string name="sort_duration_short_long">Duração (Curta \u2192 Longa)</string>
+ <string name="sort_duration_long_short">Duração (Longa \u2192 Curta)</string>
</resources>
diff --git a/core/src/main/res/values-ro-rRO/strings.xml b/core/src/main/res/values-ro-rRO/strings.xml
index 390f50767..ce82dec1e 100644
--- a/core/src/main/res/values-ro-rRO/strings.xml
+++ b/core/src/main/res/values-ro-rRO/strings.xml
@@ -50,7 +50,6 @@
<string name="mark_all_read_label">Marchează toate ca citite</string>
<string name="show_info_label">Arată informații</string>
<string name="share_link_label">Împarte adresă website</string>
- <string name="share_source_label">Împarte adresă feed</string>
<string name="feed_delete_confirmation_msg">Confirmați ștergerea feedului și a TUTUROR episoadelor pe care le-ați descărcat.</string>
<!--actions on feeditems-->
<string name="download_label">Descarcă</string>
@@ -140,8 +139,6 @@
<string name="pref_followQueue_sum">Sari la următorul element din coadă cand se termină ascultarea</string>
<string name="playback_pref">Ascultare</string>
<string name="network_pref">Rețea</string>
- <string name="pref_autoUpdateIntervall_title">Interval actualizare</string>
- <string name="pref_autoUpdateIntervall_sum">Specifică un interval în care feedurile sunt actualizate automat sau oprește funcția</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Descarcă fișiere media doar pe WiFi</string>
<string name="pref_followQueue_title">Ascultare continuă</string>
<string name="pref_downloadMediaOnWifiOnly_title">Descărcare media pe WiFi</string>
@@ -237,5 +234,6 @@
<string name="downloading_label">Se descarcă...</string>
<!--Content descriptions for image buttons-->
<!--Feed information screen-->
+ <!--Progress information-->
<!--AntennaPodSP-->
</resources>
diff --git a/core/src/main/res/values-ru/strings.xml b/core/src/main/res/values-ru/strings.xml
index ae10b314f..c2c27484a 100644
--- a/core/src/main/res/values-ru/strings.xml
+++ b/core/src/main/res/values-ru/strings.xml
@@ -17,7 +17,7 @@
<string name="downloads_completed_label">Завершено</string>
<string name="downloads_log_label">Журнал</string>
<string name="cancel_download_label">Отменить загрузку</string>
- <string name="playback_history_label">История воспроизведения</string>
+ <string name="playback_history_label">Журнал</string>
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_auth_label">Войти на gpodder.net</string>
<!--New episodes fragment-->
@@ -26,6 +26,12 @@
<!--Main activity-->
<string name="drawer_open">Открыть меню</string>
<string name="drawer_close">Закрыть меню</string>
+ <string name="drawer_feed_order_unplayed_episodes">Сортировать по количеству</string>
+ <string name="drawer_feed_order_alphabetical">Сортировать по алфавиту</string>
+ <string name="drawer_feed_counter_new_unplayed">Количество новых и непрослушанных выпусков</string>
+ <string name="drawer_feed_counter_new">Количество новых выпусков</string>
+ <string name="drawer_feed_counter_unplayed">Количество непрослушанных выпусков</string>
+ <string name="drawer_feed_counter_none">Ничего</string>
<!--Webview actions-->
<string name="open_in_browser_label">Открыть в браузере</string>
<string name="copy_url_label">Скопировать ссылку</string>
@@ -37,8 +43,11 @@
<!--Other-->
<string name="confirm_label">Подтвердить</string>
<string name="cancel_label">Отмена</string>
+ <string name="yes">Да</string>
+ <string name="no">Нет</string>
<string name="author_label">Автор</string>
<string name="language_label">Язык</string>
+ <string name="url_label">Адрес</string>
<string name="podcast_settings_label">Настройки</string>
<string name="cover_label">Обложка</string>
<string name="error_label">Ошибка</string>
@@ -58,6 +67,7 @@
<string name="close_label">Закрыть</string>
<string name="retry_label">Повторить</string>
<string name="auto_download_label">Добавить в автозагрузки</string>
+ <string name="auto_download_apply_to_items_title">Применить к предыдущим выпускам</string>
<string name="parallel_downloads_suffix">\u0020одновременных загрузок</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">URL канала</string>
@@ -73,8 +83,8 @@
<string name="mark_all_read_feed_confirmation_msg">Подтвердите, что хотите пометить все эпизоды в этом канале как прослушанные.</string>
<string name="show_info_label">Показать информацию</string>
<string name="remove_feed_label">Удалить подкаст</string>
- <string name="share_link_label">Поделиться ссылкой на сайт</string>
- <string name="share_source_label">Ссылка на канал</string>
+ <string name="share_label">Поделиться...</string>
+ <string name="share_link_label">Поделиться ссылкой</string>
<string name="feed_delete_confirmation_msg">Подтвердите удаление канала и всех выпусков, загруженных с этого канала.</string>
<string name="feed_remover_msg">Удаление канала</string>
<string name="load_complete_feed">Обновить весь канал</string>
@@ -85,11 +95,12 @@
<string name="stop_label">Остановить</string>
<string name="stream_label">Воспроизвести из сети</string>
<string name="remove_label">Удалить</string>
- <string name="remove_episode_lable">Удалить</string>
- <string name="mark_read_label">Отметить как прочитанное</string>
- <string name="mark_unread_label">Отметить как непрочитанное</string>
+ <string name="remove_episode_lable">Удалить выпуск</string>
+ <string name="mark_read_label">Отметить как прослушанное</string>
<string name="marked_as_read_label">Помечено как прослушанное</string>
+ <string name="mark_unread_label">Отметить как непрослушанное</string>
<string name="add_to_queue_label">Добавить в очередь</string>
+ <string name="added_to_queue_label">Добавлено в очередь</string>
<string name="remove_from_queue_label">Удалить из очереди</string>
<string name="visit_website_label">Посетить сайт</string>
<string name="support_label">Поддержать через Flattr</string>
@@ -202,8 +213,6 @@
<string name="pref_auto_delete_title">Автоматическое удаление</string>
<string name="playback_pref">Воспроизведение</string>
<string name="network_pref">Сеть</string>
- <string name="pref_autoUpdateIntervall_title">Интервал обновлений</string>
- <string name="pref_autoUpdateIntervall_sum">Укажите интервал через который каналы обновляются автоматически, или отключите его</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Загружать файлы только через Wi-Fi</string>
<string name="pref_followQueue_title">Непрерывное воспроизведение</string>
<string name="pref_downloadMediaOnWifiOnly_title">Загрузка по Wi-Fi</string>
@@ -270,7 +279,7 @@
<!--OPML import and export-->
<string name="opml_import_txtv_button_lable">OPML файлы позволяют перемещать ваши подкасты из одного менеджера подкастов в другой.</string>
<string name="opml_import_explanation_1">Укажите путь к файлу на устройстве</string>
- <string name="opml_import_explanation_2">Откройте OPML-файл с помощью внешних приложений: Dropbox, Google Drive или любой файловый менеджер.</string>
+ <string name="opml_import_explanation_2">Откройте OPML-файл с помощью внешних приложений: Dropbox, Google Drive или любого файлового менеджера.</string>
<string name="opml_import_explanation_3">Множество приложений умеют <i>открывать</i> OPML-файлы <i>в</i> AntennaPod, например: Google Mail, Dropbox, Google Drive и большинство файловых менеджеров.</string>
<string name="start_import_label">Начать импорт</string>
<string name="opml_import_label">Импорт OPML</string>
@@ -294,9 +303,6 @@
<string name="sleep_timer_label">Таймер сна</string>
<string name="time_left_label">Осталось времени:\u0020</string>
<string name="time_dialog_invalid_input">Неправильный ввод, время должно быть в виде числа</string>
- <string name="time_unit_seconds">с</string>
- <string name="time_unit_minutes">м</string>
- <string name="time_unit_hours">ч</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">Категории</string>
<string name="gpodnet_toplist_header">Лучшее</string>
@@ -365,6 +371,7 @@
<!--Feed information screen-->
<string name="authentication_label">Авторизация</string>
<string name="authentication_descr">Изменить имя пользователя и пароль для этого подкаста и его выпусков.</string>
+ <!--Progress information-->
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Импорт подписок из одноцелевых приложений…</string>
<string name="search_itunes_label">Поиск в iTunes</string>
diff --git a/core/src/main/res/values-sv-rSE/strings.xml b/core/src/main/res/values-sv-rSE/strings.xml
index c0c49ca7d..ad970e521 100644
--- a/core/src/main/res/values-sv-rSE/strings.xml
+++ b/core/src/main/res/values-sv-rSE/strings.xml
@@ -3,41 +3,49 @@
<!--Activitiy and fragment titles-->
<string name="app_name">AntennaPod</string>
<string name="feeds_label">Flöden</string>
- <string name="add_feed_label">Lägg till podcast</string>
+ <string name="add_feed_label">Lägg till Podcast</string>
<string name="podcasts_label">PODCASTS</string>
<string name="episodes_label">EPISODER</string>
- <string name="new_episodes_label">Nya episoder</string>
- <string name="all_episodes_label">Alla episoder</string>
+ <string name="new_episodes_label">Nya Episoder</string>
+ <string name="all_episodes_label">Alla Episoder</string>
<string name="new_label">Ny</string>
<string name="waiting_list_label">Väntelista</string>
<string name="settings_label">Inställningar</string>
- <string name="add_new_feed_label">Lägg till podcast</string>
+ <string name="add_new_feed_label">Lägg till Podcast</string>
<string name="downloads_label">Nedladdningar</string>
<string name="downloads_running_label">Körs</string>
<string name="downloads_completed_label">Färdiga</string>
<string name="downloads_log_label">Logg</string>
- <string name="cancel_download_label">Avbryt nedladdning</string>
+ <string name="cancel_download_label">Avbryt\nNedladdning</string>
<string name="playback_history_label">Uppspelningshistorik</string>
<string name="gpodnet_main_label">gpodder.net</string>
- <string name="gpodnet_auth_label">gpodder.net login</string>
+ <string name="gpodnet_auth_label">Inloggning till gpodder.net</string>
<!--New episodes fragment-->
<string name="recently_published_episodes_label">Nyligen publicerade</string>
- <string name="episode_filter_label">Visa bara nya episoder</string>
+ <string name="episode_filter_label">Visa bara nya Episoder</string>
<!--Main activity-->
<string name="drawer_open">Öppna meny</string>
<string name="drawer_close">Stäng meny</string>
<string name="drawer_preferences">Lådinställningar</string>
+ <string name="drawer_feed_order_unplayed_episodes">Sortera efter räknare</string>
+ <string name="drawer_feed_order_alphabetical">Sortera alfabetiskt</string>
+ <string name="drawer_feed_counter_new_unplayed">Antal nya och ospelade episoder</string>
+ <string name="drawer_feed_counter_new">Antal nya episoder</string>
+ <string name="drawer_feed_counter_unplayed">Antal ospelade episoder</string>
+ <string name="drawer_feed_counter_none">Inga</string>
<!--Webview actions-->
- <string name="open_in_browser_label">Öppna i webbläsare</string>
+ <string name="open_in_browser_label">Öppna i Webbläsare</string>
<string name="copy_url_label">Kopiera URL</string>
<string name="share_url_label">Dela URL</string>
- <string name="copied_url_msg">Kopierade URL till clipboard.</string>
- <string name="go_to_position_label">Gå hit</string>
+ <string name="copied_url_msg">Kopierade URL:en till Urklipp</string>
+ <string name="go_to_position_label">Gå till denna Position</string>
<!--Playback history-->
- <string name="clear_history_label">Rensa historik</string>
+ <string name="clear_history_label">Rensa Historiken</string>
<!--Other-->
<string name="confirm_label">Bekräfta</string>
<string name="cancel_label">Avbryt</string>
+ <string name="yes">Ja</string>
+ <string name="no">Nej</string>
<string name="author_label">Skapare</string>
<string name="language_label">Språk</string>
<string name="url_label">URL</string>
@@ -60,27 +68,39 @@
<string name="close_label">Stäng</string>
<string name="retry_label">Försök igen</string>
<string name="auto_download_label">Inkludera i automatiska nedladdningar</string>
+ <string name="auto_download_apply_to_items_title">Applicera på Föregående Episoder</string>
+ <string name="auto_download_apply_to_items_message">Den nya inställningen <i>Automatisk Nedladdning</i> kommer automatiskt att appliceras på nya episoder.\nVill du också applicera det på tidigare episoder?</string>
+ <string name="auto_delete_label">Ta automatiskt bort episod\n(åsidosätter global standardinställning)</string>
<string name="parallel_downloads_suffix">\u0020parallella nedladdningar</string>
+ <string name="feed_auto_download_global">Global</string>
+ <string name="feed_auto_download_always">Alltid</string>
+ <string name="feed_auto_download_never">Aldrig</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">Flödets URL</string>
<string name="etxtFeedurlHint">URL till flöde eller webbsida</string>
<string name="txtvfeedurl_label">Lägg till podcast via URL</string>
- <string name="podcastdirectories_label">Hitta podcast i mapp</string>
+ <string name="podcastdirectories_label">Hitta Podcast i Biblioteket</string>
<string name="podcastdirectories_descr">Du kan söka efter podcasts baserat på namn, kategori eller populäritet med tjänsten gpodder.net eller på iTunes Store.</string>
<string name="browse_gpoddernet_label">Bläddra på gpodder.net</string>
<!--Actions on feeds-->
<string name="mark_all_read_label">Markera alla som spelade</string>
- <string name="mark_all_read_msg">Markera alla episoder som spelade</string>
+ <string name="mark_all_read_msg">Markera alla Episoder som spelade</string>
<string name="mark_all_read_confirmation_msg">Bekräfta att du verkligen vill markera alla episoder som spelade.</string>
<string name="mark_all_read_feed_confirmation_msg">Bekräfta att du verkligen vill markera alla episoder i detta flöde som spelade.</string>
+ <string name="mark_all_seen_label">Markera alla som sedda</string>
<string name="show_info_label">Visa information</string>
- <string name="remove_feed_label">Ta bort podcast</string>
- <string name="share_link_label">Dela hemsidans länk</string>
- <string name="share_source_label">Dela flödeslänk</string>
+ <string name="remove_feed_label">Ta bort Podcast</string>
+ <string name="share_label">Dela...</string>
+ <string name="share_link_label">Dela Länk</string>
+ <string name="share_link_with_position_label">Dela Länk med Position</string>
+ <string name="share_feed_url_label">Dela Flödets URL</string>
+ <string name="share_item_url_label">Dela Episodens URL</string>
+ <string name="share_item_url_with_position_label">Dela Episodens URL med Position</string>
<string name="feed_delete_confirmation_msg">Bekräfta att du vill ta bort denna feed och ALLA avsnitt av denna feed som du har hämtat.</string>
- <string name="feed_remover_msg">Tar bort flöde</string>
- <string name="load_complete_feed">Uppdatera hela flödet</string>
- <string name="hide_episodes_title">Dölj episoder</string>
+ <string name="feed_remover_msg">Tar bort Flöde</string>
+ <string name="load_complete_feed">Uppdatera hela Flödet</string>
+ <string name="hide_episodes_title">Dölj Episoder</string>
+ <string name="episode_actions">Applicera åtgärder</string>
<string name="hide_unplayed_episodes_label">Ospelade</string>
<string name="hide_paused_episodes_label">Pausad</string>
<string name="hide_played_episodes_label">Spelad</string>
@@ -97,10 +117,10 @@
<string name="stop_label">Stopp</string>
<string name="stream_label">Strömma</string>
<string name="remove_label">Ta bort</string>
- <string name="remove_episode_lable">Ta bort episod</string>
+ <string name="remove_episode_lable">Ta bort Episod</string>
<string name="mark_read_label">Markera som spelad</string>
- <string name="mark_unread_label">Markera som ospelad</string>
<string name="marked_as_read_label">Markera som spelad</string>
+ <string name="mark_unread_label">Markera som ospelad</string>
<string name="add_to_queue_label">Lägg till i kön</string>
<string name="added_to_queue_label">Lägg till i Kö</string>
<string name="remove_from_queue_label">Ta bort från Kön</string>
@@ -109,24 +129,24 @@
<string name="enqueue_all_new">Lägg till alla i kön</string>
<string name="download_all">Ladda ner alla</string>
<string name="skip_episode_label">Hoppa över episod</string>
- <string name="activate_auto_download">Aktivera automatisk nedladdning</string>
- <string name="deactivate_auto_download">Avaktivera automatisk nedladdning</string>
- <string name="reset_position">Nollställ uppspelningsposition</string>
+ <string name="activate_auto_download">Aktivera Automatisk Nedladdning</string>
+ <string name="deactivate_auto_download">Avaktivera Automatisk Nedladdning</string>
+ <string name="reset_position">Nollställ Uppspelningspositionen</string>
<!--Download messages and labels-->
<string name="download_successful">lyckades</string>
<string name="download_failed">misslyckades</string>
<string name="download_pending">Avvaktar nedladdning</string>
<string name="download_running">Nedladdning pågår</string>
- <string name="download_error_device_not_found">Lagringsenhet hittades inte</string>
- <string name="download_error_insufficient_space">Otillräckligt utrymme</string>
+ <string name="download_error_device_not_found">Hittade ingen lagringsenhet</string>
+ <string name="download_error_insufficient_space">Otillräckligt Utrymme</string>
<string name="download_error_file_error">Filfel</string>
<string name="download_error_http_data_error">HTTP data fel</string>
<string name="download_error_error_unknown">Okänt fel</string>
<string name="download_error_parser_exception">Tolkningsfel</string>
- <string name="download_error_unsupported_type">Flödestyp utan stöd</string>
+ <string name="download_error_unsupported_type">Flödestypen stöds inte</string>
<string name="download_error_connection_error">Anslutningsfel</string>
- <string name="download_error_unknown_host">Okänd värd</string>
- <string name="download_error_unauthorized">Autentiseringsproblem</string>
+ <string name="download_error_unknown_host">Okänd Värd</string>
+ <string name="download_error_unauthorized">Autentiseringsfel</string>
<string name="cancel_all_downloads_label">Avbryt alla nedladdningar</string>
<string name="download_canceled_msg">Nedladdning avbruten</string>
<string name="download_canceled_autodownload_enabled_msg">Nedladdning avbruten\nStängde av <i>Automatisk nedladdning</i> för denna sak</string>
@@ -134,13 +154,13 @@
<string name="download_report_content_title">Nedladdningsrapport</string>
<string name="download_error_malformed_url">Felaktig webbadress</string>
<string name="download_error_io_error">IO fel</string>
- <string name="download_error_request_error">Request fel</string>
- <string name="download_error_db_access">Ingen tillgång till databasen</string>
+ <string name="download_error_request_error">Förfrågningsfel</string>
+ <string name="download_error_db_access">Åtkomstfel till databasen</string>
<string name="downloads_left">\u0020Nedladdningar kvar</string>
<string name="downloads_processing">Bearbetar nedladdningar</string>
<string name="download_notification_title">Laddar ner podcastdata</string>
<string name="download_report_content">%1$d nedladdningar lyckades, %2$d misslyckades</string>
- <string name="download_log_title_unknown">Okänd titel</string>
+ <string name="download_log_title_unknown">Okänd Titel</string>
<string name="download_type_feed">Flöde</string>
<string name="download_type_media">Mediafil</string>
<string name="download_type_image">Bild</string>
@@ -148,10 +168,10 @@
<string name="authentication_notification_title">Autentisering krävs</string>
<string name="authentication_notification_msg">Resursen du begärde kräver ett användarnamn och ett lösenord</string>
<string name="confirm_mobile_download_dialog_title">Bekräfta mobil nedladdning</string>
- <string name="confirm_mobile_download_dialog_message_not_in_queue">Nedladdning över mobil dataanslutning är avaktiverat i inställningarna.\n\nAktivera tillfälligt eller bara lägg till i kön?\n\n<small>Ditt val gäller i 10 minuter.</small></string>
- <string name="confirm_mobile_download_dialog_message">Nedladdning över mobil dataanslutning är avstängt i inställningarna.\n\nAktivera tillfälligt?\n\n<small>Ditt val gäller i 10 minuter.</small></string>
- <string name="confirm_mobile_download_dialog_only_add_to_queue">Lägg bara till i kön</string>
- <string name="confirm_mobile_download_dialog_enable_temporarily">Aktivera tillfälligt</string>
+ <string name="confirm_mobile_download_dialog_message_not_in_queue">Nedladdning över mobil dataanslutning är avaktiverat i inställningarna.\n\nDu kan välja att antingen bara lägga till episoden i kön eller att tillfälligt tillåta nedladdning.\n\n<small>Ditt val gäller i 10 minuter.</small></string>
+ <string name="confirm_mobile_download_dialog_message">Nedladdning över mobil dataanslutning är avaktiverat i inställningarna.\n\nVill du tillfälligt tillåta nedladdning?\n\n<small>Ditt val gäller i 10 minuter.</small></string>
+ <string name="confirm_mobile_download_dialog_only_add_to_queue">Köa</string>
+ <string name="confirm_mobile_download_dialog_enable_temporarily">Tillåt tillfälligt</string>
<!--Mediaplayer messages-->
<string name="player_error_msg">Fel! </string>
<string name="player_stopped_msg">Inget media spelar</string>
@@ -166,9 +186,9 @@
<string name="playbackservice_notification_title">Spelar podcast</string>
<string name="unknown_media_key">AntannaPod - Okänd mediaknapp: %1$d</string>
<!--Queue operations-->
- <string name="lock_queue">Lås kön</string>
- <string name="unlock_queue">Lås upp kön</string>
- <string name="clear_queue_label">Rensa kön</string>
+ <string name="lock_queue">Lås Kön</string>
+ <string name="unlock_queue">Lås upp Kön</string>
+ <string name="clear_queue_label">Rensa Kön</string>
<string name="undo">Ångra</string>
<string name="removed_from_queue">Föremålet avlägsnades</string>
<string name="move_to_top_label">Flytta längst upp</string>
@@ -229,37 +249,47 @@
<string name="pref_smart_mark_as_played_title">Smart markering av uppspelat innehåll</string>
<string name="playback_pref">Uppspelning</string>
<string name="network_pref">Nätverk </string>
- <string name="pref_autoUpdateIntervall_title">Uppdateringsintervall</string>
- <string name="pref_autoUpdateIntervall_sum">Ange ett intervall för att automatiskt uppdatera flödet eller avaktivera det</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">Uppdateringsintervall eller Tid på Dagen</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Ange ett intervall eller specifik tid på dagen för att uppdatera flödena automatisk.</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Du kan välja ett <i>intervall</i> som \"var 2 timmar\", en specifik <i>tid på dagen</i> som \"07:00\" eller <i>avaktivera</i> automatiska uppdateringar helt.\n\n<small>Notera: Uppdateringstiderna är inte exakta. Korta fördröjningar kan uppstå.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Avaktivera</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Sätt intervall</string>
+ <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Sätt Tid på Dagen</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Hämta mediefiler endast över WiFi</string>
- <string name="pref_followQueue_title">Kontinuerlig uppspelning</string>
+ <string name="pref_followQueue_title">Kontinuerlig Uppspelning</string>
<string name="pref_downloadMediaOnWifiOnly_title">WiFi nedladdning</string>
- <string name="pref_pauseOnHeadsetDisconnect_title">Hörlurar bortkopplade</string>
- <string name="pref_unpauseOnHeadsetReconnect_title">Hörlurar återanslutna</string>
- <string name="pref_mobileUpdate_title">Mobila uppdateringar</string>
+ <string name="pref_pauseOnHeadsetDisconnect_title">Hörlurar Bortkopplade</string>
+ <string name="pref_unpauseOnHeadsetReconnect_title">Hörlurar Återanslutna</string>
+ <string name="pref_mobileUpdate_title">Mobila Uppdateringar</string>
<string name="pref_mobileUpdate_sum">Tillåt uppdateringar via mobil dataanslutning</string>
<string name="refreshing_label">Uppdaterar</string>
<string name="flattr_settings_label">Flattr inställningar</string>
<string name="pref_flattr_auth_title">Flattr inloggning</string>
<string name="pref_flattr_auth_sum">För att Flattra saker direkt från appen, logga in på ditt Flattr-konto.</string>
- <string name="pref_flattr_this_app_title">Flattra den här appen</string>
+ <string name="pref_flattr_this_app_title">Flattra denna App</string>
<string name="pref_flattr_this_app_sum">Stöd utvecklingen av AntennaPod genom att flattra den. Tack!</string>
<string name="pref_revokeAccess_title">Återkalla åtkomst</string>
<string name="pref_revokeAccess_sum">Återkalla behörigheten till ditt Flattr-konto för denna app.</string>
<string name="pref_auto_flattr_title">Automatisk Flattring</string>
<string name="pref_auto_flattr_sum">Konfigurerar automatisk Flattring</string>
<string name="user_interface_label">Användargränssnitt</string>
- <string name="pref_set_theme_title">Välj tema</string>
- <string name="pref_nav_drawer_items_title">Ändra navigationslådan</string>
+ <string name="pref_set_theme_title">Välj Tema</string>
+ <string name="pref_nav_drawer_title">Anpassa Navigeringsrutan</string>
+ <string name="pref_nav_drawer_sum">Anpassa utseendet på navigeringsrutan.</string>
+ <string name="pref_nav_drawer_items_title">Välj saker i Navigeringsrutan</string>
<string name="pref_nav_drawer_items_sum">Ändra vilka saker som visas i navigationslådan.</string>
+ <string name="pref_nav_drawer_feed_order_title">Välj Prenumerationsordning</string>
+ <string name="pref_nav_drawer_feed_order_sum">Ändra ordningen på dina prenumerationer</string>
+ <string name="pref_nav_drawer_feed_counter_title">Välj Prenumerationsräknaren</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Ändra informationen som visas av prenumerationsräknaren</string>
<string name="pref_set_theme_sum">Ändra utseendet på AntennaPod.</string>
- <string name="pref_automatic_download_title">Automatisk nedladdning</string>
+ <string name="pref_automatic_download_title">Automatisk Nedladdning</string>
<string name="pref_automatic_download_sum">Konfigurera automatisk nedladdning av episoder.</string>
<string name="pref_autodl_wifi_filter_title">Aktivera WiFi filtrering</string>
<string name="pref_autodl_wifi_filter_sum">Tillåt automatisk nedladdning endast för utvalda WiFi-nätverk.</string>
<string name="pref_automatic_download_on_battery_title">Nedladdning vid batteridrift</string>
<string name="pref_automatic_download_on_battery_sum">Tillåt automatisk nedladdning när batteriet inte laddas</string>
- <string name="pref_parallel_downloads_title">Parallella nedladdningar</string>
+ <string name="pref_parallel_downloads_title">Parallella Nedladdningar</string>
<string name="pref_episode_cache_title">Episodcache</string>
<string name="pref_theme_title_light">Ljust</string>
<string name="pref_theme_title_dark">Mörkt</string>
@@ -281,12 +311,16 @@
<string name="pref_gpodnet_sethostname_use_default_host">Använd standardvärden</string>
<string name="pref_expandNotify_title">Expandera notifieringar</string>
<string name="pref_expandNotify_sum">Expandera alltid notifieringen för att visa uppspelningskontrollerna.</string>
- <string name="pref_persistNotify_title">Bestående uppspelningskontroller</string>
+ <string name="pref_persistNotify_title">Bestående Uppspelningskontroller</string>
<string name="pref_persistNotify_sum">Behåll notifiering och kontroller på låsskärmen när uppspelningen pausas.</string>
+ <string name="pref_showDownloadReport_title">Visa Nedladdningsrapport</string>
+ <string name="pref_showDownloadReport_sum">Visa en rapport med detaljer om felet när nedladdningar misslyckas.</string>
<string name="pref_expand_notify_unsupport_toast">Androidversioner före 4.1 har inte stöd för expanderade notifieringar.</string>
<string name="pref_queueAddToFront_sum">Lägg till episoder först i kön.</string>
- <string name="pref_queueAddToFront_title">Köa först.</string>
+ <string name="pref_queueAddToFront_title">Köa Först</string>
<string name="pref_smart_mark_as_played_disabled">Avaktiverad</string>
+ <string name="pref_image_cache_size_title">Bildcachestorlek</string>
+ <string name="pref_image_cache_size_sum">Storleken på bildcachen på disken.</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Aktivera automatisk Flattring</string>
<string name="auto_flattr_after_percent">Flattra episoden så snart %d procent har spelats</string>
@@ -305,19 +339,20 @@
<string name="opml_import_explanation_2">Använd en extern applikation som Dropbox, Google Drive eller ditt favoritval av filhanterare för att öppna en OPML fil.</string>
<string name="opml_import_explanation_3">Flera applikationer som Google Mail, Dropbox, Google Drive och de flesta filhanterare kan <i>öppna</i> OPML filer <i>med</i> AntennaPod.</string>
<string name="start_import_label">Påbörja importering</string>
- <string name="opml_import_label">Importera OPML-fil</string>
+ <string name="opml_import_label">OPML Importering</string>
<string name="opml_directory_error">FEL! </string>
<string name="reading_opml_label">Läser OPML-fil</string>
<string name="opml_reader_error">Ett fel har skett vid iläsning av opml dokumentet:</string>
<string name="opml_import_error_dir_empty">Katalogen är tom.</string>
<string name="select_all_label">Välj alla</string>
<string name="deselect_all_label">Avmarkera alla</string>
+ <string name="select_options_label">Välj ...</string>
<string name="choose_file_from_filesystem">Från lokalt filsystem</string>
<string name="choose_file_from_external_application">Använd extern applikation</string>
<string name="opml_export_label">OPML export</string>
<string name="exporting_label">Exporterar...</string>
<string name="export_error_label">Exporteringsfel</string>
- <string name="opml_export_success_title">OPML export lyckades</string>
+ <string name="opml_export_success_title">OPML Exportering lyckades.</string>
<string name="opml_export_success_sum">.opml filen skrevs till:\u0020</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Ställ in sömntimer</string>
@@ -326,9 +361,21 @@
<string name="sleep_timer_label">Sömntimer</string>
<string name="time_left_label">Återstående tid:\u0020</string>
<string name="time_dialog_invalid_input">Ogiltigt tal, tiden måste vara ett heltal</string>
- <string name="time_unit_seconds">sekunder</string>
- <string name="time_unit_minutes">minuter</string>
- <string name="time_unit_hours">timmar</string>
+ <string name="time_seconds">sekunder</string>
+ <string name="time_minutes">minuter</string>
+ <string name="time_hours">timmar</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">1 sekund</item>
+ <item quantity="other">%d sekunder</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 minut</item>
+ <item quantity="other">%d minuter</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 timme</item>
+ <item quantity="other">%d timmar</item>
+ </plurals>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">KATEGORIER</string>
<string name="gpodnet_toplist_header">BÄSTA PODCASTS</string>
@@ -360,7 +407,7 @@
<!--Directory chooser-->
<string name="selected_folder_label">Vald mapp:</string>
<string name="create_folder_label">Skapa mapp</string>
- <string name="choose_data_directory">Välj mapp</string>
+ <string name="choose_data_directory">Välj Datakatalog</string>
<string name="create_folder_msg">Skapa ny mapp med namnet \"%1$s\"?</string>
<string name="create_folder_success">Skapade ny mapp</string>
<string name="create_folder_error_no_write_access">Kan inte skriva till den här mappen</string>
@@ -370,9 +417,10 @@
<string name="folder_not_empty_dialog_msg">Den mapp du har valt är inte tom. Filer kommer att placeras direkt i denna mapp. Fortsätt ändå?</string>
<string name="set_to_default_folder">Välj standardmapp</string>
<string name="pref_pausePlaybackForFocusLoss_sum">Pausa uppspelning istället för att sänka volymen när en annan app vill spela ljud</string>
- <string name="pref_pausePlaybackForFocusLoss_title">Pausa för avbrott</string>
+ <string name="pref_pausePlaybackForFocusLoss_title">Pausa för Avbrott</string>
<string name="pref_resumeAfterCall_sum">Återuppta uppspelning när ett telefonsamtal avslutas</string>
- <string name="pref_resumeAfterCall_title">Återuppta efter samtal</string>
+ <string name="pref_resumeAfterCall_title">Fortsätt efter Samtal</string>
+ <string name="pref_restart_required">AntennaPod behöver startas om för att denna inställning ska gälla.</string>
<!--Online feed view-->
<string name="subscribe_label">Prenumerera</string>
<string name="subscribed_label">Prenumererar</string>
@@ -399,7 +447,29 @@
<!--Feed information screen-->
<string name="authentication_label">Autentisering</string>
<string name="authentication_descr">Byt ditt användarnamn och lösenord för den här podcasten och dess episoder.</string>
+ <!--Progress information-->
+ <string name="progress_upgrading_database">Uppgraderar databasen</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importerar prenumerationer från appar gjorda för ett enda syfte...</string>
<string name="search_itunes_label">Leta i iTunes</string>
+ <string name="select_label"><b>Välj ...</b></string>
+ <string name="all_label">Alla</string>
+ <string name="selected_all_label">Välj alla Episoder</string>
+ <string name="none_label">Inga</string>
+ <string name="deselected_all_label">Avmarkera alla Episoder</string>
+ <string name="played_label">Spelade</string>
+ <string name="selected_played_label">Valde spelade Episoder</string>
+ <string name="unplayed_label">Ospelade</string>
+ <string name="selected_unplayed_label">Valde ospelade Episoder</string>
+ <string name="downloaded_label">Nedladdade</string>
+ <string name="selected_downloaded_label">Valde nedladdade Episoder</string>
+ <string name="not_downloaded_label">Ej nedladdade</string>
+ <string name="selected_not_downloaded_label">Valde ej nedladdade Episoder</string>
+ <string name="sort_title"><b>Sortera efter ...</b></string>
+ <string name="sort_title_a_z">Titel (A \u2192 Ö)</string>
+ <string name="sort_title_z_a">Titel (Ö \u2192 A)</string>
+ <string name="sort_date_new_old">Datum (Ny \u2192 Gammal)</string>
+ <string name="sort_date_old_new">Datum (Gammal \u2192 Ny)</string>
+ <string name="sort_duration_short_long">Längd (Kort \u2192 Lång)</string>
+ <string name="sort_duration_long_short">Längd (Lång \u2192 Kort)</string>
</resources>
diff --git a/core/src/main/res/values-tr/strings.xml b/core/src/main/res/values-tr/strings.xml
index e83c9b48e..d202bc67c 100644
--- a/core/src/main/res/values-tr/strings.xml
+++ b/core/src/main/res/values-tr/strings.xml
@@ -26,6 +26,7 @@
<!--Main activity-->
<string name="drawer_open">Münüyü aç</string>
<string name="drawer_close">Menüyü kapat</string>
+ <string name="drawer_preferences">Çekmece Seçenekleri</string>
<!--Webview actions-->
<string name="open_in_browser_label">Tarayıcıda aç</string>
<string name="copy_url_label">URL\'yi kopyala</string>
@@ -39,6 +40,7 @@
<string name="cancel_label">İptal</string>
<string name="author_label">Yayıncı</string>
<string name="language_label">Dil</string>
+ <string name="url_label">URL</string>
<string name="podcast_settings_label">Ayarlar</string>
<string name="cover_label">Resim</string>
<string name="error_label">Hata</string>
@@ -67,17 +69,26 @@
<string name="podcastdirectories_descr">gdpodder.net dizininde yeni cep yayınlarını isme, kategoriye veya popülerliğe göre arayabilirsiniz veya iTunes mağazasında arama yapabilirsiniz.</string>
<string name="browse_gpoddernet_label">gpodder.net\'e gözat</string>
<!--Actions on feeds-->
- <string name="mark_all_read_label">Hepsini okundu olarak işaretle</string>
- <string name="mark_all_read_msg">Tüm bölümler okundu olarak işaretlendi</string>
- <string name="mark_all_read_confirmation_msg">Lütfen tüm bölümleri okundu olarak işaretlemek istediğinizi onaylayın.</string>
- <string name="mark_all_read_feed_confirmation_msg">Lütfen bu besleme içindeki tüm bölümleri okundu olarak işaretlemek istediğinizi onaylayın.</string>
+ <string name="mark_all_read_label">Hepsini oynatıldı olarak işaretle</string>
+ <string name="mark_all_read_msg">Tüm bölümleri oynatıldı olarak işaretle</string>
+ <string name="mark_all_read_confirmation_msg">Lütfen tüm bölümleri oynatıldı olarak işaretlemek istediğinizi onaylayın.</string>
+ <string name="mark_all_read_feed_confirmation_msg">Lütfen bu besleme içindeki tüm bölümleri oynatıldı olarak işaretlemek istediğinizi onaylayın.</string>
<string name="show_info_label">Bilgiyi göster</string>
<string name="remove_feed_label">Cep yayını kaldır</string>
<string name="share_link_label">Web sayfası bağlantısı paylaş</string>
- <string name="share_source_label">Besleme bağlantısını paylaş</string>
<string name="feed_delete_confirmation_msg">Lütfen bu beslemeyi ve bu beslemeye ait indirilmiş BÜTÜN bölümleri silme isteğinizi onaylayın.</string>
<string name="feed_remover_msg">Besleme kaldırılıyor</string>
<string name="load_complete_feed">Tüm beslemeyi yenile</string>
+ <string name="hide_episodes_title">Bölümleri gizle</string>
+ <string name="hide_unplayed_episodes_label">Oynatılmadı</string>
+ <string name="hide_paused_episodes_label">Duraklatıldı</string>
+ <string name="hide_played_episodes_label">Oynatıldı</string>
+ <string name="hide_queued_episodes_label">Kuyrukta</string>
+ <string name="hide_not_queued_episodes_label">Kuyrukta değil</string>
+ <string name="hide_downloaded_episodes_label">İndirildi</string>
+ <string name="hide_not_downloaded_episodes_label">İndirilmedi</string>
+ <string name="filtered_label">Filtrelendi</string>
+ <string name="refresh_failed_msg">{fa-exclamation-circle} Son yenileme başarısız oldu</string>
<!--actions on feeditems-->
<string name="download_label">İndir</string>
<string name="play_label">Oynat</string>
@@ -86,16 +97,20 @@
<string name="stream_label">Akış</string>
<string name="remove_label">Kaldır</string>
<string name="remove_episode_lable">Bölümü kaldır</string>
- <string name="mark_read_label">Okundu olarak işaretle</string>
- <string name="mark_unread_label">Okunmadı olarak işaretle</string>
- <string name="marked_as_read_label">Okundu olarak işaretlendi</string>
+ <string name="mark_read_label">Oynatıldı olarak işaretle</string>
+ <string name="marked_as_read_label">Oynatıldı olarak işaretlendi</string>
+ <string name="mark_unread_label">Oynatılmadı olarak işaretle</string>
<string name="add_to_queue_label">Kuyruğa Ekle</string>
+ <string name="added_to_queue_label">Kuyruğa Eklendi</string>
<string name="remove_from_queue_label">Kuyruktan Kaldır</string>
<string name="visit_website_label">Siteyi Ziyaret Et</string>
<string name="support_label">Flattr ile destekle</string>
<string name="enqueue_all_new">Hepsini kuyruğa ekle</string>
<string name="download_all">Hepsini indir</string>
<string name="skip_episode_label">Bölümü atla</string>
+ <string name="activate_auto_download">Otomatik indirmeyi etkinleştir</string>
+ <string name="deactivate_auto_download">Otomatik indirmeyi devre dışı bırak</string>
+ <string name="reset_position">Çalme konumunu sıfırla</string>
<!--Download messages and labels-->
<string name="download_successful">başarılı</string>
<string name="download_failed">başarısız</string>
@@ -113,7 +128,9 @@
<string name="download_error_unauthorized">Yetkilendirme hatası</string>
<string name="cancel_all_downloads_label">Bütün indirmeleri iptal et</string>
<string name="download_canceled_msg">İndirme iptal edildi</string>
- <string name="download_report_title">İndirme tamamlandı</string>
+ <string name="download_canceled_autodownload_enabled_msg">İndirme iptal edildi\nBu öğe için <i>Otomatik İndirme</i> devre dışı</string>
+ <string name="download_report_title">İndirme hata(lar) ile tamamlandı</string>
+ <string name="download_report_content_title">İndirme raporu</string>
<string name="download_error_malformed_url">Bozuk URL</string>
<string name="download_error_io_error">G/Ç Hatası</string>
<string name="download_error_request_error">İstek hatası</string>
@@ -129,6 +146,11 @@
<string name="download_request_error_dialog_message_prefix">Dosyayı indirmeye çalışırken bir hata oluştu:\u0020</string>
<string name="authentication_notification_title">Yetkilendirme gerekiyor</string>
<string name="authentication_notification_msg">İstediğiniz kaynak kullanıcı adı ve şifre istiyor</string>
+ <string name="confirm_mobile_download_dialog_title">Mobil İndirmeyi Onaylayın</string>
+ <string name="confirm_mobile_download_dialog_message_not_in_queue">Mobil veri ile indirme ayarlarda devre dışıdır.\n\nGeçici olarak açılsın mı yoksa sadece kuyruğa mı eklensin?\n\n<small>Bu tercihiniz 10 dakika boyunca hatırlanacak.</small></string>
+ <string name="confirm_mobile_download_dialog_message">Mobil veri ile indirme ayarlarda devre dışıdır.\n\nGeçici olarak açılsın mı?\n\n<small>Bu tercihiniz 10 dakika boyunca hatırlanacak.</small></string>
+ <string name="confirm_mobile_download_dialog_only_add_to_queue">Sadece Kuyruğa ekle</string>
+ <string name="confirm_mobile_download_dialog_enable_temporarily">Geçici olarak aç</string>
<!--Mediaplayer messages-->
<string name="player_error_msg">Hata!</string>
<string name="player_stopped_msg">Çalınan medya yok</string>
@@ -143,6 +165,8 @@
<string name="playbackservice_notification_title">Cep yayını çalınıyor</string>
<string name="unknown_media_key">AntennaPod - Bilinmeyen medya anahtarı: %1$d</string>
<!--Queue operations-->
+ <string name="lock_queue">Kuyruğu kilitle</string>
+ <string name="unlock_queue">Kuyruğun kilidini aç</string>
<string name="clear_queue_label">Kuyruğu temizle</string>
<string name="undo">Geri al</string>
<string name="removed_from_queue">Öge kaldırıldı</string>
@@ -202,8 +226,6 @@
<string name="pref_auto_delete_title">Otomatik Silme</string>
<string name="playback_pref">Çalma</string>
<string name="network_pref">Ağ</string>
- <string name="pref_autoUpdateIntervall_title">Güncelleme aralığı</string>
- <string name="pref_autoUpdateIntervall_sum">Beslemeleri yenilemek için bir aralık belirtin veya devre dışı bırakın.</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Medya dosyalarını sadece kablosuz bağlantı üzerinden indir</string>
<string name="pref_followQueue_title">Devamlı çalma</string>
<string name="pref_downloadMediaOnWifiOnly_title">Kablosuz medya indirmesi</string>
@@ -246,8 +268,8 @@
<string name="pref_gpodnet_setlogin_information_sum">gpodder.net hesabınız için giriş bilgisini değiştirin.</string>
<string name="pref_playback_speed_title">Çalma hızları</string>
<string name="pref_playback_speed_sum">Değişken hızlı ses yürütmesi için kullanılabilir hızları özelleştirin</string>
- <string name="pref_seek_delta_title">Arama zamanı</string>
- <string name="pref_seek_delta_sum">Geri veya ileri sararken bu kadar saniye atla</string>
+ <string name="pref_fast_forward">İleri sarma süresi</string>
+ <string name="pref_rewind">Geri sarma süresi</string>
<string name="pref_gpodnet_sethostname_title">Sunucu ismini ayarla</string>
<string name="pref_gpodnet_sethostname_use_default_host">Varsayılan sunucuyu kullan</string>
<string name="pref_expandNotify_title">Bildirimi Genişlet</string>
@@ -257,6 +279,7 @@
<string name="pref_expand_notify_unsupport_toast">Android 4.1 öncesi sürümler genişletilmiş bildirimleri desteklememektedir.</string>
<string name="pref_queueAddToFront_sum">Yeni bölümleri kuyruğun önüne ekle.</string>
<string name="pref_queueAddToFront_title">Kuyruğun önüne ekle.</string>
+ <string name="pref_smart_mark_as_played_disabled">Devre dışı</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Otomatik Flattr\'lamayı etkinleştir</string>
<string name="auto_flattr_after_percent">Bölümün yüzde %d kısmı oynatıldığında Flattr\'la</string>
@@ -296,9 +319,6 @@
<string name="sleep_timer_label">Zamanlayıcı</string>
<string name="time_left_label">Kalan süre:\u0020</string>
<string name="time_dialog_invalid_input">Geçersiz giriş, zaman bir tam sayı olmalıdır</string>
- <string name="time_unit_seconds">saniye</string>
- <string name="time_unit_minutes">dakika</string>
- <string name="time_unit_hours">saat</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">KATEGORİLER</string>
<string name="gpodnet_toplist_header">POPÜLER CEP YAYINLARI</string>
@@ -341,6 +361,8 @@
<string name="set_to_default_folder">Vaysayılan dizini seç</string>
<string name="pref_pausePlaybackForFocusLoss_sum">Başka bir uygulama ses çalmak istediğinde sesi kısmak yerine yürütmeyi duraklat</string>
<string name="pref_pausePlaybackForFocusLoss_title">Kesintiler için duraklat</string>
+ <string name="pref_resumeAfterCall_sum">Bir telefon konuşması tamamlandıktan sonra çalmaya kaldığı yerden devam et</string>
+ <string name="pref_resumeAfterCall_title">Konuşmadan sonra devam et</string>
<!--Online feed view-->
<string name="subscribe_label">Üye ol</string>
<string name="subscribed_label">Üye olundu</string>
@@ -367,6 +389,7 @@
<!--Feed information screen-->
<string name="authentication_label">Yetkilendirme</string>
<string name="authentication_descr">Bu cep yayını ve içerdiği bölümler için kullanıcı adı şifreyi değiştir.</string>
+ <!--Progress information-->
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Üyelikler tek-amaçlı uygulamalardan içe aktarılıyor...</string>
<string name="search_itunes_label">iTunes\'da Arama</string>
diff --git a/core/src/main/res/values-uk-rUA/strings.xml b/core/src/main/res/values-uk-rUA/strings.xml
index b6cd8ca98..9715e40c3 100644
--- a/core/src/main/res/values-uk-rUA/strings.xml
+++ b/core/src/main/res/values-uk-rUA/strings.xml
@@ -27,6 +27,12 @@
<string name="drawer_open">Показати меню</string>
<string name="drawer_close">Сховати меню</string>
<string name="drawer_preferences">Настройки навігації</string>
+ <string name="drawer_feed_order_unplayed_episodes">Сортувати за лічильником</string>
+ <string name="drawer_feed_order_alphabetical">Сортування за абеткою</string>
+ <string name="drawer_feed_counter_new_unplayed">Кількість нових та непрослуханих епізодів</string>
+ <string name="drawer_feed_counter_new">Кількість нових епізодів</string>
+ <string name="drawer_feed_counter_unplayed">Кількість непрослуханих епізодів</string>
+ <string name="drawer_feed_counter_none">Жодних</string>
<!--Webview actions-->
<string name="open_in_browser_label">Відкрити в браузері</string>
<string name="copy_url_label">Копіювати URL</string>
@@ -38,6 +44,8 @@
<!--Other-->
<string name="confirm_label"> Підтвердити</string>
<string name="cancel_label">Скасувати</string>
+ <string name="yes">Так</string>
+ <string name="no">Ні</string>
<string name="author_label">Автор</string>
<string name="language_label">Мова</string>
<string name="url_label">URL</string>
@@ -60,7 +68,13 @@
<string name="close_label">Закрити</string>
<string name="retry_label">Повторити знову</string>
<string name="auto_download_label">Включити до автозавантаження</string>
+ <string name="auto_download_apply_to_items_title">Застосувати до попередніх епізодів</string>
+ <string name="auto_download_apply_to_items_message">Нове налаштування <i>Автозавантаження</i> буде автоматично застосоване до нових епізодів.\nБажаєте також застосувати його до попередніх епізодів?</string>
+ <string name="auto_delete_label">Автоматичне видалення епізода\n(перевизначити глобальне налаштування за замовчуванням)</string>
<string name="parallel_downloads_suffix">\u0020паралельні завантаження</string>
+ <string name="feed_auto_download_global">Для всіх</string>
+ <string name="feed_auto_download_always">Завжди</string>
+ <string name="feed_auto_download_never">Ніколи</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">Посилання на канал</string>
<string name="etxtFeedurlHint">URL канала або сайта</string>
@@ -73,14 +87,20 @@
<string name="mark_all_read_msg">Позначено всі епізоди як грані</string>
<string name="mark_all_read_confirmation_msg">Будь ласка, підтвердіть що ви бажаєте позначити всі епізоди як грані.</string>
<string name="mark_all_read_feed_confirmation_msg">Будь ласка, підтвердіть що ви бажаєте позначити всі епізоди цього канала як грані.</string>
+ <string name="mark_all_seen_label">Позначити всі як переглянуті</string>
<string name="show_info_label">Інформація</string>
<string name="remove_feed_label">Видалити подкаст</string>
+ <string name="share_label">Поділитись...</string>
<string name="share_link_label">Поділитися URL сайту</string>
- <string name="share_source_label">Поділитися URL каналу</string>
+ <string name="share_link_with_position_label">Поділитись посиланням на позицію</string>
+ <string name="share_feed_url_label">Поділитись посиланням на канал</string>
+ <string name="share_item_url_label">Поділитись посиланням на епізод</string>
+ <string name="share_item_url_with_position_label">Поділитись посиланням на епізод з позицією</string>
<string name="feed_delete_confirmation_msg">Ви впенені що хочете видаліти канал та всі завантажені епізоди</string>
<string name="feed_remover_msg">Удаляю канал</string>
<string name="load_complete_feed">Оновити канал цілком</string>
<string name="hide_episodes_title">Приховати епізоди</string>
+ <string name="episode_actions">Застосувати дії</string>
<string name="hide_unplayed_episodes_label">Неграні</string>
<string name="hide_paused_episodes_label">На паузі</string>
<string name="hide_played_episodes_label">Грані</string>
@@ -99,8 +119,8 @@
<string name="remove_label">Видалити</string>
<string name="remove_episode_lable">Видалити епізод</string>
<string name="mark_read_label">Позначити як граний</string>
- <string name="mark_unread_label">Позначити як не граний</string>
<string name="marked_as_read_label">Позначено як граний</string>
+ <string name="mark_unread_label">Позначити як не граний</string>
<string name="add_to_queue_label">Додати до черги</string>
<string name="added_to_queue_label">Додано до черги</string>
<string name="remove_from_queue_label">Видалити з черги</string>
@@ -229,8 +249,12 @@
<string name="pref_smart_mark_as_played_title">Розумне позначення граних епізодів</string>
<string name="playback_pref">Відтворення</string>
<string name="network_pref">Мережа</string>
- <string name="pref_autoUpdateIntervall_title">Частота оновлень</string>
- <string name="pref_autoUpdateIntervall_sum">Визначити інтервал часу для автооновлювання або відключити автооновлення</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">Частота оновлень або оновлення в зазначений час</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Визначити інтервал часу або визначити час щодня для автооновлення</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Можливо встановити <i>інтервал</i> як то \"кожні 2 години\", встановити <i>час щодня</i> як то \"7:00\" або <i>відклюсити</i> автоматичне оновлення взагалі.\n\n<small>Зверніть увагу: Час оновлення не є точним. Можливі короткі затримки.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Вимкнути</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Встановити інтервал</string>
+ <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Встановити час щодня</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Завантажувати тільки через Wifi</string>
<string name="pref_followQueue_title">Грати безперервно</string>
<string name="pref_downloadMediaOnWifiOnly_title">Завантаження через Wifi</string>
@@ -250,8 +274,14 @@
<string name="pref_auto_flattr_sum">Налаштування автоматичного заохочення авторів через сервіс flattr</string>
<string name="user_interface_label">Вигляд</string>
<string name="pref_set_theme_title">Обрати тему</string>
+ <string name="pref_nav_drawer_title">Налаштувати панель навігації</string>
+ <string name="pref_nav_drawer_sum">Налаштувати вигляд панелі навігації</string>
<string name="pref_nav_drawer_items_title">Змінити настройки навігації</string>
<string name="pref_nav_drawer_items_sum">Вибрати елементи для використання у навігації</string>
+ <string name="pref_nav_drawer_feed_order_title">Встановити порядок підписок</string>
+ <string name="pref_nav_drawer_feed_order_sum">Змінити порядок ваших підписок</string>
+ <string name="pref_nav_drawer_feed_counter_title">Встановити лічильник підписок</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Змінити інформацію яку відображає лічильник підписок</string>
<string name="pref_set_theme_sum">Змінити вигляд AntennaPod</string>
<string name="pref_automatic_download_title">Автоматичне завантаження</string>
<string name="pref_automatic_download_sum">Налаштування автоматичного завантаження епізодів</string>
@@ -283,10 +313,14 @@
<string name="pref_expandNotify_sum">Завжди розгортати повідомлення, щоб показати кнопки керування.</string>
<string name="pref_persistNotify_title">Завжди показувати елементи керування відтворенням</string>
<string name="pref_persistNotify_sum">Показувати повідомлення та елементи керування на lockscreen в режимі паузи.</string>
+ <string name="pref_showDownloadReport_title">Показати звіт про завантаження</string>
+ <string name="pref_showDownloadReport_sum">У разі помилки при завантаженні створити детальний звіт про помилку.</string>
<string name="pref_expand_notify_unsupport_toast">Android до версії 4.1 не підтримує розширені повідомлення.</string>
<string name="pref_queueAddToFront_sum">Додавати нові епізоди до початку черги.</string>
<string name="pref_queueAddToFront_title">Додавати в початок черги.</string>
<string name="pref_smart_mark_as_played_disabled">Вимкнено</string>
+ <string name="pref_image_cache_size_title">Розмір кеша зображень</string>
+ <string name="pref_image_cache_size_sum">Розмір дискового кеша для зображень.</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Включити автоматичне заохочення авторів через сервіс flattr</string>
<string name="auto_flattr_after_percent">Заохотити автора через Flattr щойно %d відсотків епізода було відтворено</string>
@@ -312,6 +346,7 @@
<string name="opml_import_error_dir_empty">Директорія імпорту пуста</string>
<string name="select_all_label">Обрати все</string>
<string name="deselect_all_label">Убрати виділення</string>
+ <string name="select_options_label">Обрати ...</string>
<string name="choose_file_from_filesystem">З локальної файлової системи</string>
<string name="choose_file_from_external_application">За допомогою додатка</string>
<string name="opml_export_label">OPML экспорт</string>
@@ -326,9 +361,24 @@
<string name="sleep_timer_label">Таймер сну</string>
<string name="time_left_label">Залишилось:\u0020</string>
<string name="time_dialog_invalid_input">Помилка вводу, час повинен бути цілим</string>
- <string name="time_unit_seconds">секунд</string>
- <string name="time_unit_minutes">хвилин</string>
- <string name="time_unit_hours">годин</string>
+ <string name="time_seconds">секунд</string>
+ <string name="time_minutes">хвилин</string>
+ <string name="time_hours">годин</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">1 секунда</item>
+ <item quantity="few">%d секунди</item>
+ <item quantity="other">%d секунд</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 хвилина</item>
+ <item quantity="few">%d хвилини</item>
+ <item quantity="other">%d хвилин</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 година</item>
+ <item quantity="few">%d години</item>
+ <item quantity="other">%d годин</item>
+ </plurals>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">КАТЕГОРІЇ</string>
<string name="gpodnet_toplist_header">ТОП ПОДКАСТІВ</string>
@@ -373,6 +423,7 @@
<string name="pref_pausePlaybackForFocusLoss_title">Пауза в разі переривання</string>
<string name="pref_resumeAfterCall_sum">Відновити відтворення після закінчення дзвінка</string>
<string name="pref_resumeAfterCall_title">Відновити після дзвінка</string>
+ <string name="pref_restart_required">Для застосування змін потрібно перезапустити AntennaPod</string>
<!--Online feed view-->
<string name="subscribe_label">Підписатися</string>
<string name="subscribed_label">Підписано</string>
@@ -399,7 +450,29 @@
<!--Feed information screen-->
<string name="authentication_label">Автентикація</string>
<string name="authentication_descr">Змінити ваші логін та пароль для подкаста та епізодів</string>
+ <!--Progress information-->
+ <string name="progress_upgrading_database">Оновлення бази даних</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Імпорт подкастів з інших програм...</string>
<string name="search_itunes_label">Пошук в iTunes</string>
+ <string name="select_label"><b>Обрати ...</b></string>
+ <string name="all_label">Всі</string>
+ <string name="selected_all_label">Обрано всі епізоди</string>
+ <string name="none_label">Жодного</string>
+ <string name="deselected_all_label">Жодного епізода обрано</string>
+ <string name="played_label">Переглянуті</string>
+ <string name="selected_played_label">Обрано переглянуті епізоди</string>
+ <string name="unplayed_label">Непереглянуті</string>
+ <string name="selected_unplayed_label">Обрано непереглянуті епізоди</string>
+ <string name="downloaded_label">Завантажені</string>
+ <string name="selected_downloaded_label">Обрано завантажені епізоди</string>
+ <string name="not_downloaded_label">Незавантажені</string>
+ <string name="selected_not_downloaded_label">Обрано незавантажені епізоди</string>
+ <string name="sort_title"><b>Сортувати за ...</b></string>
+ <string name="sort_title_a_z">Назва (А \u2192 Я)</string>
+ <string name="sort_title_z_a">Назва (Я \u2192 А)</string>
+ <string name="sort_date_new_old">Дата (Нові \u2192 Старі)</string>
+ <string name="sort_date_old_new">Дата (Старі \u2192 Нові)</string>
+ <string name="sort_duration_short_long">Тривалість (Короткі \u2192 Довгі)</string>
+ <string name="sort_duration_long_short">Тривалість (Довгі \u2192 Короткі)</string>
</resources>
diff --git a/core/src/main/res/values-zh-rCN/strings.xml b/core/src/main/res/values-zh-rCN/strings.xml
index 594249a31..332c8ed4c 100644
--- a/core/src/main/res/values-zh-rCN/strings.xml
+++ b/core/src/main/res/values-zh-rCN/strings.xml
@@ -72,7 +72,6 @@
<string name="show_info_label">查看信息</string>
<string name="remove_feed_label">删除播客</string>
<string name="share_link_label">分享网站链接</string>
- <string name="share_source_label">分享订阅链接</string>
<string name="feed_delete_confirmation_msg">确认要删除这些订阅吗? 该订阅所有已经下载的曲目将一并删除. </string>
<string name="feed_remover_msg">删除订阅</string>
<string name="load_complete_feed">刷新全部订阅</string>
@@ -199,8 +198,6 @@
<string name="pref_auto_delete_title">自动删除</string>
<string name="playback_pref">播放</string>
<string name="network_pref">网络</string>
- <string name="pref_autoUpdateIntervall_title">更新周期</string>
- <string name="pref_autoUpdateIntervall_sum">设置订阅自动刷新周期</string>
<string name="pref_downloadMediaOnWifiOnly_sum">仅在 WIFI 情况下载媒体文件</string>
<string name="pref_followQueue_title">连续播放</string>
<string name="pref_downloadMediaOnWifiOnly_title">仅在 WIFI 情况下载</string>
@@ -292,9 +289,6 @@
<string name="sleep_timer_label">休眠计时器</string>
<string name="time_left_label">计时剩余:\u0020</string>
<string name="time_dialog_invalid_input">无效的输入, 时间是一个整数</string>
- <string name="time_unit_seconds">秒</string>
- <string name="time_unit_minutes">分钟</string>
- <string name="time_unit_hours">小时</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">目录</string>
<string name="gpodnet_toplist_header">头条播客</string>
@@ -363,6 +357,7 @@
<!--Feed information screen-->
<string name="authentication_label">验证</string>
<string name="authentication_descr">给本播客及曲目变更用户名及密码</string>
+ <!--Progress information-->
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">正在从选定的应用中导入订阅...</string>
<string name="search_itunes_label">搜索 iTunes</string>
diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml
index 4ecf2cf61..8f1268993 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -1,6 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+ <string-array name="spnAutoDeleteItems">
+ <item>@string/feed_auto_download_global</item>
+ <item>@string/feed_auto_download_always</item>
+ <item>@string/feed_auto_download_never</item>
+ </string-array>
+
<string-array name="smart_mark_as_played_values">
<item>0</item>
<item>15</item>
@@ -20,18 +26,7 @@
<item>60</item>
</integer-array>
- <string-array name="update_intervall_options">
- <item>Manual</item>
- <item>1 hour</item>
- <item>2 hours</item>
- <item>4 hours</item>
- <item>8 hours</item>
- <item>12 hours</item>
- <item>24 hours</item>
- </string-array>
-
<string-array name="update_intervall_values">
- <item>0</item>
<item>1</item>
<item>2</item>
<item>4</item>
@@ -39,30 +34,24 @@
<item>12</item>
<item>24</item>
</string-array>
+
<string-array name="episode_cache_size_entries">
- <item>@string/pref_episode_cache_unlimited</item>
- <item>1</item>
- <item>2</item>
<item>5</item>
<item>10</item>
- <item>20</item>
- <item>40</item>
- <item>60</item>
- <item>80</item>
+ <item>25</item>
+ <item>50</item>
<item>100</item>
+ <item>@string/pref_episode_cache_unlimited</item>
</string-array>
<string-array name="episode_cache_size_values">
- <item>-1</item>
- <item>1</item>
- <item>2</item>
<item>5</item>
<item>10</item>
- <item>20</item>
- <item>40</item>
- <item>60</item>
- <item>80</item>
+ <item>25</item>
+ <item>50</item>
<item>100</item>
+ <item>-1</item>
</string-array>
+
<string-array name="playback_speed_values">
<item>0.5</item>
<item>0.6</item>
@@ -137,6 +126,28 @@
<item>@string/add_feed_label</item>
</string-array>
+ <string-array name="nav_drawer_feed_order_options">
+ <item>@string/drawer_feed_order_unplayed_episodes</item>
+ <item>@string/drawer_feed_order_alphabetical</item>
+ </string-array>
+ <string-array name="nav_drawer_feed_order_values">
+ <item>0</item>
+ <item>1</item>
+ </string-array>
+
+ <string-array name="nav_drawer_feed_counter_options">
+ <item>@string/drawer_feed_counter_new_unplayed</item>
+ <item>@string/drawer_feed_counter_new</item>
+ <item>@string/drawer_feed_counter_unplayed</item>
+ <item>@string/drawer_feed_counter_none</item>
+ </string-array>
+ <string-array name="nav_drawer_feed_counter_values">
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ </string-array>
+
<string-array name="episode_hide_options">
<item>@string/hide_unplayed_episodes_label</item>
<item>@string/hide_paused_episodes_label</item>
@@ -157,4 +168,20 @@
<item>not_downloaded</item>
</string-array>
+ <string-array name="image_cache_size_options">
+ <item>20 MiB</item>
+ <item>50 MiB</item>
+ <item>100 MiB</item>
+ <item>250 MiB</item>
+ <item>500 MiB</item>
+ </string-array>
+
+ <string-array name="image_cache_size_values">
+ <item>20</item>
+ <item>50</item>
+ <item>100</item>
+ <item>250</item>
+ <item>500</item>
+ </string-array>
+
</resources>
diff --git a/core/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml
index e558a5c4e..bc0521dcd 100644
--- a/core/src/main/res/values/colors.xml
+++ b/core/src/main/res/values/colors.xml
@@ -3,6 +3,7 @@
<color name="white">#FFFFFF</color>
<color name="gray">#808080</color>
+ <color name="light_gray">#bfbfbf</color>
<color name="black">#000000</color>
<color name="bright_blue">#33B5E5</color>
<color name="ics_gray">#858585</color>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 3cedfb8e5..00bc077e8 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -7,46 +7,54 @@
<!-- Activitiy and fragment titles -->
<string name="app_name">AntennaPod</string>
<string name="feeds_label">Feeds</string>
- <string name="add_feed_label">Add podcast</string>
+ <string name="add_feed_label">Add Podcast</string>
<string name="podcasts_label">PODCASTS</string>
<string name="episodes_label">EPISODES</string>
- <string name="new_episodes_label">New episodes</string>
- <string name="all_episodes_label">All episodes</string>
+ <string name="new_episodes_label">New Episodes</string>
+ <string name="all_episodes_label">All Episodes</string>
<string name="new_label">New</string>
- <string name="waiting_list_label">Waiting list</string>
+ <string name="waiting_list_label">Waiting List</string>
<string name="settings_label">Settings</string>
- <string name="add_new_feed_label">Add podcast</string>
+ <string name="add_new_feed_label">Add Podcast</string>
<string name="downloads_label">Downloads</string>
<string name="downloads_running_label">Running</string>
<string name="downloads_completed_label">Completed</string>
<string name="downloads_log_label">Log</string>
- <string name="cancel_download_label">Cancel Download</string>
- <string name="playback_history_label">Playback history</string>
+ <string name="cancel_download_label">Cancel\nDownload</string>
+ <string name="playback_history_label">Playback History</string>
<string name="gpodnet_main_label">gpodder.net</string>
- <string name="gpodnet_auth_label">gpodder.net login</string>
+ <string name="gpodnet_auth_label">gpodder.net Login</string>
<!-- New episodes fragment -->
<string name="recently_published_episodes_label">Recently published</string>
- <string name="episode_filter_label">Show only new episodes</string>
+ <string name="episode_filter_label">Show only new Episodes</string>
<!-- Main activity -->
<string name="drawer_open">Open menu</string>
<string name="drawer_close">Close menu</string>
<string name="drawer_preferences">Drawer Preferences</string>
+ <string name="drawer_feed_order_unplayed_episodes">Sort by counter</string>
+ <string name="drawer_feed_order_alphabetical">Sort alphabetically</string>
+ <string name="drawer_feed_counter_new_unplayed">Number of new and unplayed episodes</string>
+ <string name="drawer_feed_counter_new">Number of new episodes</string>
+ <string name="drawer_feed_counter_unplayed">Number of unplayed episodes</string>
+ <string name="drawer_feed_counter_none">None</string>
<!-- Webview actions -->
- <string name="open_in_browser_label">Open in browser</string>
+ <string name="open_in_browser_label">Open in Browser</string>
<string name="copy_url_label">Copy URL</string>
<string name="share_url_label">Share URL</string>
- <string name="copied_url_msg">Copied URL to clipboard.</string>
- <string name="go_to_position_label">Go to this position</string>
+ <string name="copied_url_msg">Copied URL to Clipboard</string>
+ <string name="go_to_position_label">Go to this Position</string>
<!-- Playback history -->
- <string name="clear_history_label">Clear history</string>
+ <string name="clear_history_label">Clear History</string>
<!-- Other -->
<string name="confirm_label">Confirm</string>
<string name="cancel_label">Cancel</string>
+ <string name="yes">Yes</string>
+ <string name="no">No</string>
<string name="author_label">Author</string>
<string name="language_label">Language</string>
<string name="url_label">URL</string>
@@ -59,7 +67,7 @@
<string name="chapters_label">Chapters</string>
<string name="shownotes_label">Shownotes</string>
<string name="description_label">Description</string>
- <string name="most_recent_prefix">Most Recent Episode:\u0020</string>
+ <string name="most_recent_prefix">Most recent episode:\u0020</string>
<string name="episodes_suffix">\u0020episodes</string>
<string name="length_prefix">Length:\u0020</string>
<string name="size_prefix">Size:\u0020</string>
@@ -69,29 +77,41 @@
<string name="close_label">Close</string>
<string name="retry_label">Retry</string>
<string name="auto_download_label">Include in auto downloads</string>
+ <string name="auto_download_apply_to_items_title">Apply to Previous Episodes</string>
+ <string name="auto_download_apply_to_items_message">The new <i>Auto Download</i> setting will automatically be applied to new episodes.\nDo you also want to apply it to previous episodes?</string>
+ <string name="auto_delete_label">Auto Delete Episode\n(override global default)</string>
<string name="parallel_downloads_suffix">\u0020parallel downloads</string>
+ <string name="feed_auto_download_global">Global</string>
+ <string name="feed_auto_download_always">Always</string>
+ <string name="feed_auto_download_never">Never</string>
<!-- 'Add Feed' Activity labels -->
<string name="feedurl_label">Feed URL</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Add Podcast by URL</string>
- <string name="podcastdirectories_label">Find podcast in directory</string>
+ <string name="podcastdirectories_label">Find Podcast in Directory</string>
<string name="podcastdirectories_descr">You can search for new podcasts by name, category or popularity in the gpodder.net directory, or search the iTunes store.</string>
<string name="browse_gpoddernet_label">Browse gpodder.net</string>
<!-- Actions on feeds -->
<string name="mark_all_read_label">Mark all as played</string>
- <string name="mark_all_read_msg">Marked all episodes as played</string>
+ <string name="mark_all_read_msg">Marked all Episodes as played</string>
<string name="mark_all_read_confirmation_msg">Please confirm that you want to mark all episodes as being played.</string>
<string name="mark_all_read_feed_confirmation_msg">Please confirm that you want to mark all episodes in this feed as being played.</string>
+ <string name="mark_all_seen_label">Mark all as seen</string>
<string name="show_info_label">Show information</string>
- <string name="remove_feed_label">Remove podcast</string>
- <string name="share_link_label">Share website link</string>
- <string name="share_source_label">Share feed link</string>
+ <string name="remove_feed_label">Remove Podcast</string>
+ <string name="share_label">Share...</string>
+ <string name="share_link_label">Share Link</string>
+ <string name="share_link_with_position_label">Share Link with Position</string>
+ <string name="share_feed_url_label">Share Feed URL</string>
+ <string name="share_item_url_label">Share Episode URL</string>
+ <string name="share_item_url_with_position_label">Share Episode URL with Position</string>
<string name="feed_delete_confirmation_msg">Please confirm that you want to delete this feed and ALL episodes of this feed that you have downloaded.</string>
- <string name="feed_remover_msg">Removing feed</string>
- <string name="load_complete_feed">Refresh complete feed</string>
- <string name="hide_episodes_title">Hide episodes</string>
+ <string name="feed_remover_msg">Removing Feed</string>
+ <string name="load_complete_feed">Refresh complete Feed</string>
+ <string name="hide_episodes_title">Hide Episodes</string>
+ <string name="episode_actions">Apply actions</string>
<string name="hide_unplayed_episodes_label">Unplayed</string>
<string name="hide_paused_episodes_label">Paused</string>
<string name="hide_played_episodes_label">Played</string>
@@ -100,7 +120,7 @@
<string name="hide_downloaded_episodes_label">Downloaded</string>
<string name="hide_not_downloaded_episodes_label">Not downloaded</string>
<string name="filtered_label">Filtered</string>
- <string name="refresh_failed_msg">{fa-exclamation-circle} Last refresh failed</string>
+ <string name="refresh_failed_msg">{fa-exclamation-circle} Last Refresh failed</string>
<!-- actions on feeditems -->
<string name="download_label">Download</string>
@@ -109,10 +129,10 @@
<string name="stop_label">Stop</string>
<string name="stream_label">Stream</string>
<string name="remove_label">Remove</string>
- <string name="remove_episode_lable">Remove episode</string>
+ <string name="remove_episode_lable">Remove Episode</string>
<string name="mark_read_label">Mark as played</string>
- <string name="mark_unread_label">Mark as unplayed</string>
<string name="marked_as_read_label">Marked as played</string>
+ <string name="mark_unread_label">Mark as unplayed</string>
<string name="add_to_queue_label">Add to Queue</string>
<string name="added_to_queue_label">Added to Queue</string>
<string name="remove_from_queue_label">Remove from Queue</string>
@@ -121,39 +141,39 @@
<string name="enqueue_all_new">Enqueue all</string>
<string name="download_all">Download all</string>
<string name="skip_episode_label">Skip episode</string>
- <string name="activate_auto_download">Activate auto download</string>
- <string name="deactivate_auto_download">Deactivate auto download</string>
- <string name="reset_position">Reset playback position</string>
+ <string name="activate_auto_download">Activate Auto Download</string>
+ <string name="deactivate_auto_download">Deactivate Auto Download</string>
+ <string name="reset_position">Reset Playback Position</string>
<!-- Download messages and labels -->
<string name="download_successful">successful</string>
<string name="download_failed">failed</string>
<string name="download_pending">Download pending</string>
<string name="download_running">Download running</string>
- <string name="download_error_device_not_found">Storage device not found</string>
- <string name="download_error_insufficient_space">Insufficient space</string>
- <string name="download_error_file_error">File error</string>
+ <string name="download_error_device_not_found">Storage Device not found</string>
+ <string name="download_error_insufficient_space">Insufficient Space</string>
+ <string name="download_error_file_error">File Error</string>
<string name="download_error_http_data_error">HTTP Data Error</string>
<string name="download_error_error_unknown">Unknown Error</string>
<string name="download_error_parser_exception">Parser Exception</string>
- <string name="download_error_unsupported_type">Unsupported Feed type</string>
- <string name="download_error_connection_error">Connection error</string>
- <string name="download_error_unknown_host">Unknown host</string>
- <string name="download_error_unauthorized">Authentication error</string>
+ <string name="download_error_unsupported_type">Unsupported Feed Type</string>
+ <string name="download_error_connection_error">Connection Error</string>
+ <string name="download_error_unknown_host">Unknown Host</string>
+ <string name="download_error_unauthorized">Authentication Error</string>
<string name="cancel_all_downloads_label">Cancel all downloads</string>
<string name="download_canceled_msg">Download canceled</string>
<string name="download_canceled_autodownload_enabled_msg">Download canceled\nDisabled <i>Auto Download</i> for this item</string>
<string name="download_report_title">Downloads completed with error(s)</string>
- <string name="download_report_content_title">Download report</string>
+ <string name="download_report_content_title">Download Report</string>
<string name="download_error_malformed_url">Malformed URL</string>
<string name="download_error_io_error">IO Error</string>
- <string name="download_error_request_error">Request error</string>
- <string name="download_error_db_access">Database access error</string>
+ <string name="download_error_request_error">Request Error</string>
+ <string name="download_error_db_access">Database Access Error</string>
<string name="downloads_left">\u0020Downloads left</string>
<string name="downloads_processing">Processing downloads</string>
<string name="download_notification_title">Downloading podcast data</string>
<string name="download_report_content">%1$d downloads succeeded, %2$d failed</string>
- <string name="download_log_title_unknown">Unknown title</string>
+ <string name="download_log_title_unknown">Unknown Title</string>
<string name="download_type_feed">Feed</string>
<string name="download_type_media">Media file</string>
<string name="download_type_image">Image</string>
@@ -181,9 +201,9 @@
<string name="unknown_media_key">AntennaPod - Unknown media key: %1$d</string>
<!-- Queue operations -->
- <string name="lock_queue">Lock queue</string>
- <string name="unlock_queue">Unlock queue</string>
- <string name="clear_queue_label">Clear queue</string>
+ <string name="lock_queue">Lock Queue</string>
+ <string name="unlock_queue">Unlock Queue</string>
+ <string name="clear_queue_label">Clear Queue</string>
<string name="undo">Undo</string>
<string name="removed_from_queue">Item removed</string>
<string name="move_to_top_label">Move to top</string>
@@ -249,38 +269,48 @@
<string name="pref_smart_mark_as_played_title">Smart mark as played</string>
<string name="playback_pref">Playback</string>
<string name="network_pref">Network</string>
- <string name="pref_autoUpdateIntervall_title">Update interval</string>
- <string name="pref_autoUpdateIntervall_sum">Specify an interval in which the feeds are refreshed automatically or disable it</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">Update Interval or Time of Day</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Specify an interval or a specific time of day to refresh the feeds automatically</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">You can set an <i>interval</i> like \"every 2 hours\", set a specific <i>time of day</i> like \"7:00 AM\" or <i>disable</i> automatic updates altogether.\n\n<small>Please note: Update times are inexact. You may encounter a short delay.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Disable</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Set Interval</string>
+ <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Set Time of Day</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Download media files only over WiFi</string>
- <string name="pref_followQueue_title">Continuous playback</string>
+ <string name="pref_followQueue_title">Continuous Playback</string>
<string name="pref_downloadMediaOnWifiOnly_title">WiFi media download</string>
- <string name="pref_pauseOnHeadsetDisconnect_title">Headphones disconnect</string>
- <string name="pref_unpauseOnHeadsetReconnect_title">Headphones reconnect</string>
- <string name="pref_mobileUpdate_title">Mobile updates</string>
+ <string name="pref_pauseOnHeadsetDisconnect_title">Headphones Disconnect</string>
+ <string name="pref_unpauseOnHeadsetReconnect_title">Headphones Reconnect</string>
+ <string name="pref_mobileUpdate_title">Mobile Updates</string>
<string name="pref_mobileUpdate_sum">Allow updates over the mobile data connection</string>
<string name="refreshing_label">Refreshing</string>
<string name="flattr_settings_label">Flattr settings</string>
<string name="pref_flattr_auth_title">Flattr sign-in</string>
<string name="pref_flattr_auth_sum">Sign in to your flattr account to flattr things directly from the app.</string>
- <string name="pref_flattr_this_app_title">Flattr this app</string>
+ <string name="pref_flattr_this_app_title">Flattr this App</string>
<string name="pref_flattr_this_app_sum">Support the development of AntennaPod by flattring it. Thanks!</string>
<string name="pref_revokeAccess_title">Revoke access</string>
<string name="pref_revokeAccess_sum">Revoke the access permission to your flattr account for this app.</string>
<string name="pref_auto_flattr_title">Automatic Flattr</string>
<string name="pref_auto_flattr_sum">Configure automatic flattring</string>
<string name="user_interface_label">User Interface</string>
- <string name="pref_set_theme_title">Select theme</string>
- <string name="pref_nav_drawer_items_title">Change navigation drawer</string>
+ <string name="pref_set_theme_title">Select Theme</string>
+ <string name="pref_nav_drawer_title">Customize Navigation Drawer</string>
+ <string name="pref_nav_drawer_sum">Customize the appearance of the navigation drawer.</string>
+ <string name="pref_nav_drawer_items_title">Set Navigation Drawer items</string>
<string name="pref_nav_drawer_items_sum">Change which items appear in the navigation drawer.</string>
+ <string name="pref_nav_drawer_feed_order_title">Set Subscription Order</string>
+ <string name="pref_nav_drawer_feed_order_sum">Change the order of your subscriptions</string>
+ <string name="pref_nav_drawer_feed_counter_title">Set Subscription Counter</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Change the information displayed by the subscription counter</string>
<string name="pref_set_theme_sum">Change the appearance of AntennaPod.</string>
- <string name="pref_automatic_download_title">Automatic download</string>
+ <string name="pref_automatic_download_title">Automatic Download</string>
<string name="pref_automatic_download_sum">Configure the automatic download of episodes.</string>
<string name="pref_autodl_wifi_filter_title">Enable Wi-Fi filter</string>
<string name="pref_autodl_wifi_filter_sum">Allow automatic download only for selected Wi-Fi networks.</string>
<string name="pref_automatic_download_on_battery_title">Download when not charging</string>
<string name="pref_automatic_download_on_battery_sum">Allow automatic download when the battery is not charging</string>
- <string name="pref_parallel_downloads_title">Parallel downloads</string>
- <string name="pref_episode_cache_title">Episode cache</string>
+ <string name="pref_parallel_downloads_title">Parallel Downloads</string>
+ <string name="pref_episode_cache_title">Episode Cache</string>
<string name="pref_theme_title_light">Light</string>
<string name="pref_theme_title_dark">Dark</string>
<string name="pref_episode_cache_unlimited">Unlimited</string>
@@ -301,13 +331,16 @@
<string name="pref_gpodnet_sethostname_use_default_host">Use default host</string>
<string name="pref_expandNotify_title">Expand Notification</string>
<string name="pref_expandNotify_sum">Always expand the notification to show playback buttons.</string>
- <string name="pref_persistNotify_title">Persistent playback controls</string>
+ <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_showDownloadReport_title">Show Download Report</string>
+ <string name="pref_showDownloadReport_sum">If downloads fail, generate a report that shows the details of the failure.</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>
+ <string name="pref_queueAddToFront_title">Enqueue at Front</string>
<string name="pref_smart_mark_as_played_disabled">Disabled</string>
-
+ <string name="pref_image_cache_size_title">Image Cache Size</string>
+ <string name="pref_image_cache_size_sum">Size of the disk cache for images.</string>
<!-- Auto-Flattr dialog -->
<string name="auto_flattr_enable">Enable automatic flattring</string>
@@ -328,19 +361,20 @@
<string name="opml_import_explanation_1">Choose a specific file path from the local filesystem.</string>
<string name="opml_import_explanation_2">Use an external applications like Dropbox, Google Drive or your favourite file manager to open an OPML file.</string>
<string name="opml_import_explanation_3">Many applications like Google Mail, Dropbox, Google Drive and most file managers can <i>open</i> OPML files <i>with</i> AntennaPod.</string> <string name="start_import_label">Start import</string>
- <string name="opml_import_label">OPML import</string>
+ <string name="opml_import_label">OPML Import</string>
<string name="opml_directory_error">ERROR!</string>
<string name="reading_opml_label">Reading OPML file</string>
<string name="opml_reader_error">An error has occurred while reading the opml document:</string>
<string name="opml_import_error_dir_empty">The import directory is empty.</string>
<string name="select_all_label">Select all</string>
<string name="deselect_all_label">Deselect all</string>
+ <string name="select_options_label">Select ...</string>
<string name="choose_file_from_filesystem">From local filesystem</string>
<string name="choose_file_from_external_application">Use external application</string>
<string name="opml_export_label">OPML export</string>
<string name="exporting_label">Exporting...</string>
<string name="export_error_label">Export error</string>
- <string name="opml_export_success_title">OPML export successful.</string>
+ <string name="opml_export_success_title">OPML Export successful.</string>
<string name="opml_export_success_sum">The .opml file was written to:\u0020</string>
<!-- Sleep timer -->
@@ -350,9 +384,21 @@
<string name="sleep_timer_label">Sleep timer</string>
<string name="time_left_label">Time left:\u0020</string>
<string name="time_dialog_invalid_input">Invalid input, time has to be an integer</string>
- <string name="time_unit_seconds">seconds</string>
- <string name="time_unit_minutes">minutes</string>
- <string name="time_unit_hours">hours</string>
+ <string name="time_seconds">seconds</string>
+ <string name="time_minutes">minutes</string>
+ <string name="time_hours">hours</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">1 second</item>
+ <item quantity="other">%d seconds</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 minute</item>
+ <item quantity="other">%d minutes</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 hour</item>
+ <item quantity="other">%d hours</item>
+ </plurals>
<!-- gpodder.net -->
<string name="gpodnet_taglist_header">CATEGORIES</string>
@@ -388,7 +434,7 @@
<!-- Directory chooser -->
<string name="selected_folder_label">Selected folder:</string>
<string name="create_folder_label">Create folder</string>
- <string name="choose_data_directory">Choose data folder</string>
+ <string name="choose_data_directory">Choose Data Folder</string>
<string name="create_folder_msg">Create new folder with name "%1$s"?</string>
<string name="create_folder_success">Created new folder</string>
<string name="create_folder_error_no_write_access">Cannot write to this folder</string>
@@ -398,9 +444,10 @@
<string name="folder_not_empty_dialog_msg">The folder you have selected is not empty. Media downloads and other files will be placed directly in this folder. Continue anyway?</string>
<string name="set_to_default_folder">Choose default folder</string>
<string name="pref_pausePlaybackForFocusLoss_sum">Pause playback instead of lowering volume when another app wants to play sounds</string>
- <string name="pref_pausePlaybackForFocusLoss_title">Pause for interruptions</string>
+ <string name="pref_pausePlaybackForFocusLoss_title">Pause for Interruptions</string>
<string name="pref_resumeAfterCall_sum">Resume playback after a phone call completes</string>
- <string name="pref_resumeAfterCall_title">Resume after call</string>
+ <string name="pref_resumeAfterCall_title">Resume after Call</string>
+ <string name="pref_restart_required">AntennaPod has to be restarted for this change to take effect.</string>
<!-- Online feed view -->
<string name="subscribe_label">Subscribe</string>
@@ -431,8 +478,33 @@
<string name="authentication_label">Authentication</string>
<string name="authentication_descr">Change your username and password for this podcast and its episodes.</string>
+
+ <!-- Progress information -->
+ <string name="progress_upgrading_database">Upgrading the database</string>
+
<!-- AntennaPodSP -->
<string name="sp_apps_importing_feeds_msg">Importing subscriptions from single-purpose apps&#8230;</string>
<string name="search_itunes_label">Search iTunes</string>
+
+ <string name="select_label"><b>Select ...</b></string>
+ <string name="all_label">All</string>
+ <string name="selected_all_label">Selected all Episodes</string>
+ <string name="none_label">None</string>
+ <string name="deselected_all_label">Deselected all Episodes</string>
+ <string name="played_label">Played</string>
+ <string name="selected_played_label">Selected played Episodes</string>
+ <string name="unplayed_label">Unplayed</string>
+ <string name="selected_unplayed_label">Selected unplayed Episodes</string>
+ <string name="downloaded_label">Downloaded</string>
+ <string name="selected_downloaded_label">Selected downloaded Episodes</string>
+ <string name="not_downloaded_label">Not downloaded</string>
+ <string name="selected_not_downloaded_label">Selected not downloaded Episodes</string>
+ <string name="sort_title"><b>Sort by ...</b></string>
+ <string name="sort_title_a_z">Title (A \u2192 Z)</string>
+ <string name="sort_title_z_a">Title (Z \u2192 A)</string>
+ <string name="sort_date_new_old">Date (New \u2192 Old)</string>
+ <string name="sort_date_old_new">Date (Old \u2192 New)</string>
+ <string name="sort_duration_short_long">Duration (Short \u2192 Long)</string>
+ <string name="sort_duration_long_short">Duration (Long \u2192 Short)</string>
</resources>
diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml
index 8619869c8..85c908f64 100644
--- a/core/src/main/res/values/styles.xml
+++ b/core/src/main/res/values/styles.xml
@@ -263,7 +263,9 @@
<style name="BigBlurryBackground">
<item name="android:scaleType">centerCrop</item>
- <item name="android:tint">@color/image_readability_tint</item>
+ <!-- <item name="android:tint">@color/image_readability_tint</item> -->
+ <!-- Reactivate when Glide's tinting has been fixed for Android 5.x
+ Remove color filter from ItemlistFragment -->
</style>