summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java52
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java82
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java33
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java131
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java5
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java2
-rw-r--r--app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java17
-rw-r--r--app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java7
-rw-r--r--app/src/free/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java14
-rw-r--r--app/src/main/AndroidManifest.xml74
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java26
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java67
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java41
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/SelectSubscriptionActivity.java162
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java73
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java24
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/FeedSortDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java29
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java70
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java38
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/TagSettingsDialog.java44
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java35
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java66
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java125
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java46
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java131
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java76
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedStatisticsDialogFragment.java42
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedStatisticsFragment.java93
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java25
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java35
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/actions/FeedMultiSelectActionHandler.java54
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java128
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java201
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java28
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/GpodderAuthenticationFragment.java (renamed from app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java)52
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/NextcloudAuthenticationFragment.java92
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/SynchronizationPreferencesFragment.java222
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/swipeactions/SwipeActions.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java2
-rw-r--r--app/src/main/res/layout/alertdialog_sync_provider_chooser.xml24
-rw-r--r--app/src/main/res/layout/audioplayer_fragment.xml1
-rw-r--r--app/src/main/res/layout/edit_tags_dialog.xml10
-rw-r--r--app/src/main/res/layout/episode_filter_dialog.xml17
-rw-r--r--app/src/main/res/layout/feed_statistics.xml113
-rw-r--r--app/src/main/res/layout/feed_statistics_dialog.xml7
-rw-r--r--app/src/main/res/layout/feedinfo.xml294
-rw-r--r--app/src/main/res/layout/feeditem_pager_fragment.xml1
-rw-r--r--app/src/main/res/layout/feedsettings.xml1
-rw-r--r--app/src/main/res/layout/fragment_subscriptions.xml5
-rw-r--r--app/src/main/res/layout/nextcloud_auth_dialog.xml63
-rw-r--r--app/src/main/res/layout/playback_speed_feed_setting_dialog.xml37
-rw-r--r--app/src/main/res/layout/playback_speed_seek_bar.xml2
-rw-r--r--app/src/main/res/layout/quick_feed_discovery_item.xml4
-rw-r--r--app/src/main/res/layout/subscription_selection_activity.xml58
-rw-r--r--app/src/main/res/layout/time_dialog.xml196
-rw-r--r--app/src/main/res/menu/cast_enabled.xml10
-rw-r--r--app/src/main/res/menu/nav_feed_action_speeddial.xml5
-rw-r--r--app/src/main/res/menu/nav_feed_context.xml2
-rw-r--r--app/src/main/res/xml/feed_settings.xml97
-rw-r--r--app/src/main/res/xml/network_security_config.xml9
-rw-r--r--app/src/main/res/xml/preferences.xml2
-rw-r--r--app/src/main/res/xml/preferences_gpodder.xml28
-rw-r--r--app/src/main/res/xml/preferences_playback.xml8
-rw-r--r--app/src/main/res/xml/preferences_synchronization.xml31
-rw-r--r--app/src/main/res/xml/provider_paths.xml2
-rw-r--r--app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java157
-rw-r--r--app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java21
-rw-r--r--app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java480
-rw-r--r--app/src/play/java/de/danoeh/antennapod/fragment/CustomMRControllerDialogFragment.java15
-rw-r--r--app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java48
-rw-r--r--app/src/play/res/layout/media_router_controller.xml41
113 files changed, 2270 insertions, 2348 deletions
diff --git a/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java
index 9f7af3a16..2ab2361d7 100644
--- a/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java
@@ -11,6 +11,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
import org.awaitility.Awaitility;
import org.hamcrest.Matcher;
import org.junit.After;
@@ -32,7 +33,6 @@ import de.danoeh.antennapod.model.feed.FeedMedia;
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.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.IntentUtils;
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java b/app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java
index ae38fd5e7..4d57b9b43 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java
@@ -1,9 +1,10 @@
package de.test.antennapod.service.playback;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import de.danoeh.antennapod.model.playback.MediaType;
-import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.model.playback.Playable;
+import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
public class CancelablePSMPCallback implements PlaybackServiceMediaPlayer.PSMPCallback {
@@ -35,22 +36,6 @@ public class CancelablePSMPCallback implements PlaybackServiceMediaPlayer.PSMPCa
}
@Override
- public void playbackSpeedChanged(float s) {
- if (isCancelled) {
- return;
- }
- originalCallback.playbackSpeedChanged(s);
- }
-
- @Override
- public void onBufferingUpdate(int percent) {
- if (isCancelled) {
- return;
- }
- originalCallback.onBufferingUpdate(percent);
- }
-
- @Override
public void onMediaChanged(boolean reloadUI) {
if (isCancelled) {
return;
@@ -59,22 +44,6 @@ public class CancelablePSMPCallback implements PlaybackServiceMediaPlayer.PSMPCa
}
@Override
- public boolean onMediaPlayerInfo(int code, int resourceId) {
- if (isCancelled) {
- return true;
- }
- return originalCallback.onMediaPlayerInfo(code, resourceId);
- }
-
- @Override
- public boolean onMediaPlayerError(Object inObj, int what, int extra) {
- if (isCancelled) {
- return true;
- }
- return originalCallback.onMediaPlayerError(inObj, what, extra);
- }
-
- @Override
public void onPostPlayback(@NonNull Playable media, boolean ended, boolean skipped, boolean playingNext) {
if (isCancelled) {
return;
@@ -106,6 +75,15 @@ public class CancelablePSMPCallback implements PlaybackServiceMediaPlayer.PSMPCa
return originalCallback.getNextInQueue(currentMedia);
}
+ @Nullable
+ @Override
+ public Playable findMedia(@NonNull String url) {
+ if (isCancelled) {
+ return null;
+ }
+ return originalCallback.findMedia(url);
+ }
+
@Override
public void onPlaybackEnded(MediaType mediaType, boolean stopPlaying) {
if (isCancelled) {
@@ -113,4 +91,12 @@ public class CancelablePSMPCallback implements PlaybackServiceMediaPlayer.PSMPCa
}
originalCallback.onPlaybackEnded(mediaType, stopPlaying);
}
+
+ @Override
+ public void ensureMediaInfoLoaded(@NonNull Playable media) {
+ if (isCancelled) {
+ return;
+ }
+ originalCallback.ensureMediaInfoLoaded(media);
+ }
} \ No newline at end of file
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java b/app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java
index 29a854f20..fb55c7ad0 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java
@@ -1,69 +1,59 @@
package de.test.antennapod.service.playback;
import androidx.annotation.NonNull;
-import androidx.annotation.StringRes;
+import androidx.annotation.Nullable;
import de.danoeh.antennapod.model.playback.MediaType;
-import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.model.playback.Playable;
+import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
public class DefaultPSMPCallback implements PlaybackServiceMediaPlayer.PSMPCallback {
- @Override
- public void statusChanged(PlaybackServiceMediaPlayer.PSMPInfo newInfo) {
+ @Override
+ public void statusChanged(PlaybackServiceMediaPlayer.PSMPInfo newInfo) {
- }
+ }
- @Override
- public void shouldStop() {
+ @Override
+ public void shouldStop() {
- }
+ }
- @Override
- public void playbackSpeedChanged(float s) {
+ @Override
+ public void onMediaChanged(boolean reloadUI) {
- }
+ }
- @Override
- public void onBufferingUpdate(int percent) {
+ @Override
+ public void onPostPlayback(@NonNull Playable media, boolean ended, boolean skipped, boolean playingNext) {
- }
+ }
- @Override
- public void onMediaChanged(boolean reloadUI) {
+ @Override
+ public void onPlaybackStart(@NonNull Playable playable, int position) {
- }
+ }
- @Override
- public boolean onMediaPlayerInfo(int code, @StringRes int resourceId) {
- return false;
- }
+ @Override
+ public void onPlaybackPause(Playable playable, int position) {
- @Override
- public boolean onMediaPlayerError(Object inObj, int what, int extra) {
- return false;
- }
+ }
- @Override
- public void onPostPlayback(@NonNull Playable media, boolean ended, boolean skipped, boolean playingNext) {
+ @Override
+ public Playable getNextInQueue(Playable currentMedia) {
+ return null;
+ }
- }
+ @Nullable
+ @Override
+ public Playable findMedia(@NonNull String url) {
+ return null;
+ }
- @Override
- public void onPlaybackStart(@NonNull Playable playable, int position) {
+ @Override
+ public void onPlaybackEnded(MediaType mediaType, boolean stopPlaying) {
- }
+ }
- @Override
- public void onPlaybackPause(Playable playable, int position) {
-
- }
-
- @Override
- public Playable getNextInQueue(Playable currentMedia) {
- return null;
- }
-
- @Override
- public void onPlaybackEnded(MediaType mediaType, boolean stopPlaying) {
-
- }
- } \ No newline at end of file
+ @Override
+ public void ensureMediaInfoLoaded(@NonNull Playable media) {
+ }
+} \ No newline at end of file
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 dfb0e3e36..32298200e 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
@@ -5,6 +5,8 @@ import android.content.Context;
import androidx.test.filters.MediumTest;
import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
+import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
import de.test.antennapod.EspressoTestUtils;
import junit.framework.AssertionFailedError;
@@ -24,8 +26,6 @@ import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.core.service.playback.LocalPSMP;
-import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.model.playback.Playable;
import de.test.antennapod.util.service.download.HTTPBin;
@@ -514,13 +514,6 @@ public class PlaybackServiceMediaPlayerTest {
if (assertionError == null)
assertionError = new AssertionFailedError("Unexpected call to shouldStop");
}
-
- @Override
- public boolean onMediaPlayerError(Object inObj, int what, int extra) {
- if (assertionError == null)
- assertionError = new AssertionFailedError("Unexpected call to onMediaPlayerError");
- return false;
- }
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
@@ -604,14 +597,6 @@ public class PlaybackServiceMediaPlayerTest {
}
}
-
- @Override
- public boolean onMediaPlayerError(Object inObj, int what, int extra) {
- if (assertionError == null) {
- assertionError = new AssertionFailedError("Unexpected call of onMediaPlayerError");
- }
- return false;
- }
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
if (initialState == PlayerStatus.PREPARED || initialState == PlayerStatus.PLAYING || initialState == PlayerStatus.PAUSED) {
@@ -664,13 +649,6 @@ public class PlaybackServiceMediaPlayerTest {
}
}
}
-
- @Override
- public boolean onMediaPlayerError(Object inObj, int what, int extra) {
- if (assertionError == null)
- assertionError = new AssertionFailedError("Unexpected call to onMediaPlayerError");
- return false;
- }
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
@@ -738,13 +716,6 @@ public class PlaybackServiceMediaPlayerTest {
}
}
}
-
- @Override
- public boolean onMediaPlayerError(Object inObj, int what, int extra) {
- if (assertionError == null)
- assertionError = new AssertionFailedError("Unexpected call to onMediaPlayerError");
- return false;
- }
});
PlaybackServiceMediaPlayer psmp = new LocalPSMP(c, callback);
Playable p = writeTestPlayable(playableFileUrl, PLAYABLE_LOCAL_URL);
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
index 7803144e1..013d4db50 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
@@ -5,10 +5,12 @@ import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.LargeTest;
+import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
import de.danoeh.antennapod.core.widget.WidgetUpdater;
import org.awaitility.Awaitility;
import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -19,7 +21,7 @@ import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-import de.danoeh.antennapod.core.event.QueueEvent;
+import de.danoeh.antennapod.event.QueueEvent;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
@@ -144,7 +146,7 @@ public class PlaybackServiceTaskManagerTest {
FeedItem item = DBReader.getFeedItem(testItem.getId());
item.getMedia().setDownloaded(true);
item.getMedia().setFile_url("file://123");
- item.setAutoDownload(false);
+ item.disableAutoDownload();
DBWriter.setFeedMedia(item.getMedia()).get();
DBWriter.setFeedItem(item).get();
@@ -173,21 +175,6 @@ public class PlaybackServiceTaskManagerTest {
}
@Override
- public void onSleepTimerAlmostExpired(long timeLeft) {
-
- }
-
- @Override
- public void onSleepTimerExpired() {
-
- }
-
- @Override
- public void onSleepTimerReset() {
-
- }
-
- @Override
public WidgetUpdater.WidgetState requestWidgetState() {
return null;
}
@@ -234,21 +221,6 @@ public class PlaybackServiceTaskManagerTest {
}
@Override
- public void onSleepTimerAlmostExpired(long timeLeft) {
-
- }
-
- @Override
- public void onSleepTimerExpired() {
-
- }
-
- @Override
- public void onSleepTimerReset() {
-
- }
-
- @Override
public WidgetUpdater.WidgetState requestWidgetState() {
countDownLatch.countDown();
return null;
@@ -325,42 +297,20 @@ public class PlaybackServiceTaskManagerTest {
final long TIME = 2000;
final long TIMEOUT = 2 * TIME;
final CountDownLatch countDownLatch = new CountDownLatch(1);
- PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, new PlaybackServiceTaskManager.PSTMCallback() {
- @Override
- public void positionSaverTick() {
-
- }
-
- @Override
- public void onSleepTimerAlmostExpired(long timeLeft) {
-
- }
-
- @Override
- public void onSleepTimerExpired() {
+ Object timerReceiver = new Object() {
+ @Subscribe
+ public void sleepTimerUpdate(SleepTimerUpdatedEvent event) {
if (countDownLatch.getCount() == 0) {
fail();
}
countDownLatch.countDown();
}
-
- @Override
- public void onSleepTimerReset() {
-
- }
-
- @Override
- public WidgetUpdater.WidgetState requestWidgetState() {
- return null;
- }
-
- @Override
- public void onChapterLoaded(Playable media) {
-
- }
- });
+ };
+ EventBus.getDefault().register(timerReceiver);
+ PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
pstm.setSleepTimer(TIME);
countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+ EventBus.getDefault().unregister(timerReceiver);
pstm.shutdown();
}
@@ -368,44 +318,26 @@ public class PlaybackServiceTaskManagerTest {
@UiThreadTest
public void testDisableSleepTimer() throws InterruptedException {
final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
- final long TIME = 1000;
+ final long TIME = 5000;
final long TIMEOUT = 2 * TIME;
final CountDownLatch countDownLatch = new CountDownLatch(1);
- PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, new PlaybackServiceTaskManager.PSTMCallback() {
- @Override
- public void positionSaverTick() {
-
- }
-
- @Override
- public void onSleepTimerAlmostExpired(long timeLeft) {
-
- }
-
- @Override
- public void onSleepTimerExpired() {
- fail("Sleeptimer expired");
- }
-
- @Override
- public void onSleepTimerReset() {
-
- }
-
- @Override
- public WidgetUpdater.WidgetState requestWidgetState() {
- return null;
- }
-
- @Override
- public void onChapterLoaded(Playable media) {
-
+ Object timerReceiver = new Object() {
+ @Subscribe
+ public void sleepTimerUpdate(SleepTimerUpdatedEvent event) {
+ if (event.isOver()) {
+ countDownLatch.countDown();
+ } else if (event.getTimeLeft() == 1) {
+ fail("Arrived at 1 but should have been cancelled");
+ }
}
- });
+ };
+ PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
+ EventBus.getDefault().register(timerReceiver);
pstm.setSleepTimer(TIME);
pstm.disableSleepTimer();
assertFalse(countDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS));
pstm.shutdown();
+ EventBus.getDefault().unregister(timerReceiver);
}
@Test
@@ -436,21 +368,6 @@ public class PlaybackServiceTaskManagerTest {
}
@Override
- public void onSleepTimerAlmostExpired(long timeLeft) {
-
- }
-
- @Override
- public void onSleepTimerExpired() {
-
- }
-
- @Override
- public void onSleepTimerReset() {
-
- }
-
- @Override
public WidgetUpdater.WidgetState requestWidgetState() {
return null;
}
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 c71bff357..74414240f 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -126,8 +126,9 @@ public class PreferencesTest {
clickPreference(R.string.user_interface_label);
String[] buttons = res.getStringArray(R.array.compact_notification_buttons_options);
clickPreference(R.string.pref_compact_notification_buttons_title);
- // First uncheck checkbox
- onView(withText(buttons[2])).perform(click());
+ // First uncheck checkboxes
+ onView(withText(buttons[0])).perform(click());
+ onView(withText(buttons[1])).perform(click());
// Now try to check all checkboxes
onView(withText(buttons[0])).perform(click());
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 b25f957d3..eedb2d9de 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
@@ -2,8 +2,8 @@ package de.test.antennapod.ui;
import android.content.Context;
import android.util.Log;
-import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
-import de.danoeh.antennapod.core.event.QueueEvent;
+import de.danoeh.antennapod.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.event.QueueEvent;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
diff --git a/app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java b/app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java
index 601bba853..7e8fc1205 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java
@@ -8,7 +8,7 @@ import org.greenrobot.eventbus.Subscribe;
import java.util.ArrayList;
import java.util.List;
-import de.danoeh.antennapod.core.event.FeedItemEvent;
+import de.danoeh.antennapod.event.FeedItemEvent;
import io.reactivex.functions.Consumer;
/**
diff --git a/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java b/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
deleted file mode 100644
index 98d506f65..000000000
--- a/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import android.view.Menu;
-
-/**
- * Activity that allows for showing the MediaRouter button whenever there's a cast device in the
- * network.
- */
-public abstract class CastEnabledActivity extends AppCompatActivity {
- public static final String TAG = "CastEnabledActivity";
-
- public final void requestCastButton(Menu menu) {
- // no-op
- }
-}
diff --git a/app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java b/app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java
deleted file mode 100644
index fb23dfa1a..000000000
--- a/app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package de.danoeh.antennapod.config;
-
-import de.danoeh.antennapod.core.CastCallbacks;
-
-class CastCallbackImpl implements CastCallbacks {
-
-}
diff --git a/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java b/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
deleted file mode 100644
index e096f883f..000000000
--- a/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package de.danoeh.antennapod.preferences;
-
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.fragment.preferences.PlaybackPreferencesFragment;
-
-/**
- * Implements functions from PreferenceController that are flavor dependent.
- */
-public class PreferenceControllerFlavorHelper {
-
- public static void setupFlavoredUI(PlaybackPreferencesFragment ui) {
- ui.findPreference(UserPreferences.PREF_CAST_ENABLED).setEnabled(false);
- }
-}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 47648f9d3..0f8242e63 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -40,7 +40,8 @@
android:supportsRtl="true"
android:logo="@mipmap/ic_launcher"
android:resizeableActivity="true"
- android:allowAudioPlaybackCapture="true">
+ android:allowAudioPlaybackCapture="true"
+ android:networkSecurityConfig="@xml/network_security_config">
<meta-data android:name="android.webkit.WebView.MetricsOptOut"
@@ -53,6 +54,9 @@
<meta-data
android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI3a05VToCTlqBymJrbFGaKQMvF-bBAuLsOdavBA"/>
+ <meta-data
+ android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
+ android:value="de.danoeh.antennapod.playback.cast.CastOptionsProvider" />
<!-- Version < 3.0. DeX Mode and Screen Mirroring support -->
<meta-data android:name="com.samsung.android.keepalive.density" android:value="true"/>
@@ -65,15 +69,13 @@
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
+ <action android:name="android.intent.action.MUSIC_PLAYER" />
- <intent-filter>
- <action android:name=
- "android.media.action.MEDIA_PLAY_FROM_SEARCH" />
- <category android:name=
- "android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ <category android:name="android.intent.category.APP_MUSIC" />
</intent-filter>
<meta-data
@@ -98,13 +100,6 @@
android:host="antennapod.org"
android:pathPrefix="/deeplink/main"
android:scheme="https" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.VIEW" />
-
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
-
<data
android:host="antennapod.org"
android:pathPrefix="/deeplink/search"
@@ -144,11 +139,7 @@
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
- </intent-filter>
- <intent-filter>
<action android:name="de.danoeh.antennapod.FORCE_WIDGET_UPDATE"/>
- </intent-filter>
- <intent-filter>
<action android:name="de.danoeh.antennapod.STOP_WIDGET_UPDATE"/>
</intent-filter>
<meta-data
@@ -165,6 +156,7 @@
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
+ <action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
@@ -172,27 +164,15 @@
<data android:mimeType="text/xml"/>
<data android:mimeType="text/x-opml"/>
<data android:mimeType="application/xml"/>
- <data android:mimeType="application/octet-stream"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
-
- <data android:host="*"/>
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.SEND"/>
-
- <category android:name="android.intent.category.DEFAULT"/>
- <category android:name="android.intent.category.BROWSABLE"/>
-
- <data android:mimeType="text/xml"/>
- <data android:mimeType="text/plain"/>
- <data android:mimeType="text/x-opml"/>
- <data android:mimeType="application/xml"/>
- <data android:mimeType="application/octet-stream"/>
-
<data android:scheme="http"/>
<data android:scheme="https"/>
+
+ <data android:host="*"/>
+ <data android:pathPattern=".*.xml" />
+ <data android:pathPattern=".*.opml" />
</intent-filter>
</activity>
<activity
@@ -315,6 +295,18 @@
</intent-filter>
<intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+
+ <data android:pathPattern="/.*/podcast/.*" />
+ <data android:host="podcasts.apple.com" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ </intent-filter>
+
+ <intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
@@ -324,6 +316,16 @@
</activity>
+ <activity android:name=".activity.SelectSubscriptionActivity"
+ android:label="@string/shortcut_subscription_label"
+ android:icon="@drawable/ic_folder_shortcut"
+ android:theme="@style/Theme.AntennaPod.Dark.Translucent"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.CREATE_SHORTCUT" />
+ </intent-filter>
+ </activity>
+
<receiver
android:name=".receiver.ConnectivityActionReceiver"
android:exported="true">
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
index aa59e4e96..f7c96a93a 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
@@ -7,7 +7,6 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import com.google.android.material.snackbar.Snackbar;
@@ -103,22 +102,21 @@ public class BugReportActivity extends AppCompatActivity {
Runtime.getRuntime().exec(cmd);
//share file
try {
- Intent i = new Intent(Intent.ACTION_SEND);
- i.setType("text/*");
+ Intent intent = new Intent(Intent.ACTION_SEND);
+ intent.setType("text/*");
String authString = getString(de.danoeh.antennapod.core.R.string.provider_authority);
Uri fileUri = FileProvider.getUriForFile(this, authString, filename);
- i.putExtra(Intent.EXTRA_STREAM, fileUri);
- i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- PackageManager pm = getPackageManager();
- List<ResolveInfo> resInfos = pm.queryIntentActivities(i, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfos) {
- String packageName = resolveInfo.activityInfo.packageName;
- grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- }
+ intent.putExtra(Intent.EXTRA_STREAM, fileUri);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
String chooserTitle = getString(de.danoeh.antennapod.core.R.string.share_file_label);
- startActivity(Intent.createChooser(i, chooserTitle));
+ Intent chooser = Intent.createChooser(intent, chooserTitle);
+ List<ResolveInfo> resInfos = getPackageManager()
+ .queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfos) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ startActivity(chooser);
} catch (Exception e) {
e.printStackTrace();
int strResId = R.string.log_file_share_exception;
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 f07ad6ad5..7dc760e76 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -38,6 +38,7 @@ import com.bumptech.glide.Glide;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.snackbar.Snackbar;
+import de.danoeh.antennapod.playback.cast.CastEnabledActivity;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
import org.greenrobot.eventbus.EventBus;
@@ -45,7 +46,7 @@ import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.MessageEvent;
+import de.danoeh.antennapod.event.MessageEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
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 4dca1fda7..9148a9949 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -6,7 +6,6 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.LightingColorFilter;
-import android.os.Build;
import android.os.Bundle;
import android.text.Spannable;
import android.text.SpannableString;
@@ -19,6 +18,7 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -31,8 +31,8 @@ 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.event.FeedListUpdateEvent;
-import de.danoeh.antennapod.core.event.PlayerStatusEvent;
+import de.danoeh.antennapod.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.event.PlayerStatusEvent;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
@@ -60,7 +60,6 @@ import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.danoeh.antennapod.discovery.PodcastSearcherRegistry;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedPreferences;
-import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.model.playback.RemoteMedia;
import de.danoeh.antennapod.parser.feed.UnsupportedFeedtypeException;
import io.reactivex.Maybe;
@@ -101,6 +100,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
private Feed feed;
private String selectedDownloadUrl;
private Downloader downloader;
+ private String username = null;
+ private String password = null;
private boolean isPaused;
private boolean didPressSubscribe = false;
@@ -144,12 +145,11 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
if (feedUrl.contains("subscribeonandroid.com")) {
feedUrl = feedUrl.replaceFirst("((www.)?(subscribeonandroid.com/))", "");
}
- if (savedInstanceState == null) {
- lookupUrlAndDownload(feedUrl, null, null);
- } else {
- lookupUrlAndDownload(feedUrl, savedInstanceState.getString("username"),
- savedInstanceState.getString("password"));
+ if (savedInstanceState != null) {
+ username = savedInstanceState.getString("username");
+ password = savedInstanceState.getString("password");
}
+ lookupUrlAndDownload(feedUrl);
}
}
@@ -210,10 +210,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- if (feed != null && feed.getPreferences() != null) {
- outState.putString("username", feed.getPreferences().getUsername());
- outState.putString("password", feed.getPreferences().getPassword());
- }
+ outState.putString("username", username);
+ outState.putString("password", password);
}
private void resetIntent(String url) {
@@ -242,32 +240,23 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
return super.onOptionsItemSelected(item);
}
- private void lookupUrlAndDownload(String url, String username, String password) {
+ private void lookupUrlAndDownload(String url) {
download = PodcastSearcherRegistry.lookupUrl(url)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
- .subscribe(lookedUpUrl -> startFeedDownload(lookedUpUrl, username, password),
+ .subscribe(this::startFeedDownload,
error -> {
showNoPodcastFoundError();
Log.e(TAG, Log.getStackTraceString(error));
});
}
- private void startFeedDownload(String url, String username, String password) {
+ private void startFeedDownload(String url) {
Log.d(TAG, "Starting feed download");
url = URLChecker.prepareURL(url);
feed = new Feed(url, null);
- if (username != null && password != null) {
- feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL,
- VolumeAdaptionSetting.OFF, username, password));
- }
- String fileUrl;
- try {
- fileUrl = DownloadRequester.getInstance().getDownloadPathForFeed(feed).getAbsolutePath();
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- fileUrl = new File(getCacheDir(), FileNameGenerator.generateFileName(feed.getDownload_url())).toString();
- }
+ String fileUrl = new File(getExternalCacheDir(),
+ FileNameGenerator.generateFileName(feed.getDownload_url())).toString();
feed.setFile_url(fileUrl);
final DownloadRequest request = new DownloadRequest(feed.getFile_url(),
feed.getDownload_url(), "OnlineFeed", 0, Feed.FEEDFILETYPE_FEED, username, password,
@@ -293,6 +282,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
parseFeed();
} else if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
if (!isFinishing() && !isPaused) {
+ if (username != null && password != null) {
+ Toast.makeText(this, R.string.download_error_unauthorized, Toast.LENGTH_LONG).show();
+ }
dialog = new FeedViewAuthenticationDialog(OnlineFeedViewActivity.this,
R.string.authentication_notification_title,
downloader.getDownloadRequest().getSource()).create();
@@ -458,8 +450,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
final int MAX_LINES_COLLAPSED = 10;
description.setMaxLines(MAX_LINES_COLLAPSED);
description.setOnClickListener(v -> {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
- && description.getMaxLines() > MAX_LINES_COLLAPSED) {
+ if (description.getMaxLines() > MAX_LINES_COLLAPSED) {
description.setMaxLines(MAX_LINES_COLLAPSED);
} else {
description.setMaxLines(2000);
@@ -642,21 +633,17 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
if (urls.size() == 1) {
// Skip dialog and display the item directly
resetIntent(urls.get(0));
- startFeedDownload(urls.get(0), null, null);
+ startFeedDownload(urls.get(0));
return true;
}
- final ArrayAdapter<String> adapter = new ArrayAdapter<>(OnlineFeedViewActivity.this, R.layout.ellipsize_start_listitem, R.id.txtvTitle, titles);
+ final ArrayAdapter<String> adapter = new ArrayAdapter<>(OnlineFeedViewActivity.this,
+ R.layout.ellipsize_start_listitem, R.id.txtvTitle, titles);
DialogInterface.OnClickListener onClickListener = (dialog, which) -> {
String selectedUrl = urls.get(which);
dialog.dismiss();
resetIntent(selectedUrl);
- FeedPreferences prefs = feed.getPreferences();
- if(prefs != null) {
- startFeedDownload(selectedUrl, prefs.getUsername(), prefs.getPassword());
- } else {
- startFeedDownload(selectedUrl, null, null);
- }
+ startFeedDownload(selectedUrl);
};
AlertDialog.Builder ab = new AlertDialog.Builder(OnlineFeedViewActivity.this)
@@ -679,7 +666,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
private final String feedUrl;
FeedViewAuthenticationDialog(Context context, int titleRes, String feedUrl) {
- super(context, titleRes, true, null, null);
+ super(context, titleRes, true, username, password);
this.feedUrl = feedUrl;
}
@@ -691,7 +678,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
@Override
protected void onConfirmed(String username, String password) {
- startFeedDownload(feedUrl, username, password);
+ OnlineFeedViewActivity.this.username = username;
+ OnlineFeedViewActivity.this.password = password;
+ startFeedDownload(feedUrl);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java
index a6810715c..3d0c9d113 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.activity;
+import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
@@ -15,7 +16,9 @@ import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
-import androidx.annotation.NonNull;
+
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts.RequestPermission;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
@@ -35,7 +38,6 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import org.apache.commons.io.ByteOrderMark;
import org.apache.commons.io.input.BOMInputStream;
-import org.apache.commons.lang3.ArrayUtils;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -48,7 +50,6 @@ import java.util.List;
* */
public class OpmlImportActivity extends AppCompatActivity {
private static final String TAG = "OpmlImportBaseActivity";
- private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 5;
@Nullable private Uri uri;
OpmlSelectionBinding viewBinding;
private ArrayAdapter<String> listAdapter;
@@ -198,27 +199,23 @@ public class OpmlImportActivity extends AppCompatActivity {
}
private void requestPermission() {
- String[] permissions = { android.Manifest.permission.READ_EXTERNAL_STORAGE };
- ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_READ_EXTERNAL_STORAGE);
+ requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE);
}
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
- @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, 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();
- }
- }
+ private final ActivityResultLauncher<String> requestPermissionLauncher =
+ registerForActivityResult(new RequestPermission(), isGranted -> {
+ if (isGranted) {
+ 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() {
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
index 600204554..1fc16ab32 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
@@ -6,6 +6,7 @@ import android.os.Bundle;
import android.provider.Settings;
import android.view.Menu;
import android.view.MenuItem;
+
import android.view.View;
import android.view.inputmethod.InputMethodManager;
@@ -21,13 +22,13 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.databinding.SettingsActivityBinding;
import de.danoeh.antennapod.fragment.preferences.AutoDownloadPreferencesFragment;
-import de.danoeh.antennapod.fragment.preferences.GpodderPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.ImportExportPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.MainPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.NetworkPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.NotificationPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.PlaybackPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.StoragePreferencesFragment;
+import de.danoeh.antennapod.fragment.preferences.synchronization.SynchronizationPreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.SwipePreferencesFragment;
import de.danoeh.antennapod.fragment.preferences.UserInterfacePreferencesFragment;
@@ -76,8 +77,8 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe
prefFragment = new ImportExportPreferencesFragment();
} else if (screen == R.xml.preferences_autodownload) {
prefFragment = new AutoDownloadPreferencesFragment();
- } else if (screen == R.xml.preferences_gpodder) {
- prefFragment = new GpodderPreferencesFragment();
+ } else if (screen == R.xml.preferences_synchronization) {
+ prefFragment = new SynchronizationPreferencesFragment();
} else if (screen == R.xml.preferences_playback) {
prefFragment = new PlaybackPreferencesFragment();
} else if (screen == R.xml.preferences_notifications) {
@@ -101,8 +102,8 @@ public class PreferenceActivity extends AppCompatActivity implements SearchPrefe
return R.string.import_export_pref;
} else if (preferences == R.xml.preferences_user_interface) {
return R.string.user_interface_label;
- } else if (preferences == R.xml.preferences_gpodder) {
- return R.string.gpodnet_main_label;
+ } else if (preferences == R.xml.preferences_synchronization) {
+ return R.string.synchronization_pref;
} else if (preferences == R.xml.preferences_notifications) {
return R.string.notification_pref_fragment;
} else if (preferences == R.xml.feed_settings) {
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/SelectSubscriptionActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/SelectSubscriptionActivity.java
new file mode 100644
index 000000000..4ffed949e
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/activity/SelectSubscriptionActivity.java
@@ -0,0 +1,162 @@
+package de.danoeh.antennapod.activity;
+
+import static de.danoeh.antennapod.activity.MainActivity.EXTRA_FEED_ID;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.content.pm.ShortcutInfoCompat;
+import androidx.core.content.pm.ShortcutManagerCompat;
+import androidx.core.graphics.drawable.IconCompat;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.DataSource;
+import com.bumptech.glide.load.engine.GlideException;
+import com.bumptech.glide.request.RequestListener;
+import com.bumptech.glide.request.RequestOptions;
+import com.bumptech.glide.request.target.Target;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.NavDrawerData;
+import de.danoeh.antennapod.databinding.SubscriptionSelectionActivityBinding;
+import de.danoeh.antennapod.model.feed.Feed;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+public class SelectSubscriptionActivity extends AppCompatActivity {
+
+ private static final String TAG = "SelectSubscription";
+
+ private Disposable disposable;
+ private volatile List<Feed> listItems;
+
+ private SubscriptionSelectionActivityBinding viewBinding;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTranslucentTheme());
+ super.onCreate(savedInstanceState);
+
+ viewBinding = SubscriptionSelectionActivityBinding.inflate(getLayoutInflater());
+ setContentView(viewBinding.getRoot());
+ setSupportActionBar(viewBinding.toolbar);
+ setTitle(R.string.shortcut_select_subscription);
+
+ viewBinding.transparentBackground.setOnClickListener(v -> finish());
+ viewBinding.card.setOnClickListener(null);
+
+ loadSubscriptions();
+
+ final Integer[] checkedPosition = new Integer[1];
+ viewBinding.list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ viewBinding.list.setOnItemClickListener((listView, view1, position, rowId) ->
+ checkedPosition[0] = position
+ );
+ viewBinding.shortcutBtn.setOnClickListener(view -> {
+ if (checkedPosition[0] != null && Intent.ACTION_CREATE_SHORTCUT.equals(
+ getIntent().getAction())) {
+ getBitmapFromUrl(listItems.get(checkedPosition[0]));
+ }
+ });
+
+ }
+
+ public List<Feed> getFeedItems(List<NavDrawerData.DrawerItem> items, List<Feed> result) {
+ for (NavDrawerData.DrawerItem item : items) {
+ if (item.type == NavDrawerData.DrawerItem.Type.TAG) {
+ getFeedItems(((NavDrawerData.TagDrawerItem) item).children, result);
+ } else {
+ Feed feed = ((NavDrawerData.FeedDrawerItem) item).feed;
+ if (!result.contains(feed)) {
+ result.add(feed);
+ }
+ }
+ }
+ return result;
+ }
+
+ private void addShortcut(Feed feed, Bitmap bitmap) {
+ Intent intent = new Intent(this, MainActivity.class);
+ intent.setAction(Intent.ACTION_MAIN);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.putExtra(EXTRA_FEED_ID, feed.getId());
+ String id = "subscription-" + feed.getId();
+ IconCompat icon;
+
+ if (bitmap != null) {
+ icon = IconCompat.createWithAdaptiveBitmap(bitmap);
+ } else {
+ icon = IconCompat.createWithResource(this, R.drawable.ic_folder_shortcut);
+ }
+
+ ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(this, id)
+ .setShortLabel(feed.getTitle())
+ .setLongLabel(feed.getFeedTitle())
+ .setIntent(intent)
+ .setIcon(icon)
+ .build();
+
+ setResult(RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this, shortcut));
+ finish();
+ }
+
+ private void getBitmapFromUrl(Feed feed) {
+ int iconSize = (int) (128 * getResources().getDisplayMetrics().density);
+ Glide.with(this)
+ .asBitmap()
+ .load(feed.getImageUrl())
+ .apply(new RequestOptions().override(iconSize, iconSize))
+ .listener(new RequestListener<Bitmap>() {
+ @Override
+ public boolean onLoadFailed(@Nullable GlideException e, Object model,
+ Target<Bitmap> target, boolean isFirstResource) {
+ addShortcut(feed, null);
+ return true;
+ }
+
+ @Override
+ public boolean onResourceReady(Bitmap resource, Object model,
+ Target<Bitmap> target, DataSource dataSource, boolean isFirstResource) {
+ addShortcut(feed, resource);
+ return true;
+ }
+ }).submit();
+ }
+
+ private void loadSubscriptions() {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ disposable = Observable.fromCallable(
+ () -> {
+ NavDrawerData data = DBReader.getNavDrawerData();
+ return getFeedItems(data.items, new ArrayList<>());
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ result -> {
+ listItems = result;
+ ArrayList<String> titles = new ArrayList<>();
+ for (Feed feed: result) {
+ titles.add(feed.getTitle());
+ }
+ ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
+ R.layout.simple_list_item_multiple_choice_on_start, titles);
+ viewBinding.list.setAdapter(adapter);
+ }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+} \ No newline at end of file
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 d436acf0d..4ff2a5775 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -36,11 +36,13 @@ import androidx.core.view.WindowCompat;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import com.bumptech.glide.Glide;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
-import de.danoeh.antennapod.core.event.ServiceEvent;
+import de.danoeh.antennapod.event.playback.BufferUpdateEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
+import de.danoeh.antennapod.event.PlayerErrorEvent;
+import de.danoeh.antennapod.event.playback.PlaybackServiceEvent;
+import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent;
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.Converter;
@@ -50,7 +52,6 @@ import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
-import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.databinding.VideoplayerActivityBinding;
import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
@@ -60,6 +61,8 @@ import de.danoeh.antennapod.dialog.SleepTimerDialog;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.model.playback.Playable;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
+import de.danoeh.antennapod.playback.cast.CastEnabledActivity;
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -193,40 +196,11 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar.
}
@Override
- public void onBufferStart() {
- viewBinding.progressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onBufferEnd() {
- viewBinding.progressBar.setVisibility(View.INVISIBLE);
- }
-
- @Override
- public void onBufferUpdate(float progress) {
- viewBinding.sbPosition.setSecondaryProgress((int) (progress * viewBinding.sbPosition.getMax()));
- }
-
- @Override
- public void handleError(int code) {
- final AlertDialog.Builder errorDialog = new AlertDialog.Builder(VideoplayerActivity.this);
- errorDialog.setTitle(R.string.error_label);
- errorDialog.setMessage(MediaPlayerError.getErrorString(VideoplayerActivity.this, code));
- errorDialog.setNeutralButton(android.R.string.ok, (dialog, which) -> finish());
- errorDialog.show();
- }
-
- @Override
public void onReloadNotification(int code) {
VideoplayerActivity.this.onReloadNotification(code);
}
@Override
- public void onSleepTimerUpdate() {
- supportInvalidateOptionsMenu();
- }
-
- @Override
protected void updatePlayButtonShowsPlay(boolean showPlay) {
viewBinding.playButton.setIsShowPlay(showPlay);
}
@@ -261,6 +235,26 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar.
};
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ @SuppressWarnings("unused")
+ public void bufferUpdate(BufferUpdateEvent event) {
+ if (event.hasStarted()) {
+ viewBinding.progressBar.setVisibility(View.VISIBLE);
+ } else if (event.hasEnded()) {
+ viewBinding.progressBar.setVisibility(View.INVISIBLE);
+ } else {
+ viewBinding.sbPosition.setSecondaryProgress((int) (event.getProgress() * viewBinding.sbPosition.getMax()));
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ @SuppressWarnings("unused")
+ public void sleepTimerUpdate(SleepTimerUpdatedEvent event) {
+ if (event.isCancelled() || event.wasJustEnabled()) {
+ supportInvalidateOptionsMenu();
+ }
+ }
+
protected void loadMediaInfo() {
Log.d(TAG, "loadMediaInfo()");
if (controller == null || controller.getMedia() == null) {
@@ -544,12 +538,21 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar.
}
@Subscribe(threadMode = ThreadMode.MAIN)
- public void onPlaybackServiceChanged(ServiceEvent event) {
- if (event.action == ServiceEvent.Action.SERVICE_SHUT_DOWN) {
+ public void onPlaybackServiceChanged(PlaybackServiceEvent event) {
+ if (event.action == PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN) {
finish();
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onMediaPlayerError(PlayerErrorEvent event) {
+ final AlertDialog.Builder errorDialog = new AlertDialog.Builder(VideoplayerActivity.this);
+ errorDialog.setTitle(R.string.error_label);
+ errorDialog.setMessage(event.getMessage());
+ errorDialog.setNeutralButton(android.R.string.ok, (dialog, which) -> finish());
+ errorDialog.show();
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java
index 3020aba43..674071294 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java
@@ -1,21 +1,14 @@
package de.danoeh.antennapod.activity;
-import android.Manifest;
-import android.app.WallpaperManager;
import android.appwidget.AppWidgetManager;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.CheckBox;
-import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
-import androidx.core.content.ContextCompat;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.receiver.PlayerWidget;
@@ -51,7 +44,6 @@ public class WidgetConfigActivity extends AppCompatActivity {
finish();
}
- displayDeviceBackground();
opacityTextView = findViewById(R.id.widget_opacity_textView);
opacitySeekBar = findViewById(R.id.widget_opacity_seekBar);
widgetPreview = findViewById(R.id.widgetLayout);
@@ -102,16 +94,6 @@ public class WidgetConfigActivity extends AppCompatActivity {
widgetPreview.findViewById(R.id.butRew).setVisibility(ckRewind.isChecked() ? View.VISIBLE : View.GONE);
}
- 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());
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
index 2ab96e84d..5ddb6407c 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
@@ -95,7 +95,7 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
holder.preview.setVisibility(View.GONE);
holder.description.setTag(Boolean.FALSE);
} else {
- holder.description.setMaxLines(2000);
+ holder.description.setMaxLines(30);
holder.description.setTag(Boolean.TRUE);
holder.preview.setVisibility(item.getMedia() != null ? View.VISIBLE : View.GONE);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
index ff0311ab6..7854f7aa9 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -196,7 +196,7 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
bindFeedView((NavDrawerData.FeedDrawerItem) item, (FeedHolder) holder);
holder.itemView.setOnCreateContextMenuListener(itemAccess);
} else {
- bindFolderView((NavDrawerData.FolderDrawerItem) item, (FeedHolder) holder);
+ bindTagView((NavDrawerData.TagDrawerItem) item, (FeedHolder) holder);
}
}
if (viewType != VIEW_TYPE_SECTION_DIVIDER) {
@@ -327,16 +327,16 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
}
}
- private void bindFolderView(NavDrawerData.FolderDrawerItem folder, FeedHolder holder) {
+ private void bindTagView(NavDrawerData.TagDrawerItem tag, FeedHolder holder) {
Activity context = activity.get();
if (context == null) {
return;
}
- if (folder.isOpen) {
+ if (tag.isOpen) {
holder.count.setVisibility(View.GONE);
}
Glide.with(context).clear(holder.image);
- holder.image.setImageResource(R.drawable.ic_folder);
+ holder.image.setImageResource(R.drawable.ic_tag);
holder.failure.setVisibility(View.GONE);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java
index 5fec5f063..26674b2b2 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/PlaybackStatisticsListAdapter.java
@@ -1,13 +1,12 @@
package de.danoeh.antennapod.adapter;
-import android.content.Context;
-import androidx.appcompat.app.AlertDialog;
-
+import androidx.fragment.app.Fragment;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.StatisticsItem;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateFormatter;
+import de.danoeh.antennapod.fragment.FeedStatisticsDialogFragment;
import de.danoeh.antennapod.view.PieChartView;
import java.util.Date;
@@ -18,10 +17,12 @@ import java.util.List;
*/
public class PlaybackStatisticsListAdapter extends StatisticsListAdapter {
+ private final Fragment fragment;
boolean countAll = true;
- public PlaybackStatisticsListAdapter(Context context) {
- super(context);
+ public PlaybackStatisticsListAdapter(Fragment fragment) {
+ super(fragment.getContext());
+ this.fragment = fragment;
}
public void setCountAll(boolean countAll) {
@@ -60,16 +61,9 @@ public class PlaybackStatisticsListAdapter extends StatisticsListAdapter {
holder.value.setText(Converter.shortLocalizedDuration(context, time));
holder.itemView.setOnClickListener(v -> {
- AlertDialog.Builder dialog = new AlertDialog.Builder(context);
- dialog.setTitle(statsItem.feed.getTitle());
- dialog.setMessage(context.getString(R.string.statistics_details_dialog,
- countAll ? statsItem.episodesStartedIncludingMarked : statsItem.episodesStarted,
- statsItem.episodes, Converter.shortLocalizedDuration(context,
- countAll ? statsItem.timePlayedCountAll : statsItem.timePlayed),
- Converter.shortLocalizedDuration(context, statsItem.time)));
- dialog.setPositiveButton(android.R.string.ok, null);
- dialog.show();
+ FeedStatisticsDialogFragment yourDialogFragment = FeedStatisticsDialogFragment.newInstance(
+ statsItem.feed.getId(), statsItem.feed.getTitle());
+ yourDialogFragment.show(fragment.getChildFragmentManager().beginTransaction(), "DialogFragment");
});
}
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java
index 73f67d016..b637eb31d 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java
@@ -219,7 +219,7 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter<Subscription
.load();
} else {
new CoverLoader(mainActivityRef.get())
- .withResource(R.drawable.ic_folder)
+ .withResource(R.drawable.ic_tag)
.withPlaceholderView(feedTitle, true)
.withCoverView(imageView)
.load();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java
index dedf8e5e6..a2b0e98c3 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java
@@ -34,7 +34,7 @@ public class CancelDownloadActionButton extends ItemActionButton {
FeedMedia media = item.getMedia();
DownloadRequester.getInstance().cancelDownload(context, media);
if (UserPreferences.isEnableAutodownload()) {
- item.setAutoDownload(false);
+ item.disableAutoDownload();
DBWriter.setFeedItem(item);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
index a45eb5199..1f4f657b1 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
@@ -15,6 +15,5 @@ class ClientConfigurator {
ClientConfig.USER_AGENT = "AntennaPod/" + BuildConfig.VERSION_NAME;
ClientConfig.applicationCallbacks = new ApplicationCallbacksImpl();
ClientConfig.downloadServiceCallbacks = new DownloadServiceCallbacksImpl();
- ClientConfig.castCallbacks = new CastCallbackImpl();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java
index 938bb5931..590b7c897 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.config;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import de.danoeh.antennapod.R;
@@ -24,7 +25,8 @@ public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks {
args.putInt(DownloadsFragment.ARG_SELECTED_TAB, DownloadsFragment.POS_LOG);
intent.putExtra(MainActivity.EXTRA_FRAGMENT_ARGS, args);
return PendingIntent.getActivity(context,
- R.id.pending_intent_download_service_notification, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ R.id.pending_intent_download_service_notification, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
@Override
@@ -33,7 +35,8 @@ public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks {
activityIntent.setAction("request" + request.getFeedfileId());
activityIntent.putExtra(DownloadAuthenticationActivity.ARG_DOWNLOAD_REQUEST, request);
return PendingIntent.getActivity(context.getApplicationContext(),
- R.id.pending_intent_download_service_auth, activityIntent, PendingIntent.FLAG_ONE_SHOT);
+ R.id.pending_intent_download_service_auth, activityIntent,
+ PendingIntent.FLAG_ONE_SHOT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
@Override
@@ -43,15 +46,15 @@ public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks {
Bundle args = new Bundle();
args.putInt(DownloadsFragment.ARG_SELECTED_TAB, DownloadsFragment.POS_LOG);
intent.putExtra(MainActivity.EXTRA_FRAGMENT_ARGS, args);
- return PendingIntent.getActivity(context, R.id.pending_intent_download_service_report,
- intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getActivity(context, R.id.pending_intent_download_service_report, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
@Override
public PendingIntent getAutoDownloadReportNotificationContentIntent(Context context) {
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, QueueFragment.TAG);
- return PendingIntent.getActivity(context, R.id.pending_intent_download_service_autodownload_report,
- intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getActivity(context, R.id.pending_intent_download_service_autodownload_report, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java
index ee19a0339..595f37e40 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodeFilterDialog.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.dialog;
import android.content.Context;
import android.view.View;
+import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.RadioButton;
@@ -14,7 +15,6 @@ import de.danoeh.antennapod.model.feed.FeedFilter;
* Displays a dialog with a text box for filtering episodes and two radio buttons for exclusion/inclusion
*/
public abstract class EpisodeFilterDialog extends AlertDialog.Builder {
-
private final FeedFilter initialFilter;
public EpisodeFilterDialog(Context context, FeedFilter filter) {
@@ -26,8 +26,10 @@ public abstract class EpisodeFilterDialog extends AlertDialog.Builder {
setView(rootView);
final EditText etxtEpisodeFilterText = rootView.findViewById(R.id.etxtEpisodeFilterText);
+ final EditText etxtEpisodeFilterDurationText = rootView.findViewById(R.id.etxtEpisodeFilterDurationText);
final RadioButton radioInclude = rootView.findViewById(R.id.radio_filter_include);
final RadioButton radioExclude = rootView.findViewById(R.id.radio_filter_exclude);
+ final CheckBox checkboxDuration = rootView.findViewById(R.id.checkbox_filter_duration);
if (initialFilter.includeOnly()) {
radioInclude.setChecked(true);
@@ -40,18 +42,31 @@ public abstract class EpisodeFilterDialog extends AlertDialog.Builder {
radioInclude.setChecked(false);
etxtEpisodeFilterText.setText("");
}
+ if (initialFilter.hasMinimalDurationFilter()) {
+ checkboxDuration.setChecked(true);
+ // Store minimal duration in seconds, show in minutes
+ etxtEpisodeFilterDurationText.setText(String.valueOf(initialFilter.getMinimalDurationFilter() / 60));
+ }
setNegativeButton(R.string.cancel_label, null);
setPositiveButton(R.string.confirm_label, (dialog, which) -> {
String includeString = "";
String excludeString = "";
+ int minimalDuration = -1;
if (radioInclude.isChecked()) {
includeString = etxtEpisodeFilterText.getText().toString();
} else {
excludeString = etxtEpisodeFilterText.getText().toString();
}
-
- onConfirmed(new FeedFilter(includeString, excludeString));
+ if (checkboxDuration.isChecked()) {
+ try {
+ // Store minimal duration in seconds
+ minimalDuration = Integer.parseInt(etxtEpisodeFilterDurationText.getText().toString()) * 60;
+ } catch (NumberFormatException e) {
+ // Do not change anything on error
+ }
+ }
+ onConfirmed(new FeedFilter(includeString, excludeString, minimalDuration));
}
);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/FeedSortDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/FeedSortDialog.java
index 96d1b9b67..b89d05f88 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/FeedSortDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/FeedSortDialog.java
@@ -10,7 +10,7 @@ import java.util.Arrays;
import java.util.List;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
public class FeedSortDialog {
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 3186cbe2e..5cc1f99c6 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
@@ -12,9 +12,13 @@ import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.event.playback.SpeedChangedEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.view.PlaybackSpeedSeekBar;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
import java.util.List;
import java.util.Locale;
@@ -22,6 +26,8 @@ import java.util.Locale;
public class PlaybackControlsDialog extends DialogFragment {
private PlaybackController controller;
private AlertDialog dialog;
+ private PlaybackSpeedSeekBar speedSeekBar;
+ private TextView txtvPlaybackSpeed;
public static PlaybackControlsDialog newInstance() {
Bundle arguments = new Bundle();
@@ -42,10 +48,12 @@ public class PlaybackControlsDialog extends DialogFragment {
public void loadMediaInfo() {
setupUi();
setupAudioTracks();
+ updateSpeed(new SpeedChangedEvent(getCurrentPlaybackSpeedMultiplier()));
}
};
controller.init();
setupUi();
+ EventBus.getDefault().register(this);
}
@Override
@@ -53,6 +61,7 @@ public class PlaybackControlsDialog extends DialogFragment {
super.onStop();
controller.release();
controller = null;
+ EventBus.getDefault().unregister(this);
}
@NonNull
@@ -66,12 +75,14 @@ public class PlaybackControlsDialog extends DialogFragment {
}
private void setupUi() {
- final TextView txtvPlaybackSpeed = dialog.findViewById(R.id.txtvPlaybackSpeed);
-
- PlaybackSpeedSeekBar speedSeekBar = dialog.findViewById(R.id.speed_seek_bar);
- speedSeekBar.setController(controller);
- speedSeekBar.setProgressChangedListener(speed
- -> txtvPlaybackSpeed.setText(String.format(Locale.getDefault(), "%.2fx", speed)));
+ txtvPlaybackSpeed = dialog.findViewById(R.id.txtvPlaybackSpeed);
+ speedSeekBar = dialog.findViewById(R.id.speed_seek_bar);
+ speedSeekBar.setProgressChangedListener(speed -> {
+ if (controller != null) {
+ controller.setPlaybackSpeed(speed);
+ }
+ });
+ updateSpeed(new SpeedChangedEvent(controller.getCurrentPlaybackSpeedMultiplier()));
final CheckBox stereoToMono = dialog.findViewById(R.id.stereo_to_mono);
stereoToMono.setChecked(UserPreferences.stereoToMono());
@@ -100,6 +111,12 @@ public class PlaybackControlsDialog extends DialogFragment {
});
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void updateSpeed(SpeedChangedEvent event) {
+ txtvPlaybackSpeed.setText(String.format(Locale.getDefault(), "%.2fx", event.getNewSpeed()));
+ speedSeekBar.updateSpeed(event.getNewSpeed());
+ }
+
private void setupAudioTracks() {
List<String> audioTracks = controller.getAudioTracks();
int selectedAudioTrack = controller.getSelectedAudioTrack();
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 13258b4ec..ad2ed3499 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
@@ -67,6 +67,7 @@ public class ProxyDialog {
.setView(content)
.setNegativeButton(R.string.cancel_label, null)
.setPositiveButton(R.string.proxy_test_label, null)
+ .setNeutralButton(R.string.reset, null)
.show();
// To prevent cancelling the dialog on button click
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener((view) -> {
@@ -75,36 +76,19 @@ public class ProxyDialog {
test();
return;
}
- String type = (String) 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.parseInt(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);
+ setProxyConfig();
AntennapodHttpClient.reinit();
dialog.dismiss();
});
+ dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener((view) -> {
+ etHost.getText().clear();
+ etPort.getText().clear();
+ etUsername.getText().clear();
+ etPassword.getText().clear();
+ setProxyConfig();
+ });
+
List<String> types = new ArrayList<>();
types.add(Proxy.Type.DIRECT.name());
types.add(Proxy.Type.HTTP.name());
@@ -144,6 +128,11 @@ public class ProxyDialog {
spType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ if (position == 0) {
+ dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setVisibility(View.GONE);
+ } else {
+ dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setVisibility(View.VISIBLE);
+ }
enableSettings(position > 0);
setTestRequired(position > 0);
}
@@ -158,6 +147,35 @@ public class ProxyDialog {
return dialog;
}
+ private void setProxyConfig() {
+ String type = (String) 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.parseInt(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);
+ }
+
private final TextWatcher requireTestOnChange = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java
index 9fcf8be69..23c032248 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RemoveFeedDialog.java
@@ -19,18 +19,18 @@ import io.reactivex.schedulers.Schedulers;
public class RemoveFeedDialog {
private static final String TAG = "RemoveFeedDialog";
- public static void show(Context context, Feed feed, Runnable onSuccess) {
+ public static void show(Context context, Feed feed) {
List<Feed> feeds = Collections.singletonList(feed);
String message = getMessageId(context, feeds);
- showDialog(context, feeds, message, onSuccess);
+ showDialog(context, feeds, message);
}
- public static void show(Context context, List<Feed> feeds, Runnable onSuccess) {
+ public static void show(Context context, List<Feed> feeds) {
String message = getMessageId(context, feeds);
- showDialog(context, feeds, message, onSuccess);
+ showDialog(context, feeds, message);
}
- private static void showDialog(Context context, List<Feed> feeds, String message, Runnable onSuccess) {
+ private static void showDialog(Context context, List<Feed> feeds, String message) {
ConfirmationDialog dialog = new ConfirmationDialog(context, R.string.remove_feed_label, message) {
@Override
public void onConfirmButtonPressed(DialogInterface clickedDialog) {
@@ -42,20 +42,16 @@ public class RemoveFeedDialog {
progressDialog.setCancelable(false);
progressDialog.show();
- Completable.fromCallable(() -> {
+ Completable.fromAction(() -> {
for (Feed feed : feeds) {
DBWriter.deleteFeed(context, feed.getId()).get();
}
- return null;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
() -> {
Log.d(TAG, "Feed(s) deleted");
- if (onSuccess != null) {
- onSuccess.run();
- }
progressDialog.dismiss();
}, error -> {
Log.e(TAG, Log.getStackTraceString(error));
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 691bd65e8..764940e06 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
@@ -18,20 +18,17 @@ import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
-import io.reactivex.Observable;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.Disposable;
-
-import java.util.concurrent.TimeUnit;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
public class SleepTimerDialog extends DialogFragment {
private PlaybackController controller;
- private Disposable timeUpdater;
-
private EditText etxtTime;
private Spinner spTimeUnit;
private LinearLayout timeSetup;
@@ -47,19 +44,11 @@ public class SleepTimerDialog extends DialogFragment {
super.onStart();
controller = new PlaybackController(getActivity()) {
@Override
- public void onSleepTimerUpdate() {
- updateTime();
- }
-
- @Override
public void loadMediaInfo() {
- updateTime();
}
};
controller.init();
- timeUpdater = Observable.interval(1, TimeUnit.SECONDS)
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(tick -> updateTime());
+ EventBus.getDefault().register(this);
}
@Override
@@ -68,9 +57,7 @@ public class SleepTimerDialog extends DialogFragment {
if (controller != null) {
controller.release();
}
- if (timeUpdater != null) {
- timeUpdater.dispose();
- }
+ EventBus.getDefault().unregister(this);
}
@NonNull
@@ -170,13 +157,12 @@ public class SleepTimerDialog extends DialogFragment {
return builder.create();
}
- private void updateTime() {
- if (controller == null) {
- return;
- }
- timeSetup.setVisibility(controller.sleepTimerActive() ? View.GONE : View.VISIBLE);
- timeDisplay.setVisibility(controller.sleepTimerActive() ? View.VISIBLE : View.GONE);
- time.setText(Converter.getDurationStringLong((int) controller.getSleepTimerTimeLeft()));
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ @SuppressWarnings("unused")
+ public void timerUpdated(SleepTimerUpdatedEvent event) {
+ timeDisplay.setVisibility(event.isOver() || event.isCancelled() ? View.GONE : View.VISIBLE);
+ timeSetup.setVisibility(event.isOver() || event.isCancelled() ? View.VISIBLE : View.GONE);
+ time.setText(Converter.getDurationStringLong((int) event.getTimeLeft()));
}
private void closeKeyboard(View content) {
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java
index 29172bb5e..9e524188f 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java
@@ -16,7 +16,7 @@ import java.util.HashSet;
import java.util.Set;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.SubscriptionsFilter;
import de.danoeh.antennapod.core.feed.SubscriptionsFilterGroup;
import de.danoeh.antennapod.core.preferences.UserPreferences;
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/TagSettingsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/TagSettingsDialog.java
index 8ef01590f..8f5f1b802 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/TagSettingsDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/TagSettingsDialog.java
@@ -27,7 +27,9 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
public class TagSettingsDialog extends DialogFragment {
public static final String TAG = "TagSettingsDialog";
@@ -36,10 +38,10 @@ public class TagSettingsDialog extends DialogFragment {
private EditTagsDialogBinding viewBinding;
private TagSelectionAdapter adapter;
- public static TagSettingsDialog newInstance(FeedPreferences preferences) {
+ public static TagSettingsDialog newInstance(List<FeedPreferences> preferencesList) {
TagSettingsDialog fragment = new TagSettingsDialog();
Bundle args = new Bundle();
- args.putSerializable(ARG_FEED_PREFERENCES, preferences);
+ args.putSerializable(ARG_FEED_PREFERENCES, new ArrayList<>(preferencesList));
fragment.setArguments(args);
return fragment;
}
@@ -47,8 +49,14 @@ public class TagSettingsDialog extends DialogFragment {
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
- FeedPreferences preferences = (FeedPreferences) getArguments().getSerializable(ARG_FEED_PREFERENCES);
- displayedTags = new ArrayList<>(preferences.getTags());
+ ArrayList<FeedPreferences> feedPreferencesList =
+ (ArrayList<FeedPreferences>) getArguments().getSerializable(ARG_FEED_PREFERENCES);
+ Set<String> commonTags = new HashSet<>(feedPreferencesList.get(0).getTags());
+
+ for (FeedPreferences preference : feedPreferencesList) {
+ commonTags.retainAll(preference.getTags());
+ }
+ displayedTags = new ArrayList<>(commonTags);
displayedTags.remove(FeedPreferences.TAG_ROOT);
viewBinding = EditTagsDialogBinding.inflate(getLayoutInflater());
@@ -57,7 +65,7 @@ public class TagSettingsDialog extends DialogFragment {
adapter = new TagSelectionAdapter();
adapter.setHasStableIds(true);
viewBinding.tagsRecycler.setAdapter(adapter);
- viewBinding.rootFolderCheckbox.setChecked(preferences.getTags().contains(FeedPreferences.TAG_ROOT));
+ viewBinding.rootFolderCheckbox.setChecked(commonTags.contains(FeedPreferences.TAG_ROOT));
viewBinding.newTagButton.setOnClickListener(v ->
addTag(viewBinding.newTagEditText.getText().toString().trim()));
@@ -73,17 +81,16 @@ public class TagSettingsDialog extends DialogFragment {
}
});
+ if (feedPreferencesList.size() > 1) {
+ viewBinding.commonTagsInfo.setVisibility(View.VISIBLE);
+ }
+
AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
dialog.setView(viewBinding.getRoot());
- dialog.setTitle(R.string.feed_folders_label);
+ dialog.setTitle(R.string.feed_tags_label);
dialog.setPositiveButton(android.R.string.ok, (d, input) -> {
addTag(viewBinding.newTagEditText.getText().toString().trim());
- preferences.getTags().clear();
- preferences.getTags().addAll(displayedTags);
- if (viewBinding.rootFolderCheckbox.isChecked()) {
- preferences.getTags().add(FeedPreferences.TAG_ROOT);
- }
- DBWriter.setFeedPreferences(preferences);
+ updatePreferencesTags(feedPreferencesList, commonTags);
});
dialog.setNegativeButton(R.string.cancel_label, null);
return dialog.create();
@@ -96,7 +103,7 @@ public class TagSettingsDialog extends DialogFragment {
List<NavDrawerData.DrawerItem> items = data.items;
List<String> folders = new ArrayList<String>();
for (NavDrawerData.DrawerItem item : items) {
- if (item.type == NavDrawerData.DrawerItem.Type.FOLDER) {
+ if (item.type == NavDrawerData.DrawerItem.Type.TAG) {
folders.add(item.getTitle());
}
}
@@ -123,6 +130,17 @@ public class TagSettingsDialog extends DialogFragment {
adapter.notifyDataSetChanged();
}
+ private void updatePreferencesTags(List<FeedPreferences> feedPreferencesList, Set<String> commonTags) {
+ if (viewBinding.rootFolderCheckbox.isChecked()) {
+ displayedTags.add(FeedPreferences.TAG_ROOT);
+ }
+ for (FeedPreferences preferences : feedPreferencesList) {
+ preferences.getTags().removeAll(commonTags);
+ preferences.getTags().addAll(displayedTags);
+ DBWriter.setFeedPreferences(preferences);
+ }
+ }
+
public class TagSelectionAdapter extends RecyclerView.Adapter<TagSelectionAdapter.ViewHolder> {
@Override
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 def2e56a7..2bce73b79 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.dialog;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -15,10 +14,14 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.chip.Chip;
import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.event.playback.SpeedChangedEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.view.ItemOffsetDecoration;
import de.danoeh.antennapod.view.PlaybackSpeedSeekBar;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
@@ -47,22 +50,12 @@ public class VariableSpeedDialog extends BottomSheetDialogFragment {
super.onStart();
controller = new PlaybackController(getActivity()) {
@Override
- public void onPlaybackSpeedChange() {
- updateSpeed();
- }
-
- @Override
public void loadMediaInfo() {
- updateSpeed();
+ updateSpeed(new SpeedChangedEvent(controller.getCurrentPlaybackSpeedMultiplier()));
}
};
controller.init();
- speedSeekBar.setController(controller);
- }
-
- private void updateSpeed() {
- speedSeekBar.updateSpeed();
- addCurrentSpeedChip.setText(speedFormat.format(controller.getCurrentPlaybackSpeedMultiplier()));
+ EventBus.getDefault().register(this);
}
@Override
@@ -70,6 +63,13 @@ public class VariableSpeedDialog extends BottomSheetDialogFragment {
super.onStop();
controller.release();
controller = null;
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void updateSpeed(SpeedChangedEvent event) {
+ speedSeekBar.updateSpeed(event.getNewSpeed());
+ addCurrentSpeedChip.setText(speedFormat.format(event.getNewSpeed()));
}
@Nullable
@@ -78,6 +78,11 @@ public class VariableSpeedDialog extends BottomSheetDialogFragment {
@Nullable Bundle savedInstanceState) {
View root = View.inflate(getContext(), R.layout.speed_select_dialog, null);
speedSeekBar = root.findViewById(R.id.speed_seek_bar);
+ speedSeekBar.setProgressChangedListener(multiplier -> {
+ if (controller != null) {
+ controller.setPlaybackSpeed(multiplier);
+ }
+ });
RecyclerView selectedSpeedsGrid = root.findViewById(R.id.selected_speeds_grid);
selectedSpeedsGrid.setLayoutManager(new GridLayoutManager(getContext(), 3));
selectedSpeedsGrid.addItemDecoration(new ItemOffsetDecoration(getContext(), 4));
@@ -112,9 +117,7 @@ public class VariableSpeedDialog extends BottomSheetDialogFragment {
@NonNull
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
Chip chip = new Chip(getContext());
- if (Build.VERSION.SDK_INT >= 17) {
- chip.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
- }
+ chip.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
return new ViewHolder(chip);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java
index f97c1c7ab..340783208 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/GpodnetPodcastSearcher.java
@@ -1,6 +1,6 @@
package de.danoeh.antennapod.discovery;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.core.sync.SynchronizationCredentials;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetServiceException;
@@ -18,8 +18,8 @@ public class GpodnetPodcastSearcher implements PodcastSearcher {
return Single.create((SingleOnSubscribe<List<PodcastSearchResult>>) subscriber -> {
try {
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
- GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(),
- GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword());
+ SynchronizationCredentials.getHosturl(), SynchronizationCredentials.getDeviceID(),
+ SynchronizationCredentials.getUsername(), SynchronizationCredentials.getPassword());
List<GpodnetPodcast> gpodnetPodcasts = service.searchPodcasts(query, 0);
List<PodcastSearchResult> results = new ArrayList<>();
for (GpodnetPodcast podcast : gpodnetPodcasts) {
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java
index 6e894176f..5f3dd5f61 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java
@@ -17,9 +17,12 @@ import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class ItunesPodcastSearcher implements PodcastSearcher {
private static final String ITUNES_API_URL = "https://itunes.apple.com/search?media=podcast&term=%s";
+ private static final String PATTERN_BY_ID = ".*/podcasts\\.apple\\.com/.*/podcast/.*/id(\\d+).*";
public ItunesPodcastSearcher() {
}
@@ -70,9 +73,12 @@ public class ItunesPodcastSearcher implements PodcastSearcher {
@Override
public Single<String> lookupUrl(String url) {
+ Pattern pattern = Pattern.compile(PATTERN_BY_ID);
+ Matcher matcher = pattern.matcher(url);
+ final String lookupUrl = matcher.find() ? ("https://itunes.apple.com/lookup?id=" + matcher.group(1)) : url;
return Single.create(emitter -> {
OkHttpClient client = AntennapodHttpClient.getHttpClient();
- Request.Builder httpReq = new Request.Builder().url(url);
+ Request.Builder httpReq = new Request.Builder().url(lookupUrl);
try {
Response response = client.newCall(httpReq.build()).execute();
if (response.isSuccessful()) {
@@ -92,7 +98,7 @@ public class ItunesPodcastSearcher implements PodcastSearcher {
@Override
public boolean urlNeedsLookup(String url) {
- return url.contains("itunes.apple.com");
+ return url.contains("itunes.apple.com") || url.matches(PATTERN_BY_ID);
}
@Override
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 64e7f161e..8c01a4563 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.fragment;
-import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ClipboardManager;
import android.content.Context;
@@ -12,9 +11,14 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.activity.result.contract.ActivityResultContracts.GetContent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.documentfile.provider.DocumentFile;
@@ -48,14 +52,17 @@ import java.util.Collections;
public class AddFeedFragment extends Fragment {
public static final String TAG = "AddFeedFragment";
- private static final int REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH = 1;
- private static final int REQUEST_CODE_ADD_LOCAL_FOLDER = 2;
private static final String KEY_UP_ARROW = "up_arrow";
private AddfeedBinding viewBinding;
private MainActivity activity;
private boolean displayUpArrow;
+ private final ActivityResultLauncher<String> chooseOpmlImportPathLauncher =
+ registerForActivityResult(new GetContent(), this::chooseOpmlImportPathResult);
+ private final ActivityResultLauncher<Uri> addLocalFolderLauncher =
+ registerForActivityResult(new AddLocalFolder(), this::addLocalFolderResult);
+
@Override
@Nullable
public View onCreateView(@NonNull LayoutInflater inflater,
@@ -91,10 +98,7 @@ public class AddFeedFragment extends Fragment {
viewBinding.opmlImportButton.setOnClickListener(v -> {
try {
- Intent intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT);
- intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
- intentGetContentAction.setType("*/*");
- startActivityForResult(intentGetContentAction, REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH);
+ chooseOpmlImportPathLauncher.launch("*/*");
} catch (ActivityNotFoundException e) {
e.printStackTrace();
((MainActivity) getActivity())
@@ -107,9 +111,7 @@ public class AddFeedFragment extends Fragment {
return;
}
try {
- Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- startActivityForResult(intent, REQUEST_CODE_ADD_LOCAL_FOLDER);
+ addLocalFolderLauncher.launch(null);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
((MainActivity) getActivity())
@@ -157,6 +159,10 @@ public class AddFeedFragment extends Fragment {
}
private void performSearch() {
+ viewBinding.combinedFeedSearchEditText.clearFocus();
+ InputMethodManager in = (InputMethodManager)
+ getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
+ in.hideSoftInputFromWindow(viewBinding.combinedFeedSearchEditText.getWindowToken(), 0);
String query = viewBinding.combinedFeedSearchEditText.getText().toString();
if (query.matches("http[s]?://.*")) {
addUrl(query);
@@ -171,22 +177,23 @@ public class AddFeedFragment extends Fragment {
setRetainInstance(true);
}
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode != Activity.RESULT_OK || data == null) {
+ private void chooseOpmlImportPathResult(final Uri uri) {
+ if (uri == null) {
return;
}
- Uri uri = data.getData();
-
- if (requestCode == REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH) {
- Intent intent = new Intent(getContext(), OpmlImportActivity.class);
- intent.setData(uri);
- startActivity(intent);
- } else if (requestCode == REQUEST_CODE_ADD_LOCAL_FOLDER) {
- Observable.fromCallable(() -> addLocalFolder(uri))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(
+ final Intent intent = new Intent(getContext(), OpmlImportActivity.class);
+ intent.setData(uri);
+ startActivity(intent);
+ }
+
+ private void addLocalFolderResult(final Uri uri) {
+ if (uri == null) {
+ return;
+ }
+ Observable.fromCallable(() -> addLocalFolder(uri))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
feed -> {
Fragment fragment = FeedItemlistFragment.newInstance(feed.getId());
((MainActivity) getActivity()).loadChildFragment(fragment);
@@ -195,7 +202,6 @@ public class AddFeedFragment extends Fragment {
((MainActivity) getActivity())
.showSnackbarAbovePlayer(error.getLocalizedMessage(), Snackbar.LENGTH_LONG);
});
- }
}
private Feed addLocalFolder(Uri uri) throws DownloadRequestException {
@@ -219,4 +225,14 @@ public class AddFeedFragment extends Fragment {
DBTasks.forceRefreshFeed(getContext(), fromDatabase, true);
return fromDatabase;
}
+
+ private static class AddLocalFolder extends ActivityResultContracts.OpenDocumentTree {
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ @NonNull
+ @Override
+ public Intent createIntent(@NonNull final Context context, @Nullable final Uri input) {
+ return super.createIntent(context, input)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
index 168133c7a..95e2eb1aa 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
@@ -25,6 +25,14 @@ import androidx.viewpager2.widget.ViewPager2;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.snackbar.Snackbar;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.event.playback.BufferUpdateEvent;
+import de.danoeh.antennapod.event.playback.PlaybackServiceEvent;
+import de.danoeh.antennapod.event.PlayerErrorEvent;
+import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent;
+import de.danoeh.antennapod.event.playback.SpeedChangedEvent;
+import de.danoeh.antennapod.playback.cast.CastEnabledActivity;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -34,25 +42,20 @@ import java.text.NumberFormat;
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.core.event.FavoritesEvent;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
-import de.danoeh.antennapod.core.event.ServiceEvent;
+import de.danoeh.antennapod.event.FavoritesEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
import de.danoeh.antennapod.model.feed.Chapter;
-import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
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.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
-import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
import de.danoeh.antennapod.model.playback.Playable;
-import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
import de.danoeh.antennapod.dialog.SleepTimerDialog;
@@ -224,8 +227,8 @@ public class AudioPlayerFragment extends Fragment implements
}
@Subscribe(threadMode = ThreadMode.MAIN)
- public void onPlaybackServiceChanged(ServiceEvent event) {
- if (event.action == ServiceEvent.Action.SERVICE_SHUT_DOWN) {
+ public void onPlaybackServiceChanged(PlaybackServiceEvent event) {
+ if (event.action == PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN) {
((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
}
}
@@ -243,14 +246,11 @@ public class AudioPlayerFragment extends Fragment implements
});
}
- protected void updatePlaybackSpeedButton(Playable media) {
- if (butPlaybackSpeed == null || controller == null) {
- return;
- }
- float speed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(media);
- String speedStr = new DecimalFormat("0.00").format(speed);
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void updatePlaybackSpeedButton(SpeedChangedEvent event) {
+ String speedStr = new DecimalFormat("0.00").format(event.getNewSpeed());
txtvPlaybackSpeed.setText(speedStr);
- butPlaybackSpeed.setSpeed(speed);
+ butPlaybackSpeed.setSpeed(event.getNewSpeed());
}
private void loadMediaInfo(boolean includingChapters) {
@@ -282,47 +282,6 @@ public class AudioPlayerFragment extends Fragment implements
private PlaybackController newPlaybackController() {
return new PlaybackController(getActivity()) {
@Override
- public void onBufferStart() {
- progressIndicator.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onBufferEnd() {
- progressIndicator.setVisibility(View.GONE);
- }
-
- @Override
- public void onBufferUpdate(float progress) {
- if (isStreaming()) {
- sbPosition.setSecondaryProgress((int) (progress * sbPosition.getMax()));
- } else {
- sbPosition.setSecondaryProgress(0);
- }
- }
-
- @Override
- public void handleError(int code) {
- final AlertDialog.Builder errorDialog = new AlertDialog.Builder(getContext());
- errorDialog.setTitle(R.string.error_label);
- errorDialog.setMessage(MediaPlayerError.getErrorString(getContext(), code));
- errorDialog.setPositiveButton(android.R.string.ok, (dialog, which) ->
- ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED));
- if (!UserPreferences.useExoplayer()) {
- errorDialog.setNeutralButton(R.string.media_player_switch_to_exoplayer, (dialog, which) -> {
- UserPreferences.enableExoplayer();
- ((MainActivity) getActivity()).showSnackbarAbovePlayer(
- R.string.media_player_switched_to_exoplayer, Snackbar.LENGTH_LONG);
- });
- }
- errorDialog.create().show();
- }
-
- @Override
- public void onSleepTimerUpdate() {
- AudioPlayerFragment.this.loadMediaInfo(false);
- }
-
- @Override
protected void updatePlayButtonShowsPlay(boolean showPlay) {
butPlay.setIsShowPlay(showPlay);
}
@@ -336,25 +295,28 @@ public class AudioPlayerFragment extends Fragment implements
public void onPlaybackEnd() {
((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED);
}
-
- @Override
- public void onPlaybackSpeedChange() {
- updatePlaybackSpeedButton(getMedia());
- }
};
}
private void updateUi(Playable media) {
- if (controller == null) {
+ if (controller == null || media == null) {
return;
}
duration = controller.getDuration();
- updatePosition(new PlaybackPositionEvent(controller.getPosition(), duration));
- updatePlaybackSpeedButton(media);
+ updatePosition(new PlaybackPositionEvent(media.getPosition(), media.getDuration()));
+ updatePlaybackSpeedButton(new SpeedChangedEvent(PlaybackSpeedUtils.getCurrentPlaybackSpeed(media)));
setChapterDividers(media);
setupOptionsMenu(media);
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ @SuppressWarnings("unused")
+ public void sleepTimerUpdate(SleepTimerUpdatedEvent event) {
+ if (event.isCancelled() || event.wasJustEnabled()) {
+ AudioPlayerFragment.this.loadMediaInfo(false);
+ }
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -385,6 +347,20 @@ public class AudioPlayerFragment extends Fragment implements
}
@Subscribe(threadMode = ThreadMode.MAIN)
+ @SuppressWarnings("unused")
+ public void bufferUpdate(BufferUpdateEvent event) {
+ if (event.hasStarted()) {
+ progressIndicator.setVisibility(View.VISIBLE);
+ } else if (event.hasEnded()) {
+ progressIndicator.setVisibility(View.GONE);
+ } else if (controller != null && controller.isStreaming()) {
+ sbPosition.setSecondaryProgress((int) (event.getProgress() * sbPosition.getMax()));
+ } else {
+ sbPosition.setSecondaryProgress(0);
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
public void updatePosition(PlaybackPositionEvent event) {
if (controller == null || txtvPosition == null || txtvLength == null || sbPosition == null) {
return;
@@ -419,6 +395,23 @@ public class AudioPlayerFragment extends Fragment implements
AudioPlayerFragment.this.loadMediaInfo(false);
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void mediaPlayerError(PlayerErrorEvent event) {
+ final AlertDialog.Builder errorDialog = new AlertDialog.Builder(getContext());
+ errorDialog.setTitle(R.string.error_label);
+ errorDialog.setMessage(event.getMessage());
+ errorDialog.setPositiveButton(android.R.string.ok, (dialog, which) ->
+ ((MainActivity) getActivity()).getBottomSheet().setState(BottomSheetBehavior.STATE_COLLAPSED));
+ if (!UserPreferences.useExoplayer()) {
+ errorDialog.setNeutralButton(R.string.media_player_switch_to_exoplayer, (dialog, which) -> {
+ UserPreferences.enableExoplayer();
+ ((MainActivity) getActivity()).showSnackbarAbovePlayer(
+ R.string.media_player_switched_to_exoplayer, Snackbar.LENGTH_LONG);
+ });
+ }
+ errorDialog.create().show();
+ }
+
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (controller == null || txtvLength == null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
index de14f220e..04ad6e2bd 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -17,14 +17,14 @@ import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.ChaptersListAdapter;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.model.feed.Chapter;
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 6c8baef29..933147378 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -23,10 +23,10 @@ import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
import de.danoeh.antennapod.adapter.actionbutton.DeleteActionButton;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloadLogEvent;
-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.event.FeedItemEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
+import de.danoeh.antennapod.event.PlayerStatusEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.fragment.actions.EpisodeMultiSelectActionHandler;
import de.danoeh.antennapod.model.feed.FeedItem;
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 8c2203f72..2d448faa8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -45,7 +45,7 @@ import org.greenrobot.eventbus.ThreadMode;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
@@ -110,9 +110,8 @@ public class CoverFragment extends Fragment {
butNextChapter.setColorFilter(colorFilter);
butPrevChapter.setColorFilter(colorFilter);
descriptionIcon.setColorFilter(colorFilter);
- ChaptersFragment chaptersFragment = new ChaptersFragment();
chapterControl.setOnClickListener(v ->
- chaptersFragment.show(getChildFragmentManager(), ChaptersFragment.TAG));
+ new ChaptersFragment().show(getChildFragmentManager(), ChaptersFragment.TAG));
butPrevChapter.setOnClickListener(v -> seekToPrevChapter());
butNextChapter.setOnClickListener(v -> seekToNextChapter());
@@ -156,8 +155,13 @@ public class CoverFragment extends Fragment {
+ "・"
+ "\u00A0"
+ StringUtils.replace(StringUtils.stripToEmpty(pubDateStr), " ", "\u00A0"));
- Intent openFeed = MainActivity.getIntentToOpenFeed(requireContext(), ((FeedMedia) media).getItem().getFeedId());
- txtvPodcastTitle.setOnClickListener(v -> startActivity(openFeed));
+ if (media instanceof FeedMedia) {
+ Intent openFeed = MainActivity.getIntentToOpenFeed(requireContext(),
+ ((FeedMedia) media).getItem().getFeedId());
+ txtvPodcastTitle.setOnClickListener(v -> startActivity(openFeed));
+ } else {
+ txtvPodcastTitle.setOnClickListener(null);
+ }
txtvPodcastTitle.setOnLongClickListener(v -> copyText(media.getFeedTitle()));
txtvEpisodeTitle.setText(media.getEpisodeTitle());
txtvEpisodeTitle.setOnLongClickListener(v -> copyText(media.getEpisodeTitle()));
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java
index 034b111e1..230a0ce0d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java
@@ -23,7 +23,7 @@ import org.greenrobot.eventbus.EventBus;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.adapter.itunes.ItunesAdapter;
-import de.danoeh.antennapod.core.event.DiscoveryDefaultUpdateEvent;
+import de.danoeh.antennapod.event.DiscoveryDefaultUpdateEvent;
import de.danoeh.antennapod.discovery.ItunesTopListLoader;
import de.danoeh.antennapod.discovery.PodcastSearchResult;
import io.reactivex.disposables.Disposable;
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 ddbf6c078..5602dcb78 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -113,7 +113,7 @@ public class DownloadLogFragment extends ListFragment {
if (downloadRequest.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
FeedMedia media = DBReader.getFeedMedia(downloadRequest.getFeedfileId());
FeedItem feedItem = media.getItem();
- feedItem.setAutoDownload(false);
+ feedItem.disableAutoDownload();
DBWriter.setFeedItem(feedItem);
}
} else if (item instanceof DownloadStatus) {
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 7ea76bb8d..37d77d31f 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -21,10 +21,10 @@ import android.widget.TextView;
import android.widget.Toast;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
-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.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
+import de.danoeh.antennapod.event.PlayerStatusEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.view.EpisodeItemListRecyclerView;
import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder;
@@ -40,7 +40,7 @@ import de.danoeh.antennapod.activity.MainActivity;
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.event.FeedItemEvent;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.storage.DBWriter;
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 8e070738c..1e24d62f7 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -9,21 +9,23 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
+
+import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
-import de.danoeh.antennapod.core.event.ServiceEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
+import de.danoeh.antennapod.event.playback.PlaybackServiceEvent;
import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.playback.base.PlayerStatus;
import de.danoeh.antennapod.view.PlayButton;
import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -77,8 +79,8 @@ public class ExternalPlayerFragment extends Fragment {
}
@Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
+ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
butPlay.setOnClickListener(v -> {
if (controller == null) {
return;
@@ -97,12 +99,6 @@ public class ExternalPlayerFragment extends Fragment {
private PlaybackController setupPlaybackController() {
return new PlaybackController(getActivity()) {
-
- @Override
- public void onPositionObserverUpdate() {
- ExternalPlayerFragment.this.onPositionObserverUpdate();
- }
-
@Override
protected void updatePlayButtonShowsPlay(boolean showPlay) {
butPlay.setIsShowPlay(showPlay);
@@ -140,13 +136,20 @@ public class ExternalPlayerFragment extends Fragment {
}
@Subscribe(threadMode = ThreadMode.MAIN)
- public void onEventMainThread(PlaybackPositionEvent event) {
- onPositionObserverUpdate();
+ public void onPositionObserverUpdate(PlaybackPositionEvent event) {
+ if (controller == null) {
+ return;
+ } else if (controller.getPosition() == PlaybackService.INVALID_TIME
+ || controller.getDuration() == PlaybackService.INVALID_TIME) {
+ return;
+ }
+ progressBar.setProgress((int)
+ ((double) controller.getPosition() / controller.getDuration() * 100));
}
@Subscribe(threadMode = ThreadMode.MAIN)
- public void onPlaybackServiceChanged(ServiceEvent event) {
- if (event.action == ServiceEvent.Action.SERVICE_SHUT_DOWN) {
+ public void onPlaybackServiceChanged(PlaybackServiceEvent event) {
+ if (event.action == PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN) {
((MainActivity) getActivity()).setPlayerVisible(false);
}
}
@@ -193,7 +196,7 @@ public class ExternalPlayerFragment extends Fragment {
((MainActivity) getActivity()).setPlayerVisible(true);
txtvTitle.setText(media.getEpisodeTitle());
feedName.setText(media.getFeedTitle());
- onPositionObserverUpdate();
+ onPositionObserverUpdate(new PlaybackPositionEvent(media.getPosition(), media.getDuration()));
RequestOptions options = new RequestOptions()
.placeholder(R.color.light_gray)
@@ -218,15 +221,4 @@ public class ExternalPlayerFragment extends Fragment {
((MainActivity) getActivity()).getBottomSheet().setLocked(false);
}
}
-
- private void onPositionObserverUpdate() {
- if (controller == null) {
- return;
- } else if (controller.getPosition() == PlaybackService.INVALID_TIME
- || controller.getDuration() == PlaybackService.INVALID_TIME) {
- return;
- }
- progressBar.setProgress((int)
- ((double) controller.getPosition() / controller.getDuration() * 100));
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
index 986c417fd..d7bfd404d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -18,7 +18,7 @@ import org.greenrobot.eventbus.Subscribe;
import java.util.List;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.FavoritesEvent;
+import de.danoeh.antennapod.event.FavoritesEvent;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
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 da7e7e633..ae298cc1c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.fragment;
-import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.Context;
@@ -10,62 +9,56 @@ import android.graphics.LightingColorFilter;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.widget.AppCompatDrawableManager;
-import androidx.appcompat.widget.Toolbar;
-import androidx.documentfile.provider.DocumentFile;
-import androidx.fragment.app.Fragment;
import android.text.TextUtils;
-import android.text.format.Formatter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.AppCompatDrawableManager;
+import androidx.appcompat.widget.Toolbar;
+import androidx.documentfile.provider.DocumentFile;
+import androidx.fragment.app.Fragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.material.snackbar.Snackbar;
import com.joanzapata.iconify.Iconify;
-
-import org.apache.commons.lang3.StringUtils;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
-import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.model.feed.FeedFunding;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
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.StatisticsItem;
-import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.fragment.preferences.StatisticsFragment;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedFunding;
import de.danoeh.antennapod.view.ToolbarIconTintManager;
import io.reactivex.Completable;
import io.reactivex.Maybe;
import io.reactivex.MaybeOnSubscribe;
-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 java.util.ArrayList;
import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
/**
* Displays information about a feed.
@@ -74,28 +67,22 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
private static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
private static final String TAG = "FeedInfoActivity";
- private static final int REQUEST_CODE_ADD_LOCAL_FOLDER = 2;
+ private final ActivityResultLauncher<Uri> addLocalFolderLauncher =
+ registerForActivityResult(new AddLocalFolder(), this::addLocalFolderResult);
private Feed feed;
private Disposable disposable;
- private Disposable disposableStatistics;
private ImageView imgvCover;
private TextView txtvTitle;
private TextView txtvDescription;
- private TextView lblStatistics;
- private TextView txtvPodcastTime;
- private TextView txtvPodcastSpace;
- private TextView txtvPodcastEpisodeCount;
private TextView txtvFundingUrl;
private TextView lblSupport;
- private Button btnvOpenStatistics;
private TextView txtvUrl;
private TextView txtvAuthorHeader;
private ImageView imgvBackground;
private View infoContainer;
private View header;
private Toolbar toolbar;
- private ToolbarIconTintManager iconTintManager;
public static FeedInfoFragment newInstance(Feed feed) {
FeedInfoFragment fragment = new FeedInfoFragment();
@@ -133,7 +120,7 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
AppBarLayout appBar = root.findViewById(R.id.appBar);
CollapsingToolbarLayout collapsingToolbar = root.findViewById(R.id.collapsing_toolbar);
- iconTintManager = new ToolbarIconTintManager(getContext(), toolbar, collapsingToolbar) {
+ ToolbarIconTintManager iconTintManager = new ToolbarIconTintManager(getContext(), toolbar, collapsingToolbar) {
@Override
protected void doTint(Context themedContext) {
toolbar.getMenu().findItem(R.id.visit_website_item)
@@ -157,23 +144,20 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
txtvDescription = root.findViewById(R.id.txtvDescription);
- lblStatistics = root.findViewById(R.id.lblStatistics);
- txtvPodcastSpace = root.findViewById(R.id.txtvPodcastSpaceUsed);
- txtvPodcastEpisodeCount = root.findViewById(R.id.txtvPodcastEpisodeCount);
- txtvPodcastTime = root.findViewById(R.id.txtvPodcastTime);
- btnvOpenStatistics = root.findViewById(R.id.btnvOpenStatistics);
txtvUrl = root.findViewById(R.id.txtvUrl);
lblSupport = root.findViewById(R.id.lblSupport);
txtvFundingUrl = root.findViewById(R.id.txtvFundingUrl);
txtvUrl.setOnClickListener(copyUrlToClipboard);
- btnvOpenStatistics.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- StatisticsFragment fragment = new StatisticsFragment();
- ((MainActivity) getActivity()).loadChildFragment(fragment, TransitionEffect.SLIDE);
- }
+ long feedId = getArguments().getLong(EXTRA_FEED_ID);
+ getParentFragmentManager().beginTransaction().replace(R.id.statisticsFragmentContainer,
+ FeedStatisticsFragment.newInstance(feedId, false), "feed_statistics_fragment")
+ .commitAllowingStateLoss();
+
+ root.findViewById(R.id.btnvOpenStatistics).setOnClickListener(view -> {
+ StatisticsFragment fragment = new StatisticsFragment();
+ ((MainActivity) getActivity()).loadChildFragment(fragment, TransitionEffect.SLIDE);
});
return root;
@@ -195,7 +179,6 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
.subscribe(result -> {
feed = result;
showFeed();
- loadStatistics();
}, error -> Log.d(TAG, Log.getStackTraceString(error)), () -> { });
}
@@ -270,53 +253,12 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
refreshToolbarState();
}
- private void loadStatistics() {
- if (disposableStatistics != null) {
- disposableStatistics.dispose();
- }
-
- disposableStatistics =
- Observable.fromCallable(() -> {
- List<StatisticsItem> statisticsData = DBReader.getStatistics();
-
- for (StatisticsItem statisticsItem : statisticsData) {
- if (statisticsItem.feed.getId() == feed.getId()) {
- return statisticsItem;
- }
- }
-
- return null;
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> {
- txtvPodcastTime.setText(Converter.shortLocalizedDuration(
- getContext(), result.timePlayed));
- txtvPodcastSpace.setText(Formatter.formatShortFileSize(
- getContext(), result.totalDownloadSize));
- txtvPodcastEpisodeCount.setText(String.format(Locale.getDefault(),
- "%d%s", result.episodesDownloadCount,
- getString(R.string.episodes_suffix)));
- }, error -> {
- Log.d(TAG, Log.getStackTraceString(error));
- lblStatistics.setVisibility(View.GONE);
- txtvPodcastSpace.setVisibility(View.GONE);
- txtvPodcastTime.setVisibility(View.GONE);
- txtvPodcastEpisodeCount.setVisibility(View.GONE);
- btnvOpenStatistics.setVisibility(View.GONE);
- });
- }
-
@Override
public void onDestroy() {
super.onDestroy();
if (disposable != null) {
disposable.dispose();
}
-
- if (disposableStatistics != null) {
- disposableStatistics.dispose();
- }
}
private void refreshToolbarState() {
@@ -351,9 +293,7 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
alert.setMessage(R.string.reconnect_local_folder_warning);
alert.setPositiveButton(android.R.string.ok, (dialog, which) -> {
try {
- Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- startActivityForResult(intent, REQUEST_CODE_ADD_LOCAL_FOLDER);
+ addLocalFolderLauncher.launch(null);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "No activity found. Should never happen...");
}
@@ -366,16 +306,11 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
return handled;
}
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode != Activity.RESULT_OK || data == null) {
+ private void addLocalFolderResult(final Uri uri) {
+ if (uri == null) {
return;
}
- Uri uri = data.getData();
-
- if (requestCode == REQUEST_CODE_ADD_LOCAL_FOLDER) {
- reconnectLocalFolder(uri);
- }
+ reconnectLocalFolder(uri);
}
private void reconnectLocalFolder(Uri uri) {
@@ -401,4 +336,14 @@ public class FeedInfoFragment extends Fragment implements Toolbar.OnMenuItemClic
error -> ((MainActivity) getActivity())
.showSnackbarAbovePlayer(error.getLocalizedMessage(), Snackbar.LENGTH_LONG));
}
+
+ private static class AddLocalFolder extends ActivityResultContracts.OpenDocumentTree {
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ @NonNull
+ @Override
+ public Intent createIntent(@NonNull final Context context, @Nullable final Uri input) {
+ return super.createIntent(context, input)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
}
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 0ee60866d..148cf6582 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -52,13 +52,13 @@ import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
-import de.danoeh.antennapod.core.event.FavoritesEvent;
-import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
-import de.danoeh.antennapod.core.event.PlayerStatusEvent;
-import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.FavoritesEvent;
+import de.danoeh.antennapod.event.FeedItemEvent;
+import de.danoeh.antennapod.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
+import de.danoeh.antennapod.event.PlayerStatusEvent;
+import de.danoeh.antennapod.event.QueueEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
@@ -333,8 +333,8 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
new RenameFeedDialog(getActivity(), feed).show();
return true;
} else if (itemId == R.id.remove_item) {
- RemoveFeedDialog.show(getContext(), feed, () ->
- ((MainActivity) getActivity()).loadFragment(EpisodesFragment.TAG, null));
+ ((MainActivity) getActivity()).loadFragment(EpisodesFragment.TAG, null);
+ RemoveFeedDialog.show(getContext(), feed);
return true;
} else if (itemId == R.id.action_search) {
((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(feed.getId(), feed.getTitle()));
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
index dbc7f2ae3..0c2103d25 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
@@ -7,16 +7,19 @@ import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.preference.ListPreference;
+import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.SwitchPreferenceCompat;
import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.settings.SkipIntroEndingChangedEvent;
-import de.danoeh.antennapod.core.event.settings.SpeedPresetChangedEvent;
-import de.danoeh.antennapod.core.event.settings.VolumeAdaptionChangedEvent;
+import de.danoeh.antennapod.event.settings.SkipIntroEndingChangedEvent;
+import de.danoeh.antennapod.event.settings.SpeedPresetChangedEvent;
+import de.danoeh.antennapod.event.settings.VolumeAdaptionChangedEvent;
+import de.danoeh.antennapod.databinding.PlaybackSpeedFeedSettingDialogBinding;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedFilter;
import de.danoeh.antennapod.model.feed.FeedPreferences;
@@ -35,12 +38,9 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import org.greenrobot.eventbus.EventBus;
-import java.text.DecimalFormat;
-import java.text.DecimalFormatSymbols;
+import java.util.Collections;
import java.util.Locale;
-import static de.danoeh.antennapod.model.feed.FeedPreferences.SPEED_USE_GLOBAL;
-
public class FeedSettingsFragment extends Fragment {
private static final String TAG = "FeedSettingsFragment";
private static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
@@ -104,8 +104,6 @@ public class FeedSettingsFragment extends Fragment {
private static final String PREF_FEED_PLAYBACK_SPEED = "feedPlaybackSpeed";
private static final String PREF_AUTO_SKIP = "feedAutoSkip";
private static final String PREF_TAGS = "tags";
- private static final DecimalFormat SPEED_FORMAT =
- new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.US));
private Feed feed;
private Disposable disposable;
@@ -164,7 +162,6 @@ public class FeedSettingsFragment extends Fragment {
updateAutoDeleteSummary();
updateVolumeReductionValue();
updateAutoDownloadEnabled();
- updatePlaybackSpeedPreference();
if (feed.isLocalFeed()) {
findPreference(PREF_AUTHENTICATION).setVisible(false);
@@ -205,27 +202,34 @@ public class FeedSettingsFragment extends Fragment {
}
private void setupPlaybackSpeedPreference() {
- ListPreference feedPlaybackSpeedPreference = findPreference(PREF_FEED_PLAYBACK_SPEED);
-
- final String[] speeds = getResources().getStringArray(R.array.playback_speed_values);
- String[] values = new String[speeds.length + 1];
- values[0] = SPEED_FORMAT.format(SPEED_USE_GLOBAL);
-
- String[] entries = new String[speeds.length + 1];
- entries[0] = getString(R.string.feed_auto_download_global);
-
- System.arraycopy(speeds, 0, values, 1, speeds.length);
- System.arraycopy(speeds, 0, entries, 1, speeds.length);
-
- feedPlaybackSpeedPreference.setEntryValues(values);
- feedPlaybackSpeedPreference.setEntries(entries);
- feedPlaybackSpeedPreference.setOnPreferenceChangeListener((preference, newValue) -> {
- feedPreferences.setFeedPlaybackSpeed(Float.parseFloat((String) newValue));
- DBWriter.setFeedPreferences(feedPreferences);
- updatePlaybackSpeedPreference();
- EventBus.getDefault().post(
- new SpeedPresetChangedEvent(feedPreferences.getFeedPlaybackSpeed(), feed.getId()));
- return false;
+ Preference feedPlaybackSpeedPreference = findPreference(PREF_FEED_PLAYBACK_SPEED);
+ feedPlaybackSpeedPreference.setOnPreferenceClickListener(preference -> {
+ PlaybackSpeedFeedSettingDialogBinding viewBinding =
+ PlaybackSpeedFeedSettingDialogBinding.inflate(getLayoutInflater());
+ viewBinding.seekBar.setProgressChangedListener(speed ->
+ viewBinding.currentSpeedLabel.setText(String.format(Locale.getDefault(), "%.2fx", speed)));
+ float speed = feedPreferences.getFeedPlaybackSpeed();
+ viewBinding.useGlobalCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ viewBinding.seekBar.setEnabled(!isChecked);
+ viewBinding.seekBar.setAlpha(isChecked ? 0.4f : 1f);
+ viewBinding.currentSpeedLabel.setAlpha(isChecked ? 0.4f : 1f);
+ });
+ viewBinding.useGlobalCheckbox.setChecked(speed == FeedPreferences.SPEED_USE_GLOBAL);
+ viewBinding.seekBar.updateSpeed(speed == FeedPreferences.SPEED_USE_GLOBAL ? 1 : speed);
+ new AlertDialog.Builder(getContext())
+ .setTitle(R.string.playback_speed)
+ .setView(viewBinding.getRoot())
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> {
+ float newSpeed = viewBinding.useGlobalCheckbox.isChecked()
+ ? FeedPreferences.SPEED_USE_GLOBAL : viewBinding.seekBar.getCurrentSpeed();
+ feedPreferences.setFeedPlaybackSpeed(newSpeed);
+ DBWriter.setFeedPreferences(feedPreferences);
+ EventBus.getDefault().post(
+ new SpeedPresetChangedEvent(feedPreferences.getFeedPlaybackSpeed(), feed.getId()));
+ })
+ .setNegativeButton(R.string.cancel_label, null)
+ .show();
+ return true;
});
}
@@ -277,13 +281,6 @@ public class FeedSettingsFragment extends Fragment {
});
}
- private void updatePlaybackSpeedPreference() {
- ListPreference feedPlaybackSpeedPreference = findPreference(PREF_FEED_PLAYBACK_SPEED);
-
- float speedValue = feedPreferences.getFeedPlaybackSpeed();
- feedPlaybackSpeedPreference.setValue(SPEED_FORMAT.format(speedValue));
- }
-
private void updateAutoDeleteSummary() {
ListPreference autoDeletePreference = findPreference(PREF_AUTO_DELETE);
@@ -395,7 +392,8 @@ public class FeedSettingsFragment extends Fragment {
private void setupTags() {
findPreference(PREF_TAGS).setOnPreferenceClickListener(preference -> {
- TagSettingsDialog.newInstance(feedPreferences).show(getChildFragmentManager(), TagSettingsDialog.TAG);
+ TagSettingsDialog.newInstance(Collections.singletonList(feedPreferences))
+ .show(getChildFragmentManager(), TagSettingsDialog.TAG);
return true;
});
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedStatisticsDialogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedStatisticsDialogFragment.java
new file mode 100644
index 000000000..33710b2c4
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedStatisticsDialogFragment.java
@@ -0,0 +1,42 @@
+package de.danoeh.antennapod.fragment;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
+import de.danoeh.antennapod.R;
+
+public class FeedStatisticsDialogFragment extends DialogFragment {
+ private static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
+ private static final String EXTRA_FEED_TITLE = "de.danoeh.antennapod.extra.feedTitle";
+
+ public static FeedStatisticsDialogFragment newInstance(long feedId, String feedTitle) {
+ FeedStatisticsDialogFragment fragment = new FeedStatisticsDialogFragment();
+ Bundle arguments = new Bundle();
+ arguments.putLong(EXTRA_FEED_ID, feedId);
+ arguments.putString(EXTRA_FEED_TITLE, feedTitle);
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.setTitle(getArguments().getString(EXTRA_FEED_TITLE));
+ dialog.setView(R.layout.feed_statistics_dialog);
+ return dialog.create();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ long feedId = getArguments().getLong(EXTRA_FEED_ID);
+ getChildFragmentManager().beginTransaction().replace(R.id.statisticsContainer,
+ FeedStatisticsFragment.newInstance(feedId, true), "feed_statistics_fragment")
+ .commitAllowingStateLoss();
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedStatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedStatisticsFragment.java
new file mode 100644
index 000000000..e85c2a386
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedStatisticsFragment.java
@@ -0,0 +1,93 @@
+package de.danoeh.antennapod.fragment;
+
+import android.os.Bundle;
+import android.text.format.Formatter;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.StatisticsItem;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.databinding.FeedStatisticsBinding;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.util.List;
+import java.util.Locale;
+
+public class FeedStatisticsFragment extends Fragment {
+ private static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
+ private static final String EXTRA_DETAILED = "de.danoeh.antennapod.extra.detailed";
+
+ private long feedId;
+ private Disposable disposable;
+ private FeedStatisticsBinding viewBinding;
+
+ public static FeedStatisticsFragment newInstance(long feedId, boolean detailed) {
+ FeedStatisticsFragment fragment = new FeedStatisticsFragment();
+ Bundle arguments = new Bundle();
+ arguments.putLong(EXTRA_FEED_ID, feedId);
+ arguments.putBoolean(EXTRA_DETAILED, detailed);
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ feedId = getArguments().getLong(EXTRA_FEED_ID);
+ viewBinding = FeedStatisticsBinding.inflate(inflater);
+
+ if (!getArguments().getBoolean(EXTRA_DETAILED)) {
+ for (int i = 0; i < viewBinding.getRoot().getChildCount(); i++) {
+ View child = viewBinding.getRoot().getChildAt(i);
+ if ("detailed".equals(child.getTag())) {
+ child.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ loadStatistics();
+ return viewBinding.getRoot();
+ }
+
+ private void loadStatistics() {
+ disposable =
+ Observable.fromCallable(() -> {
+ List<StatisticsItem> statisticsData = DBReader.getStatistics();
+ for (StatisticsItem statisticsItem : statisticsData) {
+ if (statisticsItem.feed.getId() == feedId) {
+ return statisticsItem;
+ }
+ }
+ return null;
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(this::showStats, Throwable::printStackTrace);
+ }
+
+ private void showStats(StatisticsItem s) {
+ viewBinding.startedTotalLabel.setText(String.format(Locale.getDefault(), "%d / %d",
+ s.episodesStarted, s.episodes));
+ viewBinding.timePlayedLabel.setText(Converter.shortLocalizedDuration(getContext(), s.timePlayed));
+ viewBinding.durationPlayedLabel.setText(Converter.shortLocalizedDuration(getContext(), s.timePlayedCountAll));
+ viewBinding.totalDurationLabel.setText(Converter.shortLocalizedDuration(getContext(), s.time));
+ viewBinding.onDeviceLabel.setText(String.format(Locale.getDefault(), "%d", s.episodesDownloadCount));
+ viewBinding.spaceUsedLabel.setText(Formatter.formatShortFileSize(getContext(), s.totalDownloadSize));
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+}
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 31c6da8cd..c261370e2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -14,7 +14,6 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.text.TextUtilsCompat;
import androidx.core.util.ObjectsCompat;
@@ -42,9 +41,9 @@ import de.danoeh.antennapod.adapter.actionbutton.StreamActionButton;
import de.danoeh.antennapod.adapter.actionbutton.VisitWebsiteActionButton;
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.PlayerStatusEvent;
-import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.FeedItemEvent;
+import de.danoeh.antennapod.event.PlayerStatusEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
@@ -224,17 +223,6 @@ public class ItemFragment extends Fragment {
}
@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);
- }
-
- @Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
@@ -245,6 +233,7 @@ public class ItemFragment extends Fragment {
}
};
controller.init();
+ load();
}
@Override
@@ -398,7 +387,7 @@ public class ItemFragment extends Fragment {
long mediaId = item.getMedia().getId();
if (ArrayUtils.contains(update.mediaIds, mediaId)) {
if (itemsLoaded && getActivity() != null) {
- updateAppearance();
+ updateButtons();
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
index d42300ca7..14f6ae875 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
@@ -20,7 +20,7 @@ import org.greenrobot.eventbus.ThreadMode;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.event.FeedItemEvent;
+import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java
index 826a7e0ab..18defc545 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java
@@ -28,9 +28,9 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
-import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
-import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.event.QueueEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.dialog.TagSettingsDialog;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -50,6 +50,7 @@ import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -157,16 +158,16 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS
};
removeAllNewFlagsConfirmationDialog.createNewDialog().show();
return true;
- } else if (itemId == R.id.add_to_folder) {
- TagSettingsDialog.newInstance(feed.getPreferences()).show(getChildFragmentManager(), TagSettingsDialog.TAG);
+ } else if (itemId == R.id.edit_tags) {
+ TagSettingsDialog.newInstance(Collections.singletonList(feed.getPreferences()))
+ .show(getChildFragmentManager(), TagSettingsDialog.TAG);
return true;
} else if (itemId == R.id.rename_item) {
new RenameFeedDialog(getActivity(), feed).show();
return true;
} else if (itemId == R.id.remove_item) {
- RemoveFeedDialog.show(getContext(), feed, () -> {
- ((MainActivity) getActivity()).loadFragment(EpisodesFragment.TAG, null);
- });
+ ((MainActivity) getActivity()).loadFragment(EpisodesFragment.TAG, null);
+ RemoveFeedDialog.show(getContext(), feed);
return true;
}
return super.onContextItemSelected(item);
@@ -318,7 +319,7 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS
((MainActivity) getActivity()).getBottomSheet()
.setState(BottomSheetBehavior.STATE_COLLAPSED);
} else {
- NavDrawerData.FolderDrawerItem folder = ((NavDrawerData.FolderDrawerItem) clickedItem);
+ NavDrawerData.TagDrawerItem folder = ((NavDrawerData.TagDrawerItem) clickedItem);
if (openFolders.contains(folder.name)) {
openFolders.remove(folder.name);
} else {
@@ -388,11 +389,11 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS
for (NavDrawerData.DrawerItem item : items) {
item.setLayer(layer);
flatItems.add(item);
- if (item.type == NavDrawerData.DrawerItem.Type.FOLDER) {
- NavDrawerData.FolderDrawerItem folder = ((NavDrawerData.FolderDrawerItem) item);
+ if (item.type == NavDrawerData.DrawerItem.Type.TAG) {
+ NavDrawerData.TagDrawerItem folder = ((NavDrawerData.TagDrawerItem) item);
folder.isOpen = openFolders.contains(folder.name);
if (folder.isOpen) {
- flatItems.addAll(makeFlatDrawerData(((NavDrawerData.FolderDrawerItem) item).children, layer + 1));
+ flatItems.addAll(makeFlatDrawerData(((NavDrawerData.TagDrawerItem) item).children, layer + 1));
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java
index 992b6930c..f3080f655 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/OnlineSearchFragment.java
@@ -1,15 +1,20 @@
package de.danoeh.antennapod.fragment;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+
+import android.widget.AbsListView;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.appcompat.widget.SearchView;
+
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ProgressBar;
@@ -110,6 +115,21 @@ public class OnlineSearchFragment extends Fragment {
TextView txtvPoweredBy = root.findViewById(R.id.search_powered_by);
txtvPoweredBy.setText(getString(R.string.search_powered_by, searchProvider.getName()));
setupToolbar(root.findViewById(R.id.toolbar));
+
+ gridView.setOnScrollListener(new AbsListView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ if (scrollState == SCROLL_STATE_TOUCH_SCROLL) {
+ InputMethodManager imm = (InputMethodManager)
+ getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
+ }
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+ }
+ });
return root;
}
@@ -142,6 +162,11 @@ public class OnlineSearchFragment extends Fragment {
return false;
}
});
+ sv.setOnQueryTextFocusChangeListener((view, hasFocus) -> {
+ if (hasFocus) {
+ showInputMethod(view.findFocus());
+ }
+ });
searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
@@ -192,4 +217,11 @@ public class OnlineSearchFragment extends Fragment {
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
}
+
+ private void showInputMethod(View view) {
+ InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.showSoftInput(view, 0);
+ }
+ }
}
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 5e3d36c03..e1fa5eeb6 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -16,11 +16,11 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
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.PlaybackHistoryEvent;
-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.event.FeedItemEvent;
+import de.danoeh.antennapod.event.playback.PlaybackHistoryEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
+import de.danoeh.antennapod.event.PlayerStatusEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
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 1b7d236c6..b308db0f6 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -33,11 +33,11 @@ import de.danoeh.antennapod.adapter.QueueRecyclerAdapter;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
-import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
-import de.danoeh.antennapod.core.event.PlayerStatusEvent;
-import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.FeedItemEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
+import de.danoeh.antennapod.event.PlayerStatusEvent;
+import de.danoeh.antennapod.event.QueueEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.fragment.actions.EpisodeMultiSelectActionHandler;
import de.danoeh.antennapod.fragment.swipeactions.SwipeActions;
@@ -247,8 +247,9 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
private void refreshToolbarState() {
- toolbar.getMenu().findItem(R.id.queue_lock).setChecked(UserPreferences.isQueueLocked());
boolean keepSorted = UserPreferences.isQueueKeepSorted();
+ toolbar.getMenu().findItem(R.id.queue_lock).setChecked(UserPreferences.isQueueLocked());
+ toolbar.getMenu().findItem(R.id.queue_lock).setVisible(!keepSorted);
toolbar.getMenu().findItem(R.id.queue_sort_random).setVisible(!keepSorted);
toolbar.getMenu().findItem(R.id.queue_keep_sorted).setChecked(keepSorted);
isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(toolbar.getMenu(),
@@ -635,11 +636,6 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
}
@Override
- public boolean isItemViewSwipeEnabled() {
- return !UserPreferences.isQueueLocked();
- }
-
- @Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
// Check if drag finished
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 14f355b52..8bfcfd1ed 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
@@ -25,7 +25,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.adapter.FeedDiscoverAdapter;
-import de.danoeh.antennapod.core.event.DiscoveryDefaultUpdateEvent;
+import de.danoeh.antennapod.event.DiscoveryDefaultUpdateEvent;
import de.danoeh.antennapod.discovery.ItunesTopListLoader;
import de.danoeh.antennapod.discovery.PodcastSearchResult;
import io.reactivex.disposables.Disposable;
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 f8326d9c1..e43b6f314 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.fragment;
+
+import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -9,6 +11,7 @@ import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.view.inputmethod.InputMethodManager;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -26,10 +29,10 @@ import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
import de.danoeh.antennapod.adapter.FeedSearchResultAdapter;
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.event.FeedItemEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
+import de.danoeh.antennapod.event.PlayerStatusEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.FeedSearcher;
@@ -70,7 +73,6 @@ public class SearchFragment extends Fragment {
private SearchView searchView;
private Handler automaticSearchDebouncer;
private long lastQueryChange = 0;
-
/**
* Create a new SearchFragment that searches all feeds.
*/
@@ -153,6 +155,22 @@ public class SearchFragment extends Fragment {
if (getArguments().getString(ARG_QUERY, null) != null) {
search();
}
+ searchView.setOnQueryTextFocusChangeListener((view, hasFocus) -> {
+ if (hasFocus) {
+ showInputMethod(view.findFocus());
+ }
+ });
+ recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
+ super.onScrollStateChanged(recyclerView, newState);
+ if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
+ InputMethodManager imm = (InputMethodManager)
+ getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(recyclerView.getWindowToken(), 0);
+ }
+ }
+ });
return layout;
}
@@ -320,4 +338,11 @@ public class SearchFragment extends Fragment {
List<Feed> feeds = FeedSearcher.searchFeeds(getContext(), query);
return new Pair<>(items, feeds);
}
+
+ private void showInputMethod(View view) {
+ InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.showSoftInput(view, 0);
+ }
+ }
}
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 ea6c2ca0d..c4ac25455 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -33,6 +33,7 @@ import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.Callable;
@@ -42,8 +43,8 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.SubscriptionsRecyclerAdapter;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
-import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
@@ -250,8 +251,8 @@ public class SubscriptionFragment extends Fragment
}
@Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
+ public void onViewCreated(@NonNull View v, Bundle savedInstanceState) {
+ super.onViewCreated(v, savedInstanceState);
subscriptionAdapter = new SubscriptionsRecyclerAdapter((MainActivity) getActivity());
subscriptionAdapter.setOnSelectModeListener(this);
subscriptionRecycler.setAdapter(subscriptionAdapter);
@@ -293,9 +294,9 @@ public class SubscriptionFragment extends Fragment
NavDrawerData data = DBReader.getNavDrawerData();
List<NavDrawerData.DrawerItem> items = data.items;
for (NavDrawerData.DrawerItem item : items) {
- if (item.type == NavDrawerData.DrawerItem.Type.FOLDER
+ if (item.type == NavDrawerData.DrawerItem.Type.TAG
&& item.getTitle().equals(displayedFolder)) {
- return ((NavDrawerData.FolderDrawerItem) item).children;
+ return ((NavDrawerData.TagDrawerItem) item).children;
}
}
return items;
@@ -344,14 +345,15 @@ public class SubscriptionFragment extends Fragment
R.string.remove_all_new_flags_confirmation_msg,
() -> DBWriter.removeFeedNewFlag(feed.getId()));
return true;
- } else if (itemId == R.id.add_to_folder) {
- TagSettingsDialog.newInstance(feed.getPreferences()).show(getChildFragmentManager(), TagSettingsDialog.TAG);
+ } else if (itemId == R.id.edit_tags) {
+ TagSettingsDialog.newInstance(Collections.singletonList(feed.getPreferences()))
+ .show(getChildFragmentManager(), TagSettingsDialog.TAG);
return true;
} else if (itemId == R.id.rename_item) {
new RenameFeedDialog(getActivity(), feed).show();
return true;
} else if (itemId == R.id.remove_item) {
- RemoveFeedDialog.show(getContext(), feed, null);
+ RemoveFeedDialog.show(getContext(), feed);
return true;
} else if (itemId == R.id.multi_select) {
speedDialView.setVisibility(View.VISIBLE);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/actions/FeedMultiSelectActionHandler.java b/app/src/main/java/de/danoeh/antennapod/fragment/actions/FeedMultiSelectActionHandler.java
index f160b2241..e3dfe8ade 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/actions/FeedMultiSelectActionHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/actions/FeedMultiSelectActionHandler.java
@@ -3,19 +3,23 @@ package de.danoeh.antennapod.fragment.actions;
import android.util.Log;
import androidx.annotation.PluralsRes;
+import androidx.appcompat.app.AlertDialog;
import androidx.core.util.Consumer;
import com.google.android.material.snackbar.Snackbar;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
+import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.databinding.PlaybackSpeedFeedSettingDialogBinding;
import de.danoeh.antennapod.dialog.RemoveFeedDialog;
+import de.danoeh.antennapod.dialog.TagSettingsDialog;
import de.danoeh.antennapod.fragment.preferences.dialog.PreferenceListDialog;
import de.danoeh.antennapod.fragment.preferences.dialog.PreferenceSwitchDialog;
import de.danoeh.antennapod.model.feed.Feed;
@@ -33,7 +37,7 @@ public class FeedMultiSelectActionHandler {
public void handleAction(int id) {
if (id == R.id.remove_item) {
- RemoveFeedDialog.show(activity, selectedItems, null);
+ RemoveFeedDialog.show(activity, selectedItems);
} else if (id == R.id.keep_updated) {
keepUpdatedPrefHandler();
} else if (id == R.id.autodownload) {
@@ -42,6 +46,8 @@ public class FeedMultiSelectActionHandler {
autoDeleteEpisodesPrefHandler();
} else if (id == R.id.playback_speed) {
playbackSpeedPrefHandler();
+ } else if (id == R.id.edit_tags) {
+ editFeedPrefTags();
} else {
Log.e(TAG, "Unrecognized speed dial action item. Do nothing. id=" + id);
}
@@ -64,25 +70,26 @@ public class FeedMultiSelectActionHandler {
new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.US));
private void playbackSpeedPrefHandler() {
- final String[] speeds = activity.getResources().getStringArray(R.array.playback_speed_values);
- String[] values = new String[speeds.length + 1];
- values[0] = SPEED_FORMAT.format(FeedPreferences.SPEED_USE_GLOBAL);
-
- String[] entries = new String[speeds.length + 1];
- entries[0] = activity.getString(R.string.feed_auto_download_global);
-
- System.arraycopy(speeds, 0, values, 1, speeds.length);
- System.arraycopy(speeds, 0, entries, 1, speeds.length);
-
- PreferenceListDialog preferenceListDialog = new PreferenceListDialog(activity,
- activity.getString(R.string.playback_speed));
- preferenceListDialog.openDialog(entries);
- preferenceListDialog.setOnPreferenceChangedListener(pos -> {
- saveFeedPreferences(feedPreferences -> {
- feedPreferences.setFeedPlaybackSpeed(Float.parseFloat((String) values[pos]));
- });
-
+ PlaybackSpeedFeedSettingDialogBinding viewBinding =
+ PlaybackSpeedFeedSettingDialogBinding.inflate(activity.getLayoutInflater());
+ viewBinding.seekBar.setProgressChangedListener(speed ->
+ viewBinding.currentSpeedLabel.setText(String.format(Locale.getDefault(), "%.2fx", speed)));
+ viewBinding.useGlobalCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ viewBinding.seekBar.setEnabled(!isChecked);
+ viewBinding.seekBar.setAlpha(isChecked ? 0.4f : 1f);
+ viewBinding.currentSpeedLabel.setAlpha(isChecked ? 0.4f : 1f);
});
+ viewBinding.seekBar.updateSpeed(1.0f);
+ new AlertDialog.Builder(activity)
+ .setTitle(R.string.playback_speed)
+ .setView(viewBinding.getRoot())
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> {
+ float newSpeed = viewBinding.useGlobalCheckbox.isChecked()
+ ? FeedPreferences.SPEED_USE_GLOBAL : viewBinding.seekBar.getCurrentSpeed();
+ saveFeedPreferences(feedPreferences -> feedPreferences.setFeedPlaybackSpeed(newSpeed));
+ })
+ .setNegativeButton(R.string.cancel_label, null)
+ .show();
}
private void autoDeleteEpisodesPrefHandler() {
@@ -136,4 +143,13 @@ public class FeedMultiSelectActionHandler {
}
showMessage(R.plurals.updated_feeds_batch_label, selectedItems.size());
}
+
+ private void editFeedPrefTags() {
+ ArrayList<FeedPreferences> preferencesList = new ArrayList<>();
+ for (Feed feed : selectedItems) {
+ preferencesList.add(feed.getPreferences());
+ }
+ TagSettingsDialog.newInstance(preferencesList).show(activity.getSupportFragmentManager(),
+ TagSettingsDialog.TAG);
+ }
}
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 c813cbf7a..c2c5adc9a 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
@@ -15,7 +15,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.adapter.gpodnet.PodcastListAdapter;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.core.sync.SynchronizationCredentials;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetServiceException;
@@ -76,8 +76,8 @@ public abstract class PodcastListFragment extends Fragment {
disposable = Observable.fromCallable(
() -> {
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
- GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(),
- GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword());
+ SynchronizationCredentials.getHosturl(), SynchronizationCredentials.getDeviceID(),
+ SynchronizationCredentials.getUsername(), SynchronizationCredentials.getPassword());
return loadPodcastData(service);
})
.subscribeOn(Schedulers.io())
@@ -101,7 +101,7 @@ public abstract class PodcastListFragment extends Fragment {
}, error -> {
gridView.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
- txtvError.setText(getString(R.string.error_msg_prefix) + error.getMessage());
+ txtvError.setText(error.getMessage());
txtvError.setVisibility(View.VISIBLE);
butRetry.setVisibility(View.VISIBLE);
Log.e(TAG, Log.getStackTraceString(error));
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 f961e30bb..abdfab941 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
@@ -8,7 +8,7 @@ import androidx.annotation.NonNull;
import androidx.fragment.app.ListFragment;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.gpodnet.TagListAdapter;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.core.sync.SynchronizationCredentials;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetTag;
@@ -51,8 +51,8 @@ public class TagListFragment extends ListFragment {
disposable = Observable.fromCallable(
() -> {
GpodnetService service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
- GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(),
- GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword());
+ SynchronizationCredentials.getHosturl(), SynchronizationCredentials.getDeviceID(),
+ SynchronizationCredentials.getUsername(), SynchronizationCredentials.getPassword());
return service.getTopTags(COUNT);
})
.subscribeOn(Schedulers.io())
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
deleted file mode 100644
index 4fb734e17..000000000
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package de.danoeh.antennapod.fragment.preferences;
-
-import android.app.Activity;
-import android.os.Bundle;
-import androidx.core.text.HtmlCompat;
-import androidx.preference.PreferenceFragmentCompat;
-
-import android.text.Spanned;
-import android.text.format.DateUtils;
-import com.google.android.material.snackbar.Snackbar;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.PreferenceActivity;
-import de.danoeh.antennapod.core.event.SyncServiceEvent;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
-import de.danoeh.antennapod.core.sync.SyncService;
-import de.danoeh.antennapod.dialog.AuthenticationDialog;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
- private static final String PREF_GPODNET_LOGIN = "pref_gpodnet_authenticate";
- private static final String PREF_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information";
- private static final String PREF_GPODNET_SYNC = "pref_gpodnet_sync";
- private static final String PREF_GPODNET_FORCE_FULL_SYNC = "pref_gpodnet_force_full_sync";
- private static final String PREF_GPODNET_LOGOUT = "pref_gpodnet_logout";
-
- @Override
- public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
- addPreferencesFromResource(R.xml.preferences_gpodder);
- setupGpodderScreen();
- }
-
- @Override
- public void onStart() {
- super.onStart();
- ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.gpodnet_main_label);
- updateGpodnetPreferenceScreen();
- EventBus.getDefault().register(this);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- EventBus.getDefault().unregister(this);
- ((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle("");
- }
-
- @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
- public void syncStatusChanged(SyncServiceEvent event) {
- updateGpodnetPreferenceScreen();
- if (!GpodnetPreferences.loggedIn()) {
- return;
- }
- if (event.getMessageResId() == R.string.sync_status_error
- || event.getMessageResId() == R.string.sync_status_success) {
- updateLastGpodnetSyncReport(SyncService.isLastSyncSuccessful(getContext()),
- SyncService.getLastSyncAttempt(getContext()));
- } else {
- ((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle(event.getMessageResId());
- }
- }
-
- private void setupGpodderScreen() {
- final Activity activity = getActivity();
-
- findPreference(PREF_GPODNET_LOGIN).setOnPreferenceClickListener(preference -> {
- new GpodderAuthenticationFragment().show(getChildFragmentManager(), GpodderAuthenticationFragment.TAG);
- return true;
- });
- findPreference(PREF_GPODNET_SETLOGIN_INFORMATION)
- .setOnPreferenceClickListener(preference -> {
- AuthenticationDialog dialog = new AuthenticationDialog(activity,
- R.string.pref_gpodnet_setlogin_information_title, false, GpodnetPreferences.getUsername(),
- null) {
-
- @Override
- protected void onConfirmed(String username, String password) {
- GpodnetPreferences.setPassword(password);
- }
- };
- dialog.show();
- return true;
- });
- findPreference(PREF_GPODNET_SYNC).setOnPreferenceClickListener(preference -> {
- SyncService.syncImmediately(getActivity().getApplicationContext());
- return true;
- });
- findPreference(PREF_GPODNET_FORCE_FULL_SYNC).setOnPreferenceClickListener(preference -> {
- SyncService.fullSync(getContext());
- return true;
- });
- findPreference(PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(preference -> {
- GpodnetPreferences.logout();
- Snackbar.make(getView(), R.string.pref_gpodnet_logout_toast, Snackbar.LENGTH_LONG).show();
- updateGpodnetPreferenceScreen();
- return true;
- });
- }
-
- private void updateGpodnetPreferenceScreen() {
- final boolean loggedIn = GpodnetPreferences.loggedIn();
- findPreference(PREF_GPODNET_LOGIN).setEnabled(!loggedIn);
- findPreference(PREF_GPODNET_SETLOGIN_INFORMATION).setEnabled(loggedIn);
- findPreference(PREF_GPODNET_SYNC).setEnabled(loggedIn);
- findPreference(PREF_GPODNET_FORCE_FULL_SYNC).setEnabled(loggedIn);
- findPreference(PREF_GPODNET_LOGOUT).setEnabled(loggedIn);
- if (loggedIn) {
- String format = getActivity().getString(R.string.pref_gpodnet_login_status);
- String summary = String.format(format, GpodnetPreferences.getUsername(),
- GpodnetPreferences.getDeviceID());
- Spanned formattedSummary = HtmlCompat.fromHtml(summary, HtmlCompat.FROM_HTML_MODE_LEGACY);
- findPreference(PREF_GPODNET_LOGOUT).setSummary(formattedSummary);
- updateLastGpodnetSyncReport(SyncService.isLastSyncSuccessful(getContext()),
- SyncService.getLastSyncAttempt(getContext()));
- } else {
- findPreference(PREF_GPODNET_LOGOUT).setSummary(null);
- }
- }
-
- private void updateLastGpodnetSyncReport(boolean successful, long lastTime) {
- String status = String.format("%1$s (%2$s)", getString(successful
- ? R.string.gpodnetsync_pref_report_successful : R.string.gpodnetsync_pref_report_failed),
- DateUtils.getRelativeDateTimeString(getContext(),
- lastTime, DateUtils.MINUTE_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, DateUtils.FORMAT_SHOW_TIME));
- ((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle(status);
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java
index f6aa45e93..b72d1eb32 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/ImportExportPreferencesFragment.java
@@ -12,6 +12,13 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
+
+import androidx.activity.result.ActivityResult;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+import androidx.activity.result.contract.ActivityResultContracts.GetContent;
+import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
+import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.FileProvider;
import androidx.preference.PreferenceFragmentCompat;
@@ -54,13 +61,19 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
private static final String DEFAULT_HTML_OUTPUT_NAME = "antennapod-feeds-%s.html";
private static final String CONTENT_TYPE_HTML = "text/html";
private static final String DEFAULT_FAVORITES_OUTPUT_NAME = "antennapod-favorites-%s.html";
- private static final int REQUEST_CODE_CHOOSE_OPML_EXPORT_PATH = 1;
- private static final int REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH = 2;
- private static final int REQUEST_CODE_CHOOSE_HTML_EXPORT_PATH = 3;
- private static final int REQUEST_CODE_RESTORE_DATABASE = 4;
- private static final int REQUEST_CODE_BACKUP_DATABASE = 5;
- private static final int REQUEST_CODE_CHOOSE_FAVORITES_EXPORT_PATH = 6;
private static final String DATABASE_EXPORT_FILENAME = "AntennaPodBackup-%s.db";
+ private final ActivityResultLauncher<Intent> chooseOpmlExportPathLauncher =
+ registerForActivityResult(new StartActivityForResult(), this::chooseOpmlExportPathResult);
+ private final ActivityResultLauncher<Intent> chooseHtmlExportPathLauncher =
+ registerForActivityResult(new StartActivityForResult(), this::chooseHtmlExportPathResult);
+ private final ActivityResultLauncher<Intent> chooseFavoritesExportPathLauncher =
+ registerForActivityResult(new StartActivityForResult(), this::chooseFavoritesExportPathResult);
+ private final ActivityResultLauncher<Intent> restoreDatabaseLauncher =
+ registerForActivityResult(new StartActivityForResult(), this::restoreDatabaseResult);
+ private final ActivityResultLauncher<String> backupDatabaseLauncher =
+ registerForActivityResult(new BackupDatabase(), this::backupDatabaseResult);
+ private final ActivityResultLauncher<String> chooseOpmlImportPathLauncher =
+ registerForActivityResult(new GetContent(), this::chooseOpmlImportPathResult);
private Disposable disposable;
private ProgressDialog progressDialog;
@@ -95,23 +108,20 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
preference -> {
openExportPathPicker(CONTENT_TYPE_OPML, dateStampFilename(DEFAULT_OPML_OUTPUT_NAME),
- REQUEST_CODE_CHOOSE_OPML_EXPORT_PATH, new OpmlWriter());
+ chooseOpmlExportPathLauncher, new OpmlWriter());
return true;
}
);
findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
preference -> {
openExportPathPicker(CONTENT_TYPE_HTML, dateStampFilename(DEFAULT_HTML_OUTPUT_NAME),
- REQUEST_CODE_CHOOSE_HTML_EXPORT_PATH, new HtmlWriter());
+ chooseHtmlExportPathLauncher, new HtmlWriter());
return true;
});
findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
preference -> {
try {
- Intent intentGetContentAction = new Intent(Intent.ACTION_GET_CONTENT);
- intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
- intentGetContentAction.setType("*/*");
- startActivityForResult(intentGetContentAction, REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH);
+ chooseOpmlImportPathLauncher.launch("*/*");
} catch (ActivityNotFoundException e) {
Log.e(TAG, "No activity found. Should never happen...");
}
@@ -130,7 +140,7 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
findPreference(PREF_FAVORITE_EXPORT).setOnPreferenceClickListener(
preference -> {
openExportPathPicker(CONTENT_TYPE_HTML, dateStampFilename(DEFAULT_FAVORITES_OUTPUT_NAME),
- REQUEST_CODE_CHOOSE_FAVORITES_EXPORT_PATH, new FavoritesWriter());
+ chooseFavoritesExportPathLauncher, new FavoritesWriter());
return true;
});
}
@@ -160,12 +170,7 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
private void exportDatabase() {
if (Build.VERSION.SDK_INT >= 19) {
- Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT)
- .addCategory(Intent.CATEGORY_OPENABLE)
- .setType("application/x-sqlite3")
- .putExtra(Intent.EXTRA_TITLE, dateStampFilename(DATABASE_EXPORT_FILENAME));
-
- startActivityForResult(intent, REQUEST_CODE_BACKUP_DATABASE);
+ backupDatabaseLauncher.launch(dateStampFilename(DATABASE_EXPORT_FILENAME));
} else {
File sd = Environment.getExternalStorageDirectory();
File backupDB = new File(sd, dateStampFilename(DATABASE_EXPORT_FILENAME));
@@ -190,18 +195,10 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
// add a button
builder.setNegativeButton(R.string.no, null);
builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
- if (Build.VERSION.SDK_INT >= 19) {
- Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
- intent.setType("*/*");
- startActivityForResult(intent, REQUEST_CODE_RESTORE_DATABASE);
- } else {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.setType("*/*");
- startActivityForResult(Intent.createChooser(intent,
- getString(R.string.import_select_file)), REQUEST_CODE_RESTORE_DATABASE);
- }
- }
- );
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.setType("*/*");
+ restoreDatabaseLauncher.launch(intent);
+ });
// create and show the alert dialog
builder.show();
@@ -227,15 +224,14 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
sendIntent.putExtra(Intent.EXTRA_STREAM, streamUri);
sendIntent.setType("text/plain");
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- List<ResolveInfo> resInfoList = getContext().getPackageManager()
- .queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- getContext().grantUriPermission(packageName, streamUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
+ Intent chooserIntent = Intent.createChooser(sendIntent, getString(R.string.send_label));
+ List<ResolveInfo> resInfoList = getContext().getPackageManager()
+ .queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfoList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ getContext().grantUriPermission(packageName, streamUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
- getContext().startActivity(Intent.createChooser(sendIntent, getString(R.string.send_label)));
+ getContext().startActivity(chooserIntent);
});
alert.create().show();
}
@@ -249,64 +245,97 @@ public class ImportExportPreferencesFragment extends PreferenceFragmentCompat {
alert.show();
}
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode != Activity.RESULT_OK || data == null) {
+ private void chooseOpmlExportPathResult(final ActivityResult result) {
+ if (result.getResultCode() != Activity.RESULT_OK || result.getData() == null) {
return;
}
- Uri uri = data.getData();
+ final Uri uri = result.getData().getData();
+ exportWithWriter(new OpmlWriter(), uri);
+ }
- if (requestCode == REQUEST_CODE_CHOOSE_OPML_EXPORT_PATH) {
- exportWithWriter(new OpmlWriter(), uri);
- } else if (requestCode == REQUEST_CODE_CHOOSE_HTML_EXPORT_PATH) {
- exportWithWriter(new HtmlWriter(), uri);
- } else if (requestCode == REQUEST_CODE_CHOOSE_FAVORITES_EXPORT_PATH) {
- exportWithWriter(new FavoritesWriter(), uri);
- } else if (requestCode == REQUEST_CODE_RESTORE_DATABASE) {
- progressDialog.show();
- disposable = Completable.fromAction(() -> DatabaseExporter.importBackup(uri, getContext()))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(() -> {
- showDatabaseImportSuccessDialog();
- UserPreferences.unsetUsageCountingDate();
- progressDialog.dismiss();
- }, this::showExportErrorDialog);
- } else if (requestCode == REQUEST_CODE_BACKUP_DATABASE) {
- progressDialog.show();
- disposable = Completable.fromAction(() -> DatabaseExporter.exportToDocument(uri, getContext()))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(() -> {
- Snackbar.make(getView(), R.string.export_success_title, Snackbar.LENGTH_LONG).show();
- progressDialog.dismiss();
- }, this::showExportErrorDialog);
- } else if (requestCode == REQUEST_CODE_CHOOSE_OPML_IMPORT_PATH) {
- Intent intent = new Intent(getContext(), OpmlImportActivity.class);
- intent.setData(uri);
- startActivity(intent);
+ private void chooseHtmlExportPathResult(final ActivityResult result) {
+ if (result.getResultCode() != Activity.RESULT_OK || result.getData() == null) {
+ return;
}
+ final Uri uri = result.getData().getData();
+ exportWithWriter(new HtmlWriter(), uri);
}
- private void openExportPathPicker(String contentType, String title, int requestCode, ExportWriter writer) {
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
- Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
- .addCategory(Intent.CATEGORY_OPENABLE)
- .setType(contentType)
- .putExtra(Intent.EXTRA_TITLE, title);
+ private void chooseFavoritesExportPathResult(final ActivityResult result) {
+ if (result.getResultCode() != Activity.RESULT_OK || result.getData() == null) {
+ return;
+ }
+ final Uri uri = result.getData().getData();
+ exportWithWriter(new FavoritesWriter(), uri);
+ }
- // Creates an implicit intent to launch a file manager which lets
- // the user choose a specific directory to export to.
- try {
- startActivityForResult(intentPickAction, requestCode);
- return;
- } catch (ActivityNotFoundException e) {
- Log.e(TAG, "No activity found. Should never happen...");
- }
+ private void restoreDatabaseResult(final ActivityResult result) {
+ if (result.getResultCode() != Activity.RESULT_OK || result.getData() == null) {
+ return;
+ }
+ final Uri uri = result.getData().getData();
+ progressDialog.show();
+ disposable = Completable.fromAction(() -> DatabaseExporter.importBackup(uri, getContext()))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(() -> {
+ showDatabaseImportSuccessDialog();
+ UserPreferences.unsetUsageCountingDate();
+ progressDialog.dismiss();
+ }, this::showExportErrorDialog);
+ }
+
+ private void backupDatabaseResult(final Uri uri) {
+ if (uri == null) {
+ return;
+ }
+ progressDialog.show();
+ disposable = Completable.fromAction(() -> DatabaseExporter.exportToDocument(uri, getContext()))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(() -> {
+ Snackbar.make(getView(), R.string.export_success_title, Snackbar.LENGTH_LONG).show();
+ progressDialog.dismiss();
+ }, this::showExportErrorDialog);
+ }
+
+ private void chooseOpmlImportPathResult(final Uri uri) {
+ if (uri == null) {
+ return;
+ }
+ final Intent intent = new Intent(getContext(), OpmlImportActivity.class);
+ intent.setData(uri);
+ startActivity(intent);
+ }
+
+ private void openExportPathPicker(String contentType, String title,
+ final ActivityResultLauncher<Intent> result, ExportWriter writer) {
+ Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType(contentType)
+ .putExtra(Intent.EXTRA_TITLE, title);
+
+ // Creates an implicit intent to launch a file manager which lets
+ // the user choose a specific directory to export to.
+ try {
+ result.launch(intentPickAction);
+ return;
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found. Should never happen...");
}
// If we are using a SDK lower than API 21 or the implicit intent failed
// fallback to the legacy export process
exportWithWriter(writer, null);
}
+
+ private static class BackupDatabase extends ActivityResultContracts.CreateDocument {
+ @NonNull
+ @Override
+ public Intent createIntent(@NonNull final Context context, @NonNull final String input) {
+ return super.createIntent(context, input)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType("application/x-sqlite3");
+ }
+ }
}
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 cc09acbca..891d3737b 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
@@ -1,6 +1,8 @@
package de.danoeh.antennapod.fragment.preferences;
import android.content.Intent;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
@@ -17,12 +19,11 @@ import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.fragment.preferences.about.AboutFragment;
public class MainPreferencesFragment extends PreferenceFragmentCompat {
- private static final String TAG = "MainPreferencesFragment";
private static final String PREF_SCREEN_USER_INTERFACE = "prefScreenInterface";
private static final String PREF_SCREEN_PLAYBACK = "prefScreenPlayback";
private static final String PREF_SCREEN_NETWORK = "prefScreenNetwork";
- private static final String PREF_SCREEN_GPODDER = "prefScreenGpodder";
+ private static final String PREF_SCREEN_SYNCHRONIZATION = "prefScreenSynchronization";
private static final String PREF_SCREEN_STORAGE = "prefScreenStorage";
private static final String PREF_DOCUMENTATION = "prefDocumentation";
private static final String PREF_VIEW_FORUM = "prefViewForum";
@@ -43,15 +44,26 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
// and afterwards remove the following lines. Please keep in mind that AntennaPod is licensed under the GPL.
// This means that your application needs to be open-source under the GPL, too.
// It must also include a prominent copyright notice.
- String packageName = getContext().getPackageName();
- if (!"de.danoeh.antennapod".equals(packageName) && !"de.danoeh.antennapod.debug".equals(packageName)) {
+ int packageHash = getContext().getPackageName().hashCode();
+ if (packageHash != 1790437538 && packageHash != -1190467065) {
findPreference(PREF_CATEGORY_PROJECT).setVisible(false);
Preference copyrightNotice = new Preference(getContext());
+ copyrightNotice.setIcon(R.drawable.ic_info_white);
+ copyrightNotice.getIcon().mutate()
+ .setColorFilter(new PorterDuffColorFilter(0xffcc0000, PorterDuff.Mode.MULTIPLY));
copyrightNotice.setSummary("This application is based on AntennaPod."
+ " The AntennaPod team does NOT provide support for this unofficial version."
+ " If you can read this message, the developers of this modification"
+ " violate the GNU General Public License (GPL).");
findPreference(PREF_CATEGORY_PROJECT).getParent().addPreference(copyrightNotice);
+ } else if (packageHash == -1190467065) {
+ Preference debugNotice = new Preference(getContext());
+ debugNotice.setIcon(R.drawable.ic_info_white);
+ debugNotice.getIcon().mutate()
+ .setColorFilter(new PorterDuffColorFilter(0xffcc0000, PorterDuff.Mode.MULTIPLY));
+ debugNotice.setOrder(-1);
+ debugNotice.setSummary("This is a development version of AntennaPod and not meant for daily use");
+ findPreference(PREF_CATEGORY_PROJECT).getParent().addPreference(debugNotice);
}
}
@@ -74,8 +86,8 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_network);
return true;
});
- findPreference(PREF_SCREEN_GPODDER).setOnPreferenceClickListener(preference -> {
- ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_gpodder);
+ findPreference(PREF_SCREEN_SYNCHRONIZATION).setOnPreferenceClickListener(preference -> {
+ ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_synchronization);
return true;
});
findPreference(PREF_SCREEN_STORAGE).setOnPreferenceClickListener(preference -> {
@@ -142,8 +154,8 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
.addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_network))
.addBreadcrumb(R.string.automation)
.addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_autodownload));
- config.index(R.xml.preferences_gpodder)
- .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_gpodder));
+ config.index(R.xml.preferences_synchronization)
+ .addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_synchronization));
config.index(R.xml.preferences_notifications)
.addBreadcrumb(PreferenceActivity.getTitleOfPage(R.xml.preferences_notifications));
config.index(R.xml.feed_settings)
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java
index 94e151f7a..ba17cedb2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NotificationPreferencesFragment.java
@@ -4,11 +4,10 @@ import android.os.Bundle;
import androidx.preference.PreferenceFragmentCompat;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.core.sync.SynchronizationSettings;
public class NotificationPreferencesFragment extends PreferenceFragmentCompat {
- private static final String TAG = "NotificationPrefFragment";
private static final String PREF_GPODNET_NOTIFICATIONS = "pref_gpodnet_notifications";
@Override
@@ -24,7 +23,6 @@ public class NotificationPreferencesFragment extends PreferenceFragmentCompat {
}
private void setUpScreen() {
- final boolean loggedIn = GpodnetPreferences.loggedIn();
- findPreference(PREF_GPODNET_NOTIFICATIONS).setEnabled(loggedIn);
+ findPreference(PREF_GPODNET_NOTIFICATIONS).setEnabled(SynchronizationSettings.isProviderConnected());
}
}
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 1fa1fed58..7fa2ed4d1 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
@@ -10,13 +10,12 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
-import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.preferences.UsageStatistics;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
-import de.danoeh.antennapod.preferences.PreferenceControllerFlavorHelper;
import java.util.Map;
import org.greenrobot.eventbus.EventBus;
@@ -31,7 +30,6 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
addPreferencesFromResource(R.xml.preferences_playback);
setupPlaybackScreen();
- PreferenceControllerFlavorHelper.setupFlavoredUI(this);
buildSmartMarkAsPlayedPreference();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java
index 208ede8cc..04324f709 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackStatisticsFragment.java
@@ -68,7 +68,7 @@ public class PlaybackStatisticsFragment extends Fragment {
View root = inflater.inflate(R.layout.statistics_activity, container, false);
feedStatisticsList = root.findViewById(R.id.statistics_list);
progressBar = root.findViewById(R.id.progressBar);
- listAdapter = new PlaybackStatisticsListAdapter(getContext());
+ listAdapter = new PlaybackStatisticsListAdapter(this);
listAdapter.setCountAll(countAll);
feedStatisticsList.setLayoutManager(new LinearLayoutManager(getContext()));
feedStatisticsList.setAdapter(listAdapter);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
index 04b9677e2..ff974179e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
@@ -10,8 +10,8 @@ import androidx.preference.PreferenceFragmentCompat;
import android.widget.ListView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
-import de.danoeh.antennapod.core.event.PlayerStatusEvent;
-import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.PlayerStatusEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog;
import de.danoeh.antennapod.dialog.FeedSortDialog;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/GpodderAuthenticationFragment.java
index c0bf3e0ea..9dfe6840c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderAuthenticationFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/GpodderAuthenticationFragment.java
@@ -1,4 +1,4 @@
-package de.danoeh.antennapod.fragment.preferences;
+package de.danoeh.antennapod.fragment.preferences.synchronization;
import android.app.Dialog;
import android.content.Context;
@@ -15,30 +15,35 @@ import android.widget.ProgressBar;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.ViewFlipper;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
+
import com.google.android.material.button.MaterialButton;
import com.google.android.material.textfield.TextInputLayout;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.core.sync.SynchronizationCredentials;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.sync.SyncService;
-import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
-import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetDevice;
+import de.danoeh.antennapod.core.sync.SynchronizationProviderViewData;
+import de.danoeh.antennapod.core.sync.SynchronizationSettings;
import de.danoeh.antennapod.core.util.FileNameGenerator;
import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
+import de.danoeh.antennapod.net.sync.gpoddernet.model.GpodnetDevice;
import io.reactivex.Completable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
-import java.util.List;
-import java.util.Locale;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
/**
* Guides the user through the authentication process.
*/
@@ -83,23 +88,24 @@ public class GpodderAuthenticationFragment extends DialogFragment {
final RadioGroup serverRadioGroup = view.findViewById(R.id.serverRadioGroup);
final EditText serverUrlText = view.findViewById(R.id.serverUrlText);
- if (!GpodnetService.DEFAULT_BASE_HOST.equals(GpodnetPreferences.getHosturl())) {
- serverUrlText.setText(GpodnetPreferences.getHosturl());
+ if (!GpodnetService.DEFAULT_BASE_HOST.equals(SynchronizationCredentials.getHosturl())) {
+ serverUrlText.setText(SynchronizationCredentials.getHosturl());
}
final TextInputLayout serverUrlTextInput = view.findViewById(R.id.serverUrlTextInput);
serverRadioGroup.setOnCheckedChangeListener((group, checkedId) -> {
serverUrlTextInput.setVisibility(checkedId == R.id.customServerRadio ? View.VISIBLE : View.GONE);
});
selectHost.setOnClickListener(v -> {
+ SynchronizationCredentials.clear(getContext());
if (serverRadioGroup.getCheckedRadioButtonId() == R.id.customServerRadio) {
- GpodnetPreferences.setHosturl(serverUrlText.getText().toString());
+ SynchronizationCredentials.setHosturl(serverUrlText.getText().toString());
} else {
- GpodnetPreferences.setHosturl(GpodnetService.DEFAULT_BASE_HOST);
+ SynchronizationCredentials.setHosturl(GpodnetService.DEFAULT_BASE_HOST);
}
service = new GpodnetService(AntennapodHttpClient.getHttpClient(),
- GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(),
- GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword());
- getDialog().setTitle(GpodnetPreferences.getHosturl());
+ SynchronizationCredentials.getHosturl(), SynchronizationCredentials.getDeviceID(),
+ SynchronizationCredentials.getUsername(), SynchronizationCredentials.getPassword());
+ getDialog().setTitle(SynchronizationCredentials.getHosturl());
advance();
});
}
@@ -116,7 +122,7 @@ public class GpodderAuthenticationFragment extends DialogFragment {
createAccount.setPaintFlags(createAccount.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
createAccount.setOnClickListener(v -> IntentUtils.openInBrowser(getContext(), "https://gpodder.net/register/"));
- if (GpodnetPreferences.getHosturl().startsWith("http://")) {
+ if (SynchronizationCredentials.getHosturl().startsWith("http://")) {
createAccountWarning.setVisibility(View.VISIBLE);
}
password.setOnEditorActionListener((v, actionID, event) ->
@@ -265,15 +271,8 @@ public class GpodderAuthenticationFragment extends DialogFragment {
});
}
- private void writeLoginCredentials() {
- GpodnetPreferences.setUsername(username);
- GpodnetPreferences.setPassword(password);
- GpodnetPreferences.setDeviceID(selectedDevice.getId());
- }
-
private void advance() {
if (currentStep < STEP_FINISH) {
-
View view = viewFlipper.getChildAt(currentStep + 1);
if (currentStep == STEP_DEFAULT) {
setupHostView(view);
@@ -289,7 +288,10 @@ public class GpodderAuthenticationFragment extends DialogFragment {
if (selectedDevice == null) {
throw new IllegalStateException("Device must not be null here");
} else {
- writeLoginCredentials();
+ SynchronizationSettings.setSelectedSyncProvider(SynchronizationProviderViewData.GPODDER_NET);
+ SynchronizationCredentials.setUsername(username);
+ SynchronizationCredentials.setPassword(password);
+ SynchronizationCredentials.setDeviceID(selectedDevice.getId());
setupFinishView(view);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/NextcloudAuthenticationFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/NextcloudAuthenticationFragment.java
new file mode 100644
index 000000000..2e9260c1d
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/NextcloudAuthenticationFragment.java
@@ -0,0 +1,92 @@
+package de.danoeh.antennapod.fragment.preferences.synchronization;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.view.View;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
+import de.danoeh.antennapod.core.sync.SyncService;
+import de.danoeh.antennapod.core.sync.SynchronizationCredentials;
+import de.danoeh.antennapod.core.sync.SynchronizationProviderViewData;
+import de.danoeh.antennapod.core.sync.SynchronizationSettings;
+import de.danoeh.antennapod.databinding.NextcloudAuthDialogBinding;
+import de.danoeh.antennapod.net.sync.nextcloud.NextcloudLoginFlow;
+
+/**
+ * Guides the user through the authentication process.
+ */
+public class NextcloudAuthenticationFragment extends DialogFragment
+ implements NextcloudLoginFlow.AuthenticationCallback {
+ public static final String TAG = "NextcloudAuthenticationFragment";
+ private NextcloudAuthDialogBinding viewBinding;
+ private NextcloudLoginFlow nextcloudLoginFlow;
+ private boolean shouldDismiss = false;
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(getContext());
+ dialog.setTitle(R.string.gpodnetauth_login_butLabel);
+ dialog.setNegativeButton(R.string.cancel_label, null);
+ dialog.setCancelable(false);
+ this.setCancelable(false);
+
+ viewBinding = NextcloudAuthDialogBinding.inflate(getLayoutInflater());
+ dialog.setView(viewBinding.getRoot());
+
+ viewBinding.loginButton.setOnClickListener(v -> {
+ viewBinding.errorText.setVisibility(View.GONE);
+ viewBinding.loginButton.setVisibility(View.GONE);
+ viewBinding.loginProgressContainer.setVisibility(View.VISIBLE);
+ nextcloudLoginFlow = new NextcloudLoginFlow(AntennapodHttpClient.getHttpClient(),
+ viewBinding.serverUrlText.getText().toString(), getContext(), this);
+ nextcloudLoginFlow.start();
+ });
+
+ return dialog.create();
+ }
+
+ @Override
+ public void onDismiss(@NonNull DialogInterface dialog) {
+ super.onDismiss(dialog);
+ if (nextcloudLoginFlow != null) {
+ nextcloudLoginFlow.cancel();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (shouldDismiss) {
+ dismiss();
+ }
+ }
+
+ @Override
+ public void onNextcloudAuthenticated(String server, String username, String password) {
+ SynchronizationSettings.setSelectedSyncProvider(SynchronizationProviderViewData.NEXTCLOUD_GPODDER);
+ SynchronizationCredentials.clear(getContext());
+ SynchronizationCredentials.setPassword(password);
+ SynchronizationCredentials.setHosturl(server);
+ SynchronizationCredentials.setUsername(username);
+ SyncService.fullSync(getContext());
+ if (isVisible()) {
+ dismiss();
+ } else {
+ shouldDismiss = true;
+ }
+ }
+
+ @Override
+ public void onNextcloudAuthError(String errorMessage) {
+ viewBinding.loginProgressContainer.setVisibility(View.GONE);
+ viewBinding.errorText.setVisibility(View.VISIBLE);
+ viewBinding.errorText.setText(errorMessage);
+ viewBinding.loginButton.setVisibility(View.VISIBLE);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/SynchronizationPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/SynchronizationPreferencesFragment.java
new file mode 100644
index 000000000..8cb7f45db
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/synchronization/SynchronizationPreferencesFragment.java
@@ -0,0 +1,222 @@
+package de.danoeh.antennapod.fragment.preferences.synchronization;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.text.Spanned;
+import android.text.format.DateUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.text.HtmlCompat;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+
+import com.google.android.material.snackbar.Snackbar;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.event.SyncServiceEvent;
+import de.danoeh.antennapod.core.sync.SynchronizationCredentials;
+import de.danoeh.antennapod.core.sync.SyncService;
+import de.danoeh.antennapod.core.sync.SynchronizationProviderViewData;
+import de.danoeh.antennapod.core.sync.SynchronizationSettings;
+import de.danoeh.antennapod.dialog.AuthenticationDialog;
+
+public class SynchronizationPreferencesFragment extends PreferenceFragmentCompat {
+ private static final String PREFERENCE_SYNCHRONIZATION_DESCRIPTION = "preference_synchronization_description";
+ private static final String PREFERENCE_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information";
+ private static final String PREFERENCE_SYNC = "pref_synchronization_sync";
+ private static final String PREFERENCE_FORCE_FULL_SYNC = "pref_synchronization_force_full_sync";
+ private static final String PREFERENCE_LOGOUT = "pref_synchronization_logout";
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_synchronization);
+ setupScreen();
+ updateScreen();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.synchronization_pref);
+ updateScreen();
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ EventBus.getDefault().unregister(this);
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle("");
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
+ public void syncStatusChanged(SyncServiceEvent event) {
+ if (!SynchronizationSettings.isProviderConnected()) {
+ return;
+ }
+ updateScreen();
+ if (event.getMessageResId() == R.string.sync_status_error
+ || event.getMessageResId() == R.string.sync_status_success) {
+ updateLastSyncReport(SynchronizationSettings.isLastSyncSuccessful(),
+ SynchronizationSettings.getLastSyncAttempt());
+ } else {
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle(event.getMessageResId());
+ }
+ }
+
+ private void setupScreen() {
+ final Activity activity = getActivity();
+ findPreference(PREFERENCE_GPODNET_SETLOGIN_INFORMATION)
+ .setOnPreferenceClickListener(preference -> {
+ AuthenticationDialog dialog = new AuthenticationDialog(activity,
+ R.string.pref_gpodnet_setlogin_information_title,
+ false, SynchronizationCredentials.getUsername(), null) {
+ @Override
+ protected void onConfirmed(String username, String password) {
+ SynchronizationCredentials.setPassword(password);
+ }
+ };
+ dialog.show();
+ return true;
+ });
+ findPreference(PREFERENCE_SYNC).setOnPreferenceClickListener(preference -> {
+ SyncService.syncImmediately(getActivity().getApplicationContext());
+ return true;
+ });
+ findPreference(PREFERENCE_FORCE_FULL_SYNC).setOnPreferenceClickListener(preference -> {
+ SyncService.fullSync(getContext());
+ return true;
+ });
+ findPreference(PREFERENCE_LOGOUT).setOnPreferenceClickListener(preference -> {
+ SynchronizationCredentials.clear(getContext());
+ Snackbar.make(getView(), R.string.pref_synchronization_logout_toast, Snackbar.LENGTH_LONG).show();
+ SynchronizationSettings.setSelectedSyncProvider(null);
+ updateScreen();
+ return true;
+ });
+ }
+
+ private void updateScreen() {
+ final boolean loggedIn = SynchronizationSettings.isProviderConnected();
+ Preference preferenceHeader = findPreference(PREFERENCE_SYNCHRONIZATION_DESCRIPTION);
+ if (loggedIn) {
+ SynchronizationProviderViewData selectedProvider =
+ SynchronizationProviderViewData.fromIdentifier(getSelectedSyncProviderKey());
+ preferenceHeader.setTitle("");
+ preferenceHeader.setSummary(selectedProvider.getSummaryResource());
+ preferenceHeader.setIcon(selectedProvider.getIconResource());
+ preferenceHeader.setOnPreferenceClickListener(null);
+ } else {
+ preferenceHeader.setTitle(R.string.synchronization_choose_title);
+ preferenceHeader.setSummary(R.string.synchronization_summary_unchoosen);
+ preferenceHeader.setIcon(R.drawable.ic_cloud);
+ preferenceHeader.setOnPreferenceClickListener((preference) -> {
+ chooseProviderAndLogin();
+ return true;
+ });
+ }
+
+ Preference gpodnetSetLoginPreference = findPreference(PREFERENCE_GPODNET_SETLOGIN_INFORMATION);
+ gpodnetSetLoginPreference.setVisible(isProviderSelected(SynchronizationProviderViewData.GPODDER_NET));
+ gpodnetSetLoginPreference.setEnabled(loggedIn);
+ findPreference(PREFERENCE_SYNC).setEnabled(loggedIn);
+ findPreference(PREFERENCE_FORCE_FULL_SYNC).setEnabled(loggedIn);
+ findPreference(PREFERENCE_LOGOUT).setEnabled(loggedIn);
+ if (loggedIn) {
+ String summary = getString(R.string.synchronization_login_status,
+ SynchronizationCredentials.getUsername(), SynchronizationCredentials.getHosturl());
+ Spanned formattedSummary = HtmlCompat.fromHtml(summary, HtmlCompat.FROM_HTML_MODE_LEGACY);
+ findPreference(PREFERENCE_LOGOUT).setSummary(formattedSummary);
+ updateLastSyncReport(SynchronizationSettings.isLastSyncSuccessful(),
+ SynchronizationSettings.getLastSyncAttempt());
+ } else {
+ findPreference(PREFERENCE_LOGOUT).setSummary(null);
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle(null);
+ }
+ }
+
+ private void chooseProviderAndLogin() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ builder.setTitle(R.string.dialog_choose_sync_service_title);
+
+ SynchronizationProviderViewData[] providers = SynchronizationProviderViewData.values();
+ ListAdapter adapter = new ArrayAdapter<SynchronizationProviderViewData>(
+ getContext(), R.layout.alertdialog_sync_provider_chooser, providers) {
+
+ ViewHolder holder;
+
+ class ViewHolder {
+ ImageView icon;
+ TextView title;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final LayoutInflater inflater = LayoutInflater.from(getContext());
+ if (convertView == null) {
+ convertView = inflater.inflate(
+ R.layout.alertdialog_sync_provider_chooser, null);
+
+ holder = new ViewHolder();
+ holder.icon = (ImageView) convertView.findViewById(R.id.icon);
+ holder.title = (TextView) convertView.findViewById(R.id.title);
+ convertView.setTag(holder);
+ } else {
+ holder = (ViewHolder) convertView.getTag();
+ }
+ SynchronizationProviderViewData synchronizationProviderViewData = getItem(position);
+ holder.title.setText(synchronizationProviderViewData.getSummaryResource());
+ holder.icon.setImageResource(synchronizationProviderViewData.getIconResource());
+ return convertView;
+ }
+ };
+
+ builder.setAdapter(adapter, (dialog, which) -> {
+ switch (providers[which]) {
+ case GPODDER_NET:
+ new GpodderAuthenticationFragment()
+ .show(getChildFragmentManager(), GpodderAuthenticationFragment.TAG);
+ break;
+ case NEXTCLOUD_GPODDER:
+ new NextcloudAuthenticationFragment()
+ .show(getChildFragmentManager(), NextcloudAuthenticationFragment.TAG);
+ break;
+ default:
+ break;
+ }
+ updateScreen();
+ });
+
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ }
+
+ private boolean isProviderSelected(@NonNull SynchronizationProviderViewData provider) {
+ String selectedSyncProviderKey = getSelectedSyncProviderKey();
+ return provider.getIdentifier().equals(selectedSyncProviderKey);
+ }
+
+ private String getSelectedSyncProviderKey() {
+ return SynchronizationSettings.getSelectedSyncProviderKey();
+ }
+
+ private void updateLastSyncReport(boolean successful, long lastTime) {
+ String status = String.format("%1$s (%2$s)", getString(successful
+ ? R.string.gpodnetsync_pref_report_successful : R.string.gpodnetsync_pref_report_failed),
+ DateUtils.getRelativeDateTimeString(getContext(),
+ lastTime, DateUtils.MINUTE_IN_MILLIS, DateUtils.WEEK_IN_MILLIS, DateUtils.FORMAT_SHOW_TIME));
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setSubtitle(status);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/swipeactions/SwipeActions.java b/app/src/main/java/de/danoeh/antennapod/fragment/swipeactions/SwipeActions.java
index 50c7c1ae5..adf133856 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/swipeactions/SwipeActions.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/swipeactions/SwipeActions.java
@@ -201,12 +201,12 @@ public class SwipeActions extends ItemTouchHelper.SimpleCallback implements Life
@Override
public float getSwipeEscapeVelocity(float defaultValue) {
- return swipeOutEnabled ? defaultValue : Float.MAX_VALUE;
+ return swipeOutEnabled ? defaultValue * 1.5f : Float.MAX_VALUE;
}
@Override
public float getSwipeVelocityThreshold(float defaultValue) {
- return swipeOutEnabled ? defaultValue : 0;
+ return swipeOutEnabled ? defaultValue * 0.6f : 0;
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
index c272af7d5..23fdb86de 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -13,12 +13,12 @@ import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-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;
-import de.danoeh.antennapod.core.sync.SyncService;
+import de.danoeh.antennapod.core.sync.SynchronizationSettings;
+import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
@@ -151,7 +151,7 @@ public class FeedItemMenuHandler {
} else if (menuItemId == R.id.mark_read_item) {
selectedItem.setPlayed(true);
DBWriter.markItemPlayed(selectedItem, FeedItem.PLAYED, true);
- if (GpodnetPreferences.loggedIn()) {
+ if (SynchronizationSettings.isProviderConnected()) {
FeedMedia media = selectedItem.getMedia();
// not all items have media, Gpodder only cares about those that do
if (media != null) {
@@ -161,17 +161,17 @@ public class FeedItemMenuHandler {
.position(media.getDuration() / 1000)
.total(media.getDuration() / 1000)
.build();
- SyncService.enqueueEpisodeAction(context, actionPlay);
+ SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, actionPlay);
}
}
} else if (menuItemId == R.id.mark_unread_item) {
selectedItem.setPlayed(false);
DBWriter.markItemPlayed(selectedItem, FeedItem.UNPLAYED, false);
- if (GpodnetPreferences.loggedIn() && selectedItem.getMedia() != null) {
+ if (selectedItem.getMedia() != null) {
EpisodeAction actionNew = new EpisodeAction.Builder(selectedItem, EpisodeAction.NEW)
.currentTimestamp()
.build();
- SyncService.enqueueEpisodeAction(context, actionNew);
+ SynchronizationQueueSink.enqueueEpisodeActionIfSynchronizationIsActive(context, actionNew);
}
} else if (menuItemId == R.id.add_to_queue_item) {
DBWriter.addQueueItem(context, selectedItem);
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 84c738632..af35bbac9 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
@@ -108,9 +108,12 @@ public class PreferenceUpgrader {
}
}
if (oldVersion < 2040000) {
- SharedPreferences prefs = context.getSharedPreferences(SwipeActions.PREF_NAME, Context.MODE_PRIVATE);
- prefs.edit().putString(SwipeActions.KEY_PREFIX_SWIPEACTIONS + QueueFragment.TAG,
+ SharedPreferences swipePrefs = context.getSharedPreferences(SwipeActions.PREF_NAME, Context.MODE_PRIVATE);
+ swipePrefs.edit().putString(SwipeActions.KEY_PREFIX_SWIPEACTIONS + QueueFragment.TAG,
SwipeAction.REMOVE_FROM_QUEUE + "," + SwipeAction.REMOVE_FROM_QUEUE).apply();
}
+ if (oldVersion < 2050000) {
+ prefs.edit().putBoolean(UserPreferences.PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS, true).apply();
+ }
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java b/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
index 1075117dd..2ea15005a 100644
--- a/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
+++ b/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
@@ -22,7 +22,7 @@ public class ConnectivityActionReceiver extends BroadcastReceiver {
Log.d(TAG, "Received intent");
ClientConfig.initialize(context);
- if (NetworkUtils.autodownloadNetworkAvailable()) {
+ if (NetworkUtils.isAutoDownloadAllowed()) {
Log.d(TAG, "auto-dl network available, starting auto-download");
DBTasks.autodownloadUndownloadedItems(context);
} else { // if new network is Wi-Fi, finish ongoing downloads,
diff --git a/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java b/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java
index c75164a74..33f0d47b8 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/PlaybackSpeedSeekBar.java
@@ -9,11 +9,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Consumer;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.util.playback.PlaybackController;
public class PlaybackSpeedSeekBar extends FrameLayout {
private SeekBar seekBar;
- private PlaybackController controller;
private Consumer<Float> progressChangedListener;
public PlaybackSpeedSeekBar(@NonNull Context context) {
@@ -40,15 +38,9 @@ public class PlaybackSpeedSeekBar extends FrameLayout {
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (controller != null) {
- float playbackSpeed = (progress + 10) / 20.0f;
- controller.setPlaybackSpeed(playbackSpeed);
-
- if (progressChangedListener != null) {
- progressChangedListener.accept(playbackSpeed);
- }
- } else if (fromUser) {
- seekBar.post(() -> updateSpeed());
+ float playbackSpeed = (progress + 10) / 20.0f;
+ if (progressChangedListener != null) {
+ progressChangedListener.accept(playbackSpeed);
}
}
@@ -62,21 +54,23 @@ public class PlaybackSpeedSeekBar extends FrameLayout {
});
}
- public void updateSpeed() {
- if (controller != null) {
- seekBar.setProgress(Math.round((20 * controller.getCurrentPlaybackSpeedMultiplier()) - 10));
- }
- }
-
- public void setController(PlaybackController controller) {
- this.controller = controller;
- updateSpeed();
- if (progressChangedListener != null && controller != null) {
- progressChangedListener.accept(controller.getCurrentPlaybackSpeedMultiplier());
- }
+ public void updateSpeed(float speedMultiplier) {
+ seekBar.setProgress(Math.round((20 * speedMultiplier) - 10));
}
public void setProgressChangedListener(Consumer<Float> progressChangedListener) {
this.progressChangedListener = progressChangedListener;
}
+
+ public float getCurrentSpeed() {
+ return (seekBar.getProgress() + 10) / 20.0f;
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ seekBar.setEnabled(enabled);
+ findViewById(R.id.butDecSpeed).setEnabled(enabled);
+ findViewById(R.id.butIncSpeed).setEnabled(enabled);
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java
index cd3af5003..8d1810ecb 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java
@@ -21,7 +21,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.CoverLoader;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
-import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
import de.danoeh.antennapod.core.util.DateFormatter;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedMedia;
diff --git a/app/src/main/res/layout/alertdialog_sync_provider_chooser.xml b/app/src/main/res/layout/alertdialog_sync_provider_chooser.xml
new file mode 100644
index 000000000..9b4d62804
--- /dev/null
+++ b/app/src/main/res/layout/alertdialog_sync_provider_chooser.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="16dp">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginRight="16dip"
+ android:layout_marginEnd="16dip"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=""
+ android:layout_gravity="center" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/audioplayer_fragment.xml b/app/src/main/res/layout/audioplayer_fragment.xml
index f801930f5..7efbd23c8 100644
--- a/app/src/main/res/layout/audioplayer_fragment.xml
+++ b/app/src/main/res/layout/audioplayer_fragment.xml
@@ -12,6 +12,7 @@
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
android:layout_alignParentTop="true"
+ app:navigationContentDescription="@string/toolbar_back_button_content_description"
app:navigationIcon="?homeAsUpIndicator"
android:id="@+id/toolbar"/>
diff --git a/app/src/main/res/layout/edit_tags_dialog.xml b/app/src/main/res/layout/edit_tags_dialog.xml
index 57e3c412f..9ac0b60d3 100644
--- a/app/src/main/res/layout/edit_tags_dialog.xml
+++ b/app/src/main/res/layout/edit_tags_dialog.xml
@@ -7,6 +7,16 @@
android:orientation="vertical"
android:padding="16dp">
+ <com.joanzapata.iconify.widget.IconTextView
+ android:id="@+id/commonTagsInfo"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:visibility="gone"
+ android:textSize="@dimen/text_size_micro"
+ android:paddingBottom="16dp"
+ android:text="@string/multi_feed_common_tags_info" />
+
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/tagsRecycler"
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/episode_filter_dialog.xml b/app/src/main/res/layout/episode_filter_dialog.xml
index 9661a8e72..e8672c2f3 100644
--- a/app/src/main/res/layout/episode_filter_dialog.xml
+++ b/app/src/main/res/layout/episode_filter_dialog.xml
@@ -40,4 +40,21 @@
android:minLines="1"
android:scrollbars="vertical" />
+ <CheckBox
+ android:id="@+id/checkbox_filter_duration"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/episode_filters_duration" />
+
+ <EditText
+ android:id="@+id/etxtEpisodeFilterDurationText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:cursorVisible="true"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:inputType="numberSigned"
+ android:lines="1" />
+
</LinearLayout>
diff --git a/app/src/main/res/layout/feed_statistics.xml b/app/src/main/res/layout/feed_statistics.xml
new file mode 100644
index 000000000..f8f5ac555
--- /dev/null
+++ b/app/src/main/res/layout/feed_statistics.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<TableLayout
+ 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">
+
+ <TableRow
+ android:tag="detailed">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/statistics_episodes_started_total" />
+
+ <TextView
+ android:id="@+id/startedTotalLabel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
+ tools:text="0 / 0" />
+
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/statistics_time_played" />
+
+ <TextView
+ android:id="@+id/timePlayedLabel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
+ tools:text="0 min" />
+
+ </TableRow>
+
+ <TableRow
+ android:tag="detailed">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/statistics_duration_played_episodes" />
+
+ <TextView
+ android:id="@+id/durationPlayedLabel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
+ tools:text="0 min" />
+
+ </TableRow>
+
+ <TableRow
+ android:tag="detailed">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/statistics_total_duration" />
+
+ <TextView
+ android:id="@+id/totalDurationLabel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
+ tools:text="0 min" />
+
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/statistics_episodes_on_device" />
+
+ <TextView
+ android:id="@+id/onDeviceLabel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
+ tools:text="0" />
+
+ </TableRow>
+
+ <TableRow>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/statistics_space_used" />
+
+ <TextView
+ android:id="@+id/spaceUsedLabel"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
+ tools:text="0 MB" />
+
+ </TableRow>
+
+</TableLayout>
diff --git a/app/src/main/res/layout/feed_statistics_dialog.xml b/app/src/main/res/layout/feed_statistics_dialog.xml
new file mode 100644
index 000000000..fcd36fe7a
--- /dev/null
+++ b/app/src/main/res/layout/feed_statistics_dialog.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.fragment.app.FragmentContainerView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/statisticsContainer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp" />
diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml
index d753cbda1..b0a73cb97 100644
--- a/app/src/main/res/layout/feedinfo.xml
+++ b/app/src/main/res/layout/feedinfo.xml
@@ -1,210 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
- android:id="@+id/appBar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:id="@+id/appBar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout
- android:id="@+id/collapsing_toolbar"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="?android:attr/windowBackground"
- app:contentScrim="?android:attr/windowBackground"
- app:layout_scrollFlags="scroll|exitUntilCollapsed"
- app:scrimAnimationDuration="200">
+ android:id="@+id/collapsing_toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="?android:attr/windowBackground"
+ app:contentScrim="?android:attr/windowBackground"
+ app:layout_scrollFlags="scroll|exitUntilCollapsed"
+ app:scrimAnimationDuration="200">
<ImageView
- android:id="@+id/imgvBackground"
- style="@style/BigBlurryBackground"
- android:layout_width="match_parent"
- android:layout_height="232dp"
- android:background="@color/image_readability_tint"
- app:layout_collapseMode="parallax"
- app:layout_collapseParallaxMultiplier="0.6" />
+ android:id="@+id/imgvBackground"
+ android:layout_width="match_parent"
+ android:layout_height="232dp"
+ android:background="@color/image_readability_tint"
+ style="@style/BigBlurryBackground"
+ app:layout_collapseMode="parallax"
+ app:layout_collapseParallaxMultiplier="0.6" />
<include
- layout="@layout/feeditemlist_header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- app:layout_collapseMode="parallax"
- app:layout_collapseParallaxMultiplier="0.6" />
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ layout="@layout/feeditemlist_header"
+ app:layout_collapseMode="parallax"
+ app:layout_collapseParallaxMultiplier="0.6" />
<androidx.appcompat.widget.Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:minHeight="?attr/actionBarSize"
- android:theme="?attr/actionBarTheme"
- app:layout_collapseMode="pin"
- app:navigationIcon="?homeAsUpIndicator" />
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:minHeight="?attr/actionBarSize"
+ android:theme="?attr/actionBarTheme"
+ app:layout_collapseMode="pin"
+ app:navigationContentDescription="@string/toolbar_back_button_content_description"
+ app:navigationIcon="?homeAsUpIndicator" />
</com.google.android.material.appbar.CollapsingToolbarLayout>
+
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
- android:id="@+id/scrollView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipToPadding="false"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
- android:paddingBottom="8dp"
- android:scrollbarStyle="outsideOverlay"
- app:layout_behavior="@string/appbar_scrolling_view_behavior">
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingBottom="8dp"
+ android:scrollbarStyle="outsideOverlay"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
- android:id="@+id/infoContainer"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingHorizontal="@dimen/additional_horizontal_spacing">
+ android:id="@+id/infoContainer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingHorizontal="@dimen/additional_horizontal_spacing">
<TextView
- android:id="@+id/lblUrl"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="4dp"
- android:text="@string/url_label"
- android:textColor="?android:attr/textColorPrimary"
- tools:background="@android:color/holo_red_light" />
+ android:id="@+id/lblUrl"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="4dp"
+ android:text="@string/url_label"
+ android:textColor="?android:attr/textColorPrimary"
+ tools:background="@android:color/holo_red_light" />
<TextView
- android:id="@+id/txtvUrl"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?attr/selectableItemBackground"
- android:maxLines="4"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- tools:background="@android:color/holo_green_dark"
- tools:text="http://www.example.com/feed" />
+ android:id="@+id/txtvUrl"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground"
+ android:maxLines="4"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ tools:background="@android:color/holo_green_dark"
+ tools:text="http://www.example.com/feed" />
<TextView
- android:id="@+id/lblSupport"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="4dp"
- android:text="@string/support_funding_label"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="18sp"
- tools:background="@android:color/holo_red_light" />
+ android:id="@+id/lblSupport"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="4dp"
+ android:text="@string/support_funding_label"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="18sp"
+ tools:background="@android:color/holo_red_light" />
<TextView
- android:id="@+id/txtvFundingUrl"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:maxLines="8"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:linksClickable="true"
- android:autoLink="web"
- tools:background="@android:color/holo_green_dark" />
+ android:id="@+id/txtvFundingUrl"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="8"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:linksClickable="true"
+ android:autoLink="web"
+ tools:background="@android:color/holo_green_dark" />
<TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textSize="18sp"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="4dp"
- android:text="@string/description_label"
- android:textColor="?android:attr/textColorPrimary"
- tools:background="@android:color/holo_red_light" />
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="18sp"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="4dp"
+ android:text="@string/description_label"
+ android:textColor="?android:attr/textColorPrimary"
+ tools:background="@android:color/holo_red_light" />
<TextView
- android:id="@+id/txtvDescription"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/design_time_lorem_ipsum"
- android:textIsSelectable="true"
- tools:background="@android:color/holo_green_dark" />
+ android:id="@+id/txtvDescription"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/design_time_lorem_ipsum"
+ android:textIsSelectable="true"
+ tools:background="@android:color/holo_green_dark" />
<TextView
- android:id="@+id/lblStatistics"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:textSize="18sp"
- android:layout_marginBottom="8dp"
- android:text="@string/statistics_label"
- android:textColor="?android:attr/textColorPrimary"
- tools:background="@android:color/holo_red_light" />
-
- <TableLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/statistics_listened_for" />
-
- <TextView
- android:id="@+id/txtvPodcastTime"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="5dp" />
- </TableRow>
-
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/statistics_episodes_on_device" />
-
- <TextView
- android:id="@+id/txtvPodcastEpisodeCount"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="5dp" />
- </TableRow>
-
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/statistics_space_used" />
-
- <TextView
- android:id="@+id/txtvPodcastSpaceUsed"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="5dp" />
- </TableRow>
-
- </TableLayout>
+ android:id="@+id/lblStatistics"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:textSize="18sp"
+ android:layout_marginBottom="8dp"
+ android:text="@string/statistics_label"
+ android:textColor="?android:attr/textColorPrimary"
+ tools:background="@android:color/holo_red_light" />
+
+ <androidx.fragment.app.FragmentContainerView
+ android:id="@+id/statisticsFragmentContainer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
<Button
- android:id="@+id/btnvOpenStatistics"
- style="@style/Widget.MaterialComponents.Button.TextButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:minWidth="0dp"
- android:minHeight="0dp"
- android:text="@string/statistics_view_all" />
+ android:id="@+id/btnvOpenStatistics"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="0dp"
+ android:minHeight="0dp"
+ android:text="@string/statistics_view_all"
+ style="@style/Widget.MaterialComponents.Button.TextButton" />
</LinearLayout>
+
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/app/src/main/res/layout/feeditem_pager_fragment.xml b/app/src/main/res/layout/feeditem_pager_fragment.xml
index ac7316dd8..690ac3bc1 100644
--- a/app/src/main/res/layout/feeditem_pager_fragment.xml
+++ b/app/src/main/res/layout/feeditem_pager_fragment.xml
@@ -11,6 +11,7 @@
android:layout_height="wrap_content"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
+ app:navigationContentDescription="@string/toolbar_back_button_content_description"
app:navigationIcon="?homeAsUpIndicator"
android:id="@+id/toolbar"/>
diff --git a/app/src/main/res/layout/feedsettings.xml b/app/src/main/res/layout/feedsettings.xml
index acd1089bd..df6e666eb 100644
--- a/app/src/main/res/layout/feedsettings.xml
+++ b/app/src/main/res/layout/feedsettings.xml
@@ -11,6 +11,7 @@
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:title="@string/feed_settings_label"
+ app:navigationContentDescription="@string/toolbar_back_button_content_description"
app:navigationIcon="?homeAsUpIndicator"
android:elevation="4dp"
android:id="@+id/toolbar"/>
diff --git a/app/src/main/res/layout/fragment_subscriptions.xml b/app/src/main/res/layout/fragment_subscriptions.xml
index 6dd112eed..5672a310f 100644
--- a/app/src/main/res/layout/fragment_subscriptions.xml
+++ b/app/src/main/res/layout/fragment_subscriptions.xml
@@ -2,6 +2,7 @@
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
@@ -40,9 +41,11 @@
android:id="@+id/subscriptions_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:clipToPadding="false"
android:layout_gravity="center_horizontal"
android:paddingBottom="88dp"
- android:clipToPadding="false" />
+ tools:itemCount="2"
+ tools:listitem="@layout/subscription_item" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
diff --git a/app/src/main/res/layout/nextcloud_auth_dialog.xml b/app/src/main/res/layout/nextcloud_auth_dialog.xml
new file mode 100644
index 000000000..345eec88b
--- /dev/null
+++ b/app/src/main/res/layout/nextcloud_auth_dialog.xml
@@ -0,0 +1,63 @@
+<?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="wrap_content"
+ android:padding="16dp"
+ android:orientation="vertical"
+ android:clipToPadding="false">
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/serverUrlTextInput"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/serverUrlText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/gpodnetauth_host"
+ android:inputType="textNoSuggestions"
+ android:lines="1"
+ android:imeOptions="actionNext|flagNoFullscreen" />
+
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <LinearLayout
+ android:id="@+id/loginProgressContainer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:orientation="horizontal"
+ android:layout_gravity="center_vertical">
+
+ <ProgressBar
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="8dp"
+ android:layout_marginRight="8dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/synchronization_nextcloud_authenticate_browser" />
+
+ </LinearLayout>
+
+ <TextView
+ android:id="@+id/errorText"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ android:textColor="@color/download_failed_red"
+ android:layout_marginBottom="16dp" />
+
+ <Button
+ android:id="@+id/loginButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/gpodnetauth_login_butLabel" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/playback_speed_feed_setting_dialog.xml b/app/src/main/res/layout/playback_speed_feed_setting_dialog.xml
new file mode 100644
index 000000000..572096911
--- /dev/null
+++ b/app/src/main/res/layout/playback_speed_feed_setting_dialog.xml
@@ -0,0 +1,37 @@
+<?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="wrap_content"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <CheckBox
+ android:id="@+id/useGlobalCheckbox"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/feed_auto_download_global"
+ android:layout_marginBottom="8dp" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+
+ <de.danoeh.antennapod.view.PlaybackSpeedSeekBar
+ android:id="@+id/seekBar"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+
+ <TextView
+ android:id="@+id/currentSpeedLabel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="8dp"
+ android:layout_marginLeft="8dp" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/playback_speed_seek_bar.xml b/app/src/main/res/layout/playback_speed_seek_bar.xml
index 8c9b1725f..155a2261a 100644
--- a/app/src/main/res/layout/playback_speed_seek_bar.xml
+++ b/app/src/main/res/layout/playback_speed_seek_bar.xml
@@ -14,6 +14,7 @@
android:text="-"
android:clickable="true"
android:focusable="true"
+ android:scrollbars="none"
android:textStyle="bold"
android:textSize="24sp"
android:textColor="?attr/colorSecondary"
@@ -36,6 +37,7 @@
android:text="+"
android:clickable="true"
android:focusable="true"
+ android:scrollbars="none"
android:textStyle="bold"
android:textSize="24sp"
android:textColor="?attr/colorSecondary"
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 cb03b6677..c3a32f019 100644
--- a/app/src/main/res/layout/quick_feed_discovery_item.xml
+++ b/app/src/main/res/layout/quick_feed_discovery_item.xml
@@ -2,6 +2,7 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:squareImageView="http://schemas.android.com/apk/de.danoeh.antennapod"
+ xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="4dp"
@@ -14,7 +15,8 @@
android:elevation="4dp"
android:outlineProvider="bounds"
android:foreground="?android:attr/selectableItemBackground"
- squareImageView:direction="width" />
+ squareImageView:direction="width"
+ tools:src="@android:drawable/sym_def_app_icon"/>
</LinearLayout>
diff --git a/app/src/main/res/layout/subscription_selection_activity.xml b/app/src/main/res/layout/subscription_selection_activity.xml
new file mode 100644
index 000000000..b54e7e4a4
--- /dev/null
+++ b/app/src/main/res/layout/subscription_selection_activity.xml
@@ -0,0 +1,58 @@
+<?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:id="@+id/transparentBackground"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <androidx.cardview.widget.CardView
+ android:id="@+id/card"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="32dp"
+ android:elevation="16dp"
+ app:cardCornerRadius="4dp">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <androidx.appcompat.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="?attr/actionBarSize"
+ android:theme="?attr/actionBarTheme"
+ android:layout_alignParentTop="true" />
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_below="@id/toolbar"
+ android:background="?android:attr/listDivider" />
+
+ <ListView
+ android:id="@+id/list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false"
+ android:layout_below="@id/divider"
+ android:paddingBottom="88dp" />
+
+ <Button
+ android:id="@+id/shortcutBtn"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:layout_alignParentBottom="true"
+ android:text="@string/add_shortcut" />
+
+ </RelativeLayout>
+
+ </androidx.cardview.widget.CardView>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/time_dialog.xml b/app/src/main/res/layout/time_dialog.xml
index 6b6ab3195..138a60b33 100644
--- a/app/src/main/res/layout/time_dialog.xml
+++ b/app/src/main/res/layout/time_dialog.xml
@@ -1,136 +1,144 @@
<?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="wrap_content"
- xmlns:tools="http://schemas.android.com/tools"
- android:orientation="vertical"
- android:gravity="center"
- android:padding="16dp">
+<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="vertical"
+ android:gravity="center"
+ android:padding="16dp">
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/timeSetup"
- android:orientation="vertical">
+ android:id="@+id/timeSetup"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
<EditText
- android:id="@+id/etxtTime"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_margin="8dp"
- android:ems="2"
- android:inputType="number"
- android:maxLength="3"/>
+ android:id="@+id/etxtTime"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_margin="8dp"
+ android:ems="2"
+ android:inputType="number"
+ android:maxLength="3" />
<Spinner
- android:id="@+id/spTimeUnit"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:layout_marginTop="8dp"/>
+ android:id="@+id/spTimeUnit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp" />
+
</LinearLayout>
<Button
- android:text="@string/set_sleeptimer_label"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/setSleeptimerButton"/>
+ android:id="@+id/setSleeptimerButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/set_sleeptimer_label" />
+
</LinearLayout>
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/timeDisplay"
- android:orientation="vertical"
- android:visibility="gone">
+ android:id="@+id/timeDisplay"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:visibility="visible">
<TextView
- android:text="00:00:00"
- android:layout_gravity="center"
- android:gravity="center"
- android:textSize="32sp"
- android:textColor="?android:attr/textColorPrimary"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:id="@+id/time"/>
+ android:id="@+id/time"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="00:00:00"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:textSize="32sp"
+ android:textColor="?android:attr/textColorPrimary" />
<Button
- android:text="@string/disable_sleeptimer_label"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/disableSleeptimerButton"/>
+ android:id="@+id/disableSleeptimerButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/disable_sleeptimer_label" />
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
<Button
- android:id="@+id/extendSleepFiveMinutesButton"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginEnd="4dp"
- android:layout_marginRight="4dp"
- android:layout_weight="1"
- android:padding="5dp"
- tools:text="+5 min" />
+ android:id="@+id/extendSleepFiveMinutesButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="4dp"
+ android:layout_marginRight="4dp"
+ android:paddingHorizontal="2dp"
+ android:paddingVertical="4dp"
+ android:layout_weight="1"
+ style="?attr/materialButtonOutlinedStyle"
+ tools:text="+5 min" />
<Button
- android:id="@+id/extendSleepTenMinutesButton"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="4dp"
- android:layout_weight="1"
- tools:text="+10 min" />
+ android:id="@+id/extendSleepTenMinutesButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dp"
+ android:layout_marginEnd="4dp"
+ android:paddingHorizontal="2dp"
+ android:paddingVertical="4dp"
+ android:layout_weight="1"
+ style="?attr/materialButtonOutlinedStyle"
+ tools:text="+10 min" />
<Button
- android:id="@+id/extendSleepTwentyMinutesButton"
- style="?attr/materialButtonOutlinedStyle"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginStart="4dp"
- android:layout_marginRight="4dp"
- android:layout_marginLeft="4dp"
- android:layout_weight="1"
- tools:text="+20 min" />
+ android:id="@+id/extendSleepTwentyMinutesButton"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="4dp"
+ android:layout_marginRight="4dp"
+ android:layout_marginLeft="4dp"
+ android:paddingHorizontal="2dp"
+ android:paddingVertical="4dp"
+ android:layout_weight="1"
+ style="?attr/materialButtonOutlinedStyle"
+ tools:text="+20 min" />
</LinearLayout>
</LinearLayout>
-
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:layout_marginTop="8dp">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginTop="8dp">
<CheckBox
- android:id="@+id/cbShakeToReset"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/shake_to_reset_label"/>
+ android:id="@+id/cbShakeToReset"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/shake_to_reset_label" />
<CheckBox
- android:id="@+id/cbVibrate"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/timer_vibration_label"/>
+ android:id="@+id/cbVibrate"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/timer_vibration_label" />
<CheckBox
- android:id="@+id/chAutoEnable"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/auto_enable_label"/>
+ android:id="@+id/chAutoEnable"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/auto_enable_label" />
</LinearLayout>
diff --git a/app/src/main/res/menu/cast_enabled.xml b/app/src/main/res/menu/cast_enabled.xml
deleted file mode 100644
index d6e85c311..000000000
--- a/app/src/main/res/menu/cast_enabled.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:custom="http://schemas.android.com/apk/res-auto">
-
- <item
- android:id="@+id/media_route_menu_item"
- android:title="@string/cast_media_route_menu_title"
- custom:actionProviderClass="de.danoeh.antennapod.core.cast.SwitchableMediaRouteActionProvider"
- custom:showAsAction="ifRoom"/>
-</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/nav_feed_action_speeddial.xml b/app/src/main/res/menu/nav_feed_action_speeddial.xml
index 2dfa002bb..43bd25842 100644
--- a/app/src/main/res/menu/nav_feed_action_speeddial.xml
+++ b/app/src/main/res/menu/nav_feed_action_speeddial.xml
@@ -25,4 +25,9 @@
android:menuCategory="container"
android:title="@string/playback_speed"
android:icon="@drawable/ic_playback_speed"/>
+ <item
+ android:id="@+id/edit_tags"
+ android:menuCategory="container"
+ android:title="@string/add_to_folder"
+ android:icon="@drawable/ic_tag"/>
</menu>
diff --git a/app/src/main/res/menu/nav_feed_context.xml b/app/src/main/res/menu/nav_feed_context.xml
index 17c15cbb0..e45fe24e0 100644
--- a/app/src/main/res/menu/nav_feed_context.xml
+++ b/app/src/main/res/menu/nav_feed_context.xml
@@ -7,7 +7,7 @@
android:title="@string/remove_all_new_flags_label" />
<item
- android:id="@+id/add_to_folder"
+ android:id="@+id/edit_tags"
android:menuCategory="container"
android:title="@string/add_to_folder" />
diff --git a/app/src/main/res/xml/feed_settings.xml b/app/src/main/res/xml/feed_settings.xml
index 457ff6e5b..007f084c9 100644
--- a/app/src/main/res/xml/feed_settings.xml
+++ b/app/src/main/res/xml/feed_settings.xml
@@ -1,72 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android"
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:key="feedSettingsScreen">
<SwitchPreferenceCompat
- android:key="keepUpdated"
- android:icon="@drawable/ic_refresh"
- android:title="@string/keep_updated"
- android:summary="@string/keep_updated_summary"/>
+ android:icon="@drawable/ic_refresh"
+ android:key="keepUpdated"
+ android:summary="@string/keep_updated_summary"
+ android:title="@string/keep_updated" />
<SwitchPreferenceCompat
- android:key="episodeNotification"
- android:defaultValue="false"
- android:dependency="keepUpdated"
- android:icon="@drawable/ic_notifications"
- android:title="@string/episode_notification"
- android:summary="@string/episode_notification_summary"/>
+ android:defaultValue="false"
+ android:dependency="keepUpdated"
+ android:icon="@drawable/ic_notifications"
+ android:key="episodeNotification"
+ android:summary="@string/episode_notification_summary"
+ android:title="@string/episode_notification" />
<Preference
- android:key="authentication"
- android:icon="@drawable/ic_key"
- android:title="@string/authentication_label"
- android:summary="@string/authentication_descr"/>
+ android:icon="@drawable/ic_key"
+ android:key="authentication"
+ android:summary="@string/authentication_descr"
+ android:title="@string/authentication_label" />
<Preference
- android:key="tags"
- android:icon="@drawable/ic_folder"
- android:title="@string/feed_folders_label"
- android:summary="@string/feed_folders_summary"/>
+ android:icon="@drawable/ic_tag"
+ android:key="tags"
+ android:summary="@string/feed_tags_summary"
+ android:title="@string/feed_tags_label" />
- <ListPreference
- android:key="feedPlaybackSpeed"
- android:icon="@drawable/ic_playback_speed"
- android:title="@string/playback_speed"
- android:summary="@string/pref_feed_playback_speed_sum"/>
+ <Preference
+ android:icon="@drawable/ic_playback_speed"
+ android:key="feedPlaybackSpeed"
+ android:summary="@string/pref_feed_playback_speed_sum"
+ android:title="@string/playback_speed" />
<Preference
- android:key="feedAutoSkip"
- android:icon="@drawable/ic_skip_24dp"
- android:summary="@string/pref_feed_skip_sum"
- android:title="@string/pref_feed_skip" />
+ android:icon="@drawable/ic_skip_24dp"
+ android:key="feedAutoSkip"
+ android:summary="@string/pref_feed_skip_sum"
+ android:title="@string/pref_feed_skip" />
<ListPreference
- android:entries="@array/spnAutoDeleteItems"
- android:entryValues="@array/spnAutoDeleteValues"
- android:icon="@drawable/ic_delete"
- android:title="@string/auto_delete_label"
- android:summary="@string/feed_auto_download_global"
- android:key="autoDelete"/>
+ android:entries="@array/spnAutoDeleteItems"
+ android:entryValues="@array/spnAutoDeleteValues"
+ android:icon="@drawable/ic_delete"
+ android:key="autoDelete"
+ android:summary="@string/feed_auto_download_global"
+ android:title="@string/auto_delete_label" />
<ListPreference
- android:entries="@array/spnVolumeReductionItems"
- android:entryValues="@array/spnVolumeReductionValues"
- android:icon="@drawable/ic_volume_adaption"
- android:summary="@string/feed_volume_reduction_summary"
- android:title="@string/feed_volume_reduction"
- android:defaultValue="off"
- android:key="volumeReduction"/>
+ android:defaultValue="off"
+ android:entries="@array/spnVolumeReductionItems"
+ android:entryValues="@array/spnVolumeReductionValues"
+ android:icon="@drawable/ic_volume_adaption"
+ android:key="volumeReduction"
+ android:summary="@string/feed_volume_reduction_summary"
+ android:title="@string/feed_volume_reduction" />
<PreferenceCategory
- android:title="@string/auto_download_settings_label"
- android:key="autoDownloadCategory">
+ android:key="autoDownloadCategory"
+ android:title="@string/auto_download_settings_label">
<SwitchPreferenceCompat
- android:key="autoDownload"
- android:title="@string/auto_download_label"/>
+ android:key="autoDownload"
+ android:title="@string/auto_download_label" />
<Preference
- android:key="episodeFilter"
- android:title="@string/episode_filters_label"
- android:summary="@string/episode_filters_description"/>
+ android:key="episodeFilter"
+ android:summary="@string/episode_filters_description"
+ android:title="@string/episode_filters_label" />
</PreferenceCategory>
</PreferenceScreen>
diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml
new file mode 100644
index 000000000..d4c3fc996
--- /dev/null
+++ b/app/src/main/res/xml/network_security_config.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config xmlns:tools="http://schemas.android.com/tools">
+ <base-config cleartextTrafficPermitted="true" tools:ignore="InsecureBaseConfiguration">
+ <trust-anchors>
+ <certificates src="user" tools:ignore="AcceptsUserCertificates"/>
+ <certificates src="system" />
+ </trust-anchors>
+ </base-config>
+</network-security-config>
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index d528945c7..7c5012899 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -28,7 +28,7 @@
android:icon="@drawable/ic_network" />
<Preference
- android:key="prefScreenGpodder"
+ android:key="prefScreenSynchronization"
android:title="@string/synchronization_pref"
android:summary="@string/synchronization_sum"
android:icon="@drawable/ic_cloud" />
diff --git a/app/src/main/res/xml/preferences_gpodder.xml b/app/src/main/res/xml/preferences_gpodder.xml
deleted file mode 100644
index a210b8e11..000000000
--- a/app/src/main/res/xml/preferences_gpodder.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android">
- <Preference
- android:key="pref_gpodnet_description"
- android:icon="@drawable/gpodder_icon"
- android:summary="@string/gpodnet_description"/>
- <Preference
- android:key="pref_gpodnet_authenticate"
- android:title="@string/pref_gpodnet_authenticate_title"
- android:summary="@string/pref_gpodnet_authenticate_sum"/>
- <Preference
- android:key="pref_gpodnet_setlogin_information"
- android:title="@string/pref_gpodnet_setlogin_information_title"
- android:summary="@string/pref_gpodnet_setlogin_information_sum"/>
- <Preference
- android:key="pref_gpodnet_sync"
- android:title="@string/pref_gpodnet_sync_changes_title"
- android:summary="@string/pref_gpodnet_sync_changes_sum"/>
- <Preference
- android:key="pref_gpodnet_force_full_sync"
- android:title="@string/pref_gpodnet_full_sync_title"
- android:summary="@string/pref_gpodnet_full_sync_sum"/>
- <Preference
- android:key="pref_gpodnet_logout"
- android:title="@string/pref_gpodnet_logout_title"/>
-
-</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_playback.xml b/app/src/main/res/xml/preferences_playback.xml
index 2be8492eb..add9e8d4c 100644
--- a/app/src/main/res/xml/preferences_playback.xml
+++ b/app/src/main/res/xml/preferences_playback.xml
@@ -23,7 +23,7 @@
android:summary="@string/pref_unpauseOnBluetoothReconnect_sum"
android:title="@string/pref_unpauseOnBluetoothReconnect_title"/>
<SwitchPreferenceCompat
- android:defaultValue="false"
+ android:defaultValue="true"
android:enabled="true"
android:key="prefPauseForFocusLoss"
android:summary="@string/pref_pausePlaybackForFocusLoss_sum"
@@ -127,11 +127,5 @@
android:title="@string/media_player"
android:summary="@string/pref_media_player_message"
android:entryValues="@array/media_player_values"/>
- <SwitchPreferenceCompat
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefCast"
- android:summary="@string/pref_cast_message"
- android:title="@string/pref_cast_title"/>
</PreferenceCategory>
</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_synchronization.xml b/app/src/main/res/xml/preferences_synchronization.xml
new file mode 100644
index 000000000..fbd4ccc79
--- /dev/null
+++ b/app/src/main/res/xml/preferences_synchronization.xml
@@ -0,0 +1,31 @@
+<?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">
+
+ <Preference
+ android:key="preference_synchronization_description"
+ android:icon="@drawable/ic_notification_sync"
+ android:summary="@string/synchronization_summary_unchoosen"/>
+
+ <Preference
+ android:key="pref_gpodnet_setlogin_information"
+ android:title="@string/pref_gpodnet_setlogin_information_title"
+ android:summary="@string/pref_gpodnet_setlogin_information_sum"
+ app:isPreferenceVisible="false"/>
+
+ <Preference
+ android:key="pref_synchronization_sync"
+ android:title="@string/synchronization_sync_changes_title"
+ android:summary="@string/synchronization_sync_summary"/>
+
+ <Preference
+ android:key="pref_synchronization_force_full_sync"
+ android:title="@string/synchronization_full_sync_title"
+ android:summary="@string/synchronization_force_sync_summary"/>
+
+ <Preference
+ android:key="pref_synchronization_logout"
+ android:title="@string/synchronization_logout"/>
+
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml
index a16b679e3..045996714 100644
--- a/app/src/main/res/xml/provider_paths.xml
+++ b/app/src/main/res/xml/provider_paths.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="external_storage" path="."/>
- <root-path name="external_files" path="/storage/" />
+ <files-path name="name" path="." />
</paths>
diff --git a/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java b/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
deleted file mode 100644
index 753feb3e7..000000000
--- a/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
+++ /dev/null
@@ -1,157 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.SharedPreferences;
-import android.media.AudioManager;
-import android.os.Bundle;
-import androidx.preference.PreferenceManager;
-import androidx.appcompat.app.AppCompatActivity;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-
-import com.google.android.gms.cast.ApplicationMetadata;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.cast.CastButtonVisibilityManager;
-import de.danoeh.antennapod.core.cast.CastConsumer;
-import de.danoeh.antennapod.core.cast.CastManager;
-import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
-import de.danoeh.antennapod.core.cast.SwitchableMediaRouteActionProvider;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Activity that allows for showing the MediaRouter button whenever there's a cast device in the
- * network.
- */
-public abstract class CastEnabledActivity extends AppCompatActivity
- implements SharedPreferences.OnSharedPreferenceChangeListener {
- public static final String TAG = "CastEnabledActivity";
-
- private CastConsumer castConsumer;
- private CastManager castManager;
- private final List<CastButtonVisibilityManager> castButtons = new ArrayList<>();
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- if (!CastManager.isInitialized()) {
- return;
- }
-
- PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
- .registerOnSharedPreferenceChangeListener(this);
-
- castConsumer = new DefaultCastConsumer() {
- @Override
- public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
- onCastConnectionChanged(true);
- }
-
- @Override
- public void onDisconnected() {
- onCastConnectionChanged(false);
- }
- };
- castManager = CastManager.getInstance();
- castManager.addCastConsumer(castConsumer);
- CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager(castManager);
- castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
- onCastConnectionChanged(castManager.isConnected());
- castButtons.add(castButtonVisibilityManager);
- }
-
- @Override
- protected void onDestroy() {
- if (!CastManager.isInitialized()) {
- super.onDestroy();
- return;
- }
- PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
- .unregisterOnSharedPreferenceChangeListener(this);
- castManager.removeCastConsumer(castConsumer);
- super.onDestroy();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (!CastManager.isInitialized()) {
- return;
- }
- for (CastButtonVisibilityManager castButton : castButtons) {
- castButton.setResumed(true);
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- if (!CastManager.isInitialized()) {
- return;
- }
- for (CastButtonVisibilityManager castButton : castButtons) {
- castButton.setResumed(false);
- }
- }
-
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
- boolean newValue = UserPreferences.isCastEnabled();
- Log.d(TAG, "onSharedPreferenceChanged(), isCastEnabled set to " + newValue);
- for (CastButtonVisibilityManager castButton : castButtons) {
- castButton.setPrefEnabled(newValue);
- }
- // PlaybackService has its own listener, so if it's active we don't have to take action here.
- if (!newValue && !PlaybackService.isRunning) {
- CastManager.getInstance().disconnect();
- }
- }
- }
-
- private void onCastConnectionChanged(boolean connected) {
- if (connected) {
- for (CastButtonVisibilityManager castButton : castButtons) {
- castButton.onConnected();
- }
- setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
- } else {
- for (CastButtonVisibilityManager castButton : castButtons) {
- castButton.onDisconnected();
- }
- setVolumeControlStream(AudioManager.STREAM_MUSIC);
- }
- }
-
- /**
- * Should be called by any activity or fragment for which the cast button should be shown.
- */
- public final void requestCastButton(Menu menu) {
- if (!CastManager.isInitialized()) {
- return;
- }
-
- MenuItem mediaRouteButton = menu.findItem(R.id.media_route_menu_item);
- if (mediaRouteButton == null) {
- getMenuInflater().inflate(R.menu.cast_enabled, menu);
- mediaRouteButton = menu.findItem(R.id.media_route_menu_item);
- }
-
- SwitchableMediaRouteActionProvider mediaRouteActionProvider =
- CastManager.getInstance().addMediaRouterButton(mediaRouteButton);
- CastButtonVisibilityManager castButtonVisibilityManager =
- new CastButtonVisibilityManager(CastManager.getInstance());
- castButtonVisibilityManager.setMenu(menu);
- castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
- castButtonVisibilityManager.mediaRouteActionProvider = mediaRouteActionProvider;
- castButtonVisibilityManager.setResumed(true);
- castButtonVisibilityManager.requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
- mediaRouteActionProvider.setEnabled(castButtonVisibilityManager.shouldEnable());
- }
-}
diff --git a/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java b/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java
deleted file mode 100644
index 2a879c62d..000000000
--- a/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package de.danoeh.antennapod.config;
-
-import androidx.annotation.NonNull;
-import androidx.mediarouter.app.MediaRouteControllerDialogFragment;
-import androidx.mediarouter.app.MediaRouteDialogFactory;
-
-import de.danoeh.antennapod.core.CastCallbacks;
-import de.danoeh.antennapod.fragment.CustomMRControllerDialogFragment;
-
-public class CastCallbackImpl implements CastCallbacks {
- @Override
- public MediaRouteDialogFactory getMediaRouterDialogFactory() {
- return new MediaRouteDialogFactory() {
- @NonNull
- @Override
- public MediaRouteControllerDialogFragment onCreateControllerDialogFragment() {
- return new CustomMRControllerDialogFragment();
- }
- };
- }
-}
diff --git a/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java b/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java
deleted file mode 100644
index 6d8450a18..000000000
--- a/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java
+++ /dev/null
@@ -1,480 +0,0 @@
-package de.danoeh.antennapod.dialog;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.RemoteException;
-import androidx.annotation.NonNull;
-import android.support.v4.media.MediaDescriptionCompat;
-import android.support.v4.media.MediaMetadataCompat;
-import android.support.v4.media.session.MediaControllerCompat;
-import android.support.v4.media.session.MediaSessionCompat;
-import android.support.v4.media.session.PlaybackStateCompat;
-import androidx.core.util.Pair;
-import androidx.core.view.MarginLayoutParamsCompat;
-import androidx.core.view.accessibility.AccessibilityEventCompat;
-import androidx.mediarouter.app.MediaRouteControllerDialog;
-import androidx.palette.graphics.Palette;
-import androidx.mediarouter.media.MediaRouter;
-import androidx.appcompat.widget.AppCompatImageView;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.FrameLayout;
-import android.widget.ImageButton;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.request.RequestOptions;
-import com.bumptech.glide.request.target.Target;
-
-import java.util.concurrent.ExecutionException;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import io.reactivex.Observable;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.Disposable;
-import io.reactivex.schedulers.Schedulers;
-
-public class CustomMRControllerDialog extends MediaRouteControllerDialog {
- public static final String TAG = "CustomMRContrDialog";
-
- private MediaRouter mediaRouter;
- private MediaSessionCompat.Token token;
-
- private ImageView artView;
- private TextView titleView;
- private TextView subtitleView;
- private ImageButton playPauseButton;
- private LinearLayout rootView;
-
- private boolean viewsCreated = false;
-
- private Disposable fetchArtSubscription;
-
- private MediaControllerCompat mediaController;
- private MediaControllerCompat.Callback mediaControllerCallback;
-
- public CustomMRControllerDialog(Context context) {
- this(context, 0);
- }
-
- private CustomMRControllerDialog(Context context, int theme) {
- super(context, theme);
- mediaRouter = MediaRouter.getInstance(getContext());
- token = mediaRouter.getMediaSessionToken();
- try {
- if (token != null) {
- mediaController = new MediaControllerCompat(getContext(), token);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error creating media controller", e);
- }
-
- if (mediaController != null) {
- mediaControllerCallback = new MediaControllerCompat.Callback() {
- @Override
- public void onSessionDestroyed() {
- if (mediaController != null) {
- mediaController.unregisterCallback(mediaControllerCallback);
- mediaController = null;
- }
- }
-
- @Override
- public void onMetadataChanged(MediaMetadataCompat metadata) {
- updateViews();
- }
-
- @Override
- public void onPlaybackStateChanged(PlaybackStateCompat state) {
- updateState();
- }
- };
- mediaController.registerCallback(mediaControllerCallback);
- }
- }
-
- @Override
- public View onCreateMediaControlView(Bundle savedInstanceState) {
- boolean landscape = getContext().getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
- if (landscape) {
- /*
- * When a horizontal LinearLayout measures itself, it first measures its children and
- * settles their widths on the first pass, and only then figures out its height, never
- * revisiting the widths measurements.
- * When one has a child view that imposes a certain aspect ratio (such as an ImageView),
- * then its width and height are related to each other, and so if one allows for a large
- * height, then it will request for itself a large width as well. However, on the first
- * child measurement, the LinearLayout imposes a very relaxed height bound, that the
- * child uses to tell the width it wants, a value which the LinearLayout will interpret
- * as final, even though the child will want to change it once a more restrictive height
- * bound is imposed later.
- *
- * Our solution is, given that the heights of the children do not depend on their widths
- * in this case, we first figure out the layout's height and only then perform the
- * usual sequence of measurements.
- *
- * Note: this solution does not take into account any vertical paddings nor children's
- * vertical margins in determining the height, as this View as well as its children are
- * defined in code and no paddings/margins that would influence these computations are
- * introduced.
- *
- * There were no resources online for this type of issue as far as I could gather.
- */
- rootView = new LinearLayout(getContext()) {
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // We'd like to find the overall height before adjusting the widths within the LinearLayout
- int maxHeight = Integer.MIN_VALUE;
- if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) {
- for (int i = 0; i < getChildCount(); i++) {
- int height = Integer.MIN_VALUE;
- View child = getChildAt(i);
- ViewGroup.LayoutParams lp = child.getLayoutParams();
- // we only measure children whose layout_height is not MATCH_PARENT
- if (lp.height >= 0) {
- height = lp.height;
- } else if (lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
- child.measure(widthMeasureSpec, heightMeasureSpec);
- height = child.getMeasuredHeight();
- }
- maxHeight = Math.max(maxHeight, height);
- }
- }
- if (maxHeight > 0) {
- super.onMeasure(widthMeasureSpec,
- MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.EXACTLY));
- } else {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
- };
- rootView.setOrientation(LinearLayout.HORIZONTAL);
- } else {
- rootView = new LinearLayout(getContext());
- rootView.setOrientation(LinearLayout.VERTICAL);
- }
- FrameLayout.LayoutParams rootParams = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- rootParams.setMargins(0, 0, 0,
- getContext().getResources().getDimensionPixelSize(R.dimen.media_router_controller_bottom_margin));
- rootView.setLayoutParams(rootParams);
-
- // Start the session activity when a content item (album art, title or subtitle) is clicked.
- View.OnClickListener onClickListener = v -> {
- if (mediaController != null) {
- PendingIntent pi = mediaController.getSessionActivity();
- if (pi != null) {
- try {
- pi.send();
- dismiss();
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, pi + " was not sent, it had been canceled.");
- }
- }
- }
- };
-
- LinearLayout.LayoutParams artParams;
- /*
- * On portrait orientation, we want to limit the artView's height to 9/16 of the available
- * width. Reason is that we need to choose the height wisely otherwise we risk the dialog
- * being much larger than the screen, and there doesn't seem to be a good way to know the
- * available height beforehand.
- *
- * On landscape orientation, we want to limit the artView's width to its available height.
- * Otherwise, horizontal images would take too much space and severely restrict the space
- * for episode title and play/pause button.
- *
- * Internal implementation of ImageView only uses the source image's aspect ratio, but we
- * want to impose our own and fallback to the source image's when it is more favorable.
- * Solutions were inspired, among other similar sources, on
- * http://stackoverflow.com/questions/18077325/scale-image-to-fill-imageview-width-and-keep-aspect-ratio
- */
- if (landscape) {
- artView = new AppCompatImageView(getContext()) {
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int desiredWidth = widthMeasureSpec;
- int desiredMeasureMode = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY ?
- MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
- if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) {
- Drawable drawable = getDrawable();
- if (drawable != null) {
- int intrHeight = drawable.getIntrinsicHeight();
- int intrWidth = drawable.getIntrinsicWidth();
- int originalHeight = MeasureSpec.getSize(heightMeasureSpec);
- if (intrHeight < intrWidth) {
- desiredWidth = MeasureSpec.makeMeasureSpec(
- originalHeight, desiredMeasureMode);
- } else {
- desiredWidth = MeasureSpec.makeMeasureSpec(
- Math.round((float) originalHeight * intrWidth / intrHeight),
- desiredMeasureMode);
- }
- }
- }
- super.onMeasure(desiredWidth, heightMeasureSpec);
- }
- };
- artParams = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.MATCH_PARENT);
- MarginLayoutParamsCompat.setMarginStart(artParams,
- getContext().getResources().getDimensionPixelSize(R.dimen.media_router_controller_playback_control_horizontal_spacing));
- } else {
- artView = new AppCompatImageView(getContext()) {
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int desiredHeight = heightMeasureSpec;
- if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) {
- Drawable drawable = getDrawable();
- if (drawable != null) {
- int originalWidth = MeasureSpec.getSize(widthMeasureSpec);
- int intrHeight = drawable.getIntrinsicHeight();
- int intrWidth = drawable.getIntrinsicWidth();
- float scale;
- if (intrHeight*16 > intrWidth*9) {
- // image is taller than 16:9
- scale = (float) originalWidth * 9 / 16 / intrHeight;
- } else {
- // image is more horizontal than 16:9
- scale = (float) originalWidth / intrWidth;
- }
- desiredHeight = MeasureSpec.makeMeasureSpec(
- Math.round(intrHeight * scale),
- MeasureSpec.EXACTLY);
- }
- }
- super.onMeasure(widthMeasureSpec, desiredHeight);
- }
- };
- artParams = new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
- }
- // When we fetch the bitmap, we want to know if we should set a background color or not.
- artView.setTag(landscape);
-
- artView.setScaleType(ImageView.ScaleType.FIT_CENTER);
- artView.setOnClickListener(onClickListener);
-
- artView.setLayoutParams(artParams);
- rootView.addView(artView);
-
- ViewGroup wrapper = rootView;
-
- if (landscape) {
- // Here we wrap with a frame layout because we want to set different layout parameters
- // for landscape orientation.
- wrapper = new FrameLayout(getContext());
- wrapper.setLayoutParams(new LinearLayout.LayoutParams(
- 0,
- ViewGroup.LayoutParams.WRAP_CONTENT, 1f));
- rootView.addView(wrapper);
- rootView.setWeightSum(1f);
- }
-
- View playbackControlLayout = View.inflate(getContext(), R.layout.media_router_controller, wrapper);
-
- titleView = playbackControlLayout.findViewById(R.id.mrc_control_title);
- subtitleView = playbackControlLayout.findViewById(R.id.mrc_control_subtitle);
- playbackControlLayout.findViewById(R.id.mrc_control_title_container).setOnClickListener(onClickListener);
- playPauseButton = playbackControlLayout.findViewById(R.id.mrc_control_play_pause);
- playPauseButton.setOnClickListener(v -> {
- PlaybackStateCompat state;
- if (mediaController != null && (state = mediaController.getPlaybackState()) != null) {
- boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_PLAYING;
- if (isPlaying) {
- mediaController.getTransportControls().pause();
- } else {
- mediaController.getTransportControls().play();
- }
- // Announce the action for accessibility.
- AccessibilityManager accessibilityManager = (AccessibilityManager)
- getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager != null && accessibilityManager.isEnabled()) {
- AccessibilityEvent event = AccessibilityEvent.obtain(
- AccessibilityEventCompat.TYPE_ANNOUNCEMENT);
- event.setPackageName(getContext().getPackageName());
- event.setClassName(getClass().getName());
- int resId = isPlaying ? R.string.mr_controller_pause : R.string.mr_controller_play;
- event.getText().add(getContext().getString(resId));
- accessibilityManager.sendAccessibilityEvent(event);
- }
- }
- });
-
- viewsCreated = true;
- updateViews();
- return rootView;
- }
-
- @Override
- public void onDetachedFromWindow() {
- if (fetchArtSubscription != null) {
- fetchArtSubscription.dispose();
- fetchArtSubscription = null;
- }
- super.onDetachedFromWindow();
- }
-
- private void updateViews() {
- if (!viewsCreated || token == null || mediaController == null) {
- rootView.setVisibility(View.GONE);
- return;
- }
- MediaMetadataCompat metadata = mediaController.getMetadata();
- MediaDescriptionCompat description = metadata == null ? null : metadata.getDescription();
- if (description == null) {
- rootView.setVisibility(View.GONE);
- return;
- }
-
- PlaybackStateCompat state = mediaController.getPlaybackState();
- MediaRouter.RouteInfo route = MediaRouter.getInstance(getContext()).getSelectedRoute();
-
- CharSequence title = description.getTitle();
- boolean hasTitle = !TextUtils.isEmpty(title);
- CharSequence subtitle = description.getSubtitle();
- boolean hasSubtitle = !TextUtils.isEmpty(subtitle);
-
- boolean showTitle = false;
- boolean showSubtitle = false;
- if (route.getPresentationDisplay() != null &&
- route.getPresentationDisplay().getDisplayId() != MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE) {
- // The user is currently casting screen.
- titleView.setText(R.string.mr_controller_casting_screen);
- showTitle = true;
- } else if (state == null || state.getState() == PlaybackStateCompat.STATE_NONE) {
- // Show "No media selected" as we don't yet know the playback state.
- // (Only exception is bluetooth where we don't show anything.)
- if (!route.isBluetooth()) {
- titleView.setText(R.string.mr_controller_no_media_selected);
- showTitle = true;
- }
- } else if (!hasTitle && !hasSubtitle) {
- titleView.setText(R.string.mr_controller_no_info_available);
- showTitle = true;
- } else {
- if (hasTitle) {
- titleView.setText(title);
- showTitle = true;
- }
- if (hasSubtitle) {
- subtitleView.setText(subtitle);
- showSubtitle = true;
- }
- }
- if (showSubtitle) {
- titleView.setSingleLine();
- } else {
- titleView.setMaxLines(2);
- }
- titleView.setVisibility(showTitle ? View.VISIBLE : View.GONE);
- subtitleView.setVisibility(showSubtitle ? View.VISIBLE : View.GONE);
-
- updateState();
-
- if(rootView.getVisibility() != View.VISIBLE) {
- artView.setVisibility(View.GONE);
- rootView.setVisibility(View.VISIBLE);
- }
-
- if (fetchArtSubscription != null) {
- fetchArtSubscription.dispose();
- }
-
- fetchArtSubscription = Observable.fromCallable(() -> fetchArt(description))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> {
- fetchArtSubscription = null;
- if (artView == null) {
- return;
- }
- if (result.first != null) {
- if (!((Boolean) artView.getTag())) {
- artView.setBackgroundColor(result.second);
- }
- artView.setImageBitmap(result.first);
- artView.setVisibility(View.VISIBLE);
- } else {
- artView.setVisibility(View.GONE);
- }
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
-
- }
-
- private void updateState() {
- PlaybackStateCompat state;
- if (!viewsCreated || mediaController == null ||
- (state = mediaController.getPlaybackState()) == null) {
- return;
- }
- boolean isPlaying = state.getState() == PlaybackStateCompat.STATE_BUFFERING
- || state.getState() == PlaybackStateCompat.STATE_PLAYING;
- boolean supportsPlay = (state.getActions() & (PlaybackStateCompat.ACTION_PLAY
- | PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0;
- boolean supportsPause = (state.getActions() & (PlaybackStateCompat.ACTION_PAUSE
- | PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0;
- if (isPlaying && supportsPause) {
- playPauseButton.setVisibility(View.VISIBLE);
- playPauseButton.setImageResource(getThemeResource(getContext(), R.attr.mediaRoutePauseDrawable));
- playPauseButton.setContentDescription(getContext().getResources().getText(R.string.mr_controller_pause));
- } else if (!isPlaying && supportsPlay) {
- playPauseButton.setVisibility(View.VISIBLE);
- playPauseButton.setImageResource(getThemeResource(getContext(), R.attr.mediaRoutePlayDrawable));
- playPauseButton.setContentDescription(getContext().getResources().getText(R.string.mr_controller_play));
- } else {
- playPauseButton.setVisibility(View.GONE);
- }
- }
-
- private static int getThemeResource(Context context, int attr) {
- TypedValue value = new TypedValue();
- return context.getTheme().resolveAttribute(attr, value, true) ? value.resourceId : 0;
- }
-
- @NonNull
- private Pair<Bitmap, Integer> fetchArt(@NonNull MediaDescriptionCompat description) {
- Bitmap iconBitmap = description.getIconBitmap();
- Uri iconUri = description.getIconUri();
- Bitmap art = null;
- if (iconBitmap != null) {
- art = iconBitmap;
- } else if (iconUri != null) {
- try {
- art = Glide.with(getContext().getApplicationContext())
- .asBitmap()
- .load(iconUri.toString())
- .apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY))
- .submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
- .get();
- } catch (InterruptedException | ExecutionException e) {
- Log.e(TAG, "Image art load failed", e);
- }
- }
- int backgroundColor = 0;
- if (art != null && art.getWidth()*9 < art.getHeight()*16) {
- // Portrait art requires dominant color as background color.
- Palette palette = new Palette.Builder(art).maximumColorCount(1).generate();
- backgroundColor = palette.getSwatches().isEmpty()
- ? 0 : palette.getSwatches().get(0).getRgb();
- }
- return new Pair<>(art, backgroundColor);
- }
-}
diff --git a/app/src/play/java/de/danoeh/antennapod/fragment/CustomMRControllerDialogFragment.java b/app/src/play/java/de/danoeh/antennapod/fragment/CustomMRControllerDialogFragment.java
deleted file mode 100644
index dad7b0bfd..000000000
--- a/app/src/play/java/de/danoeh/antennapod/fragment/CustomMRControllerDialogFragment.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package de.danoeh.antennapod.fragment;
-
-import android.content.Context;
-import android.os.Bundle;
-import androidx.mediarouter.app.MediaRouteControllerDialog;
-import androidx.mediarouter.app.MediaRouteControllerDialogFragment;
-
-import de.danoeh.antennapod.dialog.CustomMRControllerDialog;
-
-public class CustomMRControllerDialogFragment extends MediaRouteControllerDialogFragment {
- @Override
- public MediaRouteControllerDialog onCreateControllerDialog(Context context, Bundle savedInstanceState) {
- return new CustomMRControllerDialog(context);
- }
-}
diff --git a/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java b/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
deleted file mode 100644
index b51fb40b0..000000000
--- a/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package de.danoeh.antennapod.preferences;
-
-import android.content.Context;
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.GoogleApiAvailability;
-
-import de.danoeh.antennapod.PodcastApp;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.fragment.preferences.PlaybackPreferencesFragment;
-
-/**
- * Implements functions from PreferenceController that are flavor dependent.
- */
-public class PreferenceControllerFlavorHelper {
-
- public static void setupFlavoredUI(PlaybackPreferencesFragment ui) {
- //checks whether Google Play Services is installed on the device (condition necessary for Cast support)
- ui.findPreference(UserPreferences.PREF_CAST_ENABLED).setOnPreferenceChangeListener((preference, o) -> {
- if (o instanceof Boolean && ((Boolean) o)) {
- final int googlePlayServicesCheck = GoogleApiAvailability.getInstance()
- .isGooglePlayServicesAvailable(ui.getActivity());
- if (googlePlayServicesCheck == ConnectionResult.SUCCESS) {
- displayRestartRequiredDialog(ui.requireContext());
- return true;
- } else {
- GoogleApiAvailability.getInstance()
- .getErrorDialog(ui.getActivity(), googlePlayServicesCheck, 0)
- .show();
- return false;
- }
- }
- return true;
- });
- }
-
- private static void displayRestartRequiredDialog(@NonNull Context context) {
- AlertDialog.Builder dialog = new AlertDialog.Builder(context);
- dialog.setTitle(android.R.string.dialog_alert_title);
- dialog.setMessage(R.string.pref_restart_required);
- dialog.setPositiveButton(android.R.string.ok, (dialog1, which) -> PodcastApp.forceRestart());
- dialog.setCancelable(false);
- dialog.show();
- }
-}
diff --git a/app/src/play/res/layout/media_router_controller.xml b/app/src/play/res/layout/media_router_controller.xml
deleted file mode 100644
index bdb1b1cc2..000000000
--- a/app/src/play/res/layout/media_router_controller.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/mrc_playback_control"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingTop="@dimen/media_router_controller_playback_control_vertical_padding"
- android:paddingBottom="@dimen/media_router_controller_playback_control_vertical_padding"
- android:paddingLeft="@dimen/media_router_controller_playback_control_start_padding"
- android:paddingStart="@dimen/media_router_controller_playback_control_start_padding"
- android:paddingRight="@dimen/media_router_controller_playback_control_horizontal_spacing"
- android:paddingEnd="@dimen/media_router_controller_playback_control_horizontal_spacing">
- <ImageButton android:id="@+id/mrc_control_play_pause"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginLeft="@dimen/media_router_controller_playback_control_horizontal_spacing"
- android:layout_marginStart="@dimen/media_router_controller_playback_control_horizontal_spacing"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true"
- android:contentDescription="@string/mr_controller_play"
- android:background="?android:attr/selectableItemBackground"/>
-
- <LinearLayout android:id="@+id/mrc_control_title_container"
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"
- android:layout_toLeftOf="@id/mrc_control_play_pause"
- android:layout_toStartOf="@id/mrc_control_play_pause"
- android:layout_centerVertical="true">
- <TextView android:id="@+id/mrc_control_title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.MediaRouter.PrimaryText"/>
- <TextView android:id="@+id/mrc_control_subtitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="@style/TextAppearance.MediaRouter.SecondaryText"
- android:singleLine="true" />
- </LinearLayout>
-</RelativeLayout>