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/handler/FeedHandlerTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java109
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java78
-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.java130
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java170
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java14
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java185
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java47
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java278
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java180
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java304
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java39
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/RewindAfterPauseUtilTest.java51
-rw-r--r--app/src/main/AndroidManifest.xml14
-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_ANTENNAPOD_AUDIOPLAYER.txt201
-rw-r--r--app/src/main/assets/LICENSE_DSLV.txt16
-rw-r--r--app/src/main/assets/LICENSE_FLEXIBLE_DIVIDER.txt201
-rw-r--r--app/src/main/assets/LICENSE_MATERIAL_DIALOGS.txt21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java24
-rw-r--r--app/src/main/java/de/danoeh/antennapod/UpdateManager.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java128
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java259
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java255
-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.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java196
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java82
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java569
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromIntentActivity.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java27
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java (renamed from app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java)222
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java245
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java348
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/DBTasksCallbacksImpl.java3
-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.java160
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java31
-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.java133
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java313
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java92
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java74
-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.java39
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java104
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java300
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java115
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java114
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java88
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java481
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java92
-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.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java38
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java6
-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.java269
-rw-r--r--app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java5
-rw-r--r--app/src/main/res/layout-v14/directory_chooser.xml2
-rw-r--r--app/src/main/res/layout-v14/download_authentication_activity.xml2
-rw-r--r--app/src/main/res/layout-v14/time_dialog.xml73
-rw-r--r--app/src/main/res/layout/all_episodes_fragment.xml33
-rw-r--r--app/src/main/res/layout/audioplayer_activity.xml2
-rw-r--r--app/src/main/res/layout/choose_speed_dialog.xml45
-rw-r--r--app/src/main/res/layout/directory_chooser.xml2
-rw-r--r--app/src/main/res/layout/download_authentication_activity.xml2
-rw-r--r--app/src/main/res/layout/external_player_fragment.xml2
-rw-r--r--app/src/main/res/layout/feedinfo.xml2
-rw-r--r--app/src/main/res/layout/feeditemlist_header.xml2
-rw-r--r--app/src/main/res/layout/nav_feedlistitem.xml2
-rw-r--r--app/src/main/res/layout/new_episodes_fragment.xml67
-rw-r--r--app/src/main/res/layout/new_episodes_listitem.xml1
-rw-r--r--app/src/main/res/layout/pager_fragment.xml20
-rw-r--r--app/src/main/res/layout/queue_fragment.xml40
-rw-r--r--app/src/main/res/layout/queue_listitem.xml18
-rw-r--r--app/src/main/res/layout/time_dialog.xml59
-rw-r--r--app/src/main/res/menu/allepisodes_context.xml9
-rw-r--r--app/src/main/res/menu/feeditem_options.xml10
-rw-r--r--app/src/main/res/menu/queue_context.xml8
-rw-r--r--app/src/main/res/xml/preferences.xml54
-rw-r--r--app/src/main/templates/about.html33
105 files changed, 5287 insertions, 3806 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/handler/FeedHandlerTest.java b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
index 13a0e3f78..5836bb699 100644
--- a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
@@ -173,7 +173,7 @@ public class FeedHandlerTest extends InstrumentationTestCase {
feed.getItems().add(item);
if (withFeedMedia) {
item.setMedia(new FeedMedia(0, item, 4711, 0, 1024*1024, "audio/mp3", null, "http://example.com/media-" + i,
- false, null, 0));
+ false, null, 0, 0));
}
}
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 42fa84d20..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,7 +1,6 @@
package de.test.antennapod.service.playback;
import android.content.Context;
-import android.media.RemoteControlClient;
import android.test.InstrumentationTestCase;
import junit.framework.AssertionFailedError;
@@ -45,7 +44,7 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
@Override
protected void tearDown() throws Exception {
super.tearDown();
- PodDBAdapter.deleteDatabase(getInstrumentation().getTargetContext());
+ PodDBAdapter.deleteDatabase();
httpServer.stop();
}
@@ -54,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");
@@ -119,12 +118,12 @@ public class PlaybackServiceMediaPlayerTest extends InstrumentationTestCase {
Feed f = new Feed(0, new Date(), "f", "l", "d", null, null, null, null, "i", null, null, "l", false);
FeedPreferences prefs = new FeedPreferences(f.getId(), false, FeedPreferences.AutoDeleteAction.NO, null, null);
f.setPreferences(prefs);
- f.setItems(new ArrayList<FeedItem>());
+ 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);
@@ -184,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);
@@ -257,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);
@@ -334,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);
@@ -412,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);
@@ -483,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);
@@ -555,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);
@@ -630,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);
@@ -708,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);
@@ -751,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) {
@@ -761,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 {
@@ -838,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);
@@ -949,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) {
@@ -1035,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);
@@ -1134,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 edb576249..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(), 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 11a34813a..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,25 +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);
+
+ // create new database
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.close();
}
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++) {
@@ -54,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);
@@ -73,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());
@@ -84,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++) {
@@ -98,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);
@@ -107,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);
@@ -119,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++) {
@@ -137,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();
@@ -160,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++) {
@@ -173,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++) {
@@ -189,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) {
@@ -207,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();
@@ -215,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) {
@@ -232,13 +221,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> unread = new ArrayList<FeedItem>();
+ List<FeedItem> unread = new ArrayList<>();
Random random = new Random();
while (unread.size() < numItems) {
@@ -249,7 +237,7 @@ public class DBReaderTest extends InstrumentationTestCase {
unread.add(item);
}
}
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setFeedItemlist(unread);
adapter.close();
@@ -257,11 +245,10 @@ 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) {
@@ -270,7 +257,6 @@ public class DBReaderTest extends InstrumentationTestCase {
}
public void testGetNewItemIds() {
- final Context context = getInstrumentation().getTargetContext();
final int numItems = 10;
List<FeedItem> unread = saveUnreadItems(numItems);
@@ -278,7 +264,7 @@ public class DBReaderTest extends InstrumentationTestCase {
for (int i = 0; i < unread.size(); i++) {
unreadIds[i] = unread.get(i).getId();
}
- List<FeedItem> unreadSaved = DBReader.getUnreadItemsList(context);
+ List<FeedItem> unreadSaved = DBReader.getUnreadItemsList();
assertNotNull(unreadSaved);
assertTrue(unread.size() == unreadSaved.size());
for(int i=0; i < unreadSaved.size(); i++) {
@@ -295,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();
@@ -314,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++) {
@@ -325,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++) {
@@ -338,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_NEW = 2;
- List<Feed> feeds = DBTestUtils.saveFeedlist(context, NUM_FEEDS, NUM_ITEMS, true);
- PodDBAdapter adapter = new PodDBAdapter(context);
+ List<Feed> feeds = DBTestUtils.saveFeedlist(NUM_FEEDS, NUM_ITEMS, true);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
for (int i = 0; i < NUM_NEW; i++) {
FeedItem item = feeds.get(0).getItems().get(i);
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);
@@ -371,7 +353,7 @@ 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_NEW, navDrawerData.numNewItems);
assertEquals(NUM_QUEUE, navDrawerData.queueSize);
@@ -379,7 +361,7 @@ public class DBReaderTest extends InstrumentationTestCase {
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());
@@ -388,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());
@@ -398,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());
}
@@ -412,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());
@@ -428,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 944fc7792..1894d6585 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
@@ -12,7 +12,6 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
-import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -21,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;
@@ -31,184 +29,36 @@ 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);
+ // 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.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<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(), 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));
- 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(), FeedItem.UNPLAYED, 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(), FeedItem.PLAYED, 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);
- adapter.open();
- adapter.setMedia(m);
- adapter.close();
-
- testPerformAutoCleanupShouldNotDeleteBecauseInQueue();
- }
-
- @FlakyTest(tolerance = 3)
public void testUpdateFeedNewFeed() {
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(), FeedItem.UNPLAYED, feed));
}
@@ -228,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];
@@ -242,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), FeedItem.PLAYED, feed));
}
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -271,7 +121,7 @@ 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);
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 c6f702f3d..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,13 +40,13 @@ 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(),
FeedItem.PLAYED, f, withChapters);
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 c5fa2f131..f5240b873 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(), 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");
@@ -111,14 +144,14 @@ public class DBWriterTest extends InstrumentationTestCase {
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,7 +172,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());
assertEquals(0, c.getCount());
@@ -164,7 +197,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<>());
feed.setImage(null);
@@ -178,11 +211,11 @@ public class DBWriterTest extends InstrumentationTestCase {
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 +233,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 +262,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,7 +275,7 @@ 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);
@@ -257,7 +290,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");
@@ -273,7 +306,7 @@ public class DBWriterTest extends InstrumentationTestCase {
}
- PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getContext());
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -289,7 +322,7 @@ 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);
@@ -309,7 +342,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");
@@ -327,7 +360,7 @@ public class DBWriterTest extends InstrumentationTestCase {
item.setImage(itemImage);
}
- PodDBAdapter adapter = new PodDBAdapter(getInstrumentation().getContext());
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -344,7 +377,7 @@ 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);
@@ -367,7 +400,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");
@@ -384,11 +417,11 @@ public class DBWriterTest extends InstrumentationTestCase {
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();
@@ -456,11 +489,11 @@ public class DBWriterTest extends InstrumentationTestCase {
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,7 +507,7 @@ 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);
@@ -497,10 +530,10 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", new Date(), "title");
feed.setItems(new ArrayList<FeedItem>());
FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
- FeedMedia media = new FeedMedia(0, item, 10, 0, 1, "mime", null, "url", false, playbackCompletionDate, 0);
+ 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 +542,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 +555,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 +571,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(), 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 +587,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 +598,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>());
+ 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 +622,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>());
+ 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 +642,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 +658,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 +672,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 +688,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(), 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 +704,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 +732,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(), 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 +755,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,7 +777,6 @@ 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>());
@@ -758,7 +785,7 @@ public class DBWriterTest extends InstrumentationTestCase {
feed.getItems().add(item);
}
- PodDBAdapter adapter = new PodDBAdapter(context);
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
adapter.setCompleteFeed(feed);
adapter.close();
@@ -768,24 +795,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.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(), 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 +821,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.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 65c962f01..c09f40b17 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;
@@ -45,8 +44,10 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
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();
@@ -60,7 +61,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
uiTestUtils.tearDown();
solo.finishOpenedActivities();
- PodDBAdapter.deleteDatabase(getInstrumentation().getTargetContext());
+ PodDBAdapter.deleteDatabase();
// reset preferences
prefs.edit().clear().commit();
@@ -79,7 +80,7 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
solo.clickOnText(solo.getString(R.string.add_feed_label));
solo.enterText(0, feed.getDownload_url());
solo.clickOnButton(solo.getString(R.string.confirm_label));
- solo.waitForActivity(DefaultOnlineFeedViewActivity.class);
+ 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 +91,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 +99,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();
@@ -152,23 +147,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,11 +174,11 @@ 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();
@@ -195,14 +190,14 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
solo.clickOnText(solo.getString(R.string.confirm_label));
solo.waitForDialogToClose();
List<String> hidden = UserPreferences.getHiddenDrawerItems();
- assertEquals(6, hidden.size());
+ 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..c43757546
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
@@ -0,0 +1,278 @@
+package de.test.antennapod.ui;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Build;
+import android.preference.PreferenceManager;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.FlakyTest;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ListView;
+
+import com.robotium.solo.Solo;
+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.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.service.playback.PlayerStatus;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+
+/**
+ * test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
+ */
+@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();
+
+ PodDBAdapter.deleteDatabase();
+
+ context = getInstrumentation().getTargetContext();
+
+ 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.clickOnScreen(50, 50);
+ 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..195e3d250 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,33 @@ 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();
+ PodDBAdapter.deleteDatabase();
- // create database
- PodDBAdapter adapter = new PodDBAdapter(context);
- adapter.open();
- adapter.close();
+ context = getInstrumentation().getTargetContext();
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();
@@ -86,86 +96,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 +221,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 a6af6c544..fabc399ba 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -14,10 +14,15 @@ 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> {
@@ -59,12 +64,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,140 +78,79 @@ 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() {
@@ -225,31 +164,16 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
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());
+ assertTrue(solo.waitForCondition(() -> pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_pausePlaybackForFocusLoss_title));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss();
- }
- }, Timeout.getLargeTimeout());
+ assertTrue(solo.waitForCondition(() -> pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
}
public void testDisableUpdateInterval() {
solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_sum));
solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_Disable));
- solo.waitForCondition(new Condition() {
- @Override
- public boolean isSatisfied() {
- return UserPreferences.getUpdateInterval() == 0;
- }
- }, 1000);
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() == 0, 1000));
}
public void testSetUpdateInterval() {
@@ -260,30 +184,16 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
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() {
@@ -292,12 +202,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() {
@@ -306,12 +211,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() {
@@ -333,12 +233,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
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() {
@@ -350,12 +245,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
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()));
}
@@ -367,12 +257,7 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
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() {
@@ -380,50 +265,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()));
+ }
+
+ 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 7bbc6e462..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();
}
}
@@ -116,7 +123,7 @@ public class UITestUtils {
}
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);
@@ -149,7 +156,7 @@ public class UITestUtils {
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);
@@ -174,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) {
@@ -206,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/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/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7ee5c880a..07a02ddfb 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.danoeh.antennapod"
- android:versionCode="1030401"
- android:versionName="1.3.4.1">
+ android:versionCode="1040011"
+ android:versionName="1.4.0.11">
<!--
Version code schema:
"1.2.3-SNAPSHOT" -> 1020300
@@ -140,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"
@@ -203,7 +206,7 @@
android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
<activity
- android:name=".activity.DefaultOnlineFeedViewActivity"
+ android:name=".activity.OnlineFeedViewActivity"
android:configChanges="orientation"
android:label="@string/add_feed_label">
<meta-data
@@ -325,6 +328,11 @@
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"
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_ANTENNAPOD_AUDIOPLAYER.txt b/app/src/main/assets/LICENSE_ANTENNAPOD_AUDIOPLAYER.txt
new file mode 100644
index 000000000..8dada3eda
--- /dev/null
+++ b/app/src/main/assets/LICENSE_ANTENNAPOD_AUDIOPLAYER.txt
@@ -0,0 +1,201 @@
+ 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_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_FLEXIBLE_DIVIDER.txt b/app/src/main/assets/LICENSE_FLEXIBLE_DIVIDER.txt
new file mode 100644
index 000000000..5c304d1a4
--- /dev/null
+++ b/app/src/main/assets/LICENSE_FLEXIBLE_DIVIDER.txt
@@ -0,0 +1,201 @@
+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_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/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index 026e3b087..83bc9afb2 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -2,10 +2,16 @@ 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 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;
@@ -34,14 +40,32 @@ public class PodcastApp extends Application {
@Override
public void onCreate() {
super.onCreate();
+
+ 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;
+ PodDBAdapter.init(this);
UpdateManager.init(this);
UserPreferences.init(this);
PlaybackPreferences.init(this);
NetworkUtils.init(this);
EventDistributor.getInstance();
+ Iconify.with(new FontAwesomeModule());
SPAUtil.sendSPAppsQueryFeedsIntent(this);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/UpdateManager.java b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java
index 2f6bb1b50..b1d7fffc8 100644
--- a/app/src/main/java/de/danoeh/antennapod/UpdateManager.java
+++ b/app/src/main/java/de/danoeh/antennapod/UpdateManager.java
@@ -65,9 +65,9 @@ public class UpdateManager {
// from now on, Glide will handle caching images
new Thread() {
public void run() {
- List<Feed> feeds = DBReader.getFeedList(context);
+ List<Feed> feeds = DBReader.getFeedList();
for (Feed podcast : feeds) {
- List<FeedItem> episodes = DBReader.getFeedItemList(context, podcast);
+ List<FeedItem> episodes = DBReader.getFeedItemList(podcast);
for (FeedItem episode : episodes) {
FeedImage image = episode.getImage();
if (image != null && image.isDownloaded() && image.getFile_url() != null) {
@@ -76,7 +76,7 @@ public class UpdateManager {
imageFile.delete();
}
image.setFile_url(null); // calls setDownloaded(false)
- DBWriter.setFeedImage(context, image);
+ DBWriter.setFeedImage(image);
}
}
}
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 035ab2f22..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,45 +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 b975d482a..b87870c69 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -5,7 +5,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.TypedArray;
-import android.os.AsyncTask;
+import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
@@ -15,21 +15,26 @@ import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.ContextMenu;
+import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLongClickListener;
+import android.view.ViewGroup;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.ScaleAnimation;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ListView;
+import android.widget.PopupWindow;
+import android.widget.SeekBar;
import android.widget.TextView;
import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import de.danoeh.antennapod.R;
@@ -40,27 +45,33 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.feed.SimpleChapter;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.service.playback.PlayerStatus;
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.CoverFragment;
import de.danoeh.antennapod.fragment.ItemDescriptionFragment;
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 {
+
private static final int POS_COVER = 0;
private static final int POS_DESCR = 1;
private static final int POS_CHAPTERS = 2;
@@ -98,6 +109,10 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
private ImageButton butNavChaptersShownotes;
private ImageButton butShowCover;
+ private Subscription subscription;
+
+ private PopupWindow popupWindow;
+
private void resetFragmentView() {
FragmentTransaction fT = getSupportFragmentManager().beginTransaction();
@@ -137,7 +152,9 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop()");
- cancelLoadTask();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
EventDistributor.getInstance().unregister(contentUpdate);
}
@@ -325,14 +342,11 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
};
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);
- }
- }
+ .getChapters(), media, position -> {
+ Chapter chapter = (Chapter)
+ chapterFragment.getListAdapter().getItem(position);
+ controller.seekToChapter(chapter);
+ }
));
}
currentlyShownFragment = chapterFragment;
@@ -426,91 +440,117 @@ 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);
+ 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);
});
registerForContextMenu(navList);
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()));
- }
+ findViewById(R.id.nav_settings).setOnClickListener(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();
- }
+ butNavChaptersShownotes.setOnClickListener(v -> {
+ if (currentlyShownPosition == POS_CHAPTERS) {
+ switchToFragment(POS_DESCR);
+ } else if (currentlyShownPosition == POS_DESCR) {
+ switchToFragment(POS_CHAPTERS);
+ } else if (currentlyShownPosition == POS_COVER) {
+ switchToLastFragment();
}
});
- butShowCover.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- switchToFragment(POS_COVER);
- }
- });
-
- 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";
- }
+ butShowCover.setOnClickListener(v -> switchToFragment(POS_COVER));
+
+ butPlaybackSpeed.setOnClickListener(v -> {
+ if (controller != null && controller.canSetPlaybackSpeed()) {
+ String[] availableSpeeds = UserPreferences
+ .getPlaybackSpeedArray();
+ String currentSpeed = UserPreferences.getPlaybackSpeed();
+
+ // Provide initial value in case the speed list has changed
+ // out from under us
+ // and our current speed isn't in the new list
+ String newSpeed;
+ if (availableSpeeds.length > 0) {
+ newSpeed = availableSpeeds[0];
+ } else {
+ newSpeed = "1.0";
+ }
- for (int i = 0; i < availableSpeeds.length; i++) {
- if (availableSpeeds[i].equals(currentSpeed)) {
- if (i == availableSpeeds.length - 1) {
- newSpeed = availableSpeeds[0];
- } else {
- newSpeed = availableSpeeds[i + 1];
- }
- break;
+ 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));
}
+ UserPreferences.setPlaybackSpeed(newSpeed);
+ controller.setPlaybackSpeed(Float.parseFloat(newSpeed));
}
});
- butPlaybackSpeed.setOnLongClickListener(new OnLongClickListener() {
- @Override
- public boolean onLongClick(View v) {
- VariableSpeedDialog.showDialog(AudioplayerActivity.this);
- return true;
- }
+ butPlaybackSpeed.setOnLongClickListener(v -> {
+
+ String[] availableSpeeds = getResources().getStringArray(R.array.playback_speed_values);
+ String currentSpeed = UserPreferences.getPlaybackSpeed();
+
+ LayoutInflater inflater = getLayoutInflater();
+ View popupView = inflater.inflate(R.layout.choose_speed_dialog, null);
+ TextView txtvSelectedSpeed = (TextView) popupView.findViewById(R.id.txtvSelectedSpeed);
+ SeekBar sbSelectSpeed = (SeekBar) popupView.findViewById(R.id.sbSelectSpeed);
+
+ txtvSelectedSpeed.setText(currentSpeed);
+ int progress = ArrayUtils.indexOf(availableSpeeds, currentSpeed);
+ int max = Math.max(progress, ArrayUtils.indexOf(availableSpeeds, "2.50"));
+ sbSelectSpeed.setMax(max);
+ sbSelectSpeed.setProgress(progress);
+ sbSelectSpeed.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ txtvSelectedSpeed.setText(availableSpeeds[progress]);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ String selectedSpeed = availableSpeeds[sbSelectSpeed.getProgress()];
+ UserPreferences.setPlaybackSpeed(selectedSpeed);
+ controller.setPlaybackSpeed(Float.parseFloat(selectedSpeed));
+ if (popupWindow != null && popupWindow.isShowing()) {
+ popupWindow.dismiss();
+ }
+ ScaleAnimation anim = new ScaleAnimation(1.0f, 1.33f, 1.0f, 1.33f,
+ butPlaybackSpeed.getWidth()/2, butPlaybackSpeed.getHeight()/2);
+ anim.setDuration(150);
+ anim.setRepeatMode(ScaleAnimation.REVERSE);
+ anim.setRepeatCount(1);
+ anim.setInterpolator(new LinearInterpolator());
+ butPlaybackSpeed.startAnimation(anim);
+ }
+ });
+ popupWindow = new PopupWindow(popupView,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ true);
+ popupWindow.setBackgroundDrawable(new BitmapDrawable());
+ popupWindow.setOutsideTouchable(true);
+ popupWindow.showAtLocation(popupView, Gravity.CENTER, 0, 0);
+ return true;
});
}
@@ -630,7 +670,7 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
}
public interface AudioplayerContentFragment {
- public void onDataSetChanged(Playable media);
+ void onDataSetChanged(Playable media);
}
@Override
@@ -685,10 +725,10 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
switch(item.getItemId()) {
case R.id.mark_all_seen_item:
- DBWriter.markFeedSeen(this, feed.getId());
+ DBWriter.markFeedSeen(feed.getId());
return true;
case R.id.mark_all_read_item:
- DBWriter.markFeedRead(this, feed.getId());
+ DBWriter.markFeedRead(feed.getId());
return true;
case R.id.remove_item:
final FeedRemover remover = new FeedRemover(this, feed) {
@@ -704,6 +744,20 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
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();
}
};
@@ -716,32 +770,22 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
private DBReader.NavDrawerData navDrawerData;
- private AsyncTask<Void, Void, DBReader.NavDrawerData> loadTask;
private void loadData() {
- loadTask = new AsyncTask<Void, Void, DBReader.NavDrawerData>() {
- @Override
- protected DBReader.NavDrawerData doInBackground(Void... params) {
- return DBReader.getNavDrawerData(AudioplayerActivity.this);
- }
-
- @Override
- protected void onPostExecute(DBReader.NavDrawerData result) {
- super.onPostExecute(result);
- navDrawerData = result;
- if (navAdapter != null) {
- navAdapter.notifyDataSetChanged();
- }
- }
- };
- loadTask.execute();
+ subscription = Observable.defer(() -> Observable.just(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 void cancelLoadTask() {
- if (loadTask != null) {
- loadTask.cancel(true);
- }
- }
+
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@@ -793,4 +837,5 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0;
}
};
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
deleted file mode 100644
index 0993ed6d6..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java
+++ /dev/null
@@ -1,255 +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.bumptech.glide.Glide;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
-
-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.glide.ApGlideSettings;
-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())) {
- 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(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 9cdaf2fc6..80883e4ae 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
@@ -26,8 +26,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
-import com.joanzapata.android.iconify.Iconify;
+import com.joanzapata.iconify.Iconify;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
@@ -111,7 +110,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
@@ -239,7 +238,7 @@ public class FeedInfoActivity extends ActionBarActivity {
prefs.setPassword(etxtPassword.getText().toString());
}
if (authInfoChanged || autoDeleteChanged) {
- DBWriter.setFeedPreferences(this, prefs);
+ DBWriter.setFeedPreferences(prefs);
}
authInfoChanged = false;
autoDeleteChanged = false;
@@ -299,7 +298,7 @@ public class FeedInfoActivity extends ActionBarActivity {
@Override
public void onConfirmButtonPressed(DialogInterface dialog) {
- DBWriter.setFeedsItemsAutoDownload(context, feed, autoDownload);
+ 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 e10e8041e..6915c817b 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -1,6 +1,6 @@
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;
@@ -9,6 +9,7 @@ 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;
@@ -18,6 +19,7 @@ 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.widget.Toolbar;
import android.util.Log;
import android.view.ContextMenu;
@@ -27,6 +29,8 @@ 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;
@@ -39,22 +43,31 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.ProgressEvent;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.QueueEvent;
+import de.danoeh.antennapod.core.event.QueueEvent;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.StorageUtils;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.fragment.AddFeedFragment;
-import de.danoeh.antennapod.fragment.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.menuhandler.NavDrawerActivity;
import de.danoeh.antennapod.preferences.PreferenceController;
import de.greenrobot.event.EventBus;
+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.
@@ -82,8 +95,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,
AddFeedFragment.TAG
@@ -96,7 +108,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
private View navDrawer;
private ListView navList;
private NavListAdapter navAdapter;
- private AdapterView.AdapterContextMenuInfo lastMenuInfo = null;
+ private int mPosition = -1;
private ActionBarDrawerToggle drawerToggle;
@@ -104,6 +116,8 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
private ProgressDialog pd;
+ private Subscription subscription;
+
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getNoTitleTheme());
@@ -131,11 +145,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);
@@ -154,12 +165,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();
@@ -169,14 +177,21 @@ 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 {
- loadFeedFragmentById(Integer.valueOf(lastFragment), null);
+ 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);
+ }
}
}
externalPlayerFragment = new ExternalPlayerFragment();
- transaction.replace(R.id.playerFragment, externalPlayerFragment);
+ transaction.replace(R.id.playerFragment, externalPlayerFragment, ExternalPlayerFragment.TAG);
transaction.commit();
checkFirstLaunch();
@@ -204,12 +219,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
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);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(PREF_IS_FIRST_LAUNCH, false);
@@ -231,21 +241,15 @@ 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();
@@ -274,18 +278,15 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
}
- public void loadFragment(final String tag, Bundle 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();
@@ -296,6 +297,12 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
case AddFeedFragment.TAG:
fragment = new AddFeedFragment();
break;
+ default:
+ // default to the queue
+ tag = QueueFragment.TAG;
+ fragment = new QueueFragment();
+ args = null;
+ break;
}
currentTitle = navAdapter.getLabel(tag);
getSupportActionBar().setTitle(currentTitle);
@@ -407,6 +414,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
showDrawerPreferencesDialog();
return true;
} else {
+ mPosition = position;
return false;
}
}
@@ -468,14 +476,30 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
@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
public boolean onOptionsItemSelected(MenuItem item) {
if (drawerToggle.onOptionsItemSelected(item)) {
@@ -490,6 +514,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
}
}
+
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
super.onCreateContextMenu(menu, v, menuInfo);
@@ -506,31 +531,23 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
menu.setHeaderTitle(feed.getTitle());
// episodes are not loaded, so we cannot check if the podcast has new or unplayed ones!
-
- // we may need to reference this elsewhere...
- lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
}
+
@Override
public boolean onContextItemSelected(MenuItem item) {
- AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
-
- if(menuInfo == null) {
- menuInfo = lastMenuInfo;
- }
-
- if(menuInfo.targetView.getParent() instanceof ListView == false
- || ((ListView)menuInfo.targetView.getParent()).getId() != R.id.nav_list) {
+ final int position = mPosition;
+ mPosition = -1; // reset
+ if(position < 0) {
return false;
}
- final int position = menuInfo.position;
Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
switch(item.getItemId()) {
case R.id.mark_all_seen_item:
- DBWriter.markFeedSeen(this, feed.getId());
+ DBWriter.markFeedSeen(feed.getId());
return true;
case R.id.mark_all_read_item:
- DBWriter.markFeedRead(this, feed.getId());
+ DBWriter.markFeedRead(feed.getId());
return true;
case R.id.remove_item:
final FeedRemover remover = new FeedRemover(this, feed) {
@@ -538,7 +555,7 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if(getSelectedNavListIndex() == position) {
- loadFragment(NewEpisodesFragment.TAG, null);
+ loadFragment(EpisodesFragment.TAG, null);
}
}
};
@@ -549,6 +566,23 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
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();
}
};
@@ -605,33 +639,21 @@ public class MainActivity extends ActionBarActivity implements NavDrawerActivity
};
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();
+ subscription = Observable.defer(() -> Observable.just(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) {
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 40a0d1801..c53a5257b 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.activity;
-import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PixelFormat;
@@ -8,6 +7,7 @@ import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -19,6 +19,8 @@ import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
+import com.afollestad.materialdialogs.MaterialDialog;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -31,7 +33,7 @@ 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;
/**
* Provides general features which are both needed for playing audio and video
@@ -291,52 +293,40 @@ public abstract class MediaplayerActivity extends ActionBarActivity
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)
+
+ 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.setPositiveButton(
- R.string.disable_sleeptimer_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- dialog.dismiss();
- controller.disableSleepTimer();
- }
- }
- );
- stDialog.setNegativeButton(R.string.cancel_label,
- new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- dialog.dismiss();
- }
- }
- );
- stDialog.create().show();
+ stDialog.positiveText(R.string.disable_sleeptimer_label);
+ stDialog.negativeText(R.string.cancel_label);
+ stDialog.callback(new MaterialDialog.ButtonCallback() {
+ @Override
+ public void onPositive(MaterialDialog dialog) {
+ dialog.dismiss();
+ controller.disableSleepTimer();
+ }
+
+ @Override
+ public void onNegative(MaterialDialog dialog) {
+ dialog.dismiss();
+ }
+ });
+ stDialog.build().show();
}
break;
case R.id.set_sleeptimer_item:
if (controller.serviceAvailable()) {
- TimeDialog td = new TimeDialog(this,
- R.string.set_sleeptimer_label,
- R.string.set_sleeptimer_label) {
-
+ SleepTimerDialog td = new SleepTimerDialog(this) {
@Override
- public void onTimeEntered(long millis) {
- controller.setSleepTimer(millis);
+ public void onTimerSet(long millis, boolean shakeToReset, boolean vibrate) {
+ controller.setSleepTimer(millis, shakeToReset, vibrate);
}
};
- td.show();
-
- break;
-
+ td.createNewDialog().show();
}
+ break;
case R.id.visit_website_item:
Uri uri = Uri.parse(media.getWebsiteLink());
startActivity(new Intent(Intent.ACTION_VIEW, uri));
@@ -503,19 +493,13 @@ public abstract class MediaplayerActivity extends ActionBarActivity
AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
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.setPrefFastForwardSecs(choice);
- txtvFF.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;
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 3ab384012..c67d65a9d 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,35 @@
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.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 +38,22 @@ 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.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 +63,11 @@ 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 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 +77,57 @@ 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.DOWNLOAD_HANDLED | EventDistributor.DOWNLOAD_QUEUED | 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;
+
+ private EventDistributor.EventListener listener = new EventDistributor.EventListener() {
+ @Override
+ public void update(EventDistributor eventDistributor, Integer arg) {
+ if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) {
+ updater = Observable.defer(() -> Observable.just(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));
@@ -87,7 +142,6 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
|| StringUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
feedUrl = (StringUtils.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 +156,63 @@ 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);
+
}
@Override
protected void onPause() {
super.onPause();
isPaused = true;
+ EventDistributor.getInstance().unregister(listener);
+ }
+
+ @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 +224,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 +231,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) {
@@ -186,133 +257,226 @@ public abstract class OnlineFeedViewActivity extends ActionBarActivity {
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();
-
-
- }
+ feed.getDownload_url(), "OnlineFeed", 0, Feed.FEEDFILETYPE_FEED, username, password,
+ true, null);
- /**
- * 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);
+ 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 (StringUtils.equalsIgnoreCase("html", e.getRootElement())) {
+ 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 +486,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 +573,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 f7e9256c0..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,8 +1,8 @@
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;
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 2ec987d1a..6e3991739 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
@@ -8,7 +8,6 @@ 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;
@@ -51,23 +50,13 @@ 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://"));
@@ -81,7 +70,7 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
}
}
if(txtvExplanation1.getVisibility() == View.VISIBLE) {
- txtvHeaderExplanation1.setText("Option " + nextOption);
+ txtvHeaderExplanation1.setText(String.format(optionLabel, nextOption));
nextOption++;
}
@@ -94,11 +83,11 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
findViewById(R.id.divider2).setVisibility(View.GONE);
butChooseExternal.setVisibility(View.GONE);
} else {
- txtvHeaderExplanation2.setText("Option " + nextOption);
+ txtvHeaderExplanation2.setText(String.format(optionLabel, nextOption));
nextOption++;
}
- txtvHeaderExplanation3.setText("Option " + nextOption);
+ txtvHeaderExplanation3.setText(String.format(optionLabel, nextOption));
}
@Override
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 133436b6f..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() {
@@ -47,8 +49,8 @@ public class PreferenceActivity extends ActionBarActivity {
protected void onCreate(Bundle savedInstanceState) {
// This must be the FIRST thing we do, otherwise other code may not have the
// reference it needs
- instance = this;
-
+ instance = new WeakReference<PreferenceActivity>(this);
+
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
@@ -102,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/adapter/ActionButtonUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
index aca2b359a..74cf6af60 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
@@ -7,6 +7,8 @@ import android.widget.ImageButton;
import org.apache.commons.lang3.Validate;
+import java.lang.ref.WeakReference;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -27,7 +29,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,
@@ -64,7 +66,7 @@ public class ActionButtonUtils {
butSecondary.setContentDescription(context.getString(labels[1]));
} else {
// item is not downloaded and not being downloaded
- LongList queueIds = DBReader.getQueueIDList(context);
+ LongList queueIds = DBReader.getQueueIDList();
if(DefaultActionButtonCallback.userAllowedMobileDownloads() ||
!DefaultActionButtonCallback.userChoseAddToQueue() || queueIds.contains(item.getId())) {
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 e279ead63..1ea7daaa3 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AdapterUtils.java
@@ -5,7 +5,7 @@ import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.joanzapata.android.iconify.Iconify;
+import com.joanzapata.iconify.Iconify;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -61,6 +61,7 @@ public class AdapterUtils {
txtvPos.setText("");
}
}, error -> {
+ txtvPos.setText("");
Log.e(TAG, Log.getStackTraceString(error));
});
} else {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
index c83b21c49..b35ff0aa9 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
@@ -3,11 +3,18 @@ package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.support.v7.widget.PopupMenu;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
import android.text.format.DateUtils;
+import android.util.Log;
+import android.view.ContextMenu;
import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
@@ -17,34 +24,46 @@ 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.android.iconify.Iconify;
+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.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
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 AllEpisodesListAdapter extends BaseAdapter {
+public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesRecycleAdapter.Holder> {
- private static final String TAG = AllEpisodesListAdapter.class.getSimpleName();
+ private static final String TAG = AllEpisodesRecycleAdapter.class.getSimpleName();
private final Context context;
private final ItemAccess itemAccess;
private final ActionButtonCallback actionButtonCallback;
private final ActionButtonUtils actionButtonUtils;
private final boolean showOnlyNewEpisodes;
+ private final WeakReference<MainActivity> mainActivityRef;
- public AllEpisodesListAdapter(Context context, ItemAccess itemAccess, ActionButtonCallback actionButtonCallback,
- boolean showOnlyNewEpisodes) {
+ private int position = -1;
+
+ public AllEpisodesRecycleAdapter(Context context,
+ MainActivity mainActivity,
+ ItemAccess itemAccess,
+ ActionButtonCallback actionButtonCallback,
+ boolean showOnlyNewEpisodes) {
super();
+ this.mainActivityRef = new WeakReference<>(mainActivity);
this.context = context;
this.itemAccess = itemAccess;
this.actionButtonUtils = new ActionButtonUtils(context);
@@ -53,55 +72,42 @@ public class AllEpisodesListAdapter extends BaseAdapter {
}
@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;
+ 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.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 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.placeholder = (TextView) convertView.findViewById(R.id.txtvPlaceholder);
- 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.progress = (ProgressBar) convertView
- .findViewById(R.id.pbar_progress);
- holder.cover = (ImageView) convertView.findViewById(R.id.imgvCover);
- holder.txtvDuration = (TextView) convertView.findViewById(R.id.txtvDuration);
- convertView.setTag(holder);
- } else {
- holder = (Holder) convertView.getTag();
- }
-
+ 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());
@@ -124,13 +130,17 @@ public class AllEpisodesListAdapter extends BaseAdapter {
holder.txtvDuration.setText("{fa-spinner}");
Iconify.addIcons(holder.txtvDuration);
NetworkUtils.getFeedMediaSizeObservable(media)
- .subscribe(size -> {
+ .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("");
}
@@ -141,7 +151,7 @@ public class AllEpisodesListAdapter extends BaseAdapter {
// item is being downloaded
holder.progress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
} else if (state == FeedItem.State.PLAYING
- || state == FeedItem.State.IN_PROGRESS) {
+ || state == FeedItem.State.IN_PROGRESS) {
if (media.getDuration() > 0) {
int progress = (int) (100.0 * media.getPosition() / media.getDuration());
holder.progress.setProgress(progress);
@@ -173,8 +183,26 @@ public class AllEpisodesListAdapter extends BaseAdapter {
.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);
+ }
- return convertView;
+ public int getPosition() {
+ int pos = position;
+ position = -1; // reset
+ return pos;
}
private class CoverTarget extends GlideDrawableImageViewTarget {
@@ -223,8 +251,10 @@ public class AllEpisodesListAdapter extends BaseAdapter {
}
};
-
- static class Holder {
+ public class Holder extends RecyclerView.ViewHolder
+ implements View.OnClickListener,
+ View.OnCreateContextMenuListener,
+ ItemTouchHelperViewHolder {
TextView placeholder;
TextView title;
TextView pubDate;
@@ -234,6 +264,60 @@ public class AllEpisodesListAdapter extends BaseAdapter {
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(mainActivityRef.get(), contextMenuInterface, item, true,
+ null);
+ }
+
}
public interface ItemAccess {
@@ -247,4 +331,26 @@ public class AllEpisodesListAdapter extends BaseAdapter {
boolean isInQueue(FeedItem item);
}
+
+ /**
+ * 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
index 22c15949a..cf0532cf1 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChapterListAdapter.java
@@ -138,9 +138,9 @@ public class ChapterListAdapter extends ArrayAdapter<Chapter> {
if (current != null) {
if (current == sc) {
holder.title.setTextColor(convertView.getResources().getColor(
- R.color.bright_blue));
+ R.color.holo_blue_light));
holder.start.setTextColor(convertView.getResources().getColor(
- R.color.bright_blue));
+ R.color.holo_blue_light));
} else {
holder.title.setTextColor(defaultTextColor);
holder.start.setTextColor(defaultTextColor);
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 c3486f2f2..efdf1a3c9 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
@@ -1,9 +1,9 @@
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 org.apache.commons.lang3.Validate;
@@ -57,7 +57,7 @@ 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);
+ LongList queueIds = DBReader.getQueueIDList();
if (NetworkUtils.isDownloadAllowed() || userAllowedMobileDownloads()) {
try {
DBTasks.downloadFeedItems(context, item);
@@ -67,7 +67,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);
@@ -75,7 +75,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();
@@ -93,7 +93,7 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
}
} else {
if (!item.isPlayed()) {
- DBWriter.markItemRead(context, item, true, true);
+ DBWriter.markItemPlayed(item, FeedItem.PLAYED, true);
}
}
}
@@ -117,14 +117,14 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
}
}
});
- LongList queueIds = DBReader.getQueueIDList(context);
+ LongList queueIds = DBReader.getQueueIDList();
if(!queueIds.contains(item.getId())) {
builder.setNeutralButton(context.getText(R.string.confirm_mobile_download_dialog_only_add_to_queue),
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
onlyAddToQueueTimeStamp = System.currentTimeMillis();
- DBWriter.addQueueItem(context, item.getId());
+ DBWriter.addQueueItem(context, item);
Toast.makeText(context, R.string.added_to_queue_label, Toast.LENGTH_SHORT).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 0eb15da8c..4ccff39af 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -11,7 +11,7 @@ import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
-import com.joanzapata.android.iconify.Iconify;
+import com.joanzapata.iconify.Iconify;
import java.util.Date;
@@ -123,7 +123,7 @@ 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 feed = DBReader.getFeed(holder.id);
if (feed != null) {
feed.setLastUpdate(new Date(0)); // force refresh
try {
@@ -135,7 +135,7 @@ public class DownloadLogAdapter extends BaseAdapter {
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);
+ FeedMedia media = DBReader.getFeedMedia(holder.id);
try {
DBTasks.downloadFeedItems(context, media.getItem());
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
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 3c91cbbbb..183c1a44e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -10,13 +10,13 @@ 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.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.joanzapata.iconify.widget.IconTextView;
import org.apache.commons.lang3.ArrayUtils;
@@ -33,6 +33,7 @@ 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;
@@ -94,6 +95,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;
@@ -212,7 +216,7 @@ public class NavListAdapter extends BaseAdapter
} else {
holder.count.setVisibility(View.GONE);
}
- } else if (tags.get(position).equals(NewEpisodesFragment.TAG)) {
+ } else if (tags.get(position).equals(EpisodesFragment.TAG)) {
int unreadItems = itemAccess.getNumberOfNewItems();
if (unreadItems > 0) {
holder.count.setVisibility(View.VISIBLE);
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 b1dfe2ad2..000000000
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueListAdapter.java
+++ /dev/null
@@ -1,245 +0,0 @@
-package de.danoeh.antennapod.adapter;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.text.format.DateUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-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.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.android.iconify.Iconify;
-
-import java.lang.ref.WeakReference;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.DownloadRequester;
-import de.danoeh.antennapod.core.util.Converter;
-import de.danoeh.antennapod.core.util.NetworkUtils;
-
-/**
- * List adapter for the queue.
- */
-public class QueueListAdapter extends BaseAdapter {
-
- private static final String TAG = QueueListAdapter.class.getSimpleName();
-
- 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.placeholder = (TextView) convertView.findViewById(R.id.txtvPlaceholder);
- holder.cover = (ImageView) convertView.findViewById(R.id.imgvCover);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
- holder.pubDate = (TextView) convertView.findViewById(R.id.txtvPubDate);
- holder.progressLeft = (TextView) convertView.findViewById(R.id.txtvProgressLeft);
- holder.progressRight = (TextView) convertView
- .findViewById(R.id.txtvProgressRight);
- holder.butSecondary = (ImageButton) convertView
- .findViewById(R.id.butSecondaryAction);
- holder.progress = (ProgressBar) convertView
- .findViewById(R.id.progressBar);
- convertView.setTag(holder);
- } else {
- holder = (Holder) convertView.getTag();
- }
-
- if(locked) {
- holder.dragHandle.setVisibility(View.GONE);
- } else {
- holder.dragHandle.setVisibility(View.VISIBLE);
- }
-
- holder.placeholder.setText(item.getFeed().getTitle());
-
- holder.title.setText(item.getTitle());
- FeedMedia media = item.getMedia();
-
- holder.title.setText(item.getTitle());
- String pubDate = DateUtils.formatDateTime(context, item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL);
- holder.pubDate.setText(pubDate.replace(" ", "\n"));
-
- 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 {
- if(media.getSize() > 0) {
- holder.progressLeft.setText(Converter.byteToString(media.getSize()));
- } else if(false == media.checkedOnSizeButUnknown()) {
- holder.progressLeft.setText("{fa-spinner}");
- Iconify.addIcons(holder.progressLeft);
- NetworkUtils.getFeedMediaSizeObservable(media)
- .subscribe(size -> {
- if (size > 0) {
- holder.progressLeft.setText(Converter.byteToString(size));
- } else {
- holder.progressLeft.setText("");
- }
- });
- } else {
- holder.progressLeft.setText("");
- }
- 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);
-
- Glide.with(context)
- .load(item.getImageUri())
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
- .into(new CoverTarget(item.getFeed().getImageUri(), holder.placeholder, holder.cover));
-
- return convertView;
- }
-
- private class CoverTarget extends GlideDrawableImageViewTarget {
-
- private final WeakReference<Uri> fallback;
- private final WeakReference<TextView> placeholder;
- private final WeakReference<ImageView> cover;
-
- public CoverTarget(Uri fallbackUri, TextView txtvPlaceholder, ImageView imgvCover) {
- super(imgvCover);
- fallback = new WeakReference<>(fallbackUri);
- placeholder = new WeakReference<>(txtvPlaceholder);
- cover = new WeakReference<>(imgvCover);
- }
-
- @Override
- public void onLoadFailed(Exception e, Drawable errorDrawable) {
- Uri fallbackUri = fallback.get();
- TextView txtvPlaceholder = placeholder.get();
- ImageView imgvCover = cover.get();
- if(fallbackUri != null && txtvPlaceholder != null && imgvCover != null) {
- Glide.with(context)
- .load(fallbackUri)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
- .into(new CoverTarget(null, txtvPlaceholder, imgvCover));
- }
- }
-
- @Override
- public void onResourceReady(GlideDrawable drawable, GlideAnimation anim) {
- super.onResourceReady(drawable, anim);
- TextView txtvPlaceholder = placeholder.get();
- if(txtvPlaceholder != null) {
- txtvPlaceholder.setVisibility(View.INVISIBLE);
- }
- }
- }
-
- private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- FeedItem item = (FeedItem) v.getTag();
- actionButtonCallback.onActionButtonPressed(item);
- }
- };
-
- static class Holder {
- ImageView dragHandle;
- ImageView cover;
- TextView placeholder;
- 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..8593e0dde
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -0,0 +1,348 @@
+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.PopupMenu;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.helper.ItemTouchHelper;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.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.DownloadRequestException;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.LongList;
+import de.danoeh.antennapod.core.util.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;
+
+ 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();
+ }
+
+ 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 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);
+ 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(mainActivity.get(), contextMenuInterface, item, true,
+ itemAccess.getQueueIds());
+ }
+
+ @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.formatDateTime(mainActivity.get(),
+ item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL);
+ pubDate.setText(pubDateStr.replace(" ", "\n"));
+
+ 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);
+ }
+ }
+
+ actionButtonUtils.configureActionButton(butSecondary, item);
+ 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();
+ }
+
+ /**
+ * 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/asynctask/OpmlExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlExportWorker.java
index 6bba956a6..3940eb8b6 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;
@@ -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/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/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
index 8a4a4efbf..c5b6ddb65 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -17,8 +17,9 @@ import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
-import com.joanzapata.android.iconify.IconDrawable;
-import com.joanzapata.android.iconify.Iconify;
+import com.joanzapata.iconify.Icon;
+import com.joanzapata.iconify.IconDrawable;
+import com.joanzapata.iconify.fonts.FontAwesomeIcons;
import java.util.ArrayList;
import java.util.Collections;
@@ -78,16 +79,14 @@ public class EpisodesApplyActionFragment extends Fragment {
mListView = (ListView) view.findViewById(android.R.id.list);
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
- mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- public void onItemClick(AdapterView<?> ListView, View view, int position, long rowId) {
- long id = episodes.get(position).getId();
- if (checkedIds.contains(id)) {
- checkedIds.remove(id);
- } else {
- checkedIds.add(id);
- }
- refreshCheckboxes();
+ 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) {
@@ -100,40 +99,15 @@ public class EpisodesApplyActionFragment extends Fragment {
checkAll();
btnAddToQueue = (Button) view.findViewById(R.id.btnAddToQueue);
- btnAddToQueue.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- queueChecked();
- }
- });
+ btnAddToQueue.setOnClickListener(v -> queueChecked());
btnMarkAsPlayed = (Button) view.findViewById(R.id.btnMarkAsPlayed);
- btnMarkAsPlayed.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- markedCheckedPlayed();
- }
- });
+ btnMarkAsPlayed.setOnClickListener(v -> markedCheckedPlayed());
btnMarkAsUnplayed = (Button) view.findViewById(R.id.btnMarkAsUnplayed);
- btnMarkAsUnplayed.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- markedCheckedUnplayed();
- }
- });
+ btnMarkAsUnplayed.setOnClickListener(v -> markedCheckedUnplayed());
btnDownload = (Button) view.findViewById(R.id.btnDownload);
- btnDownload.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- downloadChecked();
- }
- });
+ btnDownload.setOnClickListener(v -> downloadChecked());
btnDelete = (Button) view.findViewById(R.id.btnDelete);
- btnDelete.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- deleteChecked();
- }
- });
+ btnDelete.setOnClickListener(v -> deleteChecked());
return view;
}
@@ -149,36 +123,33 @@ public class EpisodesApplyActionFragment extends Fragment {
ta.recycle();
menu.findItem(R.id.sort).setIcon(new IconDrawable(getActivity(),
- Iconify.IconValue.fa_sort).color(textColor).actionBarSize());
+ FontAwesomeIcons.fa_sort).color(textColor).actionBarSize());
mSelectToggle = menu.findItem(R.id.select_toggle);
- mSelectToggle.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- if (checkedIds.size() == episodes.size()) {
- checkNone();
- } else {
- checkAll();
- }
- return true;
+ mSelectToggle.setOnMenuItemClickListener(item -> {
+ if (checkedIds.size() == episodes.size()) {
+ checkNone();
+ } else {
+ checkAll();
}
+ return true;
});
menu.findItem(R.id.select_options).setIcon(new IconDrawable(getActivity(),
- Iconify.IconValue.fa_caret_down).color(textColor).actionBarSize());
+ FontAwesomeIcons.fa_caret_down).color(textColor).actionBarSize());
}
@Override
public void onPrepareOptionsMenu (Menu menu) {
- Iconify.IconValue iVal;
+ Icon icon;
if(checkedIds.size() == episodes.size()) {
- iVal = Iconify.IconValue.fa_check_square_o;
+ icon = FontAwesomeIcons.fa_check_square_o;
} else if(checkedIds.size() == 0) {
- iVal = Iconify.IconValue.fa_square_o;
+ icon = FontAwesomeIcons.fa_square_o;
} else {
- iVal = Iconify.IconValue.fa_minus_square_o;
+ icon = FontAwesomeIcons.fa_minus_square_o;
}
- mSelectToggle.setIcon(new IconDrawable(getActivity(), iVal).color(textColor).actionBarSize());
+ mSelectToggle.setIcon(new IconDrawable(getActivity(), icon).color(textColor).actionBarSize());
}
@@ -240,14 +211,11 @@ public class EpisodesApplyActionFragment extends Fragment {
}
private void sortByTitle(final boolean reverse) {
- Collections.sort(episodes, new Comparator<FeedItem>() {
- @Override
- public int compare(FeedItem lhs, FeedItem rhs) {
- if (reverse) {
- return -1 * lhs.getTitle().compareTo(rhs.getTitle());
- } else {
- return lhs.getTitle().compareTo(rhs.getTitle());
- }
+ Collections.sort(episodes, (lhs, rhs) -> {
+ if (reverse) {
+ return -1 * lhs.getTitle().compareTo(rhs.getTitle());
+ } else {
+ return lhs.getTitle().compareTo(rhs.getTitle());
}
});
refreshTitles();
@@ -255,20 +223,17 @@ public class EpisodesApplyActionFragment extends Fragment {
}
private void sortByDate(final boolean reverse) {
- Collections.sort(episodes, new Comparator<FeedItem>() {
- @Override
- public int compare(FeedItem lhs, FeedItem rhs) {
- if (lhs.getPubDate() == null) {
- return -1;
- } else if (rhs.getPubDate() == null) {
- return 1;
- }
- int code = lhs.getPubDate().compareTo(rhs.getPubDate());
- if (reverse) {
- return -1 * code;
- } else {
- return code;
- }
+ 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();
@@ -276,22 +241,19 @@ public class EpisodesApplyActionFragment extends Fragment {
}
private void sortByDuration(final boolean reverse) {
- Collections.sort(episodes, new Comparator<FeedItem>() {
- @Override
- public int compare(FeedItem lhs, FeedItem rhs) {
- int ordering;
- if (false == lhs.hasMedia()) {
- ordering = 1;
- } else if (false == rhs.hasMedia()) {
- ordering = -1;
- } else {
- ordering = lhs.getMedia().getDuration() - rhs.getMedia().getDuration();
- }
- if(reverse) {
- return -1 * ordering;
+ Collections.sort(episodes, (lhs, rhs) -> {
+ int ordering;
+ if (false == lhs.hasMedia()) {
+ ordering = 1;
+ } else if (false == rhs.hasMedia()) {
+ ordering = -1;
} else {
- return ordering;
+ ordering = lhs.getMedia().getDuration() - rhs.getMedia().getDuration();
}
+ if(reverse) {
+ return -1 * ordering;
+ } else {
+ return ordering;
}
});
refreshTitles();
@@ -360,23 +322,17 @@ public class EpisodesApplyActionFragment extends Fragment {
}
private void queueChecked() {
- LongList orderedIds = new LongList();
- for(FeedItem episode : episodes) {
- if(checkedIds.contains(episode.getId())) {
- orderedIds.add((episode.getId()));
- }
- }
- DBWriter.addQueueItem(getActivity(), false, orderedIds.toArray());
+ DBWriter.addQueueItem(getActivity(), true, checkedIds.toArray());
close();
}
private void markedCheckedPlayed() {
- DBWriter.markItemRead(getActivity(), true, checkedIds.toArray());
+ DBWriter.markItemPlayed(FeedItem.PLAYED, checkedIds.toArray());
close();
}
private void markedCheckedUnplayed() {
- DBWriter.markItemRead(getActivity(), false, checkedIds.toArray());
+ DBWriter.markItemPlayed(FeedItem.UNPLAYED, checkedIds.toArray());
close();
}
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/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 5c4d4c430..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_seconds),
- context.getString(R.string.time_minutes),
- context.getString(R.string.time_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..4b512a48d 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -1,23 +1,37 @@
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 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 (com.aocate.media.MediaPlayer.isPrestoLibraryInstalled(context)) {
+ if (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context)
+ || UserPreferences.useSonic()) {
showSpeedSelectorDialog(context);
} else {
showGetPluginDialog(context);
@@ -25,26 +39,46 @@ public class VariableSpeedDialog {
}
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();
+ MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
+ builder.title(R.string.no_playback_plugin_title);
+ builder.content(R.string.no_playback_plugin_or_sonic_msg);
+ builder.positiveText(R.string.download_plugin_label);
+ builder.negativeText(R.string.enable_sonic);
+ builder.neutralText(R.string.close_label);
+ builder.callback(new MaterialDialog.ButtonCallback() {
+ @Override
+ public void onPositive(MaterialDialog dialog) {
+ try {
+ context.startActivity(playStoreIntent);
+ } catch (ActivityNotFoundException e) {
+ // this is usually thrown on an emulator if the Android market is not installed
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+
+ @Override
+ public void onNegative(MaterialDialog dialog) {
+ if (Build.VERSION.SDK_INT >= 16) { // just to be safe
+ UserPreferences.enableSonic(true);
+ showSpeedSelectorDialog(context);
+ }
+ }
+
+ @Override
+ public void onNeutral(MaterialDialog dialog) {
+ super.onNeutral(dialog);
+ }
+ });
+ builder.forceStacking(true);
+ MaterialDialog dialog = builder.show();
+ if(!IntentUtils.isCallable(context.getApplicationContext(), playStoreIntent)) {
+ View pos = dialog.getActionButton(DialogAction.POSITIVE);
+ pos.setEnabled(false);
+ }
+ if (Build.VERSION.SDK_INT < 16) {
+ View pos = dialog.getActionButton(DialogAction.NEGATIVE);
+ pos.setEnabled(false);
+ }
}
private static void showSpeedSelectorDialog(final Context context) {
@@ -64,37 +98,30 @@ public class VariableSpeedDialog {
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;
- }
-
- });
+ speedChecked, (dialog, which, 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);
-
+ (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..f6c80aa7c 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;
@@ -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 b4c4f1822..cdd6bc265 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -4,33 +4,31 @@ 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.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.AllEpisodesListAdapter;
+import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
@@ -45,9 +43,12 @@ 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.LongList;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
/**
* Shows unread or recently published episodes
@@ -57,30 +58,25 @@ public class AllEpisodesFragment extends Fragment {
public static final String TAG = "AllEpisodesFragment";
private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
+ EventDistributor.FEED_LIST_UPDATE |
EventDistributor.DOWNLOAD_QUEUED |
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 AdapterView.AdapterContextMenuInfo lastMenuInfo = null;
private List<FeedItem> episodes;
- private LongList queuedItemsIds;
private List<Downloader> downloaderList;
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
- private final boolean showOnlyNewEpisodes;
private AtomicReference<MainActivity> activity = new AtomicReference<MainActivity>();
@@ -88,34 +84,19 @@ public class AllEpisodesFragment extends Fragment {
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);
@@ -130,16 +111,26 @@ public class AllEpisodesFragment extends Fragment {
}
@Override
+ public void onResume() {
+ super.onResume();
+ loadItems();
+ registerForContextMenu(recyclerView);
+ }
+
+ @Override
public void onPause() {
super.onPause();
saveScrollPosition();
+ unregisterForContextMenu(recyclerView);
}
@Override
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
- stopItemLoader();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
}
@Override
@@ -155,25 +146,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();
}
}
@@ -252,7 +250,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 +266,54 @@ public class AllEpisodesFragment extends Fragment {
}
@Override
+ public boolean onContextItemSelected(MenuItem item) {
+ if(!isVisible()) {
+ return false;
+ }
+ 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);
+ 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,65 +325,11 @@ 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;
- lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
- FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queuedItemsIds);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
- if(menuInfo == null) {
- menuInfo = lastMenuInfo;
- }
- FeedItem selectedItem = itemAccess.getItem(menuInfo.position);
-
- if (selectedItem == null) {
- 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);
+ listAdapter = new AllEpisodesRecycleAdapter(activity.get(), activity.get(), itemAccess,
+ new DefaultActionButtonCallback(activity.get()), showOnlyNewEpisodes());
+ recyclerView.setAdapter(listAdapter);
downloadObserver = new DownloadObserver(activity.get(), new Handler(), downloadObserverCallback);
downloadObserver.onResume();
}
@@ -392,7 +349,7 @@ public class AllEpisodesFragment extends Fragment {
}
};
- private AllEpisodesListAdapter.ItemAccess itemAccess = new AllEpisodesListAdapter.ItemAccess() {
+ protected AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
@Override
public int getCount() {
@@ -425,11 +382,10 @@ 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;
}
};
@@ -437,7 +393,7 @@ public class AllEpisodesFragment extends Fragment {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
- startItemLoader();
+ loadItems();
if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
getActivity().supportInvalidateOptionsMenu();
}
@@ -446,77 +402,36 @@ public class AllEpisodesFragment extends Fragment {
};
private void updateShowOnlyEpisodesListViewState() {
- if (showOnlyNewEpisodes) {
- listView.setEmptyView(null);
- txtvEmpty.setVisibility(View.GONE);
- } else {
- listView.setEmptyView(txtvEmpty);
- }
}
- private ItemLoader itemLoader;
-
- protected void startItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
+ protected void loadItems() {
+ if(subscription != null) {
+ subscription.unsubscribe();
}
- itemLoader = new ItemLoader();
- itemLoader.execute();
- }
-
- protected void stopItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
+ if (viewsCreated && !itemsLoaded) {
+ recyclerView.setVisibility(View.GONE);
+ progLoading.setVisibility(View.VISIBLE);
}
+ subscription = Observable.defer(() -> Observable.just(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));
+ });
}
- 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);
- }
- }
-
- @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)
- };
- }
- } else {
- return null;
- }
- }
-
- @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];
- itemsLoaded = true;
- if (viewsCreated && activity.get() != null) {
- onFragmentLoaded();
- }
- }
- }
+ protected List<FeedItem> loadData() {
+ return DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT);
}
+
}
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..c5b582d3a 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
@@ -132,56 +144,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.defer(() -> Observable.just(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/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
index 074a87ea0..669c6ac49 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
@@ -93,27 +100,11 @@ 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) {
super.onCreateOptionsMenu(menu, inflater);
@@ -142,7 +133,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 +143,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.defer(() -> Observable.just(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 634c3c546..7b02b4f18 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -26,7 +26,7 @@ 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;
@@ -141,26 +141,12 @@ public class ExternalPlayerFragment extends Fragment {
@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());
- }
+ playbackDone();
}
@Override
@@ -192,6 +178,21 @@ 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 != null && controller.serviceAvailable()) {
@@ -229,4 +230,8 @@ public class ExternalPlayerFragment extends Fragment {
return Converter.getDurationStringLong(position) + " / "
+ Converter.getDurationStringLong(duration);
}
+
+ public PlaybackController getPlaybackControllerTestingOnly() {
+ return controller;
+ }
}
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..532516dda
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -0,0 +1,104 @@
+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;
+import de.greenrobot.event.EventBus;
+
+
+/**
+ * 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(" + event + ")");
+ loadItems();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ EventBus.getDefault().unregister(this);
+ }
+
+ @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 9693e6886..faa4413bb 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -210,7 +210,7 @@ public class ItemDescriptionFragment extends Fragment {
@Override
protected FeedItem doInBackground(Void... voids) {
- return DBReader.getFeedItem(getActivity(), getArguments().getLong(ARG_FEEDITEM_ID));
+ return DBReader.getFeedItem(getArguments().getLong(ARG_FEEDITEM_ID));
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index 4edb7f36f..dc9f9740d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -11,8 +11,6 @@ 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;
@@ -36,19 +34,17 @@ import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
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.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;
@@ -64,11 +60,15 @@ 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";
@@ -114,6 +114,8 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
private ImageButton butMore;
private PopupMenu popupMenu;
+ private Subscription subscription;
+
/**
* URL that was selected via long-press.
*/
@@ -128,55 +130,6 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
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) {
@@ -196,19 +149,18 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
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));
@@ -244,74 +196,111 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
((MainActivity) getActivity()).dismissChildFragment();
}
}
-
-
}
);
- butAction2.setOnClickListener(new View.OnClickListener()
-
- {
- @Override
- public void onClick(View v) {
- if (item == null) {
- return;
- }
+ 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));
- }
- }
- }
+ 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(getActivity(), popupMenuInterface, item, true, queue);
- } else {
- // these are already available via button1 and button2
- FeedItemMenuHandler.onPrepareMenu(getActivity(), popupMenuInterface, item, true, queue,
- R.id.mark_read_item, R.id.visit_website_item);
- }
- popupMenu.show();
- }
- }
+ butMore.setOnClickListener(v -> {
+ if (item == null) {
+ return;
+ }
+ popupMenu.getMenu().clear();
+ popupMenu.inflate(R.menu.feeditem_options);
+ if (item.hasMedia()) {
+ FeedItemMenuHandler.onPrepareMenu(getActivity(), popupMenuInterface, item, true, queue);
+ } else {
+ // these are already available via button1 and button2
+ FeedItemMenuHandler.onPrepareMenu(getActivity(), popupMenuInterface, item, true, queue,
+ R.id.mark_read_item, R.id.visit_website_item);
+ }
+ popupMenu.show();
+ }
);
- 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;
- }
- }
- }
+ popupMenu.setOnMenuItemClickListener(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;
+ }
+ }
);
return layout;
}
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ Toolbar toolbar = ((MainActivity) getActivity()).getToolbar();
+ toolbar.addView(header);
+ load();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().register(this);
+ if (downloadObserver != null) {
+ downloadObserver.setActivity(getActivity());
+ downloadObserver.onResume();
+ }
+ if(itemsLoaded) {
+ updateAppearance();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ EventDistributor.getInstance().unregister(contentUpdate);
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ resetViewState();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
+ if (webvDescription != null && root != null) {
+ root.removeView(webvDescription);
+ webvDescription.destroy();
+ }
+ }
+
+ private void resetViewState() {
+ if (downloadObserver != null) {
+ downloadObserver.onPause();
+ }
+ Toolbar toolbar = ((MainActivity) getActivity()).getToolbar();
+ toolbar.removeView(header);
+ }
+
private final FeedItemMenuHandler.MenuInterface popupMenuInterface = new FeedItemMenuHandler.MenuInterface() {
@Override
public void setItemVisibility(int id, boolean visible) {
@@ -335,8 +324,16 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
}
private void updateAppearance() {
+ if (item == null) {
+ Log.d(TAG, "updateAppearance item is null");
+ return;
+ }
+
txtvTitle.setText(item.getTitle());
- txtvPublished.setText(DateUtils.formatDateTime(getActivity(), item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
+
+ if (item.getPubDate() != null) {
+ txtvPublished.setText(DateUtils.formatDateTime(getActivity(), item.getPubDate().getTime(), DateUtils.FORMAT_ABBREV_ALL));
+ }
Glide.with(getActivity())
.load(item.getImageUri())
@@ -410,11 +407,6 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
}
}
- public void onEvent(QueueEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
- getLoaderManager().restartLoader(0, null, ItemFragment.this);
- }
-
private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
@Override
@@ -492,46 +484,18 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
}
}
- @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);
- }
- LongList data2 = DBReader.getQueueIDList(getContext());
- return Pair.create(data1, data2);
- }
- };
- }
-
- @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 {
- updateAppearance();
- }
+ public void onEventMainThread(QueueEvent event) {
+ if(event.contains(itemID)) {
+ 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);
+ updateAppearance();
}
}
};
@@ -546,4 +510,36 @@ public class ItemFragment extends Fragment implements LoaderManager.LoaderCallba
}
}
};
+
+ private void load() {
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
+ subscription = Observable.defer(() -> Observable.just(loadInBackground()))
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ item = result.first;
+ queue = result.second;
+ if (!itemsLoaded) {
+ itemsLoaded = true;
+ onFragmentLoaded();
+ } else {
+ updateAppearance();
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ }
+
+ private Pair<FeedItem,LongList> loadInBackground() {
+ FeedItem data1 = DBReader.getFeedItem(itemID);
+ if (data1 != null) {
+ Timeline t = new Timeline(getActivity(), data1);
+ webviewData = t.processShownotes(false);
+ }
+ LongList data2 = DBReader.getQueueIDList();
+ return Pair.create(data1, data2);
+ }
+
}
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 63ebf234e..ba3dfc2af 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -7,12 +7,12 @@ import android.content.Intent;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.LightingColorFilter;
-import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.ListFragment;
+import android.support.v4.util.Pair;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
@@ -24,7 +24,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;
@@ -33,9 +32,10 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.engine.DiskCacheStrategy;
-import com.joanzapata.android.iconify.IconDrawable;
-import com.joanzapata.android.iconify.Iconify;
+import com.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;
@@ -56,9 +56,10 @@ 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.event.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;
@@ -72,6 +73,10 @@ 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.
@@ -110,6 +115,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
@@ -155,7 +162,9 @@ public class ItemlistFragment extends ListFragment {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
- stopItemLoader();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
}
@Override
@@ -163,13 +172,15 @@ public class ItemlistFragment extends ListFragment {
super.onResume();
Log.d(TAG, "onResume()");
updateProgressBarVisibility();
- startItemLoader();
+ loadItems();
}
@Override
public void onDetach() {
super.onDetach();
- stopItemLoader();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
}
@Override
@@ -228,13 +239,13 @@ public class ItemlistFragment extends ListFragment {
menu.findItem(R.id.share_link_item).setVisible(false);
menu.findItem(R.id.visit_website_item).setVisible(false);
}
- int[] attrs = { android.R.attr.textColor };
- TypedArray ta = getActivity().obtainStyledAttributes(attrs);
+ int[] 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(),
- Iconify.IconValue.fa_gears).color(textColor).actionBarSize());
+ FontAwesomeIcons.fa_gears).color(textColor).actionBarSize());
isUpdatingFeed = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
@@ -263,7 +274,7 @@ public class ItemlistFragment extends ListFragment {
@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(),
@@ -384,13 +395,13 @@ public class ItemlistFragment extends ListFragment {
public void onEvent(QueueEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
- startItemLoader();
+ loadItems();
}
public void onEvent(FeedEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
if(event.feedId == feedID) {
- startItemLoader();
+ loadItems();
}
}
@@ -403,7 +414,7 @@ public class ItemlistFragment extends ListFragment {
if ((EventDistributor.DOWNLOAD_QUEUED & arg) != 0) {
updateProgressBarVisibility();
} else {
- startItemLoader();
+ loadItems();
updateProgressBarVisibility();
}
}
@@ -452,7 +463,7 @@ public class ItemlistFragment extends ListFragment {
private void refreshHeaderView() {
if (getListView() == null || feed == null) {
- Log.e(TAG, "Unable to setup listview: listView = null or feed = null");
+ Log.e(TAG, "Unable to setup listview: recyclerView = null or feed = null");
return;
}
if(feed.hasLastUpdateFailed()) {
@@ -492,7 +503,7 @@ public class ItemlistFragment extends ListFragment {
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();
@@ -550,7 +561,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) {
@@ -607,51 +618,37 @@ public class ItemlistFragment extends ListFragment {
}
};
- 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(feedID);
- }
- private void stopItemLoader() {
- if (itemLoader != null) {
- itemLoader.cancel(true);
- }
+ subscription = Observable.defer(() -> Observable.just(loadData()))
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ if (result != null) {
+ feed = result.first;
+ queuedItemsIds = result.second;
+ itemsLoaded = true;
+ if (viewsCreated) {
+ onFragmentLoaded();
+ }
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
}
- 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 != null && feed.getItemFilter() != null) {
- FeedItemFilter filter = feed.getItemFilter();
- feed.setItems(filter.filter(context, feed.getItems()));
- }
- LongList queuedItemsIds = DBReader.getQueueIDList(context);
- return new Object[] { feed, queuedItemsIds };
- } else {
- return null;
- }
- }
-
- @Override
- protected void onPostExecute(Object[] res) {
- super.onPostExecute(res);
- if (res != null) {
- feed = (Feed) res[0];
- queuedItemsIds = (LongList) res[1];
- itemsLoaded = true;
- if (viewsCreated) {
- onFragmentLoaded();
- }
- }
+ private Pair<Feed, LongList> loadData() {
+ Feed feed = DBReader.getFeed(feedID);
+ if(feed != null && feed.getItemFilter() != null) {
+ FeedItemFilter filter = feed.getItemFilter();
+ feed.setItems(filter.filter(feed.getItems()));
}
+ LongList queuedItemsIds = DBReader.getQueueIDList();
+ return Pair.create(feed, queuedItemsIds);
}
+
}
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 edd4da7fe..72704245f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
@@ -28,7 +28,6 @@ import java.util.ArrayList;
import java.util.List;
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;
@@ -96,13 +95,13 @@ public class ItunesSearchFragment extends Fragment {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(getActivity(),
- DefaultOnlineFeedViewActivity.class);
+ OnlineFeedViewActivity.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");
+ intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, "iTunes");
startActivity(intent);
}
});
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 9a25674b6..d684c064c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -1,23 +1,26 @@
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.adapter.QueueRecyclerAdapter;
+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,15 +35,15 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
private static final String PREF_NAME = "PrefNewEpisodesFragment";
- private UndoBarController undoBarController;
+ @Override
+ protected boolean showOnlyNewEpisodes() { return true; }
- public NewEpisodesFragment() {
- super(true, PREF_NAME);
- }
+ @Override
+ protected String getPrefName() { return PREF_NAME; }
public void onEvent(QueueEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
- startItemLoader();
+ loadItems();
}
@Override
@@ -58,52 +61,87 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
@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(), true, item.getId());
- 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;
- private final Context context = getActivity();
+ 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());
+
+ 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, false, itemId);
+ 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() && item.getFeed().getPreferences().getCurrentAutoDelete()) {
- 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 b094133d3..d7ffa3e23 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -1,9 +1,7 @@
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;
@@ -27,12 +25,16 @@ import de.danoeh.antennapod.core.asynctask.DownloadObserver;
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.event.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 {
@@ -53,6 +55,8 @@ public class PlaybackHistoryFragment extends ListFragment {
private DownloadObserver downloadObserver;
private List<Downloader> downloaderList;
+ private Subscription subscription;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -63,7 +67,7 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public void onResume() {
super.onResume();
- startItemLoader();
+ loadItems();
}
@Override
@@ -78,13 +82,17 @@ public class PlaybackHistoryFragment extends ListFragment {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
- stopItemLoader();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
}
@Override
public void onDetach() {
super.onDetach();
- stopItemLoader();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
activity.set(null);
}
@@ -164,7 +172,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 +184,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 +192,7 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
- startItemLoader();
+ loadItems();
getActivity().supportInvalidateOptionsMenu();
}
}
@@ -245,48 +253,32 @@ public class PlaybackHistoryFragment extends ListFragment {
}
};
- 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.defer(() -> Observable.just(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 24c9fc425..bfac7a347 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,44 @@
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.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.adapter.QueueRecyclerAdapter;
import de.danoeh.antennapod.core.asynctask.DownloadObserver;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+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;
@@ -48,13 +48,16 @@ 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
@@ -68,28 +71,19 @@ public class QueueFragment extends Fragment {
EventDistributor.PLAYER_STATUS_UPDATE;
private TextView infoBar;
- private DragSortListView listView;
- private QueueListAdapter listAdapter;
+ private RecyclerView recyclerView;
+ private QueueRecyclerAdapter recyclerAdapter;
private TextView txtvEmpty;
private ProgressBar progLoading;
- private ContextMenu contextMenu;
- private AdapterView.AdapterContextMenuInfo lastMenuInfo = null;
-
- 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;
@@ -98,6 +92,10 @@ public class QueueFragment extends Fragment {
*/
private boolean blockDownloadObserverUpdate = false;
+ private Subscription subscription;
+ private LinearLayoutManager layoutManager;
+ private ItemTouchHelper itemTouchHelper;
+
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -107,88 +105,122 @@ public class QueueFragment extends Fragment {
}
@Override
- public void onResume() {
- super.onResume();
- startItemLoader();
- }
-
- @Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
- this.activity.set((MainActivity) getActivity());
if (downloadObserver != null) {
downloadObserver.setActivity(getActivity());
downloadObserver.onResume();
}
- if (viewsCreated && itemsLoaded) {
+ if (queue != null) {
onFragmentLoaded();
}
}
@Override
- public void onPause() {
- super.onPause();
- saveScrollPosition();
+ public void onResume() {
+ super.onResume();
+ recyclerView.setAdapter(recyclerAdapter);
+ loadItems();
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().register(this);
}
@Override
- public void onStop() {
- super.onStop();
+ public void onPause() {
+ super.onPause();
+ saveScrollPosition();
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, "onEvent(" + event + ")");
+ 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:
+ int from = FeedItemUtil.indexOfItemWithId(queue, event.item.getId());
+ int to = event.position;
+ if(from != to) {
+ queue.add(to, queue.remove(from));
+ recyclerAdapter.notifyItemMoved(from, to);
+ } else {
+ // QueueFragment itself sent the event and already moved the item
+ }
+ break;
+ }
+ onFragmentLoaded();
}
- public void onEventMainThread(QueueEvent event) {
+ public void onEventMainThread(FeedItemEvent 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));
+ 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);
+ }
}
- 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);
+ 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();
}
}
private void resetViewState() {
- unregisterForContextMenu(listView);
- listAdapter = null;
- activity.set(null);
- undoBarController = null;
- viewsCreated = false;
+ recyclerAdapter = null;
blockDownloadObserverUpdate = false;
if (downloadObserver != null) {
downloadObserver.onPause();
@@ -201,17 +233,14 @@ 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) {
super.onCreateOptionsMenu(menu, inflater);
- if (itemsLoaded) {
+ if (queue != null) {
inflater.inflate(R.menu.queue, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
@@ -244,14 +273,9 @@ public class QueueFragment extends Fragment {
switch (item.getItemId()) {
case R.id.queue_lock:
boolean locked = !UserPreferences.isQueueLocked();
- if(locked) {
- listView.setDragEnabled(false);
- } else {
- listView.setDragEnabled(true);
- }
UserPreferences.setQueueLocked(locked);
getActivity().supportInvalidateOptionsMenu();
- listAdapter.setLocked(locked);
+ recyclerAdapter.setLocked(locked);
return true;
case R.id.refresh_item:
List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
@@ -269,7 +293,7 @@ public class QueueFragment extends Fragment {
public void onConfirmButtonPressed(
DialogInterface dialog) {
dialog.dismiss();
- DBWriter.clearQueue(getActivity());
+ DBWriter.clearQueue();
}
};
conDialog.createNewDialog().show();
@@ -298,54 +322,17 @@ 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;
- lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
- LongList queueIds = new LongList(queue.size());
- for(FeedItem queueItem : queue) {
- queueIds.add(queueItem.getId());
- }
- FeedItemMenuHandler.onPrepareMenu(getActivity(), contextMenuInterface, item, true, queueIds);
- }
@Override
public boolean onContextItemSelected(MenuItem item) {
- AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
- if(menuInfo == null) {
- menuInfo = lastMenuInfo;
+ if(!isVisible()) {
+ return false;
}
- FeedItem selectedItem = itemAccess.getItem(menuInfo.position);
-
+ 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);
}
@@ -366,106 +353,111 @@ public class QueueFragment extends Fragment {
View root = inflater.inflate(R.layout.queue_fragment, container, false);
infoBar = (TextView) root.findViewById(R.id.info_bar);
- listView = (DragSortListView) root.findViewById(android.R.id.list);
- txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
- progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
- listView.setEmptyView(txtvEmpty);
+ recyclerView = (RecyclerView) root.findViewById(R.id.recyclerView);
+ layoutManager = new LinearLayoutManager(getActivity());
+ recyclerView.setLayoutManager(layoutManager);
+ recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
+ recyclerView.setHasFixedSize(true);
+ registerForContextMenu(recyclerView);
- if(UserPreferences.isQueueLocked()) {
- listView.setDragEnabled(false);
- } else {
- listView.setDragEnabled(true);
- }
+ itemTouchHelper = new ItemTouchHelper(
+ new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) {
- 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()));
+ @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;
}
- }
- });
- listView.setDragSortListener(new DragSortListView.DragSortListener() {
- @Override
- public void drag(int from, int to) {
- Log.d(TAG, "drag");
- blockDownloadObserverUpdate = 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() && item.getFeed().getPreferences().getCurrentAutoDelete()) {
- 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);
+ if (recyclerAdapter == null) {
+ MainActivity activity = (MainActivity) getActivity();
+ recyclerAdapter = new QueueRecyclerAdapter(activity, itemAccess,
+ new DefaultActionButtonCallback(activity), itemTouchHelper);
+ recyclerView.setAdapter(recyclerAdapter);
+ downloadObserver = new DownloadObserver(activity, new Handler(), downloadObserverCallback);
downloadObserver.onResume();
}
- listAdapter.notifyDataSetChanged();
+ if(queue == null || queue.size() == 0) {
+ recyclerView.setVisibility(View.GONE);
+ txtvEmpty.setVisibility(View.VISIBLE);
+ } else {
+ txtvEmpty.setVisibility(View.GONE);
+ recyclerView.setVisibility(View.VISIBLE);
+ }
restoreScrollPosition();
@@ -473,7 +465,10 @@ public class QueueFragment extends Fragment {
// needs data that may have just been loaded.
getActivity().supportInvalidateOptionsMenu();
- // refresh information bar
+ refreshInfoBar();
+ }
+
+ private void refreshInfoBar() {
String info = queue.size() + getString(R.string.episodes_suffix);
if(queue.size() > 0) {
long duration = 0;
@@ -492,21 +487,21 @@ public class QueueFragment extends Fragment {
@Override
public void onContentChanged(List<Downloader> downloaderList) {
QueueFragment.this.downloaderList = downloaderList;
- if (listAdapter != null && !blockDownloadObserverUpdate) {
- listAdapter.notifyDataSetChanged();
+ if (recyclerAdapter != null && !blockDownloadObserverUpdate) {
+ recyclerAdapter.notifyDataSetChanged();
}
}
};
- 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;
+ return queue != null ? queue.get(position) : null;
}
@Override
@@ -548,13 +543,18 @@ public class QueueFragment extends Fragment {
}
return 0;
}
+
+ @Override
+ public LongList getQueueIds() {
+ return queue != null ? LongList.of(FeedItemUtil.getIds(queue)) : new LongList(0);
+ }
};
private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
- startItemLoader();
+ loadItems();
if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
getActivity().supportInvalidateOptionsMenu();
}
@@ -562,55 +562,30 @@ public class QueueFragment extends Fragment {
}
};
- 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 (queue == null) {
+ recyclerView.setVisibility(View.GONE);
+ txtvEmpty.setVisibility(View.GONE);
+ progLoading.setVisibility(View.VISIBLE);
}
+ subscription = Observable.defer(() -> Observable.just(DBReader.getQueue()))
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(items -> {
+ if(items != null) {
+ progLoading.setVisibility(View.GONE);
+ queue = items;
+ onFragmentLoaded();
+ 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 eb4d18328..544bdfc43 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
@@ -81,8 +81,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 975493ce9..edd8cdd1a 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.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
@@ -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();
}
}
};
@@ -187,53 +196,36 @@ public class SearchFragment extends ListFragment {
}
};
- 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.defer(() -> Observable.just(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/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 623c6faa7..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;
@@ -104,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/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
index 3e2fdf24f..3fa1048c0 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -72,11 +72,11 @@ public class FeedItemMenuHandler {
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 = selectedItem.isTagged(FeedItem.TAG_QUEUE);
+ 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) {
@@ -100,10 +100,9 @@ public class FeedItemMenuHandler {
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);
}
@@ -123,6 +122,11 @@ public class FeedItemMenuHandler {
if (selectedItem.getPaymentLink() == null || !selectedItem.getFlattrStatus().flattrable()) {
mi.setItemVisibility(R.id.support_item, false);
}
+
+ boolean isFavorite = selectedItem.isTagged(FeedItem.TAG_FAVORITE);
+ mi.setItemVisibility(R.id.add_to_favorites_item, !isFavorite);
+ mi.setItemVisibility(R.id.remove_from_favorites_item, isFavorite);
+
return true;
}
@@ -155,7 +159,7 @@ public class FeedItemMenuHandler {
break;
case R.id.mark_read_item:
selectedItem.setPlayed(true);
- DBWriter.markItemRead(context, selectedItem, true, false);
+ DBWriter.markItemPlayed(selectedItem, FeedItem.PLAYED, false);
if(GpodnetPreferences.loggedIn()) {
FeedMedia media = selectedItem.getMedia();
// not all items have media, Gpodder only cares about those that do
@@ -173,7 +177,7 @@ public class FeedItemMenuHandler {
break;
case R.id.mark_unread_item:
selectedItem.setPlayed(false);
- DBWriter.markItemRead(context, selectedItem, false, false);
+ DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, false);
if(GpodnetPreferences.loggedIn()) {
GpodnetEpisodeAction actionNew = new GpodnetEpisodeAction.Builder(selectedItem, Action.NEW)
.currentDeviceId()
@@ -183,27 +187,33 @@ public class FeedItemMenuHandler {
}
break;
case R.id.move_to_top_item:
- DBWriter.moveQueueItemToTop(context, selectedItem.getId(), true);
+ DBWriter.moveQueueItemToTop(selectedItem.getId(), true);
return true;
case R.id.move_to_bottom_item:
- DBWriter.moveQueueItemToBottom(context, selectedItem.getId(), true);
+ DBWriter.moveQueueItemToBottom(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());
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 3df59724d..84da32a40 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -1,10 +1,10 @@
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.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -78,7 +78,7 @@ public class FeedMenuHandler {
public void onConfirmButtonPressed(
DialogInterface dialog) {
dialog.dismiss();
- DBWriter.markFeedRead(context, selectedFeed.getId());
+ DBWriter.markFeedRead(selectedFeed.getId());
}
};
conDialog.createNewDialog().show();
@@ -138,7 +138,7 @@ public class FeedMenuHandler {
@Override
public void onClick(DialogInterface dialog, int which) {
feed.setHiddenItemProperties(hidden.toArray(new String[hidden.size()]));
- DBWriter.setFeedItemsFilter(context, feed.getId(), hidden);
+ DBWriter.setFeedItemsFilter(feed.getId(), hidden);
}
});
builder.setNegativeButton(R.string.cancel_label, null);
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 4d4b2be63..73d7da0f2 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,12 @@
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.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
@@ -14,21 +15,25 @@ 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.TimePicker;
import android.widget.Toast;
+import com.afollestad.materialdialogs.MaterialDialog;
+
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AboutActivity;
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
@@ -38,6 +43,8 @@ import de.danoeh.antennapod.activity.PreferenceActivityGingerbread;
import de.danoeh.antennapod.asynctask.OpmlExportWorker;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+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.dialog.AuthenticationDialog;
import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog;
@@ -47,8 +54,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_SETTINGS = "prefFlattrSettings";
public static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate";
public static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
@@ -70,6 +80,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));
+ }
+ }
}
/**
@@ -142,21 +164,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() {
@@ -350,7 +373,7 @@ public class PreferenceController {
public boolean onPreferenceChange(Preference preference, Object o) {
if (o instanceof String) {
int newValue = Integer.valueOf((String) o) * 1024 * 1024;
- if(newValue != UserPreferences.getImageCacheSize()) {
+ 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);
@@ -363,6 +386,7 @@ public class PreferenceController {
}
}
);
+ buildEpisodeCleanupPreference();
buildSmartMarkAsPlayedPreference();
buildAutodownloadSelectedNetworsPreference();
setSelectedNetworksEnabled(UserPreferences
@@ -377,16 +401,37 @@ public class PreferenceController {
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);
@@ -416,6 +461,28 @@ public class PreferenceController {
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();
@@ -459,6 +526,13 @@ public class PreferenceController {
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 setParallelDownloadsText(int downloads) {
@@ -518,9 +592,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) {
@@ -586,92 +658,125 @@ 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();
+ String dataFolder = UserPreferences.getDataFolder(context, null).getAbsolutePath();
+ int selectedIndex = -1;
+ File[] mediaDirs = ContextCompat.getExternalFilesDirs(context, null);
+ String[] folders = new String[mediaDirs.length];
+ CharSequence[] choices = new CharSequence[mediaDirs.length];
+ for(int i=0; i < mediaDirs.length; i++) {
+ String path = folders[i] = mediaDirs[i].getAbsolutePath();
+ if(dataFolder.equals(path)) {
+ selectedIndex = i;
+ }
+ int index = path.indexOf("Android");
+ if(index >= 0) {
+ choices[i] = path.substring(0, index);
+ } else {
+ choices[i] = path;
+ }
+ long bytes = StorageUtils.getFreeSpaceAvailable();
+ String freeSpace = String.format(context.getString(R.string.free_space_label),
+ Converter.byteToString(bytes));
+ choices[i] = Html.fromHtml("<html><small>" + choices[i]
+ + " [" + freeSpace + "]" + "</small></html>");
+ }
+ MaterialDialog dialog = new MaterialDialog.Builder(ui.getActivity())
+ .title(R.string.choose_data_directory)
+ .content(R.string.choose_data_directory_message)
+ .items(choices)
+ .itemsCallbackSingleChoice(selectedIndex, (dialog1, itemView, which, text) -> {
+ String folder = folders[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();
- AlertDialog.Builder builder = new AlertDialog.Builder(context);
- builder.setTitle(R.string.pref_autoUpdateIntervallOrTime_title);
- builder.setMessage(R.string.pref_autoUpdateIntervallOrTime_message);
- builder.setNegativeButton(R.string.pref_autoUpdateIntervallOrTime_Disable, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- UserPreferences.setUpdateInterval(0);
- }
- });
- builder.setNeutralButton(R.string.pref_autoUpdateIntervallOrTime_Interval, new DialogInterface.OnClickListener() {
+ 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 onClick(DialogInterface dialog, int which) {
+ public void onPositive(MaterialDialog dialog) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
final String[] entries = getUpdateIntervalEntries(values);
- builder.setSingleChoiceItems(entries, -1, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- int hours = Integer.valueOf(values[which]);
- UserPreferences.setUpdateInterval(hours);
- dialog.dismiss();
- }
+ builder.setSingleChoiceItems(entries, -1, (dialog1, which) -> {
+ int hours = Integer.valueOf(values[which]);
+ UserPreferences.setUpdateInterval(hours);
+ dialog1.dismiss();
});
builder.setNegativeButton(context.getString(R.string.cancel_label), null);
builder.show();
}
- });
- builder.setPositiveButton(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- int hourOfDay = 7, minute = 0;
- int[] updateTime = UserPreferences.getUpdateTimeOfDay();
- if (updateTime.length == 2) {
- hourOfDay = updateTime[0];
- minute = updateTime[1];
- }
- TimePickerDialog timePickerDialog = new TimePickerDialog(context, new TimePickerDialog.OnTimeSetListener() {
- @Override
- public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
- if (view.getTag() == null) { // onTimeSet() may get called twice!
- view.setTag("TAGGED");
- UserPreferences.setUpdateTimeOfDay(hourOfDay, minute);
- }
- }
- }, hourOfDay, minute, DateFormat.is24HourFormat(context));
- timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
- timePickerDialog.show();
- }
+
+ @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);
+ }
+ }, 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);
+ }
+ });
+ 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/service/PlayerWidgetService.java b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
index 990b3bd54..d7a049a32 100644
--- a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
+++ b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
@@ -16,7 +16,6 @@ import android.widget.RemoteViews;
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.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
@@ -59,9 +58,9 @@ public class PlayerWidgetService extends Service {
if (media.hasAlmostEnded()) {
Log.d(TAG, "smart mark as read");
FeedItem item = media.getItem();
- DBWriter.markItemRead(this, item, true, false);
+ DBWriter.markItemPlayed(item, FeedItem.PLAYED, false);
DBWriter.removeQueueItem(this, item, false);
- DBWriter.addItemToPlaybackHistory(this, media);
+ DBWriter.addItemToPlaybackHistory(media);
if (item.getFeed().getPreferences().getCurrentAutoDelete()) {
Log.d(TAG, "Delete " + media.toString());
DBWriter.deleteFeedMediaOfItem(this, media.getId());
diff --git a/app/src/main/res/layout-v14/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 8d2c7fb17..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
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/all_episodes_fragment.xml b/app/src/main/res/layout/all_episodes_fragment.xml
index 19db02f1d..81562b560 100644
--- a/app/src/main/res/layout/all_episodes_fragment.xml
+++ b/app/src/main/res/layout/all_episodes_fragment.xml
@@ -7,35 +7,14 @@
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/audioplayer_activity.xml b/app/src/main/res/layout/audioplayer_activity.xml
index 827e06e00..379028c8e 100644
--- a/app/src/main/res/layout/audioplayer_activity.xml
+++ b/app/src/main/res/layout/audioplayer_activity.xml
@@ -26,7 +26,6 @@
android:paddingLeft="8dp"
android:paddingRight="8dp">
-
<TextView
android:id="@+id/txtvTitle"
android:layout_width="0dp"
@@ -138,7 +137,6 @@
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" />
diff --git a/app/src/main/res/layout/choose_speed_dialog.xml b/app/src/main/res/layout/choose_speed_dialog.xml
new file mode 100644
index 000000000..1b461c77e
--- /dev/null
+++ b/app/src/main/res/layout/choose_speed_dialog.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v7.widget.CardView
+ xmlns:card_view="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/card_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_margin="16dp"
+ card_view:cardElevation="12dp"
+ card_view:cardCornerRadius="4dp"
+ card_view:cardUseCompatPadding="true"
+ card_view:cardBackgroundColor="?attr/overlay_background">
+
+ <LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:gravity="center">
+
+ <TextView
+ android:id="@+id/txtvSelectedSpeed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:textSize="22sp"
+ android:textStyle="bold"/>
+
+ <SeekBar
+ android:id="@+id/sbSelectSpeed"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"/>
+
+ </LinearLayout>
+
+ </android.support.v7.widget.CardView>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/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 b035f2516..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
diff --git a/app/src/main/res/layout/external_player_fragment.xml b/app/src/main/res/layout/external_player_fragment.xml
index f9608fafc..ef83baa17 100644
--- a/app/src/main/res/layout/external_player_fragment.xml
+++ b/app/src/main/res/layout/external_player_fragment.xml
@@ -13,7 +13,7 @@
<View
android:layout_width="match_parent"
android:layout_height="2dp"
- android:background="@color/bright_blue"/>
+ android:background="@color/holo_blue_light"/>
<LinearLayout
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml
index edae51847..2b49b4b35 100644
--- a/app/src/main/res/layout/feedinfo.xml
+++ b/app/src/main/res/layout/feedinfo.xml
@@ -46,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
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/nav_feedlistitem.xml b/app/src/main/res/layout/nav_feedlistitem.xml
index b9958257c..f0cbb56c1 100644
--- a/app/src/main/res/layout/nav_feedlistitem.xml
+++ b/app/src/main/res/layout/nav_feedlistitem.xml
@@ -37,7 +37,7 @@
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"
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 ec5ab1e6a..7a5652c45 100644
--- a/app/src/main/res/layout/new_episodes_listitem.xml
+++ b/app/src/main/res/layout/new_episodes_listitem.xml
@@ -5,6 +5,7 @@
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
android:orientation="horizontal"
+ android:background="?attr/selectableItemBackground"
tools:background="@android:color/darker_gray">
<RelativeLayout
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/queue_fragment.xml b/app/src/main/res/layout/queue_fragment.xml
index 339369971..901ac49fc 100644
--- a/app/src/main/res/layout/queue_fragment.xml
+++ b/app/src/main/res/layout/queue_fragment.xml
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:dslv="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -12,7 +11,7 @@
android:layout_alignParentTop="true"
android:gravity="center"
android:textSize="12sp"
- android:text="42 episodes \u2022 5 hours 17 minutes"/>
+ android:text=""/>
<View
android:id="@+id/divider"
@@ -21,26 +20,12 @@
android:layout_below="@id/info_bar"
android:background="?android:attr/listDivider"/>
- <com.mobeta.android.dslv.DragSortListView
- android:id="@android:id/list"
+ <android.support.v7.widget.RecyclerView
+ android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:clipToPadding="false"
- android:scrollbarStyle="outsideOverlay"
- android:layout_below="@+id/divider"
- 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_below="@id/divider"
+ android:scrollbars="vertical"/>
<TextView
android:id="@id/android:empty"
@@ -58,19 +43,4 @@
android:indeterminateOnly="true"
android:visibility="gone" />
- <LinearLayout
- android:id="@+id/undobar"
- android:layout_alignParentBottom="true"
- style="@style/UndoBar">
-
- <TextView
- android:id="@+id/undobar_message"
- style="@style/UndoBarMessage"/>
-
- <Button
- android:id="@+id/undobar_button"
- style="@style/UndoBarButton"/>
-
- </LinearLayout>
-
</RelativeLayout>
diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml
index 38076ff51..8572f5e05 100644
--- a/app/src/main/res/layout/queue_listitem.xml
+++ b/app/src/main/res/layout/queue_listitem.xml
@@ -6,24 +6,28 @@
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_threeline_height"
android:orientation="horizontal"
- android:paddingLeft="16dp"
+ android:paddingLeft="8dp"
+ android:gravity="center_vertical"
+ android:background="?attr/selectableItemBackground"
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="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginLeft="-16dp"
+ android:layout_marginRight="-20dp"
+ android:gravity="center"
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" />
<RelativeLayout
android:layout_width="wrap_content"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp">
<TextView
android:id="@+id/txtvPlaceholder"
android:layout_width="@dimen/thumbnail_length_queue_item"
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/menu/allepisodes_context.xml b/app/src/main/res/menu/allepisodes_context.xml
index 171e509a8..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"
diff --git a/app/src/main/res/menu/feeditem_options.xml b/app/src/main/res/menu/feeditem_options.xml
index 650912ea2..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">
diff --git a/app/src/main/res/menu/queue_context.xml b/app/src/main/res/menu/queue_context.xml
index d09f3c84c..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" />
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 35fb60c58..38350155a 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -2,7 +2,7 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<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"
@@ -17,14 +17,14 @@
android:key="prefHiddenDrawerItems"
android:summary="@string/pref_nav_drawer_items_sum"
android:title="@string/pref_nav_drawer_items_title" />
- <ListPreference
+ <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"/>
- <ListPreference
+ <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"
@@ -39,7 +39,7 @@
android:summary="@string/pref_expandNotify_sum"
android:title="@string/pref_expandNotify_title"/>
<CheckBoxPreference
- android:defaultValue="false"
+ android:defaultValue="true"
android:enabled="true"
android:key="prefPersistNotify"
android:summary="@string/pref_persistNotify_sum"
@@ -47,6 +47,12 @@
<CheckBoxPreference
android:defaultValue="true"
android:enabled="true"
+ android:key="prefLockscreenBackground"
+ android:summary="@string/pref_lockscreen_background_sum"
+ android:title="@string/pref_lockscreen_background_title"/>
+ <CheckBoxPreference
+ android:defaultValue="true"
+ android:enabled="true"
android:key="prefShowDownloadReport"
android:summary="@string/pref_showDownloadReport_sum"
android:title="@string/pref_showDownloadReport_title"/>
@@ -67,7 +73,7 @@
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
android:defaultValue="true"
@@ -79,16 +85,29 @@
<CheckBoxPreference
android:defaultValue="false"
android:enabled="true"
+ android:dependency="prefPauseOnHeadsetDisconnect"
+ android:key="prefUnpauseOnBluetoothReconnect"
+ android:summary="@string/pref_unpauseOnBluetoothReconnect_sum"
+ android:title="@string/pref_unpauseOnBluetoothReconnect_title"/>
+ <CheckBoxPreference
+ android:defaultValue="true"
+ android:enabled="true"
android:key="prefFollowQueue"
android:summary="@string/pref_followQueue_sum"
android:title="@string/pref_followQueue_title"/>
<CheckBoxPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ android:key="prefSkipKeepsEpisode"
+ android:summary="@string/pref_skip_keeps_episodes_sum"
+ android:title="@string/pref_skip_keeps_episodes_title"/>
+ <CheckBoxPreference
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"
@@ -125,12 +144,21 @@
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"/>
+
+ <com.afollestad.materialdialogs.prefs.MaterialEditTextPreference
+ android:defaultValue="4"
android:inputType="number"
android:key="prefParallelDownloads"
android:title="@string/pref_parallel_downloads_title"/>
- <ListPreference
+ <com.afollestad.materialdialogs.prefs.MaterialListPreference
android:defaultValue="20"
android:entries="@array/episode_cache_size_entries"
android:key="prefEpisodeCacheSize"
@@ -217,7 +245,15 @@
<Preference
android:key="prefAbout"
android:title="@string/about_pref"/>
+ </PreferenceCategory>
+ <PreferenceCategory android:title="@string/experimental_pref">
+ <CheckBoxPreference
+ android:defaultValue="false"
+ android:enabled="false"
+ android:key="prefSonic"
+ android:summary="@string/pref_sonic_message"
+ android:title="@string/pref_sonic_title"/>
</PreferenceCategory>
</PreferenceScreen>
diff --git a/app/src/main/templates/about.html b/app/src/main/templates/about.html
index 887b547f4..21599bdb8 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,25 +45,21 @@
</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@</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>
-
-<h2>DragSortListView <a href="https://github.com/bauerca/drag-sort-listview">(Link)</a></h2>
-by Carl Bauer, licensed under the Apache 2.0 license <a href="LICENSE_DSLV.txt">(View)</a>
+by The Apache Software Foundation, licensed under the Apache 2.0 license <a href="LICENSE_APACHE_COMMONS.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>
@@ -77,6 +79,9 @@ licensed under the MIT license <a href="LICENSE_JSOUP.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>
by Square, licensed under the Apache 2.0 license <a href="LICENSE_OKHTTP.txt">(View)</a>
@@ -86,11 +91,17 @@ by Square, licensed under the Apache 2.0 license <a href="LICENSE_OKIO.txt">(Vie
<h2>Presto Client <a href="http://www.aocate.com/presto/">(Link)</a></h2>
licensed under the Apache 2.0 license <a href="LICENSE_PRESTO.txt">(View)</a>
+<h2>RecyclerView-FlexibleDivider <a href="https://github.com/yqritc/RecyclerView-FlexibleDivider">(Link)</a></h2>
+licensed under the Apache 2.0 license <a href="LICENSE_FLEXIBLE_DIVIDER.txt">(View)</a>
+
<h2>RxAndroid <a href="https://github.com/ReactiveX/RxAndroid">(Link)</a></h2>
licensed under the Apache 2.0 license <a href="LICENSE_RX_ANDROID.txt">(View)</a>
<h2>StackBlur <a href="https://github.com/kikoso/android-stackblur">(Link)</a></h2>
by Enrique L&oacute;pez Ma&ntilde;as, licensed under the Apache 2.0 license <a href="LICENSE_STACKBLUR.txt">(View)</a>
+<h2>AntennaPod-AudioPlayer <a href="https://github.com/AntennaPod/AntennaPod-AudioPlayer/">(Link)</a></h2>
+by the AntennaPod team, licensed under the Apache 2.0 license <a href="LICENSE_ANTENNAPOD_AUDIOPLAYER.txt">(View)</a>
+
</body>
</html>