summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java2
-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/DBCleanupTests.java8
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java8
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java15
-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.java57
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java1
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java2
-rw-r--r--app/src/main/AndroidManifest.xml4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/UpdateManager.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java98
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java22
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java28
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java196
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java121
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java70
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java464
-rw-r--r--app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java3
-rw-r--r--app/src/main/res/layout/downloaded_episodeslist_item.xml19
-rw-r--r--app/src/main/res/layout/episodes_apply_action_fragment.xml103
-rw-r--r--app/src/main/res/layout/itemdescription_listitem.xml49
-rw-r--r--app/src/main/res/layout/storage_error.xml29
-rw-r--r--app/src/main/res/menu/directory_chooser.xml16
-rw-r--r--app/src/main/res/menu/downloads_completed.xml13
-rw-r--r--app/src/main/res/menu/episodes_apply_action_options.xml4
-rw-r--r--build.gradle2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java37
-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/DownloadRequest.java29
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java57
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APNullCleanupAlgorithm.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java35
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java48
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java15
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java12
-rw-r--r--core/src/main/res/drawable-hdpi/ic_create_new_folder_grey600_24dp.pngbin0 -> 198 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_create_new_folder_white_24dp.pngbin0 -> 191 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_sd_storage_grey600_36dp.pngbin0 -> 333 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_sd_storage_white_36dp.pngbin0 -> 332 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_create_new_folder_grey600_24dp.pngbin0 -> 152 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_create_new_folder_white_24dp.pngbin0 -> 149 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_sd_storage_grey600_36dp.pngbin0 -> 220 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_sd_storage_white_36dp.pngbin0 -> 214 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_create_new_folder_grey600_24dp.pngbin0 -> 248 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_create_new_folder_white_24dp.pngbin0 -> 239 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_sd_storage_grey600_36dp.pngbin0 -> 403 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_sd_storage_white_36dp.pngbin0 -> 404 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_create_new_folder_grey600_24dp.pngbin0 -> 343 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_create_new_folder_white_24dp.pngbin0 -> 339 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_sd_storage_grey600_36dp.pngbin0 -> 605 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_sd_storage_white_36dp.pngbin0 -> 593 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_grey600_24dp.pngbin0 -> 466 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_white_24dp.pngbin0 -> 463 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_sd_storage_grey600_36dp.pngbin0 -> 890 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_sd_storage_white_36dp.pngbin0 -> 890 bytes
-rw-r--r--core/src/main/res/values-uk-rUA/strings.xml31
-rw-r--r--core/src/main/res/values/attrs.xml2
-rw-r--r--core/src/main/res/values/strings.xml6
-rw-r--r--core/src/main/res/values/styles.xml8
82 files changed, 1121 insertions, 675 deletions
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 5836bb699..ee454ce8a 100644
--- a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
@@ -162,7 +162,7 @@ public class FeedHandlerTest extends InstrumentationTestCase {
if (withImage) {
image = new FeedImage(0, "image", null, "http://example.com/picture", false);
}
- Feed feed = new Feed(0, new Date(), "title", "http://example.com", "This is the description",
+ Feed feed = new Feed(0, null, "title", "http://example.com", "This is the description",
"http://example.com/payment", "Daniel", "en", null, "http://example.com/feed", image, file.getAbsolutePath(),
"http://example.com/feed", true);
feed.setItems(new ArrayList<FeedItem>());
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 d7a170c17..7862e986d 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
@@ -115,7 +115,7 @@ 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);
+ Feed f = new Feed(0, null, "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<>());
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 f06d2f2a6..5c3d32960 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
@@ -48,7 +48,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
private List<FeedItem> writeTestQueue(String pref) {
final Context c = getInstrumentation().getTargetContext();
final int NUM_ITEMS = 10;
- Feed f = new Feed(0, new Date(), "title", "link", "d", null, null, null, null, "id", null, "null", "url", false);
+ Feed f = new Feed(0, null, "title", "link", "d", null, null, null, null, "id", null, "null", "url", false);
f.setItems(new ArrayList<>());
for (int i = 0; i < NUM_ITEMS; i++) {
f.getItems().add(new FeedItem(0, pref + i, pref + i, "link", new Date(), FeedItem.PLAYED, f));
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
index afdaeead0..7300df395 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
@@ -87,7 +87,7 @@ public class DBCleanupTests extends InstrumentationTestCase {
public void testPerformAutoCleanupShouldDelete() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
List<FeedItem> items = new ArrayList<>();
feed.setItems(items);
List<File> files = new ArrayList<>();
@@ -143,7 +143,7 @@ public class DBCleanupTests extends InstrumentationTestCase {
public void testPerformAutoCleanupHandleUnplayed() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
List<FeedItem> items = new ArrayList<FeedItem>();
feed.setItems(items);
List<File> files = new ArrayList<File>();
@@ -159,7 +159,7 @@ public class DBCleanupTests extends InstrumentationTestCase {
public void testPerformAutoCleanupShouldNotDeleteBecauseInQueue() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
List<FeedItem> items = new ArrayList<>();
feed.setItems(items);
List<File> files = new ArrayList<>();
@@ -198,7 +198,7 @@ public class DBCleanupTests extends InstrumentationTestCase {
public void testPerformAutoCleanupShouldNotDeleteBecauseFavorite() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
List<FeedItem> items = new ArrayList<>();
feed.setItems(items);
List<File> files = new ArrayList<>();
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java
index 18a8d63d1..7205b42c4 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java
@@ -82,7 +82,7 @@ public class DBNullCleanupAlgorithmTest extends InstrumentationTestCase {
public void testPerformAutoCleanupShouldNotDelete() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
List<FeedItem> items = new ArrayList<>();
feed.setItems(items);
List<File> files = new ArrayList<>();
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java
index 890897f43..3bd508eaf 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java
@@ -32,7 +32,7 @@ public class DBQueueCleanupAlgorithmTest extends DBCleanupTests {
public void testPerformAutoCleanupHandleUnplayed() throws IOException {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
List<FeedItem> items = new ArrayList<>();
feed.setItems(items);
List<File> files = new ArrayList<>();
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 3988669ce..0fc3b1892 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
@@ -54,10 +54,10 @@ public class DBReaderTest extends InstrumentationTestCase {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
- Feed feed1 = new Feed(0, new Date(), "A", "link", "d", null, null, null, "rss", "A", null, "", "", true);
- Feed feed2 = new Feed(0, new Date(), "b", "link", "d", null, null, null, "rss", "b", null, "", "", true);
- Feed feed3 = new Feed(0, new Date(), "C", "link", "d", null, null, null, "rss", "C", null, "", "", true);
- Feed feed4 = new Feed(0, new Date(), "d", "link", "d", null, null, null, "rss", "d", null, "", "", true);
+ Feed feed1 = new Feed(0, null, "A", "link", "d", null, null, null, "rss", "A", null, "", "", true);
+ Feed feed2 = new Feed(0, null, "b", "link", "d", null, null, null, "rss", "b", null, "", "", true);
+ Feed feed3 = new Feed(0, null, "C", "link", "d", null, null, null, "rss", "C", null, "", "", true);
+ Feed feed4 = new Feed(0, null, "d", "link", "d", null, null, null, "rss", "d", null, "", "", true);
adapter.setCompleteFeed(feed1);
adapter.setCompleteFeed(feed2);
adapter.setCompleteFeed(feed3);
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 1894d6585..5b2393d45 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
@@ -1,13 +1,9 @@
package de.test.antennapod.storage;
import android.content.Context;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
import android.test.FlakyTest;
import android.test.InstrumentationTestCase;
-import java.io.File;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
@@ -15,14 +11,11 @@ import java.util.List;
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.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import static de.test.antennapod.storage.DBTestUtils.saveFeedlist;
-
/**
* Test class for DBTasks
*/
@@ -57,7 +50,7 @@ public class DBTasksTest extends InstrumentationTestCase {
public void testUpdateFeedNewFeed() {
final int NUM_ITEMS = 10;
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
for (int i = 0; i < NUM_ITEMS; i++) {
feed.getItems().add(new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(), FeedItem.UNPLAYED, feed));
@@ -75,8 +68,8 @@ public class DBTasksTest extends InstrumentationTestCase {
/** Two feeds with the same title, but different download URLs should be treated as different feeds. */
public void testUpdateFeedSameTitle() {
- Feed feed1 = new Feed("url1", new Date(), "title");
- Feed feed2 = new Feed("url2", new Date(), "title");
+ Feed feed1 = new Feed("url1", null, "title");
+ Feed feed2 = new Feed("url2", null, "title");
feed1.setItems(new ArrayList<>());
feed2.setItems(new ArrayList<>());
@@ -91,7 +84,7 @@ public class DBTasksTest extends InstrumentationTestCase {
final int NUM_ITEMS_OLD = 10;
final int NUM_ITEMS_NEW = 10;
- final Feed feed = new Feed("url", new Date(), "title");
+ final Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
for (int i = 0; i < NUM_ITEMS_OLD; i++) {
feed.getItems().add(new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(i), FeedItem.PLAYED, feed));
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 0af8afa83..78b807710 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
@@ -44,7 +44,7 @@ public class DBTestUtils {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
for (int i = 0; i < numFeeds; i++) {
- Feed f = new Feed(0, new Date(), "feed " + i, "link" + i, "descr", null, null,
+ Feed f = new Feed(0, null, "feed " + i, "link" + i, "descr", null, null,
null, null, "id" + i, null, null, "url" + i, false, new FlattrStatus(), false, null, null, false);
f.setItems(new ArrayList<>());
for (int j = 0; j < numItems; j++) {
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 eaae9323c..0e1d19f7b 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
@@ -59,13 +59,14 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
}
- public void testSetFeedMediaPlaybackInformation() throws IOException, ExecutionException, InterruptedException {
+ public void testSetFeedMediaPlaybackInformation()
+ throws IOException, ExecutionException, InterruptedException, TimeoutException {
final int POSITION = 50;
final long LAST_PLAYED_TIME = 1000;
final int PLAYED_DURATION = 60;
final int DURATION = 100;
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
List<FeedItem> items = new ArrayList<>();
feed.setItems(items);
FeedItem item = new FeedItem(0, "Item", "Item", "url", new Date(), FeedItem.PLAYED, feed);
@@ -73,13 +74,13 @@ public class DBWriterTest extends InstrumentationTestCase {
FeedMedia media = new FeedMedia(0, item, DURATION, 1, 1, "mime_type", "dummy path", "download_url", true, null, 0, 0);
item.setMedia(media);
- DBWriter.setFeedItem(item).get();
+ DBWriter.setFeedItem(item).get(TIMEOUT, TimeUnit.SECONDS);
media.setPosition(POSITION);
media.setLastPlayedTime(LAST_PLAYED_TIME);
media.setPlayedDuration(PLAYED_DURATION);
- DBWriter.setFeedMediaPlaybackInformation(item.getMedia()).get();
+ DBWriter.setFeedMediaPlaybackInformation(item.getMedia()).get(TIMEOUT, TimeUnit.SECONDS);
FeedItem itemFromDb = DBReader.getFeedItem(item.getId());
FeedMedia mediaFromDb = itemFromDb.getMedia();
@@ -90,12 +91,13 @@ public class DBWriterTest extends InstrumentationTestCase {
assertEquals(DURATION, mediaFromDb.getDuration());
}
- public void testDeleteFeedMediaOfItemFileExists() throws IOException, ExecutionException, InterruptedException {
+ public void testDeleteFeedMediaOfItemFileExists()
+ throws IOException, ExecutionException, InterruptedException, TimeoutException {
File dest = new File(getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER), "testFile");
assertTrue(dest.createNewFile());
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
List<FeedItem> items = new ArrayList<>();
feed.setItems(items);
FeedItem item = new FeedItem(0, "Item", "Item", "url", new Date(), FeedItem.PLAYED, feed);
@@ -112,7 +114,8 @@ public class DBWriterTest extends InstrumentationTestCase {
assertTrue(media.getId() != 0);
assertTrue(item.getId() != 0);
- DBWriter.deleteFeedMediaOfItem(getInstrumentation().getTargetContext(), media.getId()).get();
+ DBWriter.deleteFeedMediaOfItem(getInstrumentation().getTargetContext(), media.getId())
+ .get(TIMEOUT, TimeUnit.SECONDS);
media = DBReader.getFeedMedia(media.getId());
assertNotNull(media);
assertFalse(dest.exists());
@@ -124,7 +127,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
// create Feed image
@@ -197,7 +200,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
feed.setImage(null);
@@ -253,7 +256,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(null);
// create Feed image
@@ -290,7 +293,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
// create Feed image
@@ -342,7 +345,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
// create Feed image
@@ -400,7 +403,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
// create Feed image
@@ -472,7 +475,7 @@ public class DBWriterTest extends InstrumentationTestCase {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<FeedItem>());
// create Feed image
@@ -528,7 +531,7 @@ public class DBWriterTest extends InstrumentationTestCase {
private FeedMedia playbackHistorySetup(Date playbackCompletionDate) {
final Context context = getInstrumentation().getTargetContext();
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<FeedItem>());
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, 0);
@@ -542,9 +545,10 @@ public class DBWriterTest extends InstrumentationTestCase {
return media;
}
- public void testAddItemToPlaybackHistoryNotPlayedYet() throws ExecutionException, InterruptedException {
+ public void testAddItemToPlaybackHistoryNotPlayedYet()
+ throws ExecutionException, InterruptedException, TimeoutException {
FeedMedia media = playbackHistorySetup(null);
- DBWriter.addItemToPlaybackHistory(media).get();
+ DBWriter.addItemToPlaybackHistory(media).get(TIMEOUT, TimeUnit.SECONDS);
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
media = DBReader.getFeedMedia(media.getId());
@@ -554,11 +558,12 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNotNull(media.getPlaybackCompletionDate());
}
- public void testAddItemToPlaybackHistoryAlreadyPlayed() throws ExecutionException, InterruptedException {
+ public void testAddItemToPlaybackHistoryAlreadyPlayed()
+ throws ExecutionException, InterruptedException, TimeoutException {
final long OLD_DATE = 0;
FeedMedia media = playbackHistorySetup(new Date(OLD_DATE));
- DBWriter.addItemToPlaybackHistory(media).get();
+ DBWriter.addItemToPlaybackHistory(media).get(TIMEOUT, TimeUnit.SECONDS);
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
media = DBReader.getFeedMedia(media.getId());
@@ -571,7 +576,7 @@ public class DBWriterTest extends InstrumentationTestCase {
private Feed queueTestSetupMultipleItems(final int NUM_ITEMS) throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
@@ -598,7 +603,7 @@ public class DBWriterTest extends InstrumentationTestCase {
public void testAddQueueItemSingleItem() throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
@@ -622,7 +627,7 @@ public class DBWriterTest extends InstrumentationTestCase {
public void testAddQueueItemSingleItemAlreadyInQueue() throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
@@ -688,7 +693,7 @@ public class DBWriterTest extends InstrumentationTestCase {
public void testRemoveQueueItem() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
final Context context = getInstrumentation().getTargetContext();
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
@@ -733,7 +738,7 @@ public class DBWriterTest extends InstrumentationTestCase {
public void testMoveQueueItem() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
@@ -779,7 +784,7 @@ public class DBWriterTest extends InstrumentationTestCase {
public void testMarkFeedRead() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "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(), FeedItem.UNPLAYED, feed);
@@ -805,7 +810,7 @@ public class DBWriterTest extends InstrumentationTestCase {
public void testMarkAllItemsReadSameFeed() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
- Feed feed = new Feed("url", new Date(), "title");
+ Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.UNPLAYED, feed);
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 e456f3891..4e214cf81 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
@@ -142,6 +142,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
return ((MainActivity) solo.getCurrentActivity()).getSupportActionBar().getTitle().toString();
}
+ @SuppressWarnings("unchecked")
@FlakyTest(tolerance = 3)
public void testGoToPreferences() {
openNavDrawer();
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 13abbb1cc..432d4a4e6 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
@@ -143,7 +143,7 @@ public class UITestUtils {
for (int i = 0; i < NUM_FEEDS; i++) {
File bitmapFile = newBitmapFile("image" + i);
FeedImage image = new FeedImage(0, "image " + i, null, hostFile(bitmapFile), false);
- Feed feed = new Feed(0, new Date(), "Title " + i, "http://example.com/" + i, "Description of feed " + i,
+ Feed feed = new Feed(0, null, "Title " + i, "http://example.com/" + i, "Description of feed " + i,
"http://example.com/pay/feed" + i, "author " + i, "en", Feed.TYPE_RSS2, "feed" + i, image, null,
"http://example.com/feed/src/" + i, false);
image.setOwner(feed);
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4e7863730..9d7bbee67 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.danoeh.antennapod"
- android:versionCode="1050003"
- android:versionName="1.5.0.3">
+ android:versionCode="1050004"
+ android:versionName="1.5.0.4">
<!--
Version code schema:
"1.2.3-SNAPSHOT" -> 1020300
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index c1d4bc4fd..829a49a15 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -57,8 +57,8 @@ public class PodcastApp extends Application {
singleton = this;
PodDBAdapter.init(this);
- UpdateManager.init(this);
UserPreferences.init(this);
+ UpdateManager.init(this);
PlaybackPreferences.init(this);
NetworkUtils.init(this);
EventDistributor.getInstance();
diff --git a/app/src/main/java/de/danoeh/antennapod/UpdateManager.java b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java
index b1d7fffc8..0b3c43381 100644
--- a/app/src/main/java/de/danoeh/antennapod/UpdateManager.java
+++ b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java
@@ -5,14 +5,18 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.os.Build;
import android.util.Log;
+import org.antennapod.audio.MediaPlayer;
+
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.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -83,6 +87,11 @@ public class UpdateManager {
}
}.start();
}
+ if(oldVersionCode < 1050004) {
+ if(MediaPlayer.isPrestoLibraryInstalled(context) && Build.VERSION.SDK_INT >= 16) {
+ UserPreferences.enableSonic(true);
+ }
+ }
}
}
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 12bae2f51..8af6f3552 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -518,6 +518,11 @@ public class AudioplayerActivity extends MediaplayerActivity implements NavDrawe
}
@Override
+ public int getReclaimableItems() {
+ return (navDrawerData != null) ? navDrawerData.reclaimableSpace : 0;
+ }
+
+ @Override
public int getFeedCounter(long feedId) {
return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
index 25dc64232..62e85120d 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
@@ -6,8 +6,8 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.FileObserver;
import android.support.v4.app.NavUtils;
-import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -34,7 +34,8 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
* Let's the user choose a directory on the storage device. The selected folder
* will be sent back to the starting activity as an activity result.
*/
-public class DirectoryChooserActivity extends ActionBarActivity {
+public class DirectoryChooserActivity extends AppCompatActivity {
+
private static final String TAG = "DirectoryChooserActivit";
private static final String CREATE_DIRECTORY_NAME = "AntennaPod";
@@ -250,8 +251,7 @@ public class DirectoryChooserActivity extends ActionBarActivity {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- menu.findItem(R.id.new_folder_item)
- .setVisible(isValidFile(selectedDir));
+ menu.findItem(R.id.new_folder_item).setVisible(isValidFile(selectedDir));
return true;
}
@@ -333,4 +333,5 @@ public class DirectoryChooserActivity extends ActionBarActivity {
private boolean isValidFile(File file) {
return file != null && file.isDirectory() && file.canRead() && file.canWrite();
}
+
}
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 edb973a0c..9116decb0 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
@@ -224,10 +224,12 @@ public class FeedInfoActivity extends ActionBarActivity {
etxtFilterText.setText(filter.getIncludeFilter());
rdoFilterInclude.setChecked(true);
rdoFilterExclude.setChecked(false);
+ filterInclude = true;
} else if (filter.excludeOnly()) {
etxtFilterText.setText(filter.getExcludeFilter());
rdoFilterInclude.setChecked(false);
rdoFilterExclude.setChecked(true);
+ filterInclude = false;
} else {
Log.d(TAG, "No filter set");
rdoFilterInclude.setChecked(false);
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 f96764b42..bd7da7c03 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -644,10 +644,14 @@ public class MainActivity extends AppCompatActivity implements NavDrawerActivity
}
@Override
+ public int getReclaimableItems() {
+ return (navDrawerData != null) ? navDrawerData.reclaimableSpace : 0;
+ }
+
+ @Override
public int getFeedCounter(long feedId) {
return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0;
}
-
};
private void loadData() {
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 8c2b7f838..c7426c006 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -35,7 +35,6 @@ import org.jsoup.nodes.Document;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -258,7 +257,7 @@ public class OnlineFeedViewActivity extends ActionBarActivity {
private void startFeedDownload(String url, String username, String password) {
Log.d(TAG, "Starting feed download");
url = URLChecker.prepareURL(url);
- feed = new Feed(url, new Date(0));
+ feed = new Feed(url, null);
if (username != null && password != null) {
feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, username, password));
}
@@ -410,7 +409,7 @@ public class OnlineFeedViewActivity extends ActionBarActivity {
subscribeButton.setOnClickListener(v -> {
try {
- Feed f = new Feed(selectedDownloadUrl, new Date(0), feed.getTitle());
+ Feed f = new Feed(selectedDownloadUrl, null, feed.getTitle());
f.setPreferences(feed.getPreferences());
this.feed = f;
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
index d974e0e1b..46dabec12 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
@@ -1,16 +1,27 @@
package de.danoeh.antennapod.activity;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Environment;
+import android.support.v4.app.ActivityCompat;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.asynctask.OpmlFeedQueuer;
-import de.danoeh.antennapod.asynctask.OpmlImportWorker;
-import de.danoeh.antennapod.core.opml.OpmlElement;
+import com.afollestad.materialdialogs.MaterialDialog;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.asynctask.OpmlFeedQueuer;
+import de.danoeh.antennapod.asynctask.OpmlImportWorker;
+import de.danoeh.antennapod.core.opml.OpmlElement;
+import de.danoeh.antennapod.core.util.LangUtils;
+
/**
* Base activity for Opml Import - e.g. with code what to do afterwards
* */
@@ -19,22 +30,23 @@ public class OpmlImportBaseActivity extends ActionBarActivity {
private static final String TAG = "OpmlImportBaseActivity";
private OpmlImportWorker importWorker;
- /**
+ private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 5;
+ private Uri uri;
+
+ /**
* Handles the choices made by the user in the OpmlFeedChooserActivity and
* starts the OpmlFeedQueuer if necessary.
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Received result");
+ Log.d(TAG, "Received result");
if (resultCode == RESULT_CANCELED) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Activity was cancelled");
- if (finishWhenCanceled())
- finish();
+ Log.d(TAG, "Activity was cancelled");
+ if (finishWhenCanceled()) {
+ finish();
+ }
} else {
- int[] selected = data
- .getIntArrayExtra(OpmlFeedChooserActivity.EXTRA_SELECTED_ITEMS);
+ int[] selected = data.getIntArrayExtra(OpmlFeedChooserActivity.EXTRA_SELECTED_ITEMS);
if (selected != null && selected.length > 0) {
OpmlFeedQueuer queuer = new OpmlFeedQueuer(this, selected) {
@@ -50,35 +62,75 @@ public class OpmlImportBaseActivity extends ActionBarActivity {
};
queuer.executeAsync();
} else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "No items were selected");
+ Log.d(TAG, "No items were selected");
}
}
}
- /** Starts the import process. */
- protected void startImport(Reader reader) {
+ protected void importUri(Uri uri) {
+ this.uri = uri;
+ if(uri.toString().contains(Environment.getExternalStorageDirectory().toString())) {
+ int permission = ActivityCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE);
+ if (permission != PackageManager.PERMISSION_GRANTED) {
+ requestPermission();
+ return;
+ }
+ }
+ startImport();
+ }
+
+ private void requestPermission() {
+ String[] permissions = { android.Manifest.permission.READ_EXTERNAL_STORAGE };
+ ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_READ_EXTERNAL_STORAGE);
+ }
- if (reader != null) {
- importWorker = new OpmlImportWorker(this, reader) {
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String[] permissions,
+ int[] grantResults) {
+ if (requestCode != PERMISSION_REQUEST_READ_EXTERNAL_STORAGE) {
+ return;
+ }
+ if (grantResults.length > 0 && ArrayUtils.contains(grantResults, PackageManager.PERMISSION_GRANTED)) {
+ startImport();
+ } else {
+ new MaterialDialog.Builder(this)
+ .content(R.string.opml_import_ask_read_permission)
+ .positiveText(android.R.string.ok)
+ .negativeText(R.string.cancel_label)
+ .onPositive((dialog, which) -> requestPermission())
+ .onNegative((dialog, which) -> finish())
+ .show();
+ }
+ }
+
+ /** Starts the import process. */
+ protected void startImport() {
+ try {
+ Reader mReader = new InputStreamReader(getContentResolver().openInputStream(uri), LangUtils.UTF_8);
+ importWorker = new OpmlImportWorker(this, mReader) {
@Override
protected void onPostExecute(ArrayList<OpmlElement> result) {
super.onPostExecute(result);
if (result != null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Parsing was successful");
+ Log.d(TAG, "Parsing was successful");
OpmlImportHolder.setReadElements(result);
startActivityForResult(new Intent(
OpmlImportBaseActivity.this,
OpmlFeedChooserActivity.class), 0);
} else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Parser error occurred");
+ Log.d(TAG, "Parser error occurred");
}
}
};
importWorker.executeAsync();
+ } catch (Exception e) {
+ Log.d(TAG, Log.getStackTraceString(e));
+ new MaterialDialog.Builder(this)
+ .content("Cannot open OPML file: " + e.getMessage())
+ .positiveText(android.R.string.ok)
+ .show();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java
index 46e5f0e8e..ab4b0d0ee 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java
@@ -2,25 +2,14 @@ package de.danoeh.antennapod.activity;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v7.app.AlertDialog;
-import android.util.Log;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.LangUtils;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.net.URL;
/** Lets the user start the OPML-import process. */
public class OpmlImportFromIntentActivity extends OpmlImportBaseActivity {
private static final String TAG = "OpmlImportFromIntentAct";
-
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
@@ -28,15 +17,8 @@ public class OpmlImportFromIntentActivity extends OpmlImportBaseActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- try {
- Uri uri = getIntent().getData();
-
- Reader mReader = new InputStreamReader(getContentResolver().openInputStream(uri), LangUtils.UTF_8);
- startImport(mReader);
- } catch (Exception e) {
- new AlertDialog.Builder(this).setMessage("Cannot open XML - Reason: " + e.getMessage()).show();
- }
-
+ Uri uri = getIntent().getData();
+ importUri(uri);
}
@Override
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 6e3991739..15d97cc2c 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
@@ -11,16 +11,9 @@ import android.view.View;
import android.widget.Button;
import android.widget.TextView;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStreamReader;
-import java.io.Reader;
-
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;
/**
@@ -114,19 +107,6 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
}
}
- private void startImport(File file) {
- Reader mReader = null;
- try {
- mReader = new InputStreamReader(new FileInputStream(file),
- LangUtils.UTF_8);
- Log.d(TAG, "Parsing " + file.toString());
- startImport(mReader);
- } catch (FileNotFoundException e) {
- Log.d(TAG, "File not found which really should be there");
- // this should never happen as it is a file we have just chosen
- }
- }
-
/*
* Creates an implicit intent to launch a file manager which lets
* the user choose a specific OPML-file to import from.
@@ -155,13 +135,7 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == CHOOSE_OPML_FILE) {
Uri uri = data.getData();
-
- try {
- Reader mReader = new InputStreamReader(getContentResolver().openInputStream(uri), LangUtils.UTF_8);
- startImport(mReader);
- } catch (FileNotFoundException e) {
- Log.d(TAG, "File not found");
- }
+ importUri(uri);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
index b02e82f0b..e980764ec 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
@@ -1,30 +1,109 @@
package de.danoeh.antennapod.activity;
+import android.Manifest;
+import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Build;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
+import android.widget.Button;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+
+import java.io.File;
+import java.util.ArrayList;
+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.Converter;
import de.danoeh.antennapod.core.util.StorageUtils;
/** Is show if there is now external storage available. */
-public class StorageErrorActivity extends ActionBarActivity {
+public class StorageErrorActivity extends AppCompatActivity {
+
private static final String TAG = "StorageErrorActivity";
+ private static final String[] EXTERNAL_STORAGE_PERMISSIONS = {
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE };
+ private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 42;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
setContentView(R.layout.storage_error);
- }
+
+ Button btnChooseDataFolder = (Button) findViewById(R.id.btnChooseDataFolder);
+ btnChooseDataFolder.setOnClickListener(v -> {
+ if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
+ Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ showChooseDataFolderDialog();
+ } else {
+ openDirectoryChooser();
+ }
+ });
+ if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M) {
+ int readPermission = ActivityCompat.checkSelfPermission(this,
+ Manifest.permission.READ_EXTERNAL_STORAGE);
+ int writePermission = ActivityCompat.checkSelfPermission(this,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ if (readPermission != PackageManager.PERMISSION_GRANTED ||
+ writePermission != PackageManager.PERMISSION_GRANTED) {
+ requestPermission();
+ }
+ }
+ }
+
+ private void requestPermission() {
+ ActivityCompat.requestPermissions(this, EXTERNAL_STORAGE_PERMISSIONS,
+ PERMISSION_REQUEST_EXTERNAL_STORAGE);
+ }
+
+ private void openDirectoryChooser() {
+ Intent intent = new Intent(this, DirectoryChooserActivity.class);
+ startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String[] permissions,
+ int[] grantResults) {
+ if (requestCode != PERMISSION_REQUEST_EXTERNAL_STORAGE || grantResults.length != 2) {
+ return;
+ }
+ if (grantResults[0] != PackageManager.PERMISSION_GRANTED ||
+ grantResults[1] != PackageManager.PERMISSION_GRANTED) {
+ new MaterialDialog.Builder(this)
+ .content(R.string.choose_data_directory_permission_rationale)
+ .positiveText(android.R.string.ok)
+ .onPositive((dialog, which) -> requestPermission())
+ .onNegative((dialog, which) -> finish())
+ .show();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (StorageUtils.storageAvailable()) {
+ leaveErrorState();
+ } else {
+ registerReceiver(mediaUpdate, new IntentFilter(Intent.ACTION_MEDIA_MOUNTED));
+ }
+ }
@Override
protected void onPause() {
@@ -32,18 +111,102 @@ public class StorageErrorActivity extends ActionBarActivity {
try {
unregisterReceiver(mediaUpdate);
} catch (IllegalArgumentException e) {
-
+ Log.e(TAG, Log.getStackTraceString(e));
}
}
- @Override
- protected void onResume() {
- super.onResume();
- if (StorageUtils.storageAvailable()) {
- leaveErrorState();
- } else {
- registerReceiver(mediaUpdate, new IntentFilter(
- Intent.ACTION_MEDIA_MOUNTED));
+ // see PreferenceController.showChooseDataFolderDialog()
+ private void showChooseDataFolderDialog() {
+ File dataFolder = UserPreferences.getDataFolder(null);
+ if(dataFolder == null) {
+ new MaterialDialog.Builder(this)
+ .title(R.string.error_label)
+ .content(R.string.external_storage_error_msg)
+ .neutralText(android.R.string.ok)
+ .show();
+ return;
+ }
+ String dataFolderPath = dataFolder.getAbsolutePath();
+ int selectedIndex = -1;
+ File[] mediaDirs = ContextCompat.getExternalFilesDirs(this, null);
+ List<String> folders = new ArrayList<>(mediaDirs.length);
+ List<CharSequence> choices = new ArrayList<>(mediaDirs.length);
+ for(int i=0; i < mediaDirs.length; i++) {
+ if(mediaDirs[i] == null) {
+ continue;
+ }
+ String path = mediaDirs[i].getAbsolutePath();
+ folders.add(path);
+ if(dataFolderPath.equals(path)) {
+ selectedIndex = i;
+ }
+ int index = path.indexOf("Android");
+ String choice;
+ if(index >= 0) {
+ choice = path.substring(0, index);
+ } else {
+ choice = path;
+ }
+ long bytes = StorageUtils.getFreeSpaceAvailable(path);
+ String freeSpace = String.format(getString(R.string.free_space_label),
+ Converter.byteToString(bytes));
+ choices.add(Html.fromHtml("<html><small>" + choice + " [" + freeSpace + "]"
+ + "</small></html>"));
+ }
+ if(choices.size() == 0) {
+ new MaterialDialog.Builder(this)
+ .title(R.string.error_label)
+ .content(R.string.external_storage_error_msg)
+ .neutralText(android.R.string.ok)
+ .show();
+ return;
+ }
+ MaterialDialog dialog = new MaterialDialog.Builder(this)
+ .title(R.string.choose_data_directory)
+ .content(R.string.choose_data_directory_message)
+ .items(choices.toArray(new CharSequence[choices.size()]))
+ .itemsCallbackSingleChoice(selectedIndex, (dialog1, itemView, which, text) -> {
+ String folder = folders.get(which);
+ UserPreferences.setDataFolder(folder);
+ leaveErrorState();
+ return true;
+ })
+ .negativeText(R.string.cancel_label)
+ .cancelable(true)
+ .build();
+ dialog.show();
+ }
+
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == Activity.RESULT_OK &&
+ requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
+ String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
+
+ File path;
+ if (dir != null) {
+ path = new File(dir);
+ } else {
+ path = getExternalFilesDir(null);
+ }
+ String message = null;
+ if(!path.exists()) {
+ message = String.format(getString(R.string.folder_does_not_exist_error), dir);
+ } else if(!path.canRead()) {
+ message = String.format(getString(R.string.folder_not_readable_error), dir);
+ } else if(!path.canWrite()) {
+ message = String.format(getString(R.string.folder_not_writable_error), dir);
+ }
+
+ if(message == null) {
+ Log.d(TAG, "Setting data folder: " + dir);
+ UserPreferences.setDataFolder(dir);
+ leaveErrorState();
+ } else {
+ AlertDialog.Builder ab = new AlertDialog.Builder(this);
+ ab.setMessage(message);
+ ab.setPositiveButton(android.R.string.ok, null);
+ ab.show();
+ }
}
}
@@ -58,13 +221,10 @@ public class StorageErrorActivity extends ActionBarActivity {
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) {
if (intent.getBooleanExtra("read-only", true)) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Media was mounted; Finishing activity");
+ Log.d(TAG, "Media was mounted; Finishing activity");
leaveErrorState();
} else {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Media seemed to have been mounted read only");
+ Log.d(TAG, "Media seemed to have been mounted read only");
}
}
}
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 582538fb8..893c92907 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -13,8 +13,6 @@ import android.widget.Toast;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconButton;
-import java.util.Date;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.Feed;
@@ -123,9 +121,8 @@ public class DownloadLogAdapter extends BaseAdapter {
if(holder.typeId == Feed.FEEDFILETYPE_FEED) {
Feed feed = DBReader.getFeed(holder.id);
if (feed != null) {
- feed.setLastUpdate(new Date(0)); // force refresh
try {
- DBTasks.refreshFeed(context, feed);
+ DBTasks.forceRefreshFeed(context, feed);
} catch (DownloadRequestException e) {
e.printStackTrace();
}
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 ca747b9b0..53dedd496 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
@@ -25,13 +25,10 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
private final Context context;
private final ItemAccess itemAccess;
- private final int imageSize;
-
public DownloadedEpisodesListAdapter(Context context, ItemAccess itemAccess) {
super();
this.context = context;
this.itemAccess = itemAccess;
- this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length_downloaded_item);
}
@Override
@@ -52,7 +49,7 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder;
- final FeedItem item = (FeedItem) getItem(position);
+ final FeedItem item = getItem(position);
if (item == null) return null;
if (convertView == null) {
@@ -61,44 +58,44 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloaded_episodeslist_item,
parent, false);
+ holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ holder.txtvSize = (TextView) convertView.findViewById(R.id.txtvSize);
+ holder.queueStatus = (ImageView) convertView.findViewById(R.id.imgvInPlaylist);
holder.pubDate = (TextView) convertView
.findViewById(R.id.txtvPublished);
holder.butSecondary = (ImageButton) convertView
.findViewById(R.id.butSecondaryAction);
- holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
- holder.txtvSize = (TextView) convertView.findViewById(R.id.txtvSize);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
+ Glide.with(context)
+ .load(item.getImageUri())
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
+ .into(holder.imageView);
+
holder.title.setText(item.getTitle());
+ holder.txtvSize.setText(Converter.byteToString(item.getMedia().getSize()));
+ holder.queueStatus.setVisibility(item.isTagged(FeedItem.TAG_QUEUE) ? View.VISIBLE : View.GONE);
String pubDateStr = DateUtils.formatAbbrev(context, item.getPubDate());
holder.pubDate.setText(pubDateStr);
- holder.txtvSize.setText(Converter.byteToString(item.getMedia().getSize()));
- FeedItem.State state = item.getState();
+ FeedItem.State state = item.getState();
if (state == FeedItem.State.PLAYING) {
holder.butSecondary.setEnabled(false);
} else {
holder.butSecondary.setEnabled(true);
}
-
holder.butSecondary.setFocusable(false);
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
-
- Glide.with(context)
- .load(item.getImageUri())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
- .into(holder.imageView);
-
return convertView;
}
@@ -112,10 +109,11 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
static class Holder {
- TextView title;
- TextView pubDate;
ImageView imageView;
+ TextView title;
TextView txtvSize;
+ ImageView queueStatus;
+ TextView pubDate;
ImageButton butSecondary;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
index 9011c8b02..5b205e91f 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
@@ -6,11 +6,13 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
import java.util.List;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.util.DateUtils;
+
/**
* List adapter for showing a list of FeedItems with their title and description.
*/
@@ -33,6 +35,7 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.itemdescription_listitem, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ holder.pubDate = (TextView) convertView.findViewById(R.id.txtvPubDate);
holder.description = (TextView) convertView.findViewById(R.id.txtvDescription);
convertView.setTag(holder);
@@ -41,15 +44,20 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
}
holder.title.setText(item.getTitle());
+ holder.pubDate.setText(DateUtils.formatAbbrev(getContext(), item.getPubDate()));
if (item.getDescription() != null) {
- holder.description.setText(item.getDescription());
+ String description = item.getDescription()
+ .replaceAll("\n", " ")
+ .replaceAll("\\s+", " ")
+ .trim();
+ holder.description.setText(description);
}
-
return convertView;
}
static class Holder {
TextView title;
+ TextView pubDate;
TextView description;
}
}
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 d43e30d8f..269614d35 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -228,7 +228,11 @@ public class NavListAdapter extends BaseAdapter
}
} else if(tag.equals(DownloadsFragment.TAG) && UserPreferences.isEnableAutodownload()) {
int epCacheSize = UserPreferences.getEpisodeCacheSize();
- if(itemAccess.getNumberOfDownloadedItems() >= epCacheSize) {
+ // don't count episodes that can be reclaimed
+ int spaceUsed = itemAccess.getNumberOfDownloadedItems() -
+ itemAccess.getReclaimableItems();
+
+ if (spaceUsed >= epCacheSize) {
holder.count.setText("{md-disc-full 150%}");
Iconify.addIcons(holder.count);
holder.count.setVisibility(View.VISIBLE);
@@ -335,6 +339,7 @@ public class NavListAdapter extends BaseAdapter
int getQueueSize();
int getNumberOfNewItems();
int getNumberOfDownloadedItems();
+ int getReclaimableItems();
int getFeedCounter(long feedId);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java
index 00327bce0..cc27b6c9d 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java
@@ -6,7 +6,6 @@ import android.content.Context;
import android.os.AsyncTask;
import java.util.Arrays;
-import java.util.Date;
import de.danoeh.antennapod.activity.OpmlImportHolder;
import de.danoeh.antennapod.core.R;
@@ -47,7 +46,7 @@ public class OpmlFeedQueuer extends AsyncTask<Void, Void, Void> {
for (int idx = 0; idx < selection.length; idx++) {
OpmlElement element = OpmlImportHolder.getReadElements().get(
selection[idx]);
- Feed feed = new Feed(element.getXmlUrl(), new Date(0),
+ Feed feed = new Feed(element.getXmlUrl(), null,
element.getText());
try {
requester.downloadFeed(context.getApplicationContext(), feed);
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
index 6432ebd4e..ac7a9efee 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.dialog;
import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
@@ -18,10 +17,6 @@ import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
-import com.joanzapata.iconify.Icon;
-import com.joanzapata.iconify.IconDrawable;
-import com.joanzapata.iconify.fonts.FontAwesomeIcons;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -39,6 +34,14 @@ public class EpisodesApplyActionFragment extends Fragment {
public String TAG = "EpisodeActionFragment";
+ public static final int ACTION_QUEUE = 1;
+ public static final int ACTION_MARK_PLAYED = 2;
+ public static final int ACTION_MARK_UNPLAYED = 4;
+ public static final int ACTION_DOWNLOAD = 8;
+ public static final int ACTION_REMOVE = 16;
+ public static final int ACTION_ALL = ACTION_QUEUE | ACTION_MARK_PLAYED | ACTION_MARK_UNPLAYED
+ | ACTION_DOWNLOAD | ACTION_REMOVE;
+
private ListView mListView;
private ArrayAdapter<String> mAdapter;
@@ -48,27 +51,26 @@ public class EpisodesApplyActionFragment extends Fragment {
private Button btnDownload;
private Button btnDelete;
- private final Map<Long,FeedItem> idMap;
- private final List<FeedItem> episodes;
+ private final Map<Long,FeedItem> idMap = new ArrayMap<>();
+ private final List<FeedItem> episodes = new ArrayList<>();
+ private int actions;
private final List<String> titles = new ArrayList();
private final LongList checkedIds = new LongList();
private MenuItem mSelectToggle;
- private int textColor;
-
- public EpisodesApplyActionFragment() {
- this.episodes = new ArrayList<>();
- this.idMap = new ArrayMap<>();
+ public static EpisodesApplyActionFragment newInstance(List<FeedItem> items) {
+ return newInstance(items, ACTION_ALL);
}
- public void setEpisodes(List<FeedItem> episodes) {
- this.episodes.clear();
- this.episodes.addAll(episodes);
- this.idMap.clear();
- for(FeedItem episode : episodes) {
- this.idMap.put(episode.getId(), episode);
+ public static EpisodesApplyActionFragment newInstance(List<FeedItem> items, int actions) {
+ EpisodesApplyActionFragment f = new EpisodesApplyActionFragment();
+ f.episodes.addAll(items);
+ for(FeedItem episode : items) {
+ f.idMap.put(episode.getId(), episode);
}
+ f.actions = actions;
+ return f;
}
@Override
@@ -103,16 +105,48 @@ public class EpisodesApplyActionFragment extends Fragment {
mListView.setAdapter(mAdapter);
checkAll();
+ int lastVisibleDiv = 0;
btnAddToQueue = (Button) view.findViewById(R.id.btnAddToQueue);
- btnAddToQueue.setOnClickListener(v -> queueChecked());
+ if((actions & ACTION_QUEUE) != 0) {
+ btnAddToQueue.setOnClickListener(v -> queueChecked());
+ lastVisibleDiv = R.id.divider1;
+ } else {
+ btnAddToQueue.setVisibility(View.GONE);
+ view.findViewById(R.id.divider1).setVisibility(View.GONE);
+ }
btnMarkAsPlayed = (Button) view.findViewById(R.id.btnMarkAsPlayed);
- btnMarkAsPlayed.setOnClickListener(v -> markedCheckedPlayed());
+ if((actions & ACTION_MARK_PLAYED) != 0) {
+ btnMarkAsPlayed.setOnClickListener(v -> markedCheckedPlayed());
+ lastVisibleDiv = R.id.divider2;
+ } else {
+ btnMarkAsPlayed.setVisibility(View.GONE);
+ view.findViewById(R.id.divider2).setVisibility(View.GONE);
+ }
btnMarkAsUnplayed = (Button) view.findViewById(R.id.btnMarkAsUnplayed);
- btnMarkAsUnplayed.setOnClickListener(v -> markedCheckedUnplayed());
+ if((actions & ACTION_MARK_UNPLAYED) != 0) {
+ btnMarkAsUnplayed.setOnClickListener(v -> markedCheckedUnplayed());
+ lastVisibleDiv = R.id.divider3;
+ } else {
+ btnMarkAsUnplayed.setVisibility(View.GONE);
+ view.findViewById(R.id.divider3).setVisibility(View.GONE);
+ }
btnDownload = (Button) view.findViewById(R.id.btnDownload);
- btnDownload.setOnClickListener(v -> downloadChecked());
+ if((actions & ACTION_DOWNLOAD) != 0) {
+ btnDownload.setOnClickListener(v -> downloadChecked());
+ lastVisibleDiv = R.id.divider4;
+ } else {
+ btnDownload.setVisibility(View.GONE);
+ view.findViewById(R.id.divider4).setVisibility(View.GONE);
+ }
btnDelete = (Button) view.findViewById(R.id.btnDelete);
- btnDelete.setOnClickListener(v -> deleteChecked());
+ if((actions & ACTION_REMOVE) != 0) {
+ btnDelete.setOnClickListener(v -> deleteChecked());
+ } else {
+ btnDelete.setVisibility(View.GONE);
+ if(lastVisibleDiv > 0) {
+ view.findViewById(lastVisibleDiv).setVisibility(View.GONE);
+ }
+ }
return view;
}
@@ -122,11 +156,6 @@ public class EpisodesApplyActionFragment extends Fragment {
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();
-
mSelectToggle = menu.findItem(R.id.select_toggle);
mSelectToggle.setOnMenuItemClickListener(item -> {
if (checkedIds.size() == episodes.size()) {
@@ -140,22 +169,21 @@ public class EpisodesApplyActionFragment extends Fragment {
@Override
public void onPrepareOptionsMenu (Menu menu) {
- /*
- * Prepare icon for select toggle button
- */
+ // Prepare icon for select toggle button
- // Find icon attribute
int[] icon = new int[1];
- if(checkedIds.size() == episodes.size()) icon[0] = R.attr.ic_check_box;
- else if(checkedIds.size() == 0) icon[0] = R.attr.ic_check_box_outline;
- else icon[0] = R.attr.ic_indeterminate_check_box;
+ if (checkedIds.size() == episodes.size()) {
+ icon[0] = R.attr.ic_check_box;
+ } else if (checkedIds.size() == 0) {
+ icon[0] = R.attr.ic_check_box_outline;
+ } else {
+ icon[0] = R.attr.ic_indeterminate_check_box;
+ }
- // Get Drawable from attribute
TypedArray a = getActivity().obtainStyledAttributes(icon);
Drawable iconDrawable = a.getDrawable(0);
a.recycle();
- // Set icon
mSelectToggle.setIcon(iconDrawable);
}
@@ -189,6 +217,14 @@ public class EpisodesApplyActionFragment extends Fragment {
checkDownloaded(false);
resId = R.string.selected_not_downloaded_label;
break;
+ case R.id.check_queued:
+ checkQueued(true);
+ resId = R.string.selected_queued_label;
+ break;
+ case R.id.check_not_queued:
+ checkQueued(false);
+ resId = R.string.selected_not_queued_label;
+ break;
case R.id.sort_title_a_z:
sortByTitle(false);
return true;
@@ -310,6 +346,17 @@ public class EpisodesApplyActionFragment extends Fragment {
refreshCheckboxes();
}
+ private void checkQueued(boolean isQueued) {
+ for (FeedItem episode : episodes) {
+ if(episode.isTagged(FeedItem.TAG_QUEUE) == isQueued) {
+ checkedIds.add(episode.getId());
+ } else {
+ checkedIds.remove(episode.getId());
+ }
+ }
+ refreshCheckboxes();
+ }
+
private void refreshTitles() {
titles.clear();
for(FeedItem episode : episodes) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
index ce1d753e8..96abdd835 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -68,7 +68,7 @@ public class ChaptersFragment extends ListFragment implements AudioplayerContent
this.media = media;
adapter.setMedia(media);
adapter.notifyDataSetChanged();
- if(media.getChapters() == null) {
+ if(media == null || media.getChapters() == null || media.getChapters().size() == 0) {
setEmptyText(getString(R.string.no_items_label));
} else {
setEmptyText(null);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
index 954c4c9e2..a53c85f1c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -1,12 +1,20 @@
package de.danoeh.antennapod.fragment;
-import android.app.Activity;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
+import com.joanzapata.iconify.IconDrawable;
+import com.joanzapata.iconify.fonts.FontAwesomeIcons;
+
import java.util.List;
import de.danoeh.antennapod.R;
@@ -14,8 +22,10 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DownloadedEpisodesListAdapter;
import de.danoeh.antennapod.core.feed.EventDistributor;
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.storage.DBWriter;
+import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
@@ -28,22 +38,21 @@ public class CompletedDownloadsFragment extends ListFragment {
private static final String TAG = CompletedDownloadsFragment.class.getSimpleName();
- private static final int EVENTS =
- EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.DOWNLOADLOG_UPDATE |
- EventDistributor.UNREAD_ITEMS_UPDATE;
+ private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
+ EventDistributor.DOWNLOADLOG_UPDATE |
+ EventDistributor.UNREAD_ITEMS_UPDATE;
private List<FeedItem> items;
private DownloadedEpisodesListAdapter listAdapter;
private boolean viewCreated = false;
- private boolean itemsLoaded = false;
private Subscription subscription;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
loadItems();
}
@@ -81,9 +90,9 @@ public class CompletedDownloadsFragment extends ListFragment {
}
@Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- if (viewCreated && itemsLoaded) {
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ if (viewCreated && items != null) {
onFragmentLoaded();
}
}
@@ -99,7 +108,7 @@ public class CompletedDownloadsFragment extends ListFragment {
lv.setPadding(0, vertPadding, 0, vertPadding);
viewCreated = true;
- if (itemsLoaded && getActivity() != null) {
+ if (items != null && getActivity() != null) {
onFragmentLoaded();
}
}
@@ -111,7 +120,6 @@ public class CompletedDownloadsFragment extends ListFragment {
if (item != null) {
((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(item.getId()));
}
-
}
private void onFragmentLoaded() {
@@ -121,6 +129,43 @@ public class CompletedDownloadsFragment extends ListFragment {
}
setListShown(true);
listAdapter.notifyDataSetChanged();
+ getActivity().supportInvalidateOptionsMenu();
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if(!isAdded()) {
+ return;
+ }
+ super.onCreateOptionsMenu(menu, inflater);
+ if(items != null) {
+ inflater.inflate(R.menu.downloads_completed, menu);
+ MenuItem episodeActions = menu.findItem(R.id.episode_actions);
+ if(items.size() > 0) {
+ int[] attrs = {R.attr.action_bar_icon_color};
+ TypedArray ta = getActivity().obtainStyledAttributes(UserPreferences.getTheme(), attrs);
+ int textColor = ta.getColor(0, Color.GRAY);
+ ta.recycle();
+ episodeActions.setIcon(new IconDrawable(getActivity(),
+ FontAwesomeIcons.fa_gears).color(textColor).actionBarSize());
+ episodeActions.setVisible(true);
+ } else {
+ episodeActions.setVisible(false);
+ }
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.episode_actions:
+ EpisodesApplyActionFragment fragment = EpisodesApplyActionFragment
+ .newInstance(items, EpisodesApplyActionFragment.ACTION_REMOVE);
+ ((MainActivity) getActivity()).loadChildFragment(fragment);
+ return true;
+ default:
+ return false;
+ }
}
private DownloadedEpisodesListAdapter.ItemAccess itemAccess = new DownloadedEpisodesListAdapter.ItemAccess() {
@@ -157,7 +202,7 @@ public class CompletedDownloadsFragment extends ListFragment {
if(subscription != null) {
subscription.unsubscribe();
}
- if (!itemsLoaded && viewCreated) {
+ if (items == null && viewCreated) {
setListShown(false);
}
subscription = Observable.fromCallable(() -> DBReader.getDownloadedItems())
@@ -166,7 +211,6 @@ public class CompletedDownloadsFragment extends ListFragment {
.subscribe(result -> {
if (result != null) {
items = result;
- itemsLoaded = true;
if (viewCreated && getActivity() != null) {
onFragmentLoaded();
}
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 7a9b73982..303d273bc 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -252,8 +252,8 @@ public class ItemlistFragment extends ListFragment {
if (!FeedMenuHandler.onOptionsItemClicked(getActivity(), item, feed)) {
switch (item.getItemId()) {
case R.id.episode_actions:
- EpisodesApplyActionFragment fragment = new EpisodesApplyActionFragment();
- fragment.setEpisodes(feed.getItems());
+ EpisodesApplyActionFragment fragment = EpisodesApplyActionFragment
+ .newInstance(feed.getItems());
((MainActivity)getActivity()).loadChildFragment(fragment);
return true;
case R.id.remove_item:
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 830e9d419..38030f4ea 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -63,10 +63,10 @@ public class FeedMenuHandler {
final Feed selectedFeed) throws DownloadRequestException {
switch (item.getItemId()) {
case R.id.refresh_item:
- DBTasks.refreshFeed(context, selectedFeed);
+ DBTasks.forceRefreshFeed(context, selectedFeed);
break;
case R.id.refresh_complete_item:
- DBTasks.refreshCompleteFeed(context, selectedFeed);
+ DBTasks.forceRefreshCompleteFeed(context, selectedFeed);
break;
case R.id.filter_items:
showFilterDialog(context, selectedFeed);
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 785944768..c563d278f 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
@@ -1,12 +1,13 @@
package de.danoeh.antennapod.preferences;
+import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.TimePickerDialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.net.wifi.WifiConfiguration;
@@ -18,6 +19,7 @@ import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
+import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
@@ -85,6 +87,11 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
private CheckBoxPreference[] selectedNetworks;
+ private static final String[] EXTERNAL_STORAGE_PERMISSIONS = {
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE };
+ private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41;
+
public PreferenceController(PreferenceUI ui) {
this.ui = ui;
PreferenceManager.getDefaultSharedPreferences(ui.getActivity().getApplicationContext())
@@ -121,54 +128,51 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
// disable expanded notification option on unsupported android versions
ui.findPreference(PreferenceController.PREF_EXPANDED_NOTIFICATION).setEnabled(false);
ui.findPreference(PreferenceController.PREF_EXPANDED_NOTIFICATION).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- Toast toast = Toast.makeText(activity, R.string.pref_expand_notify_unsupport_toast, Toast.LENGTH_SHORT);
- toast.show();
- return true;
- }
+ preference -> {
+ Toast toast = Toast.makeText(activity,
+ R.string.pref_expand_notify_unsupport_toast, Toast.LENGTH_SHORT);
+ toast.show();
+ return true;
}
);
}
-
ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- FlattrUtils.revokeAccessToken(activity);
- checkItemVisibility();
- return true;
- }
-
+ preference -> {
+ FlattrUtils.revokeAccessToken(activity);
+ checkItemVisibility();
+ return true;
}
);
-
ui.findPreference(PreferenceController.PREF_ABOUT).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- activity.startActivity(new Intent(
- activity, AboutActivity.class));
- return true;
- }
-
+ preference -> {
+ activity.startActivity(new Intent(activity, AboutActivity.class));
+ return true;
}
);
-
ui.findPreference(PreferenceController.PREF_OPML_EXPORT).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- new OpmlExportWorker(activity)
- .executeAsync();
-
- return true;
+ preference -> {
+ new OpmlExportWorker(activity).executeAsync();
+ return true;
+ }
+ );
+ ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener(
+ preference -> {
+ if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
+ Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ showChooseDataFolderDialog();
+ } else {
+ int readPermission = ActivityCompat.checkSelfPermission(
+ activity, Manifest.permission.READ_EXTERNAL_STORAGE);
+ int writePermission = ActivityCompat.checkSelfPermission(
+ activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ if (readPermission == PackageManager.PERMISSION_GRANTED &&
+ writePermission == PackageManager.PERMISSION_GRANTED) {
+ openDirectoryChooser();
+ } else {
+ requestPermission();
+ }
}
+ return true;
}
);
ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR)
@@ -185,40 +189,29 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
}
return true;
}
- }
- );
+ }
+ );
ui.findPreference(UserPreferences.PREF_THEME)
.setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
-
- @Override
- public boolean onPreferenceChange(
- Preference preference, Object newValue) {
- Intent i = new Intent(activity, MainActivity.class);
- i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- activity.finish();
- activity.startActivity(i);
- return true;
- }
+ (preference, newValue) -> {
+ Intent i = new Intent(activity, MainActivity.class);
+ i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ activity.finish();
+ activity.startActivity(i);
+ return true;
}
);
ui.findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)
- .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- showDrawerPreferencesDialog();
- return true;
- }
+ .setOnPreferenceClickListener(preference -> {
+ showDrawerPreferencesDialog();
+ return true;
});
ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL)
- .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- showUpdateIntervalTimePreferencesDialog();
- return true;
- }
+ .setOnPreferenceClickListener(preference -> {
+ showUpdateIntervalTimePreferencesDialog();
+ return true;
});
ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL).setOnPreferenceChangeListener(
@@ -234,38 +227,30 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
});
ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER)
.setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
-
- @Override
- public boolean onPreferenceChange(
- Preference preference, Object newValue) {
- if (newValue instanceof Boolean) {
- setSelectedNetworksEnabled((Boolean) newValue);
- return true;
- } else {
- return false;
- }
+ (preference, newValue) -> {
+ if (newValue instanceof Boolean) {
+ setSelectedNetworksEnabled((Boolean) newValue);
+ return true;
+ } else {
+ return false;
}
}
);
ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)
.setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object o) {
- if (o instanceof String) {
- try {
- int value = Integer.valueOf((String) o);
- if (1 <= value && value <= 50) {
- setParallelDownloadsText(value);
- return true;
- }
- } catch (NumberFormatException e) {
- return false;
+ (preference, o) -> {
+ if (o instanceof String) {
+ try {
+ int value = Integer.valueOf((String) o);
+ if (1 <= value && value <= 50) {
+ setParallelDownloadsText(value);
+ return true;
}
+ } catch (NumberFormatException e) {
+ return false;
}
- return false;
}
+ return false;
}
);
// validate and set correct value: number of downloads between 1 and 50 (inclusive)
@@ -298,102 +283,79 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
});
ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE)
.setOnPreferenceChangeListener(
- new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object o) {
- if (o instanceof String) {
- setEpisodeCacheSizeText(UserPreferences.readEpisodeCacheSize((String) o));
- }
- return true;
+ (preference, o) -> {
+ if (o instanceof String) {
+ setEpisodeCacheSizeText(UserPreferences.readEpisodeCacheSize((String) o));
}
+ return true;
}
);
ui.findPreference(PreferenceController.PREF_PLAYBACK_SPEED_LAUNCHER)
- .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- VariableSpeedDialog.showDialog(activity);
- return true;
- }
+ .setOnPreferenceClickListener(preference -> {
+ VariableSpeedDialog.showDialog(activity);
+ return true;
});
- ui.findPreference(PreferenceController.PREF_GPODNET_SETLOGIN_INFORMATION).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- AuthenticationDialog dialog = new AuthenticationDialog(activity,
- R.string.pref_gpodnet_setlogin_information_title, false, false, GpodnetPreferences.getUsername(),
- null) {
-
- @Override
- protected void onConfirmed(String username, String password, boolean saveUsernamePassword) {
- GpodnetPreferences.setPassword(password);
- }
- };
- dialog.show();
- return true;
- }
- });
- ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- GpodnetPreferences.logout();
- Toast toast = Toast.makeText(activity, R.string.pref_gpodnet_logout_toast, Toast.LENGTH_SHORT);
- toast.show();
- updateGpodnetPreferenceScreen();
- return true;
- }
- });
- ui.findPreference(PreferenceController.PREF_GPODNET_HOSTNAME).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface dialog) {
- updateGpodnetPreferenceScreen();
- }
+ ui.findPreference(PreferenceController.PREF_GPODNET_SETLOGIN_INFORMATION)
+ .setOnPreferenceClickListener(preference -> {
+ AuthenticationDialog dialog = new AuthenticationDialog(activity,
+ R.string.pref_gpodnet_setlogin_information_title, false, false, GpodnetPreferences.getUsername(),
+ null) {
+
+ @Override
+ protected void onConfirmed(String username, String password, boolean saveUsernamePassword) {
+ GpodnetPreferences.setPassword(password);
+ }
+ };
+ dialog.show();
+ return true;
+ });
+ ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(
+ preference -> {
+ GpodnetPreferences.logout();
+ Toast toast = Toast.makeText(activity, R.string.pref_gpodnet_logout_toast, Toast.LENGTH_SHORT);
+ toast.show();
+ updateGpodnetPreferenceScreen();
+ return true;
+ });
+ ui.findPreference(PreferenceController.PREF_GPODNET_HOSTNAME).setOnPreferenceClickListener(
+ preference -> {
+ GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(dialog -> updateGpodnetPreferenceScreen());
+ return true;
});
- return true;
- }
- });
- ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
- @Override
- public boolean onPreferenceClick(Preference preference) {
- AutoFlattrPreferenceDialog.newAutoFlattrPreferenceDialog(activity,
- new AutoFlattrPreferenceDialog.AutoFlattrPreferenceDialogInterface() {
- @Override
- public void onCancelled() {
+ ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS)
+ .setOnPreferenceClickListener(preference -> {
+ AutoFlattrPreferenceDialog.newAutoFlattrPreferenceDialog(activity,
+ new AutoFlattrPreferenceDialog.AutoFlattrPreferenceDialogInterface() {
+ @Override
+ public void onCancelled() {
- }
+ }
- @Override
- public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) {
- UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue);
- checkItemVisibility();
- }
- });
- return true;
- }
- });
- 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;
+ @Override
+ public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) {
+ UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue);
+ checkItemVisibility();
}
- return false;
- }
+ });
+ return true;
+ });
+ ui.findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE).setOnPreferenceChangeListener(
+ (preference, 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;
+ }
+ );
ui.findPreference("prefSendCrashReport").setOnPreferenceClickListener(preference -> {
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType("text/plain");
@@ -427,7 +389,12 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
- File path = new File(dir);
+ File path;
+ if(dir != null) {
+ path = new File(dir);
+ } else {
+ path = ui.getActivity().getExternalFilesDir(null);
+ }
String message = null;
final Context context= ui.getActivity().getApplicationContext();
if(!path.exists()) {
@@ -628,35 +595,31 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
List<String> prefValues = Arrays.asList(UserPreferences
.getAutodownloadSelectedNetworks());
PreferenceScreen prefScreen = (PreferenceScreen) ui.findPreference(PreferenceController.AUTO_DL_PREF_SCREEN);
- Preference.OnPreferenceClickListener clickListener = new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- if (preference instanceof CheckBoxPreference) {
- String key = preference.getKey();
- ArrayList<String> prefValuesList = new ArrayList<String>(
- Arrays.asList(UserPreferences
- .getAutodownloadSelectedNetworks())
- );
- boolean newValue = ((CheckBoxPreference) preference)
- .isChecked();
- Log.d(TAG, "Selected network " + key + ". New state: " + newValue);
-
- int index = prefValuesList.indexOf(key);
- if (index >= 0 && newValue == false) {
- // remove network
- prefValuesList.remove(index);
- } else if (index < 0 && newValue == true) {
- prefValuesList.add(key);
- }
-
- UserPreferences.setAutodownloadSelectedNetworks(
- prefValuesList.toArray(new String[prefValuesList.size()])
- );
- return true;
- } else {
- return false;
+ Preference.OnPreferenceClickListener clickListener = preference -> {
+ if (preference instanceof CheckBoxPreference) {
+ String key = preference.getKey();
+ ArrayList<String> prefValuesList = new ArrayList<String>(
+ Arrays.asList(UserPreferences
+ .getAutodownloadSelectedNetworks())
+ );
+ boolean newValue = ((CheckBoxPreference) preference)
+ .isChecked();
+ Log.d(TAG, "Selected network " + key + ". New state: " + newValue);
+
+ int index = prefValuesList.indexOf(key);
+ if (index >= 0 && newValue == false) {
+ // remove network
+ prefValuesList.remove(index);
+ } else if (index < 0 && newValue == true) {
+ prefValuesList.add(key);
}
+
+ UserPreferences.setAutodownloadSelectedNetworks(
+ prefValuesList.toArray(new String[prefValuesList.size()])
+ );
+ return true;
+ } else {
+ return false;
}
};
// create preference for each known network. attach listener and set
@@ -703,7 +666,6 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
checked[i] = true;
}
}
-
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.drawer_preferences);
builder.setMultiChoiceItems(navTitles, checked, (dialog, which, isChecked) -> {
@@ -713,16 +675,26 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]);
}
});
- builder.setPositiveButton(R.string.confirm_label, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- UserPreferences.setHiddenDrawerItems(hiddenDrawerItems);
- }
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ UserPreferences.setHiddenDrawerItems(hiddenDrawerItems);
});
builder.setNegativeButton(R.string.cancel_label, null);
builder.create().show();
}
+ // CHOOSE DATA FOLDER
+
+ private void requestPermission() {
+ ActivityCompat.requestPermissions(ui.getActivity(), EXTERNAL_STORAGE_PERMISSIONS,
+ PERMISSION_REQUEST_EXTERNAL_STORAGE);
+ }
+
+ private void openDirectoryChooser() {
+ Activity activity = ui.getActivity();
+ Intent intent = new Intent(activity, DirectoryChooserActivity.class);
+ activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+
private void showChooseDataFolderDialog() {
Context context = ui.getActivity();
File dataFolder = UserPreferences.getDataFolder(null);
@@ -786,6 +758,8 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
dialog.show();
}
+ // UPDATE TIME/INTERVAL DIALOG
+
private void showUpdateIntervalTimePreferencesDialog() {
final Context context = ui.getActivity();
@@ -795,38 +769,34 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
builder.positiveText(R.string.pref_autoUpdateIntervallOrTime_Interval);
builder.negativeText(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay);
builder.neutralText(R.string.pref_autoUpdateIntervallOrTime_Disable);
- builder.callback(new MaterialDialog.ButtonCallback() {
- @Override
- public void onPositive(MaterialDialog dialog) {
- 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);
- long currInterval = UserPreferences.getUpdateInterval();
- int checkedItem = -1;
- if(currInterval > 0) {
- String currIntervalStr = String.valueOf(TimeUnit.MILLISECONDS.toHours(currInterval));
- checkedItem = ArrayUtils.indexOf(values, currIntervalStr);
- }
- builder.setSingleChoiceItems(entries, checkedItem, (dialog1, which) -> {
- int hours = Integer.valueOf(values[which]);
- UserPreferences.setUpdateInterval(hours);
- dialog1.dismiss();
- setUpdateIntervalText();
- });
- builder.setNegativeButton(context.getString(R.string.cancel_label), null);
- builder.show();
+ builder.onPositive((dialog, which) -> {
+ AlertDialog.Builder builder1 = new AlertDialog.Builder(context);
+ builder1.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
+ final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
+ final String[] entries = getUpdateIntervalEntries(values);
+ long currInterval = UserPreferences.getUpdateInterval();
+ int checkedItem = -1;
+ if(currInterval > 0) {
+ String currIntervalStr = String.valueOf(TimeUnit.MILLISECONDS.toHours(currInterval));
+ checkedItem = ArrayUtils.indexOf(values, currIntervalStr);
}
-
- @Override
- public void onNegative(MaterialDialog dialog) {
- int hourOfDay = 7, minute = 0;
- int[] updateTime = UserPreferences.getUpdateTimeOfDay();
- if (updateTime.length == 2) {
- hourOfDay = updateTime[0];
- minute = updateTime[1];
- }
- TimePickerDialog timePickerDialog = new TimePickerDialog(context,
+ builder1.setSingleChoiceItems(entries, checkedItem, (dialog1, which1) -> {
+ int hours = Integer.valueOf(values[which1]);
+ UserPreferences.setUpdateInterval(hours);
+ dialog1.dismiss();
+ setUpdateIntervalText();
+ });
+ builder1.setNegativeButton(context.getString(R.string.cancel_label), null);
+ builder1.show();
+ });
+ builder.onNegative((dialog, 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,
(view, selectedHourOfDay, selectedMinute) -> {
if (view.getTag() == null) { // onTimeSet() may get called twice!
view.setTag("TAGGED");
@@ -834,17 +804,13 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
setUpdateIntervalText();
}
}, hourOfDay, minute, DateFormat.is24HourFormat(context));
- timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
- timePickerDialog.show();
- }
-
- @Override
- public void onNeutral(MaterialDialog dialog) {
- UserPreferences.setUpdateInterval(0);
- setUpdateIntervalText();
- }
+ timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
+ timePickerDialog.show();
+ });
+ builder.onNeutral((dialog, which) -> {
+ UserPreferences.setUpdateInterval(0);
+ setUpdateIntervalText();
});
- builder.forceStacking(true);
builder.show();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java b/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java
index ef6330f82..8201fe3e2 100644
--- a/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java
+++ b/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java
@@ -8,7 +8,6 @@ import android.util.Log;
import android.widget.Toast;
import java.util.Arrays;
-import java.util.Date;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
@@ -35,7 +34,7 @@ public class SPAReceiver extends BroadcastReceiver{
if (feedUrls != null) {
if (BuildConfig.DEBUG) Log.d(TAG, "Received feeds list: " + Arrays.toString(feedUrls));
for (String url : feedUrls) {
- Feed f = new Feed(url, new Date(0));
+ Feed f = new Feed(url, null);
try {
DownloadRequester.getInstance().downloadFeed(context, f);
} catch (DownloadRequestException e) {
diff --git a/app/src/main/res/layout/downloaded_episodeslist_item.xml b/app/src/main/res/layout/downloaded_episodeslist_item.xml
index 6b5f7369a..760b6b9db 100644
--- a/app/src/main/res/layout/downloaded_episodeslist_item.xml
+++ b/app/src/main/res/layout/downloaded_episodeslist_item.xml
@@ -22,7 +22,7 @@
<RelativeLayout
android:layout_width="0dp"
- android:layout_height="match_parent"
+ android:layout_height="@dimen/thumbnail_length_downloaded_item"
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
@@ -48,7 +48,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
- android:layout_below="@id/txtvTitle"
+ android:layout_alignParentBottom="true"
tools:text="23 MB"
tools:background="@android:color/holo_green_dark" />
@@ -58,10 +58,23 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
- android:layout_below="@id/txtvTitle"
+ android:layout_alignParentBottom="true"
+ android:layout_marginLeft="8dp"
tools:text="Jan 23"
tools:background="@android:color/holo_green_dark" />
+ <ImageView
+ android:id="@+id/imgvInPlaylist"
+ android:layout_width="@dimen/enc_icons_size"
+ android:layout_height="@dimen/enc_icons_size"
+ android:layout_toLeftOf="@id/txtvPublished"
+ android:layout_alignParentBottom="true"
+ android:contentDescription="@string/in_queue_label"
+ android:src="?attr/stat_playlist"
+ android:visibility="visible"
+ tools:src="@drawable/ic_list_white_24dp"
+ tools:background="@android:color/holo_red_light" />
+
</RelativeLayout>
<include layout="@layout/vertical_list_divider"/>
diff --git a/app/src/main/res/layout/episodes_apply_action_fragment.xml b/app/src/main/res/layout/episodes_apply_action_fragment.xml
index d63088662..e9a2e2e23 100644
--- a/app/src/main/res/layout/episodes_apply_action_fragment.xml
+++ b/app/src/main/res/layout/episodes_apply_action_fragment.xml
@@ -1,16 +1,16 @@
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/bottomBar"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="68dp"
android:layout_alignParentBottom="true"
- android:orientation="horizontal"
android:gravity="center_vertical"
+ android:orientation="horizontal"
android:padding="4dp">
<Button
@@ -18,103 +18,98 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:background="@android:color/transparent"
android:drawableTop="?attr/content_new"
android:text="@string/add_to_queue_label"
- android:textSize="10sp"
- android:background="@android:color/transparent"/>
+ android:textSize="10sp" />
- <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" />
+ <View
+ android:id="@+id/divider1"
+ 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:background="@android:color/transparent"
android:drawableTop="?attr/navigation_accept"
android:text="@string/mark_read_label"
- android:textSize="10sp"
- android:background="@android:color/transparent"/>
+ android:textSize="10sp" />
- <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" />
+ <View
+ android:id="@+id/divider2"
+ 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:background="@android:color/transparent"
android:drawableTop="?attr/navigation_cancel"
android:text="@string/mark_unread_label"
- android:textSize="10sp"
- android:background="@android:color/transparent"/>
+ android:textSize="10sp" />
- <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" />
+ <View
+ android:id="@+id/divider3"
+ 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:background="@android:color/transparent"
android:drawableTop="?attr/av_download"
android:text="@string/download_label"
- android:textSize="10sp"
- android:background="@android:color/transparent"/>
+ android:textSize="10sp" />
- <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" />
+ <View
+ android:id="@+id/divider4"
+ 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:background="@android:color/transparent"
android:drawableTop="?attr/content_discard"
android:text="@string/remove_episode_lable"
- android:textSize="10sp"
- android:background="@android:color/transparent"/>
+ android:textSize="10sp" />
</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" />
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_above="@id/bottomBar"
+ android:background="?android:attr/listDivider"
+ android:paddingBottom="4dp"
+ 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>
-
+ android:layout_above="@id/divider"/>
</RelativeLayout>
diff --git a/app/src/main/res/layout/itemdescription_listitem.xml b/app/src/main/res/layout/itemdescription_listitem.xml
index ca8f974bf..51bc9a5eb 100644
--- a/app/src/main/res/layout/itemdescription_listitem.xml
+++ b/app/src/main/res/layout/itemdescription_listitem.xml
@@ -1,30 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingBottom="8dp"
tools:background="@android:color/holo_orange_light">
<TextView
+ android:id="@+id/txtvPubDate"
+ style="@android:style/TextAppearance.Small"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginLeft="8dp"
+ android:textSize="14sp"
+ android:textColor="?android:textColorSecondary"
+ android:ellipsize="end"
+ android:lines="1"
+ tools:text="22 Jan 2016"
+ tools:background="@android:color/holo_green_dark" />
+
+ <TextView
android:id="@+id/txtvTitle"
- style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_margin="16dp"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_toLeftOf="@id/txtvPubDate"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:ellipsize="end"
+ android:maxLines="2"
tools:text="Feed item title"
tools:background="@android:color/holo_green_dark" />
<TextView
android:id="@+id/txtvDescription"
- style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="16dp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:lines="3"
+ android:layout_below="@id/txtvTitle"
+ android:textSize="14sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:ellipsize="end"
+ android:maxLines="3"
tools:text="Feed item description"
tools:background="@android:color/holo_green_dark" />
-</LinearLayout>
+
+</RelativeLayout>
diff --git a/app/src/main/res/layout/storage_error.xml b/app/src/main/res/layout/storage_error.xml
index c1ee77262..8ff28b3c1 100644
--- a/app/src/main/res/layout/storage_error.xml
+++ b/app/src/main/res/layout/storage_error.xml
@@ -1,25 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center"
+ android:padding="16dp">
<ImageView
android:id="@+id/imageView1"
android:contentDescription="@string/external_storage_error_msg"
- android:layout_width="30dp"
- android:layout_height="30dp"
- android:layout_centerHorizontal="true"
- android:layout_centerVertical="true"
- android:layout_margin="16dp"
- android:src="@android:drawable/stat_notify_sdcard_usb" />
+ android:layout_width="36dp"
+ android:layout_height="36dp"
+ android:layout_margin="8dp"
+ android:src="?attr/ic_sd_storage" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_below="@+id/imageView1"
- android:layout_centerHorizontal="true"
android:layout_margin="8dp"
android:text="@string/external_storage_error_msg" />
-</RelativeLayout> \ No newline at end of file
+ <Button
+ android:id="@+id/btnChooseDataFolder"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:text="@string/choose_data_directory"/>
+
+</LinearLayout>
diff --git a/app/src/main/res/menu/directory_chooser.xml b/app/src/main/res/menu/directory_chooser.xml
index 7735ffd2c..3f860d636 100644
--- a/app/src/main/res/menu/directory_chooser.xml
+++ b/app/src/main/res/menu/directory_chooser.xml
@@ -1,14 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:custom="http://schemas.android.com/apk/res-auto">
+<menu
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:custom="http://schemas.android.com/apk/res-auto">
+
<item
android:id="@+id/new_folder_item"
android:title="@string/create_folder_label"
- custom:showAsAction="ifRoom|withText"/>
+ android:icon="?attr/ic_create_new_folder"
+ custom:showAsAction="ifRoom|withText" />
<item
android:id="@+id/set_to_default_folder_item"
- custom:showAsAction="collapseActionView"
- android:title="@string/set_to_default_folder"/>
-
+ android:title="@string/set_to_default_folder"
+ custom:showAsAction="collapseActionView" />
-</menu> \ No newline at end of file
+</menu>
diff --git a/app/src/main/res/menu/downloads_completed.xml b/app/src/main/res/menu/downloads_completed.xml
new file mode 100644
index 000000000..dc2996893
--- /dev/null
+++ b/app/src/main/res/menu/downloads_completed.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:custom="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/episode_actions"
+ android:menuCategory="container"
+ android:title="@string/episode_actions"
+ custom:showAsAction="always">
+ </item>
+
+</menu>
diff --git a/app/src/main/res/menu/episodes_apply_action_options.xml b/app/src/main/res/menu/episodes_apply_action_options.xml
index 90cba0966..3df88046d 100644
--- a/app/src/main/res/menu/episodes_apply_action_options.xml
+++ b/app/src/main/res/menu/episodes_apply_action_options.xml
@@ -42,6 +42,10 @@
android:title="@string/downloaded_label"/>
<item android:id="@+id/check_not_downloaded"
android:title="@string/not_downloaded_label"/>
+ <item android:id="@+id/check_queued"
+ android:title="@string/queued_label"/>
+ <item android:id="@+id/check_not_queued"
+ android:title="@string/not_queued_label"/>
</menu>
</item>
diff --git a/build.gradle b/build.gradle
index b4609233f..521a691ac 100644
--- a/build.gradle
+++ b/build.gradle
@@ -57,7 +57,7 @@ project.ext {
rxJavaVersion = "1.1.0"
rxJavaRulesVersion = "1.1.0.0"
- audioPlayerVersion = "v1.0.10"
+ audioPlayerVersion = "v1.0.11"
}
task wrapper(type: Wrapper) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
index 5ea0ba904..690fbdfc6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
@@ -156,10 +156,9 @@ public class OpmlBackupAgent extends BackupAgentHelper {
ArrayList<OpmlElement> opmlElements = new OpmlReader().readDocument(reader);
mChecksum = digester == null ? null : digester.digest();
DownloadRequester downloader = DownloadRequester.getInstance();
- Date lastUpdated = new Date();
for (OpmlElement opmlElem : opmlElements) {
- Feed feed = new Feed(opmlElem.getXmlUrl(), lastUpdated, opmlElem.getText());
+ Feed feed = new Feed(opmlElem.getXmlUrl(), null, opmlElem.getText());
try {
downloader.downloadFeed(mContext, feed);
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 4be788f33..d2d7cbc73 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
@@ -44,10 +44,12 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
private String author;
private FeedImage image;
private List<FeedItem> items;
+
/**
- * Date of last refresh.
+ * String that identifies the last update (adopted from Last-Modified or ETag header)
*/
- private Date lastUpdate;
+ private String lastUpdate;
+
private FlattrStatus flattrStatus;
private String paymentLink;
/**
@@ -91,18 +93,14 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
/**
* This constructor is used for restoring a feed from the database.
*/
- public Feed(long id, Date lastUpdate, String title, String link, String description, String paymentLink,
+ public Feed(long id, String lastUpdate, String title, String link, String description, String paymentLink,
String author, String language, String type, String feedIdentifier, FeedImage image, String fileUrl,
String downloadUrl, boolean downloaded, FlattrStatus status, boolean paged, String nextPageLink,
String filter, boolean lastUpdateFailed) {
super(fileUrl, downloadUrl, downloaded);
this.id = id;
this.title = title;
- if (lastUpdate != null) {
- this.lastUpdate = (Date) lastUpdate.clone();
- } else {
- this.lastUpdate = null;
- }
+ this.lastUpdate = lastUpdate;
this.link = link;
this.description = description;
this.paymentLink = paymentLink;
@@ -126,7 +124,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
/**
* This constructor is used for test purposes and uses a default flattr status object.
*/
- public Feed(long id, Date lastUpdate, String title, String link, String description, String paymentLink,
+ public Feed(long id, String lastUpdate, String title, String link, String description, String paymentLink,
String author, String language, String type, String feedIdentifier, FeedImage image, String fileUrl,
String downloadUrl, boolean downloaded) {
this(id, lastUpdate, title, link, description, paymentLink, author, language, type, feedIdentifier, image,
@@ -138,7 +136,6 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
*/
public Feed() {
super();
- lastUpdate = new Date();
this.flattrStatus = new FlattrStatus();
}
@@ -146,9 +143,9 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
* This constructor is used for requesting a feed download (it must not be used for anything else!). It should NOT be
* used if the title of the feed is already known.
*/
- public Feed(String url, Date lastUpdate) {
+ public Feed(String url, String lastUpdate) {
super(null, url, false);
- this.lastUpdate = (lastUpdate != null) ? (Date) lastUpdate.clone() : null;
+ this.lastUpdate = lastUpdate;
this.flattrStatus = new FlattrStatus();
}
@@ -156,7 +153,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
* This constructor is used for requesting a feed download (it must not be used for anything else!). It should be
* used if the title of the feed is already known.
*/
- public Feed(String url, Date lastUpdate, String title) {
+ public Feed(String url, String lastUpdate, String title) {
this(url, lastUpdate);
this.title = title;
this.flattrStatus = new FlattrStatus();
@@ -166,7 +163,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
* This constructor is used for requesting a feed download (it must not be used for anything else!). It should be
* used if the title of the feed is already known.
*/
- public Feed(String url, Date lastUpdate, String title, String username, String password) {
+ public Feed(String url, String lastUpdate, String title, String username, String password) {
this(url, lastUpdate, title);
preferences = new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL, username, password);
}
@@ -191,11 +188,9 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
int indexHide = cursor.getColumnIndex(PodDBAdapter.KEY_HIDE);
int indexLastUpdateFailed = cursor.getColumnIndex(PodDBAdapter.KEY_LAST_UPDATE_FAILED);
- Date lastUpdate = new Date(cursor.getLong(indexLastUpdate));
-
Feed feed = new Feed(
cursor.getLong(indexId),
- lastUpdate,
+ cursor.getString(indexLastUpdate),
cursor.getString(indexTitle),
cursor.getString(indexLink),
cursor.getString(indexDescription),
@@ -430,12 +425,12 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
this.items = list;
}
- public Date getLastUpdate() {
- return (lastUpdate != null) ? (Date) lastUpdate.clone() : null;
+ public String getLastUpdate() {
+ return lastUpdate;
}
- public void setLastUpdate(Date lastUpdate) {
- this.lastUpdate = (lastUpdate != null) ? (Date) lastUpdate.clone() : null;
+ public void setLastUpdate(String lastModified) {
+ this.lastUpdate = lastModified;
}
public String getFeedIdentifier() {
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 0b90cef6c..e7ebff154 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
@@ -175,7 +175,7 @@ public class GpodnetSyncService extends Service {
for (String downloadUrl : changes.getAdded()) {
if (false == localSubscriptions.contains(downloadUrl) &&
false == localRemoved.contains(downloadUrl)) {
- Feed feed = new Feed(downloadUrl, new Date(0));
+ Feed feed = new Feed(downloadUrl, null);
DownloadRequester.getInstance().downloadFeed(this, feed);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java
index bc3006eea..7f40ea6e2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java
@@ -4,6 +4,7 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import de.danoeh.antennapod.core.feed.FeedFile;
import de.danoeh.antennapod.core.util.URLChecker;
@@ -15,7 +16,7 @@ public class DownloadRequest implements Parcelable {
private final String title;
private String username;
private String password;
- private long ifModifiedSince;
+ private String lastModified;
private boolean deleteOnFailure;
private final long feedfileId;
private final int feedfileType;
@@ -60,7 +61,7 @@ public class DownloadRequest implements Parcelable {
this.feedfileType = builder.feedfileType;
this.username = builder.username;
this.password = builder.password;
- this.ifModifiedSince = builder.ifModifiedSince;
+ this.lastModified = builder.lastModified;
this.deleteOnFailure = builder.deleteOnFailure;
this.arguments = (builder.arguments != null) ? builder.arguments : new Bundle();
}
@@ -71,7 +72,7 @@ public class DownloadRequest implements Parcelable {
title = in.readString();
feedfileId = in.readLong();
feedfileType = in.readInt();
- ifModifiedSince = in.readLong();
+ lastModified = in.readString();
deleteOnFailure = (in.readByte() > 0);
arguments = in.readBundle();
if (in.dataAvail() > 0) {
@@ -98,7 +99,7 @@ public class DownloadRequest implements Parcelable {
dest.writeString(title);
dest.writeLong(feedfileId);
dest.writeInt(feedfileType);
- dest.writeLong(ifModifiedSince);
+ dest.writeString(lastModified);
dest.writeByte((deleteOnFailure) ? (byte) 1 : 0);
dest.writeBundle(arguments);
if (username != null) {
@@ -127,7 +128,7 @@ public class DownloadRequest implements Parcelable {
DownloadRequest that = (DownloadRequest) o;
- if (ifModifiedSince != that.ifModifiedSince) return false;
+ if (lastModified != that.lastModified) return false;
if (deleteOnFailure != that.deleteOnFailure) return false;
if (feedfileId != that.feedfileId) return false;
if (feedfileType != that.feedfileType) return false;
@@ -143,7 +144,6 @@ public class DownloadRequest implements Parcelable {
if (title != null ? !title.equals(that.title) : that.title != null) return false;
if (username != null ? !username.equals(that.username) : that.username != null)
return false;
-
return true;
}
@@ -154,7 +154,7 @@ public class DownloadRequest implements Parcelable {
result = 31 * result + (title != null ? title.hashCode() : 0);
result = 31 * result + (username != null ? username.hashCode() : 0);
result = 31 * result + (password != null ? password.hashCode() : 0);
- result = 31 * result + (int)ifModifiedSince;
+ result = 31 * result + (lastModified != null ? lastModified.hashCode() : 0);
result = 31 * result + (deleteOnFailure ? 1 : 0);
result = 31 * result + (int) (feedfileId ^ (feedfileId >>> 32));
result = 31 * result + feedfileType;
@@ -234,13 +234,14 @@ public class DownloadRequest implements Parcelable {
this.password = password;
}
- public DownloadRequest setIfModifiedSince(long time) {
- this.ifModifiedSince = time;
+ public DownloadRequest setLastModified(@Nullable String lastModified) {
+ this.lastModified = lastModified;
return this;
}
- public long getIfModifiedSince() {
- return this.ifModifiedSince;
+ @Nullable
+ public String getLastModified() {
+ return lastModified;
}
public boolean isDeleteOnFailure() {
@@ -257,7 +258,7 @@ public class DownloadRequest implements Parcelable {
private String title;
private String username;
private String password;
- private long ifModifiedSince;
+ private String lastModified;
private boolean deleteOnFailure = false;
private long feedfileId;
private int feedfileType;
@@ -276,8 +277,8 @@ public class DownloadRequest implements Parcelable {
return this;
}
- public Builder ifModifiedSince(long time) {
- this.ifModifiedSince = time;
+ public Builder lastModified(String lastModified) {
+ this.lastModified = lastModified;
return this;
}
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 d69228ceb..72fa1e1f8 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
@@ -824,7 +824,7 @@ public class DownloadService extends Service {
}
private Pair<DownloadRequest, FeedHandlerResult> parseFeed(DownloadRequest request) {
- Feed feed = new Feed(request.getSource(), new Date());
+ Feed feed = new Feed(request.getSource(), request.getLastModified());
feed.setFile_url(request.getDestination());
feed.setId(request.getFeedfileId());
feed.setDownloaded(true);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
index 0b9fba6f7..e3a195253 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
@@ -8,7 +8,6 @@ import com.squareup.okhttp.Protocol;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody;
-import com.squareup.okhttp.internal.http.HttpDate;
import org.apache.commons.io.IOUtils;
@@ -28,6 +27,7 @@ import java.util.Date;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.feed.FeedImage;
+import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.URIUtil;
@@ -67,13 +67,19 @@ public class HttpDownloader extends Downloader {
final URI uri = URIUtil.getURIFromRequestUrl(request.getSource());
Request.Builder httpReq = new Request.Builder().url(uri.toURL())
.header("User-Agent", ClientConfig.USER_AGENT);
- if(request.getIfModifiedSince() > 0) {
- long threeDaysAgo = System.currentTimeMillis() - 1000*60*60*24*3;
- if(request.getIfModifiedSince() > threeDaysAgo) {
- Date date = new Date(request.getIfModifiedSince());
- String httpDate = HttpDate.format(date);
- Log.d(TAG, "addHeader(\"If-Modified-Since\", \"" + httpDate + "\")");
- httpReq.addHeader("If-Modified-Since", httpDate);
+ if(!TextUtils.isEmpty(request.getLastModified())) {
+ String lastModified = request.getLastModified();
+ Date lastModifiedDate = DateUtils.parse(lastModified);
+ if(lastModifiedDate != null) {
+ long threeDaysAgo = System.currentTimeMillis() - 1000 * 60 * 60 * 24 * 3;
+ if (lastModifiedDate.getTime() > threeDaysAgo) {
+ Log.d(TAG, "addHeader(\"If-Modified-Since\", \"" + lastModified + "\")");
+ httpReq.addHeader("If-Modified-Since", lastModified);
+ }
+ } else {
+ String eTag = lastModified;
+ Log.d(TAG, "addHeader(\"If-None-Match\", \"" + eTag + "\")");
+ httpReq.addHeader("If-None-Match", eTag);
}
}
@@ -235,6 +241,12 @@ public class HttpDownloader extends Downloader {
onFail(DownloadError.ERROR_IO_ERROR, "Download completed, but nothing was read");
return;
}
+ String lastModified = response.header("Last-Modified");
+ if(lastModified != null) {
+ request.setLastModified(lastModified);
+ } else {
+ request.setLastModified(response.header("ETag"));
+ }
onSuccess();
}
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 0dc54fb6e..80703e22d 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
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
+import android.support.annotation.NonNull;
import android.util.Log;
import java.util.ArrayList;
@@ -28,30 +29,18 @@ public class APCleanupAlgorithm extends EpisodeCleanupAlgorithm {
this.numberOfDaysAfterPlayback = numberOfDaysAfterPlayback;
}
+ /**
+ * @return the number of episodes that *could* be cleaned up, if needed
+ */
+ public int getReclaimableItems()
+ {
+ return getCandidates().size();
+ }
+
@Override
public int performCleanup(Context context, int numberOfEpisodesToDelete) {
- List<FeedItem> candidates = new ArrayList<>();
- List<FeedItem> downloadedItems = DBReader.getDownloadedItems();
+ List<FeedItem> candidates = getCandidates();
List<FeedItem> delete;
- Calendar cal = Calendar.getInstance();
- cal.add(Calendar.DAY_OF_MONTH, -1 * numberOfDaysAfterPlayback);
- Date mostRecentDateForDeletion = cal.getTime();
- for (FeedItem item : downloadedItems) {
- if (item.hasMedia()
- && item.getMedia().isDownloaded()
- && !item.isTagged(FeedItem.TAG_QUEUE)
- && item.isPlayed()
- && !item.isTagged(FeedItem.TAG_FAVORITE)) {
- FeedMedia media = item.getMedia();
- // make sure this candidate was played at least the proper amount of days prior
- // to now
- if (media != null
- && media.getPlaybackCompletionDate() != null
- && media.getPlaybackCompletionDate().before(mostRecentDateForDeletion)) {
- candidates.add(item);
- }
- }
- }
Collections.sort(candidates, (lhs, rhs) -> {
Date l = lhs.getMedia().getPlaybackCompletionDate();
@@ -90,6 +79,32 @@ public class APCleanupAlgorithm extends EpisodeCleanupAlgorithm {
return counter;
}
+ @NonNull
+ private List<FeedItem> getCandidates() {
+ List<FeedItem> candidates = new ArrayList<>();
+ List<FeedItem> downloadedItems = DBReader.getDownloadedItems();
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DAY_OF_MONTH, -1 * numberOfDaysAfterPlayback);
+ Date mostRecentDateForDeletion = cal.getTime();
+ for (FeedItem item : downloadedItems) {
+ if (item.hasMedia()
+ && item.getMedia().isDownloaded()
+ && !item.isTagged(FeedItem.TAG_QUEUE)
+ && item.isPlayed()
+ && !item.isTagged(FeedItem.TAG_FAVORITE)) {
+ FeedMedia media = item.getMedia();
+ // make sure this candidate was played at least the proper amount of days prior
+ // to now
+ if (media != null
+ && media.getPlaybackCompletionDate() != null
+ && media.getPlaybackCompletionDate().before(mostRecentDateForDeletion)) {
+ candidates.add(item);
+ }
+ }
+ }
+ return candidates;
+ }
+
@Override
public int getDefaultCleanupParameter() {
return getNumEpisodesToCleanup(0);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APNullCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APNullCleanupAlgorithm.java
index 132b61853..9cec62d83 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APNullCleanupAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APNullCleanupAlgorithm.java
@@ -21,4 +21,9 @@ public class APNullCleanupAlgorithm extends EpisodeCleanupAlgorithm {
public int getDefaultCleanupParameter() {
return 0;
}
+
+ @Override
+ public int getReclaimableItems() {
+ return 0;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java
index 234d6162c..baa9a986e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
+import android.support.annotation.NonNull;
import android.util.Log;
import java.util.ArrayList;
@@ -22,19 +23,18 @@ public class APQueueCleanupAlgorithm extends EpisodeCleanupAlgorithm {
private static final String TAG = "APQueueCleanupAlgorithm";
+ /**
+ * @return the number of episodes that *could* be cleaned up, if needed
+ */
+ public int getReclaimableItems()
+ {
+ return getCandidates().size();
+ }
+
@Override
public int performCleanup(Context context, int numberOfEpisodesToDelete) {
- List<FeedItem> candidates = new ArrayList<>();
- List<FeedItem> downloadedItems = DBReader.getDownloadedItems();
+ List<FeedItem> candidates = getCandidates();
List<FeedItem> delete;
- for (FeedItem item : downloadedItems) {
- if (item.hasMedia()
- && item.getMedia().isDownloaded()
- && !item.isTagged(FeedItem.TAG_QUEUE)
- && !item.isTagged(FeedItem.TAG_FAVORITE)) {
- candidates.add(item);
- }
- }
// in the absence of better data, we'll sort by item publication date
Collections.sort(candidates, (lhs, rhs) -> {
@@ -74,6 +74,21 @@ public class APQueueCleanupAlgorithm extends EpisodeCleanupAlgorithm {
return counter;
}
+ @NonNull
+ private List<FeedItem> getCandidates() {
+ List<FeedItem> candidates = new ArrayList<>();
+ List<FeedItem> downloadedItems = DBReader.getDownloadedItems();
+ for (FeedItem item : downloadedItems) {
+ if (item.hasMedia()
+ && item.getMedia().isDownloaded()
+ && !item.isTagged(FeedItem.TAG_QUEUE)
+ && !item.isTagged(FeedItem.TAG_FAVORITE)) {
+ candidates.add(item);
+ }
+ }
+ return candidates;
+ }
+
@Override
public int getDefaultCleanupParameter() {
return getNumEpisodesToCleanup(0);
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 0563f878f..68187306d 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
@@ -1017,7 +1017,8 @@ public final class DBReader {
int numNewItems = adapter.getNumberOfNewItems();
int numDownloadedItems = adapter.getNumberOfDownloadedEpisodes();
- NavDrawerData result = new NavDrawerData(feeds, queueSize, numNewItems, numDownloadedItems, feedCounters);
+ NavDrawerData result = new NavDrawerData(feeds, queueSize, numNewItems, numDownloadedItems,
+ feedCounters, UserPreferences.getEpisodeCleanupAlgorithm().getReclaimableItems());
adapter.close();
return result;
}
@@ -1028,17 +1029,20 @@ public final class DBReader {
public int numNewItems;
public int numDownloadedItems;
public LongIntMap feedCounters;
+ public int reclaimableSpace;
public NavDrawerData(List<Feed> feeds,
int queueSize,
int numNewItems,
int numDownloadedItems,
- LongIntMap feedIndicatorValues) {
+ LongIntMap feedIndicatorValues,
+ int reclaimableSpace) {
this.feeds = feeds;
this.queueSize = queueSize;
this.numNewItems = numNewItems;
this.numDownloadedItems = numDownloadedItems;
this.feedCounters = feedIndicatorValues;
+ this.reclaimableSpace = reclaimableSpace;
}
}
}
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 efc60bfc2..ed593bb82 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
@@ -224,7 +224,28 @@ public final class DBTasks {
*/
public static void refreshCompleteFeed(final Context context, final Feed feed) {
try {
- refreshFeed(context, feed, true);
+ refreshFeed(context, feed, true, false);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DBWriter.addDownloadStatus(
+ new DownloadStatus(feed, feed
+ .getHumanReadableIdentifier(),
+ DownloadError.ERROR_REQUEST_ERROR, false, e
+ .getMessage()
+ )
+ );
+ }
+ }
+
+ /**
+ * Downloads all pages of the given feed even if feed has not been modified since last refresh
+ *
+ * @param context Used for requesting the download.
+ * @param feed The Feed object.
+ */
+ public static void forceRefreshCompleteFeed(final Context context, final Feed feed) {
+ try {
+ refreshFeed(context, feed, true, true);
} catch (DownloadRequestException e) {
e.printStackTrace();
DBWriter.addDownloadStatus(
@@ -248,11 +269,11 @@ public final class DBTasks {
public static void loadNextPageOfFeed(final Context context, Feed feed, boolean loadAllPages) throws DownloadRequestException {
if (feed.isPaged() && feed.getNextPageLink() != null) {
int pageNr = feed.getPageNr() + 1;
- Feed nextFeed = new Feed(feed.getNextPageLink(), new Date(), feed.getTitle() + "(" + pageNr + ")");
+ Feed nextFeed = new Feed(feed.getNextPageLink(), null, feed.getTitle() + "(" + pageNr + ")");
nextFeed.setPageNr(pageNr);
nextFeed.setPaged(true);
nextFeed.setId(feed.getId());
- DownloadRequester.getInstance().downloadFeed(context, nextFeed, loadAllPages);
+ DownloadRequester.getInstance().downloadFeed(context, nextFeed, loadAllPages, false);
} else {
Log.e(TAG, "loadNextPageOfFeed: Feed was either not paged or contained no nextPageLink");
}
@@ -268,12 +289,25 @@ public final class DBTasks {
public static void refreshFeed(Context context, Feed feed)
throws DownloadRequestException {
Log.d(TAG, "refreshFeed(feed.id: " + feed.getId() +")");
- refreshFeed(context, feed, false);
+ refreshFeed(context, feed, false, false);
+ }
+
+ /**
+ * Refresh a specific feed even if feed has not been modified since last refresh
+ *
+ * @param context Used for requesting the download.
+ * @param feed The Feed object.
+ */
+ public static void forceRefreshFeed(Context context, Feed feed)
+ throws DownloadRequestException {
+ Log.d(TAG, "refreshFeed(feed.id: " + feed.getId() +")");
+ refreshFeed(context, feed, false, true);
}
- private static void refreshFeed(Context context, Feed feed, boolean loadAllPages) throws DownloadRequestException {
+ private static void refreshFeed(Context context, Feed feed, boolean loadAllPages, boolean force)
+ throws DownloadRequestException {
Feed f;
- Date lastUpdate = feed.hasLastUpdateFailed() ? new Date(0) : feed.getLastUpdate();
+ String lastUpdate = feed.hasLastUpdateFailed() ? null : feed.getLastUpdate();
if (feed.getPreferences() == null) {
f = new Feed(feed.getDownload_url(), lastUpdate, feed.getTitle());
} else {
@@ -281,7 +315,7 @@ public final class DBTasks {
feed.getPreferences().getUsername(), feed.getPreferences().getPassword());
}
f.setId(feed.getId());
- DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages);
+ DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages, force);
}
/**
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 0dc1dadeb..22c9538ca 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
@@ -88,7 +88,7 @@ public class DownloadRequester {
private void download(Context context, FeedFile item, FeedFile container, File dest,
boolean overwriteIfExists, String username, String password,
- long ifModifiedSince, boolean deleteOnFailure, Bundle arguments) {
+ String lastModified, boolean deleteOnFailure, Bundle arguments) {
final boolean partiallyDownloadedFileExists = item.getFile_url() != null;
if (isDownloadingFile(item)) {
Log.e(TAG, "URL " + item.getDownload_url()
@@ -129,7 +129,7 @@ public class DownloadRequester {
DownloadRequest.Builder builder = new DownloadRequest.Builder(dest.toString(), item)
.withAuthentication(username, password)
- .ifModifiedSince(ifModifiedSince)
+ .lastModified(lastModified)
.deleteOnFailure(deleteOnFailure)
.withArguments(arguments);
DownloadRequest request = builder.build();
@@ -162,24 +162,25 @@ public class DownloadRequester {
* @param feed Feed to download
* @param loadAllPages Set to true to download all pages
*/
- public synchronized void downloadFeed(Context context, Feed feed, boolean loadAllPages)
+ public synchronized void downloadFeed(Context context, Feed feed, boolean loadAllPages,
+ boolean force)
throws DownloadRequestException {
if (feedFileValid(feed)) {
String username = (feed.getPreferences() != null) ? feed.getPreferences().getUsername() : null;
String password = (feed.getPreferences() != null) ? feed.getPreferences().getPassword() : null;
- long ifModifiedSince = feed.isPaged() ? 0 : feed.getLastUpdate().getTime();
+ String lastModified = feed.isPaged() || force ? null : feed.getLastUpdate();
Bundle args = new Bundle();
args.putInt(REQUEST_ARG_PAGE_NR, feed.getPageNr());
args.putBoolean(REQUEST_ARG_LOAD_ALL_PAGES, loadAllPages);
download(context, feed, null, new File(getFeedfilePath(context),
- getFeedfileName(feed)), true, username, password, ifModifiedSince, true, args);
+ getFeedfileName(feed)), true, username, password, lastModified, true, args);
}
}
public synchronized void downloadFeed(Context context, Feed feed) throws DownloadRequestException {
- downloadFeed(context, feed, false);
+ downloadFeed(context, feed, false, false);
}
public synchronized void downloadMedia(Context context, FeedMedia feedmedia)
@@ -204,7 +205,7 @@ public class DownloadRequester {
getMediafilename(feedmedia));
}
download(context, feedmedia, feed,
- dest, false, username, password, 0, false, null);
+ dest, false, username, password, null, false, null);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java
index 0f402745c..97cbdca33 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java
@@ -40,6 +40,11 @@ public abstract class EpisodeCleanupAlgorithm {
}
/**
+ * @return the number of episodes/items that *could* be cleaned up, if needed
+ */
+ public abstract int getReclaimableItems();
+
+ /**
* @param amountOfRoomNeeded the number of episodes we want to download
* @return the number of episodes to delete in order to make room
*/
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 85ff8fc8c..5b7f5f720 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
@@ -368,7 +368,7 @@ public class PodDBAdapter {
values.put(KEY_FILE_URL, feed.getFile_url());
values.put(KEY_DOWNLOAD_URL, feed.getDownload_url());
values.put(KEY_DOWNLOADED, feed.isDownloaded());
- values.put(KEY_LASTUPDATE, feed.getLastUpdate().getTime());
+ values.put(KEY_LASTUPDATE, feed.getLastUpdate());
values.put(KEY_TYPE, feed.getType());
values.put(KEY_FEED_IDENTIFIER, feed.getFeedIdentifier());
@@ -1151,7 +1151,7 @@ public class PodDBAdapter {
* The returned cursor uses the FEEDITEM_SEL_FI_SMALL selection.
*/
public final Cursor getNewItemsCursor() {
- String[] args = new String[] {
+ Object[] args = new String[] {
SEL_FI_SMALL_STR,
TABLE_NAME_FEED_ITEMS,
TABLE_NAME_FEEDS,
@@ -1512,7 +1512,7 @@ public class PodDBAdapter {
*/
private static class PodDBHelper extends SQLiteOpenHelper {
- private final static int VERSION = 1050003;
+ private final static int VERSION = 1050004;
private Context context;
@@ -1756,7 +1756,6 @@ public class PodDBAdapter {
db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_PUBDATE);
db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_READ);
}
-
if (oldVersion < 1050003) {
// Migrates feed list filter data
@@ -1804,6 +1803,11 @@ public class PodDBAdapter {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ " ADD COLUMN " + PodDBAdapter.KEY_KEEP_UPDATED + " INTEGER DEFAULT 1");
}
+ if (oldVersion < 1050004) {
+ // prevent old timestamps to be misinterpreted as ETags
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS
+ +" SET " + PodDBAdapter.KEY_LASTUPDATE + "=NULL");
+ }
EventBus.getDefault().post(ProgressEvent.end());
}
diff --git a/core/src/main/res/drawable-hdpi/ic_create_new_folder_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_create_new_folder_grey600_24dp.png
new file mode 100644
index 000000000..bfe98fd07
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_create_new_folder_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_create_new_folder_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_create_new_folder_white_24dp.png
new file mode 100644
index 000000000..a8b0ada87
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_create_new_folder_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_sd_storage_grey600_36dp.png b/core/src/main/res/drawable-hdpi/ic_sd_storage_grey600_36dp.png
new file mode 100644
index 000000000..0890b2e5c
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_sd_storage_grey600_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_sd_storage_white_36dp.png b/core/src/main/res/drawable-hdpi/ic_sd_storage_white_36dp.png
new file mode 100644
index 000000000..d9bfcfbfb
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_sd_storage_white_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_create_new_folder_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_create_new_folder_grey600_24dp.png
new file mode 100644
index 000000000..eeed34653
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_create_new_folder_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_create_new_folder_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_create_new_folder_white_24dp.png
new file mode 100644
index 000000000..3b9eaa827
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_create_new_folder_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_sd_storage_grey600_36dp.png b/core/src/main/res/drawable-mdpi/ic_sd_storage_grey600_36dp.png
new file mode 100644
index 000000000..f0c5ed4c3
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_sd_storage_grey600_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_sd_storage_white_36dp.png b/core/src/main/res/drawable-mdpi/ic_sd_storage_white_36dp.png
new file mode 100644
index 000000000..6595d2e1e
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_sd_storage_white_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_create_new_folder_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_create_new_folder_grey600_24dp.png
new file mode 100644
index 000000000..82f0ad458
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_create_new_folder_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_create_new_folder_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_create_new_folder_white_24dp.png
new file mode 100644
index 000000000..aa54623c8
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_create_new_folder_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_sd_storage_grey600_36dp.png b/core/src/main/res/drawable-xhdpi/ic_sd_storage_grey600_36dp.png
new file mode 100644
index 000000000..48cc94622
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_sd_storage_grey600_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_sd_storage_white_36dp.png b/core/src/main/res/drawable-xhdpi/ic_sd_storage_white_36dp.png
new file mode 100644
index 000000000..b440536f6
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_sd_storage_white_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_create_new_folder_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_create_new_folder_grey600_24dp.png
new file mode 100644
index 000000000..802fc6fa0
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_create_new_folder_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_create_new_folder_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_create_new_folder_white_24dp.png
new file mode 100644
index 000000000..91cbc73d1
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_create_new_folder_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_sd_storage_grey600_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_sd_storage_grey600_36dp.png
new file mode 100644
index 000000000..4352dbc06
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_sd_storage_grey600_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_sd_storage_white_36dp.png b/core/src/main/res/drawable-xxhdpi/ic_sd_storage_white_36dp.png
new file mode 100644
index 000000000..5e18ed274
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_sd_storage_white_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_grey600_24dp.png
new file mode 100644
index 000000000..baf7b6ef7
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_white_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_white_24dp.png
new file mode 100644
index 000000000..aa103bfd0
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_create_new_folder_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_sd_storage_grey600_36dp.png b/core/src/main/res/drawable-xxxhdpi/ic_sd_storage_grey600_36dp.png
new file mode 100644
index 000000000..cf03bc3f1
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_sd_storage_grey600_36dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_sd_storage_white_36dp.png b/core/src/main/res/drawable-xxxhdpi/ic_sd_storage_white_36dp.png
new file mode 100644
index 000000000..2996619c9
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_sd_storage_white_36dp.png
Binary files differ
diff --git a/core/src/main/res/values-uk-rUA/strings.xml b/core/src/main/res/values-uk-rUA/strings.xml
index abc5fcec1..4b231b204 100644
--- a/core/src/main/res/values-uk-rUA/strings.xml
+++ b/core/src/main/res/values-uk-rUA/strings.xml
@@ -22,6 +22,8 @@
<string name="playback_history_label">Що грало</string>
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_auth_label">gpodder.net логін</string>
+ <string name="free_space_label">%1$s вільно</string>
+ <string name="episode_cache_full_title">Кеш епізодів заповнений</string>
<!--New episodes fragment-->
<string name="recently_published_episodes_label">Щойно опубліковано</string>
<string name="episode_filter_label">Показати тількі нові епізоди</string>
@@ -100,6 +102,7 @@
<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_link_with_position_label">Поділитись посиланням на позицію</string>
<string name="share_feed_url_label">Поділитись посиланням на канал</string>
@@ -134,7 +137,9 @@
<string name="added_to_queue_label">Додано до черги</string>
<string name="remove_from_queue_label">Видалити з черги</string>
<string name="add_to_favorite_label">Додати до улюблених</string>
+ <string name="added_to_favorites">Додано до улюблених</string>
<string name="remove_from_favorite_label">Видалити з улюблених</string>
+ <string name="removed_from_favorites">Видалено з улюблених</string>
<string name="visit_website_label">Відкрити сайт</string>
<string name="support_label">Підтримати за допомогою Flattr</string>
<string name="enqueue_all_new">Додати до черги</string>
@@ -168,6 +173,11 @@
<string name="download_error_io_error">Помилка IO</string>
<string name="download_error_request_error">Помилка запиту</string>
<string name="download_error_db_access">Помилка бази даних</string>
+ <plurals name="downloads_left">
+ <item quantity="one">%d завантаження залишилось</item>
+ <item quantity="few">%d завантаження залишилось</item>
+ <item quantity="other">%d завантажень залишилось</item>
+ </plurals>
<string name="downloads_processing">Обробка завантаженого</string>
<string name="download_notification_title">Завантаження даних подкасту</string>
<string name="download_report_content">Завантажилось %1$d успішно, %2$d з помилками</string>
@@ -199,6 +209,8 @@
<!--Queue operations-->
<string name="lock_queue">Заблокувати чергу</string>
<string name="unlock_queue">Розблокувати чергу</string>
+ <string name="queue_locked">Чергу заблоковано</string>
+ <string name="queue_unlocked">Чергу разблоковано</string>
<string name="clear_queue_label">Очистити чергу</string>
<string name="undo">Скасувати</string>
<string name="removed_from_queue">Видалено</string>
@@ -270,6 +282,8 @@
<string name="pref_autoUpdateIntervallOrTime_Disable">Вимкнути</string>
<string name="pref_autoUpdateIntervallOrTime_Interval">Інтервал</string>
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Встановити час щодня</string>
+ <string name="pref_autoUpdateIntervallOrTime_every">кожні %1$s</string>
+ <string name="pref_autoUpdateIntervallOrTime_at">на %1$s</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Завантажувати тільки через Wifi</string>
<string name="pref_followQueue_title">Грати безперервно</string>
<string name="pref_downloadMediaOnWifiOnly_title">Завантаження через Wifi</string>
@@ -339,8 +353,12 @@
<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>
+ <string name="crash_report_title">Звіт про збій</string>
+ <string name="crash_report_sum">Надіслати е-пошту зі звітом про останній збій</string>
+ <string name="send_email">Надіслати е-пошту</string>
<string name="experimental_pref">Експериментальні</string>
<string name="pref_sonic_title">Програвач Sonic</string>
+ <string name="pref_current_value">Поточне значення: %1$s</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Включити автоматичне заохочення авторів через сервіс flattr</string>
<string name="auto_flattr_after_percent">Заохотити автора через Flattr щойно %d відсотків епізода було відтворено</string>
@@ -367,6 +385,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>
@@ -438,6 +457,7 @@
<string name="create_folder_error_no_write_access">Не можу записати в цю папку</string>
<string name="create_folder_error_already_exists">Папка вже існує</string>
<string name="create_folder_error">Не можу создати папку</string>
+ <string name="folder_does_not_exist_error">\"%1$s\" не існує</string>
<string name="folder_not_empty_dialog_title">Папка не є пустою</string>
<string name="folder_not_empty_dialog_msg">В папці щось є. Всі завантаження зберігаються в цю папку. Все рівно продовжувати?</string>
<string name="set_to_default_folder">Обрати папку по замовчанню</string>
@@ -449,6 +469,7 @@
<!--Online feed view-->
<string name="subscribe_label">Підписатися</string>
<string name="subscribed_label">Підписано</string>
+ <string name="downloading_label">Завантажується…</string>
<!--Content descriptions for image buttons-->
<string name="show_chapters_label">Показати глави</string>
<string name="show_shownotes_label">Показати нотатки</string>
@@ -471,11 +492,14 @@
<!--Feed information screen-->
<string name="authentication_label">Автентикація</string>
<string name="authentication_descr">Змінити ваші логін та пароль для подкаста та епізодів</string>
+ <string name="auto_download_settings_label">Налаштування автозавантаження</string>
+ <string name="episode_filters_label">Фільтр епізодів</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="filter">Фільтр</string>
<string name="all_label">Всі</string>
<string name="selected_all_label">Обрано всі епізоди</string>
<string name="none_label">Жодного</string>
@@ -495,5 +519,12 @@
<string name="sort_duration_short_long">Тривалість (Короткі \u2192 Довгі)</string>
<string name="sort_duration_long_short">Тривалість (Довгі \u2192 Короткі)</string>
<!--Rating dialog-->
+ <string name="rating_later_label">Нагадати згодом</string>
<!--Audio controls-->
+ <string name="playback_speed">Швидкість програвання</string>
+ <string name="volume">Гучність</string>
+ <string name="left_short">Л</string>
+ <string name="right_short">П</string>
+ <string name="audio_effects">Аудіоефекти</string>
+ <string name="stereo_to_mono">Зробити моно із стерео</string>
</resources>
diff --git a/core/src/main/res/values/attrs.xml b/core/src/main/res/values/attrs.xml
index 0eb4d581e..2d3379d95 100644
--- a/core/src/main/res/values/attrs.xml
+++ b/core/src/main/res/values/attrs.xml
@@ -50,6 +50,8 @@
<attr name="ic_check_box_outline" format="reference"/>
<attr name="ic_indeterminate_check_box" format="reference"/>
<attr name="ic_sort" format="reference"/>
+ <attr name="ic_sd_storage" format="reference"/>
+ <attr name="ic_create_new_folder" format="reference"/>
<!-- Used in itemdescription -->
<attr name="non_transparent_background" format="reference"/>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 04b3feef6..912a60f55 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -421,6 +421,7 @@
<string name="export_error_label">Export error</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>
+ <string name="opml_import_ask_read_permission">Access to external storage is required to read the OPML file</string>
<!-- Sleep timer -->
<string name="set_sleeptimer_label">Set sleep timer</string>
@@ -484,6 +485,7 @@
<string name="create_folder_label">Create folder</string>
<string name="choose_data_directory">Choose Data Folder</string>
<string name="choose_data_directory_message">Please choose the base of your data folder. AntennaPod will create the appropriate sub-directories.</string>
+ <string name="choose_data_directory_permission_rationale">Access to external storage is required to change the 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>
@@ -560,6 +562,10 @@
<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="queued_label">Queued</string>
+ <string name="selected_queued_label">Selected queued Episodes</string>
+ <string name="not_queued_label">Not queued</string>
+ <string name="selected_not_queued_label">Selected not queued Episodes</string>
<string name="sort_title"><b>Sort by&#8230;</b></string>
<string name="sort_title_a_z">Title (A \u2192 Z)</string>
<string name="sort_title_z_a">Title (Z \u2192 A)</string>
diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml
index d3718a460..c4a731a53 100644
--- a/core/src/main/res/values/styles.xml
+++ b/core/src/main/res/values/styles.xml
@@ -57,6 +57,8 @@
<item name="attr/ic_check_box_outline">@drawable/ic_check_box_outline_blank_grey600_24dp</item>
<item name="attr/ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_grey600_24dp</item>
<item name="attr/ic_sort">@drawable/ic_sort_grey600_24dp</item>
+ <item name="attr/ic_sd_storage">@drawable/ic_sd_storage_grey600_36dp</item>
+ <item name="attr/ic_create_new_folder">@drawable/ic_create_new_folder_grey600_24dp</item>
</style>
<style name="Theme.AntennaPod.Dark" parent="Theme.AppCompat">
@@ -114,6 +116,8 @@
<item name="attr/ic_check_box_outline">@drawable/ic_check_box_outline_blank_white_24dp</item>
<item name="attr/ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_white_24dp</item>
<item name="attr/ic_sort">@drawable/ic_sort_white_24dp</item>
+ <item name="attr/ic_sd_storage">@drawable/ic_sd_storage_white_36dp</item>
+ <item name="attr/ic_create_new_folder">@drawable/ic_create_new_folder_white_24dp</item>
</style>
<style name="Theme.AntennaPod.Light.NoTitle" parent="Theme.AppCompat.Light.NoActionBar">
@@ -173,6 +177,8 @@
<item name="attr/ic_check_box_outline">@drawable/ic_check_box_outline_blank_grey600_24dp</item>
<item name="attr/ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_grey600_24dp</item>
<item name="attr/ic_sort">@drawable/ic_sort_grey600_24dp</item>
+ <item name="attr/ic_sd_storage">@drawable/ic_sd_storage_grey600_36dp</item>
+ <item name="attr/ic_create_new_folder">@drawable/ic_create_new_folder_grey600_24dp</item>
</style>
<style name="Theme.AntennaPod.Dark.NoTitle" parent="Theme.AppCompat.NoActionBar">
@@ -231,6 +237,8 @@
<item name="attr/ic_check_box_outline">@drawable/ic_check_box_outline_blank_white_24dp</item>
<item name="attr/ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_white_24dp</item>
<item name="attr/ic_sort">@drawable/ic_sort_white_24dp</item>
+ <item name="attr/ic_sd_storage">@drawable/ic_sd_storage_white_36dp</item>
+ <item name="attr/ic_create_new_folder">@drawable/ic_create_new_folder_white_24dp</item>
</style>
<style name="Theme.AntennaPod.VideoPlayer" parent="@style/Theme.AntennaPod.Dark">