summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java119
-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.java58
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java13
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java12
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java9
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java24
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java8
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java38
-rw-r--r--app/src/main/AndroidManifest.xml4
-rw-r--r--app/src/main/assets/LICENSE_ANTENNAPOD_AUDIOPLAYER.txt201
-rw-r--r--app/src/main/assets/LICENSE_APACHE-2.0.txt (renamed from app/src/main/assets/LICENSE_APACHE_COMMONS.txt)0
-rw-r--r--app/src/main/assets/LICENSE_EVENTBUS.txt202
-rw-r--r--app/src/main/assets/LICENSE_FLATTR4J.txt202
-rw-r--r--app/src/main/assets/LICENSE_FLEXIBLE_DIVIDER.txt201
-rw-r--r--app/src/main/assets/LICENSE_OKIO.txt202
-rw-r--r--app/src/main/assets/LICENSE_RX_ANDROID.txt200
-rw-r--r--app/src/main/assets/LICENSE_STACKBLUR.txt71
-rw-r--r--app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/UpdateManager.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java670
-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.java106
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java50
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java640
-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/activity/VideoplayerActivity.java199
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java25
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java199
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java199
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java57
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java26
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java44
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java20
-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.java30
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java49
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java51
-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.java133
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java201
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java52
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java77
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java76
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java77
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java68
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java190
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java273
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java108
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java388
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java27
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java54
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java31
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java56
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java587
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/SwitchCompatPreference.java37
-rw-r--r--app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java8
-rw-r--r--app/src/main/res/drawable/shadow.xml7
-rw-r--r--app/src/main/res/layout/audio_controls.xml136
-rw-r--r--app/src/main/res/layout/audioplayer_activity.xml266
-rw-r--r--app/src/main/res/layout/choose_speed_dialog.xml45
-rw-r--r--app/src/main/res/layout/cover_fragment.xml66
-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/external_player_fragment.xml136
-rw-r--r--app/src/main/res/layout/feedinfo.xml93
-rw-r--r--app/src/main/res/layout/feeditem_fragment.xml132
-rw-r--r--app/src/main/res/layout/feeditem_fragment_header.xml136
-rw-r--r--app/src/main/res/layout/fragment_itunes_search.xml86
-rw-r--r--app/src/main/res/layout/gpodnet_podcast_listitem.xml20
-rw-r--r--app/src/main/res/layout/itemdescription_listitem.xml49
-rw-r--r--app/src/main/res/layout/itunes_podcast_listitem.xml86
-rw-r--r--app/src/main/res/layout/main.xml12
-rw-r--r--app/src/main/res/layout/nav_feedlistitem.xml2
-rw-r--r--app/src/main/res/layout/new_episodes_listitem.xml4
-rw-r--r--app/src/main/res/layout/preference_switch_layout.xml9
-rw-r--r--app/src/main/res/layout/queue_listitem.xml30
-rw-r--r--app/src/main/res/layout/storage_error.xml29
-rw-r--r--app/src/main/res/layout/videoplayer_activity.xml41
-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.xml21
-rw-r--r--app/src/main/res/menu/feeditemlist_context.xml9
-rw-r--r--app/src/main/res/menu/feedlist.xml4
-rw-r--r--app/src/main/res/menu/itunes_search.xml13
-rw-r--r--app/src/main/res/menu/mediaplayer.xml27
-rw-r--r--app/src/main/res/xml/preferences.xml90
-rw-r--r--app/src/main/templates/about.html16
112 files changed, 4330 insertions, 4242 deletions
diff --git a/app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java b/app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java
new file mode 100644
index 000000000..23a05ac8c
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java
@@ -0,0 +1,119 @@
+package de.test.antennapod.feed;
+
+import android.test.AndroidTestCase;
+
+import de.danoeh.antennapod.core.feed.FeedFilter;
+import de.danoeh.antennapod.core.feed.FeedItem;
+
+public class FeedFilterTest extends AndroidTestCase {
+
+ public void testNullFilter() throws Exception {
+ FeedFilter filter = new FeedFilter();
+ FeedItem item = new FeedItem();
+ item.setTitle("Hello world");
+
+ assertTrue(!filter.excludeOnly());
+ assertTrue(!filter.includeOnly());
+ assertEquals("", filter.getExcludeFilter());
+ assertEquals("", filter.getIncludeFilter());
+ assertTrue(filter.shouldAutoDownload(item));
+ }
+
+ public void testBasicIncludeFilter() throws Exception {
+ String includeFilter = "Hello";
+ FeedFilter filter = new FeedFilter(includeFilter, "");
+ FeedItem item = new FeedItem();
+ item.setTitle("Hello world");
+
+ FeedItem item2 = new FeedItem();
+ item2.setTitle("Don't include me");
+
+ assertTrue(!filter.excludeOnly());
+ assertTrue(filter.includeOnly());
+ assertEquals("", filter.getExcludeFilter());
+ assertEquals(includeFilter, filter.getIncludeFilter());
+ assertTrue(filter.shouldAutoDownload(item));
+ assertTrue(!filter.shouldAutoDownload(item2));
+ }
+
+ public void testBasicExcludeFilter() throws Exception {
+ String excludeFilter = "Hello";
+ FeedFilter filter = new FeedFilter("", excludeFilter);
+ FeedItem item = new FeedItem();
+ item.setTitle("Hello world");
+
+ FeedItem item2 = new FeedItem();
+ item2.setTitle("Item2");
+
+ assertTrue(filter.excludeOnly());
+ assertTrue(!filter.includeOnly());
+ assertEquals(excludeFilter, filter.getExcludeFilter());
+ assertEquals("", filter.getIncludeFilter());
+ assertTrue(!filter.shouldAutoDownload(item));
+ assertTrue(filter.shouldAutoDownload(item2));
+ }
+
+ public void testComplexIncludeFilter() throws Exception {
+ String includeFilter = "Hello \n\"Two words\"";
+ FeedFilter filter = new FeedFilter(includeFilter, "");
+ FeedItem item = new FeedItem();
+ item.setTitle("hello world");
+
+ FeedItem item2 = new FeedItem();
+ item2.setTitle("Two three words");
+
+ FeedItem item3 = new FeedItem();
+ item3.setTitle("One two words");
+
+ assertTrue(!filter.excludeOnly());
+ assertTrue(filter.includeOnly());
+ assertEquals("", filter.getExcludeFilter());
+ assertEquals(includeFilter, filter.getIncludeFilter());
+ assertTrue(filter.shouldAutoDownload(item));
+ assertTrue(!filter.shouldAutoDownload(item2));
+ assertTrue(filter.shouldAutoDownload(item3));
+ }
+
+ public void testComplexExcludeFilter() throws Exception {
+ String excludeFilter = "Hello \"Two words\"";
+ FeedFilter filter = new FeedFilter("", excludeFilter);
+ FeedItem item = new FeedItem();
+ item.setTitle("hello world");
+
+ FeedItem item2 = new FeedItem();
+ item2.setTitle("One three words");
+
+ FeedItem item3 = new FeedItem();
+ item3.setTitle("One two words");
+
+ assertTrue(filter.excludeOnly());
+ assertTrue(!filter.includeOnly());
+ assertEquals(excludeFilter, filter.getExcludeFilter());
+ assertEquals("", filter.getIncludeFilter());
+ assertTrue(!filter.shouldAutoDownload(item));
+ assertTrue(filter.shouldAutoDownload(item2));
+ assertTrue(!filter.shouldAutoDownload(item3));
+ }
+
+ public void testComboFilter() throws Exception {
+ String includeFilter = "Hello world";
+ String excludeFilter = "dislike";
+ FeedFilter filter = new FeedFilter(includeFilter, excludeFilter);
+
+ FeedItem download = new FeedItem();
+ download.setTitle("Hello everyone!");
+ // because, while it has words from the include filter it also has exclude words
+ FeedItem doNotDownload = new FeedItem();
+ doNotDownload.setTitle("I dislike the world");
+ // because it has no words from the include filter
+ FeedItem doNotDownload2 = new FeedItem();
+ doNotDownload2.setTitle("no words to include");
+
+ assertTrue(filter.hasExcludeFilter());
+ assertTrue(filter.hasIncludeFilter());
+ assertTrue(filter.shouldAutoDownload(download));
+ assertTrue(!filter.shouldAutoDownload(doNotDownload));
+ assertTrue(!filter.shouldAutoDownload(doNotDownload2));
+ }
+
+}
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 d61d4ab52..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
@@ -189,6 +192,7 @@ public class DBWriterTest extends InstrumentationTestCase {
c.close();
c = adapter.getSimpleChaptersOfFeedItemCursor(item);
assertEquals(0, c.getCount());
+ c.close();
}
}
@@ -196,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);
@@ -252,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
@@ -289,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
@@ -341,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
@@ -399,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
@@ -471,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
@@ -527,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);
@@ -541,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());
@@ -553,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());
@@ -570,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);
@@ -597,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);
@@ -621,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);
@@ -687,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);
@@ -732,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);
@@ -778,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);
@@ -804,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 4dae53a15..4e214cf81 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
@@ -51,6 +51,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
adapter.close();
// override first launch preference
+ // do this BEFORE calling getActivity()!
prefs = getInstrumentation().getTargetContext().getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE);
prefs.edit().putBoolean(MainActivity.PREF_IS_FIRST_LAUNCH, false).commit();
@@ -71,7 +72,8 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
}
private void openNavDrawer() {
- solo.clickOnScreen(50, 50);
+ solo.clickOnImageButton(0);
+ getInstrumentation().waitForIdleSync();
}
public void testAddFeed() throws Exception {
@@ -125,7 +127,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
assertEquals(solo.getString(R.string.add_feed_label), getActionbarTitle());
// podcasts
- ListView list = (ListView)solo.getView(R.id.nav_list);
+ ListView list = (ListView) solo.getView(R.id.nav_list);
for (int i = 0; i < uiTestUtils.hostedFeeds.size(); i++) {
Feed f = uiTestUtils.hostedFeeds.get(i);
solo.clickOnScreen(50, 50); // open nav drawer
@@ -137,9 +139,10 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
}
private String getActionbarTitle() {
- return ((MainActivity)solo.getCurrentActivity()).getMainActivtyActionBar().getTitle().toString();
+ return ((MainActivity) solo.getCurrentActivity()).getSupportActionBar().getTitle().toString();
}
+ @SuppressWarnings("unchecked")
@FlakyTest(tolerance = 3)
public void testGoToPreferences() {
openNavDrawer();
@@ -185,14 +188,14 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
openNavDrawer();
solo.clickLongOnText(solo.getString(R.string.queue_label));
solo.waitForDialogToOpen();
- for(String title : titles) {
+ for (String title : titles) {
solo.clickOnText(title);
}
solo.clickOnText(solo.getString(R.string.confirm_label));
solo.waitForDialogToClose();
List<String> hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(titles.length, hidden.size());
- for(String tag : MainActivity.NAV_DRAWER_TAGS) {
+ for (String tag : MainActivity.NAV_DRAWER_TAGS) {
assertTrue(hidden.contains(tag));
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
index c43757546..d4994fb1a 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
@@ -1,7 +1,6 @@
package de.test.antennapod.ui;
import android.annotation.TargetApi;
-import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -10,7 +9,6 @@ import android.preference.PreferenceManager;
import android.test.ActivityInstrumentationTestCase2;
import android.test.FlakyTest;
import android.view.View;
-import android.widget.ImageButton;
import android.widget.ListView;
import com.robotium.solo.Solo;
@@ -21,15 +19,12 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import de.danoeh.antennapod.core.util.playback.Playable;
-import de.danoeh.antennapod.core.util.playback.PlaybackController;
/**
* test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
@@ -54,10 +49,11 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
public void setUp() throws Exception {
super.setUp();
- PodDBAdapter.deleteDatabase();
-
context = getInstrumentation().getTargetContext();
+ PodDBAdapter.init(context);
+ PodDBAdapter.deleteDatabase();
+
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit()
.clear()
@@ -90,7 +86,7 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
}
private void openNavDrawer() {
- solo.clickOnScreen(50, 50);
+ solo.clickOnImageButton(0);
getInstrumentation().waitForIdleSync();
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
index 195e3d250..5ce495f9a 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
@@ -46,10 +46,11 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
public void setUp() throws Exception {
super.setUp();
- PodDBAdapter.deleteDatabase();
-
context = getInstrumentation().getTargetContext();
+ PodDBAdapter.init(context);
+ PodDBAdapter.deleteDatabase();
+
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit()
.clear()
@@ -79,9 +80,9 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
super.tearDown();
}
-
private void openNavDrawer() {
- solo.clickOnScreen(50, 50);
+ solo.clickOnImageButton(0);
+ getInstrumentation().waitForIdleSync();
}
private void setContinuousPlaybackPreference(boolean value) {
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
index fabc399ba..54741502c 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -3,17 +3,10 @@ package de.test.antennapod.ui;
import android.content.Context;
import android.content.res.Resources;
import android.test.ActivityInstrumentationTestCase2;
-import android.test.FlakyTest;
-import com.robotium.solo.Condition;
import com.robotium.solo.Solo;
import com.robotium.solo.Timeout;
-import org.apache.commons.io.IOUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.List;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.R;
@@ -156,8 +149,8 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
public void testPlaybackSpeeds() {
solo.clickOnText(solo.getString(R.string.pref_playback_speed_title));
solo.waitForDialogToOpen(1000);
- assertTrue(solo.searchText(solo.getString(R.string.no_playback_plugin_title)));
- solo.clickOnText(solo.getString(R.string.close_label));
+ assertTrue(solo.searchText(res.getStringArray(R.array.playback_speed_values)[0]));
+ solo.clickOnText(solo.getString(R.string.cancel_label));
solo.waitForDialogToClose(1000);
}
@@ -230,6 +223,8 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
String[] values = res.getStringArray(R.array.episode_cache_size_values);
String entry = entries[entries.length/2];
final int value = Integer.valueOf(values[values.length/2]);
+ solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
+ solo.waitForText(solo.getString(R.string.pref_automatic_download_title));
solo.clickOnText(solo.getString(R.string.pref_episode_cache_title));
solo.waitForDialogToOpen();
solo.clickOnText(entry);
@@ -241,6 +236,11 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
String[] values = res.getStringArray(R.array.episode_cache_size_values);
String minEntry = entries[0];
final int minValue = Integer.valueOf(values[0]);
+ solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
+ solo.waitForText(solo.getString(R.string.pref_automatic_download_title));
+ if(!UserPreferences.isEnableAutodownload()) {
+ solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
+ }
solo.clickOnText(solo.getString(R.string.pref_episode_cache_title));
solo.waitForDialogToOpen(1000);
solo.scrollUp();
@@ -248,12 +248,16 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == minValue, Timeout.getLargeTimeout()));
}
-
public void testSetEpisodeCacheMax() {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
String[] values = res.getStringArray(R.array.episode_cache_size_values);
String maxEntry = entries[entries.length-1];
final int maxValue = Integer.valueOf(values[values.length-1]);
+ solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
+ solo.waitForText(solo.getString(R.string.pref_automatic_download_title));
+ if(!UserPreferences.isEnableAutodownload()) {
+ solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
+ }
solo.clickOnText(solo.getString(R.string.pref_episode_cache_title));
solo.waitForDialogToOpen();
solo.clickOnText(maxEntry);
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/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java
index 6c5a350de..53fd7d7fd 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java
@@ -1,15 +1,15 @@
package de.test.antennapod.ui;
import android.test.InstrumentationTestCase;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import org.apache.http.HttpStatus;
import java.io.File;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedItem;
+
/**
* Test for the UITestUtils. Makes sure that all URLs are reachable and that the class does not cause any crashes.
*/
@@ -55,7 +55,7 @@ public class UITestUtilsTest extends InstrumentationTestCase {
conn.setRequestMethod("GET");
conn.connect();
int rc = conn.getResponseCode();
- assertEquals(HttpStatus.SC_OK, rc);
+ assertEquals(HttpURLConnection.HTTP_OK, rc);
conn.disconnect();
}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java b/app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java
index 4a5818479..28ff6694e 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java
@@ -1,6 +1,21 @@
package de.test.antennapod.util.service.download;
-import java.io.*;
+import android.support.v4.util.ArrayMap;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.PushbackInputStream;
+import java.io.RandomAccessFile;
+import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
@@ -14,7 +29,6 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -281,7 +295,7 @@ public abstract class NanoHTTPD {
* @return HTTP response, see class Response for details
*/
public Response serve(IHTTPSession session) {
- Map<String, String> files = new HashMap<String, String>();
+ Map<String, String> files = new ArrayMap<>();
Method method = session.getMethod();
if (Method.PUT.equals(method) || Method.POST.equals(method)) {
try {
@@ -334,7 +348,7 @@ public abstract class NanoHTTPD {
* @return a map of <code>String</code> (parameter name) to <code>List&lt;String&gt;</code> (a list of the values supplied).
*/
protected Map<String, List<String>> decodeParameters(String queryString) {
- Map<String, List<String>> parms = new HashMap<String, List<String>>();
+ Map<String, List<String>> parms = new ArrayMap<String, List<String>>();
if (queryString != null) {
StringTokenizer st = new StringTokenizer(queryString, "&");
while (st.hasMoreTokens()) {
@@ -549,7 +563,7 @@ public abstract class NanoHTTPD {
/**
* Headers for the HTTP response. Use addHeader() to add lines.
*/
- private Map<String, String> header = new HashMap<String, String>();
+ private Map<String, String> header = new ArrayMap<String, String>();
/**
* The request method that spawned this response.
*/
@@ -851,7 +865,7 @@ public abstract class NanoHTTPD {
this.inputStream = new PushbackInputStream(inputStream, BUFSIZE);
this.outputStream = outputStream;
String remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress().toString();
- headers = new HashMap<String, String>();
+ headers = new ArrayMap<String, String>();
headers.put("remote-addr", remoteIp);
headers.put("http-client-ip", remoteIp);
@@ -895,16 +909,16 @@ public abstract class NanoHTTPD {
inputStream.unread(buf, splitbyte, rlen - splitbyte);
}
- parms = new HashMap<String, String>();
+ parms = new ArrayMap<String, String>();
if(null == headers) {
- headers = new HashMap<String, String>();
+ headers = new ArrayMap<String, String>();
}
// Create a BufferedReader for parsing the header.
BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, rlen)));
// Decode the header into parms and header java properties
- Map<String, String> pre = new HashMap<String, String>();
+ Map<String, String> pre = new ArrayMap<String, String>();
decodeHeader(hin, pre, parms, headers);
method = Method.lookup(pre.get("method"));
@@ -1102,7 +1116,7 @@ public abstract class NanoHTTPD {
throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html");
}
boundarycount++;
- Map<String, String> item = new HashMap<String, String>();
+ Map<String, String> item = new ArrayMap<String, String>();
mpline = in.readLine();
while (mpline != null && mpline.trim().length() > 0) {
int p = mpline.indexOf(':');
@@ -1117,7 +1131,7 @@ public abstract class NanoHTTPD {
throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html");
}
StringTokenizer st = new StringTokenizer(contentDisposition, ";");
- Map<String, String> disposition = new HashMap<String, String>();
+ Map<String, String> disposition = new ArrayMap<String, String>();
while (st.hasMoreTokens()) {
String token = st.nextToken().trim();
int p = token.indexOf('=');
@@ -1352,7 +1366,7 @@ public abstract class NanoHTTPD {
* @author LordFokas
*/
public class CookieHandler implements Iterable<String> {
- private HashMap<String, String> cookies = new HashMap<String, String>();
+ private ArrayMap<String, String> cookies = new ArrayMap<String, String>();
private ArrayList<Cookie> queue = new ArrayList<Cookie>();
public CookieHandler(Map<String, String> httpHeaders) {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ae2e69b5c..36067d52e 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="1040104"
- android:versionName="1.4.1.4">
+ android:versionCode="1050009"
+ android:versionName="1.5.0.9">
<!--
Version code schema:
"1.2.3-SNAPSHOT" -> 1020300
diff --git a/app/src/main/assets/LICENSE_ANTENNAPOD_AUDIOPLAYER.txt b/app/src/main/assets/LICENSE_ANTENNAPOD_AUDIOPLAYER.txt
deleted file mode 100644
index 8dada3eda..000000000
--- a/app/src/main/assets/LICENSE_ANTENNAPOD_AUDIOPLAYER.txt
+++ /dev/null
@@ -1,201 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "{}"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright {yyyy} {name of copyright owner}
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/app/src/main/assets/LICENSE_APACHE_COMMONS.txt b/app/src/main/assets/LICENSE_APACHE-2.0.txt
index d64569567..d64569567 100644
--- a/app/src/main/assets/LICENSE_APACHE_COMMONS.txt
+++ b/app/src/main/assets/LICENSE_APACHE-2.0.txt
diff --git a/app/src/main/assets/LICENSE_EVENTBUS.txt b/app/src/main/assets/LICENSE_EVENTBUS.txt
deleted file mode 100644
index d64569567..000000000
--- a/app/src/main/assets/LICENSE_EVENTBUS.txt
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/app/src/main/assets/LICENSE_FLATTR4J.txt b/app/src/main/assets/LICENSE_FLATTR4J.txt
deleted file mode 100644
index d64569567..000000000
--- a/app/src/main/assets/LICENSE_FLATTR4J.txt
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/app/src/main/assets/LICENSE_FLEXIBLE_DIVIDER.txt b/app/src/main/assets/LICENSE_FLEXIBLE_DIVIDER.txt
deleted file mode 100644
index 5c304d1a4..000000000
--- a/app/src/main/assets/LICENSE_FLEXIBLE_DIVIDER.txt
+++ /dev/null
@@ -1,201 +0,0 @@
-Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "{}"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright {yyyy} {name of copyright owner}
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/app/src/main/assets/LICENSE_OKIO.txt b/app/src/main/assets/LICENSE_OKIO.txt
deleted file mode 100644
index d64569567..000000000
--- a/app/src/main/assets/LICENSE_OKIO.txt
+++ /dev/null
@@ -1,202 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/app/src/main/assets/LICENSE_RX_ANDROID.txt b/app/src/main/assets/LICENSE_RX_ANDROID.txt
deleted file mode 100644
index 4fe2d187f..000000000
--- a/app/src/main/assets/LICENSE_RX_ANDROID.txt
+++ /dev/null
@@ -1,200 +0,0 @@
-
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/app/src/main/assets/LICENSE_STACKBLUR.txt b/app/src/main/assets/LICENSE_STACKBLUR.txt
deleted file mode 100644
index b66736ccb..000000000
--- a/app/src/main/assets/LICENSE_STACKBLUR.txt
+++ /dev/null
@@ -1,71 +0,0 @@
-Apache License, Version 2.0
-
-Version 2.0, January 2004
-
-http://www.apache.org/licenses/
-
-TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-1. Definitions.
-
-"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
-
-"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
-
-"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
-
-"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
-
-"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
-
-"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
-
-"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
-
-"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
-
-"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
-
-"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
-
-2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
-
-3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
-
-4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
-
-You must give any other recipients of the Work or Derivative Works a copy of this License; and
-You must cause any modified files to carry prominent notices stating that You changed the files; and
-You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
-If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
-
-You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
-5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
-
-6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
-
-7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
-
-8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
-
-9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
-
-END OF TERMS AND CONDITIONS
-
-APPENDIX: How to apply the Apache License to your work
-To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
-
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
index 17942a93c..ea2166674 100644
--- a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
+++ b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
@@ -34,8 +34,11 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler {
out = new PrintWriter(new FileWriter(path));
out.println("[ Environment ]");
out.println("Android version: " + Build.VERSION.RELEASE);
+ out.println("OS version: " + System.getProperty("os.version"));
out.println("AntennaPod version: " + BuildConfig.VERSION_NAME);
- out.println("Phone model: " + Build.MODEL);
+ out.println("Model: " + Build.MODEL);
+ out.println("Device: " + Build.DEVICE);
+ out.println("Product: " + Build.PRODUCT);
out.println();
out.println("[ StackTrace ]");
ex.printStackTrace(out);
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index 835f43f40..829a49a15 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -6,6 +6,7 @@ import android.os.StrictMode;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.fonts.FontAwesomeModule;
+import com.joanzapata.iconify.fonts.MaterialModule;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
@@ -56,12 +57,13 @@ 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();
Iconify.with(new FontAwesomeModule());
+ Iconify.with(new MaterialModule());
SPAUtil.sendSPAppsQueryFeedsIntent(this);
}
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 220724af4..b4e8d4d71 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -1,54 +1,44 @@
package de.danoeh.antennapod.activity;
+import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.graphics.drawable.BitmapDrawable;
-import android.os.Bundle;
+import android.os.Build;
+import android.support.annotation.Nullable;
+import android.support.design.widget.AppBarLayout;
import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.app.ListFragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
+import android.util.TypedValue;
import android.view.ContextMenu;
-import android.view.Gravity;
-import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.LinearInterpolator;
-import android.view.animation.ScaleAnimation;
import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ImageButton;
import android.widget.ListView;
-import android.widget.PopupWindow;
-import android.widget.SeekBar;
-import android.widget.TextView;
-import com.bumptech.glide.Glide;
+import com.viewpagerindicator.CirclePageIndicator;
-import org.apache.commons.lang3.ArrayUtils;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.adapter.ChapterListAdapter;
+import de.danoeh.antennapod.adapter.ChaptersListAdapter;
import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
-import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
-import de.danoeh.antennapod.core.feed.SimpleChapter;
-import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
@@ -57,8 +47,14 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.fragment.AddFeedFragment;
+import de.danoeh.antennapod.fragment.ChaptersFragment;
import de.danoeh.antennapod.fragment.CoverFragment;
+import de.danoeh.antennapod.fragment.DownloadsFragment;
+import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.ItemDescriptionFragment;
+import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
+import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
import de.danoeh.antennapod.preferences.PreferenceController;
import rx.Observable;
@@ -69,85 +65,40 @@ import rx.schedulers.Schedulers;
/**
* Activity for playing audio files.
*/
-public class AudioplayerActivity extends MediaplayerActivity implements ItemDescriptionFragment.ItemDescriptionFragmentCallback,
- NavDrawerActivity {
+public class AudioplayerActivity extends MediaplayerActivity implements NavDrawerActivity {
private static final int POS_COVER = 0;
private static final int POS_DESCR = 1;
private static final int POS_CHAPTERS = 2;
private static final int NUM_CONTENT_FRAGMENTS = 3;
- private static final int POS_NONE = -1;
final String TAG = "AudioplayerActivity";
private static final String PREFS = "AudioPlayerActivityPreferences";
private static final String PREF_KEY_SELECTED_FRAGMENT_POSITION = "selectedFragmentPosition";
- private static final String PREF_PLAYABLE_ID = "playableId";
+
+ public static final String[] NAV_DRAWER_TAGS = {
+ QueueFragment.TAG,
+ EpisodesFragment.TAG,
+ DownloadsFragment.TAG,
+ PlaybackHistoryFragment.TAG,
+ AddFeedFragment.TAG
+ };
+
+ private AtomicBoolean isSetup = new AtomicBoolean(false);
private DrawerLayout drawerLayout;
private NavListAdapter navAdapter;
private ListView navList;
- private AdapterView.AdapterContextMenuInfo lastMenuInfo = null;
private View navDrawer;
private ActionBarDrawerToggle drawerToggle;
+ private int mPosition = -1;
- private Fragment[] detachedFragments;
-
- private CoverFragment coverFragment;
- private ItemDescriptionFragment descriptionFragment;
- private ListFragment chapterFragment;
-
- private Fragment currentlyShownFragment;
- private int currentlyShownPosition = -1;
- private int lastShownPosition = POS_NONE;
- /**
- * Used if onResume was called without loadMediaInfo.
- */
- private int savedPosition = -1;
-
- private TextView txtvTitle;
- private Button butPlaybackSpeed;
- private ImageButton butNavChaptersShownotes;
- private ImageButton butShowCover;
+ private Playable media;
+ private ViewPager pager;
+ private AudioplayerPagerAdapter pagerAdapter;
private Subscription subscription;
- private PopupWindow popupWindow;
-
- private void resetFragmentView() {
- FragmentTransaction fT = getSupportFragmentManager().beginTransaction();
-
- if (coverFragment != null) {
- Log.d(TAG, "Removing cover fragment");
- fT.remove(coverFragment);
- }
- if (descriptionFragment != null) {
- Log.d(TAG, "Removing description fragment");
- fT.remove(descriptionFragment);
- }
- if (chapterFragment != null) {
- Log.d(TAG, "Removing chapter fragment");
- fT.remove(chapterFragment);
- }
- if (currentlyShownFragment != null) {
- Log.d(TAG, "Removing currently shown fragment");
- fT.remove(currentlyShownFragment);
- }
- for (int i = 0; i < detachedFragments.length; i++) {
- Fragment f = detachedFragments[i];
- if (f != null) {
- Log.d(TAG, "Removing detached fragment");
- fT.remove(f);
- }
- }
- fT.commit();
- currentlyShownFragment = null;
- coverFragment = null;
- descriptionFragment = null;
- chapterFragment = null;
- currentlyShownPosition = -1;
- detachedFragments = new Fragment[NUM_CONTENT_FRAGMENTS];
- }
-
@Override
protected void onStop() {
super.onStop();
@@ -156,91 +107,48 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
subscription.unsubscribe();
}
EventDistributor.getInstance().unregister(contentUpdate);
+ saveCurrentFragment();
}
@Override
- protected void chooseTheme() {
- setTheme(UserPreferences.getNoTitleTheme());
+ public void onDestroy() {
+ super.onDestroy();
+ // don't risk creating memory leaks
+ navAdapter = null;
+ drawerToggle = null;
+ pager = null;
+ pagerAdapter = null;
}
@Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- detachedFragments = new Fragment[NUM_CONTENT_FRAGMENTS];
+ protected void chooseTheme() {
+ setTheme(UserPreferences.getNoTitleTheme());
}
- private void savePreferences() {
+ private void saveCurrentFragment() {
+ if(pager == null) {
+ return;
+ }
Log.d(TAG, "Saving preferences");
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- if (currentlyShownPosition >= 0 && controller != null
- && controller.getMedia() != null) {
- editor.putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION,
- currentlyShownPosition);
- editor.putString(PREF_PLAYABLE_ID, controller.getMedia()
- .getIdentifier().toString());
- } else {
- editor.putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, -1);
- editor.putString(PREF_PLAYABLE_ID, "");
- }
- editor.commit();
-
- savedPosition = currentlyShownPosition;
+ prefs.edit()
+ .putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, pager.getCurrentItem())
+ .commit();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- drawerToggle.onConfigurationChanged(newConfig);
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- // super.onSaveInstanceState(outState); would cause crash
- Log.d(TAG, "onSaveInstanceState");
- }
-
- @Override
- protected void onPause() {
- savePreferences();
- resetFragmentView();
- super.onPause();
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- super.onRestoreInstanceState(savedInstanceState);
- restoreFromPreferences();
+ if(drawerToggle != null) {
+ drawerToggle.onConfigurationChanged(newConfig);
+ }
}
- /**
- * Tries to restore the selected fragment position from the Activity's
- * preferences.
- *
- * @return true if restoreFromPrefernces changed the activity's state
- */
- private boolean restoreFromPreferences() {
+ private void loadLastFragment() {
Log.d(TAG, "Restoring instance state");
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
- int savedPosition = prefs.getInt(PREF_KEY_SELECTED_FRAGMENT_POSITION,
- -1);
- String playableId = prefs.getString(PREF_PLAYABLE_ID, "");
-
- if (savedPosition != -1
- && controller != null
- && controller.getMedia() != null
- && controller.getMedia().getIdentifier().toString()
- .equals(playableId)) {
- switchToFragment(savedPosition);
- return true;
- } else if (controller == null || controller.getMedia() == null) {
- Log.d(TAG, "Couldn't restore from preferences: controller or media was null");
- } else {
- Log.d(TAG, "Couldn't restore from preferences: savedPosition was -1 or saved identifier and playable identifier didn't match.\nsavedPosition: "
- + savedPosition + ", id: " + playableId);
-
- }
- return false;
+ int lastPosition = prefs.getInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, -1);
+ pager.setCurrentItem(lastPosition);
}
@Override
@@ -260,8 +168,9 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
true);
startService(launchIntent);
}
- if (savedPosition != -1) {
- switchToFragment(savedPosition);
+ if(pagerAdapter != null && controller != null && controller.getMedia() != media) {
+ media = controller.getMedia();
+ pagerAdapter.onMediaChanged(media);
}
EventDistributor.getInstance().register(contentUpdate);
@@ -292,147 +201,28 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
@Override
protected void clearStatusMsg() {
// TODO Hide progress bar here
-
}
- /**
- * Changes the currently displayed fragment.
- *
- * @param pos Must be POS_COVER, POS_DESCR, or POS_CHAPTERS
- */
- private void switchToFragment(int pos) {
- Log.d(TAG, "Switching contentView to position " + pos);
- if (currentlyShownPosition != pos && controller != null) {
- Playable media = controller.getMedia();
- if (media != null) {
- FragmentTransaction ft = getSupportFragmentManager()
- .beginTransaction();
- if (currentlyShownFragment != null) {
- detachedFragments[currentlyShownPosition] = currentlyShownFragment;
- ft.detach(currentlyShownFragment);
- }
- switch (pos) {
- case POS_COVER:
- if (coverFragment == null) {
- Log.i(TAG, "Using new coverfragment");
- coverFragment = CoverFragment.newInstance(media);
- }
- currentlyShownFragment = coverFragment;
- break;
- case POS_DESCR:
- if (descriptionFragment == null) {
- descriptionFragment = ItemDescriptionFragment
- .newInstance(media, true, true);
- }
- currentlyShownFragment = descriptionFragment;
- break;
- case POS_CHAPTERS:
- if (chapterFragment == null) {
- chapterFragment = new ListFragment() {
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- // add padding
- final ListView lv = getListView();
- lv.setClipToPadding(false);
- final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
- lv.setPadding(0, vertPadding, 0, vertPadding);
- }
- };
- chapterFragment.setListAdapter(new ChapterListAdapter(
- AudioplayerActivity.this, 0, media
- .getChapters(), media, position -> {
- Chapter chapter = (Chapter)
- chapterFragment.getListAdapter().getItem(position);
- controller.seekToChapter(chapter);
- }
- ));
- }
- currentlyShownFragment = chapterFragment;
- break;
- }
- if (currentlyShownFragment != null) {
- lastShownPosition = currentlyShownPosition;
- currentlyShownPosition = pos;
- if (detachedFragments[pos] != null) {
- Log.d(TAG, "Reattaching fragment at position " + pos);
- ft.attach(detachedFragments[pos]);
- } else {
- ft.add(R.id.contentView, currentlyShownFragment);
- }
- ft.disallowAddToBackStack();
- ft.commit();
- updateNavButtonDrawable();
- }
- }
- }
- }
-
- /**
- * Switches to the fragment that was displayed before the current one or the description fragment
- * if no fragment was previously displayed.
- */
- public void switchToLastFragment() {
- if (lastShownPosition != POS_NONE) {
- switchToFragment(lastShownPosition);
- } else {
- switchToFragment(POS_DESCR);
- }
- }
-
- private void updateNavButtonDrawable() {
-
- final int[] buttonTexts = new int[]{R.string.show_shownotes_label,
- R.string.show_chapters_label};
-
- final TypedArray drawables = obtainStyledAttributes(new int[]{
- R.attr.navigation_shownotes, R.attr.navigation_chapters});
- final Playable media = controller.getMedia();
- if (butNavChaptersShownotes != null && butShowCover != null && media != null) {
-
- butNavChaptersShownotes.setTag(R.id.imageloader_key, null);
- setNavButtonVisibility();
- switch (currentlyShownPosition) {
- case POS_COVER:
- butShowCover.setVisibility(View.GONE);
- if (lastShownPosition == POS_CHAPTERS) {
- butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(1));
- butNavChaptersShownotes.setContentDescription(getString(buttonTexts[1]));
- } else {
- butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(0));
- butNavChaptersShownotes.setContentDescription(getString(buttonTexts[0]));
- }
- break;
- case POS_DESCR:
- butShowCover.setVisibility(View.VISIBLE);
- butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(1));
- butNavChaptersShownotes.setContentDescription(getString(buttonTexts[1]));
- break;
- case POS_CHAPTERS:
- butShowCover.setVisibility(View.VISIBLE);
- butNavChaptersShownotes.setImageDrawable(drawables.getDrawable(0));
- butNavChaptersShownotes.setContentDescription(getString(buttonTexts[0]));
- break;
- }
- }
- drawables.recycle();
- }
@Override
protected void setupGUI() {
+ if(isSetup.getAndSet(true)) {
+ return;
+ }
super.setupGUI();
- resetFragmentView();
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setTitle("");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ findViewById(R.id.shadow).setVisibility(View.GONE);
+ AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appBar);
+ float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());
+ appBarLayout.setElevation(px);
+ }
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
navList = (ListView) findViewById(R.id.nav_list);
navDrawer = findViewById(R.id.nav_layout);
- butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed);
- butNavChaptersShownotes = (ImageButton) findViewById(R.id.butNavChaptersShownotes);
- butShowCover = (ImageButton) findViewById(R.id.butCover);
- txtvTitle = (TextView) findViewById(R.id.txtvTitle);
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close);
drawerToggle.setDrawerIndicatorEnabled(false);
@@ -450,6 +240,15 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
}
drawerLayout.closeDrawer(navDrawer);
});
+ navList.setOnItemLongClickListener((parent, view, position, id) -> {
+ if (position < navAdapter.getTags().size()) {
+ showDrawerPreferencesDialog();
+ return true;
+ } else {
+ mPosition = position;
+ return false;
+ }
+ });
registerForContextMenu(navList);
drawerToggle.syncState();
@@ -458,128 +257,13 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
startActivity(new Intent(AudioplayerActivity.this, PreferenceController.getPreferenceActivity()));
});
- butNavChaptersShownotes.setOnClickListener(v -> {
- if (currentlyShownPosition == POS_CHAPTERS) {
- switchToFragment(POS_DESCR);
- } else if (currentlyShownPosition == POS_DESCR) {
- switchToFragment(POS_CHAPTERS);
- } else if (currentlyShownPosition == POS_COVER) {
- switchToLastFragment();
- }
- });
-
- butShowCover.setOnClickListener(v -> switchToFragment(POS_COVER));
-
- butPlaybackSpeed.setOnClickListener(v -> {
- if (controller != null && controller.canSetPlaybackSpeed()) {
- String[] availableSpeeds = UserPreferences
- .getPlaybackSpeedArray();
- String currentSpeed = UserPreferences.getPlaybackSpeed();
-
- // Provide initial value in case the speed list has changed
- // out from under us
- // and our current speed isn't in the new list
- String newSpeed;
- if (availableSpeeds.length > 0) {
- newSpeed = availableSpeeds[0];
- } else {
- newSpeed = "1.0";
- }
-
- for (int i = 0; i < availableSpeeds.length; i++) {
- if (availableSpeeds[i].equals(currentSpeed)) {
- if (i == availableSpeeds.length - 1) {
- newSpeed = availableSpeeds[0];
- } else {
- newSpeed = availableSpeeds[i + 1];
- }
- break;
- }
- }
- UserPreferences.setPlaybackSpeed(newSpeed);
- controller.setPlaybackSpeed(Float.parseFloat(newSpeed));
- }
- });
-
- butPlaybackSpeed.setOnLongClickListener(v -> {
-
- String[] availableSpeeds = getResources().getStringArray(R.array.playback_speed_values);
- String currentSpeed = UserPreferences.getPlaybackSpeed();
-
- LayoutInflater inflater = getLayoutInflater();
- View popupView = inflater.inflate(R.layout.choose_speed_dialog, null);
- TextView txtvSelectedSpeed = (TextView) popupView.findViewById(R.id.txtvSelectedSpeed);
- SeekBar sbSelectSpeed = (SeekBar) popupView.findViewById(R.id.sbSelectSpeed);
-
- txtvSelectedSpeed.setText(currentSpeed);
- int progress = ArrayUtils.indexOf(availableSpeeds, currentSpeed);
- int max = Math.max(progress, ArrayUtils.indexOf(availableSpeeds, "2.50"));
- sbSelectSpeed.setMax(max);
- sbSelectSpeed.setProgress(progress);
- sbSelectSpeed.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- txtvSelectedSpeed.setText(availableSpeeds[progress]);
- }
-
- @Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- String selectedSpeed = availableSpeeds[sbSelectSpeed.getProgress()];
- UserPreferences.setPlaybackSpeed(selectedSpeed);
- controller.setPlaybackSpeed(Float.parseFloat(selectedSpeed));
- if (popupWindow != null && popupWindow.isShowing()) {
- popupWindow.dismiss();
- }
- ScaleAnimation anim = new ScaleAnimation(1.0f, 1.33f, 1.0f, 1.33f,
- butPlaybackSpeed.getWidth()/2, butPlaybackSpeed.getHeight()/2);
- anim.setDuration(150);
- anim.setRepeatMode(ScaleAnimation.REVERSE);
- anim.setRepeatCount(1);
- anim.setInterpolator(new LinearInterpolator());
- butPlaybackSpeed.startAnimation(anim);
- }
- });
- popupWindow = new PopupWindow(popupView,
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT,
- true);
- popupWindow.setBackgroundDrawable(new BitmapDrawable());
- popupWindow.setOutsideTouchable(true);
- popupWindow.showAtLocation(popupView, Gravity.CENTER, 0, 0);
- return true;
- });
- }
-
- private void setNavButtonVisibility() {
- if (butNavChaptersShownotes != null) {
- if (controller != null) {
- Playable media = controller.getMedia();
- if (media != null) {
- if (media.getChapters() != null || currentlyShownPosition == POS_COVER) {
- butNavChaptersShownotes.setVisibility(View.VISIBLE);
- return;
- }
- }
- }
- butNavChaptersShownotes.setVisibility(View.GONE);
- }
-
- }
-
- @Override
- protected void onPlaybackSpeedChange() {
- super.onPlaybackSpeedChange();
- updateButPlaybackSpeed();
- }
-
- private void updateButPlaybackSpeed() {
- if (controller != null && controller.canSetPlaybackSpeed()) {
- butPlaybackSpeed.setText(UserPreferences.getPlaybackSpeed());
- }
+ pager = (ViewPager) findViewById(R.id.pager);
+ pagerAdapter = new AudioplayerPagerAdapter(getSupportFragmentManager());
+ pager.setAdapter(pagerAdapter);
+ CirclePageIndicator pageIndicator = (CirclePageIndicator) findViewById(R.id.page_indicator);
+ pageIndicator.setViewPager(pager);
+ loadLastFragment();
+ pager.onSaveInstanceState();
}
@Override
@@ -593,49 +277,23 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
if (!super.loadMediaInfo()) {
return false;
}
- final Playable media = controller.getMedia();
- if (media == null) {
- return false;
- }
- txtvTitle.setText(media.getEpisodeTitle());
- getSupportActionBar().setTitle("");
- Glide.with(this)
- .load(media.getImageUri())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
- .into(butShowCover);
-
- setNavButtonVisibility();
-
- if (currentlyShownPosition == -1) {
- if (!restoreFromPreferences()) {
- switchToFragment(POS_COVER);
- }
- }
- if (currentlyShownFragment instanceof AudioplayerContentFragment) {
- ((AudioplayerContentFragment) currentlyShownFragment)
- .onDataSetChanged(media);
- }
-
- if (controller == null
- || !controller.canSetPlaybackSpeed()) {
- butPlaybackSpeed.setVisibility(View.GONE);
- } else {
- butPlaybackSpeed.setVisibility(View.VISIBLE);
+ if(controller.getMedia() != media) {
+ media = controller.getMedia();
+ pagerAdapter.onMediaChanged(media);
}
-
- updateButPlaybackSpeed();
return true;
}
public void notifyMediaPositionChanged() {
- if (chapterFragment != null) {
- ArrayAdapter<SimpleChapter> adapter = (ArrayAdapter<SimpleChapter>) chapterFragment
- .getListAdapter();
- adapter.notifyDataSetChanged();
+ if(pagerAdapter == null) {
+ return;
+ }
+ ChaptersFragment chaptersFragment = pagerAdapter.getChaptersFragment();
+ if(chaptersFragment != null) {
+ ChaptersListAdapter adapter = (ChaptersListAdapter) chaptersFragment.getListAdapter();
+ if (adapter != null) {
+ adapter.notifyDataSetChanged();
+ }
}
}
@@ -659,7 +317,6 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
clearStatusMsg();
}
- @Override
public PlaybackController getPlaybackController() {
return controller;
}
@@ -669,10 +326,6 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer);
}
- public interface AudioplayerContentFragment {
- void onDataSetChanged(Playable media);
- }
-
@Override
protected int getContentViewResourceId() {
return R.layout.audioplayer_activity;
@@ -704,24 +357,15 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
menu.setHeaderTitle(feed.getTitle());
// episodes are not loaded, so we cannot check if the podcast has new or unplayed ones!
-
- // we may need to reference this elsewhere...
- lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
}
@Override
public boolean onContextItemSelected(MenuItem item) {
- AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
-
- if(menuInfo == null) {
- menuInfo = lastMenuInfo;
- }
-
- if(menuInfo.targetView.getParent() instanceof ListView == false
- || ((ListView)menuInfo.targetView.getParent()).getId() != R.id.nav_list) {
+ final int position = mPosition;
+ mPosition = -1; // reset
+ if(position < 0) {
return false;
}
- int position = menuInfo.position;
Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
switch(item.getItemId()) {
case R.id.mark_all_seen_item:
@@ -768,6 +412,47 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
}
}
+ @Override
+ public void onBackPressed() {
+ if(isDrawerOpen()) {
+ drawerLayout.closeDrawer(navDrawer);
+ } else if (pager == null || pager.getCurrentItem() == 0) {
+ // If the user is currently looking at the first step, allow the system to handle the
+ // Back button. This calls finish() on this activity and pops the back stack.
+ super.onBackPressed();
+ } else {
+ // Otherwise, select the previous step.
+ pager.setCurrentItem(pager.getCurrentItem() - 1);
+ }
+ }
+
+ public void showDrawerPreferencesDialog() {
+ final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems();
+ String[] navLabels = new String[NAV_DRAWER_TAGS.length];
+ final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length];
+ for (int i = 0; i < NAV_DRAWER_TAGS.length; i++) {
+ String tag = NAV_DRAWER_TAGS[i];
+ navLabels[i] = navAdapter.getLabel(tag);
+ if (!hiddenDrawerItems.contains(tag)) {
+ checked[i] = true;
+ }
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.drawer_preferences);
+ builder.setMultiChoiceItems(navLabels, checked, (dialog, which, isChecked) -> {
+ if (isChecked) {
+ hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]);
+ } else {
+ hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]);
+ }
+ });
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ UserPreferences.setHiddenDrawerItems(hiddenDrawerItems);
+ });
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ }
private DBReader.NavDrawerData navDrawerData;
@@ -810,7 +495,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
@Override
public Feed getItem(int position) {
- if (navDrawerData != null && position < navDrawerData.feeds.size()) {
+ if (navDrawerData != null && 0 <= position && position < navDrawerData.feeds.size()) {
return navDrawerData.feeds.get(position);
} else {
return null;
@@ -833,9 +518,80 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
}
@Override
+ public int getNumberOfDownloadedItems() {
+ return (navDrawerData != null) ? navDrawerData.numDownloadedItems : 0;
+ }
+
+ @Override
+ public int getReclaimableItems() {
+ return (navDrawerData != null) ? navDrawerData.reclaimableSpace : 0;
+ }
+
+ @Override
public int getFeedCounter(long feedId) {
return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0;
}
};
+ public interface AudioplayerContentFragment {
+ void onMediaChanged(Playable media);
+ }
+
+ private class AudioplayerPagerAdapter extends FragmentStatePagerAdapter {
+
+ public AudioplayerPagerAdapter(FragmentManager fm) {
+ super(fm);
+ }
+
+ private CoverFragment coverFragment;
+ private ItemDescriptionFragment itemDescriptionFragment;
+ private ChaptersFragment chaptersFragment;
+
+ public void onMediaChanged(Playable media) {
+ if(coverFragment != null) {
+ coverFragment.onMediaChanged(media);
+ }
+ if(itemDescriptionFragment != null) {
+ itemDescriptionFragment.onMediaChanged(media);
+ }
+ if(chaptersFragment != null) {
+ chaptersFragment.onMediaChanged(media);
+ }
+ }
+
+ @Nullable
+ public ChaptersFragment getChaptersFragment() {
+ return chaptersFragment;
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ Log.d(TAG, "getItem(" + position + ")");
+ switch (position) {
+ case POS_COVER:
+ if(coverFragment == null) {
+ coverFragment = CoverFragment.newInstance(media);
+ }
+ return coverFragment;
+ case POS_DESCR:
+ if(itemDescriptionFragment == null) {
+ itemDescriptionFragment = ItemDescriptionFragment.newInstance(media, true, true);
+ }
+ return itemDescriptionFragment;
+ case POS_CHAPTERS:
+ if(chaptersFragment == null) {
+ chaptersFragment = ChaptersFragment.newInstance(media, controller);
+ }
+ return chaptersFragment;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public int getCount() {
+ return NUM_CONTENT_FRAGMENTS;
+ }
+ }
+
}
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 80883e4ae..9116decb0 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
@@ -18,9 +18,9 @@ import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.CheckBox;
-import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageView;
+import android.widget.RadioButton;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
@@ -32,6 +32,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedFilter;
import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -61,8 +62,13 @@ public class FeedInfoActivity extends ActionBarActivity {
private TextView txtvUrl;
private EditText etxtUsername;
private EditText etxtPassword;
+ private EditText etxtFilterText;
+ private RadioButton rdoFilterInclude;
+ private RadioButton rdoFilterExclude;
private CheckBox cbxAutoDownload;
+ private CheckBox cbxKeepUpdated;
private Spinner spnAutoDelete;
+ private boolean filterInclude = true;
private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() {
@Override
@@ -100,9 +106,21 @@ public class FeedInfoActivity extends ActionBarActivity {
txtvAuthor = (TextView) findViewById(R.id.txtvAuthor);
txtvUrl = (TextView) findViewById(R.id.txtvUrl);
cbxAutoDownload = (CheckBox) findViewById(R.id.cbxAutoDownload);
+ cbxKeepUpdated = (CheckBox) findViewById(R.id.cbxKeepUpdated);
spnAutoDelete = (Spinner) findViewById(R.id.spnAutoDelete);
etxtUsername = (EditText) findViewById(R.id.etxtUsername);
etxtPassword = (EditText) findViewById(R.id.etxtPassword);
+ etxtFilterText = (EditText) findViewById(R.id.etxtEpisodeFilterText);
+ rdoFilterInclude = (RadioButton) findViewById(R.id.radio_filter_include);
+ rdoFilterInclude.setOnClickListener(v -> {
+ filterInclude = true;
+ filterTextChanged = true;
+ });
+ rdoFilterExclude = (RadioButton) findViewById(R.id.radio_filter_exclude);
+ rdoFilterExclude.setOnClickListener(v -> {
+ filterInclude = false;
+ filterTextChanged = true;
+ });
txtvUrl.setOnClickListener(copyUrlToClipboard);
@@ -151,15 +169,18 @@ public class FeedInfoActivity extends ActionBarActivity {
cbxAutoDownload.setEnabled(UserPreferences.isEnableAutodownload());
cbxAutoDownload.setChecked(prefs.getAutoDownload());
- cbxAutoDownload.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
- feed.getPreferences().setAutoDownload(checked);
- feed.savePreferences(FeedInfoActivity.this);
- ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedInfoActivity.this,
- feed, checked);
- dialog.createNewDialog().show();
- }
+ cbxAutoDownload.setOnCheckedChangeListener((compoundButton, checked) -> {
+ feed.getPreferences().setAutoDownload(checked);
+ feed.savePreferences(FeedInfoActivity.this);
+ updateAutoDownloadSettings();
+ ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedInfoActivity.this,
+ feed, checked);
+ dialog.createNewDialog().show();
+ });
+ cbxKeepUpdated.setChecked(prefs.getKeepUpdated());
+ cbxKeepUpdated.setOnCheckedChangeListener((compoundButton, checked) -> {
+ feed.getPreferences().setKeepUpdated(checked);
+ feed.savePreferences(FeedInfoActivity.this);
});
spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
@@ -184,6 +205,7 @@ public class FeedInfoActivity extends ActionBarActivity {
feed.getPreferences().setAutoDeleteAction(auto_delete_action);// p
autoDeleteChanged = true;
}
+
@Override
public void onNothingSelected(AdapterView<?> parent) {
// Another interface callback
@@ -197,8 +219,27 @@ public class FeedInfoActivity extends ActionBarActivity {
etxtUsername.addTextChangedListener(authTextWatcher);
etxtPassword.addTextChangedListener(authTextWatcher);
- supportInvalidateOptionsMenu();
+ FeedFilter filter = prefs.getFilter();
+ if (filter.includeOnly()) {
+ 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);
+ rdoFilterExclude.setChecked(false);
+ etxtFilterText.setText("");
+ }
+ etxtFilterText.addTextChangedListener(filterTextWatcher);
+ supportInvalidateOptionsMenu();
+ updateAutoDownloadSettings();
} else {
Log.e(TAG, "Activity was started with invalid arguments");
}
@@ -227,6 +268,25 @@ public class FeedInfoActivity extends ActionBarActivity {
}
};
+ private boolean filterTextChanged = false;
+
+ private TextWatcher filterTextWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ filterTextChanged = true;
+ }
+ };
+
@Override
protected void onPause() {
super.onPause();
@@ -237,11 +297,24 @@ public class FeedInfoActivity extends ActionBarActivity {
prefs.setUsername(etxtUsername.getText().toString());
prefs.setPassword(etxtPassword.getText().toString());
}
- if (authInfoChanged || autoDeleteChanged) {
+ if (filterTextChanged) {
+ Log.d(TAG, "Filter info changed, saving...");
+ String filterText = etxtFilterText.getText().toString();
+ String includeString = "";
+ String excludeString = "";
+ if (filterInclude) {
+ includeString = filterText;
+ } else {
+ excludeString = filterText;
+ }
+ prefs.setFilter(new FeedFilter(includeString, excludeString));
+ }
+ if (authInfoChanged || autoDeleteChanged || filterTextChanged) {
DBWriter.setFeedPreferences(prefs);
}
authInfoChanged = false;
autoDeleteChanged = false;
+ filterTextChanged = false;
}
}
@@ -282,6 +355,15 @@ public class FeedInfoActivity extends ActionBarActivity {
}
}
+ private void updateAutoDownloadSettings() {
+ if (feed != null && feed.getPreferences() != null) {
+ boolean enabled = feed.getPreferences().getAutoDownload() && UserPreferences.isEnableAutodownload();
+ rdoFilterInclude.setEnabled(enabled);
+ rdoFilterExclude.setEnabled(enabled);
+ etxtFilterText.setEnabled(enabled);
+ }
+ }
+
private class ApplyToEpisodesDialog extends ConfirmationDialog {
private final Feed feed;
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 211b895d0..bd7da7c03 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -8,8 +8,6 @@ import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.DataSetObserver;
import android.media.AudioManager;
-import android.media.Rating;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -17,12 +15,12 @@ import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
+import android.util.TypedValue;
import android.view.ContextMenu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -73,7 +71,7 @@ import rx.schedulers.Schedulers;
/**
* The activity that is shown when the user launches the app.
*/
-public class MainActivity extends ActionBarActivity implements NavDrawerActivity {
+public class MainActivity extends AppCompatActivity implements NavDrawerActivity {
private static final String TAG = "MainActivity";
@@ -127,7 +125,13 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- getSupportActionBar().setElevation(3.0f);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ findViewById(R.id.shadow).setVisibility(View.GONE);
+ int elevation = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4,
+ getResources().getDisplayMetrics());
+ getSupportActionBar().setElevation(elevation);
+ }
currentTitle = getTitle();
@@ -220,6 +224,9 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
if (prefs.getBoolean(PREF_IS_FIRST_LAUNCH, true)) {
new Handler().postDelayed(() -> drawerLayout.openDrawer(navDrawer), 1500);
+ // for backward compatibility, we only change defaults for fresh installs
+ UserPreferences.setUpdateInterval(12);
+
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(PREF_IS_FIRST_LAUNCH, false);
edit.commit();
@@ -254,10 +261,6 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
builder.create().show();
}
- public ActionBar getMainActivtyActionBar() {
- return getSupportActionBar();
- }
-
public boolean isDrawerOpen() {
return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer);
}
@@ -365,10 +368,6 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
getSupportFragmentManager().popBackStack();
}
- public Toolbar getToolbar() {
- return toolbar;
- }
-
private int getSelectedNavListIndex() {
String currentFragment = getLastNavFragment();
if(currentFragment == null) {
@@ -593,8 +592,16 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
}
+ @Override
+ public void onBackPressed() {
+ if(isDrawerOpen()) {
+ drawerLayout.closeDrawer(navDrawer);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
private DBReader.NavDrawerData navDrawerData;
- private AsyncTask<Void, Void, DBReader.NavDrawerData> loadTask;
private int selectedNavListIndex = 0;
private NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() {
@@ -609,7 +616,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
@Override
public Feed getItem(int position) {
- if (navDrawerData != null && position < navDrawerData.feeds.size()) {
+ if (navDrawerData != null && 0 <= position && position < navDrawerData.feeds.size()) {
return navDrawerData.feeds.get(position);
} else {
return null;
@@ -632,10 +639,19 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
@Override
+ public int getNumberOfDownloadedItems() {
+ return (navDrawerData != null) ? navDrawerData.numDownloadedItems : 0;
+ }
+
+ @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/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index c53a5257b..0d878b556 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -1,32 +1,44 @@
package de.danoeh.antennapod.activity;
-import android.content.DialogInterface;
+import android.annotation.TargetApi;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.TypedArray;
+import android.graphics.Color;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v4.view.ViewCompat;
import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-
import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
+import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
+import com.bumptech.glide.Glide;
+import com.joanzapata.iconify.IconDrawable;
+import com.joanzapata.iconify.fonts.FontAwesomeIcons;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
@@ -34,25 +46,37 @@ import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.dialog.SleepTimerDialog;
+import de.danoeh.antennapod.dialog.VariableSpeedDialog;
+import rx.Observable;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
+
/**
* Provides general features which are both needed for playing audio and video
* files.
*/
-public abstract class MediaplayerActivity extends ActionBarActivity
- implements OnSeekBarChangeListener {
+public abstract class MediaplayerActivity extends AppCompatActivity implements OnSeekBarChangeListener {
private static final String TAG = "MediaplayerActivity";
+ private static final String PREFS = "MediaPlayerActivityPreferences";
+ private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft";
protected PlaybackController controller;
protected TextView txtvPosition;
protected TextView txtvLength;
protected SeekBar sbPosition;
- protected ImageButton butPlay;
+ protected Button butPlaybackSpeed;
protected ImageButton butRev;
protected TextView txtvRev;
+ protected ImageButton butPlay;
protected ImageButton butFF;
protected TextView txtvFF;
+ protected ImageButton butSkip;
+
+ protected boolean showTimeLeft = false;
+
+ private boolean isFavorite = false;
private PlaybackController newPlaybackController() {
return new PlaybackController(this, false) {
@@ -152,7 +176,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
}
protected void onPlaybackSpeedChange() {
-
+ updateButPlaybackSpeed();
}
protected void onServiceQueried() {
@@ -182,8 +206,10 @@ public abstract class MediaplayerActivity extends ActionBarActivity
@Override
protected void onPause() {
super.onPause();
- controller.reinitServiceIfPaused();
- controller.pause();
+ if(controller != null) {
+ controller.reinitServiceIfPaused();
+ controller.pause();
+ }
}
/**
@@ -205,8 +231,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
protected void onBufferUpdate(float progress) {
if (sbPosition != null) {
- sbPosition.setSecondaryProgress((int) progress
- * sbPosition.getMax());
+ sbPosition.setSecondaryProgress((int) progress * sbPosition.getMax());
}
}
@@ -233,10 +258,17 @@ public abstract class MediaplayerActivity extends ActionBarActivity
}
}
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ Glide.get(this).trimMemory(level);
+ }
+
@Override
- protected void onDestroy() {
- super.onDestroy();
- Log.d(TAG, "onDestroy()");
+ public void onLowMemory() {
+ super.onLowMemory();
+ Glide.get(this).clearMemory();
}
@Override
@@ -250,18 +282,23 @@ public abstract class MediaplayerActivity extends ActionBarActivity
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
+ if (controller == null) {
+ return false;
+ }
Playable media = controller.getMedia();
menu.findItem(R.id.support_item).setVisible(
media != null && media.getPaymentLink() != null &&
(media instanceof FeedMedia) &&
+ ((FeedMedia) media).getItem() != null &&
((FeedMedia) media).getItem().getFlattrStatus().flattrable()
);
boolean hasWebsiteLink = media != null && media.getWebsiteLink() != null;
menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink);
- boolean isItemAndHasLink = media != null && (media instanceof FeedMedia) && ((FeedMedia) media).getItem().getLink() != null;
+ boolean isItemAndHasLink = media != null && (media instanceof FeedMedia) &&
+ ((FeedMedia) media).getItem() != null && ((FeedMedia) media).getItem().getLink() != null;
menu.findItem(R.id.share_link_item).setVisible(isItemAndHasLink);
menu.findItem(R.id.share_link_with_position_item).setVisible(isItemAndHasLink);
@@ -271,16 +308,37 @@ public abstract class MediaplayerActivity extends ActionBarActivity
menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink);
- menu.findItem(R.id.skip_episode_item).setVisible(media != null);
+ menu.findItem(R.id.add_to_favorites_item).setVisible(false);
+ menu.findItem(R.id.remove_from_favorites_item).setVisible(false);
+ if(media != null && media instanceof FeedMedia) {
+ menu.findItem(R.id.add_to_favorites_item).setVisible(!isFavorite);
+ menu.findItem(R.id.remove_from_favorites_item).setVisible(isFavorite);
+ }
+
boolean sleepTimerSet = controller.sleepTimerActive();
boolean sleepTimerNotSet = controller.sleepTimerNotActive();
menu.findItem(R.id.set_sleeptimer_item).setVisible(sleepTimerNotSet);
menu.findItem(R.id.disable_sleeptimer_item).setVisible(sleepTimerSet);
+
+ if (this instanceof AudioplayerActivity) {
+ int[] attrs = {R.attr.action_bar_icon_color};
+ TypedArray ta = obtainStyledAttributes(UserPreferences.getTheme(), attrs);
+ int textColor = ta.getColor(0, Color.GRAY);
+ ta.recycle();
+ menu.findItem(R.id.audio_controls).setIcon(new IconDrawable(this,
+ FontAwesomeIcons.fa_sliders).color(textColor).actionBarSize());
+ } else {
+ menu.findItem(R.id.audio_controls).setVisible(false);
+ }
+
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
+ if (controller == null) {
+ return false;
+ }
Playable media = controller.getMedia();
if (item.getItemId() == android.R.id.home) {
Intent intent = new Intent(MediaplayerActivity.this,
@@ -289,84 +347,235 @@ public abstract class MediaplayerActivity extends ActionBarActivity
| Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
return true;
- } else if (media != null) {
- switch (item.getItemId()) {
- case R.id.disable_sleeptimer_item:
- if (controller.serviceAvailable()) {
-
- MaterialDialog.Builder stDialog = new MaterialDialog.Builder(this);
- stDialog.title(R.string.sleep_timer_label);
- stDialog.content(getString(R.string.time_left_label)
- + Converter.getDurationStringLong((int) controller
- .getSleepTimerTimeLeft()));
- stDialog.positiveText(R.string.disable_sleeptimer_label);
- stDialog.negativeText(R.string.cancel_label);
- stDialog.callback(new MaterialDialog.ButtonCallback() {
+ } else {
+ if (media != null) {
+ switch (item.getItemId()) {
+ case R.id.add_to_favorites_item:
+ if(media instanceof FeedMedia) {
+ FeedItem feedItem = ((FeedMedia)media).getItem();
+ if(feedItem != null) {
+ DBWriter.addFavoriteItem(feedItem);
+ isFavorite = true;
+ invalidateOptionsMenu();
+ Toast.makeText(this, R.string.added_to_favorites, Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+ break;
+ case R.id.remove_from_favorites_item:
+ if(media instanceof FeedMedia) {
+ FeedItem feedItem = ((FeedMedia)media).getItem();
+ if(feedItem != null) {
+ DBWriter.removeFavoriteItem(feedItem);
+ isFavorite = false;
+ invalidateOptionsMenu();
+ Toast.makeText(this, R.string.removed_from_favorites, Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+ break;
+ case R.id.disable_sleeptimer_item:
+ if (controller.serviceAvailable()) {
+
+ MaterialDialog.Builder stDialog = new MaterialDialog.Builder(this);
+ stDialog.title(R.string.sleep_timer_label);
+ stDialog.content(getString(R.string.time_left_label)
+ + Converter.getDurationStringLong((int) controller
+ .getSleepTimerTimeLeft()));
+ stDialog.positiveText(R.string.disable_sleeptimer_label);
+ stDialog.negativeText(R.string.cancel_label);
+ stDialog.callback(new MaterialDialog.ButtonCallback() {
+ @Override
+ public void onPositive(MaterialDialog dialog) {
+ dialog.dismiss();
+ controller.disableSleepTimer();
+ }
+
+ @Override
+ public void onNegative(MaterialDialog dialog) {
+ dialog.dismiss();
+ }
+ });
+ stDialog.build().show();
+ }
+ break;
+ case R.id.set_sleeptimer_item:
+ if (controller.serviceAvailable()) {
+ SleepTimerDialog td = new SleepTimerDialog(this) {
+ @Override
+ public void onTimerSet(long millis, boolean shakeToReset, boolean vibrate) {
+ controller.setSleepTimer(millis, shakeToReset, vibrate);
+ }
+ };
+ td.createNewDialog().show();
+ }
+ break;
+ case R.id.audio_controls:
+ MaterialDialog dialog = new MaterialDialog.Builder(this)
+ .title(R.string.audio_controls)
+ .customView(R.layout.audio_controls, true)
+ .neutralText(R.string.close_label)
+ .onNeutral((dialog1, which) -> {
+ final SeekBar left = (SeekBar) dialog1.findViewById(R.id.volume_left);
+ final SeekBar right = (SeekBar) dialog1.findViewById(R.id.volume_right);
+ UserPreferences.setVolume(left.getProgress(), right.getProgress());
+ })
+ .show();
+ final SeekBar barPlaybackSpeed = (SeekBar) dialog.findViewById(R.id.playback_speed);
+ final Button butDecSpeed = (Button) dialog.findViewById(R.id.butDecSpeed);
+ butDecSpeed.setOnClickListener(v -> {
+ if(controller != null && controller.canSetPlaybackSpeed()) {
+ barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 2);
+ } else {
+ VariableSpeedDialog.showGetPluginDialog(this);
+ }
+ });
+ final Button butIncSpeed = (Button) dialog.findViewById(R.id.butIncSpeed);
+ butIncSpeed.setOnClickListener(v -> {
+ if(controller != null && controller.canSetPlaybackSpeed()) {
+ barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 2);
+ } else {
+ VariableSpeedDialog.showGetPluginDialog(this);
+ }
+ });
+
+ final TextView txtvPlaybackSpeed = (TextView) dialog.findViewById(R.id.txtvPlaybackSpeed);
+ float currentSpeed = 1.0f;
+ try {
+ currentSpeed = Float.parseFloat(UserPreferences.getPlaybackSpeed());
+ } catch (NumberFormatException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ UserPreferences.setPlaybackSpeed(String.valueOf(currentSpeed));
+ }
+
+ txtvPlaybackSpeed.setText(String.format("%.2fx", currentSpeed));
+ barPlaybackSpeed.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if(controller != null && controller.canSetPlaybackSpeed()) {
+ float playbackSpeed = (progress + 10) / 20.0f;
+ controller.setPlaybackSpeed(playbackSpeed);
+ String speed = String.format("%.2f", playbackSpeed);
+ UserPreferences.setPlaybackSpeed(speed);
+ txtvPlaybackSpeed.setText(speed + "x");
+ } else if(fromUser) {
+ float speed = Float.valueOf(UserPreferences.getPlaybackSpeed());
+ barPlaybackSpeed.post(() -> {
+ barPlaybackSpeed.setProgress((int) (20 * speed) - 10);
+ });
+ }
+ }
+
@Override
- public void onPositive(MaterialDialog dialog) {
- dialog.dismiss();
- controller.disableSleepTimer();
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if(controller != null && !controller.canSetPlaybackSpeed()) {
+ VariableSpeedDialog.showGetPluginDialog(MediaplayerActivity.this);
+ }
}
@Override
- public void onNegative(MaterialDialog dialog) {
- dialog.dismiss();
+ public void onStopTrackingTouch(SeekBar seekBar) {
}
});
- stDialog.build().show();
- }
- break;
- case R.id.set_sleeptimer_item:
- if (controller.serviceAvailable()) {
- SleepTimerDialog td = new SleepTimerDialog(this) {
+ barPlaybackSpeed.setProgress((int) (20 * currentSpeed) - 10);
+
+ final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left);
+ barLeftVolume.setProgress(100);
+ final SeekBar barRightVolume = (SeekBar) dialog.findViewById(R.id.volume_right);
+ barRightVolume.setProgress(100);
+ final CheckBox stereoToMono = (CheckBox) dialog.findViewById(R.id.stereo_to_mono);
+ stereoToMono.setChecked(UserPreferences.stereoToMono());
+ if (controller != null && !controller.canDownmix()) {
+ stereoToMono.setEnabled(false);
+ String sonicOnly = getString(R.string.sonic_only);
+ stereoToMono.setText(stereoToMono.getText() + " [" + sonicOnly + "]");
+ }
+
+ barLeftVolume.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
- public void onTimerSet(long millis, boolean shakeToReset, boolean vibrate) {
- controller.setSleepTimer(millis, shakeToReset, vibrate);
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ float leftVolume = 1.0f, rightVolume = 1.0f;
+ if (progress < 100) {
+ leftVolume = progress / 100.0f;
+ }
+ if (barRightVolume.getProgress() < 100) {
+ rightVolume = barRightVolume.getProgress() / 100.0f;
+ }
+ controller.setVolume(leftVolume, rightVolume);
}
- };
- td.createNewDialog().show();
- }
- break;
- case R.id.visit_website_item:
- Uri uri = Uri.parse(media.getWebsiteLink());
- startActivity(new Intent(Intent.ACTION_VIEW, uri));
- break;
- case R.id.support_item:
- if (media instanceof FeedMedia) {
- DBTasks.flattrItemIfLoggedIn(this, ((FeedMedia) media).getItem());
- }
- break;
- case R.id.share_link_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem());
- }
- break;
- case R.id.share_download_url_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem());
- }
- break;
- case R.id.share_link_with_position_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem(), true);
- }
- break;
- case R.id.share_download_url_with_position_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem(), true);
- }
- break;
- case R.id.skip_episode_item:
- sendBroadcast(new Intent(
- PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
- break;
- default:
- return false;
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ barRightVolume.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ float leftVolume = 1.0f, rightVolume = 1.0f;
+ if (progress < 100) {
+ rightVolume = progress / 100.0f;
+ }
+ if (barLeftVolume.getProgress() < 100) {
+ leftVolume = barLeftVolume.getProgress() / 100.0f;
+ }
+ controller.setVolume(leftVolume, rightVolume);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ stereoToMono.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ UserPreferences.stereoToMono(isChecked);
+ if (controller != null) {
+ controller.setDownmix(isChecked);
+ }
+ });
+ break;
+ case R.id.visit_website_item:
+ Uri uri = Uri.parse(media.getWebsiteLink());
+ startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ break;
+ case R.id.support_item:
+ if (media instanceof FeedMedia) {
+ DBTasks.flattrItemIfLoggedIn(this, ((FeedMedia) media).getItem());
+ }
+ break;
+ case R.id.share_link_item:
+ if (media instanceof FeedMedia) {
+ ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem());
+ }
+ break;
+ case R.id.share_download_url_item:
+ if (media instanceof FeedMedia) {
+ ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem());
+ }
+ break;
+ case R.id.share_link_with_position_item:
+ if (media instanceof FeedMedia) {
+ ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem(), true);
+ }
+ break;
+ case R.id.share_download_url_with_position_item:
+ if (media instanceof FeedMedia) {
+ ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem(), true);
+ }
+ break;
+ default:
+ return false;
+ }
+ return true;
+ } else {
+ return false;
}
- return true;
- } else {
- return false;
}
}
@@ -375,7 +584,9 @@ public abstract class MediaplayerActivity extends ActionBarActivity
super.onResume();
Log.d(TAG, "onResume()");
StorageUtils.checkStorageAvailability(this);
- controller.init();
+ if(controller != null) {
+ controller.init();
+ }
}
/**
@@ -389,26 +600,31 @@ public abstract class MediaplayerActivity extends ActionBarActivity
protected abstract void clearStatusMsg();
protected void onPositionObserverUpdate() {
- if (controller != null) {
- int currentPosition = controller.getPosition();
- int duration = controller.getDuration();
- Log.d(TAG, "currentPosition " + Converter
- .getDurationStringLong(currentPosition));
- if (currentPosition != PlaybackService.INVALID_TIME
- && duration != PlaybackService.INVALID_TIME
- && controller.getMedia() != null) {
- txtvPosition.setText(Converter
- .getDurationStringLong(currentPosition));
- txtvLength.setText(Converter.getDurationStringLong(duration));
- updateProgressbarPosition(currentPosition, duration);
- } else {
- Log.w(TAG, "Could not react to position observer update because of invalid time");
- }
+ if (controller == null || txtvPosition == null || txtvLength == null) {
+ return;
+ }
+ int currentPosition = controller.getPosition();
+ int duration = controller.getDuration();
+ Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition));
+ if (currentPosition == PlaybackService.INVALID_TIME ||
+ duration == PlaybackService.INVALID_TIME) {
+ Log.w(TAG, "Could not react to position observer update because of invalid time");
+ return;
}
+ txtvPosition.setText(Converter.getDurationStringLong(currentPosition));
+ if (showTimeLeft) {
+ txtvLength.setText("-" + Converter.getDurationStringLong(duration - currentPosition));
+ } else {
+ txtvLength.setText(Converter.getDurationStringLong(duration));
+ }
+ updateProgressbarPosition(currentPosition, duration);
}
private void updateProgressbarPosition(int position, int duration) {
- Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration +")");
+ Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration + ")");
+ if(sbPosition == null) {
+ return;
+ }
float progress = ((float) position) / duration;
sbPosition.setProgress((int) (progress * sbPosition.getMax()));
}
@@ -422,16 +638,23 @@ public abstract class MediaplayerActivity extends ActionBarActivity
protected boolean loadMediaInfo() {
Log.d(TAG, "loadMediaInfo()");
Playable media = controller.getMedia();
+ SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
+ showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
if (media != null) {
- txtvPosition.setText(Converter.getDurationStringLong((media
- .getPosition())));
-
- if (media.getDuration() != 0) {
- txtvLength.setText(Converter.getDurationStringLong(media
- .getDuration()));
- float progress = ((float) media.getPosition())
- / media.getDuration();
- sbPosition.setProgress((int) (progress * sbPosition.getMax()));
+ onPositionObserverUpdate();
+ checkFavorite();
+ if(butPlaybackSpeed != null) {
+ if (controller == null) {
+ butPlaybackSpeed.setVisibility(View.GONE);
+ } else {
+ butPlaybackSpeed.setVisibility(View.VISIBLE);
+ if (controller.canSetPlaybackSpeed()) {
+ ViewCompat.setAlpha(butPlaybackSpeed, 1.0f);
+ } else {
+ ViewCompat.setAlpha(butPlaybackSpeed, 0.5f);
+ }
+ }
+ updateButPlaybackSpeed();
}
return true;
} else {
@@ -443,18 +666,45 @@ public abstract class MediaplayerActivity extends ActionBarActivity
setContentView(getContentViewResourceId());
sbPosition = (SeekBar) findViewById(R.id.sbPosition);
txtvPosition = (TextView) findViewById(R.id.txtvPosition);
+
+ SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
+ showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
+ Log.d("timeleft", showTimeLeft ? "true" : "false");
txtvLength = (TextView) findViewById(R.id.txtvLength);
- butPlay = (ImageButton) findViewById(R.id.butPlay);
+ txtvLength.setOnClickListener(v -> {
+ showTimeLeft = !showTimeLeft;
+ Playable media = controller.getMedia();
+ if (media == null) {
+ return;
+ }
+
+ String length;
+ if (showTimeLeft) {
+ length = "-" + Converter.getDurationStringLong(media.getDuration() - media.getPosition());
+ } else {
+ length = Converter.getDurationStringLong(media.getDuration());
+ }
+ txtvLength.setText(length);
+
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(PREF_SHOW_TIME_LEFT, showTimeLeft);
+ editor.apply();
+ Log.d("timeleft on click", showTimeLeft ? "true" : "false");
+ });
+
+ butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed);
butRev = (ImageButton) findViewById(R.id.butRev);
txtvRev = (TextView) findViewById(R.id.txtvRev);
- if(txtvRev != null) {
+ if (txtvRev != null) {
txtvRev.setText(String.valueOf(UserPreferences.getRewindSecs()));
}
+ butPlay = (ImageButton) findViewById(R.id.butPlay);
butFF = (ImageButton) findViewById(R.id.butFF);
txtvFF = (TextView) findViewById(R.id.txtvFF);
- if(txtvFF != null) {
+ if (txtvFF != null) {
txtvFF.setText(String.valueOf(UserPreferences.getFastFowardSecs()));
}
+ butSkip = (ImageButton) findViewById(R.id.butSkip);
// SEEKBAR SETUP
@@ -462,91 +712,123 @@ public abstract class MediaplayerActivity extends ActionBarActivity
// BUTTON SETUP
- butPlay.setOnClickListener(controller.newOnPlayButtonClickListener());
+ if(butPlaybackSpeed != null) {
+ butPlaybackSpeed.setOnClickListener(v -> {
+ if (controller == null) {
+ return;
+ }
+ if (controller.canSetPlaybackSpeed()) {
+ String[] availableSpeeds = UserPreferences.getPlaybackSpeedArray();
+ String currentSpeed = UserPreferences.getPlaybackSpeed();
+
+ // Provide initial value in case the speed list has changed
+ // out from under us
+ // and our current speed isn't in the new list
+ String newSpeed;
+ if (availableSpeeds.length > 0) {
+ newSpeed = availableSpeeds[0];
+ } else {
+ newSpeed = "1.00";
+ }
- if (butFF != null) {
- butFF.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- int curr = controller.getPosition();
- controller.seekTo(curr + UserPreferences.getFastFowardSecs() * 1000);
+ for (int i = 0; i < availableSpeeds.length; i++) {
+ if (availableSpeeds[i].equals(currentSpeed)) {
+ if (i == availableSpeeds.length - 1) {
+ newSpeed = availableSpeeds[0];
+ } else {
+ newSpeed = availableSpeeds[i + 1];
+ }
+ break;
+ }
+ }
+ UserPreferences.setPlaybackSpeed(newSpeed);
+ controller.setPlaybackSpeed(Float.parseFloat(newSpeed));
+ } else {
+ VariableSpeedDialog.showGetPluginDialog(this);
}
});
- butFF.setOnLongClickListener(new View.OnLongClickListener() {
+ butPlaybackSpeed.setOnLongClickListener(v -> {
+ VariableSpeedDialog.showDialog(this);
+ return true;
+ });
+ }
+
+ if (butRev != null) {
+ butRev.setOnClickListener(v -> {
+ int curr = controller.getPosition();
+ controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000);
+ });
+ butRev.setOnLongClickListener(new View.OnLongClickListener() {
int choice;
@Override
public boolean onLongClick(View v) {
int checked = 0;
- int rewindSecs = UserPreferences.getFastFowardSecs();
+ int rewindSecs = UserPreferences.getRewindSecs();
final int[] values = getResources().getIntArray(R.array.seek_delta_values);
final String[] choices = new String[values.length];
- for(int i=0; i < values.length; i++) {
+ for (int i = 0; i < values.length; i++) {
if (rewindSecs == values[i]) {
checked = i;
}
- choices[i] = String.valueOf(values[i]) + " "
- + getString(R.string.time_seconds);
+ choices[i] = String.valueOf(values[i]) + " " + getString(R.string.time_seconds);
}
choice = values[checked];
AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
- builder.setTitle(R.string.pref_fast_forward);
+ builder.setTitle(R.string.pref_rewind);
builder.setSingleChoiceItems(choices, checked,
(dialog, which) -> {
choice = values[which];
});
builder.setNegativeButton(R.string.cancel_label, null);
builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
- UserPreferences.setPrefFastForwardSecs(choice);
- txtvFF.setText(String.valueOf(choice));
+ UserPreferences.setPrefRewindSecs(choice);
+ if(txtvRev != null){
+ txtvRev.setText(String.valueOf(choice));
+ }
});
builder.create().show();
return true;
}
});
}
- if (butRev != null) {
- butRev.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- int curr = controller.getPosition();
- controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000);
- }
+
+ butPlay.setOnClickListener(controller.newOnPlayButtonClickListener());
+
+ if (butFF != null) {
+ butFF.setOnClickListener(v -> {
+ int curr = controller.getPosition();
+ controller.seekTo(curr + UserPreferences.getFastFowardSecs() * 1000);
});
- butRev.setOnLongClickListener(new View.OnLongClickListener() {
+ butFF.setOnLongClickListener(new View.OnLongClickListener() {
int choice;
@Override
public boolean onLongClick(View v) {
int checked = 0;
- int rewindSecs = UserPreferences.getRewindSecs();
+ int rewindSecs = UserPreferences.getFastFowardSecs();
final int[] values = getResources().getIntArray(R.array.seek_delta_values);
final String[] choices = new String[values.length];
- for(int i=0; i < values.length; i++) {
+ for (int i = 0; i < values.length; i++) {
if (rewindSecs == values[i]) {
checked = i;
}
- choices[i] = String.valueOf(values[i]) + " "
- + getString(R.string.time_seconds);
+ choices[i] = String.valueOf(values[i]) + " " + getString(R.string.time_seconds);
}
choice = values[checked];
AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
- builder.setTitle(R.string.pref_rewind);
+ builder.setTitle(R.string.pref_fast_forward);
builder.setSingleChoiceItems(choices, checked,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- choice = values[which];
- }
+ (dialog, which) -> {
+ choice = values[which];
});
builder.setNegativeButton(R.string.cancel_label, null);
- builder.setPositiveButton(R.string.confirm_label, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- UserPreferences.setPrefRewindSecs(choice);
- txtvRev.setText(String.valueOf(choice));
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ UserPreferences.setPrefFastForwardSecs(choice);
+ if(txtvFF != null) {
+ txtvFF.setText(String.valueOf(choice));
}
});
builder.create().show();
@@ -555,6 +837,11 @@ public abstract class MediaplayerActivity extends ActionBarActivity
});
}
+ if (butSkip != null) {
+ butSkip.setOnClickListener(v -> {
+ sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
+ });
+ }
}
protected abstract int getContentViewResourceId();
@@ -562,15 +849,11 @@ public abstract class MediaplayerActivity extends ActionBarActivity
void handleError(int errorCode) {
final AlertDialog.Builder errorDialog = new AlertDialog.Builder(this);
errorDialog.setTitle(R.string.error_label);
- errorDialog
- .setMessage(MediaPlayerError.getErrorString(this, errorCode));
+ errorDialog.setMessage(MediaPlayerError.getErrorString(this, errorCode));
errorDialog.setNeutralButton("OK",
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- finish();
- }
+ (dialog, which) -> {
+ dialog.dismiss();
+ finish();
}
);
errorDialog.create().show();
@@ -579,11 +862,21 @@ public abstract class MediaplayerActivity extends ActionBarActivity
float prog;
@Override
- public void onProgressChanged(SeekBar seekBar, int progress,
- boolean fromUser) {
- if (controller != null) {
- prog = controller.onSeekBarProgressChanged(seekBar, progress, fromUser,
- txtvPosition);
+ public void onProgressChanged (SeekBar seekBar,int progress, boolean fromUser) {
+ if (controller == null || txtvLength == null) {
+ return;
+ }
+ prog = controller.onSeekBarProgressChanged(seekBar, progress, fromUser, txtvPosition);
+ if (showTimeLeft && prog != 0) {
+ int duration = controller.getDuration();
+ String length = "-" + Converter.getDurationStringLong(duration - (int) (prog * duration));
+ txtvLength.setText(length);
+ }
+ }
+
+ private void updateButPlaybackSpeed() {
+ if (controller != null && butPlaybackSpeed != null) {
+ butPlaybackSpeed.setText(UserPreferences.getPlaybackSpeed() + "x");
}
}
@@ -601,4 +894,23 @@ public abstract class MediaplayerActivity extends ActionBarActivity
}
}
+ private void checkFavorite() {
+ Playable playable = controller.getMedia();
+ if (playable != null && playable instanceof FeedMedia) {
+ FeedItem feedItem = ((FeedMedia) playable).getItem();
+ if (feedItem != null) {
+ Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId()))
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(item -> {
+ boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE);
+ if(isFavorite != isFav) {
+ isFavorite = isFav;
+ invalidateOptionsMenu();
+ }
+ });
+ }
+ }
+ }
+
}
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 7f975b3c8..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(this)) {
- 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/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
index 60eb290b5..ee459dbc6 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -3,9 +3,9 @@ package de.danoeh.antennapod.activity;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
import android.support.v4.view.WindowCompat;
import android.util.Log;
import android.util.Pair;
@@ -19,7 +19,9 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.SeekBar;
-import de.danoeh.antennapod.BuildConfig;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.atomic.AtomicBoolean;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
@@ -39,8 +41,12 @@ public class VideoplayerActivity extends MediaplayerActivity {
*/
private boolean videoControlsShowing = true;
private boolean videoSurfaceCreated = false;
- private VideoControlsHider videoControlsToggler;
+ private VideoControlsHider videoControlsHider = new VideoControlsHider(this);
+
+ private AtomicBoolean isSetup = new AtomicBoolean(false);
+
+ private LinearLayout controls;
private LinearLayout videoOverlay;
private AspectRatioVideoView videoview;
private ProgressBar progressIndicator;
@@ -61,25 +67,12 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
@Override
- protected void onPause() {
- super.onPause();
- if (videoControlsToggler != null) {
- videoControlsToggler.cancel(true);
- }
- if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) {
- controller.pause();
- }
- }
-
- @Override
protected void onResume() {
super.onResume();
if (getIntent().getAction() != null
&& getIntent().getAction().equals(Intent.ACTION_VIEW)) {
Intent intent = getIntent();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Received VIEW intent: "
- + intent.getData().getPath());
+ Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath());
ExternalMedia media = new ExternalMedia(intent.getData().getPath(),
MediaType.VIDEO);
Intent launchIntent = new Intent(this, PlaybackService.class);
@@ -94,6 +87,22 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
@Override
+ protected void onPause() {
+ super.onPause();
+ videoControlsHider.stop();
+ if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) {
+ controller.pause();
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ videoControlsHider.stop();
+ videoControlsHider = null;
+ }
+
+ @Override
protected boolean loadMediaInfo() {
if (!super.loadMediaInfo()) {
return false;
@@ -104,14 +113,17 @@ public class VideoplayerActivity extends MediaplayerActivity {
getSupportActionBar().setTitle(media.getFeedTitle());
return true;
}
-
return false;
}
@Override
protected void setupGUI() {
+ if(isSetup.getAndSet(true)) {
+ return;
+ }
super.setupGUI();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ controls = (LinearLayout) findViewById(R.id.controls);
videoOverlay = (LinearLayout) findViewById(R.id.overlay);
videoview = (AspectRatioVideoView) findViewById(R.id.videoview);
progressIndicator = (ProgressBar) findViewById(R.id.progressIndicator);
@@ -133,14 +145,11 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void onAwaitingVideoSurface() {
if (videoSurfaceCreated) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Videosurface already created, setting videosurface now");
+ Log.d(TAG, "Videosurface already created, setting videosurface now");
Pair<Integer, Integer> videoSize = controller.getVideoSize();
if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
+ Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
videoview.setVideoSize(videoSize.first, videoSize.second);
} else {
Log.e(TAG, "Could not determine video size");
@@ -156,7 +165,6 @@ public class VideoplayerActivity extends MediaplayerActivity {
} else {
progressIndicator.setVisibility(View.INVISIBLE);
}
-
}
@Override
@@ -164,38 +172,23 @@ public class VideoplayerActivity extends MediaplayerActivity {
progressIndicator.setVisibility(View.INVISIBLE);
}
- View.OnTouchListener onVideoviewTouched = new View.OnTouchListener() {
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- if (videoControlsToggler != null) {
- videoControlsToggler.cancel(true);
- }
- toggleVideoControlsVisibility();
- if (videoControlsShowing) {
- setupVideoControlsToggler();
- }
-
- return true;
- } else {
- return false;
+ View.OnTouchListener onVideoviewTouched = (v, event) -> {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ videoControlsHider.stop();
+ toggleVideoControlsVisibility();
+ if (videoControlsShowing) {
+ setupVideoControlsToggler();
}
+ return true;
+ } else {
+ return false;
}
};
@SuppressLint("NewApi")
void setupVideoControlsToggler() {
- if (videoControlsToggler != null) {
- videoControlsToggler.cancel(true);
- }
- videoControlsToggler = new VideoControlsHider();
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- videoControlsToggler
- .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- videoControlsToggler.execute();
- }
+ videoControlsHider.stop();
+ videoControlsHider.start();
}
private void toggleVideoControlsVisibility() {
@@ -209,46 +202,6 @@ public class VideoplayerActivity extends MediaplayerActivity {
videoControlsShowing = !videoControlsShowing;
}
- /**
- * Hides the videocontrols after a certain period of time.
- */
- public class VideoControlsHider extends AsyncTask<Void, Void, Void> {
- @Override
- protected void onCancelled() {
- videoControlsToggler = null;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- videoControlsToggler = null;
- }
-
- private static final int WAITING_INTERVALL = 5000;
- private static final String TAG = "VideoControlsToggler";
-
- @Override
- protected void onProgressUpdate(Void... values) {
- if (videoControlsShowing) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Hiding video controls");
- getSupportActionBar().hide();
- hideVideoControls();
- videoControlsShowing = false;
- }
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- try {
- Thread.sleep(WAITING_INTERVALL);
- } catch (InterruptedException e) {
- return null;
- }
- publishProgress();
- return null;
- }
-
- }
private final SurfaceHolder.Callback surfaceHolderCallback = new SurfaceHolder.Callback() {
@Override
@@ -259,15 +212,13 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
public void surfaceCreated(SurfaceHolder holder) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Videoview holder created");
+ Log.d(TAG, "Videoview holder created");
videoSurfaceCreated = true;
if (controller.getStatus() == PlayerStatus.PLAYING) {
if (controller.serviceAvailable()) {
controller.setVideoSurface(holder);
} else {
- Log.e(TAG,
- "Could'nt attach surface to mediaplayer - reference to service was null");
+ Log.e(TAG, "Could'nt attach surface to mediaplayer - reference to service was null");
}
}
@@ -275,8 +226,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Videosurface was destroyed");
+ Log.d(TAG, "Videosurface was destroyed");
videoSurfaceCreated = false;
controller.notifyVideoSurfaceAbandoned();
}
@@ -286,9 +236,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void onReloadNotification(int notificationCode) {
if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "ReloadNotification received, switching to Audioplayer now");
+ Log.d(TAG, "ReloadNotification received, switching to Audioplayer now");
finish();
startActivity(new Intent(this, AudioplayerActivity.class));
}
@@ -297,9 +245,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
super.onStartTrackingTouch(seekBar);
- if (videoControlsToggler != null) {
- videoControlsToggler.cancel(true);
- }
+ videoControlsHider.stop();
}
@Override
@@ -321,12 +267,11 @@ public class VideoplayerActivity extends MediaplayerActivity {
@SuppressLint("NewApi")
private void showVideoControls() {
videoOverlay.setVisibility(View.VISIBLE);
- butPlay.setVisibility(View.VISIBLE);
- final Animation animation = AnimationUtils.loadAnimation(this,
- R.anim.fade_in);
+ controls.setVisibility(View.VISIBLE);
+ final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_in);
if (animation != null) {
videoOverlay.startAnimation(animation);
- butPlay.startAnimation(animation);
+ controls.startAnimation(animation);
}
if (Build.VERSION.SDK_INT >= 14) {
videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
@@ -335,11 +280,10 @@ public class VideoplayerActivity extends MediaplayerActivity {
@SuppressLint("NewApi")
private void hideVideoControls() {
- final Animation animation = AnimationUtils.loadAnimation(this,
- R.anim.fade_out);
+ final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out);
if (animation != null) {
videoOverlay.startAnimation(animation);
- butPlay.startAnimation(animation);
+ controls.startAnimation(animation);
}
if (Build.VERSION.SDK_INT >= 14) {
int videoviewFlag = (Build.VERSION.SDK_INT >= 16) ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0;
@@ -348,7 +292,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
videoOverlay.setFitsSystemWindows(true);
}
videoOverlay.setVisibility(View.GONE);
- butPlay.setVisibility(View.GONE);
+ controls.setVisibility(View.GONE);
}
@Override
@@ -356,7 +300,6 @@ public class VideoplayerActivity extends MediaplayerActivity {
return R.layout.videoplayer_activity;
}
-
@Override
protected void setScreenOn(boolean enable) {
super.setScreenOn(enable);
@@ -366,4 +309,38 @@ public class VideoplayerActivity extends MediaplayerActivity {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
+
+ private static class VideoControlsHider extends Handler {
+
+ private static final int DELAY = 5000;
+
+ private WeakReference<VideoplayerActivity> activity;
+
+ public VideoControlsHider(VideoplayerActivity activity) {
+ this.activity = new WeakReference<>(activity);
+ }
+
+ private final Runnable hideVideoControls = () -> {
+ VideoplayerActivity vpa = activity.get();
+ if(vpa == null) {
+ return;
+ }
+ if (vpa.videoControlsShowing) {
+ Log.d(TAG, "Hiding video controls");
+ vpa.getSupportActionBar().hide();
+ vpa.hideVideoControls();
+ vpa.videoControlsShowing = false;
+ }
+ };
+
+ public void start() {
+ this.postDelayed(hideVideoControls, DELAY);
+ }
+
+ public void stop() {
+ this.removeCallbacks(hideVideoControls);
+ }
+
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
index 1ea7daaa3..935a0dcd4 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
@@ -47,9 +47,10 @@ public class AdapterUtils {
- media.getPosition()));
}
} else if (!media.isDownloaded()) {
+ Log.d(TAG, "size: " + media.getSize());
if (media.getSize() > 0) {
txtvPos.setText(Converter.byteToString(media.getSize()));
- } else if(false == media.checkedOnSizeButUnknown()) {
+ } else if(NetworkUtils.isDownloadAllowed() && false == media.checkedOnSizeButUnknown()) {
txtvPos.setText("{fa-spinner}");
Iconify.addIcons(txtvPos);
NetworkUtils.getFeedMediaSizeObservable(media)
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
index f120aa1d5..6d4fc6f1e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
@@ -2,9 +2,9 @@ package de.danoeh.antennapod.adapter;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
-import android.text.format.DateUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@@ -15,7 +15,6 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -36,6 +35,8 @@ import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.fragment.ItemFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
@@ -70,11 +71,11 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
this.showOnlyNewEpisodes = showOnlyNewEpisodes;
if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
- playingBackGroundColor = mainActivity.getResources().getColor(R.color.highlight_dark);
+ playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_dark);
} else {
- playingBackGroundColor = mainActivity.getResources().getColor(R.color.highlight_light);
+ playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_light);
}
- normalBackGroundColor = mainActivity.getResources().getColor(android.R.color.transparent);
+ normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent);
}
@Override
@@ -118,8 +119,8 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.placeholder.setVisibility(View.VISIBLE);
holder.placeholder.setText(item.getFeed().getTitle());
holder.title.setText(item.getTitle());
- holder.pubDate.setText(DateUtils.formatDateTime(mainActivityRef.get(),
- item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
+ String pubDateStr = DateUtils.formatAbbrev(mainActivityRef.get(), item.getPubDate());
+ holder.pubDate.setText(pubDateStr);
if (showOnlyNewEpisodes || false == item.isNew()) {
holder.statusUnread.setVisibility(View.INVISIBLE);
} else {
@@ -134,7 +135,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
} else if (media.getSize() > 0) {
holder.txtvDuration.setText(Converter.byteToString(media.getSize()));
- } else if(false == media.checkedOnSizeButUnknown()) {
+ } else if(NetworkUtils.isDownloadAllowed() && false == media.checkedOnSizeButUnknown()) {
holder.txtvDuration.setText("{fa-spinner}");
Iconify.addIcons(holder.txtvDuration);
NetworkUtils.getFeedMediaSizeObservable(media)
@@ -329,8 +330,8 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
item1.setVisible(visible);
}
};
- FeedItemMenuHandler.onPrepareMenu(mainActivityRef.get(), contextMenuInterface, item, true,
- null);
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true,
+ itemAccess.getQueueIds(), itemAccess.getFavoritesIds());
}
}
@@ -345,6 +346,10 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
boolean isInQueue(FeedItem item);
+ LongList getQueueIds();
+
+ LongList getFavoritesIds();
+
}
/**
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java
deleted file mode 100644
index cf0532cf1..000000000
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package de.danoeh.antennapod.adapter;
-
-import android.content.Context;
-import android.text.Layout;
-import android.text.Selection;
-import android.text.Spannable;
-import android.text.Spanned;
-import android.text.style.ClickableSpan;
-import android.text.util.Linkify;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnTouchListener;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.ImageButton;
-import android.widget.TextView;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.util.ChapterUtils;
-import de.danoeh.antennapod.core.util.Converter;
-import de.danoeh.antennapod.core.util.playback.Playable;
-
-import java.util.List;
-
-public class ChapterListAdapter extends ArrayAdapter<Chapter> {
-
- private static final String TAG = "ChapterListAdapter";
-
- private List<Chapter> chapters;
- private Playable media;
-
- private int defaultTextColor;
- private final Callback callback;
-
- public ChapterListAdapter(Context context, int textViewResourceId,
- List<Chapter> objects, Playable media, Callback callback) {
- super(context, textViewResourceId, objects);
- this.chapters = objects;
- this.media = media;
- this.callback = callback;
- }
-
- @Override
- public View getView(final int position, View convertView, ViewGroup parent) {
- Holder holder;
-
- Chapter sc = getItem(position);
-
- // Inflate Layout
- if (convertView == null) {
- holder = new Holder();
- LayoutInflater inflater = (LayoutInflater) getContext()
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- convertView = inflater.inflate(R.layout.simplechapter_item, parent, false);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
- defaultTextColor = holder.title.getTextColors().getDefaultColor();
- holder.start = (TextView) convertView.findViewById(R.id.txtvStart);
- holder.link = (TextView) convertView.findViewById(R.id.txtvLink);
- holder.butPlayChapter = (ImageButton) convertView.findViewById(R.id.butPlayChapter);
- convertView.setTag(holder);
- } else {
- holder = (Holder) convertView.getTag();
-
- }
-
- holder.title.setText(sc.getTitle());
- holder.start.setText(Converter.getDurationStringLong((int) sc
- .getStart()));
- if (sc.getLink() != null) {
- holder.link.setVisibility(View.VISIBLE);
- holder.link.setText(sc.getLink());
- Linkify.addLinks(holder.link, Linkify.WEB_URLS);
- } else {
- holder.link.setVisibility(View.GONE);
- }
- holder.link.setMovementMethod(null);
- holder.link.setOnTouchListener(new OnTouchListener() {
-
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- // from
- // http://stackoverflow.com/questions/7236840/android-textview-linkify-intercepts-with-parent-view-gestures
- TextView widget = (TextView) v;
- Object text = widget.getText();
- if (text instanceof Spanned) {
- Spannable buffer = (Spannable) text;
-
- int action = event.getAction();
-
- if (action == MotionEvent.ACTION_UP
- || action == MotionEvent.ACTION_DOWN) {
- int x = (int) event.getX();
- int y = (int) event.getY();
-
- x -= widget.getTotalPaddingLeft();
- y -= widget.getTotalPaddingTop();
-
- x += widget.getScrollX();
- y += widget.getScrollY();
-
- Layout layout = widget.getLayout();
- int line = layout.getLineForVertical(y);
- int off = layout.getOffsetForHorizontal(line, x);
-
- ClickableSpan[] link = buffer.getSpans(off, off,
- ClickableSpan.class);
-
- if (link.length != 0) {
- if (action == MotionEvent.ACTION_UP) {
- link[0].onClick(widget);
- } else if (action == MotionEvent.ACTION_DOWN) {
- Selection.setSelection(buffer,
- buffer.getSpanStart(link[0]),
- buffer.getSpanEnd(link[0]));
- }
- return true;
- }
- }
-
- }
-
- return false;
-
- }
- });
- holder.butPlayChapter.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (callback != null) {
- callback.onPlayChapterButtonClicked(position);
- }
- }
- });
- Chapter current = ChapterUtils.getCurrentChapter(media);
- if (current != null) {
- if (current == sc) {
- holder.title.setTextColor(convertView.getResources().getColor(
- R.color.holo_blue_light));
- holder.start.setTextColor(convertView.getResources().getColor(
- R.color.holo_blue_light));
- } else {
- holder.title.setTextColor(defaultTextColor);
- holder.start.setTextColor(defaultTextColor);
- }
- } else {
- Log.w(TAG, "Could not find out what the current chapter is.");
- }
-
- return convertView;
- }
-
- static class Holder {
- TextView title;
- TextView start;
- TextView link;
- ImageButton butPlayChapter;
- }
-
- @Override
- public int getCount() {
- // ignore invalid chapters
- int counter = 0;
- if (chapters != null) {
- for (Chapter chapter : chapters) {
- if (!ignoreChapter(chapter)) {
- counter++;
- }
- }
- }
- return counter;
- }
-
- private boolean ignoreChapter(Chapter c) {
- return media.getDuration() > 0 && media.getDuration() < c.getStart();
- }
-
- @Override
- public Chapter getItem(int position) {
- int i = 0;
- for (Chapter chapter : chapters) {
- if (!ignoreChapter(chapter)) {
- if (i == position) {
- return chapter;
- } else {
- i++;
- }
- }
- }
- return super.getItem(position);
- }
-
- public static interface Callback {
- public void onPlayChapterButtonClicked(int position);
- }
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
new file mode 100644
index 000000000..8bde1097b
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
@@ -0,0 +1,199 @@
+package de.danoeh.antennapod.adapter;
+
+import android.content.Context;
+import android.support.v4.content.ContextCompat;
+import android.text.Layout;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.Spanned;
+import android.text.style.ClickableSpan;
+import android.text.util.Linkify;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.ChapterUtils;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.playback.Playable;
+
+public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
+
+ private static final String TAG = "ChapterListAdapter";
+
+ private Playable media;
+
+ private int defaultTextColor;
+ private final Callback callback;
+
+ public ChaptersListAdapter(Context context, int textViewResourceId, Callback callback) {
+ super(context, textViewResourceId);
+ this.callback = callback;
+ }
+
+ public void setMedia(Playable media) {
+ this.media = media;
+ }
+
+ @Override
+ public View getView(final int position, View convertView, ViewGroup parent) {
+ Holder holder;
+
+ Chapter sc = getItem(position);
+
+ // Inflate Layout
+ if (convertView == null) {
+ holder = new Holder();
+ LayoutInflater inflater = (LayoutInflater) getContext()
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ convertView = inflater.inflate(R.layout.simplechapter_item, parent, false);
+ holder.view = convertView;
+ holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ defaultTextColor = holder.title.getTextColors().getDefaultColor();
+ holder.start = (TextView) convertView.findViewById(R.id.txtvStart);
+ holder.link = (TextView) convertView.findViewById(R.id.txtvLink);
+ holder.butPlayChapter = (ImageButton) convertView.findViewById(R.id.butPlayChapter);
+ convertView.setTag(holder);
+ } else {
+ holder = (Holder) convertView.getTag();
+
+ }
+
+ holder.title.setText(sc.getTitle());
+ holder.start.setText(Converter.getDurationStringLong((int) sc
+ .getStart()));
+ if (sc.getLink() != null) {
+ holder.link.setVisibility(View.VISIBLE);
+ holder.link.setText(sc.getLink());
+ Linkify.addLinks(holder.link, Linkify.WEB_URLS);
+ } else {
+ holder.link.setVisibility(View.GONE);
+ }
+ holder.link.setMovementMethod(null);
+ holder.link.setOnTouchListener((v, event) -> {
+ // from
+ // http://stackoverflow.com/questions/7236840/android-textview-linkify-intercepts-with-parent-view-gestures
+ TextView widget = (TextView) v;
+ Object text = widget.getText();
+ if (text instanceof Spanned) {
+ Spannable buffer = (Spannable) text;
+
+ int action = event.getAction();
+
+ if (action == MotionEvent.ACTION_UP
+ || action == MotionEvent.ACTION_DOWN) {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+
+ x -= widget.getTotalPaddingLeft();
+ y -= widget.getTotalPaddingTop();
+
+ x += widget.getScrollX();
+ y += widget.getScrollY();
+
+ Layout layout = widget.getLayout();
+ int line = layout.getLineForVertical(y);
+ int off = layout.getOffsetForHorizontal(line, x);
+
+ ClickableSpan[] link = buffer.getSpans(off, off,
+ ClickableSpan.class);
+
+ if (link.length != 0) {
+ if (action == MotionEvent.ACTION_UP) {
+ link[0].onClick(widget);
+ } else if (action == MotionEvent.ACTION_DOWN) {
+ Selection.setSelection(buffer,
+ buffer.getSpanStart(link[0]),
+ buffer.getSpanEnd(link[0]));
+ }
+ return true;
+ }
+ }
+
+ }
+
+ return false;
+
+ });
+ holder.butPlayChapter.setOnClickListener(v -> {
+ if (callback != null) {
+ callback.onPlayChapterButtonClicked(position);
+ }
+ });
+ Chapter current = ChapterUtils.getCurrentChapter(media);
+ if (current != null) {
+ if (current == sc) {
+ int playingBackGroundColor;
+ if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
+ playingBackGroundColor = ContextCompat.getColor(getContext(), R.color.highlight_dark);
+ } else {
+ playingBackGroundColor = ContextCompat.getColor(getContext(), R.color.highlight_light);
+ }
+ holder.view.setBackgroundColor(playingBackGroundColor);
+ } else {
+ holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
+ holder.title.setTextColor(defaultTextColor);
+ holder.start.setTextColor(defaultTextColor);
+ }
+ } else {
+ Log.w(TAG, "Could not find out what the current chapter is.");
+ }
+
+ return convertView;
+ }
+
+ static class Holder {
+ View view;
+ TextView title;
+ TextView start;
+ TextView link;
+ ImageButton butPlayChapter;
+ }
+
+ @Override
+ public int getCount() {
+ if(media == null || media.getChapters() == null) {
+ return 0;
+ }
+ // ignore invalid chapters
+ int counter = 0;
+ for (Chapter chapter : media.getChapters()) {
+ if (!ignoreChapter(chapter)) {
+ counter++;
+ }
+ }
+ return counter;
+ }
+
+ private boolean ignoreChapter(Chapter c) {
+ return media.getDuration() > 0 && media.getDuration() < c.getStart();
+ }
+
+ @Override
+ public Chapter getItem(int position) {
+ int i = 0;
+ for (Chapter chapter : media.getChapters()) {
+ if (!ignoreChapter(chapter)) {
+ if (i == position) {
+ return chapter;
+ } else {
+ i++;
+ }
+ }
+ }
+ return super.getItem(position);
+ }
+
+ public interface Callback {
+ void onPlayChapterButtonClicked(int position);
+ }
+
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
index efdf1a3c9..469a807e1 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
@@ -6,6 +6,8 @@ import android.content.Intent;
import android.support.v7.app.AlertDialog;
import android.widget.Toast;
+import com.afollestad.materialdialogs.MaterialDialog;
+
import org.apache.commons.lang3.Validate;
import de.danoeh.antennapod.R;
@@ -99,40 +101,33 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
}
private void confirmMobileDownload(final Context context, final FeedItem item) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
builder
- .setTitle(R.string.confirm_mobile_download_dialog_title)
- .setMessage(context.getText(R.string.confirm_mobile_download_dialog_message))
- .setPositiveButton(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- allowMobileDownloadsTimestamp = System.currentTimeMillis();
- try {
- DBTasks.downloadFeedItems(context, item);
- Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
- }
- }
- });
+ .title(R.string.confirm_mobile_download_dialog_title)
+ .content(R.string.confirm_mobile_download_dialog_message)
+ .positiveText(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily))
+ .onPositive((dialog, which) -> {
+ allowMobileDownloadsTimestamp = System.currentTimeMillis();
+ try {
+ DBTasks.downloadFeedItems(context, item);
+ Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
+ }
+ });
LongList queueIds = DBReader.getQueueIDList();
if(!queueIds.contains(item.getId())) {
- builder.setNeutralButton(context.getText(R.string.confirm_mobile_download_dialog_only_add_to_queue),
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- onlyAddToQueueTimeStamp = System.currentTimeMillis();
- DBWriter.addQueueItem(context, item);
- Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show();
- }
- })
- .setMessage(context.getText(R.string.confirm_mobile_download_dialog_message_not_in_queue));
- } else {
- builder.setMessage(context.getText(R.string.confirm_mobile_download_dialog_message));
+ builder
+ .content(R.string.confirm_mobile_download_dialog_message_not_in_queue)
+ .neutralText(R.string.confirm_mobile_download_dialog_only_add_to_queue)
+ .onNeutral((dialog, which) -> {
+ onlyAddToQueueTimeStamp = System.currentTimeMillis();
+ DBWriter.addQueueItem(context, item);
+ Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show();
+ });
}
- builder.create()
- .show();
+ builder.show();
}
+
}
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 9d7a509cf..9ff80424c 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
+import android.support.v4.content.ContextCompat;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.LayoutInflater;
@@ -13,8 +14,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;
@@ -76,14 +75,14 @@ public class DownloadLogAdapter extends BaseAdapter {
status.getCompletionDate().getTime(),
System.currentTimeMillis(), 0, 0));
if (status.isSuccessful()) {
- holder.icon.setTextColor(convertView.getResources().getColor(
+ holder.icon.setTextColor(ContextCompat.getColor(convertView.getContext(),
R.color.download_success_green));
holder.icon.setText("{fa-check-circle}");
Iconify.addIcons(holder.icon);
holder.retry.setVisibility(View.GONE);
holder.reason.setVisibility(View.GONE);
} else {
- holder.icon.setTextColor(convertView.getResources().getColor(
+ holder.icon.setTextColor(ContextCompat.getColor(convertView.getContext(),
R.color.download_failed_red));
holder.icon.setText("{fa-times-circle}");
Iconify.addIcons(holder.icon);
@@ -123,9 +122,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();
}
@@ -134,12 +132,16 @@ public class DownloadLogAdapter extends BaseAdapter {
}
} else if(holder.typeId == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
FeedMedia media = DBReader.getFeedMedia(holder.id);
- try {
- DBTasks.downloadFeedItems(context, media.getItem());
- Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
+ if (media != null) {
+ try {
+ DBTasks.downloadFeedItems(context, media.getItem());
+ Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
+ }
+ } else {
+ Log.wtf(TAG, "Could not find media for id: " + holder.id);
}
} else {
Log.wtf(TAG, "Unexpected type id: " + holder.typeId);
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 2b1eccea5..53dedd496 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
-import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -11,12 +10,12 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.DateUtils;
/**
* Shows a list of downloaded episodes
@@ -26,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
@@ -53,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) {
@@ -62,43 +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.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
holder.txtvSize.setText(Converter.byteToString(item.getMedia().getSize()));
- FeedItem.State state = item.getState();
+ holder.queueStatus.setVisibility(item.isTagged(FeedItem.TAG_QUEUE) ? View.VISIBLE : View.GONE);
+ String pubDateStr = DateUtils.formatAbbrev(context, item.getPubDate());
+ holder.pubDate.setText(pubDateStr);
+ 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/DownloadlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
index 206d07f0f..4a0ff1f12 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
+import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -74,7 +75,7 @@ public class DownloadlistAdapter extends BaseAdapter {
}
if (position == selectedItemIndex) {
- convertView.setBackgroundColor(convertView.getResources().getColor(
+ convertView.setBackgroundColor(ContextCompat.getColor(convertView.getContext(),
ThemeUtils.getSelectionBackgroundColor()));
} else {
convertView.setBackgroundResource(0);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
index 1972e675e..7ca8d6a31 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
@@ -2,7 +2,7 @@ package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.content.res.TypedArray;
-import android.text.format.DateUtils;
+import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -21,7 +21,9 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
/**
@@ -57,8 +59,12 @@ public class FeedItemlistAdapter extends BaseAdapter {
this.actionButtonUtils = new ActionButtonUtils(context);
this.makePlayedItemsTransparent = makePlayedItemsTransparent;
- playingBackGroundColor = context.getResources().getColor(R.color.highlight_light);
- normalBackGroundColor = context.getResources().getColor(android.R.color.transparent);
+ if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
+ playingBackGroundColor = ContextCompat.getColor(context, R.color.highlight_dark);
+ } else {
+ playingBackGroundColor = ContextCompat.getColor(context, R.color.highlight_light);
+ }
+ normalBackGroundColor = ContextCompat.getColor(context, android.R.color.transparent);
}
@Override
@@ -113,8 +119,8 @@ public class FeedItemlistAdapter extends BaseAdapter {
if (!(getItemViewType(position) == Adapter.IGNORE_ITEM_VIEW_TYPE)) {
convertView.setVisibility(View.VISIBLE);
if (position == selectedItemIndex) {
- convertView.setBackgroundColor(convertView.getResources()
- .getColor(ThemeUtils.getSelectionBackgroundColor()));
+ convertView.setBackgroundColor(ContextCompat.getColor(convertView.getContext(),
+ ThemeUtils.getSelectionBackgroundColor()));
} else {
convertView.setBackgroundResource(0);
}
@@ -138,8 +144,8 @@ public class FeedItemlistAdapter extends BaseAdapter {
ViewHelper.setAlpha(convertView, 1.0f);
}
- holder.published.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
-
+ String pubDateStr = DateUtils.formatAbbrev(context, item.getPubDate());
+ holder.published.setText(pubDateStr);
FeedMedia media = item.getMedia();
if (media == null) {
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 183c1a44e..6d7e6dcac 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -6,6 +6,7 @@ import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.preference.PreferenceManager;
+import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -15,7 +16,7 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconTextView;
import org.apache.commons.lang3.ArrayUtils;
@@ -208,7 +209,8 @@ public class NavListAdapter extends BaseAdapter
holder.title.setText(title);
- if (tags.get(position).equals(QueueFragment.TAG)) {
+ String tag = tags.get(position);
+ if (tag.equals(QueueFragment.TAG)) {
int queueSize = itemAccess.getQueueSize();
if (queueSize > 0) {
holder.count.setVisibility(View.VISIBLE);
@@ -216,7 +218,7 @@ public class NavListAdapter extends BaseAdapter
} else {
holder.count.setVisibility(View.GONE);
}
- } else if (tags.get(position).equals(EpisodesFragment.TAG)) {
+ } else if (tag.equals(EpisodesFragment.TAG)) {
int unreadItems = itemAccess.getNumberOfNewItems();
if (unreadItems > 0) {
holder.count.setVisibility(View.VISIBLE);
@@ -224,6 +226,26 @@ public class NavListAdapter extends BaseAdapter
} else {
holder.count.setVisibility(View.GONE);
}
+ } else if(tag.equals(DownloadsFragment.TAG) && UserPreferences.isEnableAutodownload()) {
+ int epCacheSize = UserPreferences.getEpisodeCacheSize();
+ // don't count episodes that can be reclaimed
+ int spaceUsed = itemAccess.getNumberOfDownloadedItems() -
+ itemAccess.getReclaimableItems();
+
+ if (epCacheSize > 0 && spaceUsed >= epCacheSize) {
+ holder.count.setText("{md-disc-full 150%}");
+ Iconify.addIcons(holder.count);
+ holder.count.setVisibility(View.VISIBLE);
+ holder.count.setOnClickListener(v -> {
+ new AlertDialog.Builder(context)
+ .setTitle(R.string.episode_cache_full_title)
+ .setMessage(R.string.episode_cache_full_message)
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> {})
+ .show();
+ });
+ } else {
+ holder.count.setVisibility(View.GONE);
+ }
} else {
holder.count.setVisibility(View.GONE);
}
@@ -316,6 +338,8 @@ public class NavListAdapter extends BaseAdapter
int getSelectedItemIndex();
int getQueueSize();
int getNumberOfNewItems();
+ int getNumberOfDownloadedItems();
+ int getReclaimableItems();
int getFeedCounter(long feedId);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
index 3e4dd4deb..aa3368258 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -3,10 +3,11 @@ package de.danoeh.antennapod.adapter;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
-import android.text.format.DateUtils;
+import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@@ -38,6 +39,7 @@ import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.fragment.ItemFragment;
@@ -76,11 +78,11 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
locked = UserPreferences.isQueueLocked();
if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
- playingBackGroundColor = mainActivity.getResources().getColor(R.color.highlight_dark);
+ playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_dark);
} else {
- playingBackGroundColor = mainActivity.getResources().getColor(R.color.highlight_light);
+ playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_light);
}
- normalBackGroundColor = mainActivity.getResources().getColor(android.R.color.transparent);
+ normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent);
}
public void setLocked(boolean locked) {
@@ -181,8 +183,8 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
item1.setVisible(visible);
}
};
- FeedItemMenuHandler.onPrepareMenu(mainActivity.get(), contextMenuInterface, item, true,
- itemAccess.getQueueIds());
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true,
+ itemAccess.getQueueIds(), itemAccess.getFavoritesIds());
}
@Override
@@ -209,9 +211,21 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
FeedMedia media = item.getMedia();
title.setText(item.getTitle());
- String pubDateStr = DateUtils.formatDateTime(mainActivity.get(),
- item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL);
- pubDate.setText(pubDateStr.replace(" ", "\n"));
+ String pubDateStr = DateUtils.formatAbbrev(mainActivity.get(), item.getPubDate());
+ int index = 0;
+ if(countMatches(pubDateStr, ' ') == 1 || countMatches(pubDateStr, ' ') == 2) {
+ index = pubDateStr.lastIndexOf(' ');
+ } else if(countMatches(pubDateStr, '.') == 2) {
+ index = pubDateStr.lastIndexOf('.');
+ } else if(countMatches(pubDateStr, '-') == 2) {
+ index = pubDateStr.lastIndexOf('-');
+ } else if(countMatches(pubDateStr, '/') == 2) {
+ index = pubDateStr.lastIndexOf('/');
+ }
+ if(index > 0) {
+ pubDateStr = pubDateStr.substring(0, index+1).trim() + "\n" + pubDateStr.substring(index+1);
+ }
+ pubDate.setText(pubDateStr);
if (media != null) {
final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
@@ -238,7 +252,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
} else {
if(media.getSize() > 0) {
progressLeft.setText(Converter.byteToString(media.getSize()));
- } else if(false == media.checkedOnSizeButUnknown()) {
+ } else if(NetworkUtils.isDownloadAllowed() && false == media.checkedOnSizeButUnknown()) {
progressLeft.setText("{fa-spinner}");
Iconify.addIcons(progressLeft);
NetworkUtils.getFeedMediaSizeObservable(media)
@@ -337,6 +351,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
long getItemDownloadSize(FeedItem item);
int getItemDownloadProgressPercent(FeedItem item);
LongList getQueueIds();
+ LongList getFavoritesIds();
}
/**
@@ -360,4 +375,18 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
*/
void onItemClear();
}
+
+ // Oh Xiaomi, I hate you so much. How did you manage to fuck this up?
+ private static int countMatches(final CharSequence str, final char ch) {
+ if (TextUtils.isEmpty(str)) {
+ return 0;
+ }
+ int count = 0;
+ for (int i = 0; i < str.length(); i++) {
+ if (ch == str.charAt(i)) {
+ count++;
+ }
+ }
+ return count;
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
index 08ffdd197..47ac4c757 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
@@ -10,6 +10,7 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -65,6 +66,12 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
//Set the title
viewHolder.titleView.setText(podcast.title);
+ if(!podcast.feedUrl.contains("itunes.apple.com")) {
+ viewHolder.urlView.setText(podcast.feedUrl);
+ viewHolder.urlView.setVisibility(View.VISIBLE);
+ } else {
+ viewHolder.urlView.setVisibility(View.GONE);
+ }
//Update the empty imageView with the image from the feed
Glide.with(context)
@@ -94,6 +101,8 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
*/
public final TextView titleView;
+ public final TextView urlView;
+
/**
* Constructor
@@ -102,6 +111,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
PodcastViewHolder(View view){
coverView = (ImageView) view.findViewById(R.id.imgvCover);
titleView = (TextView) view.findViewById(R.id.txtvTitle);
+ urlView = (TextView) view.findViewById(R.id.txtvUrl);
}
}
@@ -124,16 +134,47 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
*/
public final String feedUrl;
+
+ private Podcast(String title, String imageUrl, String feedUrl) {
+ this.title = title;
+ this.imageUrl = imageUrl;
+ this.feedUrl = feedUrl;
+ }
+
+ /**
+ * Constructs a Podcast instance from a iTunes search result
+ *
+ * @param json object holding the podcast information
+ * @throws JSONException
+ */
+ public static Podcast fromSearch(JSONObject json) throws JSONException {
+ String title = json.getString("collectionName");
+ String imageUrl = json.getString("artworkUrl100");
+ String feedUrl = json.getString("feedUrl");
+ return new Podcast(title, imageUrl, feedUrl);
+ }
+
/**
- * Constructor.
+ * Constructs a Podcast instance from iTunes toplist entry
*
* @param json object holding the podcast information
* @throws JSONException
*/
- public Podcast(JSONObject json) throws JSONException {
- title = json.getString("collectionName");
- imageUrl = json.getString("artworkUrl100");
- feedUrl = json.getString("feedUrl");
+ public static Podcast fromToplist(JSONObject json) throws JSONException {
+ String title = json.getJSONObject("title").getString("label");
+ String imageUrl = null;
+ JSONArray images = json.getJSONArray("im:image");
+ for(int i=0; imageUrl == null && i < images.length(); i++) {
+ JSONObject image = images.getJSONObject(i);
+ String height = image.getJSONObject("attributes").getString("height");
+ if(Integer.valueOf(height) >= 100) {
+ imageUrl = image.getString("label");
+ }
+ }
+ String feedUrl = "https://itunes.apple.com/lookup?id=" +
+ json.getJSONObject("id").getJSONObject("attributes").getString("im:id");
+ return new Podcast(title, imageUrl, feedUrl);
}
+
}
}
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 e867540e4..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,10 +1,11 @@
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;
import android.support.v4.app.Fragment;
+import android.support.v4.util.ArrayMap;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -16,13 +17,8 @@ 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.HashMap;
import java.util.List;
import java.util.Map;
@@ -38,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;
@@ -47,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 HashMap<>();
+ 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
@@ -102,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;
}
@@ -121,14 +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();
-
- menu.findItem(R.id.sort).setIcon(new IconDrawable(getActivity(),
- FontAwesomeIcons.fa_sort).color(textColor).actionBarSize());
-
mSelectToggle = menu.findItem(R.id.select_toggle);
mSelectToggle.setOnMenuItemClickListener(item -> {
if (checkedIds.size() == episodes.size()) {
@@ -138,23 +165,26 @@ public class EpisodesApplyActionFragment extends Fragment {
}
return true;
});
-
- menu.findItem(R.id.select_options).setIcon(new IconDrawable(getActivity(),
- FontAwesomeIcons.fa_caret_down).color(textColor).actionBarSize());
}
@Override
public void onPrepareOptionsMenu (Menu menu) {
- Icon icon;
- if(checkedIds.size() == episodes.size()) {
- icon = FontAwesomeIcons.fa_check_square_o;
- } else if(checkedIds.size() == 0) {
- icon = FontAwesomeIcons.fa_square_o;
+ // Prepare icon for select toggle button
+
+ 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 = FontAwesomeIcons.fa_minus_square_o;
+ icon[0] = R.attr.ic_indeterminate_check_box;
}
- mSelectToggle.setIcon(new IconDrawable(getActivity(), icon).color(textColor).actionBarSize());
+ TypedArray a = getActivity().obtainStyledAttributes(icon);
+ Drawable iconDrawable = a.getDrawable(0);
+ a.recycle();
+
+ mSelectToggle.setIcon(iconDrawable);
}
@Override
@@ -187,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;
@@ -308,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/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
index 4b512a48d..3ed82b9bd 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -21,107 +21,104 @@ import de.danoeh.antennapod.core.util.IntentUtils;
public class VariableSpeedDialog {
- private static final String TAG = VariableSpeedDialog.class.getSimpleName();
-
- private static final Intent playStoreIntent = new Intent(Intent.ACTION_VIEW,
- Uri.parse("market://details?id=com.falconware.prestissimo"));
-
- private VariableSpeedDialog() {
- }
-
- public static void showDialog(final Context context) {
- if (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context)
- || UserPreferences.useSonic()) {
- showSpeedSelectorDialog(context);
- } else {
- showGetPluginDialog(context);
- }
- }
-
- private static void showGetPluginDialog(final Context context) {
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
- builder.title(R.string.no_playback_plugin_title);
- builder.content(R.string.no_playback_plugin_or_sonic_msg);
- builder.positiveText(R.string.download_plugin_label);
- builder.negativeText(R.string.enable_sonic);
- builder.neutralText(R.string.close_label);
- builder.callback(new MaterialDialog.ButtonCallback() {
- @Override
- public void onPositive(MaterialDialog dialog) {
- try {
- context.startActivity(playStoreIntent);
- } catch (ActivityNotFoundException e) {
- // this is usually thrown on an emulator if the Android market is not installed
- Log.e(TAG, Log.getStackTraceString(e));
- }
- }
-
- @Override
- public void onNegative(MaterialDialog dialog) {
- if (Build.VERSION.SDK_INT >= 16) { // just to be safe
- UserPreferences.enableSonic(true);
- showSpeedSelectorDialog(context);
- }
- }
-
- @Override
- public void onNeutral(MaterialDialog dialog) {
- super.onNeutral(dialog);
- }
- });
- builder.forceStacking(true);
- MaterialDialog dialog = builder.show();
- if(!IntentUtils.isCallable(context.getApplicationContext(), playStoreIntent)) {
- View pos = dialog.getActionButton(DialogAction.POSITIVE);
- pos.setEnabled(false);
- }
- if (Build.VERSION.SDK_INT < 16) {
- View pos = dialog.getActionButton(DialogAction.NEGATIVE);
- pos.setEnabled(false);
- }
- }
-
- private static void showSpeedSelectorDialog(final Context context) {
- final String[] speedValues = context.getResources().getStringArray(
- R.array.playback_speed_values);
- // According to Java spec these get initialized to false on creation
- final boolean[] speedChecked = new boolean[speedValues.length];
-
- // Build the "isChecked" array so that multiChoice dialog is
- // populated correctly
- List<String> selectedSpeedList = Arrays.asList(UserPreferences
- .getPlaybackSpeedArray());
- for (int i = 0; i < speedValues.length; i++) {
- speedChecked[i] = selectedSpeedList.contains(speedValues[i]);
- }
-
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.set_playback_speed_label);
- builder.setMultiChoiceItems(R.array.playback_speed_values,
- speedChecked, (dialog, which, isChecked) -> {
- speedChecked[which] = isChecked;
- });
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.setPositiveButton(android.R.string.ok,
- (dialog, which) -> {
- int choiceCount = 0;
- for (int i = 0; i < speedChecked.length; i++) {
- if (speedChecked[i]) {
- choiceCount++;
- }
- }
- String[] newSpeedValues = new String[choiceCount];
- int newSpeedIndex = 0;
- for (int i = 0; i < speedChecked.length; i++) {
- if (speedChecked[i]) {
- newSpeedValues[newSpeedIndex++] = speedValues[i];
- }
- }
-
- UserPreferences.setPlaybackSpeedArray(newSpeedValues);
-
- });
- builder.create().show();
- }
+ private static final String TAG = VariableSpeedDialog.class.getSimpleName();
+
+ private static final Intent playStoreIntent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse("market://details?id=com.falconware.prestissimo"));
+
+ private VariableSpeedDialog() {
+ }
+
+ public static void showDialog(final Context context) {
+ if (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context)
+ || UserPreferences.useSonic()
+ || Build.VERSION.SDK_INT >= 23) {
+ showSpeedSelectorDialog(context);
+ } else {
+ showGetPluginDialog(context, true);
+ }
+ }
+
+ public static void showGetPluginDialog(final Context context) {
+ showGetPluginDialog(context, false);
+ }
+
+ private static void showGetPluginDialog(final Context context, boolean showSpeedSelector) {
+ MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
+ builder.title(R.string.no_playback_plugin_title);
+ builder.content(R.string.no_playback_plugin_or_sonic_msg);
+ builder.positiveText(R.string.enable_sonic);
+ builder.negativeText(R.string.download_plugin_label);
+ builder.neutralText(R.string.close_label);
+ builder.onPositive((dialog, which) -> {
+ if (Build.VERSION.SDK_INT >= 16) { // just to be safe
+ UserPreferences.enableSonic(true);
+ if(showSpeedSelector) {
+ showSpeedSelectorDialog(context);
+ }
+ }
+ });
+ builder.onNegative((dialog, which) -> {
+ try {
+ context.startActivity(playStoreIntent);
+ } catch (ActivityNotFoundException e) {
+ // this is usually thrown on an emulator if the Android market is not installed
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ });
+ builder.forceStacking(true);
+ MaterialDialog dialog = builder.show();
+ if (Build.VERSION.SDK_INT < 16) {
+ View pos = dialog.getActionButton(DialogAction.POSITIVE);
+ pos.setEnabled(false);
+ }
+ if(!IntentUtils.isCallable(context.getApplicationContext(), playStoreIntent)) {
+ View pos = dialog.getActionButton(DialogAction.NEGATIVE);
+ pos.setEnabled(false);
+ }
+ }
+
+ private static void showSpeedSelectorDialog(final Context context) {
+ final String[] speedValues = context.getResources().getStringArray(
+ R.array.playback_speed_values);
+ // According to Java spec these get initialized to false on creation
+ final boolean[] speedChecked = new boolean[speedValues.length];
+
+ // Build the "isChecked" array so that multiChoice dialog is
+ // populated correctly
+ List<String> selectedSpeedList = Arrays.asList(UserPreferences
+ .getPlaybackSpeedArray());
+ for (int i = 0; i < speedValues.length; i++) {
+ speedChecked[i] = selectedSpeedList.contains(speedValues[i]);
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.set_playback_speed_label);
+ builder.setMultiChoiceItems(R.array.playback_speed_values,
+ speedChecked, (dialog, which, isChecked) -> {
+ speedChecked[which] = isChecked;
+ });
+ builder.setNegativeButton(android.R.string.cancel, null);
+ builder.setPositiveButton(android.R.string.ok,
+ (dialog, which) -> {
+ int choiceCount = 0;
+ for (int i = 0; i < speedChecked.length; i++) {
+ if (speedChecked[i]) {
+ choiceCount++;
+ }
+ }
+ String[] newSpeedValues = new String[choiceCount];
+ int newSpeedIndex = 0;
+ for (int i = 0; i < speedChecked.length; i++) {
+ if (speedChecked[i]) {
+ newSpeedValues[newSpeedIndex++] = speedValues[i];
+ }
+ }
+
+ UserPreferences.setPlaybackSpeedArray(newSpeedValues);
+
+ });
+ builder.create().show();
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
index f6c80aa7c..d979dc382 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -45,7 +45,7 @@ public class AddFeedFragment extends Fragment {
Button butSearchITunes = (Button) root.findViewById(R.id.butSearchItunes);
final MainActivity activity = (MainActivity) getActivity();
- activity.getMainActivtyActionBar().setTitle(R.string.add_feed_label);
+ activity.getSupportActionBar().setTitle(R.string.add_feed_label);
butSearchITunes.setOnClickListener(new View.OnClickListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
index 37be8f020..273c75240 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -10,6 +10,7 @@ import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
+import android.support.v7.widget.SimpleItemAnimator;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -45,6 +46,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemUtil;
+import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.greenrobot.event.EventBus;
@@ -189,6 +191,9 @@ public class AllEpisodesFragment extends Fragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if(!isAdded()) {
+ return;
+ }
super.onCreateOptionsMenu(menu, inflater);
if (itemsLoaded) {
inflater.inflate(R.menu.new_episodes, menu);
@@ -303,6 +308,10 @@ public class AllEpisodesFragment extends Fragment {
View root = inflater.inflate(fragmentResource, container, false);
recyclerView = (RecyclerView) root.findViewById(android.R.id.list);
+ RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator();
+ if (animator instanceof SimpleItemAnimator) {
+ ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
+ }
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.setHasFixedSize(true);
@@ -348,7 +357,7 @@ public class AllEpisodesFragment extends Fragment {
@Override
public FeedItem getItem(int position) {
- if (episodes != null && position < episodes.size()) {
+ if (episodes != null && 0 <= position && position < episodes.size()) {
return episodes.get(position);
}
return null;
@@ -374,6 +383,34 @@ public class AllEpisodesFragment extends Fragment {
}
return false;
}
+
+ @Override
+ public LongList getQueueIds() {
+ LongList queueIds = new LongList();
+ if(episodes == null) {
+ return queueIds;
+ }
+ for(FeedItem item : episodes) {
+ if(item.isTagged(FeedItem.TAG_QUEUE)) {
+ queueIds.add(item.getId());
+ }
+ }
+ return queueIds;
+ }
+
+ @Override
+ public LongList getFavoritesIds() {
+ LongList favoritesIds = new LongList();
+ if(episodes == null) {
+ return favoritesIds;
+ }
+ for(FeedItem item : episodes) {
+ if(item.isTagged(FeedItem.TAG_FAVORITE)) {
+ favoritesIds.add(item.getId());
+ }
+ }
+ return favoritesIds;
+ }
};
public void onEventMainThread(FeedItemEvent event) {
@@ -397,14 +434,15 @@ public class AllEpisodesFragment extends Fragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if(update.feedIds.length > 0) {
- if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ if (isUpdatingFeeds != update.feedIds.length > 0) {
getActivity().supportInvalidateOptionsMenu();
- }
}
- if(update.mediaIds.length > 0) {
- if(listAdapter != null) {
- listAdapter.notifyDataSetChanged();
+ if(listAdapter != null && update.mediaIds.length > 0) {
+ for(long mediaId : update.mediaIds) {
+ int pos = FeedItemUtil.indexOfItemWithMediaId(episodes, mediaId);
+ if(pos >= 0) {
+ listAdapter.notifyItemChanged(pos);
+ }
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
new file mode 100644
index 000000000..96abdd835
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -0,0 +1,77 @@
+package de.danoeh.antennapod.fragment;
+
+import android.os.Bundle;
+import android.support.v4.app.ListFragment;
+import android.view.View;
+import android.widget.ListView;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment;
+import de.danoeh.antennapod.adapter.ChaptersListAdapter;
+import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+
+
+public class ChaptersFragment extends ListFragment implements AudioplayerContentFragment {
+
+ private Playable media;
+ private PlaybackController controller;
+
+ private ChaptersListAdapter adapter;
+
+ public static ChaptersFragment newInstance(Playable media, PlaybackController controller) {
+ ChaptersFragment f = new ChaptersFragment();
+ f.media = media;
+ f.controller = controller;
+ return f;
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ // add padding
+ final ListView lv = getListView();
+ lv.setClipToPadding(false);
+ final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
+ lv.setPadding(0, vertPadding, 0, vertPadding);
+
+ adapter = new ChaptersListAdapter(getActivity(), 0, pos -> {
+ Chapter chapter = (Chapter) getListAdapter().getItem(pos);
+ controller.seekToChapter(chapter);
+ });
+ setListAdapter(adapter);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ adapter.setMedia(media);
+ adapter.notifyDataSetChanged();
+ if(media == null || media.getChapters() == null) {
+ setEmptyText(getString(R.string.no_chapters_label));
+ } else {
+ setEmptyText(null);
+ }
+ }
+
+ public void onDestroy() {
+ super.onDestroy();
+ adapter = null;
+ }
+
+ @Override
+ public void onMediaChanged(Playable media) {
+ if(this.media == media || adapter == null) {
+ return;
+ }
+ this.media = media;
+ adapter.setMedia(media);
+ adapter.notifyDataSetChanged();
+ 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 a5568b16e..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() {
@@ -131,7 +176,11 @@ public class CompletedDownloadsFragment extends ListFragment {
@Override
public FeedItem getItem(int position) {
- return (items != null) ? items.get(position) : null;
+ if (items != null && 0 <= position && position < items.size()) {
+ return items.get(position);
+ } else {
+ return null;
+ }
}
@Override
@@ -153,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())
@@ -162,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/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
index a1667cce0..d3b97f9df 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -1,7 +1,5 @@
package de.danoeh.antennapod.fragment;
-import android.app.Activity;
-import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
@@ -9,13 +7,11 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
+import android.widget.TextView;
import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
-import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.AudioplayerActivity;
import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.util.playback.Playable;
@@ -23,17 +19,18 @@ import de.danoeh.antennapod.core.util.playback.Playable;
/**
* Displays the cover and the title of a FeedItem.
*/
-public class CoverFragment extends Fragment implements
- AudioplayerContentFragment {
+public class CoverFragment extends Fragment implements AudioplayerContentFragment {
+
private static final String TAG = "CoverFragment";
private static final String ARG_PLAYABLE = "arg.playable";
private Playable media;
+ private View root;
+ private TextView txtvPodcastTitle;
+ private TextView txtvEpisodeTitle;
private ImageView imgvCover;
- private boolean viewCreated = false;
-
public static CoverFragment newInstance(Playable item) {
CoverFragment f = new CoverFragment();
if (item != null) {
@@ -47,7 +44,6 @@ public class CoverFragment extends Fragment implements
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setRetainInstance(true);
Bundle args = getArguments();
if (args != null) {
media = args.getParcelable(ARG_PLAYABLE);
@@ -59,39 +55,23 @@ public class CoverFragment extends Fragment implements
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- View root = inflater.inflate(R.layout.cover_fragment, container, false);
+ root = inflater.inflate(R.layout.cover_fragment, container, false);
+ txtvPodcastTitle = (TextView) root.findViewById(R.id.txtvPodcastTitle);
+ txtvEpisodeTitle = (TextView) root.findViewById(R.id.txtvEpisodeTitle);
imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
- imgvCover.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Activity activity = getActivity();
- if (activity != null && activity instanceof AudioplayerActivity) {
- ((AudioplayerActivity)activity).switchToLastFragment();
- }
- }
- });
- viewCreated = true;
return root;
}
private void loadMediaInfo() {
if (media != null) {
- imgvCover.post(new Runnable() {
-
- @Override
- public void run() {
- Context c = getActivity();
- if (c != null) {
- Glide.with(c)
- .load(media.getImageUri())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .dontAnimate()
- .into(imgvCover);
- }
- }
- });
+ txtvPodcastTitle.setText(media.getFeedTitle());
+ txtvEpisodeTitle.setText(media.getEpisodeTitle());
+ Glide.with(this)
+ .load(media.getImageUri())
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .dontAnimate()
+ .fitCenter()
+ .into(imgvCover);
} else {
Log.w(TAG, "loadMediaInfo was called while media was null");
}
@@ -99,12 +79,10 @@ public class CoverFragment extends Fragment implements
@Override
public void onStart() {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "On Start");
+ Log.d(TAG, "On Start");
super.onStart();
if (media != null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Loading media info");
+ Log.d(TAG, "Loading media info");
loadMediaInfo();
} else {
Log.w(TAG, "Unable to load media info: media was null");
@@ -112,12 +90,19 @@ public class CoverFragment extends Fragment implements
}
@Override
- public void onDataSetChanged(Playable media) {
- this.media = media;
- if (viewCreated) {
- loadMediaInfo();
- }
+ public void onDestroy() {
+ super.onDestroy();
+ // prevent memory leaks
+ root = null;
+ }
+ @Override
+ public void onMediaChanged(Playable media) {
+ if(!isAdded() || this.media == media) {
+ return;
+ }
+ this.media = media;
+ loadMediaInfo();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
index 90cacd2e4..b470d379a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -91,7 +91,11 @@ public class DownloadLogFragment extends ListFragment {
@Override
public DownloadStatus getItem(int position) {
- return (downloadLog != null) ? downloadLog.get(position) : null;
+ if (downloadLog != null && 0 <= position && position < downloadLog.size()) {
+ return downloadLog.get(position);
+ } else {
+ return null;
+ }
}
};
@@ -107,6 +111,9 @@ public class DownloadLogFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if(!isAdded()) {
+ return;
+ }
super.onCreateOptionsMenu(menu, inflater);
if (itemsLoaded) {
MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
index 7b02b4f18..80a9bf0b3 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -9,10 +9,10 @@ import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
+import android.widget.ProgressBar;
import android.widget.TextView;
import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
@@ -30,9 +30,10 @@ public class ExternalPlayerFragment extends Fragment {
private ViewGroup fragmentLayout;
private ImageView imgvCover;
- private ViewGroup layoutInfo;
private TextView txtvTitle;
private ImageButton butPlay;
+ private TextView mFeedName;
+ private ProgressBar mProgressBar;
private PlaybackController controller;
@@ -47,11 +48,12 @@ public class ExternalPlayerFragment extends Fragment {
container, false);
fragmentLayout = (ViewGroup) root.findViewById(R.id.fragmentLayout);
imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
- layoutInfo = (ViewGroup) root.findViewById(R.id.layoutInfo);
txtvTitle = (TextView) root.findViewById(R.id.txtvTitle);
butPlay = (ImageButton) root.findViewById(R.id.butPlay);
+ mFeedName = (TextView) root.findViewById(R.id.txtvAuthor);
+ mProgressBar = (ProgressBar) root.findViewById(R.id.episodeProgress);
- layoutInfo.setOnClickListener(new OnClickListener() {
+ fragmentLayout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
@@ -77,35 +79,8 @@ public class ExternalPlayerFragment extends Fragment {
return new PlaybackController(getActivity(), true) {
@Override
- public void setupGUI() {
- }
-
- @Override
public void onPositionObserverUpdate() {
- }
-
- @Override
- public void onReloadNotification(int code) {
- }
-
- @Override
- public void onBufferStart() {
- }
-
- @Override
- public void onBufferEnd() {
- }
-
- @Override
- public void onBufferUpdate(float progress) {
- }
-
- @Override
- public void onSleepTimerUpdate() {
- }
-
- @Override
- public void handleError(int code) {
+ ExternalPlayerFragment.this.onPositionObserverUpdate();
}
@Override
@@ -113,13 +88,6 @@ public class ExternalPlayerFragment extends Fragment {
return butPlay;
}
- @Override
- public void postStatusMsg(int msg) {
- }
-
- @Override
- public void clearStatusMsg() {
- }
@Override
public boolean loadMediaInfo() {
@@ -132,14 +100,6 @@ public class ExternalPlayerFragment extends Fragment {
}
@Override
- public void onAwaitingVideoSurface() {
- }
-
- @Override
- public void onServiceQueried() {
- }
-
- @Override
public void onShutdownNotification() {
playbackDone();
}
@@ -148,10 +108,6 @@ public class ExternalPlayerFragment extends Fragment {
public void onPlaybackEnd() {
playbackDone();
}
-
- @Override
- public void onPlaybackSpeedChange() {
- }
};
}
@@ -159,6 +115,8 @@ public class ExternalPlayerFragment extends Fragment {
public void onResume() {
super.onResume();
controller.init();
+ mProgressBar.setProgress((int)
+ ((double) controller.getPosition() / controller.getDuration() * 100));
}
@Override
@@ -199,6 +157,9 @@ public class ExternalPlayerFragment extends Fragment {
Playable media = controller.getMedia();
if (media != null) {
txtvTitle.setText(media.getEpisodeTitle());
+ mFeedName.setText(media.getFeedTitle());
+ mProgressBar.setProgress((int)
+ ((double) controller.getPosition() / controller.getDuration() * 100));
Glide.with(getActivity())
.load(media.getImageUri())
@@ -234,4 +195,9 @@ public class ExternalPlayerFragment extends Fragment {
public PlaybackController getPlaybackControllerTestingOnly() {
return controller;
}
+
+ public void onPositionObserverUpdate() {
+ mProgressBar.setProgress((int)
+ ((double) controller.getPosition() / controller.getDuration() * 100));
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index faa4413bb..c1c1aab6c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -7,8 +7,9 @@ import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.res.TypedArray;
+import android.graphics.Color;
import android.net.Uri;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
@@ -20,12 +21,14 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.webkit.WebSettings.LayoutAlgorithm;
+import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.AudioplayerActivity;
+import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -36,11 +39,15 @@ import de.danoeh.antennapod.core.util.ShownotesProvider;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Displays the description of a Playable object in a Webview.
*/
-public class ItemDescriptionFragment extends Fragment {
+public class ItemDescriptionFragment extends Fragment implements AudioplayerContentFragment {
private static final String TAG = "ItemDescriptionFragment";
@@ -59,8 +66,7 @@ public class ItemDescriptionFragment extends Fragment {
private ShownotesProvider shownotesProvider;
private Playable media;
-
- private AsyncTask<Void, Void, Void> webViewLoader;
+ private Subscription webViewLoader;
/**
* URL that was selected via long-press.
@@ -106,17 +112,17 @@ public class ItemDescriptionFragment extends Fragment {
Bundle savedInstanceState) {
Log.d(TAG, "Creating view");
webvDescription = new WebView(getActivity());
- if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
- if (Build.VERSION.SDK_INT >= 11
- && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
- webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- }
- webvDescription.setBackgroundColor(getResources().getColor(
- R.color.black));
+ if (Build.VERSION.SDK_INT >= 11) {
+ webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
+ TypedArray ta = getActivity().getTheme().obtainStyledAttributes(new int[]
+ {android.R.attr.colorBackground});
+ int backgroundColor = ta.getColor(0, UserPreferences.getTheme() ==
+ R.style.Theme_AntennaPod_Dark ? Color.BLACK : Color.WHITE);
+ ta.recycle();
+ webvDescription.setBackgroundColor(backgroundColor);
webvDescription.getSettings().setUseWideViewPort(false);
- webvDescription.getSettings().setLayoutAlgorithm(
- LayoutAlgorithm.NARROW_COLUMNS);
+ webvDescription.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
webvDescription.getSettings().setLoadWithOverviewMode(true);
webvDescription.setOnLongClickListener(webViewLongClickListener);
webvDescription.setWebViewClient(new WebViewClient() {
@@ -142,14 +148,7 @@ public class ItemDescriptionFragment extends Fragment {
super.onPageFinished(view, url);
Log.d(TAG, "Page finished");
// Restoring the scroll position might not always work
- view.postDelayed(new Runnable() {
-
- @Override
- public void run() {
- restoreFromPreference();
- }
-
- }, 50);
+ view.postDelayed(() -> restoreFromPreference(), 50);
}
});
@@ -159,26 +158,11 @@ public class ItemDescriptionFragment extends Fragment {
}
@Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- Log.d(TAG, "Fragment attached");
- }
-
- @Override
- public void onDetach() {
- super.onDetach();
- Log.d(TAG, "Fragment detached");
- if (webViewLoader != null) {
- webViewLoader.cancel(true);
- }
- }
-
- @Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Fragment destroyed");
if (webViewLoader != null) {
- webViewLoader.cancel(true);
+ webViewLoader.unsubscribe();
}
if (webvDescription != null) {
webvDescription.removeAllViews();
@@ -194,7 +178,6 @@ public class ItemDescriptionFragment extends Fragment {
Bundle args = getArguments();
saveState = args.getBoolean(ARG_SAVE_STATE, false);
highlightTimecodes = args.getBoolean(ARG_HIGHLIGHT_TIMECODES, false);
-
}
@Override
@@ -204,46 +187,21 @@ public class ItemDescriptionFragment extends Fragment {
if (args.containsKey(ARG_PLAYABLE)) {
media = args.getParcelable(ARG_PLAYABLE);
shownotesProvider = media;
- startLoader();
+ load();
} else if (args.containsKey(ARG_FEEDITEM_ID)) {
- AsyncTask<Void, Void, FeedItem> itemLoadTask = new AsyncTask<Void, Void, FeedItem>() {
-
- @Override
- protected FeedItem doInBackground(Void... voids) {
- return DBReader.getFeedItem(getArguments().getLong(ARG_FEEDITEM_ID));
- }
-
- @Override
- protected void onPostExecute(FeedItem feedItem) {
- super.onPostExecute(feedItem);
- shownotesProvider = feedItem;
- startLoader();
- }
- };
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- itemLoadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- itemLoadTask.execute();
- }
+ long id = getArguments().getLong(ARG_FEEDITEM_ID);
+ Observable.defer(() -> Observable.just(DBReader.getFeedItem(id)))
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(feedItem -> {
+ shownotesProvider = feedItem;
+ load();
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
-
-
- }
-
- @Override
- public void onResume() {
- super.onResume();
}
- @SuppressLint("NewApi")
- private void startLoader() {
- webViewLoader = createLoader();
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- webViewLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- webViewLoader.execute();
- }
- }
private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
@@ -338,49 +296,30 @@ public class ItemDescriptionFragment extends Fragment {
}
}
- private AsyncTask<Void, Void, Void> createLoader() {
- return new AsyncTask<Void, Void, Void>() {
- @Override
- protected void onCancelled() {
- super.onCancelled();
- webViewLoader = null;
- }
-
- String data;
-
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- // /webvDescription.loadData(url, "text/html", "utf-8");
- webvDescription.loadDataWithBaseURL(null, data, "text/html",
- "utf-8", "about:blank");
- Log.d(TAG, "Webview loaded");
- webViewLoader = null;
- }
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- Log.d(TAG, "Loading Webview");
- try {
- Activity activity = getActivity();
- if (activity != null) {
- Timeline timeline = new Timeline(activity, shownotesProvider);
- data = timeline.processShownotes(highlightTimecodes);
- } else {
- cancel(true);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
+ private void load() {
+ Log.d(TAG, "load()");
+ if(webViewLoader != null) {
+ webViewLoader.unsubscribe();
+ }
+ if(shownotesProvider == null) {
+ return;
+ }
+ webViewLoader = Observable.defer(() -> Observable.just(loadData()))
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(data -> {
+ webvDescription.loadDataWithBaseURL(null, data, "text/html",
+ "utf-8", "about:blank");
+ Log.d(TAG, "Webview loaded");
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ }
- };
+ private String loadData() {
+ Timeline timeline = new Timeline(getActivity(), shownotesProvider);
+ String data = timeline.processShownotes(highlightTimecodes);
+ return data;
}
@Override
@@ -410,7 +349,7 @@ public class ItemDescriptionFragment extends Fragment {
}
private boolean restoreFromPreference() {
- if (saveState) {
+ if (!saveState) {
Log.d(TAG, "Restoring from preferences");
Activity activity = getActivity();
if (activity != null) {
@@ -433,15 +372,22 @@ public class ItemDescriptionFragment extends Fragment {
private void onTimecodeLinkSelected(String link) {
int time = Timeline.getTimecodeLinkTime(link);
- if (getActivity() != null && getActivity() instanceof ItemDescriptionFragmentCallback) {
- PlaybackController pc = ((ItemDescriptionFragmentCallback) getActivity()).getPlaybackController();
+ if (getActivity() != null && getActivity() instanceof AudioplayerActivity) {
+ PlaybackController pc = ((AudioplayerActivity) getActivity()).getPlaybackController();
if (pc != null) {
pc.seekTo(time);
}
}
}
- public interface ItemDescriptionFragmentCallback {
- public PlaybackController getPlaybackController();
+ @Override
+ public void onMediaChanged(Playable media) {
+ if(this.media == media || webvDescription == null) {
+ return;
+ }
+ this.media = media;
+ this.shownotesProvider = media;
+ load();
}
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index 10d56d5cf..1382929af 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -4,35 +4,32 @@ import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
-import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
-import android.support.v4.util.Pair;
-import android.support.v7.widget.PopupMenu;
-import android.support.v7.widget.Toolbar;
+import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
-import android.widget.Button;
-import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
+import com.joanzapata.iconify.Iconify;
+import com.joanzapata.iconify.widget.IconButton;
import org.apache.commons.lang3.ArrayUtils;
@@ -43,6 +40,7 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
+import de.danoeh.antennapod.core.event.FavoritesEvent;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
@@ -57,6 +55,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.ShareUtils;
@@ -97,11 +96,11 @@ public class ItemFragment extends Fragment {
private long itemID;
private FeedItem item;
private LongList queue;
+ private LongList favorites;
private String webviewData;
private List<Downloader> downloaderList;
private ViewGroup root;
- private View header;
private WebView webvDescription;
private TextView txtvTitle;
private TextView txtvDuration;
@@ -109,10 +108,9 @@ public class ItemFragment extends Fragment {
private ImageView imgvCover;
private ProgressBar progbarDownload;
private ProgressBar progbarLoading;
- private Button butAction1;
- private Button butAction2;
- private ImageButton butMore;
- private PopupMenu popupMenu;
+ private IconButton butAction1;
+ private IconButton butAction2;
+ private Menu popupMenu;
private Subscription subscription;
@@ -125,7 +123,7 @@ public class ItemFragment extends Fragment {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
- setHasOptionsMenu(false);
+ setHasOptionsMenu(true);
itemID = getArguments().getLong(ARG_FEEDITEM, -1);
}
@@ -134,15 +132,12 @@ public class ItemFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
- ((MainActivity) getActivity()).getSupportActionBar().setTitle("");
- Toolbar toolbar = ((MainActivity) getActivity()).getToolbar();
View layout = inflater.inflate(R.layout.feeditem_fragment, container, false);
- header = inflater.inflate(R.layout.feeditem_fragment_header, toolbar, false);
root = (ViewGroup) layout.findViewById(R.id.content_root);
- txtvTitle = (TextView) header.findViewById(R.id.txtvTitle);
- txtvDuration = (TextView) header.findViewById(R.id.txtvDuration);
- txtvPublished = (TextView) header.findViewById(R.id.txtvPublished);
+ txtvTitle = (TextView) layout.findViewById(R.id.txtvTitle);
+ txtvDuration = (TextView) layout.findViewById(R.id.txtvDuration);
+ txtvPublished = (TextView) layout.findViewById(R.id.txtvPublished);
if (Build.VERSION.SDK_INT >= 14) { // ellipsize is causing problems on old versions, see #448
txtvTitle.setEllipsize(TextUtils.TruncateAt.END);
}
@@ -152,8 +147,7 @@ public class ItemFragment extends Fragment {
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
- webvDescription.setBackgroundColor(getResources().getColor(
- R.color.black));
+ webvDescription.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.black));
}
webvDescription.getSettings().setUseWideViewPort(false);
webvDescription.getSettings().setLayoutAlgorithm(
@@ -172,81 +166,43 @@ public class ItemFragment extends Fragment {
});
registerForContextMenu(webvDescription);
- imgvCover = (ImageView) header.findViewById(R.id.imgvCover);
- progbarDownload = (ProgressBar) header.findViewById(R.id.progbarDownload);
+ imgvCover = (ImageView) layout.findViewById(R.id.imgvCover);
+ progbarDownload = (ProgressBar) layout.findViewById(R.id.progbarDownload);
progbarLoading = (ProgressBar) layout.findViewById(R.id.progbarLoading);
- butAction1 = (Button) header.findViewById(R.id.butAction1);
- butAction2 = (Button) header.findViewById(R.id.butAction2);
- butMore = (ImageButton) header.findViewById(R.id.butMoreActions);
- popupMenu = new PopupMenu(getActivity(), butMore);
-
- butAction1.setOnClickListener(new View.OnClickListener() {
- DefaultActionButtonCallback actionButtonCallback = new DefaultActionButtonCallback(getActivity());
-
- @Override
-
- public void onClick(View v) {
- if (item == null) {
- return;
- }
- actionButtonCallback.onActionButtonPressed(item);
- FeedMedia media = item.getMedia();
- if (media != null && media.isDownloaded()) {
- // playback was started, dialog should close itself
- ((MainActivity) getActivity()).dismissChildFragment();
- }
- }
- }
- );
+ butAction1 = (IconButton) layout.findViewById(R.id.butAction1);
+ butAction2 = (IconButton) layout.findViewById(R.id.butAction2);
- butAction2.setOnClickListener(v -> {
- if (item == null) {
- return;
- }
-
- if (item.hasMedia()) {
- FeedMedia media = item.getMedia();
- if (!media.isDownloaded()) {
- DBTasks.playMedia(getActivity(), media, true, true, true);
- ((MainActivity) getActivity()).dismissChildFragment();
- } else {
- DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
- }
- } else if (item.getLink() != null) {
- Uri uri = Uri.parse(item.getLink());
- getActivity().startActivity(new Intent(Intent.ACTION_VIEW, uri));
- }
+ butAction1.setOnClickListener(v -> {
+ if (item == null) {
+ return;
}
- );
-
- butMore.setOnClickListener(v -> {
- if (item == null) {
- return;
- }
- popupMenu.getMenu().clear();
- popupMenu.inflate(R.menu.feeditem_options);
- if (item.hasMedia()) {
- FeedItemMenuHandler.onPrepareMenu(getActivity(), popupMenuInterface, item, true, queue);
- } else {
- // these are already available via button1 and button2
- FeedItemMenuHandler.onPrepareMenu(getActivity(), popupMenuInterface, item, true, queue,
- R.id.mark_read_item, R.id.visit_website_item);
- }
- popupMenu.show();
+ DefaultActionButtonCallback actionButtonCallback = new DefaultActionButtonCallback(getActivity());
+ actionButtonCallback.onActionButtonPressed(item);
+ FeedMedia media = item.getMedia();
+ if (media != null && media.isDownloaded()) {
+ // playback was started, dialog should close itself
+ ((MainActivity) getActivity()).dismissChildFragment();
}
- );
+ });
- popupMenu.setOnMenuItemClickListener(menuItem -> {
+ butAction2.setOnClickListener(v -> {
+ if (item == null) {
+ return;
+ }
- try {
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), menuItem.getItemId(), item);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
- return true;
+ if (item.hasMedia()) {
+ FeedMedia media = item.getMedia();
+ if (!media.isDownloaded()) {
+ DBTasks.playMedia(getActivity(), media, true, true, true);
+ ((MainActivity) getActivity()).dismissChildFragment();
+ } else {
+ DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
}
+ } else if (item.getLink() != null) {
+ Uri uri = Uri.parse(item.getLink());
+ getActivity().startActivity(new Intent(Intent.ACTION_VIEW, uri));
}
- );
+ });
return layout;
}
@@ -254,8 +210,6 @@ public class ItemFragment extends Fragment {
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- Toolbar toolbar = ((MainActivity) getActivity()).getToolbar();
- toolbar.addView(header);
load();
}
@@ -279,7 +233,6 @@ public class ItemFragment extends Fragment {
@Override
public void onDestroyView() {
super.onDestroyView();
- resetViewState();
if(subscription != null) {
subscription.unsubscribe();
}
@@ -289,15 +242,37 @@ public class ItemFragment extends Fragment {
}
}
- private void resetViewState() {
- Toolbar toolbar = ((MainActivity) getActivity()).getToolbar();
- toolbar.removeView(header);
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if(!isAdded() || item == null) {
+ return;
+ }
+ inflater.inflate(R.menu.feeditem_options, menu);
+ popupMenu = menu;
+ if (item.hasMedia()) {
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue, favorites);
+ } else {
+ // these are already available via button1 and button2
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue, favorites,
+ R.id.mark_read_item, R.id.visit_website_item);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ try {
+ return FeedItemMenuHandler.onMenuItemClicked(getActivity(), menuItem.getItemId(), item);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
+ return true;
+ }
}
private final FeedItemMenuHandler.MenuInterface popupMenuInterface = new FeedItemMenuHandler.MenuInterface() {
@Override
public void setItemVisibility(int id, boolean visible) {
- MenuItem item = popupMenu.getMenu().findItem(id);
+ MenuItem item = popupMenu.findItem(id);
if (item != null) {
item.setVisible(visible);
}
@@ -319,11 +294,12 @@ public class ItemFragment extends Fragment {
Log.d(TAG, "updateAppearance item is null");
return;
}
-
+ getActivity().supportInvalidateOptionsMenu();
txtvTitle.setText(item.getTitle());
if (item.getPubDate() != null) {
- txtvPublished.setText(DateUtils.formatDateTime(getActivity(), item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
+ String pubDateStr = DateUtils.formatAbbrev(getActivity(), item.getPubDate());
+ txtvPublished.setText(pubDateStr);
}
Glide.with(getActivity())
@@ -335,7 +311,7 @@ public class ItemFragment extends Fragment {
.dontAnimate()
.into(imgvCover);
- progbarDownload.setVisibility(View.INVISIBLE);
+ progbarDownload.setVisibility(View.GONE);
if (item.hasMedia() && downloaderList != null) {
for (Downloader downloader : downloaderList) {
if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
@@ -347,54 +323,55 @@ public class ItemFragment extends Fragment {
}
FeedMedia media = item.getMedia();
+ String butAction1Icon = null;
+ int butAction1Text = 0;
+ String butAction2Icon = null;
+ int butAction2Text = 0;
if (media == null) {
- TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.navigation_accept,
- R.attr.location_web_site});
-
if (!item.isPlayed()) {
- butAction1.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(0), null, null, null);
- butAction1.setText(getActivity().getString(R.string.mark_read_label));
- butAction1.setVisibility(View.VISIBLE);
- } else {
- butAction1.setVisibility(View.INVISIBLE);
+ butAction1Icon = "{fa-check 24sp}";
+ butAction1Text = R.string.mark_read_label;
}
-
if (item.getLink() != null) {
- butAction2.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(1), null, null, null);
- butAction2.setText(getActivity().getString(R.string.visit_website_label));
- } else {
- butAction2.setEnabled(false);
+ butAction2Icon = "{md-web 24sp}";
+ butAction2Text = R.string.visit_website_label;
}
-
- drawables.recycle();
- } else {if(media.getDuration() > 0) {
+ } else {
+ if(media.getDuration() > 0) {
txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
}
-
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
- TypedArray drawables = getActivity().obtainStyledAttributes(new int[]{R.attr.av_play,
- R.attr.av_download, R.attr.action_stream, R.attr.content_discard, R.attr.navigation_cancel});
-
if (!media.isDownloaded()) {
- butAction2.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(2), null, null, null);
- butAction2.setText(getActivity().getString(R.string.stream_label));
+ butAction2Icon = "{md-settings-input-antenna 24sp}";
+ butAction2Text = R.string.stream_label;
} else {
- butAction2.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(3), null, null, null);
- butAction2.setText(getActivity().getString(R.string.remove_episode_lable));
+ butAction2Icon = "{md-delete 24sp}";
+ butAction2Text = R.string.remove_label;
}
-
if (isDownloading) {
- butAction1.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(4), null, null, null);
- butAction1.setText(getActivity().getString(R.string.cancel_download_label));
+ butAction1Icon = "{md-cancel 24sp}";
+ butAction1Text = R.string.cancel_label;
} else if (media.isDownloaded()) {
- butAction1.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(0), null, null, null);
- butAction1.setText(getActivity().getString(R.string.play_label));
+ butAction1Icon = "{md-play-arrow 24sp}";
+ butAction1Text = R.string.play_label;
} else {
- butAction1.setCompoundDrawablesWithIntrinsicBounds(drawables.getDrawable(1), null, null, null);
- butAction1.setText(getActivity().getString(R.string.download_label));
+ butAction1Icon = "{md-file-download 24sp}";
+ butAction1Text = R.string.download_label;
}
-
- drawables.recycle();
+ }
+ if(butAction1Icon != null && butAction1Text != 0) {
+ butAction1.setText(butAction1Icon +"\u0020\u0020" + getActivity().getString(butAction1Text));
+ Iconify.addIcons(butAction1);
+ butAction1.setVisibility(View.VISIBLE);
+ } else {
+ butAction1.setVisibility(View.INVISIBLE);
+ }
+ if(butAction2Icon != null && butAction2Text != 0) {
+ butAction2.setText(butAction2Icon +"\u0020\u0020" + getActivity().getString(butAction2Text));
+ Iconify.addIcons(butAction2);
+ butAction2.setVisibility(View.VISIBLE);
+ } else {
+ butAction2.setVisibility(View.INVISIBLE);
}
}
@@ -481,6 +458,12 @@ public class ItemFragment extends Fragment {
}
}
+ public void onEventMainThread(FavoritesEvent event) {
+ if(event.item.getId() == itemID) {
+ load();
+ }
+ }
+
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
for(FeedItem item : event.items) {
@@ -524,8 +507,9 @@ public class ItemFragment extends Fragment {
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
- item = result.first;
- queue = result.second;
+ item = (FeedItem) result[0];
+ queue = (LongList) result[1];
+ favorites = (LongList) result[2];
if (!itemsLoaded) {
itemsLoaded = true;
onFragmentLoaded();
@@ -537,14 +521,25 @@ public class ItemFragment extends Fragment {
});
}
- private Pair<FeedItem,LongList> loadInBackground() {
- FeedItem data1 = DBReader.getFeedItem(itemID);
- if (data1 != null) {
- Timeline t = new Timeline(getActivity(), data1);
+ private Object[] loadInBackground() {
+ FeedItem feedItem = DBReader.getFeedItem(itemID);
+ if (feedItem != null) {
+ Timeline t = new Timeline(getActivity(), feedItem);
webviewData = t.processShownotes(false);
}
- LongList data2 = DBReader.getQueueIDList();
- return Pair.create(data1, data2);
+ LongList queue;
+ if(feedItem.isTagged(FeedItem.TAG_QUEUE)) {
+ queue = LongList.of(feedItem.getId());
+ } else {
+ queue = new LongList(0);
+ }
+ LongList favorites;
+ if(feedItem.isTagged(FeedItem.TAG_FAVORITE)) {
+ favorites = LongList.of(feedItem.getId());
+ } else {
+ favorites = new LongList(0);
+ }
+ return new Object[] { feedItem, queue, favorites };
}
}
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 09d2f5676..303d273bc 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -9,11 +9,8 @@ import android.graphics.Color;
import android.graphics.LightingColorFilter;
import android.os.Build;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
import android.support.v4.app.ListFragment;
-import android.support.v4.util.Pair;
import android.support.v4.view.MenuItemCompat;
-import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.ContextMenu;
@@ -50,6 +47,7 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
+import de.danoeh.antennapod.core.event.FavoritesEvent;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
@@ -88,6 +86,7 @@ public class ItemlistFragment extends ListFragment {
private static final String TAG = "ItemlistFragment";
private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE
+ | EventDistributor.FEED_LIST_UPDATE
| EventDistributor.PLAYER_STATUS_UPDATE;
public static final String EXTRA_SELECTED_FEEDITEM = "extra.de.danoeh.antennapod.activity.selected_feeditem";
@@ -100,6 +99,7 @@ public class ItemlistFragment extends ListFragment {
private long feedID;
private Feed feed;
private LongList queuedItemsIds;
+ private LongList favoritedItemsId;
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
@@ -145,34 +145,26 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().registerSticky(this);
if (viewsCreated && itemsLoaded) {
onFragmentLoaded();
}
}
@Override
- public void onStop() {
- super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
- EventBus.getDefault().unregister(this);
- if(subscription != null) {
- subscription.unsubscribe();
- }
- }
-
- @Override
public void onResume() {
super.onResume();
- Log.d(TAG, "onResume()");
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().registerSticky(this);
+ ((MainActivity)getActivity()).getSupportActionBar().setTitle("");
updateProgressBarVisibility();
loadItems();
}
@Override
- public void onDetach() {
- super.onDetach();
+ public void onPause() {
+ super.onPause();
+ EventDistributor.getInstance().unregister(contentUpdate);
+ EventBus.getDefault().unregister(this);
if(subscription != null) {
subscription.unsubscribe();
}
@@ -203,6 +195,9 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if(!isAdded()) {
+ return;
+ }
super.onCreateOptionsMenu(menu, inflater);
if (itemsLoaded) {
@@ -257,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:
@@ -330,7 +325,8 @@ public class ItemlistFragment extends ListFragment {
contextMenu = menu;
lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
- FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queuedItemsIds);
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, queuedItemsIds,
+ favoritedItemsId);
}
@Override
@@ -368,7 +364,6 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
- ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle("");
registerForContextMenu(getListView());
@@ -380,9 +375,14 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
+ if(adapter == null) {
+ return;
+ }
FeedItem selection = adapter.getItem(position - l.getHeaderViewsCount());
if (selection != null) {
- ((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(selection.getId()));
+ MainActivity activity = (MainActivity) getActivity();
+ activity.loadChildFragment(ItemFragment.newInstance(selection.getId()));
+ activity.getSupportActionBar().setTitle(feed.getTitle());
}
}
@@ -391,6 +391,11 @@ public class ItemlistFragment extends ListFragment {
loadItems();
}
+ public void onEvent(FavoritesEvent event) {
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
+ loadItems();
+ }
+
public void onEvent(FeedEvent event) {
Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
if(event.feedId == feedID) {
@@ -417,13 +422,11 @@ public class ItemlistFragment extends ListFragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if(update.feedIds.length > 0) {
+ if (isUpdatingFeed != event.update.feedIds.length > 0) {
updateProgressBarVisibility();
}
- if(update.mediaIds.length > 0) {
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
+ if(adapter != null && update.mediaIds.length > 0) {
+ adapter.notifyDataSetChanged();
}
}
@@ -549,15 +552,12 @@ public class ItemlistFragment extends ListFragment {
.dontAnimate()
.into(imgvCover);
- butShowInfo.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (viewsCreated && itemsLoaded) {
- Intent startIntent = new Intent(getActivity(), FeedInfoActivity.class);
- startIntent.putExtra(FeedInfoActivity.EXTRA_FEED_ID,
- feed.getId());
- startActivity(startIntent);
- }
+ butShowInfo.setOnClickListener(v -> {
+ if (viewsCreated && itemsLoaded) {
+ Intent startIntent = new Intent(getActivity(), FeedInfoActivity.class);
+ startIntent.putExtra(FeedInfoActivity.EXTRA_FEED_ID,
+ feed.getId());
+ startActivity(startIntent);
}
});
}
@@ -575,16 +575,13 @@ public class ItemlistFragment extends ListFragment {
View header = inflater.inflate(R.layout.more_content_list_footer, lv, false);
lv.addFooterView(header);
listFooter = new MoreContentListFooterUtil(header);
- listFooter.setClickListener(new MoreContentListFooterUtil.Listener() {
- @Override
- public void onClick() {
- if (feed != null) {
- try {
- DBTasks.loadNextPageOfFeed(getActivity(), feed, false);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage());
- }
+ listFooter.setClickListener(() -> {
+ if (feed != null) {
+ try {
+ DBTasks.loadNextPageOfFeed(getActivity(), feed, false);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage());
}
}
});
@@ -595,7 +592,11 @@ public class ItemlistFragment extends ListFragment {
@Override
public FeedItem getItem(int position) {
- return (feed != null) ? feed.getItemAtIndex(position) : null;
+ if (feed != null && 0 <= position && position < feed.getNumOfItems()) {
+ return feed.getItemAtIndex(position);
+ } else {
+ return null;
+ }
}
@Override
@@ -627,14 +628,14 @@ public class ItemlistFragment extends ListFragment {
if(subscription != null) {
subscription.unsubscribe();
}
-
subscription = Observable.fromCallable(() -> loadData())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result != null) {
- feed = result.first;
- queuedItemsIds = result.second;
+ feed = (Feed) result[0];
+ queuedItemsIds = (LongList) result[1];
+ favoritedItemsId = (LongList) result[2];
itemsLoaded = true;
if (viewsCreated) {
onFragmentLoaded();
@@ -645,14 +646,15 @@ public class ItemlistFragment extends ListFragment {
});
}
- private Pair<Feed, LongList> loadData() {
+ private Object[] loadData() {
Feed feed = DBReader.getFeed(feedID);
if(feed != null && feed.getItemFilter() != null) {
FeedItemFilter filter = feed.getItemFilter();
feed.setItems(filter.filter(feed.getItems()));
}
LongList queuedItemsIds = DBReader.getQueueIDList();
- return Pair.create(feed, queuedItemsIds);
+ LongList favoritedItemsId = DBReader.getFavoriteIDList();
+ return new Object[] { feed, queuedItemsIds, favoritedItemsId };
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
index 72704245f..eb947dc2b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
@@ -1,22 +1,27 @@
package de.danoeh.antennapod.fragment;
import android.content.Intent;
-import android.content.res.Resources;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
+import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.SearchView;
+import android.util.Log;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
+import android.widget.Button;
import android.widget.GridView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.Request;
+import com.squareup.okhttp.Response;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -26,31 +31,45 @@ import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.adapter.itunes.ItunesAdapter;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
+import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast;
//Searches iTunes store for given string and displays results in a list
public class ItunesSearchFragment extends Fragment {
- final String TAG = "ItunesSearchFragment";
- /**
- * Search input field
- */
- private SearchView searchView;
+
+ private static final String TAG = "ItunesSearchFragment";
+
+ private static final String API_URL = "https://itunes.apple.com/search?media=podcast&term=%s";
+
/**
* Adapter responsible with the search results
*/
private ItunesAdapter adapter;
+ private GridView gridView;
+ private ProgressBar progressBar;
+ private TextView txtvError;
+ private Button butRetry;
+ private TextView txtvEmpty;
/**
* List of podcasts retreived from the search
*/
private List<Podcast> searchResults;
+ private List<Podcast> topList;
+ private Subscription subscription;
/**
* Replace adapter data with provided search results from SearchTask.
@@ -59,13 +78,17 @@ public class ItunesSearchFragment extends Fragment {
void updateData(List<Podcast> result) {
this.searchResults = result;
adapter.clear();
-
- //ArrayAdapter.addAll() requires minsdk > 10
- for(Podcast p: result) {
- adapter.add(p);
+ if (result != null && result.size() > 0) {
+ gridView.setVisibility(View.VISIBLE);
+ txtvEmpty.setVisibility(View.GONE);
+ for (Podcast p : result) {
+ adapter.add(p);
+ }
+ adapter.notifyDataSetInvalidated();
+ } else {
+ gridView.setVisibility(View.GONE);
+ txtvEmpty.setVisibility(View.VISIBLE);
}
-
- adapter.notifyDataSetInvalidated();
}
/**
@@ -78,47 +101,105 @@ public class ItunesSearchFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- adapter = new ItunesAdapter(getActivity(), new ArrayList<Podcast>());
-
+ setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
- View view = inflater.inflate(R.layout.fragment_itunes_search, container, false);
- GridView gridView = (GridView) view.findViewById(R.id.gridView);
+ View root = inflater.inflate(R.layout.fragment_itunes_search, container, false);
+ gridView = (GridView) root.findViewById(R.id.gridView);
+ adapter = new ItunesAdapter(getActivity(), new ArrayList<>());
gridView.setAdapter(adapter);
//Show information about the podcast when the list item is clicked
- gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- Intent intent = new Intent(getActivity(),
- OnlineFeedViewActivity.class);
-
- //Tell the OnlineFeedViewActivity where to go
- String url = searchResults.get(position).feedUrl;
- intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, url);
-
+ gridView.setOnItemClickListener((parent, view1, position, id) -> {
+ Podcast podcast = searchResults.get(position);
+ if (!podcast.feedUrl.contains("itunes.apple.com")) {
+ Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
+ intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, podcast.feedUrl);
intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes");
startActivity(intent);
+ } else {
+ gridView.setVisibility(View.GONE);
+ progressBar.setVisibility(View.VISIBLE);
+ rx.Observable.create((Observable.OnSubscribe<String>) subscriber -> {
+ OkHttpClient client = AntennapodHttpClient.getHttpClient();
+ Request.Builder httpReq = new Request.Builder()
+ .url(podcast.feedUrl)
+ .header("User-Agent", ClientConfig.USER_AGENT);
+ try {
+ Response response = client.newCall(httpReq.build()).execute();
+ if (response.isSuccessful()) {
+ String resultString = response.body().string();
+ JSONObject result = new JSONObject(resultString);
+ JSONObject results = result.getJSONArray("results").getJSONObject(0);
+ String feedUrl = results.getString("feedUrl");
+ subscriber.onNext(feedUrl);
+ } else {
+ String prefix = getString(R.string.error_msg_prefix);
+ subscriber.onError(new IOException(prefix + response));
+ }
+ } catch (IOException | JSONException e) {
+ subscriber.onError(e);
+ }
+ subscriber.onCompleted();
+ })
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(feedUrl -> {
+ progressBar.setVisibility(View.GONE);
+ gridView.setVisibility(View.VISIBLE);
+ Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
+ intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, feedUrl);
+ intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes");
+ startActivity(intent);
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ progressBar.setVisibility(View.GONE);
+ gridView.setVisibility(View.VISIBLE);
+ String prefix = getString(R.string.error_msg_prefix);
+ new MaterialDialog.Builder(getActivity())
+ .content(prefix + " " + error.getMessage())
+ .neutralText(android.R.string.ok)
+ .show();
+ });
}
});
+ progressBar = (ProgressBar) root.findViewById(R.id.progressBar);
+ txtvError = (TextView) root.findViewById(R.id.txtvError);
+ butRetry = (Button) root.findViewById(R.id.butRetry);
+ txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
+
+ loadToplist();
+
+ return root;
+ }
- //Configure search input view to be expanded by default with a visible submit button
- searchView = (SearchView) view.findViewById(R.id.itunes_search_view);
- searchView.setIconifiedByDefault(false);
- searchView.setIconified(false);
- searchView.setSubmitButtonEnabled(true);
- searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (subscription != null) {
+ subscription.unsubscribe();
+ }
+ adapter = null;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.itunes_search, menu);
+ MenuItem searchItem = menu.findItem(R.id.action_search);
+ final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
+ MenuItemUtils.adjustTextColor(getActivity(), sv);
+ sv.setQueryHint(getString(R.string.search_itunes_label));
+ sv.setOnQueryTextListener(new android.support.v7.widget.SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
- //This prevents onQueryTextSubmit() from being called twice when keyboard is used
- //to submit the query.
- searchView.clearFocus();
- new SearchTask(s).execute();
- return false;
+ sv.clearFocus();
+ search(s);
+ return true;
}
@Override
@@ -126,88 +207,153 @@ public class ItunesSearchFragment extends Fragment {
return false;
}
});
-
- SearchView.SearchAutoComplete textField = (SearchView.SearchAutoComplete) searchView.findViewById(de.danoeh.antennapod.R.id.search_src_text);
- if(UserPreferences.getTheme() == de.danoeh.antennapod.R.style.Theme_AntennaPod_Dark) {
- textField.setTextColor(Resources.getSystem().getColor(android.R.color.white));
- } else {
- textField.setTextColor(Resources.getSystem().getColor(android.R.color.black));
- }
-
- return view;
- }
-
- /**
- * Search the iTunes store for podcasts using the given query
- */
- class SearchTask extends AsyncTask<Void,Void,Void> {
- /**
- * Incomplete iTunes API search URL
- */
- final String apiUrl = "https://itunes.apple.com/search?media=podcast&term=%s";
-
- /**
- * Search terms
- */
- final String query;
-
- /**
- * Search result
- */
- final List<Podcast> taskData = new ArrayList<>();
-
- /**
- * Constructor
- *
- * @param query Search string
- */
- public SearchTask(String query) {
- String encodedQuery = null;
- try {
- encodedQuery = URLEncoder.encode(query, "UTF-8");
- } catch(UnsupportedEncodingException e) {
- // this won't ever be thrown
- }
- if(encodedQuery != null) {
- this.query = encodedQuery;
- } else {
- this.query = query; // failsafe
+ MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ return true;
}
- }
- //Get the podcast data
- @Override
- protected Void doInBackground(Void... params) {
-
- //Spaces in the query need to be replaced with '+' character.
- String formattedUrl = String.format(apiUrl, query).replace(' ', '+');
-
- HttpClient client = new DefaultHttpClient();
- HttpGet get = new HttpGet(formattedUrl);
-
- try {
- HttpResponse response = client.execute(get);
- String resultString = EntityUtils.toString(response.getEntity());
- JSONObject result = new JSONObject(resultString);
- JSONArray j = result.getJSONArray("results");
-
- for (int i = 0; i < j.length(); i++){
- JSONObject podcastJson = j.getJSONObject(i);
- Podcast podcast = new Podcast(podcastJson);
- taskData.add(podcast);
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ if(searchResults != null) {
+ searchResults = null;
+ updateData(topList);
}
-
- } catch (IOException | JSONException e) {
- e.printStackTrace();
+ return true;
}
- return null;
+ });
+ }
+
+ private void loadToplist() {
+ if (subscription != null) {
+ subscription.unsubscribe();
}
+ gridView.setVisibility(View.GONE);
+ txtvError.setVisibility(View.GONE);
+ butRetry.setVisibility(View.GONE);
+ txtvEmpty.setVisibility(View.GONE);
+ progressBar.setVisibility(View.VISIBLE);
+ subscription = rx.Observable.create((Observable.OnSubscribe<List<Podcast>>) subscriber -> {
+ String lang = Locale.getDefault().getLanguage();
+ String url = "https://itunes.apple.com/" + lang + "/rss/toppodcasts/limit=25/explicit=true/json";
+ OkHttpClient client = AntennapodHttpClient.getHttpClient();
+ Request.Builder httpReq = new Request.Builder()
+ .url(url)
+ .header("User-Agent", ClientConfig.USER_AGENT);
+ List<Podcast> results = new ArrayList<>();
+ try {
+ Response response = client.newCall(httpReq.build()).execute();
+ if(!response.isSuccessful()) {
+ // toplist for language does not exist, fall back to united states
+ url = "https://itunes.apple.com/us/rss/toppodcasts/limit=25/explicit=true/json";
+ httpReq = new Request.Builder()
+ .url(url)
+ .header("User-Agent", ClientConfig.USER_AGENT);
+ response = client.newCall(httpReq.build()).execute();
+ }
+ if(response.isSuccessful()) {
+ String resultString = response.body().string();
+ JSONObject result = new JSONObject(resultString);
+ JSONObject feed = result.getJSONObject("feed");
+ JSONArray entries = feed.getJSONArray("entry");
+
+ for(int i=0; i < entries.length(); i++) {
+ JSONObject json = entries.getJSONObject(i);
+ Podcast podcast = Podcast.fromToplist(json);
+ results.add(podcast);
+ }
+ }
+ else {
+ String prefix = getString(R.string.error_msg_prefix);
+ subscriber.onError(new IOException(prefix + response));
+ }
+ } catch (IOException | JSONException e) {
+ subscriber.onError(e);
+ }
+ subscriber.onNext(results);
+ subscriber.onCompleted();
+ })
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(podcasts -> {
+ progressBar.setVisibility(View.GONE);
+ topList = podcasts;
+ updateData(topList);
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ progressBar.setVisibility(View.GONE);
+ txtvError.setText(error.toString());
+ txtvError.setVisibility(View.VISIBLE);
+ butRetry.setOnClickListener(v -> loadToplist());
+ butRetry.setVisibility(View.VISIBLE);
+ });
+ }
- //Save the data and update the list
- @Override
- protected void onPostExecute(Void aVoid) {
- super.onPostExecute(aVoid);
- updateData(taskData);
+ private void search(String query) {
+ if (subscription != null) {
+ subscription.unsubscribe();
}
+ gridView.setVisibility(View.GONE);
+ txtvError.setVisibility(View.GONE);
+ butRetry.setVisibility(View.GONE);
+ txtvEmpty.setVisibility(View.GONE);
+ progressBar.setVisibility(View.VISIBLE);
+ subscription = rx.Observable.create((Observable.OnSubscribe<List<Podcast>>) subscriber -> {
+ String encodedQuery = null;
+ try {
+ encodedQuery = URLEncoder.encode(query, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ // this won't ever be thrown
+ }
+ if (encodedQuery == null) {
+ encodedQuery = query; // failsafe
+ }
+
+ //Spaces in the query need to be replaced with '+' character.
+ String formattedUrl = String.format(API_URL, query).replace(' ', '+');
+
+ OkHttpClient client = AntennapodHttpClient.getHttpClient();
+ Request.Builder httpReq = new Request.Builder()
+ .url(formattedUrl)
+ .header("User-Agent", ClientConfig.USER_AGENT);
+ List<Podcast> podcasts = new ArrayList<>();
+ try {
+ Response response = client.newCall(httpReq.build()).execute();
+
+ if(response.isSuccessful()) {
+ String resultString = response.body().string();
+ JSONObject result = new JSONObject(resultString);
+ JSONArray j = result.getJSONArray("results");
+
+ for (int i = 0; i < j.length(); i++) {
+ JSONObject podcastJson = j.getJSONObject(i);
+ Podcast podcast = Podcast.fromSearch(podcastJson);
+ podcasts.add(podcast);
+ }
+ }
+ else {
+ String prefix = getString(R.string.error_msg_prefix);
+ subscriber.onError(new IOException(prefix + response));
+ }
+ } catch (IOException | JSONException e) {
+ subscriber.onError(e);
+ }
+ subscriber.onNext(podcasts);
+ subscriber.onCompleted();
+ })
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(podcasts -> {
+ progressBar.setVisibility(View.GONE);
+ updateData(podcasts);
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ progressBar.setVisibility(View.GONE);
+ txtvError.setText(error.toString());
+ txtvError.setVisibility(View.VISIBLE);
+ butRetry.setOnClickListener(v -> search(query));
+ butRetry.setVisibility(View.VISIBLE);
+ });
}
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
index b47a197c3..c5b77fae2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -1,6 +1,6 @@
package de.danoeh.antennapod.fragment;
-import android.app.Activity;
+import android.content.Context;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
@@ -14,7 +14,6 @@ import android.view.View;
import android.widget.ListView;
import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
@@ -22,10 +21,10 @@ import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
+import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -50,16 +49,13 @@ public class PlaybackHistoryFragment extends ListFragment {
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
- private AtomicReference<Activity> activity = new AtomicReference<Activity>();
-
private List<Downloader> downloaderList;
private Subscription subscription;
@Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- this.activity.set(activity);
+ public void onAttach(Context context) {
+ super.onAttach(context);
if (viewsCreated && itemsLoaded) {
onFragmentLoaded();
}
@@ -123,7 +119,6 @@ public class PlaybackHistoryFragment extends ListFragment {
if(subscription != null) {
subscription.unsubscribe();
}
- activity.set(null);
}
@Override
@@ -138,7 +133,7 @@ public class PlaybackHistoryFragment extends ListFragment {
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
if (adapter != null) {
- adapter.notifyDataSetChanged();
+ adapter.notifyDataSetChanged();
}
}
@@ -153,6 +148,9 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if(!isAdded()) {
+ return;
+ }
super.onCreateOptionsMenu(menu, inflater);
if (itemsLoaded) {
MenuItem clearHistory = menu.add(Menu.NONE, R.id.clear_history_item, Menu.CATEGORY_CONTAINER, R.string.clear_history_label);
@@ -210,7 +208,8 @@ public class PlaybackHistoryFragment extends ListFragment {
// played items shoudln't be transparent for this fragment since, *all* items
// in this fragment will, by definition, be played. So it serves no purpose and can make
// it harder to read.
- adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(activity.get()), true, false);
+ adapter = new FeedItemlistAdapter(getActivity(), itemAccess,
+ new DefaultActionButtonCallback(getActivity()), true, false);
setListAdapter(adapter);
}
setListShown(true);
@@ -244,7 +243,11 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public FeedItem getItem(int position) {
- return (playbackHistory != null) ? playbackHistory.get(position) : null;
+ if (playbackHistory != null && 0 <= position && position < playbackHistory.size()) {
+ return playbackHistory.get(position);
+ } else {
+ return null;
+ }
}
};
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
index 46bbfd13c..b3f6c3534 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -10,6 +10,7 @@ import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
+import android.support.v7.widget.SimpleItemAnimator;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.LayoutInflater;
@@ -67,6 +68,7 @@ public class QueueFragment extends Fragment {
public static final String TAG = "QueueFragment";
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
+ EventDistributor.UNREAD_ITEMS_UPDATE | // sent when playback position is reset
EventDistributor.PLAYER_STATUS_UPDATE;
private TextView infoBar;
@@ -179,13 +181,15 @@ public class QueueFragment extends Fragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (update.feedIds.length > 0) {
- if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
- getActivity().supportInvalidateOptionsMenu();
- }
- } else if (update.mediaIds.length > 0) {
- if (recyclerAdapter != null) {
- recyclerAdapter.notifyDataSetChanged();
+ if (isUpdatingFeeds != update.feedIds.length > 0) {
+ getActivity().supportInvalidateOptionsMenu();
+ }
+ if (recyclerAdapter != null && update.mediaIds.length > 0) {
+ for (long mediaId : update.mediaIds) {
+ int pos = FeedItemUtil.indexOfItemWithMediaId(queue, mediaId);
+ if (pos >= 0) {
+ recyclerAdapter.notifyItemChanged(pos);
+ }
}
}
}
@@ -232,6 +236,9 @@ public class QueueFragment extends Fragment {
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if(!isAdded()) {
+ return;
+ }
super.onCreateOptionsMenu(menu, inflater);
if (queue != null) {
inflater.inflate(R.menu.queue, menu);
@@ -265,10 +272,17 @@ public class QueueFragment extends Fragment {
if (!super.onOptionsItemSelected(item)) {
switch (item.getItemId()) {
case R.id.queue_lock:
- boolean locked = !UserPreferences.isQueueLocked();
- UserPreferences.setQueueLocked(locked);
+ boolean newLockState = !UserPreferences.isQueueLocked();
+ UserPreferences.setQueueLocked(newLockState);
getActivity().supportInvalidateOptionsMenu();
- recyclerAdapter.setLocked(locked);
+ recyclerAdapter.setLocked(newLockState);
+ if (newLockState) {
+ Snackbar.make(getActivity().findViewById(R.id.content), R.string
+ .queue_locked, Snackbar.LENGTH_SHORT).show();
+ } else {
+ Snackbar.make(getActivity().findViewById(R.id.content), R.string
+ .queue_unlocked, Snackbar.LENGTH_SHORT).show();
+ }
return true;
case R.id.refresh_item:
List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
@@ -363,6 +377,10 @@ public class QueueFragment extends Fragment {
View root = inflater.inflate(R.layout.queue_fragment, container, false);
infoBar = (TextView) root.findViewById(R.id.info_bar);
recyclerView = (RecyclerView) root.findViewById(R.id.recyclerView);
+ RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator();
+ if (animator instanceof SimpleItemAnimator) {
+ ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
+ }
layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
@@ -500,7 +518,7 @@ public class QueueFragment extends Fragment {
@Override
public FeedItem getItem(int position) {
- if(queue != null && position < queue.size()) {
+ if (queue != null && 0 <= position && position < queue.size()) {
return queue.get(position);
}
return null;
@@ -550,6 +568,20 @@ public class QueueFragment extends Fragment {
public LongList getQueueIds() {
return queue != null ? LongList.of(FeedItemUtil.getIds(queue)) : new LongList(0);
}
+
+ @Override
+ public LongList getFavoritesIds() {
+ LongList favoritesIds = new LongList();
+ if(queue == null) {
+ return favoritesIds;
+ }
+ for(FeedItem item : queue) {
+ if(item.isTagged(FeedItem.TAG_FAVORITE)) {
+ favoritesIds.add(item.getId());
+ }
+ }
+ return favoritesIds;
+ }
};
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
index d81d18640..ba526edb3 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
@@ -83,7 +83,11 @@ public class RunningDownloadsFragment extends ListFragment {
@Override
public Downloader getItem(int position) {
- return (downloaderList != null) ? downloaderList.get(position) : null;
+ if (downloaderList != null && 0 <= position && position < downloaderList.size()) {
+ return downloaderList.get(position);
+ } else {
+ return null;
+ }
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
index 43354ad28..dbd18163c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -4,7 +4,7 @@ import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.view.MenuItemCompat;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.Menu;
@@ -116,7 +116,7 @@ public class SearchFragment extends ListFragment {
final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
lv.setPadding(0, vertPadding, 0, vertPadding);
- ((ActionBarActivity) getActivity()).getSupportActionBar().setTitle(R.string.search_label);
+ ((AppCompatActivity) getActivity()).getSupportActionBar().setTitle(R.string.search_label);
viewCreated = true;
if (itemsLoaded) {
onFragmentLoaded();
@@ -192,7 +192,11 @@ public class SearchFragment extends ListFragment {
@Override
public SearchResult getItem(int position) {
- return (searchResults != null) ? searchResults.get(position) : null;
+ if (searchResults != null && 0 <= position && position < searchResults.size()) {
+ return searchResults.get(position);
+ } else {
+ return null;
+ }
}
};
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
index e2450f03d..d39829260 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
@@ -45,7 +45,7 @@ public class TagFragment extends PodcastListFragment {
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- ((MainActivity) getActivity()).getMainActivtyActionBar().setTitle(tag.getTitle());
+ ((MainActivity) getActivity()).getSupportActionBar().setTitle(tag.getTitle());
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
index 5bd567a2f..338f02e61 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
@@ -80,7 +80,7 @@ public class TagListFragment extends ListFragment {
@Override
public void onResume() {
super.onResume();
- ((MainActivity) getActivity()).getMainActivtyActionBar().setTitle(R.string.add_feed_label);
+ ((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.add_feed_label);
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
index 0197cc88c..58fe8afbf 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -57,22 +57,25 @@ public class FeedItemMenuHandler {
* @param queueAccess Used for testing if the queue contains the selected item
* @return Returns true if selectedItem is not null.
*/
- public static boolean onPrepareMenu(Context context, MenuInterface mi, FeedItem selectedItem,
- boolean showExtendedMenu, LongList queueAccess) {
+ public static boolean onPrepareMenu(MenuInterface mi,
+ FeedItem selectedItem,
+ boolean showExtendedMenu,
+ LongList queueAccess,
+ LongList favorites) {
if (selectedItem == null) {
return false;
}
boolean hasMedia = selectedItem.getMedia() != null;
- boolean isPlaying = hasMedia
- && selectedItem.getState() == FeedItem.State.PLAYING;
-
- FeedItem.State state = selectedItem.getState();
+ boolean isPlaying = hasMedia && selectedItem.getState() == FeedItem.State.PLAYING;
if (!isPlaying) {
mi.setItemVisibility(R.id.skip_episode_item, false);
}
- boolean isInQueue = selectedItem.isTagged(FeedItem.TAG_QUEUE);
+ boolean isInQueue = false;
+ if(queueAccess != null) {
+ isInQueue = queueAccess.contains(selectedItem.getId());
+ }
if(queueAccess == null || queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId()) {
mi.setItemVisibility(R.id.move_to_top_item, false);
}
@@ -123,7 +126,7 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.support_item, false);
}
- boolean isFavorite = selectedItem.isTagged(FeedItem.TAG_FAVORITE);
+ boolean isFavorite = favorites != null && favorites.contains(selectedItem.getId());
mi.setItemVisibility(R.id.add_to_favorites_item, !isFavorite);
mi.setItemVisibility(R.id.remove_from_favorites_item, isFavorite);
@@ -137,9 +140,13 @@ public class FeedItemMenuHandler {
* @param excludeIds Menu item that should be excluded
* @return true if selectedItem is not null.
*/
- public static boolean onPrepareMenu(Context context, MenuInterface mi, FeedItem selectedItem,
- boolean showExtendedMenu, LongList queueAccess, int... excludeIds) {
- boolean rc = onPrepareMenu(context, mi, selectedItem, showExtendedMenu, queueAccess);
+ public static boolean onPrepareMenu(MenuInterface mi,
+ FeedItem selectedItem,
+ boolean showExtendedMenu,
+ LongList queueAccess,
+ LongList favorites,
+ int... excludeIds) {
+ boolean rc = onPrepareMenu(mi, selectedItem, showExtendedMenu, queueAccess, favorites);
if (rc && excludeIds != null) {
for (int id : excludeIds) {
mi.setItemVisibility(id, false);
@@ -178,7 +185,7 @@ public class FeedItemMenuHandler {
case R.id.mark_unread_item:
selectedItem.setPlayed(false);
DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, false);
- if(GpodnetPreferences.loggedIn()) {
+ if(GpodnetPreferences.loggedIn() && selectedItem.getMedia() != null) {
GpodnetEpisodeAction actionNew = new GpodnetEpisodeAction.Builder(selectedItem, Action.NEW)
.currentDeviceId()
.currentTimestamp()
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 84da32a40..38030f4ea 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -5,15 +5,17 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AlertDialog;
+import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
@@ -61,13 +63,13 @@ 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.hide_items:
- showHideDialog(context, selectedFeed);
+ case R.id.filter_items:
+ showFilterDialog(context, selectedFeed);
break;
case R.id.mark_all_read_item:
ConfirmationDialog conDialog = new ConfirmationDialog(context,
@@ -108,38 +110,38 @@ public class FeedMenuHandler {
return true;
}
- private static void showHideDialog(final Context context, final Feed feed) {
-
- final String[] items = context.getResources().getStringArray(R.array.episode_hide_options);
- final String[] values = context.getResources().getStringArray(R.array.episode_hide_values);
+ private static void showFilterDialog(final Context context, final Feed feed) {
+ final String[] items = context.getResources().getStringArray(R.array.episode_filter_options);
+ final String[] values = context.getResources().getStringArray(R.array.episode_filter_values);
final boolean[] checkedItems = new boolean[items.length];
- final List<String> hidden = new ArrayList<String>(Arrays.asList(feed.getItemFilter().getValues()));
+ final Set<String> filter = new HashSet<>(Arrays.asList(feed.getItemFilter().getValues()));
+ Iterator<String> it = filter.iterator();
+ while(it.hasNext()) {
+ // make sure we have no empty strings in the filter list
+ if(TextUtils.isEmpty(it.next())) {
+ it.remove();
+ }
+ }
for(int i=0; i < values.length; i++) {
String value = values[i];
- if(hidden.contains(value)) {
+ if(filter.contains(value)) {
checkedItems[i] = true;
}
}
AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.hide_episodes_title);
- builder.setMultiChoiceItems(items, checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which, boolean isChecked) {
- if (isChecked) {
- hidden.add(values[which]);
- } else {
- hidden.remove(values[which]);
- }
+ builder.setTitle(R.string.filter);
+ builder.setMultiChoiceItems(items, checkedItems, (dialog, which, isChecked) -> {
+ if (isChecked) {
+ filter.add(values[which]);
+ } else {
+ filter.remove(values[which]);
}
});
- builder.setPositiveButton(R.string.confirm_label, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- feed.setHiddenItemProperties(hidden.toArray(new String[hidden.size()]));
- DBWriter.setFeedItemsFilter(feed.getId(), hidden);
- }
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ feed.setItemFilter(filter.toArray(new String[filter.size()]));
+ DBWriter.setFeedItemsFilter(feed.getId(), filter);
});
builder.setNegativeButton(R.string.cancel_label, null);
builder.create().show();
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
index cfc540fd6..0d2ff8a75 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
@@ -34,10 +34,10 @@ public class MenuItemUtils extends de.danoeh.antennapod.core.menuhandler.MenuIte
TypedArray ta = context.obtainStyledAttributes(lockIcons);
if (UserPreferences.isQueueLocked()) {
queueLock.setTitle(de.danoeh.antennapod.R.string.unlock_queue);
- queueLock.setIcon(ta.getDrawable(1));
+ queueLock.setIcon(ta.getDrawable(0));
} else {
queueLock.setTitle(de.danoeh.antennapod.R.string.lock_queue);
- queueLock.setIcon(ta.getDrawable(0));
+ queueLock.setIcon(ta.getDrawable(1));
}
}
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 94f5d822e..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;
@@ -30,10 +32,15 @@ import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
+import org.apache.commons.lang3.ArrayUtils;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.CrashReportWriter;
import de.danoeh.antennapod.R;
@@ -80,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())
@@ -116,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)
@@ -180,88 +189,68 @@ 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(new Preference.OnPreferenceChangeListener() {
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- if (newValue instanceof Boolean) {
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled((Boolean) newValue);
- setSelectedNetworksEnabled((Boolean) newValue && UserPreferences.isEnableAutodownloadWifiFilter());
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY).setEnabled((Boolean) newValue);
- }
- return true;
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL).setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ if (newValue instanceof Boolean) {
+ boolean enabled = (Boolean) newValue;
+ ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE).setEnabled(enabled);
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY).setEnabled(enabled);
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled(enabled);
+ setSelectedNetworksEnabled(enabled && UserPreferences.isEnableAutodownloadWifiFilter());
}
+ return true;
});
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)
@@ -294,111 +283,87 @@ 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");
- String to[] = { "Martin.Fietz@gmail.com" };
- emailIntent .putExtra(Intent.EXTRA_EMAIL, to);
+ emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"Martin.Fietz@gmail.com"});
+ emailIntent.putExtra(Intent.EXTRA_SUBJECT, "AntennaPod Crash Report");
+ emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe what you were doing when the app crashed");
// the attachment
- emailIntent .putExtra(Intent.EXTRA_STREAM, Uri.fromFile(CrashReportWriter.getFile()));
- // the mail subject
- emailIntent .putExtra(Intent.EXTRA_SUBJECT, "AntennaPod Crash Report");
+ emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(CrashReportWriter.getFile()));
String intentTitle = ui.getActivity().getString(R.string.send_email);
ui.getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle));
return true;
@@ -411,6 +376,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
public void onResume() {
checkItemVisibility();
+ setUpdateIntervalText();
setParallelDownloadsText(UserPreferences.getParallelDownloads());
setEpisodeCacheSizeText(UserPreferences.getEpisodeCacheSize());
setDataFolderText();
@@ -423,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()) {
@@ -503,15 +474,19 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
final Resources res = ui.getActivity().getResources();
ListPreference pref = (ListPreference) ui.findPreference(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS);
- String[] values = res.getStringArray(
- R.array.smart_mark_as_played_values);
+ String[] values = res.getStringArray(R.array.smart_mark_as_played_values);
String[] entries = new String[values.length];
for (int x = 0; x < values.length; x++) {
if(x == 0) {
entries[x] = res.getString(R.string.pref_smart_mark_as_played_disabled);
} else {
Integer v = Integer.parseInt(values[x]);
- entries[x] = res.getQuantityString(R.plurals.time_seconds_quantified, v, v);
+ if(v < 60) {
+ entries[x] = res.getQuantityString(R.plurals.time_seconds_quantified, v, v);
+ } else {
+ v /= 60;
+ entries[x] = res.getQuantityString(R.plurals.time_minutes_quantified, v, v);
+ }
}
}
pref.setEntries(entries);
@@ -527,21 +502,17 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
@SuppressWarnings("deprecation")
private void checkItemVisibility() {
-
boolean hasFlattrToken = FlattrUtils.hasToken();
-
ui.findPreference(PreferenceController.PREF_FLATTR_SETTINGS).setEnabled(FlattrUtils.hasAPICredentials());
ui.findPreference(PreferenceController.PREF_FLATTR_AUTH).setEnabled(!hasFlattrToken);
ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setEnabled(hasFlattrToken);
ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS).setEnabled(hasFlattrToken);
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER)
- .setEnabled(UserPreferences.isEnableAutodownload());
- setSelectedNetworksEnabled(UserPreferences.isEnableAutodownload()
- && UserPreferences.isEnableAutodownloadWifiFilter());
-
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY)
- .setEnabled(UserPreferences.isEnableAutodownload());
+ boolean autoDownload = UserPreferences.isEnableAutodownload();
+ ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE).setEnabled(autoDownload);
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY).setEnabled(autoDownload);
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled(autoDownload);
+ setSelectedNetworksEnabled(autoDownload && UserPreferences.isEnableAutodownloadWifiFilter());
ui.findPreference("prefSendCrashReport").setEnabled(CrashReportWriter.getFile().exists());
@@ -553,6 +524,32 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
}
}
+ private void setUpdateIntervalText() {
+ Context context = ui.getActivity().getApplicationContext();
+ String val;
+ long interval = UserPreferences.getUpdateInterval();
+ if(interval > 0) {
+ int hours = (int) TimeUnit.MILLISECONDS.toHours(interval);
+ String hoursStr = context.getResources().getQuantityString(R.plurals.time_hours_quantified, hours, hours);
+ val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_every), hoursStr);
+ } else {
+ int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
+ if(timeOfDay.length == 2) {
+ Calendar cal = new GregorianCalendar();
+ cal.set(Calendar.HOUR_OF_DAY, timeOfDay[0]);
+ cal.set(Calendar.MINUTE, timeOfDay[1]);
+ String timeOfDayStr = DateFormat.getTimeFormat(context).format(cal.getTime());
+ val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_at),
+ timeOfDayStr);
+ } else {
+ val = context.getString(R.string.pref_smart_mark_as_played_disabled);
+ }
+ }
+ String summary = context.getString(R.string.pref_autoUpdateIntervallOrTime_sum) + "\n"
+ + String.format(context.getString(R.string.pref_current_value), val);
+ ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL).setSummary(summary);
+ }
+
private void setParallelDownloadsText(int downloads) {
final Resources res = ui.getActivity().getResources();
@@ -598,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
@@ -673,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) -> {
@@ -683,46 +675,78 @@ 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();
- String dataFolder = UserPreferences.getDataFolder(null).getAbsolutePath();
+ File dataFolder = UserPreferences.getDataFolder(null);
+ if(dataFolder == null) {
+ new MaterialDialog.Builder(ui.getActivity())
+ .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(context, null);
- String[] folders = new String[mediaDirs.length];
- CharSequence[] choices = new CharSequence[mediaDirs.length];
+ List<String> folders = new ArrayList<>(mediaDirs.length);
+ List<CharSequence> choices = new ArrayList<>(mediaDirs.length);
for(int i=0; i < mediaDirs.length; i++) {
- String path = folders[i] = mediaDirs[i].getAbsolutePath();
- if(dataFolder.equals(path)) {
+ 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) {
- choices[i] = path.substring(0, index);
+ choice = path.substring(0, index);
} else {
- choices[i] = path;
+ choice = path;
}
- long bytes = StorageUtils.getFreeSpaceAvailable();
+ long bytes = StorageUtils.getFreeSpaceAvailable(path);
String freeSpace = String.format(context.getString(R.string.free_space_label),
Converter.byteToString(bytes));
- choices[i] = Html.fromHtml("<html><small>" + choices[i]
- + " [" + freeSpace + "]" + "</small></html>");
+ choices.add(Html.fromHtml("<html><small>" + choice
+ + " [" + freeSpace + "]" + "</small></html>"));
+ }
+ if(choices.size() == 0) {
+ new MaterialDialog.Builder(ui.getActivity())
+ .title(R.string.error_label)
+ .content(R.string.external_storage_error_msg)
+ .neutralText(android.R.string.ok)
+ .show();
+ return;
}
MaterialDialog dialog = new MaterialDialog.Builder(ui.getActivity())
.title(R.string.choose_data_directory)
.content(R.string.choose_data_directory_message)
- .items(choices)
+ .items(choices.toArray(new CharSequence[choices.size()]))
.itemsCallbackSingleChoice(selectedIndex, (dialog1, itemView, which, text) -> {
- String folder = folders[which];
+ String folder = folders.get(which);
Log.d(TAG, "data folder: " + folder);
UserPreferences.setDataFolder(folder);
setDataFolderText();
@@ -734,6 +758,8 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
dialog.show();
}
+ // UPDATE TIME/INTERVAL DIALOG
+
private void showUpdateIntervalTimePreferencesDialog() {
final Context context = ui.getActivity();
@@ -743,47 +769,48 @@ 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);
- builder.setSingleChoiceItems(entries, -1, (dialog1, which) -> {
- int hours = Integer.valueOf(values[which]);
- UserPreferences.setUpdateInterval(hours);
- dialog1.dismiss();
- });
- 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");
UserPreferences.setUpdateTimeOfDay(selectedHourOfDay, selectedMinute);
+ 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);
- }
+ 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/preferences/SwitchCompatPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/SwitchCompatPreference.java
new file mode 100644
index 000000000..10c11b88e
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/SwitchCompatPreference.java
@@ -0,0 +1,37 @@
+package de.danoeh.antennapod.preferences;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.preference.CheckBoxPreference;
+import android.util.AttributeSet;
+
+import de.danoeh.antennapod.R;
+
+public class SwitchCompatPreference extends CheckBoxPreference {
+
+ public SwitchCompatPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public SwitchCompatPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ public SwitchCompatPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public SwitchCompatPreference(Context context) {
+ super(context);
+ init();
+ }
+
+ private void init() {
+ setWidgetLayoutResource(R.layout.preference_switch_layout);
+ }
+} \ No newline at end of file
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/java/de/danoeh/antennapod/service/PlayerWidgetService.java b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
index d61a189c2..323060f81 100644
--- a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
+++ b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
@@ -128,7 +128,7 @@ public class PlayerWidgetService extends Service {
views.setTextViewText(R.id.txtvTitle, media.getEpisodeTitle());
- String progressString = getProgressString(media);
+ String progressString = getProgressString();
if (progressString != null) {
views.setViewVisibility(R.id.txtvProgress, View.VISIBLE);
views.setTextViewText(R.id.txtvProgress, progressString);
@@ -181,9 +181,9 @@ public class PlayerWidgetService extends Service {
return PendingIntent.getBroadcast(this, 0, startingIntent, 0);
}
- private String getProgressString(Playable media) {
- int position = media.getPosition();
- int duration = media.getDuration();
+ private String getProgressString() {
+ int position = playbackService.getCurrentPosition();
+ int duration = playbackService.getDuration();
if (position > 0 && duration > 0) {
return Converter.getDurationStringLong(position) + " / "
+ Converter.getDurationStringLong(duration);
diff --git a/app/src/main/res/drawable/shadow.xml b/app/src/main/res/drawable/shadow.xml
new file mode 100644
index 000000000..fc5110e0b
--- /dev/null
+++ b/app/src/main/res/drawable/shadow.xml
@@ -0,0 +1,7 @@
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:startColor="@android:color/transparent"
+ android:endColor="#40000000"
+ android:angle="90" />
+</shape>
diff --git a/app/src/main/res/layout/audio_controls.xml b/app/src/main/res/layout/audio_controls.xml
new file mode 100644
index 000000000..852b6e922
--- /dev/null
+++ b/app/src/main/res/layout/audio_controls.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
+ android:text="@string/playback_speed"
+ android:textStyle="bold"/>
+
+ <TextView
+ android:id="@+id/txtvPlaybackSpeed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="16dp"
+ android:text="1.00x"/>
+
+ </LinearLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-12dp">
+
+ <Button
+ android:id="@+id/butDecSpeed"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:layout_alignParentLeft="true"
+ android:gravity="center"
+ android:text="-"
+ android:textStyle="bold"
+ android:textColor="@color/status_progress"
+ android:textSize="24sp"
+ android:background="@drawable/borderless_button_dark"/>
+
+ <Button
+ android:id="@+id/butIncSpeed"
+ android:layout_width="32dp"
+ android:layout_height="32dp"
+ android:minWidth="0dp"
+ android:layout_alignParentRight="true"
+ android:gravity="center"
+ android:text="+"
+ android:textStyle="bold"
+ android:textColor="@color/status_progress"
+ android:textSize="24sp"
+ android:background="@drawable/borderless_button_dark"/>
+
+ <SeekBar
+ android:id="@+id/playback_speed"
+ android:layout_width="match_parent"
+ android:layout_height="32dp"
+ android:layout_toRightOf="@id/butDecSpeed"
+ android:layout_toLeftOf="@id/butIncSpeed"
+ android:layout_centerVertical="true"
+ android:max="40"/>
+
+ </RelativeLayout>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
+ android:text="@string/volume"
+ android:textStyle="bold"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-12dp"
+ android:layout_marginLeft="8dp"
+ android:orientation="horizontal"
+ android:gravity="center">
+
+ <TextView
+ android:id="@+id/txtvLeft"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/left_short" />
+
+ <SeekBar
+ android:id="@+id/volume_left"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:max="100" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginLeft="8dp"
+ android:orientation="horizontal"
+ android:gravity="center">
+
+ <TextView
+ android:id="@+id/txtvRight"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/right_short" />
+
+ <SeekBar
+ android:id="@+id/volume_right"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:max="100"/>
+
+ </LinearLayout>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="12dp"
+ style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
+ android:text="@string/audio_effects"
+ android:textStyle="bold"/>
+
+ <CheckBox
+ android:id="@+id/stereo_to_mono"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="-12dp"
+ android:text="@string/stereo_to_mono" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/audioplayer_activity.xml b/app/src/main/res/layout/audioplayer_activity.xml
index 379028c8e..fb4f995a2 100644
--- a/app/src/main/res/layout/audioplayer_activity.xml
+++ b/app/src/main/res/layout/audioplayer_activity.xml
@@ -1,219 +1,219 @@
<?xml version="1.0" encoding="utf-8"?>
-<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.v4.widget.DrawerLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:background="@android:color/holo_red_dark">
- <LinearLayout
+ <RelativeLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+ android:layout_height="match_parent">
+
+ <android.support.design.widget.AppBarLayout
+ android:id="@+id/appBar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
- tools:background="@android:color/darker_gray">
+ tools:background="@android:color/darker_gray"/>
- <LinearLayout
+ <com.viewpagerindicator.CirclePageIndicator
+ android:id="@+id/page_indicator"
+ android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="horizontal"
- android:paddingLeft="8dp"
- android:paddingRight="8dp">
-
- <TextView
- android:id="@+id/txtvTitle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_weight="1"
- android:ellipsize="end"
- android:gravity="left"
- android:maxLines="2"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="16sp"
- tools:text="Audio title"
- tools:background="@android:color/holo_green_dark" />
-
- <ImageButton
- android:id="@+id/butCover"
- android:layout_width="32dp"
- android:layout_height="32dp"
- android:layout_gravity="center_vertical"
- android:layout_marginLeft="8dp"
- android:background="?attr/selectableItemBackground"
- android:contentDescription="@string/show_cover_label"
- android:gravity="right"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark" />
-
-
- </LinearLayout>
- </android.support.v7.widget.Toolbar>
-
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/content"
+ android:layout_marginTop="-12dp"
+ android:layout_marginBottom="4dp"
+ android:background="@android:color/transparent"
+ app:fillColor="?android:attr/textColorSecondary"
+ app:strokeColor="?android:attr/textColorSecondary"
+ app:radius="4dp" />
+
+ </android.support.design.widget.AppBarLayout>
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/playtime_layout"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="?attr/non_transparent_background"
- android:foreground="?android:windowContentOverlay"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:layout_alignParentBottom="true"
+ android:background="?attr/overlay_drawable"
android:orientation="vertical">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/txtvPosition"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true"
+ android:layout_marginLeft="8dp"
+ android:text="@string/position_default_label"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="@dimen/text_size_micro"
+ tools:background="@android:color/holo_green_dark" />
+
+ <TextView
+ android:id="@+id/txtvLength"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:layout_marginRight="8dp"
+ android:text="@string/position_default_label"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="@dimen/text_size_micro"
+ tools:background="@android:color/holo_green_dark" />
+
+ <SeekBar
+ android:id="@+id/sbPosition"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_toLeftOf="@id/txtvLength"
+ android:layout_toRightOf="@id/txtvPosition"
+ android:max="500"
+ tools:background="@android:color/holo_green_dark" />
+
+ </RelativeLayout>
+
<RelativeLayout
android:id="@+id/player_control"
android:layout_width="match_parent"
- android:layout_height="@dimen/audioplayer_playercontrols_length"
- android:layout_alignParentBottom="true"
+ android:layout_height="wrap_content"
+ android:paddingTop="4dp"
+ android:paddingBottom="8dp"
android:background="?attr/overlay_background"
tools:background="@android:color/holo_purple">
<ImageButton
android:id="@+id/butPlay"
android:layout_width="@dimen/audioplayer_playercontrols_length"
- android:layout_height="match_parent"
+ android:layout_height="@dimen/audioplayer_playercontrols_length"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
android:layout_centerHorizontal="true"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/pause_label"
android:src="?attr/av_pause"
+ android:scaleType="fitCenter"
tools:src="@drawable/ic_pause_white_36dp"
tools:background="@android:color/holo_green_dark" />
<ImageButton
android:id="@+id/butRev"
android:layout_width="@dimen/audioplayer_playercontrols_length"
- android:layout_height="match_parent"
+ android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_toLeftOf="@id/butPlay"
+ android:layout_marginLeft="16dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/rewind_label"
android:src="?attr/av_rew_big"
+ android:scaleType="fitCenter"
tools:src="@drawable/ic_fast_rewind_white_36dp"
tools:background="@android:color/holo_blue_dark" />
<TextView
android:id="@+id/txtvRev"
android:layout_width="wrap_content"
- android:layout_height="32dp"
- android:layout_alignTop="@id/butRev"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/butRev"
android:layout_alignLeft="@id/butRev"
android:layout_alignRight="@id/butRev"
+ android:layout_marginTop="-8dp"
android:gravity="center"
android:text="30"
- android:textSize="8dp"
+ android:textSize="10sp"
+ android:textColor="?android:attr/textColorSecondary"
android:clickable="false"/>
+ <Button
+ android:id="@+id/butPlaybackSpeed"
+ android:layout_width="@dimen/audioplayer_playercontrols_length"
+ android:layout_height="@dimen/audioplayer_playercontrols_length"
+ android:layout_toLeftOf="@id/butRev"
+ android:background="?attr/selectableItemBackground"
+ android:contentDescription="@string/set_playback_speed_label"
+ android:src="?attr/av_fast_forward"
+ android:textSize="@dimen/text_size_medium"
+ android:textAllCaps="false"
+ tools:background="@android:color/holo_green_dark" />
+
<ImageButton
android:id="@+id/butFF"
android:layout_width="@dimen/audioplayer_playercontrols_length"
- android:layout_height="match_parent"
+ android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_toRightOf="@id/butPlay"
+ android:layout_marginRight="16dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/fast_forward_label"
android:src="?attr/av_ff_big"
+ android:scaleType="fitCenter"
tools:src="@drawable/ic_fast_forward_white_36dp"
tools:background="@android:color/holo_blue_dark" />
<TextView
android:id="@+id/txtvFF"
android:layout_width="wrap_content"
- android:layout_height="32dp"
- android:layout_alignTop="@id/butFF"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/butFF"
android:layout_alignLeft="@id/butFF"
android:layout_alignRight="@id/butFF"
+ android:layout_marginTop="-8dp"
android:gravity="center"
android:text="30"
- android:textSize="8dp"
+ android:textSize="10sp"
+ android:textColor="?android:attr/textColorSecondary"
android:clickable="false"/>
- <Button
- android:id="@+id/butPlaybackSpeed"
- android:layout_width="@dimen/audioplayer_playercontrols_length"
- android:layout_height="match_parent"
- android:layout_toRightOf="@id/butFF"
- android:background="?attr/selectableItemBackground"
- android:contentDescription="@string/set_playback_speed_label"
- android:src="?attr/av_fast_forward"
- android:textSize="@dimen/text_size_medium"
- android:visibility="gone"
- tools:background="@android:color/holo_green_dark" />
-
<ImageButton
- android:id="@+id/butNavChaptersShownotes"
+ android:id="@+id/butSkip"
android:layout_width="@dimen/audioplayer_playercontrols_length"
- android:layout_height="match_parent"
- android:layout_toLeftOf="@id/butRev"
+ android:layout_height="@dimen/audioplayer_playercontrols_length"
+ android:layout_toRightOf="@id/butFF"
android:background="?attr/selectableItemBackground"
- android:scaleType="centerInside"
- android:src="@drawable/ic_toc_white_36dp"
- tools:background="@android:color/holo_green_dark" />
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/playtime_layout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_above="@id/player_control"
- android:layout_alignParentLeft="true"
- android:background="?attr/overlay_drawable">
-
- <TextView
- android:id="@+id/txtvPosition"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_centerVertical="true"
- android:layout_marginLeft="8dp"
- android:layout_marginTop="16dp"
- android:text="@string/position_default_label"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="@dimen/text_size_micro"
- tools:background="@android:color/holo_green_dark" />
-
- <TextView
- android:id="@+id/txtvLength"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:layout_centerVertical="true"
- android:layout_marginRight="8dp"
- android:layout_marginTop="16dp"
- android:text="@string/position_default_label"
- android:textColor="?android:attr/textColorSecondary"
- android:textSize="@dimen/text_size_micro"
- tools:background="@android:color/holo_green_dark" />
-
- <SeekBar
- android:id="@+id/sbPosition"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:layout_marginTop="16dp"
- android:layout_toLeftOf="@id/txtvLength"
- android:layout_toRightOf="@id/txtvPosition"
- android:max="500"
+ android:scaleType="fitCenter"
+ android:src="?attr/av_skip_big"
+ android:contentDescription="@string/skip_episode_label"
+ tools:src="@drawable/ic_skip_white_36dp"
tools:background="@android:color/holo_green_dark" />
</RelativeLayout>
- <FrameLayout
- android:id="@+id/contentView"
- android:layout_width="match_parent"
- android:layout_height="0px"
- android:layout_above="@id/playtime_layout"
- android:layout_alignParentTop="true"
- android:foreground="?android:windowContentOverlay"
- tools:background="@android:color/holo_orange_light" />
+ </LinearLayout>
- </RelativeLayout>
+ <android.support.v4.view.ViewPager
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_above="@id/playtime_layout"
+ android:layout_below="@id/appBar"
+ android:foreground="?android:windowContentOverlay"
+ tools:background="@android:color/holo_orange_light" />
+ <View
+ android:id="@+id/shadow"
+ android:layout_width="match_parent"
+ android:layout_height="4dp"
+ android:layout_below="@id/appBar"
+ android:background="@drawable/shadow" />
- </LinearLayout>
+ </RelativeLayout>
<include layout="@layout/nav_list" />
diff --git a/app/src/main/res/layout/choose_speed_dialog.xml b/app/src/main/res/layout/choose_speed_dialog.xml
deleted file mode 100644
index 1b461c77e..000000000
--- a/app/src/main/res/layout/choose_speed_dialog.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <android.support.v7.widget.CardView
- xmlns:card_view="http://schemas.android.com/apk/res-auto"
- android:id="@+id/card_view"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_margin="16dp"
- card_view:cardElevation="12dp"
- card_view:cardCornerRadius="4dp"
- card_view:cardUseCompatPadding="true"
- card_view:cardBackgroundColor="?attr/overlay_background">
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:gravity="center">
-
- <TextView
- android:id="@+id/txtvSelectedSpeed"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:textSize="22sp"
- android:textStyle="bold"/>
-
- <SeekBar
- android:id="@+id/sbSelectSpeed"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"/>
-
- </LinearLayout>
-
- </android.support.v7.widget.CardView>
-
-</LinearLayout>
diff --git a/app/src/main/res/layout/cover_fragment.xml b/app/src/main/res/layout/cover_fragment.xml
index 4bbdeae06..9ad1ff9c6 100644
--- a/app/src/main/res/layout/cover_fragment.xml
+++ b/app/src/main/res/layout/cover_fragment.xml
@@ -1,19 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+
+<android.support.percent.PercentRelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/cover_fragment_root"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
+ android:layout_height="match_parent">
<ImageView
android:id="@+id/imgvCover"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_centerInParent="true"
android:contentDescription="@string/cover_label"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:adjustViewBounds="true"
android:scaleType="fitCenter"
+ app:layout_aspectRatio="100%"
+ app:layout_widthPercent="82%"
tools:src="@android:drawable/sym_def_app_icon" />
-</RelativeLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:layout_above="@id/imgvCover">
+
+ <TextView
+ android:id="@+id/txtvPodcastTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:gravity="center"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:text="Podcast"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:layout_below="@id/imgvCover">
+
+ <TextView
+ android:id="@+id/txtvEpisodeTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_marginStart="8dp"
+ android:layout_marginEnd="8dp"
+ android:gravity="center"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:text="Episode"
+ android:textColor="?android:attr/textColorPrimary" />
+
+ </LinearLayout>
+
+</android.support.percent.PercentRelativeLayout>
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/external_player_fragment.xml b/app/src/main/res/layout/external_player_fragment.xml
index ef83baa17..ac55b4c40 100644
--- a/app/src/main/res/layout/external_player_fragment.xml
+++ b/app/src/main/res/layout/external_player_fragment.xml
@@ -1,69 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/fragmentLayout"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone"
- android:background="?attr/colorPrimary"
- tools:visibility="visible"
- tools:background="@android:color/darker_gray">
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/fragmentLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="gone">
- <View
+ <RelativeLayout
android:layout_width="match_parent"
- android:layout_height="2dp"
- android:background="@color/holo_blue_light"/>
+ android:layout_height="@dimen/external_player_height">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ <ImageView
+ android:id="@+id/imgvCover"
+ android:contentDescription="@string/cover_label"
+ android:layout_width="@dimen/external_player_height"
+ android:layout_height="@dimen/external_player_height"
+ android:adjustViewBounds="true"
+ android:cropToPadding="true"
+ android:scaleType="centerCrop"
+ tools:src="@drawable/ic_drag_vertical_white_48dp"
+ tools:background="@android:color/holo_green_dark"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"/>
- <RelativeLayout
- android:id="@+id/layoutInfo"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:background="?attr/selectableItemBackground">
+ <ProgressBar
+ android:id="@+id/episodeProgress"
+ android:layout_width="match_parent"
+ android:layout_height="4dp"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
+ android:layout_alignParentTop="true"
+ style="?attr/progressBarTheme"
+ android:indeterminate="false"
+ tools:progress="100"/>
- <ImageView
- android:id="@+id/imgvCover"
- android:contentDescription="@string/cover_label"
- android:layout_width="@dimen/external_player_height"
- android:layout_height="@dimen/external_player_height"
- android:layout_alignParentLeft="true"
- android:padding="4dp"
- android:adjustViewBounds="true"
- android:cropToPadding="true"
- android:scaleType="fitXY"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark" />
+ <TextView
+ android:id="@+id/txtvTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="26dp"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_toLeftOf="@id/butPlay"
+ android:layout_toStartOf="@id/butPlay"
+ style="@style/Base.TextAppearance.AppCompat.Body1"
+ android:ellipsize="end"
+ android:maxLines="1"
+ tools:text="Episode title that is too long and will cause the text to wrap"/>
- <TextView
- android:id="@+id/txtvTitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:layout_marginTop="8dp"
- android:layout_toRightOf="@id/imgvCover"
- android:ellipsize="end"
- android:maxLines="2"
- android:textSize="18sp"
- android:fontFamily="sans-serif-light"
- tools:text="Playback item title"
- tools:background="@android:color/holo_green_dark"/>
- </RelativeLayout>
+ <TextView
+ android:id="@+id/txtvAuthor"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/episodeProgress"
+ android:layout_marginTop="26dp"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_toLeftOf="@id/butPlay"
+ android:layout_toStartOf="@id/butPlay"
+ style="@style/TextAppearance.AppCompat.Body1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:ellipsize="end"
+ android:maxLines="1"
+ tools:text="Episode author that is too long and will cause the text to wrap"/>
<ImageButton
android:id="@+id/butPlay"
- android:contentDescription="@string/pause_label"
- android:layout_width="@dimen/external_player_height"
- android:layout_height="@dimen/external_player_height"
- android:background="?attr/selectableItemBackground"
- tools:src="@drawable/ic_play_arrow_white_36dp"
- tools:background="@android:color/holo_green_dark"/>
- </LinearLayout>
+ android:layout_width="52dp"
+ android:layout_height="52dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@id/episodeProgress"
+ android:layout_centerVertical="true"
+ android:contentDescription="@string/pause_label"
+ android:background="?attr/selectableItemBackground"
+ tools:src="@drawable/ic_play_arrow_white_36dp"/>
+
+ </RelativeLayout>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml
index 2b49b4b35..4b545e3cc 100644
--- a/app/src/main/res/layout/feedinfo.xml
+++ b/app/src/main/res/layout/feedinfo.xml
@@ -150,17 +150,6 @@
android:text="@string/podcast_settings_label"
android:layout_marginTop="8dp"/>
- <CheckBox
- android:id="@+id/cbxAutoDownload"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="@string/auto_download_label"
- android:enabled="false"
- android:textColor="?android:attr/textColorPrimary"
- tools:background="@android:color/holo_red_light"
- android:checked="false" />
-
<android.support.v7.widget.GridLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -192,6 +181,17 @@
android:clickable="true" />
</android.support.v7.widget.GridLayout>
+ <CheckBox
+ android:id="@+id/cbxKeepUpdated"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/keep_updated"
+ android:enabled="true"
+ android:textColor="?android:attr/textColorPrimary"
+ tools:background="@android:color/holo_red_light"
+ android:checked="true" />
+
<TextView
android:id="@+id/txtvAuthentication"
android:layout_width="match_parent"
@@ -266,6 +266,77 @@
</android.support.v7.widget.GridLayout>
<TextView
+ android:id="@+id/txtvAutoDownloadSettings"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/auto_download_settings_label"
+ android:textSize="@dimen/text_size_medium"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <CheckBox
+ android:id="@+id/cbxAutoDownload"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/auto_download_label"
+ android:enabled="false"
+ android:textColor="?android:attr/textColorPrimary"
+ tools:background="@android:color/holo_red_light"
+ android:checked="false" />
+
+ <TextView
+ android:id="@+id/txtvEpisodeFilters"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/episode_filters_label"
+ android:textSize="@dimen/text_size_medium"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/txtvEpisodeFiltersDescription"
+ android:text="@string/episode_filters_description"
+ android:textSize="@dimen/text_size_small"
+ android:textColor="?android:attr/textColorPrimary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"/>
+
+ <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/radio_filter_group"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:orientation="horizontal">
+ <RadioButton android:id="@+id/radio_filter_include"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/episode_filters_include"
+ android:onClick="onRadioButtonClicked"/>
+ <RadioButton android:id="@+id/radio_filter_exclude"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/episode_filters_exclude"
+ android:onClick="onRadioButtonClicked"/>
+ </RadioGroup>
+
+ <EditText
+ android:id="@+id/etxtEpisodeFilterText"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:lines="8"
+ android:minLines="1"
+ android:maxLines="20"
+ android:scrollbars="vertical"
+ android:hint="@string/episode_filters_hint"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
+
+ <TextView
style="@style/AntennaPod.TextView.Heading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml
index 5e1b580d2..c18e5fd6c 100644
--- a/app/src/main/res/layout/feeditem_fragment.xml
+++ b/app/src/main/res/layout/feeditem_fragment.xml
@@ -1,13 +1,141 @@
<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <LinearLayout
+ android:id="@+id/header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:background="?attr/colorPrimary"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ tools:background="@android:color/darker_gray">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="16dp"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp">
+
+ <ImageView
+ android:id="@+id/imgvCover"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:contentDescription="@string/cover_label"
+ android:gravity="center_vertical"
+ tools:src="@drawable/ic_stat_antenna_default"
+ tools:background="@android:color/holo_green_dark" />
+
+ <TextView
+ android:id="@+id/txtvTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="16dp"
+ android:layout_alignTop="@id/imgvCover"
+ android:layout_toRightOf="@id/imgvCover"
+ android:includeFontPadding="false"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:ellipsize="end"
+ android:maxLines="5"
+ tools:text="Podcast title"
+ tools:background="@android:color/holo_green_dark" />
+
+ <TextView
+ android:id="@+id/txtvDuration"
+ style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_below="@id/txtvTitle"
+ android:layout_marginLeft="16dp"
+ tools:text="00:42:23"
+ tools:background="@android:color/holo_green_dark"/>
+
+ <TextView
+ android:id="@+id/txtvPublished"
+ style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:layout_below="@id/txtvTitle"
+ android:layout_marginLeft="8dp"
+ tools:text="Jan\n23"
+ tools:background="@android:color/holo_green_dark" />
+
+ </RelativeLayout>
+
+ <ProgressBar
+ android:id="@+id/progbarDownload"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="16dp"
+ android:layout_marginRight="16dp"
+ android:orientation="horizontal"
+ tools:background="@android:color/holo_blue_bright">
+
+ <com.joanzapata.iconify.widget.IconButton
+ android:id="@+id/butAction1"
+ android:layout_width="0dp"
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginRight="8dp"
+ android:layout_weight="1"
+ android:background="?attr/selectableItemBackground"
+ android:ellipsize="end"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/text_size_small"
+ tools:text="Button 1"
+ tools:background="@android:color/holo_red_light" />
+
+ <com.joanzapata.iconify.widget.IconButton
+ android:id="@+id/butAction2"
+ android:layout_width="0dp"
+ android:layout_height="48dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="8dp"
+ android:layout_weight="1"
+ android:background="?attr/selectableItemBackground"
+ android:ellipsize="end"
+ android:gravity="center"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/text_size_small"
+ tools:text="Button 2"
+ tools:background="@android:color/holo_orange_dark" />
+
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="2dp"
+ android:background="@color/light_gray"/>
+
+ </LinearLayout>
<WebView
android:id="@+id/webvDescription"
android:layout_width="match_parent"
+ android:layout_below="@id/header"
android:layout_height="match_parent"
android:foreground="?android:windowContentOverlay" />
@@ -22,4 +150,4 @@
android:layout_gravity="center"
android:indeterminate="true" />
</FrameLayout>
-</FrameLayout> \ No newline at end of file
+</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/feeditem_fragment_header.xml b/app/src/main/res/layout/feeditem_fragment_header.xml
deleted file mode 100644
index 2534dddbe..000000000
--- a/app/src/main/res/layout/feeditem_fragment_header.xml
+++ /dev/null
@@ -1,136 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:background="?attr/colorPrimary"
- android:gravity="center_horizontal"
- android:orientation="vertical"
- tools:background="@android:color/darker_gray">
-
-
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:orientation="horizontal"
- android:paddingBottom="0dp">
-
- <ImageView
- android:id="@+id/imgvCover"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="8dp"
- android:layout_marginTop="16dp"
- android:contentDescription="@string/cover_label"
- android:gravity="center_vertical"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark" />
-
-
- <ImageButton
- android:id="@+id/butMoreActions"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:background="?attr/selectableItemBackground"
- android:contentDescription="@string/butAction_label"
- android:paddingTop="4dp"
- android:src="?attr/ic_action_overflow"
- tools:src="@drawable/ic_info_white_24dp"
- tools:background="@android:color/holo_green_dark" />
-
- <TextView
- android:id="@+id/txtvTitle"
- style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_marginBottom="8dp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="8dp"
- android:layout_marginTop="16dp"
- android:layout_toLeftOf="@id/butMoreActions"
- android:layout_toRightOf="@id/imgvCover"
- android:maxLines="5"
- tools:text="Podcast title"
- tools:background="@android:color/holo_green_dark" />
-
- <TextView
- android:id="@+id/txtvDuration"
- style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@id/imgvCover"
- android:layout_below="@id/txtvTitle"
- android:layout_marginLeft="16dp"
- tools:text="00:42:23"
- tools:background="@android:color/holo_green_dark"/>
-
- <TextView
- android:id="@+id/txtvPublished"
- style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toLeftOf="@id/butMoreActions"
- android:layout_marginRight="8dp"
- tools:text="Jan 23"
- tools:background="@android:color/holo_green_dark"
- android:layout_below="@+id/txtvTitle"/>
-
- </RelativeLayout>
-
- <ProgressBar
- android:id="@+id/progbarDownload"
- style="?android:attr/progressBarStyleHorizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:visibility="invisible" />
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="?attr/actionBarSize"
- android:layout_marginRight="8dp"
- android:orientation="horizontal"
- tools:background="@android:color/holo_blue_bright">
-
- <Button
- android:id="@+id/butAction1"
- android:layout_width="0dp"
- android:layout_height="48dp"
- android:layout_gravity="center_vertical"
- android:layout_marginRight="8dp"
- android:layout_weight="1"
- android:background="?attr/selectableItemBackground"
- android:ellipsize="end"
- android:paddingTop="4dp"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_small"
- tools:text="Button 1"
- tools:background="@android:color/holo_red_light" />
-
- <Button
- android:id="@+id/butAction2"
- android:layout_width="0dp"
- android:layout_height="48dp"
- android:layout_gravity="center_vertical"
- android:layout_marginLeft="8dp"
- android:layout_weight="1"
- android:background="?attr/selectableItemBackground"
- android:ellipsize="end"
- android:paddingTop="4dp"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_small"
- tools:text="Button 2"
- tools:background="@android:color/holo_orange_dark" />
-
- </LinearLayout>
-
-
-</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_itunes_search.xml b/app/src/main/res/layout/fragment_itunes_search.xml
index e57c59554..0cc13f74c 100644
--- a/app/src/main/res/layout/fragment_itunes_search.xml
+++ b/app/src/main/res/layout/fragment_itunes_search.xml
@@ -1,26 +1,64 @@
-<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"
-tools:context="de.danoeh.antennapod.activity.ITunesSearchActivity">
-<android.support.v7.widget.SearchView
- android:id="@+id/itunes_search_view"
- android:layout_height="wrap_content"
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- />
-<GridView
- android:id="@+id/gridView"
- android:layout_below="@id/itunes_search_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipToPadding="false"
- android:columnWidth="400dp"
- android:gravity="center"
- android:horizontalSpacing="8dp"
- android:numColumns="auto_fit"
- android:paddingBottom="@dimen/list_vertical_padding"
- android:paddingTop="@dimen/list_vertical_padding"
- android:stretchMode="columnWidth"
- android:verticalSpacing="8dp"
- tools:listitem="@layout/gpodnet_podcast_listitem" />
+ android:layout_height="match_parent">
+
+ <GridView
+ android:id="@+id/gridView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:columnWidth="400dp"
+ android:gravity="center"
+ android:horizontalSpacing="8dp"
+ android:numColumns="auto_fit"
+ android:paddingBottom="@dimen/list_vertical_padding"
+ android:paddingTop="@dimen/list_vertical_padding"
+ android:stretchMode="columnWidth"
+ android:verticalSpacing="8dp"
+ tools:listitem="@layout/gpodnet_podcast_listitem" />
+
+ <TextView
+ android:id="@id/android:empty"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_centerInParent="true"
+ android:gravity="center"
+ android:visibility="gone"
+ android:text="@string/search_status_no_results" />
+
+ <ProgressBar
+ android:id="@+id/progressBar"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:indeterminateOnly="true"
+ android:visibility="gone"/>
+
+ <TextView
+ android:id="@+id/txtvError"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:layout_margin="16dp"
+ android:textAlignment="center"
+ android:textSize="@dimen/text_size_small"
+ android:visibility="gone"
+ tools:visibility="visible"
+ tools:text="Error message"
+ tools:background="@android:color/holo_red_light" />
+
+ <Button
+ android:id="@+id/butRetry"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/txtvError"
+ android:layout_centerHorizontal="true"
+ android:layout_margin="16dp"
+ android:text="@string/retry_label"
+ android:visibility="gone"
+ tools:visibility="visible"
+ tools:background="@android:color/holo_red_light" />
+
</RelativeLayout>
diff --git a/app/src/main/res/layout/gpodnet_podcast_listitem.xml b/app/src/main/res/layout/gpodnet_podcast_listitem.xml
index 84c6c280e..bbe8e65d6 100644
--- a/app/src/main/res/layout/gpodnet_podcast_listitem.xml
+++ b/app/src/main/res/layout/gpodnet_podcast_listitem.xml
@@ -3,7 +3,11 @@
<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="@dimen/listitem_threeline_height"
+ android:layout_height="wrap_content"
+ android:paddingTop="8dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingBottom="8dp"
tools:background="@android:color/darker_gray">
<ImageView
@@ -11,11 +15,8 @@
android:layout_width="@dimen/thumbnail_length_itemlist"
android:layout_height="@dimen/thumbnail_length_itemlist"
android:layout_alignParentLeft="true"
- android:layout_centerVertical="true"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_alignParentTop="true"
android:layout_marginRight="8dp"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:adjustViewBounds="true"
android:contentDescription="@string/cover_label"
android:cropToPadding="true"
@@ -29,7 +30,6 @@
android:layout_height="wrap_content"
android:layout_alignTop="@id/txtvTitle"
android:layout_alignParentRight="true"
- android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
android:orientation="horizontal">
<ImageView
@@ -52,15 +52,14 @@
<TextView
android:id="@+id/txtvTitle"
- style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
+ style="@style/AntennaPod.TextView.ListItemPrimaryTitle2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginBottom="@dimen/list_vertical_padding"
- android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
android:layout_toRightOf="@id/imgvCover"
android:layout_toLeftOf="@id/subscribers_container"
android:layout_alignTop="@id/imgvCover"
- android:lines="1"
+ android:maxLines="2"
+ android:includeFontPadding="false"
tools:text="Title"
tools:background="@android:color/holo_green_dark" />
@@ -69,7 +68,6 @@
style="android:style/TextAppearance.Small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
android:layout_toRightOf="@id/imgvCover"
android:layout_below="@id/txtvTitle"
android:textSize="14sp"
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/itunes_podcast_listitem.xml b/app/src/main/res/layout/itunes_podcast_listitem.xml
index 41b1f495f..1e6e5a836 100644
--- a/app/src/main/res/layout/itunes_podcast_listitem.xml
+++ b/app/src/main/res/layout/itunes_podcast_listitem.xml
@@ -1,38 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
-<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="@dimen/listitem_threeline_height"
-tools:background="@android:color/darker_gray">
-
-<ImageView
- android:id="@+id/imgvCover"
- android:layout_width="@dimen/thumbnail_length_itemlist"
- android:layout_height="@dimen/thumbnail_length_itemlist"
- android:layout_alignParentLeft="true"
- android:layout_centerVertical="true"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
- android:layout_marginRight="8dp"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
- android:adjustViewBounds="true"
- android:contentDescription="@string/cover_label"
- android:cropToPadding="true"
- android:scaleType="fitXY"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark" />
-
-<TextView
- android:id="@+id/txtvTitle"
- style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
+<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="wrap_content"
- android:layout_centerVertical="true"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
- android:layout_toRightOf="@id/imgvCover"
- android:maxLines="1"
- tools:text="Podcast title"
- tools:background="@android:color/holo_green_dark" />
+ android:paddingTop="8dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingBottom="8dp"
+ tools:background="@android:color/darker_gray">
+
+ <ImageView
+ android:id="@+id/imgvCover"
+ android:layout_width="@dimen/thumbnail_length_itemlist"
+ android:layout_height="@dimen/thumbnail_length_itemlist"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:layout_marginRight="8dp"
+ android:adjustViewBounds="true"
+ android:contentDescription="@string/cover_label"
+ android:cropToPadding="true"
+ android:scaleType="fitXY"
+ tools:background="@android:color/holo_green_dark"
+ tools:src="@drawable/ic_stat_antenna_default" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_centerVertical="true"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/txtvTitle"
+ style="@style/AntennaPod.TextView.ListItemPrimaryTitle2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ tools:background="@android:color/holo_green_dark"
+ tools:text="Podcast title" />
+
+ <TextView
+ android:id="@+id/txtvUrl"
+ style="android:style/TextAppearance.Small"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="14sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:ellipsize="middle"
+ android:maxLines="2"
+ tools:text="http://www.example.com/feed"
+ tools:background="@android:color/holo_green_dark"/>
+
+ </LinearLayout>
+
</RelativeLayout>
diff --git a/app/src/main/res/layout/main.xml b/app/src/main/res/layout/main.xml
index 46be5bc5f..c05132b42 100644
--- a/app/src/main/res/layout/main.xml
+++ b/app/src/main/res/layout/main.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<android.support.v4.widget.DrawerLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
@@ -10,7 +11,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
<FrameLayout
android:id="@+id/playerFragment"
android:layout_width="match_parent"
@@ -27,6 +27,14 @@
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
tools:background="@android:color/holo_blue_dark" />
+
+ <View
+ android:id="@+id/shadow"
+ android:layout_width="match_parent"
+ android:layout_height="4dp"
+ android:layout_below="@id/toolbar"
+ android:background="@drawable/shadow" />
+
<FrameLayout
android:id="@+id/main_view"
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/nav_feedlistitem.xml b/app/src/main/res/layout/nav_feedlistitem.xml
index f0cbb56c1..18b5255aa 100644
--- a/app/src/main/res/layout/nav_feedlistitem.xml
+++ b/app/src/main/res/layout/nav_feedlistitem.xml
@@ -17,7 +17,7 @@
android:layout_centerVertical="true"
android:adjustViewBounds="true"
android:cropToPadding="true"
- android:scaleType="fitXY"
+ android:scaleType="centerCrop"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:layout_marginLeft="@dimen/listitem_icon_leftpadding"
diff --git a/app/src/main/res/layout/new_episodes_listitem.xml b/app/src/main/res/layout/new_episodes_listitem.xml
index cde1b4aa6..0f1e873f3 100644
--- a/app/src/main/res/layout/new_episodes_listitem.xml
+++ b/app/src/main/res/layout/new_episodes_listitem.xml
@@ -33,8 +33,8 @@
<ImageView
android:id="@+id/imgvCover"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
+ android:layout_height="64dp"
+ android:layout_width="64dp"
android:layout_alignLeft="@id/txtvPlaceholder"
android:layout_alignTop="@id/txtvPlaceholder"
android:layout_alignRight="@id/txtvPlaceholder"
diff --git a/app/src/main/res/layout/preference_switch_layout.xml b/app/src/main/res/layout/preference_switch_layout.xml
new file mode 100644
index 000000000..54fa74061
--- /dev/null
+++ b/app/src/main/res/layout/preference_switch_layout.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v7.widget.SwitchCompat
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@null"
+ android:clickable="false"
+ android:focusable="false" /> \ No newline at end of file
diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml
index d950f11d2..8de80e355 100644
--- a/app/src/main/res/layout/queue_listitem.xml
+++ b/app/src/main/res/layout/queue_listitem.xml
@@ -12,17 +12,19 @@
android:layout_height="@dimen/listitem_threeline_height"
android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
- android:paddingLeft="8dp"
android:gravity="center_vertical"
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
tools:background="@android:color/darker_gray" >
<ImageView
android:id="@+id/drag_handle"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_width="104dp"
+ android:layout_height="64dp"
android:layout_marginLeft="-16dp"
- android:layout_marginRight="-20dp"
- android:gravity="center"
+ android:layout_marginStart="-16dp"
+ android:layout_marginRight="-72dp"
+ android:layout_marginEnd="-72dp"
android:contentDescription="@string/drag_handle_content_description"
android:scaleType="fitXY"
android:src="?attr/dragview_background"
@@ -32,26 +34,22 @@
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="8dp">
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp">
<TextView
android:id="@+id/txtvPlaceholder"
android:layout_width="@dimen/thumbnail_length_queue_item"
android:layout_height="@dimen/thumbnail_length_queue_item"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
- android:layout_gravity="center_vertical"
+ android:layout_centerVertical="true"
android:gravity="center"
android:background="@color/light_gray"
android:maxLines="3"
android:ellipsize="end"/>
<ImageView
android:id="@+id/imgvCover"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignLeft="@id/txtvPlaceholder"
- android:layout_alignTop="@id/txtvPlaceholder"
- android:layout_alignRight="@id/txtvPlaceholder"
- android:layout_alignBottom="@id/txtvPlaceholder"
+ android:layout_width="@dimen/thumbnail_length_queue_item"
+ android:layout_height="@dimen/thumbnail_length_queue_item"
+ android:layout_centerVertical="true"
android:contentDescription="@string/cover_label"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark"/>
@@ -77,7 +75,7 @@
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="8dp"
- android:gravity="right|bottom"
+ android:gravity="right|top"
android:text="Feb\n12"
tools:background="@android:color/holo_blue_light" />
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/layout/videoplayer_activity.xml b/app/src/main/res/layout/videoplayer_activity.xml
index 766cd6e04..2e1097fb8 100644
--- a/app/src/main/res/layout/videoplayer_activity.xml
+++ b/app/src/main/res/layout/videoplayer_activity.xml
@@ -19,14 +19,41 @@
android:indeterminateOnly="true"
android:visibility="invisible" />
- <ImageButton
- android:id="@+id/butPlay"
+ <LinearLayout
+ android:id="@+id/controls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:background="@drawable/overlay_button_circle_background"
- android:contentDescription="@string/pause_label"
- android:src="@drawable/ic_av_pause_circle_outline_80dp" />
+ android:orientation="horizontal">
+
+ <ImageButton
+ android:id="@+id/butRev"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:background="@drawable/overlay_button_circle_background"
+ android:contentDescription="@string/pause_label"
+ android:src="@drawable/ic_av_rewind_80dp" />
+
+ <ImageButton
+ android:id="@+id/butPlay"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:background="@drawable/overlay_button_circle_background"
+ android:contentDescription="@string/pause_label"
+ android:src="@drawable/ic_av_pause_circle_outline_80dp" />
+
+ <ImageButton
+ android:id="@+id/butFF"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:background="@drawable/overlay_button_circle_background"
+ android:contentDescription="@string/pause_label"
+ android:src="@drawable/ic_av_fast_forward_80dp" />
+
+ </LinearLayout>
<LinearLayout
android:id="@+id/overlay"
@@ -77,7 +104,9 @@
android:layout_toLeftOf="@+id/txtvLength"
android:layout_toRightOf="@+id/txtvPosition"
android:max="500" />
+
</RelativeLayout>
+
</LinearLayout>
-</FrameLayout> \ No newline at end of file
+</FrameLayout>
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 88bef8d1f..3df88046d 100644
--- a/app/src/main/res/menu/episodes_apply_action_options.xml
+++ b/app/src/main/res/menu/episodes_apply_action_options.xml
@@ -4,11 +4,10 @@
<item
android:id="@+id/sort"
+ android:icon="?attr/ic_sort"
android:title="@string/sort"
app:showAsAction="always">
<menu>
- <item android:id="@+id/sort_title"
- android:title="@string/sort_title"/>
<item android:id="@+id/sort_title_a_z"
android:title="@string/sort_title_a_z"/>
<item android:id="@+id/sort_title_z_a"
@@ -25,18 +24,12 @@
</item>
<item
- android:id="@+id/select_toggle"
- android:title="@string/select_all_label"
- app:showAsAction="always"/>
-
- <item
android:id="@+id/select_options"
- android:title="@string/all_label"
+ android:icon="?attr/ic_filter"
+ android:title="@string/filter"
app:showAsAction="always">
<menu>
- <item android:id="@+id/select_label"
- android:title="@string/select_label"/>
<item android:id="@+id/check_all"
android:title="@string/all_label"/>
<item android:id="@+id/check_none"
@@ -49,7 +42,15 @@
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>
+ <item
+ android:id="@+id/select_toggle"
+ android:title="@string/select_all_label"
+ app:showAsAction="always"/>
</menu>
diff --git a/app/src/main/res/menu/feeditemlist_context.xml b/app/src/main/res/menu/feeditemlist_context.xml
index 7b10e5cce..271cb96a7 100644
--- a/app/src/main/res/menu/feeditemlist_context.xml
+++ b/app/src/main/res/menu/feeditemlist_context.xml
@@ -26,6 +26,15 @@
android:title="@string/remove_from_queue_label" />
<item
+ android:id="@+id/add_to_favorites_item"
+ android:menuCategory="container"
+ android:title="@string/add_to_favorite_label" />
+ <item
+ android:id="@+id/remove_from_favorites_item"
+ android:menuCategory="container"
+ android:title="@string/remove_from_favorite_label" />
+
+ <item
android:id="@+id/reset_position"
android:menuCategory="container"
android:title="@string/reset_position" />
diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml
index 54b90f5c6..ed03c08d6 100644
--- a/app/src/main/res/menu/feedlist.xml
+++ b/app/src/main/res/menu/feedlist.xml
@@ -3,10 +3,10 @@
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
- android:id="@+id/hide_items"
+ android:id="@+id/filter_items"
android:icon="?attr/ic_filter"
android:menuCategory="container"
- android:title="@string/hide_episodes_title"
+ android:title="@string/filter"
custom:showAsAction="always">
</item>
<item
diff --git a/app/src/main/res/menu/itunes_search.xml b/app/src/main/res/menu/itunes_search.xml
new file mode 100644
index 000000000..88fa36a4a
--- /dev/null
+++ b/app/src/main/res/menu/itunes_search.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/action_search"
+ android:icon="?attr/action_search"
+ custom:showAsAction="collapseActionView|ifRoom"
+ custom:actionViewClass="android.support.v7.widget.SearchView"
+ android:title="@string/search_label"/>
+
+</menu>
diff --git a/app/src/main/res/menu/mediaplayer.xml b/app/src/main/res/menu/mediaplayer.xml
index 053e68552..c4086bf5e 100644
--- a/app/src/main/res/menu/mediaplayer.xml
+++ b/app/src/main/res/menu/mediaplayer.xml
@@ -3,22 +3,35 @@
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
+ android:id="@+id/add_to_favorites_item"
+ android:icon="?attr/ic_fav"
+ android:title="@string/add_to_favorite_label"
+ custom:showAsAction="always">
+ </item>
+ <item
+ android:id="@+id/remove_from_favorites_item"
+ android:icon="?attr/ic_unfav"
+ android:title="@string/remove_from_favorite_label"
+ custom:showAsAction="always">
+ </item>
+
+ <item
android:id="@+id/disable_sleeptimer_item"
- android:icon="?attr/device_access_time"
+ android:icon="?attr/ic_sleep_off"
custom:showAsAction="always"
android:title="@string/sleep_timer_label">
</item>
<item
android:id="@+id/set_sleeptimer_item"
- custom:showAsAction="collapseActionView"
+ android:icon="?attr/ic_sleep"
+ custom:showAsAction="always"
android:title="@string/set_sleeptimer_label">
</item>
<item
- android:id="@+id/skip_episode_item"
- custom:showAsAction="collapseActionView"
- android:title="@string/skip_episode_label"
- android:visible="true">
+ android:id="@+id/audio_controls"
+ android:title="@string/audio_controls"
+ custom:showAsAction="always">
</item>
<item
@@ -30,7 +43,9 @@
</item>
<item
android:id="@+id/share_item"
+ android:icon="?attr/social_share"
android:menuCategory="container"
+ custom:showAsAction="ifRoom"
android:title="@string/share_label">
<menu>
<item
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index e9e9d48f7..3ecd79b1e 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
<PreferenceCategory android:title="@string/user_interface_label">
<com.afollestad.materialdialogs.prefs.MaterialListPreference
@@ -8,7 +10,8 @@
android:title="@string/pref_set_theme_title"
android:key="prefTheme"
android:summary="@string/pref_set_theme_sum"
- android:defaultValue="0"/>
+ android:defaultValue="0"
+ app:useStockLayout="true"/>
<PreferenceScreen
android:key="prefDrawerSettings"
android:summary="@string/pref_nav_drawer_sum"
@@ -23,34 +26,36 @@
android:title="@string/pref_nav_drawer_feed_order_title"
android:key="prefDrawerFeedOrder"
android:summary="@string/pref_nav_drawer_feed_order_sum"
- android:defaultValue="0"/>
+ android:defaultValue="0"
+ app:useStockLayout="true"/>
<com.afollestad.materialdialogs.prefs.MaterialListPreference
android:entryValues="@array/nav_drawer_feed_counter_values"
android:entries="@array/nav_drawer_feed_counter_options"
android:title="@string/pref_nav_drawer_feed_counter_title"
android:key="prefDrawerFeedIndicator"
android:summary="@string/pref_nav_drawer_feed_counter_sum"
- android:defaultValue="0"/>
+ android:defaultValue="0"
+ app:useStockLayout="true"/>
</PreferenceScreen>
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="false"
android:enabled="true"
android:key="prefExpandNotify"
android:summary="@string/pref_expandNotify_sum"
android:title="@string/pref_expandNotify_title"/>
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="true"
android:enabled="true"
android:key="prefPersistNotify"
android:summary="@string/pref_persistNotify_sum"
android:title="@string/pref_persistNotify_title"/>
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="true"
android:enabled="true"
android:key="prefLockscreenBackground"
android:summary="@string/pref_lockscreen_background_sum"
android:title="@string/pref_lockscreen_background_title"/>
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="true"
android:enabled="true"
android:key="prefShowDownloadReport"
@@ -59,7 +64,7 @@
</PreferenceCategory>
<PreferenceCategory android:title="@string/queue_label">
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="false"
android:enabled="true"
android:key="prefQueueAddToFront"
@@ -69,45 +74,51 @@
</PreferenceCategory>
<PreferenceCategory android:title="@string/playback_pref">
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
+ android:defaultValue="true"
+ android:enabled="false"
+ android:key="prefSonic"
+ android:summary="@string/pref_sonic_message"
+ android:title="@string/pref_sonic_title"/>
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="true"
android:enabled="true"
android:key="prefPauseOnHeadsetDisconnect"
android:summary="@string/pref_pauseOnDisconnect_sum"
android:title="@string/pref_pauseOnHeadsetDisconnect_title"/>
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="true"
android:enabled="true"
android:dependency="prefPauseOnHeadsetDisconnect"
android:key="prefUnpauseOnHeadsetReconnect"
android:summary="@string/pref_unpauseOnHeadsetReconnect_sum"
android:title="@string/pref_unpauseOnHeadsetReconnect_title"/>
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="false"
android:enabled="true"
android:dependency="prefPauseOnHeadsetDisconnect"
android:key="prefUnpauseOnBluetoothReconnect"
android:summary="@string/pref_unpauseOnBluetoothReconnect_sum"
android:title="@string/pref_unpauseOnBluetoothReconnect_title"/>
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="false"
android:enabled="true"
android:key="prefHardwareForwardButtonSkips"
android:summary="@string/pref_hardwareForwardButtonSkips_sum"
android:title="@string/pref_hardwareForwardButtonSkips_title"/>
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="true"
android:enabled="true"
android:key="prefFollowQueue"
android:summary="@string/pref_followQueue_sum"
android:title="@string/pref_followQueue_title"/>
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="true"
android:enabled="true"
android:key="prefSkipKeepsEpisode"
android:summary="@string/pref_skip_keeps_episodes_sum"
android:title="@string/pref_skip_keeps_episodes_title"/>
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="false"
android:enabled="true"
android:key="prefAutoDelete"
@@ -119,19 +130,20 @@
android:entryValues="@array/smart_mark_as_played_values"
android:key="prefSmartMarkAsPlayedSecs"
android:summary="@string/pref_smart_mark_as_played_sum"
- android:title="@string/pref_smart_mark_as_played_title"/>
+ android:title="@string/pref_smart_mark_as_played_title"
+ app:useStockLayout="true"/>
<Preference
android:key="prefPlaybackSpeedLauncher"
android:summary="@string/pref_playback_speed_sum"
android:title="@string/pref_playback_speed_title" />
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="false"
android:enabled="true"
android:key="prefPauseForFocusLoss"
android:summary="@string/pref_pausePlaybackForFocusLoss_sum"
android:title="@string/pref_pausePlaybackForFocusLoss_title" />
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="true"
android:enabled="true"
android:key="prefResumeAfterCall"
@@ -144,46 +156,47 @@
android:key="prefAutoUpdateIntervall"
android:summary="@string/pref_autoUpdateIntervallOrTime_sum"
android:title="@string/pref_autoUpdateIntervallOrTime_title"/>
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="false"
android:enabled="true"
android:key="prefMobileUpdate"
android:summary="@string/pref_mobileUpdate_sum"
android:title="@string/pref_mobileUpdate_title"/>
-
<com.afollestad.materialdialogs.prefs.MaterialListPreference
android:defaultValue="-1"
android:entries="@array/episode_cleanup_entries"
android:key="prefEpisodeCleanup"
android:title="@string/pref_episode_cleanup_title"
android:summary="@string/pref_episode_cleanup_summary"
- android:entryValues="@array/episode_cleanup_values"/>
-
+ android:entryValues="@array/episode_cleanup_values"
+ app:useStockLayout="true"/>
<com.afollestad.materialdialogs.prefs.MaterialEditTextPreference
android:defaultValue="4"
android:inputType="number"
android:key="prefParallelDownloads"
- android:title="@string/pref_parallel_downloads_title"/>
- <com.afollestad.materialdialogs.prefs.MaterialListPreference
- android:defaultValue="20"
- android:entries="@array/episode_cache_size_entries"
- android:key="prefEpisodeCacheSize"
- android:title="@string/pref_episode_cache_title"
- android:entryValues="@array/episode_cache_size_values"/>
+ android:title="@string/pref_parallel_downloads_title"
+ app:useStockLayout="true"/>
<PreferenceScreen
android:summary="@string/pref_automatic_download_sum"
android:key="prefAutoDownloadSettings"
android:title="@string/pref_automatic_download_title">
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:key="prefEnableAutoDl"
android:title="@string/pref_automatic_download_title"
android:defaultValue="false"/>
- <CheckBoxPreference
+ <com.afollestad.materialdialogs.prefs.MaterialListPreference
+ android:defaultValue="20"
+ android:entries="@array/episode_cache_size_entries"
+ android:key="prefEpisodeCacheSize"
+ android:title="@string/pref_episode_cache_title"
+ android:entryValues="@array/episode_cache_size_values"
+ app:useStockLayout="true"/>
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:key="prefEnableAutoDownloadOnBattery"
android:title="@string/pref_automatic_download_on_battery_title"
android:summary="@string/pref_automatic_download_on_battery_sum"
android:defaultValue="true"/>
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:key="prefEnableAutoDownloadWifiFilter"
android:title="@string/pref_autodl_wifi_filter_title"
android:summary="@string/pref_autodl_wifi_filter_sum"/>
@@ -256,14 +269,5 @@
android:title="@string/crash_report_title"
android:summary="@string/crash_report_sum"/>
</PreferenceCategory>
-
- <PreferenceCategory android:title="@string/experimental_pref">
- <CheckBoxPreference
- android:defaultValue="false"
- android:enabled="false"
- android:key="prefSonic"
- android:summary="@string/pref_sonic_message"
- android:title="@string/pref_sonic_title"/>
- </PreferenceCategory>
-
+
</PreferenceScreen>
diff --git a/app/src/main/templates/about.html b/app/src/main/templates/about.html
index 21599bdb8..222429b17 100644
--- a/app/src/main/templates/about.html
+++ b/app/src/main/templates/about.html
@@ -59,13 +59,13 @@
<h1>Used libraries</h1>
<h2>Apache Commons <a href="http://commons.apache.org/">(Link)</a></h2>
-by The Apache Software Foundation, licensed under the Apache 2.0 license <a href="LICENSE_APACHE_COMMONS.txt">(View)</a>
+by The Apache Software Foundation, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>EventBus <a href="https://github.com/greenrobot/EventBus">(Link)</a></h2>
-by greenrobot, licensed under the Apache 2.0 license <a href="LICENSE_EVENTBUS.txt">(View)</a>
+by greenrobot, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>flattr4j <a href="http://www.shredzone.org/projects/flattr4j/wiki">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_FLATTR4J.txt">(View)</a>
+licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>Glide <a href="https://github.com/bumptech/glide/">(Link)</a></h2>
licensed under the Simplified BSD license <a href="LICENSE_GLIDE.txt">(View)</a>
@@ -86,22 +86,22 @@ by Aidan Michael Follestad, licensed under the MIT License <a href="LICENSE_MATE
by Square, licensed under the Apache 2.0 license <a href="LICENSE_OKHTTP.txt">(View)</a>
<h2>Okio <a href="https://github.com/square/okio">(Link)</a></h2>
-by Square, licensed under the Apache 2.0 license <a href="LICENSE_OKIO.txt">(View)</a>
+by Square, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>Presto Client <a href="http://www.aocate.com/presto/">(Link)</a></h2>
licensed under the Apache 2.0 license <a href="LICENSE_PRESTO.txt">(View)</a>
<h2>RecyclerView-FlexibleDivider <a href="https://github.com/yqritc/RecyclerView-FlexibleDivider">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_FLEXIBLE_DIVIDER.txt">(View)</a>
+licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>RxAndroid <a href="https://github.com/ReactiveX/RxAndroid">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_RX_ANDROID.txt">(View)</a>
+licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>StackBlur <a href="https://github.com/kikoso/android-stackblur">(Link)</a></h2>
-by Enrique L&oacute;pez Ma&ntilde;as, licensed under the Apache 2.0 license <a href="LICENSE_STACKBLUR.txt">(View)</a>
+by Enrique L&oacute;pez Ma&ntilde;as, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
<h2>AntennaPod-AudioPlayer <a href="https://github.com/AntennaPod/AntennaPod-AudioPlayer/">(Link)</a></h2>
-by the AntennaPod team, licensed under the Apache 2.0 license <a href="LICENSE_ANTENNAPOD_AUDIOPLAYER.txt">(View)</a>
+by the AntennaPod team, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
</body>
</html>