summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/androidTest/assets/3sec.mp3bin0 -> 49043 bytes
-rw-r--r--app/src/androidTest/java/de/test/antennapod/AntennaPodTestRunner.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java49
-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.java27
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java133
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java80
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java212
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java115
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java50
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java174
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java216
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java16
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java234
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java64
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java274
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java185
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java360
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java44
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java8
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/RewindAfterPauseUtilTest.java51
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java38
-rw-r--r--app/src/main/AndroidManifest.xml45
-rw-r--r--app/src/main/assets/.gitignore2
-rw-r--r--app/src/main/assets/3sec.mp3bin0 -> 49043 bytes
-rw-r--r--app/src/main/assets/LICENSE.html17
-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_DSLV.txt16
-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_GLIDE.txt94
-rw-r--r--app/src/main/assets/LICENSE_MATERIAL_DIALOGS.txt21
-rw-r--r--app/src/main/assets/LICENSE_OKIO.txt202
-rw-r--r--app/src/main/assets/LICENSE_PICASSO.txt13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/AppConfig.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java52
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java54
-rw-r--r--app/src/main/java/de/danoeh/antennapod/UpdateManager.java88
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java130
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java756
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java249
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java646
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java199
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java425
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java628
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java586
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java88
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java26
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java199
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java39
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java185
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java375
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java197
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java198
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java90
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java44
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java69
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java56
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java178
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java378
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java119
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/DBTasksCallbacksImpl.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java153
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java376
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java31
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java136
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java154
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java138
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java200
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java430
-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.java98
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java80
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java83
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java120
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java121
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java91
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java247
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java601
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java328
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java387
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java128
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java203
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java608
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java73
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java104
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java51
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.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.java124
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java74
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/CustomEditTextPreference.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java448
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/SwitchCompatPreference.java37
-rw-r--r--app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java22
-rw-r--r--app/src/main/java/de/danoeh/antennapod/receiver/PlayerWidget.java95
-rw-r--r--app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java343
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/SubscriptionViewItem.java38
-rw-r--r--app/src/main/res/drawable/shadow.xml7
-rw-r--r--app/src/main/res/layout-v14/authentication_dialog.xml10
-rw-r--r--app/src/main/res/layout-v14/directory_chooser.xml2
-rw-r--r--app/src/main/res/layout-v14/download_authentication_activity.xml12
-rw-r--r--app/src/main/res/layout-v14/time_dialog.xml73
-rw-r--r--app/src/main/res/layout/addfeed.xml53
-rw-r--r--app/src/main/res/layout/all_episodes_fragment.xml34
-rw-r--r--app/src/main/res/layout/audio_controls.xml146
-rw-r--r--app/src/main/res/layout/audioplayer_activity.xml268
-rw-r--r--app/src/main/res/layout/authentication_dialog.xml10
-rw-r--r--app/src/main/res/layout/cover_fragment.xml66
-rw-r--r--app/src/main/res/layout/directory_chooser.xml2
-rw-r--r--app/src/main/res/layout/download_authentication_activity.xml12
-rw-r--r--app/src/main/res/layout/downloadlog_item.xml3
-rw-r--r--app/src/main/res/layout/episodes_apply_action_fragment.xml120
-rw-r--r--app/src/main/res/layout/external_player_fragment.xml136
-rw-r--r--app/src/main/res/layout/feedinfo.xml136
-rw-r--r--app/src/main/res/layout/feeditem_fragment.xml132
-rw-r--r--app/src/main/res/layout/feeditem_fragment_header.xml137
-rw-r--r--app/src/main/res/layout/feeditemlist_header.xml2
-rw-r--r--app/src/main/res/layout/feeditemlist_item.xml9
-rw-r--r--app/src/main/res/layout/fragment_itunes_search.xml86
-rw-r--r--app/src/main/res/layout/gpodnet_podcast_list.xml2
-rw-r--r--app/src/main/res/layout/gpodnet_podcast_listitem.xml20
-rw-r--r--app/src/main/res/layout/gpodnetauth_credentials.xml10
-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.xml43
-rw-r--r--app/src/main/res/layout/new_episodes_fragment.xml67
-rw-r--r--app/src/main/res/layout/new_episodes_listitem.xml69
-rw-r--r--app/src/main/res/layout/opml_import.xml164
-rw-r--r--app/src/main/res/layout/pager_fragment.xml20
-rw-r--r--app/src/main/res/layout/preference_switch_layout.xml9
-rw-r--r--app/src/main/res/layout/queue_fragment.xml64
-rw-r--r--app/src/main/res/layout/queue_listitem.xml64
-rw-r--r--app/src/main/res/layout/subscription_view.xml8
-rw-r--r--app/src/main/res/layout/time_dialog.xml59
-rw-r--r--app/src/main/res/layout/videoplayer_activity.xml41
-rw-r--r--app/src/main/res/menu/allepisodes_context.xml36
-rw-r--r--app/src/main/res/menu/episodes_apply_action_options.xml52
-rw-r--r--app/src/main/res/menu/feedinfo.xml6
-rw-r--r--app/src/main/res/menu/feeditem_options.xml39
-rw-r--r--app/src/main/res/menu/feeditemlist_context.xml37
-rw-r--r--app/src/main/res/menu/feedlist.xml38
-rw-r--r--app/src/main/res/menu/itunes_search.xml13
-rw-r--r--app/src/main/res/menu/mediaplayer.xml56
-rw-r--r--app/src/main/res/menu/nav_feed_context.xml19
-rw-r--r--app/src/main/res/menu/queue_context.xml37
-rw-r--r--app/src/main/res/xml/player_widget_info.xml9
-rw-r--r--app/src/main/res/xml/preferences.xml167
-rw-r--r--app/src/main/templates/about.html66
170 files changed, 10933 insertions, 7648 deletions
diff --git a/app/src/androidTest/assets/3sec.mp3 b/app/src/androidTest/assets/3sec.mp3
new file mode 100644
index 000000000..8ae450d01
--- /dev/null
+++ b/app/src/androidTest/assets/3sec.mp3
Binary files differ
diff --git a/app/src/androidTest/java/de/test/antennapod/AntennaPodTestRunner.java b/app/src/androidTest/java/de/test/antennapod/AntennaPodTestRunner.java
index 24cd6e669..c321e6494 100644
--- a/app/src/androidTest/java/de/test/antennapod/AntennaPodTestRunner.java
+++ b/app/src/androidTest/java/de/test/antennapod/AntennaPodTestRunner.java
@@ -2,6 +2,7 @@ package de.test.antennapod;
import android.test.InstrumentationTestRunner;
import android.test.suitebuilder.TestSuiteBuilder;
+
import junit.framework.TestSuite;
public class AntennaPodTestRunner extends InstrumentationTestRunner {
@@ -13,4 +14,5 @@ public class AntennaPodTestRunner extends InstrumentationTestRunner {
.excludePackages("de.test.antennapod.gpodnet")
.build();
}
+
}
diff --git a/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java b/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java
new file mode 100644
index 000000000..80dded59f
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java
@@ -0,0 +1,49 @@
+package de.test.antennapod.entities;
+
+import android.annotation.SuppressLint;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.test.InstrumentationTestCase;
+
+import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.util.playback.ExternalMedia;
+
+/**
+ * Tests for {@link ExternalMedia} entity.
+ */
+public class ExternalMediaTest extends InstrumentationTestCase {
+
+ private static final int NOT_SET = -1;
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ clearSharedPrefs();
+ }
+
+ @SuppressLint("CommitPrefEdits")
+ private void clearSharedPrefs() {
+ SharedPreferences prefs = getDefaultSharedPrefs();
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.clear();
+ editor.commit();
+ }
+
+ private SharedPreferences getDefaultSharedPrefs() {
+ return PreferenceManager.getDefaultSharedPreferences(getInstrumentation().getTargetContext());
+ }
+
+ public void testSaveCurrentPositionUpdatesPreferences() {
+ final int POSITION = 50;
+ final int LAST_PLAYED_TIME = 1650;
+
+ assertEquals(NOT_SET, getDefaultSharedPrefs().getInt(ExternalMedia.PREF_POSITION, NOT_SET));
+ assertEquals(NOT_SET, getDefaultSharedPrefs().getLong(ExternalMedia.PREF_LAST_PLAYED_TIME, NOT_SET));
+
+ ExternalMedia media = new ExternalMedia("source", MediaType.AUDIO);
+ media.saveCurrentPosition(getDefaultSharedPrefs(), POSITION, LAST_PLAYED_TIME);
+
+ assertEquals(POSITION, getDefaultSharedPrefs().getInt(ExternalMedia.PREF_POSITION, NOT_SET));
+ assertEquals(LAST_PLAYED_TIME, getDefaultSharedPrefs().getLong(ExternalMedia.PREF_LAST_PLAYED_TIME, NOT_SET));
+ }
+}
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 52067c971..5836bb699 100644
--- a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
@@ -2,15 +2,9 @@ package de.test.antennapod.handler;
import android.content.Context;
import android.test.InstrumentationTestCase;
-import de.danoeh.antennapod.core.feed.*;
-import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
-import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException;
-import de.test.antennapod.util.syndication.feedgenerator.AtomGenerator;
-import de.test.antennapod.util.syndication.feedgenerator.FeedGenerator;
-import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
+
import org.xml.sax.SAXException;
-import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -19,6 +13,19 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
+import javax.xml.parsers.ParserConfigurationException;
+
+import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedImage;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
+import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException;
+import de.test.antennapod.util.syndication.feedgenerator.AtomGenerator;
+import de.test.antennapod.util.syndication.feedgenerator.FeedGenerator;
+import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
+
/**
* Tests for FeedHandler
*/
@@ -162,11 +169,11 @@ public class FeedHandlerTest extends InstrumentationTestCase {
for (int i = 0; i < numItems; i++) {
FeedItem item = new FeedItem(0, "item-" + i, "http://example.com/item-" + i,
- "http://example.com/items/" + i, new Date(i*60000), false, feed);
+ "http://example.com/items/" + i, new Date(i*60000), FeedItem.UNPLAYED, feed);
feed.getItems().add(item);
if (withFeedMedia) {
- item.setMedia(new FeedMedia(0, item, 4711, 0, 100, "audio/mp3", null, "http://example.com/media-" + i,
- false, null, 0));
+ item.setMedia(new FeedMedia(0, item, 4711, 0, 1024*1024, "audio/mp3", null, "http://example.com/media-" + i,
+ false, null, 0, 0));
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
index 443fbed7e..4a5883c64 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
@@ -43,7 +43,7 @@ public class HttpDownloaderTest extends InstrumentationTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
- UserPreferences.createInstance(getInstrumentation().getTargetContext());
+ UserPreferences.init(getInstrumentation().getTargetContext());
destDir = getInstrumentation().getTargetContext().getExternalFilesDir(DOWNLOAD_DIR);
assertNotNull(destDir);
assertTrue(destDir.exists());
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
index aac4c245a..d7a170c17 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
@@ -1,17 +1,10 @@
package de.test.antennapod.service.playback;
import android.content.Context;
-import android.media.RemoteControlClient;
import android.test.InstrumentationTestCase;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import de.danoeh.antennapod.core.util.playback.Playable;
-import de.test.antennapod.util.service.download.HTTPBin;
+
import junit.framework.AssertionFailedError;
+
import org.apache.commons.io.IOUtils;
import java.io.File;
@@ -23,6 +16,16 @@ import java.util.Date;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
+import de.danoeh.antennapod.core.service.playback.PlayerStatus;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import de.test.antennapod.util.service.download.HTTPBin;
+
/**
* Test class for PlaybackServiceMediaPlayer
*/
@@ -41,7 +44,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
@Override
protected void tearDown() throws Exception {
super.tearDown();
- PodDBAdapter.deleteDatabase(getInstrumentation().getTargetContext());
+ PodDBAdapter.deleteDatabase();
httpServer.stop();
}
@@ -50,16 +53,16 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
super.setUp();
assertionError = null;
- final Context context = getInstrumentation().getTargetContext();
- context.deleteDatabase(PodDBAdapter.DATABASE_NAME);
- // make sure database is created
- PodDBAdapter adapter = new PodDBAdapter(context);
+ // create new database
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.close();
httpServer = new HTTPBin();
httpServer.start();
+ final Context context = getInstrumentation().getTargetContext();
File cacheDir = context.getExternalFilesDir("testFiles");
if (cacheDir == null)
cacheDir = context.getExternalFilesDir("testFiles");
@@ -113,12 +116,14 @@ 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);
- f.setItems(new ArrayList<FeedItem>());
- FeedItem i = new FeedItem(0, "t", "i", "l", new Date(), false, f);
+ FeedPreferences prefs = new FeedPreferences(f.getId(), false, FeedPreferences.AutoDeleteAction.NO, null, null);
+ f.setPreferences(prefs);
+ f.setItems(new ArrayList<>());
+ FeedItem i = new FeedItem(0, "t", "i", "l", new Date(), FeedItem.UNPLAYED, f);
f.getItems().add(i);
- FeedMedia media = new FeedMedia(0, i, 0, 0, 0, "audio/wav", fileUrl, downloadUrl, fileUrl != null, null, 0);
+ FeedMedia media = new FeedMedia(0, i, 0, 0, 0, "audio/wav", fileUrl, downloadUrl, fileUrl != null, null, 0, 0);
i.setMedia(media);
- PodDBAdapter adapter = new PodDBAdapter(c);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(f);
assertTrue(media.getId() != 0);
@@ -178,14 +183,10 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
PlaybackServiceMediaPlayer psmp = new PlaybackServiceMediaPlayer(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
@@ -251,14 +252,9 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
-
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
PlaybackServiceMediaPlayer psmp = new PlaybackServiceMediaPlayer(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
@@ -328,14 +324,9 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
-
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
PlaybackServiceMediaPlayer psmp = new PlaybackServiceMediaPlayer(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
@@ -406,14 +397,10 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
PlaybackServiceMediaPlayer psmp = new PlaybackServiceMediaPlayer(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
@@ -477,14 +464,10 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
PlaybackServiceMediaPlayer psmp = new PlaybackServiceMediaPlayer(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@@ -549,14 +532,9 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
-
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
PlaybackServiceMediaPlayer psmp = new PlaybackServiceMediaPlayer(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@@ -624,14 +602,9 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
-
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
PlaybackServiceMediaPlayer psmp = new PlaybackServiceMediaPlayer(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@@ -702,14 +675,9 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
-
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
PlaybackServiceMediaPlayer psmp = new PlaybackServiceMediaPlayer(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@@ -745,9 +713,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean onMediaPlayerInfo(int code) {
- return false;
- }
+ public boolean onMediaPlayerInfo(int code) { return false; }
@Override
public boolean onMediaPlayerError(Object inObj, int what, int extra) {
@@ -755,14 +721,9 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
-
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
private void pauseTestSkeleton(final PlayerStatus initialState, final boolean stream, final boolean abandonAudioFocus, final boolean reinit, long timeoutSeconds) throws InterruptedException {
@@ -832,14 +793,9 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
-
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
PlaybackServiceMediaPlayer psmp = new PlaybackServiceMediaPlayer(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@@ -943,14 +899,9 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
-
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
PlaybackServiceMediaPlayer psmp = new PlaybackServiceMediaPlayer(c, callback);
if (initialState == PlayerStatus.PREPARED || initialState == PlayerStatus.PLAYING || initialState == PlayerStatus.PAUSED) {
@@ -1029,14 +980,9 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
-
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
PlaybackServiceMediaPlayer psmp = new PlaybackServiceMediaPlayer(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
@@ -1128,14 +1074,9 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
}
@Override
- public boolean endPlayback(boolean playNextEpisode) {
+ public boolean endPlayback(boolean playNextEpisode, boolean wasSkipped) {
return false;
}
-
- @Override
- public RemoteControlClient getRemoteControlClient() {
- return null;
- }
};
PlaybackServiceMediaPlayer psmp = new PlaybackServiceMediaPlayer(c, callback);
Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
index d74128c2f..f06d2f2a6 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
@@ -12,7 +12,7 @@ import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.QueueEvent;
+import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.playback.Playable;
@@ -26,16 +26,16 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
@Override
protected void tearDown() throws Exception {
super.tearDown();
- assertTrue(PodDBAdapter.deleteDatabase(getInstrumentation().getTargetContext()));
+ PodDBAdapter.deleteDatabase();
}
@Override
protected void setUp() throws Exception {
super.setUp();
- final Context context = getInstrumentation().getTargetContext();
- context.deleteDatabase(PodDBAdapter.DATABASE_NAME);
- // make sure database is created
- PodDBAdapter adapter = new PodDBAdapter(context);
+
+ // create new database
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.close();
}
@@ -49,11 +49,11 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
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);
- f.setItems(new ArrayList<FeedItem>());
+ 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(), true, f));
+ f.getItems().add(new FeedItem(0, pref + i, pref + i, "link", new Date(), FeedItem.PLAYED, f));
}
- PodDBAdapter adapter = new PodDBAdapter(c);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(f);
adapter.setQueue(f.getItems());
@@ -97,7 +97,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
};
EventDistributor.getInstance().register(queueListener);
List<FeedItem> queue = writeTestQueue("a");
- EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED_ITEMS, queue));
+ EventBus.getDefault().post(QueueEvent.setQueue(queue));
countDownLatch.await(5000, TimeUnit.MILLISECONDS);
assertNotNull(queue);
@@ -122,11 +122,21 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
@Override
+ public void onSleepTimerAlmostExpired() {
+
+ }
+
+ @Override
public void onSleepTimerExpired() {
}
@Override
+ public void onSleepTimerReset() {
+
+ }
+
+ @Override
public void onWidgetUpdaterTick() {
}
@@ -170,11 +180,21 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
@Override
+ public void onSleepTimerAlmostExpired() {
+
+ }
+
+ @Override
public void onSleepTimerExpired() {
}
@Override
+ public void onSleepTimerReset() {
+
+ }
+
+ @Override
public void onWidgetUpdaterTick() {
countDownLatch.countDown();
}
@@ -221,7 +241,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
pstm.startWidgetUpdater();
pstm.startPositionSaver();
- pstm.setSleepTimer(100000);
+ pstm.setSleepTimer(100000, false, false);
pstm.cancelAllTasks();
assertFalse(pstm.isPositionSaverActive());
assertFalse(pstm.isWidgetUpdaterActive());
@@ -241,6 +261,11 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
@Override
+ public void onSleepTimerAlmostExpired() {
+
+ }
+
+ @Override
public void onSleepTimerExpired() {
if (countDownLatch.getCount() == 0) {
fail();
@@ -249,6 +274,11 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
@Override
+ public void onSleepTimerReset() {
+
+ }
+
+ @Override
public void onWidgetUpdaterTick() {
}
@@ -258,7 +288,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
});
- pstm.setSleepTimer(TIME);
+ pstm.setSleepTimer(TIME, false, false);
countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
pstm.shutdown();
}
@@ -275,11 +305,21 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
@Override
+ public void onSleepTimerAlmostExpired() {
+
+ }
+
+ @Override
public void onSleepTimerExpired() {
fail("Sleeptimer expired");
}
@Override
+ public void onSleepTimerReset() {
+
+ }
+
+ @Override
public void onWidgetUpdaterTick() {
}
@@ -289,7 +329,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
});
- pstm.setSleepTimer(TIME);
+ pstm.setSleepTimer(TIME, false, false);
pstm.disableSleepTimer();
assertFalse(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS));
pstm.shutdown();
@@ -298,7 +338,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
public void testIsSleepTimerActivePositive() {
final Context c = getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
- pstm.setSleepTimer(10000);
+ pstm.setSleepTimer(10000, false, false);
assertTrue(pstm.isSleepTimerActive());
pstm.shutdown();
}
@@ -306,7 +346,7 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
public void testIsSleepTimerActiveNegative() {
final Context c = getInstrumentation().getTargetContext();
PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
- pstm.setSleepTimer(10000);
+ pstm.setSleepTimer(10000, false, false);
pstm.disableSleepTimer();
assertFalse(pstm.isSleepTimerActive());
pstm.shutdown();
@@ -319,11 +359,21 @@ public class PlaybackServiceTaskManagerTest extends InstrumentationTestCase {
}
@Override
+ public void onSleepTimerAlmostExpired() {
+
+ }
+
+ @Override
public void onSleepTimerExpired() {
}
@Override
+ public void onSleepTimerReset() {
+
+ }
+
+ @Override
public void onWidgetUpdaterTick() {
}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
new file mode 100644
index 000000000..afdaeead0
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
@@ -0,0 +1,212 @@
+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.Date;
+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.DBTasks;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+
+import static de.test.antennapod.storage.DBTestUtils.saveFeedlist;
+
+/**
+ * Test class for DBTasks
+ */
+public class DBCleanupTests extends InstrumentationTestCase {
+
+ private static final String TAG = "DBTasksTest";
+ protected static final int EPISODE_CACHE_SIZE = 5;
+ private final int cleanupAlgorithm;
+
+ protected Context context;
+
+ protected File destFolder;
+
+ public DBCleanupTests() {
+ this.cleanupAlgorithm = UserPreferences.EPISODE_CLEANUP_DEFAULT;
+ }
+
+ public DBCleanupTests(int cleanupAlgorithm) {
+ this.cleanupAlgorithm = cleanupAlgorithm;
+ }
+
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ assertTrue(PodDBAdapter.deleteDatabase());
+
+ cleanupDestFolder(destFolder);
+ assertTrue(destFolder.delete());
+ }
+
+ private void cleanupDestFolder(File destFolder) {
+ for (File f : destFolder.listFiles()) {
+ assertTrue(f.delete());
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ context = getInstrumentation().getTargetContext();
+ destFolder = context.getExternalCacheDir();
+ cleanupDestFolder(destFolder);
+ assertNotNull(destFolder);
+ assertTrue(destFolder.exists());
+ assertTrue(destFolder.canWrite());
+
+ // create new database
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.close();
+
+ SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()).edit();
+ prefEdit.putString(UserPreferences.PREF_EPISODE_CACHE_SIZE, Integer.toString(EPISODE_CACHE_SIZE));
+ prefEdit.putString(UserPreferences.PREF_EPISODE_CLEANUP, Integer.toString(cleanupAlgorithm));
+ prefEdit.commit();
+
+ UserPreferences.init(context);
+ }
+
+ @FlakyTest(tolerance = 3)
+ public void testPerformAutoCleanupShouldDelete() throws IOException {
+ final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
+
+ Feed feed = new Feed("url", new Date(), "title");
+ List<FeedItem> items = new ArrayList<>();
+ feed.setItems(items);
+ List<File> files = new ArrayList<>();
+ populateItems(NUM_ITEMS, feed, items, files, FeedItem.PLAYED, false, false);
+
+ DBTasks.performAutoCleanup(context);
+ for (int i = 0; i < files.size(); i++) {
+ if (i < EPISODE_CACHE_SIZE) {
+ assertTrue(files.get(i).exists());
+ } else {
+ assertFalse(files.get(i).exists());
+ }
+ }
+ }
+
+ protected void populateItems(final int numItems, Feed feed, List<FeedItem> items,
+ List<File> files, int itemState, boolean addToQueue,
+ boolean addToFavorites) throws IOException {
+ for (int i = 0; i < numItems; i++) {
+ Date itemDate = new Date(numItems - i);
+ Date playbackCompletionDate = null;
+ if (itemState == FeedItem.PLAYED) {
+ playbackCompletionDate = itemDate;
+ }
+ FeedItem item = new FeedItem(0, "title", "id", "link", itemDate, itemState, feed);
+
+ File f = new File(destFolder, "file " + i);
+ assertTrue(f.createNewFile());
+ files.add(f);
+ item.setMedia(new FeedMedia(0, item, 1, 0, 1L, "m", f.getAbsolutePath(), "url", true, playbackCompletionDate, 0, 0));
+ items.add(item);
+ }
+
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.setCompleteFeed(feed);
+ if (addToQueue) {
+ adapter.setQueue(items);
+ }
+ if (addToFavorites) {
+ adapter.setFavorites(items);
+ }
+ adapter.close();
+
+ assertTrue(feed.getId() != 0);
+ for (FeedItem item : items) {
+ assertTrue(item.getId() != 0);
+ assertTrue(item.getMedia().getId() != 0);
+ }
+ }
+
+ @FlakyTest(tolerance = 3)
+ public void testPerformAutoCleanupHandleUnplayed() throws IOException {
+ final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
+
+ Feed feed = new Feed("url", new Date(), "title");
+ List<FeedItem> items = new ArrayList<FeedItem>();
+ feed.setItems(items);
+ List<File> files = new ArrayList<File>();
+ populateItems(NUM_ITEMS, feed, items, files, FeedItem.UNPLAYED, false, false);
+
+ DBTasks.performAutoCleanup(context);
+ for (File file : files) {
+ assertTrue(file.exists());
+ }
+ }
+
+ @FlakyTest(tolerance = 3)
+ public void testPerformAutoCleanupShouldNotDeleteBecauseInQueue() throws IOException {
+ final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
+
+ Feed feed = new Feed("url", new Date(), "title");
+ List<FeedItem> items = new ArrayList<>();
+ feed.setItems(items);
+ List<File> files = new ArrayList<>();
+ populateItems(NUM_ITEMS, feed, items, files, FeedItem.PLAYED, true, false);
+
+ DBTasks.performAutoCleanup(context);
+ for (File file : files) {
+ assertTrue(file.exists());
+ }
+ }
+
+ /**
+ * Reproduces a bug where DBTasks.performAutoCleanup(android.content.Context) would use the ID of the FeedItem in the
+ * call to DBWriter.deleteFeedMediaOfItem instead of the ID of the FeedMedia. This would cause the wrong item to be deleted.
+ * @throws IOException
+ */
+ @FlakyTest(tolerance = 3)
+ public void testPerformAutoCleanupShouldNotDeleteBecauseInQueue_withFeedsWithNoMedia() throws IOException {
+ // add feed with no enclosures so that item ID != media ID
+ saveFeedlist(1, 10, false);
+
+ // add candidate for performAutoCleanup
+ List<Feed> feeds = saveFeedlist(1, 1, true);
+ FeedMedia m = feeds.get(0).getItems().get(0).getMedia();
+ m.setDownloaded(true);
+ m.setFile_url("file");
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.setMedia(m);
+ adapter.close();
+
+ testPerformAutoCleanupShouldNotDeleteBecauseInQueue();
+ }
+
+ @FlakyTest(tolerance = 3)
+ public void testPerformAutoCleanupShouldNotDeleteBecauseFavorite() throws IOException {
+ final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
+
+ Feed feed = new Feed("url", new Date(), "title");
+ List<FeedItem> items = new ArrayList<>();
+ feed.setItems(items);
+ List<File> files = new ArrayList<>();
+ populateItems(NUM_ITEMS, feed, items, files, FeedItem.PLAYED, false, true);
+
+ DBTasks.performAutoCleanup(context);
+ for (File file : files) {
+ assertTrue(file.exists());
+ }
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java
new file mode 100644
index 000000000..18a8d63d1
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java
@@ -0,0 +1,115 @@
+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.Date;
+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.DBTasks;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+
+import static de.test.antennapod.storage.DBTestUtils.saveFeedlist;
+
+/**
+ * Tests that the APNullCleanupAlgorithm is working correctly.
+ */
+public class DBNullCleanupAlgorithmTest extends InstrumentationTestCase {
+
+ private static final String TAG = "DBNullCleanupAlgorithmTest";
+ private static final int EPISODE_CACHE_SIZE = 5;
+
+ private Context context;
+
+ private File destFolder;
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ assertTrue(PodDBAdapter.deleteDatabase());
+
+ cleanupDestFolder(destFolder);
+ assertTrue(destFolder.delete());
+ }
+
+ private void cleanupDestFolder(File destFolder) {
+ for (File f : destFolder.listFiles()) {
+ assertTrue(f.delete());
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ context = getInstrumentation().getTargetContext();
+ destFolder = context.getExternalCacheDir();
+ cleanupDestFolder(destFolder);
+ assertNotNull(destFolder);
+ assertTrue(destFolder.exists());
+ assertTrue(destFolder.canWrite());
+
+ // create new database
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.close();
+
+ SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()).edit();
+ prefEdit.putString(UserPreferences.PREF_EPISODE_CACHE_SIZE, Integer.toString(EPISODE_CACHE_SIZE));
+ prefEdit.putString(UserPreferences.PREF_EPISODE_CLEANUP, Integer.toString(UserPreferences.EPISODE_CLEANUP_NULL));
+ prefEdit.commit();
+
+ UserPreferences.init(context);
+ }
+
+ /**
+ * A test with no items in the queue, but multiple items downloaded.
+ * The null algorithm should never delete any items, even if they're played and not in the queue.
+ * @throws IOException
+ */
+ @FlakyTest(tolerance = 3)
+ public void testPerformAutoCleanupShouldNotDelete() throws IOException {
+ final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
+
+ Feed feed = new Feed("url", new Date(), "title");
+ List<FeedItem> items = new ArrayList<>();
+ feed.setItems(items);
+ List<File> files = new ArrayList<>();
+ for (int i = 0; i < NUM_ITEMS; i++) {
+ FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
+
+ File f = new File(destFolder, "file " + i);
+ assertTrue(f.createNewFile());
+ files.add(f);
+ item.setMedia(new FeedMedia(0, item, 1, 0, 1L, "m", f.getAbsolutePath(), "url", true,
+ new Date(NUM_ITEMS - i), 0, 0));
+ items.add(item);
+ }
+
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.setCompleteFeed(feed);
+ adapter.close();
+
+ assertTrue(feed.getId() != 0);
+ for (FeedItem item : items) {
+ assertTrue(item.getId() != 0);
+ assertTrue(item.getMedia().getId() != 0);
+ }
+ DBTasks.performAutoCleanup(context);
+ for (int i = 0; i < files.size(); i++) {
+ assertTrue(files.get(i).exists());
+ }
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java
new file mode 100644
index 000000000..890897f43
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java
@@ -0,0 +1,50 @@
+package de.test.antennapod.storage;
+
+import android.test.FlakyTest;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.storage.DBTasks;
+
+/**
+ * Tests that the APQueueCleanupAlgorithm is working correctly.
+ */
+public class DBQueueCleanupAlgorithmTest extends DBCleanupTests {
+
+ private static final String TAG = "DBQueueCleanupAlgorithmTest";
+
+ public DBQueueCleanupAlgorithmTest() {
+ super(UserPreferences.EPISODE_CLEANUP_QUEUE);
+ }
+
+ /**
+ * For APQueueCleanupAlgorithm we expect even unplayed episodes to be deleted if needed
+ * if they aren't in the queue
+ */
+ @FlakyTest(tolerance = 3)
+ public void testPerformAutoCleanupHandleUnplayed() throws IOException {
+ final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
+
+ Feed feed = new Feed("url", new Date(), "title");
+ List<FeedItem> items = new ArrayList<>();
+ feed.setItems(items);
+ List<File> files = new ArrayList<>();
+ populateItems(NUM_ITEMS, feed, items, files, FeedItem.UNPLAYED, false, false);
+
+ DBTasks.performAutoCleanup(context);
+ for (int i = 0; i < files.size(); i++) {
+ if (i < EPISODE_CACHE_SIZE) {
+ assertTrue(files.get(i).exists());
+ } else {
+ assertFalse(files.get(i).exists());
+ }
+ }
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
index 95d2ce58a..3988669ce 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
@@ -15,7 +15,6 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.FeedItemStatistics;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.LongList;
-import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
import static de.test.antennapod.storage.DBTestUtils.saveFeedlist;
@@ -27,57 +26,23 @@ public class DBReaderTest extends InstrumentationTestCase {
@Override
protected void tearDown() throws Exception {
super.tearDown();
- final Context context = getInstrumentation().getTargetContext();
- assertTrue(PodDBAdapter.deleteDatabase(context));
+ assertTrue(PodDBAdapter.deleteDatabase());
}
@Override
protected void setUp() throws Exception {
super.setUp();
- final Context context = getInstrumentation().getTargetContext();
- context.deleteDatabase(PodDBAdapter.DATABASE_NAME);
- // make sure database is created
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- adapter.close();
- }
- private void expiredFeedListTestHelper(long lastUpdate, long expirationTime, boolean shouldReturn) {
- final Context context = getInstrumentation().getTargetContext();
- Feed feed = new Feed(0, new Date(lastUpdate), "feed", "link", "descr", null,
- null, null, null, "feed", null, null, "url", false, new FlattrStatus(), false, null, null, false);
- feed.setItems(new ArrayList<FeedItem>());
- PodDBAdapter adapter = new PodDBAdapter(context);
+ // create new database
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
- adapter.setCompleteFeed(feed);
adapter.close();
-
- assertTrue(feed.getId() != 0);
- List<Feed> expiredFeeds = DBReader.getExpiredFeedsList(context, expirationTime);
- assertNotNull(expiredFeeds);
- if (shouldReturn) {
- assertTrue(expiredFeeds.size() == 1);
- assertTrue(expiredFeeds.get(0).getId() == feed.getId());
- } else {
- assertTrue(expiredFeeds.isEmpty());
- }
}
- public void testGetExpiredFeedsListShouldReturnFeed() {
- final long expirationTime = 1000 * 60 * 60; // 1 hour
- expiredFeedListTestHelper(System.currentTimeMillis() - expirationTime - 1, expirationTime, true);
- }
-
- public void testGetExpiredFeedsListShouldNotReturnFeed() {
- final long expirationTime = 1000 * 60 * 60; // 1 hour
- expiredFeedListTestHelper(System.currentTimeMillis() - expirationTime / 2, expirationTime, false);
- }
-
-
public void testGetFeedList() {
- final Context context = getInstrumentation().getTargetContext();
- List<Feed> feeds = saveFeedlist(context, 10, 0, false);
- List<Feed> savedFeeds = DBReader.getFeedList(context);
+ List<Feed> feeds = saveFeedlist(10, 0, false);
+ List<Feed> savedFeeds = DBReader.getFeedList();
assertNotNull(savedFeeds);
assertEquals(feeds.size(), savedFeeds.size());
for (int i = 0; i < feeds.size(); i++) {
@@ -86,8 +51,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testGetFeedListSortOrder() {
- final Context context = getInstrumentation().getTargetContext();
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
Feed feed1 = new Feed(0, new Date(), "A", "link", "d", null, null, null, "rss", "A", null, "", "", true);
@@ -105,7 +69,7 @@ public class DBReaderTest extends InstrumentationTestCase {
adapter.close();
- List<Feed> saved = DBReader.getFeedList(context);
+ List<Feed> saved = DBReader.getFeedList();
assertNotNull(saved);
assertEquals("Wrong size: ", 4, saved.size());
@@ -116,9 +80,8 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testFeedListDownloadUrls() {
- final Context context = getInstrumentation().getTargetContext();
- List<Feed> feeds = saveFeedlist(context, 10, 0, false);
- List<String> urls = DBReader.getFeedListDownloadUrls(context);
+ List<Feed> feeds = saveFeedlist(10, 0, false);
+ List<String> urls = DBReader.getFeedListDownloadUrls();
assertNotNull(urls);
assertTrue(urls.size() == feeds.size());
for (int i = 0; i < urls.size(); i++) {
@@ -130,8 +93,8 @@ public class DBReaderTest extends InstrumentationTestCase {
final Context context = getInstrumentation().getTargetContext();
final int numFeeds = 10;
final int numItems = 1;
- List<Feed> feeds = saveFeedlist(context, numFeeds, numItems, false);
- List<FeedItem> items = new ArrayList<FeedItem>();
+ List<Feed> feeds = saveFeedlist(numFeeds, numItems, false);
+ List<FeedItem> items = new ArrayList<>();
for (Feed f : feeds) {
for (FeedItem item : f.getItems()) {
item.setFeed(null);
@@ -139,7 +102,7 @@ public class DBReaderTest extends InstrumentationTestCase {
items.add(item);
}
}
- DBReader.loadFeedDataOfFeedItemlist(context, items);
+ DBReader.loadAdditionalFeedItemListData(items);
for (int i = 0; i < numFeeds; i++) {
for (int j = 0; j < numItems; j++) {
FeedItem item = feeds.get(i).getItems().get(j);
@@ -151,13 +114,12 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testGetFeedItemList() {
- final Context context = getInstrumentation().getTargetContext();
final int numFeeds = 1;
final int numItems = 10;
- Feed feed = saveFeedlist(context, numFeeds, numItems, false).get(0);
+ Feed feed = saveFeedlist(numFeeds, numItems, false).get(0);
List<FeedItem> items = feed.getItems();
feed.setItems(null);
- List<FeedItem> savedItems = DBReader.getFeedItemList(context, feed);
+ List<FeedItem> savedItems = DBReader.getFeedItemList(feed);
assertNotNull(savedItems);
assertTrue(savedItems.size() == items.size());
for (int i = 0; i < savedItems.size(); i++) {
@@ -169,22 +131,21 @@ public class DBReaderTest extends InstrumentationTestCase {
if (numItems <= 0) {
throw new IllegalArgumentException("numItems<=0");
}
- final Context context = getInstrumentation().getTargetContext();
- List<Feed> feeds = saveFeedlist(context, numItems, numItems, false);
- List<FeedItem> allItems = new ArrayList<FeedItem>();
+ List<Feed> feeds = saveFeedlist(numItems, numItems, false);
+ List<FeedItem> allItems = new ArrayList<>();
for (Feed f : feeds) {
allItems.addAll(f.getItems());
}
// take random items from every feed
Random random = new Random();
- List<FeedItem> queue = new ArrayList<FeedItem>();
+ List<FeedItem> queue = new ArrayList<>();
while (queue.size() < numItems) {
int index = random.nextInt(numItems);
if (!queue.contains(allItems.get(index))) {
queue.add(allItems.get(index));
}
}
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setQueue(queue);
adapter.close();
@@ -192,10 +153,9 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testGetQueueIDList() {
- final Context context = getInstrumentation().getTargetContext();
final int numItems = 10;
List<FeedItem> queue = saveQueue(numItems);
- LongList ids = DBReader.getQueueIDList(context);
+ LongList ids = DBReader.getQueueIDList();
assertNotNull(ids);
assertTrue(queue.size() == ids.size());
for (int i = 0; i < queue.size(); i++) {
@@ -205,10 +165,9 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testGetQueue() {
- final Context context = getInstrumentation().getTargetContext();
final int numItems = 10;
List<FeedItem> queue = saveQueue(numItems);
- List<FeedItem> savedQueue = DBReader.getQueue(context);
+ List<FeedItem> savedQueue = DBReader.getQueue();
assertNotNull(savedQueue);
assertTrue(queue.size() == savedQueue.size());
for (int i = 0; i < queue.size(); i++) {
@@ -221,13 +180,12 @@ public class DBReaderTest extends InstrumentationTestCase {
if (numItems <= 0) {
throw new IllegalArgumentException("numItems<=0");
}
- final Context context = getInstrumentation().getTargetContext();
- List<Feed> feeds = saveFeedlist(context, numItems, numItems, true);
- List<FeedItem> items = new ArrayList<FeedItem>();
+ List<Feed> feeds = saveFeedlist(numItems, numItems, true);
+ List<FeedItem> items = new ArrayList<>();
for (Feed f : feeds) {
items.addAll(f.getItems());
}
- List<FeedItem> downloaded = new ArrayList<FeedItem>();
+ List<FeedItem> downloaded = new ArrayList<>();
Random random = new Random();
while (downloaded.size() < numItems) {
@@ -239,7 +197,7 @@ public class DBReaderTest extends InstrumentationTestCase {
downloaded.add(item);
}
}
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedItemlist(downloaded);
adapter.close();
@@ -247,10 +205,9 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testGetDownloadedItems() {
- final Context context = getInstrumentation().getTargetContext();
final int numItems = 10;
List<FeedItem> downloaded = saveDownloadedItems(numItems);
- List<FeedItem> downloaded_saved = DBReader.getDownloadedItems(context);
+ List<FeedItem> downloaded_saved = DBReader.getDownloadedItems();
assertNotNull(downloaded_saved);
assertTrue(downloaded_saved.size() == downloaded.size());
for (FeedItem item : downloaded_saved) {
@@ -264,24 +221,23 @@ public class DBReaderTest extends InstrumentationTestCase {
if (numItems <= 0) {
throw new IllegalArgumentException("numItems<=0");
}
- final Context context = getInstrumentation().getTargetContext();
- List<Feed> feeds = saveFeedlist(context, numItems, numItems, true);
- List<FeedItem> items = new ArrayList<FeedItem>();
+ List<Feed> feeds = saveFeedlist(numItems, numItems, true);
+ List<FeedItem> items = new ArrayList<>();
for (Feed f : feeds) {
items.addAll(f.getItems());
}
- List<FeedItem> unread = new ArrayList<FeedItem>();
+ List<FeedItem> unread = new ArrayList<>();
Random random = new Random();
while (unread.size() < numItems) {
int i = random.nextInt(numItems);
if (!unread.contains(items.get(i))) {
FeedItem item = items.get(i);
- item.setRead(false);
+ item.setPlayed(false);
unread.add(item);
}
}
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedItemlist(unread);
adapter.close();
@@ -289,20 +245,18 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testGetUnreadItemsList() {
- final Context context = getInstrumentation().getTargetContext();
final int numItems = 10;
List<FeedItem> unread = saveUnreadItems(numItems);
- List<FeedItem> unreadSaved = DBReader.getUnreadItemsList(context);
+ List<FeedItem> unreadSaved = DBReader.getUnreadItemsList();
assertNotNull(unreadSaved);
assertTrue(unread.size() == unreadSaved.size());
for (FeedItem item : unreadSaved) {
- assertFalse(item.isRead());
+ assertFalse(item.isPlayed());
}
}
public void testGetNewItemIds() {
- final Context context = getInstrumentation().getTargetContext();
final int numItems = 10;
List<FeedItem> unread = saveUnreadItems(numItems);
@@ -310,11 +264,11 @@ public class DBReaderTest extends InstrumentationTestCase {
for (int i = 0; i < unread.size(); i++) {
unreadIds[i] = unread.get(i).getId();
}
- LongList unreadSaved = DBReader.getNewItemIds(context);
+ List<FeedItem> unreadSaved = DBReader.getUnreadItemsList();
assertNotNull(unreadSaved);
assertTrue(unread.size() == unreadSaved.size());
for(int i=0; i < unreadSaved.size(); i++) {
- long savedId = unreadSaved.get(i);
+ long savedId = unreadSaved.get(i).getId();
boolean found = false;
for (long id : unreadIds) {
if (id == savedId) {
@@ -327,16 +281,15 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testGetPlaybackHistory() {
- final Context context = getInstrumentation().getTargetContext();
final int numItems = (DBReader.PLAYBACK_HISTORY_SIZE + 1) * 2;
final int playedItems = DBReader.PLAYBACK_HISTORY_SIZE + 1;
final int numReturnedItems = Math.min(playedItems, DBReader.PLAYBACK_HISTORY_SIZE);
final int numFeeds = 1;
- Feed feed = DBTestUtils.saveFeedlist(context, numFeeds, numItems, true).get(0);
+ Feed feed = DBTestUtils.saveFeedlist(numFeeds, numItems, true).get(0);
long[] ids = new long[playedItems];
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
for (int i = 0; i < playedItems; i++) {
FeedMedia m = feed.getItems().get(i).getMedia();
@@ -346,7 +299,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
adapter.close();
- List<FeedItem> saved = DBReader.getPlaybackHistory(context);
+ List<FeedItem> saved = DBReader.getPlaybackHistory();
assertNotNull(saved);
assertEquals("Wrong size: ", numReturnedItems, saved.size());
for (int i = 0; i < numReturnedItems; i++) {
@@ -357,11 +310,10 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testGetFeedStatisticsCheckOrder() {
- final Context context = getInstrumentation().getTargetContext();
final int NUM_FEEDS = 10;
final int NUM_ITEMS = 10;
- List<Feed> feeds = DBTestUtils.saveFeedlist(context, NUM_FEEDS, NUM_ITEMS, false);
- List<FeedItemStatistics> statistics = DBReader.getFeedStatisticsList(context);
+ List<Feed> feeds = DBTestUtils.saveFeedlist(NUM_FEEDS, NUM_ITEMS, false);
+ List<FeedItemStatistics> statistics = DBReader.getFeedStatisticsList();
assertNotNull(statistics);
assertEquals(feeds.size(), statistics.size());
for (int i = 0; i < NUM_FEEDS; i++) {
@@ -370,31 +322,29 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testGetNavDrawerDataQueueEmptyNoUnreadItems() {
- final Context context = getInstrumentation().getTargetContext();
final int NUM_FEEDS = 10;
final int NUM_ITEMS = 10;
- List<Feed> feeds = DBTestUtils.saveFeedlist(context, NUM_FEEDS, NUM_ITEMS, true);
- DBReader.NavDrawerData navDrawerData = DBReader.getNavDrawerData(context);
+ List<Feed> feeds = DBTestUtils.saveFeedlist(NUM_FEEDS, NUM_ITEMS, true);
+ DBReader.NavDrawerData navDrawerData = DBReader.getNavDrawerData();
assertEquals(NUM_FEEDS, navDrawerData.feeds.size());
assertEquals(0, navDrawerData.numNewItems);
assertEquals(0, navDrawerData.queueSize);
}
public void testGetNavDrawerDataQueueNotEmptyWithUnreadItems() {
- final Context context = getInstrumentation().getTargetContext();
final int NUM_FEEDS = 10;
final int NUM_ITEMS = 10;
final int NUM_QUEUE = 1;
- final int NUM_UNREAD = 2;
- List<Feed> feeds = DBTestUtils.saveFeedlist(context, NUM_FEEDS, NUM_ITEMS, true);
- PodDBAdapter adapter = new PodDBAdapter(context);
+ final int NUM_NEW = 2;
+ List<Feed> feeds = DBTestUtils.saveFeedlist(NUM_FEEDS, NUM_ITEMS, true);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
- for (int i = 0; i < NUM_UNREAD; i++) {
+ for (int i = 0; i < NUM_NEW; i++) {
FeedItem item = feeds.get(0).getItems().get(i);
- item.setRead(false);
+ item.setNew();
adapter.setSingleFeedItem(item);
}
- List<FeedItem> queue = new ArrayList<FeedItem>();
+ List<FeedItem> queue = new ArrayList<>();
for (int i = 0; i < NUM_QUEUE; i++) {
FeedItem item = feeds.get(1).getItems().get(i);
queue.add(item);
@@ -403,15 +353,15 @@ public class DBReaderTest extends InstrumentationTestCase {
adapter.close();
- DBReader.NavDrawerData navDrawerData = DBReader.getNavDrawerData(context);
+ DBReader.NavDrawerData navDrawerData = DBReader.getNavDrawerData();
assertEquals(NUM_FEEDS, navDrawerData.feeds.size());
- assertEquals(NUM_UNREAD, navDrawerData.numNewItems);
+ assertEquals(NUM_NEW, navDrawerData.numNewItems);
assertEquals(NUM_QUEUE, navDrawerData.queueSize);
}
public void testGetFeedItemlistCheckChaptersFalse() throws Exception {
Context context = getInstrumentation().getTargetContext();
- List<Feed> feeds = DBTestUtils.saveFeedlist(context, 10, 10, false, false, 0);
+ List<Feed> feeds = DBTestUtils.saveFeedlist(10, 10, false, false, 0);
for (Feed feed : feeds) {
for (FeedItem item : feed.getItems()) {
assertFalse(item.hasChapters());
@@ -420,8 +370,7 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testGetFeedItemlistCheckChaptersTrue() throws Exception {
- Context context = getInstrumentation().getTargetContext();
- List<Feed> feeds = saveFeedlist(context, 10, 10, false, true, 10);
+ List<Feed> feeds = saveFeedlist(10, 10, false, true, 10);
for (Feed feed : feeds) {
for (FeedItem item : feed.getItems()) {
assertTrue(item.hasChapters());
@@ -430,13 +379,12 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testLoadChaptersOfFeedItemNoChapters() throws Exception {
- Context context = getInstrumentation().getTargetContext();
- List<Feed> feeds = saveFeedlist(context, 1, 3, false, false, 0);
- saveFeedlist(context, 1, 3, false, true, 3);
+ List<Feed> feeds = saveFeedlist(1, 3, false, false, 0);
+ saveFeedlist(1, 3, false, true, 3);
for (Feed feed : feeds) {
for (FeedItem item : feed.getItems()) {
assertFalse(item.hasChapters());
- DBReader.loadChaptersOfFeedItem(context, item);
+ DBReader.loadChaptersOfFeedItem(item);
assertFalse(item.hasChapters());
assertNull(item.getChapters());
}
@@ -444,14 +392,13 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testLoadChaptersOfFeedItemWithChapters() throws Exception {
- Context context = getInstrumentation().getTargetContext();
final int NUM_CHAPTERS = 3;
- DBTestUtils.saveFeedlist(context, 1, 3, false, false, 0);
- List<Feed> feeds = saveFeedlist(context, 1, 3, false, true, NUM_CHAPTERS);
+ DBTestUtils.saveFeedlist(1, 3, false, false, 0);
+ List<Feed> feeds = saveFeedlist(1, 3, false, true, NUM_CHAPTERS);
for (Feed feed : feeds) {
for (FeedItem item : feed.getItems()) {
assertTrue(item.hasChapters());
- DBReader.loadChaptersOfFeedItem(context, item);
+ DBReader.loadChaptersOfFeedItem(item);
assertTrue(item.hasChapters());
assertNotNull(item.getChapters());
assertEquals(NUM_CHAPTERS, item.getChapters().size());
@@ -460,11 +407,10 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testGetItemWithChapters() throws Exception {
- Context context = getInstrumentation().getTargetContext();
final int NUM_CHAPTERS = 3;
- List<Feed> feeds = saveFeedlist(context, 1, 1, false, true, NUM_CHAPTERS);
+ List<Feed> feeds = saveFeedlist(1, 1, false, true, NUM_CHAPTERS);
FeedItem item1 = feeds.get(0).getItems().get(0);
- FeedItem item2 = DBReader.getFeedItem(context, item1.getId());
+ FeedItem item2 = DBReader.getFeedItem(item1.getId());
assertTrue(item2.hasChapters());
assertEquals(item1.getChapters(), item2.getChapters());
}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
index 16f50cf28..1894d6585 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
@@ -20,7 +20,6 @@ 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 de.danoeh.antennapod.core.util.flattr.FlattrStatus;
import static de.test.antennapod.storage.DBTestUtils.saveFeedlist;
@@ -30,174 +29,28 @@ import static de.test.antennapod.storage.DBTestUtils.saveFeedlist;
public class DBTasksTest extends InstrumentationTestCase {
private static final String TAG = "DBTasksTest";
- private static final int EPISODE_CACHE_SIZE = 5;
private Context context;
-
- private File destFolder;
@Override
protected void tearDown() throws Exception {
super.tearDown();
- assertTrue(PodDBAdapter.deleteDatabase(context));
-
- for (File f : destFolder.listFiles()) {
- assertTrue(f.delete());
- }
- assertTrue(destFolder.delete());
+ assertTrue(PodDBAdapter.deleteDatabase());
}
@Override
protected void setUp() throws Exception {
super.setUp();
context = getInstrumentation().getTargetContext();
- destFolder = context.getExternalCacheDir();
- assertNotNull(destFolder);
- assertTrue(destFolder.exists());
- assertTrue(destFolder.canWrite());
-
- context.deleteDatabase(PodDBAdapter.DATABASE_NAME);
- // make sure database is created
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- adapter.close();
-
- SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext()).edit();
- prefEdit.putString(UserPreferences.PREF_EPISODE_CACHE_SIZE, Integer.toString(EPISODE_CACHE_SIZE));
- prefEdit.commit();
- }
-
- @FlakyTest(tolerance = 3)
- public void testPerformAutoCleanupShouldDelete() throws IOException {
- final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
-
- Feed feed = new Feed("url", new Date(), "title");
- List<FeedItem> items = new ArrayList<FeedItem>();
- feed.setItems(items);
- List<File> files = new ArrayList<File>();
- for (int i = 0; i < NUM_ITEMS; i++) {
- FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
-
- File f = new File(destFolder, "file " + i);
- assertTrue(f.createNewFile());
- files.add(f);
- item.setMedia(new FeedMedia(0, item, 1, 0, 1L, "m", f.getAbsolutePath(), "url", true, new Date(NUM_ITEMS - i), 0));
- items.add(item);
- }
-
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- adapter.setCompleteFeed(feed);
- adapter.close();
-
- assertTrue(feed.getId() != 0);
- for (FeedItem item : items) {
- assertTrue(item.getId() != 0);
- assertTrue(item.getMedia().getId() != 0);
- }
- DBTasks.performAutoCleanup(context);
- for (int i = 0; i < files.size(); i++) {
- if (i < EPISODE_CACHE_SIZE) {
- assertTrue(files.get(i).exists());
- } else {
- assertFalse(files.get(i).exists());
- }
- }
- }
-
- @FlakyTest(tolerance = 3)
- public void testPerformAutoCleanupShouldNotDeleteBecauseUnread() throws IOException {
- final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
-
- Feed feed = new Feed("url", new Date(), "title");
- List<FeedItem> items = new ArrayList<FeedItem>();
- feed.setItems(items);
- List<File> files = new ArrayList<File>();
- for (int i = 0; i < NUM_ITEMS; i++) {
- FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), false, feed);
-
- File f = new File(destFolder, "file " + i);
- assertTrue(f.createNewFile());
- assertTrue(f.exists());
- files.add(f);
- item.setMedia(new FeedMedia(0, item, 1, 0, 1L, "m", f.getAbsolutePath(), "url", true, new Date(NUM_ITEMS - i), 0));
- items.add(item);
- }
-
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- adapter.setCompleteFeed(feed);
- adapter.close();
-
- assertTrue(feed.getId() != 0);
- for (FeedItem item : items) {
- assertTrue(item.getId() != 0);
- assertTrue(item.getMedia().getId() != 0);
- }
- DBTasks.performAutoCleanup(context);
- for (File file : files) {
- assertTrue(file.exists());
- }
- }
-
- @FlakyTest(tolerance = 3)
- public void testPerformAutoCleanupShouldNotDeleteBecauseInQueue() throws IOException {
- final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
-
- Feed feed = new Feed("url", new Date(), "title");
- List<FeedItem> items = new ArrayList<FeedItem>();
- feed.setItems(items);
- List<File> files = new ArrayList<File>();
- for (int i = 0; i < NUM_ITEMS; i++) {
- FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
-
- File f = new File(destFolder, "file " + i);
- assertTrue(f.createNewFile());
- assertTrue(f.exists());
- files.add(f);
- item.setMedia(new FeedMedia(0, item, 1, 0, 1L, "m", f.getAbsolutePath(), "url", true, new Date(NUM_ITEMS - i), 0));
- items.add(item);
- }
-
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- adapter.setCompleteFeed(feed);
- adapter.setQueue(items);
- adapter.close();
-
- assertTrue(feed.getId() != 0);
- for (FeedItem item : items) {
- assertTrue(item.getId() != 0);
- assertTrue(item.getMedia().getId() != 0);
- }
- DBTasks.performAutoCleanup(context);
- for (File file : files) {
- assertTrue(file.exists());
- }
- }
-
- /**
- * Reproduces a bug where DBTasks.performAutoCleanup(android.content.Context) would use the ID of the FeedItem in the
- * call to DBWriter.deleteFeedMediaOfItem instead of the ID of the FeedMedia. This would cause the wrong item to be deleted.
- * @throws IOException
- */
- @FlakyTest(tolerance = 3)
- public void testPerformAutoCleanupShouldNotDeleteBecauseInQueue_withFeedsWithNoMedia() throws IOException {
- // add feed with no enclosures so that item ID != media ID
- saveFeedlist(context, 1, 10, false);
- // add candidate for performAutoCleanup
- List<Feed> feeds = saveFeedlist(context, 1, 1, true);
- FeedMedia m = feeds.get(0).getItems().get(0).getMedia();
- m.setDownloaded(true);
- m.setFile_url("file");
- PodDBAdapter adapter = new PodDBAdapter(context);
+ // create new database
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
- adapter.setMedia(m);
adapter.close();
- testPerformAutoCleanupShouldNotDeleteBecauseInQueue();
+ UserPreferences.init(context);
}
@FlakyTest(tolerance = 3)
@@ -205,16 +58,16 @@ public class DBTasksTest extends InstrumentationTestCase {
final int NUM_ITEMS = 10;
Feed feed = new Feed("url", new Date(), "title");
- feed.setItems(new ArrayList<FeedItem>());
+ 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(), false, feed));
+ feed.getItems().add(new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(), FeedItem.UNPLAYED, feed));
}
Feed newFeed = DBTasks.updateFeed(context, feed)[0];
assertTrue(newFeed == feed);
assertTrue(feed.getId() != 0);
for (FeedItem item : feed.getItems()) {
- assertFalse(item.isRead());
+ assertFalse(item.isPlayed());
assertTrue(item.getId() != 0);
}
}
@@ -225,8 +78,8 @@ public class DBTasksTest extends InstrumentationTestCase {
Feed feed1 = new Feed("url1", new Date(), "title");
Feed feed2 = new Feed("url2", new Date(), "title");
- feed1.setItems(new ArrayList<FeedItem>());
- feed2.setItems(new ArrayList<FeedItem>());
+ feed1.setItems(new ArrayList<>());
+ feed2.setItems(new ArrayList<>());
Feed savedFeed1 = DBTasks.updateFeed(context, feed1)[0];
Feed savedFeed2 = DBTasks.updateFeed(context, feed2)[0];
@@ -239,11 +92,11 @@ public class DBTasksTest extends InstrumentationTestCase {
final int NUM_ITEMS_NEW = 10;
final Feed feed = new Feed("url", new Date(), "title");
- feed.setItems(new ArrayList<FeedItem>());
+ 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), true, feed));
+ feed.getItems().add(new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(i), FeedItem.PLAYED, feed));
}
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -260,7 +113,7 @@ public class DBTasksTest extends InstrumentationTestCase {
}
for (int i = NUM_ITEMS_OLD; i < NUM_ITEMS_NEW + NUM_ITEMS_OLD; i++) {
- feed.getItems().add(0, new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(i), true, feed));
+ feed.getItems().add(0, new FeedItem(0, "item " + i, "id " + i, "link " + i, new Date(i), FeedItem.UNPLAYED, feed));
}
final Feed newFeed = DBTasks.updateFeed(context, feed)[0];
@@ -268,13 +121,12 @@ public class DBTasksTest extends InstrumentationTestCase {
updatedFeedTest(newFeed, feedID, itemIDs, NUM_ITEMS_OLD, NUM_ITEMS_NEW);
- final Feed feedFromDB = DBReader.getFeed(context, newFeed.getId());
+ final Feed feedFromDB = DBReader.getFeed(newFeed.getId());
assertNotNull(feedFromDB);
assertTrue(feedFromDB.getId() == newFeed.getId());
updatedFeedTest(feedFromDB, feedID, itemIDs, NUM_ITEMS_OLD, NUM_ITEMS_NEW);
}
- @FlakyTest(tolerance = 3)
private void updatedFeedTest(final Feed newFeed, long feedID, List<Long> itemIDs, final int NUM_ITEMS_OLD, final int NUM_ITEMS_NEW) {
assertTrue(newFeed.getId() == feedID);
assertTrue(newFeed.getItems().size() == NUM_ITEMS_NEW + NUM_ITEMS_OLD);
@@ -284,7 +136,7 @@ public class DBTasksTest extends InstrumentationTestCase {
FeedItem item = newFeed.getItems().get(i);
assertTrue(item.getFeed() == newFeed);
assertTrue(item.getId() == itemIDs.get(i));
- assertTrue(item.isRead());
+ assertTrue(item.isPlayed());
assertTrue(item.getPubDate().getTime() >= lastDate.getTime());
lastDate = item.getPubDate();
}
@@ -292,43 +144,9 @@ public class DBTasksTest extends InstrumentationTestCase {
FeedItem item = newFeed.getItems().get(i);
assertTrue(item.getFeed() == newFeed);
assertTrue(item.getId() != 0);
- assertFalse(item.isRead());
+ assertFalse(item.isPlayed());
assertTrue(item.getPubDate().getTime() >= lastDate.getTime());
lastDate = item.getPubDate();
}
}
-
- @FlakyTest(tolerance = 3)
- private void expiredFeedListTestHelper(long lastUpdate, long expirationTime, boolean shouldReturn) {
- UserPreferences.setUpdateInterval(context, expirationTime);
- Feed feed = new Feed(0, new Date(lastUpdate), "feed", "link", "descr", null,
- null, null, null, "feed", null, null, "url", false, new FlattrStatus(), false, null, null, false);
- feed.setItems(new ArrayList<FeedItem>());
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- adapter.setCompleteFeed(feed);
- adapter.close();
-
- assertTrue(feed.getId() != 0);
- List<Feed> expiredFeeds = DBTasks.getExpiredFeeds(context);
- assertNotNull(expiredFeeds);
- if (shouldReturn) {
- assertTrue(expiredFeeds.size() == 1);
- assertTrue(expiredFeeds.get(0).getId() == feed.getId());
- } else {
- assertTrue(expiredFeeds.isEmpty());
- }
- }
-
- @FlakyTest(tolerance = 3)
- public void testGetExpiredFeedsTestShouldReturn() {
- final long expirationTime = 1000 * 60 * 60;
- expiredFeedListTestHelper(System.currentTimeMillis() - expirationTime - 1, expirationTime, true);
- }
-
- @FlakyTest(tolerance = 3)
- public void testGetExpiredFeedsTestShouldNotReturn() {
- final long expirationTime = 1000 * 60 * 60;
- expiredFeedListTestHelper(System.currentTimeMillis() - expirationTime / 2, expirationTime, false);
- }
}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
index 4f33cd798..0af8afa83 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
@@ -1,7 +1,5 @@
package de.test.antennapod.storage;
-import android.content.Context;
-
import junit.framework.Assert;
import java.util.ArrayList;
@@ -26,14 +24,14 @@ public class DBTestUtils {
/**
* Use this method when tests don't involve chapters.
*/
- public static List<Feed> saveFeedlist(Context context, int numFeeds, int numItems, boolean withMedia) {
- return saveFeedlist(context, numFeeds, numItems, withMedia, false, 0);
+ public static List<Feed> saveFeedlist(int numFeeds, int numItems, boolean withMedia) {
+ return saveFeedlist(numFeeds, numItems, withMedia, false, 0);
}
/**
* Use this method when tests involve chapters.
*/
- public static List<Feed> saveFeedlist(Context context, int numFeeds, int numItems, boolean withMedia,
+ public static List<Feed> saveFeedlist(int numFeeds, int numItems, boolean withMedia,
boolean withChapters, int numChapters) {
if (numFeeds <= 0) {
throw new IllegalArgumentException("numFeeds<=0");
@@ -42,16 +40,16 @@ public class DBTestUtils {
throw new IllegalArgumentException("numItems<0");
}
- List<Feed> feeds = new ArrayList<Feed>();
- PodDBAdapter adapter = new PodDBAdapter(context);
+ List<Feed> feeds = new ArrayList<>();
+ 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,
null, null, "id" + i, null, null, "url" + i, false, new FlattrStatus(), false, null, null, false);
- f.setItems(new ArrayList<FeedItem>());
+ f.setItems(new ArrayList<>());
for (int j = 0; j < numItems; j++) {
FeedItem item = new FeedItem(0, "item " + j, "id" + j, "link" + j, new Date(),
- true, f, withChapters);
+ FeedItem.PLAYED, f, withChapters);
if (withMedia) {
FeedMedia media = new FeedMedia(item, "url" + j, 1, "audio/mp3");
item.setMedia(media);
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
index f3b0c9165..eaae9323c 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
@@ -29,6 +29,7 @@ import de.danoeh.antennapod.core.storage.PodDBAdapter;
* Test class for DBWriter
*/
public class DBWriterTest extends InstrumentationTestCase {
+
private static final String TAG = "DBWriterTest";
private static final String TEST_FOLDER = "testDBWriter";
private static final long TIMEOUT = 5L;
@@ -36,9 +37,10 @@ public class DBWriterTest extends InstrumentationTestCase {
@Override
protected void tearDown() throws Exception {
super.tearDown();
- final Context context = getInstrumentation().getTargetContext();
- assertTrue(PodDBAdapter.deleteDatabase(getInstrumentation().getTargetContext()));
+ assertTrue(PodDBAdapter.deleteDatabase());
+
+ final Context context = getInstrumentation().getTargetContext();
File testDir = context.getExternalFilesDir(TEST_FOLDER);
assertNotNull(testDir);
for (File f : testDir.listFiles()) {
@@ -49,30 +51,61 @@ public class DBWriterTest extends InstrumentationTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
- final Context context = getInstrumentation().getTargetContext();
- context.deleteDatabase(PodDBAdapter.DATABASE_NAME);
- // make sure database is created
- PodDBAdapter adapter = new PodDBAdapter(context);
+
+ // create new database
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.close();
}
+ public void testSetFeedMediaPlaybackInformation() throws IOException, ExecutionException, InterruptedException {
+ 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");
+ List<FeedItem> items = new ArrayList<>();
+ feed.setItems(items);
+ FeedItem item = new FeedItem(0, "Item", "Item", "url", new Date(), FeedItem.PLAYED, feed);
+ items.add(item);
+ 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();
+
+ media.setPosition(POSITION);
+ media.setLastPlayedTime(LAST_PLAYED_TIME);
+ media.setPlayedDuration(PLAYED_DURATION);
+
+ DBWriter.setFeedMediaPlaybackInformation(item.getMedia()).get();
+
+ FeedItem itemFromDb = DBReader.getFeedItem(item.getId());
+ FeedMedia mediaFromDb = itemFromDb.getMedia();
+
+ assertEquals(POSITION, mediaFromDb.getPosition());
+ assertEquals(LAST_PLAYED_TIME, mediaFromDb.getLastPlayedTime());
+ assertEquals(PLAYED_DURATION, mediaFromDb.getPlayedDuration());
+ assertEquals(DURATION, mediaFromDb.getDuration());
+ }
+
public void testDeleteFeedMediaOfItemFileExists() throws IOException, ExecutionException, InterruptedException {
File dest = new File(getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER), "testFile");
assertTrue(dest.createNewFile());
Feed feed = new Feed("url", new Date(), "title");
- List<FeedItem> items = new ArrayList<FeedItem>();
+ List<FeedItem> items = new ArrayList<>();
feed.setItems(items);
- FeedItem item = new FeedItem(0, "Item", "Item", "url", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "Item", "Item", "url", new Date(), FeedItem.PLAYED, feed);
- FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", dest.getAbsolutePath(), "download_url", true, null, 0);
+ FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", dest.getAbsolutePath(), "download_url", true, null, 0, 0);
item.setMedia(media);
items.add(item);
- PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getTargetContext());
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -80,7 +113,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertTrue(item.getId() != 0);
DBWriter.deleteFeedMediaOfItem(getInstrumentation().getTargetContext(), media.getId()).get();
- media = DBReader.getFeedMedia(getInstrumentation().getTargetContext(), media.getId());
+ media = DBReader.getFeedMedia(media.getId());
assertNotNull(media);
assertFalse(dest.exists());
assertFalse(media.isDownloaded());
@@ -92,7 +125,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNotNull(destFolder);
Feed feed = new Feed("url", new Date(), "title");
- feed.setItems(new ArrayList<FeedItem>());
+ feed.setItems(new ArrayList<>());
// create Feed image
File imgFile = new File(destFolder, "image");
@@ -104,21 +137,21 @@ public class DBWriterTest extends InstrumentationTestCase {
List<File> itemFiles = new ArrayList<File>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
- FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed, true);
+ FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed, true);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
assertTrue(enc.createNewFile());
itemFiles.add(enc);
- FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", true, null, 0);
+ FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", true, null, 0, 0);
item.setMedia(media);
item.setChapters(new ArrayList<Chapter>());
item.getChapters().add(new SimpleChapter(0, "item " + i, item, "example.com"));
}
- PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getContext());
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -139,12 +172,12 @@ public class DBWriterTest extends InstrumentationTestCase {
assertFalse(f.exists());
}
- adapter = new PodDBAdapter(getInstrumentation().getContext());
+ adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId());
assertEquals(0, c.getCount());
c.close();
- c = adapter.getImageCursor(image.getId());
+ c = adapter.getImageCursor(String.valueOf(image.getId()));
assertEquals(0, c.getCount());
c.close();
for (FeedItem item : feed.getItems()) {
@@ -156,6 +189,7 @@ public class DBWriterTest extends InstrumentationTestCase {
c.close();
c = adapter.getSimpleChaptersOfFeedItemCursor(item);
assertEquals(0, c.getCount());
+ c.close();
}
}
@@ -164,25 +198,25 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNotNull(destFolder);
Feed feed = new Feed("url", new Date(), "title");
- feed.setItems(new ArrayList<FeedItem>());
+ feed.setItems(new ArrayList<>());
feed.setImage(null);
List<File> itemFiles = new ArrayList<File>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
- FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
assertTrue(enc.createNewFile());
itemFiles.add(enc);
- FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", true, null, 0);
+ FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", true, null, 0, 0);
item.setMedia(media);
}
- PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getContext());
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -200,7 +234,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertFalse(f.exists());
}
- adapter = new PodDBAdapter(getInstrumentation().getContext());
+ adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0);
@@ -229,7 +263,7 @@ public class DBWriterTest extends InstrumentationTestCase {
image.setOwner(feed);
feed.setImage(image);
- PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getContext());
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -242,12 +276,12 @@ public class DBWriterTest extends InstrumentationTestCase {
// check if files still exist
assertFalse(imgFile.exists());
- adapter = new PodDBAdapter(getInstrumentation().getContext());
+ adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0);
c.close();
- c = adapter.getImageCursor(image.getId());
+ c = adapter.getImageCursor(String.valueOf(image.getId()));
assertTrue(c.getCount() == 0);
c.close();
}
@@ -257,7 +291,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNotNull(destFolder);
Feed feed = new Feed("url", new Date(), "title");
- feed.setItems(new ArrayList<FeedItem>());
+ feed.setItems(new ArrayList<>());
// create Feed image
File imgFile = new File(destFolder, "image");
@@ -268,12 +302,12 @@ public class DBWriterTest extends InstrumentationTestCase {
// create items
for (int i = 0; i < 10; i++) {
- FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
}
- PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getContext());
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -289,12 +323,12 @@ public class DBWriterTest extends InstrumentationTestCase {
// check if files still exist
assertFalse(imgFile.exists());
- adapter = new PodDBAdapter(getInstrumentation().getContext());
+ adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0);
c.close();
- c = adapter.getImageCursor(image.getId());
+ c = adapter.getImageCursor(String.valueOf(image.getId()));
assertTrue(c.getCount() == 0);
c.close();
for (FeedItem item : feed.getItems()) {
@@ -309,7 +343,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNotNull(destFolder);
Feed feed = new Feed("url", new Date(), "title");
- feed.setItems(new ArrayList<FeedItem>());
+ feed.setItems(new ArrayList<>());
// create Feed image
File imgFile = new File(destFolder, "image");
@@ -320,14 +354,14 @@ public class DBWriterTest extends InstrumentationTestCase {
// create items with images
for (int i = 0; i < 10; i++) {
- FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
File itemImageFile = new File(destFolder, "item-image-" + i);
FeedImage itemImage = new FeedImage(0, "item-image" + i, itemImageFile.getAbsolutePath(), "url", true);
item.setImage(itemImage);
}
- PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getContext());
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -344,19 +378,19 @@ public class DBWriterTest extends InstrumentationTestCase {
// check if files still exist
assertFalse(imgFile.exists());
- adapter = new PodDBAdapter(getInstrumentation().getContext());
+ adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0);
c.close();
- c = adapter.getImageCursor(image.getId());
+ c = adapter.getImageCursor(String.valueOf(image.getId()));
assertTrue(c.getCount() == 0);
c.close();
for (FeedItem item : feed.getItems()) {
c = adapter.getFeedItemCursor(String.valueOf(item.getId()));
assertTrue(c.getCount() == 0);
c.close();
- c = adapter.getImageCursor(item.getImage().getId());
+ c = adapter.getImageCursor(String.valueOf(item.getImage().getId()));
assertEquals(0, c.getCount());
c.close();
}
@@ -367,7 +401,7 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNotNull(destFolder);
Feed feed = new Feed("url", new Date(), "title");
- feed.setItems(new ArrayList<FeedItem>());
+ feed.setItems(new ArrayList<>());
// create Feed image
File imgFile = new File(destFolder, "image");
@@ -378,17 +412,17 @@ public class DBWriterTest extends InstrumentationTestCase {
List<File> itemFiles = new ArrayList<File>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
- FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
itemFiles.add(enc);
- FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0);
+ FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0, 0);
item.setMedia(media);
}
- PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getContext());
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -417,7 +451,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0);
c.close();
- c = adapter.getImageCursor(image.getId());
+ c = adapter.getImageCursor(String.valueOf(image.getId()));
assertTrue(c.getCount() == 0);
c.close();
for (FeedItem item : feed.getItems()) {
@@ -450,17 +484,17 @@ public class DBWriterTest extends InstrumentationTestCase {
List<File> itemFiles = new ArrayList<File>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
- FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), true, feed);
+ FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
File enc = new File(destFolder, "file " + i);
itemFiles.add(enc);
- FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0);
+ FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0, 0);
item.setMedia(media);
}
- PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getContext());
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -474,12 +508,12 @@ public class DBWriterTest extends InstrumentationTestCase {
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
- adapter = new PodDBAdapter(getInstrumentation().getContext());
+ adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0);
c.close();
- c = adapter.getImageCursor(image.getId());
+ c = adapter.getImageCursor(String.valueOf(image.getId()));
assertTrue(c.getCount() == 0);
c.close();
for (FeedItem item : feed.getItems()) {
@@ -496,11 +530,11 @@ public class DBWriterTest extends InstrumentationTestCase {
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
- FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
- FeedMedia media = new FeedMedia(0, item, 10, 0, 1, "mime", null, "url", false, playbackCompletionDate, 0);
+ 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);
feed.getItems().add(item);
item.setMedia(media);
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -509,13 +543,11 @@ public class DBWriterTest extends InstrumentationTestCase {
}
public void testAddItemToPlaybackHistoryNotPlayedYet() throws ExecutionException, InterruptedException {
- final Context context = getInstrumentation().getTargetContext();
-
FeedMedia media = playbackHistorySetup(null);
- DBWriter.addItemToPlaybackHistory(context, media).get();
- PodDBAdapter adapter = new PodDBAdapter(context);
+ DBWriter.addItemToPlaybackHistory(media).get();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
- media = DBReader.getFeedMedia(context, media.getId());
+ media = DBReader.getFeedMedia(media.getId());
adapter.close();
assertNotNull(media);
@@ -524,13 +556,12 @@ public class DBWriterTest extends InstrumentationTestCase {
public void testAddItemToPlaybackHistoryAlreadyPlayed() throws ExecutionException, InterruptedException {
final long OLD_DATE = 0;
- final Context context = getInstrumentation().getTargetContext();
FeedMedia media = playbackHistorySetup(new Date(OLD_DATE));
- DBWriter.addItemToPlaybackHistory(getInstrumentation().getTargetContext(), media).get();
- PodDBAdapter adapter = new PodDBAdapter(context);
+ DBWriter.addItemToPlaybackHistory(media).get();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
- media = DBReader.getFeedMedia(context, media.getId());
+ media = DBReader.getFeedMedia(media.getId());
adapter.close();
assertNotNull(media);
@@ -541,13 +572,13 @@ 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.setItems(new ArrayList<FeedItem>());
+ 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(), true, feed);
+ FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
}
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -557,7 +588,7 @@ public class DBWriterTest extends InstrumentationTestCase {
}
List<Future<?>> futures = new ArrayList<Future<?>>();
for (FeedItem item : feed.getItems()) {
- futures.add(DBWriter.addQueueItem(context, item.getId()));
+ futures.add(DBWriter.addQueueItem(context, item));
}
for (Future<?> f : futures) {
f.get(TIMEOUT, TimeUnit.SECONDS);
@@ -568,19 +599,19 @@ 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.setItems(new ArrayList<FeedItem>());
- FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
+ feed.setItems(new ArrayList<>());
+ FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
assertTrue(item.getId() != 0);
- DBWriter.addQueueItem(context, item.getId()).get(TIMEOUT, TimeUnit.SECONDS);
+ DBWriter.addQueueItem(context, item).get(TIMEOUT, TimeUnit.SECONDS);
- adapter = new PodDBAdapter(context);
+ adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor cursor = adapter.getQueueIDCursor();
assertTrue(cursor.moveToFirst());
@@ -592,19 +623,19 @@ 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.setItems(new ArrayList<FeedItem>());
- FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), true, feed);
+ feed.setItems(new ArrayList<>());
+ FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
assertTrue(item.getId() != 0);
- DBWriter.addQueueItem(context, item.getId()).get(TIMEOUT, TimeUnit.SECONDS);
+ DBWriter.addQueueItem(context, item).get(TIMEOUT, TimeUnit.SECONDS);
- adapter = new PodDBAdapter(context);
+ adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor cursor = adapter.getQueueIDCursor();
assertTrue(cursor.moveToFirst());
@@ -612,8 +643,8 @@ public class DBWriterTest extends InstrumentationTestCase {
cursor.close();
adapter.close();
- DBWriter.addQueueItem(context, item.getId()).get(TIMEOUT, TimeUnit.SECONDS);
- adapter = new PodDBAdapter(context);
+ DBWriter.addQueueItem(context, item).get(TIMEOUT, TimeUnit.SECONDS);
+ adapter = PodDBAdapter.getInstance();
adapter.open();
cursor = adapter.getQueueIDCursor();
assertTrue(cursor.moveToFirst());
@@ -628,7 +659,7 @@ public class DBWriterTest extends InstrumentationTestCase {
final int NUM_ITEMS = 10;
Feed feed = queueTestSetupMultipleItems(NUM_ITEMS);
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor cursor = adapter.getQueueIDCursor();
assertTrue(cursor.moveToFirst());
@@ -642,12 +673,11 @@ public class DBWriterTest extends InstrumentationTestCase {
}
public void testClearQueue() throws InterruptedException, ExecutionException, TimeoutException {
- final Context context = getInstrumentation().getTargetContext();
final int NUM_ITEMS = 10;
Feed feed = queueTestSetupMultipleItems(NUM_ITEMS);
- DBWriter.clearQueue(context).get(TIMEOUT, TimeUnit.SECONDS);
- PodDBAdapter adapter = new PodDBAdapter(context);
+ DBWriter.clearQueue().get(TIMEOUT, TimeUnit.SECONDS);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor cursor = adapter.getQueueIDCursor();
assertFalse(cursor.moveToFirst());
@@ -659,13 +689,13 @@ public class DBWriterTest extends InstrumentationTestCase {
final int NUM_ITEMS = 10;
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", new Date(), "title");
- feed.setItems(new ArrayList<FeedItem>());
+ 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(), true, feed);
+ FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
}
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -675,13 +705,13 @@ public class DBWriterTest extends InstrumentationTestCase {
}
for (int removeIndex = 0; removeIndex < NUM_ITEMS; removeIndex++) {
final FeedItem item = feed.getItems().get(removeIndex);
- adapter = new PodDBAdapter(context);
+ adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setQueue(feed.getItems());
adapter.close();
DBWriter.removeQueueItem(context, item, false).get(TIMEOUT, TimeUnit.SECONDS);
- adapter = new PodDBAdapter(context);
+ adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor queue = adapter.getQueueIDCursor();
assertTrue(queue.getCount() == NUM_ITEMS - 1);
@@ -703,15 +733,14 @@ public class DBWriterTest extends InstrumentationTestCase {
public void testMoveQueueItem() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
- final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", new Date(), "title");
- feed.setItems(new ArrayList<FeedItem>());
+ 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(), true, feed);
+ FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
}
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -727,13 +756,13 @@ public class DBWriterTest extends InstrumentationTestCase {
Log.d(TAG, String.format("testMoveQueueItem: From=%d, To=%d", from, to));
final long fromID = feed.getItems().get(from).getId();
- adapter = new PodDBAdapter(context);
+ adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setQueue(feed.getItems());
adapter.close();
- DBWriter.moveQueueItem(context, from, to, false).get(TIMEOUT, TimeUnit.SECONDS);
- adapter = new PodDBAdapter(context);
+ DBWriter.moveQueueItem(from, to, false).get(TIMEOUT, TimeUnit.SECONDS);
+ adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor queue = adapter.getQueueIDCursor();
assertTrue(queue.getCount() == NUM_ITEMS);
@@ -749,16 +778,15 @@ public class DBWriterTest extends InstrumentationTestCase {
}
public void testMarkFeedRead() throws InterruptedException, ExecutionException, TimeoutException {
- final Context context = getInstrumentation().getTargetContext();
final int NUM_ITEMS = 10;
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
for (int i = 0; i < NUM_ITEMS; i++) {
- FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), false, feed);
+ FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.UNPLAYED, feed);
feed.getItems().add(item);
}
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -768,24 +796,23 @@ public class DBWriterTest extends InstrumentationTestCase {
assertTrue(item.getId() != 0);
}
- DBWriter.markFeedRead(context, feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
- List<FeedItem> loadedItems = DBReader.getFeedItemList(context, feed);
+ DBWriter.markFeedRead(feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
+ List<FeedItem> loadedItems = DBReader.getFeedItemList(feed);
for (FeedItem item : loadedItems) {
- assertTrue(item.isRead());
+ assertTrue(item.isPlayed());
}
}
public void testMarkAllItemsReadSameFeed() throws InterruptedException, ExecutionException, TimeoutException {
- final Context context = getInstrumentation().getTargetContext();
final int NUM_ITEMS = 10;
Feed feed = new Feed("url", new Date(), "title");
- feed.setItems(new ArrayList<FeedItem>());
+ 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(), false, feed);
+ FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.UNPLAYED, feed);
feed.getItems().add(item);
}
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -795,11 +822,10 @@ public class DBWriterTest extends InstrumentationTestCase {
assertTrue(item.getId() != 0);
}
- DBWriter.markAllItemsRead(context).get(TIMEOUT, TimeUnit.SECONDS);
- List<FeedItem> loadedItems = DBReader.getFeedItemList(context, feed);
+ DBWriter.markAllItemsRead().get(TIMEOUT, TimeUnit.SECONDS);
+ List<FeedItem> loadedItems = DBReader.getFeedItemList(feed);
for (FeedItem item : loadedItems) {
- assertTrue(item.isRead());
+ assertTrue(item.isPlayed());
}
}
-
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
index 0326174e3..e456f3891 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
@@ -13,14 +13,13 @@ import java.util.Arrays;
import java.util.List;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.DefaultOnlineFeedViewActivity;
import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import de.danoeh.antennapod.fragment.AllEpisodesFragment;
import de.danoeh.antennapod.fragment.DownloadsFragment;
-import de.danoeh.antennapod.fragment.NewEpisodesFragment;
+import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.preferences.PreferenceController;
@@ -42,17 +41,21 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
@Override
protected void setUp() throws Exception {
super.setUp();
- solo = new Solo(getInstrumentation(), getActivity());
uiTestUtils = new UITestUtils(getInstrumentation().getTargetContext());
uiTestUtils.setup();
- // create database
- PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getTargetContext());
+
+ // create new database
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
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();
+
+ solo = new Solo(getInstrumentation(), getActivity());
}
@Override
@@ -60,7 +63,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
uiTestUtils.tearDown();
solo.finishOpenedActivities();
- PodDBAdapter.deleteDatabase(getInstrumentation().getTargetContext());
+ PodDBAdapter.deleteDatabase();
// reset preferences
prefs.edit().clear().commit();
@@ -69,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 {
@@ -78,8 +82,8 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
openNavDrawer();
solo.clickOnText(solo.getString(R.string.add_feed_label));
solo.enterText(0, feed.getDownload_url());
- solo.clickOnButton(0);
- solo.waitForActivity(DefaultOnlineFeedViewActivity.class);
+ solo.clickOnButton(solo.getString(R.string.confirm_label));
+ solo.waitForActivity(OnlineFeedViewActivity.class);
solo.waitForView(R.id.butSubscribe);
assertEquals(solo.getString(R.string.subscribe_label), solo.getButton(0).getText().toString());
solo.clickOnButton(0);
@@ -90,7 +94,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
public void testClickNavDrawer() throws Exception {
uiTestUtils.addLocalFeedData(false);
- UserPreferences.setHiddenDrawerItems(getInstrumentation().getTargetContext(), new ArrayList<String>());
+ UserPreferences.setHiddenDrawerItems(new ArrayList<String>());
// queue
openNavDrawer();
@@ -98,17 +102,11 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
solo.waitForView(android.R.id.list);
assertEquals(solo.getString(R.string.queue_label), getActionbarTitle());
- // new episodes
+ // episodes
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.new_episodes_label));
+ solo.clickOnText(solo.getString(R.string.episodes_label));
solo.waitForView(android.R.id.list);
- assertEquals(solo.getString(R.string.new_episodes_label), getActionbarTitle());
-
- // all episodes
- openNavDrawer();
- solo.clickOnText(solo.getString(R.string.all_episodes_label));
- solo.waitForView(android.R.id.list);
- assertEquals(solo.getString(R.string.all_episodes_label), getActionbarTitle());
+ assertEquals(solo.getString(R.string.episodes_label), getActionbarTitle());
// downloads
openNavDrawer();
@@ -129,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
@@ -141,7 +139,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
}
private String getActionbarTitle() {
- return ((MainActivity)solo.getCurrentActivity()).getMainActivtyActionBar().getTitle().toString();
+ return ((MainActivity) solo.getCurrentActivity()).getSupportActionBar().getTitle().toString();
}
@FlakyTest(tolerance = 3)
@@ -152,23 +150,23 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
}
public void testDrawerPreferencesHideSomeElements() {
- UserPreferences.setHiddenDrawerItems(getInstrumentation().getTargetContext(), new ArrayList<String>());
+ UserPreferences.setHiddenDrawerItems(new ArrayList<String>());
openNavDrawer();
solo.clickLongOnText(solo.getString(R.string.queue_label));
solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.all_episodes_label));
+ solo.clickOnText(solo.getString(R.string.episodes_label));
solo.clickOnText(solo.getString(R.string.playback_history_label));
solo.clickOnText(solo.getString(R.string.confirm_label));
solo.waitForDialogToClose();
List<String> hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(2, hidden.size());
- assertTrue(hidden.contains(AllEpisodesFragment.TAG));
+ assertTrue(hidden.contains(EpisodesFragment.TAG));
assertTrue(hidden.contains(PlaybackHistoryFragment.TAG));
}
public void testDrawerPreferencesUnhideSomeElements() {
- List<String> hidden = Arrays.asList(NewEpisodesFragment.TAG, DownloadsFragment.TAG);
- UserPreferences.setHiddenDrawerItems(getInstrumentation().getTargetContext(), hidden);
+ List<String> hidden = Arrays.asList(PlaybackHistoryFragment.TAG, DownloadsFragment.TAG);
+ UserPreferences.setHiddenDrawerItems(hidden);
openNavDrawer();
solo.clickLongOnText(solo.getString(R.string.queue_label));
solo.waitForDialogToOpen();
@@ -179,30 +177,30 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(2, hidden.size());
assertTrue(hidden.contains(QueueFragment.TAG));
- assertTrue(hidden.contains(NewEpisodesFragment.TAG));
+ assertTrue(hidden.contains(PlaybackHistoryFragment.TAG));
}
public void testDrawerPreferencesHideAllElements() {
- UserPreferences.setHiddenDrawerItems(getInstrumentation().getTargetContext(), new ArrayList<String>());
+ UserPreferences.setHiddenDrawerItems(new ArrayList<String>());
String[] titles = getInstrumentation().getTargetContext().getResources().getStringArray(R.array.nav_drawer_titles);
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(6, hidden.size());
- for(String tag : MainActivity.NAV_DRAWER_TAGS) {
+ assertEquals(titles.length, hidden.size());
+ for (String tag : MainActivity.NAV_DRAWER_TAGS) {
assertTrue(hidden.contains(tag));
}
}
public void testDrawerPreferencesHideCurrentElement() {
- UserPreferences.setHiddenDrawerItems(getInstrumentation().getTargetContext(), new ArrayList<String>());
+ UserPreferences.setHiddenDrawerItems(new ArrayList<String>());
openNavDrawer();
String downloads = solo.getString(R.string.downloads_label);
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
new file mode 100644
index 000000000..d4994fb1a
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
@@ -0,0 +1,274 @@
+package de.test.antennapod.ui;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.preference.PreferenceManager;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.FlakyTest;
+import android.view.View;
+import android.widget.ListView;
+
+import com.robotium.solo.Solo;
+import com.robotium.solo.Timeout;
+
+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.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;
+
+/**
+ * test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
+ */
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActivity> {
+
+ private static final String TAG = PlaybackTest.class.getSimpleName();
+ public static final int EPISODES_DRAWER_LIST_INDEX = 1;
+ public static final int QUEUE_DRAWER_LIST_INDEX = 0;
+
+ private Solo solo;
+ private UITestUtils uiTestUtils;
+
+ private Context context;
+
+ public PlaybackSonicTest() {
+ super(MainActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ context = getInstrumentation().getTargetContext();
+
+ PodDBAdapter.init(context);
+ PodDBAdapter.deleteDatabase();
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit()
+ .clear()
+ .putBoolean(UserPreferences.PREF_UNPAUSE_ON_HEADSET_RECONNECT, false)
+ .putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false)
+ .putBoolean(UserPreferences.PREF_SONIC, true)
+ .commit();
+
+ solo = new Solo(getInstrumentation(), getActivity());
+
+ uiTestUtils = new UITestUtils(context);
+ uiTestUtils.setup();
+
+ // create database
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.close();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ solo.finishOpenedActivities();
+ uiTestUtils.tearDown();
+
+ // shut down playback service
+ skipEpisode();
+ context.sendBroadcast(new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
+
+ super.tearDown();
+ }
+
+ private void openNavDrawer() {
+ solo.clickOnImageButton(0);
+ getInstrumentation().waitForIdleSync();
+ }
+
+ private void setContinuousPlaybackPreference(boolean value) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit();
+ }
+
+ private void skipEpisode() {
+ Intent skipIntent = new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
+ context.sendBroadcast(skipIntent);
+ }
+
+ private void startLocalPlayback() {
+ openNavDrawer();
+ // if we try to just click on plain old text then
+ // we might wind up clicking on the fragment title and not
+ // the drawer element like we want.
+ ListView drawerView = (ListView)solo.getView(R.id.nav_list);
+ // this should be 'Episodes'
+ View targetView = drawerView.getChildAt(EPISODES_DRAWER_LIST_INDEX);
+ solo.waitForView(targetView);
+ solo.clickOnView(targetView);
+ getInstrumentation().waitForIdleSync();
+ solo.waitForText(solo.getString(R.string.all_episodes_short_label));
+ solo.clickOnText(solo.getString(R.string.all_episodes_short_label));
+ getInstrumentation().waitForIdleSync();
+
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(10);
+ assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
+
+ solo.clickOnView(solo.getView(R.id.butSecondaryAction));
+ long mediaId = episodes.get(0).getMedia().getId();
+ boolean playing = solo.waitForCondition(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
+ } else {
+ return false;
+ }
+ }, Timeout.getSmallTimeout());
+ assertTrue(playing);
+ }
+
+ private void startLocalPlaybackFromQueue() {
+ openNavDrawer();
+
+ // if we try to just click on plain old text then
+ // we might wind up clicking on the fragment title and not
+ // the drawer element like we want.
+ ListView drawerView = (ListView)solo.getView(R.id.nav_list);
+ // this should be 'Queue'
+ View targetView = drawerView.getChildAt(QUEUE_DRAWER_LIST_INDEX);
+ solo.waitForView(targetView);
+ getInstrumentation().waitForIdleSync();
+ solo.clickOnView(targetView);
+ assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
+
+ final List<FeedItem> queue = DBReader.getQueue();
+ solo.clickOnImageButton(1);
+ assertTrue(solo.waitForView(solo.getView(R.id.butPlay)));
+ long mediaId = queue.get(0).getMedia().getId();
+ boolean playing = solo.waitForCondition(() -> {
+ if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
+ } else {
+ return false;
+ }
+ }, Timeout.getSmallTimeout());
+ assertTrue(playing);
+ }
+
+ public void testStartLocal() throws Exception {
+ uiTestUtils.addLocalFeedData(true);
+ DBWriter.clearQueue().get();
+ startLocalPlayback();
+ }
+
+ public void testContinousPlaybackOffSingleEpisode() throws Exception {
+ setContinuousPlaybackPreference(false);
+ uiTestUtils.addLocalFeedData(true);
+ DBWriter.clearQueue().get();
+ startLocalPlayback();
+ }
+
+ @FlakyTest(tolerance = 3)
+ public void testContinousPlaybackOffMultipleEpisodes() throws Exception {
+ setContinuousPlaybackPreference(false);
+ uiTestUtils.addLocalFeedData(true);
+ List<FeedItem> queue = DBReader.getQueue();
+ final FeedItem first = queue.get(0);
+
+ startLocalPlaybackFromQueue();
+ boolean stopped = solo.waitForCondition(() -> {
+ if (uiTestUtils.getPlaybackController(getActivity()).getStatus()
+ != PlayerStatus.PLAYING) {
+ return true;
+ } else if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId()
+ != first.getMedia().getId();
+ } else {
+ return true;
+ }
+ }, Timeout.getSmallTimeout());
+ assertTrue(stopped);
+ Thread.sleep(1000);
+ PlayerStatus status = uiTestUtils.getPlaybackController(getActivity()).getStatus();
+ assertFalse(status.equals(PlayerStatus.PLAYING));
+ }
+
+ @FlakyTest(tolerance = 3)
+ public void testContinuousPlaybackOnMultipleEpisodes() throws Exception {
+ setContinuousPlaybackPreference(true);
+ uiTestUtils.addLocalFeedData(true);
+ List<FeedItem> queue = DBReader.getQueue();
+ final FeedItem first = queue.get(0);
+ final FeedItem second = queue.get(1);
+
+ startLocalPlaybackFromQueue();
+ boolean firstPlaying = solo.waitForCondition(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId()
+ == first.getMedia().getId();
+ } else {
+ return false;
+ }
+ }, Timeout.getSmallTimeout());
+ assertTrue(firstPlaying);
+ boolean secondPlaying = solo.waitForCondition(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId()
+ == second.getMedia().getId();
+ } else {
+ return false;
+ }
+ }, Timeout.getLargeTimeout());
+ assertTrue(secondPlaying);
+ }
+
+ /**
+ * Check if an episode can be played twice without problems.
+ */
+ private void replayEpisodeCheck(boolean followQueue) throws Exception {
+ setContinuousPlaybackPreference(followQueue);
+ uiTestUtils.addLocalFeedData(true);
+ DBWriter.clearQueue().get();
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(10);
+
+ startLocalPlayback();
+ long mediaId = episodes.get(0).getMedia().getId();
+ boolean startedPlaying = solo.waitForCondition(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
+ } else {
+ return false;
+ }
+ }, Timeout.getSmallTimeout());
+ assertTrue(startedPlaying);
+
+ boolean stoppedPlaying = solo.waitForCondition(() -> {
+ return uiTestUtils.getCurrentMedia(getActivity()) == null
+ || uiTestUtils.getCurrentMedia(getActivity()).getId() != mediaId;
+ }, Timeout.getLargeTimeout());
+ assertTrue(stoppedPlaying);
+
+ startLocalPlayback();
+ boolean startedReplay = solo.waitForCondition(() -> {
+ if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
+ } else {
+ return false;
+ }
+ }, Timeout.getLargeTimeout());
+ assertTrue(startedReplay);
+ }
+
+ public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
+ replayEpisodeCheck(true);
+ }
+
+ public void testReplayEpisodeContinuousPlaybackOff() throws Exception {
+ replayEpisodeCheck(false);
+ }
+
+
+}
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 775bc0ecd..5ce495f9a 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
@@ -5,8 +5,10 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.test.ActivityInstrumentationTestCase2;
+import android.test.FlakyTest;
+import android.view.View;
+import android.widget.ListView;
-import com.robotium.solo.Condition;
import com.robotium.solo.Solo;
import com.robotium.solo.Timeout;
@@ -17,15 +19,20 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.FeedItem;
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;
/**
- * Test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
+ * test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
*/
public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity> {
+ private static final String TAG = PlaybackTest.class.getSimpleName();
+ public static final int EPISODES_DRAWER_LIST_INDEX = 1;
+ public static final int QUEUE_DRAWER_LIST_INDEX = 0;
+
private Solo solo;
private UITestUtils uiTestUtils;
@@ -38,30 +45,34 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
@Override
public void setUp() throws Exception {
super.setUp();
- solo = new Solo(getInstrumentation(), getActivity());
- context = getInstrumentation().getContext();
- uiTestUtils = new UITestUtils(context);
- uiTestUtils.setup();
+ context = getInstrumentation().getTargetContext();
- // create database
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- adapter.close();
+ PodDBAdapter.init(context);
+ PodDBAdapter.deleteDatabase();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit()
+ .clear()
.putBoolean(UserPreferences.PREF_UNPAUSE_ON_HEADSET_RECONNECT, false)
.putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false)
- .putString(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS, "")
.commit();
+
+ solo = new Solo(getInstrumentation(), getActivity());
+
+ uiTestUtils = new UITestUtils(context);
+ uiTestUtils.setup();
+
+ // create database
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.close();
}
@Override
public void tearDown() throws Exception {
- uiTestUtils.tearDown();
solo.finishOpenedActivities();
- PodDBAdapter.deleteDatabase(context);
+ uiTestUtils.tearDown();
// shut down playback service
skipEpisode();
@@ -69,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) {
@@ -86,86 +97,123 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
private void startLocalPlayback() {
openNavDrawer();
-
- solo.clickOnText(solo.getString(R.string.all_episodes_label));
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(context, 10);
+ // if we try to just click on plain old text then
+ // we might wind up clicking on the fragment title and not
+ // the drawer element like we want.
+ ListView drawerView = (ListView)solo.getView(R.id.nav_list);
+ // this should be 'Episodes'
+ View targetView = drawerView.getChildAt(EPISODES_DRAWER_LIST_INDEX);
+ solo.waitForView(targetView);
+ solo.clickOnView(targetView);
+ solo.waitForText(solo.getString(R.string.all_episodes_short_label));
+ solo.clickOnText(solo.getString(R.string.all_episodes_short_label));
+
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(10);
assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
solo.clickOnView(solo.getView(R.id.butSecondaryAction));
- assertTrue(solo.waitForView(solo.getView(R.id.butPlay)));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return episodes.get(0).getMedia().isCurrentlyPlaying();
+ long mediaId = episodes.get(0).getMedia().getId();
+ boolean playing = solo.waitForCondition(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
+ } else {
+ return false;
}
- }, Timeout.getLargeTimeout());
+ }, Timeout.getSmallTimeout());
+ assertTrue(playing);
}
private void startLocalPlaybackFromQueue() {
+ openNavDrawer();
+ // if we try to just click on plain old text then
+ // we might wind up clicking on the fragment title and not
+ // the drawer element like we want.
+ ListView drawerView = (ListView)solo.getView(R.id.nav_list);
+ // this should be 'Queue'
+ View targetView = drawerView.getChildAt(QUEUE_DRAWER_LIST_INDEX);
+ solo.waitForView(targetView);
+ solo.clickOnView(targetView);
assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
- final List<FeedItem> queue = DBReader.getQueue(context);
+
+ final List<FeedItem> queue = DBReader.getQueue();
solo.clickOnImageButton(1);
assertTrue(solo.waitForView(solo.getView(R.id.butPlay)));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return queue.get(0).getMedia().isCurrentlyPlaying();
+ long mediaId = queue.get(0).getMedia().getId();
+ boolean playing = solo.waitForCondition(() -> {
+ if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
+ } else {
+ return false;
}
- }, Timeout.getLargeTimeout());
+ }, Timeout.getSmallTimeout());
+
+ assertTrue(playing);
}
public void testStartLocal() throws Exception {
uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue(context).get();
+ DBWriter.clearQueue().get();
startLocalPlayback();
}
public void testContinousPlaybackOffSingleEpisode() throws Exception {
setContinuousPlaybackPreference(false);
uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue(context).get();
+ DBWriter.clearQueue().get();
startLocalPlayback();
}
-
+ @FlakyTest(tolerance = 3)
public void testContinousPlaybackOffMultipleEpisodes() throws Exception {
setContinuousPlaybackPreference(false);
uiTestUtils.addLocalFeedData(true);
- List<FeedItem> queue = DBReader.getQueue(context);
+ List<FeedItem> queue = DBReader.getQueue();
final FeedItem first = queue.get(0);
- final FeedItem second = queue.get(1);
-
startLocalPlaybackFromQueue();
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return first.getMedia().isCurrentlyPlaying() == false;
+ boolean stopped = solo.waitForCondition(() -> {
+ if (uiTestUtils.getPlaybackController(getActivity()).getStatus()
+ != PlayerStatus.PLAYING) {
+ return true;
+ } else if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId()
+ != first.getMedia().getId();
+ } else {
+ return true;
}
- }, 10000);
+ }, Timeout.getSmallTimeout());
+ assertTrue(stopped);
Thread.sleep(1000);
- assertTrue(second.getMedia().isCurrentlyPlaying() == false);
+ PlayerStatus status = uiTestUtils.getPlaybackController(getActivity()).getStatus();
+ assertFalse(status.equals(PlayerStatus.PLAYING));
}
+ @FlakyTest(tolerance = 3)
public void testContinuousPlaybackOnMultipleEpisodes() throws Exception {
setContinuousPlaybackPreference(true);
uiTestUtils.addLocalFeedData(true);
- List<FeedItem> queue = DBReader.getQueue(context);
+ List<FeedItem> queue = DBReader.getQueue();
final FeedItem first = queue.get(0);
final FeedItem second = queue.get(1);
startLocalPlaybackFromQueue();
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return first.getMedia().isCurrentlyPlaying() == false;
+ boolean firstPlaying = solo.waitForCondition(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId()
+ == first.getMedia().getId();
+ } else {
+ return false;
}
- }, 10000);
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return second.getMedia().isCurrentlyPlaying() == true;
+ }, Timeout.getSmallTimeout());
+ assertTrue(firstPlaying);
+ boolean secondPlaying = solo.waitForCondition(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId()
+ == second.getMedia().getId();
+ } else {
+ return false;
}
- }, 10000);
+ }, Timeout.getLargeTimeout());
+ assertTrue(secondPlaying);
}
/**
@@ -174,24 +222,35 @@ public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity>
private void replayEpisodeCheck(boolean followQueue) throws Exception {
setContinuousPlaybackPreference(followQueue);
uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue(context).get();
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(context, 10);
+ DBWriter.clearQueue().get();
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(10);
startLocalPlayback();
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return false == episodes.get(0).getMedia().isCurrentlyPlaying();
+ long mediaId = episodes.get(0).getMedia().getId();
+ boolean startedPlaying = solo.waitForCondition(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
+ } else {
+ return false;
}
+ }, Timeout.getSmallTimeout());
+ assertTrue(startedPlaying);
+
+ boolean stoppedPlaying = solo.waitForCondition(() -> {
+ return uiTestUtils.getCurrentMedia(getActivity()) == null
+ || uiTestUtils.getCurrentMedia(getActivity()).getId() != mediaId;
}, Timeout.getLargeTimeout());
+ assertTrue(stoppedPlaying);
startLocalPlayback();
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return false == episodes.get(0).getMedia().isCurrentlyPlaying();
+ boolean startedReplay = solo.waitForCondition(() -> {
+ if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
+ } else {
+ return false;
}
}, Timeout.getLargeTimeout());
+ assertTrue(startedReplay);
}
public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
index eb1cb9c71..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,21 +3,19 @@ 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;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
+import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
+import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
+import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
public class PreferencesTest extends ActivityInstrumentationTestCase2<PreferenceActivity> {
@@ -39,7 +37,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
Timeout.setLargeTimeout(1000);
context = getInstrumentation().getTargetContext();
res = getActivity().getResources();
- UserPreferences.createInstance(context);
+ UserPreferences.init(context);
}
@Override
@@ -59,12 +57,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
solo.clickOnText(solo.getString(R.string.pref_set_theme_title));
solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(otherTheme));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return UserPreferences.getTheme() != theme;
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
}
public void testSwitchThemeBack() {
@@ -78,210 +71,122 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
solo.clickOnText(solo.getString(R.string.pref_set_theme_title));
solo.waitForDialogToOpen(1000);
solo.clickOnText(solo.getString(otherTheme));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return UserPreferences.getTheme() != theme;
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
}
public void testExpandNotification() {
final int priority = UserPreferences.getNotifyPriority();
solo.clickOnText(solo.getString(R.string.pref_expandNotify_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return priority != UserPreferences.getNotifyPriority();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> priority != UserPreferences.getNotifyPriority(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_expandNotify_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return priority == UserPreferences.getNotifyPriority();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> priority == UserPreferences.getNotifyPriority(), Timeout.getLargeTimeout()));
}
public void testEnablePersistentPlaybackControls() {
final boolean persistNotify = UserPreferences.isPersistNotify();
solo.clickOnText(solo.getString(R.string.pref_persistNotify_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return persistNotify != UserPreferences.isPersistNotify();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> persistNotify != UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_persistNotify_title));
- solo.waitForCondition(new Condition() {
- @Override public boolean isSatisfied() {
- return persistNotify == UserPreferences.isPersistNotify();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> persistNotify == UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
}
public void testEnqueueAtFront() {
final boolean enqueueAtFront = UserPreferences.enqueueAtFront();
solo.clickOnText(solo.getString(R.string.pref_queueAddToFront_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return enqueueAtFront != UserPreferences.enqueueAtFront();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> enqueueAtFront != UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_queueAddToFront_title));
- solo.waitForCondition(new Condition() {
- @Override public boolean isSatisfied() {
- return enqueueAtFront == UserPreferences.enqueueAtFront();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> enqueueAtFront == UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
}
public void testHeadPhonesDisconnect() {
final boolean pauseOnHeadsetDisconnect = UserPreferences.isPauseOnHeadsetDisconnect();
solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
- solo.waitForCondition(new Condition() {
- @Override public boolean isSatisfied() {
- return pauseOnHeadsetDisconnect != UserPreferences.isPauseOnHeadsetDisconnect();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect != UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
- solo.waitForCondition(new Condition() {
- @Override public boolean isSatisfied() {
- return pauseOnHeadsetDisconnect == UserPreferences.isPauseOnHeadsetDisconnect();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect == UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
}
public void testHeadPhonesReconnect() {
if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return UserPreferences.isPauseOnHeadsetDisconnect();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
}
final boolean unpauseOnHeadsetReconnect = UserPreferences.isUnpauseOnHeadsetReconnect();
solo.clickOnText(solo.getString(R.string.pref_unpauseOnHeadsetReconnect_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return unpauseOnHeadsetReconnect != UserPreferences.isUnpauseOnHeadsetReconnect();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> unpauseOnHeadsetReconnect != UserPreferences.isUnpauseOnHeadsetReconnect(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_unpauseOnHeadsetReconnect_title));
- solo.waitForCondition(new Condition() {
- @Override public boolean isSatisfied() {
- return unpauseOnHeadsetReconnect == UserPreferences.isUnpauseOnHeadsetReconnect();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> unpauseOnHeadsetReconnect == UserPreferences.isUnpauseOnHeadsetReconnect(), Timeout.getLargeTimeout()));
+ }
+
+ public void testBluetoothReconnect() {
+ if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
+ solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
+ assertTrue(solo.waitForCondition(() -> UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
+ }
+ final boolean unpauseOnBluetoothReconnect = UserPreferences.isUnpauseOnBluetoothReconnect();
+ solo.clickOnText(solo.getString(R.string.pref_unpauseOnBluetoothReconnect_title));
+ assertTrue(solo.waitForCondition(() -> unpauseOnBluetoothReconnect != UserPreferences.isUnpauseOnBluetoothReconnect(), Timeout.getLargeTimeout()));
+ solo.clickOnText(solo.getString(R.string.pref_unpauseOnBluetoothReconnect_title));
+ assertTrue(solo.waitForCondition(() -> unpauseOnBluetoothReconnect == UserPreferences.isUnpauseOnBluetoothReconnect(), Timeout.getLargeTimeout()));
}
public void testContinuousPlayback() {
final boolean continuousPlayback = UserPreferences.isFollowQueue();
solo.clickOnText(solo.getString(R.string.pref_followQueue_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return continuousPlayback != UserPreferences.isFollowQueue();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> continuousPlayback != UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_followQueue_title));
- solo.waitForCondition(new Condition() {
- @Override public boolean isSatisfied() {
- return continuousPlayback == UserPreferences.isFollowQueue();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> continuousPlayback == UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
}
public void testAutoDelete() {
final boolean autoDelete = UserPreferences.isAutoDelete();
solo.clickOnText(solo.getString(R.string.pref_auto_delete_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return autoDelete != UserPreferences.isAutoDelete();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> autoDelete != UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_auto_delete_title));
- solo.waitForCondition(new Condition() {
- @Override public boolean isSatisfied() {
- return autoDelete == UserPreferences.isAutoDelete();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> autoDelete == UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
}
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);
}
public void testPauseForInterruptions() {
final boolean pauseForFocusLoss = UserPreferences.shouldPauseForFocusLoss();
solo.clickOnText(solo.getString(R.string.pref_pausePlaybackForFocusLoss_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss();
- }
- }, Timeout.getLargeTimeout());
- solo.clickOnText(solo.getString(R.string.pref_auto_delete_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
+ solo.clickOnText(solo.getString(R.string.pref_pausePlaybackForFocusLoss_title));
+ assertTrue(solo.waitForCondition(() -> pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
}
public void testDisableUpdateInterval() {
- solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervall_title));
+ solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_sum));
solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.pref_update_interval_hours_manual));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return UserPreferences.getUpdateInterval() == 0;
- }
- }, 1000);
+ solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_Disable));
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() == 0, 1000));
}
public void testSetUpdateInterval() {
- solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervall_title));
+ solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_title));
+ solo.waitForDialogToOpen();
+ solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
solo.waitForDialogToOpen();
String search = "12 " + solo.getString(R.string.pref_update_interval_hours_plural);
solo.clickOnText(search);
solo.waitForDialogToClose();
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return UserPreferences.getUpdateInterval() == 12;
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() ==
+ TimeUnit.HOURS.toMillis(12), Timeout.getLargeTimeout()));
}
public void testMobileUpdates() {
final boolean mobileUpdates = UserPreferences.isAllowMobileUpdate();
solo.clickOnText(solo.getString(R.string.pref_mobileUpdate_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return mobileUpdates != UserPreferences.isAllowMobileUpdate();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> mobileUpdates != UserPreferences.isAllowMobileUpdate(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_mobileUpdate_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return mobileUpdates == UserPreferences.isAllowMobileUpdate();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> mobileUpdates == UserPreferences.isAllowMobileUpdate(), Timeout.getLargeTimeout()));
}
public void testSetSequentialDownload() {
@@ -290,12 +195,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
solo.clearEditText(0);
solo.enterText(0, "1");
solo.clickOnText(solo.getString(android.R.string.ok));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return UserPreferences.getParallelDownloads() == 1;
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 1, Timeout.getLargeTimeout()));
}
public void testSetParallelDownloads() {
@@ -304,12 +204,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
solo.clearEditText(0);
solo.enterText(0, "10");
solo.clickOnText(solo.getString(android.R.string.ok));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return UserPreferences.getParallelDownloads() == 10;
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 10, Timeout.getLargeTimeout()));
}
public void testSetParallelDownloadsInvalidInput() {
@@ -328,15 +223,12 @@ 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);
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return UserPreferences.getEpisodeCacheSize() == value;
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == value, Timeout.getLargeTimeout()));
}
public void testSetEpisodeCacheMin() {
@@ -344,33 +236,32 @@ 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();
solo.clickOnText(minEntry);
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return UserPreferences.getEpisodeCacheSize() == minValue;
- }
- }, Timeout.getLargeTimeout());
+ 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);
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return UserPreferences.getEpisodeCacheSize() == maxValue;
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == maxValue, Timeout.getLargeTimeout()));
}
public void testAutomaticDownload() {
@@ -378,76 +269,73 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
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_automatic_download_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return automaticDownload != UserPreferences.isEnableAutodownload();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> automaticDownload != UserPreferences.isEnableAutodownload(), Timeout.getLargeTimeout()));
if(UserPreferences.isEnableAutodownload() == false) {
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
}
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return UserPreferences.isEnableAutodownload() == true;
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> UserPreferences.isEnableAutodownload() == true, Timeout.getLargeTimeout()));
final boolean enableAutodownloadOnBattery = UserPreferences.isEnableAutodownloadOnBattery();
solo.clickOnText(solo.getString(R.string.pref_automatic_download_on_battery_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return enableAutodownloadOnBattery != UserPreferences.isEnableAutodownloadOnBattery();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> enableAutodownloadOnBattery != UserPreferences.isEnableAutodownloadOnBattery(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_on_battery_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery(), Timeout.getLargeTimeout()));
final boolean enableWifiFilter = UserPreferences.isEnableAutodownloadWifiFilter();
solo.clickOnText(solo.getString(R.string.pref_autodl_wifi_filter_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return enableWifiFilter != UserPreferences.isEnableAutodownloadWifiFilter();
- }
- }, Timeout.getLargeTimeout());
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_on_battery_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return enableWifiFilter == UserPreferences.isEnableAutodownloadWifiFilter();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> enableWifiFilter != UserPreferences.isEnableAutodownloadWifiFilter(), Timeout.getLargeTimeout()));
+ solo.clickOnText(solo.getString(R.string.pref_autodl_wifi_filter_title));
+ assertTrue(solo.waitForCondition(() -> enableWifiFilter == UserPreferences.isEnableAutodownloadWifiFilter(), Timeout.getLargeTimeout()));
}
- @FlakyTest(tolerance = 3)
- public void testAbout() throws IOException {
- int numViews = 0, numLinks = 0;
- InputStream input = getActivity().getResources().getAssets().open("about.html");
- List<String> lines = IOUtils.readLines(input);
- input.close();
- for(String line : lines) {
- if(line.contains("(View)")) {
- numViews++;
- } else if(line.contains("(Link)")) {
- numLinks++;
- }
- }
- for(int i=0; i < numViews; i++) {
- solo.clickOnText(solo.getString(R.string.about_pref));
- solo.clickOnText("(View)", i);
- solo.goBack();
- }
- for(int i=0; i < numLinks; i++) {
- solo.clickOnText(solo.getString(R.string.about_pref));
- solo.clickOnText("(Link)", i);
- solo.goBack();
- }
+ public void testEpisodeCleanupQueueOnly() {
+ solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
+ solo.waitForText(solo.getString(R.string.episode_cleanup_queue_removal));
+ solo.clickOnText(solo.getString(R.string.episode_cleanup_queue_removal));
+ assertTrue(solo.waitForCondition(() -> {
+ EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
+ return alg instanceof APQueueCleanupAlgorithm;
+ },
+ Timeout.getLargeTimeout()));
+ }
+
+ public void testEpisodeCleanupNeverAlg() {
+ solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
+ solo.waitForText(solo.getString(R.string.episode_cleanup_never));
+ solo.clickOnText(solo.getString(R.string.episode_cleanup_never));
+ assertTrue(solo.waitForCondition(() -> {
+ EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
+ return alg instanceof APNullCleanupAlgorithm;
+ },
+ Timeout.getLargeTimeout()));
+ }
+
+ public void testEpisodeCleanupClassic() {
+ solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
+ solo.waitForText(solo.getString(R.string.episode_cleanup_after_listening));
+ solo.clickOnText(solo.getString(R.string.episode_cleanup_after_listening));
+ assertTrue(solo.waitForCondition(() -> {
+ EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
+ if (alg instanceof APCleanupAlgorithm) {
+ APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
+ return cleanupAlg.getNumberOfDaysAfterPlayback() == 0;
+ }
+ return false;
+ },
+ Timeout.getLargeTimeout()));
+ }
+
+ public void testEpisodeCleanupNumDays() {
+ solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
+ solo.waitForText(solo.getString(R.string.episode_cleanup_after_listening));
+ solo.clickOnText("5");
+ assertTrue(solo.waitForCondition(() -> {
+ EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
+ if (alg instanceof APCleanupAlgorithm) {
+ APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
+ return cleanupAlg.getNumberOfDaysAfterPlayback() == 5;
+ }
+ return false;
+ },
+ Timeout.getLargeTimeout()));
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
index 613826932..13abbb1cc 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
@@ -4,6 +4,7 @@ import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Build;
+import android.util.Log;
import junit.framework.Assert;
@@ -20,13 +21,16 @@ import java.util.Date;
import java.util.List;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.QueueEvent;
+import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
import de.greenrobot.event.EventBus;
import de.test.antennapod.util.service.download.HTTPBin;
import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
@@ -38,12 +42,15 @@ import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class UITestUtils {
+ private static final String TAG = UITestUtils.class.getSimpleName();
+
private static final String DATA_FOLDER = "test/UITestUtils";
public static final int NUM_FEEDS = 5;
public static final int NUM_ITEMS_PER_FEED = 10;
public static final int HOME_VIEW = (Build.VERSION.SDK_INT >= 11) ? android.R.id.home : R.id.home;
+ public static final String TEST_FILE_NAME = "3sec.mp3";
private Context context;
@@ -79,7 +86,7 @@ public class UITestUtils {
server.stop();
if (localFeedDataAdded) {
- PodDBAdapter.deleteDatabase(context);
+ PodDBAdapter.deleteDatabase();
}
}
@@ -111,9 +118,12 @@ public class UITestUtils {
private File newMediaFile(String name) throws IOException {
File mediaFile = new File(hostedMediaDir, name);
+ if(mediaFile.exists()) {
+ mediaFile.delete();
+ }
Assert.assertFalse(mediaFile.exists());
- InputStream in = context.getAssets().open("testfile.mp3");
+ InputStream in = context.getAssets().open(TEST_FILE_NAME);
Assert.assertNotNull(in);
FileOutputStream out = new FileOutputStream(mediaFile);
@@ -142,11 +152,11 @@ public class UITestUtils {
List<FeedItem> items = new ArrayList<FeedItem>();
for (int j = 0; j < NUM_ITEMS_PER_FEED; j++) {
FeedItem item = new FeedItem(j, "Feed " + (i+1) + ": Item " + (j+1), "item" + j,
- "http://example.com/feed" + i + "/item/" + j, new Date(), false, feed);
+ "http://example.com/feed" + i + "/item/" + j, new Date(), FeedItem.UNPLAYED, feed);
items.add(item);
File mediaFile = newMediaFile("feed-" + i + "-episode-" + j + ".mp3");
- item.setMedia(new FeedMedia(j, item, 0, 0, mediaFile.length(), "audio/mp3", null, hostFile(mediaFile), false, null, 0));
+ item.setMedia(new FeedMedia(j, item, 0, 0, mediaFile.length(), "audio/mp3", null, hostFile(mediaFile), false, null, 0, 0));
}
feed.setItems(items);
@@ -171,16 +181,15 @@ public class UITestUtils {
*/
public void addLocalFeedData(boolean downloadEpisodes) throws Exception {
if (localFeedDataAdded) {
- throw new IllegalStateException("addLocalFeedData was called twice on the same instance");
+ Log.w(TAG, "addLocalFeedData was called twice on the same instance");
+ // might be a flaky test, this is actually not that severe
+ return;
}
if (!feedDataHosted) {
addHostedFeedData();
}
- List<FeedItem> queue = new ArrayList<FeedItem>();
-
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
+ List<FeedItem> queue = new ArrayList<>();
for (Feed feed : hostedFeeds) {
feed.setDownloaded(true);
if (feed.getImage() != null) {
@@ -203,10 +212,23 @@ public class UITestUtils {
queue.add(feed.getItems().get(0));
feed.getItems().get(1).getMedia().setPlaybackCompletionDate(new Date());
}
+ localFeedDataAdded = true;
+
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
adapter.setCompleteFeed(hostedFeeds.toArray(new Feed[hostedFeeds.size()]));
adapter.setQueue(queue);
adapter.close();
EventDistributor.getInstance().sendFeedUpdateBroadcast();
- EventBus.getDefault().post(new QueueEvent(QueueEvent.Action.ADDED_ITEMS, queue));
+ EventBus.getDefault().post(QueueEvent.setQueue(queue));
+ }
+
+ public PlaybackController getPlaybackController(MainActivity mainActivity) {
+ ExternalPlayerFragment fragment = (ExternalPlayerFragment)mainActivity.getSupportFragmentManager().findFragmentByTag(ExternalPlayerFragment.TAG);
+ return fragment.getPlaybackControllerTestingOnly();
+ }
+
+ public FeedMedia getCurrentMedia(MainActivity mainActivity) {
+ return (FeedMedia)getPlaybackController(mainActivity).getMedia();
}
}
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/RewindAfterPauseUtilTest.java b/app/src/androidTest/java/de/test/antennapod/util/RewindAfterPauseUtilTest.java
new file mode 100644
index 000000000..fcfb16eb4
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/util/RewindAfterPauseUtilTest.java
@@ -0,0 +1,51 @@
+package de.test.antennapod.util;
+
+import junit.framework.*;
+
+import de.danoeh.antennapod.core.util.*;
+
+/**
+ * Tests for {@link RewindAfterPauseUtils}.
+ */
+public class RewindAfterPauseUtilTest extends TestCase {
+
+ public void testCalculatePositionWithRewindNoRewind() {
+ final int ORIGINAL_POSITION = 10000;
+ long lastPlayed = System.currentTimeMillis();
+ int position = RewindAfterPauseUtils.calculatePositionWithRewind(ORIGINAL_POSITION, lastPlayed);
+
+ assertEquals(ORIGINAL_POSITION, position);
+ }
+
+ public void testCalculatePositionWithRewindSmallRewind() {
+ final int ORIGINAL_POSITION = 10000;
+ long lastPlayed = System.currentTimeMillis() - RewindAfterPauseUtils.ELAPSED_TIME_FOR_SHORT_REWIND - 1000;
+ int position = RewindAfterPauseUtils.calculatePositionWithRewind(ORIGINAL_POSITION, lastPlayed);
+
+ assertEquals(ORIGINAL_POSITION - RewindAfterPauseUtils.SHORT_REWIND, position);
+ }
+
+ public void testCalculatePositionWithRewindMediumRewind() {
+ final int ORIGINAL_POSITION = 10000;
+ long lastPlayed = System.currentTimeMillis() - RewindAfterPauseUtils.ELAPSED_TIME_FOR_MEDIUM_REWIND - 1000;
+ int position = RewindAfterPauseUtils.calculatePositionWithRewind(ORIGINAL_POSITION, lastPlayed);
+
+ assertEquals(ORIGINAL_POSITION - RewindAfterPauseUtils.MEDIUM_REWIND, position);
+ }
+
+ public void testCalculatePositionWithRewindLongRewind() {
+ final int ORIGINAL_POSITION = 30000;
+ long lastPlayed = System.currentTimeMillis() - RewindAfterPauseUtils.ELAPSED_TIME_FOR_LONG_REWIND - 1000;
+ int position = RewindAfterPauseUtils.calculatePositionWithRewind(ORIGINAL_POSITION, lastPlayed);
+
+ assertEquals(ORIGINAL_POSITION - RewindAfterPauseUtils.LONG_REWIND, position);
+ }
+
+ public void testCalculatePositionWithRewindNegativeNumber() {
+ final int ORIGINAL_POSITION = 100;
+ long lastPlayed = System.currentTimeMillis() - RewindAfterPauseUtils.ELAPSED_TIME_FOR_LONG_REWIND - 1000;
+ int position = RewindAfterPauseUtils.calculatePositionWithRewind(ORIGINAL_POSITION, lastPlayed);
+
+ assertEquals(0, position);
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java b/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
index 2c56b71cc..7e535e12c 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
@@ -31,7 +31,7 @@ public class TimelineTest extends InstrumentationTestCase {
}
private Playable newTestPlayable(List<Chapter> chapters, String shownotes) {
- FeedItem item = new FeedItem(0, "Item", "item-id", "http://example.com/item", new Date(), true, null);
+ FeedItem item = new FeedItem(0, "Item", "item-id", "http://example.com/item", new Date(), FeedItem.PLAYED, null);
item.setChapters(chapters);
item.setContentEncoded(shownotes);
FeedMedia media = new FeedMedia(item, "http://example.com/episode", 100, "audio/mp3");
diff --git a/app/src/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 97f8bbdad..4e7863730 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,9 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.danoeh.antennapod"
- android:versionCode="51"
- android:versionName="1.2">
-
+ android:versionCode="1050003"
+ android:versionName="1.5.0.3">
+ <!--
+ Version code schema:
+ "1.2.3-SNAPSHOT" -> 1020300
+ "1.2.3-RC4" -> 1020304
+ -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
@@ -32,8 +36,7 @@
android:label="@string/app_name"
android:backupAgent=".core.backup.OpmlBackupAgent"
android:restoreAnyVersion="true"
- android:logo="@drawable/ic_launcher"
- android:theme="@style/Theme.AntennaPod.Light">
+ android:logo="@drawable/ic_launcher">
<meta-data
android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI3a05VToCTlqBymJrbFGaKQMvF-bBAuLsOdavBA"/>
@@ -137,6 +140,9 @@
<activity
android:name=".activity.AboutActivity"
android:label="@string/about_pref">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
</activity>
<activity
android:name=".activity.OpmlImportFromPathActivity"
@@ -155,21 +161,14 @@
<data
android:host="*"
- android:mimeType="*/*"
android:pathPattern=".*\\.opml"
- android:scheme="file"/>
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.VIEW"/>
-
- <category android:name="android.intent.category.DEFAULT"/>
- <category android:name="android.intent.category.BROWSABLE"/>
-
+ android:scheme="file"
+ android:mimeType="*/*"/>
<data
android:host="*"
android:pathPattern=".*\\.opml"
- android:scheme="file"
- android:mimeType="text/x-opml"/>
+ android:scheme="content"
+ android:mimeType="*/*"/>
</intent-filter>
</activity>
<activity
@@ -207,8 +206,9 @@
android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
<activity
- android:name=".activity.DefaultOnlineFeedViewActivity"
- android:configChanges="orientation">
+ android:name=".activity.OnlineFeedViewActivity"
+ android:configChanges="orientation"
+ android:label="@string/add_feed_label">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="de.danoeh.antennapod.activity.MainActivity"/>
@@ -328,6 +328,15 @@
android:scheme="package"/>
</intent-filter>
</receiver>
+ <receiver android:name="de.danoeh.antennapod.core.service.playback.MediaButtonIntentReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.MEDIA_BUTTON" />
+ </intent-filter>
+ </receiver>
+
+ <meta-data
+ android:name="de.danoeh.antennapod.core.glide.ApGlideModule"
+ android:value="GlideModule" />
</application>
</manifest>
diff --git a/app/src/main/assets/.gitignore b/app/src/main/assets/.gitignore
index 328840cfc..f4de63f77 100644
--- a/app/src/main/assets/.gitignore
+++ b/app/src/main/assets/.gitignore
@@ -1,2 +1,4 @@
# this file is generated automatically
about.html
+LICENSE.txt
+CONTRIBUTORS.txt
diff --git a/app/src/main/assets/3sec.mp3 b/app/src/main/assets/3sec.mp3
new file mode 100644
index 000000000..8ae450d01
--- /dev/null
+++ b/app/src/main/assets/3sec.mp3
Binary files differ
diff --git a/app/src/main/assets/LICENSE.html b/app/src/main/assets/LICENSE.html
deleted file mode 100644
index d38547791..000000000
--- a/app/src/main/assets/LICENSE.html
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
- <title>MIT License</title>
- </head>
- <body>
-<p>Copyright (c) 2012 Daniel Oeh</p>
-
-<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>
-
-<p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>
-
-</p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
-
- </body>
-</html> \ No newline at end of file
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_DSLV.txt b/app/src/main/assets/LICENSE_DSLV.txt
deleted file mode 100644
index 2a2de04a3..000000000
--- a/app/src/main/assets/LICENSE_DSLV.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-A subclass of the Android ListView component that enables drag
-and drop re-ordering of list items.
-
-Copyright 2012 Carl Bauer
-
-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_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_GLIDE.txt b/app/src/main/assets/LICENSE_GLIDE.txt
new file mode 100644
index 000000000..f5111eeab
--- /dev/null
+++ b/app/src/main/assets/LICENSE_GLIDE.txt
@@ -0,0 +1,94 @@
+License for everything not in third_party and not otherwise marked:
+
+Copyright 2014 Google, Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are
+permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of
+ conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ of conditions and the following disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY GOOGLE, INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE, INC. OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation are those of the
+authors and should not be interpreted as representing official policies, either expressed
+or implied, of Google, Inc.
+---------------------------------------------------------------------------------------------
+License for third_party/disklrucache:
+
+Copyright 2012 Jake Wharton
+Copyright 2011 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+---------------------------------------------------------------------------------------------
+License for third_party/gif_decoder:
+
+Copyright (c) 2013 Xcellent Creations, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+---------------------------------------------------------------------------------------------
+License for third_party/gif_encoder/AnimatedGifEncoder.java and
+third_party/gif_encoder/LZWEncoder.java:
+
+No copyright asserted on the source code of this class. May be used for any
+purpose, however, refer to the Unisys LZW patent for restrictions on use of
+the associated LZWEncoder class. Please forward any corrections to
+kweiner@fmsware.com.
+
+-----------------------------------------------------------------------------
+License for third_party/gif_encoder/NeuQuant.java
+
+Copyright (c) 1994 Anthony Dekker
+
+NEUQUANT Neural-Net quantization algorithm by Anthony Dekker, 1994. See
+"Kohonen neural networks for optimal colour quantization" in "Network:
+Computation in Neural Systems" Vol. 5 (1994) pp 351-367. for a discussion of
+the algorithm.
+
+Any party obtaining a copy of these files from the author, directly or
+indirectly, is granted, free of charge, a full and unrestricted irrevocable,
+world-wide, paid up, royalty-free, nonexclusive right and license to deal in
+this software and documentation files (the "Software"), including without
+limitation the rights to use, copy, modify, merge, publish, distribute,
+sublicense, and/or sell copies of the Software, and to permit persons who
+receive copies from any such party to do so, with the only requirement being
+that this copyright notice remain intact.
diff --git a/app/src/main/assets/LICENSE_MATERIAL_DIALOGS.txt b/app/src/main/assets/LICENSE_MATERIAL_DIALOGS.txt
new file mode 100644
index 000000000..f6bf77906
--- /dev/null
+++ b/app/src/main/assets/LICENSE_MATERIAL_DIALOGS.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Aidan Michael Follestad
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
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_PICASSO.txt b/app/src/main/assets/LICENSE_PICASSO.txt
deleted file mode 100644
index 0bf6b9f8e..000000000
--- a/app/src/main/assets/LICENSE_PICASSO.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-Copyright 2013 Square, Inc.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
diff --git a/app/src/main/java/de/danoeh/antennapod/AppConfig.java b/app/src/main/java/de/danoeh/antennapod/AppConfig.java
deleted file mode 100644
index 24f13d4a3..000000000
--- a/app/src/main/java/de/danoeh/antennapod/AppConfig.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package de.danoeh.antennapod;
-
-public final class AppConfig {
- /** Should be used when setting User-Agent header for HTTP-requests. */
- public final static String USER_AGENT = "AntennaPod/0.9.9.4";
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
new file mode 100644
index 000000000..ea2166674
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
@@ -0,0 +1,52 @@
+package de.danoeh.antennapod;
+
+import android.os.Build;
+import android.util.Log;
+
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
+public class CrashReportWriter implements Thread.UncaughtExceptionHandler {
+
+ private static final String TAG = "CrashReportWriter";
+
+ private final Thread.UncaughtExceptionHandler defaultHandler;
+
+ public CrashReportWriter() {
+ defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+ }
+
+ public static File getFile() {
+ return new File(UserPreferences.getDataFolder(null), "crash-report.log");
+ }
+
+ @Override
+ public void uncaughtException(Thread thread, Throwable ex) {
+ File path = getFile();
+ PrintWriter out = null;
+ try {
+ 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("Model: " + Build.MODEL);
+ out.println("Device: " + Build.DEVICE);
+ out.println("Product: " + Build.PRODUCT);
+ out.println();
+ out.println("[ StackTrace ]");
+ ex.printStackTrace(out);
+ } catch (IOException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ } finally {
+ IOUtils.closeQuietly(out);
+ }
+ defaultHandler.uncaughtException(thread, ex);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index 451094909..c1d4bc4fd 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -1,12 +1,18 @@
package de.danoeh.antennapod;
import android.app.Application;
-import android.content.res.Configuration;
+import android.os.Build;
+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.asynctask.PicassoProvider;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.spa.SPAUtil;
/** Main application class. */
@@ -21,10 +27,6 @@ public class PodcastApp extends Application {
}
}
- private static final String TAG = "PodcastApp";
-
- private static float LOGICAL_DENSITY;
-
private static PodcastApp singleton;
public static PodcastApp getInstance() {
@@ -34,24 +36,36 @@ public class PodcastApp extends Application {
@Override
public void onCreate() {
super.onCreate();
+
+ Thread.setDefaultUncaughtExceptionHandler(new CrashReportWriter());
+
+ if(BuildConfig.DEBUG) {
+ StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder()
+ .detectLeakedSqlLiteObjects()
+ .penaltyLog()
+ .penaltyDropBox();
+ if (Build.VERSION.SDK_INT >= 11) {
+ builder.detectActivityLeaks();
+ builder.detectLeakedClosableObjects();
+ }
+ if(Build.VERSION.SDK_INT >= 16) {
+ builder.detectLeakedRegistrationObjects();
+ }
+ StrictMode.setVmPolicy(builder.build());
+ }
+
singleton = this;
- LOGICAL_DENSITY = getResources().getDisplayMetrics().density;
- PicassoProvider.setupPicassoInstance(this);
- UserPreferences.createInstance(this);
- PlaybackPreferences.createInstance(this);
+ PodDBAdapter.init(this);
+ UpdateManager.init(this);
+ UserPreferences.init(this);
+ PlaybackPreferences.init(this);
+ NetworkUtils.init(this);
EventDistributor.getInstance();
+ Iconify.with(new FontAwesomeModule());
+ Iconify.with(new MaterialModule());
SPAUtil.sendSPAppsQueryFeedsIntent(this);
- }
-
- public static float getLogicalDensity() {
- return LOGICAL_DENSITY;
- }
-
- public boolean isLargeScreen() {
- return (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_LARGE
- || (getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) == Configuration.SCREENLAYOUT_SIZE_XLARGE;
+ }
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/UpdateManager.java b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java
new file mode 100644
index 000000000..b1d7fffc8
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java
@@ -0,0 +1,88 @@
+package de.danoeh.antennapod;
+
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import java.io.File;
+import java.util.List;
+
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedImage;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+
+/*
+ * This class's job is do perform maintenance tasks whenever the app has been updated
+ */
+public class UpdateManager {
+
+ public static final String TAG = UpdateManager.class.getSimpleName();
+
+ private static final String PREF_NAME = "app_version";
+ private static final String KEY_VERSION_CODE = "version_code";
+
+ private static int currentVersionCode;
+
+ private static Context context;
+ private static SharedPreferences prefs;
+
+ public static void init(Context context) {
+ UpdateManager.context = context;
+ prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+ PackageManager pm = context.getPackageManager();
+ try {
+ PackageInfo info = pm.getPackageInfo(context.getPackageName(), 0);
+ currentVersionCode = info.versionCode;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Failed to obtain package info for package name: " + context.getPackageName(), e);
+ currentVersionCode = 0;
+ return;
+ }
+ final int oldVersionCode = getStoredVersionCode();
+ Log.d(TAG, "old: " + oldVersionCode + ", current: " + currentVersionCode);
+ if(oldVersionCode < currentVersionCode) {
+ onUpgrade(oldVersionCode, currentVersionCode);
+ setCurrentVersionCode();
+ }
+ }
+
+ public static int getStoredVersionCode() {
+ return prefs.getInt(KEY_VERSION_CODE, -1);
+ }
+
+ public static void setCurrentVersionCode() {
+ prefs.edit().putInt(KEY_VERSION_CODE, currentVersionCode).apply();
+ }
+
+ private static void onUpgrade(final int oldVersionCode, final int newVersionCode) {
+ if(oldVersionCode < 1030099) {
+ // delete the now obsolete image cache
+ // from now on, Glide will handle caching images
+ new Thread() {
+ public void run() {
+ List<Feed> feeds = DBReader.getFeedList();
+ for (Feed podcast : feeds) {
+ List<FeedItem> episodes = DBReader.getFeedItemList(podcast);
+ for (FeedItem episode : episodes) {
+ FeedImage image = episode.getImage();
+ if (image != null && image.isDownloaded() && image.getFile_url() != null) {
+ File imageFile = new File(image.getFile_url());
+ if (imageFile.exists()) {
+ imageFile.delete();
+ }
+ image.setFile_url(null); // calls setDownloaded(false)
+ DBWriter.setFeedImage(image);
+ }
+ }
+ }
+ }
+ }.start();
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
index 811628ebf..c835f8073 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
@@ -1,43 +1,165 @@
package de.danoeh.antennapod.activity;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.LinearLayout;
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import rx.Observable;
+import rx.Subscriber;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Displays the 'about' screen
*/
public class AboutActivity extends ActionBarActivity {
+ private static final String TAG = AboutActivity.class.getSimpleName();
+
private WebView webview;
private LinearLayout webviewContainer;
+ private int depth = 0;
+
+ private Subscription subscription;
@Override
protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
- getSupportActionBar().hide();
+ getSupportActionBar().setDisplayShowHomeEnabled(true);
setContentView(R.layout.about);
webviewContainer = (LinearLayout) findViewById(R.id.webvContainer);
webview = (WebView) findViewById(R.id.webvAbout);
+ webview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
+ 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) {
+ webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+ webview.setBackgroundColor(Color.TRANSPARENT);
+ }
webview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
- view.loadUrl(url);
- return false;
+ if(url.startsWith("http")) {
+ depth++;
+ return false;
+ } else {
+ url = url.replace("file:///android_asset/", "");
+ loadAsset(url);
+ return true;
+ }
}
});
- webview.loadUrl("file:///android_asset/about.html");
+ loadAsset("about.html");
+ }
+
+ private void loadAsset(String filename) {
+ subscription = Observable.create(new Observable.OnSubscribe<String>() {
+ @Override
+ public void call(Subscriber<? super String> subscriber) {
+ InputStream input = null;
+ try {
+ TypedArray res = AboutActivity.this.getTheme().obtainStyledAttributes(
+ new int[] { android.R.attr.textColorPrimary });
+ int colorResource = res.getColor(0, 0);
+ String colorString = String.format("#%06X", 0xFFFFFF & colorResource);
+ res.recycle();
+ input = getAssets().open(filename);
+ String webViewData = IOUtils.toString(input, Charset.defaultCharset());
+ if(false == webViewData.startsWith("<!DOCTYPE html>")) {
+ //webViewData = webViewData.replace("\n\n", "</p><p>");
+ webViewData = webViewData.replace("%", "&#37;");
+ webViewData =
+ "<!DOCTYPE html>" +
+ "<html>" +
+ "<head>" +
+ " <meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">" +
+ " <style type=\"text/css\">" +
+ " @font-face {" +
+ " font-family: 'Roboto-Light';" +
+ " src: url('file:///android_asset/Roboto-Light.ttf');" +
+ " }" +
+ " * {" +
+ " color: %s;" +
+ " font-family: roboto-Light;" +
+ " font-size: 8pt;" +
+ " }" +
+ " </style>" +
+ "</head><body><p>" + webViewData + "</p></body></html>";
+ webViewData = webViewData.replace("\n", "<br/>");
+ depth++;
+ } else {
+ depth = 0;
+ }
+ webViewData = String.format(webViewData, colorString);
+ subscriber.onNext(webViewData);
+ } catch (IOException e) {
+ subscriber.onError(e);
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ subscriber.onCompleted();
+ }
+ })
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(webviewData -> {
+ webview.loadDataWithBaseURL("file:///android_asset/", webviewData, "text/html",
+ "utf-8", "about:blank");
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ }
+
+ @Override
+ public void onBackPressed() {
+ Log.d(TAG, "depth: " + depth);
+ if(depth == 1) {
+ loadAsset("about.html");
+ } else if(depth > 1) {
+ webview.goBack();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
}
@Override
protected void onDestroy() {
super.onDestroy();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
if (webviewContainer != null && webview != null) {
webviewContainer.removeAllViews();
webview.destroy();
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
index b59f6ba35..12bae2f51 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -1,165 +1,139 @@
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.os.AsyncTask;
-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.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ImageButton;
import android.widget.ListView;
-import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.viewpagerindicator.CirclePageIndicator;
-import org.apache.commons.lang3.StringUtils;
+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.feed.Chapter;
+import de.danoeh.antennapod.core.asynctask.FeedRemover;
+import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
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.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.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
-import de.danoeh.antennapod.dialog.VariableSpeedDialog;
+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;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+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 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 Playable media;
+ private ViewPager mPager;
+ private AudioplayerPagerAdapter mPagerAdapter;
- private TextView txtvTitle;
- private Button butPlaybackSpeed;
- private ImageButton butNavChaptersShownotes;
- private ImageButton butShowCover;
-
- 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];
- }
+ private Subscription subscription;
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop()");
- cancelLoadTask();
+ if(subscription != null) {
+ 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;
+ mPager = null;
+ mPagerAdapter = 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(mPager == 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, mPager.getCurrentItem())
+ .commit();
}
@Override
@@ -168,59 +142,17 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
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();
- }
-
- /**
- * 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);
+ mPager.setCurrentItem(lastPosition);
}
@Override
protected void onResume() {
super.onResume();
- if (StringUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
+ if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
Intent intent = getIntent();
Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath());
ExternalMedia media = new ExternalMedia(intent.getData().getPath(),
@@ -234,8 +166,9 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
true);
startService(launchIntent);
}
- if (savedPosition != -1) {
- switchToFragment(savedPosition);
+ if(mPagerAdapter != null && controller != null && controller.getMedia() != media) {
+ media = controller.getMedia();
+ mPagerAdapter.onMediaChanged(media);
}
EventDistributor.getInstance().register(contentUpdate);
@@ -266,150 +199,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, new ChapterListAdapter.Callback() {
- @Override
- public void onPlayChapterButtonClicked(int 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);
@@ -417,119 +228,40 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
navAdapter = new NavListAdapter(itemAccess, this);
navList.setAdapter(navAdapter);
- navList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- int viewType = parent.getAdapter().getItemViewType(position);
- if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER) {
- Intent intent = new Intent(AudioplayerActivity.this, MainActivity.class);
- intent.putExtra(MainActivity.EXTRA_NAV_TYPE, viewType);
- intent.putExtra(MainActivity.EXTRA_NAV_INDEX, position);
- startActivity(intent);
- }
- drawerLayout.closeDrawer(navDrawer);
- }
- });
- drawerToggle.syncState();
-
- findViewById(R.id.nav_settings).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- drawerLayout.closeDrawer(navDrawer);
- startActivity(new Intent(AudioplayerActivity.this, PreferenceController.getPreferenceActivity()));
- }
- });
-
- butNavChaptersShownotes.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (currentlyShownPosition == POS_CHAPTERS) {
- switchToFragment(POS_DESCR);
- } else if (currentlyShownPosition == POS_DESCR) {
- switchToFragment(POS_CHAPTERS);
- } else if (currentlyShownPosition == POS_COVER) {
- switchToLastFragment();
- }
- }
- });
-
- butShowCover.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- switchToFragment(POS_COVER);
+ navList.setOnItemClickListener((parent, view, position, id) -> {
+ int viewType = parent.getAdapter().getItemViewType(position);
+ if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER) {
+ Intent intent = new Intent(AudioplayerActivity.this, MainActivity.class);
+ intent.putExtra(MainActivity.EXTRA_NAV_TYPE, viewType);
+ intent.putExtra(MainActivity.EXTRA_NAV_INDEX, position);
+ startActivity(intent);
}
+ drawerLayout.closeDrawer(navDrawer);
});
-
- butPlaybackSpeed.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View 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(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- VariableSpeedDialog.showDialog(AudioplayerActivity.this);
+ navList.setOnItemLongClickListener((parent, view, position, id) -> {
+ if (position < navAdapter.getTags().size()) {
+ showDrawerPreferencesDialog();
return true;
+ } else {
+ mPosition = position;
+ return false;
}
});
- }
-
- 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);
- }
-
- }
+ registerForContextMenu(navList);
+ drawerToggle.syncState();
- @Override
- protected void onPlaybackSpeedChange() {
- super.onPlaybackSpeedChange();
- updateButPlaybackSpeed();
- }
+ findViewById(R.id.nav_settings).setOnClickListener(v -> {
+ drawerLayout.closeDrawer(navDrawer);
+ startActivity(new Intent(AudioplayerActivity.this, PreferenceController.getPreferenceActivity()));
+ });
- private void updateButPlaybackSpeed() {
- if (controller != null && controller.canSetPlaybackSpeed()) {
- butPlaybackSpeed.setText(UserPreferences.getPlaybackSpeed());
- }
+ mPager = (ViewPager) findViewById(R.id.pager);
+ mPagerAdapter = new AudioplayerPagerAdapter(getSupportFragmentManager());
+ mPager.setAdapter(mPagerAdapter);
+ CirclePageIndicator pageIndicator = (CirclePageIndicator) findViewById(R.id.page_indicator);
+ pageIndicator.setViewPager(mPager);
+ loadLastFragment();
+ mPager.onSaveInstanceState();
}
@Override
@@ -543,45 +275,20 @@ 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("");
- Picasso.with(this)
- .load(media.getImageUri())
- .fit()
- .into(butShowCover);
-
- setNavButtonVisibility();
-
- if (currentlyShownPosition == -1) {
- if (!restoreFromPreferences()) {
- switchToFragment(POS_COVER);
- }
+ if(controller.getMedia() != media) {
+ media = controller.getMedia();
+ mPagerAdapter.onMediaChanged(media);
}
- if (currentlyShownFragment instanceof AudioplayerContentFragment) {
- ((AudioplayerContentFragment) currentlyShownFragment)
- .onDataSetChanged(media);
- }
-
- if (controller == null
- || !controller.canSetPlaybackSpeed()) {
- butPlaybackSpeed.setVisibility(View.GONE);
- } else {
- butPlaybackSpeed.setVisibility(View.VISIBLE);
- }
-
- updateButPlaybackSpeed();
return true;
}
public void notifyMediaPositionChanged() {
- if (chapterFragment != null) {
- ArrayAdapter<SimpleChapter> adapter = (ArrayAdapter<SimpleChapter>) chapterFragment
- .getListAdapter();
- adapter.notifyDataSetChanged();
+ ChaptersFragment chaptersFragment = mPagerAdapter.getChaptersFragment();
+ if(chaptersFragment != null) {
+ ChaptersListAdapter adapter = (ChaptersListAdapter) chaptersFragment.getListAdapter();
+ if (adapter != null) {
+ adapter.notifyDataSetChanged();
+ }
}
}
@@ -605,7 +312,6 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
clearStatusMsg();
}
- @Override
public PlaybackController getPlaybackController() {
return controller;
}
@@ -615,10 +321,6 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer);
}
- public interface AudioplayerContentFragment {
- public void onDataSetChanged(Playable media);
- }
-
@Override
protected int getContentViewResourceId() {
return R.layout.audioplayer_activity;
@@ -634,34 +336,137 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
}
}
- private DBReader.NavDrawerData navDrawerData;
- private AsyncTask<Void, Void, DBReader.NavDrawerData> loadTask;
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ if(v.getId() != R.id.nav_list) {
+ return;
+ }
+ AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ int position = adapterInfo.position;
+ if(position < navAdapter.getSubscriptionOffset()) {
+ return;
+ }
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.nav_feed_context, menu);
+ Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
+ menu.setHeaderTitle(feed.getTitle());
+ // episodes are not loaded, so we cannot check if the podcast has new or unplayed ones!
+ }
- private void loadData() {
- loadTask = new AsyncTask<Void, Void, DBReader.NavDrawerData>() {
- @Override
- protected DBReader.NavDrawerData doInBackground(Void... params) {
- return DBReader.getNavDrawerData(AudioplayerActivity.this);
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ final int position = mPosition;
+ mPosition = -1; // reset
+ if(position < 0) {
+ return false;
+ }
+ Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
+ switch(item.getItemId()) {
+ case R.id.mark_all_seen_item:
+ DBWriter.markFeedSeen(feed.getId());
+ return true;
+ case R.id.mark_all_read_item:
+ DBWriter.markFeedRead(feed.getId());
+ return true;
+ case R.id.remove_item:
+ final FeedRemover remover = new FeedRemover(this, feed) {
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ }
+ };
+ ConfirmationDialog conDialog = new ConfirmationDialog(this,
+ R.string.remove_feed_label,
+ R.string.feed_delete_confirmation_msg) {
+ @Override
+ public void onConfirmButtonPressed(
+ DialogInterface dialog) {
+ dialog.dismiss();
+ if (controller != null) {
+ Playable playable = controller.getMedia();
+ if (playable != null && playable instanceof FeedMedia) {
+ FeedMedia media = (FeedMedia) playable;
+ if (media.getItem().getFeed().getId() == feed.getId()) {
+ Log.d(TAG, "Currently playing episode is about to be deleted, skipping");
+ remover.skipOnCompletion = true;
+ if(controller.getStatus() == PlayerStatus.PLAYING) {
+ sendBroadcast(new Intent(
+ PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
+ }
+ }
+ }
+ }
+ remover.executeAsync();
+ }
+ };
+ conDialog.createNewDialog().show();
+ return true;
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if(isDrawerOpen()) {
+ drawerLayout.closeDrawer(navDrawer);
+ } else if (mPager.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.
+ mPager.setCurrentItem(mPager.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;
}
+ }
- @Override
- protected void onPostExecute(DBReader.NavDrawerData result) {
- super.onPostExecute(result);
- navDrawerData = result;
- if (navAdapter != null) {
- navAdapter.notifyDataSetChanged();
- }
+ 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]);
}
- };
- loadTask.execute();
+ });
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ UserPreferences.setHiddenDrawerItems(hiddenDrawerItems);
+ });
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
}
- private void cancelLoadTask() {
- if (loadTask != null) {
- loadTask.cancel(true);
- }
+ private DBReader.NavDrawerData navDrawerData;
+
+ private void loadData() {
+ subscription = Observable.fromCallable(() -> DBReader.getNavDrawerData())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ navDrawerData = result;
+ if (navAdapter != null) {
+ navAdapter.notifyDataSetChanged();
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
+
+
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
@@ -685,7 +490,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;
@@ -708,8 +513,75 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
}
@Override
- public int getNumberOfUnreadFeedItems(long feedId) {
- return (navDrawerData != null) ? navDrawerData.numUnreadFeedItems.get(feedId) : 0;
+ public int getNumberOfDownloadedItems() {
+ return (navDrawerData != null) ? navDrawerData.numDownloadedItems : 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/DefaultOnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
deleted file mode 100644
index 287ae3568..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
+++ /dev/null
@@ -1,249 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncTask;
-import android.os.Bundle;
-import android.support.v4.app.NavUtils;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-import android.widget.Button;
-import android.widget.ImageView;
-import android.widget.ListView;
-import android.widget.Spinner;
-import android.widget.TextView;
-
-import com.squareup.picasso.Picasso;
-
-import org.apache.commons.lang3.StringUtils;
-import org.jsoup.Jsoup;
-import org.jsoup.examples.HtmlToPlainText;
-import org.jsoup.nodes.Document;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
-import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
-import de.danoeh.antennapod.core.feed.EventDistributor;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DownloadRequestException;
-import de.danoeh.antennapod.core.storage.DownloadRequester;
-
-/**
- * Default implementation of OnlineFeedViewActivity. Shows the downloaded feed's items with their descriptions,
- * a subscribe button and a spinner for choosing alternate feed URLs.
- */
-public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
- private static final String TAG = "DefaultOnlineFeedViewActivity";
-
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED | EventDistributor.DOWNLOAD_QUEUED | EventDistributor.FEED_LIST_UPDATE;
- private volatile List<Feed> feeds;
- private Feed feed;
- private String selectedDownloadUrl;
-
- private Button subscribeButton;
-
- @Override
- protected void onCreate(Bundle arg0) {
- super.onCreate(arg0);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- Intent destIntent = new Intent(this, MainActivity.class);
- if (NavUtils.shouldUpRecreateTask(this, destIntent)) {
- startActivity(destIntent);
- } else {
- NavUtils.navigateUpFromSameTask(this);
- }
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- protected void loadData() {
- super.loadData();
- feeds = DBReader.getFeedList(this);
- }
-
- @Override
- protected void beforeShowFeedInformation(Feed feed, Map<String, String> alternateFeedUrls) {
- super.beforeShowFeedInformation(feed, alternateFeedUrls);
-
- // remove HTML tags from descriptions
-
- if (BuildConfig.DEBUG) Log.d(TAG, "Removing HTML from shownotes");
- if (feed.getItems() != null) {
- HtmlToPlainText formatter = new HtmlToPlainText();
- for (FeedItem item : feed.getItems()) {
- if (item.getDescription() != null) {
- Document description = Jsoup.parse(item.getDescription());
- item.setDescription(StringUtils.trim(formatter.getPlainText(description)));
- }
- }
- }
- }
-
- @Override
- protected void showFeedInformation(final Feed feed, final Map<String, String> alternateFeedUrls) {
- super.showFeedInformation(feed, alternateFeedUrls);
- setContentView(R.layout.listview_activity);
-
- this.feed = feed;
- this.selectedDownloadUrl = feed.getDownload_url();
- EventDistributor.getInstance().register(listener);
- ListView listView = (ListView) findViewById(R.id.listview);
- LayoutInflater inflater = (LayoutInflater)
- getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false);
- listView.addHeaderView(header);
-
- listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems()));
-
- ImageView cover = (ImageView) header.findViewById(R.id.imgvCover);
- TextView title = (TextView) header.findViewById(R.id.txtvTitle);
- TextView author = (TextView) header.findViewById(R.id.txtvAuthor);
- TextView description = (TextView) header.findViewById(R.id.txtvDescription);
- Spinner spAlternateUrls = (Spinner) header.findViewById(R.id.spinnerAlternateUrls);
-
- subscribeButton = (Button) header.findViewById(R.id.butSubscribe);
-
- if (feed.getImage() != null && StringUtils.isNotBlank(feed.getImage().getDownload_url())) {
- Picasso.with(this)
- .load(feed.getImage().getDownload_url())
- .fit()
- .into(cover);
- }
-
- title.setText(feed.getTitle());
- author.setText(feed.getAuthor());
- description.setText(feed.getDescription());
-
- subscribeButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- try {
- Feed f = new Feed(selectedDownloadUrl, new Date(0), feed.getTitle());
- f.setPreferences(feed.getPreferences());
- DefaultOnlineFeedViewActivity.this.feed = f;
-
- DownloadRequester.getInstance().downloadFeed(
- DefaultOnlineFeedViewActivity.this,
- f);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DownloadRequestErrorDialogCreator.newRequestErrorDialog(DefaultOnlineFeedViewActivity.this,
- e.getMessage());
- }
- setSubscribeButtonState(feed);
- }
- });
-
- if (alternateFeedUrls.isEmpty()) {
- spAlternateUrls.setVisibility(View.GONE);
- } else {
- spAlternateUrls.setVisibility(View.VISIBLE);
-
- final List<String> alternateUrlsList = new ArrayList<String>();
- final List<String> alternateUrlsTitleList = new ArrayList<String>();
-
- alternateUrlsList.add(feed.getDownload_url());
- alternateUrlsTitleList.add(feed.getTitle());
-
-
- alternateUrlsList.addAll(alternateFeedUrls.keySet());
- for (String url : alternateFeedUrls.keySet()) {
- alternateUrlsTitleList.add(alternateFeedUrls.get(url));
- }
- ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, alternateUrlsTitleList);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- spAlternateUrls.setAdapter(adapter);
- spAlternateUrls.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- selectedDownloadUrl = alternateUrlsList.get(position);
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
-
- }
- });
-
-
- }
- setSubscribeButtonState(feed);
-
- }
-
- private boolean feedInFeedlist(Feed feed) {
- if (feeds == null || feed == null)
- return false;
- for (Feed f : feeds) {
- if (f.getIdentifyingValue().equals(feed.getIdentifyingValue())) {
- return true;
- }
- }
- return false;
- }
-
- private void setSubscribeButtonState(Feed feed) {
- if (subscribeButton != null && feed != null) {
- if (DownloadRequester.getInstance().isDownloadingFile(feed.getDownload_url())) {
- subscribeButton.setEnabled(false);
- subscribeButton.setText(R.string.downloading_label);
- } else if (feedInFeedlist(feed)) {
- subscribeButton.setEnabled(false);
- subscribeButton.setText(R.string.subscribed_label);
- } else {
- subscribeButton.setEnabled(true);
- subscribeButton.setText(R.string.subscribe_label);
- }
- }
- }
-
- EventDistributor.EventListener listener = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) {
- new AsyncTask<Void, Void, List<Feed>>() {
- @Override
- protected List<Feed> doInBackground(Void... params) {
- return DBReader.getFeedList(DefaultOnlineFeedViewActivity.this);
- }
-
- @Override
- protected void onPostExecute(List<Feed> feeds) {
- super.onPostExecute(feeds);
- DefaultOnlineFeedViewActivity.this.feeds = feeds;
- setSubscribeButtonState(feed);
- }
- }.execute();
- } else if ((arg & EVENTS) != 0) {
- setSubscribeButtonState(feed);
- }
- }
- };
-
- @Override
- protected void onStop() {
- super.onStop();
- EventDistributor.getInstance().unregister(listener);
- }
-}
-
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 559fa0574..25dc64232 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
@@ -1,370 +1,336 @@
package de.danoeh.antennapod.activity;
import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
import android.content.Intent;
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.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
-import android.widget.*;
-import android.widget.AdapterView.OnItemClickListener;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ImageButton;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.R;
+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 {
- private static final String TAG = "DirectoryChooserActivity";
-
- private static final String CREATE_DIRECTORY_NAME = "AntennaPod";
-
- public static final String RESULT_SELECTED_DIR = "selected_dir";
- public static final int RESULT_CODE_DIR_SELECTED = 1;
-
- private Button butConfirm;
- private Button butCancel;
- private ImageButton butNavUp;
- private TextView txtvSelectedFolder;
- private ListView listDirectories;
-
- private ArrayAdapter<String> listDirectoriesAdapter;
- private ArrayList<String> filenames;
- /** The directory that is currently being shown. */
- private File selectedDir;
- private File[] filesInDir;
-
- private FileObserver fileObserver;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- setContentView(R.layout.directory_chooser);
- butConfirm = (Button) findViewById(R.id.butConfirm);
- butCancel = (Button) findViewById(R.id.butCancel);
- butNavUp = (ImageButton) findViewById(R.id.butNavUp);
- txtvSelectedFolder = (TextView) findViewById(R.id.txtvSelectedFolder);
- listDirectories = (ListView) findViewById(R.id.directory_list);
-
- butConfirm.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- if (isValidFile(selectedDir)) {
- if (selectedDir.list().length == 0) {
- returnSelectedFolder();
- } else {
- showNonEmptyDirectoryWarning();
- }
- }
- }
-
- private void showNonEmptyDirectoryWarning() {
- AlertDialog.Builder adb = new AlertDialog.Builder(
- DirectoryChooserActivity.this);
- adb.setTitle(R.string.folder_not_empty_dialog_title);
- adb.setMessage(R.string.folder_not_empty_dialog_msg);
- adb.setNegativeButton(R.string.cancel_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- dialog.dismiss();
- }
- });
- adb.setPositiveButton(R.string.confirm_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- dialog.dismiss();
- returnSelectedFolder();
- }
- });
- adb.create().show();
- }
- });
-
- butCancel.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- setResult(Activity.RESULT_CANCELED);
- finish();
- }
- });
-
- listDirectories.setOnItemClickListener(new OnItemClickListener() {
-
- @Override
- public void onItemClick(AdapterView<?> adapter, View view,
- int position, long id) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Selected index: " + position);
- if (filesInDir != null && position >= 0
- && position < filesInDir.length) {
- changeDirectory(filesInDir[position]);
- }
- }
- });
-
- butNavUp.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- File parent = null;
- if (selectedDir != null
- && (parent = selectedDir.getParentFile()) != null) {
- changeDirectory(parent);
- }
- }
- });
-
- filenames = new ArrayList<String>();
- listDirectoriesAdapter = new ArrayAdapter<String>(this,
- android.R.layout.simple_list_item_1, filenames);
- listDirectories.setAdapter(listDirectoriesAdapter);
- changeDirectory(Environment.getExternalStorageDirectory());
- }
-
- /**
- * Finishes the activity and returns the selected folder as a result. The
- * selected folder can also be null.
- */
- private void returnSelectedFolder() {
- if (selectedDir != null && BuildConfig.DEBUG)
- Log.d(TAG, "Returning " + selectedDir.getAbsolutePath()
- + " as result");
- Intent resultData = new Intent();
- if (selectedDir != null) {
- resultData.putExtra(RESULT_SELECTED_DIR,
- selectedDir.getAbsolutePath());
- }
- setResult(RESULT_CODE_DIR_SELECTED, resultData);
- finish();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- if (fileObserver != null) {
- fileObserver.stopWatching();
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (fileObserver != null) {
- fileObserver.startWatching();
- }
- }
-
- /**
- * Change the directory that is currently being displayed.
- *
- * @param dir
- * The file the activity should switch to. This File must be
- * non-null and a directory, otherwise the displayed directory
- * will not be changed
- */
- private void changeDirectory(File dir) {
- if (dir != null && dir.isDirectory()) {
- File[] contents = dir.listFiles();
- if (contents != null) {
- int numDirectories = 0;
- for (File f : contents) {
- if (f.isDirectory()) {
- numDirectories++;
- }
- }
- filesInDir = new File[numDirectories];
- filenames.clear();
- for (int i = 0, counter = 0; i < numDirectories; counter++) {
- if (contents[counter].isDirectory()) {
- filesInDir[i] = contents[counter];
- filenames.add(contents[counter].getName());
- i++;
- }
- }
- Arrays.sort(filesInDir);
- Collections.sort(filenames);
- selectedDir = dir;
- txtvSelectedFolder.setText(dir.getAbsolutePath());
- listDirectoriesAdapter.notifyDataSetChanged();
- fileObserver = createFileObserver(dir.getAbsolutePath());
- fileObserver.startWatching();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Changed directory to " + dir.getAbsolutePath());
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Could not change folder: contents of dir were null");
- }
- } else {
- if (dir == null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Could not change folder: dir was null");
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Could not change folder: dir is no directory");
- }
- }
- refreshButtonState();
- }
-
- /**
- * Changes the state of the buttons depending on the currently selected file
- * or folder.
- */
- private void refreshButtonState() {
- if (selectedDir != null) {
- butConfirm.setEnabled(isValidFile(selectedDir));
- supportInvalidateOptionsMenu();
- }
- }
-
- /** Refresh the contents of the directory that is currently shown. */
- private void refreshDirectory() {
- if (selectedDir != null) {
- changeDirectory(selectedDir);
- }
- }
-
- /** Sets up a FileObserver to watch the current directory. */
- private FileObserver createFileObserver(String path) {
- return new FileObserver(path, FileObserver.CREATE | FileObserver.DELETE
- | FileObserver.MOVED_FROM | FileObserver.MOVED_TO) {
-
- @Override
- public void onEvent(int event, String path) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "FileObserver received event " + event);
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- refreshDirectory();
- }
- });
- }
- };
- }
-
- @Override
- public boolean onPrepareOptionsMenu(Menu menu) {
+ private static final String TAG = "DirectoryChooserActivit";
+
+ private static final String CREATE_DIRECTORY_NAME = "AntennaPod";
+
+ public static final String RESULT_SELECTED_DIR = "selected_dir";
+ public static final int RESULT_CODE_DIR_SELECTED = 1;
+
+ private Button butConfirm;
+ private Button butCancel;
+ private ImageButton butNavUp;
+ private TextView txtvSelectedFolder;
+ private ListView listDirectories;
+
+ private ArrayAdapter<String> listDirectoriesAdapter;
+ private ArrayList<String> filenames;
+ /** The directory that is currently being shown. */
+ private File selectedDir;
+ private File[] filesInDir;
+
+ private FileObserver fileObserver;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+
+ setContentView(R.layout.directory_chooser);
+ butConfirm = (Button) findViewById(R.id.butConfirm);
+ butCancel = (Button) findViewById(R.id.butCancel);
+ butNavUp = (ImageButton) findViewById(R.id.butNavUp);
+ txtvSelectedFolder = (TextView) findViewById(R.id.txtvSelectedFolder);
+ listDirectories = (ListView) findViewById(R.id.directory_list);
+
+ butConfirm.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ if (isValidFile(selectedDir)) {
+ if (selectedDir.list().length == 0) {
+ returnSelectedFolder();
+ } else {
+ showNonEmptyDirectoryWarning();
+ }
+ }
+ }
+
+ private void showNonEmptyDirectoryWarning() {
+ AlertDialog.Builder adb = new AlertDialog.Builder(
+ DirectoryChooserActivity.this);
+ adb.setTitle(R.string.folder_not_empty_dialog_title);
+ adb.setMessage(R.string.folder_not_empty_dialog_msg);
+ adb.setNegativeButton(R.string.cancel_label,
+ (dialog, which) -> {
+ dialog.dismiss();
+ });
+ adb.setPositiveButton(R.string.confirm_label,
+ (dialog, which) -> {
+ dialog.dismiss();
+ returnSelectedFolder();
+ });
+ adb.create().show();
+ }
+ });
+
+ butCancel.setOnClickListener(v -> {
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ });
+
+ listDirectories.setOnItemClickListener((adapter, view, position, id) -> {
+ Log.d(TAG, "Selected index: " + position);
+ if (filesInDir != null && position >= 0
+ && position < filesInDir.length) {
+ changeDirectory(filesInDir[position]);
+ }
+ });
+
+ butNavUp.setOnClickListener(v -> {
+ File parent = null;
+ if (selectedDir != null
+ && (parent = selectedDir.getParentFile()) != null) {
+ changeDirectory(parent);
+ }
+ });
+
+ filenames = new ArrayList<>();
+ listDirectoriesAdapter = new ArrayAdapter<>(this,
+ android.R.layout.simple_list_item_1, filenames);
+ listDirectories.setAdapter(listDirectoriesAdapter);
+ changeDirectory(Environment.getExternalStorageDirectory());
+ }
+
+ /**
+ * Finishes the activity and returns the selected folder as a result. The
+ * selected folder can also be null.
+ */
+ private void returnSelectedFolder() {
+ if (selectedDir != null && BuildConfig.DEBUG)
+ Log.d(TAG, "Returning " + selectedDir.getAbsolutePath()
+ + " as result");
+ Intent resultData = new Intent();
+ if (selectedDir != null) {
+ resultData.putExtra(RESULT_SELECTED_DIR,
+ selectedDir.getAbsolutePath());
+ }
+ setResult(Activity.RESULT_OK, resultData);
+ finish();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (fileObserver != null) {
+ fileObserver.stopWatching();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (fileObserver != null) {
+ fileObserver.startWatching();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ listDirectoriesAdapter = null;
+ fileObserver = null;
+ }
+
+ /**
+ * Change the directory that is currently being displayed.
+ *
+ * @param dir
+ * The file the activity should switch to. This File must be
+ * non-null and a directory, otherwise the displayed directory
+ * will not be changed
+ */
+ private void changeDirectory(File dir) {
+ if (dir != null && dir.isDirectory()) {
+ File[] contents = dir.listFiles();
+ if (contents != null) {
+ int numDirectories = 0;
+ for (File f : contents) {
+ if (f.isDirectory()) {
+ numDirectories++;
+ }
+ }
+ filesInDir = new File[numDirectories];
+ filenames.clear();
+ for (int i = 0, counter = 0; i < numDirectories; counter++) {
+ if (contents[counter].isDirectory()) {
+ filesInDir[i] = contents[counter];
+ filenames.add(contents[counter].getName());
+ i++;
+ }
+ }
+ Arrays.sort(filesInDir);
+ Collections.sort(filenames);
+ selectedDir = dir;
+ txtvSelectedFolder.setText(dir.getAbsolutePath());
+ listDirectoriesAdapter.notifyDataSetChanged();
+ fileObserver = createFileObserver(dir.getAbsolutePath());
+ fileObserver.startWatching();
+ Log.d(TAG, "Changed directory to " + dir.getAbsolutePath());
+ } else {
+ Log.d(TAG, "Could not change folder: contents of dir were null");
+ }
+ } else {
+ if (dir == null) {
+ Log.d(TAG, "Could not change folder: dir was null");
+ } else {
+ Log.d(TAG, "Could not change folder: dir is no directory");
+ }
+ }
+ refreshButtonState();
+ }
+
+ /**
+ * Changes the state of the buttons depending on the currently selected file
+ * or folder.
+ */
+ private void refreshButtonState() {
+ if (selectedDir != null) {
+ butConfirm.setEnabled(isValidFile(selectedDir));
+ supportInvalidateOptionsMenu();
+ }
+ }
+
+ /** Refresh the contents of the directory that is currently shown. */
+ private void refreshDirectory() {
+ if (selectedDir != null) {
+ changeDirectory(selectedDir);
+ }
+ }
+
+ /** Sets up a FileObserver to watch the current directory. */
+ private FileObserver createFileObserver(String path) {
+ return new FileObserver(path, FileObserver.CREATE | FileObserver.DELETE
+ | FileObserver.MOVED_FROM | FileObserver.MOVED_TO) {
+
+ @Override
+ public void onEvent(int event, String path) {
+ Log.d(TAG, "FileObserver received event " + event);
+ runOnUiThread(() -> refreshDirectory());
+ }
+ };
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- menu.findItem(R.id.new_folder_item)
- .setVisible(isValidFile(selectedDir));
- return true;
- }
+ menu.findItem(R.id.new_folder_item)
+ .setVisible(isValidFile(selectedDir));
+ return true;
+ }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.directory_chooser, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- NavUtils.navigateUpFromSameTask(this);
- return true;
- case R.id.new_folder_item:
- openNewFolderDialog();
- return true;
- case R.id.set_to_default_folder_item:
- selectedDir = null;
- returnSelectedFolder();
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Shows a confirmation dialog that asks the user if he wants to create a
- * new folder.
- */
- private void openNewFolderDialog() {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.create_folder_label);
- builder.setMessage(String.format(getString(R.string.create_folder_msg),
- CREATE_DIRECTORY_NAME));
- builder.setNegativeButton(R.string.cancel_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
- });
- builder.setPositiveButton(R.string.confirm_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- int msg = createFolder();
- Toast t = Toast.makeText(DirectoryChooserActivity.this,
- msg, Toast.LENGTH_SHORT);
- t.show();
- }
- });
- builder.create().show();
- }
-
- /**
- * Creates a new folder in the current directory with the name
- * CREATE_DIRECTORY_NAME.
- */
- private int createFolder() {
- if (selectedDir == null) {
- return R.string.create_folder_error;
- } else if (selectedDir.canWrite()) {
- File newDir = new File(selectedDir, CREATE_DIRECTORY_NAME);
- if (!newDir.exists()) {
- boolean result = newDir.mkdir();
- if (result) {
- return R.string.create_folder_success;
- } else {
- return R.string.create_folder_error;
- }
- } else {
- return R.string.create_folder_error_already_exists;
- }
- } else {
- return R.string.create_folder_error_no_write_access;
- }
- }
-
- /** Returns true if the selected file or directory would be valid selection. */
- private boolean isValidFile(File file) {
- return (file != null && file.isDirectory() && file.canRead() && file
- .canWrite());
- }
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.directory_chooser, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ NavUtils.navigateUpFromSameTask(this);
+ return true;
+ case R.id.new_folder_item:
+ openNewFolderDialog();
+ return true;
+ case R.id.set_to_default_folder_item:
+ selectedDir = null;
+ returnSelectedFolder();
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Shows a confirmation dialog that asks the user if he wants to create a
+ * new folder.
+ */
+ private void openNewFolderDialog() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setTitle(R.string.create_folder_label);
+ builder.setMessage(String.format(getString(R.string.create_folder_msg),
+ CREATE_DIRECTORY_NAME));
+ builder.setNegativeButton(R.string.cancel_label,
+ (dialog, which) -> {
+ dialog.dismiss();
+ });
+ builder.setPositiveButton(R.string.confirm_label,
+ (dialog, which) -> {
+ dialog.dismiss();
+ int msg = createFolder();
+ Toast t = Toast.makeText(DirectoryChooserActivity.this,
+ msg, Toast.LENGTH_SHORT);
+ t.show();
+ });
+ builder.create().show();
+ }
+
+ /**
+ * Creates a new folder in the current directory with the name
+ * CREATE_DIRECTORY_NAME.
+ */
+ private int createFolder() {
+ if (selectedDir == null) {
+ return R.string.create_folder_error;
+ } else if (selectedDir.canWrite()) {
+ File newDir = new File(selectedDir, CREATE_DIRECTORY_NAME);
+ if (!newDir.exists()) {
+ boolean result = newDir.mkdir();
+ if (result) {
+ return R.string.create_folder_success;
+ } else {
+ return R.string.create_folder_error;
+ }
+ } else {
+ return R.string.create_folder_error_already_exists;
+ }
+ } else {
+ return R.string.create_folder_error_no_write_access;
+ }
+ }
+
+ /** Returns true if the selected file or directory would be valid selection. */
+ 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 24b684752..edb973a0c 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
@@ -2,6 +2,9 @@ package de.danoeh.antennapod.activity;
import android.content.ClipData;
import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
@@ -12,24 +15,31 @@ import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.CheckBox;
-import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageView;
+import android.widget.RadioButton;
+import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
-import com.joanzapata.android.iconify.Iconify;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.joanzapata.iconify.Iconify;
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;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LangUtils;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
@@ -38,6 +48,7 @@ import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
*/
public class FeedInfoActivity extends ActionBarActivity {
private static final String TAG = "FeedInfoActivity";
+ private boolean autoDeleteChanged = false;
public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
@@ -51,7 +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
@@ -89,8 +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);
@@ -98,7 +128,7 @@ public class FeedInfoActivity extends ActionBarActivity {
@Override
protected Feed doInBackground(Long... params) {
- return DBReader.getFeed(FeedInfoActivity.this, params[0]);
+ return DBReader.getFeed(params[0]);
}
@Override
@@ -108,19 +138,25 @@ public class FeedInfoActivity extends ActionBarActivity {
Log.d(TAG, "Language is " + feed.getLanguage());
Log.d(TAG, "Author is " + feed.getAuthor());
Log.d(TAG, "URL is " + feed.getDownload_url());
+ FeedPreferences prefs = feed.getPreferences();
imgvCover.post(new Runnable() {
@Override
public void run() {
- Picasso.with(FeedInfoActivity.this)
+ Glide.with(FeedInfoActivity.this)
.load(feed.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(imgvCover);
}
});
txtvTitle.setText(feed.getTitle());
- txtvDescription.setText(feed.getDescription().trim());
+ String description = feed.getDescription();
+ txtvDescription.setText((description != null) ? description.trim() : "");
if (feed.getAuthor() != null) {
txtvAuthor.setText(feed.getAuthor());
}
@@ -132,23 +168,76 @@ public class FeedInfoActivity extends ActionBarActivity {
Iconify.addIcons(txtvUrl);
cbxAutoDownload.setEnabled(UserPreferences.isEnableAutodownload());
- cbxAutoDownload.setChecked(feed.getPreferences().getAutoDownload());
- cbxAutoDownload.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ cbxAutoDownload.setChecked(prefs.getAutoDownload());
+ 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
+ public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+ FeedPreferences.AutoDeleteAction auto_delete_action;
+ switch (parent.getSelectedItemPosition()) {
+ case 0:
+ auto_delete_action = FeedPreferences.AutoDeleteAction.GLOBAL;
+ break;
+
+ case 1:
+ auto_delete_action = FeedPreferences.AutoDeleteAction.YES;
+ break;
+
+ case 2:
+ auto_delete_action = FeedPreferences.AutoDeleteAction.NO;
+ break;
+
+ default: // TODO - add exceptions here
+ return;
+ }
+ feed.getPreferences().setAutoDeleteAction(auto_delete_action);// p
+ autoDeleteChanged = true;
+ }
+
@Override
- public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
- feed.getPreferences().setAutoDownload(checked);
- feed.savePreferences(FeedInfoActivity.this);
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Another interface callback
}
});
+ spnAutoDelete.setSelection(prefs.getAutoDeleteAction().ordinal());
- etxtUsername.setText(feed.getPreferences().getUsername());
- etxtPassword.setText(feed.getPreferences().getPassword());
+ etxtUsername.setText(prefs.getUsername());
+ etxtPassword.setText(prefs.getPassword());
etxtUsername.addTextChangedListener(authTextWatcher);
etxtPassword.addTextChangedListener(authTextWatcher);
- supportInvalidateOptionsMenu();
+ FeedFilter filter = prefs.getFilter();
+ if (filter.includeOnly()) {
+ etxtFilterText.setText(filter.getIncludeFilter());
+ rdoFilterInclude.setChecked(true);
+ rdoFilterExclude.setChecked(false);
+ } else if (filter.excludeOnly()) {
+ etxtFilterText.setText(filter.getExcludeFilter());
+ rdoFilterInclude.setChecked(false);
+ rdoFilterExclude.setChecked(true);
+ } 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");
}
@@ -177,16 +266,53 @@ 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();
- if (feed != null && authInfoChanged) {
- Log.d(TAG, "Auth info changed, saving credentials");
+ if (feed != null) {
FeedPreferences prefs = feed.getPreferences();
- prefs.setUsername(etxtUsername.getText().toString());
- prefs.setPassword(etxtPassword.getText().toString());
- DBWriter.setFeedPreferences(this, prefs);
+ if (authInfoChanged) {
+ Log.d(TAG, "Auth info changed, saving credentials");
+ prefs.setUsername(etxtUsername.getText().toString());
+ prefs.setPassword(etxtPassword.getText().toString());
+ }
+ if (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;
}
}
@@ -204,7 +330,8 @@ public class FeedInfoActivity extends ActionBarActivity {
menu.findItem(R.id.support_item).setVisible(
feed != null && feed.getPaymentLink() != null);
menu.findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null);
- menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null);
+ menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null &&
+ IntentUtils.isCallable(this, new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink()))));
return true;
}
@@ -225,4 +352,34 @@ public class FeedInfoActivity extends ActionBarActivity {
return super.onOptionsItemSelected(item);
}
}
+
+ 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;
+ private final boolean autoDownload;
+
+ public ApplyToEpisodesDialog(Context context, Feed feed, boolean autoDownload) {
+ super(context, R.string.auto_download_apply_to_items_title,
+ R.string.auto_download_apply_to_items_message);
+ this.feed = feed;
+ this.autoDownload = autoDownload;
+ setPositiveText(R.string.yes);
+ setNegativeText(R.string.no);
+ }
+
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ DBWriter.setFeedsItemsAutoDownload(feed, autoDownload);
+ }
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index 0be521f8b..d57199941 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -1,62 +1,82 @@
package de.danoeh.antennapod.activity;
-import android.app.AlertDialog;
+import android.annotation.TargetApi;
+import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.DataSetObserver;
import android.media.AudioManager;
-import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.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;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
+
+import com.bumptech.glide.Glide;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.Validate;
+
+import java.util.List;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.NavListAdapter;
+import de.danoeh.antennapod.core.asynctask.FeedRemover;
+import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.ProgressEvent;
+import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.QueueEvent;
+import de.danoeh.antennapod.core.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.util.StorageUtils;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.fragment.AddFeedFragment;
-import de.danoeh.antennapod.fragment.AllEpisodesFragment;
import de.danoeh.antennapod.fragment.DownloadsFragment;
+import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
import de.danoeh.antennapod.fragment.ItemlistFragment;
-import de.danoeh.antennapod.fragment.NewEpisodesFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
import de.danoeh.antennapod.preferences.PreferenceController;
import de.greenrobot.event.EventBus;
-import java.util.List;
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.Validate;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+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";
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
- | EventDistributor.DOWNLOAD_QUEUED
- | EventDistributor.FEED_LIST_UPDATE
+ private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE
| EventDistributor.UNREAD_ITEMS_UPDATE;
public static final String PREF_NAME = "MainActivityPrefs";
@@ -73,8 +93,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
public static final String[] NAV_DRAWER_TAGS = {
QueueFragment.TAG,
- NewEpisodesFragment.TAG,
- AllEpisodesFragment.TAG,
+ EpisodesFragment.TAG,
DownloadsFragment.TAG,
PlaybackHistoryFragment.TAG,
SubscriptionFragment.TAG,
@@ -88,12 +107,16 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
private View navDrawer;
private ListView navList;
private NavListAdapter navAdapter;
+ private int mPosition = -1;
private ActionBarDrawerToggle drawerToggle;
- private CharSequence drawerTitle;
private CharSequence currentTitle;
+ private ProgressDialog pd;
+
+ private Subscription subscription;
+
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getNoTitleTheme());
@@ -104,9 +127,15 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- getSupportActionBar().setElevation(3.0f);
- drawerTitle = currentTitle = getTitle();
+ 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();
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
navList = (ListView) findViewById(R.id.nav_list);
@@ -121,11 +150,8 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
final FragmentManager fm = getSupportFragmentManager();
- fm.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
- @Override
- public void onBackStackChanged() {
- drawerToggle.setDrawerIndicatorEnabled(fm.getBackStackEntryCount() == 0);
- }
+ fm.addOnBackStackChangedListener(() -> {
+ drawerToggle.setDrawerIndicatorEnabled(fm.getBackStackEntryCount() == 0);
});
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@@ -135,6 +161,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
navList.setAdapter(navAdapter);
navList.setOnItemClickListener(navListClickListener);
navList.setOnItemLongClickListener(newListLongClickListener);
+ registerForContextMenu(navList);
navAdapter.registerDataSetObserver(new DataSetObserver() {
@Override
@@ -143,12 +170,9 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
});
- findViewById(R.id.nav_settings).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- drawerLayout.closeDrawer(navDrawer);
- startActivity(new Intent(MainActivity.this, PreferenceController.getPreferenceActivity()));
- }
+ findViewById(R.id.nav_settings).setOnClickListener(v -> {
+ drawerLayout.closeDrawer(navDrawer);
+ startActivity(new Intent(MainActivity.this, PreferenceController.getPreferenceActivity()));
});
FragmentTransaction transaction = fm.beginTransaction();
@@ -158,23 +182,31 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
transaction.replace(R.id.main_view, mainFragment);
} else {
String lastFragment = getLastNavFragment();
- if(ArrayUtils.contains(NAV_DRAWER_TAGS, lastFragment)) {
+ if (ArrayUtils.contains(NAV_DRAWER_TAGS, lastFragment)) {
loadFragment(lastFragment, null);
+ } else {
+ try {
+ loadFeedFragmentById(Integer.valueOf(lastFragment), null);
+ } catch (NumberFormatException e) {
+ // it's not a number, this happens if we removed
+ // a label from the NAV_DRAWER_TAGS
+ // give them a nice default...
+ loadFragment(QueueFragment.TAG, null);
+ }
}
- // else: lastFragment contains feed id - drawer data is not loaded yet,
- // so loading is postponed until then
}
externalPlayerFragment = new ExternalPlayerFragment();
- transaction.replace(R.id.playerFragment, externalPlayerFragment);
+ transaction.replace(R.id.playerFragment, externalPlayerFragment, ExternalPlayerFragment.TAG);
transaction.commit();
checkFirstLaunch();
}
private void saveLastNavFragment(String tag) {
+ Log.d(TAG, "saveLastNavFragment(tag: " + tag + ")");
SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
SharedPreferences.Editor edit = prefs.edit();
- if(tag != null) {
+ if (tag != null) {
edit.putString(PREF_LAST_FRAGMENT_TAG, tag);
} else {
edit.remove(PREF_LAST_FRAGMENT_TAG);
@@ -184,18 +216,18 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
private String getLastNavFragment() {
SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
- return prefs.getString(PREF_LAST_FRAGMENT_TAG, QueueFragment.TAG);
+ String lastFragment = prefs.getString(PREF_LAST_FRAGMENT_TAG, QueueFragment.TAG);
+ Log.d(TAG, "getLastNavFragment() -> " + lastFragment);
+ return lastFragment;
}
private void checkFirstLaunch() {
SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
if (prefs.getBoolean(PREF_IS_FIRST_LAUNCH, true)) {
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- drawerLayout.openDrawer(navDrawer);
- }
- }, 1500);
+ 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);
@@ -217,30 +249,20 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle(R.string.drawer_preferences);
- builder.setMultiChoiceItems(navLabels, checked, new DialogInterface.OnMultiChoiceClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which, boolean isChecked) {
- if (isChecked) {
- hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]);
- } else {
- hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]);
- }
+ 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, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- UserPreferences.setHiddenDrawerItems(MainActivity.this, hiddenDrawerItems);
- }
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ UserPreferences.setHiddenDrawerItems(hiddenDrawerItems);
});
builder.setNegativeButton(R.string.cancel_label, null);
builder.create().show();
}
- public ActionBar getMainActivtyActionBar() {
- return getSupportActionBar();
- }
-
public boolean isDrawerOpen() {
return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer);
}
@@ -250,6 +272,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
public void loadFragment(int index, Bundle args) {
+ Log.d(TAG, "loadFragment(index: " + index + ", args: " + args + ")");
if (index < navAdapter.getSubscriptionOffset()) {
String tag = navAdapter.getTags().get(index);
loadFragment(tag, args);
@@ -259,18 +282,15 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
}
- public void loadFragment(final String tag, Bundle args) {
- Log.d(TAG, "loadFragment(\"" + tag + "\", " + args + ")");
+ public void loadFragment(String tag, Bundle args) {
+ Log.d(TAG, "loadFragment(tag: " + tag + ", args: " + args + ")");
Fragment fragment = null;
switch (tag) {
case QueueFragment.TAG:
fragment = new QueueFragment();
break;
- case NewEpisodesFragment.TAG:
- fragment = new NewEpisodesFragment();
- break;
- case AllEpisodesFragment.TAG:
- fragment = new AllEpisodesFragment();
+ case EpisodesFragment.TAG:
+ fragment = new EpisodesFragment();
break;
case DownloadsFragment.TAG:
fragment = new DownloadsFragment();
@@ -286,6 +306,12 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
subscriptionFragment.setItemAccess(itemAccess);
fragment = subscriptionFragment;
break;
+ default:
+ // default to the queue
+ tag = QueueFragment.TAG;
+ fragment = new QueueFragment();
+ args = null;
+ break;
}
currentTitle = navAdapter.getLabel(tag);
getSupportActionBar().setTitle(currentTitle);
@@ -298,22 +324,25 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
private void loadFeedFragmentByPosition(int relPos, Bundle args) {
- if(relPos < 0) {
+ if (relPos < 0) {
return;
}
Feed feed = itemAccess.getItem(relPos);
- long feedId = feed.getId();
+ loadFeedFragmentById(feed.getId(), args);
+ }
+
+ public void loadFeedFragmentById(long feedId, Bundle args) {
Fragment fragment = ItemlistFragment.newInstance(feedId);
- if(args != null) {
+ if (args != null) {
fragment.setArguments(args);
}
- saveLastNavFragment(String.valueOf(feed.getId()));
+ saveLastNavFragment(String.valueOf(feedId));
currentTitle = "";
getSupportActionBar().setTitle(currentTitle);
loadChildFragment(fragment);
}
- private void loadFeedFragment(Feed feed){
+ private void loadFeedFragment(Feed feed) {
long feedId = feed.getId();
Fragment fragment = ItemlistFragment.newInstance(feedId);
currentTitle = "";
@@ -321,21 +350,6 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
loadChildFragment(fragment);
}
- public void loadFeedFragmentById(long feedId) {
- if (navDrawerData != null) {
- int relPos = -1;
- List<Feed> feeds = navDrawerData.feeds;
- for (int i = 0; relPos < 0 && i < feeds.size(); i++) {
- if (feeds.get(i).getId() == feedId) {
- relPos = i;
- }
- }
- if(relPos >= 0) {
- loadFeedFragmentByPosition(relPos, null);
- }
- }
- }
-
private void loadFragment(Fragment fragment) {
FragmentManager fragmentManager = getSupportFragmentManager();
// clear back stack
@@ -345,7 +359,13 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
FragmentTransaction t = fragmentManager.beginTransaction();
t.replace(R.id.main_view, fragment, "main");
fragmentManager.popBackStack();
- t.commit();
+ // TODO: we have to allow state loss here
+ // since this function can get called from an AsyncTask which
+ // could be finishing after our app has already committed state
+ // and is about to get shutdown. What we *should* do is
+ // not commit anything in an AsyncTask, but that's a bigger
+ // change than we want now.
+ t.commitAllowingStateLoss();
if (navAdapter != null) {
navAdapter.notifyDataSetChanged();
}
@@ -364,24 +384,26 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
getSupportFragmentManager().popBackStack();
}
- public Toolbar getToolbar() {
- return toolbar;
- }
-
private int getSelectedNavListIndex() {
- String lastFragment = getLastNavFragment();
- int tagIndex = navAdapter.getTags().indexOf(lastFragment);
- if(tagIndex >= 0) {
+ String currentFragment = getLastNavFragment();
+ if (currentFragment == null) {
+ // should not happen, but better safe than sorry
+ return -1;
+ }
+ int tagIndex = navAdapter.getTags().indexOf(currentFragment);
+ if (tagIndex >= 0) {
return tagIndex;
- } else if(ArrayUtils.contains(NAV_DRAWER_TAGS, lastFragment)) {
+ } else if (ArrayUtils.contains(NAV_DRAWER_TAGS, currentFragment)) {
// the fragment was just hidden
return -1;
} else { // last fragment was not a list, but a feed
- long feedId = Long.parseLong(lastFragment);
- List<Feed> feeds = navDrawerData.feeds;
- for (int i = 0; i < feeds.size(); i++) {
- if (feeds.get(i).getId() == feedId) {
- return i + navAdapter.getSubscriptionOffset();
+ long feedId = Long.parseLong(currentFragment);
+ if (navDrawerData != null) {
+ List<Feed> feeds = navDrawerData.feeds;
+ for (int i = 0; i < feeds.size(); i++) {
+ if (feeds.get(i).getId() == feedId) {
+ return i + navAdapter.getSubscriptionOffset();
+ }
}
}
return -1;
@@ -402,10 +424,11 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
private AdapterView.OnItemLongClickListener newListLongClickListener = new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
- if(position < navAdapter.getTags().size()) {
+ if (position < navAdapter.getTags().size()) {
showDrawerPreferencesDialog();
return true;
} else {
+ mPosition = position;
return false;
}
}
@@ -443,6 +466,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
+ RatingDialog.init(this);
}
@Override
@@ -460,16 +484,35 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
(intent.hasExtra(EXTRA_NAV_INDEX) || intent.hasExtra(EXTRA_FRAGMENT_TAG))) {
handleNavIntent();
}
-
loadData();
+ RatingDialog.check();
}
@Override
protected void onStop() {
super.onStop();
- cancelLoadTask();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
+ if (subscription != null) {
+ subscription.unsubscribe();
+ }
+ if (pd != null) {
+ pd.dismiss();
+ }
+ }
+
+
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ Glide.get(this).trimMemory(level);
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ Glide.get(this).clearMemory();
}
@Override
@@ -486,8 +529,95 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
}
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ if (v.getId() != R.id.nav_list) {
+ return;
+ }
+ AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ int position = adapterInfo.position;
+ if (position < navAdapter.getSubscriptionOffset()) {
+ return;
+ }
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.nav_feed_context, menu);
+ Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
+ menu.setHeaderTitle(feed.getTitle());
+ // episodes are not loaded, so we cannot check if the podcast has new or unplayed ones!
+ }
+
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ final int position = mPosition;
+ mPosition = -1; // reset
+ if (position < 0) {
+ return false;
+ }
+ Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
+ switch (item.getItemId()) {
+ case R.id.mark_all_seen_item:
+ DBWriter.markFeedSeen(feed.getId());
+ return true;
+ case R.id.mark_all_read_item:
+ DBWriter.markFeedRead(feed.getId());
+ return true;
+ case R.id.remove_item:
+ final FeedRemover remover = new FeedRemover(this, feed) {
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ if (getSelectedNavListIndex() == position) {
+ loadFragment(EpisodesFragment.TAG, null);
+ }
+ }
+ };
+ ConfirmationDialog conDialog = new ConfirmationDialog(this,
+ R.string.remove_feed_label,
+ R.string.feed_delete_confirmation_msg) {
+ @Override
+ public void onConfirmButtonPressed(
+ DialogInterface dialog) {
+ dialog.dismiss();
+ if (externalPlayerFragment != null) {
+ PlaybackController controller = externalPlayerFragment.getPlaybackControllerTestingOnly();
+ if (controller != null) {
+ Playable playable = controller.getMedia();
+ if (playable != null && playable instanceof FeedMedia) {
+ FeedMedia media = (FeedMedia) playable;
+ if (media.getItem().getFeed().getId() == feed.getId()) {
+ Log.d(TAG, "Currently playing episode is about to be deleted, skipping");
+ remover.skipOnCompletion = true;
+ if (controller.getStatus() == PlayerStatus.PLAYING) {
+ sendBroadcast(new Intent(
+ PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
+ }
+ }
+ }
+ }
+ }
+ remover.executeAsync();
+ }
+ };
+ conDialog.createNewDialog().show();
+ return true;
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ @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() {
@@ -502,7 +632,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;
@@ -525,47 +655,33 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
@Override
- public int getNumberOfUnreadFeedItems(long feedId) {
- return (navDrawerData != null) ? navDrawerData.numUnreadFeedItems.get(feedId) : 0;
+ public int getNumberOfDownloadedItems() {
+ return (navDrawerData != null) ? navDrawerData.numDownloadedItems : 0;
+ }
+
+ @Override
+ public int getFeedCounter(long feedId) {
+ return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0;
}
};
private void loadData() {
- cancelLoadTask();
- loadTask = new AsyncTask<Void, Void, DBReader.NavDrawerData>() {
- @Override
- protected DBReader.NavDrawerData doInBackground(Void... params) {
- return DBReader.getNavDrawerData(MainActivity.this);
- }
-
- @Override
- protected void onPostExecute(DBReader.NavDrawerData result) {
- super.onPostExecute(navDrawerData);
- boolean handleIntent = (navDrawerData == null);
-
- navDrawerData = result;
- navAdapter.notifyDataSetChanged();
-
- String lastFragment = getLastNavFragment();
- if(!ArrayUtils.contains(NAV_DRAWER_TAGS, lastFragment)) {
- long feedId = Long.valueOf(lastFragment);
- loadFeedFragmentById(feedId);
- saveLastNavFragment(null);
- }
+ subscription = Observable.fromCallable(() -> DBReader.getNavDrawerData())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ boolean handleIntent = (navDrawerData == null);
- if (handleIntent) {
- handleNavIntent();
- }
- }
- };
- loadTask.execute();
- }
+ navDrawerData = result;
+ navAdapter.notifyDataSetChanged();
- private void cancelLoadTask() {
- if (loadTask != null) {
- loadTask.cancel(true);
- }
+ if (handleIntent) {
+ handleNavIntent();
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
public void onEvent(QueueEvent event) {
@@ -573,10 +689,28 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
loadData();
}
- public void onEvent(SubscriptionFragment.SubscriptionEvent event){
+ public void onEvent(SubscriptionFragment.SubscriptionEvent event) {
loadFeedFragment(event.feed);
}
+ public void onEventMainThread(ProgressEvent event) {
+ Log.d(TAG, "onEvent(" + event + ")");
+ switch (event.action) {
+ case START:
+ pd = new ProgressDialog(this);
+ pd.setMessage(event.message);
+ pd.setIndeterminate(true);
+ pd.setCancelable(false);
+ pd.show();
+ break;
+ case END:
+ if (pd != null) {
+ pd.dismiss();
+ }
+ break;
+ }
+ }
+
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
@@ -610,15 +744,4 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
super.onNewIntent(intent);
setIntent(intent);
}
-
- @Override
- public void onBackPressed() {
- // Make sure to have consistent behaviour across android apps
- // Close the nav drawer if open on the first back button press
- if(drawerLayout.isDrawerOpen(navDrawer)){
- drawerLayout.closeDrawer(navDrawer);
- return;
- }
- super.onBackPressed();
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index 0cd388b9d..bdc210651 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -1,56 +1,82 @@
package de.danoeh.antennapod.activity;
-import android.app.AlertDialog;
-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;
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.TimeDialog;
+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) {
@@ -150,7 +176,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
}
protected void onPlaybackSpeedChange() {
-
+ updateButPlaybackSpeed();
}
protected void onServiceQueried() {
@@ -231,10 +257,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
@@ -248,27 +281,63 @@ 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()
);
- menu.findItem(R.id.share_link_item).setVisible(
- media != null && media.getWebsiteLink() != null);
- menu.findItem(R.id.visit_website_item).setVisible(
- media != null && media.getWebsiteLink() != null);
- menu.findItem(R.id.skip_episode_item).setVisible(media != null);
+
+ 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() != 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);
+
+ boolean isItemHasDownloadLink = media != null && (media instanceof FeedMedia) && ((FeedMedia) media).getDownload_url() != null;
+ menu.findItem(R.id.share_download_url_item).setVisible(isItemHasDownloadLink);
+ menu.findItem(R.id.share_download_url_with_position_item).setVisible(isItemHasDownloadLink);
+
+ menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink);
+
+ 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,
@@ -277,80 +346,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()) {
- AlertDialog.Builder stDialog = new AlertDialog.Builder(this);
- stDialog.setTitle(R.string.sleep_timer_label);
- stDialog.setMessage(getString(R.string.time_left_label)
- + Converter.getDurationStringLong((int) controller
- .getSleepTimerTimeLeft()));
- stDialog.setPositiveButton(
- R.string.disable_sleeptimer_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- dialog.dismiss();
- controller.disableSleepTimer();
- }
+ } 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();
}
- );
- stDialog.setNegativeButton(R.string.cancel_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- dialog.dismiss();
- }
+
+ @Override
+ public void onNegative(MaterialDialog dialog) {
+ dialog.dismiss();
}
- );
- stDialog.create().show();
- }
- break;
- case R.id.set_sleeptimer_item:
- if (controller.serviceAvailable()) {
- TimeDialog td = new TimeDialog(this,
- R.string.set_sleeptimer_label,
- R.string.set_sleeptimer_label) {
+ });
+ 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, false)
+ .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 onTimeEntered(long millis) {
- controller.setSleepTimer(millis);
+ 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);
+ });
+ }
}
- };
- td.show();
- break;
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if(controller != null && !controller.canSetPlaybackSpeed()) {
+ VariableSpeedDialog.showGetPluginDialog(MediaplayerActivity.this);
+ }
+ }
- }
- 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) {
- FeedItem feedItem = ((FeedMedia) media).getItem();
- DBTasks.flattrItemIfLoggedIn(this, feedItem);
- }
- break;
- case R.id.share_link_item:
- ShareUtils.shareLink(this, media.getWebsiteLink());
- break;
- case R.id.skip_episode_item:
- sendBroadcast(new Intent(
- PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
- break;
- default:
- return false;
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ 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 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);
+ }
+
+ @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;
}
}
@@ -383,7 +607,13 @@ public abstract class MediaplayerActivity extends ActionBarActivity
&& controller.getMedia() != null) {
txtvPosition.setText(Converter
.getDurationStringLong(currentPosition));
- txtvLength.setText(Converter.getDurationStringLong(duration));
+ if (showTimeLeft) {
+ txtvLength.setText("-" + Converter
+ .getDurationStringLong(duration - currentPosition));
+ } else {
+ txtvLength.setText(Converter
+ .getDurationStringLong(duration));
+ }
updateProgressbarPosition(currentPosition, duration);
} else {
Log.w(TAG, "Could not react to position observer update because of invalid time");
@@ -392,7 +622,7 @@ public abstract class MediaplayerActivity extends ActionBarActivity
}
private void updateProgressbarPosition(int position, int duration) {
- Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration +")");
+ Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration + ")");
float progress = ((float) position) / duration;
sbPosition.setProgress((int) (progress * sbPosition.getMax()));
}
@@ -406,16 +636,33 @@ 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())));
+ txtvPosition.setText(Converter.getDurationStringLong((media.getPosition())));
if (media.getDuration() != 0) {
- txtvLength.setText(Converter.getDurationStringLong(media
- .getDuration()));
- float progress = ((float) media.getPosition())
- / media.getDuration();
+ txtvLength.setText(Converter.getDurationStringLong(media.getDuration()));
+ float progress = ((float) media.getPosition()) / media.getDuration();
sbPosition.setProgress((int) (progress * sbPosition.getMax()));
+ if (showTimeLeft) {
+ int timeLeft = media.getDuration() - media.getPosition();
+ txtvLength.setText("-" + Converter.getDurationStringLong(timeLeft));
+ }
+ }
+ 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 {
@@ -427,18 +674,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
@@ -446,98 +720,120 @@ 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_unit_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,
- 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.setPrefFastForwardSecs(choice);
- txtvFF.setText(String.valueOf(choice));
- }
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ UserPreferences.setPrefRewindSecs(choice);
+ 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_unit_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);
+ txtvFF.setText(String.valueOf(choice));
});
builder.create().show();
return true;
@@ -545,6 +841,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();
@@ -555,12 +856,9 @@ public abstract class MediaplayerActivity extends ActionBarActivity
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();
@@ -569,11 +867,20 @@ public abstract class MediaplayerActivity extends ActionBarActivity
float prog;
@Override
- public void onProgressChanged(SeekBar seekBar, int progress,
- boolean fromUser) {
+ public void onProgressChanged (SeekBar seekBar,int progress, boolean fromUser) {
if (controller != null) {
- prog = controller.onSeekBarProgressChanged(seekBar, progress, fromUser,
- txtvPosition);
+ 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");
}
}
@@ -591,4 +898,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 2b1b13ae6..8c2b7f838 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -1,21 +1,36 @@
package de.danoeh.antennapod.activity;
-import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.os.Bundle;
+import android.os.Looper;
+import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AlertDialog;
+import android.text.TextUtils;
import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
import org.apache.commons.lang3.StringUtils;
-import org.xml.sax.SAXException;
+import org.jsoup.Jsoup;
+import org.jsoup.examples.HtmlToPlainText;
+import org.jsoup.nodes.Document;
import java.io.File;
import java.io.IOException;
@@ -24,16 +39,23 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
-import javax.xml.parsers.ParserConfigurationException;
-
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
+import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.service.download.HttpDownloader;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
import de.danoeh.antennapod.core.syndication.handler.FeedHandlerResult;
import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException;
@@ -43,6 +65,12 @@ import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.URLChecker;
import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
+import de.greenrobot.event.EventBus;
+import rx.Observable;
+import rx.Subscriber;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Downloads a feed from a feed URL and parses it. Subclasses can display the
@@ -52,27 +80,62 @@ import de.danoeh.antennapod.dialog.AuthenticationDialog;
* If the feed cannot be downloaded or parsed, an error dialog will be displayed
* and the activity will finish as soon as the error dialog is closed.
*/
-public abstract class OnlineFeedViewActivity extends ActionBarActivity {
+public class OnlineFeedViewActivity extends ActionBarActivity {
+
private static final String TAG = "OnlineFeedViewActivity";
+
public static final String ARG_FEEDURL = "arg.feedurl";
- /**
- * Optional argument: specify a title for the actionbar.
- */
+ // Optional argument: specify a title for the actionbar.
public static final String ARG_TITLE = "title";
+ private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE;
+
public static final int RESULT_ERROR = 2;
+ private volatile List<Feed> feeds;
private Feed feed;
- private Map<String, String> alternateFeedUrls;
+ private String selectedDownloadUrl;
private Downloader downloader;
private boolean isPaused;
+ private Dialog dialog;
+
+ private Button subscribeButton;
+
+ private Subscription download;
+ private Subscription parser;
+ private Subscription updater;
+
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ setSubscribeButtonState(feed);
+ }
+
+ private EventDistributor.EventListener listener = new EventDistributor.EventListener() {
+ @Override
+ public void update(EventDistributor eventDistributor, Integer arg) {
+ if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) {
+ updater = Observable.fromCallable(() -> DBReader.getFeedList())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(feeds -> {
+ OnlineFeedViewActivity.this.feeds = feeds;
+ setSubscribeButtonState(feed);
+ }
+ );
+ } else if ((arg & EVENTS) != 0) {
+ setSubscribeButtonState(feed);
+ }
+ }
+ };
+
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
if (getIntent() != null && getIntent().hasExtra(ARG_TITLE)) {
getSupportActionBar().setTitle(getIntent().getStringExtra(ARG_TITLE));
@@ -83,11 +146,10 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
final String feedUrl;
if (getIntent().hasExtra(ARG_FEEDURL)) {
feedUrl = getIntent().getStringExtra(ARG_FEEDURL);
- } else if (StringUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)
- || StringUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
- feedUrl = (StringUtils.equals(getIntent().getAction(), Intent.ACTION_SEND))
+ } else if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)
+ || TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
+ feedUrl = (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND))
? getIntent().getStringExtra(Intent.EXTRA_TEXT) : getIntent().getDataString();
-
getSupportActionBar().setTitle(R.string.add_new_feed_label);
} else {
throw new IllegalArgumentException("Activity must be started with feedurl argument!");
@@ -102,16 +164,64 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
}
}
+ /**
+ * Displays a progress indicator.
+ */
+ private void setLoadingLayout() {
+ RelativeLayout rl = new RelativeLayout(this);
+ RelativeLayout.LayoutParams rlLayoutParams = new RelativeLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT);
+
+ ProgressBar pb = new ProgressBar(this);
+ pb.setIndeterminate(true);
+ RelativeLayout.LayoutParams pbLayoutParams = new RelativeLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ pbLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
+ rl.addView(pb, pbLayoutParams);
+ addContentView(rl, rlLayoutParams);
+ }
+
@Override
protected void onResume() {
super.onResume();
isPaused = false;
+ EventDistributor.getInstance().register(listener);
+ EventBus.getDefault().register(this);
}
@Override
protected void onPause() {
super.onPause();
isPaused = true;
+ EventDistributor.getInstance().unregister(listener);
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ if (downloader != null && !downloader.isFinished()) {
+ downloader.cancel();
+ }
+ if(dialog != null && dialog.isShowing()) {
+ dialog.dismiss();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if(updater != null) {
+ updater.unsubscribe();
+ }
+ if(download != null) {
+ download.unsubscribe();
+ }
+ if(parser != null) {
+ parser.unsubscribe();
+ }
}
@Override
@@ -123,14 +233,6 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
}
}
- @Override
- protected void onStop() {
- super.onStop();
- if (downloader != null && !downloader.isFinished()) {
- downloader.cancel();
- }
- }
-
private void resetIntent(String url, String title) {
Intent intent = new Intent();
intent.putExtra(ARG_FEEDURL, url);
@@ -138,41 +240,19 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
setIntent(intent);
}
-
- private void onDownloadCompleted(final Downloader downloader) {
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- Log.d(TAG, "Download was completed");
- DownloadStatus status = downloader.getResult();
- if (status != null) {
- if (!status.isCancelled()) {
- if (status.isSuccessful()) {
- parseFeed();
- } else if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
- if (!isFinishing() && !isPaused) {
- Dialog dialog = new FeedViewAuthenticationDialog(OnlineFeedViewActivity.this,
- R.string.authentication_notification_title, downloader.getDownloadRequest().getSource());
- dialog.show();
- }
- } else {
- String errorMsg = status.getReason().getErrorString(
- OnlineFeedViewActivity.this);
- if (errorMsg != null
- && status.getReasonDetailed() != null) {
- errorMsg += " (" + status.getReasonDetailed() + ")";
- }
- showErrorDialog(errorMsg);
- }
- }
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ Intent destIntent = new Intent(this, MainActivity.class);
+ if (NavUtils.shouldUpRecreateTask(this, destIntent)) {
+ startActivity(destIntent);
} else {
- Log.wtf(TAG, "DownloadStatus returned by Downloader was null");
- finish();
+ NavUtils.navigateUpFromSameTask(this);
}
- }
- });
-
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
}
private void startFeedDownload(String url, String username, String password) {
@@ -180,139 +260,232 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
url = URLChecker.prepareURL(url);
feed = new Feed(url, new Date(0));
if (username != null && password != null) {
- feed.setPreferences(new FeedPreferences(0, false, username, password));
+ feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL, username, password));
}
String fileUrl = new File(getExternalCacheDir(),
FileNameGenerator.generateFileName(feed.getDownload_url())).toString();
feed.setFile_url(fileUrl);
final DownloadRequest request = new DownloadRequest(feed.getFile_url(),
- feed.getDownload_url(), "OnlineFeed", 0, Feed.FEEDFILETYPE_FEED, username, password, true, null);
- downloader = new HttpDownloader(request);
- new Thread() {
- @Override
- public void run() {
- loadData();
- downloader.call();
- onDownloadCompleted(downloader);
- }
- }.start();
-
-
- }
-
- /**
- * Displays a progress indicator.
- */
- private void setLoadingLayout() {
- RelativeLayout rl = new RelativeLayout(this);
- RelativeLayout.LayoutParams rlLayoutParams = new RelativeLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.MATCH_PARENT);
+ feed.getDownload_url(), "OnlineFeed", 0, Feed.FEEDFILETYPE_FEED, username, password,
+ true, null);
- ProgressBar pb = new ProgressBar(this);
- pb.setIndeterminate(true);
- RelativeLayout.LayoutParams pbLayoutParams = new RelativeLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.WRAP_CONTENT);
- pbLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
- rl.addView(pb, pbLayoutParams);
- addContentView(rl, rlLayoutParams);
+ download = Observable.create(new Observable.OnSubscribe<DownloadStatus>() {
+ @Override
+ public void call(Subscriber<? super DownloadStatus> subscriber) {
+ feeds = DBReader.getFeedList();
+ downloader = new HttpDownloader(request);
+ downloader.call();
+ Log.d(TAG, "Download was completed");
+ subscriber.onNext(downloader.getResult());
+ subscriber.onCompleted();
+ }
+ })
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(status -> {
+ if (status != null) {
+ if (!status.isCancelled()) {
+ if (status.isSuccessful()) {
+ parseFeed();
+ } else if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
+ if (!isFinishing() && !isPaused) {
+ dialog = new FeedViewAuthenticationDialog(OnlineFeedViewActivity.this,
+ R.string.authentication_notification_title, downloader.getDownloadRequest().getSource());
+ dialog.show();
+ }
+ } else {
+ String errorMsg = status.getReason().getErrorString(OnlineFeedViewActivity.this);
+ if (errorMsg != null && status.getReasonDetailed() != null) {
+ errorMsg += " (" + status.getReasonDetailed() + ")";
+ }
+ showErrorDialog(errorMsg);
+ }
+ }
+ } else {
+ Log.wtf(TAG, "DownloadStatus returned by Downloader was null");
+ finish();
+ }
+ });
}
private void parseFeed() {
if (feed == null || feed.getFile_url() == null && feed.isDownloaded()) {
- throw new IllegalStateException(
- "feed must be non-null and downloaded when parseFeed is called");
+ throw new IllegalStateException("feed must be non-null and downloaded when parseFeed is called");
}
-
Log.d(TAG, "Parsing feed");
- Thread thread = new Thread() {
-
+ parser = Observable.create(new Observable.OnSubscribe<FeedHandlerResult>() {
@Override
- public void run() {
- String reasonDetailed = "";
- boolean successful = false;
- FeedHandler handler = new FeedHandler();
- try {
- FeedHandlerResult result = handler.parseFeed(feed);
- feed = result.feed;
- alternateFeedUrls = result.alternateFeedUrls;
- successful = true;
- } catch (SAXException e) {
- e.printStackTrace();
- reasonDetailed = e.getMessage();
- } catch (IOException e) {
- e.printStackTrace();
- reasonDetailed = e.getMessage();
- } catch (ParserConfigurationException e) {
- e.printStackTrace();
- reasonDetailed = e.getMessage();
- } catch (UnsupportedFeedtypeException e) {
- Log.d(TAG, "Unsupported feed type detected");
- if (StringUtils.equalsIgnoreCase("html", e.getRootElement())) {
- if (showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url())) {
- return;
+ public void call(Subscriber<? super FeedHandlerResult> subscriber) {
+ FeedHandler handler = new FeedHandler();
+ try {
+ FeedHandlerResult result = handler.parseFeed(feed);
+ subscriber.onNext(result);
+ } catch (UnsupportedFeedtypeException e) {
+ Log.d(TAG, "Unsupported feed type detected");
+ if (TextUtils.equals("html", e.getRootElement().toLowerCase())) {
+ showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url());
+ } else {
+ subscriber.onError(e);
+ }
+ } catch (Exception e) {
+ subscriber.onError(e);
+ } finally {
+ boolean rc = new File(feed.getFile_url()).delete();
+ Log.d(TAG, "Deleted feed source file. Result: " + rc);
+ subscriber.onCompleted();
}
- } else {
- e.printStackTrace();
- reasonDetailed = e.getMessage();
}
- } finally {
- boolean rc = new File(feed.getFile_url()).delete();
- Log.d(TAG, "Deleted feed source file. Result: " + rc);
- }
-
- if (successful) {
- beforeShowFeedInformation(feed, alternateFeedUrls);
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- showFeedInformation(feed, alternateFeedUrls);
- }
- });
- } else {
- final String errorMsg =
- DownloadError.ERROR_PARSER_EXCEPTION.getErrorString(
- OnlineFeedViewActivity.this)
- + " (" + reasonDetailed + ")";
- runOnUiThread(new Runnable() {
-
- @Override
- public void run() {
- showErrorDialog(errorMsg);
- }
- });
- }
- }
- };
- thread.start();
- }
-
- /**
- * Can be used to load data asynchronously.
- */
- protected void loadData() {
-
+ })
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ beforeShowFeedInformation(result.feed);
+ showFeedInformation(result.feed, result.alternateFeedUrls);
+ }, error -> {
+ String errorMsg = DownloadError.ERROR_PARSER_EXCEPTION.getErrorString(
+ OnlineFeedViewActivity.this) + " (" + error.getMessage() + ")";
+ showErrorDialog(errorMsg);
+ });
}
/**
* Called after the feed has been downloaded and parsed and before showFeedInformation is called.
* This method is executed on a background thread
*/
- protected void beforeShowFeedInformation(Feed feed, Map<String, String> alternateFeedUrls) {
-
+ private void beforeShowFeedInformation(Feed feed) {
+ // remove HTML tags from descriptions
+ Log.d(TAG, "Removing HTML from shownotes");
+ if (feed.getItems() != null) {
+ HtmlToPlainText formatter = new HtmlToPlainText();
+ for (FeedItem item : feed.getItems()) {
+ if (item.getDescription() != null) {
+ Document description = Jsoup.parse(item.getDescription());
+ item.setDescription(StringUtils.trim(formatter.getPlainText(description)));
+ }
+ }
+ }
}
/**
* Called when feed parsed successfully.
* This method is executed on the GUI thread.
*/
- protected void showFeedInformation(Feed feed, Map<String, String> alternateFeedUrls) {
+ private void showFeedInformation(final Feed feed, Map<String, String> alternateFeedUrls) {
+ setContentView(R.layout.listview_activity);
+
+ this.feed = feed;
+ this.selectedDownloadUrl = feed.getDownload_url();
+ EventDistributor.getInstance().register(listener);
+ ListView listView = (ListView) findViewById(R.id.listview);
+ LayoutInflater inflater = (LayoutInflater)
+ getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false);
+ listView.addHeaderView(header);
+
+ listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems()));
+
+ ImageView cover = (ImageView) header.findViewById(R.id.imgvCover);
+ TextView title = (TextView) header.findViewById(R.id.txtvTitle);
+ TextView author = (TextView) header.findViewById(R.id.txtvAuthor);
+ TextView description = (TextView) header.findViewById(R.id.txtvDescription);
+ Spinner spAlternateUrls = (Spinner) header.findViewById(R.id.spinnerAlternateUrls);
+
+ subscribeButton = (Button) header.findViewById(R.id.butSubscribe);
+
+ if (feed.getImage() != null && StringUtils.isNotBlank(feed.getImage().getDownload_url())) {
+ Glide.with(this)
+ .load(feed.getImage().getDownload_url())
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
+ .into(cover);
+ }
+
+ title.setText(feed.getTitle());
+ author.setText(feed.getAuthor());
+ description.setText(feed.getDescription());
+
+ subscribeButton.setOnClickListener(v -> {
+ try {
+ Feed f = new Feed(selectedDownloadUrl, new Date(0), feed.getTitle());
+ f.setPreferences(feed.getPreferences());
+ this.feed = f;
+
+ DownloadRequester.getInstance().downloadFeed(this, f);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DownloadRequestErrorDialogCreator.newRequestErrorDialog(OnlineFeedViewActivity.this,
+ e.getMessage());
+ }
+ setSubscribeButtonState(feed);
+ });
+
+ if (alternateFeedUrls.isEmpty()) {
+ spAlternateUrls.setVisibility(View.GONE);
+ } else {
+ spAlternateUrls.setVisibility(View.VISIBLE);
+
+ final List<String> alternateUrlsList = new ArrayList<>();
+ final List<String> alternateUrlsTitleList = new ArrayList<>();
+
+ alternateUrlsList.add(feed.getDownload_url());
+ alternateUrlsTitleList.add(feed.getTitle());
+
+ alternateUrlsList.addAll(alternateFeedUrls.keySet());
+ for (String url : alternateFeedUrls.keySet()) {
+ alternateUrlsTitleList.add(alternateFeedUrls.get(url));
+ }
+ ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item, alternateUrlsTitleList);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spAlternateUrls.setAdapter(adapter);
+ spAlternateUrls.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ selectedDownloadUrl = alternateUrlsList.get(position);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+
+ }
+ });
+ }
+ setSubscribeButtonState(feed);
+ }
+
+ private void setSubscribeButtonState(Feed feed) {
+ if (subscribeButton != null && feed != null) {
+ if (DownloadRequester.getInstance().isDownloadingFile(feed.getDownload_url())) {
+ subscribeButton.setEnabled(false);
+ subscribeButton.setText(R.string.downloading_label);
+ } else if (feedInFeedlist(feed)) {
+ subscribeButton.setEnabled(false);
+ subscribeButton.setText(R.string.subscribed_label);
+ } else {
+ subscribeButton.setEnabled(true);
+ subscribeButton.setText(R.string.subscribe_label);
+ }
+ }
+ }
+
+ private boolean feedInFeedlist(Feed feed) {
+ if (feeds == null || feed == null) {
+ return false;
+ }
+ for (Feed f : feeds) {
+ if (f.getIdentifyingValue().equals(feed.getIdentifyingValue())) {
+ return true;
+ }
+ }
+ return false;
}
private void showErrorDialog(String errorMsg) {
+ assert(Looper.myLooper() == Looper.getMainLooper()); // run on UI thread
if (!isFinishing() && !isPaused) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.error_label);
@@ -322,85 +495,71 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
builder.setMessage(R.string.error_msg_prefix);
}
builder.setNeutralButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
+ (dialog, which) -> {
+ dialog.cancel();
}
);
- builder.setOnCancelListener(new OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- setResult(RESULT_ERROR);
- finish();
- }
+ builder.setOnCancelListener(dialog -> {
+ setResult(RESULT_ERROR);
+ finish();
});
- builder.show();
+ if(dialog != null && dialog.isShowing()) {
+ dialog.dismiss();
+ }
+ dialog = builder.show();
}
}
- private boolean showFeedDiscoveryDialog(File feedFile, String baseUrl) {
+ private void showFeedDiscoveryDialog(File feedFile, String baseUrl) {
FeedDiscoverer fd = new FeedDiscoverer();
final Map<String, String> urlsMap;
try {
urlsMap = fd.findLinks(feedFile, baseUrl);
if (urlsMap == null || urlsMap.isEmpty()) {
- return false;
+ return;
}
} catch (IOException e) {
e.printStackTrace();
- return false;
+ return;
}
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (isPaused || isFinishing()) {
- return;
- }
+ if (isPaused || isFinishing()) {
+ return;
+ }
- final List<String> titles = new ArrayList<String>();
- final List<String> urls = new ArrayList<String>();
+ final List<String> titles = new ArrayList<>();
+ final List<String> urls = new ArrayList<>();
- urls.addAll(urlsMap.keySet());
- for (String url : urls) {
- titles.add(urlsMap.get(url));
- }
+ urls.addAll(urlsMap.keySet());
+ for (String url : urls) {
+ titles.add(urlsMap.get(url));
+ }
- final ArrayAdapter<String> adapter = new ArrayAdapter<String>(OnlineFeedViewActivity.this, R.layout.ellipsize_start_listitem, R.id.txtvTitle, titles);
- DialogInterface.OnClickListener onClickListener = new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String selectedUrl = urls.get(which);
- dialog.dismiss();
- resetIntent(selectedUrl, titles.get(which));
- FeedPreferences prefs = feed.getPreferences();
- if(prefs != null) {
- startFeedDownload(selectedUrl, prefs.getUsername(), prefs.getPassword());
- } else {
- startFeedDownload(selectedUrl, null, null);
- }
- }
- };
-
- AlertDialog.Builder ab = new AlertDialog.Builder(OnlineFeedViewActivity.this)
- .setTitle(R.string.feeds_label)
- .setCancelable(true)
- .setOnCancelListener(new OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- finish();
- }
- })
- .setAdapter(adapter, onClickListener);
- ab.show();
+ final ArrayAdapter<String> adapter = new ArrayAdapter<>(OnlineFeedViewActivity.this, R.layout.ellipsize_start_listitem, R.id.txtvTitle, titles);
+ DialogInterface.OnClickListener onClickListener = (dialog, which) -> {
+ String selectedUrl = urls.get(which);
+ dialog.dismiss();
+ resetIntent(selectedUrl, titles.get(which));
+ FeedPreferences prefs = feed.getPreferences();
+ if(prefs != null) {
+ startFeedDownload(selectedUrl, prefs.getUsername(), prefs.getPassword());
+ } else {
+ startFeedDownload(selectedUrl, null, null);
}
- });
+ };
+ AlertDialog.Builder ab = new AlertDialog.Builder(OnlineFeedViewActivity.this)
+ .setTitle(R.string.feeds_label)
+ .setCancelable(true)
+ .setOnCancelListener(dialog -> finish())
+ .setAdapter(adapter, onClickListener);
- return true;
+ runOnUiThread(() -> {
+ if(dialog != null && dialog.isShowing()) {
+ dialog.dismiss();
+ }
+ dialog = ab.show();
+ });
}
private class FeedViewAuthenticationDialog extends AuthenticationDialog {
@@ -423,4 +582,5 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
startFeedDownload(feedUrl, username, password);
}
}
+
}
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 e42072ead..46e5f0e8e 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java
@@ -1,18 +1,27 @@
package de.danoeh.antennapod.activity;
-import android.app.AlertDialog;
+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 {
- @Override
+ private static final String TAG = "OpmlImportFromIntentAct";
+
+
+ @Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
@@ -20,10 +29,10 @@ public class OpmlImportFromIntentActivity extends OpmlImportBaseActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
try {
- URL mOpmlURL = new URL(getIntent().getData().toString());
- BufferedReader in = new BufferedReader(new InputStreamReader(mOpmlURL.openStream(),
- LangUtils.UTF_8));
- startImport(in);
+ 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();
}
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 c1bbb7e52..6e3991739 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
@@ -2,14 +2,12 @@ package de.danoeh.antennapod.activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
-import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
@@ -18,11 +16,10 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.Reader;
-import java.util.List;
-import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LangUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
@@ -31,7 +28,7 @@ import de.danoeh.antennapod.core.util.StorageUtils;
*/
public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
- private static final String TAG = "OpmlImportFromPathActivity";
+ private static final String TAG = "OpmlImportFromPathAct";
private static final int CHOOSE_OPML_FILE = 1;
@@ -53,59 +50,44 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
final TextView txtvHeaderExplanation3 = (TextView) findViewById(R.id.txtvHeadingExplanation3);
Button butChooseFilesystem = (Button) findViewById(R.id.butChooseFileFromFilesystem);
- butChooseFilesystem.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- chooseFileFromFilesystem();
- }
-
- });
+ butChooseFilesystem.setOnClickListener(v -> chooseFileFromFilesystem());
Button butChooseExternal = (Button) findViewById(R.id.butChooseFileFromExternal);
- butChooseExternal.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- chooseFileFromExternal();
- }
- });
-
- int nextOption = 1;
+ butChooseExternal.setOnClickListener(v -> chooseFileFromExternal());
+
+ int nextOption = 1;
+ String optionLabel = getString(R.string.opml_import_option);
intentPickAction = new Intent(Intent.ACTION_PICK);
intentPickAction.setData(Uri.parse("file://"));
- List<ResolveInfo> intentActivities = getPackageManager()
- .queryIntentActivities(intentPickAction, CHOOSE_OPML_FILE);
- if(intentActivities.size() == 0) {
- intentPickAction.setData(null);
- intentActivities = getPackageManager()
- .queryIntentActivities(intentPickAction, CHOOSE_OPML_FILE);
- if(intentActivities.size() == 0) {
- txtvHeaderExplanation1.setVisibility(View.GONE);
- txtvExplanation1.setVisibility(View.GONE);
- findViewById(R.id.divider1).setVisibility(View.GONE);
- butChooseFilesystem.setVisibility(View.GONE);
- }
+
+ if(false == IntentUtils.isCallable(getApplicationContext(), intentPickAction)) {
+ intentPickAction.setData(null);
+ if(false == IntentUtils.isCallable(getApplicationContext(), intentPickAction)) {
+ txtvHeaderExplanation1.setVisibility(View.GONE);
+ txtvExplanation1.setVisibility(View.GONE);
+ findViewById(R.id.divider1).setVisibility(View.GONE);
+ butChooseFilesystem.setVisibility(View.GONE);
}
+ }
if(txtvExplanation1.getVisibility() == View.VISIBLE) {
- txtvHeaderExplanation1.setText("Option " + nextOption);
- nextOption++;
- }
+ txtvHeaderExplanation1.setText(String.format(optionLabel, nextOption));
+ nextOption++;
+ }
intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT);
intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
intentGetContentAction.setType("*/*");
- intentActivities = getPackageManager()
- .queryIntentActivities(intentGetContentAction, CHOOSE_OPML_FILE);
- if(intentActivities.size() == 0) {
- txtvHeaderExplanation2.setVisibility(View.GONE);
- txtvExplanation2.setVisibility(View.GONE);
- findViewById(R.id.divider2).setVisibility(View.GONE);
- butChooseExternal.setVisibility(View.GONE);
- } else {
- txtvHeaderExplanation2.setText("Option " + nextOption);
- nextOption++;
- }
+ if(false == IntentUtils.isCallable(getApplicationContext(), intentGetContentAction)) {
+ txtvHeaderExplanation2.setVisibility(View.GONE);
+ txtvExplanation2.setVisibility(View.GONE);
+ findViewById(R.id.divider2).setVisibility(View.GONE);
+ butChooseExternal.setVisibility(View.GONE);
+ } else {
+ txtvHeaderExplanation2.setText(String.format(optionLabel, nextOption));
+ nextOption++;
+ }
- txtvHeaderExplanation3.setText("Option " + nextOption);
+ txtvHeaderExplanation3.setText(String.format(optionLabel, nextOption));
}
@Override
@@ -137,7 +119,7 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
try {
mReader = new InputStreamReader(new FileInputStream(file),
LangUtils.UTF_8);
- if (BuildConfig.DEBUG) Log.d(TAG, "Parsing " + file.toString());
+ Log.d(TAG, "Parsing " + file.toString());
startImport(mReader);
} catch (FileNotFoundException e) {
Log.d(TAG, "File not found which really should be there");
@@ -172,8 +154,14 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && requestCode == CHOOSE_OPML_FILE) {
- String filename = data.getData().getPath();
- startImport(new File(filename));
+ 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");
+ }
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
index 3802de2a6..80ccb7c99 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
@@ -14,6 +14,8 @@ import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import java.lang.ref.WeakReference;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.preferences.PreferenceController;
@@ -26,7 +28,7 @@ public class PreferenceActivity extends ActionBarActivity {
private PreferenceController preferenceController;
private MainFragment prefFragment;
- private static PreferenceActivity instance;
+ private static WeakReference<PreferenceActivity> instance;
private final PreferenceController.PreferenceUI preferenceUI = new PreferenceController.PreferenceUI() {
@@ -45,9 +47,12 @@ public class PreferenceActivity extends ActionBarActivity {
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
protected void onCreate(Bundle savedInstanceState) {
+ // This must be the FIRST thing we do, otherwise other code may not have the
+ // reference it needs
+ instance = new WeakReference<PreferenceActivity>(this);
+
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
- instance = this;
ActionBar ab = getSupportActionBar();
if (ab != null) {
@@ -60,10 +65,13 @@ public class PreferenceActivity extends ActionBarActivity {
root.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(root);
- prefFragment = new MainFragment();
- getFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit();
+ // we need to create the PreferenceController before the MainFragment
+ // since the MainFragment depends on the preferenceController already being created
preferenceController = new PreferenceController(preferenceUI);
+
+ prefFragment = new MainFragment();
+ getFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit();
}
@Override
@@ -96,13 +104,19 @@ public class PreferenceActivity extends ActionBarActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
- instance.preferenceController.onCreate();
+ PreferenceActivity activity = instance.get();
+ if(activity != null && activity.preferenceController != null) {
+ activity.preferenceController.onCreate();
+ }
}
@Override
public void onResume() {
super.onResume();
- instance.preferenceController.onResume();
+ PreferenceActivity activity = instance.get();
+ if(activity != null && activity.preferenceController != null) {
+ activity.preferenceController.onResume();
+ }
}
}
}
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 173bec6b2..b02e82f0b 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
@@ -6,10 +6,9 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
+import android.text.TextUtils;
import android.util.Log;
-import org.apache.commons.lang3.StringUtils;
-
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -40,7 +39,7 @@ public class StorageErrorActivity extends ActionBarActivity {
@Override
protected void onResume() {
super.onResume();
- if (StorageUtils.storageAvailable(this)) {
+ if (StorageUtils.storageAvailable()) {
leaveErrorState();
} else {
registerReceiver(mediaUpdate, new IntentFilter(
@@ -57,7 +56,7 @@ public class StorageErrorActivity extends ActionBarActivity {
@Override
public void onReceive(Context context, Intent intent) {
- if (StringUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) {
+ 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");
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/activity/gpoddernet/GpodnetAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
index 511115b3c..28c2b7206 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
@@ -97,7 +97,7 @@ public class GpodnetAuthenticationActivity extends ActionBarActivity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
- NavUtils.navigateUpFromSameTask(this);
+ finish();
return true;
}
return super.onOptionsItemSelected(item);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
index 8e347a819..1a8f0a67a 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
@@ -10,9 +10,7 @@ import org.apache.commons.lang3.Validate;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DownloadRequester;
-import de.danoeh.antennapod.core.util.LongList;
/**
* Utility methods for the action button that is displayed on the right hand side
@@ -27,7 +25,7 @@ public class ActionButtonUtils {
public ActionButtonUtils(Context context) {
Validate.notNull(context);
- this.context = context;
+ this.context = context.getApplicationContext();
drawables = context.obtainStyledAttributes(new int[] {
R.attr.av_play,
R.attr.navigation_cancel,
@@ -49,7 +47,7 @@ public class ActionButtonUtils {
* Sets the displayed bitmap and content description of the given
* action button so that it matches the state of the FeedItem.
*/
- public void configureActionButton(ImageButton butSecondary, FeedItem item) {
+ public void configureActionButton(ImageButton butSecondary, FeedItem item, boolean isInQueue) {
Validate.isTrue(butSecondary != null && item != null, "butSecondary or item was null");
final FeedMedia media = item.getMedia();
@@ -64,9 +62,8 @@ public class ActionButtonUtils {
butSecondary.setContentDescription(context.getString(labels[1]));
} else {
// item is not downloaded and not being downloaded
- LongList queueIds = DBReader.getQueueIDList(context);
if(DefaultActionButtonCallback.userAllowedMobileDownloads() ||
- !DefaultActionButtonCallback.userChoseAddToQueue() || queueIds.contains(item.getId())) {
+ !DefaultActionButtonCallback.userChoseAddToQueue() || isInQueue) {
butSecondary.setVisibility(View.VISIBLE);
butSecondary.setImageDrawable(drawables.getDrawable(2));
butSecondary.setContentDescription(context.getString(labels[2]));
@@ -88,7 +85,7 @@ public class ActionButtonUtils {
butSecondary.setContentDescription(context.getString(labels[0]));
}
} else {
- if (item.isRead()) {
+ if (item.isPlayed()) {
butSecondary.setVisibility(View.INVISIBLE);
} else {
butSecondary.setVisibility(View.VISIBLE);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
index e22b31361..1ea7daaa3 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
@@ -1,19 +1,24 @@
package de.danoeh.antennapod.adapter;
-import android.content.res.Resources;
+import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
+import com.joanzapata.iconify.Iconify;
+
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.NetworkUtils;
/**
* Utility methods for adapters
*/
public class AdapterUtils {
+ private static final String TAG = AdapterUtils.class.getSimpleName();
+
private AdapterUtils() {
}
@@ -21,7 +26,7 @@ public class AdapterUtils {
/**
* Updates the contents of the TextView that shows the current playback position and the ProgressBar.
*/
- public static void updateEpisodePlaybackProgress(FeedItem item, Resources res, TextView txtvPos, ProgressBar episodeProgress) {
+ public static void updateEpisodePlaybackProgress(FeedItem item, TextView txtvPos, ProgressBar episodeProgress) {
FeedMedia media = item.getMedia();
episodeProgress.setVisibility(View.GONE);
if (media == null) {
@@ -36,18 +41,34 @@ public class AdapterUtils {
|| state == FeedItem.State.IN_PROGRESS) {
if (media.getDuration() > 0) {
episodeProgress.setVisibility(View.VISIBLE);
- episodeProgress
- .setProgress((int) (((double) media
+ episodeProgress.setProgress((int) (((double) media
.getPosition()) / media.getDuration() * 100));
- txtvPos.setText(Converter
- .getDurationStringLong(media.getDuration()
+ txtvPos.setText(Converter.getDurationStringLong(media.getDuration()
- media.getPosition()));
}
} else if (!media.isDownloaded()) {
- txtvPos.setText(Converter.byteToString(media.getSize()));
+ if (media.getSize() > 0) {
+ txtvPos.setText(Converter.byteToString(media.getSize()));
+ } else if(false == media.checkedOnSizeButUnknown()) {
+ txtvPos.setText("{fa-spinner}");
+ Iconify.addIcons(txtvPos);
+ NetworkUtils.getFeedMediaSizeObservable(media)
+ .subscribe(
+ size -> {
+ if (size > 0) {
+ txtvPos.setText(Converter.byteToString(size));
+ } else {
+ txtvPos.setText("");
+ }
+ }, error -> {
+ txtvPos.setText("");
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ } else {
+ txtvPos.setText("");
+ }
} else {
- txtvPos.setText(Converter.getDurationStringLong(media
- .getDuration()));
+ txtvPos.setText(Converter.getDurationStringLong(media.getDuration()));
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java
deleted file mode 100644
index ea0c96be9..000000000
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java
+++ /dev/null
@@ -1,185 +0,0 @@
-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;
-import android.widget.BaseAdapter;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.squareup.picasso.Picasso;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.storage.DownloadRequester;
-import de.danoeh.antennapod.core.util.Converter;
-
-/**
- * List adapter for the list of new episodes
- */
-public class AllEpisodesListAdapter extends BaseAdapter {
-
- private final Context context;
- private final ItemAccess itemAccess;
- private final ActionButtonCallback actionButtonCallback;
- private final ActionButtonUtils actionButtonUtils;
- private final boolean showOnlyNewEpisodes;
-
- public AllEpisodesListAdapter(Context context, ItemAccess itemAccess, ActionButtonCallback actionButtonCallback,
- boolean showOnlyNewEpisodes) {
- super();
- this.context = context;
- this.itemAccess = itemAccess;
- this.actionButtonUtils = new ActionButtonUtils(context);
- this.actionButtonCallback = actionButtonCallback;
- this.showOnlyNewEpisodes = showOnlyNewEpisodes;
- }
-
- @Override
- public int getCount() {
- return itemAccess.getCount();
- }
-
- @Override
- public Object getItem(int position) {
- return itemAccess.getItem(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public int getViewTypeCount() {
- return 1;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Holder holder;
- final FeedItem item = (FeedItem) getItem(position);
- if (item == null) return null;
-
- if (convertView == null) {
- holder = new Holder();
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.new_episodes_listitem,
- parent, false);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
- holder.pubDate = (TextView) convertView
- .findViewById(R.id.txtvPublished);
- holder.statusUnread = convertView.findViewById(R.id.statusUnread);
- holder.butSecondary = (ImageButton) convertView
- .findViewById(R.id.butSecondaryAction);
- holder.queueStatus = (ImageView) convertView
- .findViewById(R.id.imgvInPlaylist);
- holder.downloadProgress = (ProgressBar) convertView
- .findViewById(R.id.pbar_download_progress);
- holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
- holder.txtvDuration = (TextView) convertView.findViewById(R.id.txtvDuration);
- convertView.setTag(holder);
- } else {
- holder = (Holder) convertView.getTag();
- }
-
- holder.title.setText(item.getTitle());
- holder.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
- if (showOnlyNewEpisodes || item.isRead() || false == itemAccess.isNew(item)) {
- holder.statusUnread.setVisibility(View.INVISIBLE);
- } else {
- holder.statusUnread.setVisibility(View.VISIBLE);
- }
-
- FeedMedia media = item.getMedia();
- if (media != null) {
- final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
-
- if (media.getDuration() > 0) {
- holder.txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
- } else if (media.getSize() > 0) {
- holder.txtvDuration.setText(Converter.byteToString(media.getSize()));
- } else {
- holder.txtvDuration.setText("");
- }
-
- if (isDownloadingMedia) {
- holder.downloadProgress.setVisibility(View.VISIBLE);
- holder.txtvDuration.setVisibility(View.GONE);
- holder.pubDate.setVisibility(View.GONE);
- } else {
- holder.txtvDuration.setVisibility(View.VISIBLE);
- holder.pubDate.setVisibility(View.VISIBLE);
- holder.downloadProgress.setVisibility(View.GONE);
- }
-
- if (!media.isDownloaded()) {
- if (isDownloadingMedia) {
- // item is being downloaded
- holder.downloadProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
- }
- }
- } else {
- holder.downloadProgress.setVisibility(View.GONE);
- holder.txtvDuration.setVisibility(View.GONE);
- }
-
- if (itemAccess.isInQueue(item)) {
- holder.queueStatus.setVisibility(View.VISIBLE);
- } else {
- holder.queueStatus.setVisibility(View.INVISIBLE);
- }
-
- actionButtonUtils.configureActionButton(holder.butSecondary, item);
- holder.butSecondary.setFocusable(false);
- holder.butSecondary.setTag(item);
- holder.butSecondary.setOnClickListener(secondaryActionListener);
-
- Picasso.with(context)
- .load(item.getImageUri())
- .fit()
- .into(holder.imageView);
-
- return convertView;
- }
-
- private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- FeedItem item = (FeedItem) v.getTag();
- actionButtonCallback.onActionButtonPressed(item);
- }
- };
-
-
- static class Holder {
- TextView title;
- TextView pubDate;
- View statusUnread;
- ImageView queueStatus;
- ImageView imageView;
- ProgressBar downloadProgress;
- TextView txtvDuration;
- ImageButton butSecondary;
- }
-
- public interface ItemAccess {
-
- int getCount();
-
- FeedItem getItem(int position);
-
- int getItemDownloadProgressPercent(FeedItem item);
-
- boolean isInQueue(FeedItem item);
-
- boolean isNew(FeedItem item);
-
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
new file mode 100644
index 000000000..d749b0313
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
@@ -0,0 +1,375 @@
+package de.danoeh.antennapod.adapter;
+
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+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.resource.drawable.GlideDrawable;
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
+import com.joanzapata.iconify.Iconify;
+import com.nineoldandroids.view.ViewHelper;
+
+import java.lang.ref.WeakReference;
+
+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.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;
+
+/**
+ * List adapter for the list of new episodes
+ */
+public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesRecycleAdapter.Holder> {
+
+ private static final String TAG = AllEpisodesRecycleAdapter.class.getSimpleName();
+
+ private final WeakReference<MainActivity> mainActivityRef;
+ private final ItemAccess itemAccess;
+ private final ActionButtonCallback actionButtonCallback;
+ private final ActionButtonUtils actionButtonUtils;
+ private final boolean showOnlyNewEpisodes;
+
+ private int position = -1;
+
+ private final int playingBackGroundColor;
+ private final int normalBackGroundColor;
+
+ public AllEpisodesRecycleAdapter(MainActivity mainActivity,
+ ItemAccess itemAccess,
+ ActionButtonCallback actionButtonCallback,
+ boolean showOnlyNewEpisodes) {
+ super();
+ this.mainActivityRef = new WeakReference<>(mainActivity);
+ this.itemAccess = itemAccess;
+ this.actionButtonUtils = new ActionButtonUtils(mainActivity);
+ this.actionButtonCallback = actionButtonCallback;
+ this.showOnlyNewEpisodes = showOnlyNewEpisodes;
+
+ if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
+ playingBackGroundColor = mainActivity.getResources().getColor(R.color.highlight_dark);
+ } else {
+ playingBackGroundColor = mainActivity.getResources().getColor(R.color.highlight_light);
+ }
+ normalBackGroundColor = mainActivity.getResources().getColor(android.R.color.transparent);
+ }
+
+ @Override
+ public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.new_episodes_listitem, parent, false);
+ Holder holder = new Holder(view);
+ holder.container = (FrameLayout) view.findViewById(R.id.container);
+ holder.placeholder = (TextView) view.findViewById(R.id.txtvPlaceholder);
+ holder.title = (TextView) view.findViewById(R.id.txtvTitle);
+ holder.pubDate = (TextView) view
+ .findViewById(R.id.txtvPublished);
+ holder.statusUnread = view.findViewById(R.id.statusUnread);
+ holder.butSecondary = (ImageButton) view
+ .findViewById(R.id.butSecondaryAction);
+ holder.queueStatus = (ImageView) view
+ .findViewById(R.id.imgvInPlaylist);
+ holder.progress = (ProgressBar) view
+ .findViewById(R.id.pbar_progress);
+ holder.cover = (ImageView) view.findViewById(R.id.imgvCover);
+ holder.txtvDuration = (TextView) view.findViewById(R.id.txtvDuration);
+ holder.item = null;
+ holder.mainActivityRef = mainActivityRef;
+ holder.position = -1;
+ // so we can grab this later
+ view.setTag(holder);
+
+ return holder;
+ }
+
+ @Override
+ public void onBindViewHolder(final Holder holder, int position) {
+ final FeedItem item = itemAccess.getItem(position);
+ if (item == null) return;
+ holder.itemView.setOnLongClickListener(v -> {
+ this.position = position;
+ return false;
+ });
+ holder.item = item;
+ holder.position = position;
+ holder.placeholder.setVisibility(View.VISIBLE);
+ holder.placeholder.setText(item.getFeed().getTitle());
+ holder.title.setText(item.getTitle());
+ String pubDateStr = DateUtils.formatAbbrev(mainActivityRef.get(), item.getPubDate());
+ holder.pubDate.setText(pubDateStr);
+ if (showOnlyNewEpisodes || false == item.isNew()) {
+ holder.statusUnread.setVisibility(View.INVISIBLE);
+ } else {
+ holder.statusUnread.setVisibility(View.VISIBLE);
+ }
+
+ FeedMedia media = item.getMedia();
+ if (media != null) {
+ final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
+
+ if (media.getDuration() > 0) {
+ holder.txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
+ } else if (media.getSize() > 0) {
+ holder.txtvDuration.setText(Converter.byteToString(media.getSize()));
+ } else if(false == media.checkedOnSizeButUnknown()) {
+ holder.txtvDuration.setText("{fa-spinner}");
+ Iconify.addIcons(holder.txtvDuration);
+ NetworkUtils.getFeedMediaSizeObservable(media)
+ .subscribe(
+ size -> {
+ if (size > 0) {
+ holder.txtvDuration.setText(Converter.byteToString(size));
+ } else {
+ holder.txtvDuration.setText("");
+ }
+ }, error -> {
+ holder.txtvDuration.setText("");
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ } else {
+ holder.txtvDuration.setText("");
+ }
+
+ FeedItem.State state = item.getState();
+ if (isDownloadingMedia) {
+ holder.progress.setVisibility(View.VISIBLE);
+ // item is being downloaded
+ holder.progress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
+ } else if (state == FeedItem.State.PLAYING
+ || state == FeedItem.State.IN_PROGRESS) {
+ if (media.getDuration() > 0) {
+ int progress = (int) (100.0 * media.getPosition() / media.getDuration());
+ holder.progress.setProgress(progress);
+ holder.progress.setVisibility(View.VISIBLE);
+ }
+ } else {
+ holder.progress.setVisibility(View.GONE);
+ }
+
+ if(media.isCurrentlyPlaying()) {
+ holder.container.setBackgroundColor(playingBackGroundColor);
+ } else {
+ holder.container.setBackgroundColor(normalBackGroundColor);
+ }
+ } else {
+ holder.progress.setVisibility(View.GONE);
+ holder.txtvDuration.setVisibility(View.GONE);
+ }
+
+ boolean isInQueue = itemAccess.isInQueue(item);
+ if (isInQueue) {
+ holder.queueStatus.setVisibility(View.VISIBLE);
+ } else {
+ holder.queueStatus.setVisibility(View.INVISIBLE);
+ }
+
+ actionButtonUtils.configureActionButton(holder.butSecondary, item, isInQueue);
+ holder.butSecondary.setFocusable(false);
+ holder.butSecondary.setTag(item);
+ holder.butSecondary.setOnClickListener(secondaryActionListener);
+
+ Glide.with(mainActivityRef.get())
+ .load(item.getImageUri())
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
+ .into(new CoverTarget(item.getFeed().getImageUri(), holder.placeholder, holder.cover));
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getItemCount() {
+ return itemAccess.getCount();
+ }
+
+ public FeedItem getItem(int position) {
+ return itemAccess.getItem(position);
+ }
+
+ public int getPosition() {
+ int pos = position;
+ position = -1; // reset
+ return pos;
+ }
+
+ private class CoverTarget extends GlideDrawableImageViewTarget {
+
+ private final WeakReference<Uri> fallback;
+ private final WeakReference<TextView> placeholder;
+ private final WeakReference<ImageView> cover;
+
+ public CoverTarget(Uri fallbackUri, TextView txtvPlaceholder, ImageView imgvCover) {
+ super(imgvCover);
+ fallback = new WeakReference<>(fallbackUri);
+ placeholder = new WeakReference<>(txtvPlaceholder);
+ cover = new WeakReference<>(imgvCover);
+ }
+
+ @Override
+ public void onLoadFailed(Exception e, Drawable errorDrawable) {
+ Uri fallbackUri = fallback.get();
+ TextView txtvPlaceholder = placeholder.get();
+ ImageView imgvCover = cover.get();
+ if(fallbackUri != null && txtvPlaceholder != null && imgvCover != null) {
+ Glide.with(mainActivityRef.get())
+ .load(fallbackUri)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
+ .into(new CoverTarget(null, txtvPlaceholder, imgvCover));
+ }
+ }
+
+ @Override
+ public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) {
+ super.onResourceReady(drawable, anim);
+ TextView txtvPlaceholder = placeholder.get();
+ if(txtvPlaceholder != null) {
+ txtvPlaceholder.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ FeedItem item = (FeedItem) v.getTag();
+ actionButtonCallback.onActionButtonPressed(item);
+ }
+ };
+
+ public class Holder extends RecyclerView.ViewHolder
+ implements View.OnClickListener,
+ View.OnCreateContextMenuListener,
+ ItemTouchHelperViewHolder {
+ FrameLayout container;
+ TextView placeholder;
+ TextView title;
+ TextView pubDate;
+ View statusUnread;
+ ImageView queueStatus;
+ ImageView cover;
+ ProgressBar progress;
+ TextView txtvDuration;
+ ImageButton butSecondary;
+ FeedItem item;
+ WeakReference<MainActivity> mainActivityRef;
+ int position;
+
+ public Holder(View itemView) {
+ super(itemView);
+ itemView.setOnClickListener(this);
+ itemView.setOnCreateContextMenuListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ MainActivity mainActivity = mainActivityRef.get();
+ if (mainActivity != null) {
+ mainActivity.loadChildFragment(ItemFragment.newInstance(item.getId()));
+ }
+ }
+
+ @Override
+ public void onItemSelected() {
+ ViewHelper.setAlpha(itemView, 0.5f);
+ }
+
+ @Override
+ public void onItemClear() {
+ ViewHelper.setAlpha(itemView, 1.0f);
+ }
+
+ public FeedItem getFeedItem() { return item; }
+
+ @Override
+ public void onCreateContextMenu(final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ FeedItem item = itemAccess.getItem(getAdapterPosition());
+
+ MenuInflater inflater = mainActivityRef.get().getMenuInflater();
+ inflater.inflate(R.menu.allepisodes_context, menu);
+
+ if (item != null) {
+ menu.setHeaderTitle(item.getTitle());
+ }
+
+ FeedItemMenuHandler.MenuInterface contextMenuInterface = (id, visible) -> {
+ if (menu == null) {
+ return;
+ }
+ MenuItem item1 = menu.findItem(id);
+ if (item1 != null) {
+ item1.setVisible(visible);
+ }
+ };
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true,
+ itemAccess.getQueueIds(), itemAccess.getFavoritesIds());
+ }
+
+ }
+
+ public interface ItemAccess {
+
+ int getCount();
+
+ FeedItem getItem(int position);
+
+ int getItemDownloadProgressPercent(FeedItem item);
+
+ boolean isInQueue(FeedItem item);
+
+ LongList getQueueIds();
+
+ LongList getFavoritesIds();
+
+ }
+
+ /**
+ * Notifies a View Holder of relevant callbacks from
+ * {@link ItemTouchHelper.Callback}.
+ */
+ public interface ItemTouchHelperViewHolder {
+
+ /**
+ * Called when the {@link ItemTouchHelper} first registers an
+ * item as being moved or swiped.
+ * Implementations should update the item view to indicate
+ * it's active state.
+ */
+ void onItemSelected();
+
+
+ /**
+ * Called when the {@link ItemTouchHelper} has completed the
+ * move or swipe, and the active item state should be cleared.
+ */
+ void onItemClear();
+ }
+}
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 67fb4c3b1..000000000
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java
+++ /dev/null
@@ -1,197 +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.bright_blue));
- holder.start.setTextColor(convertView.getResources().getColor(
- R.color.bright_blue));
- } 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;
- 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..1c9439ee6
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
@@ -0,0 +1,198 @@
+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.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 = getContext().getResources().getColor(R.color.highlight_dark);
+ } else {
+ playingBackGroundColor = getContext().getResources().getColor(R.color.highlight_light);
+ }
+ holder.view.setBackgroundColor(playingBackGroundColor);
+ } else {
+ holder.view.setBackgroundColor(getContext().getResources().getColor(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 3d233817b..469a807e1 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
@@ -1,22 +1,21 @@
package de.danoeh.antennapod.adapter;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
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;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -60,8 +59,8 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
final FeedMedia media = item.getMedia();
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
if (!isDownloading && !media.isDownloaded()) {
- LongList queueIds = DBReader.getQueueIDList(context);
- if (NetworkUtils.isDownloadAllowed(context) || userAllowedMobileDownloads()) {
+ LongList queueIds = DBReader.getQueueIDList();
+ if (NetworkUtils.isDownloadAllowed() || userAllowedMobileDownloads()) {
try {
DBTasks.downloadFeedItems(context, item);
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
@@ -70,7 +69,7 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
DownloadRequestErrorDialogCreator.newRequestErrorDialog(context, e.getMessage());
}
} else if(userChoseAddToQueue() && !queueIds.contains(item.getId())) {
- DBWriter.addQueueItem(context, item.getId());
+ DBWriter.addQueueItem(context, item);
Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).show();
} else {
confirmMobileDownload(context, item);
@@ -78,7 +77,7 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
} else if (isDownloading) {
DownloadRequester.getInstance().cancelDownload(context, media);
if(UserPreferences.isEnableAutodownload()) {
- DBWriter.setFeedItemAutoDownload(context, media.getItem(), false);
+ DBWriter.setFeedItemAutoDownload(media.getItem(), false);
Toast.makeText(context, R.string.download_canceled_autodownload_enabled_msg, Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, R.string.download_canceled_msg, Toast.LENGTH_LONG).show();
@@ -95,61 +94,40 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
}
}
} else {
- if (!item.isRead()) {
- DBWriter.markItemRead(context, item, true, true);
-
- if(GpodnetPreferences.loggedIn()) {
- // gpodder: send played action
- FeedMedia media = item.getMedia();
- GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, GpodnetEpisodeAction.Action.PLAY)
- .currentDeviceId()
- .currentTimestamp()
- .started(media.getDuration() / 1000)
- .position(media.getDuration() / 1000)
- .total(media.getDuration() / 1000)
- .build();
- GpodnetPreferences.enqueueEpisodeAction(action);
- }
+ if (!item.isPlayed()) {
+ DBWriter.markItemPlayed(item, FeedItem.PLAYED, true);
}
}
}
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(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());
- }
- }
- });
- LongList queueIds = DBReader.getQueueIDList(context);
+ .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(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.getId());
- 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.setNegativeButton(R.string.cancel_label, null)
- .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 f29cfdf2f..582538fb8 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -7,11 +7,11 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
-import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
-import com.joanzapata.android.iconify.Iconify;
+import com.joanzapata.iconify.Iconify;
+import com.joanzapata.iconify.widget.IconButton;
import java.util.Date;
@@ -50,7 +50,7 @@ public class DownloadLogAdapter extends BaseAdapter {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloadlog_item, parent, false);
holder.icon = (TextView) convertView.findViewById(R.id.txtvIcon);
- holder.retry = (Button) convertView.findViewById(R.id.btnRetry);
+ holder.retry = (IconButton) convertView.findViewById(R.id.btnRetry);
holder.date = (TextView) convertView.findViewById(R.id.txtvDate);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.type = (TextView) convertView.findViewById(R.id.txtvType);
@@ -96,8 +96,6 @@ public class DownloadLogAdapter extends BaseAdapter {
if(status.getFeedfileType() != FeedImage.FEEDFILETYPE_FEEDIMAGE &&
!newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
holder.retry.setVisibility(View.VISIBLE);
- holder.retry.setText("{fa-repeat}");
- Iconify.addIcons(holder.retry);
holder.retry.setOnClickListener(clickListener);
ButtonHolder btnHolder;
if(holder.retry.getTag() != null) {
@@ -123,21 +121,29 @@ public class DownloadLogAdapter extends BaseAdapter {
public void onClick(View v) {
ButtonHolder holder = (ButtonHolder) v.getTag();
if(holder.typeId == Feed.FEEDFILETYPE_FEED) {
- Feed feed = DBReader.getFeed(context, holder.id);
- feed.setLastUpdate(new Date(0)); // force refresh
- try {
- DBTasks.refreshFeed(context, feed);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
+ Feed feed = DBReader.getFeed(holder.id);
+ if (feed != null) {
+ feed.setLastUpdate(new Date(0)); // force refresh
+ try {
+ DBTasks.refreshFeed(context, feed);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ }
+ } else {
+ Log.wtf(TAG, "Could not find feed for feed id: " + holder.id);
}
} else if(holder.typeId == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- FeedMedia media = DBReader.getFeedMedia(context, holder.id);
- 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());
+ FeedMedia media = DBReader.getFeedMedia(holder.id);
+ 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);
@@ -157,7 +163,7 @@ public class DownloadLogAdapter extends BaseAdapter {
static class Holder {
TextView icon;
- Button retry;
+ IconButton retry;
TextView title;
TextView type;
TextView date;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
index 15e0a7a33..ca747b9b0 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;
@@ -10,11 +9,13 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
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
@@ -73,7 +74,8 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
}
holder.title.setText(item.getTitle());
- holder.pubDate.setText(DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
+ String pubDateStr = DateUtils.formatAbbrev(context, item.getPubDate());
+ holder.pubDate.setText(pubDateStr);
holder.txtvSize.setText(Converter.byteToString(item.getMedia().getSize()));
FeedItem.State state = item.getState();
@@ -88,9 +90,13 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
holder.butSecondary.setOnClickListener(secondaryActionListener);
- Picasso.with(context)
+ Glide.with(context)
.load(item.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(holder.imageView);
return convertView;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
index b39e23d42..e483738b4 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,6 @@ package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.content.res.TypedArray;
-import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -11,6 +10,7 @@ import android.widget.Adapter;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
@@ -20,7 +20,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;
/**
@@ -33,13 +35,20 @@ public class FeedItemlistAdapter extends BaseAdapter {
private final Context context;
private boolean showFeedtitle;
private int selectedItemIndex;
+ /** true if played items should be made partially transparent */
+ private boolean makePlayedItemsTransparent;
private final ActionButtonUtils actionButtonUtils;
public static final int SELECTION_NONE = -1;
+ private final int playingBackGroundColor;
+ private final int normalBackGroundColor;
+
public FeedItemlistAdapter(Context context,
ItemAccess itemAccess,
- ActionButtonCallback callback, boolean showFeedtitle) {
+ ActionButtonCallback callback,
+ boolean showFeedtitle,
+ boolean makePlayedItemsTransparent) {
super();
this.callback = callback;
this.context = context;
@@ -47,6 +56,14 @@ public class FeedItemlistAdapter extends BaseAdapter {
this.showFeedtitle = showFeedtitle;
this.selectedItemIndex = SELECTION_NONE;
this.actionButtonUtils = new ActionButtonUtils(context);
+ this.makePlayedItemsTransparent = makePlayedItemsTransparent;
+
+ if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
+ playingBackGroundColor = context.getResources().getColor(R.color.highlight_dark);
+ } else {
+ playingBackGroundColor = context.getResources().getColor(R.color.highlight_light);
+ }
+ normalBackGroundColor = context.getResources().getColor(android.R.color.transparent);
}
@Override
@@ -75,6 +92,8 @@ public class FeedItemlistAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.feeditemlist_item, parent, false);
+ holder.container = (LinearLayout) convertView
+ .findViewById(R.id.container);
holder.title = (TextView) convertView
.findViewById(R.id.txtvItemname);
holder.lenSize = (TextView) convertView
@@ -86,7 +105,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
holder.inPlaylist = (ImageView) convertView
.findViewById(R.id.imgvInPlaylist);
holder.type = (ImageView) convertView.findViewById(R.id.imgvType);
- holder.statusUnread = (View) convertView
+ holder.statusUnread = convertView
.findViewById(R.id.statusUnread);
holder.episodeProgress = (ProgressBar) convertView
.findViewById(R.id.pbar_episode_progress);
@@ -95,6 +114,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
} else {
holder = (Holder) convertView.getTag();
}
+
if (!(getItemViewType(position) == Adapter.IGNORE_ITEM_VIEW_TYPE)) {
convertView.setVisibility(View.VISIBLE);
if (position == selectedItemIndex) {
@@ -106,25 +126,25 @@ public class FeedItemlistAdapter extends BaseAdapter {
StringBuilder buffer = new StringBuilder(item.getTitle());
if (showFeedtitle) {
- buffer.append("(");
+ buffer.append(" (");
buffer.append(item.getFeed().getTitle());
buffer.append(")");
}
holder.title.setText(buffer.toString());
- if(false == item.isRead() && itemAccess.isNew(item)) {
+ if(item.isNew()) {
holder.statusUnread.setVisibility(View.VISIBLE);
} else {
holder.statusUnread.setVisibility(View.INVISIBLE);
}
- if(item.isRead()) {
+ if(item.isPlayed() && makePlayedItemsTransparent) {
ViewHelper.setAlpha(convertView, 0.5f);
} else {
ViewHelper.setAlpha(convertView, 1.0f);
}
- 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) {
@@ -134,18 +154,17 @@ public class FeedItemlistAdapter extends BaseAdapter {
holder.lenSize.setVisibility(View.INVISIBLE);
} else {
- AdapterUtils.updateEpisodePlaybackProgress(item, context.getResources(), holder.lenSize, holder.episodeProgress);
+ AdapterUtils.updateEpisodePlaybackProgress(item, holder.lenSize, holder.episodeProgress);
- if (((ItemAccess) itemAccess).isInQueue(item)) {
+ if (itemAccess.isInQueue(item)) {
holder.inPlaylist.setVisibility(View.VISIBLE);
} else {
holder.inPlaylist.setVisibility(View.INVISIBLE);
}
- if (DownloadRequester.getInstance().isDownloadingFile(
- item.getMedia())) {
+ if (DownloadRequester.getInstance().isDownloadingFile(item.getMedia())) {
holder.episodeProgress.setVisibility(View.VISIBLE);
- holder.episodeProgress.setProgress(((ItemAccess) itemAccess).getItemDownloadProgressPercent(item));
+ holder.episodeProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
} else {
if(media.getPosition() == 0) {
holder.episodeProgress.setVisibility(View.GONE);
@@ -169,9 +188,18 @@ public class FeedItemlistAdapter extends BaseAdapter {
holder.type.setImageBitmap(null);
holder.type.setVisibility(View.GONE);
}
+
+ if(media.isCurrentlyPlaying()) {
+ if(media.isCurrentlyPlaying()) {
+ holder.container.setBackgroundColor(playingBackGroundColor);
+ } else {
+ holder.container.setBackgroundColor(normalBackGroundColor);
+ }
+ }
}
- actionButtonUtils.configureActionButton(holder.butAction, item);
+ boolean isInQueue = itemAccess.isInQueue(item);
+ actionButtonUtils.configureActionButton(holder.butAction, item, isInQueue);
holder.butAction.setFocusable(false);
holder.butAction.setTag(item);
holder.butAction.setOnClickListener(butActionListener);
@@ -180,7 +208,6 @@ public class FeedItemlistAdapter extends BaseAdapter {
convertView.setVisibility(View.GONE);
}
return convertView;
-
}
private final OnClickListener butActionListener = new OnClickListener() {
@@ -192,6 +219,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
};
static class Holder {
+ LinearLayout container;
TextView title;
TextView published;
TextView lenSize;
@@ -202,15 +230,6 @@ public class FeedItemlistAdapter extends BaseAdapter {
ProgressBar episodeProgress;
}
- public int getSelectedItemIndex() {
- return selectedItemIndex;
- }
-
- public void setSelectedItemIndex(int selectedItemIndex) {
- this.selectedItemIndex = selectedItemIndex;
- notifyDataSetChanged();
- }
-
public interface ItemAccess {
boolean isInQueue(FeedItem item);
@@ -221,8 +240,6 @@ public class FeedItemlistAdapter extends BaseAdapter {
FeedItem getItem(int position);
- boolean isNew(FeedItem item);
-
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
index 81d997d65..b20af1773 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -6,15 +6,18 @@ 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;
import android.widget.BaseAdapter;
-import android.widget.IconTextView;
import android.widget.ImageView;
+import android.widget.RelativeLayout;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.joanzapata.iconify.Iconify;
+import com.joanzapata.iconify.widget.IconTextView;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import org.apache.commons.lang3.ArrayUtils;
@@ -27,10 +30,12 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.fragment.AddFeedFragment;
import de.danoeh.antennapod.fragment.AllEpisodesFragment;
import de.danoeh.antennapod.fragment.DownloadsFragment;
+import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.NewEpisodesFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
@@ -92,6 +97,9 @@ public class NavListAdapter extends BaseAdapter
case NewEpisodesFragment.TAG:
icon = R.attr.ic_new;
break;
+ case EpisodesFragment.TAG:
+ icon = R.attr.feed;
+ break;
case AllEpisodesFragment.TAG:
icon = R.attr.feed;
break;
@@ -205,7 +213,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);
@@ -213,7 +222,7 @@ public class NavListAdapter extends BaseAdapter
} else {
holder.count.setVisibility(View.GONE);
}
- } else if (tags.get(position).equals(NewEpisodesFragment.TAG)) {
+ } else if (tag.equals(EpisodesFragment.TAG)) {
int unreadItems = itemAccess.getNumberOfNewItems();
if (unreadItems > 0) {
holder.count.setVisibility(View.VISIBLE);
@@ -221,6 +230,22 @@ public class NavListAdapter extends BaseAdapter
} else {
holder.count.setVisibility(View.GONE);
}
+ } else if(tag.equals(DownloadsFragment.TAG) && UserPreferences.isEnableAutodownload()) {
+ int epCacheSize = UserPreferences.getEpisodeCacheSize();
+ if(itemAccess.getNumberOfDownloadedItems() >= 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);
}
@@ -262,26 +287,34 @@ public class NavListAdapter extends BaseAdapter
holder = (FeedHolder) convertView.getTag();
}
- Picasso.with(context)
+ Glide.with(context)
.load(feed.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(holder.image);
holder.title.setText(feed.getTitle());
if(feed.hasLastUpdateFailed()) {
+ RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) holder.title.getLayoutParams();
+ p.addRule(RelativeLayout.LEFT_OF, R.id.itxtvFailure);
holder.failure.setVisibility(View.VISIBLE);
} else {
+ RelativeLayout.LayoutParams p = (RelativeLayout.LayoutParams) holder.title.getLayoutParams();
+ p.addRule(RelativeLayout.LEFT_OF, R.id.txtvCount);
holder.failure.setVisibility(View.GONE);
}
- int feedUnreadItems = itemAccess.getNumberOfUnreadFeedItems(feed.getId());
- if(feedUnreadItems > 0) {
+ int counter = itemAccess.getFeedCounter(feed.getId());
+ if(counter > 0) {
holder.count.setVisibility(View.VISIBLE);
- holder.count.setText(String.valueOf(feedUnreadItems));
+ holder.count.setText(String.valueOf(counter));
holder.count.setTypeface(holder.title.getTypeface());
} else {
- holder.count.setVisibility(View.INVISIBLE);
+ holder.count.setVisibility(View.GONE);
}
return convertView;
}
@@ -305,7 +338,8 @@ public class NavListAdapter extends BaseAdapter
int getSelectedItemIndex();
int getQueueSize();
int getNumberOfNewItems();
- int getNumberOfUnreadFeedItems(long feedId);
+ int getNumberOfDownloadedItems();
+ int getFeedCounter(long feedId);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java
deleted file mode 100644
index bba5a00a9..000000000
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java
+++ /dev/null
@@ -1,178 +0,0 @@
-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;
-import android.widget.BaseAdapter;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.squareup.picasso.Picasso;
-
-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.storage.DownloadRequester;
-import de.danoeh.antennapod.core.util.Converter;
-
-/**
- * List adapter for the queue.
- */
-public class QueueListAdapter extends BaseAdapter {
-
-
- private final Context context;
- private final ItemAccess itemAccess;
- private final ActionButtonCallback actionButtonCallback;
- private final ActionButtonUtils actionButtonUtils;
-
- private boolean locked;
-
-
- public QueueListAdapter(Context context, ItemAccess itemAccess, ActionButtonCallback actionButtonCallback) {
- super();
- this.context = context;
- this.itemAccess = itemAccess;
- this.actionButtonUtils = new ActionButtonUtils(context);
- this.actionButtonCallback = actionButtonCallback;
- locked = UserPreferences.isQueueLocked();
- }
-
- public void setLocked(boolean locked) {
- this.locked = locked;
- notifyDataSetChanged();
- }
-
- @Override
- public int getCount() {
- return itemAccess.getCount();
- }
-
- @Override
- public Object getItem(int position) {
- return itemAccess.getItem(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- Holder holder;
- final FeedItem item = (FeedItem) getItem(position);
- if (item == null) return null;
-
- if (convertView == null) {
- holder = new Holder();
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- convertView = inflater.inflate(R.layout.queue_listitem,
- parent, false);
- holder.dragHandle = (ImageView) convertView.findViewById(R.id.drag_handle);
- holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
- holder.pubDate = (TextView) convertView.findViewById(R.id.txtvPubDate);
- holder.progressLeft = (TextView) convertView.findViewById(R.id.txtvProgressLeft);
- holder.progressRight = (TextView) convertView
- .findViewById(R.id.txtvProgressRight);
- holder.butSecondary = (ImageButton) convertView
- .findViewById(R.id.butSecondaryAction);
- holder.progress = (ProgressBar) convertView
- .findViewById(R.id.progressBar);
- holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
- convertView.setTag(holder);
- } else {
- holder = (Holder) convertView.getTag();
- }
-
- if(locked) {
- holder.dragHandle.setVisibility(View.GONE);
- } else {
- holder.dragHandle.setVisibility(View.VISIBLE);
- }
-
- holder.title.setText(item.getTitle());
- FeedMedia media = item.getMedia();
-
-
- holder.title.setText(item.getTitle());
- String pubDate = DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL);
- holder.pubDate.setText(pubDate.replace(" ", "\n"));
-
- if (media != null) {
- final boolean isDownloadingMedia = DownloadRequester.getInstance().isDownloadingFile(media);
- FeedItem.State state = item.getState();
- if (isDownloadingMedia) {
- holder.progressLeft.setText(Converter.byteToString(itemAccess.getItemDownloadedBytes(item)));
- if(itemAccess.getItemDownloadSize(item) > 0) {
- holder.progressRight.setText(Converter.byteToString(itemAccess.getItemDownloadSize(item)));
- } else {
- holder.progressRight.setText(Converter.byteToString(media.getSize()));
- }
- holder.progress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
- holder.progress.setVisibility(View.VISIBLE);
- } else if (state == FeedItem.State.PLAYING
- || state == FeedItem.State.IN_PROGRESS) {
- if (media.getDuration() > 0) {
- int progress = (int) (100.0 * media.getPosition() / media.getDuration());
- holder.progress.setProgress(progress);
- holder.progress.setVisibility(View.VISIBLE);
- holder.progressLeft.setText(Converter
- .getDurationStringLong(media.getPosition()));
- holder.progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
- }
- } else {
- holder.progressLeft.setText(Converter.byteToString(media.getSize()));
- holder.progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
- holder.progress.setVisibility(View.GONE);
- }
- }
-
- actionButtonUtils.configureActionButton(holder.butSecondary, item);
- holder.butSecondary.setFocusable(false);
- holder.butSecondary.setTag(item);
- holder.butSecondary.setOnClickListener(secondaryActionListener);
-
- Picasso.with(context)
- .load(item.getImageUri())
- .fit()
- .into(holder.imageView);
-
- return convertView;
- }
-
- private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- FeedItem item = (FeedItem) v.getTag();
- actionButtonCallback.onActionButtonPressed(item);
- }
- };
-
-
- static class Holder {
- ImageView dragHandle;
- ImageView imageView;
- TextView title;
- TextView pubDate;
- TextView progressLeft;
- TextView progressRight;
- ProgressBar progress;
- ImageButton butSecondary;
- }
-
- public interface ItemAccess {
- FeedItem getItem(int position);
- int getCount();
- long getItemDownloadedBytes(FeedItem item);
- long getItemDownloadSize(FeedItem item);
- int getItemDownloadProgressPercent(FeedItem item);
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
new file mode 100644
index 000000000..d0266be6d
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -0,0 +1,378 @@
+package de.danoeh.antennapod.adapter;
+
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+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.resource.drawable.GlideDrawable;
+import com.bumptech.glide.request.animation.GlideAnimation;
+import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
+import com.joanzapata.iconify.Iconify;
+import com.nineoldandroids.view.ViewHelper;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.lang.ref.WeakReference;
+
+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.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;
+
+/**
+ * List adapter for the queue.
+ */
+public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdapter.ViewHolder> {
+
+ private static final String TAG = QueueRecyclerAdapter.class.getSimpleName();
+
+ private WeakReference<MainActivity> mainActivity;
+ private final ItemAccess itemAccess;
+ private final ActionButtonCallback actionButtonCallback;
+ private final ActionButtonUtils actionButtonUtils;
+ private final ItemTouchHelper itemTouchHelper;
+
+ private boolean locked;
+
+ private FeedItem selectedItem;
+
+ private final int playingBackGroundColor;
+ private final int normalBackGroundColor;
+
+ public QueueRecyclerAdapter(MainActivity mainActivity,
+ ItemAccess itemAccess,
+ ActionButtonCallback actionButtonCallback,
+ ItemTouchHelper itemTouchHelper) {
+ super();
+ this.mainActivity = new WeakReference<>(mainActivity);
+ this.itemAccess = itemAccess;
+ this.actionButtonUtils = new ActionButtonUtils(mainActivity);
+ this.actionButtonCallback = actionButtonCallback;
+ this.itemTouchHelper = itemTouchHelper;
+ locked = UserPreferences.isQueueLocked();
+
+ if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
+ playingBackGroundColor = mainActivity.getResources().getColor(R.color.highlight_dark);
+ } else {
+ playingBackGroundColor = mainActivity.getResources().getColor(R.color.highlight_light);
+ }
+ normalBackGroundColor = mainActivity.getResources().getColor(android.R.color.transparent);
+ }
+
+ public void setLocked(boolean locked) {
+ this.locked = locked;
+ notifyDataSetChanged();
+ }
+
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.queue_listitem, parent, false);
+ return new ViewHolder(view);
+ }
+
+ public void onBindViewHolder(ViewHolder holder, int pos) {
+ FeedItem item = itemAccess.getItem(pos);
+ holder.bind(item);
+ holder.itemView.setOnLongClickListener(v -> {
+ selectedItem = item;
+ return false;
+ });
+ }
+
+ @Nullable
+ public FeedItem getSelectedItem() {
+ return selectedItem;
+ }
+
+ public int getItemCount() {
+ return itemAccess.getCount();
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener,
+ View.OnCreateContextMenuListener,
+ ItemTouchHelperViewHolder {
+
+ private final FrameLayout container;
+ private final ImageView dragHandle;
+ private final TextView placeholder;
+ private final ImageView cover;
+ private final TextView title;
+ private final TextView pubDate;
+ private final TextView progressLeft;
+ private final TextView progressRight;
+ private final ProgressBar progressBar;
+ private final ImageButton butSecondary;
+
+ private FeedItem item;
+
+ public ViewHolder(View v) {
+ super(v);
+ container = (FrameLayout) v.findViewById(R.id.container);
+ dragHandle = (ImageView) v.findViewById(R.id.drag_handle);
+ placeholder = (TextView) v.findViewById(R.id.txtvPlaceholder);
+ cover = (ImageView) v.findViewById(R.id.imgvCover);
+ title = (TextView) v.findViewById(R.id.txtvTitle);
+ pubDate = (TextView) v.findViewById(R.id.txtvPubDate);
+ progressLeft = (TextView) v.findViewById(R.id.txtvProgressLeft);
+ progressRight = (TextView) v.findViewById(R.id.txtvProgressRight);
+ butSecondary = (ImageButton) v.findViewById(R.id.butSecondaryAction);
+ progressBar = (ProgressBar) v.findViewById(R.id.progressBar);
+ v.setTag(this);
+ v.setOnClickListener(this);
+ v.setOnCreateContextMenuListener(this);
+ dragHandle.setOnTouchListener((v1, event) -> {
+ if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
+ Log.d(TAG, "startDrag()");
+ itemTouchHelper.startDrag(ViewHolder.this);
+ }
+ return false;
+ });
+ }
+
+ @Override
+ public void onClick(View v) {
+ MainActivity activity = mainActivity.get();
+ if (activity != null) {
+ activity.loadChildFragment(ItemFragment.newInstance(item.getId()));
+ }
+ }
+
+ @Override
+ public void onCreateContextMenu(final ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+ FeedItem item = itemAccess.getItem(getAdapterPosition());
+
+ MenuInflater inflater = mainActivity.get().getMenuInflater();
+ inflater.inflate(R.menu.queue_context, menu);
+
+ if (item != null) {
+ menu.setHeaderTitle(item.getTitle());
+ }
+
+ FeedItemMenuHandler.MenuInterface contextMenuInterface = (id, visible) -> {
+ if (menu == null) {
+ return;
+ }
+ MenuItem item1 = menu.findItem(id);
+ if (item1 != null) {
+ item1.setVisible(visible);
+ }
+ };
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true,
+ itemAccess.getQueueIds(), itemAccess.getFavoritesIds());
+ }
+
+ @Override
+ public void onItemSelected() {
+ ViewHelper.setAlpha(itemView, 0.5f);
+ }
+
+ @Override
+ public void onItemClear() {
+ ViewHelper.setAlpha(itemView, 1.0f);
+ }
+
+ public void bind(FeedItem item) {
+ this.item = item;
+ if(locked) {
+ dragHandle.setVisibility(View.GONE);
+ } else {
+ dragHandle.setVisibility(View.VISIBLE);
+ }
+
+ placeholder.setText(item.getFeed().getTitle());
+
+ title.setText(item.getTitle());
+ FeedMedia media = item.getMedia();
+
+ title.setText(item.getTitle());
+ String pubDateStr = DateUtils.formatAbbrev(mainActivity.get(), item.getPubDate());
+ int index = 0;
+ if(StringUtils.countMatches(pubDateStr, ' ') == 1 || StringUtils.countMatches(pubDateStr, ' ') == 2) {
+ index = pubDateStr.lastIndexOf(' ');
+ } else if(StringUtils.countMatches(pubDateStr, '.') == 2) {
+ index = pubDateStr.lastIndexOf('.');
+ } else if(StringUtils.countMatches(pubDateStr, '-') == 2) {
+ index = pubDateStr.lastIndexOf('-');
+ } else if(StringUtils.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);
+ FeedItem.State state = item.getState();
+ if (isDownloadingMedia) {
+ progressLeft.setText(Converter.byteToString(itemAccess.getItemDownloadedBytes(item)));
+ if(itemAccess.getItemDownloadSize(item) > 0) {
+ progressRight.setText(Converter.byteToString(itemAccess.getItemDownloadSize(item)));
+ } else {
+ progressRight.setText(Converter.byteToString(media.getSize()));
+ }
+ progressBar.setProgress(itemAccess.getItemDownloadProgressPercent(item));
+ progressBar.setVisibility(View.VISIBLE);
+ } else if (state == FeedItem.State.PLAYING
+ || state == FeedItem.State.IN_PROGRESS) {
+ if (media.getDuration() > 0) {
+ int progress = (int) (100.0 * media.getPosition() / media.getDuration());
+ progressBar.setProgress(progress);
+ progressBar.setVisibility(View.VISIBLE);
+ progressLeft.setText(Converter
+ .getDurationStringLong(media.getPosition()));
+ progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
+ }
+ } else {
+ if(media.getSize() > 0) {
+ progressLeft.setText(Converter.byteToString(media.getSize()));
+ } else if(false == media.checkedOnSizeButUnknown()) {
+ progressLeft.setText("{fa-spinner}");
+ Iconify.addIcons(progressLeft);
+ NetworkUtils.getFeedMediaSizeObservable(media)
+ .subscribe(
+ size -> {
+ if (size > 0) {
+ progressLeft.setText(Converter.byteToString(size));
+ } else {
+ progressLeft.setText("");
+ }
+ }, error -> {
+ progressLeft.setText("");
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ } else {
+ progressLeft.setText("");
+ }
+ progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
+ progressBar.setVisibility(View.GONE);
+ }
+
+ if(media.isCurrentlyPlaying()) {
+ container.setBackgroundColor(playingBackGroundColor);
+ } else {
+ container.setBackgroundColor(normalBackGroundColor);
+ }
+ }
+
+ actionButtonUtils.configureActionButton(butSecondary, item, true);
+ butSecondary.setFocusable(false);
+ butSecondary.setTag(item);
+ butSecondary.setOnClickListener(secondaryActionListener);
+
+ Glide.with(mainActivity.get())
+ .load(item.getImageUri())
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
+ .into(new CoverTarget(item.getFeed().getImageUri(), placeholder, cover));
+ }
+
+ }
+
+
+ private class CoverTarget extends GlideDrawableImageViewTarget {
+
+ private final WeakReference<Uri> fallback;
+ private final WeakReference<TextView> placeholder;
+ private final WeakReference<ImageView> cover;
+
+ public CoverTarget(Uri fallbackUri, TextView txtvPlaceholder, ImageView imgvCover) {
+ super(imgvCover);
+ fallback = new WeakReference<>(fallbackUri);
+ placeholder = new WeakReference<>(txtvPlaceholder);
+ cover = new WeakReference<>(imgvCover);
+ }
+
+ @Override
+ public void onLoadFailed(Exception e, Drawable errorDrawable) {
+ Uri fallbackUri = fallback.get();
+ TextView txtvPlaceholder = placeholder.get();
+ ImageView imgvCover = cover.get();
+ if(fallbackUri != null && txtvPlaceholder != null && imgvCover != null) {
+ Glide.with(mainActivity.get())
+ .load(fallbackUri)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
+ .into(new CoverTarget(null, txtvPlaceholder, imgvCover));
+ }
+ }
+
+ @Override
+ public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) {
+ super.onResourceReady(drawable, anim);
+ TextView txtvPlaceholder = placeholder.get();
+ if(txtvPlaceholder != null) {
+ txtvPlaceholder.setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ FeedItem item = (FeedItem) v.getTag();
+ actionButtonCallback.onActionButtonPressed(item);
+ }
+ };
+
+
+ public interface ItemAccess {
+ FeedItem getItem(int position);
+ int getCount();
+ long getItemDownloadedBytes(FeedItem item);
+ long getItemDownloadSize(FeedItem item);
+ int getItemDownloadProgressPercent(FeedItem item);
+ LongList getQueueIds();
+ LongList getFavoritesIds();
+ }
+
+ /**
+ * Notifies a View Holder of relevant callbacks from
+ * {@link ItemTouchHelper.Callback}.
+ */
+ public interface ItemTouchHelperViewHolder {
+
+ /**
+ * Called when the {@link ItemTouchHelper} first registers an
+ * item as being moved or swiped.
+ * Implementations should update the item view to indicate
+ * it's active state.
+ */
+ void onItemSelected();
+
+
+ /**
+ * Called when the {@link ItemTouchHelper} has completed the
+ * move or swipe, and the active item state should be cleared.
+ */
+ void onItemClear();
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
index cedce7903..83f5dcb4d 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
@@ -8,13 +8,15 @@ import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.SearchResult;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
/**
* List adapter for search activity.
@@ -73,9 +75,13 @@ public class SearchlistAdapter extends BaseAdapter {
holder.title.setText(feed.getTitle());
holder.subtitle.setVisibility(View.GONE);
- Picasso.with(context)
+ Glide.with(context)
.load(feed.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(holder.cover);
} else if (component.getClass() == FeedItem.class) {
@@ -86,9 +92,13 @@ public class SearchlistAdapter extends BaseAdapter {
holder.subtitle.setText(result.getSubtitle());
}
- Picasso.with(context)
+ Glide.with(context)
.load(item.getFeed().getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(holder.cover);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
index 5928ad119..0ff976b98 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
@@ -60,7 +60,7 @@ public class SubscriptionsAdapter extends BaseAdapter {
holder = (Holder) convertView.getTag();
}
- holder.itemView.setFeed(item, mItemAccess.getNumberOfUnreadFeedItems(item.getId()));
+ holder.itemView.setFeed(item);
return convertView;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
index b85709c5e..743f9fc86 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
@@ -8,13 +8,15 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetPodcast;
/**
@@ -49,9 +51,13 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
}
if (StringUtils.isNotBlank(podcast.getLogoUrl())) {
- Picasso.with(convertView.getContext())
+ Glide.with(convertView.getContext())
.load(podcast.getLogoUrl())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(holder.image);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
index 4fc2838b7..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
@@ -1,23 +1,19 @@
package de.danoeh.antennapod.adapter.itunes;
import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.os.AsyncTask;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+
+import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
-import java.io.IOException;
import java.util.List;
import de.danoeh.antennapod.R;
@@ -46,55 +42,6 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
this.context = context;
}
- /**
- * Updates the given ImageView with the image in the given Podcast's imageUrl
- */
- class FetchImageTask extends AsyncTask<Void,Void,Bitmap>{
- /**
- * Current podcast
- */
- private final Podcast podcast;
-
- /**
- * ImageView to be updated
- */
- private final ImageView imageView;
-
- /**
- * Constructor
- *
- * @param podcast Podcast that has the image
- * @param imageView UI image to be updated
- */
- FetchImageTask(Podcast podcast, ImageView imageView){
- this.podcast = podcast;
- this.imageView = imageView;
- }
-
- //Get the image from the url
- @Override
- protected Bitmap doInBackground(Void... params) {
- HttpClient client = new DefaultHttpClient();
- HttpGet get = new HttpGet(podcast.imageUrl);
- try {
- HttpResponse response = client.execute(get);
- return BitmapFactory.decodeStream(response.getEntity().getContent());
- } catch (IOException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- //Set the background image for the podcast
- @Override
- protected void onPostExecute(Bitmap img) {
- super.onPostExecute(img);
- if(img!=null) {
- imageView.setImageBitmap(img);
- }
- }
- }
-
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//Current podcast
@@ -119,9 +66,21 @@ 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
- new FetchImageTask(podcast,viewHolder.coverView).execute();
+ Glide.with(context)
+ .load(podcast.imageUrl)
+ .placeholder(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .fitCenter()
+ .dontAnimate()
+ .into(viewHolder.coverView);
//Feed the grid view
return view;
@@ -142,6 +101,8 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
*/
public final TextView titleView;
+ public final TextView urlView;
+
/**
* Constructor
@@ -150,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);
}
}
@@ -172,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;
+ }
+
/**
- * Constructor.
+ * Constructs a Podcast instance from a iTunes search result
*
* @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 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);
}
+
+ /**
+ * Constructs a Podcast instance from iTunes toplist entry
+ *
+ * @param json object holding the podcast information
+ * @throws JSONException
+ */
+ 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/OpmlExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java
index 6bba956a6..5c24c2822 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java
@@ -1,11 +1,13 @@
package de.danoeh.antennapod.asynctask;
import android.annotation.SuppressLint;
-import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
import android.os.AsyncTask;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import java.io.File;
@@ -47,7 +49,7 @@ public class OpmlExportWorker extends AsyncTask<Void, Void, Void> {
OpmlWriter opmlWriter = new OpmlWriter();
if (output == null) {
output = new File(
- UserPreferences.getDataFolder(context, EXPORT_DIR),
+ UserPreferences.getDataFolder(EXPORT_DIR),
DEFAULT_OUTPUT_NAME);
if (output.exists()) {
Log.w(TAG, "Overwriting previously exported file.");
@@ -57,7 +59,7 @@ public class OpmlExportWorker extends AsyncTask<Void, Void, Void> {
OutputStreamWriter writer = null;
try {
writer = new OutputStreamWriter(new FileOutputStream(output), LangUtils.UTF_8);
- opmlWriter.writeDocument(DBReader.getFeedList(context), writer);
+ opmlWriter.writeDocument(DBReader.getFeedList(), writer);
} catch (IOException e) {
e.printStackTrace();
exception = e;
@@ -93,7 +95,17 @@ public class OpmlExportWorker extends AsyncTask<Void, Void, Void> {
alert.setTitle(R.string.opml_export_success_title);
alert.setMessage(context
.getString(R.string.opml_export_success_sum)
- + output.toString());
+ + output.toString())
+ .setPositiveButton(R.string.send_label, (dialog, which) -> {
+ Uri outputUri = Uri.fromFile(output);
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_SUBJECT,
+ context.getResources().getText(R.string.opml_export_label));
+ sendIntent.putExtra(Intent.EXTRA_STREAM, outputUri);
+ sendIntent.setType("text/plain");
+ context.startActivity(Intent.createChooser(sendIntent,
+ context.getResources().getText(R.string.send_label)));
+ });
}
alert.create().show();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
index 5486bc4fb..86636485d 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
@@ -1,12 +1,12 @@
package de.danoeh.antennapod.asynctask;
import android.annotation.SuppressLint;
-import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.AsyncTask;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.R;
@@ -37,8 +37,7 @@ public class OpmlImportWorker extends
@Override
protected ArrayList<OpmlElement> doInBackground(Void... params) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Starting background work");
+ Log.d(TAG, "Starting background work");
if (mReader==null) {
return null;
@@ -72,21 +71,14 @@ public class OpmlImportWorker extends
}
progDialog.dismiss();
if (exception != null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "An error occurred while trying to parse the opml document");
+ Log.d(TAG, "An error occurred while trying to parse the opml document");
AlertDialog.Builder alert = new AlertDialog.Builder(context);
alert.setTitle(R.string.error_label);
alert.setMessage(context.getString(R.string.opml_reader_error)
+ exception.getMessage());
- alert.setNeutralButton(android.R.string.ok, new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.dismiss();
- }
-
- });
+ alert.setNeutralButton(android.R.string.ok, (dialog, which) -> {
+ dialog.dismiss();
+ });
alert.create().show();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java
index 4d9be5d78..008aacfa5 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/ApplicationCallbacksImpl.java
@@ -8,7 +8,6 @@ import android.content.Intent;
import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.activity.StorageErrorActivity;
import de.danoeh.antennapod.core.ApplicationCallbacks;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
public class ApplicationCallbacksImpl implements ApplicationCallbacks {
@@ -22,8 +21,4 @@ public class ApplicationCallbacksImpl implements ApplicationCallbacks {
return new Intent(context, StorageErrorActivity.class);
}
- @Override
- public void setUpdateInterval(long updateInterval) {
- UserPreferences.restartUpdateAlarm(updateInterval, updateInterval);
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
index 10666aa36..932b9d22f 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
@@ -14,7 +14,6 @@ public class ClientConfigurator {
ClientConfig.downloadServiceCallbacks = new DownloadServiceCallbacksImpl();
ClientConfig.gpodnetCallbacks = new GpodnetCallbacksImpl();
ClientConfig.playbackServiceCallbacks = new PlaybackServiceCallbacksImpl();
- ClientConfig.storageCallbacks = new StorageCallbacksImpl();
ClientConfig.flattrCallbacks = new FlattrCallbacksImpl();
ClientConfig.dbTasksCallbacks = new DBTasksCallbacksImpl();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/DBTasksCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/DBTasksCallbacksImpl.java
index 75dcb2ef1..9f8af1142 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/DBTasksCallbacksImpl.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/DBTasksCallbacksImpl.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.config;
import de.danoeh.antennapod.core.DBTasksCallbacks;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APDownloadAlgorithm;
import de.danoeh.antennapod.core.storage.AutomaticDownloadAlgorithm;
@@ -15,6 +16,6 @@ public class DBTasksCallbacksImpl implements DBTasksCallbacks {
@Override
public EpisodeCleanupAlgorithm getEpisodeCacheCleanupAlgorithm() {
- return new APCleanupAlgorithm();
+ return UserPreferences.getEpisodeCleanupAlgorithm();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java
deleted file mode 100644
index 943e05690..000000000
--- a/app/src/main/java/de/danoeh/antennapod/config/StorageCallbacksImpl.java
+++ /dev/null
@@ -1,153 +0,0 @@
-package de.danoeh.antennapod.config;
-
-
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.util.Log;
-
-import de.danoeh.antennapod.core.StorageCallbacks;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-
-public class StorageCallbacksImpl implements StorageCallbacks {
-
- @Override
- public int getDatabaseVersion() {
- return 15;
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- Log.w("DBAdapter", "Upgrading from version " + oldVersion + " to "
- + newVersion + ".");
- if (oldVersion <= 1) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN "
- + PodDBAdapter.KEY_TYPE + " TEXT");
- }
- if (oldVersion <= 2) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS
- + " ADD COLUMN " + PodDBAdapter.KEY_LINK + " TEXT");
- }
- if (oldVersion <= 3) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + PodDBAdapter.KEY_ITEM_IDENTIFIER + " TEXT");
- }
- if (oldVersion <= 4) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN "
- + PodDBAdapter.KEY_FEED_IDENTIFIER + " TEXT");
- }
- if (oldVersion <= 5) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG
- + " ADD COLUMN " + PodDBAdapter.KEY_REASON_DETAILED + " TEXT");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG
- + " ADD COLUMN " + PodDBAdapter.KEY_DOWNLOADSTATUS_TITLE + " TEXT");
- }
- if (oldVersion <= 6) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS
- + " ADD COLUMN " + PodDBAdapter.KEY_CHAPTER_TYPE + " INTEGER");
- }
- if (oldVersion <= 7) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " ADD COLUMN " + PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE
- + " INTEGER");
- }
- if (oldVersion <= 8) {
- final int KEY_ID_POSITION = 0;
- final int KEY_MEDIA_POSITION = 1;
-
- // Add feeditem column to feedmedia table
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " ADD COLUMN " + PodDBAdapter.KEY_FEEDITEM
- + " INTEGER");
- Cursor feeditemCursor = db.query(PodDBAdapter.TABLE_NAME_FEED_ITEMS,
- new String[]{PodDBAdapter.KEY_ID, PodDBAdapter.KEY_MEDIA}, "? > 0",
- new String[]{PodDBAdapter.KEY_MEDIA}, null, null, null);
- if (feeditemCursor.moveToFirst()) {
- db.beginTransaction();
- ContentValues contentValues = new ContentValues();
- do {
- long mediaId = feeditemCursor.getLong(KEY_MEDIA_POSITION);
- contentValues.put(PodDBAdapter.KEY_FEEDITEM, feeditemCursor.getLong(KEY_ID_POSITION));
- db.update(PodDBAdapter.TABLE_NAME_FEED_MEDIA, contentValues, PodDBAdapter.KEY_ID + "=?", new String[]{String.valueOf(mediaId)});
- contentValues.clear();
- } while (feeditemCursor.moveToNext());
- db.setTransactionSuccessful();
- db.endTransaction();
- }
- feeditemCursor.close();
- }
- if (oldVersion <= 9) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD
- + " INTEGER DEFAULT 1");
- }
- if (oldVersion <= 10) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_FLATTR_STATUS
- + " INTEGER");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + PodDBAdapter.KEY_FLATTR_STATUS
- + " INTEGER");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " ADD COLUMN " + PodDBAdapter.KEY_PLAYED_DURATION
- + " INTEGER");
- }
- if (oldVersion <= 11) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_USERNAME
- + " TEXT");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_PASSWORD
- + " TEXT");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + PodDBAdapter.KEY_IMAGE
- + " INTEGER");
- }
- if (oldVersion <= 12) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_IS_PAGED + " INTEGER DEFAULT 0");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_NEXT_PAGE_LINK + " TEXT");
- }
- if (oldVersion <= 13) {
- // remove duplicate rows in "Chapters" table that were created because of a bug.
- db.execSQL(String.format("DELETE FROM %s WHERE %s NOT IN " +
- "(SELECT MIN(%s) as %s FROM %s GROUP BY %s,%s,%s,%s,%s)",
- PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS,
- PodDBAdapter.KEY_ID,
- PodDBAdapter.KEY_ID,
- PodDBAdapter.KEY_ID,
- PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS,
- PodDBAdapter.KEY_TITLE,
- PodDBAdapter.KEY_START,
- PodDBAdapter.KEY_FEEDITEM,
- PodDBAdapter.KEY_LINK,
- PodDBAdapter.KEY_CHAPTER_TYPE));
- }
- if(oldVersion <= 14) {
-
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD + " INTEGER");
- db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " SET " + PodDBAdapter.KEY_AUTO_DOWNLOAD + " = "
- + "(SELECT " + PodDBAdapter.KEY_AUTO_DOWNLOAD
- + " FROM " + PodDBAdapter.TABLE_NAME_FEEDS
- + " WHERE " + PodDBAdapter.TABLE_NAME_FEEDS + "." + PodDBAdapter.KEY_ID
- + " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_FEED + ")");
-
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_HIDE + " TEXT");
-
-
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0");
-
- // create indexes
- db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_FEED);
- db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_IMAGE);
- db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDMEDIA_FEEDITEM);
- db.execSQL(PodDBAdapter.CREATE_INDEX_QUEUE_FEEDITEM);
- db.execSQL(PodDBAdapter.CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM);
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
index 1585f9b86..75b1bc8d2 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
@@ -2,9 +2,9 @@ package de.danoeh.antennapod.dialog;
import android.annotation.SuppressLint;
import android.app.Activity;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.support.v7.app.AlertDialog;
import android.view.View;
import android.widget.CheckBox;
import android.widget.SeekBar;
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
new file mode 100644
index 000000000..6432ebd4e
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -0,0 +1,376 @@
+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;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.joanzapata.iconify.Icon;
+import com.joanzapata.iconify.IconDrawable;
+import com.joanzapata.iconify.fonts.FontAwesomeIcons;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.util.LongList;
+
+public class EpisodesApplyActionFragment extends Fragment {
+
+ public String TAG = "EpisodeActionFragment";
+
+ private ListView mListView;
+ private ArrayAdapter<String> mAdapter;
+
+ private Button btnAddToQueue;
+ private Button btnMarkAsPlayed;
+ private Button btnMarkAsUnplayed;
+ private Button btnDownload;
+ private Button btnDelete;
+
+ private final Map<Long,FeedItem> idMap;
+ private final List<FeedItem> episodes;
+ private final List<String> titles = new ArrayList();
+ private final LongList checkedIds = new LongList();
+
+ private MenuItem mSelectToggle;
+
+ private int textColor;
+
+ public EpisodesApplyActionFragment() {
+ this.episodes = new ArrayList<>();
+ this.idMap = new ArrayMap<>();
+ }
+
+ 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);
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.episodes_apply_action_fragment, container, false);
+
+ mListView = (ListView) view.findViewById(android.R.id.list);
+ mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+ mListView.setOnItemClickListener((ListView, view1, position, rowId) -> {
+ long id = episodes.get(position).getId();
+ if (checkedIds.contains(id)) {
+ checkedIds.remove(id);
+ } else {
+ checkedIds.add(id);
+ }
+ refreshCheckboxes();
+ });
+
+ for(FeedItem episode : episodes) {
+ titles.add(episode.getTitle());
+ }
+
+ mAdapter = new ArrayAdapter<>(getActivity(),
+ android.R.layout.simple_list_item_multiple_choice, titles);
+ mListView.setAdapter(mAdapter);
+ checkAll();
+
+ btnAddToQueue = (Button) view.findViewById(R.id.btnAddToQueue);
+ btnAddToQueue.setOnClickListener(v -> queueChecked());
+ btnMarkAsPlayed = (Button) view.findViewById(R.id.btnMarkAsPlayed);
+ btnMarkAsPlayed.setOnClickListener(v -> markedCheckedPlayed());
+ btnMarkAsUnplayed = (Button) view.findViewById(R.id.btnMarkAsUnplayed);
+ btnMarkAsUnplayed.setOnClickListener(v -> markedCheckedUnplayed());
+ btnDownload = (Button) view.findViewById(R.id.btnDownload);
+ btnDownload.setOnClickListener(v -> downloadChecked());
+ btnDelete = (Button) view.findViewById(R.id.btnDelete);
+ btnDelete.setOnClickListener(v -> deleteChecked());
+
+ return view;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.episodes_apply_action_options, menu);
+
+ int[] attrs = { android.R.attr.textColor };
+ TypedArray ta = getActivity().obtainStyledAttributes(attrs);
+ textColor = ta.getColor(0, Color.GRAY);
+ ta.recycle();
+
+ mSelectToggle = menu.findItem(R.id.select_toggle);
+ mSelectToggle.setOnMenuItemClickListener(item -> {
+ if (checkedIds.size() == episodes.size()) {
+ checkNone();
+ } else {
+ checkAll();
+ }
+ return true;
+ });
+ }
+
+ @Override
+ public void onPrepareOptionsMenu (Menu menu) {
+ /*
+ * Prepare icon for select toggle button
+ */
+
+ // Find icon attribute
+ int[] icon = new int[1];
+ if(checkedIds.size() == episodes.size()) icon[0] = R.attr.ic_check_box;
+ else if(checkedIds.size() == 0) icon[0] = R.attr.ic_check_box_outline;
+ else icon[0] = R.attr.ic_indeterminate_check_box;
+
+ // Get Drawable from attribute
+ TypedArray a = getActivity().obtainStyledAttributes(icon);
+ Drawable iconDrawable = a.getDrawable(0);
+ a.recycle();
+
+ // Set icon
+ mSelectToggle.setIcon(iconDrawable);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ int resId = 0;
+ switch(item.getItemId()) {
+ case R.id.select_options:
+ return true;
+ case R.id.check_all:
+ checkAll();
+ resId = R.string.selected_all_label;
+ break;
+ case R.id.check_none:
+ checkNone();
+ resId = R.string.deselected_all_label;
+ break;
+ case R.id.check_played:
+ checkPlayed(true);
+ resId = R.string.selected_played_label;
+ break;
+ case R.id.check_unplayed:
+ checkPlayed(false);
+ resId = R.string.selected_unplayed_label;
+ break;
+ case R.id.check_downloaded:
+ checkDownloaded(true);
+ resId = R.string.selected_downloaded_label;
+ break;
+ case R.id.check_not_downloaded:
+ checkDownloaded(false);
+ resId = R.string.selected_not_downloaded_label;
+ break;
+ case R.id.sort_title_a_z:
+ sortByTitle(false);
+ return true;
+ case R.id.sort_title_z_a:
+ sortByTitle(true);
+ return true;
+ case R.id.sort_date_new_old:
+ sortByDate(true);
+ return true;
+ case R.id.sort_date_old_new:
+ sortByDate(false);
+ return true;
+ case R.id.sort_duration_long_short:
+ sortByDuration(true);
+ return true;
+ case R.id.sort_duration_short_long:
+ sortByDuration(false);
+ return true;
+ }
+ if(resId != 0) {
+ Toast.makeText(getActivity(), resId, Toast.LENGTH_SHORT).show();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private void sortByTitle(final boolean reverse) {
+ Collections.sort(episodes, (lhs, rhs) -> {
+ if (reverse) {
+ return -1 * lhs.getTitle().compareTo(rhs.getTitle());
+ } else {
+ return lhs.getTitle().compareTo(rhs.getTitle());
+ }
+ });
+ refreshTitles();
+ refreshCheckboxes();
+ }
+
+ private void sortByDate(final boolean reverse) {
+ Collections.sort(episodes, (lhs, rhs) -> {
+ if (lhs.getPubDate() == null) {
+ return -1;
+ } else if (rhs.getPubDate() == null) {
+ return 1;
+ }
+ int code = lhs.getPubDate().compareTo(rhs.getPubDate());
+ if (reverse) {
+ return -1 * code;
+ } else {
+ return code;
+ }
+ });
+ refreshTitles();
+ refreshCheckboxes();
+ }
+
+ private void sortByDuration(final boolean reverse) {
+ Collections.sort(episodes, (lhs, rhs) -> {
+ int ordering;
+ if (false == lhs.hasMedia()) {
+ ordering = 1;
+ } else if (false == rhs.hasMedia()) {
+ ordering = -1;
+ } else {
+ ordering = lhs.getMedia().getDuration() - rhs.getMedia().getDuration();
+ }
+ if(reverse) {
+ return -1 * ordering;
+ } else {
+ return ordering;
+ }
+ });
+ refreshTitles();
+ refreshCheckboxes();
+ }
+
+ private void checkAll() {
+ for (FeedItem episode : episodes) {
+ if(false == checkedIds.contains(episode.getId())) {
+ checkedIds.add(episode.getId());
+ }
+ }
+ refreshCheckboxes();
+ }
+
+ private void checkNone() {
+ checkedIds.clear();
+ refreshCheckboxes();
+ }
+
+ private void checkPlayed(boolean isPlayed) {
+ for (FeedItem episode : episodes) {
+ if(episode.isPlayed() == isPlayed) {
+ if(!checkedIds.contains(episode.getId())) {
+ checkedIds.add(episode.getId());
+ }
+ } else {
+ if(checkedIds.contains(episode.getId())) {
+ checkedIds.remove(episode.getId());
+ }
+ }
+ }
+ refreshCheckboxes();
+ }
+
+ private void checkDownloaded(boolean isDownloaded) {
+ for (FeedItem episode : episodes) {
+ if(episode.hasMedia() && episode.getMedia().isDownloaded() == isDownloaded) {
+ if(!checkedIds.contains(episode.getId())) {
+ checkedIds.add(episode.getId());
+ }
+ } else {
+ if(checkedIds.contains(episode.getId())) {
+ checkedIds.remove(episode.getId());
+ }
+ }
+ }
+ refreshCheckboxes();
+ }
+
+ private void refreshTitles() {
+ titles.clear();
+ for(FeedItem episode : episodes) {
+ titles.add(episode.getTitle());
+ }
+ mAdapter.notifyDataSetChanged();
+ }
+
+ private void refreshCheckboxes() {
+ for (int i = 0; i < episodes.size(); i++) {
+ FeedItem episode = episodes.get(i);
+ boolean checked = checkedIds.contains(episode.getId());
+ mListView.setItemChecked(i, checked);
+ }
+ ActivityCompat.invalidateOptionsMenu(EpisodesApplyActionFragment.this.getActivity());
+ }
+
+ private void queueChecked() {
+ DBWriter.addQueueItem(getActivity(), true, checkedIds.toArray());
+ close();
+ }
+
+ private void markedCheckedPlayed() {
+ DBWriter.markItemPlayed(FeedItem.PLAYED, checkedIds.toArray());
+ close();
+ }
+
+ private void markedCheckedUnplayed() {
+ DBWriter.markItemPlayed(FeedItem.UNPLAYED, checkedIds.toArray());
+ close();
+ }
+
+ private void downloadChecked() {
+ // download the check episodes in the same order as they are currently displayed
+ List<FeedItem> toDownload = new ArrayList<FeedItem>(checkedIds.size());
+ for(FeedItem episode : episodes) {
+ if(checkedIds.contains(episode.getId())) {
+ toDownload.add(episode);
+ }
+ }
+ try {
+ DBTasks.downloadFeedItems(getActivity(), toDownload.toArray(new FeedItem[0]));
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage());
+ }
+ close();
+ }
+
+ private void deleteChecked() {
+ for(long id : checkedIds.toArray()) {
+ FeedItem episode = idMap.get(id);
+ if(episode.hasMedia()) {
+ DBWriter.deleteFeedMediaOfItem(getActivity(), episode.getMedia().getId());
+ }
+ }
+ close();
+ }
+
+ private void close() {
+ getActivity().getSupportFragmentManager().popBackStack();
+ }
+
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
index 16fb77f2a..5f531e88f 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.dialog;
-import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
+import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.InputType;
import android.view.View;
@@ -26,28 +26,19 @@ public class GpodnetSetHostnameDialog {
et.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
dialog.setTitle(R.string.pref_gpodnet_sethostname_title)
.setView(setupContentView(context, et))
- .setPositiveButton(R.string.confirm_label, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final Editable e = et.getText();
- if (e != null) {
- GpodnetPreferences.setHostname(e.toString());
- }
- dialog.dismiss();
+ .setPositiveButton(R.string.confirm_label, (dialog1, which) -> {
+ final Editable e = et.getText();
+ if (e != null) {
+ GpodnetPreferences.setHostname(e.toString());
}
+ dialog1.dismiss();
})
- .setNegativeButton(R.string.cancel_label, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
+ .setNegativeButton(R.string.cancel_label, (dialog1, which) -> {
+ dialog1.cancel();
})
- .setNeutralButton(R.string.pref_gpodnet_sethostname_use_default_host, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- GpodnetPreferences.setHostname(GpodnetService.DEFAULT_BASE_HOST);
- dialog.dismiss();
- }
+ .setNeutralButton(R.string.pref_gpodnet_sethostname_use_default_host, (dialog1, which) -> {
+ GpodnetPreferences.setHostname(GpodnetService.DEFAULT_BASE_HOST);
+ dialog1.dismiss();
})
.setCancelable(true);
return dialog.show();
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
new file mode 100644
index 000000000..ed0db92a4
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
@@ -0,0 +1,136 @@
+package de.danoeh.antennapod.dialog;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.support.annotation.Nullable;
+import android.util.Log;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.TimeUnit;
+
+import de.danoeh.antennapod.R;
+
+public class RatingDialog {
+
+ private static final String TAG = RatingDialog.class.getSimpleName();
+ private static final int AFTER_DAYS = 7;
+
+ private static WeakReference<Context> mContext;
+ private static SharedPreferences mPreferences;
+ private static Dialog mDialog;
+
+ private static final String PREFS_NAME = "RatingPrefs";
+ private static final String KEY_RATED = "KEY_WAS_RATED";
+ private static final String KEY_FIRST_START_DATE = "KEY_FIRST_HIT_DATE";
+
+ public static void init(Context context) {
+ mContext = new WeakReference<>(context);
+ mPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
+
+ long firstDate = mPreferences.getLong(KEY_FIRST_START_DATE, 0);
+ if (firstDate == 0) {
+ resetStartDate();
+ }
+ }
+
+ public static void check() {
+ if (mDialog != null && mDialog.isShowing()) {
+ return;
+ }
+ if (shouldShow()) {
+ try {
+ mDialog = createDialog();
+ if (mDialog != null) {
+ mDialog.show();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+ }
+
+ public static void rateNow() {
+ Context context = mContext.get();
+ if(context == null) {
+ return;
+ }
+ final String appPackage = "de.danoeh.antennapod";
+ final Uri uri = Uri.parse("https://play.google.com/store/apps/details?id=" + appPackage);
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ saveRated();
+ }
+
+ public static boolean rated() {
+ return mPreferences.getBoolean(KEY_RATED, false);
+ }
+
+ public static void saveRated() {
+ mPreferences
+ .edit()
+ .putBoolean(KEY_RATED, true)
+ .apply();
+ }
+
+ private static void resetStartDate() {
+ mPreferences
+ .edit()
+ .putLong(KEY_FIRST_START_DATE, System.currentTimeMillis())
+ .apply();
+ }
+
+ private static boolean shouldShow() {
+ if (rated()) {
+ return false;
+ }
+
+ long now = System.currentTimeMillis();
+ long firstDate = mPreferences.getLong(KEY_FIRST_START_DATE, now);
+ long diff = now - firstDate;
+ long diffDays = TimeUnit.DAYS.convert(diff, TimeUnit.MILLISECONDS);
+ if (diffDays >= AFTER_DAYS) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Nullable
+ private static MaterialDialog createDialog() {
+ Context context = mContext.get();
+ if(context == null) {
+ return null;
+ }
+ MaterialDialog dialog = new MaterialDialog.Builder(context)
+ .title(R.string.rating_title)
+ .content(R.string.rating_message)
+ .positiveText(R.string.rating_now_label)
+ .negativeText(R.string.rating_never_label)
+ .neutralText(R.string.rating_later_label)
+ .callback(new MaterialDialog.ButtonCallback() {
+ @Override
+ public void onPositive(MaterialDialog dialog) {
+ rateNow();
+ }
+
+ @Override
+ public void onNegative(MaterialDialog dialog) {
+ saveRated();
+ }
+
+ @Override
+ public void onNeutral(MaterialDialog dialog) {
+ resetStartDate();
+ }
+ })
+ .cancelListener(dialog1 -> resetStartDate())
+ .build();
+ return dialog;
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
new file mode 100644
index 000000000..930079e40
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
@@ -0,0 +1,154 @@
+package de.danoeh.antennapod.dialog;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.Toast;
+
+import com.afollestad.materialdialogs.DialogAction;
+import com.afollestad.materialdialogs.MaterialDialog;
+
+import java.util.concurrent.TimeUnit;
+
+import de.danoeh.antennapod.R;
+
+public abstract class SleepTimerDialog {
+
+ private static final String TAG = SleepTimerDialog.class.getSimpleName();
+
+ private static final int DEFAULT_SPINNER_POSITION = 1;
+
+ private Context context;
+ private String PREF_NAME = "SleepTimerDialog";
+ private String PREF_VALUE = "LastValue";
+ private String PREF_TIME_UNIT = "LastTimeUnit";
+ private String PREF_VIBRATE = "Vibrate";
+ private String PREF_SHAKE_TO_RESET = "ShakeToReset";
+ private SharedPreferences prefs;
+
+ private MaterialDialog dialog;
+ private EditText etxtTime;
+ private Spinner spTimeUnit;
+ private CheckBox cbShakeToReset;
+ private CheckBox cbVibrate;
+
+
+ private TimeUnit[] units = { TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS };
+
+ public SleepTimerDialog(Context context) {
+ this.context = context;
+ prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+ }
+
+ public MaterialDialog createNewDialog() {
+ MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
+ builder.title(R.string.set_sleeptimer_label);
+ builder.customView(R.layout.time_dialog, false);
+ builder.positiveText(R.string.set_sleeptimer_label);
+ builder.negativeText(R.string.cancel_label);
+ builder.callback(new MaterialDialog.ButtonCallback() {
+ @Override
+ public void onNegative(MaterialDialog dialog) {
+ dialog.dismiss();
+ }
+
+ @Override
+ public void onPositive(MaterialDialog dialog) {
+ try {
+ savePreferences();
+ long input = readTimeMillis();
+ onTimerSet(input, cbShakeToReset.isChecked(), cbVibrate.isChecked());
+ dialog.dismiss();
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ Toast toast = Toast.makeText(context, R.string.time_dialog_invalid_input,
+ Toast.LENGTH_LONG);
+ toast.show();
+ }
+ }
+ });
+ dialog = builder.build();
+
+ View view = dialog.getView();
+ etxtTime = (EditText) view.findViewById(R.id.etxtTime);
+ spTimeUnit = (Spinner) view.findViewById(R.id.spTimeUnit);
+ cbShakeToReset = (CheckBox) view.findViewById(R.id.cbShakeToReset);
+ cbVibrate = (CheckBox) view.findViewById(R.id.cbVibrate);
+
+ etxtTime.setText(prefs.getString(PREF_VALUE, "15"));
+ etxtTime.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ checkInputLength(s.length());
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+ });
+ etxtTime.postDelayed(() -> {
+ InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.showSoftInput(etxtTime, InputMethodManager.SHOW_IMPLICIT);
+ }, 100);
+
+ String[] spinnerContent = new String[] {
+ context.getString(R.string.time_seconds),
+ context.getString(R.string.time_minutes),
+ context.getString(R.string.time_hours) };
+ ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(context,
+ android.R.layout.simple_spinner_item, spinnerContent);
+ spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ spTimeUnit.setAdapter(spinnerAdapter);
+ int selection = prefs.getInt(PREF_TIME_UNIT, DEFAULT_SPINNER_POSITION);
+ spTimeUnit.setSelection(selection);
+
+ cbShakeToReset.setChecked(prefs.getBoolean(PREF_SHAKE_TO_RESET, true));
+ cbVibrate.setChecked(prefs.getBoolean(PREF_VIBRATE, true));
+
+ return dialog;
+ }
+
+ private void checkInputLength(int length) {
+ if (length > 0) {
+ Log.d(TAG, "Length is larger than 0, enabling confirm button");
+ dialog.getActionButton(DialogAction.POSITIVE).setEnabled(true);
+ } else {
+ Log.d(TAG, "Length is smaller than 0, disabling confirm button");
+ dialog.getActionButton(DialogAction.POSITIVE).setEnabled(false);
+ }
+ }
+
+ public abstract void onTimerSet(long millis, boolean shakeToReset, boolean vibrate);
+
+ private long readTimeMillis() {
+ TimeUnit selectedUnit = units[spTimeUnit.getSelectedItemPosition()];
+ long value = Long.valueOf(etxtTime.getText().toString());
+ return selectedUnit.toMillis(value);
+ }
+
+ private void savePreferences() {
+ prefs.edit()
+ .putString(PREF_VALUE, etxtTime.getText().toString())
+ .putInt(PREF_TIME_UNIT, spTimeUnit.getSelectedItemPosition())
+ .putBoolean(PREF_SHAKE_TO_RESET, cbShakeToReset.isChecked())
+ .putBoolean(PREF_VIBRATE, cbVibrate.isChecked())
+ .apply();
+ }
+
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java
deleted file mode 100644
index 6561d501e..000000000
--- a/app/src/main/java/de/danoeh/antennapod/dialog/TimeDialog.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package de.danoeh.antennapod.dialog;
-
-import android.app.Dialog;
-import android.content.Context;
-import android.os.Bundle;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.util.Log;
-import android.view.View;
-import android.view.Window;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.*;
-import de.danoeh.antennapod.core.BuildConfig;
-import de.danoeh.antennapod.R;
-
-import java.util.concurrent.TimeUnit;
-
-public abstract class TimeDialog extends Dialog {
- private static final String TAG = "TimeDialog";
-
- private static final int DEFAULT_SPINNER_POSITION = 1;
-
- private Context context;
-
- private EditText etxtTime;
- private Spinner spTimeUnit;
- private Button butConfirm;
- private Button butCancel;
-
- private TimeUnit[] units = {TimeUnit.SECONDS, TimeUnit.MINUTES,
- TimeUnit.HOURS};
-
- public TimeDialog(Context context, int titleTextId, int leftButtonTextId) {
- super(context);
- this.context = context;
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- String[] spinnerContent = new String[]{context.getString(R.string.time_unit_seconds),
- context.getString(R.string.time_unit_minutes),
- context.getString(R.string.time_unit_hours)};
-
- setContentView(R.layout.time_dialog);
- etxtTime = (EditText) findViewById(R.id.etxtTime);
- spTimeUnit = (Spinner) findViewById(R.id.spTimeUnit);
- butConfirm = (Button) findViewById(R.id.butConfirm);
- butCancel = (Button) findViewById(R.id.butCancel);
-
- butConfirm.setText(R.string.set_sleeptimer_label);
- butCancel.setText(R.string.cancel_label);
- setTitle(R.string.set_sleeptimer_label);
- ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<String>(
- this.getContext(), android.R.layout.simple_spinner_item,
- spinnerContent);
- spinnerAdapter
- .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- spTimeUnit.setAdapter(spinnerAdapter);
- spTimeUnit.setSelection(DEFAULT_SPINNER_POSITION);
- butCancel.setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- dismiss();
- }
- });
- butConfirm.setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- try {
- long input = readTimeMillis();
- onTimeEntered(input);
- dismiss();
- } catch (NumberFormatException e) {
- e.printStackTrace();
- Toast toast = Toast.makeText(context,
- R.string.time_dialog_invalid_input,
- Toast.LENGTH_LONG);
- toast.show();
- }
- }
- });
- etxtTime.addTextChangedListener(new TextWatcher() {
-
- @Override
- public void afterTextChanged(Editable s) {
- checkInputLength(s.length());
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count,
- int after) {
-
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before,
- int count) {
-
- }
- });
- checkInputLength(etxtTime.getText().length());
- etxtTime.postDelayed(new Runnable() {
- @Override
- public void run() {
- InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.showSoftInput(etxtTime, InputMethodManager.SHOW_IMPLICIT);
- }
- }, 100);
-
-
-
- }
-
- private void checkInputLength(int length) {
- if (length > 0) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Length is larger than 0, enabling confirm button");
- butConfirm.setEnabled(true);
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Length is smaller than 0, disabling confirm button");
- butConfirm.setEnabled(false);
- }
- }
-
- public abstract void onTimeEntered(long millis);
-
- private long readTimeMillis() {
- TimeUnit selectedUnit = units[spTimeUnit.getSelectedItemPosition()];
- long value = Long.valueOf(etxtTime.getText().toString());
- return selectedUnit.toMillis(value);
- }
-
-}
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 8eba51540..3ed82b9bd 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -1,100 +1,124 @@
package de.danoeh.antennapod.dialog;
-import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
+import android.os.Build;
+import android.support.v7.app.AlertDialog;
+import android.util.Log;
+import android.view.View;
+
+import com.afollestad.materialdialogs.DialogAction;
+import com.afollestad.materialdialogs.MaterialDialog;
import java.util.Arrays;
import java.util.List;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.IntentUtils;
+
public class VariableSpeedDialog {
- private VariableSpeedDialog() {
- }
-
- public static void showDialog(final Context context) {
- if (com.aocate.media.MediaPlayer.isPrestoLibraryInstalled(context)) {
- showSpeedSelectorDialog(context);
- } else {
- showGetPluginDialog(context);
- }
- }
-
- private static void showGetPluginDialog(final Context context) {
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.no_playback_plugin_title);
- builder.setMessage(R.string.no_playback_plugin_msg);
- builder.setNegativeButton(R.string.close_label, null);
- builder.setPositiveButton(R.string.download_plugin_label,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- try {
- Intent playStoreIntent = new Intent(
- Intent.ACTION_VIEW,
- Uri.parse("market://details?id=com.falconware.prestissimo"));
- context.startActivity(playStoreIntent);
- } catch (ActivityNotFoundException e) {
- // this is usually thrown on an emulator if the Android market is not installed
- e.printStackTrace();
- }
- }
- });
- builder.create().show();
- }
-
- 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, new DialogInterface.OnMultiChoiceClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which,
- boolean isChecked) {
- speedChecked[which] = isChecked;
- }
-
- });
- builder.setNegativeButton(android.R.string.cancel, null);
- builder.setPositiveButton(android.R.string.ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int 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 bbe6fab46..d979dc382 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -10,7 +10,6 @@ import android.widget.Button;
import android.widget.EditText;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.DefaultOnlineFeedViewActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
@@ -46,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
@@ -73,7 +72,7 @@ public class AddFeedFragment extends Fragment {
butConfirm.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- Intent intent = new Intent(getActivity(), DefaultOnlineFeedViewActivity.class);
+ Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, etxtFeedurl.getText().toString());
intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, getString(R.string.add_feed_label));
startActivity(intent);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
index ff5485251..273c75240 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -4,36 +4,36 @@ import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Handler;
import android.support.v4.app.Fragment;
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.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.widget.AdapterView;
import android.widget.ProgressBar;
-import android.widget.TextView;
import android.widget.Toast;
-import com.mobeta.android.dslv.DragSortListView;
+import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
-import de.danoeh.antennapod.adapter.AllEpisodesListAdapter;
-import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
+import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -45,9 +45,15 @@ import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.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;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Shows unread or recently published episodes
@@ -56,90 +62,74 @@ public class AllEpisodesFragment extends Fragment {
public static final String TAG = "AllEpisodesFragment";
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.DOWNLOAD_QUEUED |
+ private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE |
EventDistributor.UNREAD_ITEMS_UPDATE |
EventDistributor.PLAYER_STATUS_UPDATE;
private static final int RECENT_EPISODES_LIMIT = 150;
private static final String DEFAULT_PREF_NAME = "PrefAllEpisodesFragment";
- private static final String PREF_KEY_LIST_TOP = "list_top";
- private static final String PREF_KEY_LIST_SELECTION = "list_selection";
+ private static final String PREF_SCROLL_POSITION = "scroll_position";
+ private static final String PREF_SCROLL_OFFSET = "scroll_offset";
- private String prefName;
- protected DragSortListView listView;
- private AllEpisodesListAdapter listAdapter;
- private TextView txtvEmpty;
+ protected RecyclerView recyclerView;
+ private AllEpisodesRecycleAdapter listAdapter;
private ProgressBar progLoading;
- private ContextMenu contextMenu;
private List<FeedItem> episodes;
- private LongList queuedItemsIds;
- private LongList newItemsIds;
private List<Downloader> downloaderList;
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
- private final boolean showOnlyNewEpisodes;
private AtomicReference<MainActivity> activity = new AtomicReference<MainActivity>();
- private DownloadObserver downloadObserver = null;
-
private boolean isUpdatingFeeds;
- public AllEpisodesFragment() {
- // by default we show all the episodes
- this(false, DEFAULT_PREF_NAME);
- }
+ protected Subscription subscription;
+ private LinearLayoutManager layoutManager;
- // this is only going to be called by our sub-class.
- // The Android docs say to avoid non-default constructors
- // but I think this will be OK since it will only be invoked
- // from a fragment via a default constructor
- protected AllEpisodesFragment(boolean showOnlyNewEpisodes, String prefName) {
- this.showOnlyNewEpisodes = showOnlyNewEpisodes;
- this.prefName = prefName;
- }
+ protected boolean showOnlyNewEpisodes() { return false; }
+ protected String getPrefName() { return DEFAULT_PREF_NAME; }
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setRetainInstance(true);
setHasOptionsMenu(true);
}
@Override
- public void onResume() {
- super.onResume();
- startItemLoader();
- }
-
- @Override
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
this.activity.set((MainActivity) getActivity());
- if (downloadObserver != null) {
- downloadObserver.setActivity(getActivity());
- downloadObserver.onResume();
- }
if (viewsCreated && itemsLoaded) {
onFragmentLoaded();
}
}
@Override
+ public void onResume() {
+ super.onResume();
+ EventBus.getDefault().registerSticky(this);
+ loadItems();
+ registerForContextMenu(recyclerView);
+ }
+
+ @Override
public void onPause() {
super.onPause();
+ EventBus.getDefault().unregister(this);
saveScrollPosition();
+ unregisterForContextMenu(recyclerView);
}
@Override
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
- stopItemLoader();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
}
@Override
@@ -155,25 +145,32 @@ public class AllEpisodesFragment extends Fragment {
}
private void saveScrollPosition() {
- SharedPreferences prefs = getActivity().getSharedPreferences(prefName, Context.MODE_PRIVATE);
+ int firstItem = layoutManager.findFirstVisibleItemPosition();
+ View firstItemView = layoutManager.findViewByPosition(firstItem);
+ float topOffset;
+ if(firstItemView == null) {
+ topOffset = 0;
+ } else {
+ topOffset = firstItemView.getTop();
+ }
+
+ SharedPreferences prefs = getActivity().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
- View v = listView.getChildAt(0);
- int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop());
- editor.putInt(PREF_KEY_LIST_SELECTION, listView.getFirstVisiblePosition());
- editor.putInt(PREF_KEY_LIST_TOP, top);
+ editor.putInt(PREF_SCROLL_POSITION, firstItem);
+ editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
editor.commit();
}
private void restoreScrollPosition() {
- SharedPreferences prefs = getActivity().getSharedPreferences(prefName, Context.MODE_PRIVATE);
- int listSelection = prefs.getInt(PREF_KEY_LIST_SELECTION, 0);
- int top = prefs.getInt(PREF_KEY_LIST_TOP, 0);
- if (listSelection > 0 || top > 0) {
- listView.setSelectionFromTop(listSelection, top);
+ SharedPreferences prefs = getActivity().getSharedPreferences(getPrefName(), Context.MODE_PRIVATE);
+ int position = prefs.getInt(PREF_SCROLL_POSITION, 0);
+ float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
+ if (position > 0 || offset > 0) {
+ layoutManager.scrollToPositionWithOffset(position, (int) offset);
// restore once, then forget
SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(PREF_KEY_LIST_SELECTION, 0);
- editor.putInt(PREF_KEY_LIST_TOP, 0);
+ editor.putInt(PREF_SCROLL_POSITION, 0);
+ editor.putFloat(PREF_SCROLL_OFFSET, 0.0f);
editor.commit();
}
}
@@ -182,9 +179,6 @@ public class AllEpisodesFragment extends Fragment {
listAdapter = null;
activity.set(null);
viewsCreated = false;
- if (downloadObserver != null) {
- downloadObserver.onPause();
- }
}
@@ -197,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);
@@ -252,7 +249,7 @@ public class AllEpisodesFragment extends Fragment {
public void onConfirmButtonPressed(
DialogInterface dialog) {
dialog.dismiss();
- DBWriter.markAllItemsRead(getActivity());
+ DBWriter.markAllItemsRead();
Toast.makeText(getActivity(), R.string.mark_all_read_msg, Toast.LENGTH_SHORT).show();
}
};
@@ -268,41 +265,62 @@ public class AllEpisodesFragment extends Fragment {
}
@Override
+ public boolean onContextItemSelected(MenuItem item) {
+ Log.d(TAG, "onContextItemSelected() called with: " + "item = [" + item + "]");
+ if(!isVisible()) {
+ return false;
+ }
+ if(item.getItemId() == R.id.share_item) {
+ return true; // avoids that the position is reset when we need it in the submenu
+ }
+ int pos = listAdapter.getPosition();
+ if(pos < 0) {
+ return false;
+ }
+ FeedItem selectedItem = itemAccess.getItem(pos);
+
+ if (selectedItem == null) {
+ Log.i(TAG, "Selected item at position " + pos + " was null, ignoring selection");
+ return super.onContextItemSelected(item);
+ }
+
+ try {
+ return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
+ return true;
+ }
+ }
+
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return onCreateViewHelper(inflater, container, savedInstanceState,
- R.layout.all_episodes_fragment, R.string.all_episodes_label);
+ R.layout.all_episodes_fragment);
}
protected View onCreateViewHelper(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState,
- int fragmentResource,
- int titleString) {
+ int fragmentResource) {
super.onCreateView(inflater, container, savedInstanceState);
- ((MainActivity) getActivity()).getSupportActionBar().setTitle(titleString);
View root = inflater.inflate(fragmentResource, container, false);
- listView = (DragSortListView) root.findViewById(android.R.id.list);
- txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
- progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
-
- listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- FeedItem item = (FeedItem) listAdapter.getItem(position - listView.getHeaderViewsCount());
- if (item != null) {
- ((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(item.getId()));
- }
-
- }
- });
+ 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);
+ recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
- registerForContextMenu(listView);
+ progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
if (!itemsLoaded) {
progLoading.setVisibility(View.VISIBLE);
- txtvEmpty.setVisibility(View.GONE);
}
viewsCreated = true;
@@ -314,63 +332,12 @@ public class AllEpisodesFragment extends Fragment {
return root;
}
- private final FeedItemMenuHandler.MenuInterface contextMenuInterface = new FeedItemMenuHandler.MenuInterface() {
- @Override
- public void setItemVisibility(int id, boolean visible) {
- if(contextMenu == null) {
- return;
- }
- MenuItem item = contextMenu.findItem(id);
- if (item != null) {
- item.setVisible(visible);
- }
- }
- };
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
- FeedItem item = itemAccess.getItem(adapterInfo.position);
-
- MenuInflater inflater = getActivity().getMenuInflater();
- inflater.inflate(R.menu.allepisodes_context, menu);
-
- if (item != null) {
- menu.setHeaderTitle(item.getTitle());
- }
-
- contextMenu = menu;
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, queuedItemsIds);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
- FeedItem selectedItem = itemAccess.getItem(menuInfo.position);
-
- if (selectedItem == null) {
- Log.i(TAG, "Selected item at position " + menuInfo.position + " was null, ignoring selection");
- return super.onContextItemSelected(item);
- }
-
- try {
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
- return true;
- }
- }
-
private void onFragmentLoaded() {
if (listAdapter == null) {
- listAdapter = new AllEpisodesListAdapter(activity.get(), itemAccess,
- new DefaultActionButtonCallback(activity.get()), showOnlyNewEpisodes);
- listView.setAdapter(listAdapter);
- listView.setEmptyView(txtvEmpty);
- downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback);
- downloadObserver.onResume();
+ MainActivity mainActivity = activity.get();
+ listAdapter = new AllEpisodesRecycleAdapter(mainActivity, itemAccess,
+ new DefaultActionButtonCallback(mainActivity), showOnlyNewEpisodes());
+ recyclerView.setAdapter(listAdapter);
}
listAdapter.notifyDataSetChanged();
restoreScrollPosition();
@@ -378,28 +345,11 @@ public class AllEpisodesFragment extends Fragment {
updateShowOnlyEpisodesListViewState();
}
- private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
- @Override
- public void onContentChanged() {
- if (listAdapter != null) {
- listAdapter.notifyDataSetChanged();
- }
- }
-
- @Override
- public void onDownloadDataAvailable(List<Downloader> downloaderList) {
- AllEpisodesFragment.this.downloaderList = downloaderList;
- if (listAdapter != null) {
- listAdapter.notifyDataSetChanged();
- }
- }
- };
-
- private AllEpisodesListAdapter.ItemAccess itemAccess = new AllEpisodesListAdapter.ItemAccess() {
+ protected AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
@Override
public int getCount() {
- if (itemsLoaded) {
+ if (episodes != null) {
return episodes.size();
}
return 0;
@@ -407,7 +357,7 @@ public class AllEpisodesFragment extends Fragment {
@Override
public FeedItem getItem(int position) {
- if (itemsLoaded) {
+ if (episodes != null && 0 <= position && position < episodes.size()) {
return episodes.get(position);
}
return null;
@@ -428,112 +378,118 @@ public class AllEpisodesFragment extends Fragment {
@Override
public boolean isInQueue(FeedItem item) {
- if (itemsLoaded) {
- return queuedItemsIds.contains(item.getId());
- } else {
- return false;
+ if (item != null) {
+ return item.isTagged(FeedItem.TAG_QUEUE);
}
+ return false;
}
@Override
- public boolean isNew(FeedItem item) {
- if (itemsLoaded) {
- // should actually never be called in NewEpisodesFragment, but better safe than sorry
- return showOnlyNewEpisodes || newItemsIds.contains(item.getId());
- } else {
- return false;
+ 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;
}
-
- };
-
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- startItemLoader();
- if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
- getActivity().supportInvalidateOptionsMenu();
+ 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;
}
};
- private void updateShowOnlyEpisodesListViewState() {
- if (showOnlyNewEpisodes) {
- listView.setEmptyView(null);
- txtvEmpty.setVisibility(View.GONE);
- } else {
- listView.setEmptyView(txtvEmpty);
+ public void onEventMainThread(FeedItemEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ if(episodes == null || listAdapter == null) {
+ return;
}
- }
-
- private ItemLoader itemLoader;
-
- protected void startItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
- }
- itemLoader = new ItemLoader();
- itemLoader.execute();
- }
-
- protected void stopItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
+ for(int i=0, size = event.items.size(); i < size; i++) {
+ FeedItem item = event.items.get(i);
+ int pos = FeedItemUtil.indexOfItemWithId(episodes, item.getId());
+ if(pos >= 0) {
+ episodes.remove(pos);
+ episodes.add(pos, item);
+ listAdapter.notifyItemChanged(pos);
+ }
}
}
- private class ItemLoader extends AsyncTask<Void, Void, Object[]> {
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- if (viewsCreated && !itemsLoaded) {
- listView.setVisibility(View.GONE);
- txtvEmpty.setVisibility(View.GONE);
- progLoading.setVisibility(View.VISIBLE);
- }
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if (isUpdatingFeeds != update.feedIds.length > 0) {
+ getActivity().supportInvalidateOptionsMenu();
}
-
- @Override
- protected Object[] doInBackground(Void... params) {
- Context context = activity.get();
- if (context != null) {
- if(showOnlyNewEpisodes) {
- return new Object[] {
- DBReader.getNewItemsList(context),
- DBReader.getQueueIDList(context),
- null // see ItemAccess.isNew
- };
- } else {
- return new Object[]{
- DBReader.getRecentlyPublishedEpisodes(context, RECENT_EPISODES_LIMIT),
- DBReader.getQueueIDList(context),
- DBReader.getNewItemIds(context)
- };
+ if(listAdapter != null && update.mediaIds.length > 0) {
+ for(long mediaId : update.mediaIds) {
+ int pos = FeedItemUtil.indexOfItemWithMediaId(episodes, mediaId);
+ if(pos >= 0) {
+ listAdapter.notifyItemChanged(pos);
}
- } else {
- return null;
}
}
+ }
+ private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
- protected void onPostExecute(Object[] lists) {
- super.onPostExecute(lists);
- listView.setVisibility(View.VISIBLE);
- progLoading.setVisibility(View.GONE);
-
- if (lists != null) {
- episodes = (List<FeedItem>) lists[0];
- queuedItemsIds = (LongList) lists[1];
- newItemsIds = (LongList) lists[2];
- itemsLoaded = true;
- if (viewsCreated && activity.get() != null) {
- onFragmentLoaded();
+ public void update(EventDistributor eventDistributor, Integer arg) {
+ if ((arg & EVENTS) != 0) {
+ loadItems();
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ getActivity().supportInvalidateOptionsMenu();
}
}
}
+ };
+
+ private void updateShowOnlyEpisodesListViewState() {
+ }
+
+ protected void loadItems() {
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
+ if (viewsCreated && !itemsLoaded) {
+ recyclerView.setVisibility(View.GONE);
+ progLoading.setVisibility(View.VISIBLE);
+ }
+ subscription = Observable.fromCallable(() -> loadData())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(data -> {
+ recyclerView.setVisibility(View.VISIBLE);
+ progLoading.setVisibility(View.GONE);
+ if (data != null) {
+ episodes = data;
+ itemsLoaded = true;
+ if (viewsCreated && activity.get() != null) {
+ onFragmentLoaded();
+ }
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ }
+
+ protected List<FeedItem> loadData() {
+ return DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT);
}
+
}
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..ce1d753e8
--- /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.getChapters() == null) {
+ 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 3ca5b3c89..954c4c9e2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -1,14 +1,12 @@
package de.danoeh.antennapod.fragment;
import android.app.Activity;
-import android.content.Context;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
+import android.util.Log;
import android.view.View;
import android.widget.ListView;
-import java.util.Collections;
import java.util.List;
import de.danoeh.antennapod.R;
@@ -18,11 +16,18 @@ import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Displays all running downloads and provides a button to delete them
*/
public class CompletedDownloadsFragment extends ListFragment {
+
+ private static final String TAG = CompletedDownloadsFragment.class.getSimpleName();
+
private static final int EVENTS =
EventDistributor.DOWNLOAD_HANDLED |
EventDistributor.DOWNLOADLOG_UPDATE |
@@ -34,11 +39,12 @@ public class CompletedDownloadsFragment extends ListFragment {
private boolean viewCreated = false;
private boolean itemsLoaded = false;
+ private Subscription subscription;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-
- startItemLoader();
+ loadItems();
}
@Override
@@ -51,13 +57,17 @@ public class CompletedDownloadsFragment extends ListFragment {
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
- stopItemLoader();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
}
@Override
public void onDetach() {
super.onDetach();
- stopItemLoader();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
}
@Override
@@ -65,7 +75,9 @@ public class CompletedDownloadsFragment extends ListFragment {
super.onDestroyView();
listAdapter = null;
viewCreated = false;
- stopItemLoader();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
}
@Override
@@ -119,7 +131,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
@@ -132,56 +148,32 @@ public class CompletedDownloadsFragment extends ListFragment {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
- startItemLoader();
+ loadItems();
}
}
};
- private ItemLoader itemLoader;
-
- private void startItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
+ private void loadItems() {
+ if(subscription != null) {
+ subscription.unsubscribe();
}
- itemLoader = new ItemLoader();
- itemLoader.execute();
- }
-
- private void stopItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
+ if (!itemsLoaded && viewCreated) {
+ setListShown(false);
}
+ subscription = Observable.fromCallable(() -> DBReader.getDownloadedItems())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ if (result != null) {
+ items = result;
+ itemsLoaded = true;
+ if (viewCreated && getActivity() != null) {
+ onFragmentLoaded();
+ }
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
- private class ItemLoader extends AsyncTask<Void, Void, List<FeedItem>> {
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- if (!itemsLoaded && viewCreated) {
- setListShown(false);
- }
- }
-
- @Override
- protected void onPostExecute(List<FeedItem> results) {
- super.onPostExecute(results);
- if (results != null) {
- items = results;
- itemsLoaded = true;
- if (viewCreated && getActivity() != null) {
- onFragmentLoaded();
- }
- }
- }
-
- @Override
- protected List<FeedItem> doInBackground(Void... params) {
- Context context = getActivity();
- if (context != null) {
- return DBReader.getDownloadedItems(context);
- }
- return Collections.emptyList();
- }
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
index 3076f8136..931d14924 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,29 +7,30 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
+import android.widget.TextView;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
-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;
/**
* 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) {
@@ -45,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);
@@ -57,35 +55,28 @@ 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(imgvCover == null) {
+ return;
+ }
if (media != null) {
- imgvCover.post(new Runnable() {
-
- @Override
- public void run() {
- Context c = getActivity();
- if (c != null) {
- Picasso.with(c)
- .load(media.getImageUri())
- .into(imgvCover);
- }
- }
- });
+ Log.d(TAG, "feed title: " + media.getFeedTitle());
+ Log.d(TAG, "episode title: " + media.getEpisodeTitle());
+ 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");
}
@@ -93,12 +84,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");
@@ -106,12 +95,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(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 074a87ea0..b470d379a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -1,11 +1,10 @@
package de.danoeh.antennapod.fragment;
-import android.content.Context;
import android.content.res.TypedArray;
-import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.view.MenuItemCompat;
+import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -20,6 +19,10 @@ import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Shows the download log
@@ -34,19 +37,23 @@ public class DownloadLogFragment extends ListFragment {
private boolean viewsCreated = false;
private boolean itemsLoaded = false;
+ private Subscription subscription;
+
@Override
public void onStart() {
super.onStart();
setHasOptionsMenu(true);
EventDistributor.getInstance().register(contentUpdate);
- startItemLoader();
+ loadItems();
}
@Override
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
- stopItemLoader();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
}
@Override
@@ -84,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;
+ }
}
};
@@ -93,29 +104,16 @@ public class DownloadLogFragment extends ListFragment {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EventDistributor.DOWNLOADLOG_UPDATE) != 0) {
- startItemLoader();
+ loadItems();
}
}
};
- private ItemLoader itemLoader;
-
- private void startItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
- }
- itemLoader = new ItemLoader();
- itemLoader.execute();
- }
-
- private void stopItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
- }
- }
-
@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);
@@ -142,7 +140,7 @@ public class DownloadLogFragment extends ListFragment {
if (!super.onOptionsItemSelected(item)) {
switch (item.getItemId()) {
case R.id.clear_history_item:
- DBWriter.clearDownloadLog(getActivity());
+ DBWriter.clearDownloadLog();
return true;
default:
return false;
@@ -152,27 +150,24 @@ public class DownloadLogFragment extends ListFragment {
}
}
- private class ItemLoader extends AsyncTask<Void, Void, List<DownloadStatus>> {
-
- @Override
- protected void onPostExecute(List<DownloadStatus> downloadStatuses) {
- super.onPostExecute(downloadStatuses);
- if (downloadStatuses != null) {
- downloadLog = downloadStatuses;
- itemsLoaded = true;
- if (viewsCreated) {
- onFragmentLoaded();
- }
- }
- }
-
- @Override
- protected List<DownloadStatus> doInBackground(Void... params) {
- Context context = getActivity();
- if (context != null) {
- return DBReader.getDownloadLog(context);
- }
- return null;
+ private void loadItems() {
+ if(subscription != null) {
+ subscription.unsubscribe();
}
+ subscription = Observable.fromCallable(() -> DBReader.getDownloadLog())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ if (result != null) {
+ downloadLog = result;
+ itemsLoaded = true;
+ if (viewsCreated) {
+ onFragmentLoaded();
+ }
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
index 1ce379cf8..52a38ccb9 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
@@ -1,7 +1,10 @@
package de.danoeh.antennapod.fragment;
+import android.content.Context;
+import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
+import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
@@ -25,15 +28,24 @@ public class DownloadsFragment extends Fragment {
public static final int POS_COMPLETED = 1;
public static final int POS_LOG = 2;
- private ViewPager pager;
+ private static final String PREF_LAST_TAB_POSITION = "tab_position";
+
+ private ViewPager viewPager;
+ private TabLayout tabLayout;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(R.layout.pager_fragment, container, false);
- pager = (ViewPager) root.findViewById(R.id.pager);
+
+ viewPager = (ViewPager)root.findViewById(R.id.viewpager);
DownloadsPagerAdapter pagerAdapter = new DownloadsPagerAdapter(getChildFragmentManager(), getResources());
- pager.setAdapter(pagerAdapter);
+ viewPager.setAdapter(pagerAdapter);
+
+ // Give the TabLayout the ViewPager
+ tabLayout = (TabLayout) root.findViewById(R.id.sliding_tabs);
+ tabLayout.setupWithViewPager(viewPager);
+
return root;
}
@@ -42,10 +54,30 @@ public class DownloadsFragment extends Fragment {
super.onViewCreated(view, savedInstanceState);
if (getArguments() != null) {
int tab = getArguments().getInt(ARG_SELECTED_TAB);
- pager.setCurrentItem(tab, false);
+ viewPager.setCurrentItem(tab, false);
}
}
+ @Override
+ public void onPause() {
+ super.onPause();
+ // save our tab selection
+ SharedPreferences prefs = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(PREF_LAST_TAB_POSITION, tabLayout.getSelectedTabPosition());
+ editor.apply();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ // restore our last position
+ SharedPreferences prefs = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE);
+ int lastPosition = prefs.getInt(PREF_LAST_TAB_POSITION, 0);
+ viewPager.setCurrentItem(lastPosition);
+ }
+
public class DownloadsPagerAdapter extends FragmentPagerAdapter {
Resources resources;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
new file mode 100644
index 000000000..f23981935
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
@@ -0,0 +1,120 @@
+package de.danoeh.antennapod.fragment;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.design.widget.TabLayout;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+
+public class EpisodesFragment extends Fragment {
+
+ public static final String TAG = "EpisodesFragment";
+ private static final String PREF_LAST_TAB_POSITION = "tab_position";
+
+ public static final int POS_NEW_EPISODES = 0;
+ public static final int POS_ALL_EPISODES = 1;
+ public static final int POS_FAV_EPISODES = 2;
+ public static final int TOTAL_COUNT = 3;
+
+
+ private TabLayout tabLayout;
+ private ViewPager viewPager;
+
+ //Mandatory Constructor
+ public EpisodesFragment() {
+ }
+
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ }
+
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ setHasOptionsMenu(true);
+ ((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.episodes_label);
+
+ View rootView = inflater.inflate(R.layout.pager_fragment, container, false);
+ viewPager = (ViewPager)rootView.findViewById(R.id.viewpager);
+ viewPager.setAdapter(new EpisodesPagerAdapter(getChildFragmentManager(), getResources()));
+
+ // Give the TabLayout the ViewPager
+ tabLayout = (TabLayout) rootView.findViewById(R.id.sliding_tabs);
+ tabLayout.setupWithViewPager(viewPager);
+
+ return rootView;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ // save our tab selection
+ SharedPreferences prefs = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(PREF_LAST_TAB_POSITION, tabLayout.getSelectedTabPosition());
+ editor.apply();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ // restore our last position
+ SharedPreferences prefs = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE);
+ int lastPosition = prefs.getInt(PREF_LAST_TAB_POSITION, 0);
+ viewPager.setCurrentItem(lastPosition);
+ }
+
+ public static class EpisodesPagerAdapter extends FragmentPagerAdapter {
+
+ private final Resources resources;
+
+ public EpisodesPagerAdapter(FragmentManager fm, Resources resources) {
+ super(fm);
+ this.resources = resources;
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ switch (position) {
+ case POS_ALL_EPISODES:
+ return new AllEpisodesFragment();
+ case POS_NEW_EPISODES:
+ return new NewEpisodesFragment();
+ case POS_FAV_EPISODES:
+ return new FavoriteEpisodesFragment();
+ }
+ return null;
+ }
+
+ @Override
+ public int getCount() {
+ return TOTAL_COUNT;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ switch (position) {
+ case POS_ALL_EPISODES:
+ return resources.getString(R.string.all_episodes_short_label);
+ case POS_NEW_EPISODES:
+ return resources.getString(R.string.new_label);
+ case POS_FAV_EPISODES:
+ return resources.getString(R.string.favorite_episodes_label);
+ default:
+ return super.getPageTitle(position);
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
index fdb128f03..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,11 +9,13 @@ 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.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.playback.Playable;
@@ -24,13 +26,14 @@ import de.danoeh.antennapod.core.util.playback.PlaybackController;
* if the PlaybackService is running
*/
public class ExternalPlayerFragment extends Fragment {
- private static final String TAG = "ExternalPlayerFragment";
+ public static final String TAG = "ExternalPlayerFragment";
private ViewGroup fragmentLayout;
private ImageView imgvCover;
- private ViewGroup layoutInfo;
private TextView txtvTitle;
private ImageButton butPlay;
+ private TextView mFeedName;
+ private ProgressBar mProgressBar;
private PlaybackController controller;
@@ -45,17 +48,18 @@ 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) {
Log.d(TAG, "layoutInfo was clicked");
- if (controller.getMedia() != null) {
+ if (controller != null && controller.getMedia() != null) {
startActivity(PlaybackService.getPlayerActivityIntent(
getActivity(), controller.getMedia()));
}
@@ -75,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
@@ -111,13 +88,6 @@ public class ExternalPlayerFragment extends Fragment {
return butPlay;
}
- @Override
- public void postStatusMsg(int msg) {
- }
-
- @Override
- public void clearStatusMsg() {
- }
@Override
public boolean loadMediaInfo() {
@@ -130,39 +100,13 @@ public class ExternalPlayerFragment extends Fragment {
}
@Override
- public void onAwaitingVideoSurface() {
- }
-
- @Override
- public void onServiceQueried() {
- }
-
- @Override
public void onShutdownNotification() {
- if (fragmentLayout != null) {
- fragmentLayout.setVisibility(View.GONE);
- }
- controller = setupPlaybackController();
- if (butPlay != null) {
- butPlay.setOnClickListener(controller
- .newOnPlayButtonClickListener());
- }
+ playbackDone();
}
@Override
public void onPlaybackEnd() {
- if (fragmentLayout != null) {
- fragmentLayout.setVisibility(View.GONE);
- }
- controller = setupPlaybackController();
- if (butPlay != null) {
- butPlay.setOnClickListener(controller
- .newOnPlayButtonClickListener());
- }
- }
-
- @Override
- public void onPlaybackSpeedChange() {
+ playbackDone();
}
};
}
@@ -171,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
@@ -190,16 +136,38 @@ public class ExternalPlayerFragment extends Fragment {
}
}
+ private void playbackDone() {
+ if (fragmentLayout != null) {
+ fragmentLayout.setVisibility(View.GONE);
+ }
+ if (controller != null) {
+ controller.release();
+ }
+ controller = setupPlaybackController();
+ if (butPlay != null) {
+ butPlay.setOnClickListener(controller
+ .newOnPlayButtonClickListener());
+ }
+ controller.init();
+ }
+
private boolean loadMediaInfo() {
Log.d(TAG, "Loading media info");
- if (controller.serviceAvailable()) {
+ if (controller != null && controller.serviceAvailable()) {
Playable media = controller.getMedia();
if (media != null) {
txtvTitle.setText(media.getEpisodeTitle());
+ mFeedName.setText(media.getFeedTitle());
+ mProgressBar.setProgress((int)
+ ((double) controller.getPosition() / controller.getDuration() * 100));
- Picasso.with(getActivity())
+ Glide.with(getActivity())
.load(media.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(imgvCover);
fragmentLayout.setVisibility(View.VISIBLE);
@@ -223,4 +191,13 @@ public class ExternalPlayerFragment extends Fragment {
return Converter.getDurationStringLong(position) + " / "
+ Converter.getDurationStringLong(duration);
}
+
+ 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/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
new file mode 100644
index 000000000..65305df3d
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -0,0 +1,91 @@
+package de.danoeh.antennapod.fragment;
+
+import android.os.Bundle;
+import android.support.design.widget.Snackbar;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.List;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
+import de.danoeh.antennapod.core.event.FavoritesEvent;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+
+
+/**
+ * Like 'EpisodesFragment' except that it only shows favorite episodes and
+ * supports swiping to remove from favorites.
+ */
+
+public class FavoriteEpisodesFragment extends AllEpisodesFragment {
+
+ public static final String TAG = "FavoriteEpisodesFrag";
+
+ private static final String PREF_NAME = "PrefFavoriteEpisodesFragment";
+
+ @Override
+ protected boolean showOnlyNewEpisodes() { return true; }
+
+ @Override
+ protected String getPrefName() { return PREF_NAME; }
+
+ public void onEvent(FavoritesEvent event) {
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
+ loadItems();
+ }
+
+ @Override
+ protected void resetViewState() {
+ super.resetViewState();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View root = super.onCreateViewHelper(inflater, container, savedInstanceState,
+ R.layout.all_episodes_fragment);
+
+ ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
+ @Override
+ public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
+ return false;
+ }
+
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
+ AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder;
+ Log.d(TAG, "remove(" + holder.getItemId() + ")");
+
+ if (subscription != null) {
+ subscription.unsubscribe();
+ }
+ FeedItem item = holder.getFeedItem();
+ if (item != null) {
+ DBWriter.removeFavoriteItem(item);
+
+ Snackbar snackbar = Snackbar.make(root, getString(R.string.removed_item),
+ Snackbar.LENGTH_LONG);
+ snackbar.setAction(getString(R.string.undo), v -> {
+ DBWriter.addFavoriteItem(item);
+ });
+ snackbar.show();
+ }
+ }
+ };
+
+ ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
+ itemTouchHelper.attachToRecyclerView(recyclerView);
+ return root;
+ }
+
+ @Override
+ protected List<FeedItem> loadData() {
+ return DBReader.getFavoriteItemsList();
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index a7c6d62e6..4c723e5ff 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,27 +21,33 @@ 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.BuildConfig;
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;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.ShownotesProvider;
import de.danoeh.antennapod.core.util.playback.Playable;
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";
@@ -55,12 +62,12 @@ public class ItemDescriptionFragment extends Fragment {
private static final String ARG_HIGHLIGHT_TIMECODES = "arg.highlightTimecodes";
private WebView webvDescription;
+ private String webvData;
private ShownotesProvider shownotesProvider;
private Playable media;
-
- private AsyncTask<Void, Void, Void> webViewLoader;
+ private Subscription webViewLoader;
/**
* URL that was selected via long-press.
@@ -104,20 +111,19 @@ public class ItemDescriptionFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Creating view");
+ Log.d(TAG, "Creating view");
webvDescription = new WebView(getActivity());
- if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
- if (Build.VERSION.SDK_INT >= 11
- && 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() {
@@ -141,17 +147,9 @@ public class ItemDescriptionFragment extends Fragment {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Page finished");
+ Log.d(TAG, "Page finished");
// Restoring the scroll position might not always work
- view.postDelayed(new Runnable() {
-
- @Override
- public void run() {
- restoreFromPreference();
- }
-
- }, 50);
+ view.postDelayed(() -> restoreFromPreference(), 50);
}
});
@@ -161,29 +159,11 @@ public class ItemDescriptionFragment extends Fragment {
}
@Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Fragment attached");
- }
-
- @Override
- public void onDetach() {
- super.onDetach();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Fragment detached");
- if (webViewLoader != null) {
- webViewLoader.cancel(true);
- }
- }
-
- @Override
public void onDestroy() {
super.onDestroy();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Fragment destroyed");
+ Log.d(TAG, "Fragment destroyed");
if (webViewLoader != null) {
- webViewLoader.cancel(true);
+ webViewLoader.unsubscribe();
}
if (webvDescription != null) {
webvDescription.removeAllViews();
@@ -195,12 +175,10 @@ public class ItemDescriptionFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Creating fragment");
+ Log.d(TAG, "Creating fragment");
Bundle args = getArguments();
saveState = args.getBoolean(ARG_SAVE_STATE, false);
highlightTimecodes = args.getBoolean(ARG_HIGHLIGHT_TIMECODES, false);
-
}
@Override
@@ -210,46 +188,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(getActivity(), 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() {
@@ -258,11 +211,7 @@ public class ItemDescriptionFragment extends Fragment {
WebView.HitTestResult r = webvDescription.getHitTestResult();
if (r != null
&& r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Link of webview was long-pressed. Extra: "
- + r.getExtra()
- );
+ Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.getExtra());
selectedURL = r.getExtra();
webvDescription.showContextMenu();
return true;
@@ -281,8 +230,10 @@ public class ItemDescriptionFragment extends Fragment {
switch (item.getItemId()) {
case R.id.open_in_browser_item:
Uri uri = Uri.parse(selectedURL);
- getActivity()
- .startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ getActivity().startActivity(intent);
+ }
break;
case R.id.share_url_item:
ShareUtils.shareLink(getActivity(), selectedURL);
@@ -331,8 +282,12 @@ public class ItemDescriptionFragment extends Fragment {
R.string.go_to_position_label);
menu.setHeaderTitle(Converter.getDurationStringLong(Timeline.getTimecodeLinkTime(selectedURL)));
} else {
- menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
- R.string.open_in_browser_label);
+ Uri uri = Uri.parse(selectedURL);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
+ R.string.open_in_browser_label);
+ }
menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
R.string.copy_url_label);
menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
@@ -342,51 +297,31 @@ 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");
- if (BuildConfig.DEBUG)
+ 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 -> {
+ webvData = data;
+ 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) {
- if (BuildConfig.DEBUG)
- 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;
- }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ }
- };
+ private String loadData() {
+ Timeline timeline = new Timeline(getActivity(), shownotesProvider);
+ String data = timeline.processShownotes(highlightTimecodes);
+ return data;
}
@Override
@@ -397,24 +332,17 @@ public class ItemDescriptionFragment extends Fragment {
private void savePreference() {
if (saveState) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Saving preferences");
+ Log.d(TAG, "Saving preferences");
SharedPreferences prefs = getActivity().getSharedPreferences(PREF,
Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
if (media != null && webvDescription != null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Saving scroll position: "
- + webvDescription.getScrollY()
- );
+ Log.d(TAG, "Saving scroll position: " + webvDescription.getScrollY());
editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY());
editor.putString(PREF_PLAYABLE_ID, media.getIdentifier()
.toString());
} else {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "savePreferences was called while media or webview was null");
+ Log.d(TAG, "savePreferences was called while media or webview was null");
editor.putInt(PREF_SCROLL_Y, -1);
editor.putString(PREF_PLAYABLE_ID, "");
}
@@ -423,9 +351,8 @@ public class ItemDescriptionFragment extends Fragment {
}
private boolean restoreFromPreference() {
- if (saveState) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Restoring from preferences");
+ if (!saveState) {
+ Log.d(TAG, "Restoring from preferences");
Activity activity = getActivity();
if (activity != null) {
SharedPreferences prefs = activity.getSharedPreferences(
@@ -435,8 +362,7 @@ public class ItemDescriptionFragment extends Fragment {
if (scrollY != -1 && media != null
&& id.equals(media.getIdentifier().toString())
&& webvDescription != null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Restored scroll Position: " + scrollY);
+ Log.d(TAG, "Restored scroll Position: " + scrollY);
webvDescription.scrollTo(webvDescription.getScrollX(),
scrollY);
return true;
@@ -448,15 +374,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) {
+ 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 51a1e2252..ce80dc827 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -1,50 +1,51 @@
package de.danoeh.antennapod.fragment;
import android.annotation.TargetApi;
-import android.content.ActivityNotFoundException;
+import android.content.ClipData;
+import android.content.Context;
import android.content.Intent;
-import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
-import android.support.v4.app.LoaderManager;
-import android.support.v4.content.Loader;
-import android.support.v4.util.Pair;
-import android.support.v7.widget.PopupMenu;
-import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.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.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.joanzapata.iconify.Iconify;
+import com.joanzapata.iconify.widget.IconButton;
+
+import org.apache.commons.lang3.ArrayUtils;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
-import de.danoeh.antennapod.core.asynctask.DBTaskLoader;
-import de.danoeh.antennapod.core.asynctask.DownloadObserver;
+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;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.QueueEvent;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -53,21 +54,26 @@ 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;
import de.danoeh.antennapod.core.util.playback.Timeline;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.greenrobot.event.EventBus;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Displays information about a FeedItem and actions.
*/
-public class ItemFragment extends Fragment implements LoaderManager.LoaderCallbacks<Pair<FeedItem, LongList>> {
+public class ItemFragment extends Fragment {
private static final String TAG = "ItemFragment";
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.DOWNLOAD_QUEUED |
- EventDistributor.UNREAD_ITEMS_UPDATE;
+ private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE;
private static final String ARG_FEEDITEM = "feeditem";
@@ -89,12 +95,11 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
private long itemID;
private FeedItem item;
private LongList queue;
+ private LongList favorites;
private String webviewData;
- private DownloadObserver downloadObserver;
private List<Downloader> downloaderList;
private ViewGroup root;
- private View header;
private WebView webvDescription;
private TextView txtvTitle;
private TextView txtvDuration;
@@ -102,209 +107,172 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
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;
+
+ /**
+ * URL that was selected via long-press.
+ */
+ private String selectedURL;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
- setHasOptionsMenu(false);
+ setHasOptionsMenu(true);
itemID = getArguments().getLong(ARG_FEEDITEM, -1);
}
- @Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- getLoaderManager().initLoader(0, null, this);
- Toolbar toolbar = ((MainActivity) getActivity()).getToolbar();
- toolbar.addView(header);
- }
-
- @Override
- public void onStart() {
- super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
- if (downloadObserver != null) {
- downloadObserver.setActivity(getActivity());
- downloadObserver.onResume();
- }
- if (itemsLoaded) {
- onFragmentLoaded();
- }
-
- }
-
- @Override
- public void onStop() {
- super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
- EventBus.getDefault().unregister(this);
- }
-
- private void resetViewState() {
- if (downloadObserver != null) {
- downloadObserver.onPause();
- }
- Toolbar toolbar = ((MainActivity) getActivity()).getToolbar();
- toolbar.removeView(header);
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- resetViewState();
- if (webvDescription != null && root != null) {
- root.removeView(webvDescription);
- webvDescription.destroy();
- }
- }
-
-
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@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);
}
webvDescription = (WebView) layout.findViewById(R.id.webvDescription);
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) {
+ && 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));
+ R.color.black));
}
webvDescription.getSettings().setUseWideViewPort(false);
webvDescription.getSettings().setLayoutAlgorithm(
- WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
+ WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webvDescription.getSettings().setLoadWithOverviewMode(true);
+ webvDescription.setOnLongClickListener(webViewLongClickListener);
webvDescription.setWebViewClient(new WebViewClient() {
-
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- try {
+ if(IntentUtils.isCallable(getActivity(), intent)) {
startActivity(intent);
- } catch (ActivityNotFoundException e) {
- e.printStackTrace();
- return true;
}
return true;
}
});
+ registerForContextMenu(webvDescription);
- imgvCover = (ImageView) header.findViewById(R.id.imgvCover);
- progbarDownload = (ProgressBar) header.findViewById(R.id.progbarDownload);
+ 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();
- }
- }
-
-
- }
- );
-
- butAction2.setOnClickListener(new View.OnClickListener()
-
- {
- @Override
- public void onClick(View 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));
- }
- }
- }
- );
-
- butMore.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (item == null) {
- return;
- }
- popupMenu.getMenu().clear();
- popupMenu.inflate(R.menu.feeditem_options);
- if (item.hasMedia()) {
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue);
- } else {
- // these are already available via button1 and button2
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, queue,
- R.id.mark_read_item, R.id.visit_website_item);
- }
- popupMenu.show();
- }
- }
- );
-
- popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(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;
- }
- }
- }
- );
+ butAction1 = (IconButton) layout.findViewById(R.id.butAction1);
+ butAction2 = (IconButton) layout.findViewById(R.id.butAction2);
+
+ butAction1.setOnClickListener(v -> {
+ if (item == null) {
+ return;
+ }
+ 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();
+ }
+ });
+
+ 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));
+ }
+ });
return layout;
}
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ load();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().registerSticky(this);
+ if(itemsLoaded) {
+ updateAppearance();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ EventDistributor.getInstance().unregister(contentUpdate);
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
+ if (webvDescription != null && root != null) {
+ root.removeView(webvDescription);
+ webvDescription.destroy();
+ }
+ }
+
+ @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);
}
@@ -313,23 +281,36 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
private void onFragmentLoaded() {
- progbarLoading.setVisibility(View.GONE);
+ progbarLoading.setVisibility(View.INVISIBLE);
if (webviewData != null) {
webvDescription.loadDataWithBaseURL(null, webviewData, "text/html",
"utf-8", "about:blank");
}
updateAppearance();
- downloadObserver = new DownloadObserver(getActivity(), new Handler(), downloadObserverCallback);
- downloadObserver.onResume();
}
private void updateAppearance() {
+ if (item == null) {
+ Log.d(TAG, "updateAppearance item is null");
+ return;
+ }
+ getActivity().supportInvalidateOptionsMenu();
txtvTitle.setText(item.getTitle());
- txtvPublished.setText(DateUtils.formatDateTime(getActivity(), item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
- Picasso.with(getActivity()).load(item.getImageUri())
- .fit()
+ if (item.getPubDate() != null) {
+ String pubDateStr = DateUtils.formatAbbrev(getActivity(), item.getPubDate());
+ txtvPublished.setText(pubDateStr);
+ }
+
+ Glide.with(getActivity())
+ .load(item.getImageUri())
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(imgvCover);
+
progbarDownload.setVisibility(View.GONE);
if (item.hasMedia() && downloaderList != null) {
for (Downloader downloader : downloaderList) {
@@ -342,121 +323,223 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
}
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.isRead()) {
- 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);
+ if (!item.isPlayed()) {
+ 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);
}
}
- public void onEvent(QueueEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
- getLoaderManager().restartLoader(0, null, ItemFragment.this);
+ private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ WebView.HitTestResult r = webvDescription.getHitTestResult();
+ if (r != null
+ && r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
+ Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.getExtra());
+ selectedURL = r.getExtra();
+ webvDescription.showContextMenu();
+ return true;
+ }
+ selectedURL = null;
+ return false;
+ }
+ };
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ boolean handled = selectedURL != null;
+ if (selectedURL != null) {
+ switch (item.getItemId()) {
+ case R.id.open_in_browser_item:
+ Uri uri = Uri.parse(selectedURL);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ getActivity().startActivity(intent);
+ }
+ break;
+ case R.id.share_url_item:
+ ShareUtils.shareLink(getActivity(), selectedURL);
+ break;
+ case R.id.copy_url_item:
+ if (android.os.Build.VERSION.SDK_INT >= 11) {
+ ClipData clipData = ClipData.newPlainText(selectedURL,
+ selectedURL);
+ android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(clipData);
+ } else {
+ android.text.ClipboardManager cm = (android.text.ClipboardManager) getActivity()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setText(selectedURL);
+ }
+ Toast t = Toast.makeText(getActivity(),
+ R.string.copied_url_msg, Toast.LENGTH_SHORT);
+ t.show();
+ break;
+ default:
+ handled = false;
+ break;
+
+ }
+ selectedURL = null;
+ }
+ return handled;
}
@Override
- public Loader<Pair<FeedItem,LongList>> onCreateLoader(int id, Bundle args) {
- return new DBTaskLoader<Pair<FeedItem,LongList>>(getActivity()) {
- @Override
- public Pair<FeedItem,LongList> loadInBackground() {
- FeedItem data1 = DBReader.getFeedItem(getContext(), itemID);
- if (data1 != null) {
- Timeline t = new Timeline(getActivity(), data1);
- webviewData = t.processShownotes(false);
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenu.ContextMenuInfo menuInfo) {
+ if (selectedURL != null) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ Uri uri = Uri.parse(selectedURL);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
+ R.string.open_in_browser_label);
}
- LongList data2 = DBReader.getQueueIDList(getContext());
- return Pair.create(data1, data2);
+ menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
+ R.string.copy_url_label);
+ menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
+ R.string.share_url_label);
+ menu.setHeaderTitle(selectedURL);
+ }
+ }
+
+ public void onEventMainThread(QueueEvent event) {
+ if(event.contains(itemID)) {
+ load();
+ }
+ }
+
+ 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) {
+ if(itemID == item.getId()) {
+ load();
+ return;
}
- };
+ }
}
- @Override
- public void onLoadFinished(Loader<Pair<FeedItem,LongList>> loader, Pair<FeedItem,LongList> data) {
-
- if (data != null) {
- item = data.first;
- queue = data.second;
- if (!itemsLoaded) {
- itemsLoaded = true;
- onFragmentLoaded();
- } else {
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if(item == null || item.getMedia() == null) {
+ return;
+ }
+ long mediaId = item.getMedia().getId();
+ if(ArrayUtils.contains(update.mediaIds, mediaId)) {
+ if (itemsLoaded && getActivity() != null) {
updateAppearance();
}
}
}
- @Override
- public void onLoaderReset(Loader<Pair<FeedItem,LongList>> loader) {
- }
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
- getLoaderManager().restartLoader(0, null, ItemFragment.this);
+ load();
}
}
};
- private final DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
-
- @Override
- public void onContentChanged() {
- if (itemsLoaded && getActivity() != null) {
- updateAppearance();
- }
+ private void load() {
+ if(subscription != null) {
+ subscription.unsubscribe();
}
+ subscription = Observable.fromCallable(() -> loadInBackground())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ item = (FeedItem) result[0];
+ queue = (LongList) result[1];
+ favorites = (LongList) result[2];
+ if (!itemsLoaded) {
+ itemsLoaded = true;
+ onFragmentLoaded();
+ } else {
+ updateAppearance();
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ }
- @Override
- public void onDownloadDataAvailable(List<Downloader> downloaderList) {
- ItemFragment.this.downloaderList = downloaderList;
- if (itemsLoaded && getActivity() != null) {
- updateAppearance();
- }
+ private Object[] loadInBackground() {
+ FeedItem feedItem = DBReader.getFeedItem(itemID);
+ if (feedItem != null) {
+ Timeline t = new Timeline(getActivity(), feedItem);
+ webviewData = t.processShownotes(false);
}
- };
+ 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 a9cbe8291..7a9b73982 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -4,14 +4,13 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.os.AsyncTask;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.LightingColorFilter;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
import android.support.v4.app.ListFragment;
import android.support.v4.view.MenuItemCompat;
-
-import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.ContextMenu;
@@ -21,7 +20,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
-import android.widget.IconTextView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListAdapter;
@@ -29,8 +27,11 @@ import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
-import com.joanzapata.android.iconify.Iconify;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.joanzapata.iconify.IconDrawable;
+import com.joanzapata.iconify.Iconify;
+import com.joanzapata.iconify.fonts.FontAwesomeIcons;
+import com.joanzapata.iconify.widget.IconTextView;
import org.apache.commons.lang3.Validate;
@@ -41,30 +42,41 @@ import de.danoeh.antennapod.activity.FeedInfoActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
-import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
-import de.danoeh.antennapod.core.asynctask.PicassoProvider;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
+import de.danoeh.antennapod.core.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;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedItemFilter;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.QueueEvent;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.glide.FastBlurTransformation;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
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.core.util.gui.MoreContentListFooterUtil;
+import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.greenrobot.event.EventBus;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Displays a list of FeedItems.
@@ -73,9 +85,8 @@ import de.greenrobot.event.EventBus;
public class ItemlistFragment extends ListFragment {
private static final String TAG = "ItemlistFragment";
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED
- | EventDistributor.DOWNLOAD_QUEUED
- | EventDistributor.UNREAD_ITEMS_UPDATE
+ 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";
@@ -83,17 +94,16 @@ public class ItemlistFragment extends ListFragment {
protected FeedItemlistAdapter adapter;
private ContextMenu contextMenu;
+ private AdapterView.AdapterContextMenuInfo lastMenuInfo = null;
private long feedID;
private Feed feed;
private LongList queuedItemsIds;
- private LongList newItemsIds;
-
+ private LongList favoritedItemsId;
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
- private DownloadObserver downloadObserver;
private List<Downloader> downloaderList;
private MoreContentListFooterUtil listFooter;
@@ -104,6 +114,8 @@ public class ItemlistFragment extends ListFragment {
private TextView txtvInformation;
+ private Subscription subscription;
+
/**
* Creates new ItemlistFragment which shows the Feeditems of a specific
* feed. Sets 'showFeedtitle' to false
@@ -133,36 +145,29 @@ public class ItemlistFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
- if (downloadObserver != null) {
- downloadObserver.setActivity(getActivity());
- downloadObserver.onResume();
- }
if (viewsCreated && itemsLoaded) {
onFragmentLoaded();
}
}
@Override
- public void onStop() {
- super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
- EventBus.getDefault().unregister(this);
- stopItemLoader();
- }
-
- @Override
public void onResume() {
super.onResume();
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().registerSticky(this);
+ ((MainActivity)getActivity()).getSupportActionBar().setTitle("");
updateProgressBarVisibility();
- startItemLoader();
+ loadItems();
}
@Override
- public void onDetach() {
- super.onDetach();
- stopItemLoader();
+ public void onPause() {
+ super.onPause();
+ EventDistributor.getInstance().unregister(contentUpdate);
+ EventBus.getDefault().unregister(this);
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
}
@Override
@@ -175,9 +180,6 @@ public class ItemlistFragment extends ListFragment {
adapter = null;
viewsCreated = false;
listFooter = null;
- if (downloadObserver != null) {
- downloadObserver.onPause();
- }
}
private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() {
@@ -193,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) {
@@ -217,6 +222,18 @@ public class ItemlistFragment extends ListFragment {
return false;
}
});
+ if(feed == null || feed.getLink() == null) {
+ menu.findItem(R.id.share_link_item).setVisible(false);
+ menu.findItem(R.id.visit_website_item).setVisible(false);
+ }
+ int[] attrs = { R.attr.action_bar_icon_color };
+ TypedArray ta = getActivity().obtainStyledAttributes(UserPreferences.getTheme(), attrs);
+ int textColor = ta.getColor(0, Color.GRAY);
+ ta.recycle();
+
+ menu.findItem(R.id.episode_actions).setIcon(new IconDrawable(getActivity(),
+ FontAwesomeIcons.fa_gears).color(textColor).actionBarSize());
+
isUpdatingFeed = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
}
@@ -234,13 +251,18 @@ public class ItemlistFragment extends ListFragment {
try {
if (!FeedMenuHandler.onOptionsItemClicked(getActivity(), item, feed)) {
switch (item.getItemId()) {
+ case R.id.episode_actions:
+ EpisodesApplyActionFragment fragment = new EpisodesApplyActionFragment();
+ fragment.setEpisodes(feed.getItems());
+ ((MainActivity)getActivity()).loadChildFragment(fragment);
+ return true;
case R.id.remove_item:
final FeedRemover remover = new FeedRemover(
getActivity(), feed) {
@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
- ((MainActivity) getActivity()).loadFragment(NewEpisodesFragment.TAG, null);
+ ((MainActivity) getActivity()).loadFragment(EpisodesFragment.TAG, null);
}
};
ConfirmationDialog conDialog = new ConfirmationDialog(getActivity(),
@@ -302,12 +324,17 @@ public class ItemlistFragment extends ListFragment {
}
contextMenu = menu;
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, queuedItemsIds);
+ lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, queuedItemsIds,
+ favoritedItemsId);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
+ if(menuInfo == null) {
+ menuInfo = lastMenuInfo;
+ }
// because of addHeaderView(), positions are increased by 1!
FeedItem selectedItem = itemAccess.getItem(menuInfo.position-1);
@@ -337,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());
@@ -349,21 +375,58 @@ 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());
}
}
public void onEvent(QueueEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
- startItemLoader();
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
+ loadItems();
+ }
+
+ public void onEvent(FavoritesEvent event) {
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
+ loadItems();
}
public void onEvent(FeedEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
if(event.feedId == feedID) {
- startItemLoader();
+ loadItems();
+ }
+ }
+
+ public void onEventMainThread(FeedItemEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ boolean queueChanged = false;
+ if(feed == null || feed.getItems() == null || adapter == null) {
+ return;
+ }
+ for(FeedItem item : event.items) {
+ int pos = FeedItemUtil.indexOfItemWithId(feed.getItems(), item.getId());
+ if(pos >= 0) {
+ loadItems();
+ return;
+ }
+ }
+ }
+
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if (isUpdatingFeed != event.update.feedIds.length > 0) {
+ updateProgressBarVisibility();
+ }
+ if(adapter != null && update.mediaIds.length > 0) {
+ adapter.notifyDataSetChanged();
}
}
@@ -373,12 +436,8 @@ public class ItemlistFragment extends ListFragment {
public void update(EventDistributor eventDistributor, Integer arg) {
if ((EVENTS & arg) != 0) {
Log.d(TAG, "Received contentUpdate Intent. arg " + arg);
- if ((EventDistributor.DOWNLOAD_QUEUED & arg) != 0) {
- updateProgressBarVisibility();
- } else {
- startItemLoader();
- updateProgressBarVisibility();
- }
+ loadItems();
+ updateProgressBarVisibility();
}
}
};
@@ -396,15 +455,16 @@ public class ItemlistFragment extends ListFragment {
private boolean insideOnFragmentLoaded = false;
private void onFragmentLoaded() {
+ if(!isVisible()) {
+ return;
+ }
insideOnFragmentLoaded = true;
if (adapter == null) {
setListAdapter(null);
setupHeaderView();
setupFooterView();
- adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false);
+ adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(getActivity()), false, true);
setListAdapter(adapter);
- downloadObserver = new DownloadObserver(getActivity(), new Handler(), downloadObserverCallback);
- downloadObserver.onResume();
}
refreshHeaderView();
setListShown(true);
@@ -421,6 +481,10 @@ public class ItemlistFragment extends ListFragment {
}
private void refreshHeaderView() {
+ if (getListView() == null || feed == null) {
+ Log.e(TAG, "Unable to setup listview: recyclerView = null or feed = null");
+ return;
+ }
if(feed.hasLastUpdateFailed()) {
txtvFailure.setVisibility(View.VISIBLE);
} else {
@@ -440,32 +504,13 @@ public class ItemlistFragment extends ListFragment {
txtvInformation.setVisibility(View.GONE);
}
} else {
-
txtvInformation.setVisibility(View.GONE);
}
}
-
- private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
- @Override
- public void onContentChanged() {
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
- }
-
- @Override
- public void onDownloadDataAvailable(List<Downloader> downloaderList) {
- ItemlistFragment.this.downloaderList = downloaderList;
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
- }
- };
-
private void setupHeaderView() {
if (getListView() == null || feed == null) {
- Log.e(TAG, "Unable to setup listview: listView = null or feed = null");
+ Log.e(TAG, "Unable to setup listview: recyclerView = null or feed = null");
return;
}
ListView lv = getListView();
@@ -485,28 +530,34 @@ public class ItemlistFragment extends ListFragment {
txtvTitle.setText(feed.getTitle());
txtvAuthor.setText(feed.getAuthor());
- Picasso.with(getActivity())
+
+ // https://github.com/bumptech/glide/issues/529
+ imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
+
+ Glide.with(getActivity())
.load(feed.getImageUri())
.placeholder(R.color.image_readability_tint)
.error(R.color.image_readability_tint)
- .transform(PicassoProvider.blurTransformation)
- .resize(PicassoProvider.BLUR_IMAGE_SIZE, PicassoProvider.BLUR_IMAGE_SIZE)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .transform(new FastBlurTransformation(getActivity()))
+ .dontAnimate()
.into(imgvBackground);
- Picasso.with(getActivity())
+ Glide.with(getActivity())
.load(feed.getImageUri())
- .fit()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
.into(imgvCover);
- butShowInfo.setOnClickListener(new View.OnClickListener() {
- @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);
}
});
}
@@ -514,7 +565,7 @@ public class ItemlistFragment extends ListFragment {
private void setupFooterView() {
if (getListView() == null || feed == null) {
- Log.e(TAG, "Unable to setup listview: listView = null or feed = null");
+ Log.e(TAG, "Unable to setup listview: recyclerView = null or feed = null");
return;
}
if (feed.isPaged() && feed.getNextPageLink() != null) {
@@ -524,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());
}
}
});
@@ -544,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
@@ -558,11 +610,6 @@ public class ItemlistFragment extends ListFragment {
}
@Override
- public boolean isNew(FeedItem item) {
- return (newItemsIds != null) && newItemsIds.contains(item.getId());
- }
-
- @Override
public int getItemDownloadProgressPercent(FeedItem item) {
if (downloaderList != null) {
for (Downloader downloader : downloaderList) {
@@ -576,53 +623,38 @@ public class ItemlistFragment extends ListFragment {
}
};
- private ItemLoader itemLoader;
- private void startItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
- }
- itemLoader = new ItemLoader();
- itemLoader.execute(feedID);
+ private void loadItems() {
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
+ subscription = Observable.fromCallable(() -> loadData())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ if (result != null) {
+ feed = (Feed) result[0];
+ queuedItemsIds = (LongList) result[1];
+ favoritedItemsId = (LongList) result[2];
+ itemsLoaded = true;
+ if (viewsCreated) {
+ onFragmentLoaded();
+ }
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
- private void stopItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
+ 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();
+ LongList favoritedItemsId = DBReader.getFavoriteIDList();
+ return new Object[] { feed, queuedItemsIds, favoritedItemsId };
}
- private class ItemLoader extends AsyncTask<Long, Void, Object[]> {
- @Override
- protected Object[] doInBackground(Long... params) {
- long feedID = params[0];
- Context context = getActivity();
- if (context != null) {
- Feed feed = DBReader.getFeed(context, feedID);
- if(feed.getItemFilter() != null) {
- FeedItemFilter filter = feed.getItemFilter();
- feed.setItems(filter.filter(context, feed.getItems()));
- }
- LongList queuedItemsIds = DBReader.getQueueIDList(context);
- LongList newItemsIds = DBReader.getNewItemIds(context);
- return new Object[] { feed, queuedItemsIds, newItemsIds };
- } else {
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(Object[] res) {
- super.onPostExecute(res);
- if (res != null) {
- feed = (Feed) res[0];
- queuedItemsIds = (LongList) res[1];
- newItemsIds = res[2] == null ? null : (LongList) res[2];
- itemsLoaded = true;
- if (viewsCreated) {
- onFragmentLoaded();
- }
- }
- }
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
index 16789d694..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,55 +1,75 @@
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;
import java.io.IOException;
+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.DefaultOnlineFeedViewActivity;
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.*;
+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.
@@ -58,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();
}
/**
@@ -77,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(),
- DefaultOnlineFeedViewActivity.class);
-
- //Tell the OnlineFeedViewActivity where to go
- String url = searchResults.get(position).feedUrl;
- intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, url);
-
- intent.putExtra(DefaultOnlineFeedViewActivity.ARG_TITLE, "iTunes");
+ 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();
- //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() {
+ return root;
+ }
+
+ @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
@@ -125,78 +207,153 @@ public class ItunesSearchFragment extends Fragment {
return false;
}
});
+ MenuItemCompat.setOnActionExpandListener(searchItem, new MenuItemCompat.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ return true;
+ }
- 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){
- this.query = query;
- }
-
- //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/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
index 4bce3c7ba..b996e1cb3 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -1,24 +1,25 @@
package de.danoeh.antennapod.fragment;
-import android.content.Context;
import android.os.Bundle;
+import android.os.Handler;
+import android.support.design.widget.Snackbar;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import com.mobeta.android.dslv.DragSortListView;
+import java.util.List;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
+import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.QueueEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.util.gui.FeedItemUndoToken;
-import de.danoeh.antennapod.core.util.gui.UndoBarController;
-import de.greenrobot.event.EventBus;
/**
@@ -32,78 +33,101 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
private static final String PREF_NAME = "PrefNewEpisodesFragment";
- private UndoBarController undoBarController;
-
- public NewEpisodesFragment() {
- super(true, PREF_NAME);
- }
-
- public void onEvent(QueueEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
- startItemLoader();
- }
-
@Override
- public void onStart() {
- super.onStart();
- EventBus.getDefault().register(this);
- }
+ protected boolean showOnlyNewEpisodes() { return true; }
@Override
- public void onStop() {
- super.onStop();
- EventBus.getDefault().unregister(this);
+ protected String getPrefName() { return PREF_NAME; }
+
+ public void onEvent(QueueEvent event) {
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
+ loadItems();
}
@Override
protected void resetViewState() {
super.resetViewState();
- undoBarController = null;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = super.onCreateViewHelper(inflater, container, savedInstanceState,
- R.layout.new_episodes_fragment, R.string.new_episodes_label);
+ R.layout.all_episodes_fragment);
- listView.setRemoveListener(new DragSortListView.RemoveListener() {
+ ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
- public void remove(int which) {
- Log.d(TAG, "remove(" + which + ")");
- stopItemLoader();
- FeedItem item = (FeedItem) listView.getAdapter().getItem(which);
- DBWriter.markItemRead(getActivity(), item.getId(), true);
- undoBarController.showUndoBar(false,
- getString(R.string.marked_as_read_label), new FeedItemUndoToken(item,
- which)
- );
+ public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
+ return false;
}
- });
- undoBarController = new UndoBarController<FeedItemUndoToken>(root.findViewById(R.id.undobar), new UndoBarController.UndoListener<FeedItemUndoToken>() {
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
+ AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder;
+
+ Log.d(TAG, "remove(" + holder.getItemId() + ")");
+ if (subscription != null) {
+ subscription.unsubscribe();
+ }
+ FeedItem item = holder.getFeedItem();
+ // we're marking it as unplayed since the user didn't actually play it
+ // but they don't want it considered 'NEW' anymore
+ DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
- private final Context context = getActivity();
+ final Handler h = new Handler(getActivity().getMainLooper());
+ final Runnable r = () -> {
+ FeedMedia media = item.getMedia();
+ if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
+ DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
+ }
+ };
+
+ Snackbar snackbar = Snackbar.make(root, getString(R.string.marked_as_read_label),
+ Snackbar.LENGTH_LONG);
+ snackbar.setAction(getString(R.string.undo), v -> {
+ DBWriter.markItemPlayed(FeedItem.NEW, item.getId());
+ // don't forget to cancel the thing that's going to remove the media
+ h.removeCallbacks(r);
+ });
+ snackbar.show();
+ h.postDelayed(r, (int)Math.ceil(snackbar.getDuration() * 1.05f));
+ }
@Override
- public void onUndo(FeedItemUndoToken token) {
- if (token != null) {
- long itemId = token.getFeedItemId();
- DBWriter.markItemRead(context, itemId, false);
+ public void onSelectedChanged(RecyclerView.ViewHolder viewHolder,
+ int actionState) {
+ // We only want the active item
+ if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
+ if (viewHolder instanceof AllEpisodesRecycleAdapter.ItemTouchHelperViewHolder) {
+ AllEpisodesRecycleAdapter.ItemTouchHelperViewHolder itemViewHolder =
+ (AllEpisodesRecycleAdapter.ItemTouchHelperViewHolder) viewHolder;
+ itemViewHolder.onItemSelected();
+ }
}
+
+ super.onSelectedChanged(viewHolder, actionState);
}
@Override
- public void onHide(FeedItemUndoToken token) {
- if (token != null && context != null) {
- long itemId = token.getFeedItemId();
- FeedItem item = DBReader.getFeedItem(context, itemId);
- FeedMedia media = item.getMedia();
- if(media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
- DBWriter.deleteFeedMediaOfItem(context, media.getId());
- }
+ public void clearView(RecyclerView recyclerView,
+ RecyclerView.ViewHolder viewHolder) {
+ super.clearView(recyclerView, viewHolder);
+
+ if (viewHolder instanceof AllEpisodesRecycleAdapter.ItemTouchHelperViewHolder) {
+ AllEpisodesRecycleAdapter.ItemTouchHelperViewHolder itemViewHolder =
+ (AllEpisodesRecycleAdapter.ItemTouchHelperViewHolder) viewHolder;
+ itemViewHolder.onItemClear();
}
}
- });
+ };
+
+ ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
+ itemTouchHelper.attachToRecyclerView(recyclerView);
+
return root;
}
+ @Override
+ protected List<FeedItem> loadData() {
+ return DBReader.getNewItemsList();
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
index 9099829d8..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,11 +1,8 @@
package de.danoeh.antennapod.fragment;
-import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Handler;
import android.support.v4.app.ListFragment;
import android.support.v4.util.Pair;
import android.support.v4.view.MenuItemCompat;
@@ -17,22 +14,26 @@ 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;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
-import de.danoeh.antennapod.core.asynctask.DownloadObserver;
+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.feed.QueueEvent;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.LongList;
import de.greenrobot.event.EventBus;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
public class PlaybackHistoryFragment extends ListFragment {
@@ -48,11 +49,18 @@ public class PlaybackHistoryFragment extends ListFragment {
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
- private AtomicReference<Activity> activity = new AtomicReference<Activity>();
-
- private DownloadObserver downloadObserver;
private List<Downloader> downloaderList;
+ private Subscription subscription;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ if (viewsCreated && itemsLoaded) {
+ onFragmentLoaded();
+ }
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -61,43 +69,55 @@ public class PlaybackHistoryFragment extends 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);
+
+ viewsCreated = true;
+ if (itemsLoaded) {
+ onFragmentLoaded();
+ }
+ }
+
+
+ @Override
public void onResume() {
super.onResume();
- startItemLoader();
+ EventBus.getDefault().registerSticky(this);
+ loadItems();
}
@Override
public void onStart() {
super.onStart();
EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ EventBus.getDefault().unregister(this);
}
@Override
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
- EventBus.getDefault().unregister(this);
- stopItemLoader();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
}
@Override
public void onDetach() {
super.onDetach();
- stopItemLoader();
- activity.set(null);
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- this.activity.set(activity);
- if (downloadObserver != null) {
- downloadObserver.setActivity(activity);
- downloadObserver.onResume();
- }
- if (viewsCreated && itemsLoaded) {
- onFragmentLoaded();
+ if(subscription != null) {
+ subscription.unsubscribe();
}
}
@@ -106,24 +126,14 @@ public class PlaybackHistoryFragment extends ListFragment {
super.onDestroyView();
adapter = null;
viewsCreated = false;
- if (downloadObserver != null) {
- downloadObserver.onPause();
- }
}
- @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);
-
- viewsCreated = true;
- if (itemsLoaded) {
- onFragmentLoaded();
+ public void onEvent(DownloadEvent event) {
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if (adapter != null) {
+ adapter.notifyDataSetChanged();
}
}
@@ -138,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);
@@ -164,7 +177,7 @@ public class PlaybackHistoryFragment extends ListFragment {
if (!super.onOptionsItemSelected(item)) {
switch (item.getItemId()) {
case R.id.clear_history_item:
- DBWriter.clearPlaybackHistory(getActivity());
+ DBWriter.clearPlaybackHistory();
return true;
default:
return false;
@@ -176,7 +189,7 @@ public class PlaybackHistoryFragment extends ListFragment {
public void onEvent(QueueEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
- startItemLoader();
+ loadItems();
}
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@@ -184,7 +197,7 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
- startItemLoader();
+ loadItems();
getActivity().supportInvalidateOptionsMenu();
}
}
@@ -192,33 +205,18 @@ public class PlaybackHistoryFragment extends ListFragment {
private void onFragmentLoaded() {
if (adapter == null) {
- adapter = new FeedItemlistAdapter(getActivity(), itemAccess, new DefaultActionButtonCallback(activity.get()), true);
+ // played items shoudln't be transparent for this fragment since, *all* items
+ // in this fragment will, by definition, be played. So it serves no purpose and can make
+ // it harder to read.
+ adapter = new FeedItemlistAdapter(getActivity(), itemAccess,
+ new DefaultActionButtonCallback(getActivity()), true, false);
setListAdapter(adapter);
- downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback);
- downloadObserver.onResume();
}
setListShown(true);
adapter.notifyDataSetChanged();
getActivity().supportInvalidateOptionsMenu();
}
- private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
- @Override
- public void onContentChanged() {
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
- }
-
- @Override
- public void onDownloadDataAvailable(List<Downloader> downloaderList) {
- PlaybackHistoryFragment.this.downloaderList = downloaderList;
- if (adapter != null) {
- adapter.notifyDataSetChanged();
- }
- }
- };
-
private FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() {
@Override
public boolean isInQueue(FeedItem item) {
@@ -226,11 +224,6 @@ public class PlaybackHistoryFragment extends ListFragment {
}
@Override
- public boolean isNew(FeedItem item) {
- return false;
- }
-
- @Override
public int getItemDownloadProgressPercent(FeedItem item) {
if (downloaderList != null) {
for (Downloader downloader : downloaderList) {
@@ -250,52 +243,40 @@ 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;
+ }
}
};
- private ItemLoader itemLoader;
-
- private void startItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
+ private void loadItems() {
+ if(subscription != null) {
+ subscription.unsubscribe();
}
- itemLoader = new ItemLoader();
- itemLoader.execute();
+ subscription = Observable.fromCallable(() -> loadData())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ if (result != null) {
+ playbackHistory = result.first;
+ queue = result.second;
+ itemsLoaded = true;
+ if (viewsCreated) {
+ onFragmentLoaded();
+ }
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
- private void stopItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
- }
+ private Pair<List<FeedItem>, LongList> loadData() {
+ List<FeedItem> history = DBReader.getPlaybackHistory();
+ LongList queue = DBReader.getQueueIDList();
+ DBReader.loadAdditionalFeedItemListData(history);
+ return Pair.create(history, queue);
}
- private class ItemLoader extends AsyncTask<Void, Void, Pair<List<FeedItem>,LongList>> {
-
- @Override
- protected Pair<List<FeedItem>,LongList> doInBackground(Void... params) {
- Context context = activity.get();
- if (context != null) {
- List<FeedItem> history = DBReader.getPlaybackHistory(context);
- LongList queue = DBReader.getQueueIDList(context);
- DBReader.loadFeedDataOfFeedItemlist(context, history);
- return Pair.create(history, queue);
- } else {
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(Pair<List<FeedItem>,LongList> res) {
- super.onPostExecute(res);
- if (res != null) {
- playbackHistory = res.first;
- queue = res.second;
- itemsLoaded = true;
- if (viewsCreated) {
- onFragmentLoaded();
- }
- }
- }
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
index d82c7b8f7..b3f6c3534 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -1,44 +1,45 @@
package de.danoeh.antennapod.fragment;
-import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
-import android.os.AsyncTask;
import android.os.Bundle;
-import android.os.Handler;
+import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
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.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.widget.AdapterView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-import com.mobeta.android.dslv.DragSortListView;
+import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import java.util.List;
-import java.util.concurrent.atomic.AtomicReference;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
-import de.danoeh.antennapod.adapter.QueueListAdapter;
-import de.danoeh.antennapod.core.asynctask.DownloadObserver;
+import de.danoeh.antennapod.adapter.QueueRecyclerAdapter;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
+import de.danoeh.antennapod.core.event.FeedItemEvent;
+import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.QueueEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
@@ -47,13 +48,17 @@ import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.QueueSorter;
-import de.danoeh.antennapod.core.util.gui.FeedItemUndoToken;
-import de.danoeh.antennapod.core.util.gui.UndoBarController;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.greenrobot.event.EventBus;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Shows all items in the queue
@@ -63,37 +68,27 @@ public class QueueFragment extends Fragment {
public static final String TAG = "QueueFragment";
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.DOWNLOAD_QUEUED |
+ EventDistributor.UNREAD_ITEMS_UPDATE | // sent when playback position is reset
EventDistributor.PLAYER_STATUS_UPDATE;
- private DragSortListView listView;
- private QueueListAdapter listAdapter;
+ private TextView infoBar;
+ private RecyclerView recyclerView;
+ private QueueRecyclerAdapter recyclerAdapter;
private TextView txtvEmpty;
private ProgressBar progLoading;
- private ContextMenu contextMenu;
-
- private UndoBarController<FeedItemUndoToken> undoBarController;
-
private List<FeedItem> queue;
private List<Downloader> downloaderList;
- private boolean itemsLoaded = false;
- private boolean viewsCreated = false;
private boolean isUpdatingFeeds = false;
private static final String PREFS = "QueueFragment";
- private static final String PREF_KEY_LIST_TOP = "list_top";
- private static final String PREF_KEY_LIST_SELECTION = "list_selection";
-
- private AtomicReference<Activity> activity = new AtomicReference<Activity>();
+ private static final String PREF_SCROLL_POSITION = "scroll_position";
+ private static final String PREF_SCROLL_OFFSET = "scroll_offset";
- private DownloadObserver downloadObserver = null;
-
- /**
- * Download observer updates won't result in an upate of the list adapter if this is true.
- */
- private boolean blockDownloadObserverUpdate = false;
+ private Subscription subscription;
+ private LinearLayoutManager layoutManager;
+ private ItemTouchHelper itemTouchHelper;
@Override
@@ -104,92 +99,129 @@ public class QueueFragment extends Fragment {
}
@Override
- public void onResume() {
- super.onResume();
- startItemLoader();
+ public void onStart() {
+ super.onStart();
+ if (queue != null) {
+ onFragmentLoaded(true);
+ }
}
@Override
- public void onStart() {
- super.onStart();
+ public void onResume() {
+ super.onResume();
+ recyclerView.setAdapter(recyclerAdapter);
+ loadItems(true);
EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
- this.activity.set((MainActivity) getActivity());
- if (downloadObserver != null) {
- downloadObserver.setActivity(getActivity());
- downloadObserver.onResume();
- }
- if (viewsCreated && itemsLoaded) {
- onFragmentLoaded();
- }
+ EventBus.getDefault().registerSticky(this);
}
@Override
public void onPause() {
super.onPause();
saveScrollPosition();
- }
-
- @Override
- public void onStop() {
- super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
- stopItemLoader();
- if(undoBarController.isShowing()) {
- undoBarController.close();
+ if(subscription != null) {
+ subscription.unsubscribe();
}
}
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- this.activity.set((MainActivity) activity);
+ public void onEventMainThread(QueueEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ if(queue == null || recyclerAdapter == null) {
+ return;
+ }
+ switch(event.action) {
+ case ADDED:
+ queue.add(event.position, event.item);
+ recyclerAdapter.notifyItemInserted(event.position);
+ break;
+ case SET_QUEUE:
+ queue = event.items;
+ recyclerAdapter.notifyDataSetChanged();
+ break;
+ case REMOVED:
+ case IRREVERSIBLE_REMOVED:
+ int position = FeedItemUtil.indexOfItemWithId(queue, event.item.getId());
+ queue.remove(position);
+ recyclerAdapter.notifyItemRemoved(position);
+ break;
+ case CLEARED:
+ queue.clear();
+ recyclerAdapter.notifyDataSetChanged();
+ break;
+ case SORTED:
+ queue = event.items;
+ recyclerAdapter.notifyDataSetChanged();
+ break;
+ case MOVED:
+ return;
+ }
+ saveScrollPosition();
+ onFragmentLoaded(false);
}
- public void onEventMainThread(QueueEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
- if(event.action == QueueEvent.Action.REMOVED) {
- undoBarController.showUndoBar(false, getString(R.string.removed_from_queue),
- new FeedItemUndoToken(event.item, event.position));
+ public void onEventMainThread(FeedItemEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ if(queue == null || recyclerAdapter == null) {
+ return;
+ }
+ for(int i=0, size = event.items.size(); i < size; i++) {
+ FeedItem item = event.items.get(i);
+ int pos = FeedItemUtil.indexOfItemWithId(queue, item.getId());
+ if(pos >= 0) {
+ queue.remove(pos);
+ queue.add(pos, item);
+ recyclerAdapter.notifyItemChanged(pos);
+ }
+ }
+ }
+
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ 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);
+ }
+ }
}
- startItemLoader();
}
private void saveScrollPosition() {
+ int firstItem = layoutManager.findFirstVisibleItemPosition();
+ View firstItemView = layoutManager.findViewByPosition(firstItem);
+ float topOffset;
+ if(firstItemView == null) {
+ topOffset = 0;
+ } else {
+ topOffset = firstItemView.getTop();
+ }
+
SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
- View v = listView.getChildAt(0);
- int top = (v == null) ? 0 : (v.getTop() - listView.getPaddingTop());
- editor.putInt(PREF_KEY_LIST_SELECTION, listView.getFirstVisiblePosition());
- editor.putInt(PREF_KEY_LIST_TOP, top);
+ editor.putInt(PREF_SCROLL_POSITION, firstItem);
+ editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
editor.commit();
}
private void restoreScrollPosition() {
SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
- int listSelection = prefs.getInt(PREF_KEY_LIST_SELECTION, 0);
- int top = prefs.getInt(PREF_KEY_LIST_TOP, 0);
- if(listSelection > 0 || top > 0) {
- listView.setSelectionFromTop(listSelection, top);
- // restore once, then forget
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(PREF_KEY_LIST_SELECTION, 0);
- editor.putInt(PREF_KEY_LIST_TOP, 0);
- editor.commit();
+ int position = prefs.getInt(PREF_SCROLL_POSITION, 0);
+ float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
+ if (position > 0 || offset > 0) {
+ layoutManager.scrollToPositionWithOffset(position, (int) offset);
}
}
private void resetViewState() {
- unregisterForContextMenu(listView);
- listAdapter = null;
- activity.set(null);
- undoBarController = null;
- viewsCreated = false;
- blockDownloadObserverUpdate = false;
- if (downloadObserver != null) {
- downloadObserver.onPause();
- }
+ recyclerAdapter = null;
}
@Override
@@ -198,17 +230,17 @@ public class QueueFragment extends Fragment {
resetViewState();
}
- private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = new MenuItemUtils.UpdateRefreshMenuItemChecker() {
- @Override
- public boolean isRefreshing() {
- return DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
- }
+ private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = () -> {
+ return DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
};
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if(!isAdded()) {
+ return;
+ }
super.onCreateOptionsMenu(menu, inflater);
- if (itemsLoaded) {
+ if (queue != null) {
inflater.inflate(R.menu.queue, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
@@ -240,15 +272,17 @@ public class QueueFragment extends Fragment {
if (!super.onOptionsItemSelected(item)) {
switch (item.getItemId()) {
case R.id.queue_lock:
- boolean locked = !UserPreferences.isQueueLocked();
- if(locked) {
- listView.setDragEnabled(false);
+ boolean newLockState = !UserPreferences.isQueueLocked();
+ UserPreferences.setQueueLocked(newLockState);
+ getActivity().supportInvalidateOptionsMenu();
+ recyclerAdapter.setLocked(newLockState);
+ if (newLockState) {
+ Snackbar.make(getActivity().findViewById(R.id.content), R.string
+ .queue_locked, Snackbar.LENGTH_SHORT).show();
} else {
- listView.setDragEnabled(true);
+ Snackbar.make(getActivity().findViewById(R.id.content), R.string
+ .queue_unlocked, Snackbar.LENGTH_SHORT).show();
}
- UserPreferences.setQueueLocked(locked);
- getActivity().supportInvalidateOptionsMenu();
- listAdapter.setLocked(locked);
return true;
case R.id.refresh_item:
List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
@@ -266,7 +300,7 @@ public class QueueFragment extends Fragment {
public void onConfirmButtonPressed(
DialogInterface dialog) {
dialog.dismiss();
- DBWriter.clearQueue(getActivity());
+ DBWriter.clearQueue();
}
};
conDialog.createNewDialog().show();
@@ -295,59 +329,42 @@ public class QueueFragment extends Fragment {
} else {
return true;
}
-
}
- private final FeedItemMenuHandler.MenuInterface contextMenuInterface = new FeedItemMenuHandler.MenuInterface() {
- @Override
- public void setItemVisibility(int id, boolean visible) {
- if(contextMenu == null) {
- return;
- }
- MenuItem item = contextMenu.findItem(id);
- if (item != null) {
- item.setVisible(visible);
- }
- }
- };
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
- FeedItem item = itemAccess.getItem(adapterInfo.position);
-
- MenuInflater inflater = getActivity().getMenuInflater();
- inflater.inflate(R.menu.queue_context, menu);
-
- if (item != null) {
- menu.setHeaderTitle(item.getTitle());
- }
-
- contextMenu = menu;
- LongList queueIds = new LongList(queue.size());
- for(FeedItem queueItem : queue) {
- queueIds.add(queueItem.getId());
- }
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, queueIds);
- }
@Override
public boolean onContextItemSelected(MenuItem item) {
- AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
- FeedItem selectedItem = itemAccess.getItem(menuInfo.position);
-
+ Log.d(TAG, "onContextItemSelected() called with: " + "item = [" + item + "]");
+ if(!isVisible()) {
+ return false;
+ }
+ FeedItem selectedItem = recyclerAdapter.getSelectedItem();
if (selectedItem == null) {
- Log.i(TAG, "Selected item at position " + menuInfo.position + " was null, ignoring selection");
+ Log.i(TAG, "Selected item was null, ignoring selection");
return super.onContextItemSelected(item);
}
- try {
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
- return true;
+ switch(item.getItemId()) {
+ case R.id.move_to_top_item:
+ int position = FeedItemUtil.indexOfItemWithId(queue, selectedItem.getId());
+ queue.add(0, queue.remove(position));
+ recyclerAdapter.notifyItemMoved(position, 0);
+ DBWriter.moveQueueItemToTop(selectedItem.getId(), true);
+ return true;
+ case R.id.move_to_bottom_item:
+ position = FeedItemUtil.indexOfItemWithId(queue, selectedItem.getId());
+ queue.add(queue.size()-1, queue.remove(position));
+ recyclerAdapter.notifyItemMoved(position, queue.size()-1);
+ DBWriter.moveQueueItemToBottom(selectedItem.getId(), true);
+ return true;
+ default:
+ try {
+ return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
+ return true;
+ }
}
}
@@ -358,140 +375,153 @@ public class QueueFragment extends Fragment {
((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.queue_label);
View root = inflater.inflate(R.layout.queue_fragment, container, false);
- listView = (DragSortListView) root.findViewById(android.R.id.list);
- txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
- progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
- listView.setEmptyView(txtvEmpty);
-
- if(UserPreferences.isQueueLocked()) {
- listView.setDragEnabled(false);
- } else {
- listView.setDragEnabled(true);
+ 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());
+ recyclerView.setHasFixedSize(true);
+ registerForContextMenu(recyclerView);
- listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- FeedItem item = (FeedItem) listAdapter.getItem(position - listView.getHeaderViewsCount());
- if (item != null) {
- ((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(item.getId()));
- }
- }
- });
+ itemTouchHelper = new ItemTouchHelper(
+ new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) {
- listView.setDragSortListener(new DragSortListView.DragSortListener() {
- @Override
- public void drag(int from, int to) {
- Log.d(TAG, "drag");
- blockDownloadObserverUpdate = true;
- }
+ @Override
+ public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
+ int from = viewHolder.getAdapterPosition();
+ int to = target.getAdapterPosition();
+ Log.d(TAG, "move(" + from + ", " + to + ")");
+ queue.add(to, queue.remove(from));
+ recyclerAdapter.notifyItemMoved(from, to);
+ DBWriter.moveQueueItem(from, to, true);
+ return true;
+ }
- @Override
- public void drop(int from, int to) {
- Log.d(TAG, "drop");
- blockDownloadObserverUpdate = false;
- stopItemLoader();
- final FeedItem item = queue.remove(from);
- queue.add(to, item);
- listAdapter.notifyDataSetChanged();
- DBWriter.moveQueueItem(getActivity(), from, to, true);
- }
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
+ final int position = viewHolder.getAdapterPosition();
+ Log.d(TAG, "remove(" + position + ")");
+ final FeedItem item = queue.get(position);
+ final boolean isRead = item.isPlayed();
+ DBWriter.markItemPlayed(FeedItem.PLAYED, item.getId());
+ DBWriter.removeQueueItem(getActivity(), item, true);
+ Snackbar snackbar = Snackbar.make(root, getString(R.string.marked_as_read_label), Snackbar.LENGTH_LONG);
+ snackbar.setAction(getString(R.string.undo), v -> {
+ DBWriter.addQueueItemAt(getActivity(), item.getId(), position, false);
+ if(false == isRead) {
+ DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
+ }
+ });
+ snackbar.show();
+ }
- @Override
- public void remove(int which) {
- Log.d(TAG, "remove(" + which + ")");
- stopItemLoader();
- FeedItem item = (FeedItem) listView.getAdapter().getItem(which);
- DBWriter.removeQueueItem(getActivity(), item, true);
- }
- });
+ @Override
+ public boolean isLongPressDragEnabled() {
+ return false == UserPreferences.isQueueLocked();
+ }
- undoBarController = new UndoBarController<FeedItemUndoToken>(root.findViewById(R.id.undobar),
- new UndoBarController.UndoListener<FeedItemUndoToken>() {
+ @Override
+ public boolean isItemViewSwipeEnabled() {
+ return false == UserPreferences.isQueueLocked();
+ }
- private final Context context = getActivity();
+ @Override
+ public void onSelectedChanged(RecyclerView.ViewHolder viewHolder,
+ int actionState) {
+ // We only want the active item
+ if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
+ if (viewHolder instanceof QueueRecyclerAdapter.ItemTouchHelperViewHolder) {
+ QueueRecyclerAdapter.ItemTouchHelperViewHolder itemViewHolder =
+ (QueueRecyclerAdapter.ItemTouchHelperViewHolder) viewHolder;
+ itemViewHolder.onItemSelected();
+ }
+ }
- @Override
- public void onUndo(FeedItemUndoToken token) {
- if (token != null) {
- long itemId = token.getFeedItemId();
- int position = token.getPosition();
- DBWriter.addQueueItemAt(context, itemId, position, false);
+ super.onSelectedChanged(viewHolder, actionState);
}
- }
-
- @Override
- public void onHide(FeedItemUndoToken token) {
- if (token != null && context != null) {
- long itemId = token.getFeedItemId();
- FeedItem item = DBReader.getFeedItem(context, itemId);
- FeedMedia media = item.getMedia();
- if(media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
- DBWriter.deleteFeedMediaOfItem(context, media.getId());
+ @Override
+ public void clearView(RecyclerView recyclerView,
+ RecyclerView.ViewHolder viewHolder) {
+ super.clearView(recyclerView, viewHolder);
+
+ if (viewHolder instanceof QueueRecyclerAdapter.ItemTouchHelperViewHolder) {
+ QueueRecyclerAdapter.ItemTouchHelperViewHolder itemViewHolder =
+ (QueueRecyclerAdapter.ItemTouchHelperViewHolder) viewHolder;
+ itemViewHolder.onItemClear();
}
}
}
+ );
+ itemTouchHelper.attachToRecyclerView(recyclerView);
- });
-
- registerForContextMenu(listView);
-
- if (!itemsLoaded) {
- progLoading.setVisibility(View.VISIBLE);
- txtvEmpty.setVisibility(View.GONE);
- }
-
- viewsCreated = true;
-
- if (itemsLoaded && activity.get() != null) {
- onFragmentLoaded();
- }
+ txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
+ txtvEmpty.setVisibility(View.GONE);
+ progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
+ progLoading.setVisibility(View.VISIBLE);
return root;
}
- private void onFragmentLoaded() {
- if (listAdapter == null) {
- listAdapter = new QueueListAdapter(activity.get(), itemAccess, new DefaultActionButtonCallback(activity.get()));
- listView.setAdapter(listAdapter);
- downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback);
- downloadObserver.onResume();
+ private void onFragmentLoaded(final boolean restoreScrollPosition) {
+ if (recyclerAdapter == null) {
+ MainActivity activity = (MainActivity) getActivity();
+ recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess,
+ new DefaultActionButtonCallback(activity), itemTouchHelper);
+ recyclerView.setAdapter(recyclerAdapter);
+ }
+ if(queue == null || queue.size() == 0) {
+ recyclerView.setVisibility(View.GONE);
+ txtvEmpty.setVisibility(View.VISIBLE);
+ } else {
+ txtvEmpty.setVisibility(View.GONE);
+ recyclerView.setVisibility(View.VISIBLE);
}
- listAdapter.notifyDataSetChanged();
- restoreScrollPosition();
+ if (restoreScrollPosition) {
+ restoreScrollPosition();
+ }
// we need to refresh the options menu because it sometimes
// needs data that may have just been loaded.
getActivity().supportInvalidateOptionsMenu();
- }
- private DownloadObserver.Callback downloadObserverCallback = new DownloadObserver.Callback() {
- @Override
- public void onContentChanged() {
- if (listAdapter != null && !blockDownloadObserverUpdate) {
- listAdapter.notifyDataSetChanged();
- }
- }
+ refreshInfoBar();
+ }
- @Override
- public void onDownloadDataAvailable(List<Downloader> downloaderList) {
- QueueFragment.this.downloaderList = downloaderList;
- if (listAdapter != null && !blockDownloadObserverUpdate) {
- listAdapter.notifyDataSetChanged();
+ private void refreshInfoBar() {
+ String info = queue.size() + getString(R.string.episodes_suffix);
+ if(queue.size() > 0) {
+ long duration = 0;
+ for(FeedItem item : queue) {
+ if(item.getMedia() != null) {
+ duration += item.getMedia().getDuration();
+ }
}
+ info += " \u2022 ";
+ info += Converter.getDurationStringLocalized(getActivity(), duration);
}
- };
+ infoBar.setText(info);
+ }
- private QueueListAdapter.ItemAccess itemAccess = new QueueListAdapter.ItemAccess() {
+ private QueueRecyclerAdapter.ItemAccess itemAccess = new QueueRecyclerAdapter.ItemAccess() {
@Override
public int getCount() {
- return (itemsLoaded) ? queue.size() : 0;
+ return queue != null ? queue.size() : 0;
}
@Override
public FeedItem getItem(int position) {
- return (itemsLoaded) ? queue.get(position) : null;
+ if (queue != null && 0 <= position && position < queue.size()) {
+ return queue.get(position);
+ }
+ return null;
}
@Override
@@ -533,13 +563,33 @@ public class QueueFragment extends Fragment {
}
return 0;
}
+
+ @Override
+ 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() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
- startItemLoader();
+ Log.d(TAG, "arg: " + arg);
+ loadItems(false);
if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
getActivity().supportInvalidateOptionsMenu();
}
@@ -547,55 +597,31 @@ public class QueueFragment extends Fragment {
}
};
- private ItemLoader itemLoader;
-
- private void startItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
+ private void loadItems(final boolean restoreScrollPosition) {
+ Log.d(TAG, "loadItems()");
+ if(subscription != null) {
+ subscription.unsubscribe();
}
- itemLoader = new ItemLoader();
- itemLoader.execute();
- }
-
- private void stopItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
+ if (queue == null) {
+ recyclerView.setVisibility(View.GONE);
+ txtvEmpty.setVisibility(View.GONE);
+ progLoading.setVisibility(View.VISIBLE);
}
+ subscription = Observable.fromCallable(() -> DBReader.getQueue())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(items -> {
+ if(items != null) {
+ progLoading.setVisibility(View.GONE);
+ queue = items;
+ onFragmentLoaded(restoreScrollPosition);
+ if(recyclerAdapter != null) {
+ recyclerAdapter.notifyDataSetChanged();
+ }
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
- private class ItemLoader extends AsyncTask<Void, Void, List<FeedItem>> {
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- if (viewsCreated && !itemsLoaded) {
- listView.setVisibility(View.GONE);
- txtvEmpty.setVisibility(View.GONE);
- progLoading.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- protected void onPostExecute(List<FeedItem> feedItems) {
- super.onPostExecute(feedItems);
- listView.setVisibility(View.VISIBLE);
- progLoading.setVisibility(View.GONE);
-
- if (feedItems != null) {
- queue = feedItems;
- itemsLoaded = true;
- if (viewsCreated && activity.get() != null) {
- onFragmentLoaded();
- }
- }
- }
-
- @Override
- protected List<FeedItem> doInBackground(Void... params) {
- Context context = activity.get();
- if (context != null) {
- return DBReader.getQueue(context);
- }
- return null;
- }
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
index b1b61f74b..ba526edb3 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
-import android.os.Handler;
import android.support.v4.app.ListFragment;
+import android.util.Log;
import android.view.View;
import android.widget.ListView;
import android.widget.Toast;
@@ -11,7 +11,8 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadlistAdapter;
-import de.danoeh.antennapod.core.asynctask.DownloadObserver;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
@@ -19,24 +20,17 @@ import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.greenrobot.event.EventBus;
/**
* Displays all running downloads and provides actions to cancel them
*/
public class RunningDownloadsFragment extends ListFragment {
- private static final String TAG = "RunningDownloadsFragment";
-
- private DownloadObserver downloadObserver;
- private List<Downloader> downloaderList;
+ private static final String TAG = "RunningDownloadsFrag";
- @Override
- public void onDetach() {
- super.onDetach();
- if (downloadObserver != null) {
- downloadObserver.onPause();
- }
- }
+ private DownloadlistAdapter adapter;
+ private List<Downloader> downloaderList;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
@@ -48,24 +42,39 @@ public class RunningDownloadsFragment extends ListFragment {
final int vertPadding = getResources().getDimensionPixelSize(R.dimen.list_vertical_padding);
lv.setPadding(0, vertPadding, 0, vertPadding);
- final DownloadlistAdapter downloadlistAdapter = new DownloadlistAdapter(getActivity(), itemAccess);
- setListAdapter(downloadlistAdapter);
+ adapter = new DownloadlistAdapter(getActivity(), itemAccess);
+ setListAdapter(adapter);
+ }
- downloadObserver = new DownloadObserver(getActivity(), new Handler(), new DownloadObserver.Callback() {
- @Override
- public void onContentChanged() {
- downloadlistAdapter.notifyDataSetChanged();
- }
+ @Override
+ public void onResume() {
+ super.onResume();
+ EventBus.getDefault().registerSticky(this);
+ }
- @Override
- public void onDownloadDataAvailable(List<Downloader> downloaderList) {
- RunningDownloadsFragment.this.downloaderList = downloaderList;
- downloadlistAdapter.notifyDataSetChanged();
- }
- });
- downloadObserver.onResume();
+ @Override
+ public void onPause() {
+ super.onPause();
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ setListAdapter(null);
+ adapter = null;
+ }
+
+ public void onEvent(DownloadEvent event) {
+ Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if (adapter != null) {
+ adapter.notifyDataSetChanged();
+ }
}
+
private DownloadlistAdapter.ItemAccess itemAccess = new DownloadlistAdapter.ItemAccess() {
@Override
public int getCount() {
@@ -74,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
@@ -84,8 +97,8 @@ public class RunningDownloadsFragment extends ListFragment {
if(downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA &&
UserPreferences.isEnableAutodownload()) {
- FeedMedia media = DBReader.getFeedMedia(getActivity(), downloadRequest.getFeedfileId());
- DBWriter.setFeedItemAutoDownload(getActivity(), media.getItem(), false);
+ FeedMedia media = DBReader.getFeedMedia(downloadRequest.getFeedfileId());
+ DBWriter.setFeedItemAutoDownload(media.getItem(), false);
Toast.makeText(getActivity(), R.string.download_canceled_autodownload_enabled_msg, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getActivity(), R.string.download_canceled_msg, Toast.LENGTH_SHORT).show();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
index fc6225409..dbd18163c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -1,19 +1,18 @@
package de.danoeh.antennapod.fragment;
import android.content.Context;
-import android.os.AsyncTask;
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;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
-import java.util.Collections;
import java.util.List;
import de.danoeh.antennapod.R;
@@ -25,6 +24,10 @@ import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.SearchResult;
import de.danoeh.antennapod.core.storage.FeedSearcher;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Performs a search operation on all feeds or one specific feed and displays the search result.
@@ -41,6 +44,8 @@ public class SearchFragment extends ListFragment {
private boolean viewCreated = false;
private boolean itemsLoaded = false;
+ private Subscription subscription;
+
/**
* Create a new SearchFragment that searches all feeds.
*/
@@ -68,7 +73,7 @@ public class SearchFragment extends ListFragment {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
- startSearchTask();
+ search();
}
@Override
@@ -80,14 +85,18 @@ public class SearchFragment extends ListFragment {
@Override
public void onStop() {
super.onStop();
- stopSearchTask();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
EventDistributor.getInstance().unregister(contentUpdate);
}
@Override
public void onDetach() {
super.onDetach();
- stopSearchTask();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
}
@Override
@@ -107,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();
@@ -120,7 +129,7 @@ public class SearchFragment extends ListFragment {
SearchResult result = (SearchResult) l.getAdapter().getItem(position);
FeedComponent comp = result.getComponent();
if (comp.getClass() == Feed.class) {
- ((MainActivity) getActivity()).loadFeedFragmentById(comp.getId());
+ ((MainActivity) getActivity()).loadFeedFragmentById(comp.getId(), null);
} else {
if (comp.getClass() == FeedItem.class) {
FeedItem item = (FeedItem) comp;
@@ -143,7 +152,7 @@ public class SearchFragment extends ListFragment {
public boolean onQueryTextSubmit(String s) {
getArguments().putString(ARG_QUERY, s);
itemsLoaded = false;
- startSearchTask();
+ search();
return true;
}
@@ -161,7 +170,7 @@ public class SearchFragment extends ListFragment {
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & (EventDistributor.UNREAD_ITEMS_UPDATE
| EventDistributor.DOWNLOAD_HANDLED)) != 0) {
- startSearchTask();
+ search();
}
}
};
@@ -183,57 +192,44 @@ 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;
+ }
}
};
- private SearchTask searchTask;
- private void startSearchTask() {
- if (searchTask != null) {
- searchTask.cancel(true);
+ private void search() {
+ if(subscription != null) {
+ subscription.unsubscribe();
}
- searchTask = new SearchTask();
- searchTask.execute(getArguments());
- }
-
- private void stopSearchTask() {
- if (searchTask != null) {
- searchTask.cancel(true);
+ if (viewCreated && !itemsLoaded) {
+ setListShown(false);
}
+ subscription = Observable.fromCallable(() -> performSearch())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ if (result != null) {
+ itemsLoaded = true;
+ searchResults = result;
+ if (viewCreated) {
+ onFragmentLoaded();
+ }
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
- private class SearchTask extends AsyncTask<Bundle, Void, List<SearchResult>> {
- @Override
- protected List<SearchResult> doInBackground(Bundle... params) {
- String query = params[0].getString(ARG_QUERY);
- long feed = params[0].getLong(ARG_FEED);
- Context context = getActivity();
- if (context != null) {
- return FeedSearcher.performSearch(context, query, feed);
- } else {
- return Collections.emptyList();
- }
- }
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- if (viewCreated && !itemsLoaded) {
- setListShown(false);
- }
- }
-
- @Override
- protected void onPostExecute(List<SearchResult> results) {
- super.onPostExecute(results);
- if (results != null) {
- itemsLoaded = true;
- searchResults = results;
- if (viewCreated) {
- onFragmentLoaded();
- }
- }
- }
+ private List<SearchResult> performSearch() {
+ Bundle args = getArguments();
+ String query = args.getString(ARG_QUERY);
+ long feed = args.getLong(ARG_FEED);
+ Context context = getActivity();
+ return FeedSearcher.performSearch(context, query, feed);
}
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
index 7f37ea680..286212891 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.fragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -15,12 +16,12 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.adapter.SubscriptionsAdapter;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.storage.DBReader;
import de.greenrobot.event.EventBus;
import rx.Observable;
-import rx.functions.Action1;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Fragment for displaying feed subscriptions
@@ -65,7 +66,20 @@ public class SubscriptionFragment extends Fragment {
mSubscriptionAdapter = new SubscriptionsAdapter(getActivity(), mItemAccess);
mSubscriptionGridLayout.setAdapter(mSubscriptionAdapter);
- refreshSubscriptionList();
+
+ Observable.fromCallable(() -> loadData())
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ mDrawerData = result;
+ mSubscriptionList = mDrawerData.feeds;
+ mSubscriptionAdapter.setItemAccess(mItemAccess);
+ mSubscriptionAdapter.notifyDataSetChanged();
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+
+
mSubscriptionGridLayout.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
@@ -79,38 +93,9 @@ public class SubscriptionFragment extends Fragment {
}
- private void refreshSubscriptionList() {
- Observable.just(loadData()).subscribe(new Action1<DBReader.NavDrawerData>() {
- @Override
- public void call(DBReader.NavDrawerData navDrawerData) {
- mDrawerData = navDrawerData;
- mSubscriptionList = mDrawerData.feeds;
- mSubscriptionAdapter.setItemAccess(mItemAccess);
- mSubscriptionAdapter.notifyDataSetChanged();
- }
- });
- }
-
- EventDistributor.EventListener updateListener = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) {
- refreshSubscriptionList();
- }
- }
- };
-
-
- @Override
- public void onStop() {
- super.onStop();
- EventDistributor.getInstance().unregister(updateListener);
- }
-
@Override
public void onResume() {
super.onResume();
- EventDistributor.getInstance().register(updateListener);
}
public class SubscriptionEvent {
@@ -123,6 +108,6 @@ public class SubscriptionFragment extends Fragment {
private DBReader.NavDrawerData loadData() {
- return DBReader.getNavDrawerData(getActivity());
+ return DBReader.getNavDrawerData();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java
index 55d4b940f..aff5069c6 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java
@@ -1,7 +1,10 @@
package de.danoeh.antennapod.fragment.gpodnet;
+import android.content.Context;
+import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
+import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
@@ -17,16 +20,49 @@ import de.danoeh.antennapod.R;
*/
public class GpodnetMainFragment extends Fragment {
+ public static final String TAG = "GpodnetMainFragment";
+
+ private static final String PREF_LAST_TAB_POSITION = "tab_position";
+ private TabLayout tabLayout;
+ private ViewPager viewPager;
+
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(R.layout.pager_fragment, container, false);
- ViewPager pager = (ViewPager) root.findViewById(R.id.pager);
+
+ viewPager = (ViewPager)root.findViewById(R.id.viewpager);
GpodnetPagerAdapter pagerAdapter = new GpodnetPagerAdapter(getChildFragmentManager(), getResources());
- pager.setAdapter(pagerAdapter);
+ viewPager.setAdapter(pagerAdapter);
+
+ // Give the TabLayout the ViewPager
+ tabLayout = (TabLayout) root.findViewById(R.id.sliding_tabs);
+ tabLayout.setupWithViewPager(viewPager);
+
return root;
}
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ // save our tab selection
+ SharedPreferences prefs = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(PREF_LAST_TAB_POSITION, tabLayout.getSelectedTabPosition());
+ editor.apply();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ // restore our last position
+ SharedPreferences prefs = getActivity().getSharedPreferences(TAG, Context.MODE_PRIVATE);
+ int lastPosition = prefs.getInt(PREF_LAST_TAB_POSITION, 0);
+ viewPager.setCurrentItem(lastPosition);
+ }
+
public class GpodnetPagerAdapter extends FragmentPagerAdapter {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
index 6139a4901..204f36956 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
@@ -23,7 +23,6 @@ import android.widget.TextView;
import java.util.List;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.DefaultOnlineFeedViewActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.adapter.gpodnet.PodcastListAdapter;
@@ -62,7 +61,10 @@ public abstract class PodcastListFragment extends Fragment {
@Override
public boolean onQueryTextSubmit(String s) {
sv.clearFocus();
- ((MainActivity) getActivity()).loadChildFragment(SearchListFragment.newInstance(s));
+ MainActivity activity = (MainActivity)getActivity();
+ if (activity != null) {
+ activity.loadChildFragment(SearchListFragment.newInstance(s));
+ }
return true;
}
@@ -101,9 +103,9 @@ public abstract class PodcastListFragment extends Fragment {
protected void onPodcastSelected(GpodnetPodcast selection) {
Log.d(TAG, "Selected podcast: " + selection.toString());
- Intent intent = new Intent(getActivity(), DefaultOnlineFeedViewActivity.class);
+ Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, selection.getUrl());
- intent.putExtra(DefaultOnlineFeedViewActivity.ARG_TITLE, getString(R.string.gpodnet_main_label));
+ intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, getString(R.string.gpodnet_main_label));
startActivity(intent);
}
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 fe1a09149..58fe8afbf 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -4,6 +4,7 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
+import android.widget.Toast;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -16,7 +17,7 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
-import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.ShareUtils;
@@ -56,26 +57,29 @@ public class FeedItemMenuHandler {
* @param queueAccess Used for testing if the queue contains the selected item
* @return Returns true if selectedItem is not null.
*/
- public static boolean onPrepareMenu(MenuInterface mi, FeedItem selectedItem,
- 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 = queueAccess.contains(selectedItem.getId());
- if(queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId()) {
+ 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);
}
- if(queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId()) {
+ if(queueAccess == null || queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId()) {
mi.setItemVisibility(R.id.move_to_bottom_item, false);
}
if (!isInQueue || isPlaying) {
@@ -84,14 +88,24 @@ public class FeedItemMenuHandler {
if (!(!isInQueue && selectedItem.getMedia() != null)) {
mi.setItemVisibility(R.id.add_to_queue_item, false);
}
+
if (!showExtendedMenu || selectedItem.getLink() == null) {
+ mi.setItemVisibility(R.id.visit_website_item, false);
mi.setItemVisibility(R.id.share_link_item, false);
+ mi.setItemVisibility(R.id.share_link_with_position_item, false);
+ }
+ if (!showExtendedMenu || !hasMedia || selectedItem.getMedia().getDownload_url() == null) {
+ mi.setItemVisibility(R.id.share_download_url_item, false);
+ mi.setItemVisibility(R.id.share_download_url_with_position_item, false);
+ }
+ if(false == hasMedia || selectedItem.getMedia().getPosition() <= 0) {
+ mi.setItemVisibility(R.id.share_link_with_position_item, false);
+ mi.setItemVisibility(R.id.share_download_url_with_position_item, false);
}
- if (!(state == FeedItem.State.UNREAD || state == FeedItem.State.IN_PROGRESS)) {
+ if (selectedItem.isPlayed()) {
mi.setItemVisibility(R.id.mark_read_item, false);
- }
- if (!(state == FeedItem.State.IN_PROGRESS || state == FeedItem.State.READ)) {
+ } else {
mi.setItemVisibility(R.id.mark_unread_item, false);
}
@@ -108,13 +122,14 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.deactivate_auto_download, false);
}
- if (!showExtendedMenu || selectedItem.getLink() == null) {
- mi.setItemVisibility(R.id.visit_website_item, false);
- }
-
if (selectedItem.getPaymentLink() == null || !selectedItem.getFlattrStatus().flattrable()) {
mi.setItemVisibility(R.id.support_item, false);
}
+
+ 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);
+
return true;
}
@@ -126,20 +141,22 @@ public class FeedItemMenuHandler {
* @return true if selectedItem is not null.
*/
public static boolean onPrepareMenu(MenuInterface mi,
- FeedItem selectedItem, boolean showExtendedMenu, LongList queueAccess, int... excludeIds) {
- boolean rc = onPrepareMenu(mi, selectedItem, showExtendedMenu, queueAccess);
+ 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);
}
}
-
return rc;
}
public static boolean onMenuItemClicked(Context context, int menuItemId,
FeedItem selectedItem) throws DownloadRequestException {
- DownloadRequester requester = DownloadRequester.getInstance();
switch (menuItemId) {
case R.id.skip_episode_item:
context.sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
@@ -148,24 +165,27 @@ public class FeedItemMenuHandler {
DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId());
break;
case R.id.mark_read_item:
- selectedItem.setRead(true);
- DBWriter.markItemRead(context, selectedItem, true, false);
+ selectedItem.setPlayed(true);
+ DBWriter.markItemPlayed(selectedItem, FeedItem.PLAYED, false);
if(GpodnetPreferences.loggedIn()) {
FeedMedia media = selectedItem.getMedia();
- GpodnetEpisodeAction actionPlay = new GpodnetEpisodeAction.Builder(selectedItem, Action.PLAY)
- .currentDeviceId()
- .currentTimestamp()
- .started(media.getDuration() / 1000)
- .position(media.getDuration() / 1000)
- .total(media.getDuration() / 1000)
- .build();
- GpodnetPreferences.enqueueEpisodeAction(actionPlay);
+ // not all items have media, Gpodder only cares about those that do
+ if (media != null) {
+ GpodnetEpisodeAction actionPlay = new GpodnetEpisodeAction.Builder(selectedItem, Action.PLAY)
+ .currentDeviceId()
+ .currentTimestamp()
+ .started(media.getDuration() / 1000)
+ .position(media.getDuration() / 1000)
+ .total(media.getDuration() / 1000)
+ .build();
+ GpodnetPreferences.enqueueEpisodeAction(actionPlay);
+ }
}
break;
case R.id.mark_unread_item:
- selectedItem.setRead(false);
- DBWriter.markItemRead(context, selectedItem, false, false);
- if(GpodnetPreferences.loggedIn()) {
+ selectedItem.setPlayed(false);
+ DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, false);
+ if(GpodnetPreferences.loggedIn() && selectedItem.getMedia() != null) {
GpodnetEpisodeAction actionNew = new GpodnetEpisodeAction.Builder(selectedItem, Action.NEW)
.currentDeviceId()
.currentTimestamp()
@@ -173,32 +193,39 @@ public class FeedItemMenuHandler {
GpodnetPreferences.enqueueEpisodeAction(actionNew);
}
break;
- case R.id.move_to_top_item:
- DBWriter.moveQueueItemToTop(context, selectedItem.getId(), true);
- return true;
- case R.id.move_to_bottom_item:
- DBWriter.moveQueueItemToBottom(context, selectedItem.getId(), true);
case R.id.add_to_queue_item:
- DBWriter.addQueueItem(context, selectedItem.getId());
+ DBWriter.addQueueItem(context, selectedItem);
break;
case R.id.remove_from_queue_item:
DBWriter.removeQueueItem(context, selectedItem, true);
break;
+ case R.id.add_to_favorites_item:
+ DBWriter.addFavoriteItem(selectedItem);
+ break;
+ case R.id.remove_from_favorites_item:
+ DBWriter.removeFavoriteItem(selectedItem);
+ break;
case R.id.reset_position:
selectedItem.getMedia().setPosition(0);
- DBWriter.markItemRead(context, selectedItem, false, true);
+ DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, true);
break;
case R.id.activate_auto_download:
selectedItem.setAutoDownload(true);
- DBWriter.setFeedItemAutoDownload(context, selectedItem, true);
+ DBWriter.setFeedItemAutoDownload(selectedItem, true);
break;
case R.id.deactivate_auto_download:
selectedItem.setAutoDownload(false);
- DBWriter.setFeedItemAutoDownload(context, selectedItem, false);
+ DBWriter.setFeedItemAutoDownload(selectedItem, false);
break;
case R.id.visit_website_item:
Uri uri = Uri.parse(selectedItem.getLink());
- context.startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(context, intent)) {
+ context.startActivity(intent);
+ } else {
+ Toast.makeText(context, context.getString(R.string.download_error_malformed_url),
+ Toast.LENGTH_SHORT);
+ }
break;
case R.id.support_item:
DBTasks.flattrItemIfLoggedIn(context, selectedItem);
@@ -206,6 +233,15 @@ public class FeedItemMenuHandler {
case R.id.share_link_item:
ShareUtils.shareFeedItemLink(context, selectedItem);
break;
+ case R.id.share_download_url_item:
+ ShareUtils.shareFeedItemDownloadLink(context, selectedItem);
+ break;
+ case R.id.share_link_with_position_item:
+ ShareUtils.shareFeedItemLink(context, selectedItem, true);
+ break;
+ case R.id.share_download_url_with_position_item:
+ ShareUtils.shareFeedItemDownloadLink(context, selectedItem, true);
+ break;
default:
Log.d(TAG, "Unknown menuItemId: " + menuItemId);
return false;
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
index 7bd8fedc9..830e9d419 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -1,18 +1,21 @@
package de.danoeh.antennapod.menuhandler;
-import android.app.AlertDialog;
import android.content.Context;
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;
@@ -20,6 +23,7 @@ import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
/**
@@ -39,11 +43,11 @@ public class FeedMenuHandler {
}
Log.d(TAG, "Preparing options menu");
- menu.findItem(R.id.mark_all_read_item).setVisible(selectedFeed.hasNewItems());
- if (selectedFeed.getPaymentLink() != null && selectedFeed.getFlattrStatus().flattrable())
+ if (selectedFeed.getPaymentLink() != null && selectedFeed.getFlattrStatus().flattrable()) {
menu.findItem(R.id.support_item).setVisible(true);
- else
+ } else {
menu.findItem(R.id.support_item).setVisible(false);
+ }
menu.findItem(R.id.refresh_complete_item).setVisible(selectedFeed.isPaged());
@@ -64,8 +68,8 @@ public class FeedMenuHandler {
case R.id.refresh_complete_item:
DBTasks.refreshCompleteFeed(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,
@@ -76,14 +80,20 @@ public class FeedMenuHandler {
public void onConfirmButtonPressed(
DialogInterface dialog) {
dialog.dismiss();
- DBWriter.markFeedRead(context, selectedFeed.getId());
+ DBWriter.markFeedRead(selectedFeed.getId());
}
};
conDialog.createNewDialog().show();
break;
case R.id.visit_website_item:
Uri uri = Uri.parse(selectedFeed.getLink());
- context.startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(context, intent)) {
+ context.startActivity(intent);
+ } else {
+ Toast.makeText(context, context.getString(R.string.download_error_malformed_url),
+ Toast.LENGTH_SHORT);
+ }
break;
case R.id.support_item:
DBTasks.flattrFeedIfLoggedIn(context, selectedFeed);
@@ -91,7 +101,7 @@ public class FeedMenuHandler {
case R.id.share_link_item:
ShareUtils.shareFeedlink(context, selectedFeed);
break;
- case R.id.share_source_item:
+ case R.id.share_download_url_item:
ShareUtils.shareFeedDownloadLink(context, selectedFeed);
break;
default:
@@ -100,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(context, 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/CustomEditTextPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/CustomEditTextPreference.java
deleted file mode 100644
index 898a56004..000000000
--- a/app/src/main/java/de/danoeh/antennapod/preferences/CustomEditTextPreference.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package de.danoeh.antennapod.preferences;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.os.Build;
-import android.preference.EditTextPreference;
-import android.util.AttributeSet;
-
-import de.danoeh.antennapod.R;
-
-public class CustomEditTextPreference extends EditTextPreference {
-
- public CustomEditTextPreference(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public CustomEditTextPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public CustomEditTextPreference(Context context) {
- super(context);
- }
-
- @Override
- protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
- if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
- builder.setInverseBackgroundForced(true);
- getEditText().setTextColor(getContext().getResources().getColor(R.color.black));
- }
- }
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
index f387b7524..785944768 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
@@ -1,11 +1,14 @@
package de.danoeh.antennapod.preferences;
+import android.annotation.SuppressLint;
import android.app.Activity;
-import android.app.AlertDialog;
+import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.res.Resources;
+import android.net.Uri;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
@@ -13,19 +16,31 @@ import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
+import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
import android.text.Editable;
+import android.text.Html;
import android.text.TextWatcher;
+import android.text.format.DateFormat;
import android.util.Log;
import android.widget.EditText;
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.BuildConfig;
+import de.danoeh.antennapod.CrashReportWriter;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AboutActivity;
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
@@ -33,12 +48,11 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.activity.PreferenceActivityGingerbread;
import de.danoeh.antennapod.asynctask.OpmlExportWorker;
-import de.danoeh.antennapod.core.asynctask.FlattrClickWorker;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
-import de.danoeh.antennapod.core.util.flattr.SimpleFlattrThing;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog;
import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
@@ -47,9 +61,11 @@ import de.danoeh.antennapod.dialog.VariableSpeedDialog;
/**
* Sets up a preference UI that lets the user change user preferences.
*/
-public class PreferenceController {
+
+public class PreferenceController implements SharedPreferences.OnSharedPreferenceChangeListener {
+
private static final String TAG = "PreferenceController";
- public static final String PREF_FLATTR_THIS_APP = "prefFlattrThisApp";
+
public static final String PREF_FLATTR_SETTINGS = "prefFlattrSettings";
public static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate";
public static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
@@ -71,6 +87,18 @@ public class PreferenceController {
public PreferenceController(PreferenceUI ui) {
this.ui = ui;
+ PreferenceManager.getDefaultSharedPreferences(ui.getActivity().getApplicationContext())
+ .registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if(key.equals(UserPreferences.PREF_SONIC)) {
+ CheckBoxPreference prefSonic = (CheckBoxPreference) ui.findPreference(UserPreferences.PREF_SONIC);
+ if(prefSonic != null) {
+ prefSonic.setChecked(sharedPreferences.getBoolean(UserPreferences.PREF_SONIC, false));
+ }
+ }
}
/**
@@ -105,23 +133,6 @@ public class PreferenceController {
);
}
- ui.findPreference(PreferenceController.PREF_FLATTR_THIS_APP).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- new FlattrClickWorker(activity,
- new SimpleFlattrThing(activity.getString(R.string.app_name),
- FlattrUtils.APP_URL,
- new FlattrStatus(FlattrStatus.STATUS_QUEUE)
- )
- ).executeAsync();
-
- return true;
- }
- }
- );
-
ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() {
@@ -160,21 +171,22 @@ public class PreferenceController {
}
}
);
-
- ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener(
- new Preference.OnPreferenceClickListener() {
-
- @Override
- public boolean onPreferenceClick(Preference preference) {
- activity.startActivityForResult(
- new Intent(activity,
- DirectoryChooserActivity.class),
- DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED
- );
- return true;
- }
- }
- );
+ ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR)
+ .setOnPreferenceClickListener(
+ new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ if (Build.VERSION.SDK_INT >= 19) {
+ showChooseDataFolderDialog();
+ } else {
+ Intent intent = new Intent(activity, DirectoryChooserActivity.class);
+ activity.startActivityForResult(intent,
+ DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+ return true;
+ }
+ }
+ );
ui.findPreference(UserPreferences.PREF_THEME)
.setOnPreferenceChangeListener(
new Preference.OnPreferenceChangeListener() {
@@ -200,18 +212,26 @@ public class PreferenceController {
}
});
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL)
- .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL)
+ .setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@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);
- }
+ public boolean onPreferenceClick(Preference preference) {
+ showUpdateIntervalTimePreferencesDialog();
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() {
@@ -240,7 +260,7 @@ public class PreferenceController {
setParallelDownloadsText(value);
return true;
}
- } catch(NumberFormatException e) {
+ } catch (NumberFormatException e) {
return false;
}
}
@@ -249,17 +269,19 @@ public class PreferenceController {
}
);
// validate and set correct value: number of downloads between 1 and 50 (inclusive)
- final EditText ev = ((EditTextPreference)ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)).getEditText();
+ final EditText ev = ((EditTextPreference) ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)).getEditText();
ev.addTextChangedListener(new TextWatcher() {
@Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
@Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
@Override
public void afterTextChanged(Editable s) {
- if(s.length() > 0) {
+ if (s.length() > 0) {
try {
int value = Integer.valueOf(s.toString());
if (value <= 0) {
@@ -267,7 +289,7 @@ public class PreferenceController {
} else if (value > 50) {
ev.setText("50");
}
- } catch(NumberFormatException e) {
+ } catch (NumberFormatException e) {
ev.setText("6");
}
ev.setSelection(ev.getText().length());
@@ -345,38 +367,91 @@ public class PreferenceController {
@Override
public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) {
- UserPreferences.setAutoFlattrSettings(activity, autoFlattrEnabled, autoFlattrValue);
+ UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue);
checkItemVisibility();
}
});
return true;
}
});
- buildUpdateIntervalPreference();
+ ui.findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE)
+ .setOnPreferenceChangeListener(
+ new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object o) {
+ if (o instanceof String) {
+ int newValue = Integer.valueOf((String) o) * 1024 * 1024;
+ if (newValue != UserPreferences.getImageCacheSize()) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(ui.getActivity());
+ dialog.setTitle(android.R.string.dialog_alert_title);
+ dialog.setMessage(R.string.pref_restart_required);
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.show();
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+ );
+ ui.findPreference("prefSendCrashReport").setOnPreferenceClickListener(preference -> {
+ Intent emailIntent = new Intent(Intent.ACTION_SEND);
+ emailIntent.setType("text/plain");
+ 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()));
+ String intentTitle = ui.getActivity().getString(R.string.send_email);
+ ui.getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle));
+ return true;
+ });
+ buildEpisodeCleanupPreference();
buildSmartMarkAsPlayedPreference();
buildAutodownloadSelectedNetworsPreference();
- setSelectedNetworksEnabled(UserPreferences
- .isEnableAutodownloadWifiFilter());
+ setSelectedNetworksEnabled(UserPreferences.isEnableAutodownloadWifiFilter());
}
public void onResume() {
checkItemVisibility();
+ setUpdateIntervalText();
setParallelDownloadsText(UserPreferences.getParallelDownloads());
setEpisodeCacheSizeText(UserPreferences.getEpisodeCacheSize());
setDataFolderText();
updateGpodnetPreferenceScreen();
}
+ @SuppressLint("NewApi")
public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
- String dir = data
- .getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Setting data folder");
- UserPreferences.setDataFolder(dir);
+ if (resultCode == Activity.RESULT_OK &&
+ requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
+ String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
+
+ File path = new File(dir);
+ String message = null;
+ final Context context= ui.getActivity().getApplicationContext();
+ if(!path.exists()) {
+ message = String.format(context.getString(R.string.folder_does_not_exist_error), dir);
+ } else if(!path.canRead()) {
+ message = String.format(context.getString(R.string.folder_not_readable_error), dir);
+ } else if(!path.canWrite()) {
+ message = String.format(context.getString(R.string.folder_not_writable_error), dir);
+ }
+
+ if(message == null) {
+ Log.d(TAG, "Setting data folder: " + dir);
+ UserPreferences.setDataFolder(dir);
+ setDataFolderText();
+ } else {
+ AlertDialog.Builder ab = new AlertDialog.Builder(ui.getActivity());
+ ab.setMessage(message);
+ ab.setPositiveButton(android.R.string.ok, null);
+ ab.show();
+ }
}
}
+
private void updateGpodnetPreferenceScreen() {
final boolean loggedIn = GpodnetPreferences.loggedIn();
ui.findPreference(PreferenceController.PREF_GPODNET_LOGIN).setEnabled(!loggedIn);
@@ -385,12 +460,8 @@ public class PreferenceController {
ui.findPreference(PreferenceController.PREF_GPODNET_HOSTNAME).setSummary(GpodnetPreferences.getHostname());
}
- private void buildUpdateIntervalPreference() {
+ private String[] getUpdateIntervalEntries(final String[] values) {
final Resources res = ui.getActivity().getResources();
-
- ListPreference pref = (ListPreference) ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL);
- String[] values = res.getStringArray(
- R.array.update_intervall_values);
String[] entries = new String[values.length];
for (int x = 0; x < values.length; x++) {
Integer v = Integer.parseInt(values[x]);
@@ -399,34 +470,56 @@ public class PreferenceController {
entries[x] = res.getString(R.string.pref_update_interval_hours_manual);
break;
case 1:
- entries[x] = v
- + " "
- + res.getString(R.string.pref_update_interval_hours_singular);
+ entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_singular);
break;
default:
- entries[x] = v + " "
- + res.getString(R.string.pref_update_interval_hours_plural);
+ entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_plural);
break;
}
}
- pref.setEntries(entries);
+ return entries;
+ }
+ private void buildEpisodeCleanupPreference() {
+ final Resources res = ui.getActivity().getResources();
+
+ ListPreference pref = (ListPreference) ui.findPreference(UserPreferences.PREF_EPISODE_CLEANUP);
+ String[] values = res.getStringArray(
+ R.array.episode_cleanup_values);
+ String[] entries = new String[values.length];
+ for (int x = 0; x < values.length; x++) {
+ int v = Integer.parseInt(values[x]);
+ if (v == UserPreferences.EPISODE_CLEANUP_QUEUE) {
+ entries[x] = res.getString(R.string.episode_cleanup_queue_removal);
+ } else if (v == UserPreferences.EPISODE_CLEANUP_NULL){
+ entries[x] = res.getString(R.string.episode_cleanup_never);
+ } else if (v == 0) {
+ entries[x] = res.getString(R.string.episode_cleanup_after_listening);
+ } else {
+ entries[x] = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, v, v);
+ }
+ }
+ pref.setEntries(entries);
}
private void buildSmartMarkAsPlayedPreference() {
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] = v + " " + res.getString(R.string.time_unit_seconds);
+ 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);
@@ -442,21 +535,52 @@ public class PreferenceController {
@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());
+ 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());
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY)
- .setEnabled(UserPreferences.isEnableAutodownload());
+ if (Build.VERSION.SDK_INT >= 16) {
+ ui.findPreference(UserPreferences.PREF_SONIC).setEnabled(true);
+ } else {
+ Preference prefSonic = ui.findPreference(UserPreferences.PREF_SONIC);
+ prefSonic.setSummary("[Android 4.1+]\n" + prefSonic.getSummary());
+ }
+ }
+
+ 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) {
@@ -482,7 +606,7 @@ public class PreferenceController {
}
private void setDataFolderText() {
- File f = UserPreferences.getDataFolder(ui.getActivity(), null);
+ File f = UserPreferences.getDataFolder(null);
if (f != null) {
ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR)
.setSummary(f.getAbsolutePath());
@@ -516,9 +640,7 @@ public class PreferenceController {
);
boolean newValue = ((CheckBoxPreference) preference)
.isChecked();
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Selected network " + key
- + ". New state: " + newValue);
+ Log.d(TAG, "Selected network " + key + ". New state: " + newValue);
int index = prefValuesList.indexOf(key);
if (index >= 0 && newValue == false) {
@@ -529,9 +651,7 @@ public class PreferenceController {
}
UserPreferences.setAutodownloadSelectedNetworks(
- activity, prefValuesList
- .toArray(new String[prefValuesList
- .size()])
+ prefValuesList.toArray(new String[prefValuesList.size()])
);
return true;
} else {
@@ -586,36 +706,156 @@ public class PreferenceController {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.drawer_preferences);
- builder.setMultiChoiceItems(navTitles, checked, new DialogInterface.OnMultiChoiceClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which, boolean isChecked) {
- if (isChecked) {
- hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]);
- } else {
- hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]);
- }
+ builder.setMultiChoiceItems(navTitles, 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, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- UserPreferences.setHiddenDrawerItems(context, hiddenDrawerItems);
+ UserPreferences.setHiddenDrawerItems(hiddenDrawerItems);
}
});
builder.setNegativeButton(R.string.cancel_label, null);
builder.create().show();
}
+ private void showChooseDataFolderDialog() {
+ Context context = ui.getActivity();
+ 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);
+ 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(context.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(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.toArray(new CharSequence[choices.size()]))
+ .itemsCallbackSingleChoice(selectedIndex, (dialog1, itemView, which, text) -> {
+ String folder = folders.get(which);
+ Log.d(TAG, "data folder: " + folder);
+ UserPreferences.setDataFolder(folder);
+ setDataFolderText();
+ return true;
+ })
+ .negativeText(R.string.cancel_label)
+ .cancelable(true)
+ .build();
+ dialog.show();
+ }
+
+ private void showUpdateIntervalTimePreferencesDialog() {
+ final Context context = ui.getActivity();
+ MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
+ builder.title(R.string.pref_autoUpdateIntervallOrTime_title);
+ builder.content(R.string.pref_autoUpdateIntervallOrTime_message);
+ builder.positiveText(R.string.pref_autoUpdateIntervallOrTime_Interval);
+ builder.negativeText(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay);
+ builder.neutralText(R.string.pref_autoUpdateIntervallOrTime_Disable);
+ builder.callback(new MaterialDialog.ButtonCallback() {
+ @Override
+ public void onPositive(MaterialDialog dialog) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
+ final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
+ final String[] entries = getUpdateIntervalEntries(values);
+ long currInterval = UserPreferences.getUpdateInterval();
+ int checkedItem = -1;
+ if(currInterval > 0) {
+ String currIntervalStr = String.valueOf(TimeUnit.MILLISECONDS.toHours(currInterval));
+ checkedItem = ArrayUtils.indexOf(values, currIntervalStr);
+ }
+ builder.setSingleChoiceItems(entries, checkedItem, (dialog1, which) -> {
+ int hours = Integer.valueOf(values[which]);
+ UserPreferences.setUpdateInterval(hours);
+ dialog1.dismiss();
+ setUpdateIntervalText();
+ });
+ builder.setNegativeButton(context.getString(R.string.cancel_label), null);
+ builder.show();
+ }
+
+ @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,
+ (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);
+ setUpdateIntervalText();
+ }
+ });
+ builder.forceStacking(true);
+ builder.show();
+ }
- public static interface PreferenceUI {
+ public interface PreferenceUI {
/**
* Finds a preference based on its key.
*/
- public Preference findPreference(CharSequence key);
+ Preference findPreference(CharSequence key);
- public Activity getActivity();
+ Activity getActivity();
}
}
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/ConnectivityActionReceiver.java b/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
index f55a7603f..665ddc3b5 100644
--- a/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
+++ b/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
@@ -5,28 +5,23 @@ import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
+import android.text.TextUtils;
import android.util.Log;
-import org.apache.commons.lang3.StringUtils;
-
-import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.NetworkUtils;
public class ConnectivityActionReceiver extends BroadcastReceiver {
- private static final String TAG = "ConnectivityActionReceiver";
+ private static final String TAG = "ConnectivityActionRecvr";
@Override
public void onReceive(final Context context, Intent intent) {
- if (StringUtils.equals(intent.getAction(), ConnectivityManager.CONNECTIVITY_ACTION)) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Received intent");
+ if (TextUtils.equals(intent.getAction(), ConnectivityManager.CONNECTIVITY_ACTION)) {
+ Log.d(TAG, "Received intent");
- if (NetworkUtils.autodownloadNetworkAvailable(context)) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "auto-dl network available, starting auto-download");
+ if (NetworkUtils.autodownloadNetworkAvailable()) {
+ Log.d(TAG, "auto-dl network available, starting auto-download");
DBTasks.autodownloadUndownloadedItems(context);
} else { // if new network is Wi-Fi, finish ongoing downloads,
// otherwise cancel all downloads
@@ -34,12 +29,9 @@ public class ConnectivityActionReceiver extends BroadcastReceiver {
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
if (ni == null || ni.getType() != ConnectivityManager.TYPE_WIFI) {
- if (BuildConfig.DEBUG)
- Log.i(TAG,
- "Device is no longer connected to Wi-Fi. Cancelling ongoing downloads");
+ Log.i(TAG, "Device is no longer connected to Wi-Fi. Cancelling ongoing downloads");
DownloadRequester.getInstance().cancelAllDownloads(context);
}
-
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/receiver/PlayerWidget.java b/app/src/main/java/de/danoeh/antennapod/receiver/PlayerWidget.java
index 7ab386edf..7000827c6 100644
--- a/app/src/main/java/de/danoeh/antennapod/receiver/PlayerWidget.java
+++ b/app/src/main/java/de/danoeh/antennapod/receiver/PlayerWidget.java
@@ -4,46 +4,75 @@ import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.text.TextUtils;
import android.util.Log;
-import org.apache.commons.lang3.StringUtils;
-
-import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.service.PlayerWidgetService;
public class PlayerWidget extends AppWidgetProvider {
- private static final String TAG = "PlayerWidget";
+ private static final String TAG = "PlayerWidget";
+ private static final String PREFS_NAME = "PlayerWidgetPrefs";
+ private static final String KEY_ENABLED = "WidgetEnabled";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "onReceive");
+ super.onReceive(context, intent);
+ // don't do anything if we're not enabled
+ if (!isEnabled(context)) {
+ return;
+ }
+
+ // these come from the PlaybackService when things should get updated
+ if (TextUtils.equals(intent.getAction(), PlaybackService.FORCE_WIDGET_UPDATE)) {
+ startUpdate(context);
+ } else if (TextUtils.equals(intent.getAction(), PlaybackService.STOP_WIDGET_UPDATE)) {
+ stopUpdate(context);
+ }
+ }
+
+ @Override
+ public void onEnabled(Context context) {
+ super.onEnabled(context);
+ Log.d(TAG, "Widget enabled");
+ setEnabled(context, true);
+ startUpdate(context);
+ }
@Override
- public void onReceive(Context context, Intent intent) {
- if (StringUtils.equals(intent.getAction(), PlaybackService.FORCE_WIDGET_UPDATE)) {
- startUpdate(context);
- } else if (StringUtils.equals(intent.getAction(), PlaybackService.STOP_WIDGET_UPDATE)) {
- stopUpdate(context);
- }
-
- }
-
- @Override
- public void onEnabled(Context context) {
- super.onEnabled(context);
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Widget enabled");
- }
-
- @Override
- public void onUpdate(Context context, AppWidgetManager appWidgetManager,
- int[] appWidgetIds) {
- startUpdate(context);
- }
-
- private void startUpdate(Context context) {
- context.startService(new Intent(context, PlayerWidgetService.class));
- }
-
- private void stopUpdate(Context context) {
- context.stopService(new Intent(context, PlayerWidgetService.class));
- }
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager,
+ int[] appWidgetIds) {
+ Log.d(TAG, "onUpdate() called with: " + "context = [" + context + "], appWidgetManager = [" + appWidgetManager + "], appWidgetIds = [" + appWidgetIds + "]");
+ startUpdate(context);
+ }
+
+ @Override
+ public void onDisabled(Context context) {
+ super.onDisabled(context);
+ Log.d(TAG, "Widet disabled");
+ setEnabled(context, false);
+ stopUpdate(context);
+ }
+
+ private void startUpdate(Context context) {
+ Log.d(TAG, "startUpdate() called with: " + "context = [" + context + "]");
+ context.startService(new Intent(context, PlayerWidgetService.class));
+ }
+
+ private void stopUpdate(Context context) {
+ Log.d(TAG, "stopUpdate() called with: " + "context = [" + context + "]");
+ context.stopService(new Intent(context, PlayerWidgetService.class));
+ }
+
+ private boolean isEnabled(Context context) {
+ SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
+ return prefs.getBoolean(KEY_ENABLED, false);
+ }
+ private void setEnabled(Context context, boolean enabled) {
+ SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
+ prefs.edit().putBoolean(KEY_ENABLED, enabled).apply();
+ }
}
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 d15108bfe..ef6330f82 100644
--- a/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java
+++ b/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java
@@ -3,11 +3,10 @@ package de.danoeh.antennapod.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
-import org.apache.commons.lang3.StringUtils;
-
import java.util.Arrays;
import java.util.Date;
@@ -29,7 +28,7 @@ public class SPAReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
- if (StringUtils.equals(intent.getAction(), ACTION_SP_APPS_QUERY_FEEDS_REPSONSE)) {
+ if (TextUtils.equals(intent.getAction(), ACTION_SP_APPS_QUERY_FEEDS_REPSONSE)) {
if (BuildConfig.DEBUG) Log.d(TAG, "Received SP_APPS_QUERY_RESPONSE");
if (intent.hasExtra(ACTION_SP_APPS_QUERY_FEEDS_REPSONSE_FEEDS_EXTRA)) {
String[] feedUrls = intent.getStringArrayExtra(ACTION_SP_APPS_QUERY_FEEDS_REPSONSE_FEEDS_EXTRA);
diff --git a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
index 1fe9e2cf9..323060f81 100644
--- a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
+++ b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
@@ -14,206 +14,229 @@ import android.view.View;
import android.widget.RemoteViews;
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.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.receiver.PlayerWidget;
-/** Updates the state of the player widget */
+/**
+ * Updates the state of the player widget
+ */
public class PlayerWidgetService extends Service {
- private static final String TAG = "PlayerWidgetService";
+ private static final String TAG = "PlayerWidgetService";
- private PlaybackService playbackService;
+ private PlaybackService playbackService;
- /** Controls write access to playbackservice reference */
+ /**
+ * Controls write access to playbackservice reference
+ */
private Object psLock;
- /** True while service is updating the widget */
- private volatile boolean isUpdating;
+ /**
+ * True while service is updating the widget
+ */
+ private volatile boolean isUpdating;
- public PlayerWidgetService() {
- }
+ public PlayerWidgetService() {
+ }
- @Override
- public void onCreate() {
- super.onCreate();
- Log.d(TAG, "Service created");
- isUpdating = false;
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.d(TAG, "Service created");
+ isUpdating = false;
psLock = new Object();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- Log.d(TAG, "Service is about to be destroyed");
- if (playbackService != null) {
- Playable playable = playbackService.getPlayable();
- if (playable != null && playable instanceof FeedMedia) {
- FeedMedia media = (FeedMedia) playable;
- if (media.hasAlmostEnded()) {
- Log.d(TAG, "smart mark as read");
- FeedItem item = media.getItem();
- DBWriter.markItemRead(this, item, true, false);
- DBWriter.removeQueueItem(this, item, false);
- DBWriter.addItemToPlaybackHistory(this, media);
- if (UserPreferences.isAutoDelete()) {
- Log.d(TAG, "Delete " + media.toString());
- DBWriter.deleteFeedMediaOfItem(this, media.getId());
- }
- }
- }
- }
-
- try {
- unbindService(mConnection);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "IllegalArgumentException when trying to unbind service");
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (!isUpdating) {
- if (playbackService == null && PlaybackService.isRunning) {
- bindService(new Intent(this, PlaybackService.class),
- mConnection, 0);
- } else {
- startViewUpdaterIfNotRunning();
- }
- } else {
- Log.d(TAG, "Service was called while updating. Ignoring update request");
- }
- return Service.START_NOT_STICKY;
- }
-
- private void updateViews() {
- if (playbackService == null) {
- return;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ Log.d(TAG, "Service is about to be destroyed");
+ if (playbackService != null) {
+ Playable playable = playbackService.getPlayable();
+ if (playable != null && playable instanceof FeedMedia) {
+ FeedMedia media = (FeedMedia) playable;
+ if (media.hasAlmostEnded()) {
+ Log.d(TAG, "smart mark as read");
+ FeedItem item = media.getItem();
+ DBWriter.markItemPlayed(item, FeedItem.PLAYED, false);
+ DBWriter.removeQueueItem(this, item, false);
+ DBWriter.addItemToPlaybackHistory(media);
+ if (item.getFeed().getPreferences().getCurrentAutoDelete()) {
+ Log.d(TAG, "Delete " + media.toString());
+ DBWriter.deleteFeedMediaOfItem(this, media.getId());
+ }
+ }
+ }
+ }
+
+ try {
+ unbindService(mConnection);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "IllegalArgumentException when trying to unbind service");
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (!isUpdating) {
+ if (playbackService == null && PlaybackService.isRunning) {
+ bindService(new Intent(this, PlaybackService.class),
+ mConnection, 0);
+ } else {
+ startViewUpdaterIfNotRunning();
+ }
+ } else {
+ Log.d(TAG, "Service was called while updating. Ignoring update request");
}
- isUpdating = true;
-
- ComponentName playerWidget = new ComponentName(this, PlayerWidget.class);
- AppWidgetManager manager = AppWidgetManager.getInstance(this);
- RemoteViews views = new RemoteViews(getPackageName(),
- R.layout.player_widget);
- PendingIntent startMediaplayer = PendingIntent.getActivity(this, 0,
- PlaybackService.getPlayerActivityIntent(this), 0);
-
- views.setOnClickPendingIntent(R.id.layout_left, startMediaplayer);
- final Playable media = playbackService.getPlayable();
- if (playbackService != null && media != null) {
- PlayerStatus status = playbackService.getStatus();
-
- views.setTextViewText(R.id.txtvTitle, media.getEpisodeTitle());
-
- String progressString = getProgressString(media);
- if (progressString != null) {
- views.setTextViewText(R.id.txtvProgress, progressString);
- }
-
- if (status == PlayerStatus.PLAYING) {
- views.setImageViewResource(R.id.butPlay, R.drawable.ic_pause_white_24dp);
- if (Build.VERSION.SDK_INT >= 15) {
- views.setContentDescription(R.id.butPlay, getString(R.string.pause_label));
+ return Service.START_NOT_STICKY;
+ }
+
+ private void updateViews() {
+ isUpdating = true;
+
+ ComponentName playerWidget = new ComponentName(this, PlayerWidget.class);
+ AppWidgetManager manager = AppWidgetManager.getInstance(this);
+ RemoteViews views = new RemoteViews(getPackageName(),
+ R.layout.player_widget);
+ PendingIntent startMediaplayer = PendingIntent.getActivity(this, 0,
+ PlaybackService.getPlayerActivityIntent(this), 0);
+
+ Intent startApp = new Intent(getBaseContext(), MainActivity.class);
+ startApp.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startApp.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, QueueFragment.TAG);
+ PendingIntent startAppPending = PendingIntent.getActivity(getBaseContext(), 0, startApp, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ boolean nothingPlaying = false;
+ if (playbackService != null) {
+ final Playable media = playbackService.getPlayable();
+ if (media != null) {
+ PlayerStatus status = playbackService.getStatus();
+ views.setOnClickPendingIntent(R.id.layout_left, startMediaplayer);
+
+ views.setTextViewText(R.id.txtvTitle, media.getEpisodeTitle());
+
+ String progressString = getProgressString();
+ if (progressString != null) {
+ views.setViewVisibility(R.id.txtvProgress, View.VISIBLE);
+ views.setTextViewText(R.id.txtvProgress, progressString);
}
- } else {
- views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp);
- if (Build.VERSION.SDK_INT >= 15) {
- views.setContentDescription(R.id.butPlay, getString(R.string.play_label));
+
+ if (status == PlayerStatus.PLAYING) {
+ views.setImageViewResource(R.id.butPlay, R.drawable.ic_pause_white_24dp);
+ if (Build.VERSION.SDK_INT >= 15) {
+ views.setContentDescription(R.id.butPlay, getString(R.string.pause_label));
+ }
+ } else {
+ views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp);
+ if (Build.VERSION.SDK_INT >= 15) {
+ views.setContentDescription(R.id.butPlay, getString(R.string.play_label));
+ }
}
- }
- views.setOnClickPendingIntent(R.id.butPlay,
- createMediaButtonIntent());
- } else {
- views.setViewVisibility(R.id.txtvProgress, View.INVISIBLE);
- views.setTextViewText(R.id.txtvTitle,
- this.getString(R.string.no_media_playing_label));
- views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp);
-
- }
-
- manager.updateAppWidget(playerWidget, views);
- isUpdating = false;
- }
-
- /** Creates an intent which fakes a mediabutton press */
- private PendingIntent createMediaButtonIntent() {
- KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
- Intent startingIntent = new Intent(
- MediaButtonReceiver.NOTIFY_BUTTON_RECEIVER);
- startingIntent.putExtra(Intent.EXTRA_KEY_EVENT, event);
-
- return PendingIntent.getBroadcast(this, 0, startingIntent, 0);
- }
-
- private String getProgressString(Playable media) {
- int position = media.getPosition();
- int duration = media.getDuration();
- if (position > 0 && duration > 0) {
- return Converter.getDurationStringLong(position) + " / "
- + Converter.getDurationStringLong(duration);
- } else {
- return null;
- }
- }
-
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- Log.d(TAG, "Connection to service established");
+ views.setOnClickPendingIntent(R.id.butPlay,
+ createMediaButtonIntent());
+ } else {
+ nothingPlaying = true;
+ }
+ } else {
+ nothingPlaying = true;
+ }
+
+ if (nothingPlaying) {
+ // start the app if they click anything
+ views.setOnClickPendingIntent(R.id.layout_left, startAppPending);
+ views.setOnClickPendingIntent(R.id.butPlay, startAppPending);
+ views.setViewVisibility(R.id.txtvProgress, View.INVISIBLE);
+ views.setTextViewText(R.id.txtvTitle,
+ this.getString(R.string.no_media_playing_label));
+ views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp);
+ }
+
+ manager.updateAppWidget(playerWidget, views);
+ isUpdating = false;
+ }
+
+ /**
+ * Creates an intent which fakes a mediabutton press
+ */
+ private PendingIntent createMediaButtonIntent() {
+ KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+ Intent startingIntent = new Intent(
+ MediaButtonReceiver.NOTIFY_BUTTON_RECEIVER);
+ startingIntent.putExtra(Intent.EXTRA_KEY_EVENT, event);
+
+ return PendingIntent.getBroadcast(this, 0, startingIntent, 0);
+ }
+
+ private String getProgressString() {
+ int position = playbackService.getCurrentPosition();
+ int duration = playbackService.getDuration();
+ if (position > 0 && duration > 0) {
+ return Converter.getDurationStringLong(position) + " / "
+ + Converter.getDurationStringLong(duration);
+ } else {
+ return null;
+ }
+ }
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.d(TAG, "Connection to service established");
synchronized (psLock) {
playbackService = ((PlaybackService.LocalBinder) service)
.getService();
startViewUpdaterIfNotRunning();
}
- }
+ }
- @Override
- public void onServiceDisconnected(ComponentName name) {
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
synchronized (psLock) {
playbackService = null;
Log.d(TAG, "Disconnected from service");
}
- }
+ }
- };
+ };
- private void startViewUpdaterIfNotRunning() {
- if (!isUpdating) {
- ViewUpdater updateThread = new ViewUpdater(this);
- updateThread.start();
- }
- }
+ private void startViewUpdaterIfNotRunning() {
+ if (!isUpdating) {
+ ViewUpdater updateThread = new ViewUpdater(this);
+ updateThread.start();
+ }
+ }
- class ViewUpdater extends Thread {
- private static final String THREAD_NAME = "ViewUpdater";
- private PlayerWidgetService service;
+ class ViewUpdater extends Thread {
+ private static final String THREAD_NAME = "ViewUpdater";
+ private PlayerWidgetService service;
- public ViewUpdater(PlayerWidgetService service) {
- super();
- setName(THREAD_NAME);
- this.service = service;
+ public ViewUpdater(PlayerWidgetService service) {
+ super();
+ setName(THREAD_NAME);
+ this.service = service;
- }
+ }
- @Override
- public void run() {
+ @Override
+ public void run() {
synchronized (psLock) {
service.updateViews();
}
- }
+ }
- }
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/SubscriptionViewItem.java b/app/src/main/java/de/danoeh/antennapod/view/SubscriptionViewItem.java
index ee2567364..7703554f9 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/SubscriptionViewItem.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/SubscriptionViewItem.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.view;
import android.content.Context;
+import android.net.Uri;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
@@ -8,8 +9,10 @@ import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
-import com.squareup.picasso.Callback;
-import com.squareup.picasso.Picasso;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.resource.drawable.GlideDrawable;
+import com.bumptech.glide.request.RequestListener;
+import com.bumptech.glide.request.target.Target;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
@@ -56,24 +59,31 @@ public class SubscriptionViewItem extends RelativeLayout {
mUnreadCountText = (TextView) view.findViewById(R.id.unread_count_text);
}
- public void setFeed(final Feed feed, int unreadCount) {
+ public void setFeed(Feed feed) {
mFeedTitle.setVisibility(VISIBLE);
mFeedTitle.setText(feed.getTitle());
+ Glide.with(mContext)
+ .load(feed.getImageUri())
+ .listener(new RequestListener<Uri, GlideDrawable>() {
+ @Override
+ public boolean onException(Exception e, Uri model, Target<GlideDrawable> target, boolean isFirstResource) {
+ return false;
+ }
- Picasso.with(mContext).load(feed.getImageUri()).centerCrop().fit().into(mImageView, new Callback() {
- @Override
- public void onSuccess() {
- mFeedTitle.setVisibility(GONE);
- }
-
- @Override
- public void onError() {
- }
- });
- mUnreadCountText.setText(unreadCount + "");
+ @Override
+ public boolean onResourceReady(GlideDrawable resource, Uri model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
+ mFeedTitle.setVisibility(INVISIBLE);
+ return false;
+ }
+ })
+ .centerCrop()
+ .into(mImageView);
// Removing the updated time. It could be the latest podcast updated time in the future.
//mTextTime.setText(TimeUtils.getTimeAgo(feed.getLastUpdate().getTime(), mContext));
mTextTime.setVisibility(GONE);
+
+ // Could be the count of unread/ not played feed items
+ //mUnreadCountText.setText(String.valueOf(feed.getNumOfItems()));
}
}
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-v14/authentication_dialog.xml b/app/src/main/res/layout-v14/authentication_dialog.xml
index ed05dab1c..00e74c9e1 100644
--- a/app/src/main/res/layout-v14/authentication_dialog.xml
+++ b/app/src/main/res/layout-v14/authentication_dialog.xml
@@ -15,7 +15,10 @@
android:layout_height="0dp"
android:layout_weight="1"
android:layout_margin="16dp"
- android:hint="@string/username_label"/>
+ android:hint="@string/username_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<EditText
android:id="@+id/etxtPassword"
@@ -24,7 +27,10 @@
android:layout_weight="1"
android:layout_margin="16dp"
android:inputType="textPassword"
- android:hint="@string/password_label"/>
+ android:hint="@string/password_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<CheckBox
android:id="@+id/chkSaveUsernamePassword"
diff --git a/app/src/main/res/layout-v14/directory_chooser.xml b/app/src/main/res/layout-v14/directory_chooser.xml
index 8f9c4ee93..14e2f6a38 100644
--- a/app/src/main/res/layout-v14/directory_chooser.xml
+++ b/app/src/main/res/layout-v14/directory_chooser.xml
@@ -101,7 +101,7 @@
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/butNavUp"
- android:background="@color/bright_blue" />
+ android:background="@color/holo_blue_light" />
</RelativeLayout>
<ListView
diff --git a/app/src/main/res/layout-v14/download_authentication_activity.xml b/app/src/main/res/layout-v14/download_authentication_activity.xml
index c1fe55ceb..f6925dc3a 100644
--- a/app/src/main/res/layout-v14/download_authentication_activity.xml
+++ b/app/src/main/res/layout-v14/download_authentication_activity.xml
@@ -12,7 +12,7 @@
android:layout_alignParentTop="true"
android:textSize="@dimen/text_size_large"
android:layout_margin="16dp"
- android:textColor="@color/bright_blue"
+ android:textColor="@color/holo_blue_light"
android:textStyle="italic"/>
<TextView
@@ -31,7 +31,10 @@
android:layout_margin="16dp"
android:id="@+id/etxtUsername"
android:hint="@string/username_label"
- android:layout_below="@id/txtvDescription"/>
+ android:layout_below="@id/txtvDescription"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<EditText
android:layout_width="match_parent"
@@ -40,7 +43,10 @@
android:id="@+id/etxtPassword"
android:hint="@string/password_label"
android:inputType="textPassword"
- android:layout_below="@id/etxtUsername"/>
+ android:layout_below="@id/etxtUsername"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<RelativeLayout
android:id="@+id/footer"
diff --git a/app/src/main/res/layout-v14/time_dialog.xml b/app/src/main/res/layout-v14/time_dialog.xml
index 7fd4309d5..06c2cce14 100644
--- a/app/src/main/res/layout-v14/time_dialog.xml
+++ b/app/src/main/res/layout-v14/time_dialog.xml
@@ -1,21 +1,22 @@
<?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:orientation="vertical" >
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center">
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<EditText
android:id="@+id/etxtTime"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="8dp"
- android:ems="7"
+ android:ems="2"
android:hint="@string/enter_time_here_label"
android:inputType="number"
android:maxLength="2" >
@@ -25,54 +26,34 @@
<Spinner
android:id="@+id/spTimeUnit"
- android:layout_width="180dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp" />
</LinearLayout>
- <RelativeLayout
- android:id="@+id/footer"
- android:layout_width="fill_parent"
- android:layout_height="48dp" >
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_alignParentTop="true"
- android:background="?android:attr/dividerVertical" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:textSize="16sp"
+ android:text="@string/timer_about_to_expire_label"/>
- <View
- android:id="@+id/horizontal_divider"
- android:layout_width="1dip"
- android:layout_height="fill_parent"
- android:layout_alignParentTop="true"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="4dp"
- android:layout_marginTop="4dp"
- android:background="?android:attr/dividerVertical" />
+ <CheckBox android:id="@+id/cbShakeToReset"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shake_to_reset_label"/>
- <Button
- android:id="@+id/butCancel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:layout_toLeftOf="@id/horizontal_divider"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/cancel_label" />
+ <CheckBox android:id="@+id/cbVibrate"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/timer_vibration_label"/>
- <Button
- android:id="@+id/butConfirm"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:layout_toRightOf="@id/horizontal_divider"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/confirm_label" />
- </RelativeLayout>
+ </LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/addfeed.xml b/app/src/main/res/layout/addfeed.xml
index b7babbafa..dff24c650 100644
--- a/app/src/main/res/layout/addfeed.xml
+++ b/app/src/main/res/layout/addfeed.xml
@@ -15,26 +15,33 @@
android:orientation="vertical">
<TextView
- android:id="@+id/txtvFeedurl"
+ android:id="@+id/txtvPodcastDirectories"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/AntennaPod.TextView.Heading"
- android:text="@string/txtvfeedurl_label"/>
+ android:text="@string/podcastdirectories_label"/>
- <EditText
- android:id="@+id/etxtFeedurl"
+ <TextView
+ android:id="@+id/txtvPodcastDirectoriesDescr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
- android:hint="@string/etxtFeedurlHint"
- android:inputType="textUri"/>
+ android:text="@string/podcastdirectories_descr"
+ android:textSize="@dimen/text_size_medium"
+ android:layout_marginTop="4dp"/>
<Button
- android:id="@+id/butConfirm"
+ android:id="@+id/butSearchItunes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- android:text="@string/confirm_label"/>
+ android:text="@string/search_itunes_label"/>
+
+ <Button
+ android:id="@+id/butBrowseGpoddernet"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/browse_gpoddernet_label"/>
<View
android:id="@+id/divider1"
@@ -44,34 +51,30 @@
android:background="?android:attr/listDivider"/>
<TextView
- android:id="@+id/txtvPodcastDirectories"
+ android:id="@+id/txtvFeedurl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/divider1"
style="@style/AntennaPod.TextView.Heading"
- android:text="@string/podcastdirectories_label"/>
-
- <TextView
- android:id="@+id/txtvPodcastDirectoriesDescr"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/podcastdirectories_descr"
- android:textSize="@dimen/text_size_medium"
- android:layout_marginTop="4dp"/>
+ android:text="@string/txtvfeedurl_label"/>
- <Button
- android:id="@+id/butBrowseGpoddernet"
+ <EditText
+ android:id="@+id/etxtFeedurl"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="@string/browse_gpoddernet_label"/>
+ android:layout_marginTop="4dp"
+ android:hint="@string/etxtFeedurlHint"
+ android:inputType="textUri"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<Button
- android:id="@+id/butSearchItunes"
+ android:id="@+id/butConfirm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- android:text="@string/search_itunes_label"/>
+ android:text="@string/confirm_label"/>
<View
android:id="@+id/divider2"
diff --git a/app/src/main/res/layout/all_episodes_fragment.xml b/app/src/main/res/layout/all_episodes_fragment.xml
index 19db02f1d..5336fb8ce 100644
--- a/app/src/main/res/layout/all_episodes_fragment.xml
+++ b/app/src/main/res/layout/all_episodes_fragment.xml
@@ -1,41 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:dslv="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.mobeta.android.dslv.DragSortListView
- android:id="@android:id/list"
- android:scrollbarStyle="outsideOverlay"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="@dimen/list_vertical_padding"
- android:paddingBottom="@dimen/list_vertical_padding"
- android:clipToPadding="false"
- dslv:collapsed_height="2dp"
- dslv:drag_enabled="false"
- dslv:drag_scroll_start="0.33"
- dslv:float_alpha="0.6"
- dslv:max_drag_scroll_speed="0.5"
- dslv:remove_enabled="true"
- dslv:remove_mode="flingRemove"
- dslv:slide_shuffle_speed="0.3"
- dslv:sort_enabled="false"
- dslv:track_drag_sort="false"
- dslv:float_background_color="?attr/dragview_float_background"
- dslv:use_default_controller="true"
- tools:background="@android:color/holo_green_dark"/>
-
- <TextView
- android:id="@id/android:empty"
+ <android.support.v7.widget.RecyclerView
+ android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center"
- android:gravity="center"
- android:text="@string/no_items_label"/>
+ android:scrollbarStyle="outsideOverlay"
+ android:paddingTop="@dimen/list_vertical_padding"
+ android:paddingBottom="@dimen/list_vertical_padding"
+ android:clipToPadding="false"/>
<ProgressBar
android:id="@+id/progLoading"
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..7532722dd
--- /dev/null
+++ b/app/src/main/res/layout/audio_controls.xml
@@ -0,0 +1,146 @@
+<?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"
+ android:layout_marginTop="12dp"
+ android:layout_marginLeft="24dp">
+
+ <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"
+ android:layout_marginLeft="24dp"
+ android:layout_marginRight="24dp">
+
+ <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"
+ android:layout_marginLeft="24dp"
+ 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="24dp"
+ android:layout_marginRight="24dp"
+ 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="24dp"
+ android:layout_marginRight="24dp"
+ 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"
+ android:layout_marginLeft="24dp"
+ 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:layout_marginLeft="24dp"
+ android:layout_marginRight="24dp"
+ 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 827e06e00..fb4f995a2 100644
--- a/app/src/main/res/layout/audioplayer_activity.xml
+++ b/app/src/main/res/layout/audioplayer_activity.xml
@@ -1,221 +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:textColor="@color/gray"
- 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"
+ 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>
- <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">
+ </LinearLayout>
- <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"
- 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" />
-
- </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/authentication_dialog.xml b/app/src/main/res/layout/authentication_dialog.xml
index 82260eb43..e18ab42eb 100644
--- a/app/src/main/res/layout/authentication_dialog.xml
+++ b/app/src/main/res/layout/authentication_dialog.xml
@@ -16,7 +16,10 @@
android:layout_height="0dp"
android:layout_weight="1"
android:layout_margin="16dp"
- android:hint="@string/username_label"/>
+ android:hint="@string/username_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<EditText
android:id="@+id/etxtPassword"
@@ -25,7 +28,10 @@
android:layout_weight="1"
android:layout_margin="16dp"
android:inputType="textPassword"
- android:hint="@string/password_label"/>
+ android:hint="@string/password_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<CheckBox
android:id="@+id/chkSaveUsernamePassword"
diff --git a/app/src/main/res/layout/cover_fragment.xml b/app/src/main/res/layout/cover_fragment.xml
index 18540aa1f..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:scaleType="fitCenter"
+ app:layout_aspectRatio="100%"
+ app:layout_widthPercent="82%"
+ tools:src="@android:drawable/sym_def_app_icon" />
+
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center"
- android:adjustViewBounds="true"
- android:scaleType="centerInside"
- tools:src="@android:drawable/sym_def_app_icon" />
+ 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>
-</RelativeLayout> \ No newline at end of file
+</android.support.percent.PercentRelativeLayout>
diff --git a/app/src/main/res/layout/directory_chooser.xml b/app/src/main/res/layout/directory_chooser.xml
index 93cd1c0d3..635a73cf4 100644
--- a/app/src/main/res/layout/directory_chooser.xml
+++ b/app/src/main/res/layout/directory_chooser.xml
@@ -78,7 +78,7 @@
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_below="@id/butNavUp"
- android:background="@color/bright_blue" />
+ android:background="@color/holo_blue_light" />
</RelativeLayout>
<ListView
diff --git a/app/src/main/res/layout/download_authentication_activity.xml b/app/src/main/res/layout/download_authentication_activity.xml
index 69106c9b3..27604973a 100644
--- a/app/src/main/res/layout/download_authentication_activity.xml
+++ b/app/src/main/res/layout/download_authentication_activity.xml
@@ -12,7 +12,7 @@
android:layout_alignParentTop="true"
android:textSize="@dimen/text_size_large"
android:layout_margin="16dp"
- android:textColor="@color/bright_blue"
+ android:textColor="@color/holo_blue_light"
android:textStyle="italic"/>
<TextView
@@ -31,7 +31,10 @@
android:layout_margin="16dp"
android:id="@+id/etxtUsername"
android:hint="@string/username_label"
- android:layout_below="@id/txtvDescription"/>
+ android:layout_below="@id/txtvDescription"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<EditText
android:layout_width="match_parent"
@@ -40,7 +43,10 @@
android:id="@+id/etxtPassword"
android:hint="@string/password_label"
android:inputType="textPassword"
- android:layout_below="@id/etxtUsername"/>
+ android:layout_below="@id/etxtUsername"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<LinearLayout
android:id="@+id/footer"
diff --git a/app/src/main/res/layout/downloadlog_item.xml b/app/src/main/res/layout/downloadlog_item.xml
index c6a34a517..20d879933 100644
--- a/app/src/main/res/layout/downloadlog_item.xml
+++ b/app/src/main/res/layout/downloadlog_item.xml
@@ -20,7 +20,7 @@
tools:text="[Icon]"
android:gravity="center" />
- <Button
+ <com.joanzapata.iconify.widget.IconButton
android:id="@+id/btnRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -28,6 +28,7 @@
android:layout_alignLeft="@id/txtvIcon"
android:layout_alignRight="@id/txtvIcon"
android:layout_marginTop="8dp"
+ android:text="{fa-repeat}"
tools:text="↻" />
<TextView
diff --git a/app/src/main/res/layout/episodes_apply_action_fragment.xml b/app/src/main/res/layout/episodes_apply_action_fragment.xml
new file mode 100644
index 000000000..d63088662
--- /dev/null
+++ b/app/src/main/res/layout/episodes_apply_action_fragment.xml
@@ -0,0 +1,120 @@
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+
+ <LinearLayout
+ android:id="@+id/bottomBar"
+ android:layout_width="wrap_content"
+ android:layout_height="68dp"
+ android:layout_alignParentBottom="true"
+ android:orientation="horizontal"
+ android:gravity="center_vertical"
+ android:padding="4dp">
+
+ <Button
+ android:id="@+id/btnAddToQueue"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableTop="?attr/content_new"
+ android:text="@string/add_to_queue_label"
+ android:textSize="10sp"
+ android:background="@android:color/transparent"/>
+
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_margin="4dp"
+ android:background="?android:attr/listDivider"
+ tools:background="@android:color/holo_red_dark" />
+
+ <Button
+ android:id="@+id/btnMarkAsPlayed"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableTop="?attr/navigation_accept"
+ android:text="@string/mark_read_label"
+ android:textSize="10sp"
+ android:background="@android:color/transparent"/>
+
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_margin="4dp"
+ android:background="?android:attr/listDivider"
+ tools:background="@android:color/holo_red_dark" />
+
+ <Button
+ android:id="@+id/btnMarkAsUnplayed"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableTop="?attr/navigation_cancel"
+ android:text="@string/mark_unread_label"
+ android:textSize="10sp"
+ android:background="@android:color/transparent"/>
+
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_margin="4dp"
+ android:background="?android:attr/listDivider"
+ tools:background="@android:color/holo_red_dark" />
+
+ <Button
+ android:id="@+id/btnDownload"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableTop="?attr/av_download"
+ android:text="@string/download_label"
+ android:textSize="10sp"
+ android:background="@android:color/transparent"/>
+
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:layout_margin="4dp"
+ android:background="?android:attr/listDivider"
+ tools:background="@android:color/holo_red_dark" />
+
+ <Button
+ android:id="@+id/btnDelete"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawableTop="?attr/content_discard"
+ android:text="@string/remove_episode_lable"
+ android:textSize="10sp"
+ android:background="@android:color/transparent"/>
+
+ </LinearLayout>
+
+ <View
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?android:attr/listDivider"
+ android:paddingBottom="4dp"
+ android:layout_above="@id/bottomBar"
+ tools:background="@android:color/holo_red_dark" />
+
+ <ListView
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_above="@id/divider">
+
+ </ListView>
+
+
+</RelativeLayout>
diff --git a/app/src/main/res/layout/external_player_fragment.xml b/app/src/main/res/layout/external_player_fragment.xml
index f9608fafc..b928b6233 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/bright_blue"/>
+ 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="fitXY"
+ 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 db897865c..4b545e3cc 100644
--- a/app/src/main/res/layout/feedinfo.xml
+++ b/app/src/main/res/layout/feedinfo.xml
@@ -20,8 +20,8 @@
<ImageView
android:id="@+id/imgvCover"
android:contentDescription="@string/cover_label"
- android:layout_width="70dp"
- android:layout_height="70dp"
+ android:layout_width="80dp"
+ android:layout_height="80dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
tools:src="@drawable/ic_stat_antenna_default"
@@ -33,7 +33,9 @@
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
+ android:layout_alignTop="@id/imgvCover"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_alignBottom="@id/imgvCover"
style="@style/AntennaPod.TextView.Heading"
tools:text="Feed title"
tools:background="@android:color/holo_green_dark"/>
@@ -44,7 +46,7 @@
android:layout_height="1dp"
android:layout_below="@id/imgvCover"
android:layout_marginTop="8dp"
- android:background="@color/bright_blue"/>
+ android:background="@color/holo_blue_light"/>
</RelativeLayout>
<ScrollView
@@ -52,7 +54,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
- android:scrollbarStyle="outsideInset"
+ android:scrollbarStyle="outsideOverlay"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingBottom="8dp">
@@ -119,7 +121,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
- android:layout_marginBottom="8dp"
app:layout_row="2"
app:layout_column="0"
android:lines="1"
@@ -129,10 +130,13 @@
<TextView
android:id="@+id/txtvUrl"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:paddingBottom="4dp"
app:layout_row="2"
app:layout_column="1"
+ app:layout_gravity="fill"
+ android:maxLines="4"
tools:text="http://www.example.com/feed"
tools:background="@android:color/holo_green_dark"/>
@@ -146,16 +150,47 @@
android:text="@string/podcast_settings_label"
android:layout_marginTop="8dp"/>
+ <android.support.v7.widget.GridLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ app:columnCount="2"
+ app:rowCount="1">
+
+ <TextView
+ android:id="@+id/txtvFeedAutoDelete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/auto_delete_label"
+ app:layout_row="0"
+ app:layout_column="0"
+ app:layout_gravity="center_vertical"
+ android:layout_marginRight="10dp" />
+
+ <Spinner
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:id="@+id/spnAutoDelete"
+ android:entries="@array/spnAutoDeleteItems"
+ android:layout_marginTop="8dp"
+ app:layout_row="0"
+ app:layout_column="1"
+ android:spinnerMode="dropdown"
+ app:layout_gravity="center"
+ android:dropDownWidth="wrap_content"
+ android:clickable="true" />
+ </android.support.v7.widget.GridLayout>
+
<CheckBox
- android:id="@+id/cbxAutoDownload"
+ android:id="@+id/cbxKeepUpdated"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- android:text="@string/auto_download_label"
- android:enabled="false"
+ android:text="@string/keep_updated"
+ android:enabled="true"
android:textColor="?android:attr/textColorPrimary"
tools:background="@android:color/holo_red_light"
- android:checked="false"/>
+ android:checked="true" />
<TextView
android:id="@+id/txtvAuthentication"
@@ -200,7 +235,10 @@
android:layout_height="wrap_content"
app:layout_row="0"
app:layout_column="1"
- android:hint="@string/username_label"/>
+ android:hint="@string/username_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<TextView
android:id="@+id/txtvPassword"
@@ -211,7 +249,10 @@
app:layout_row="1"
app:layout_column="0"
android:text="@string/password_label"
- android:textColor="?android:attr/textColorPrimary"/>
+ android:textColor="?android:attr/textColorPrimary"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<EditText
android:id="@+id/etxtPassword"
@@ -225,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 a21488306..000000000
--- a/app/src/main/res/layout/feeditem_fragment_header.xml
+++ /dev/null
@@ -1,137 +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_marginBottom="16dp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:visibility="gone" />
-
- <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/feeditemlist_header.xml b/app/src/main/res/layout/feeditemlist_header.xml
index 667f777af..361b583c9 100644
--- a/app/src/main/res/layout/feeditemlist_header.xml
+++ b/app/src/main/res/layout/feeditemlist_header.xml
@@ -78,7 +78,7 @@
tools:text="Podcast author"
tools:background="@android:color/holo_green_dark" />
- <IconTextView
+ <com.joanzapata.iconify.widget.IconTextView
android:id="@+id/txtvFailure"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/feeditemlist_item.xml b/app/src/main/res/layout/feeditemlist_item.xml
index 6b7c45978..d2b85e7df 100644
--- a/app/src/main/res/layout/feeditemlist_item.xml
+++ b/app/src/main/res/layout/feeditemlist_item.xml
@@ -1,14 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
<RelativeLayout
-
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
@@ -103,12 +104,10 @@
android:indeterminate="false"
/>
-
-
</RelativeLayout>
<include layout="@layout/vertical_list_divider"/>
<include layout="@layout/secondary_action"/>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/app/src/main/res/layout/fragment_itunes_search.xml b/app/src/main/res/layout/fragment_itunes_search.xml
index 17ffe349b..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="200dp"
- 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_list.xml b/app/src/main/res/layout/gpodnet_podcast_list.xml
index 05df70f31..d8b06e4f7 100644
--- a/app/src/main/res/layout/gpodnet_podcast_list.xml
+++ b/app/src/main/res/layout/gpodnet_podcast_list.xml
@@ -10,7 +10,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
- android:columnWidth="200dp"
+ android:columnWidth="400dp"
android:gravity="center"
android:horizontalSpacing="8dp"
android:numColumns="auto_fit"
diff --git a/app/src/main/res/layout/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/gpodnetauth_credentials.xml b/app/src/main/res/layout/gpodnetauth_credentials.xml
index 8436570fc..13c5977f8 100644
--- a/app/src/main/res/layout/gpodnetauth_credentials.xml
+++ b/app/src/main/res/layout/gpodnetauth_credentials.xml
@@ -30,7 +30,10 @@
android:layout_height="wrap_content"
android:hint="@string/username_label"
android:layout_below="@id/txtvDescription"
- android:layout_margin="8dp"/>
+ android:layout_margin="8dp"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<EditText
android:id="@+id/etxtPassword"
@@ -39,7 +42,10 @@
android:hint="@string/password_label"
android:layout_below="@id/etxtUsername"
android:inputType="textPassword"
- android:layout_margin="8dp"/>
+ android:layout_margin="8dp"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
<Button
android:id="@+id/butLogin"
diff --git a/app/src/main/res/layout/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 238beff88..f0cbb56c1 100644
--- a/app/src/main/res/layout/nav_feedlistitem.xml
+++ b/app/src/main/res/layout/nav_feedlistitem.xml
@@ -5,6 +5,7 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_iconwithtext_height"
+ android:paddingRight="@dimen/listitem_threeline_verticalpadding"
tools:background="@android:color/darker_gray">
<ImageView
@@ -24,47 +25,49 @@
tools:background="@android:color/holo_green_dark"/>
<TextView
- android:id="@+id/txtvTitle"
- android:lines="1"
- android:ellipsize="end"
- android:singleLine="true"
- android:layout_centerVertical="true"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_navdrawer"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/listitem_iconwithtext_textleftpadding"
- android:layout_marginRight="@dimen/listitem_icon_rightpadding"
- android:layout_toRightOf="@id/imgvCover"
- tools:text="Navigation feed item title"
- tools:background="@android:color/holo_green_dark"/>
-
- <TextView
android:id="@+id/txtvCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/list_vertical_padding"
android:lines="1"
android:textColor="?android:attr/textColorTertiary"
android:textSize="@dimen/text_size_navdrawer"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="@dimen/listitem_icon_rightpadding"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
tools:text="23"
tools:background="@android:color/holo_green_dark"/>
- <IconTextView
+ <com.joanzapata.iconify.widget.IconTextView
android:id="@+id/itxtvFailure"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/txtvCount"
+ android:layout_marginLeft="@dimen/list_vertical_padding"
+ android:layout_alignWithParentIfMissing="true"
android:lines="1"
android:text="{fa-exclamation-circle}"
android:textColor="@color/download_failed_red"
android:textSize="@dimen/text_size_navdrawer"
- android:layout_marginLeft="8dp"
android:layout_centerVertical="true"
tools:text="!"
tools:background="@android:color/holo_green_dark"/>
+ <TextView
+ android:id="@+id/txtvTitle"
+ android:lines="1"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:layout_centerVertical="true"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/text_size_navdrawer"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/listitem_iconwithtext_textleftpadding"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_toLeftOf="@id/itxtvFailure"
+ android:layout_alignWithParentIfMissing="true"
+ tools:text="Navigation feed item title"
+ tools:background="@android:color/holo_green_dark"/>
+
+
</RelativeLayout>
diff --git a/app/src/main/res/layout/new_episodes_fragment.xml b/app/src/main/res/layout/new_episodes_fragment.xml
deleted file mode 100644
index e90171630..000000000
--- a/app/src/main/res/layout/new_episodes_fragment.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:dslv="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <com.mobeta.android.dslv.DragSortListView
- android:id="@android:id/list"
- android:scrollbarStyle="outsideOverlay"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingTop="@dimen/list_vertical_padding"
- android:paddingBottom="@dimen/list_vertical_padding"
- android:clipToPadding="false"
- dslv:collapsed_height="2dp"
- dslv:drag_enabled="true"
- dslv:drag_handle_id="@id/drag_handle"
- dslv:drag_scroll_start="0.33"
- dslv:float_alpha="0.6"
- dslv:max_drag_scroll_speed="0.5"
- dslv:remove_enabled="true"
- dslv:remove_mode="flingRemove"
- dslv:slide_shuffle_speed="0.3"
- dslv:sort_enabled="false"
- dslv:track_drag_sort="false"
- dslv:float_background_color="?attr/dragview_float_background"
- dslv:use_default_controller="true"
- tools:background="@android:color/holo_green_dark"/>
-
- <TextView
- android:id="@id/android:empty"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:gravity="center"
- android:text="@string/no_items_label"/>
-
- <ProgressBar
- android:id="@+id/progLoading"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:indeterminateOnly="true"
- android:visibility="gone"
- tools:visibility="visible"
- tools:layout_width="match_parent"
- tools:layout_height="64dp"
- tools:background="@android:color/holo_red_light"/>
-
- <LinearLayout
- android:id="@+id/undobar"
- style="@style/UndoBar">
-
- <TextView
- android:id="@+id/undobar_message"
- style="@style/UndoBarMessage"/>
-
- <Button
- android:id="@+id/undobar_button"
- style="@style/UndoBarButton"/>
-
- </LinearLayout>
-
-</FrameLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/new_episodes_listitem.xml b/app/src/main/res/layout/new_episodes_listitem.xml
index b738cf836..0f1e873f3 100644
--- a/app/src/main/res/layout/new_episodes_listitem.xml
+++ b/app/src/main/res/layout/new_episodes_listitem.xml
@@ -1,24 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout
+ android:id="@+id/container"
+ 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">
+
+<LinearLayout
+ android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
+ android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
- <ImageView
- android:id="@+id/imgvImage"
- android:layout_width="@dimen/thumbnail_length_itemlist"
- android:layout_height="@dimen/thumbnail_length_itemlist"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
- android:contentDescription="@string/cover_label"
- android:scaleType="centerCrop"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark" />
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <TextView
+ android:id="@+id/txtvPlaceholder"
+ android:layout_width="@dimen/thumbnail_length_itemlist"
+ android:layout_height="@dimen/thumbnail_length_itemlist"
+ android:layout_gravity="center_vertical"
+ android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
+ android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
+ android:background="@color/light_gray"
+ android:ellipsize="end"
+ android:maxLines="3"
+ android:gravity="center"/>
+
+ <ImageView
+ android:id="@+id/imgvCover"
+ android:layout_height="64dp"
+ android:layout_width="64dp"
+ android:layout_alignLeft="@id/txtvPlaceholder"
+ android:layout_alignTop="@id/txtvPlaceholder"
+ android:layout_alignRight="@id/txtvPlaceholder"
+ android:layout_alignBottom="@id/txtvPlaceholder"
+ android:contentDescription="@string/cover_label"
+ tools:src="@drawable/ic_stat_antenna_default"
+ tools:background="@android:color/holo_green_dark" />
+
+ </RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
@@ -43,7 +68,7 @@
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
@@ -55,10 +80,10 @@
android:id="@+id/bottom_bar"
android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_below="@id/txtvTitle"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
- android:layout_marginTop="16dp"
tools:background="@android:color/holo_red_light" >
<TextView
@@ -93,14 +118,12 @@
tools:background="@android:color/holo_green_dark" />
<ProgressBar
- android:id="@+id/pbar_download_progress"
+ android:id="@+id/pbar_progress"
style="?android:attr/progressBarStyleHorizontal"
- android:layout_width="0dp"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:layout_toLeftOf="@id/txtvPublished"
- android:layout_toRightOf="@id/txtvDuration"
+ android:layout_below="@id/txtvDuration"
+ android:layout_marginTop="-2dp"
android:max="100" />
</RelativeLayout>
@@ -110,4 +133,6 @@
<include layout="@layout/secondary_action" />
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
+
+</FrameLayout>
diff --git a/app/src/main/res/layout/opml_import.xml b/app/src/main/res/layout/opml_import.xml
index 5ece4f09f..2a67e7ee1 100644
--- a/app/src/main/res/layout/opml_import.xml
+++ b/app/src/main/res/layout/opml_import.xml
@@ -1,92 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:paddingTop="8dp"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:paddingBottom="8dp"
- tools:background="@android:color/darker_gray">
-
- <TextView
- android:id="@+id/txtvHeadingExplanation1"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout
+ 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"
- style="@style/AntennaPod.TextView.Heading"
- android:text="@string/txtvfeedurl_label"/>
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingTop="8dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingBottom="8dp"
+ tools:background="@android:color/darker_gray">
- <TextView
- android:id="@+id/txtvExplanation1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/opml_import_explanation_1"
- android:textSize="@dimen/text_size_medium"
- android:layout_marginTop="4dp"
- tools:background="@android:color/holo_green_dark" />
+ <TextView
+ android:id="@+id/txtvHeadingExplanation1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/AntennaPod.TextView.Heading"
+ android:text="@string/txtvfeedurl_label"/>
- <Button
- android:id="@+id/butChooseFileFromFilesystem"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="8dp"
- android:text="@string/choose_file_from_filesystem" />
+ <TextView
+ android:id="@+id/txtvExplanation1"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/opml_import_explanation_1"
+ android:textSize="@dimen/text_size_medium"
+ android:layout_marginTop="4dp"
+ tools:background="@android:color/holo_green_dark" />
- <View
- android:id="@+id/divider1"
- android:layout_width="fill_parent"
- android:layout_height="1dp"
- android:layout_margin="16dp"
- android:background="?android:attr/listDivider"/>
+ <Button
+ android:id="@+id/butChooseFileFromFilesystem"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ android:text="@string/choose_file_from_filesystem" />
- <TextView
- android:id="@+id/txtvHeadingExplanation2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/AntennaPod.TextView.Heading"
- android:text="@string/txtvfeedurl_label"/>
+ <View
+ android:id="@+id/divider1"
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:layout_margin="16dp"
+ android:background="?android:attr/listDivider"/>
- <TextView
- android:id="@+id/txtvExplanation2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/opml_import_explanation_2"
- android:textSize="@dimen/text_size_medium"
- android:layout_marginTop="4dp"
- tools:background="@android:color/holo_green_dark" />
+ <TextView
+ android:id="@+id/txtvHeadingExplanation2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/AntennaPod.TextView.Heading"
+ android:text="@string/txtvfeedurl_label"/>
- <Button
- android:id="@+id/butChooseFileFromExternal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginTop="8dp"
- android:text="@string/choose_file_from_external_application" />
+ <TextView
+ android:id="@+id/txtvExplanation2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/opml_import_explanation_2"
+ android:textSize="@dimen/text_size_medium"
+ android:layout_marginTop="4dp"
+ tools:background="@android:color/holo_green_dark" />
- <View
- android:id="@+id/divider2"
- android:layout_width="fill_parent"
- android:layout_height="1dp"
- android:layout_margin="16dp"
- android:background="?android:attr/listDivider"/>
+ <Button
+ android:id="@+id/butChooseFileFromExternal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginTop="8dp"
+ android:text="@string/choose_file_from_external_application" />
- <TextView
- android:id="@+id/txtvHeadingExplanation3"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/AntennaPod.TextView.Heading"
- android:text="@string/txtvfeedurl_label"/>
+ <View
+ android:id="@+id/divider2"
+ android:layout_width="fill_parent"
+ android:layout_height="1dp"
+ android:layout_margin="16dp"
+ android:background="?android:attr/listDivider"/>
- <TextView
- android:id="@+id/txtvExplanation3"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/opml_import_explanation_3"
- android:textSize="@dimen/text_size_medium"
- android:layout_marginTop="4dp"
- tools:background="@android:color/holo_green_dark" />
+ <TextView
+ android:id="@+id/txtvHeadingExplanation3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/AntennaPod.TextView.Heading"
+ android:text="@string/txtvfeedurl_label"/>
+
+ <TextView
+ android:id="@+id/txtvExplanation3"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/opml_import_explanation_3"
+ android:textSize="@dimen/text_size_medium"
+ android:layout_marginTop="4dp"
+ tools:background="@android:color/holo_green_dark" />
-</LinearLayout> \ No newline at end of file
+ </LinearLayout>
+</ScrollView>
diff --git a/app/src/main/res/layout/pager_fragment.xml b/app/src/main/res/layout/pager_fragment.xml
index ed639a2db..54b711b1c 100644
--- a/app/src/main/res/layout/pager_fragment.xml
+++ b/app/src/main/res/layout/pager_fragment.xml
@@ -1,18 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
- <android.support.v4.view.ViewPager
- android:id="@+id/pager"
+
+ <android.support.design.widget.TabLayout
+ android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="wrap_content"
+ app:tabGravity="fill"
+ app:tabMode="fixed" />
- <android.support.v4.view.PagerTabStrip
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top" />
- </android.support.v4.view.ViewPager>
+ <android.support.v4.view.ViewPager
+ android:id="@+id/viewpager"
+ android:layout_width="match_parent"
+ android:layout_height="0px"
+ android:layout_weight="1" />
</LinearLayout> \ No newline at end of file
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_fragment.xml b/app/src/main/res/layout/queue_fragment.xml
index 307d95a8d..901ac49fc 100644
--- a/app/src/main/res/layout/queue_fragment.xml
+++ b/app/src/main/res/layout/queue_fragment.xml
@@ -1,37 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:dslv="http://schemas.android.com/apk/res-auto"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <com.mobeta.android.dslv.DragSortListView
- android:id="@android:id/list"
+ <TextView
+ android:id="@+id/info_bar"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipToPadding="false"
- android:paddingBottom="@dimen/list_vertical_padding"
- android:paddingTop="@dimen/list_vertical_padding"
- android:scrollbarStyle="outsideOverlay"
- dslv:collapsed_height="2dp"
- dslv:drag_enabled="true"
- dslv:drag_handle_id="@id/drag_handle"
- dslv:drag_scroll_start="0.33"
- dslv:float_alpha="0.6"
- dslv:float_background_color="?attr/dragview_float_background"
- dslv:max_drag_scroll_speed="0.5"
- dslv:remove_enabled="true"
- dslv:remove_mode="flingRemove"
- dslv:slide_shuffle_speed="0.3"
- dslv:sort_enabled="true"
- dslv:track_drag_sort="true"
- dslv:use_default_controller="true" />
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:gravity="center"
+ android:textSize="12sp"
+ android:text=""/>
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_below="@id/info_bar"
+ android:background="?android:attr/listDivider"/>
+
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/recyclerView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/divider"
+ android:scrollbars="vertical"/>
<TextView
android:id="@id/android:empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center"
+ android:layout_centerInParent="true"
android:gravity="center"
android:text="@string/no_items_label" />
@@ -39,22 +39,8 @@
android:id="@+id/progLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center"
+ android:layout_centerInParent="true"
android:indeterminateOnly="true"
android:visibility="gone" />
- <LinearLayout
- android:id="@+id/undobar"
- style="@style/UndoBar">
-
- <TextView
- android:id="@+id/undobar_message"
- style="@style/UndoBarMessage"/>
-
- <Button
- android:id="@+id/undobar_button"
- style="@style/UndoBarButton"/>
-
- </LinearLayout>
-
-</FrameLayout>
+</RelativeLayout>
diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml
index 39e9c72a5..8de80e355 100644
--- a/app/src/main/res/layout/queue_listitem.xml
+++ b/app/src/main/res/layout/queue_listitem.xml
@@ -1,37 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<FrameLayout
+ android:id="@+id/container"
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">
+
+<LinearLayout
+ android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
+ android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
- android:paddingLeft="16dp"
+ 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="100dp"
- android:layout_height="match_parent"
- android:layout_marginLeft="-8dp"
- android:layout_marginRight="-64dp"
+ android:layout_width="104dp"
+ android:layout_height="64dp"
+ android:layout_marginLeft="-16dp"
+ 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"
- tools:src="@drawable/ic_drag_handle"
+ tools:src="@drawable/ic_drag_vertical_grey600_48dp"
tools:background="@android:color/holo_green_dark" />
- <ImageView
- android:id="@+id/imgvImage"
- android:layout_width="@dimen/thumbnail_length_queue_item"
- android:layout_height="@dimen/thumbnail_length_queue_item"
- android:layout_gravity="center_vertical"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
- android:contentDescription="@string/cover_label"
- android:scaleType="centerCrop"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark"/>
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ 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_centerVertical="true"
+ android:gravity="center"
+ android:background="@color/light_gray"
+ android:maxLines="3"
+ android:ellipsize="end"/>
+ <ImageView
+ android:id="@+id/imgvCover"
+ 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"/>
+ </RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
@@ -53,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" />
@@ -116,4 +138,6 @@
<include layout="@layout/secondary_action"/>
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
+
+</FrameLayout>
diff --git a/app/src/main/res/layout/subscription_view.xml b/app/src/main/res/layout/subscription_view.xml
index 4bb3093a8..6eb6ba094 100644
--- a/app/src/main/res/layout/subscription_view.xml
+++ b/app/src/main/res/layout/subscription_view.xml
@@ -23,9 +23,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
- android:layout_margin="@dimen/minimum_text_margin"
+ android:layout_margin="@dimen/widget_margin"
android:ellipsize="end"
- android:textColor="@color/overlay_dark"
+ android:padding="@dimen/widget_margin"
+ style="@style/AntennaPod.TextView.Heading"
android:textSize="15sp"
android:textStyle="bold"
tools:text="@string/app_name" />
@@ -39,7 +40,7 @@
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
- android:layout_margin="@dimen/minimum_text_margin"
+ android:layout_margin="@dimen/widget_margin"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/white"
@@ -51,6 +52,7 @@
android:layout_height="18dp"
android:layout_alignParentBottom="true"
android:layout_margin="5dp"
+ android:visibility="gone"
android:background="@drawable/unread_circle">
<TextView
diff --git a/app/src/main/res/layout/time_dialog.xml b/app/src/main/res/layout/time_dialog.xml
index a42f87781..b270e82f7 100644
--- a/app/src/main/res/layout/time_dialog.xml
+++ b/app/src/main/res/layout/time_dialog.xml
@@ -1,57 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical" >
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center">
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal" >
+ android:orientation="horizontal">
<EditText
android:id="@+id/etxtTime"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="8dp"
- android:ems="7"
+ android:ems="2"
android:hint="@string/enter_time_here_label"
- android:inputType="number"
- android:maxLength="2" >
-
- </EditText>
+ android:inputType="number"
+ android:maxLength="2" />
<Spinner
android:id="@+id/spTimeUnit"
- android:layout_width="180dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp" />
+
</LinearLayout>
<LinearLayout
- style="@android:style/ButtonBar"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal" >
+ android:orientation="vertical">
- <Button
- android:id="@+id/butConfirm"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginRight="8dp"
- android:layout_weight="1"
- tools:text="Confirm" />
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:textSize="16sp"
+ android:text="@string/timer_about_to_expire_label"/>
+
+ <CheckBox android:id="@+id/cbShakeToReset"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/shake_to_reset_label"/>
+
+ <CheckBox android:id="@+id/cbVibrate"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/timer_vibration_label"/>
- <Button
- android:id="@+id/butCancel"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- tools:text="Cancel" />
</LinearLayout>
</LinearLayout> \ No newline at end of file
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/allepisodes_context.xml b/app/src/main/res/menu/allepisodes_context.xml
index f89ad5065..c5356535c 100644
--- a/app/src/main/res/menu/allepisodes_context.xml
+++ b/app/src/main/res/menu/allepisodes_context.xml
@@ -24,7 +24,14 @@
android:id="@+id/remove_from_queue_item"
android:menuCategory="container"
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"
@@ -40,13 +47,32 @@
android:title="@string/deactivate_auto_download" />
<item
- android:id="@+id/share_link_item"
- android:menuCategory="container"
- android:title="@string/share_link_label" />
- <item
android:id="@+id/visit_website_item"
android:menuCategory="container"
android:title="@string/visit_website_label" />
+ <item
+ android:id="@+id/share_item"
+ android:menuCategory="container"
+ android:title="@string/share_label">
+ <menu>
+ <item
+ android:id="@+id/share_link_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_label" />
+ <item
+ android:id="@+id/share_link_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_with_position_label" />
+ <item
+ android:id="@+id/share_download_url_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_label" />
+ <item
+ android:id="@+id/share_download_url_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_with_position_label" />
+ </menu>
+ </item>
<item
android:id="@+id/support_item"
diff --git a/app/src/main/res/menu/episodes_apply_action_options.xml b/app/src/main/res/menu/episodes_apply_action_options.xml
new file mode 100644
index 000000000..90cba0966
--- /dev/null
+++ b/app/src/main/res/menu/episodes_apply_action_options.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/sort"
+ android:icon="?attr/ic_sort"
+ android:title="@string/sort"
+ app:showAsAction="always">
+ <menu>
+ <item android:id="@+id/sort_title_a_z"
+ android:title="@string/sort_title_a_z"/>
+ <item android:id="@+id/sort_title_z_a"
+ android:title="@string/sort_title_z_a"/>
+ <item android:id="@+id/sort_date_new_old"
+ android:title="@string/sort_date_new_old"/>
+ <item android:id="@+id/sort_date_old_new"
+ android:title="@string/sort_date_old_new"/>
+ <item android:id="@+id/sort_duration_short_long"
+ android:title="@string/sort_duration_short_long"/>
+ <item android:id="@+id/sort_duration_long_short"
+ android:title="@string/sort_duration_long_short"/>
+ </menu>
+ </item>
+
+ <item
+ android:id="@+id/select_options"
+ android:icon="?attr/ic_filter"
+ android:title="@string/filter"
+ app:showAsAction="always">
+
+ <menu>
+ <item android:id="@+id/check_all"
+ android:title="@string/all_label"/>
+ <item android:id="@+id/check_none"
+ android:title="@string/none_label"/>
+ <item android:id="@+id/check_played"
+ android:title="@string/played_label"/>
+ <item android:id="@+id/check_unplayed"
+ android:title="@string/unplayed_label"/>
+ <item android:id="@+id/check_downloaded"
+ android:title="@string/downloaded_label"/>
+ <item android:id="@+id/check_not_downloaded"
+ android:title="@string/not_downloaded_label"/>
+ </menu>
+ </item>
+
+ <item
+ android:id="@+id/select_toggle"
+ android:title="@string/select_all_label"
+ app:showAsAction="always"/>
+</menu>
diff --git a/app/src/main/res/menu/feedinfo.xml b/app/src/main/res/menu/feedinfo.xml
index be50cb87d..9fdd56b6c 100644
--- a/app/src/main/res/menu/feedinfo.xml
+++ b/app/src/main/res/menu/feedinfo.xml
@@ -20,9 +20,9 @@
android:title="@string/share_link_label">
</item>
<item
- android:id="@+id/share_source_item"
+ android:id="@+id/share_download_url_item"
custom:showAsAction="collapseActionView"
- android:title="@string/share_source_label">
+ android:title="@string/share_feed_url_label">
</item>
-</menu> \ No newline at end of file
+</menu>
diff --git a/app/src/main/res/menu/feeditem_options.xml b/app/src/main/res/menu/feeditem_options.xml
index f8e9b9c75..898081486 100644
--- a/app/src/main/res/menu/feeditem_options.xml
+++ b/app/src/main/res/menu/feeditem_options.xml
@@ -31,6 +31,16 @@
</item>
<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"
custom:showAsAction="collapseActionView"
android:title="@string/reset_position">
@@ -48,17 +58,36 @@
</item>
<item
- android:id="@+id/share_link_item"
- custom:showAsAction="collapseActionView"
- android:title="@string/share_link_label">
- </item>
- <item
android:id="@+id/visit_website_item"
android:icon="?attr/location_web_site"
custom:showAsAction="ifRoom|collapseActionView"
android:title="@string/visit_website_label">
</item>
<item
+ android:id="@+id/share_item"
+ android:menuCategory="container"
+ android:title="@string/share_label">
+ <menu>
+ <item
+ android:id="@+id/share_link_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_label" />
+ <item
+ android:id="@+id/share_link_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_with_position_label" />
+ <item
+ android:id="@+id/share_download_url_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_label" />
+ <item
+ android:id="@+id/share_download_url_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_with_position_label" />
+ </menu>
+ </item>
+
+ <item
android:id="@+id/support_item"
custom:showAsAction="collapseActionView"
android:title="@string/support_label">
diff --git a/app/src/main/res/menu/feeditemlist_context.xml b/app/src/main/res/menu/feeditemlist_context.xml
index f89ad5065..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" />
@@ -40,13 +49,33 @@
android:title="@string/deactivate_auto_download" />
<item
- android:id="@+id/share_link_item"
- android:menuCategory="container"
- android:title="@string/share_link_label" />
- <item
android:id="@+id/visit_website_item"
android:menuCategory="container"
android:title="@string/visit_website_label" />
+ <item
+ android:id="@+id/share_item"
+ android:menuCategory="container"
+ android:title="@string/share_label">
+ <menu>
+ <item
+ android:id="@+id/share_link_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_label" />
+ <item
+ android:id="@+id/share_link_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_with_position_label" />
+ <item
+ android:id="@+id/share_download_url_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_label" />
+ <item
+ android:id="@+id/share_download_url_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_with_position_label" />
+ </menu>
+ </item>
+
<item
android:id="@+id/support_item"
diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml
index e0da72667..ed03c08d6 100644
--- a/app/src/main/res/menu/feedlist.xml
+++ b/app/src/main/res/menu/feedlist.xml
@@ -3,10 +3,16 @@
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
+ android:id="@+id/episode_actions"
+ android:menuCategory="container"
+ android:title="@string/episode_actions"
custom:showAsAction="always">
</item>
<item
@@ -31,18 +37,34 @@
android:title="@string/search_label"/>
<item
- android:id="@+id/mark_all_read_item"
- android:menuCategory="container"
- android:title="@string/mark_all_read_label"
- custom:showAsAction="collapseActionView">
- </item>
- <item
android:id="@+id/support_item"
android:menuCategory="container"
android:title="@string/support_label"
android:visible="false"
custom:showAsAction="collapseActionView">
</item>
+
+ <item
+ android:id="@+id/visit_website_item"
+ android:icon="?attr/location_web_site"
+ android:menuCategory="container"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/visit_website_label"
+ android:visible="true">
+ </item>
+ <item
+ android:id="@+id/share_link_item"
+ android:menuCategory="container"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/share_link_label">
+ </item>
+ <item
+ android:id="@+id/share_download_url_item"
+ android:menuCategory="container"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/share_feed_url_label">
+ </item>
+
<item
android:id="@+id/remove_item"
android:icon="?attr/content_discard"
diff --git a/app/src/main/res/menu/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 288e44401..c4086bf5e 100644
--- a/app/src/main/res/menu/mediaplayer.xml
+++ b/app/src/main/res/menu/mediaplayer.xml
@@ -3,21 +3,37 @@
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/share_link_item"
- custom:showAsAction="collapseActionView"
- android:title="@string/share_link_label">
+ android:id="@+id/audio_controls"
+ android:title="@string/audio_controls"
+ custom:showAsAction="always">
</item>
+
<item
android:id="@+id/visit_website_item"
android:icon="?attr/location_web_site"
@@ -26,15 +42,35 @@
android:visible="false">
</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
+ android:id="@+id/share_link_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_label" />
+ <item
+ android:id="@+id/share_link_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_with_position_label" />
+ <item
+ android:id="@+id/share_download_url_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_label" />
+ <item
+ android:id="@+id/share_download_url_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_with_position_label" />
+ </menu>
+ </item>
+ <item
android:id="@+id/support_item"
custom:showAsAction="collapseActionView"
android:title="@string/support_label"
android:visible="false">
</item>
- <item
- android:id="@id/skip_episode_item"
- custom:showAsAction="collapseActionView"
- android:title="@string/skip_episode_label"
- android:visible="true"/>
</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/nav_feed_context.xml b/app/src/main/res/menu/nav_feed_context.xml
new file mode 100644
index 000000000..4bf067d25
--- /dev/null
+++ b/app/src/main/res/menu/nav_feed_context.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:id="@+id/mark_all_seen_item"
+ android:menuCategory="container"
+ android:title="@string/mark_all_seen_label" />
+
+ <item
+ android:id="@+id/mark_all_read_item"
+ android:menuCategory="container"
+ android:title="@string/mark_all_read_label" />
+
+ <item
+ android:id="@+id/remove_item"
+ android:menuCategory="container"
+ android:title="@string/remove_feed_label" />
+
+</menu>
diff --git a/app/src/main/res/menu/queue_context.xml b/app/src/main/res/menu/queue_context.xml
index 6ab2daabf..3eb1d9d5e 100644
--- a/app/src/main/res/menu/queue_context.xml
+++ b/app/src/main/res/menu/queue_context.xml
@@ -28,6 +28,14 @@
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" />
@@ -36,21 +44,38 @@
android:id="@+id/activate_auto_download"
android:menuCategory="container"
android:title="@string/activate_auto_download" />
-
<item
android:id="@+id/deactivate_auto_download"
android:menuCategory="container"
android:title="@string/deactivate_auto_download" />
<item
- android:id="@+id/share_link_item"
- android:menuCategory="container"
- android:title="@string/share_link_label" />
- <item
android:id="@+id/visit_website_item"
android:menuCategory="container"
android:title="@string/visit_website_label" />
-
+ <item
+ android:id="@+id/share_item"
+ android:menuCategory="container"
+ android:title="@string/share_label">
+ <menu>
+ <item
+ android:id="@+id/share_link_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_label" />
+ <item
+ android:id="@+id/share_link_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_link_with_position_label" />
+ <item
+ android:id="@+id/share_download_url_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_label" />
+ <item
+ android:id="@+id/share_download_url_with_position_item"
+ android:menuCategory="container"
+ android:title="@string/share_item_url_with_position_label" />
+ </menu>
+ </item>
<item
android:id="@+id/support_item"
android:menuCategory="container"
diff --git a/app/src/main/res/xml/player_widget_info.xml b/app/src/main/res/xml/player_widget_info.xml
index 831f6daf0..1dbfabcd6 100644
--- a/app/src/main/res/xml/player_widget_info.xml
+++ b/app/src/main/res/xml/player_widget_info.xml
@@ -1,4 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
-<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:resizeMode="none" android:initialLayout="@layout/player_widget" android:minHeight="40dp" android:minWidth="250dp">
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+ android:resizeMode="horizontal"
+ android:initialLayout="@layout/player_widget"
+ android:updatePeriodMillis="86400000"
+ android:previewImage="@drawable/ic_widget_preview"
+ android:minHeight="40dp"
+ android:minWidth="250dp"
+ android:minResizeWidth="40dp">
</appwidget-provider> \ No newline at end of file
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index e848915cf..3ecd79b1e 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -1,34 +1,70 @@
<?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">
- <ListPreference
+ <com.afollestad.materialdialogs.prefs.MaterialListPreference
android:entryValues="@array/theme_values"
android:entries="@array/theme_options"
android:title="@string/pref_set_theme_title"
android:key="prefTheme"
android:summary="@string/pref_set_theme_sum"
- android:defaultValue="0"/>
- <Preference
- android:key="prefHiddenDrawerItems"
- android:summary="@string/pref_nav_drawer_items_sum"
- android:title="@string/pref_nav_drawer_items_title" />
- <CheckBoxPreference
+ android:defaultValue="0"
+ app:useStockLayout="true"/>
+ <PreferenceScreen
+ android:key="prefDrawerSettings"
+ android:summary="@string/pref_nav_drawer_sum"
+ android:title="@string/pref_nav_drawer_title">
+ <Preference
+ android:key="prefHiddenDrawerItems"
+ android:summary="@string/pref_nav_drawer_items_sum"
+ android:title="@string/pref_nav_drawer_items_title" />
+ <com.afollestad.materialdialogs.prefs.MaterialListPreference
+ android:entryValues="@array/nav_drawer_feed_order_values"
+ android:entries="@array/nav_drawer_feed_order_options"
+ android:title="@string/pref_nav_drawer_feed_order_title"
+ android:key="prefDrawerFeedOrder"
+ android:summary="@string/pref_nav_drawer_feed_order_sum"
+ android:defaultValue="0"
+ 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"
+ app:useStockLayout="true"/>
+ </PreferenceScreen>
+ <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
- android:defaultValue="false"
+ <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"/>
+ <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"/>
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ android:key="prefShowDownloadReport"
+ android:summary="@string/pref_showDownloadReport_sum"
+ android:title="@string/pref_showDownloadReport_title"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/queue_label">
- <CheckBoxPreference
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="false"
android:enabled="true"
android:key="prefQueueAddToFront"
@@ -38,50 +74,76 @@
</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_pauseOnHeadsetDisconnect_sum"
+ 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"/>
+ <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"/>
+ <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"/>
+ <de.danoeh.antennapod.preferences.SwitchCompatPreference
android:defaultValue="false"
android:enabled="true"
android:key="prefAutoDelete"
android:summary="@string/pref_auto_delete_sum"
android:title="@string/pref_auto_delete_title"/>
- <ListPreference
+ <com.afollestad.materialdialogs.prefs.MaterialListPreference
android:defaultValue="30"
android:entries="@array/smart_mark_as_played_values"
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"
@@ -90,44 +152,51 @@
</PreferenceCategory>
<PreferenceCategory android:title="@string/network_pref">
- <ListPreference
- android:defaultValue="0"
- android:entries="@array/update_intervall_values"
- android:entryValues="@array/update_intervall_values"
+ <Preference
android:key="prefAutoUpdateIntervall"
- android:summary="@string/pref_autoUpdateIntervall_sum"
- android:title="@string/pref_autoUpdateIntervall_title"/>
- <CheckBoxPreference
+ android:summary="@string/pref_autoUpdateIntervallOrTime_sum"
+ android:title="@string/pref_autoUpdateIntervallOrTime_title"/>
+ <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"/>
- <de.danoeh.antennapod.preferences.CustomEditTextPreference
- android:defaultValue="6"
+ <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"
+ app:useStockLayout="true"/>
+ <com.afollestad.materialdialogs.prefs.MaterialEditTextPreference
+ android:defaultValue="4"
android:inputType="number"
android:key="prefParallelDownloads"
- android:title="@string/pref_parallel_downloads_title"/>
- <ListPreference
- 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"/>
@@ -182,19 +251,23 @@
<Preference
android:title="@string/choose_data_directory"
android:key="prefChooseDataDir"/>
- <Preference
- android:key="prefFlattrThisApp"
- android:summary="@string/pref_flattr_this_app_sum"
- android:title="@string/pref_flattr_this_app_title">
- </Preference>
+ <ListPreference
+ android:entryValues="@array/image_cache_size_values"
+ android:entries="@array/image_cache_size_options"
+ android:title="@string/pref_image_cache_size_title"
+ android:key="prefImageCacheSize"
+ android:summary="@string/pref_image_cache_size_sum"
+ android:defaultValue="100"/>
<Preference
android:key="prefOpmlExport"
android:title="@string/opml_export_label"/>
<Preference
android:key="prefAbout"
android:title="@string/about_pref"/>
-
-
+ <Preference
+ android:key="prefSendCrashReport"
+ android:title="@string/crash_report_title"
+ android:summary="@string/crash_report_sum"/>
</PreferenceCategory>
-
+
</PreferenceScreen>
diff --git a/app/src/main/templates/about.html b/app/src/main/templates/about.html
index 3313fa12f..222429b17 100644
--- a/app/src/main/templates/about.html
+++ b/app/src/main/templates/about.html
@@ -4,16 +4,22 @@
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<style type="text/css">
+ @font-face {
+ font-family: 'Roboto-Light';
+ src: url('file:///android_asset/Roboto-Light.ttf');
+ }
+
* {
- font-family: Helvetica
+ color: %s;
+ font-family: roboto-Light;
+ font-size: 12pt;
}
+
header {
display: block;
margin-left: auto;
margin-right: auto;
padding-bottom: 500px;
-
-
}
versiontag {
@@ -39,51 +45,63 @@
</head>
<body>
<div id="header" align="center">
- <img src="logo.png" alt="Logo" width="100px" height="100px"/>
+ <img src="file:///android_asset/logo.png" alt="Logo" width="100px" height="100px"/>
- <p>AntennaPod, Version @versionname@, Build @versioncode@</p>
+ <p>AntennaPod, Version @versionname@</p>
+ <p>Commit: @commit@</p>
<p>Created by Daniel Oeh</p>
- <p>Copyright © 2015 AntennaPod Contributors <a href="https://github.com/AntennaPod/AntennaPod/blob/master/CONTRIBUTORS">(View)</a></p>
+ <p>Copyright &copy; 2015 AntennaPod Contributors <a href="CONTRIBUTORS.txt">(View)</a></p>
- <p>Licensed under the MIT License <a href="LICENSE.html">(View)</a></p>
+ <p>Licensed under the MIT License <a href="LICENSE.txt">(View)</a></p>
</div>
<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_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>drag-sort-listview <a href="https://github.com/bauerca/drag-sort-listview">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_DSLV.txt">(View)</a>
+<h2>Glide <a href="https://github.com/bumptech/glide/">(Link)</a></h2>
+licensed under the Simplified BSD license <a href="LICENSE_GLIDE.txt">(View)</a>
-<h2>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>Iconify <a href="https://github.com/JoanZapata/android-iconify">(Link)</a></h2>
+by Joan Zapata, licensed under the Apache 2.0 license <a href="LICENSE_ANDROID_ICONIFY.txt">(View)</a>
<h2>jsoup <a href="http://jsoup.org/">(Link)</a></h2>
licensed under the MIT license <a href="LICENSE_JSOUP.txt">(View)</a>
-<h2>Picasso <a href="https://github.com/square/picasso">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_PICASSO.txt">(View)</a>
+<h2>Material Design Icons <a href="https://github.com/google/material-design-icons">(Link)</a></h2>
+by Google, licensed under an Attribution-ShareAlike 4.0 International license <a href="LICENSE_MATERIAL_DESIGN_ICONS.txt">(View)</a>
+
+<h2>Material Dialogs <a href="https://github.com/afollestad/material-dialogs">(Link)</a></h2>
+by Aidan Michael Follestad, licensed under the MIT License <a href="LICENSE_MATERIAL_DIALOGS.txt">(View)</a>
<h2>OkHttp <a href="https://github.com/square/okhttp">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_OKHTTP.txt">(View)</a>
+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>
-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>Material Design Icons <a href="https://github.com/google/material-design-icons">(Link)</a></h2>
-by Google, licensed under an Attribution-ShareAlike 4.0 International license <a href="LICENSE_MATERIAL_DESIGN_ICONS.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>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>
+<h2>RecyclerView-FlexibleDivider <a href="https://github.com/yqritc/RecyclerView-FlexibleDivider">(Link)</a></h2>
+licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-<h2>Iconify <a href="https://github.com/JoanZapata/android-iconify">(Link)</a></h2>
-by Joan Zapata, licensed under the Apache 2.0 license <a href="LICENSE_ANDROID_ICONIFY.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_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_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_APACHE-2.0.txt">(View)</a>
</body>
</html>