summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/androidTest/assets/3sec.mp3bin49043 -> 0 bytes
-rw-r--r--app/src/androidTest/assets/testfile.mp3bin20606 -> 0 bytes
-rw-r--r--app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java73
-rw-r--r--app/src/androidTest/java/de/test/antennapod/IgnoreOnCi.java15
-rw-r--r--app/src/androidTest/java/de/test/antennapod/NthMatcher.java9
-rw-r--r--app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java308
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java104
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java29
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java36
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java11
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/AutoDownloadTest.java164
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java1
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java95
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java30
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java17
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java39
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java277
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java375
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java351
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java10
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java120
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/TextOnlyFeedsTest.java71
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java47
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java128
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java73
-rw-r--r--app/src/main/AndroidManifest.xml20
-rw-r--r--app/src/main/assets/30sec.mp3bin0 -> 120017 bytes
-rw-r--r--app/src/main/assets/developers.csv119
-rw-r--r--app/src/main/assets/licenses.xml105
-rwxr-xr-xapp/src/main/assets/logo.pngbin60183 -> 0 bytes
-rw-r--r--app/src/main/assets/testfile.mp3bin20606 -> 0 bytes
-rw-r--r--app/src/main/assets/translators.csv46
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java61
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java158
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java29
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java170
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java139
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java131
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java107
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java59
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java24
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java64
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java55
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java30
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java112
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java51
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java34
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java120
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java26
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java73
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java61
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java37
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java26
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java51
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java70
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java237
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java196
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java30
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java101
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java26
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java65
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java56
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java126
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java64
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java60
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java27
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java65
-rw-r--r--app/src/main/play/listings/de-DE/full-description.txt65
-rw-r--r--app/src/main/play/listings/en-US/full-description.txt63
-rw-r--r--app/src/main/play/listings/es-ES/full-description.txt53
-rw-r--r--app/src/main/play/listings/fr-FR/full-description.txt65
-rw-r--r--app/src/main/play/release-notes/en-US/default.txt15
-rw-r--r--app/src/main/res/layout/about_teaser.xml7
-rw-r--r--app/src/main/res/layout/activity_widget_config.xml80
-rw-r--r--app/src/main/res/layout/addfeed.xml9
-rw-r--r--app/src/main/res/layout/audio_controls.xml3
-rw-r--r--app/src/main/res/layout/authentication_dialog.xml109
-rw-r--r--app/src/main/res/layout/choose_data_folder_dialog.xml12
-rw-r--r--app/src/main/res/layout/choose_data_folder_dialog_entry.xml7
-rw-r--r--app/src/main/res/layout/edit_text_dialog.xml15
-rw-r--r--app/src/main/res/layout/feeditem_fragment.xml41
-rw-r--r--app/src/main/res/layout/feeditem_pager_fragment.xml6
-rw-r--r--app/src/main/res/layout/fragment_itunes_search.xml2
-rw-r--r--app/src/main/res/layout/mediaplayerinfo_activity.xml2
-rw-r--r--app/src/main/res/layout/numberpicker.xml2
-rw-r--r--app/src/main/res/layout/onlinefeedview_activity.xml63
-rw-r--r--app/src/main/res/layout/onlinefeedview_header.xml24
-rw-r--r--app/src/main/res/layout/proxy_settings.xml6
-rw-r--r--app/src/main/res/layout/quick_feed_discovery.xml35
-rw-r--r--app/src/main/res/layout/quick_feed_discovery_item.xml6
-rw-r--r--app/src/main/res/layout/simple_icon_list_item.xml41
-rw-r--r--app/src/main/res/layout/time_dialog.xml9
-rw-r--r--app/src/main/res/layout/videoplayer_activity.xml13
-rw-r--r--app/src/main/res/menu/episodes.xml4
-rw-r--r--app/src/main/res/menu/feedlist.xml19
-rw-r--r--app/src/main/res/xml/feed_settings.xml4
-rw-r--r--app/src/main/res/xml/player_widget_info.xml3
-rw-r--r--app/src/main/res/xml/preferences_about.xml33
-rw-r--r--app/src/main/res/xml/preferences_autodownload.xml6
-rw-r--r--app/src/main/res/xml/preferences_playback.xml30
-rw-r--r--app/src/main/res/xml/preferences_user_interface.xml18
-rw-r--r--app/src/main/templates/about.html183
142 files changed, 3949 insertions, 2907 deletions
diff --git a/app/src/androidTest/assets/3sec.mp3 b/app/src/androidTest/assets/3sec.mp3
deleted file mode 100644
index 8ae450d01..000000000
--- a/app/src/androidTest/assets/3sec.mp3
+++ /dev/null
Binary files differ
diff --git a/app/src/androidTest/assets/testfile.mp3 b/app/src/androidTest/assets/testfile.mp3
deleted file mode 100644
index f15faadf3..000000000
--- a/app/src/androidTest/assets/testfile.mp3
+++ /dev/null
Binary files differ
diff --git a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
index f895b4d5e..bbd8a705c 100644
--- a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
@@ -1,11 +1,14 @@
package de.test.antennapod;
import android.content.Context;
+import android.content.Intent;
+import androidx.annotation.IdRes;
import androidx.annotation.StringRes;
import androidx.test.InstrumentationRegistry;
import androidx.test.espresso.PerformException;
import androidx.test.espresso.UiController;
import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.contrib.DrawerActions;
import androidx.test.espresso.contrib.RecyclerViewActions;
import androidx.test.espresso.util.HumanReadables;
@@ -13,11 +16,16 @@ import androidx.test.espresso.util.TreeIterables;
import android.view.View;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.service.download.DownloadService;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.dialog.RatingDialog;
+import org.awaitility.Awaitility;
+import org.awaitility.core.ConditionTimeoutException;
import org.hamcrest.Matcher;
import java.io.File;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import static androidx.test.espresso.Espresso.onView;
@@ -26,6 +34,7 @@ import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.Matchers.allOf;
public class EspressoTestUtils {
/**
@@ -75,6 +84,31 @@ public class EspressoTestUtils {
}
/**
+ * Perform action of waiting for a specific view id.
+ * https://stackoverflow.com/a/30338665/
+ * @param id The id of the child to click.
+ */
+ public static ViewAction clickChildViewWithId(final @IdRes int id) {
+ return new ViewAction() {
+ @Override
+ public Matcher<View> getConstraints() {
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return "Click on a child view with specified id.";
+ }
+
+ @Override
+ public void perform(UiController uiController, View view) {
+ View v = view.findViewById(id);
+ v.performClick();
+ }
+ };
+ }
+
+ /**
* Clear all app databases
*/
public static void clearPreferences() {
@@ -114,7 +148,9 @@ public class EspressoTestUtils {
public static void clickPreference(@StringRes int title) {
onView(withId(R.id.recycler_view)).perform(
- RecyclerViewActions.actionOnItem(hasDescendant(withText(title)),
+ RecyclerViewActions.actionOnItem(
+ allOf(hasDescendant(withText(title)),
+ hasDescendant(withId(android.R.id.widget_frame))),
click()));
}
@@ -123,8 +159,37 @@ public class EspressoTestUtils {
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
}
- public static void closeNavDrawer() {
- onView(isRoot()).perform(waitForView(withId(R.id.drawer_layout), 1000));
- onView(withId(R.id.drawer_layout)).perform(DrawerActions.close());
+ public static ViewInteraction onDrawerItem(Matcher<View> viewMatcher) {
+ return onView(allOf(viewMatcher, withId(R.id.txtvTitle)));
+ }
+
+ public static void tryKillPlaybackService() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ context.stopService(new Intent(context, PlaybackService.class));
+ try {
+ // Android has no reliable way to stop a service instantly.
+ // Calling stopSelf marks allows the system to destroy the service but the actual call
+ // to onDestroy takes until the next GC of the system, which we can not influence.
+ // Try to wait for the service at least a bit.
+ Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> !PlaybackService.isRunning);
+ } catch (ConditionTimeoutException e) {
+ e.printStackTrace();
+ }
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+
+ public static void tryKillDownloadService() {
+ Context context = InstrumentationRegistry.getTargetContext();
+ context.stopService(new Intent(context, DownloadService.class));
+ try {
+ // Android has no reliable way to stop a service instantly.
+ // Calling stopSelf marks allows the system to destroy the service but the actual call
+ // to onDestroy takes until the next GC of the system, which we can not influence.
+ // Try to wait for the service at least a bit.
+ Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> !DownloadService.isRunning);
+ } catch (ConditionTimeoutException e) {
+ e.printStackTrace();
+ }
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation().waitForIdleSync();
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/IgnoreOnCi.java b/app/src/androidTest/java/de/test/antennapod/IgnoreOnCi.java
new file mode 100644
index 000000000..41ab15b9b
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/IgnoreOnCi.java
@@ -0,0 +1,15 @@
+package de.test.antennapod;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Tests with this annotation are ignored on CI. This could be reasonable
+ * if the performance of the CI server is not enough to provide a reliable result.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface IgnoreOnCi {
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/NthMatcher.java b/app/src/androidTest/java/de/test/antennapod/NthMatcher.java
index f9ecacda5..85eabcc63 100644
--- a/app/src/androidTest/java/de/test/antennapod/NthMatcher.java
+++ b/app/src/androidTest/java/de/test/antennapod/NthMatcher.java
@@ -11,11 +11,7 @@ public class NthMatcher {
return nth(matcher, 1);
}
- public static <T> Matcher<T> second(final Matcher<T> matcher) {
- return nth(matcher, 2);
- }
-
- private static <T> Matcher<T> nth(final Matcher<T> matcher, final int index) {
+ public static <T> Matcher<T> nth(final Matcher<T> matcher, final int index) {
return new BaseMatcher<T>() {
AtomicInteger count = new AtomicInteger(0);
@@ -31,7 +27,8 @@ public class NthMatcher {
@Override
public void describeTo(final Description description) {
- description.appendText("should return first matching item");
+ description.appendText("Item #" + index + " ");
+ description.appendDescriptionOf(matcher);
}
};
}
diff --git a/app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java b/app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java
index cd3847f17..161393b8a 100644
--- a/app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java
@@ -29,7 +29,7 @@ public class GPodnetServiceTest {
private static final String PW = "";
@Before
- protected void setUp() {
+ public void setUp() {
service = new GpodnetService();
}
diff --git a/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java
new file mode 100644
index 000000000..cc380813e
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java
@@ -0,0 +1,308 @@
+package de.test.antennapod.playback;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.view.KeyEvent;
+import android.view.View;
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+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.util.IntentUtils;
+import de.danoeh.antennapod.core.util.LongList;
+import de.test.antennapod.EspressoTestUtils;
+import de.test.antennapod.IgnoreOnCi;
+import de.test.antennapod.ui.UITestUtils;
+import org.awaitility.Awaitility;
+import org.hamcrest.Matcher;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
+import static androidx.test.espresso.matcher.ViewMatchers.hasMinimumChildCount;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static de.test.antennapod.EspressoTestUtils.clickChildViewWithId;
+import static de.test.antennapod.EspressoTestUtils.onDrawerItem;
+import static de.test.antennapod.EspressoTestUtils.openNavDrawer;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test cases for starting and ending playback from the MainActivity and AudioPlayerActivity.
+ */
+@LargeTest
+@IgnoreOnCi
+@RunWith(Parameterized.class)
+public class PlaybackTest {
+ @Rule
+ public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class, false, false);
+
+ @Parameterized.Parameter(value = 0)
+ public String playerToUse;
+ private UITestUtils uiTestUtils;
+ protected Context context;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Collection<Object[]> initParameters() {
+ return Arrays.asList(new Object[][] { { "exoplayer" }, { "builtin" }, { "sonic" } });
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.clearDatabase();
+ EspressoTestUtils.makeNotFirstRun();
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putString(UserPreferences.PREF_MEDIA_PLAYER, playerToUse).apply();
+
+ uiTestUtils = new UITestUtils(context);
+ uiTestUtils.setup();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ activityTestRule.finishActivity();
+ EspressoTestUtils.tryKillPlaybackService();
+ uiTestUtils.tearDown();
+ }
+
+ @Test
+ public void testContinousPlaybackOffMultipleEpisodes() throws Exception {
+ setContinuousPlaybackPreference(false);
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+ playFromQueue(0);
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(
+ () -> uiTestUtils.getPlaybackController(getActivity()).getStatus() == PlayerStatus.INITIALIZED);
+ }
+
+ @Test
+ public void testContinuousPlaybackOnMultipleEpisodes() throws Exception {
+ setContinuousPlaybackPreference(true);
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+
+ List<FeedItem> queue = DBReader.getQueue();
+ final FeedItem first = queue.get(0);
+ final FeedItem second = queue.get(1);
+
+ playFromQueue(0);
+ Awaitility.await().atMost(2, TimeUnit.SECONDS).until(
+ () -> first.getMedia().equals(uiTestUtils.getCurrentMedia()));
+ Awaitility.await().atMost(6, TimeUnit.SECONDS).until(
+ () -> second.getMedia().equals(uiTestUtils.getCurrentMedia()));
+ }
+
+
+ @Test
+ public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
+ replayEpisodeCheck(true);
+ }
+
+ @Test
+ public void testReplayEpisodeContinuousPlaybackOff() throws Exception {
+ replayEpisodeCheck(false);
+ }
+
+ @Test
+ public void testSmartMarkAsPlayed_Skip_Average() throws Exception {
+ doTestSmartMarkAsPlayed_Skip_ForEpisode(0);
+ }
+
+ @Test
+ public void testSmartMarkAsPlayed_Skip_LastEpisodeInQueue() throws Exception {
+ doTestSmartMarkAsPlayed_Skip_ForEpisode(-1);
+ }
+
+ @Test
+ public void testSmartMarkAsPlayed_Pause_WontAffectItem() throws Exception {
+ setSmartMarkAsPlayedPreference(60);
+
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+
+ final int fiIdx = 0;
+ final FeedItem feedItem = DBReader.getQueue().get(fiIdx);
+
+ playFromQueue(fiIdx);
+
+ // let playback run a bit then pause
+ Awaitility.await()
+ .atMost(1000, MILLISECONDS)
+ .until(() -> PlayerStatus.PLAYING == uiTestUtils.getPlaybackController(getActivity()).getStatus());
+ pauseEpisode();
+ Awaitility.await()
+ .atMost(1000, MILLISECONDS)
+ .until(() -> PlayerStatus.PAUSED == uiTestUtils.getPlaybackController(getActivity()).getStatus());
+
+ assertThat("Ensure even with smart mark as play, after pause, the item remains in the queue.",
+ DBReader.getQueue(), hasItems(feedItem));
+ assertThat("Ensure even with smart mark as play, after pause, the item played status remains false.",
+ DBReader.getFeedItem(feedItem.getId()).isPlayed(), is(false));
+ }
+
+ @Test
+ public void testStartLocal() throws Exception {
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+ DBWriter.clearQueue().get();
+ startLocalPlayback();
+ }
+
+ @Test
+ public void testContinousPlaybackOffSingleEpisode() throws Exception {
+ setContinuousPlaybackPreference(false);
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+ DBWriter.clearQueue().get();
+ startLocalPlayback();
+ }
+
+ protected MainActivity getActivity() {
+ return activityTestRule.getActivity();
+ }
+
+ protected void setContinuousPlaybackPreference(boolean value) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit();
+ }
+
+ protected void setSkipKeepsEpisodePreference(boolean value) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putBoolean(UserPreferences.PREF_SKIP_KEEPS_EPISODE, value).commit();
+ }
+
+ protected void setSmartMarkAsPlayedPreference(int smartMarkAsPlayedSecs) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putString(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS,
+ Integer.toString(smartMarkAsPlayedSecs, 10))
+ .commit();
+ }
+
+ private void skipEpisode() {
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
+ }
+
+ protected void pauseEpisode() {
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+ }
+
+ protected void startLocalPlayback() {
+ openNavDrawer();
+ onDrawerItem(withText(R.string.episodes_label)).perform(click());
+ onView(isRoot()).perform(waitForView(withText(R.string.all_episodes_short_label), 1000));
+ onView(withText(R.string.all_episodes_short_label)).perform(click());
+
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
+ Matcher<View> allEpisodesMatcher = allOf(withId(android.R.id.list), isDisplayed(), hasMinimumChildCount(2));
+ onView(isRoot()).perform(waitForView(allEpisodesMatcher, 1000));
+ onView(allEpisodesMatcher).perform(actionOnItemAtPosition(0, clickChildViewWithId(R.id.butSecondaryAction)));
+
+ FeedMedia media = episodes.get(0).getMedia();
+ Awaitility.await().atMost(1, TimeUnit.SECONDS).until(
+ () -> media.equals(uiTestUtils.getCurrentMedia()));
+ }
+
+ /**
+ *
+ * @param itemIdx The 0-based index of the episode to be played in the queue.
+ */
+ protected void playFromQueue(int itemIdx) {
+ final List<FeedItem> queue = DBReader.getQueue();
+
+ Matcher<View> queueMatcher = allOf(withId(R.id.recyclerView), isDisplayed(), hasMinimumChildCount(2));
+ onView(isRoot()).perform(waitForView(queueMatcher, 1000));
+ onView(queueMatcher).perform(actionOnItemAtPosition(itemIdx, clickChildViewWithId(R.id.butSecondaryAction)));
+
+ FeedMedia media = queue.get(itemIdx).getMedia();
+ Awaitility.await().atMost(1, TimeUnit.SECONDS).until(
+ () -> media.equals(uiTestUtils.getCurrentMedia()));
+
+ }
+
+ /**
+ * Check if an episode can be played twice without problems.
+ */
+ protected void replayEpisodeCheck(boolean followQueue) throws Exception {
+ setContinuousPlaybackPreference(followQueue);
+ uiTestUtils.addLocalFeedData(true);
+ DBWriter.clearQueue().get();
+ activityTestRule.launchActivity(new Intent());
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
+
+ startLocalPlayback();
+ FeedMedia media = episodes.get(0).getMedia();
+ Awaitility.await().atMost(1, TimeUnit.SECONDS).until(
+ () -> media.equals(uiTestUtils.getCurrentMedia()));
+
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(
+ () -> !media.equals(uiTestUtils.getCurrentMedia()));
+
+ startLocalPlayback();
+
+ Awaitility.await().atMost(1, TimeUnit.SECONDS).until(
+ () -> media.equals(uiTestUtils.getCurrentMedia()));
+ }
+
+ protected void doTestSmartMarkAsPlayed_Skip_ForEpisode(int itemIdxNegAllowed) throws Exception {
+ setSmartMarkAsPlayedPreference(60);
+ // ensure when an episode is skipped, it is removed due to smart as played
+ setSkipKeepsEpisodePreference(false);
+ uiTestUtils.setMediaFileName("30sec.mp3");
+ uiTestUtils.addLocalFeedData(true);
+
+ LongList queue = DBReader.getQueueIDList();
+ int fiIdx;
+ if (itemIdxNegAllowed >= 0) {
+ fiIdx = itemIdxNegAllowed;
+ } else { // negative index: count from the end, with -1 being the last one, etc.
+ fiIdx = queue.size() + itemIdxNegAllowed;
+ }
+ final long feedItemId = queue.get(fiIdx);
+ queue.removeIndex(fiIdx);
+ assertFalse(queue.contains(feedItemId)); // Verify that episode is in queue only once
+
+ activityTestRule.launchActivity(new Intent());
+ playFromQueue(fiIdx);
+
+ skipEpisode();
+
+ // assert item no longer in queue (needs to wait till skip is asynchronously processed)
+ Awaitility.await()
+ .atMost(5000, MILLISECONDS)
+ .until(() -> !DBReader.getQueueIDList().contains(feedItemId));
+ assertTrue(DBReader.getFeedItem(feedItemId).isPlayed());
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java
index cb099cc85..1ca4de1ad 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java
@@ -1,10 +1,13 @@
package de.test.antennapod.service.download;
+import android.content.Context;
+import android.content.Intent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import de.test.antennapod.EspressoTestUtils;
import org.awaitility.Awaitility;
import org.awaitility.core.ConditionTimeoutException;
import org.junit.After;
@@ -21,10 +24,12 @@ import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.service.download.Downloader;
+import de.danoeh.antennapod.core.service.download.DownloaderFactory;
import de.danoeh.antennapod.core.service.download.StubDownloader;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -47,17 +52,21 @@ public class DownloadServiceTest {
private Feed testFeed = null;
private FeedMedia testMedia11 = null;
- private DownloadService.DownloaderFactory origFactory = null;
+ private DownloaderFactory origFactory = null;
@Before
public void setUp() throws Exception {
+ EspressoTestUtils.clearDatabase();
+ EspressoTestUtils.clearPreferences();
origFactory = DownloadService.getDownloaderFactory();
testFeed = setUpTestFeeds();
testMedia11 = testFeed.getItemAtIndex(0).getMedia();
}
private Feed setUpTestFeeds() throws Exception {
- Feed feed = new Feed("url", null, "Test Feed title 1");
+ // To avoid complication in case of test failures, leaving behind orphaned
+ // media files: add a timestamp so that each test run will have its own directory for media files.
+ Feed feed = new Feed("url", null, "Test Feed title 1 " + System.currentTimeMillis());
List<FeedItem> items = new ArrayList<>();
feed.setItems(items);
FeedItem item1 = new FeedItem(0, "Item 1-1", "Item 1-1", "url", new Date(), FeedItem.NEW, feed);
@@ -73,32 +82,51 @@ public class DownloadServiceTest {
@After
public void tearDown() throws Exception {
DownloadService.setDownloaderFactory(origFactory);
+ Context context = InstrumentationRegistry.getTargetContext();
+ DownloadRequester.getInstance().cancelAllDownloads(context);
+ context.stopService(new Intent(context, DownloadService.class));
+ EspressoTestUtils.tryKillDownloadService();
}
@Test
- public void testEventsGeneratedCaseMediaDownloadSuccess() throws Exception {
+ public void testEventsGeneratedCaseMediaDownloadSuccess_noEnqueue() throws Exception {
+ doTestEventsGeneratedCaseMediaDownloadSuccess(false, 1);
+ }
+
+ @Test
+ public void testEventsGeneratedCaseMediaDownloadSuccess_withEnqueue() throws Exception {
+ // enqueue itself generates additional FeedItem event
+ doTestEventsGeneratedCaseMediaDownloadSuccess(true, 2);
+ }
+
+ private void doTestEventsGeneratedCaseMediaDownloadSuccess(boolean enqueueDownloaded,
+ int numEventsExpected)
+ throws Exception {
// create a stub download that returns successful
//
// OPEN: Ideally, I'd like the download time long enough so that multiple in-progress DownloadEvents
// are generated (to simulate typical download), but it'll make download time quite long (1-2 seconds)
// to do so
DownloadService.setDownloaderFactory(new StubDownloaderFactory(50, downloadStatus -> {
- downloadStatus.setSuccessful();
+ downloadStatus.setSuccessful();
}));
+ UserPreferences.setEnqueueDownloadedEpisodes(enqueueDownloaded);
withFeedItemEventListener(feedItemEventListener -> {
try {
assertEquals(0, feedItemEventListener.getEvents().size());
assertFalse("The media in test should not yet been downloaded",
DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
- DownloadRequester.getInstance().downloadMedia(InstrumentationRegistry.getTargetContext(),
- testMedia11);
- Awaitility.await()
+ DownloadRequester.getInstance().downloadMedia(false, InstrumentationRegistry.getTargetContext(),
+ testMedia11.getItem());Awaitility.await()
.atMost(1000, TimeUnit.MILLISECONDS)
- .until(() -> feedItemEventListener.getEvents().size() > 0);
+ .until(() -> feedItemEventListener.getEvents().size() >= numEventsExpected);
assertTrue("After media download has completed, FeedMedia object in db should indicate so.",
DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
+ assertEquals("The FeedItem should have been " + (enqueueDownloaded ? "" : "not ") + "enqueued",
+ enqueueDownloaded,
+ DBReader.getQueueIDList().contains(testMedia11.getItem().getId()));
} catch (ConditionTimeoutException cte) {
fail("The expected FeedItemEvent (for media download complete) has not been posted. "
+ cte.getMessage());
@@ -106,7 +134,65 @@ public class DownloadServiceTest {
});
}
- private static class StubDownloaderFactory implements DownloadService.DownloaderFactory {
+ @Test
+ public void testCancelDownload_UndoEnqueue_Normal() throws Exception {
+ doTestCancelDownload_UndoEnqueue(false);
+ }
+
+ @Test
+ public void testCancelDownload_UndoEnqueue_AlreadyInQueue() throws Exception {
+ doTestCancelDownload_UndoEnqueue(true);
+ }
+
+ private void doTestCancelDownload_UndoEnqueue(boolean itemAlreadyInQueue) throws Exception {
+ Context context = InstrumentationRegistry.getTargetContext();
+ // let download takes longer to ensure the test can cancel the download in time
+ DownloadService.setDownloaderFactory(new StubDownloaderFactory(10000, downloadStatus -> {
+ downloadStatus.setSuccessful();
+ }));
+ UserPreferences.setEnqueueDownloadedEpisodes(true);
+ UserPreferences.setEnableAutodownload(false);
+
+ final long item1Id = testMedia11.getItem().getId();
+ if (itemAlreadyInQueue) {
+ // simulate item already in queue condition
+ DBWriter.addQueueItem(context, false, item1Id).get();
+ assertTrue(DBReader.getQueueIDList().contains(item1Id));
+ } else {
+ assertFalse(DBReader.getQueueIDList().contains(item1Id));
+ }
+
+ withFeedItemEventListener(feedItemEventListener -> {
+ DownloadRequester.getInstance().downloadMedia(false, context, testMedia11.getItem());
+ if (itemAlreadyInQueue) {
+ Awaitility.await("download service receives the request - "
+ + "no event is expected before cancel is issued")
+ .atLeast(100, TimeUnit.MILLISECONDS)
+ .until(() -> true);
+ } else {
+ Awaitility.await("item enqueue event")
+ .atMost(2000, TimeUnit.MILLISECONDS)
+ .until(() -> feedItemEventListener.getEvents().size() >= 1);
+ }
+ DownloadRequester.getInstance().cancelDownload(context, testMedia11);
+ final int totalNumEventsExpected = itemAlreadyInQueue ? 1 : 3;
+ Awaitility.await("item dequeue event + download termination event")
+ .atMost(1000, TimeUnit.MILLISECONDS)
+ .until(() -> feedItemEventListener.getEvents().size() >= totalNumEventsExpected);
+ assertFalse("The download should have been canceled",
+ DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
+ if (itemAlreadyInQueue) {
+ assertTrue("The FeedItem should still be in the queue after the download is cancelled."
+ + " It's there before download.",
+ DBReader.getQueueIDList().contains(item1Id));
+ } else {
+ assertFalse("The FeedItem should not be in the queue after the download is cancelled.",
+ DBReader.getQueueIDList().contains(item1Id));
+ }
+ });
+ }
+
+ private static class StubDownloaderFactory implements DownloaderFactory {
private final long downloadTime;
@NonNull
diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
index 4530b7679..7d9ec7c98 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
@@ -29,10 +29,9 @@ public class HttpDownloaderTest {
private static final String TAG = "HttpDownloaderTest";
private static final String DOWNLOAD_DIR = "testdownloads";
- private static boolean successful = true;
-
+ private String url404;
+ private String urlAuth;
private File destDir;
-
private HTTPBin httpServer;
public HttpDownloaderTest() {
@@ -57,6 +56,8 @@ public class HttpDownloaderTest {
assertTrue(destDir.exists());
httpServer = new HTTPBin();
httpServer.start();
+ url404 = httpServer.getBaseUrl() + "/status/404";
+ urlAuth = httpServer.getBaseUrl() + "/basic-auth/user/passwd";
}
private FeedFileImpl setupFeedFile(String downloadUrl, String title, boolean deleteExisting) {
@@ -88,33 +89,29 @@ public class HttpDownloaderTest {
return downloader;
}
-
- private static final String URL_404 = HTTPBin.BASE_URL + "/status/404";
- private static final String URL_AUTH = HTTPBin.BASE_URL + "/basic-auth/user/passwd";
-
@Test
public void testPassingHttp() {
- download(HTTPBin.BASE_URL + "/status/200", "test200", true);
+ download(httpServer.getBaseUrl() + "/status/200", "test200", true);
}
@Test
public void testRedirect() {
- download(HTTPBin.BASE_URL + "/redirect/4", "testRedirect", true);
+ download(httpServer.getBaseUrl() + "/redirect/4", "testRedirect", true);
}
@Test
public void testGzip() {
- download(HTTPBin.BASE_URL + "/gzip/100", "testGzip", true);
+ download(httpServer.getBaseUrl() + "/gzip/100", "testGzip", true);
}
@Test
public void test404() {
- download(URL_404, "test404", false);
+ download(url404, "test404", false);
}
@Test
public void testCancel() {
- final String url = HTTPBin.BASE_URL + "/delay/3";
+ final String url = httpServer.getBaseUrl() + "/delay/3";
FeedFileImpl feedFile = setupFeedFile(url, "delay", true);
final Downloader downloader = new HttpDownloader(new DownloadRequest(feedFile.getFile_url(), url, "delay", 0, feedFile.getTypeAsInt()));
Thread t = new Thread() {
@@ -139,7 +136,7 @@ public class HttpDownloaderTest {
@Test
public void testDeleteOnFailShouldDelete() {
- Downloader downloader = download(URL_404, "testDeleteOnFailShouldDelete", false, true, null, null, true);
+ Downloader downloader = download(url404, "testDeleteOnFailShouldDelete", false, true, null, null, true);
assertFalse(new File(downloader.getDownloadRequest().getDestination()).exists());
}
@@ -149,18 +146,18 @@ public class HttpDownloaderTest {
File dest = new File(destDir, filename);
dest.delete();
assertTrue(dest.createNewFile());
- Downloader downloader = download(URL_404, filename, false, false, null, null, false);
+ Downloader downloader = download(url404, filename, false, false, null, null, false);
assertTrue(new File(downloader.getDownloadRequest().getDestination()).exists());
}
@Test
public void testAuthenticationShouldSucceed() throws InterruptedException {
- download(URL_AUTH, "testAuthSuccess", true, true, "user", "passwd", true);
+ download(urlAuth, "testAuthSuccess", true, true, "user", "passwd", true);
}
@Test
public void testAuthenticationShouldFail() {
- Downloader downloader = download(URL_AUTH, "testAuthSuccess", false, true, "user", "Wrong passwd", true);
+ Downloader downloader = download(urlAuth, "testAuthSuccess", false, true, "user", "Wrong passwd", true);
assertEquals(DownloadError.ERROR_UNAUTHORIZED, downloader.getResult().getReason());
}
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 e4081a55a..f7f3c1aa7 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
@@ -2,8 +2,8 @@ package de.test.antennapod.service.playback;
import android.content.Context;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import de.danoeh.antennapod.core.feed.VolumeReductionSetting;
import de.test.antennapod.EspressoTestUtils;
import junit.framework.AssertionFailedError;
@@ -33,7 +33,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -46,13 +46,12 @@ import static org.junit.Assert.fail;
*/
@MediumTest
public class PlaybackServiceMediaPlayerTest {
- private static final String PLAYABLE_FILE_URL = "http://127.0.0.1:" + HTTPBin.PORT + "/files/0";
private static final String PLAYABLE_DEST_URL = "psmptestfile.mp3";
private String PLAYABLE_LOCAL_URL = null;
private static final int LATCH_TIMEOUT_SECONDS = 3;
private HTTPBin httpServer;
-
+ private String playableFileUrl;
private volatile AssertionFailedError assertionError;
@After
@@ -68,10 +67,11 @@ public class PlaybackServiceMediaPlayerTest {
EspressoTestUtils.makeNotFirstRun();
EspressoTestUtils.clearDatabase();
- final Context context = InstrumentationRegistry.getTargetContext();
+ final Context context = getInstrumentation().getTargetContext();
httpServer = new HTTPBin();
httpServer.start();
+ playableFileUrl = httpServer.getBaseUrl() + "/files/0";
File cacheDir = context.getExternalFilesDir("testFiles");
if (cacheDir == null)
@@ -82,7 +82,7 @@ public class PlaybackServiceMediaPlayerTest {
assertTrue(cacheDir.canWrite());
assertTrue(cacheDir.canRead());
if (!dest.exists()) {
- InputStream i = InstrumentationRegistry.getContext().getAssets().open("testfile.mp3");
+ InputStream i = getInstrumentation().getTargetContext().getAssets().open("3sec.mp3");
OutputStream o = new FileOutputStream(new File(cacheDir, PLAYABLE_DEST_URL));
IOUtils.copy(i, o);
o.flush();
@@ -168,7 +168,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
+ Playable p = writeTestPlayable(playableFileUrl, null);
psmp.playMediaObject(p, true, false, false);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -208,7 +208,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
+ Playable p = writeTestPlayable(playableFileUrl, null);
psmp.playMediaObject(p, true, true, false);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
@@ -252,7 +252,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
+ Playable p = writeTestPlayable(playableFileUrl, null);
psmp.playMediaObject(p, true, false, true);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -297,7 +297,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, null);
+ Playable p = writeTestPlayable(playableFileUrl, null);
psmp.playMediaObject(p, true, true, true);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -335,7 +335,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
psmp.playMediaObject(p, false, false, false);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -374,7 +374,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
psmp.playMediaObject(p, false, true, false);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -416,7 +416,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
psmp.playMediaObject(p, false, false, true);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -461,7 +461,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
psmp.playMediaObject(p, false, true, true);
boolean res = countDownLatch.await(LATCH_TIMEOUT_SECONDS, TimeUnit.SECONDS);
if (assertionError != null)
@@ -524,7 +524,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
if (initialState == PlayerStatus.PLAYING) {
psmp.playMediaObject(p, stream, true, true);
}
@@ -617,7 +617,7 @@ public class PlaybackServiceMediaPlayerTest {
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
if (initialState == PlayerStatus.PREPARED || initialState == PlayerStatus.PLAYING || initialState == PlayerStatus.PAUSED) {
boolean startWhenPrepared = (initialState != PlayerStatus.PREPARED);
- psmp.playMediaObject(writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL), false, startWhenPrepared, true);
+ psmp.playMediaObject(writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL), false, startWhenPrepared, true);
}
if (initialState == PlayerStatus.PAUSED) {
psmp.pause(false, false);
@@ -674,7 +674,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
if (initialState == PlayerStatus.INITIALIZED
|| initialState == PlayerStatus.PLAYING
|| initialState == PlayerStatus.PREPARED
@@ -748,7 +748,7 @@ public class PlaybackServiceMediaPlayerTest {
}
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
- Playable p = writeTestPlayable(PLAYABLE_FILE_URL, PLAYABLE_LOCAL_URL);
+ Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
boolean prepareImmediately = initialState != PlayerStatus.INITIALIZED;
boolean startImmediately = initialState != PlayerStatus.PREPARED;
psmp.playMediaObject(p, false, startImmediately, prepareImmediately);
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 415ee9b68..dbc86a228 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
@@ -18,7 +18,6 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
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;
@@ -106,18 +105,8 @@ public class PlaybackServiceTaskManagerTest {
assertNotNull(testQueue);
assertTrue(testQueue.isEmpty());
-
- final CountDownLatch countDownLatch = new CountDownLatch(1);
- EventDistributor.EventListener queueListener = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- countDownLatch.countDown();
- }
- };
- EventDistributor.getInstance().register(queueListener);
List<FeedItem> queue = writeTestQueue("a");
EventBus.getDefault().post(QueueEvent.setQueue(queue));
- countDownLatch.await(5000, TimeUnit.MILLISECONDS);
assertNotNull(queue);
testQueue = pstm.getQueue();
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/AutoDownloadTest.java b/app/src/androidTest/java/de/test/antennapod/storage/AutoDownloadTest.java
new file mode 100644
index 000000000..b89f1b9bc
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/storage/AutoDownloadTest.java
@@ -0,0 +1,164 @@
+package de.test.antennapod.storage;
+
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import de.test.antennapod.EspressoTestUtils;
+import org.awaitility.Awaitility;
+import org.awaitility.core.ConditionTimeoutException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.DBTasksCallbacks;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.storage.AutomaticDownloadAlgorithm;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import de.test.antennapod.ui.UITestUtils;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class AutoDownloadTest {
+
+ private Context context;
+ private UITestUtils stubFeedsServer;
+
+ private DBTasksCallbacks dbTasksCallbacksOrig;
+
+ @Before
+ public void setUp() throws Exception {
+ context = ApplicationProvider.getApplicationContext();
+
+ stubFeedsServer = new UITestUtils(context);
+ stubFeedsServer.setup();
+
+ dbTasksCallbacksOrig = ClientConfig.dbTasksCallbacks;
+
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.clearDatabase();
+ UserPreferences.setAllowMobileStreaming(true);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ ClientConfig.dbTasksCallbacks = dbTasksCallbacksOrig;
+ EspressoTestUtils.tryKillPlaybackService();
+ stubFeedsServer.tearDown();
+ }
+
+ /**
+ * A cross-functional test, ensuring playback's behavior works with Auto Download in boundary condition.
+ *
+ * Scenario:
+ * - For setting enqueue location AFTER_CURRENTLY_PLAYING
+ * - when playback of an episode is complete and the app advances to the next episode (continuous playback on)
+ * - when automatic download kicks in,
+ * - ensure the next episode is the current playing one, needed for AFTER_CURRENTLY_PLAYING enqueue location.
+ */
+ @Test
+ public void downloadsEnqueuedToAfterCurrent_CurrentAdvancedToNextOnPlaybackComplete() throws Exception {
+ UserPreferences.setFollowQueue(true); // continuous playback
+
+ // Setup: feeds and queue
+ // downloads 3 of them, leave some in new state (auto-downloadable)
+ stubFeedsServer.addLocalFeedData(false);
+ List<FeedItem> queue = DBReader.getQueue();
+ assertTrue(queue.size() > 1);
+ FeedItem item0 = queue.get(0);
+ FeedItem item1 = queue.get(1);
+
+ // Setup: enable automatic download
+ // it is not needed, as the actual automatic download is stubbed.
+ StubDownloadAlgorithm stubDownloadAlgorithm = new StubDownloadAlgorithm();
+ useDownloadAlgorithm(stubDownloadAlgorithm);
+
+ // Actual test
+ // Play the first one in the queue
+ playEpisode(item0);
+
+ try {
+ // when playback is complete, advances to the next one, and auto download kicks in,
+ // ensure that currently playing has been advanced to the next one by this point.
+ Awaitility.await("advanced to the next episode")
+ .atMost(6000, MILLISECONDS) // the test mp3 media is 3-second long. twice should be enough
+ .until(() -> item1.equals(stubDownloadAlgorithm.getCurrentlyPlayingAtDownload()));
+ } catch (ConditionTimeoutException cte) {
+ FeedItem actual = stubDownloadAlgorithm.getCurrentlyPlayingAtDownload();
+ fail("when auto download is triggered, the next episode should be playing: ("
+ + item1.getId() + ", " + item1.getTitle() + ") . "
+ + "Actual playing: ("
+ + (actual == null ? "" : actual.getId() + ", " + actual.getTitle()) + ")"
+ );
+ }
+
+ }
+
+ private void playEpisode(@NonNull FeedItem item) {
+ FeedMedia media = item.getMedia();
+ DBTasks.playMedia(context, media, false, true, true);
+ Awaitility.await("episode is playing")
+ .atMost(2000, MILLISECONDS)
+ .until(() -> item.equals(getCurrentlyPlaying()));
+ }
+
+ private FeedItem getCurrentlyPlaying() {
+ Playable playable = Playable.PlayableUtils.createInstanceFromPreferences(context);
+ if (playable == null) {
+ return null;
+ }
+ return ((FeedMedia) playable).getItem();
+ }
+
+ private void useDownloadAlgorithm(final AutomaticDownloadAlgorithm downloadAlgorithm) {
+ ClientConfig.dbTasksCallbacks = new DBTasksCallbacks() {
+ @Override
+ public AutomaticDownloadAlgorithm getAutomaticDownloadAlgorithm() {
+ return downloadAlgorithm;
+ }
+
+ @Override
+ public EpisodeCleanupAlgorithm getEpisodeCacheCleanupAlgorithm() {
+ return dbTasksCallbacksOrig.getEpisodeCacheCleanupAlgorithm();
+ }
+ };
+ }
+
+ private class StubDownloadAlgorithm implements AutomaticDownloadAlgorithm {
+ @Nullable
+ private FeedItem currentlyPlaying;
+
+ @Override
+ public Runnable autoDownloadUndownloadedItems(Context context) {
+ return () -> {
+ if (currentlyPlaying == null) {
+ currentlyPlaying = getCurrentlyPlaying();
+ } else {
+ throw new AssertionError("Stub automatic download should be invoked once and only once");
+ }
+ };
+ }
+
+ @Nullable
+ FeedItem getCurrentlyPlayingAtDownload() {
+ return currentlyPlaying;
+ }
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
index 1acb96d01..88d78fd14 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
@@ -82,6 +82,7 @@ public class DBCleanupTests {
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.putBoolean(UserPreferences.PREF_ENABLE_AUTODL, true);
prefEdit.commit();
UserPreferences.init(context);
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 55ea16f13..090cd2213 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
@@ -2,26 +2,31 @@ package de.test.antennapod.storage;
import android.content.Context;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import static de.danoeh.antennapod.core.util.FeedItemUtil.getIdList;
import static java.util.Collections.singletonList;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -179,4 +184,82 @@ public class DBTasksTest {
lastDate = item.getPubDate();
}
}
+
+ @Test
+ public void testAddQueueItemsInDownload_EnqueueEnabled() throws Exception {
+ // Setup test data / environment
+ UserPreferences.setEnqueueDownloadedEpisodes(true);
+ UserPreferences.setEnqueueLocation(UserPreferences.EnqueueLocation.BACK);
+
+ List<FeedItem> fis1 = createSavedFeed("Feed 1", 2).getItems();
+ List<FeedItem> fis2 = createSavedFeed("Feed 2", 3).getItems();
+
+ DBWriter.addQueueItem(context, fis1.get(0), fis2.get(0)).get();
+ // the first item fis1.get(0) is already in the queue
+ FeedItem[] itemsToDownload = new FeedItem[]{ fis1.get(0), fis1.get(1), fis2.get(2), fis2.get(1) };
+
+ // Expectations:
+ List<FeedItem> expectedEnqueued = Arrays.asList(fis1.get(1), fis2.get(2), fis2.get(1));
+ List<FeedItem> expectedQueue = new ArrayList<>();
+ expectedQueue.addAll(DBReader.getQueue());
+ expectedQueue.addAll(expectedEnqueued);
+
+ // Run actual test and assert results
+ List<? extends FeedItem> actualEnqueued =
+ DBTasks.enqueueFeedItemsToDownload(context, Arrays.asList(itemsToDownload));
+
+ assertEqualsByIds("Only items not in the queue are enqueued", expectedEnqueued, actualEnqueued);
+ assertEqualsByIds("Queue has new items appended", expectedQueue, DBReader.getQueue());
+ }
+
+ @Test
+ public void testAddQueueItemsInDownload_EnqueueDisabled() throws Exception {
+ // Setup test data / environment
+ UserPreferences.setEnqueueDownloadedEpisodes(false);
+
+ List<FeedItem> fis1 = createSavedFeed("Feed 1", 2).getItems();
+ List<FeedItem> fis2 = createSavedFeed("Feed 2", 3).getItems();
+
+ DBWriter.addQueueItem(context, fis1.get(0), fis2.get(0)).get();
+ FeedItem[] itemsToDownload = new FeedItem[]{ fis1.get(0), fis1.get(1), fis2.get(2), fis2.get(1) };
+
+ // Expectations:
+ List<FeedItem> expectedEnqueued = Collections.emptyList();
+ List<FeedItem> expectedQueue = DBReader.getQueue();
+
+ // Run actual test and assert results
+ List<? extends FeedItem> actualEnqueued =
+ DBTasks.enqueueFeedItemsToDownload(context, Arrays.asList(itemsToDownload));
+
+ assertEqualsByIds("No item is enqueued", expectedEnqueued, actualEnqueued);
+ assertEqualsByIds("Queue is unchanged", expectedQueue, DBReader.getQueue());
+ }
+
+ private void assertEqualsByIds(String msg, List<? extends FeedItem> expected, List<? extends FeedItem> actual) {
+ // assert only the IDs, so that any differences are easily to spot.
+ List<Long> expectedIds = getIdList(expected);
+ List<Long> actualIds = getIdList(actual);
+ assertEquals(msg, expectedIds, actualIds);
+ }
+
+ private Feed createSavedFeed(String title, int numFeedItems) {
+ final Feed feed = new Feed("url", null, title);
+
+ if (numFeedItems > 0) {
+ List<FeedItem> items = new ArrayList<>(numFeedItems);
+ for (int i = 1; i <= numFeedItems; i++) {
+ FeedItem item = new FeedItem(0, "item " + i + " of " + title, "id", "link",
+ new Date(), FeedItem.UNPLAYED, feed);
+ items.add(item);
+ }
+ feed.setItems(items);
+ }
+
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.setCompleteFeed(feed);
+ adapter.close();
+ return feed;
+ }
+
}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
index 09a8466e6..ebf59fea7 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
@@ -44,8 +44,8 @@ class DBTestUtils {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
for (int i = 0; i < numFeeds; i++) {
- Feed f = new Feed(0, null, "feed " + i, null, "link" + i, "descr", null, null,
- null, null, "id" + i, null, null, "url" + i, false, false, null, null, false);
+ Feed f = new Feed(0, null, "feed " + i, "link" + i, "descr", null, null,
+ null, null, "id" + i, null, null, "url" + i, false);
f.setItems(new ArrayList<>());
for (int j = 0; j < numItems; j++) {
FeedItem item = new FeedItem(0, "item " + j, "id" + j, "link" + j, new Date(),
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 42f4413d3..10ca281cb 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
@@ -4,12 +4,15 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.preference.PreferenceManager;
+import android.util.Log;
+
import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
import androidx.test.filters.MediumTest;
-import android.util.Log;
import org.awaitility.Awaitility;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import java.io.File;
import java.io.IOException;
@@ -29,9 +32,7 @@ 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.Consumer;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
@@ -321,15 +322,11 @@ public class DBWriterTest {
feed.setImageUrl("url");
- List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
-
File enc = new File(destFolder, "file " + i);
- itemFiles.add(enc);
-
FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0, 0);
item.setMedia(media);
}
@@ -386,15 +383,11 @@ public class DBWriterTest {
feed.setImageUrl("url");
- List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
-
File enc = new File(destFolder, "file " + i);
- itemFiles.add(enc);
-
FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0, 0);
item.setMedia(media);
}
@@ -475,11 +468,12 @@ public class DBWriterTest {
assertFalse(OLD_DATE == media.getPlaybackCompletionDate().getTime());
}
- private Feed queueTestSetupMultipleItems(final int NUM_ITEMS) throws InterruptedException, ExecutionException, TimeoutException {
+ private Feed queueTestSetupMultipleItems(final int numItems) throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
+ UserPreferences.setEnqueueLocation(UserPreferences.EnqueueLocation.BACK);
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
- for (int i = 0; i < NUM_ITEMS; i++) {
+ for (int i = 0; i < numItems; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
}
@@ -573,12 +567,16 @@ public class DBWriterTest {
Cursor cursor = adapter.getQueueIDCursor();
assertTrue(cursor.moveToFirst());
assertTrue(cursor.getCount() == NUM_ITEMS);
+ List<Long> expectedIds = FeedItemUtil.getIdList(feed.getItems());
+ List<Long> actualIds = new ArrayList<>();
for (int i = 0; i < NUM_ITEMS; i++) {
assertTrue(cursor.moveToPosition(i));
- assertTrue(cursor.getLong(0) == feed.getItems().get(i).getId());
+ actualIds.add(cursor.getLong(0));
}
cursor.close();
adapter.close();
+ assertEquals("Bulk add to queue: result order should be the same as the order given",
+ expectedIds, actualIds);
}
@Test
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 9f16888fa..0784cb078 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
@@ -3,10 +3,10 @@ package de.test.antennapod.ui;
import android.app.Activity;
import android.content.Intent;
import androidx.test.InstrumentationRegistry;
+import androidx.test.espresso.Espresso;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.robotium.solo.Solo;
-import com.robotium.solo.Timeout;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.Feed;
@@ -23,10 +23,18 @@ import java.io.IOException;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.action.ViewActions.typeText;
import static androidx.test.espresso.contrib.ActivityResultMatchers.hasResultCode;
+import static androidx.test.espresso.intent.Intents.intended;
+import static androidx.test.espresso.intent.Intents.times;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static de.test.antennapod.EspressoTestUtils.clickPreference;
import static de.test.antennapod.EspressoTestUtils.openNavDrawer;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
import static junit.framework.TestCase.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -69,10 +77,11 @@ public class MainActivityTest {
final Feed feed = uiTestUtils.hostedFeeds.get(0);
openNavDrawer();
onView(withText(R.string.add_feed_label)).perform(click());
- solo.enterText(1, feed.getDownload_url());
- onView(withText(R.string.confirm_label)).perform(click());
+ onView(withId(R.id.etxtFeedurl)).perform(scrollTo(), typeText(feed.getDownload_url()));
+ onView(withText(R.string.confirm_label)).perform(scrollTo(), click());
+ Espresso.closeSoftKeyboard();
onView(withText(R.string.subscribe_label)).perform(click());
- assertTrue(solo.waitForText(solo.getString(R.string.open_podcast), 0, Timeout.getLargeTimeout(), false));
+ onView(isRoot()).perform(waitForView(withId(R.id.butShowSettings), 5000));
}
private String getActionbarTitle() {
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java b/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
index 360b55ce6..aa4389057 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
@@ -2,11 +2,9 @@ package de.test.antennapod.ui;
import android.content.Intent;
import androidx.test.InstrumentationRegistry;
-import androidx.test.espresso.ViewInteraction;
import androidx.test.espresso.contrib.DrawerActions;
import androidx.test.espresso.intent.rule.IntentsTestRule;
import androidx.test.runner.AndroidJUnit4;
-import android.view.View;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
@@ -17,7 +15,6 @@ import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.test.antennapod.EspressoTestUtils;
-import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -29,15 +26,18 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import static androidx.test.espresso.Espresso.onData;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.longClick;
import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
import static androidx.test.espresso.intent.Intents.intended;
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static de.test.antennapod.EspressoTestUtils.onDrawerItem;
import static de.test.antennapod.EspressoTestUtils.waitForView;
import static de.test.antennapod.NthMatcher.first;
import static junit.framework.TestCase.assertTrue;
@@ -45,7 +45,7 @@ import static org.hamcrest.Matchers.allOf;
import static org.junit.Assert.assertEquals;
/**
- * User interface tests for MainActivity drawer
+ * User interface tests for MainActivity drawer.
*/
@RunWith(AndroidJUnit4.class)
public class NavigationDrawerTest {
@@ -53,7 +53,7 @@ public class NavigationDrawerTest {
private UITestUtils uiTestUtils;
@Rule
- public IntentsTestRule<MainActivity> mActivityRule = new IntentsTestRule<>(MainActivity.class, false, false);
+ public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(MainActivity.class, false, false);
@Before
public void setUp() throws IOException {
@@ -75,16 +75,12 @@ public class NavigationDrawerTest {
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
}
- private ViewInteraction onDrawerItem(Matcher<View> viewMatcher) {
- return onView(allOf(viewMatcher, withId(R.id.txtvTitle)));
- }
-
@Test
public void testClickNavDrawer() throws Exception {
uiTestUtils.addLocalFeedData(false);
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
- mActivityRule.launchActivity(new Intent());
- MainActivity activity = mActivityRule.getActivity();
+ activityRule.launchActivity(new Intent());
+ MainActivity activity = activityRule.getActivity();
// queue
openNavDrawer();
@@ -118,6 +114,7 @@ public class NavigationDrawerTest {
// add podcast
openNavDrawer();
+ onView(withId(R.id.nav_list)).perform(swipeUp());
onDrawerItem(withText(R.string.add_feed_label)).perform(click());
onView(isRoot()).perform(waitForView(withId(R.id.txtvFeedurl), 1000));
assertEquals(activity.getString(R.string.add_feed_label), activity.getSupportActionBar().getTitle());
@@ -134,7 +131,7 @@ public class NavigationDrawerTest {
@Test
public void testGoToPreferences() {
- mActivityRule.launchActivity(new Intent());
+ activityRule.launchActivity(new Intent());
openNavDrawer();
onView(withText(R.string.settings_label)).perform(click());
intended(hasComponent(PreferenceActivity.class.getName()));
@@ -143,7 +140,7 @@ public class NavigationDrawerTest {
@Test
public void testDrawerPreferencesHideSomeElements() {
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
- mActivityRule.launchActivity(new Intent());
+ activityRule.launchActivity(new Intent());
openNavDrawer();
onDrawerItem(withText(R.string.queue_label)).perform(longClick());
onView(withText(R.string.episodes_label)).perform(click());
@@ -160,7 +157,7 @@ public class NavigationDrawerTest {
public void testDrawerPreferencesUnhideSomeElements() {
List<String> hidden = Arrays.asList(PlaybackHistoryFragment.TAG, DownloadsFragment.TAG);
UserPreferences.setHiddenDrawerItems(hidden);
- mActivityRule.launchActivity(new Intent());
+ activityRule.launchActivity(new Intent());
openNavDrawer();
onView(first(withText(R.string.queue_label))).perform(longClick());
@@ -178,14 +175,20 @@ public class NavigationDrawerTest {
@Test
public void testDrawerPreferencesHideAllElements() {
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
- mActivityRule.launchActivity(new Intent());
- String[] titles = mActivityRule.getActivity().getResources().getStringArray(R.array.nav_drawer_titles);
+ activityRule.launchActivity(new Intent());
+ String[] titles = activityRule.getActivity().getResources().getStringArray(R.array.nav_drawer_titles);
openNavDrawer();
onView(first(withText(R.string.queue_label))).perform(longClick());
- for (String title : titles) {
+ for (int i = 0; i < titles.length; i++) {
+ String title = titles[i];
onView(first(withText(title))).perform(click());
+
+ if (i == 3) {
+ onView(withId(R.id.select_dialog_listview)).perform(swipeUp());
+ }
}
+
onView(withText(R.string.confirm_label)).perform(click());
List<String> hidden = UserPreferences.getHiddenDrawerItems();
@@ -198,7 +201,7 @@ public class NavigationDrawerTest {
@Test
public void testDrawerPreferencesHideCurrentElement() {
UserPreferences.setHiddenDrawerItems(new ArrayList<>());
- mActivityRule.launchActivity(new Intent());
+ activityRule.launchActivity(new Intent());
openNavDrawer();
onView(withText(R.string.downloads_label)).perform(click());
openNavDrawer();
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
deleted file mode 100644
index 5b3530ea8..000000000
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
+++ /dev/null
@@ -1,277 +0,0 @@
-package de.test.antennapod.ui;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
-import android.view.View;
-import android.widget.ListView;
-
-import com.robotium.solo.Solo;
-import com.robotium.solo.Timeout;
-
-import java.util.List;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.test.antennapod.EspressoTestUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-/**
- * test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
- */
-@LargeTest
-public class PlaybackSonicTest {
- private static final int EPISODES_DRAWER_LIST_INDEX = 1;
- private static final int QUEUE_DRAWER_LIST_INDEX = 0;
-
- private Solo solo;
- private UITestUtils uiTestUtils;
-
- private Context context;
-
- @Rule
- public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class, false, false);
-
- @Before
- public void setUp() throws Exception {
- EspressoTestUtils.clearPreferences();
- EspressoTestUtils.makeNotFirstRun();
- EspressoTestUtils.clearDatabase();
- context = InstrumentationRegistry.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_MEDIA_PLAYER, "sonic")
- .commit();
-
- activityTestRule.launchActivity(new Intent());
- solo = new Solo(getInstrumentation(), activityTestRule.getActivity());
-
- uiTestUtils = new UITestUtils(context);
- uiTestUtils.setup();
- }
-
- @After
- public void tearDown() throws Exception {
- solo.finishOpenedActivities();
- uiTestUtils.tearDown();
-
- // shut down playback service
- skipEpisode();
- context.sendBroadcast(new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
- }
-
- private MainActivity getActivity() {
- return activityTestRule.getActivity();
- }
-
- private void openNavDrawer() {
- solo.clickOnImageButton(0);
- getInstrumentation().waitForIdleSync();
- }
-
- private void setContinuousPlaybackPreference(boolean value) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit();
- }
-
- private void skipEpisode() {
- Intent skipIntent = new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
- context.sendBroadcast(skipIntent);
- }
-
- private void startLocalPlayback() {
- openNavDrawer();
- // if we try to just click on plain old text then
- // we might wind up clicking on the fragment title and not
- // the drawer element like we want.
- ListView drawerView = (ListView)solo.getView(R.id.nav_list);
- // this should be 'Episodes'
- View targetView = drawerView.getChildAt(EPISODES_DRAWER_LIST_INDEX);
- solo.waitForView(targetView);
- solo.clickOnView(targetView);
- getInstrumentation().waitForIdleSync();
- solo.waitForText(solo.getString(R.string.all_episodes_short_label));
- solo.clickOnText(solo.getString(R.string.all_episodes_short_label));
- getInstrumentation().waitForIdleSync();
-
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 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);
- }
-
- @Test
- public void testStartLocal() throws Exception {
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- startLocalPlayback();
- }
-
- @Test
- public void testContinousPlaybackOffSingleEpisode() throws Exception {
- setContinuousPlaybackPreference(false);
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- startLocalPlayback();
- }
-
- @Test
- 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));
- }
-
- @Test
- 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(0, 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(() ->
- 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);
- }
-
- @Test
- public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
- replayEpisodeCheck(true);
- }
-
- @Test
- 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
deleted file mode 100644
index ec5dc804e..000000000
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
+++ /dev/null
@@ -1,375 +0,0 @@
-package de.test.antennapod.ui;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
-import androidx.test.rule.ActivityTestRule;
-import android.view.View;
-import android.widget.ListView;
-
-import com.robotium.solo.Solo;
-import com.robotium.solo.Timeout;
-
-import org.awaitility.Awaitility;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.List;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-
-import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static org.hamcrest.Matchers.hasItems;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
-
-/**
- * test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
- */
-@LargeTest
-public class PlaybackTest {
- private static final int EPISODES_DRAWER_LIST_INDEX = 1;
- private static final int QUEUE_DRAWER_LIST_INDEX = 0;
-
- @Rule
- public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class);
-
- private Solo solo;
- private UITestUtils uiTestUtils;
- private Context context;
-
- @Before
- public void setUp() throws Exception {
- context = InstrumentationRegistry.getTargetContext();
-
- PodDBAdapter.init(context);
- PodDBAdapter.deleteDatabase();
-
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit()
- .clear()
- .putBoolean(UserPreferences.PREF_UNPAUSE_ON_HEADSET_RECONNECT, false)
- .putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false)
- .commit();
-
- solo = new Solo(InstrumentationRegistry.getInstrumentation(), getActivity());
-
- uiTestUtils = new UITestUtils(context);
- uiTestUtils.setup();
-
- // create database
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- adapter.close();
- }
-
- @After
- public void tearDown() throws Exception {
- solo.finishOpenedActivities();
- uiTestUtils.tearDown();
-
- // shut down playback service
- skipEpisode();
- context.sendBroadcast(new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
- }
-
- private MainActivity getActivity() {
- return activityTestRule.getActivity();
- }
-
- private void openNavDrawer() {
- solo.clickOnImageButton(0);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- private void setContinuousPlaybackPreference(boolean value) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit();
- }
-
- private void setSkipKeepsEpisodePreference(boolean value) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit().putBoolean(UserPreferences.PREF_SKIP_KEEPS_EPISODE, value).commit();
- }
-
- private void setSmartMarkAsPlayedPreference(int smartMarkAsPlayedSecs) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit().putString(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS,
- Integer.toString(smartMarkAsPlayedSecs, 10))
- .commit();
- }
-
- private void skipEpisode() {
- Intent skipIntent = new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
- context.sendBroadcast(skipIntent);
- }
-
- private void pauseEpisode() {
- Intent pauseIntent = new Intent(PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
- context.sendBroadcast(pauseIntent);
- }
-
- 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);
- 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(0, 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() {
- gotoQueueScreen();
- playFromQueue(0);
- }
-
- private void gotoQueueScreen() {
- 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)));
- }
-
- /**
- *
- * @param itemIdx The 0-based index of the episode to be played in the queue.
- */
- private void playFromQueue(int itemIdx) {
- final List<FeedItem> queue = DBReader.getQueue();
- solo.clickOnImageButton(itemIdx + 1);
- assertTrue(solo.waitForView(solo.getView(R.id.butPlay)));
- long mediaId = queue.get(itemIdx).getMedia().getId();
- boolean playing = solo.waitForCondition(() -> {
- if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
-
- assertTrue(playing);
- }
-
- @Test
- public void testStartLocal() throws Exception {
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- startLocalPlayback();
- }
-
- @Test
- public void testContinousPlaybackOffSingleEpisode() throws Exception {
- setContinuousPlaybackPreference(false);
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- startLocalPlayback();
- }
-
- @Test
- 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));
- }
-
- @Test
- 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(0, 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(() ->
- 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);
- }
-
- @Test
- public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
- replayEpisodeCheck(true);
- }
-
- @Test
- public void testReplayEpisodeContinuousPlaybackOff() throws Exception {
- replayEpisodeCheck(false);
- }
-
- @Test
- public void testSmartMarkAsPlayed_Skip_Average() throws Exception {
- doTestSmartMarkAsPlayed_Skip_ForEpisode(0);
- }
-
- @Test
- public void testSmartMarkAsPlayed_Skip_LastEpisodeInQueue() throws Exception {
- doTestSmartMarkAsPlayed_Skip_ForEpisode(-1);
- }
-
- private void doTestSmartMarkAsPlayed_Skip_ForEpisode(int itemIdxNegAllowed) throws Exception {
- setSmartMarkAsPlayedPreference(60);
- // ensure when an episode is skipped, it is removed due to smart as played
- setSkipKeepsEpisodePreference(false);
-
- uiTestUtils.addLocalFeedData(true);
-
- int fiIdx;
- if (itemIdxNegAllowed >= 0) {
- fiIdx = itemIdxNegAllowed;
- } else { // negative index: count from the end, with -1 being the last one, etc.
- fiIdx = DBReader.getQueue().size() + itemIdxNegAllowed;
- }
- final FeedItem feedItem = DBReader.getQueue().get(fiIdx);
-
- gotoQueueScreen();
- playFromQueue(fiIdx);
-
- skipEpisode();
-
- // assert item no longer in queue (needs to wait till skip is asynchronously processed)
- Awaitility.await()
- .atMost(1000, MILLISECONDS)
- .untilAsserted(() -> {
- assertThat("Ensure smart mark as play will lead to the item removed from the queue",
- DBReader.getQueue(), not(hasItems(feedItem)));
- });
- assertThat(DBReader.getFeedItem(feedItem.getId()).isPlayed(), is(true));
- }
-
- @Test
- public void testSmartMarkAsPlayed_Pause_WontAffectItem() throws Exception {
- setSmartMarkAsPlayedPreference(60);
-
- uiTestUtils.addLocalFeedData(true);
-
- final int fiIdx = 0;
- final FeedItem feedItem = DBReader.getQueue().get(fiIdx);
-
- gotoQueueScreen();
- playFromQueue(fiIdx);
-
- // let playback run a bit then pause
- Awaitility.await()
- .atMost(1000, MILLISECONDS)
- .until(() -> PlayerStatus.PLAYING == uiTestUtils.getPlaybackController(getActivity()).getStatus());
- pauseEpisode();
- Awaitility.await()
- .atMost(1000, MILLISECONDS)
- .until(() -> PlayerStatus.PAUSED == uiTestUtils.getPlaybackController(getActivity()).getStatus());
-
- assertThat("Ensure even with smart mark as play, after pause, the item remains in the queue.",
- DBReader.getQueue(), hasItems(feedItem));
- assertThat("Ensure even with smart mark as play, after pause, the item played status remains false.",
- DBReader.getFeedItem(feedItem.getId()).isPlayed(), is(false));
- }
-
-}
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 b46af83c8..a68afbc2e 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -4,23 +4,14 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.preference.PreferenceManager;
+import androidx.annotation.StringRes;
import androidx.test.filters.LargeTest;
-
import androidx.test.rule.ActivityTestRule;
-import com.robotium.solo.Solo;
-import com.robotium.solo.Timeout;
-
-import de.test.antennapod.EspressoTestUtils;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.concurrent.TimeUnit;
-
+import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
@@ -28,20 +19,39 @@ import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
+import de.test.antennapod.EspressoTestUtils;
+import org.awaitility.Awaitility;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.replaceText;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.action.ViewActions.swipeDown;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static de.test.antennapod.EspressoTestUtils.clickPreference;
-import static junit.framework.TestCase.assertEquals;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.not;
@LargeTest
public class PreferencesTest {
- private Solo solo;
private Resources res;
@Rule
@@ -49,14 +59,12 @@ public class PreferencesTest {
@Before
public void setUp() {
+ EspressoTestUtils.clearDatabase();
EspressoTestUtils.clearPreferences();
mActivityRule.launchActivity(new Intent());
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivityRule.getActivity());
prefs.edit().putBoolean(UserPreferences.PREF_ENABLE_AUTODL, true).commit();
- solo = new Solo(getInstrumentation(), mActivityRule.getActivity());
- Timeout.setSmallTimeout(500);
- Timeout.setLargeTimeout(1000);
res = mActivityRule.getActivity().getResources();
UserPreferences.init(mActivityRule.getActivity());
}
@@ -65,7 +73,7 @@ public class PreferencesTest {
public void testSwitchTheme() {
final int theme = UserPreferences.getTheme();
int otherTheme;
- if(theme == de.danoeh.antennapod.core.R.style.Theme_AntennaPod_Light) {
+ if (theme == de.danoeh.antennapod.core.R.style.Theme_AntennaPod_Light) {
otherTheme = R.string.pref_theme_title_dark;
} else {
otherTheme = R.string.pref_theme_title_light;
@@ -73,14 +81,15 @@ public class PreferencesTest {
clickPreference(R.string.user_interface_label);
clickPreference(R.string.pref_set_theme_title);
onView(withText(otherTheme)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getTheme() != theme);
}
@Test
public void testSwitchThemeBack() {
final int theme = UserPreferences.getTheme();
int otherTheme;
- if(theme == de.danoeh.antennapod.core.R.style.Theme_AntennaPod_Light) {
+ if (theme == de.danoeh.antennapod.core.R.style.Theme_AntennaPod_Light) {
otherTheme = R.string.pref_theme_title_dark;
} else {
otherTheme = R.string.pref_theme_title_light;
@@ -88,7 +97,8 @@ public class PreferencesTest {
clickPreference(R.string.user_interface_label);
clickPreference(R.string.pref_set_theme_title);
onView(withText(otherTheme)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getTheme() != theme);
}
@Test
@@ -96,18 +106,18 @@ public class PreferencesTest {
final boolean persistNotify = UserPreferences.isPersistNotify();
clickPreference(R.string.user_interface_label);
clickPreference(R.string.pref_persistNotify_title);
- assertTrue(solo.waitForCondition(() -> persistNotify != UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> persistNotify != UserPreferences.isPersistNotify());
clickPreference(R.string.pref_persistNotify_title);
- assertTrue(solo.waitForCondition(() -> persistNotify == UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> persistNotify == UserPreferences.isPersistNotify());
}
@Test
public void testSetLockscreenButtons() {
- onView(withText(R.string.user_interface_label)).perform(click());
- solo.scrollDown();
+ clickPreference(R.string.user_interface_label);
String[] buttons = res.getStringArray(R.array.compact_notification_buttons_options);
- onView(withText(R.string.pref_compact_notification_buttons_title)).perform(click());
- solo.waitForDialogToOpen(1000);
+ clickPreference(R.string.pref_compact_notification_buttons_title);
// First uncheck checkbox
onView(withText(buttons[2])).perform(click());
@@ -117,24 +127,39 @@ public class PreferencesTest {
onView(withText(buttons[2])).perform(click());
// Make sure that the third checkbox is unchecked
- assertTrue(!solo.isTextChecked(buttons[2]));
+ onView(withText(buttons[2])).check(matches(not(isChecked())));
+
+ String snackBarText = String.format(res.getString(
+ R.string.pref_compact_notification_buttons_dialog_error), 2);
+ Awaitility.await().ignoreExceptions().atMost(4000, MILLISECONDS)
+ .until(() -> {
+ onView(withText(snackBarText)).check(doesNotExist());
+ return true;
+ });
+
onView(withText(R.string.confirm_label)).perform(click());
- solo.waitForDialogToClose(1000);
- assertTrue(solo.waitForCondition(UserPreferences::showRewindOnCompactNotification, Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(UserPreferences::showFastForwardOnCompactNotification, Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> !UserPreferences.showSkipOnCompactNotification(), Timeout.getLargeTimeout()));
+
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::showRewindOnCompactNotification);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::showFastForwardOnCompactNotification);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> !UserPreferences.showSkipOnCompactNotification());
}
@Test
- public void testEnqueueAtFront() {
- onView(withText(R.string.playback_pref)).perform(click());
- final boolean enqueueAtFront = UserPreferences.enqueueAtFront();
- solo.scrollDown();
- solo.scrollDown();
- onView(withText(R.string.pref_queueAddToFront_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enqueueAtFront != UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
- onView(withText(R.string.pref_queueAddToFront_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enqueueAtFront == UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
+ public void testEnqueueLocation() {
+ clickPreference(R.string.playback_pref);
+ doTestEnqueueLocation(R.string.enqueue_location_after_current, EnqueueLocation.AFTER_CURRENTLY_PLAYING);
+ doTestEnqueueLocation(R.string.enqueue_location_front, EnqueueLocation.FRONT);
+ doTestEnqueueLocation(R.string.enqueue_location_back, EnqueueLocation.BACK);
+ }
+
+ private void doTestEnqueueLocation(@StringRes int optionResId, EnqueueLocation expected) {
+ clickPreference(R.string.pref_enqueue_location_title);
+ onView(withText(optionResId)).perform(click());
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> expected == UserPreferences.getEnqueueLocation());
}
@Test
@@ -142,49 +167,57 @@ public class PreferencesTest {
onView(withText(R.string.playback_pref)).perform(click());
final boolean pauseOnHeadsetDisconnect = UserPreferences.isPauseOnHeadsetDisconnect();
onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect != UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> pauseOnHeadsetDisconnect != UserPreferences.isPauseOnHeadsetDisconnect());
onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect == UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> pauseOnHeadsetDisconnect == UserPreferences.isPauseOnHeadsetDisconnect());
}
@Test
public void testHeadPhonesReconnect() {
onView(withText(R.string.playback_pref)).perform(click());
- if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
+ if (!UserPreferences.isPauseOnHeadsetDisconnect()) {
onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(UserPreferences::isPauseOnHeadsetDisconnect, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::isPauseOnHeadsetDisconnect);
}
final boolean unpauseOnHeadsetReconnect = UserPreferences.isUnpauseOnHeadsetReconnect();
onView(withText(R.string.pref_unpauseOnHeadsetReconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> unpauseOnHeadsetReconnect != UserPreferences.isUnpauseOnHeadsetReconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> unpauseOnHeadsetReconnect != UserPreferences.isUnpauseOnHeadsetReconnect());
onView(withText(R.string.pref_unpauseOnHeadsetReconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> unpauseOnHeadsetReconnect == UserPreferences.isUnpauseOnHeadsetReconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> unpauseOnHeadsetReconnect == UserPreferences.isUnpauseOnHeadsetReconnect());
}
@Test
public void testBluetoothReconnect() {
onView(withText(R.string.playback_pref)).perform(click());
- if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
+ if (!UserPreferences.isPauseOnHeadsetDisconnect()) {
onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(UserPreferences::isPauseOnHeadsetDisconnect, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::isPauseOnHeadsetDisconnect);
}
final boolean unpauseOnBluetoothReconnect = UserPreferences.isUnpauseOnBluetoothReconnect();
onView(withText(R.string.pref_unpauseOnBluetoothReconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> unpauseOnBluetoothReconnect != UserPreferences.isUnpauseOnBluetoothReconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> unpauseOnBluetoothReconnect != UserPreferences.isUnpauseOnBluetoothReconnect());
onView(withText(R.string.pref_unpauseOnBluetoothReconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> unpauseOnBluetoothReconnect == UserPreferences.isUnpauseOnBluetoothReconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> unpauseOnBluetoothReconnect == UserPreferences.isUnpauseOnBluetoothReconnect());
}
@Test
public void testContinuousPlayback() {
- onView(withText(R.string.playback_pref)).perform(click());
+ clickPreference(R.string.playback_pref);
final boolean continuousPlayback = UserPreferences.isFollowQueue();
- solo.scrollDown();
- solo.scrollDown();
- onView(withText(R.string.pref_followQueue_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> continuousPlayback != UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
- onView(withText(R.string.pref_followQueue_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> continuousPlayback == UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
+ clickPreference(R.string.pref_followQueue_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> continuousPlayback != UserPreferences.isFollowQueue());
+ clickPreference(R.string.pref_followQueue_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> continuousPlayback == UserPreferences.isFollowQueue());
}
@Test
@@ -192,9 +225,11 @@ public class PreferencesTest {
onView(withText(R.string.storage_pref)).perform(click());
final boolean autoDelete = UserPreferences.isAutoDelete();
onView(withText(R.string.pref_auto_delete_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> autoDelete != UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> autoDelete != UserPreferences.isAutoDelete());
onView(withText(R.string.pref_auto_delete_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> autoDelete == UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> autoDelete == UserPreferences.isAutoDelete());
}
@Test
@@ -203,7 +238,7 @@ public class PreferencesTest {
clickPreference(R.string.media_player);
onView(withText(R.string.media_player_exoplayer)).perform(click());
clickPreference(R.string.pref_playback_speed_title);
- solo.waitForDialogToOpen();
+ onView(isRoot()).perform(waitForView(withText("0.50"), 1000));
onView(withText("0.50")).check(matches(isDisplayed()));
onView(withText(R.string.cancel_label)).perform(click());
}
@@ -212,10 +247,12 @@ public class PreferencesTest {
public void testPauseForInterruptions() {
onView(withText(R.string.playback_pref)).perform(click());
final boolean pauseForFocusLoss = UserPreferences.shouldPauseForFocusLoss();
- onView(withText(R.string.pref_pausePlaybackForFocusLoss_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
- onView(withText(R.string.pref_pausePlaybackForFocusLoss_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
+ clickPreference(R.string.pref_pausePlaybackForFocusLoss_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss());
+ clickPreference(R.string.pref_pausePlaybackForFocusLoss_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss());
}
@Test
@@ -223,7 +260,8 @@ public class PreferencesTest {
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_autoUpdateIntervallOrTime_title)).perform(click());
onView(withText(R.string.pref_autoUpdateIntervallOrTime_Disable)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() == 0, 1000));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getUpdateInterval() == 0);
}
@Test
@@ -231,59 +269,58 @@ public class PreferencesTest {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_autoUpdateIntervallOrTime_title);
onView(withText(R.string.pref_autoUpdateIntervallOrTime_Interval)).perform(click());
- String search = "12 " + solo.getString(R.string.pref_update_interval_hours_plural);
+ String search = "12 " + res.getString(R.string.pref_update_interval_hours_plural);
onView(withText(search)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() ==
- TimeUnit.HOURS.toMillis(12), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getUpdateInterval() == TimeUnit.HOURS.toMillis(12));
}
@Test
public void testSetSequentialDownload() {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_parallel_downloads_title);
- solo.waitForDialogToOpen();
- solo.clearEditText(0);
- solo.enterText(0, "1");
+ onView(isRoot()).perform(waitForView(withClassName(endsWith("EditText")), 1000));
+ onView(withClassName(endsWith("EditText"))).perform(replaceText("1"));
onView(withText(android.R.string.ok)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 1, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getParallelDownloads() == 1);
}
@Test
public void testSetParallelDownloads() {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_parallel_downloads_title);
- solo.waitForDialogToOpen();
- solo.clearEditText(0);
- solo.enterText(0, "10");
+ onView(isRoot()).perform(waitForView(withClassName(endsWith("EditText")), 1000));
+ onView(withClassName(endsWith("EditText"))).perform(replaceText("10"));
onView(withText(android.R.string.ok)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 10, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getParallelDownloads() == 10);
}
@Test
public void testSetParallelDownloadsInvalidInput() {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_parallel_downloads_title);
- solo.waitForDialogToOpen();
- solo.clearEditText(0);
- solo.enterText(0, "0");
- assertEquals("", solo.getEditText(0).getText().toString());
- solo.clearEditText(0);
- solo.enterText(0, "100");
- assertEquals("", solo.getEditText(0).getText().toString());
+ onView(isRoot()).perform(waitForView(withClassName(endsWith("EditText")), 1000));
+ onView(withClassName(endsWith("EditText"))).perform(replaceText("0"));
+ onView(withClassName(endsWith("EditText"))).check(matches(withText("")));
+ onView(withClassName(endsWith("EditText"))).perform(replaceText("100"));
+ onView(withClassName(endsWith("EditText"))).check(matches(withText("")));
}
@Test
public void testSetEpisodeCache() {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
String[] values = res.getStringArray(R.array.episode_cache_size_values);
- String entry = entries[entries.length/2];
- final int value = Integer.valueOf(values[values.length/2]);
+ String entry = entries[entries.length / 2];
+ final int value = Integer.parseInt(values[values.length / 2]);
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_automatic_download_title);
clickPreference(R.string.pref_episode_cache_title);
- solo.waitForDialogToOpen();
- solo.clickOnText(entry);
- assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == value, Timeout.getLargeTimeout()));
+ onView(isRoot()).perform(waitForView(withText(entry), 1000));
+ onView(withText(entry)).perform(click());
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCacheSize() == value);
}
@Test
@@ -291,27 +328,30 @@ public class PreferencesTest {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
String[] values = res.getStringArray(R.array.episode_cache_size_values);
String minEntry = entries[0];
- final int minValue = Integer.valueOf(values[0]);
+ final int minValue = Integer.parseInt(values[0]);
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_automatic_download_title);
clickPreference(R.string.pref_episode_cache_title);
- solo.scrollUp();
+ onView(withId(R.id.select_dialog_listview)).perform(swipeDown());
onView(withText(minEntry)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == minValue, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCacheSize() == minValue);
}
@Test
public void testSetEpisodeCacheMax() {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
String[] values = res.getStringArray(R.array.episode_cache_size_values);
- String maxEntry = entries[entries.length-1];
- final int maxValue = Integer.valueOf(values[values.length-1]);
+ String maxEntry = entries[entries.length - 1];
+ final int maxValue = Integer.parseInt(values[values.length - 1]);
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_automatic_download_title)).perform(click());
onView(withText(R.string.pref_episode_cache_title)).perform(click());
+ onView(withId(R.id.select_dialog_listview)).perform(swipeUp());
onView(withText(maxEntry)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == maxValue, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCacheSize() == maxValue);
}
@Test
@@ -320,22 +360,27 @@ public class PreferencesTest {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_automatic_download_title);
clickPreference(R.string.pref_automatic_download_title);
-
- assertTrue(solo.waitForCondition(() -> automaticDownload != UserPreferences.isEnableAutodownload(), Timeout.getLargeTimeout()));
- if(UserPreferences.isEnableAutodownload() == false) {
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> automaticDownload != UserPreferences.isEnableAutodownload());
+ if (!UserPreferences.isEnableAutodownload()) {
clickPreference(R.string.pref_automatic_download_title);
}
- assertTrue(solo.waitForCondition(() -> UserPreferences.isEnableAutodownload() == true, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::isEnableAutodownload);
final boolean enableAutodownloadOnBattery = UserPreferences.isEnableAutodownloadOnBattery();
- onView(withText(R.string.pref_automatic_download_on_battery_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enableAutodownloadOnBattery != UserPreferences.isEnableAutodownloadOnBattery(), Timeout.getLargeTimeout()));
- onView(withText(R.string.pref_automatic_download_on_battery_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery(), Timeout.getLargeTimeout()));
+ clickPreference(R.string.pref_automatic_download_on_battery_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> enableAutodownloadOnBattery != UserPreferences.isEnableAutodownloadOnBattery());
+ clickPreference(R.string.pref_automatic_download_on_battery_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery());
final boolean enableWifiFilter = UserPreferences.isEnableAutodownloadWifiFilter();
- onView(withText(R.string.pref_autodl_wifi_filter_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enableWifiFilter != UserPreferences.isEnableAutodownloadWifiFilter(), Timeout.getLargeTimeout()));
- onView(withText(R.string.pref_autodl_wifi_filter_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enableWifiFilter == UserPreferences.isEnableAutodownloadWifiFilter(), Timeout.getLargeTimeout()));
+ clickPreference(R.string.pref_autodl_wifi_filter_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> enableWifiFilter != UserPreferences.isEnableAutodownloadWifiFilter());
+ clickPreference(R.string.pref_autodl_wifi_filter_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> enableWifiFilter == UserPreferences.isEnableAutodownloadWifiFilter());
}
@Test
@@ -343,13 +388,10 @@ public class PreferencesTest {
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_automatic_download_title)).perform(click());
onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
- solo.waitForText(solo.getString(R.string.episode_cleanup_queue_removal));
+ onView(isRoot()).perform(waitForView(withText(R.string.episode_cleanup_queue_removal), 1000));
onView(withText(R.string.episode_cleanup_queue_removal)).perform(click());
- assertTrue(solo.waitForCondition(() -> {
- EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
- return alg instanceof APQueueCleanupAlgorithm;
- },
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCleanupAlgorithm() instanceof APQueueCleanupAlgorithm);
}
@Test
@@ -357,13 +399,10 @@ public class PreferencesTest {
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_automatic_download_title)).perform(click());
onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
- solo.waitForText(solo.getString(R.string.episode_cleanup_never));
+ onView(withId(R.id.select_dialog_listview)).perform(swipeUp());
onView(withText(R.string.episode_cleanup_never)).perform(click());
- assertTrue(solo.waitForCondition(() -> {
- EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
- return alg instanceof APNullCleanupAlgorithm;
- },
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCleanupAlgorithm() instanceof APNullCleanupAlgorithm);
}
@Test
@@ -371,17 +410,17 @@ public class PreferencesTest {
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_automatic_download_title)).perform(click());
onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
- solo.waitForText(solo.getString(R.string.episode_cleanup_after_listening));
+ onView(isRoot()).perform(waitForView(withText(R.string.episode_cleanup_after_listening), 1000));
onView(withText(R.string.episode_cleanup_after_listening)).perform(click());
- assertTrue(solo.waitForCondition(() -> {
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
if (alg instanceof APCleanupAlgorithm) {
- APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
+ APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm) alg;
return cleanupAlg.getNumberOfHoursAfterPlayback() == 0;
}
return false;
- },
- Timeout.getLargeTimeout()));
+ });
}
@Test
@@ -389,24 +428,24 @@ public class PreferencesTest {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_automatic_download_title);
clickPreference(R.string.pref_episode_cleanup_title);
- solo.waitForDialogToOpen();
- String search = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, 5, 5);
+ String search = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, 3, 3);
+ onView(isRoot()).perform(waitForView(withText(search), 1000));
onView(withText(search)).perform(click());
- assertTrue(solo.waitForCondition(() -> {
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
if (alg instanceof APCleanupAlgorithm) {
- APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
- return cleanupAlg.getNumberOfHoursAfterPlayback() == 120; // 5 days
+ APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm) alg;
+ return cleanupAlg.getNumberOfHoursAfterPlayback() == 72; // 5 days
}
return false;
- },
- Timeout.getLargeTimeout()));
+ });
}
@Test
public void testRewindChange() {
int seconds = UserPreferences.getRewindSecs();
- int deltas[] = res.getIntArray(R.array.seek_delta_values);
+ int[] deltas = res.getIntArray(R.array.seek_delta_values);
clickPreference(R.string.playback_pref);
clickPreference(R.string.pref_rewind);
@@ -416,11 +455,11 @@ public class PreferencesTest {
// Find next value (wrapping around to next)
int newIndex = (currentIndex + 1) % deltas.length;
- onView(withText(String.valueOf(deltas[newIndex]) + " seconds")).perform(click());
+ onView(withText(deltas[newIndex] + " seconds")).perform(click());
onView(withText("Confirm")).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getRewindSecs() == deltas[newIndex],
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getRewindSecs() == deltas[newIndex]);
}
@Test
@@ -428,7 +467,7 @@ public class PreferencesTest {
clickPreference(R.string.playback_pref);
for (int i = 2; i > 0; i--) { // repeat twice to catch any error where fastforward is tracking rewind
int seconds = UserPreferences.getFastForwardSecs();
- int deltas[] = res.getIntArray(R.array.seek_delta_values);
+ int[] deltas = res.getIntArray(R.array.seek_delta_values);
clickPreference(R.string.pref_fast_forward);
@@ -441,9 +480,8 @@ public class PreferencesTest {
onView(withText(deltas[newIndex] + " seconds")).perform(click());
onView(withText("Confirm")).perform(click());
- solo.waitForDialogToClose();
- assertTrue(solo.waitForCondition(() -> UserPreferences.getFastForwardSecs() == deltas[newIndex],
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getFastForwardSecs() == deltas[newIndex]);
}
}
@@ -454,26 +492,26 @@ public class PreferencesTest {
onView(withText(R.string.back_button_go_to_page)).perform(click());
onView(withText(R.string.queue_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
- Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(QueueFragment.TAG),
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonGoToPage().equals(QueueFragment.TAG));
clickPreference(R.string.pref_back_button_behavior_title);
onView(withText(R.string.back_button_go_to_page)).perform(click());
onView(withText(R.string.episodes_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
- Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(EpisodesFragment.TAG),
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonGoToPage().equals(EpisodesFragment.TAG));
clickPreference(R.string.pref_back_button_behavior_title);
onView(withText(R.string.back_button_go_to_page)).perform(click());
onView(withText(R.string.subscriptions_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
- Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(SubscriptionFragment.TAG),
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonGoToPage().equals(SubscriptionFragment.TAG));
}
@Test
@@ -481,12 +519,15 @@ public class PreferencesTest {
clickPreference(R.string.storage_pref);
if (!UserPreferences.shouldDeleteRemoveFromQueue()) {
clickPreference(R.string.pref_delete_removes_from_queue_title);
- assertTrue(solo.waitForCondition(UserPreferences::shouldDeleteRemoveFromQueue, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::shouldDeleteRemoveFromQueue);
}
final boolean deleteRemovesFromQueue = UserPreferences.shouldDeleteRemoveFromQueue();
onView(withText(R.string.pref_delete_removes_from_queue_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> deleteRemovesFromQueue != UserPreferences.shouldDeleteRemoveFromQueue(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> deleteRemovesFromQueue != UserPreferences.shouldDeleteRemoveFromQueue());
onView(withText(R.string.pref_delete_removes_from_queue_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> deleteRemovesFromQueue == UserPreferences.shouldDeleteRemoveFromQueue(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> deleteRemovesFromQueue == UserPreferences.shouldDeleteRemoveFromQueue());
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java b/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
index 7f0bf8fa2..37d76bb6d 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
@@ -15,17 +15,20 @@ import org.junit.runner.RunWith;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.endsWith;
/**
- * User interface tests for queue fragment
+ * User interface tests for queue fragment.
*/
@RunWith(AndroidJUnit4.class)
public class QueueFragmentTest {
@Rule
- public IntentsTestRule<MainActivity> mActivityRule = new IntentsTestRule<>(MainActivity.class, false, false);
+ public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(MainActivity.class, false, false);
@Before
public void setUp() {
@@ -33,12 +36,13 @@ public class QueueFragmentTest {
EspressoTestUtils.makeNotFirstRun();
EspressoTestUtils.clearDatabase();
EspressoTestUtils.setLastNavFragment(QueueFragment.TAG);
- mActivityRule.launchActivity(new Intent());
+ activityRule.launchActivity(new Intent());
}
@Test
public void testLockEmptyQueue() {
onView(withContentDescription(R.string.lock_queue)).perform(click());
+ onView(allOf(withClassName(endsWith("Button")), withText(R.string.lock_queue))).perform(click());
onView(withContentDescription(R.string.unlock_queue)).perform(click());
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java b/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java
new file mode 100644
index 000000000..df32ed3c0
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/ui/SpeedChangeTest.java
@@ -0,0 +1,120 @@
+package de.test.antennapod.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.AudioplayerActivity;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.playback.PlayerStatus;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.fragment.QueueFragment;
+import de.test.antennapod.EspressoTestUtils;
+import de.test.antennapod.IgnoreOnCi;
+import org.awaitility.Awaitility;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
+
+/**
+ * User interface tests for changing the playback speed.
+ */
+@RunWith(AndroidJUnit4.class)
+@IgnoreOnCi
+public class SpeedChangeTest {
+
+ @Rule
+ public ActivityTestRule<AudioplayerActivity> activityRule
+ = new ActivityTestRule<>(AudioplayerActivity.class, false, false);
+ private UITestUtils uiTestUtils;
+ private String[] availableSpeeds;
+
+ @Before
+ public void setUp() throws Exception {
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.makeNotFirstRun();
+ EspressoTestUtils.clearDatabase();
+ EspressoTestUtils.setLastNavFragment(QueueFragment.TAG);
+
+ Context context = getInstrumentation().getTargetContext();
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, true).commit();
+
+ uiTestUtils = new UITestUtils(context);
+ uiTestUtils.setup();
+ uiTestUtils.setMediaFileName("30sec.mp3");
+ uiTestUtils.addLocalFeedData(true);
+
+ List<FeedItem> queue = DBReader.getQueue();
+ PlaybackPreferences.writeMediaPlaying(queue.get(0).getMedia(), PlayerStatus.PAUSED, false);
+ UserPreferences.setPlaybackSpeedArray(new String[] {"1.00", "2.00", "3.00"});
+ availableSpeeds = UserPreferences.getPlaybackSpeedArray();
+
+ EspressoTestUtils.tryKillPlaybackService();
+ activityRule.launchActivity(new Intent());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uiTestUtils.tearDown();
+ }
+
+ @Test
+ public void testChangeSpeedServiceNotRunning() {
+ clickThroughSpeeds();
+ }
+
+ @Test
+ public void testChangeSpeedPlaying() {
+ onView(isRoot()).perform(waitForView(withId(R.id.butPlay), 1000));
+ onView(withId(R.id.butPlay)).perform(click());
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
+ -> activityRule.getActivity().getPlaybackController().getStatus() == PlayerStatus.PLAYING);
+ clickThroughSpeeds();
+ }
+
+ @Test
+ public void testChangeSpeedPaused() {
+ onView(isRoot()).perform(waitForView(withId(R.id.butPlay), 1000));
+ onView(withId(R.id.butPlay)).perform(click());
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
+ -> activityRule.getActivity().getPlaybackController().getStatus() == PlayerStatus.PLAYING);
+ onView(withId(R.id.butPlay)).perform(click());
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(()
+ -> activityRule.getActivity().getPlaybackController().getStatus() == PlayerStatus.PAUSED);
+ clickThroughSpeeds();
+ }
+
+ private void clickThroughSpeeds() {
+ onView(isRoot()).perform(waitForView(withText(availableSpeeds[0]), 1000));
+ onView(withId(R.id.txtvPlaybackSpeed)).check(matches(withText(availableSpeeds[0])));
+ onView(withId(R.id.butPlaybackSpeed)).perform(click());
+ onView(isRoot()).perform(waitForView(withText(availableSpeeds[1]), 1000));
+ onView(withId(R.id.txtvPlaybackSpeed)).check(matches(withText(availableSpeeds[1])));
+ onView(withId(R.id.butPlaybackSpeed)).perform(click());
+ onView(isRoot()).perform(waitForView(withText(availableSpeeds[2]), 1000));
+ onView(withId(R.id.txtvPlaybackSpeed)).check(matches(withText(availableSpeeds[2])));
+ onView(withId(R.id.butPlaybackSpeed)).perform(click());
+ onView(isRoot()).perform(waitForView(withText(availableSpeeds[0]), 1000));
+ onView(withId(R.id.txtvPlaybackSpeed)).check(matches(withText(availableSpeeds[0])));
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/TextOnlyFeedsTest.java b/app/src/androidTest/java/de/test/antennapod/ui/TextOnlyFeedsTest.java
new file mode 100644
index 000000000..7ba7a57f2
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/ui/TextOnlyFeedsTest.java
@@ -0,0 +1,71 @@
+package de.test.antennapod.ui;
+
+import android.content.Intent;
+import androidx.test.espresso.intent.rule.IntentsTestRule;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.test.antennapod.EspressoTestUtils;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static de.test.antennapod.EspressoTestUtils.onDrawerItem;
+import static de.test.antennapod.EspressoTestUtils.openNavDrawer;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.not;
+
+/**
+ * Test UI for feeds that do not have media files
+ */
+@RunWith(AndroidJUnit4.class)
+public class TextOnlyFeedsTest {
+
+ private UITestUtils uiTestUtils;
+
+ @Rule
+ public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(MainActivity.class, false, false);
+
+ @Before
+ public void setUp() throws IOException {
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.makeNotFirstRun();
+ EspressoTestUtils.clearDatabase();
+
+ uiTestUtils = new UITestUtils(InstrumentationRegistry.getInstrumentation().getTargetContext());
+ uiTestUtils.setHostTextOnlyFeeds(true);
+ uiTestUtils.setup();
+
+ activityRule.launchActivity(new Intent());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ uiTestUtils.tearDown();
+ }
+
+ @Test
+ public void testMarkAsPlayedList() throws Exception {
+ uiTestUtils.addLocalFeedData(false);
+ final Feed feed = uiTestUtils.hostedFeeds.get(0);
+ openNavDrawer();
+ onDrawerItem(withText(feed.getTitle())).perform(scrollTo(), click());
+ onView(withText(feed.getItemAtIndex(0).getTitle())).perform(click());
+ onView(withText(R.string.mark_read_label)).perform(click());
+ onView(isRoot()).perform(waitForView(allOf(withText(R.string.mark_read_label), not(isDisplayed())), 3000));
+ }
+
+}
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 905c65c34..8e16d1a3b 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,8 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.core.util.playback.Playable;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@@ -18,7 +20,6 @@ import java.util.List;
import de.danoeh.antennapod.activity.MainActivity;
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;
@@ -34,16 +35,15 @@ import org.junit.Assert;
* Utility methods for UI tests.
* Starts a web server that hosts feeds, episodes and images.
*/
-class UITestUtils {
+public class UITestUtils {
private static final String TAG = UITestUtils.class.getSimpleName();
private static final int NUM_FEEDS = 5;
private static final int NUM_ITEMS_PER_FEED = 10;
- private static final String TEST_FILE_NAME = "3sec.mp3";
-
-
+ private String testFileName = "3sec.mp3";
+ private boolean hostTextOnlyFeeds = false;
private final Context context;
private final HTTPBin server = new HTTPBin();
private File destDir;
@@ -89,13 +89,13 @@ class UITestUtils {
out.close();
int id = server.serveFile(feedFile);
Assert.assertTrue(id != -1);
- return String.format("%s/files/%d", HTTPBin.BASE_URL, id);
+ return String.format("%s/files/%d", server.getBaseUrl(), id);
}
private String hostFile(File file) {
int id = server.serveFile(file);
Assert.assertTrue(id != -1);
- return String.format("%s/files/%d", HTTPBin.BASE_URL, id);
+ return String.format("%s/files/%d", server.getBaseUrl(), id);
}
private File newBitmapFile(String name) throws IOException {
@@ -109,12 +109,12 @@ class UITestUtils {
private File newMediaFile(String name) throws IOException {
File mediaFile = new File(hostedMediaDir, name);
- if(mediaFile.exists()) {
+ if (mediaFile.exists()) {
mediaFile.delete();
}
Assert.assertFalse(mediaFile.exists());
- InputStream in = context.getAssets().open(TEST_FILE_NAME);
+ InputStream in = context.getAssets().open(testFileName);
Assert.assertNotNull(in);
FileOutputStream out = new FileOutputStream(mediaFile);
@@ -143,9 +143,10 @@ class UITestUtils {
"http://example.com/feed" + i + "/item/" + j, new Date(), FeedItem.UNPLAYED, feed);
items.add(item);
- File mediaFile = newMediaFile("feed-" + i + "-episode-" + j + ".mp3");
- item.setMedia(new FeedMedia(j, item, 0, 0, mediaFile.length(), "audio/mp3", null, hostFile(mediaFile), false, null, 0, 0));
-
+ if (!hostTextOnlyFeeds) {
+ 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, 0));
+ }
}
feed.setItems(items);
feed.setDownload_url(hostFeed(feed));
@@ -192,7 +193,9 @@ class UITestUtils {
}
queue.add(feed.getItems().get(0));
- feed.getItems().get(1).getMedia().setPlaybackCompletionDate(new Date());
+ if (feed.getItems().get(1).hasMedia()) {
+ feed.getItems().get(1).getMedia().setPlaybackCompletionDate(new Date());
+ }
}
localFeedDataAdded = true;
@@ -201,16 +204,26 @@ class UITestUtils {
adapter.setCompleteFeed(hostedFeeds.toArray(new Feed[hostedFeeds.size()]));
adapter.setQueue(queue);
adapter.close();
- EventDistributor.getInstance().sendFeedUpdateBroadcast();
+ EventBus.getDefault().post(new FeedListUpdateEvent(hostedFeeds));
EventBus.getDefault().post(QueueEvent.setQueue(queue));
}
public PlaybackController getPlaybackController(MainActivity mainActivity) {
- ExternalPlayerFragment fragment = (ExternalPlayerFragment)mainActivity.getSupportFragmentManager().findFragmentByTag(ExternalPlayerFragment.TAG);
+ ExternalPlayerFragment fragment = (ExternalPlayerFragment) mainActivity.getSupportFragmentManager()
+ .findFragmentByTag(ExternalPlayerFragment.TAG);
return fragment.getPlaybackControllerTestingOnly();
}
- public FeedMedia getCurrentMedia(MainActivity mainActivity) {
- return (FeedMedia)getPlaybackController(mainActivity).getMedia();
+ public FeedMedia getCurrentMedia() {
+ Playable playable = Playable.PlayableUtils.createInstanceFromPreferences(context);
+ return (FeedMedia) playable;
+ }
+
+ public void setMediaFileName(String filename) {
+ testFileName = filename;
+ }
+
+ public void setHostTextOnlyFeeds(boolean hostTextOnlyFeeds) {
+ this.hostTextOnlyFeeds = hostTextOnlyFeeds;
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java b/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
index f2dfca92e..d33eb55b8 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
@@ -1,7 +1,7 @@
package de.test.antennapod.util;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.LargeTest;
+import android.content.Context;
+import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import android.text.TextUtils;
@@ -13,77 +13,79 @@ import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@SmallTest
public class FilenameGeneratorTest {
- private static final String VALID1 = "abc abc";
- private static final String INVALID1 = "ab/c: <abc";
- private static final String INVALID2 = "abc abc ";
+ private static final String VALID1 = "abc abc";
+ private static final String INVALID1 = "ab/c: <abc";
+ private static final String INVALID2 = "abc abc ";
public FilenameGeneratorTest() {
super();
}
@Test
- public void testGenerateFileName() throws IOException {
- String result = FileNameGenerator.generateFileName(VALID1);
- assertEquals(result, VALID1);
- createFiles(result);
- }
-
- @Test
- public void testGenerateFileName1() throws IOException {
- String result = FileNameGenerator.generateFileName(INVALID1);
- assertEquals(result, VALID1);
- createFiles(result);
- }
-
- @Test
- public void testGenerateFileName2() throws IOException {
- String result = FileNameGenerator.generateFileName(INVALID2);
- assertEquals(result, VALID1);
- createFiles(result);
- }
-
- @Test
- public void testFeedTitleContainsApostrophe() {
- String result = FileNameGenerator.generateFileName("Feed's Title ...");
- assertEquals("Feeds Title", result);
- }
-
- @Test
- public void testFeedTitleContainsDash() {
- String result = FileNameGenerator.generateFileName("Left - Right");
- assertEquals("Left - Right", result);
- }
-
- @Test
- public void testInvalidInput() {
- String result = FileNameGenerator.generateFileName("???");
- assertTrue(!TextUtils.isEmpty(result));
- }
-
- /**
- * Tests if files can be created.
- *
- * @throws IOException
- */
- private void createFiles(String name) throws IOException {
- File cache = InstrumentationRegistry.getContext().getExternalCacheDir();
- File testFile = new File(cache, name);
- testFile.mkdir();
- assertTrue(testFile.exists());
- testFile.delete();
- assertTrue(testFile.createNewFile());
-
- }
-
- @After
- public void tearDown() throws Exception {
- File f = new File(InstrumentationRegistry.getContext().getExternalCacheDir(), VALID1);
- f.delete();
- }
+ public void testGenerateFileName() throws IOException {
+ String result = FileNameGenerator.generateFileName(VALID1);
+ assertEquals(result, VALID1);
+ createFiles(result);
+ }
+
+ @Test
+ public void testGenerateFileName1() throws IOException {
+ String result = FileNameGenerator.generateFileName(INVALID1);
+ assertEquals(result, VALID1);
+ createFiles(result);
+ }
+
+ @Test
+ public void testGenerateFileName2() throws IOException {
+ String result = FileNameGenerator.generateFileName(INVALID2);
+ assertEquals(result, VALID1);
+ createFiles(result);
+ }
+
+ @Test
+ public void testFeedTitleContainsApostrophe() {
+ String result = FileNameGenerator.generateFileName("Feed's Title ...");
+ assertEquals("Feeds Title", result);
+ }
+
+ @Test
+ public void testFeedTitleContainsDash() {
+ String result = FileNameGenerator.generateFileName("Left - Right");
+ assertEquals("Left - Right", result);
+ }
+
+ @Test
+ public void testInvalidInput() {
+ String result = FileNameGenerator.generateFileName("???");
+ assertFalse(TextUtils.isEmpty(result));
+ }
+
+ /**
+ * Tests if files can be created.
+ *
+ * @throws IOException
+ */
+ private void createFiles(String name) throws IOException {
+ File cache = InstrumentationRegistry.getInstrumentation().getTargetContext().getExternalCacheDir();
+ File testFile = new File(cache, name);
+ testFile.mkdir();
+ assertTrue(testFile.exists());
+ testFile.delete();
+ assertTrue(testFile.createNewFile());
+
+ }
+
+ @After
+ public void tearDown() {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ File f = new File(context.getExternalCacheDir(), VALID1);
+ f.delete();
+ }
}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java b/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
index 4158fd31c..c588cdbc2 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java
@@ -39,9 +39,6 @@ import de.danoeh.antennapod.BuildConfig;
*/
public class HTTPBin extends NanoHTTPD {
private static final String TAG = "HTTPBin";
- public static final int PORT = 8124;
- public static final String BASE_URL = "http://127.0.0.1:" + HTTPBin.PORT;
-
private static final String MIME_HTML = "text/html";
private static final String MIME_PLAIN = "text/plain";
@@ -49,10 +46,14 @@ public class HTTPBin extends NanoHTTPD {
private final List<File> servedFiles;
public HTTPBin() {
- super(PORT);
+ super(0); // Let system pick a free port
this.servedFiles = new ArrayList<>();
}
+ public String getBaseUrl() {
+ return "http://127.0.0.1:" + getListeningPort();
+ }
+
/**
* Adds the given file to the server.
*
@@ -98,7 +99,7 @@ public class HTTPBin extends NanoHTTPD {
if (func.equalsIgnoreCase("status")) {
try {
int code = Integer.parseInt(param);
- return getStatus(code);
+ return new Response(getStatus(code), MIME_HTML, "");
} catch (NumberFormatException e) {
e.printStackTrace();
return getInternalError();
@@ -284,31 +285,45 @@ public class HTTPBin extends NanoHTTPD {
return response;
}
- private Response getStatus(final int code) {
- Response.IStatus status = (code == 200) ? Response.Status.OK :
- (code == 201) ? Response.Status.CREATED :
- (code == 206) ? Response.Status.PARTIAL_CONTENT :
- (code == 301) ? Response.Status.REDIRECT :
- (code == 304) ? Response.Status.NOT_MODIFIED :
- (code == 400) ? Response.Status.BAD_REQUEST :
- (code == 401) ? Response.Status.UNAUTHORIZED :
- (code == 403) ? Response.Status.FORBIDDEN :
- (code == 404) ? Response.Status.NOT_FOUND :
- (code == 405) ? Response.Status.METHOD_NOT_ALLOWED :
- (code == 416) ? Response.Status.RANGE_NOT_SATISFIABLE :
- (code == 500) ? Response.Status.INTERNAL_ERROR : new Response.IStatus() {
- @Override
- public int getRequestStatus() {
- return code;
- }
-
- @Override
- public String getDescription() {
- return "Unknown";
- }
- };
- return new Response(status, MIME_HTML, "");
+ private Response.IStatus getStatus(final int code) {
+ switch (code) {
+ case 200:
+ return Response.Status.OK;
+ case 201:
+ return Response.Status.CREATED;
+ case 206:
+ return Response.Status.PARTIAL_CONTENT;
+ case 301:
+ return Response.Status.REDIRECT;
+ case 304:
+ return Response.Status.NOT_MODIFIED;
+ case 400:
+ return Response.Status.BAD_REQUEST;
+ case 401:
+ return Response.Status.UNAUTHORIZED;
+ case 403:
+ return Response.Status.FORBIDDEN;
+ case 404:
+ return Response.Status.NOT_FOUND;
+ case 405:
+ return Response.Status.METHOD_NOT_ALLOWED;
+ case 416:
+ return Response.Status.RANGE_NOT_SATISFIABLE;
+ case 500:
+ return Response.Status.INTERNAL_ERROR;
+ default:
+ return new Response.IStatus() {
+ @Override
+ public int getRequestStatus() {
+ return code;
+ }
+ @Override
+ public String getDescription() {
+ return "Unknown";
+ }
+ };
+ }
}
private Response getRedirectResponse(int times) {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 488d4e473..086206dcd 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -35,6 +35,15 @@
android:restoreAnyVersion="true"
android:usesCleartextTraffic="true"
android:logo="@mipmap/ic_launcher">
+
+ <activity
+ android:name=".activity.WidgetConfigActivity"
+ android:label="@string/widget_settings">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_CONFIGUR"/>
+ </intent-filter>
+ </activity>
+
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@drawable/ic_antenna" />
<meta-data
@@ -132,14 +141,6 @@
<activity android:name=".activity.StorageErrorActivity">
</activity>
<activity
- android:name=".activity.AboutActivity"
- android:configChanges="keyboardHidden|orientation|screenSize"
- 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.ImportExportActivity"
android:label="@string/import_export">
<meta-data
@@ -228,6 +229,7 @@
<activity
android:name=".activity.OnlineFeedViewActivity"
android:configChanges="orientation|screenSize"
+ android:theme="@style/Theme.AntennaPod.Dark.Translucent"
android:label="@string/add_feed_label">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@@ -336,7 +338,7 @@
<action android:name="de.danoeh.antennapdsp.intent.SP_APPS_QUERY_FEEDS_RESPONSE"/>
</intent-filter>
</receiver>
-
+
<provider
android:authorities="@string/provider_authority"
android:name="androidx.core.content.FileProvider"
diff --git a/app/src/main/assets/30sec.mp3 b/app/src/main/assets/30sec.mp3
new file mode 100644
index 000000000..48e398434
--- /dev/null
+++ b/app/src/main/assets/30sec.mp3
Binary files differ
diff --git a/app/src/main/assets/developers.csv b/app/src/main/assets/developers.csv
new file mode 100644
index 000000000..460e896f9
--- /dev/null
+++ b/app/src/main/assets/developers.csv
@@ -0,0 +1,119 @@
+danieloeh;968613;Original creator of AntennaPod (retired)
+mfietz;6860662;Maintainer
+ByteHamster;5811634;Maintainer
+TomHennen;5216560;Maintainer (retired)
+orionlee;250644;Contributor
+domingos86;9538859;Contributor
+andersonvom;69922;Contributor
+patheticpat;16046;Contributor
+spacecowboy;223655;Contributor
+brad;1614;Contributor
+Cj-Malone;10121513;Contributor
+gaul;848247;Contributor
+qkolj;6667105;Contributor
+pachecosf;46357909;Contributor
+ahangarha;11241315;Contributor
+rharriso;570910;Contributor
+xgouchet;818706;Contributor
+damoasda;46045854;Contributor
+sevenmaster;12869538;Contributor
+TheRealFalcon;153674;Contributor
+keunes;11229646;Contributor
+shortspider;5712543;Contributor
+hannesa2;3314607;Contributor
+jas14;569991;Contributor
+udif;809640;Contributor
+dirkmueller;1029152;Contributor
+jatinkumarg;20503830;Contributor
+peschmae0;4450993;Contributor
+orelogo;15976578;Contributor
+ydinath;4193331;Contributor
+CedricCabessa;365097;Contributor
+mchelen;30691;Contributor
+johnjohndoe;144518;Contributor
+dethstar;1239177;Contributor
+drabux;10663142;Contributor
+saqura;1935380;Contributor
+bibz;5141956;Contributor
+hzulla;1705654;Contributor
+deandreamatias;21011641;Contributor
+MeirAtIMDDE;4421079;Contributor
+egsavage;126165;Contributor
+ligi;111600;Contributor
+dreiss;4121;Contributor
+liesen;26872;Contributor
+nereocystis;2257107;Contributor
+rezanejati;16049370;Contributor
+twiceyuan;2619800;Contributor
+HaBaLeS;730902;Contributor
+volhol;11587858;Contributor
+CameronBanga;611354;Contributor
+HolgerJeromin;2410353;Contributor
+xisberto;1914956;Contributor
+jmue;898577;Contributor
+katrinleinweber;9948149;Contributor
+LatinSuD;451487;Contributor
+24hours;650407;Contributor
+SosoTughushi;19908097;Contributor
+fabolhak;20029691;Contributor
+archibishop;36948493;Contributor
+alifeflow;24603829;Contributor
+toggles;14695;Contributor
+matdb;48329535;Contributor
+kingargyle;177042;Contributor
+dsmith47;14109426;Contributor
+hannesaa2;18496079;Contributor
+jhunnius;9149031;Contributor
+ShadowIce;59123;Contributor
+raghulj;57007;Contributor
+raghulrm;5362986;Contributor
+stevomit;205959;Contributor
+skitt;2128935;Contributor
+mr-intj;6268767;Contributor
+tuxayo;2678215;Contributor
+alimemonzx;44647595;Contributor
+alanorth;191754;Contributor
+alexte;7724992;Contributor
+andrey-krutov;1488973;Contributor
+arantius;84729;Contributor
+bws9000;262625;Contributor
+chrissicool;232590;Contributor
+cszucko;1810383;Contributor
+CWftw;1498303;Contributor
+danielm5;66779;Contributor
+ariedov;958646;Contributor
+brettle;118192;Contributor
+eirikv;4076243;Contributor
+eerden;277513;Contributor
+jannic;232606;Contributor
+Foso;5015532;Contributor
+Kaligule;3586246;Contributor
+kvithayathil;1056073;Contributor
+luiscruz;1080714;Contributor
+mlasson;5814258;Contributor
+M-arcel;56698158;Contributor
+mo;7117;Contributor
+mdeveloper20;2319126;Contributor
+mschuetz;108637;Contributor
+MolarAmbiguity;10541979;Contributor
+mounirlamouri;573590;Contributor
+ortylp;470439;Contributor
+PtilopsisLeucotis;54054883;Contributor
+SamWhited;512573;Contributor
+selivan;1208989;Contributor
+sonnayasomnambula;7716779;Contributor
+sethoscope;534043;Contributor
+shantanahardy;26757164;Contributor
+mamehacker;16738348;Contributor
+danners;116551;Contributor
+corecode;177979;Contributor
+vimsick;20211590;Contributor
+edent;837136;Contributor
+atrus6;357881;Contributor
+waylife;3348620;Contributor
+wseemann;2296196;Contributor
+amhokies;3124968;Contributor
+axq;5077221;Contributor
+fossterer;4236021;Contributor
+lightonflux;1377943;Contributor
+minusf;3632883;Contributor
diff --git a/app/src/main/assets/licenses.xml b/app/src/main/assets/licenses.xml
new file mode 100644
index 000000000..c69f692cf
--- /dev/null
+++ b/app/src/main/assets/licenses.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<libraries>
+ <library
+ name="AntennaPod"
+ author="The AntennaPod team"
+ website="https://github.com/AntennaPod/AntennaPod/"
+ license="MIT"
+ licenseText="LICENSE.txt" />
+ <library
+ name="AntennaPod-AudioPlayer"
+ author="The AntennaPod team"
+ website="https://github.com/AntennaPod/AntennaPod-AudioPlayer/"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="Apache Commons"
+ author="The Apache Software Foundation"
+ website="http://commons.apache.org/"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="EventBus"
+ author="greenrobot"
+ website="https://github.com/greenrobot/EventBus"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="ExoPlayer"
+ author="Google"
+ website="https://github.com/google/ExoPlayer"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="Floating Action Button Speed Dial"
+ author="Roberto Leinardi"
+ website="https://github.com/leinardi/FloatingActionButtonSpeedDial"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="Glide"
+ author="bumptech"
+ website="https://github.com/bumptech/glide/"
+ license="Simplified BSD"
+ licenseText="LICENSE_GLIDE.txt" />
+ <library
+ name="Iconify"
+ author="Joan Zapata"
+ website="https://github.com/JoanZapata/android-iconify"
+ license="Apache 2.0"
+ licenseText="LICENSE_ANDROID_ICONIFY.txt" />
+ <library
+ name="jsoup"
+ author="Jonathan Hedley"
+ website="http://jsoup.org/"
+ license="MIT"
+ licenseText="LICENSE_JSOUP.txt" />
+ <library
+ name="Material Design Icons"
+ author="Google"
+ website="https://github.com/google/material-design-icons"
+ license="Attribution-ShareAlike 4.0 International"
+ licenseText="LICENSE_MATERIAL_DESIGN_ICONS.txt" />
+ <library
+ name="Material Design Icons"
+ author="Templarian"
+ website="https://github.com/Templarian/MaterialDesign"
+ license="SIL Open Font, Version 1.1"
+ licenseText="LICENSE_SIL.txt" />
+ <library
+ name="OkHttp"
+ author="Square"
+ website="https://github.com/square/okhttp"
+ license="Apache 2.0"
+ licenseText="LICENSE_OKHTTP.txt" />
+ <library
+ name="Okio"
+ author="Square"
+ website="https://github.com/square/okio"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="RecyclerView-FlexibleDivider"
+ author="yqritc"
+ website="https://github.com/yqritc/RecyclerView-FlexibleDivider"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="RxAndroid"
+ author="ReactiveX"
+ website="https://github.com/ReactiveX/RxAndroid"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="StackBlur"
+ author="Enrique López Mañas"
+ website="https://github.com/kikoso/android-stackblur"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="Triangle Label View"
+ author="Shota Saito"
+ website="https://github.com/shts/TriangleLabelView"
+ license="Apache 2.0"
+ licenseText="LICENSE_TRIANGLE_LABEL_VIEW.txt" />
+</libraries>
diff --git a/app/src/main/assets/logo.png b/app/src/main/assets/logo.png
deleted file mode 100755
index 3b5261b28..000000000
--- a/app/src/main/assets/logo.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/testfile.mp3 b/app/src/main/assets/testfile.mp3
deleted file mode 100644
index f15faadf3..000000000
--- a/app/src/main/assets/testfile.mp3
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/translators.csv b/app/src/main/assets/translators.csv
new file mode 100644
index 000000000..9670fb652
--- /dev/null
+++ b/app/src/main/assets/translators.csv
@@ -0,0 +1,46 @@
+Arabic;abdelrahman.fahem93, abdunnasir, abuzar3.khalid, desha, iDemo, mohamedagamy, msahouli, nabilMaghura
+Asturian (ast_ES);enolp
+Azerbaijani;danieloeh, kotfenix
+Basque;gaztainalde, pospolos, zakurranputza
+Bulgarian;solusitor
+Catalan;dvd1985, exort12, javiercoll, lambdani, marcmetallextrem, xc70
+Chinese (zh_CN);bebeauties38, cyril3, domingos86, dudeG, ErlichLiu, Felix2yu, gaohongyuan, Guaidaodl, Huck0, iconteral, JayYoung, jhxie, kavdx, kyleehee, linxiangyu, molisiye, owen8877, RainSlide, Sak94664, spice2wolf, stellaxuyi, tupunco, wi24rd, wongsyrone, xukeek, yangyang, yiqiok, YogaGuru
+Chinese (zh_TW);gugod, nigelinux, pggdt, Solomon, ymhuang0808
+Czech (cs_CZ);anotheranonymoususer, elich, Hanzmeister, mcepl, petnek, svetlemodry
+Danish;CasperHN, jhertel
+Dutch;e2jk, glotzbach, rwv, Vistaus
+English;mfietz, sterylmreep
+Estonian;Eraser
+Finnish;danieloeh, elguitar, Sahtor
+French;cactux, ChaoticMind, clombion, e2jk, edewaele, lacouture, LouFex, Matth78, mfietz, Poussinou, repat, Sioul, sterylmreep, TacoTheDank, Tilwa, vcariven, whenrow
+Galician;antiparvos, pikamoku, Raichely
+German;112358, altegedanken, barilla, benedikt.g, bitsunited, Buggi, ByteHamster, ceving, ChaoticMind, Chaquotay, dab0015, dadosch, DerSilly, die_otto, DJaeger, elkangaroo, enz, fidel, finsterwalder, Foso, GNi33, hightower5, HolgerJeromin, kalei, lohmann, LostInWeb, mfietz, moasda, nilso, repat, SAPlayer, schafia, Schroedingberg, sevenmaster, sucaml, Teaspoon, theonlytruth, weltenwort, Wyrrrd, ypid
+Modern Greek (1453-);antonist, danieloeh, hua2016s, jack.ath92, MSavoritias, pavlosv
+Hebrew (he_IL);amir.dafnyman, E1i9, mongoose4004, pinkasey, rellieberman, Yaron, הלוי11
+Hindi (hi_IN);ankitiitb1069, Isaasu, nmabhinandan, purple.coder, siddhusengar
+Hungarian;glatz.balazs, hurrikan, lna91, marthynw, meskobalazs, naren93, tszauer, ttyborg42
+Icelandic;marthjod
+Indonesian;jff, levirs565, luke137, rezafaiza, silvanael16
+Italian (it_IT);aalex70, allin, apanontin, Bonnee, buongiorgio, giuseppep, Guybrush88, ilmanzo, m.chinni, neonsoftware, niccord, nixxo, sevenmaster, theloca95
+Japanese;Naofumi, RACER1, sh3llc4t, TranslatorG
+Kannada (kn_IN);chiraag.nataraj, thejeshgn
+Korean;changwoo, libliboom, seungrye, skcha
+Lithuanian;naglis
+Macedonian;krisfremen
+Malayalam;joice, rashivkp, rubenroy
+Norwegian;timbast
+Norwegian Bokmål (nb_NO);corkie, heraldo, kongk, timbast
+Persian;ahangarha, F7D, sinamoghaddas
+Polish (pl_PL);d6210809, hiro2020, Iwangelion, kRkk, lomapur, mandlus, maniexx, Mephistofeles, shark103, tyle
+Portuguese;andersonvom, domingos86, emansije, smarquespt
+Portuguese (pt_BR);alexupits, alysonborges, andersonvom, arua, caioau, carlo_valente, castrors, deandreamatias, edman, Firmino, jackmiras, Junin, lipefire, lluccia, lucasmotacr, mbaltar, rogervezaro, RubeensVinicius, SamWilliam, silvanael16
+Romanian (ro_RO);corneliu.e, fuzzmz, ralienpp
+Russian (ru_RU);astra1, btimofeev, Duke_Raven, gammja, GaynullinDima, MegMasters98, mercutiy, null, overmind88, PtilopsisLeucotis, s.chebotar, shams4real, skvheadless, un_logic, Vladryyu, whereisthetea, zhenya97
+Slovenian (sl_SI);panter23
+Spanish;AleksSyntek, andersonvom, Atreyu94, coperfix, deandreamatias, domingos86, dvd1985, Fitoschido, frandavid100, hard_ware, javiercoll, Juanmuto, lambdani, LatinSuD, leogrignafini, palopezv, TacoTheDank, tres.14159, wakutiteo
+Swahili (macrolanguage);kmtra
+Swedish (sv_SE);albin.brantin, Bio, bpnilsson, ChaoticMind, jony08, nilso, SharpMelon, TiloWiklund, TwoD
+Telugu;Isaasu, veeven
+Turkish;basarancaner, brsata, Erdy, golcuk, overbite, Slsdem
+Ukrainian (uk_UA);older, paul_sm, sergiyr, zhenya97
+Vietnamese;abnvolk, nguyenvui, ppanhh, vietnamesel10n
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index 94d281a45..4e6b8fa29 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod;
import android.app.Application;
-import android.os.Build;
import android.os.StrictMode;
import com.joanzapata.iconify.Iconify;
@@ -10,7 +9,6 @@ import com.joanzapata.iconify.fonts.MaterialModule;
import de.danoeh.antennapod.core.ApCoreEventBusIndex;
import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.spa.SPAUtil;
import org.greenrobot.eventbus.EventBus;
@@ -26,46 +24,43 @@ public class PodcastApp extends Application {
}
}
- private static PodcastApp singleton;
+ private static PodcastApp singleton;
- public static PodcastApp getInstance() {
- return singleton;
- }
+ public static PodcastApp getInstance() {
+ return singleton;
+ }
- @Override
- public void onCreate() {
- super.onCreate();
+ @Override
+ public void onCreate() {
+ super.onCreate();
- Thread.setDefaultUncaughtExceptionHandler(new CrashReportWriter());
+ Thread.setDefaultUncaughtExceptionHandler(new CrashReportWriter());
- if(BuildConfig.DEBUG) {
- StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder()
- .detectLeakedSqlLiteObjects()
- .penaltyLog()
- .penaltyDropBox();
- builder.detectActivityLeaks();
- builder.detectLeakedClosableObjects();
- if(Build.VERSION.SDK_INT >= 16) {
- builder.detectLeakedRegistrationObjects();
- }
- StrictMode.setVmPolicy(builder.build());
- }
+ if (BuildConfig.DEBUG) {
+ StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder()
+ .detectLeakedSqlLiteObjects()
+ .penaltyLog()
+ .penaltyDropBox()
+ .detectActivityLeaks()
+ .detectLeakedClosableObjects()
+ .detectLeakedRegistrationObjects();
+ StrictMode.setVmPolicy(builder.build());
+ }
- singleton = this;
+ singleton = this;
- ClientConfig.initialize(this);
+ ClientConfig.initialize(this);
- EventDistributor.getInstance();
- Iconify.with(new FontAwesomeModule());
- Iconify.with(new MaterialModule());
+ Iconify.with(new FontAwesomeModule());
+ Iconify.with(new MaterialModule());
SPAUtil.sendSPAppsQueryFeedsIntent(this);
- EventBus.builder()
- .addIndex(new ApEventBusIndex())
- .addIndex(new ApCoreEventBusIndex())
- .logNoSubscriberMessages(false)
- .sendNoSubscriberEvent(false)
- .installDefaultEventBus();
+ EventBus.builder()
+ .addIndex(new ApEventBusIndex())
+ .addIndex(new ApCoreEventBusIndex())
+ .logNoSubscriberMessages(false)
+ .sendNoSubscriberEvent(false)
+ .installDefaultEventBus();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
deleted file mode 100644
index ef7ea2b16..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
+++ /dev/null
@@ -1,158 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.os.Build;
-import android.os.Bundle;
-import androidx.appcompat.app.AppCompatActivity;
-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 de.danoeh.antennapod.core.util.IntentUtils;
-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 io.reactivex.Single;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.Disposable;
-import io.reactivex.schedulers.Schedulers;
-
-/**
- * Displays the 'about' screen
- */
-public class AboutActivity extends AppCompatActivity {
-
- private static final String TAG = AboutActivity.class.getSimpleName();
-
- private WebView webView;
- private LinearLayout webViewContainer;
- private Disposable disposable;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
- getSupportActionBar().setDisplayShowHomeEnabled(true);
- setContentView(R.layout.about);
- webViewContainer = findViewById(R.id.webViewContainer);
- webView = findViewById(R.id.webViewAbout);
- webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
- if (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) {
- if (url.startsWith("http")) {
- IntentUtils.openInBrowser(AboutActivity.this, url);
- return true;
- } else {
- url = url.replace("file:///android_asset/", "");
- loadAsset(url);
- return true;
- }
- }
-
- });
- loadAsset("about.html");
- }
-
- private void loadAsset(String filename) {
- disposable = Single.create(subscriber -> {
- InputStream input = null;
- try {
- TypedArray res = AboutActivity.this.getTheme().obtainStyledAttributes(
- new int[] { R.attr.about_screen_font_color, R.attr.about_screen_background,
- R.attr.about_screen_card_background, R.attr.about_screen_card_border});
- String fontColor = String.format("#%06X", 0xFFFFFF & res.getColor(0, 0));
- String backgroundColor = String.format("#%06X", 0xFFFFFF & res.getColor(1, 0));
- String cardBackground = String.format("#%06X", 0xFFFFFF & res.getColor(2, 0));
- String cardBorder = String.format("#%06X", 0xFFFFFF & res.getColor(3, 0));
- res.recycle();
- input = getAssets().open(filename);
- String webViewData = IOUtils.toString(input, Charset.defaultCharset());
- if (!webViewData.startsWith("<!DOCTYPE html>")) {
- 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: @fontcolor@;" +
- " font-family: roboto-Light;" +
- " font-size: 8pt;" +
- " }" +
- " </style>" +
- "</head><body><p>" + webViewData + "</p></body></html>";
- webViewData = webViewData.replace("\n", "<br/>");
- }
- webViewData = webViewData.replace("@fontcolor@", fontColor);
- webViewData = webViewData.replace("@background@", backgroundColor);
- webViewData = webViewData.replace("@card_background@", cardBackground);
- webViewData = webViewData.replace("@card_border@", cardBorder);
- subscriber.onSuccess(webViewData);
- } catch (IOException e) {
- Log.e(TAG, Log.getStackTraceString(e));
- subscriber.onError(e);
- } finally {
- IOUtils.closeQuietly(input);
- }
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(
- webViewData ->
- webView.loadDataWithBaseURL("file:///android_asset/", webViewData.toString(), "text/html", "utf-8", "file:///android_asset/" + filename.toString()),
- error -> Log.e(TAG, Log.getStackTraceString(error))
- );
- }
-
- @Override
- public void onBackPressed() {
- if (webView.canGoBack()) {
- 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 (disposable != null) {
- disposable.dispose();
- }
- 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 8eb0b1e0b..7f8c14b03 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -13,7 +13,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
-import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
@@ -83,7 +83,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
}
float speed = 1.0f;
if(controller.canSetPlaybackSpeed()) {
- speed = PlaybackSpeedHelper.getCurrentPlaybackSpeed(controller.getMedia());
+ speed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(controller.getMedia());
}
String speedStr = new DecimalFormat("0.00").format(speed);
txtvPlaybackSpeed.setText(speedStr);
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 6a38f8f0a..0023e6d7f 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -34,6 +34,8 @@ import android.widget.Toast;
import com.bumptech.glide.Glide;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
import org.greenrobot.eventbus.EventBus;
@@ -48,7 +50,6 @@ import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.MessageEvent;
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.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -84,9 +85,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
private static final String TAG = "MainActivity";
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE
- | EventDistributor.UNREAD_ITEMS_UPDATE;
-
public static final String PREF_NAME = "MainActivityPrefs";
public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch";
public static final String PREF_LAST_FRAGMENT_TAG = "prefMainActivityLastFragmentTag";
@@ -319,8 +317,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
fragment = new AddFeedFragment();
break;
case SubscriptionFragment.TAG:
- SubscriptionFragment subscriptionFragment = new SubscriptionFragment();
- fragment = subscriptionFragment;
+ fragment = new SubscriptionFragment();
break;
default:
// default to the queue
@@ -490,7 +487,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
RatingDialog.init(this);
}
@@ -518,7 +514,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
@Override
protected void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if (disposable != null) {
disposable.dispose();
@@ -817,16 +812,16 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
snackbar.show();
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ loadData();
+ }
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EVENTS & arg) != 0) {
- Log.d(TAG, "Received contentUpdate Intent.");
- loadData();
- }
- }
- };
+
+ @Subscribe
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ loadData();
+ }
private void handleNavIntent() {
Log.d(TAG, "handleNavIntent()");
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 a060e258a..538ed1231 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -28,7 +28,6 @@ import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
-import com.afollestad.materialdialogs.MaterialDialog;
import com.bumptech.glide.Glide;
import com.joanzapata.iconify.IconDrawable;
import com.joanzapata.iconify.fonts.FontAwesomeIcons;
@@ -64,6 +63,7 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -391,10 +391,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
| Intent.FLAG_ACTIVITY_NEW_TASK);
View cover = findViewById(R.id.imgvCover);
- if (cover != null && Build.VERSION.SDK_INT >= 16) {
- ActivityOptionsCompat options = ActivityOptionsCompat.
- makeSceneTransitionAnimation(MediaplayerActivity.this,
- cover, "coverTransition");
+ if (cover != null) {
+ ActivityOptionsCompat options = ActivityOptionsCompat
+ .makeSceneTransitionAnimation(MediaplayerActivity.this, cover, "coverTransition");
startActivity(intent, options.toBundle());
} else {
startActivity(intent);
@@ -426,19 +425,15 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
case R.id.disable_sleeptimer_item:
if (controller.serviceAvailable()) {
- MaterialDialog.Builder stDialog = new MaterialDialog.Builder(this);
- stDialog.title(R.string.sleep_timer_label);
- stDialog.content(getString(R.string.time_left_label)
- + Converter.getDurationStringLong((int) controller
- .getSleepTimerTimeLeft()));
- stDialog.positiveText(R.string.disable_sleeptimer_label);
- stDialog.negativeText(R.string.cancel_label);
- stDialog.onPositive((dialog, which) -> {
- dialog.dismiss();
- controller.disableSleepTimer();
- });
- stDialog.onNegative((dialog, which) -> dialog.dismiss());
- stDialog.build().show();
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.sleep_timer_label)
+ .setMessage(getString(R.string.time_left_label)
+ + Converter.getDurationStringLong((int) controller
+ .getSleepTimerTimeLeft()))
+ .setPositiveButton(R.string.disable_sleeptimer_label, (dialog, which)
+ -> controller.disableSleepTimer())
+ .setNegativeButton(R.string.cancel_label, null)
+ .show();
}
break;
case R.id.set_sleeptimer_item:
@@ -504,7 +499,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private static String getWebsiteLinkWithFallback(Playable media) {
if (media == null) {
return null;
- } else if (media.getWebsiteLink() != null) {
+ } else if (StringUtils.isNotBlank(media.getWebsiteLink())) {
return media.getWebsiteLink();
} else if (media instanceof FeedMedia) {
return FeedItemUtil.getLinkWithFallback(((FeedMedia)media).getItem());
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
index 016168b45..21f4ad5be 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
@@ -36,8 +36,8 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.MessageEvent;
-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.preferences.UserPreferences;
@@ -119,7 +119,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
if (disposable != null) {
disposable.dispose();
}
- EventDistributor.getInstance().unregister(contentUpdate);
saveCurrentFragment();
}
@@ -171,7 +170,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
@Override
protected void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
loadData();
}
@@ -445,16 +443,10 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
snackbar.show();
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
-
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EventDistributor.FEED_LIST_UPDATE & arg) != 0) {
- Log.d(TAG, "Received contentUpdate Intent.");
- loadData();
- }
- }
- };
+ @Subscribe
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ loadData();
+ }
private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() {
@Override
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 ede6e6962..af11e9219 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -10,7 +10,6 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import androidx.core.app.NavUtils;
-import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.text.TextUtils;
@@ -22,22 +21,19 @@ 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 com.bumptech.glide.request.RequestOptions;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
import java.io.File;
import java.io.IOException;
@@ -49,7 +45,6 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedPreferences;
@@ -95,60 +90,31 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
public static final String ARG_TITLE = "title";
private static final int RESULT_ERROR = 2;
private static final String TAG = "OnlineFeedViewActivity";
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE;
private volatile List<Feed> feeds;
private Feed feed;
private String selectedDownloadUrl;
private Downloader downloader;
private boolean isPaused;
+ private boolean didPressSubscribe = false;
private Dialog dialog;
-
+ private ListView listView;
private Button subscribeButton;
+ private ProgressBar progressBar;
private Disposable download;
private Disposable parser;
private Disposable updater;
- private final EventDistributor.EventListener listener = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) {
- updater = Observable.fromCallable(DBReader::getFeedList)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(
- feeds -> {
- OnlineFeedViewActivity.this.feeds = feeds;
- setSubscribeButtonState(feed);
- }, error -> Log.e(TAG, Log.getStackTraceString(error))
- );
- } else if ((arg & EVENTS) != 0) {
- setSubscribeButtonState(feed);
- }
- }
- };
-
- @Subscribe(threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- setSubscribeButtonState(feed);
- }
@Override
protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
+ setTheme(UserPreferences.getTranslucentTheme());
super.onCreate(savedInstanceState);
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- }
-
- if (actionBar != null && getIntent() != null && getIntent().hasExtra(ARG_TITLE)) {
- actionBar.setTitle(getIntent().getStringExtra(ARG_TITLE));
- }
-
StorageUtils.checkStorageAvailability(this);
+ setContentView(R.layout.onlinefeedview_activity);
+ listView = findViewById(R.id.listview);
+ progressBar = findViewById(R.id.progressBar);
String feedUrl = null;
if (getIntent().hasExtra(ARG_FEEDURL)) {
@@ -157,9 +123,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|| TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
feedUrl = TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)
? getIntent().getStringExtra(Intent.EXTRA_TEXT) : getIntent().getDataString();
- if (actionBar != null) {
- actionBar.setTitle(R.string.add_feed_label);
- }
}
if (feedUrl == null) {
@@ -184,26 +147,14 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
* 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);
+ progressBar.setVisibility(View.VISIBLE);
+ findViewById(R.id.feedDisplay).setVisibility(View.GONE);
}
@Override
protected void onStart() {
super.onStart();
isPaused = false;
- EventDistributor.getInstance().register(listener);
EventBus.getDefault().register(this);
}
@@ -211,7 +162,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
protected void onStop() {
super.onStop();
isPaused = true;
- EventDistributor.getInstance().unregister(listener);
EventBus.getDefault().unregister(this);
if (downloader != null && !downloader.isFinished()) {
downloader.cancel();
@@ -252,6 +202,12 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
@Override
+ public void finish() {
+ super.finish();
+ overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
@@ -301,7 +257,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
} else if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
if (!isFinishing() && !isPaused) {
dialog = new FeedViewAuthenticationDialog(OnlineFeedViewActivity.this,
- R.string.authentication_notification_title, downloader.getDownloadRequest().getSource());
+ R.string.authentication_notification_title,
+ downloader.getDownloadRequest().getSource()).create();
dialog.show();
}
} else {
@@ -313,6 +270,25 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
}
+ @Subscribe
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ updater = Observable.fromCallable(DBReader::getFeedList)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ feeds -> {
+ OnlineFeedViewActivity.this.feeds = feeds;
+ handleUpdatedFeedStatus(feed);
+ }, error -> Log.e(TAG, Log.getStackTraceString(error))
+ );
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ handleUpdatedFeedStatus(feed);
+ }
+
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");
@@ -368,20 +344,14 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
* This method is executed on a background thread
*/
private void beforeShowFeedInformation(Feed feed) {
- final HtmlToPlainText formatter = new HtmlToPlainText();
- if(Feed.TYPE_ATOM1.equals(feed.getType()) && feed.getDescription() != null) {
- // remove HTML tags from descriptions
- Log.d(TAG, "Removing HTML from feed description");
- Document feedDescription = Jsoup.parse(feed.getDescription());
- feed.setDescription(StringUtils.trim(formatter.getPlainText(feedDescription)));
- }
+ Log.d(TAG, "Removing HTML from feed description");
+
+ feed.setDescription(HtmlToPlainText.getPlainText(feed.getDescription()));
+
Log.d(TAG, "Removing HTML from shownotes");
if (feed.getItems() != null) {
for (FeedItem item : feed.getItems()) {
- if (item.getDescription() != null) {
- Document itemDescription = Jsoup.parse(item.getDescription());
- item.setDescription(StringUtils.trim(formatter.getPlainText(itemDescription)));
- }
+ item.setDescription(HtmlToPlainText.getPlainText(item.getDescription()));
}
}
}
@@ -391,30 +361,27 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
* This method is executed on the GUI thread.
*/
private void showFeedInformation(final Feed feed, Map<String, String> alternateFeedUrls) {
- setContentView(R.layout.listview_activity);
-
+ progressBar.setVisibility(View.GONE);
+ findViewById(R.id.feedDisplay).setVisibility(View.VISIBLE);
this.feed = feed;
this.selectedDownloadUrl = feed.getDownload_url();
- EventDistributor.getInstance().register(listener);
- ListView listView = findViewById(R.id.listview);
listView.setSelector(android.R.color.transparent);
- LayoutInflater inflater = LayoutInflater.from(this);
- View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false);
- listView.addHeaderView(header);
-
listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems()));
- ImageView cover = header.findViewById(R.id.imgvCover);
- ImageView headerBackground = header.findViewById(R.id.imgvBackground);
- header.findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
- header.findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
+ ImageView cover = findViewById(R.id.imgvCover);
+ ImageView headerBackground = findViewById(R.id.imgvBackground);
+ findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
+ findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
headerBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
- TextView title = header.findViewById(R.id.txtvTitle);
- TextView author = header.findViewById(R.id.txtvAuthor);
+ TextView title = findViewById(R.id.txtvTitle);
+ TextView author = findViewById(R.id.txtvAuthor);
+ Spinner spAlternateUrls = findViewById(R.id.spinnerAlternateUrls);
+
+ View header = View.inflate(this, R.layout.onlinefeedview_header, null);
+ listView.addHeaderView(header);
TextView description = header.findViewById(R.id.txtvDescription);
- Spinner spAlternateUrls = header.findViewById(R.id.spinnerAlternateUrls);
- subscribeButton = header.findViewById(R.id.butSubscribe);
+ subscribeButton = findViewById(R.id.butSubscribe);
if (StringUtils.isNotBlank(feed.getImageUrl())) {
Glide.with(this)
@@ -442,11 +409,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
description.setText(feed.getDescription());
subscribeButton.setOnClickListener(v -> {
- if(feedInFeedlist(feed)) {
- // feed.getId() is always 0, we have to retrieve the id from the feed list from
- // the database
- Intent intent = MainActivity.getIntentToOpenFeed(this, getFeedId(feed));
- startActivity(intent);
+ if (feedInFeedlist(feed)) {
+ openFeed();
} else {
Feed f = new Feed(selectedDownloadUrl, null, feed.getTitle());
f.setPreferences(feed.getPreferences());
@@ -457,7 +421,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
Log.e(TAG, Log.getStackTraceString(e));
DownloadRequestErrorDialogCreator.newRequestErrorDialog(this, e.getMessage());
}
- setSubscribeButtonState(feed);
+ didPressSubscribe = true;
+ handleUpdatedFeedStatus(feed);
}
});
@@ -503,17 +468,28 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
});
}
- setSubscribeButtonState(feed);
+ handleUpdatedFeedStatus(feed);
+ }
+
+ private void openFeed() {
+ // feed.getId() is always 0, we have to retrieve the id from the feed list from
+ // the database
+ Intent intent = MainActivity.getIntentToOpenFeed(this, getFeedId(feed));
+ finish();
+ startActivity(intent);
}
- private void setSubscribeButtonState(Feed feed) {
+ private void handleUpdatedFeedStatus(Feed feed) {
if (subscribeButton != null && feed != null) {
if (DownloadRequester.getInstance().isDownloadingFile(feed.getDownload_url())) {
subscribeButton.setEnabled(false);
- subscribeButton.setText(R.string.downloading_label);
+ subscribeButton.setText(R.string.subscribing_label);
} else if (feedInFeedlist(feed)) {
subscribeButton.setEnabled(true);
subscribeButton.setText(R.string.open_podcast);
+ if (didPressSubscribe) {
+ openFeed();
+ }
} else {
subscribeButton.setEnabled(true);
subscribeButton.setText(R.string.subscribe_label);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
index 9caff0fc0..d7a4b9517 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
@@ -6,12 +6,11 @@ import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import org.apache.commons.lang3.ArrayUtils;
import java.io.InputStreamReader;
@@ -30,55 +29,55 @@ import de.danoeh.antennapod.core.util.LangUtils;
public class OpmlImportBaseActivity extends AppCompatActivity {
private static final String TAG = "OpmlImportBaseActivity";
- private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 5;
+ private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 5;
private OpmlImportWorker importWorker;
- @Nullable private Uri uri;
-
- /**
- * Handles the choices made by the user in the OpmlFeedChooserActivity and
- * starts the OpmlFeedQueuer if necessary.
- */
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- Log.d(TAG, "Received result");
- if (resultCode == RESULT_CANCELED) {
- Log.d(TAG, "Activity was cancelled");
+ @Nullable private Uri uri;
+
+ /**
+ * Handles the choices made by the user in the OpmlFeedChooserActivity and
+ * starts the OpmlFeedQueuer if necessary.
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(TAG, "Received result");
+ if (resultCode == RESULT_CANCELED) {
+ Log.d(TAG, "Activity was cancelled");
if (finishWhenCanceled()) {
- finish();
- }
- } else {
- int[] selected = data.getIntArrayExtra(OpmlFeedChooserActivity.EXTRA_SELECTED_ITEMS);
- if (selected != null && selected.length > 0) {
- OpmlFeedQueuer queuer = new OpmlFeedQueuer(this, selected) {
-
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- Intent intent = new Intent(OpmlImportBaseActivity.this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- }
-
- };
- queuer.executeAsync();
- } else {
- Log.d(TAG, "No items were selected");
- }
- }
- }
-
- void importUri(@Nullable Uri uri) {
+ finish();
+ }
+ } else {
+ int[] selected = data.getIntArrayExtra(OpmlFeedChooserActivity.EXTRA_SELECTED_ITEMS);
+ if (selected != null && selected.length > 0) {
+ OpmlFeedQueuer queuer = new OpmlFeedQueuer(this, selected) {
+
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ Intent intent = new Intent(OpmlImportBaseActivity.this, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+
+ };
+ queuer.executeAsync();
+ } else {
+ Log.d(TAG, "No items were selected");
+ }
+ }
+ }
+
+ void importUri(@Nullable Uri uri) {
if(uri == null) {
- new MaterialDialog.Builder(this)
- .content(R.string.opml_import_error_no_file)
- .positiveText(android.R.string.ok)
+ new AlertDialog.Builder(this)
+ .setMessage(R.string.opml_import_error_no_file)
+ .setPositiveButton(android.R.string.ok, null)
.show();
return;
}
- this.uri = uri;
+ this.uri = uri;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
- uri.toString().contains(Environment.getExternalStorageDirectory().toString())) {
+ uri.toString().contains(Environment.getExternalStorageDirectory().toString())) {
int permission = ActivityCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
requestPermission();
@@ -88,30 +87,28 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
startImport();
}
- private void requestPermission() {
- String[] permissions = { android.Manifest.permission.READ_EXTERNAL_STORAGE };
- ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_READ_EXTERNAL_STORAGE);
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode,
- String[] permissions,
- int[] grantResults) {
- if (requestCode != PERMISSION_REQUEST_READ_EXTERNAL_STORAGE) {
- return;
- }
- if (grantResults.length > 0 && ArrayUtils.contains(grantResults, PackageManager.PERMISSION_GRANTED)) {
- startImport();
- } else {
- new MaterialDialog.Builder(this)
- .content(R.string.opml_import_ask_read_permission)
- .positiveText(android.R.string.ok)
- .negativeText(R.string.cancel_label)
- .onPositive((dialog, which) -> requestPermission())
- .onNegative((dialog, which) -> finish())
+ private void requestPermission() {
+ String[] permissions = { android.Manifest.permission.READ_EXTERNAL_STORAGE };
+ ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_READ_EXTERNAL_STORAGE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String[] permissions,
+ int[] grantResults) {
+ if (requestCode != PERMISSION_REQUEST_READ_EXTERNAL_STORAGE) {
+ return;
+ }
+ if (grantResults.length > 0 && ArrayUtils.contains(grantResults, PackageManager.PERMISSION_GRANTED)) {
+ startImport();
+ } else {
+ new AlertDialog.Builder(this)
+ .setMessage(R.string.opml_import_ask_read_permission)
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> requestPermission())
+ .setNegativeButton(R.string.cancel_label, (dialog, which) -> finish())
.show();
- }
- }
+ }
+ }
/** Starts the import process. */
private void startImport() {
@@ -136,10 +133,10 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
importWorker.executeAsync();
} catch (Exception e) {
Log.d(TAG, Log.getStackTraceString(e));
- String message = getString(R.string.opml_reader_error);
- new MaterialDialog.Builder(this)
- .content(message + " " + e.getMessage())
- .positiveText(android.R.string.ok)
+ String message = getString(R.string.opml_reader_error);
+ new AlertDialog.Builder(this)
+ .setMessage(message + " " + e.getMessage())
+ .setPositiveButton(android.R.string.ok, null)
.show();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
index c9c9a0e2c..8527949b0 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
@@ -16,8 +16,6 @@ import android.text.TextUtils;
import android.util.Log;
import android.widget.Button;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import java.io.File;
import de.danoeh.antennapod.R;
@@ -28,22 +26,22 @@ import de.danoeh.antennapod.dialog.ChooseDataFolderDialog;
/** Is show if there is now external storage available. */
public class StorageErrorActivity extends AppCompatActivity {
- private static final String TAG = "StorageErrorActivity";
+ private static final String TAG = "StorageErrorActivity";
private static final String[] EXTERNAL_STORAGE_PERMISSIONS = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE };
private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 42;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
- setContentView(R.layout.storage_error);
+ setContentView(R.layout.storage_error);
- Button btnChooseDataFolder = findViewById(R.id.btnChooseDataFolder);
- btnChooseDataFolder.setOnClickListener(v -> {
+ Button btnChooseDataFolder = findViewById(R.id.btnChooseDataFolder);
+ btnChooseDataFolder.setOnClickListener(v -> {
if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
showChooseDataFolderDialog();
@@ -82,11 +80,10 @@ public class StorageErrorActivity extends AppCompatActivity {
}
if (grantResults[0] != PackageManager.PERMISSION_GRANTED ||
grantResults[1] != PackageManager.PERMISSION_GRANTED) {
- new MaterialDialog.Builder(this)
- .content(R.string.choose_data_directory_permission_rationale)
- .positiveText(android.R.string.ok)
- .onPositive((dialog, which) -> requestPermission())
- .onNegative((dialog, which) -> finish())
+ new AlertDialog.Builder(this)
+ .setMessage(R.string.choose_data_directory_permission_rationale)
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> requestPermission())
+ .setNegativeButton(android.R.string.cancel, (dialog, which) -> finish())
.show();
}
}
@@ -101,15 +98,15 @@ public class StorageErrorActivity extends AppCompatActivity {
}
}
- @Override
- protected void onPause() {
- super.onPause();
- try {
- unregisterReceiver(mediaUpdate);
- } catch (IllegalArgumentException e) {
+ @Override
+ protected void onPause() {
+ super.onPause();
+ try {
+ unregisterReceiver(mediaUpdate);
+ } catch (IllegalArgumentException e) {
Log.e(TAG, Log.getStackTraceString(e));
- }
- }
+ }
+ }
// see PreferenceController.showChooseDataFolderDialog()
private void showChooseDataFolderDialog() {
@@ -123,9 +120,9 @@ public class StorageErrorActivity extends AppCompatActivity {
});
}
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode == Activity.RESULT_OK &&
- requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == Activity.RESULT_OK &&
+ requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
File path;
@@ -138,46 +135,46 @@ public class StorageErrorActivity extends AppCompatActivity {
return;
}
String message = null;
- if(!path.exists()) {
- message = String.format(getString(R.string.folder_does_not_exist_error), dir);
- } else if(!path.canRead()) {
- message = String.format(getString(R.string.folder_not_readable_error), dir);
- } else if(!path.canWrite()) {
- message = String.format(getString(R.string.folder_not_writable_error), dir);
- }
-
- if(message == null) {
- Log.d(TAG, "Setting data folder: " + dir);
- UserPreferences.setDataFolder(dir);
- leaveErrorState();
- } else {
- AlertDialog.Builder ab = new AlertDialog.Builder(this);
- ab.setMessage(message);
- ab.setPositiveButton(android.R.string.ok, null);
- ab.show();
- }
- }
- }
-
- private void leaveErrorState() {
- finish();
- startActivity(new Intent(this, MainActivity.class));
- }
-
- private final BroadcastReceiver mediaUpdate = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (TextUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) {
- if (intent.getBooleanExtra("read-only", true)) {
- Log.d(TAG, "Media was mounted; Finishing activity");
- leaveErrorState();
- } else {
- Log.d(TAG, "Media seemed to have been mounted read only");
- }
- }
- }
-
- };
+ if(!path.exists()) {
+ message = String.format(getString(R.string.folder_does_not_exist_error), dir);
+ } else if(!path.canRead()) {
+ message = String.format(getString(R.string.folder_not_readable_error), dir);
+ } else if(!path.canWrite()) {
+ message = String.format(getString(R.string.folder_not_writable_error), dir);
+ }
+
+ if(message == null) {
+ Log.d(TAG, "Setting data folder: " + dir);
+ UserPreferences.setDataFolder(dir);
+ leaveErrorState();
+ } else {
+ AlertDialog.Builder ab = new AlertDialog.Builder(this);
+ ab.setMessage(message);
+ ab.setPositiveButton(android.R.string.ok, null);
+ ab.show();
+ }
+ }
+ }
+
+ private void leaveErrorState() {
+ finish();
+ startActivity(new Intent(this, MainActivity.class));
+ }
+
+ private final BroadcastReceiver mediaUpdate = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (TextUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) {
+ if (intent.getBooleanExtra("read-only", true)) {
+ Log.d(TAG, "Media was mounted; Finishing activity");
+ leaveErrorState();
+ } else {
+ Log.d(TAG, "Media seemed to have been mounted read only");
+ }
+ }
+ }
+
+ };
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
index 2d28ea561..0c6ae2645 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -151,10 +151,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
videoview.getHolder().addCallback(surfaceHolderCallback);
videoframe.setOnTouchListener(onVideoviewTouched);
videoOverlay.setOnTouchListener((view, motionEvent) -> true); // To suppress touches directly below the slider
-
- if (Build.VERSION.SDK_INT >= 16) {
- videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- }
+ videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
videoOverlay.setFitsSystemWindows(true);
setupVideoControlsToggler();
@@ -351,9 +348,9 @@ public class VideoplayerActivity extends MediaplayerActivity {
controls.startAnimation(animation);
}
}
- int videoviewFlag = (Build.VERSION.SDK_INT >= 16) ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0;
- getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | videoviewFlag);
+ getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
+ | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
videoOverlay.setFitsSystemWindows(true);
videoOverlay.setVisibility(View.GONE);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java
new file mode 100644
index 000000000..474b96c38
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java
@@ -0,0 +1,107 @@
+package de.danoeh.antennapod.activity;
+
+import android.Manifest;
+import android.app.WallpaperManager;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.appwidget.AppWidgetManager;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import androidx.core.content.ContextCompat;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.receiver.PlayerWidget;
+import de.danoeh.antennapod.core.service.PlayerWidgetJobService;
+
+public class WidgetConfigActivity extends AppCompatActivity {
+ private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
+
+ private SeekBar opacitySeekBar;
+ private TextView opacityTextView;
+ private RelativeLayout widgetPreview;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_widget_config);
+
+ Intent configIntent = getIntent();
+ Bundle extras = configIntent.getExtras();
+ if (extras != null) {
+ appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+
+ Intent resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ setResult(RESULT_CANCELED, resultValue);
+ if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ finish();
+ }
+
+ displayDeviceBackground();
+ opacityTextView = findViewById(R.id.widget_opacity_textView);
+ opacitySeekBar = findViewById(R.id.widget_opacity_seekBar);
+ widgetPreview = findViewById(R.id.widgetLayout);
+ findViewById(R.id.butConfirm).setOnClickListener(this::confirmCreateWidget);
+ opacitySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
+ opacityTextView.setText(seekBar.getProgress() + "%");
+ int color = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.getProgress());
+ widgetPreview.setBackgroundColor(color);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ });
+ }
+
+ private void displayDeviceBackground() {
+ int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
+ if (Build.VERSION.SDK_INT < 27 || permission == PackageManager.PERMISSION_GRANTED) {
+ final WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
+ final Drawable wallpaperDrawable = wallpaperManager.getDrawable();
+ ImageView background = findViewById(R.id.widget_config_background);
+ background.setImageDrawable(wallpaperDrawable);
+ }
+ }
+
+ private void confirmCreateWidget(View v) {
+ int backgroundColor = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.getProgress());
+
+ SharedPreferences prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, backgroundColor);
+ editor.apply();
+
+ Intent resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ setResult(RESULT_OK, resultValue);
+ finish();
+ PlayerWidgetJobService.updateWidget(this);
+ }
+
+ private int getColorWithAlpha(int color, int opacity) {
+ return (int) Math.round(0xFF * (0.01 * opacity)) * 0x1000000 + color;
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
index 5b735cd1f..9cd5cc3ab 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
@@ -35,10 +35,12 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.fragment.ItemFragment;
+import de.danoeh.antennapod.fragment.ItemPagerFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
@@ -53,7 +55,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
private final boolean showOnlyNewEpisodes;
private FeedItem selectedItem;
- private Holder currentlyPlayingItem = null;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
@@ -171,7 +172,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
if (media.isCurrentlyPlaying()) {
holder.container.setBackgroundColor(playingBackGroundColor);
- currentlyPlayingItem = holder;
} else {
holder.container.setBackgroundColor(normalBackGroundColor);
}
@@ -194,29 +194,13 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.butSecondary.setTag(item);
new CoverLoader(mainActivityRef.get())
- .withUri(item.getImageLocation())
+ .withUri(ImageResourceUtils.getImageLocation(item))
.withFallbackUri(item.getFeed().getImageLocation())
.withPlaceholderView(holder.placeholder)
.withCoverView(holder.cover)
.load();
}
- @Override
- public void onBindViewHolder(@NonNull Holder holder, int pos, List<Object> payload) {
- onBindViewHolder(holder, pos);
-
- if (holder == currentlyPlayingItem && payload.size() == 1 && payload.get(0) instanceof PlaybackPositionEvent) {
- PlaybackPositionEvent event = (PlaybackPositionEvent) payload.get(0);
- holder.progress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
- }
- }
-
- public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event) {
- if (currentlyPlayingItem != null && currentlyPlayingItem.getAdapterPosition() != RecyclerView.NO_POSITION) {
- notifyItemChanged(currentlyPlayingItem.getAdapterPosition(), event);
- }
- }
-
@Nullable
public FeedItem getSelectedItem() {
return selectedItem;
@@ -262,7 +246,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
MainActivity mainActivity = mainActivityRef.get();
if (mainActivity != null) {
long[] ids = itemAccess.getItemsIds().toArray();
- mainActivity.loadChildFragment(ItemFragment.newInstance(ids, getAdapterPosition()));
+ mainActivity.loadChildFragment(ItemPagerFragment.newInstance(ids, getAdapterPosition()));
}
}
@@ -301,6 +285,14 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item);
}
+ public boolean isCurrentlyPlayingItem() {
+ return item.getMedia() != null && item.getMedia().isCurrentlyPlaying();
+ }
+
+ public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
+ progress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
+ }
+
}
public interface ItemAccess {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java
index 9014de525..e3ca5b5a5 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.adapter;
import android.app.Dialog;
import android.content.Context;
+import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
@@ -20,7 +21,6 @@ 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.dialog.ChooseDataFolderDialog;
-import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
public class DataFolderAdapter extends RecyclerView.Adapter<DataFolderAdapter.ViewHolder> {
@@ -105,7 +105,7 @@ public class DataFolderAdapter extends RecyclerView.Adapter<DataFolderAdapter.Vi
private TextView path;
private TextView size;
private RadioButton radioButton;
- private MaterialProgressBar progressBar;
+ private ProgressBar progressBar;
ViewHolder(View itemView) {
super(itemView);
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 b8764c2ae..4d66fd486 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -2,7 +2,6 @@ package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.os.Build;
-import androidx.core.content.ContextCompat;
import android.text.Layout;
import android.text.format.DateUtils;
import android.util.Log;
@@ -13,6 +12,8 @@ import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;
+import androidx.core.content.ContextCompat;
+
import com.joanzapata.iconify.widget.IconButton;
import com.joanzapata.iconify.widget.IconTextView;
@@ -24,6 +25,7 @@ import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
/** Displays a list of DownloadStatus entries. */
public class DownloadLogAdapter extends BaseAdapter {
@@ -132,7 +134,7 @@ public class DownloadLogAdapter extends BaseAdapter {
FeedMedia media = DBReader.getFeedMedia(holder.id);
if (media != null) {
try {
- DBTasks.downloadFeedItems(context, media.getItem());
+ DownloadRequester.getInstance().downloadMedia(context, media.getItem());
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
} catch (DownloadRequestException e) {
e.printStackTrace();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
index 98d55dd97..b083908a8 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
@@ -17,9 +17,9 @@ import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
/**
* Shows a list of downloaded episodes
@@ -79,7 +79,7 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
}
Glide.with(context)
- .load(item.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(item))
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java
index df7ec46e0..66fa79a4e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java
@@ -59,6 +59,8 @@ public class FeedDiscoverAdapter extends BaseAdapter {
final PodcastSearchResult podcast = getItem(position);
+ holder.imageView.setContentDescription(podcast.title);
+
Glide.with(mainActivityRef.get())
.load(podcast.imageUrl)
.apply(new RequestOptions()
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
index fcdcb4ba6..5ccec0ade 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.adapter;
import android.os.Build;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.core.view.MotionEventCompat;
@@ -26,10 +25,10 @@ import android.widget.TextView;
import com.joanzapata.iconify.Iconify;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.fragment.ItemPagerFragment;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference;
-import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
@@ -40,10 +39,10 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
-import de.danoeh.antennapod.fragment.ItemFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
@@ -60,7 +59,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
private boolean locked;
private FeedItem selectedItem;
- private ViewHolder currentlyPlayingItem = null;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
@@ -83,11 +81,13 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
notifyDataSetChanged();
}
+ @Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.queue_listitem, parent, false);
return new ViewHolder(view);
}
+ @Override
public void onBindViewHolder(ViewHolder holder, int pos) {
FeedItem item = itemAccess.getItem(pos);
holder.bind(item);
@@ -97,18 +97,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
});
}
- @Override
- public void onBindViewHolder(@NonNull ViewHolder holder, int pos, List<Object> payload) {
- onBindViewHolder(holder, pos);
-
- if (holder == currentlyPlayingItem && payload.size() == 1 && payload.get(0) instanceof PlaybackPositionEvent) {
- PlaybackPositionEvent event = (PlaybackPositionEvent) payload.get(0);
- holder.progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
- holder.progressLeft.setText(Converter.getDurationStringLong(event.getPosition()));
- holder.progressRight.setText(Converter.getDurationStringLong(event.getDuration()));
- }
- }
-
@Nullable
public FeedItem getSelectedItem() {
return selectedItem;
@@ -124,12 +112,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
return itemAccess.getCount();
}
- public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event) {
- if (currentlyPlayingItem != null && currentlyPlayingItem.getAdapterPosition() != RecyclerView.NO_POSITION) {
- notifyItemChanged(currentlyPlayingItem.getAdapterPosition(), event);
- }
- }
-
public class ViewHolder extends RecyclerView.ViewHolder
implements View.OnClickListener,
View.OnCreateContextMenuListener,
@@ -181,7 +163,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
if (activity != null) {
long[] ids = itemAccess.getQueueIds().toArray();
int position = ArrayUtils.indexOf(ids, item.getId());
- activity.loadChildFragment(ItemFragment.newInstance(ids, position));
+ activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
}
@@ -309,7 +291,6 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
if(media.isCurrentlyPlaying()) {
container.setBackgroundColor(playingBackGroundColor);
- currentlyPlayingItem = this;
} else {
container.setBackgroundColor(normalBackGroundColor);
}
@@ -322,13 +303,22 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
butSecondary.setTag(item);
new CoverLoader(mainActivity.get())
- .withUri(item.getImageLocation())
+ .withUri(ImageResourceUtils.getImageLocation(item))
.withFallbackUri(item.getFeed().getImageLocation())
.withPlaceholderView(placeholder)
.withCoverView(cover)
.load();
}
+ public boolean isCurrentlyPlayingItem() {
+ return item.getMedia() != null && item.getMedia().isCurrentlyPlaying();
+ }
+
+ public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
+ progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
+ progressLeft.setText(Converter.getDurationStringLong(event.getPosition()));
+ progressRight.setText(Converter.getDurationStringLong(event.getDuration()));
+ }
}
public interface ItemAccess {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java
new file mode 100644
index 000000000..10bda4efa
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java
@@ -0,0 +1,59 @@
+package de.danoeh.antennapod.adapter;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.request.RequestOptions;
+import de.danoeh.antennapod.R;
+
+import java.util.List;
+
+/**
+ * Displays a list of items that have a subtitle and an icon.
+ */
+public class SimpleIconListAdapter<T extends SimpleIconListAdapter.ListItem> extends ArrayAdapter<T> {
+ private final Context context;
+ private final List<T> listItems;
+
+ public SimpleIconListAdapter(Context context, List<T> listItems) {
+ super(context, R.layout.simple_icon_list_item, listItems);
+ this.context = context;
+ this.listItems = listItems;
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ if (view == null) {
+ view = View.inflate(context, R.layout.simple_icon_list_item, null);
+ }
+
+ ListItem item = listItems.get(position);
+ ((TextView) view.findViewById(R.id.title)).setText(item.title);
+ ((TextView) view.findViewById(R.id.subtitle)).setText(item.subtitle);
+ Glide.with(context)
+ .load(item.imageUrl)
+ .apply(new RequestOptions()
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .fitCenter()
+ .dontAnimate())
+ .into(((ImageView) view.findViewById(R.id.icon)));
+ return view;
+ }
+
+ public static class ListItem {
+ public final String title;
+ public final String subtitle;
+ public final String imageUrl;
+
+ public ListItem(String title, String subtitle, String imageUrl) {
+ this.title = title;
+ this.subtitle = subtitle;
+ this.imageUrl = imageUrl;
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
index 5d52eb263..3141c6046 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
@@ -84,6 +84,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
if (feed == null) return null;
holder.feedTitle.setText(feed.getTitle());
+ holder.imageView.setContentDescription(feed.getTitle());
holder.feedTitle.setVisibility(View.VISIBLE);
int count = itemAccess.getFeedCounter(feed.getId());
if(count > 0) {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
index 55ca5471b..026386cf9 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
@@ -1,16 +1,16 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
+import android.widget.Toast;
+
import androidx.annotation.AttrRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
-import android.widget.Toast;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -64,7 +64,7 @@ class DownloadActionButton extends ItemActionButton {
private void downloadEpisode(Context context) {
try {
- DBTasks.downloadFeedItems(context, item);
+ DownloadRequester.getInstance().downloadMedia(context, item);
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
} catch (DownloadRequestException e) {
e.printStackTrace();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
index 31e9fccb5..861c6a4be 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
@@ -10,6 +10,7 @@ import android.widget.ImageButton;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
public abstract class ItemActionButton {
@@ -43,6 +44,8 @@ public abstract class ItemActionButton {
return new PlayActionButton(item);
} else if (isDownloadingMedia) {
return new CancelDownloadActionButton(item);
+ } else if (UserPreferences.streamOverDownload()) {
+ return new StreamActionButton(item);
} else if (MobileDownloadHelper.userAllowedMobileDownloads() || !MobileDownloadHelper.userChoseAddToQueue() || isInQueue) {
return new DownloadActionButton(item, isInQueue);
} else {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
index f8d2a139e..77efd9023 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
@@ -3,15 +3,14 @@ package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
import android.widget.Toast;
-import com.afollestad.materialdialogs.MaterialDialog;
-
+import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
-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;
class MobileDownloadHelper {
private static long addToQueueTimestamp;
@@ -27,16 +26,15 @@ class MobileDownloadHelper {
}
static void confirmMobileDownload(final Context context, final FeedItem item) {
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context)
- .title(R.string.confirm_mobile_download_dialog_title)
- .content(R.string.confirm_mobile_download_dialog_message)
- .positiveText(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily))
- .onPositive((dialog, which) -> downloadFeedItems(context, item));
+ AlertDialog.Builder builder = new AlertDialog.Builder(context)
+ .setTitle(R.string.confirm_mobile_download_dialog_title)
+ .setMessage(R.string.confirm_mobile_download_dialog_message)
+ .setPositiveButton(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily),
+ (dialog, which) -> downloadFeedItems(context, item));
if (!DBReader.getQueueIDList().contains(item.getId())) {
- builder
- .content(R.string.confirm_mobile_download_dialog_message_not_in_queue)
- .neutralText(R.string.confirm_mobile_download_dialog_only_add_to_queue)
- .onNeutral((dialog, which) -> addToQueue(context, item));
+ builder.setMessage(R.string.confirm_mobile_download_dialog_message_not_in_queue)
+ .setNeutralButton(R.string.confirm_mobile_download_dialog_only_add_to_queue,
+ (dialog, which) -> addToQueue(context, item));
}
builder.show();
}
@@ -50,7 +48,7 @@ class MobileDownloadHelper {
private static void downloadFeedItems(Context context, FeedItem item) {
allowMobileDownloadTimestamp = System.currentTimeMillis();
try {
- DBTasks.downloadFeedItems(context, item);
+ DownloadRequester.getInstance().downloadMedia(context, item);
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
} catch (DownloadRequestException e) {
e.printStackTrace();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
index 23a7e03ad..0d314b5eb 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
@@ -12,7 +12,6 @@ import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE;
-import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE;
class PlayActionButton extends ItemActionButton {
@@ -52,12 +51,14 @@ class PlayActionButton extends ItemActionButton {
}
private void togglePlayPause(Context context, FeedMedia media) {
- new PlaybackServiceStarter(context, media)
- .startWhenPrepared(true)
- .shouldStream(false)
- .start();
-
- String pauseOrResume = media.isCurrentlyPlaying() ? ACTION_PAUSE_PLAY_CURRENT_EPISODE : ACTION_RESUME_PLAY_CURRENT_EPISODE;
- IntentUtils.sendLocalBroadcast(context, pauseOrResume);
+ if (media.isCurrentlyPlaying()) {
+ IntentUtils.sendLocalBroadcast(context, ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+ } else {
+ new PlaybackServiceStarter(context, media)
+ .callEvenIfRunning(true)
+ .startWhenPrepared(true)
+ .shouldStream(false)
+ .start();
+ }
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java
new file mode 100644
index 000000000..c1e619fdf
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java
@@ -0,0 +1,64 @@
+package de.danoeh.antennapod.adapter.actionbutton;
+
+import android.content.Context;
+
+import androidx.annotation.AttrRes;
+import androidx.annotation.StringRes;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
+
+import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE;
+
+public class StreamActionButton extends ItemActionButton {
+
+ StreamActionButton(FeedItem item) {
+ super(item);
+ }
+
+ @Override
+ @StringRes
+ public int getLabel() {
+ return R.string.stream_label;
+ }
+
+ @Override
+ @AttrRes
+ public int getDrawable() {
+ FeedMedia media = item.getMedia();
+ if (media != null && media.isCurrentlyPlaying()) {
+ return R.attr.av_pause;
+ }
+ return R.attr.action_stream;
+ }
+
+ @Override
+ public void onClick(Context context) {
+ final FeedMedia media = item.getMedia();
+ if (media == null) {
+ return;
+ }
+
+ if (media.isPlaying()) {
+ togglePlayPause(context, media);
+ } else {
+ DBTasks.playMedia(context, media, false, true, true);
+ }
+ }
+
+ private void togglePlayPause(Context context, FeedMedia media) {
+ if (media.isCurrentlyPlaying()) {
+ IntentUtils.sendLocalBroadcast(context, ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+ } else {
+ new PlaybackServiceStarter(context, media)
+ .callEvenIfRunning(true)
+ .startWhenPrepared(true)
+ .shouldStream(true)
+ .start();
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java
index 2c41b8cb8..073f7b550 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java
@@ -1,51 +1,29 @@
package de.danoeh.antennapod.dialog;
-import android.app.Dialog;
+import android.app.AlertDialog;
import android.content.Context;
-import android.os.Bundle;
import android.view.View;
-import android.view.Window;
-import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
-
import de.danoeh.antennapod.R;
/**
* Displays a dialog with a username and password text field and an optional checkbox to save username and preferences.
*/
-public abstract class AuthenticationDialog extends Dialog {
-
- private final int titleRes;
- private final boolean enableUsernameField;
- private final boolean showSaveCredentialsCheckbox;
- private final String usernameInitialValue;
- private final String passwordInitialValue;
+public abstract class AuthenticationDialog extends AlertDialog.Builder {
- public AuthenticationDialog(Context context, int titleRes, boolean enableUsernameField, boolean showSaveCredentialsCheckbox, String usernameInitialValue, String passwordInitialValue) {
+ public AuthenticationDialog(Context context, int titleRes, boolean enableUsernameField,
+ boolean showSaveCredentialsCheckbox, String usernameInitialValue,
+ String passwordInitialValue) {
super(context);
- this.titleRes = titleRes;
- this.enableUsernameField = enableUsernameField;
- this.showSaveCredentialsCheckbox = showSaveCredentialsCheckbox;
- this.usernameInitialValue = usernameInitialValue;
- this.passwordInitialValue = passwordInitialValue;
- }
+ setTitle(titleRes);
+ View rootView = View.inflate(context, R.layout.authentication_dialog, null);
+ setView(rootView);
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.authentication_dialog);
- final EditText etxtUsername = findViewById(R.id.etxtUsername);
- final EditText etxtPassword = findViewById(R.id.etxtPassword);
- final CheckBox saveUsernamePassword = findViewById(R.id.chkSaveUsernamePassword);
- final Button butConfirm = findViewById(R.id.butConfirm);
- final Button butCancel = findViewById(R.id.butCancel);
+ final EditText etxtUsername = rootView.findViewById(R.id.etxtUsername);
+ final EditText etxtPassword = rootView.findViewById(R.id.etxtPassword);
+ final CheckBox saveUsernamePassword = rootView.findViewById(R.id.chkSaveUsernamePassword);
- if (titleRes != 0) {
- setTitle(titleRes);
- } else {
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- }
etxtUsername.setEnabled(enableUsernameField);
if (showSaveCredentialsCheckbox) {
saveUsernamePassword.setVisibility(View.VISIBLE);
@@ -59,13 +37,10 @@ public abstract class AuthenticationDialog extends Dialog {
etxtPassword.setText(passwordInitialValue);
}
setOnCancelListener(dialog -> onCancelled());
- butCancel.setOnClickListener(v -> cancel());
- butConfirm.setOnClickListener(v -> {
- onConfirmed(etxtUsername.getText().toString(),
- etxtPassword.getText().toString(),
- showSaveCredentialsCheckbox && saveUsernamePassword.isChecked());
- dismiss();
- });
+ setNegativeButton(R.string.cancel_label, null);
+ setPositiveButton(R.string.confirm_label, (dialog, which)
+ -> onConfirmed(etxtUsername.getText().toString(), etxtPassword.getText().toString(),
+ showSaveCredentialsCheckbox && saveUsernamePassword.isChecked()));
}
protected void onCancelled() {
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
index 4cfa7e870..ec285a8f6 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
@@ -2,8 +2,10 @@ package de.danoeh.antennapod.dialog;
import android.content.Context;
-import com.afollestad.materialdialogs.MaterialDialog;
-
+import android.view.View;
+import androidx.appcompat.app.AlertDialog;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DataFolderAdapter;
@@ -25,21 +27,23 @@ public class ChooseDataFolderDialog {
DataFolderAdapter adapter = new DataFolderAdapter(context, handlerFunc);
if (adapter.getItemCount() == 0) {
- new MaterialDialog.Builder(context)
- .title(R.string.error_label)
- .content(R.string.external_storage_error_msg)
- .neutralText(android.R.string.ok)
+ new AlertDialog.Builder(context)
+ .setTitle(R.string.error_label)
+ .setMessage(R.string.external_storage_error_msg)
+ .setPositiveButton(android.R.string.ok, null)
.show();
return;
}
- MaterialDialog dialog = new MaterialDialog.Builder(context)
- .title(R.string.choose_data_directory)
- .content(R.string.choose_data_directory_message)
- .adapter(adapter, null)
- .negativeText(R.string.cancel_label)
- .cancelable(true)
- .build();
+ View content = View.inflate(context, R.layout.choose_data_folder_dialog, null);
+ AlertDialog dialog = new AlertDialog.Builder(context)
+ .setView(content)
+ .setTitle(R.string.choose_data_directory)
+ .setMessage(R.string.choose_data_directory_message)
+ .setNegativeButton(R.string.cancel_label, null)
+ .create();
+ ((RecyclerView) content.findViewById(R.id.recyclerView)).setLayoutManager(new LinearLayoutManager(context));
+ ((RecyclerView) content.findViewById(R.id.recyclerView)).setAdapter(adapter);
adapter.setDialog(dialog);
dialog.show();
}
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 f6d08b7bf..ff131aeba 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -4,16 +4,6 @@ import android.app.AlertDialog;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import androidx.annotation.IdRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.PluralsRes;
-import androidx.annotation.StringRes;
-import com.google.android.material.snackbar.Snackbar;
-import androidx.core.app.ActivityCompat;
-import androidx.fragment.app.Fragment;
-import androidx.collection.ArrayMap;
-import androidx.appcompat.app.ActionBar;
-import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -24,6 +14,17 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.PluralsRes;
+import androidx.annotation.StringRes;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.collection.ArrayMap;
+import androidx.core.app.ActivityCompat;
+import androidx.fragment.app.Fragment;
+
+import com.google.android.material.snackbar.Snackbar;
import com.leinardi.android.speeddial.SpeedDialView;
import java.util.ArrayList;
@@ -35,10 +36,12 @@ import java.util.Map;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.LongList;
+import de.danoeh.antennapod.core.util.SortOrder;
public class EpisodesApplyActionFragment extends Fragment {
@@ -263,6 +266,18 @@ public class EpisodesApplyActionFragment extends Fragment {
mSelectToggle.setTitle(titleResId);
}
+ private static final Map<Integer, SortOrder> menuItemIdToSortOrder;
+ static {
+ Map<Integer, SortOrder> map = new ArrayMap<>();
+ map.put(R.id.sort_title_a_z, SortOrder.EPISODE_TITLE_A_Z);
+ map.put(R.id.sort_title_z_a, SortOrder.EPISODE_TITLE_Z_A);
+ map.put(R.id.sort_date_new_old, SortOrder.DATE_NEW_OLD);
+ map.put(R.id.sort_date_old_new, SortOrder.DATE_OLD_NEW);
+ map.put(R.id.sort_duration_long_short, SortOrder.DURATION_LONG_SHORT);
+ map.put(R.id.sort_duration_short_long, SortOrder.DURATION_SHORT_LONG);
+ menuItemIdToSortOrder = Collections.unmodifiableMap(map);
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
@StringRes int resId = 0;
@@ -305,24 +320,12 @@ public class EpisodesApplyActionFragment extends Fragment {
checkWithMedia();
resId = R.string.selected_has_media_label;
break;
- case R.id.sort_title_a_z:
- sortByTitle(false);
- return true;
- case R.id.sort_title_z_a:
- sortByTitle(true);
- return true;
- case R.id.sort_date_new_old:
- sortByDate(true);
- return true;
- case R.id.sort_date_old_new:
- sortByDate(false);
- return true;
- case R.id.sort_duration_long_short:
- sortByDuration(true);
- return true;
- case R.id.sort_duration_short_long:
- sortByDuration(false);
- return true;
+ default: // handle various sort options
+ SortOrder sortOrder = menuItemIdToSortOrder.get(item.getItemId());
+ if (sortOrder != null) {
+ sort(sortOrder);
+ return true;
+ }
}
if(resId != 0) {
Snackbar.make(getActivity().findViewById(R.id.content), resId, Snackbar.LENGTH_SHORT)
@@ -333,52 +336,9 @@ public class EpisodesApplyActionFragment extends Fragment {
}
}
- private void sortByTitle(final boolean reverse) {
- Collections.sort(episodes, (lhs, rhs) -> {
- if (reverse) {
- return -1 * lhs.getTitle().compareTo(rhs.getTitle());
- } else {
- return lhs.getTitle().compareTo(rhs.getTitle());
- }
- });
- refreshTitles();
- refreshCheckboxes();
- }
-
- private void sortByDate(final boolean reverse) {
- Collections.sort(episodes, (lhs, rhs) -> {
- if (lhs.getPubDate() == null) {
- return -1;
- } else if (rhs.getPubDate() == null) {
- return 1;
- }
- int code = lhs.getPubDate().compareTo(rhs.getPubDate());
- if (reverse) {
- return -1 * code;
- } else {
- return code;
- }
- });
- refreshTitles();
- refreshCheckboxes();
- }
-
- private void sortByDuration(final boolean reverse) {
- Collections.sort(episodes, (lhs, rhs) -> {
- int ordering;
- if (!lhs.hasMedia()) {
- ordering = 1;
- } else if (!rhs.hasMedia()) {
- ordering = -1;
- } else {
- ordering = lhs.getMedia().getDuration() - rhs.getMedia().getDuration();
- }
- if(reverse) {
- return -1 * ordering;
- } else {
- return ordering;
- }
- });
+ private void sort(@NonNull SortOrder sortOrder) {
+ FeedItemPermutors.getPermutor(sortOrder)
+ .reorder(episodes);
refreshTitles();
refreshCheckboxes();
}
@@ -525,7 +485,7 @@ public class EpisodesApplyActionFragment extends Fragment {
}
}
try {
- DBTasks.downloadFeedItems(getActivity(), toDownload.toArray(new FeedItem[toDownload.size()]));
+ DownloadRequester.getInstance().downloadMedia(getActivity(), toDownload.toArray(new FeedItem[toDownload.size()]));
} catch (DownloadRequestException e) {
e.printStackTrace();
DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage());
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java
new file mode 100644
index 000000000..2ee716c7c
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java
@@ -0,0 +1,51 @@
+package de.danoeh.antennapod.dialog;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.util.SortOrder;
+
+public abstract class IntraFeedSortDialog {
+
+ @Nullable
+ protected SortOrder currentSortOrder;
+ @NonNull
+ protected Context context;
+
+ public IntraFeedSortDialog(@NonNull Context context, @Nullable SortOrder sortOrder) {
+ this.context = context;
+ this.currentSortOrder = sortOrder;
+ }
+
+ public void openDialog() {
+ final String[] items = context.getResources().getStringArray(R.array.feed_episodes_sort_options);
+ final String[] valueStrs = context.getResources().getStringArray(R.array.feed_episodes_sort_values);
+ final SortOrder[] values = new SortOrder[valueStrs.length];
+ for (int i = 0; i < valueStrs.length; i++) {
+ values[i] = SortOrder.valueOf(valueStrs[i]);
+ }
+
+ int idxCurrentSort = -1;
+ for (int i = 0; i < values.length; i++) {
+ if (currentSortOrder == values[i]) {
+ idxCurrentSort = i;
+ break;
+ }
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.sort);
+ builder.setSingleChoiceItems(items, idxCurrentSort, (dialog, idxNewSort) -> {
+ updateSort(values[idxNewSort]);
+ dialog.dismiss();
+ });
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ }
+
+ protected abstract void updateSort(@NonNull SortOrder sortOrder);
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
index f53dbe57a..3e4e40a5b 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
@@ -3,15 +3,16 @@ package de.danoeh.antennapod.dialog;
import android.app.Dialog;
import android.os.Bundle;
import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.SeekBar;
import android.widget.TextView;
-import com.afollestad.materialdialogs.MaterialDialog;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import java.util.Locale;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.PlaybackSpeedHelper;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.playback.Playable;
@@ -24,7 +25,7 @@ public class PlaybackControlsDialog extends DialogFragment {
private static final String ARGUMENT_IS_PLAYING_VIDEO = "isPlayingVideo";
private PlaybackController controller;
- private MaterialDialog dialog;
+ private AlertDialog dialog;
private boolean isPlayingVideo;
public static PlaybackControlsDialog newInstance(boolean isPlayingVideo) {
@@ -42,7 +43,12 @@ public class PlaybackControlsDialog extends DialogFragment {
@Override
public void onStart() {
super.onStart();
- controller = new PlaybackController(getActivity(), false);
+ controller = new PlaybackController(getActivity(), false) {
+ @Override
+ public void setupGUI() {
+ setupUi();
+ }
+ };
controller.init();
setupUi();
}
@@ -59,15 +65,14 @@ public class PlaybackControlsDialog extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
isPlayingVideo = getArguments() != null && getArguments().getBoolean(ARGUMENT_IS_PLAYING_VIDEO);
- dialog = new MaterialDialog.Builder(getContext())
- .title(R.string.audio_controls)
- .customView(R.layout.audio_controls, true)
- .neutralText(R.string.close_label)
- .onNeutral((dialog1, which) -> {
- final SeekBar left = (SeekBar) dialog1.findViewById(R.id.volume_left);
- final SeekBar right = (SeekBar) dialog1.findViewById(R.id.volume_right);
+ dialog = new AlertDialog.Builder(getContext())
+ .setTitle(R.string.audio_controls)
+ .setView(R.layout.audio_controls)
+ .setPositiveButton(R.string.close_label, (dialog1, which) -> {
+ final SeekBar left = dialog.findViewById(R.id.volume_left);
+ final SeekBar right = dialog.findViewById(R.id.volume_right);
UserPreferences.setVolume(left.getProgress(), right.getProgress());
- }).build();
+ }).create();
return dialog;
}
@@ -110,6 +115,7 @@ public class PlaybackControlsDialog extends DialogFragment {
controller.setPlaybackSpeed(playbackSpeed);
String speedPref = String.format(Locale.US, "%.2f", playbackSpeed);
+ PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(playbackSpeed);
if (isPlayingVideo) {
UserPreferences.setVideoPlaybackSpeed(speedPref);
} else {
@@ -136,7 +142,7 @@ public class PlaybackControlsDialog extends DialogFragment {
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
- barPlaybackSpeed.setProgress((int) ((currentSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP));
+ barPlaybackSpeed.setProgress(Math.round((currentSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP));
final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left);
barLeftVolume.setProgress(UserPreferences.getLeftVolumePercentage());
@@ -212,6 +218,6 @@ public class PlaybackControlsDialog extends DialogFragment {
media = controller.getMedia();
}
- return PlaybackSpeedHelper.getCurrentPlaybackSpeed(media);
+ return PlaybackSpeedUtils.getCurrentPlaybackSpeed(media);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
index 0499d02f1..11256f2de 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
@@ -4,6 +4,7 @@ import android.app.Dialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
+import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import android.text.Editable;
import android.text.TextUtils;
@@ -16,10 +17,6 @@ import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
-import com.afollestad.materialdialogs.DialogAction;
-import com.afollestad.materialdialogs.MaterialDialog;
-import com.afollestad.materialdialogs.internal.MDButton;
-
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
@@ -48,7 +45,7 @@ public class ProxyDialog {
private final Context context;
- private MaterialDialog dialog;
+ private AlertDialog dialog;
private Spinner spType;
private EditText etHost;
@@ -64,54 +61,53 @@ public class ProxyDialog {
this.context = context;
}
- public Dialog createDialog() {
- dialog = new MaterialDialog.Builder(context)
- .title(R.string.pref_proxy_title)
- .customView(R.layout.proxy_settings, true)
- .positiveText(R.string.proxy_test_label)
- .negativeText(R.string.cancel_label)
- .onPositive((dialog1, which) -> {
- if(!testSuccessful) {
- dialog.getActionButton(DialogAction.POSITIVE).setEnabled(false);
- test();
- return;
- }
- String type = (String) ((Spinner) dialog1.findViewById(R.id.spType)).getSelectedItem();
- ProxyConfig proxy;
- if(Proxy.Type.valueOf(type) == Proxy.Type.DIRECT) {
- proxy = ProxyConfig.direct();
- } else {
- String host = etHost.getText().toString();
- String port = etPort.getText().toString();
- String username = etUsername.getText().toString();
- if(TextUtils.isEmpty(username)) {
- username = null;
- }
- String password = etPassword.getText().toString();
- if(TextUtils.isEmpty(password)) {
- password = null;
- }
- int portValue = 0;
- if(!TextUtils.isEmpty(port)) {
- portValue = Integer.valueOf(port);
- }
- if (Proxy.Type.valueOf(type) == Proxy.Type.SOCKS) {
- proxy = ProxyConfig.socks(host, portValue, username, password);
- } else {
- proxy = ProxyConfig.http(host, portValue, username, password);
- }
- }
- UserPreferences.setProxyConfig(proxy);
- AntennapodHttpClient.reinit();
- dialog.dismiss();
- })
- .onNegative((dialog1, which) -> dialog1.dismiss())
- .autoDismiss(false)
- .build();
- View view = dialog.getCustomView();
- spType = view.findViewById(R.id.spType);
+ public Dialog show() {
+ View content = View.inflate(context, R.layout.proxy_settings, null);
+ dialog = new AlertDialog.Builder(context)
+ .setTitle(R.string.pref_proxy_title)
+ .setView(content)
+ .setNegativeButton(R.string.cancel_label, null)
+ .setPositiveButton(R.string.proxy_test_label, null)
+ .show();
+ // To prevent cancelling the dialog on button click
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener((view) -> {
+ if (!testSuccessful) {
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
+ test();
+ return;
+ }
+ String type = (String) ((Spinner) content.findViewById(R.id.spType)).getSelectedItem();
+ ProxyConfig proxy;
+ if (Proxy.Type.valueOf(type) == Proxy.Type.DIRECT) {
+ proxy = ProxyConfig.direct();
+ } else {
+ String host = etHost.getText().toString();
+ String port = etPort.getText().toString();
+ String username = etUsername.getText().toString();
+ if(TextUtils.isEmpty(username)) {
+ username = null;
+ }
+ String password = etPassword.getText().toString();
+ if(TextUtils.isEmpty(password)) {
+ password = null;
+ }
+ int portValue = 0;
+ if(!TextUtils.isEmpty(port)) {
+ portValue = Integer.valueOf(port);
+ }
+ if (Proxy.Type.valueOf(type) == Proxy.Type.SOCKS) {
+ proxy = ProxyConfig.socks(host, portValue, username, password);
+ } else {
+ proxy = ProxyConfig.http(host, portValue, username, password);
+ }
+ }
+ UserPreferences.setProxyConfig(proxy);
+ AntennapodHttpClient.reinit();
+ dialog.dismiss();
+ });
- List<String> types= new ArrayList<>();
+ spType = content.findViewById(R.id.spType);
+ List<String> types = new ArrayList<>();
types.add(Proxy.Type.DIRECT.name());
types.add(Proxy.Type.HTTP.name());
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@@ -123,22 +119,22 @@ public class ProxyDialog {
spType.setAdapter(adapter);
ProxyConfig proxyConfig = UserPreferences.getProxyConfig();
spType.setSelection(adapter.getPosition(proxyConfig.type.name()));
- etHost = view.findViewById(R.id.etHost);
+ etHost = content.findViewById(R.id.etHost);
if(!TextUtils.isEmpty(proxyConfig.host)) {
etHost.setText(proxyConfig.host);
}
etHost.addTextChangedListener(requireTestOnChange);
- etPort = view.findViewById(R.id.etPort);
+ etPort = content.findViewById(R.id.etPort);
if(proxyConfig.port > 0) {
etPort.setText(String.valueOf(proxyConfig.port));
}
etPort.addTextChangedListener(requireTestOnChange);
- etUsername = view.findViewById(R.id.etUsername);
+ etUsername = content.findViewById(R.id.etUsername);
if(!TextUtils.isEmpty(proxyConfig.username)) {
etUsername.setText(proxyConfig.username);
}
etUsername.addTextChangedListener(requireTestOnChange);
- etPassword = view.findViewById(R.id.etPassword);
+ etPassword = content.findViewById(R.id.etPassword);
if(!TextUtils.isEmpty(proxyConfig.password)) {
etPassword.setText(proxyConfig.username);
}
@@ -159,7 +155,7 @@ public class ProxyDialog {
enableSettings(false);
}
});
- txtvMessage = view.findViewById(R.id.txtvMessage);
+ txtvMessage = content.findViewById(R.id.txtvMessage);
checkValidity();
return dialog;
}
@@ -230,14 +226,12 @@ public class ProxyDialog {
private void setTestRequired(boolean required) {
if(required) {
testSuccessful = false;
- MDButton button = dialog.getActionButton(DialogAction.POSITIVE);
- button.setText(context.getText(R.string.proxy_test_label));
- button.setEnabled(true);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.proxy_test_label);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
} else {
testSuccessful = true;
- MDButton button = dialog.getActionButton(DialogAction.POSITIVE);
- button.setText(context.getText(android.R.string.ok));
- button.setEnabled(true);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(android.R.string.ok);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
index c49e9153e..7cb274708 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
@@ -7,11 +7,10 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import android.util.Log;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
+import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.util.IntentUtils;
@@ -97,21 +96,18 @@ public class RatingDialog {
}
@Nullable
- private static MaterialDialog createDialog() {
+ private static AlertDialog createDialog() {
Context context = mContext.get();
- if(context == null) {
+ if (context == null) {
return null;
}
- return new MaterialDialog.Builder(context)
- .title(R.string.rating_title)
- .content(R.string.rating_message)
- .positiveText(R.string.rating_now_label)
- .negativeText(R.string.rating_never_label)
- .neutralText(R.string.rating_later_label)
- .onPositive((dialog, which) -> rateNow())
- .onNegative((dialog, which) -> saveRated())
- .onNeutral((dialog, which) -> resetStartDate())
- .cancelListener(dialog1 -> resetStartDate())
- .build();
+ return new AlertDialog.Builder(context)
+ .setTitle(R.string.rating_title)
+ .setMessage(R.string.rating_message)
+ .setPositiveButton(R.string.rating_now_label, (dialog, which) -> rateNow())
+ .setNegativeButton(R.string.rating_never_label, (dialog, which) -> saveRated())
+ .setNeutralButton(R.string.rating_later_label, (dialog, which) -> resetStartDate())
+ .setOnCancelListener(dialog1 -> resetStartDate())
+ .create();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java
index 31a544582..699c6f492 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java
@@ -3,10 +3,12 @@ package de.danoeh.antennapod.dialog;
import android.app.Activity;
import android.text.InputType;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import java.lang.ref.WeakReference;
+import android.view.View;
+import android.widget.EditText;
+import androidx.appcompat.app.AlertDialog;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -25,20 +27,24 @@ public class RenameFeedDialog {
if(activity == null) {
return;
}
- new MaterialDialog.Builder(activity)
- .title(de.danoeh.antennapod.core.R.string.rename_feed_label)
- .inputType(InputType.TYPE_CLASS_TEXT)
- .input(feed.getTitle(), feed.getTitle(), true, (dialog, input) -> {
- feed.setCustomTitle(input.toString());
+
+ View content = View.inflate(activity, R.layout.edit_text_dialog, null);
+ EditText editText = content.findViewById(R.id.text);
+ editText.setText(feed.getTitle());
+ AlertDialog dialog = new AlertDialog.Builder(activity)
+ .setView(content)
+ .setTitle(de.danoeh.antennapod.core.R.string.rename_feed_label)
+ .setPositiveButton(android.R.string.ok, (d, input) -> {
+ feed.setCustomTitle(editText.getText().toString());
DBWriter.setFeedCustomTitle(feed);
- dialog.dismiss();
})
- .neutralText(de.danoeh.antennapod.core.R.string.reset)
- .onNeutral((dialog, which) -> dialog.getInputEditText().setText(feed.getFeedTitle()))
- .negativeText(de.danoeh.antennapod.core.R.string.cancel_label)
- .onNegative((dialog, which) -> dialog.dismiss())
- .autoDismiss(false)
+ .setNeutralButton(de.danoeh.antennapod.core.R.string.reset, null)
+ .setNegativeButton(de.danoeh.antennapod.core.R.string.cancel_label, null)
.show();
+
+ // To prevent cancelling the dialog on button click
+ dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(
+ (view) -> editText.setText(feed.getFeedTitle()));
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
index dc056a3f9..8d176c708 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
@@ -3,7 +3,6 @@ package de.danoeh.antennapod.dialog;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
-import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
@@ -12,9 +11,7 @@ import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
-import com.afollestad.materialdialogs.DialogAction;
-import com.afollestad.materialdialogs.MaterialDialog;
-
+import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
@@ -26,7 +23,7 @@ public abstract class SleepTimerDialog {
private final Context context;
- private MaterialDialog dialog;
+ private AlertDialog dialog;
private EditText etxtTime;
private Spinner spTimeUnit;
private CheckBox cbShakeToReset;
@@ -38,40 +35,38 @@ public abstract class SleepTimerDialog {
this.context = context;
}
- 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.onNegative((dialog, which) -> dialog.dismiss());
- builder.onPositive((dialog, which) -> {
- try {
- savePreferences();
- long input = SleepTimerPreferences.timerMillis();
- 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 = view.findViewById(R.id.etxtTime);
- spTimeUnit = view.findViewById(R.id.spTimeUnit);
- cbShakeToReset = view.findViewById(R.id.cbShakeToReset);
- cbVibrate = view.findViewById(R.id.cbVibrate);
- chAutoEnable = view.findViewById(R.id.chAutoEnable);
+ public AlertDialog createNewDialog() {
+ View content = View.inflate(context, R.layout.time_dialog, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.set_sleeptimer_label);
+ builder.setView(content);
+ builder.setNegativeButton(R.string.cancel_label, (dialog, which) -> dialog.dismiss());
+ builder.setPositiveButton(R.string.set_sleeptimer_label, (dialog, which) -> {
+ try {
+ savePreferences();
+ long input = SleepTimerPreferences.timerMillis();
+ 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.create();
+
+ etxtTime = content.findViewById(R.id.etxtTime);
+ spTimeUnit = content.findViewById(R.id.spTimeUnit);
+ cbShakeToReset = content.findViewById(R.id.cbShakeToReset);
+ cbVibrate = content.findViewById(R.id.cbVibrate);
+ chAutoEnable = content.findViewById(R.id.chAutoEnable);
etxtTime.setText(SleepTimerPreferences.lastTimerValue());
etxtTime.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
- checkInputLength(s.length());
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(s.length() > 0);
}
@Override
@@ -109,16 +104,6 @@ public abstract class SleepTimerDialog {
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 void savePreferences() {
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 1cf34b2b3..ef624ebe6 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -1,37 +1,21 @@
package de.danoeh.antennapod.dialog;
-import android.content.ActivityNotFoundException;
import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
import android.os.Build;
import androidx.appcompat.app.AlertDialog;
-import android.util.Log;
-import android.view.View;
-
-import com.afollestad.materialdialogs.DialogAction;
-import com.afollestad.materialdialogs.MaterialDialog;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
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 (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context)
- || UserPreferences.useSonic()
+ if (UserPreferences.useSonic()
|| UserPreferences.useExoplayer()
|| Build.VERSION.SDK_INT >= 23) {
showSpeedSelectorDialog(context);
@@ -45,38 +29,17 @@ public class VariableSpeedDialog {
}
private static void showGetPluginDialog(final Context context, boolean showSpeedSelector) {
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
- builder.title(R.string.no_playback_plugin_title);
- builder.content(R.string.no_playback_plugin_or_sonic_msg);
- builder.positiveText(R.string.enable_sonic);
- builder.negativeText(R.string.download_plugin_label);
- builder.neutralText(R.string.close_label);
- builder.onPositive((dialog, which) -> {
- if (Build.VERSION.SDK_INT >= 16) { // just to be safe
- UserPreferences.enableSonic();
- if(showSpeedSelector) {
- showSpeedSelectorDialog(context);
- }
- }
- });
- builder.onNegative((dialog, which) -> {
- try {
- context.startActivity(playStoreIntent);
- } catch (ActivityNotFoundException e) {
- // this is usually thrown on an emulator if the Android market is not installed
- Log.e(TAG, Log.getStackTraceString(e));
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.no_playback_plugin_title);
+ builder.setMessage(R.string.no_playback_plugin_or_sonic_msg);
+ builder.setPositiveButton(R.string.enable_sonic, (dialog, which) -> {
+ UserPreferences.enableSonic();
+ if (showSpeedSelector) {
+ showSpeedSelectorDialog(context);
}
});
- builder.forceStacking(true);
- MaterialDialog dialog = builder.show();
- if (Build.VERSION.SDK_INT < 16) {
- View pos = dialog.getActionButton(DialogAction.POSITIVE);
- pos.setEnabled(false);
- }
- if(!IntentUtils.isCallable(context.getApplicationContext(), playStoreIntent)) {
- View pos = dialog.getActionButton(DialogAction.NEGATIVE);
- pos.setEnabled(false);
- }
+ builder.setNeutralButton(R.string.close_label, null);
+ builder.show();
}
private static void showSpeedSelectorDialog(final Context context) {
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 b7bfe3438..2cfe7c1e8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -46,7 +46,7 @@ public class AddFeedFragment extends Fragment {
View butOpmlImport = root.findViewById(R.id.btn_opml_import);
butOpmlImport.setOnClickListener(v -> startActivity(new Intent(getActivity(),
OpmlImportFromPathActivity.class)));
-
+ root.findViewById(R.id.search_icon).setOnClickListener(view -> performSearch());
return root;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
index 5ab781fe3..47d7a0b86 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
@@ -101,7 +101,6 @@ public class CombinedSearchFragment extends Fragment {
inflater.inflate(R.menu.itunes_search, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_label));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
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 1917e4c75..7f70daaec 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -16,7 +16,8 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DownloadedEpisodesListAdapter;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.DownloadLogEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -27,6 +28,8 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_ADD_TO_QUEUE;
import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_DELETE;
@@ -38,10 +41,6 @@ public class CompletedDownloadsFragment extends ListFragment {
private static final String TAG = CompletedDownloadsFragment.class.getSimpleName();
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.DOWNLOADLOG_UPDATE |
- EventDistributor.UNREAD_ITEMS_UPDATE;
-
private List<FeedItem> items = new ArrayList<>();
private DownloadedEpisodesListAdapter listAdapter;
private Disposable disposable;
@@ -56,19 +55,24 @@ public class CompletedDownloadsFragment extends ListFragment {
listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess);
setListAdapter(listAdapter);
setListShown(false);
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ EventBus.getDefault().unregister(this);
+ super.onDestroyView();
}
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
loadItems();
}
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
@@ -79,7 +83,7 @@ public class CompletedDownloadsFragment extends ListFragment {
super.onListItemClick(l, v, position, id);
position -= l.getHeaderViewsCount();
long[] ids = FeedItemUtil.getIds(items);
- ((MainActivity) requireActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
+ ((MainActivity) requireActivity()).loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
@Override
@@ -135,14 +139,15 @@ public class CompletedDownloadsFragment extends ListFragment {
}
};
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- loadItems();
- }
- }
- };
+ @Subscribe
+ public void onDownloadLogChanged(DownloadLogEvent event) {
+ loadItems();
+ }
+
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ loadItems();
+ }
private void loadItems() {
if (disposable != null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
index cf9ee6c41..5467d71a8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -15,6 +15,7 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import io.reactivex.Maybe;
@@ -70,7 +71,7 @@ public class CoverFragment extends Fragment {
txtvPodcastTitle.setText(media.getFeedTitle());
txtvEpisodeTitle.setText(media.getEpisodeTitle());
Glide.with(this)
- .load(media.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(media))
.apply(new RequestOptions()
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.dontAnimate()
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 528c50747..16337b00d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -19,7 +19,7 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadLogAdapter;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.DownloadLogEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
@@ -30,6 +30,8 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
/**
* Shows the download log
@@ -45,14 +47,12 @@ public class DownloadLogFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
loadItems();
}
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
@@ -77,6 +77,13 @@ public class DownloadLogFragment extends ListFragment {
adapter = new DownloadLogAdapter(getActivity(), itemAccess);
setListAdapter(adapter);
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ EventBus.getDefault().unregister(this);
+ super.onDestroyView();
}
private void onFragmentLoaded() {
@@ -133,15 +140,10 @@ public class DownloadLogFragment extends ListFragment {
}
};
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
-
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EventDistributor.DOWNLOADLOG_UPDATE) != 0) {
- loadItems();
- }
- }
- };
+ @Subscribe
+ public void onDownloadLogChanged(DownloadLogEvent event) {
+ loadItems();
+ }
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
index 3fc67f795..5dbb703b7 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -24,7 +24,10 @@ import android.widget.Toast;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -39,7 +42,6 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
@@ -64,11 +66,6 @@ import io.reactivex.schedulers.Schedulers;
public abstract class EpisodesListFragment extends Fragment {
public static final String TAG = "EpisodesListFragment";
-
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE |
- EventDistributor.UNREAD_ITEMS_UPDATE |
- EventDistributor.PLAYER_STATUS_UPDATE;
-
private static final String DEFAULT_PREF_NAME = "PrefAllEpisodesFragment";
private static final String PREF_SCROLL_POSITION = "scroll_position";
private static final String PREF_SCROLL_OFFSET = "scroll_offset";
@@ -102,7 +99,6 @@ public abstract class EpisodesListFragment extends Fragment {
public void onStart() {
super.onStart();
setHasOptionsMenu(true);
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
loadItems();
}
@@ -124,7 +120,6 @@ public abstract class EpisodesListFragment extends Fragment {
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
- EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
@@ -174,7 +169,6 @@ public abstract class EpisodesListFragment extends Fragment {
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_hint));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
@@ -388,7 +382,14 @@ public abstract class EpisodesListFragment extends Fragment {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(PlaybackPositionEvent event) {
if (listAdapter != null) {
- listAdapter.notifyCurrentlyPlayingItemChanged(event);
+ for (int i = 0; i < listAdapter.getItemCount(); i++) {
+ AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)
+ recyclerView.findViewHolderForAdapterPosition(i);
+ if (holder != null && holder.isCurrentlyPlayingItem()) {
+ holder.notifyPlaybackPositionUpdated(event);
+ break;
+ }
+ }
}
}
@@ -414,17 +415,27 @@ public abstract class EpisodesListFragment extends Fragment {
}
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- loadItems();
- if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
- requireActivity().invalidateOptionsMenu();
- }
- }
+ private void updateUi() {
+ loadItems();
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ requireActivity().invalidateOptionsMenu();
}
- };
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlayerStatusChanged(PlayerStatusEvent event) {
+ updateUi();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ updateUi();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ updateUi();
+ }
void loadItems() {
if (disposable != null) {
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 bbc33c6ca..ce2232a55 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -22,6 +22,7 @@ import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import io.reactivex.Maybe;
@@ -70,9 +71,9 @@ public class ExternalPlayerFragment extends Fragment {
if (controller != null && controller.getMedia() != null) {
Intent intent = PlaybackService.getPlayerActivityIntent(getActivity(), controller.getMedia());
- if (Build.VERSION.SDK_INT >= 16 && controller.getMedia().getMediaType() == MediaType.AUDIO) {
- ActivityOptionsCompat options = ActivityOptionsCompat.
- makeSceneTransitionAnimation(getActivity(), imgvCover, "coverTransition");
+ if (controller.getMedia().getMediaType() == MediaType.AUDIO) {
+ ActivityOptionsCompat options = ActivityOptionsCompat
+ .makeSceneTransitionAnimation(getActivity(), imgvCover, "coverTransition");
startActivity(intent, options.toBundle());
} else {
startActivity(intent);
@@ -227,7 +228,7 @@ public class ExternalPlayerFragment extends Fragment {
onPositionObserverUpdate();
Glide.with(getActivity())
- .load(media.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(media))
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
index 5282a6bb2..f73735658 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -54,7 +54,8 @@ public class FavoriteEpisodesFragment extends EpisodesListFragment {
emptyView.setTitle(R.string.no_fav_episodes_head_label);
emptyView.setMessage(R.string.no_fav_episodes_label);
- ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
+ ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0,
+ ItemTouchHelper.LEFT) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
index 6b270e220..79637d79a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
@@ -167,16 +167,8 @@ public class FeedInfoFragment extends Fragment {
txtvTitle.setText(feed.getTitle());
- String description = feed.getDescription();
- if(description != null) {
- if(Feed.TYPE_ATOM1.equals(feed.getType())) {
- HtmlToPlainText formatter = new HtmlToPlainText();
- Document feedDescription = Jsoup.parse(feed.getDescription());
- description = StringUtils.trim(formatter.getPlainText(feedDescription));
- }
- } else {
- description = "";
- }
+ String description = HtmlToPlainText.getPlainText(feed.getDescription());
+
txtvDescription.setText(description);
if (!TextUtils.isEmpty(feed.getAuthor())) {
@@ -222,6 +214,10 @@ public class FeedInfoFragment extends Fragment {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
+ if (feed == null) {
+ Toast.makeText(getContext(), R.string.please_wait_for_data, Toast.LENGTH_LONG).show();
+ return super.onOptionsItemSelected(item);
+ }
boolean handled = false;
try {
handled = FeedMenuHandler.onOptionsItemClicked(getContext(), item, feed);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
index 0c33dce5a..33343948f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -5,10 +5,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.graphics.LightingColorFilter;
import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.fragment.app.ListFragment;
-import androidx.core.view.MenuItemCompat;
-import androidx.appcompat.widget.SearchView;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@@ -23,12 +19,17 @@ import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.appcompat.widget.SearchView;
+import androidx.core.view.MenuItemCompat;
+import androidx.fragment.app.ListFragment;
+
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconTextView;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import org.apache.commons.lang3.Validate;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
@@ -45,7 +46,12 @@ import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -59,6 +65,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.Optional;
@@ -79,12 +86,6 @@ import io.reactivex.schedulers.Schedulers;
@SuppressLint("ValidFragment")
public class FeedItemlistFragment extends ListFragment {
private static final String TAG = "ItemlistFragment";
-
- private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE
- | EventDistributor.FEED_LIST_UPDATE
- | EventDistributor.PLAYER_STATUS_UPDATE;
-
- public static final String EXTRA_SELECTED_FEEDITEM = "extra.de.danoeh.antennapod.activity.selected_feeditem";
private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id";
private FeedItemlistAdapter adapter;
@@ -151,7 +152,6 @@ public class FeedItemlistFragment extends ListFragment {
registerForContextMenu(getListView());
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
loadItems();
}
@@ -160,7 +160,6 @@ public class FeedItemlistFragment extends ListFragment {
public void onDestroyView() {
super.onDestroyView();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if (disposable != null) {
disposable.dispose();
@@ -187,11 +186,11 @@ public class FeedItemlistFragment extends ListFragment {
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_hint));
searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
+ menu.findItem(R.id.sort_items).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.findItem(R.id.filter_items).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.findItem(R.id.episode_actions).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.findItem(R.id.refresh_item).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
@@ -237,6 +236,10 @@ public class FeedItemlistFragment extends ListFragment {
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (!super.onOptionsItemSelected(item)) {
+ if (feed == null) {
+ Toast.makeText(getContext(), R.string.please_wait_for_data, Toast.LENGTH_LONG).show();
+ return true;
+ }
try {
if (!FeedMenuHandler.onOptionsItemClicked(getActivity(), item, feed)) {
switch (item.getItemId()) {
@@ -345,7 +348,7 @@ public class FeedItemlistFragment extends ListFragment {
position -= l.getHeaderViewsCount();
MainActivity activity = (MainActivity) getActivity();
long[] ids = FeedItemUtil.getIds(feed.getItems());
- activity.loadChildFragment(ItemFragment.newInstance(ids, position));
+ activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position));
activity.getSupportActionBar().setTitle(feed.getTitle());
}
@@ -392,18 +395,28 @@ public class FeedItemlistFragment extends ListFragment {
}
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ private void updateUi() {
+ refreshHeaderView();
+ loadItems();
+ updateProgressBarVisibility();
+ }
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EVENTS & arg) != 0) {
- Log.d(TAG, "Received contentUpdate Intent. arg " + arg);
- refreshHeaderView();
- loadItems();
- updateProgressBarVisibility();
- }
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlayerStatusChanged(PlayerStatusEvent event) {
+ updateUi();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ updateUi();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ if (event.contains(feed)) {
+ updateUi();
}
- };
+ }
private void updateProgressBarVisibility() {
if (isUpdatingFeed != updateRefreshMenuItemChecker.isRefreshing()) {
@@ -629,6 +642,11 @@ public class FeedItemlistFragment extends ListFragment {
FeedItemFilter filter = feed.getItemFilter();
feed.setItems(filter.filter(feed.getItems()));
}
+ if (feed != null && feed.getSortOrder() != null) {
+ List<FeedItem> feedItems = feed.getItems();
+ FeedItemPermutors.getPermutor(feed.getSortOrder()).reorder(feedItems);
+ feed.setItems(feedItems);
+ }
return Optional.ofNullable(feed);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
index a3c3df340..aa26610aa 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
@@ -100,7 +100,6 @@ public class FyydSearchFragment extends Fragment {
inflater.inflate(R.menu.itunes_search, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_fyyd_label));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
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 a97d60099..85978b761 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -144,7 +144,6 @@ public class ItemDescriptionFragment extends Fragment {
}
};
- @SuppressWarnings("deprecation")
@SuppressLint("NewApi")
@Override
public boolean onContextItemSelected(MenuItem item) {
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 f17f8c645..2806f48ba 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -3,55 +3,49 @@ package de.danoeh.antennapod.fragment;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
+import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-import androidx.core.content.ContextCompat;
-import androidx.core.view.GestureDetectorCompat;
import android.text.Layout;
import android.text.TextUtils;
import android.util.Log;
+import android.util.TypedValue;
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.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
+import android.widget.Button;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-
+import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconButton;
-
-import org.apache.commons.lang3.ArrayUtils;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.util.List;
-
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.CastEnabledActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.Downloader;
@@ -61,63 +55,45 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
-import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.playback.Timeline;
-import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
-import de.danoeh.antennapod.view.OnSwipeGesture;
-import de.danoeh.antennapod.view.SwipeGestureDetector;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.apache.commons.lang3.ArrayUtils;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.List;
/**
* Displays information about a FeedItem and actions.
*/
-public class ItemFragment extends Fragment implements OnSwipeGesture {
+public class ItemFragment extends Fragment {
private static final String TAG = "ItemFragment";
-
- private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE;
-
- private static final String ARG_FEEDITEMS = "feeditems";
- private static final String ARG_FEEDITEM_POS = "feeditem_pos";
-
- private GestureDetectorCompat headerGestureDetector;
- private GestureDetectorCompat webviewGestureDetector;
+ private static final String ARG_FEEDITEM = "feeditem";
/**
* Creates a new instance of an ItemFragment
*
- * @param feeditem The ID of the FeedItem that should be displayed.
+ * @param feeditem The ID of the FeedItem to show
* @return The ItemFragment instance
*/
public static ItemFragment newInstance(long feeditem) {
- return newInstance(new long[] { feeditem }, 0);
- }
-
- /**
- * Creates a new instance of an ItemFragment
- *
- * @param feeditems The IDs of the FeedItems that belong to the same list
- * @param feedItemPos The position of the FeedItem that is currently shown
- * @return The ItemFragment instance
- */
- public static ItemFragment newInstance(long[] feeditems, int feedItemPos) {
ItemFragment fragment = new ItemFragment();
Bundle args = new Bundle();
- args.putLongArray(ARG_FEEDITEMS, feeditems);
- args.putInt(ARG_FEEDITEM_POS, feedItemPos);
+ args.putLong(ARG_FEEDITEM, feeditem);
fragment.setArguments(args);
return fragment;
}
private boolean itemsLoaded = false;
- private long[] feedItems;
- private int feedItemPos;
+ private long itemId;
private FeedItem item;
private String webviewData;
private List<Downloader> downloaderList;
@@ -131,9 +107,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
private ImageView imgvCover;
private ProgressBar progbarDownload;
private ProgressBar progbarLoading;
- private IconButton butAction1;
- private IconButton butAction2;
- private Menu popupMenu;
+ private Button butAction1;
+ private Button butAction2;
private Disposable disposable;
@@ -145,20 +120,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setRetainInstance(true);
- setHasOptionsMenu(true);
-
- feedItems = getArguments().getLongArray(ARG_FEEDITEMS);
- feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS);
- headerGestureDetector = new GestureDetectorCompat(getActivity(), new SwipeGestureDetector(this));
- webviewGestureDetector = new GestureDetectorCompat(getActivity(), new SwipeGestureDetector(this) {
- // necessary for the longclick context menu to work properly
- @Override
- public boolean onDown(MotionEvent e) {
- return false;
- }
- });
+ itemId = getArguments().getLong(ARG_FEEDITEM);
}
@Override
@@ -168,11 +131,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
root = layout.findViewById(R.id.content_root);
- LinearLayout header = root.findViewById(R.id.header);
- if(feedItems.length > 0) {
- header.setOnTouchListener((v, event) -> headerGestureDetector.onTouchEvent(event));
- }
-
txtvPodcast = layout.findViewById(R.id.txtvPodcast);
txtvPodcast.setOnClickListener(v -> openPodcast());
txtvTitle = layout.findViewById(R.id.txtvTitle);
@@ -203,10 +161,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
webvDescription.getSettings().setLayoutAlgorithm(
WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webvDescription.getSettings().setLoadWithOverviewMode(true);
- if(feedItems.length > 0) {
webvDescription.setOnLongClickListener(webViewLongClickListener);
- }
- webvDescription.setOnTouchListener((v, event) -> webviewGestureDetector.onTouchEvent(event));
+
webvDescription.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
@@ -260,6 +216,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
@Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ load();
+ }
+
+ @Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@@ -267,15 +229,13 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
- load();
}
@Override
public void onResume() {
super.onResume();
- if(itemsLoaded) {
+ if (itemsLoaded) {
progbarLoading.setVisibility(View.GONE);
updateAppearance();
}
@@ -284,7 +244,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
}
@@ -300,68 +259,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
}
- @Override
- public boolean onSwipeLeftToRight() {
- return swipeFeedItem(-1);
- }
-
- @Override
- public boolean onSwipeRightToLeft() {
- return swipeFeedItem(+1);
- }
-
- private boolean swipeFeedItem(int position) {
- Log.d(TAG, String.format("onSwipe() shift: %s", position));
- feedItemPos = (feedItemPos + position) % feedItems.length;
- if (feedItemPos < 0) {
- feedItemPos = feedItems.length - 1;
- }
- load();
- return true;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded() || item == null) {
- return;
- }
- super.onCreateOptionsMenu(menu, inflater);
- if (Flavors.FLAVOR == Flavors.PLAY) {
- ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
- }
- inflater.inflate(R.menu.feeditem_options, menu);
- popupMenu = menu;
- if (item.hasMedia()) {
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item);
- } else {
- // these are already available via button1 and button2
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item,
- R.id.mark_read_item, R.id.visit_website_item);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem menuItem) {
- switch(menuItem.getItemId()) {
- case R.id.open_podcast:
- openPodcast();
- return true;
- default:
- return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item);
- }
- }
-
- private final FeedItemMenuHandler.MenuInterface popupMenuInterface = new FeedItemMenuHandler.MenuInterface() {
- @Override
- public void setItemVisibility(int id, boolean visible) {
- MenuItem item = popupMenu.findItem(id);
- if (item != null) {
- item.setVisible(visible);
- }
- }
- };
-
-
private void onFragmentLoaded() {
if (webviewData != null) {
webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData, "text/html", "utf-8", "about:blank");
@@ -374,7 +271,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
Log.d(TAG, "updateAppearance item is null");
return;
}
- getActivity().supportInvalidateOptionsMenu();
txtvPodcast.setText(item.getFeed().getTitle());
txtvTitle.setText(item.getTitle());
@@ -384,7 +280,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
Glide.with(getActivity())
- .load(item.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(item))
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
@@ -405,53 +301,59 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
FeedMedia media = item.getMedia();
- String butAction1Icon = null;
- int butAction1Text = 0;
- String butAction2Icon = null;
- int butAction2Text = 0;
+ @AttrRes int butAction1Icon = 0;
+ @StringRes int butAction1Text = 0;
+ @AttrRes int butAction2Icon = 0;
+ @StringRes int butAction2Text = 0;
if (media == null) {
if (!item.isPlayed()) {
- butAction1Icon = "{fa-check 24sp}";
+ butAction1Icon = R.attr.navigation_accept;
butAction1Text = R.string.mark_read_label;
}
if (item.getLink() != null) {
- butAction2Icon = "{md-web 24sp}";
+ butAction2Icon = R.attr.location_web_site;
butAction2Text = R.string.visit_website_label;
}
} else {
- if(media.getDuration() > 0) {
+ if (media.getDuration() > 0) {
txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
}
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
if (!media.isDownloaded()) {
- butAction2Icon = "{md-settings-input-antenna 24sp}";
+ butAction2Icon = R.attr.action_stream;
butAction2Text = R.string.stream_label;
} else {
- butAction2Icon = "{md-delete 24sp}";
+ butAction2Icon = R.attr.content_discard;
butAction2Text = R.string.delete_label;
}
if (isDownloading) {
- butAction1Icon = "{md-cancel 24sp}";
+ butAction1Icon = R.attr.navigation_cancel;
butAction1Text = R.string.cancel_label;
} else if (media.isDownloaded()) {
- butAction1Icon = "{md-play-arrow 24sp}";
+ butAction1Icon = R.attr.av_play;
butAction1Text = R.string.play_label;
} else {
- butAction1Icon = "{md-file-download 24sp}";
+ butAction1Icon = R.attr.av_download;
butAction1Text = R.string.download_label;
}
}
- if(butAction1Icon != null && butAction1Text != 0) {
- butAction1.setText(butAction1Icon +"\u0020\u0020" + getActivity().getString(butAction1Text));
- Iconify.addIcons(butAction1);
+ if (butAction1Icon != 0 && butAction1Text != 0) {
+ butAction1.setText(butAction1Text);
+ butAction1.setTransformationMethod(null);
+ TypedValue typedValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(butAction1Icon, typedValue, true);
+ butAction1.setCompoundDrawablesWithIntrinsicBounds(typedValue.resourceId, 0, 0, 0);
butAction1.setVisibility(View.VISIBLE);
} else {
butAction1.setVisibility(View.INVISIBLE);
}
- if(butAction2Icon != null && butAction2Text != 0) {
- butAction2.setText(butAction2Icon +"\u0020\u0020" + getActivity().getString(butAction2Text));
- Iconify.addIcons(butAction2);
+ if (butAction2Icon != 0 && butAction2Text != 0) {
+ butAction2.setText(butAction2Text);
+ butAction2.setTransformationMethod(null);
+ TypedValue typedValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(butAction2Icon, typedValue, true);
+ butAction2.setCompoundDrawablesWithIntrinsicBounds(typedValue.resourceId, 0, 0, 0);
butAction2.setVisibility(View.VISIBLE);
} else {
butAction2.setVisibility(View.INVISIBLE);
@@ -533,8 +435,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- for(FeedItem item : event.items) {
- if(feedItems[feedItemPos] == item.getId()) {
+ for (FeedItem item : event.items) {
+ if (this.item.getId() == item.getId()) {
load();
return;
}
@@ -557,18 +459,13 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
}
-
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- load();
- }
- }
- };
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ load();
+ }
private void load() {
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
progbarLoading.setVisibility(View.VISIBLE);
@@ -585,7 +482,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Nullable
private FeedItem loadInBackground() {
- FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]);
+ FeedItem feedItem = DBReader.getFeedItem(itemId);
Context context = getContext();
if (feedItem != null && context != null) {
Timeline t = new Timeline(context, feedItem);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
new file mode 100644
index 000000000..fdac649d1
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
@@ -0,0 +1,196 @@
+package de.danoeh.antennapod.fragment;
+
+import android.os.Bundle;
+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 androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.view.ViewCompat;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.CastEnabledActivity;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.util.Flavors;
+import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Displays information about a list of FeedItems.
+ */
+public class ItemPagerFragment extends Fragment {
+ private static final String ARG_FEEDITEMS = "feeditems";
+ private static final String ARG_FEEDITEM_POS = "feeditem_pos";
+
+ /**
+ * Creates a new instance of an ItemPagerFragment.
+ *
+ * @param feeditem The ID of the FeedItem that should be displayed.
+ * @return The ItemFragment instance
+ */
+ public static ItemPagerFragment newInstance(long feeditem) {
+ return newInstance(new long[] { feeditem }, 0);
+ }
+
+ /**
+ * Creates a new instance of an ItemPagerFragment.
+ *
+ * @param feeditems The IDs of the FeedItems that belong to the same list
+ * @param feedItemPos The position of the FeedItem that is currently shown
+ * @return The ItemFragment instance
+ */
+ public static ItemPagerFragment newInstance(long[] feeditems, int feedItemPos) {
+ if (feeditems.length <= feedItemPos) {
+ throw new IllegalArgumentException("Trying to show a feed item that is out of the list");
+ }
+ ItemPagerFragment fragment = new ItemPagerFragment();
+ Bundle args = new Bundle();
+ args.putLongArray(ARG_FEEDITEMS, feeditems);
+ args.putInt(ARG_FEEDITEM_POS, feedItemPos);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ private long[] feedItems;
+ private int feedItemPos;
+ private FeedItem item;
+ private Disposable disposable;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+
+ feedItems = getArguments().getLongArray(ARG_FEEDITEMS);
+ feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS);
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ View layout = inflater.inflate(R.layout.feeditem_pager_fragment, container, false);
+
+ ViewPager pager = layout.findViewById(R.id.pager);
+ // FragmentStatePagerAdapter documentation:
+ // > When using FragmentStatePagerAdapter the host ViewPager must have a valid ID set.
+ // When opening multiple ItemPagerFragments by clicking "item" -> "visit podcast" -> "item" -> etc,
+ // the ID is no longer unique and FragmentStatePagerAdapter does not display any pages.
+ int newId = ViewCompat.generateViewId();
+ pager.setId(newId);
+ pager.setAdapter(new ItemPagerAdapter());
+ pager.setCurrentItem(feedItemPos);
+ loadItem(feedItems[feedItemPos]);
+ pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ loadItem(feedItems[position]);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+
+ return layout;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ private void loadItem(long itemId) {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+
+ disposable = Observable.fromCallable(() -> DBReader.getFeedItem(itemId))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ item = result;
+ getActivity().invalidateOptionsMenu();
+ }, Throwable::printStackTrace);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if (!isAdded() || item == null) {
+ return;
+ }
+ super.onCreateOptionsMenu(menu, inflater);
+ if (Flavors.FLAVOR == Flavors.PLAY) {
+ ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ }
+ inflater.inflate(R.menu.feeditem_options, menu);
+
+ FeedItemMenuHandler.MenuInterface popupMenuInterface = (id, visible) -> {
+ MenuItem item = menu.findItem(id);
+ if (item != null) {
+ item.setVisible(visible);
+ }
+ };
+
+ if (item.hasMedia()) {
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item);
+ } else {
+ // these are already available via button1 and button2
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item,
+ R.id.mark_read_item, R.id.visit_website_item);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ switch (menuItem.getItemId()) {
+ case R.id.open_podcast:
+ openPodcast();
+ return true;
+ default:
+ return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item);
+ }
+ }
+
+ private void openPodcast() {
+ Fragment fragment = FeedItemlistFragment.newInstance(item.getFeedId());
+ ((MainActivity) getActivity()).loadChildFragment(fragment);
+ }
+
+ private class ItemPagerAdapter extends FragmentStatePagerAdapter {
+
+ ItemPagerAdapter() {
+ super(getFragmentManager(), BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+ }
+
+ @NonNull
+ @Override
+ public Fragment getItem(int position) {
+ return ItemFragment.newInstance(feedItems[position]);
+ }
+
+ @Override
+ public int getCount() {
+ return feedItems.length;
+ }
+ }
+}
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 673b58901..4b1544e47 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Bundle;
+import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.core.view.MenuItemCompat;
import androidx.appcompat.widget.SearchView;
@@ -18,8 +19,6 @@ import android.widget.GridView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import de.danoeh.antennapod.discovery.ItunesPodcastSearcher;
import de.danoeh.antennapod.discovery.ItunesTopListLoader;
import de.danoeh.antennapod.discovery.PodcastSearchResult;
@@ -120,9 +119,9 @@ public class ItunesSearchFragment extends Fragment {
progressBar.setVisibility(View.GONE);
gridView.setVisibility(View.VISIBLE);
String prefix = getString(R.string.error_msg_prefix);
- new MaterialDialog.Builder(getActivity())
- .content(prefix + " " + error.getMessage())
- .neutralText(android.R.string.ok)
+ new AlertDialog.Builder(getActivity())
+ .setMessage(prefix + " " + error.getMessage())
+ .setPositiveButton(android.R.string.ok, null)
.show();
});
});
@@ -151,7 +150,6 @@ public class ItunesSearchFragment extends Fragment {
inflater.inflate(R.menu.itunes_search, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_itunes_label));
sv.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() {
@Override
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 5dbb84bc7..2bfdd040b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -54,7 +54,8 @@ public class NewEpisodesFragment extends EpisodesListFragment {
emptyView.setTitle(R.string.no_new_episodes_head_label);
emptyView.setMessage(R.string.no_new_episodes_label);
- ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
+ ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0,
+ ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
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 f9fca87fc..a97e3dae8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -12,6 +12,8 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
+import de.danoeh.antennapod.core.event.PlaybackHistoryEvent;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -24,7 +26,6 @@ import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-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.service.download.Downloader;
@@ -39,12 +40,8 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public class PlaybackHistoryFragment extends ListFragment {
-
public static final String TAG = "PlaybackHistoryFragment";
- private static final int EVENTS = EventDistributor.PLAYBACK_HISTORY_UPDATE |
- EventDistributor.PLAYER_STATUS_UPDATE;
-
private List<FeedItem> playbackHistory;
private FeedItemlistAdapter adapter;
private List<Downloader> downloaderList;
@@ -83,7 +80,6 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
loadItems();
}
@@ -92,7 +88,6 @@ public class PlaybackHistoryFragment extends ListFragment {
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
- EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
@@ -111,7 +106,7 @@ public class PlaybackHistoryFragment extends ListFragment {
super.onListItemClick(l, v, position, id);
position -= l.getHeaderViewsCount();
long[] ids = FeedItemUtil.getIds(playbackHistory);
- ((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
+ ((MainActivity) getActivity()).loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
@Override
@@ -166,16 +161,17 @@ public class PlaybackHistoryFragment extends ListFragment {
}
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onHistoryUpdated(PlaybackHistoryEvent event) {
+ loadItems();
+ getActivity().supportInvalidateOptionsMenu();
+ }
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- loadItems();
- getActivity().supportInvalidateOptionsMenu();
- }
- }
- };
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlayerStatusChanged(PlayerStatusEvent event) {
+ loadItems();
+ getActivity().supportInvalidateOptionsMenu();
+ }
private void onFragmentLoaded() {
adapter.notifyDataSetChanged();
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 b550669f3..b36ce6145 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -4,15 +4,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
-import com.google.android.material.snackbar.Snackbar;
-import androidx.fragment.app.Fragment;
-import androidx.core.view.MenuItemCompat;
-import androidx.appcompat.app.AlertDialog;
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.appcompat.widget.SearchView;
-import androidx.recyclerview.widget.SimpleItemAnimator;
-import androidx.recyclerview.widget.ItemTouchHelper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -24,9 +15,18 @@ import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.SearchView;
+import androidx.core.view.MenuItemCompat;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.SimpleItemAnimator;
+
+import com.google.android.material.snackbar.Snackbar;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -40,11 +40,13 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
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.preferences.PlaybackSpeedHelper;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
@@ -54,7 +56,6 @@ 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.SortOrder;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
@@ -73,13 +74,8 @@ import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_REM
* Shows all items in the queue
*/
public class QueueFragment extends Fragment {
-
public static final String TAG = "QueueFragment";
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.UNREAD_ITEMS_UPDATE | // sent when playback position is reset
- EventDistributor.PLAYER_STATUS_UPDATE;
-
private TextView infoBar;
private RecyclerView recyclerView;
private QueueRecyclerAdapter recyclerAdapter;
@@ -117,7 +113,6 @@ public class QueueFragment extends Fragment {
onFragmentLoaded(true);
}
loadItems(true);
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
}
@@ -130,9 +125,8 @@ public class QueueFragment extends Fragment {
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
}
@@ -218,7 +212,31 @@ public class QueueFragment extends Fragment {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(PlaybackPositionEvent event) {
if (recyclerAdapter != null) {
- recyclerAdapter.notifyCurrentlyPlayingItemChanged(event);
+ for (int i = 0; i < recyclerAdapter.getItemCount(); i++) {
+ QueueRecyclerAdapter.ViewHolder holder = (QueueRecyclerAdapter.ViewHolder)
+ recyclerView.findViewHolderForAdapterPosition(i);
+ if (holder != null && holder.isCurrentlyPlayingItem()) {
+ holder.notifyPlaybackPositionUpdated(event);
+ break;
+ }
+ }
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlayerStatusChanged(PlayerStatusEvent event) {
+ loadItems(false);
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ getActivity().supportInvalidateOptionsMenu();
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ // Sent when playback position is reset
+ loadItems(false);
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ getActivity().supportInvalidateOptionsMenu();
}
}
@@ -270,7 +288,6 @@ public class QueueFragment extends Fragment {
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_hint));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
@@ -331,7 +348,7 @@ public class QueueFragment extends Fragment {
conDialog.createNewDialog().show();
return true;
case R.id.episode_actions:
- ((MainActivity) requireActivity()) .loadChildFragment(
+ ((MainActivity) requireActivity()).loadChildFragment(
EpisodesApplyActionFragment.newInstance(queue, ACTION_DELETE | ACTION_REMOVE_FROM_QUEUE));
return true;
case R.id.queue_sort_episode_title_asc:
@@ -373,7 +390,7 @@ public class QueueFragment extends Fragment {
UserPreferences.setQueueKeepSorted(keepSortedNew);
if (keepSortedNew) {
SortOrder sortOrder = UserPreferences.getQueueKeepSortedOrder();
- QueueSorter.sort(sortOrder, true);
+ DBWriter.reorderQueue(sortOrder, true);
if (recyclerAdapter != null) {
recyclerAdapter.setLocked(true);
}
@@ -439,7 +456,7 @@ public class QueueFragment extends Fragment {
*/
private void setSortOrder(SortOrder sortOrder) {
UserPreferences.setQueueKeepSortedOrder(sortOrder);
- QueueSorter.sort(sortOrder, true);
+ DBWriter.reorderQueue(sortOrder, true);
}
@Override
@@ -492,7 +509,8 @@ public class QueueFragment extends Fragment {
registerForContextMenu(recyclerView);
itemTouchHelper = new ItemTouchHelper(
- new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) {
+ new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
+ ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
// Position tracking whilst dragging
int dragFrom = -1;
@@ -634,14 +652,16 @@ public class QueueFragment extends Fragment {
private void refreshInfoBar() {
String info = queue.size() + getString(R.string.episodes_suffix);
- if(queue.size() > 0) {
+ if (queue.size() > 0) {
long timeLeft = 0;
- for(FeedItem item : queue) {
- float playbackSpeed = PlaybackSpeedHelper.getCurrentPlaybackSpeed(item.getMedia());
- if(item.getMedia() != null) {
- timeLeft +=
- (long) ((item.getMedia().getDuration() - item.getMedia().getPosition())
- / playbackSpeed);
+ for (FeedItem item : queue) {
+ float playbackSpeed = 1;
+ if (UserPreferences.timeRespectsSpeed()) {
+ playbackSpeed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(item.getMedia());
+ }
+ if (item.getMedia() != null) {
+ long itemTimeLeft = item.getMedia().getDuration() - item.getMedia().getPosition();
+ timeLeft += itemTimeLeft / playbackSpeed;
}
}
info += " • ";
@@ -711,19 +731,6 @@ public class QueueFragment extends Fragment {
}
};
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- Log.d(TAG, "arg: " + arg);
- loadItems(false);
- if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
- getActivity().supportInvalidateOptionsMenu();
- }
- }
- }
- };
-
private void loadItems(final boolean restoreScrollPosition) {
Log.d(TAG, "loadItems()");
if(disposable != null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
index 226209740..7e217cde4 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Bundle;
+import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
@@ -11,7 +12,6 @@ import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.afollestad.materialdialogs.MaterialDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
@@ -110,9 +110,9 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView.
Log.e(TAG, Log.getStackTraceString(error));
view.setAlpha(1f);
String prefix = getString(R.string.error_msg_prefix);
- new MaterialDialog.Builder(getActivity())
- .content(prefix + " " + error.getMessage())
- .neutralText(android.R.string.ok)
+ new AlertDialog.Builder(getActivity())
+ .setMessage(prefix + " " + error.getMessage())
+ .setPositiveButton(android.R.string.ok, null)
.show();
});
}
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 528fa7c32..7e8823c27 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
@@ -25,6 +25,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.view.EmptyViewHandler;
+import org.greenrobot.eventbus.ThreadMode;
/**
* Displays all running downloads and provides actions to cancel them
@@ -75,7 +76,7 @@ public class RunningDownloadsFragment extends ListFragment {
setListAdapter(null);
}
- @Subscribe(sticky = true)
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(DownloadEvent event) {
Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
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 7f3c60e5d..d124d6aa2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -20,7 +20,7 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.SearchlistAdapter;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -30,6 +30,8 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
/**
* Performs a search operation on all feeds or one specific feed and displays the search result.
@@ -76,7 +78,6 @@ public class SearchFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
search();
}
@@ -86,7 +87,6 @@ public class SearchFragment extends ListFragment {
if(disposable != null) {
disposable.dispose();
}
- EventDistributor.getInstance().unregister(contentUpdate);
}
@Override
@@ -103,6 +103,13 @@ public class SearchFragment extends ListFragment {
searchAdapter = new SearchlistAdapter(getActivity(), itemAccess);
setListAdapter(searchAdapter);
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ EventBus.getDefault().unregister(this);
}
@Override
@@ -145,15 +152,10 @@ public class SearchFragment extends ListFragment {
MenuItemCompat.setActionView(item, sv);
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & (EventDistributor.UNREAD_ITEMS_UPDATE
- | EventDistributor.DOWNLOAD_HANDLED)) != 0) {
- search();
- }
- }
- };
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ search();
+ }
private void onSearchResults(List<SearchResult> results) {
searchResults = results;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
index 253c99c4e..af3649ed0 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -28,7 +28,8 @@ import de.danoeh.antennapod.adapter.SubscriptionsAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
@@ -41,6 +42,7 @@ import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -55,9 +57,6 @@ import org.greenrobot.eventbus.ThreadMode;
public class SubscriptionFragment extends Fragment {
public static final String TAG = "SubscriptionFragment";
-
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE
- | EventDistributor.UNREAD_ITEMS_UPDATE;
private static final String PREFS = "SubscriptionFragment";
private static final String PREF_NUM_COLUMNS = "columns";
@@ -65,6 +64,7 @@ public class SubscriptionFragment extends Fragment {
private DBReader.NavDrawerData navDrawerData;
private SubscriptionsAdapter subscriptionAdapter;
private FloatingActionButton subscriptionAddButton;
+ private EmptyViewHandler emptyView;
private int mPosition = -1;
private boolean isUpdatingFeeds = false;
@@ -138,12 +138,22 @@ public class SubscriptionFragment extends Fragment {
getActivity().invalidateOptionsMenu();
}
+ private void setupEmptyView() {
+ emptyView = new EmptyViewHandler(getContext());
+ emptyView.setIcon(R.attr.ic_folder);
+ emptyView.setTitle(R.string.no_subscriptions_head_label);
+ emptyView.setMessage(R.string.no_subscriptions_label);
+ emptyView.attachToListView(subscriptionGridLayout);
+ }
+
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
+
subscriptionAdapter = new SubscriptionsAdapter((MainActivity) getActivity(), itemAccess);
subscriptionGridLayout.setAdapter(subscriptionAdapter);
subscriptionGridLayout.setOnItemClickListener(subscriptionAdapter);
+ setupEmptyView();
subscriptionAddButton.setOnClickListener(view -> {
if (getActivity() instanceof MainActivity) {
@@ -159,7 +169,6 @@ public class SubscriptionFragment extends Fragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
loadSubscriptions();
}
@@ -167,7 +176,6 @@ public class SubscriptionFragment extends Fragment {
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if(disposable != null) {
disposable.dispose();
@@ -178,12 +186,14 @@ public class SubscriptionFragment extends Fragment {
if(disposable != null) {
disposable.dispose();
}
+ emptyView.hide();
disposable = Observable.fromCallable(DBReader::getNavDrawerData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
navDrawerData = result;
subscriptionAdapter.notifyDataSetChanged();
+ emptyView.updateVisibility();
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
@@ -294,15 +304,15 @@ public class SubscriptionFragment extends Fragment {
dialog.createNewDialog().show();
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EVENTS & arg) != 0) {
- Log.d(TAG, "Received contentUpdate Intent.");
- loadSubscriptions();
- }
- }
- };
+ @Subscribe
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ loadSubscriptions();
+ }
+
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ loadSubscriptions();
+ }
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(DownloadEvent event) {
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 1d6debbfe..4baa74df6 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
@@ -54,7 +54,6 @@ public abstract class PodcastListFragment extends Fragment {
inflater.inflate(R.menu.gpodder_podcasts, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.gpodnet_search_hint));
sv.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
index 60fc1f446..ffe69aa9a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
@@ -50,7 +50,6 @@ public class SearchListFragment extends PodcastListFragment {
// parent already inflated menu
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.gpodnet_search_hint));
sv.setQuery(query, false);
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
index b5a95bc33..cde8fb3df 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
@@ -40,7 +40,6 @@ public class TagListFragment extends ListFragment {
inflater.inflate(R.menu.gpodder_podcasts, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.gpodnet_search_hint));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java
new file mode 100644
index 000000000..62a5eb306
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java
@@ -0,0 +1,65 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.ListFragment;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.adapter.SimpleIconListAdapter;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+public class AboutDevelopersFragment extends ListFragment {
+ private Disposable developersLoader;
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setDivider(null);
+ getListView().setSelector(android.R.color.transparent);
+
+ developersLoader = Single.create((SingleOnSubscribe<ArrayList<SimpleIconListAdapter.ListItem>>) emitter -> {
+ ArrayList<SimpleIconListAdapter.ListItem> developers = new ArrayList<>();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ getContext().getAssets().open("developers.csv")));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] info = line.split(";");
+ developers.add(new SimpleIconListAdapter.ListItem(info[0], info[2],
+ "https://avatars2.githubusercontent.com/u/" + info[1] + "?s=60&v=4"));
+ }
+ emitter.onSuccess(developers);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ developers -> setListAdapter(new SimpleIconListAdapter<>(getContext(), developers)),
+ error -> Toast.makeText(getContext(), "Error while loading developers", Toast.LENGTH_LONG).show()
+ );
+
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (developersLoader != null) {
+ developersLoader.dispose();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.developers);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java
new file mode 100644
index 000000000..0fa7bd4bb
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java
@@ -0,0 +1,56 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Bundle;
+import androidx.preference.PreferenceFragmentCompat;
+import com.google.android.material.snackbar.Snackbar;
+import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.core.util.IntentUtils;
+
+public class AboutFragment extends PreferenceFragmentCompat {
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_about);
+
+ findPreference("about_version").setSummary(String.format(
+ "%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.COMMIT_HASH));
+ findPreference("about_version").setOnPreferenceClickListener((preference) -> {
+ ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText(getString(R.string.bug_report_title),
+ findPreference("about_version").getSummary());
+ clipboard.setPrimaryClip(clip);
+ Snackbar.make(getView(), R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show();
+ return true;
+ });
+ findPreference("about_developers").setOnPreferenceClickListener((preference) -> {
+ getFragmentManager().beginTransaction().replace(R.id.content, new AboutDevelopersFragment())
+ .addToBackStack(getString(R.string.developers)).commit();
+ return true;
+ });
+ findPreference("about_translators").setOnPreferenceClickListener((preference) -> {
+ getFragmentManager().beginTransaction().replace(R.id.content, new AboutTranslatorsFragment())
+ .addToBackStack(getString(R.string.translators)).commit();
+ return true;
+ });
+ findPreference("about_privacy_policy").setOnPreferenceClickListener((preference) -> {
+ IntentUtils.openInBrowser(getContext(), "https://antennapod.org/privacy.html");
+ return true;
+ });
+ findPreference("about_licenses").setOnPreferenceClickListener((preference) -> {
+ getFragmentManager().beginTransaction().replace(R.id.content, new AboutLicensesFragment())
+ .addToBackStack(getString(R.string.translators)).commit();
+ return true;
+ });
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.about_pref);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java
new file mode 100644
index 000000000..536d11e01
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java
@@ -0,0 +1,126 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.ListFragment;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.adapter.SimpleIconListAdapter;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+public class AboutLicensesFragment extends ListFragment {
+ private Disposable licensesLoader;
+ private final ArrayList<LicenseItem> licenses = new ArrayList<>();
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setDivider(null);
+
+ licensesLoader = Single.create((SingleOnSubscribe<ArrayList<LicenseItem>>) emitter -> {
+ licenses.clear();
+ InputStream stream = getContext().getAssets().open("licenses.xml");
+ DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ NodeList libraryList = docBuilder.parse(stream).getElementsByTagName("library");
+ for (int i = 0; i < libraryList.getLength(); i++) {
+ NamedNodeMap lib = libraryList.item(i).getAttributes();
+ licenses.add(new LicenseItem(
+ lib.getNamedItem("name").getTextContent(),
+ String.format("By %s, %s license",
+ lib.getNamedItem("author").getTextContent(),
+ lib.getNamedItem("license").getTextContent()),
+ null,
+ lib.getNamedItem("website").getTextContent(),
+ lib.getNamedItem("licenseText").getTextContent()));
+ }
+ emitter.onSuccess(licenses);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ developers -> setListAdapter(new SimpleIconListAdapter<LicenseItem>(getContext(), developers)),
+ error -> Toast.makeText(getContext(), "Error while loading licenses", Toast.LENGTH_LONG).show()
+ );
+
+ }
+
+ private static class LicenseItem extends SimpleIconListAdapter.ListItem {
+ final String licenseUrl;
+ final String licenseTextFile;
+
+ LicenseItem(String title, String subtitle, String imageUrl, String licenseUrl, String licenseTextFile) {
+ super(title, subtitle, imageUrl);
+ this.licenseUrl = licenseUrl;
+ this.licenseTextFile = licenseTextFile;
+ }
+ }
+
+ @Override
+ public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+
+ LicenseItem item = licenses.get(position);
+ CharSequence[] items = {"View website", "View license"};
+ new AlertDialog.Builder(getContext())
+ .setTitle(item.title)
+ .setItems(items, (dialog, which) -> {
+ if (which == 0) {
+ IntentUtils.openInBrowser(getContext(), item.licenseUrl);
+ } else if (which == 1) {
+ showLicenseText(item.licenseTextFile);
+ }
+ }).show();
+ }
+
+ private void showLicenseText(String licenseTextFile) {
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ getContext().getAssets().open(licenseTextFile)));
+ StringBuilder licenseText = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ licenseText.append(line).append("\n");
+ }
+
+ new AlertDialog.Builder(getContext())
+ .setMessage(licenseText)
+ .show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (licensesLoader != null) {
+ licensesLoader.dispose();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.licenses);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java
new file mode 100644
index 000000000..914dbb9a2
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java
@@ -0,0 +1,64 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.ListFragment;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.adapter.SimpleIconListAdapter;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+public class AboutTranslatorsFragment extends ListFragment {
+ private Disposable translatorsLoader;
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setDivider(null);
+ getListView().setSelector(android.R.color.transparent);
+
+ translatorsLoader = Single.create((SingleOnSubscribe<ArrayList<SimpleIconListAdapter.ListItem>>) emitter -> {
+ ArrayList<SimpleIconListAdapter.ListItem> translators = new ArrayList<>();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ getContext().getAssets().open("translators.csv")));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] info = line.split(";");
+ translators.add(new SimpleIconListAdapter.ListItem(info[0], info[1], null));
+ }
+ emitter.onSuccess(translators);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ translators -> setListAdapter(new SimpleIconListAdapter<>(getContext(), translators)),
+ error -> Toast.makeText(getContext(), "Error while loading translators", Toast.LENGTH_LONG).show()
+ );
+
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (translatorsLoader != null) {
+ translatorsLoader.dispose();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.translators);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
index 121b7fef8..fa17fed0a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
@@ -50,7 +50,7 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
@Override
public void onStart() {
super.onStart();
- ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.auto_download_label);
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.pref_automatic_download_title);
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
index 00e69f1db..5fd38d663 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
@@ -7,7 +7,6 @@ import androidx.preference.PreferenceFragmentCompat;
import com.bytehamster.lib.preferencesearch.SearchConfiguration;
import com.bytehamster.lib.preferencesearch.SearchPreference;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.AboutActivity;
import de.danoeh.antennapod.activity.BugReportActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.util.IntentUtils;
@@ -63,7 +62,8 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
findPreference(PREF_ABOUT).setOnPreferenceClickListener(
preference -> {
- startActivity(new Intent(getActivity(), AboutActivity.class));
+ getFragmentManager().beginTransaction().replace(R.id.content, new AboutFragment())
+ .addToBackStack(getString(R.string.about_pref)).commit();
return true;
}
);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
index 440660942..1ca8f63aa 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
@@ -5,11 +5,8 @@ import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import android.text.format.DateFormat;
-import com.afollestad.materialdialogs.MaterialDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -65,7 +62,7 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
// validate and set correct value: number of downloads between 1 and 50 (inclusive)
findPreference(PREF_PROXY).setOnPreferenceClickListener(preference -> {
ProxyDialog dialog = new ProxyDialog(getActivity());
- dialog.createDialog().show();
+ dialog.show();
return true;
});
}
@@ -107,13 +104,10 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
private void showUpdateIntervalTimePreferencesDialog() {
final Context context = getActivity();
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
- builder.title(R.string.pref_autoUpdateIntervallOrTime_title);
- builder.content(R.string.pref_autoUpdateIntervallOrTime_message);
- builder.positiveText(R.string.pref_autoUpdateIntervallOrTime_Interval);
- builder.negativeText(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay);
- builder.neutralText(R.string.pref_autoUpdateIntervallOrTime_Disable);
- builder.onPositive((dialog, which) -> {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.pref_autoUpdateIntervallOrTime_title);
+ builder.setMessage(R.string.pref_autoUpdateIntervallOrTime_message);
+ builder.setPositiveButton(R.string.pref_autoUpdateIntervallOrTime_Interval, (dialog, which) -> {
AlertDialog.Builder builder1 = new AlertDialog.Builder(context);
builder1.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
@@ -133,8 +127,9 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
builder1.setNegativeButton(context.getString(R.string.cancel_label), null);
builder1.show();
});
- builder.onNegative((dialog, which) -> {
- int hourOfDay = 7, minute = 0;
+ builder.setNegativeButton(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay, (dialog, which) -> {
+ int hourOfDay = 7;
+ int minute = 0;
int[] updateTime = UserPreferences.getUpdateTimeOfDay();
if (updateTime.length == 2) {
hourOfDay = updateTime[0];
@@ -151,7 +146,7 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
timePickerDialog.show();
});
- builder.onNeutral((dialog, which) -> {
+ builder.setNeutralButton(R.string.pref_autoUpdateIntervallOrTime_Disable, (dialog, which) -> {
UserPreferences.disableAutoUpdate();
setUpdateIntervalText();
});
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
index 1795dfc29..b82bba89b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
@@ -4,8 +4,15 @@ import android.app.Activity;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.collection.ArrayMap;
import androidx.preference.ListPreference;
+import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
+
+import java.util.Map;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MediaplayerActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
@@ -34,12 +41,6 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.playback_pref);
}
- @Override
- public void onResume() {
- super.onResume();
- checkSonicItemVisibility();
- }
-
private void setupPlaybackScreen() {
final Activity activity = getActivity();
@@ -63,6 +64,43 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
behaviour.setEntries(R.array.video_background_behavior_options_without_pip);
behaviour.setEntryValues(R.array.video_background_behavior_values_without_pip);
}
+
+ buildEnqueueLocationPreference();
+ }
+
+ private void buildEnqueueLocationPreference() {
+ final Resources res = requireActivity().getResources();
+ final Map<String, String> options = new ArrayMap<>();
+ {
+ String[] keys = res.getStringArray(R.array.enqueue_location_values);
+ String[] values = res.getStringArray(R.array.enqueue_location_options);
+ for (int i = 0; i < keys.length; i++) {
+ options.put(keys[i], values[i]);
+ }
+ }
+
+ ListPreference pref = requirePreference(UserPreferences.PREF_ENQUEUE_LOCATION);
+ pref.setSummary(res.getString(R.string.pref_enqueue_location_sum, options.get(pref.getValue())));
+
+ pref.setOnPreferenceChangeListener((preference, newValue) -> {
+ if (!(newValue instanceof String)) {
+ return false;
+ }
+ String newValStr = (String)newValue;
+ pref.setSummary(res.getString(R.string.pref_enqueue_location_sum, options.get(newValStr)));
+ return true;
+ });
+ }
+
+ @NonNull
+ private <T extends Preference> T requirePreference(@NonNull CharSequence key) {
+ // Possibly put it to a common method in abstract base class
+ T result = findPreference(key);
+ if (result == null) {
+ throw new IllegalArgumentException("Preference with key '" + key + "' is not found");
+
+ }
+ return result;
}
private void buildSmartMarkAsPlayedPreference() {
@@ -86,14 +124,4 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
}
pref.setEntries(entries);
}
-
-
-
- private void checkSonicItemVisibility() {
- if (Build.VERSION.SDK_INT < 16) {
- ListPreference p = (ListPreference) findPreference(UserPreferences.PREF_MEDIA_PLAYER);
- p.setEntries(R.array.media_player_options_no_sonic);
- p.setEntryValues(R.array.media_player_values_no_sonic);
- }
- }
}
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 60eaf14a5..2f3459beb 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -13,6 +13,7 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -200,6 +201,10 @@ public class FeedItemMenuHandler {
break;
case R.id.reset_position:
selectedItem.getMedia().setPosition(0);
+ if (PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == selectedItem.getMedia().getId()) {
+ PlaybackPreferences.writeNoMediaPlaying();
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE);
+ }
DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, true);
break;
case R.id.activate_auto_download:
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 5d012168e..e32deba27 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -2,12 +2,15 @@ package de.danoeh.antennapod.menuhandler;
import android.content.Context;
import android.content.DialogInterface;
-import androidx.appcompat.app.AlertDialog;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import androidx.annotation.NonNull;
+
+import org.apache.commons.lang3.StringUtils;
+
import java.util.Set;
import de.danoeh.antennapod.R;
@@ -18,7 +21,9 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
+import de.danoeh.antennapod.core.util.SortOrder;
import de.danoeh.antennapod.dialog.FilterDialog;
+import de.danoeh.antennapod.dialog.IntraFeedSortDialog;
/**
* Handles interactions with the FeedItemMenu.
@@ -42,6 +47,10 @@ public class FeedMenuHandler {
Log.d(TAG, "Preparing options menu");
menu.findItem(R.id.refresh_complete_item).setVisible(selectedFeed.isPaged());
+ if (StringUtils.isBlank(selectedFeed.getLink())) {
+ menu.findItem(R.id.visit_website_item).setVisible(false);
+ menu.findItem(R.id.share_link_item).setVisible(false);
+ }
return true;
}
@@ -60,6 +69,9 @@ public class FeedMenuHandler {
case R.id.refresh_complete_item:
DBTasks.forceRefreshCompleteFeed(context, selectedFeed);
break;
+ case R.id.sort_items:
+ showSortDialog(context, selectedFeed);
+ break;
case R.id.filter_items:
showFilterDialog(context, selectedFeed);
break;
@@ -103,4 +115,17 @@ public class FeedMenuHandler {
filterDialog.openDialog();
}
+
+
+ private static void showSortDialog(Context context, Feed selectedFeed) {
+ IntraFeedSortDialog sortDialog = new IntraFeedSortDialog(context, selectedFeed.getSortOrder()) {
+ @Override
+ protected void updateSort(@NonNull SortOrder sortOrder) {
+ selectedFeed.setSortOrder(sortOrder);
+ DBWriter.setFeedItemSortOrder(selectedFeed.getId(), sortOrder);
+ }
+ };
+ sortDialog.openDialog();
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
index 4a57726b1..64eb72ee3 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
@@ -2,14 +2,8 @@ package de.danoeh.antennapod.menuhandler;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.os.Build;
-import androidx.appcompat.widget.SearchView;
import android.view.Menu;
import android.view.MenuItem;
-import android.widget.EditText;
-
-import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
/**
@@ -17,18 +11,6 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
*/
public class MenuItemUtils extends de.danoeh.antennapod.core.menuhandler.MenuItemUtils {
- public static void adjustTextColor(Context context, SearchView sv) {
- if(Build.VERSION.SDK_INT < 14) {
- EditText searchEditText = sv.findViewById(R.id.search_src_text);
- if (UserPreferences.getTheme() == de.danoeh.antennapod.R.style.Theme_AntennaPod_Dark
- || UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack) {
- searchEditText.setTextColor(Color.WHITE);
- } else {
- searchEditText.setTextColor(Color.BLACK);
- }
- }
- }
-
@SuppressWarnings("ResourceType")
public static void refreshLockItem(Context context, Menu menu) {
final MenuItem queueLock = menu.findItem(de.danoeh.antennapod.R.id.queue_lock);
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
index 6392d0535..4686f0303 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
@@ -7,6 +7,7 @@ import android.preference.PreferenceManager;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
@@ -23,7 +24,6 @@ public class PreferenceUpgrader {
int newVersion = BuildConfig.VERSION_CODE;
if (oldVersion != newVersion) {
- NotificationUtils.createChannels(context);
AutoUpdateManager.restartUpdateAlarm();
upgrade(oldVersion);
@@ -32,6 +32,9 @@ public class PreferenceUpgrader {
}
private static void upgrade(int oldVersion) {
+ if (oldVersion == -1) {
+ return;
+ }
if (oldVersion < 1070196) {
// migrate episode cleanup value (unit changed from days to hours)
int oldValueInDays = UserPreferences.getEpisodeCleanupValue();
@@ -72,6 +75,14 @@ public class PreferenceUpgrader {
}
UserPreferences.setQueueLocked(false);
+ prefs.edit().putBoolean(UserPreferences.PREF_STREAM_OVER_DOWNLOAD, false).apply();
+
+ if (!prefs.contains(UserPreferences.PREF_ENQUEUE_LOCATION)) {
+ final String keyOldPrefEnqueueFront = "prefQueueAddToFront";
+ boolean enqueueAtFront = prefs.getBoolean(keyOldPrefEnqueueFront, false);
+ EnqueueLocation enqueueLocation = enqueueAtFront ? EnqueueLocation.FRONT : EnqueueLocation.BACK;
+ UserPreferences.setEnqueueLocation(enqueueLocation);
+ }
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java b/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java
index e79389fb3..4c116fa2d 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java
@@ -88,7 +88,7 @@ public class AspectRatioVideoView extends VideoView {
mVideoWidth = videoWidth;
mVideoHeight = videoHeight;
- /**
+ /*
* If this isn't set the video is stretched across the
* SurfaceHolders display surface (i.e. the SurfaceHolder
* as the same size and the video is drawn to fit this
diff --git a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
index 1516c4eb6..a2d8ec091 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
@@ -1,7 +1,10 @@
package de.danoeh.antennapod.view;
import android.content.Context;
+import android.database.DataSetObserver;
import android.graphics.drawable.Drawable;
+import android.widget.AbsListView;
+import android.widget.ListAdapter;
import androidx.annotation.AttrRes;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
@@ -9,7 +12,6 @@ import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
-import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -17,8 +19,9 @@ import de.danoeh.antennapod.R;
public class EmptyViewHandler {
private boolean layoutAdded = false;
- private RecyclerView recyclerView;
- private RecyclerView.Adapter adapter;
+ private View list;
+ private ListAdapter listAdapter;
+ private RecyclerView.Adapter recyclerAdapter;
private final Context context;
private final View emptyView;
@@ -54,44 +57,60 @@ public class EmptyViewHandler {
emptyView.setVisibility(View.GONE);
}
- public void attachToListView(ListView listView) {
+ public void attachToListView(AbsListView listView) {
if (layoutAdded) {
- throw new IllegalStateException("Can not attach to ListView multiple times");
+ throw new IllegalStateException("Can not attach EmptyView multiple times");
}
+ addToParentView(listView);
layoutAdded = true;
- ((ViewGroup) listView.getParent()).addView(emptyView);
+ this.list = listView;
listView.setEmptyView(emptyView);
+ updateAdapter(listView.getAdapter());
}
public void attachToRecyclerView(RecyclerView recyclerView) {
if (layoutAdded) {
- throw new IllegalStateException("Can not attach to ListView multiple times");
+ throw new IllegalStateException("Can not attach EmptyView multiple times");
}
+ addToParentView(recyclerView);
layoutAdded = true;
- this.recyclerView = recyclerView;
- ViewGroup parent = ((ViewGroup) recyclerView.getParent());
- parent.addView(emptyView);
+ this.list = recyclerView;
updateAdapter(recyclerView.getAdapter());
+ }
+ private void addToParentView(View view) {
+ ViewGroup parent = ((ViewGroup) view.getParent());
+ parent.addView(emptyView);
if (parent instanceof RelativeLayout) {
RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams)emptyView.getLayoutParams();
+ (RelativeLayout.LayoutParams) emptyView.getLayoutParams();
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
emptyView.setLayoutParams(layoutParams);
}
}
public void updateAdapter(RecyclerView.Adapter adapter) {
- if (this.adapter != null) {
- this.adapter.unregisterAdapterDataObserver(adapterObserver);
+ if (this.recyclerAdapter != null) {
+ this.recyclerAdapter.unregisterAdapterDataObserver(adapterObserver);
}
- this.adapter = adapter;
+ this.recyclerAdapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(adapterObserver);
}
updateVisibility();
}
+ private void updateAdapter(ListAdapter adapter) {
+ if (this.listAdapter != null) {
+ this.listAdapter.unregisterDataSetObserver(listAdapterObserver);
+ }
+ this.listAdapter = adapter;
+ if (adapter != null) {
+ adapter.registerDataSetObserver(listAdapterObserver);
+ }
+ updateVisibility();
+ }
+
private final SimpleAdapterDataObserver adapterObserver = new SimpleAdapterDataObserver() {
@Override
public void anythingChanged() {
@@ -99,14 +118,22 @@ public class EmptyViewHandler {
}
};
- private void updateVisibility() {
+ private final DataSetObserver listAdapterObserver = new DataSetObserver() {
+ public void onChanged() {
+ updateVisibility();
+ }
+ };
+
+ public void updateVisibility() {
boolean empty;
- if (adapter == null) {
- empty = true;
+ if (recyclerAdapter != null) {
+ empty = recyclerAdapter.getItemCount() == 0;
+ } else if (listAdapter != null) {
+ empty = listAdapter.isEmpty();
} else {
- empty = adapter.getItemCount() == 0;
+ empty = true;
}
- recyclerView.setVisibility(empty ? View.GONE : View.VISIBLE);
+ list.setVisibility(empty ? View.GONE : View.VISIBLE);
emptyView.setVisibility(empty ? View.VISIBLE : View.GONE);
}
}
diff --git a/app/src/main/play/listings/de-DE/full-description.txt b/app/src/main/play/listings/de-DE/full-description.txt
index c49417c3e..5365983bb 100644
--- a/app/src/main/play/listings/de-DE/full-description.txt
+++ b/app/src/main/play/listings/de-DE/full-description.txt
@@ -1,42 +1,31 @@
-AntennaPod ist ein Podcast-Manager und -Player, der dir unmittelbar Zugriff auf Millionen von freien und bezahlten Podcasts ermöglicht, angefangen von unabhängigen Podcastern zu großen Rundfunkanstalten oder Hörfunksendern wie BBC, NPR und CNN. Abonniere, importiere und exportiere deine Feeds mühelos mit Hilfe des iTunes-Verzeichnisses, OPML-Dateien oder einfachen RSS-URLs. Reduziere Aufwand, Stromverbrauch und Datenverbrauch durch die Kontrolle der Downloads (bestimmte Uhrzeiten, Intervalle, WiFi-Netze) und des Löschens (basierend auf deinen Favoriten und weiteren Einstellungen).
-Aber am wichtigsten: Downloade, streame oder füge Episoden zur Abspielliste hinzu und genieße sie mit einstellbarer Abspielgeschwindigkeit, Unterstützung von Kapiteln und Schlummerfunktion.
-
-AntennaPod ist, von Podcast-Enthusiasten gemacht, frei im Sinne des Wortes: Open Source, keine Kosten, keine Werbung.
-
-<b>Alle Funktionen:</b><br>
-IMPORTIERE, ORGANISIERE UND HÖRE<br>
-&#8226; Importiere oder füge Feeds über das iTunes und gPodder.net Verzeichnis, OMPL Dateien und RSS oder Atom Links hinzu.
-&#8226; Bediene die Wiedergabe von überall: Startbildschirm-Widget, Benachrichtigung und Kopfhörer- und Bluetooth-Bedienelemente<br>
-&#8226; Genieße das Zuhören auf deine Art mit einstellbarer Abspielgeschwindigkeit, der Unterstützung von Kapiteln (MP3, OGG, Podlove) und ausgereifter Schlummerfunktion (durch Schütteln zurücksetzen, Lautstärke verringern und Geschwindigkeit verlangsamen)
-&#8226; Greife auf Passwort-geschützte Feeds und Episoden zu<br>
-&#8226; Nutze den Vorteil von Paged Feeds (http://www.podlove.org/paged-feeds)
-
-ORDNE, TEILE & GENIEßE
-&#8226; Bleib an den Besten der Besten dran, indem Du Episoden als Favoriten markierst<br>
-&#8226; Finde Episoden durch die Liste zuletzt gespielter Episoden oder durch Suche in Titel und Shownotes
-&#8226; Teile Episoden and Feeds über soziale Medien, E-Mail, den gPodder.net-Dienst oder als OPML-Export
-
-STEUERE DAS SYSTEM<br>
-&#8226; Kontrolliere automatisches Herunterladen: Wähle Feeds aus, schließe mobile Netze aus, suche bestimmte WiFi-Netze aus, setze voraus, dass das Smartphone geladen wird und lege Zeitpunkte oder Intervalle fest<br>
-&#8226; Verwalte deinen Speicherplatz durch das Festlegen der Anzahl gespeicherter Episoden, schlaues Löschen und durch Auswahl des Speicherortes<br>
-&#8226; Benutze AntennaPod in deiner Sprache (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Passe das Aussehen mit dem hellen oder dunklen Theme an<br>
-&#8226; Sichere deine Abonnements mit gPodder.net oder über den OPML-Export
-
-<b>Tritt der AntennaPod-Community bei!</b><br>
+AntennaPod ist ein Podcast-Manager und -Player, der dir unmittelbar Zugriff auf Millionen von freien und bezahlten Podcasts ermöglicht. Angefangen von unabhängigen Podcastern zu großen Rundfunkanstalten oder Hörfunksendern wie BBC, NPR und CNN. Abonniere, importiere und exportiere deine Feeds mühelos mit Hilfe des iTunes-Verzeichnisses, OPML-Dateien oder einfachen RSS-URLs.
+Downloade, streame oder sortiere Episoden in der Abspielliste und genieße sie mit einstellbarer Abspielgeschwindigkeit, Unterstützung von Kapiteln und Schlummerfunktion.
+Reduziere Aufwand, Stromverbrauch und Datenverbrauch durch leistungsfähige Kontrolle der Downloads (bestimmte Uhrzeiten, Intervalle, WiFi-Netze) und des Löschens (basierend auf deinen Favoriten und weiteren Einstellungen).
+
+AntennaPod ist von Podcast-Enthusiasten gemacht und frei im wahrsten Sinne des Wortes: Open Source, keine Kosten, keine Werbung.
+
+<b>Importiere, organisiere und spiele ab</b>
+• Bediene die Wiedergabe von überall: Startbildschirm-Widget, Benachrichtigung und Kopfhörer- und Bluetooth-Bedienelemente
+• Füge Feeds über iTunes und gPodder.net oder über OMPL Dateien und RSS oder Atom Links hinzu.
+• Genieße das Zuhören genau wie du willst - mit einstellbarer Abspielgeschwindigkeit, der Unterstützung von Kapiteln und ausgereifter Schlummerfunktion (durch Schütteln zurücksetzen, Lautstärke verringern)
+• Greife auf Passwort-geschützte Feeds und Episoden zu
+
+<b>Behalte den Ãœberblick</b>
+• Behalte den Überblick über die Besten der Besten, indem du Episoden als Favoriten markierst
+• Finde Episoden durch die Liste zuletzt gespielter Episoden oder durch Suche in Titel und Shownotes
+• Teile Episoden and Feeds über soziale Medien, E-Mail, den gPodder.net-Dienst oder als OPML-Export
+
+<b>Behalte die Kontrolle</b>
+• Kontrolliere automatisches Herunterladen: Wähle Feeds aus, schließe mobile Netze aus, suche bestimmte WiFi-Netze aus, setze voraus, dass das Smartphone geladen wird und lege Zeitpunkte oder Intervalle fest
+• Verwalte deinen Speicherplatz durch das Festlegen der Anzahl gespeicherter Episoden, schlaues Löschen und durch Auswahl des Speicherortes
+• Passe das Aussehen mit dem hellen oder dunklen Theme an deine Wünsche an
+• Sichere deine Abonnements mit gPodder.net oder über den OPML-Export
+
+<b>Tritt der AntennaPod-Community bei!</b>
AntennaPod wird aktiv von Freiwilligen weiterentwickelt. Auch du kannst bei der Entwicklung mit Quellcode oder Kommentaren mitwirken!
-Wir verwenden GitHub für Funktionswünsche (Feature Requests), Fehlerberichte (Bug Reports) und zum Beisteuern von Code (Code Contributions).
+Wir verwenden GitHub für Funktionswünsche, Fehlerberichte und zur Beteiligung an der Entwicklung:
https://www.github.com/AntennaPod/AntennaPod
-Teile deine Idee und Lieblingspodcastmomente und äußere Deine Dankbarkeit gegenüber allen Freiwilligen in unserer Google Group:<br>
-https://groups.google.com/forum/#!forum/antennapod (Englisch)
-
-Du hast eine Frage oder willst uns Feedback geben?
-https://twitter.com/@AntennaPod
-
-Mit Transifex kannst du uns beim Ãœbersetzen helfen:<br>
-https://www.transifex.com/antennapod/antennapod
-
-Probiere unser Beta-Testing-Programm aus, um die neusten Funktionen als Erster zu erhalten:<br>
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
+Mit Transifex kannst du uns beim Ãœbersetzen helfen:
+https://www.transifex.com/antennapod/antennapod \ No newline at end of file
diff --git a/app/src/main/play/listings/en-US/full-description.txt b/app/src/main/play/listings/en-US/full-description.txt
index c562387af..e0042e07f 100644
--- a/app/src/main/play/listings/en-US/full-description.txt
+++ b/app/src/main/play/listings/en-US/full-description.txt
@@ -1,42 +1,31 @@
-AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based on your favourites and delay settings).<br>
-But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer.
-
-Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
-
-<b>All features:</b><br>
-IMPORT, ORGANIZE AND PLAY<br>
-&#8226; Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links<br>
-&#8226; Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls<br>
-&#8226; Enjoy listening your way with adjustable playback speed, chapter support (MP3, VorbisComment and Podlove), remembered playback position and an advanced sleep timer (shake to reset, lower volume and slow down playback)<br>
-&#8226; Access password-protected feeds and episodes<br>
-&#8226; Take advantage of paged feeds (www.podlove.org/paged-feeds)
-
-KEEP TRACK, SHARE & APPRECIATE<br>
-&#8226; Keep track of the best of the best by marking episodes as favourites<br>
-&#8226; Find that one episode through the playback history or by searching (titles and shownotes)<br>
-&#8226; Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export<br>
-
-CONTROL THE SYSTEM<br>
-&#8226; Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals<br>
-&#8226; Manage storage by setting the amount of cached episodes, smart deletion (based on your favourites and play status) and selecting your preferred location<br>
-&#8226; Use AntennaPod in your language (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Adapt to your environment using the light and dark theme<br>
-&#8226; Back-up your subscriptions with the gPodder.net integration and OPML export
-
-<b>Join the AntennaPod community!</b><br>
+AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs.
+Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer.
+Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based on your favourites and delay settings).
+
+Made by podcast-enthusiasts, AntennaPod is free in all senses of the word: open source, no costs, no ads.
+
+<b>Import, organize and play</b>
+• Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls
+• Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links
+• Enjoy listening your way with adjustable playback speed, chapter support, remembered playback position and an advanced sleep timer (shake to reset, lower volume)
+• Access password-protected feeds and episodes
+
+<b>Keep track, share & appreciate</b>
+• Keep track of the best of the best by marking episodes as favourites
+• Find that one episode through the playback history or by searching titles and shownotes
+• Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export
+
+<b>Control the system</b>
+• Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals
+• Manage storage by setting the amount of cached episodes, smart deletion and selecting your preferred location
+• Adapt to your environment using the light and dark theme
+• Back-up your subscriptions with the gPodder.net integration and OPML export
+
+<b>Join the AntennaPod community!</b>
AntennaPod is under active development by volunteers. You can contribute too, with code or with comment!
-GitHub is the place to go for feature requests, bug reports and code contributions:<br>
+GitHub is the place to go for feature requests, bug reports and code contributions:
https://www.github.com/AntennaPod/AntennaPod
-Our Google Group is the place to share your ideas, favourite podcasting moments and gratitude to all the volunteers:<br>
-https://groups.google.com/forum/#!forum/antennapod
-
-Have a question or want to give us feedback?
-https://twitter.com/@AntennaPod
-
-Transifex is the place to help with translations:<br>
+Transifex is the place to help with translations:
https://www.transifex.com/antennapod/antennapod
-
-Check out our Beta Testing programme to get the latest features first:<br>
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod
diff --git a/app/src/main/play/listings/es-ES/full-description.txt b/app/src/main/play/listings/es-ES/full-description.txt
index 8c5cba745..b98757cee 100644
--- a/app/src/main/play/listings/es-ES/full-description.txt
+++ b/app/src/main/play/listings/es-ES/full-description.txt
@@ -1,42 +1,31 @@
-AntennaPod es un reproductor y administrador de pódcast que te da acceso instantáneo a millones de pódcast gratuitos y de pago, desde editores independientes a grandes editoriales como la BBC, NPR y CNN. Añade, importa y exporta tus fuentes sin complicaciones usando la base de datos de pódcast de iTunes, archivos OPML o las URL de tipo RSS. Ahorra esfuerzo, energía de la batería y uso de datos móviles con potentes controles de automatización para descargar episodios (especifica horarios, intervalos y redes wifi) y elimina episodios (según tus favoritos y la configuración de retardo).<br>
-Y lo que es más importante: Descarga, escucha en stream, o añade la cola episodios y disfrútalos como quieras con velocidad de reproducción ajustable, soporte para capítulos y temporizador de sueño.
+AntennaPod is un gestor y reproductor de podcast que te da acceso instantáneo a millones de podcast gratuitos y de pago, desde podcasters independientes a grandes estaciones como la BBC, NPR y CNN. Agrega, importa y exporta las fuentes de manera sencilla usando el listado de iTunes, archivos OPML o las URL de tipo RSS. Ahorra esfuerzo, batería y datos con los controles de descarga y de borrado de episodios (basado en favoritos y ajustes de tiempo).
+Descarga, escucha en stream, o añade la cola episodios y disfrútalos como quieras con velocidad de reproducción ajustable, soporte para capítulos y temporizador de sueño.
+Ahorra esuferzo, batería y consumo de datos con los potentes controles de automatización para descarga (especifica a qué horas y desde qué redes Wi-Fi) y borrado de episodios antiguos (basado en tus preferencias).
Creado por entusiastas del pódcast, AntennaPod es libre en todos los sentidos: código abierto, gratuito y sin publicidad.
-<b>Todas las características:</b><br>
-IMPORTAR, ORGANIZAR Y REPRODUCIR<br>
-&#8226; Añade e importa fuentes mediante los directorios de iTunes y gPodder.net, archivos OPML y enlaces RSS o Atom<br>
-&#8226; Administra la reproducción desde cualquier parte: control en pantalla de inicio, notificación del sistema y controles de auricular y bluetooth<br>
-&#8226; Disfruta escuchando a tu manera con velocidad de reproducción ajustable, soporte de capítulos (MP3, VorbisComment y Podlove), recordatorio del punto de reproducción y el temporizador de sueño avanzado (agita para restablecer, bajar el volumen y disminuir la velocidad de reproducción)<br>
-&#8226; Accede a fuentes y episodios protegidos con contraseña<br>
-&#8226; Aprovecha las fuentes paginadas (www.podlove.org/paged-feeds)
+<b>Importar, organizar y reproducir</b>
+• Administra la reproducción desde cualquier parte: control en pantalla de inicio, notificación del sistema y controles de auricular y bluetooth
+• Añade e importa fuentes mediante los directorios de iTunes y gPodder.net, archivos OPML y enlaces RSS o Atom
+• Disfruta escuchando a tu manera con velocidad de reproducción ajustable, soporte de capítulos (MP3, VorbisComment y Podlove), recordatorio del punto de reproducción y el temporizador de sueño avanzado (agita para restablecer, bajar el volumen y disminuir la velocidad de reproducción)
+• Accede a fuentes y episodios protegidos con contraseña
-MANTÉN UN SEGUIMIENTO, COMPARTE Y APRECIA
-&#8226; Haz un seguimiento de lo mejor de lo mejor marcando episodios como favoritos<br>
-&#8226; Encuentra ese episodio a través del historial de reproducción o por búsqueda (títulos y notas de episodios)<br>
-&#8226; Comparte episodios y fuentes a través de las avanzadas redes sociales y opciones de correo electrónico, los servicios de gPodder.net y la exportación OPML<br>
+<b>Seguir, valorar y compartir </b>
+• Haz un seguimiento de lo mejor de lo mejor marcando episodios como favoritos
+• Busca ese episodio en el historial o busca por título y notas
+• Comparte episodios y fuentes a través de las avanzadas redes sociales y opciones de correo electrónico, los servicios de gPodder.net y la exportación OPML
-CONTROLA EL SISTEMA<br>
-&#8226; Controla las descargas automáticas: elige las fuentes, excluye las redes móviles, selecciona redes wifi específicas, o solo cuando el teléfono se esté cargando y establece horarios o intervalos<br>
-&#8226; Administra el almacenamiento configurando la cantidad de episodios almacenados, el borrado inteligente (según tus favoritos y el estado de reproducción) y selecciona tu ubicación preferida<br>
-&#8226; Usa AntennaPod en tu idioma (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Adáptate a tu entorno usando el tema claro u oscuro<br>
-&#8226; Haz una copia de seguridad de tus suscripciones con la integración de gPodder.net y la exportación OPML
+<b>Control del sistema</b>
+• Controla las descargas automáticas: elige las fuentes, excluye las redes móviles, selecciona redes wifi específicas, o solo cuando el teléfono se esté cargando y establece horarios o intervalos
+• Gestiona el almacenamiento configurando la cantidad de episodios en caché, configura borrado inteligente y eligiendo tu ubicación favorita
+• Adáptate a tu entorno usando el tema claro u oscuro
+• Haz una copia de seguridad de tus suscripciones con la integración de gPodder.net y la exportación OPML
-<b>¡Únete a la comunidad AntennaPod!</b><br>
+<b>¡Únete a la comunidad AntennaPod!</b>
AntennaPod está en continuo desarrollo por voluntarios. ¡Tú también puedes contribuir, con tu código o con tus comentarios!
-GitHub es el sitio que debes visitar para solicitar características nuevas, reportar fallos y contribuir con código<br>
+GitHub es el sitio que debes visitar para solicitar características nuevas, reportar fallos y contribuir con código:
https://www.github.com/AntennaPod/AntennaPod
-Nuestro Grupo de Google es el sitio para compartir tus ideas, momentos favoritos de tus pódcast y tu gratitud a los voluntarios:<br>
-https://groups.google.com/forum/#!forum/antennapod
-
-¿Tienes una pregunta o quieres darnos tu opinión?
-https://twitter.com/@AntennaPod
-
-Transifex es el sitio para ayudar con las traducciones:<br>
-https://www.transifex.com/antennapod/antennapod
-
-Echa un vistazo a nuestro programa de pruebas Beta y ser el primero en usar las nuevas características:<br>
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
+Para ayudar con las traducciones en Transifex:
+https://www.transifex.com/antennapod/antennapod \ No newline at end of file
diff --git a/app/src/main/play/listings/fr-FR/full-description.txt b/app/src/main/play/listings/fr-FR/full-description.txt
index 5c69f3892..fe8734be4 100644
--- a/app/src/main/play/listings/fr-FR/full-description.txt
+++ b/app/src/main/play/listings/fr-FR/full-description.txt
@@ -1,42 +1,31 @@
-AntennaPod est un lecteur de podcast permettant l'accès à des millions de podcast gratuits ou payants produit aussi bien par des podcasters indépendants que des éditeurs professionnels comme la BBC, NPR ou CNN. Ajoutez, importez et exportez leurs flux facilement à partir d'ITunes, de fichiers OPML ou simplement à partir de liens RSS. Economisez votre temps, votre batterie et votre consommation de données grâce à l'automatisation des téléchargements (date, fréquence, choix du réseau WiFi, etc...) et de la suppression des épisodes (selon vos critères)<br>
-Avant tout : téléchargez, streamez ou ajoutez à la liste de lecture vos épisodes et écoutez les comme vous voulez grâce au réglage de vitesse de lecture, au support des chapitres et au minuteur d'arrêt.
-
-Conçu par des fans de podcast, AntennaPod est gratuit dans tous les sens du terme : open source, gratuit et sans publicité.
-
-<b>Caractéristiques complètes :</b><br>
-IMPORTER, GÉRER ET ÉCOUTER<br>
-&#8226; Ajouter et importer à partir d'iTunes, gPodder.net, fichiers OPML, liens RSS ou Atom<br>
-&#8226; Gérez la lecture de n'importe où : widget sur l'écran d'accueil, notification système, commande casque ou Bluetooth<br>
-&#8226; Écoutez à votre façon grâce à une vitesse de lecture réglable, au support des chapitres (MP3, VorbisComment et Podlove), à l'enregistrement de la position de lecture et à une mise en veille automatique puissante (secouez pour prolonger le minuteur, baisser le volume et ralentir la lecture)<br>
-&#8226; Accès aux flux et épisodes protégés par mot de passe<br>
-&#8226; Tirez profit des flux paginés (www.podlove.org/paged-feeds)
-
-SUIVEZ, PARTAGEZ & PROFITEZ<br>
-&#8226; Marquer les meilleurs épisodes en tant que favoris<br>
-&#8226; Retrouvez un épisode à partir de l'historique de lecture ou en recherchant parmi les titres et commentaires des épisodes précédents<br>
-&#8226; Partagez vos épisodes et flux sur les réseaux sociaux, par email, sur gPodder.net ou en les exportant au format OPML<br>
-
-CONTRÔLER LE SYSTÈME<br>
-&#8226; Prenez le contrôle en automatisant vos téléchargements : choix des flux, restriction de la connexion mobile, sélection du réseau WIFI à utiliser, uniquement durant la recharge et spécifiez la fréquence de mise à jour vous-même<br>
-&#8226; Gérez l'espace de stockage en paramétrant le nombre d'épisodes à garder, leur suppression automatique (en fonction de vos favoris et de leur statut) et leur emplacement sur le disque<br>
-&#8226; Utilisez AntennaPod dans votre langue (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Choix d'un thème clair ou sombre selon vos préférences<br>
-&#8226; Sauvegardez vos abonnements avec l’intégration à gPodder.net et les exports OPML
-
-<b>Rejoignez la communauté d'AntennaPod !</b><br>
+AntennaPod est un lecteur de podcast permettant l'accès à des millions de podcast gratuits ou payants produit par des podcasters indépendants ou de gros éditeurs comme la BBC, NPR, CNN. Ajoutez, importez et exportez facilement des podcasts à partir d'ITunes, de fichiers OPML ou de liens RSS.
+Téléchargez, streamez ou ajoutez à la liste de lecture vos épisodes et écoutez les comme vous aimez avec une vitesse de lecture réglable, le support des chapitres et un minuteur d'arrêt automatique.
+Economisez votre temps, votre batterie et votre consommation de données grâce à l'automatisation des téléchargements (date, fréquence, choix du réseau WiFi) et la suppression des épisodes (selon vos critères)
+
+Conçu par des fans de podcast, AntennaPod est gratuit, open source et sans publicité.
+
+<b>Importez, organisez et écoutez</b>
+• Gérez la lecture à l'aide : de widget, de notification système, de commande casque ou bluetooth
+• Ajouter et importer à partir d'iTunes, gPodder.net, de fichiers OPML, de liens RSS ou Atom
+• Ecoutez à votre façon grâce à une vitesse de lecture réglable, au support des chapitres, à l'enregistrement de la position de lecture et à un minuteur d'arrêt automatique puissant (secouez pour réinitialiser, baisse du volume)
+• Accès possible aux flux et épisodes nécessitant un mot de passe
+
+<b>Suivez, partagez et appréciez</b>
+• Enregistrez les meilleurs épisodes en tant que favoris
+• Retrouvez un épisode à partir de l'historique de lecture ou en recherchant les noms et commentaires des épisodes
+• Partagez vos épisodes et flux sur les réseaux sociaux, par email, sur gPodder.net ou en les exportant au format OPML
+
+<b>Contrôlez</b>
+• Prenez le contrôle en automatisant vos téléchargements : flux spécifiques, restriction de la connexion mobile, réseaux WIFI autorisés, seulement pendant la recharge, fréquence de mise à jour
+• Gérez l'espace de stockage en réglant le nombre d'épisodes à garder, quand les supprimer et où les enregistrer
+• Choisissez le thème de l'interface
+• Sauvegardez vos abonnements avec gPodder.net ou des exports OPML
+
+<b>Rejoignez la communauté d'AntennaPod !</b>
AntennaPod est développé activement par des volontaires. Vous pouvez aussi contribuer avec du code, des traductions ou des commentaires !
-GitHub est l'endroit où aller pour demander de nouvelles options, faire part de bug ou pour contribuer au code :<br>
+Allez sur GitHub pour demander de nouvelles options, signaler des bugs ou pour contribuer au développement :
https://www.github.com/AntennaPod/AntennaPod
-Rejoignez notre Google Group pour partager vos idées, podcast préférés et vos remerciements à tous les bénévoles :<br>
-https://groups.google.com/forum/#!forum/antennapod
-
-Vous avez une question ou des suggestions ?
-https://twitter.com/@AntennaPod
-
-Retrouvez nous sur Transifex pour contribuer à la traduction de cette app :<br>
-https://www.transifex.com/antennapod/antennapod
-
-Jetez un coup d’œil à notre programme de version Beta pour bénéficier des dernières options :<br>
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
+Allez sur Transifex pour aider la traduction :
+https://www.transifex.com/antennapod/antennapod \ No newline at end of file
diff --git a/app/src/main/play/release-notes/en-US/default.txt b/app/src/main/play/release-notes/en-US/default.txt
index 11371852d..20f9bf802 100644
--- a/app/src/main/play/release-notes/en-US/default.txt
+++ b/app/src/main/play/release-notes/en-US/default.txt
@@ -1,8 +1,7 @@
-- Display episode image on widget (by @brad)
-- Added checkbox to keep queue sorted (by @damoasda)
-- New UI for "Add podcast" screen (by @ByteHamster)
-- Added batch editing to the queue (by @ByteHamster)
-- Added option to adapt remaining time to playback speed (by @CedricCabessa)
-- Removed broken Flattr integration (by @ByteHamster)
-- Added filter to "All episodes" list (by @jhunnius)
-- Tons of bug fixes and performance improvements
+- Added per-feed playback speed setting (by @spacecowboy)
+- Support sorting in Podcast screen (by @orionlee)
+- Option to show stream button rather than download in lists (by @dsmith47)
+- Option to replace Episode cover with Podcast cover (by @xgouchet)
+- Transparent widget (by @M-arcel)
+- User interface tweaks (by @ByteHamster)
+- Tons of bug fixes and improvements
diff --git a/app/src/main/res/layout/about_teaser.xml b/app/src/main/res/layout/about_teaser.xml
new file mode 100644
index 000000000..a9e50f6da
--- /dev/null
+++ b/app/src/main/res/layout/about_teaser.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:adjustViewBounds="true"
+ android:layout_height="wrap_content" app:srcCompat="@drawable/teaser" /> \ No newline at end of file
diff --git a/app/src/main/res/layout/activity_widget_config.xml b/app/src/main/res/layout/activity_widget_config.xml
new file mode 100644
index 000000000..ca8aba52d
--- /dev/null
+++ b/app/src/main/res/layout/activity_widget_config.xml
@@ -0,0 +1,80 @@
+<?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"
+ tools:context="de.danoeh.antennapod.activity.WidgetConfigActivity">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:layout_gravity="center">
+
+ <ImageView
+ android:id="@+id/widget_config_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@drawable/teaser"
+ android:scaleType="centerCrop" />
+
+ <include
+ android:id="@+id/widget_config_preview"
+ layout="@layout/player_widget"
+ android:layout_width="match_parent"
+ android:layout_height="80dp"
+ android:layout_gravity="center"
+ android:layout_margin="16dp" />
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/textView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/widget_opacity"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorPrimary" />
+
+ <TextView
+ android:id="@+id/widget_opacity_textView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="end"
+ android:text="100%"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ </LinearLayout>
+
+ <SeekBar
+ android:id="@+id/widget_opacity_seekBar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:max="100"
+ android:progress="100" />
+
+ <Button
+ android:id="@+id/butConfirm"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/widget_create_button" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/addfeed.xml b/app/src/main/res/layout/addfeed.xml
index 99fa1c3f9..9e57e0743 100644
--- a/app/src/main/res/layout/addfeed.xml
+++ b/app/src/main/res/layout/addfeed.xml
@@ -32,6 +32,7 @@
android:layout_marginRight="8dp"
android:contentDescription="@string/search_podcast_hint"
app:srcCompat="?attr/action_search"
+ android:id="@+id/search_icon"
android:scaleType="center"/>
<EditText
@@ -72,7 +73,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="8dp"
+ android:padding="16dp"
android:orientation="vertical">
<TextView
@@ -82,6 +83,8 @@
android:text="@string/txtvfeedurl_label"
android:textSize="18sp"
android:layout_marginBottom="8dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginStart="4dp"
android:textColor="?android:attr/textColorPrimary"/>
<EditText
@@ -96,8 +99,10 @@
<Button
android:id="@+id/butConfirm"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ style="?android:attr/buttonBarButtonStyle"
android:text="@string/confirm_label"/>
</LinearLayout>
diff --git a/app/src/main/res/layout/audio_controls.xml b/app/src/main/res/layout/audio_controls.xml
index 090aec47f..5049db215 100644
--- a/app/src/main/res/layout/audio_controls.xml
+++ b/app/src/main/res/layout/audio_controls.xml
@@ -3,7 +3,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:padding="16dp">
<LinearLayout
android:layout_width="wrap_content"
diff --git a/app/src/main/res/layout/authentication_dialog.xml b/app/src/main/res/layout/authentication_dialog.xml
index 187045c63..1661eaea1 100644
--- a/app/src/main/res/layout/authentication_dialog.xml
+++ b/app/src/main/res/layout/authentication_dialog.xml
@@ -4,88 +4,33 @@
android:layout_height="match_parent"
android:orientation="vertical" >
- <LinearLayout
+ <EditText
+ android:id="@+id/etxtUsername"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:layout_margin="16dp"
+ android:hint="@string/username_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
+
+ <EditText
+ android:id="@+id/etxtPassword"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:layout_margin="16dp"
+ android:inputType="textPassword"
+ android:hint="@string/password_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
+
+ <CheckBox
+ android:id="@+id/chkSaveUsernamePassword"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <EditText
- android:id="@+id/etxtUsername"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:layout_margin="16dp"
- android:hint="@string/username_label"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:cursorVisible="true"/>
-
- <EditText
- android:id="@+id/etxtPassword"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1"
- android:layout_margin="16dp"
- android:inputType="textPassword"
- android:hint="@string/password_label"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:cursorVisible="true"/>
-
- <CheckBox
- android:id="@+id/chkSaveUsernamePassword"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:text="@string/save_username_password_label"/>
- </LinearLayout>
-
- <RelativeLayout
- android:id="@+id/footer"
- android:layout_width="fill_parent"
- android:layout_height="48dp" >
-
- <View
- android:layout_width="match_parent"
- android:layout_height="1dip"
- android:layout_alignParentTop="true"
- android:background="?android:attr/dividerVertical" />
-
- <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" />
-
- <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_alignParentStart="true"
- android:layout_alignParentTop="true"
- android:layout_toLeftOf="@id/horizontal_divider"
- android:layout_toStartOf="@id/horizontal_divider"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/cancel_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_alignParentEnd="true"
- android:layout_alignParentTop="true"
- android:layout_toRightOf="@id/horizontal_divider"
- android:layout_toEndOf="@id/horizontal_divider"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/confirm_label" />
- </RelativeLayout>
-
+ android:layout_margin="16dp"
+ android:text="@string/save_username_password_label"/>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/choose_data_folder_dialog.xml b/app/src/main/res/layout/choose_data_folder_dialog.xml
new file mode 100644
index 000000000..bac14a108
--- /dev/null
+++ b/app/src/main/res/layout/choose_data_folder_dialog.xml
@@ -0,0 +1,12 @@
+<?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">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/recyclerView" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/choose_data_folder_dialog_entry.xml b/app/src/main/res/layout/choose_data_folder_dialog_entry.xml
index f4a2ff703..ae59c0614 100644
--- a/app/src/main/res/layout/choose_data_folder_dialog_entry.xml
+++ b/app/src/main/res/layout/choose_data_folder_dialog_entry.xml
@@ -37,12 +37,11 @@
android:layout_height="wrap_content"
tools:text="2 GB" />
- <me.zhanghai.android.materialprogressbar.MaterialProgressBar
+ <ProgressBar
android:id="@+id/used_space"
- style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal"
+ style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:mpb_progressStyle="horizontal" />
+ android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
diff --git a/app/src/main/res/layout/edit_text_dialog.xml b/app/src/main/res/layout/edit_text_dialog.xml
new file mode 100644
index 000000000..6bf0bc6cb
--- /dev/null
+++ b/app/src/main/res/layout/edit_text_dialog.xml
@@ -0,0 +1,15 @@
+<?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:padding="16dp">
+
+ <EditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="text"
+ android:ems="10"
+ android:id="@+id/text" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml
index b047b3da0..3352fdf19 100644
--- a/app/src/main/res/layout/feeditem_fragment.xml
+++ b/app/src/main/res/layout/feeditem_fragment.xml
@@ -112,38 +112,43 @@
android:orientation="horizontal"
tools:background="@android:color/holo_blue_bright">
- <com.joanzapata.iconify.widget.IconButton
- android:id="@+id/butAction1"
+ <View
android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" />
+
+ <Button
+ android:id="@+id/butAction1"
+ style="?attr/buttonBarButtonStyle"
+ android:layout_width="wrap_content"
android:layout_height="48dp"
- android:layout_gravity="center_vertical"
- android:layout_marginRight="8dp"
- android:layout_marginEnd="8dp"
- android:layout_weight="1"
- android:background="?attr/selectableItemBackground"
android:ellipsize="end"
- android:gravity="center"
+ android:drawablePadding="8dp"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_small"
tools:text="Button 1"
tools:background="@android:color/holo_red_light" />
- <com.joanzapata.iconify.widget.IconButton
- android:id="@+id/butAction2"
+ <View
android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" />
+
+ <Button
+ android:id="@+id/butAction2"
+ style="?attr/buttonBarButtonStyle"
+ android:layout_width="wrap_content"
android:layout_height="48dp"
- android:layout_gravity="center_vertical"
- android:layout_marginLeft="8dp"
- android:layout_marginStart="8dp"
- android:layout_weight="1"
- android:background="?attr/selectableItemBackground"
+ android:drawablePadding="8dp"
android:ellipsize="end"
- android:gravity="center"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_small"
tools:text="Button 2"
tools:background="@android:color/holo_orange_dark" />
+ <View
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" />
+
</LinearLayout>
<View
diff --git a/app/src/main/res/layout/feeditem_pager_fragment.xml b/app/src/main/res/layout/feeditem_pager_fragment.xml
new file mode 100644
index 000000000..8ea5bf4f9
--- /dev/null
+++ b/app/src/main/res/layout/feeditem_pager_fragment.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.viewpager.widget.ViewPager
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/app/src/main/res/layout/fragment_itunes_search.xml b/app/src/main/res/layout/fragment_itunes_search.xml
index 0cc13f74c..211f6c9ef 100644
--- a/app/src/main/res/layout/fragment_itunes_search.xml
+++ b/app/src/main/res/layout/fragment_itunes_search.xml
@@ -20,7 +20,7 @@
tools:listitem="@layout/gpodnet_podcast_listitem" />
<TextView
- android:id="@id/android:empty"
+ android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
diff --git a/app/src/main/res/layout/mediaplayerinfo_activity.xml b/app/src/main/res/layout/mediaplayerinfo_activity.xml
index b759b0092..636cbb015 100644
--- a/app/src/main/res/layout/mediaplayerinfo_activity.xml
+++ b/app/src/main/res/layout/mediaplayerinfo_activity.xml
@@ -169,7 +169,7 @@
android:contentDescription="@string/set_playback_speed_label"
android:src="?attr/av_speed"
android:scaleType="fitCenter"
- tools:src="@drawable/ic_playback_speed_white"
+ tools:src="@drawable/ic_playback_speed_white_48dp"
tools:visibility="gone"
tools:background="@android:color/holo_green_dark" />
diff --git a/app/src/main/res/layout/numberpicker.xml b/app/src/main/res/layout/numberpicker.xml
index 813326bd6..d493f2e6c 100644
--- a/app/src/main/res/layout/numberpicker.xml
+++ b/app/src/main/res/layout/numberpicker.xml
@@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:padding="32dp">
+ android:padding="16dp">
<EditText
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/onlinefeedview_activity.xml b/app/src/main/res/layout/onlinefeedview_activity.xml
new file mode 100644
index 000000000..4ed8a0341
--- /dev/null
+++ b/app/src/main/res/layout/onlinefeedview_activity.xml
@@ -0,0 +1,63 @@
+<?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">
+
+ <androidx.cardview.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:cardCornerRadius="4dp"
+ android:layout_margin="32dp"
+ android:elevation="16dp">
+
+ <FrameLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ProgressBar
+ style="?android:attr/progressBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/progressBar"
+ android:layout_gravity="center"/>
+
+ <LinearLayout
+ android:id="@+id/feedDisplay"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include layout="@layout/feeditemlist_header"/>
+
+ <Spinner
+ android:id="@+id/spinnerAlternateUrls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_marginTop="8dp"
+ android:padding="8dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/text_size_micro"/>
+
+ <Button
+ android:id="@+id/butSubscribe"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:focusable="false"
+ android:text="@string/subscribe_label"/>
+
+ <ListView
+ android:id="@+id/listview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ </LinearLayout>
+
+ </FrameLayout>
+ </androidx.cardview.widget.CardView>
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/onlinefeedview_header.xml b/app/src/main/res/layout/onlinefeedview_header.xml
index 057bfb379..aa690e142 100644
--- a/app/src/main/res/layout/onlinefeedview_header.xml
+++ b/app/src/main/res/layout/onlinefeedview_header.xml
@@ -6,27 +6,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent">
- <include layout="@layout/feeditemlist_header"/>
-
- <Spinner
- android:id="@+id/spinnerAlternateUrls"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:layout_marginTop="8dp"
- android:padding="8dp"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_micro"/>
-
- <Button
- android:id="@+id/butSubscribe"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:focusable="false"
- android:text="@string/subscribe_label"/>
-
<TextView
android:id="@+id/txtvDescription"
android:layout_width="wrap_content"
@@ -50,8 +29,7 @@
android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
- android:textColor="?attr/colorAccent"
- android:textSize="@dimen/text_size_small"
+ android:textSize="@dimen/text_size_medium"
android:text="@string/episodes_label"/>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/proxy_settings.xml b/app/src/main/res/layout/proxy_settings.xml
index 983325030..e4e57cc92 100644
--- a/app/src/main/res/layout/proxy_settings.xml
+++ b/app/src/main/res/layout/proxy_settings.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:orientation="vertical">
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
<TextView
android:id="@+id/txtvType"
diff --git a/app/src/main/res/layout/quick_feed_discovery.xml b/app/src/main/res/layout/quick_feed_discovery.xml
index e791d19d7..75a35a130 100644
--- a/app/src/main/res/layout/quick_feed_discovery.xml
+++ b/app/src/main/res/layout/quick_feed_discovery.xml
@@ -10,35 +10,32 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="8dp"
+ android:padding="16dp"
android:orientation="vertical">
- <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
- android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
<TextView
- android:layout_width="wrap_content"
+ android:layout_width="0dip"
android:layout_height="wrap_content"
android:text="@string/discover"
android:textSize="18sp"
android:layout_marginBottom="8dp"
+ android:layout_weight="1"
+ android:layout_marginLeft="4dp"
+ android:layout_marginStart="4dp"
android:textColor="?android:attr/textColorPrimary"/>
- <TextView
- android:layout_width="0dp"
+ <Button
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="0dp"
+ android:minWidth="0dp"
android:text="@string/discover_more"
- android:gravity="end"
- android:textAlignment="viewEnd"
- android:textSize="18sp"
- android:background="?android:attr/selectableItemBackground"
- android:layout_marginBottom="8dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:layout_weight="1"
android:id="@+id/discover_more"
- android:textColor="@color/antennapod_blue"/>
+ style="?android:attr/buttonBarButtonStyle"/>
</LinearLayout>
<RelativeLayout
@@ -52,8 +49,6 @@
android:numColumns="4"
app:layout_columnWeight="1"
app:layout_rowWeight="1"
- android:horizontalSpacing="4dp"
- android:verticalSpacing="4dp"
android:scrollbars="none"
android:layout_marginTop="8dp"
android:layout_centerInParent="true"
@@ -70,7 +65,7 @@
<TextView
android:id="@+id/discover_error"
- android:textColor="@color/md_edittext_error"
+ android:textColor="@color/download_failed_red"
android:layout_width="match_parent"
android:layout_centerInParent="true"
android:layout_height="wrap_content" />
diff --git a/app/src/main/res/layout/quick_feed_discovery_item.xml b/app/src/main/res/layout/quick_feed_discovery_item.xml
index 6b0c98013..3a0fe0b4b 100644
--- a/app/src/main/res/layout/quick_feed_discovery_item.xml
+++ b/app/src/main/res/layout/quick_feed_discovery_item.xml
@@ -2,12 +2,16 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:padding="4dp"
+ android:clipToPadding="false">
<de.danoeh.antennapod.view.SquareImageView
android:id="@+id/discovery_cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:elevation="4dp"
+ android:outlineProvider="bounds"
android:foreground="?android:attr/selectableItemBackground"/>
</LinearLayout>
diff --git a/app/src/main/res/layout/simple_icon_list_item.xml b/app/src/main/res/layout/simple_icon_list_item.xml
new file mode 100644
index 000000000..7ed129204
--- /dev/null
+++ b/app/src/main/res/layout/simple_icon_list_item.xml
@@ -0,0 +1,41 @@
+<?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="wrap_content"
+ android:orientation="horizontal"
+ android:padding="16dp">
+
+ <ImageView
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:id="@+id/icon"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginLeft="16dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="16dp">
+
+ <TextView
+ tools:text="Title"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/title"/>
+
+ <TextView
+ tools:text="Subtitle"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="14sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/subtitle"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/time_dialog.xml b/app/src/main/res/layout/time_dialog.xml
index ba4249268..b3742c20c 100644
--- a/app/src/main/res/layout/time_dialog.xml
+++ b/app/src/main/res/layout/time_dialog.xml
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:gravity="center">
+ android:gravity="center"
+ android:padding="16dp">
<LinearLayout
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
@@ -33,7 +34,7 @@
</LinearLayout>
<LinearLayout
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
diff --git a/app/src/main/res/layout/videoplayer_activity.xml b/app/src/main/res/layout/videoplayer_activity.xml
index 4de46810e..b26ef304c 100644
--- a/app/src/main/res/layout/videoplayer_activity.xml
+++ b/app/src/main/res/layout/videoplayer_activity.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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:background="@color/black"
@@ -33,27 +34,27 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
- android:background="@drawable/md_transparent"
+ android:background="@android:color/transparent"
android:contentDescription="@string/rewind_label"
- android:src="@drawable/ic_av_fast_rewind_white_80dp" />
+ app:srcCompat="@drawable/ic_av_fast_rewind_white_80dp" />
<ImageButton
android:id="@+id/butPlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
- android:background="@drawable/md_transparent"
+ android:background="@android:color/transparent"
android:contentDescription="@string/pause_label"
- android:src="@drawable/ic_av_pause_white_80dp" />
+ app:srcCompat="@drawable/ic_av_pause_white_80dp" />
<ImageButton
android:id="@+id/butFF"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
- android:background="@drawable/md_transparent"
+ android:background="@android:color/transparent"
android:contentDescription="@string/fast_forward_label"
- android:src="@drawable/ic_av_fast_forward_white_80dp" />
+ app:srcCompat="@drawable/ic_av_fast_forward_white_80dp" />
</LinearLayout>
diff --git a/app/src/main/res/menu/episodes.xml b/app/src/main/res/menu/episodes.xml
index 7398754e5..2fac77375 100644
--- a/app/src/main/res/menu/episodes.xml
+++ b/app/src/main/res/menu/episodes.xml
@@ -6,7 +6,7 @@
<item
android:id="@+id/action_search"
android:icon="?attr/action_search"
- custom:showAsAction="collapseActionView|ifRoom"
+ custom:showAsAction="collapseActionView|always"
custom:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/search_label"/>
@@ -14,7 +14,7 @@
android:id="@+id/refresh_item"
android:title="@string/refresh_label"
android:menuCategory="container"
- custom:showAsAction="ifRoom"
+ custom:showAsAction="always"
android:icon="?attr/navigation_refresh"/>
<item
diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml
index fdd0e01bc..13c019b65 100644
--- a/app/src/main/res/menu/feedlist.xml
+++ b/app/src/main/res/menu/feedlist.xml
@@ -3,17 +3,17 @@
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
- android:id="@+id/filter_items"
- android:icon="?attr/ic_filter"
+ android:id="@+id/sort_items"
+ android:icon="?attr/ic_sort"
android:menuCategory="container"
- android:title="@string/filter"
+ android:title="@string/sort"
custom:showAsAction="always">
</item>
<item
- android:id="@+id/episode_actions"
+ android:id="@+id/filter_items"
+ android:icon="?attr/ic_filter"
android:menuCategory="container"
- android:icon="?attr/checkbox_multiple"
- android:title="@string/batch_edit"
+ android:title="@string/filter"
custom:showAsAction="always">
</item>
<item
@@ -38,6 +38,13 @@
android:title="@string/search_label"/>
<item
+ android:id="@+id/episode_actions"
+ android:menuCategory="container"
+ android:icon="?attr/checkbox_multiple"
+ android:title="@string/batch_edit"
+ custom:showAsAction="collapseActionView">
+ </item>
+ <item
android:id="@+id/visit_website_item"
android:icon="?attr/location_web_site"
android:menuCategory="container"
diff --git a/app/src/main/res/xml/feed_settings.xml b/app/src/main/res/xml/feed_settings.xml
index 2e58d3445..9f2a0ba82 100644
--- a/app/src/main/res/xml/feed_settings.xml
+++ b/app/src/main/res/xml/feed_settings.xml
@@ -4,22 +4,26 @@
<SwitchPreference
android:key="keepUpdated"
+ android:icon="?attr/navigation_refresh"
android:title="@string/keep_updated"
android:summary="@string/keep_updated_summary"/>
<Preference
android:key="authentication"
+ android:icon="?attr/ic_key"
android:title="@string/authentication_label"
android:summary="@string/authentication_descr"/>
<ListPreference
android:key="feedPlaybackSpeed"
+ android:icon="?attr/ic_settings_speed"
android:title="@string/playback_speed"
android:summary="@string/pref_feed_playback_speed_sum"/>
<ListPreference
android:entries="@array/spnAutoDeleteItems"
android:entryValues="@array/spnAutoDeleteValues"
+ android:icon="?attr/content_discard"
android:title="@string/auto_delete_label"
android:key="autoDelete"/>
diff --git a/app/src/main/res/xml/player_widget_info.xml b/app/src/main/res/xml/player_widget_info.xml
index 1dbfabcd6..79cdd4a69 100644
--- a/app/src/main/res/xml/player_widget_info.xml
+++ b/app/src/main/res/xml/player_widget_info.xml
@@ -6,6 +6,7 @@
android:previewImage="@drawable/ic_widget_preview"
android:minHeight="40dp"
android:minWidth="250dp"
- android:minResizeWidth="40dp">
+ android:minResizeWidth="40dp"
+ android:configure="de.danoeh.antennapod.activity.WidgetConfigActivity">
</appwidget-provider> \ No newline at end of file
diff --git a/app/src/main/res/xml/preferences_about.xml b/app/src/main/res/xml/preferences_about.xml
new file mode 100644
index 000000000..9b8d744e1
--- /dev/null
+++ b/app/src/main/res/xml/preferences_about.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <Preference
+ android:layout="@layout/about_teaser"/>
+ <Preference
+ android:key="about_version"
+ android:title="@string/antennapod_version"
+ android:icon="?attr/ic_unfav"
+ android:summary="1.7.2 (asd8qs)"/>
+ <Preference
+ android:key="about_developers"
+ android:icon="?attr/ic_settings"
+ android:summary="@string/developers_summary"
+ android:title="@string/developers"/>
+ <Preference
+ android:key="about_translators"
+ android:icon="?attr/ic_chat"
+ android:summary="@string/translators_summary"
+ android:title="@string/translators"/>
+ <Preference
+ android:key="about_privacy_policy"
+ android:icon="?attr/ic_questionmark"
+ android:summary="https://antennapod.org/privacy.html"
+ android:title="@string/privacy_policy"/>
+ <Preference
+ android:key="about_licenses"
+ android:icon="?attr/action_about"
+ android:summary="@string/licenses_summary"
+ android:title="@string/licenses"/>
+
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_autodownload.xml b/app/src/main/res/xml/preferences_autodownload.xml
index a4967c839..333224aa0 100644
--- a/app/src/main/res/xml/preferences_autodownload.xml
+++ b/app/src/main/res/xml/preferences_autodownload.xml
@@ -15,16 +15,14 @@
android:key="prefEpisodeCacheSize"
android:title="@string/pref_episode_cache_title"
android:summary="@string/pref_episode_cache_summary"
- android:entryValues="@array/episode_cache_size_values"
- app:useStockLayout="true"/>
+ android:entryValues="@array/episode_cache_size_values"/>
<ListPreference
android:defaultValue="-1"
android:entries="@array/episode_cleanup_entries"
android:key="prefEpisodeCleanup"
android:title="@string/pref_episode_cleanup_title"
android:summary="@string/pref_episode_cleanup_summary"
- android:entryValues="@array/episode_cleanup_values"
- app:useStockLayout="true"/>
+ android:entryValues="@array/episode_cleanup_values"/>
<SwitchPreference
android:key="prefEnableAutoDownloadOnBattery"
android:title="@string/pref_automatic_download_on_battery_title"
diff --git a/app/src/main/res/xml/preferences_playback.xml b/app/src/main/res/xml/preferences_playback.xml
index d609d3daa..7a7535dda 100644
--- a/app/src/main/res/xml/preferences_playback.xml
+++ b/app/src/main/res/xml/preferences_playback.xml
@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/interruptions">
<SwitchPreference
@@ -42,8 +40,7 @@
android:entryValues="@array/video_background_behavior_values"
android:key="prefVideoBehavior"
android:summary="@string/pref_videoBehavior_sum"
- android:title="@string/pref_videoBehavior_title"
- app:useStockLayout="true"/>
+ android:title="@string/pref_videoBehavior_title"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/playback_control">
@@ -76,6 +73,11 @@
android:key="prefPlaybackTimeRespectsSpeed"
android:summary="@string/pref_playback_time_respects_speed_sum"
android:title="@string/pref_playback_time_respects_speed_title"/>
+ <SwitchPreference
+ android:defaultValue="false"
+ android:key="prefStreamOverDownload"
+ android:summary="@string/pref_stream_over_download_sum"
+ android:title="@string/pref_stream_over_download_title"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/queue_label">
@@ -85,12 +87,12 @@
android:key="prefEnqueueDownloaded"
android:summary="@string/pref_enqueue_downloaded_summary"
android:title="@string/pref_enqueue_downloaded_title" />
- <SwitchPreference
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefQueueAddToFront"
- android:summary="@string/pref_queueAddToFront_sum"
- android:title="@string/pref_queueAddToFront_title"/>
+ <ListPreference
+ android:defaultValue="BACK"
+ android:entries="@array/enqueue_location_options"
+ android:entryValues="@array/enqueue_location_values"
+ android:key="prefEnqueueLocation"
+ android:title="@string/pref_enqueue_location_title"/>
<SwitchPreference
android:defaultValue="true"
android:enabled="true"
@@ -103,8 +105,7 @@
android:entryValues="@array/smart_mark_as_played_values"
android:key="prefSmartMarkAsPlayedSecs"
android:summary="@string/pref_smart_mark_as_played_sum"
- android:title="@string/pref_smart_mark_as_played_title"
- app:useStockLayout="true"/>
+ android:title="@string/pref_smart_mark_as_played_title"/>
<SwitchPreference
android:defaultValue="true"
android:enabled="true"
@@ -120,8 +121,7 @@
android:key="prefMediaPlayer"
android:title="@string/media_player"
android:summary="@string/pref_media_player_message"
- android:entryValues="@array/media_player_values"
- app:useStockLayout="true"/>
+ android:entryValues="@array/media_player_values"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/experimental_pref">
diff --git a/app/src/main/res/xml/preferences_user_interface.xml b/app/src/main/res/xml/preferences_user_interface.xml
index c48e9adc8..20a06c4c4 100644
--- a/app/src/main/res/xml/preferences_user_interface.xml
+++ b/app/src/main/res/xml/preferences_user_interface.xml
@@ -10,8 +10,7 @@
android:title="@string/pref_set_theme_title"
android:key="prefTheme"
android:summary="@string/pref_set_theme_sum"
- android:defaultValue="system"
- app:useStockLayout="true"/>
+ android:defaultValue="system"/>
<Preference
android:key="prefHiddenDrawerItems"
android:summary="@string/pref_nav_drawer_items_sum"
@@ -22,16 +21,20 @@
android:title="@string/pref_nav_drawer_feed_order_title"
android:key="prefDrawerFeedOrder"
android:summary="@string/pref_nav_drawer_feed_order_sum"
- android:defaultValue="0"
- app:useStockLayout="true"/>
+ android:defaultValue="0"/>
<ListPreference
android:entryValues="@array/nav_drawer_feed_counter_values"
android:entries="@array/nav_drawer_feed_counter_options"
android:title="@string/pref_nav_drawer_feed_counter_title"
android:key="prefDrawerFeedIndicator"
android:summary="@string/pref_nav_drawer_feed_counter_sum"
- android:defaultValue="0"
- app:useStockLayout="true"/>
+ android:defaultValue="0"/>
+ <SwitchPreference
+ android:title="@string/pref_episode_cover_title"
+ android:key="prefEpisodeCover"
+ android:summary="@string/pref_episode_cover_summary"
+ android:defaultValue="true"
+ android:enabled="true"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/external_elements">
<SwitchPreference
@@ -64,7 +67,6 @@
android:key="prefBackButtonBehavior"
android:title="@string/pref_back_button_behavior_title"
android:summary="@string/pref_back_button_behavior_sum"
- android:defaultValue="default"
- app:useStockLayout="true"/>
+ android:defaultValue="default"/>
</PreferenceCategory>
</PreferenceScreen>
diff --git a/app/src/main/templates/about.html b/app/src/main/templates/about.html
deleted file mode 100644
index fd70ab549..000000000
--- a/app/src/main/templates/about.html
+++ /dev/null
@@ -1,183 +0,0 @@
-<!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');
- }
-
- html, body {
- background: @background@;
- margin: 0;
- padding: 0;
- }
-
- * {
- color: @fontcolor@;
- font-family: roboto-Light;
- font-size: 12pt;
- }
-
- img#logo {
- display: block;
- margin-left: auto;
- margin-right: auto;
- max-height: 200px;
- max-height: 50vh;
- max-width: 100%;
- height: auto;
- width: auto;
- }
-
- div#logobackground{
- width: 100%;
- background: #42a5f5;
- }
-
- .card {
- background: @card_background@;
- margin: 10px;
- padding: 10px;
- border: 1px solid @card_border@;
- border-top-width: 0;
- border-bottom-width: 2px;
- }
-
- h1 {
- font-size: 15pt;
- margin-left: 20px;
- }
-
- h2 {
- font-size: 13pt;
- margin-top: 0px;
- }
-
- a {
- font-size: 14px;
- color: #00A8DF;
- text-decoration: none;
- }
-
- </style>
- <title>About AntennaPod</title>
-</head>
-<body>
-<div id="logobackground">
-<img id="logo" src="file:///android_asset/logo.png" alt="Logo"/>
-</div>
-
-<h1>AntennaPod</h1>
-
-<div class="card">
-<table>
-<tr><td>Version:</td><td><b>@versionname@</b></td></tr>
-<tr><td>Commit:</td><td><b>@commit@</b></td></tr>
-</table>
-</div>
-
-<div class="card">
-Created by Daniel Oeh<br />
-Copyright &copy; 2012-@year@<br />
-AntennaPod Contributors <a href="CONTRIBUTORS.txt">(View)</a><br />
-Licensed under the MIT License <a href="LICENSE.txt">(View)</a><br />
-Privacy Policy <a href="https://antennapod.org/privacy.html">(View)</a>
-</div>
-
-<h1>Used libraries</h1>
-
-<div class="card">
-<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-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>EventBus <a href="https://github.com/greenrobot/EventBus">(Link)</a></h2>
-by greenrobot, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>ExoPlayer <a href="https://github.com/google/ExoPlayer">(Link)</a></h2>
-by Google, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
- <h2>Floating Action Button Speed Dial <a href="https://github.com/leinardi/FloatingActionButtonSpeedDial">(Link)</a></h2>
- by Roberto Leinardi, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Glide <a href="https://github.com/bumptech/glide/">(Link)</a></h2>
-licensed under the Simplified BSD license <a href="LICENSE_GLIDE.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Iconify <a href="https://github.com/JoanZapata/android-iconify">(Link)</a></h2>
-by Joan Zapata, licensed under the Apache 2.0 license <a href="LICENSE_ANDROID_ICONIFY.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>jsoup <a href="http://jsoup.org/">(Link)</a></h2>
-licensed under the MIT license <a href="LICENSE_JSOUP.txt">(View)</a>
-</div>
-
-<div class="card">
-<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>
-</div>
-
-<div class="card">
-<h2>Material Design Icons <a href="https://github.com/Templarian/MaterialDesign">(Link)</a></h2>
-by Templarian, licensed under the SIL Open Font License, Version 1.1 <a href="LICENSE_SIL.txt">(View)</a>
-</div>
-
-<div class="card">
-<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>
-</div>
-
-<div class="card">
-<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>
-</div>
-
-<div class="card">
-<h2>Okio <a href="https://github.com/square/okio">(Link)</a></h2>
-by Square, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<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>
-</div>
-
-<div class="card">
-<h2>RecyclerView-FlexibleDivider <a href="https://github.com/yqritc/RecyclerView-FlexibleDivider">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>RxAndroid <a href="https://github.com/ReactiveX/RxAndroid">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>StackBlur <a href="https://github.com/kikoso/android-stackblur">(Link)</a></h2>
-by Enrique L&oacute;pez Ma&ntilde;as, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Triangle Label View <a href="https://github.com/shts/TriangleLabelView">(Link)</a></h2>
-by Shota Saito, licensed under the Apache 2.0 license <a href="LICENSE_TRIANGLE_LABEL_VIEW.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>AntennaPod-AudioPlayer <a href="https://github.com/AntennaPod/AntennaPod-AudioPlayer/">(Link)</a></h2>
-by the AntennaPod team, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-</body>
-</html>