summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorByteHamster <info@bytehamster.com>2024-02-18 23:59:46 +0100
committerByteHamster <info@bytehamster.com>2024-02-18 23:59:46 +0100
commit7a40a505f3700c5e2fe812cba9ef626579409c83 (patch)
tree139617b459e3f9ed6de50fc25a76e31f64ec4ee0
parentdc63386e8992f3430e82748282a7843c519a4865 (diff)
parent0d29e44de5ac97f2285aca241e4f4290c26ea0f9 (diff)
downloadAntennaPod-7a40a505f3700c5e2fe812cba9ef626579409c83.zip
Merge branch 'develop'
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yml4
-rw-r--r--.github/ISSUE_TEMPLATE/feature_request.yml4
-rw-r--r--.github/workflows/close-if-no-reply.yml12
-rw-r--r--app/build.gradle2
-rw-r--r--app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java54
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java32
-rw-r--r--app/src/main/assets/licenses.xml6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java28
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java37
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java68
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ItemFilterDialog.java38
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ItemSortDialog.java104
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java117
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java73
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java41
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java58
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java35
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java126
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java57
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/MenuItemToSortOrderConverter.java44
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java112
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java95
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java86
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/ui/home/HomeFragment.java21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/ui/home/sections/EchoSection.java26
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java10
-rw-r--r--app/src/main/res/layout/audio_controls.xml13
-rw-r--r--app/src/main/res/layout/feedinfo.xml3
-rw-r--r--app/src/main/res/layout/filter_dialog.xml47
-rw-r--r--app/src/main/res/layout/home_section.xml1
-rw-r--r--app/src/main/res/layout/nextcloud_auth_dialog.xml104
-rw-r--r--app/src/main/res/layout/sort_dialog.xml28
-rw-r--r--app/src/main/res/layout/sort_dialog_item.xml10
-rw-r--r--app/src/main/res/layout/sort_dialog_item_active.xml10
-rw-r--r--app/src/main/res/layout/speed_select_dialog.xml64
-rw-r--r--app/src/main/res/layout/time_dialog.xml246
-rw-r--r--app/src/main/res/menu/downloads_completed.xml8
-rw-r--r--app/src/main/res/menu/episodes.xml7
-rw-r--r--app/src/main/res/menu/feedlist.xml3
-rw-r--r--app/src/main/res/menu/home.xml3
-rw-r--r--app/src/main/res/menu/inbox.xml7
-rw-r--r--app/src/main/res/menu/mediaplayer.xml7
-rw-r--r--app/src/main/res/menu/queue.xml7
-rw-r--r--app/src/main/res/menu/sort_menu.xml99
-rw-r--r--app/src/main/res/menu/subscriptions.xml3
-rw-r--r--app/src/main/res/values/integers.xml1
-rw-r--r--app/src/main/res/xml/preferences_user_interface.xml6
-rw-r--r--core/src/debug/res/mipmap-hdpi/ic_launcher.pngbin8724 -> 0 bytes
-rw-r--r--core/src/debug/res/mipmap-hdpi/ic_launcher_background.pngbin0 -> 8672 bytes
-rw-r--r--core/src/debug/res/mipmap-hdpi/ic_launcher_foreground.pngbin0 -> 10705 bytes
-rw-r--r--core/src/debug/res/mipmap-mdpi/ic_launcher.pngbin4821 -> 0 bytes
-rw-r--r--core/src/debug/res/mipmap-mdpi/ic_launcher_background.pngbin0 -> 4187 bytes
-rw-r--r--core/src/debug/res/mipmap-mdpi/ic_launcher_foreground.pngbin0 -> 5235 bytes
-rw-r--r--core/src/debug/res/mipmap-xhdpi/ic_launcher.pngbin13548 -> 0 bytes
-rw-r--r--core/src/debug/res/mipmap-xhdpi/ic_launcher_background.pngbin0 -> 10040 bytes
-rw-r--r--core/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.pngbin0 -> 12181 bytes
-rw-r--r--core/src/debug/res/mipmap-xxhdpi/ic_launcher.pngbin25054 -> 0 bytes
-rw-r--r--core/src/debug/res/mipmap-xxhdpi/ic_launcher_background.pngbin0 -> 17155 bytes
-rw-r--r--core/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.pngbin0 -> 21041 bytes
-rw-r--r--core/src/debug/res/mipmap-xxxhdpi/ic_launcher.pngbin38151 -> 0 bytes
-rw-r--r--core/src/debug/res/mipmap-xxxhdpi/ic_launcher_background.pngbin0 -> 23926 bytes
-rw-r--r--core/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.pngbin0 -> 30166 bytes
-rw-r--r--core/src/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.pngbin0 -> 10269 bytes
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/menuhandler/MenuItemUtils.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java98
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java47
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesser.java15
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java13
-rw-r--r--core/src/main/res/drawable/ic_disc_alert.xml13
-rw-r--r--core/src/main/res/drawable/ic_disc_full.xml9
-rw-r--r--core/src/main/res/drawable/ic_paperclip.xml16
-rw-r--r--core/src/main/res/drawable/ic_sliders.xml5
-rw-r--r--core/src/main/res/layout/refresh_action_view.xml8
-rw-r--r--core/src/main/res/values/arrays.xml48
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java35
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesserTest.java3
-rw-r--r--model/src/main/java/de/danoeh/antennapod/model/feed/FeedItem.java52
-rw-r--r--storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBUpgrader.java4
-rw-r--r--storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java7
-rw-r--r--storage/database/src/main/java/de/danoeh/antennapod/storage/database/mapper/FeedItemCursorMapper.java6
-rw-r--r--storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java67
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoActivity.java94
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/FinalShareScreen.java44
-rw-r--r--ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WavesScreen.java38
-rw-r--r--ui/i18n/src/main/res/values/strings.xml34
-rw-r--r--ui/png-icons/src/main/res/drawable/ic_notification_next_chapter.xml7
-rw-r--r--ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/downloads/DownloadStatisticsListAdapter.java9
-rw-r--r--ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/subscriptions/PlaybackStatisticsListAdapter.java4
-rw-r--r--ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/subscriptions/StatisticsFilterDialog.java4
101 files changed, 1430 insertions, 1348 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 50576fdc1..263d5450f 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -11,7 +11,9 @@ body:
attributes:
label: Checklist
options:
- - label: I have used the search function for [open](https://github.com/AntennaPod/AntennaPod/issues) **and** [closed](https://github.com/AntennaPod/AntennaPod/issues?q=is%3Aissue+is%3Aclosed) issues to see if someone else has already submitted the same bug report.
+ - label: I have used the search function for [**OPEN**](https://github.com/AntennaPod/AntennaPod/issues) issues to see if someone else has already submitted the same bug report.
+ required: true
+ - label: I have **also** used the search function for [**CLOSED**](https://github.com/AntennaPod/AntennaPod/issues?q=is%3Aissue+is%3Aclosed) issues to see if the problem is already solved and just waiting to be released.
required: true
- label: I will describe the problem with as much detail as possible.
required: true
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
index fbac2857a..8853e82e2 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -6,7 +6,9 @@ body:
attributes:
label: Checklist
options:
- - label: I have used the search function for [open](https://github.com/AntennaPod/AntennaPod/issues) **and** [closed](https://github.com/AntennaPod/AntennaPod/issues?q=is%3Aissue+is%3Aclosed) issues to see if someone else has already submitted the same feature request.
+ - label: I have used the search function for [**OPEN**](https://github.com/AntennaPod/AntennaPod/issues) issues to see if someone else has already submitted the same feature request.
+ required: true
+ - label: I have **also** used the search function for [**CLOSED**](https://github.com/AntennaPod/AntennaPod/issues?q=is%3Aissue+is%3Aclosed) issues to see if the feature was already implemented and is just waiting to be released, or if the feature was rejected.
required: true
- label: I will describe the problem with as much detail as possible.
required: true
diff --git a/.github/workflows/close-if-no-reply.yml b/.github/workflows/close-if-no-reply.yml
index ba4903a7f..1c02efce0 100644
--- a/.github/workflows/close-if-no-reply.yml
+++ b/.github/workflows/close-if-no-reply.yml
@@ -13,15 +13,15 @@ jobs:
steps:
- uses: actions/stale@v5
with:
- days-before-stale: 15
- days-before-close: 15
+ days-before-stale: 7
+ days-before-close: 7
only-labels: 'Awaiting reply'
stale-issue-label: 'Still awaiting reply'
stale-pr-label: 'Still awaiting reply'
- stale-issue-message: "This issue will be closed when we don't get a reply within 15 days."
- stale-pr-message: "This PR will be closed when we don't get a reply within 15 days."
+ stale-issue-message: "This issue will be closed when we don't get a reply within 7 days."
+ stale-pr-message: "This PR will be closed when we don't get a reply within 7 days."
labels-to-remove-when-unstale: 'Awaiting reply'
close-issue-label: "Close reason: no reply"
close-pr-label: "Close reason: no reply"
- close-issue-message: "This issue was closed because we didn't get a reply for 30 days."
- close-pr-message: "This PR was closed because we didn't get a reply for 30 days."
+ close-issue-message: "This issue was closed because we didn't get a reply for 14 days."
+ close-pr-message: "This PR was closed because we didn't get a reply for 14 days."
diff --git a/app/build.gradle b/app/build.gradle
index 1a180e855..997984650 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -123,7 +123,7 @@ dependencies {
implementation "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion"
implementation "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
implementation 'com.leinardi.android:speed-dial:3.2.0'
- implementation 'com.github.ByteHamster:SearchPreference:v2.0.0'
+ implementation 'com.github.ByteHamster:SearchPreference:v2.5.0'
implementation 'com.github.skydoves:balloon:1.5.3'
implementation 'com.github.xabaras:RecyclerViewSwipeDecorator:1.3'
implementation "com.annimon:stream:$annimonStreamVersion"
diff --git a/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java b/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java
deleted file mode 100644
index 835af1f95..000000000
--- a/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-import androidx.annotation.NonNull;
-import androidx.core.util.Consumer;
-import de.danoeh.antennapod.model.download.DownloadResult;
-import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest;
-
-public class StubDownloader extends Downloader {
-
- private final long downloadTime;
-
- @NonNull
- private final Consumer<DownloadResult> onDownloadComplete;
-
- public StubDownloader(@NonNull DownloadRequest request, long downloadTime,
- @NonNull Consumer<DownloadResult> onDownloadComplete) {
- super(request);
- this.downloadTime = downloadTime;
- this.onDownloadComplete = onDownloadComplete;
- }
-
- @Override
- protected void download() {
- try {
- Thread.sleep(downloadTime);
- } catch (Throwable t) {
- t.printStackTrace();
- }
- onDownloadComplete.accept(result);
- }
-
- @NonNull
- @Override
- public DownloadRequest getDownloadRequest() {
- return super.getDownloadRequest();
- }
-
- @NonNull
- @Override
- public DownloadResult getResult() {
- return super.getResult();
- }
-
- @Override
- public boolean isFinished() {
- return super.isFinished();
- }
-
- @Override
- public void cancel() {
- super.cancel();
- result.setCancelled();
- }
-}
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 24c20242a..9ba4276be 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -3,6 +3,7 @@ package de.test.antennapod.ui;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
+
import androidx.annotation.StringRes;
import androidx.preference.PreferenceManager;
import androidx.test.filters.LargeTest;
@@ -30,9 +31,7 @@ import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.action.ViewActions.scrollTo;
import static androidx.test.espresso.action.ViewActions.swipeDown;
import static androidx.test.espresso.action.ViewActions.swipeUp;
-import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
@@ -40,7 +39,6 @@ import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static de.test.antennapod.EspressoTestUtils.clickPreference;
import static de.test.antennapod.EspressoTestUtils.waitForView;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -80,38 +78,22 @@ public class PreferencesTest {
}
@Test
- public void testSetLockscreenButtons() {
+ public void testSetNotificationButtons() {
clickPreference(R.string.user_interface_label);
- String[] buttons = res.getStringArray(R.array.compact_notification_buttons_options);
- clickPreference(R.string.pref_compact_notification_buttons_title);
+ String[] buttons = res.getStringArray(R.array.full_notification_buttons_options);
+ clickPreference(R.string.pref_full_notification_buttons_title);
// 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());
onView(withText(buttons[1])).perform(click());
onView(withText(buttons[2])).perform(click());
- // Make sure that the third checkbox is unchecked
- onView(withText(buttons[2])).check(matches(not(isChecked())));
-
- String snackBarText = String.format(res.getString(
- R.string.pref_compact_notification_buttons_dialog_error), 2);
- Awaitility.await().ignoreExceptions().atMost(4000, MILLISECONDS)
- .until(() -> {
- onView(withText(snackBarText)).check(doesNotExist());
- return true;
- });
-
onView(withText(R.string.confirm_label)).perform(click());
Awaitility.await().atMost(1000, MILLISECONDS)
- .until(UserPreferences::showRewindOnCompactNotification);
+ .until(() -> UserPreferences.showSkipOnFullNotification());
Awaitility.await().atMost(1000, MILLISECONDS)
- .until(UserPreferences::showFastForwardOnCompactNotification);
+ .until(() -> UserPreferences.showNextChapterOnFullNotification());
Awaitility.await().atMost(1000, MILLISECONDS)
- .until(() -> !UserPreferences.showSkipOnCompactNotification());
+ .until(() -> !UserPreferences.showPlaybackSpeedOnFullNotification());
}
@Test
diff --git a/app/src/main/assets/licenses.xml b/app/src/main/assets/licenses.xml
index f458da2aa..de06d42a1 100644
--- a/app/src/main/assets/licenses.xml
+++ b/app/src/main/assets/licenses.xml
@@ -7,12 +7,6 @@
license="GPL-3.0"
licenseText="LICENSE.txt" />
<library
- name="AntennaPod-AudioPlayer"
- author="The AntennaPod team"
- website="https://github.com/AntennaPod/AntennaPod-AudioPlayer"
- license="Apache 2.0"
- licenseText="LICENSE_APACHE-2.0.txt" />
- <library
name="Android Jetpack"
author="Google"
website="https://developer.android.com/jetpack"
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 c5d656aa0..031117170 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -36,6 +36,7 @@ import de.danoeh.antennapod.core.feed.FeedUrlNotFoundException;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceInterface;
import de.danoeh.antennapod.core.util.DownloadErrorLabel;
+import de.danoeh.antennapod.databinding.EditTextDialogBinding;
import de.danoeh.antennapod.databinding.OnlinefeedviewHeaderBinding;
import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedListUpdateEvent;
@@ -95,7 +96,7 @@ import java.util.Map;
public class OnlineFeedViewActivity extends AppCompatActivity {
public static final String ARG_FEEDURL = "arg.feedurl";
- // Optional argument: specify a title for the actionbar.
+ public static final String ARG_WAS_MANUAL_URL = "manual_url";
private static final int RESULT_ERROR = 2;
private static final String TAG = "OnlineFeedViewActivity";
private static final String PREFS = "OnlineFeedViewActivityPreferences";
@@ -598,7 +599,10 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
builder.setMessage(R.string.download_error_error_unknown);
}
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.cancel());
- builder.setOnDismissListener(dialog -> {
+ if (getIntent().getBooleanExtra(ARG_WAS_MANUAL_URL, false)) {
+ builder.setNeutralButton(R.string.edit_url_menu, (dialog, which) -> editUrl());
+ }
+ builder.setOnCancelListener(dialog -> {
setResult(RESULT_ERROR);
finish();
});
@@ -609,6 +613,26 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
}
+ private void editUrl() {
+ MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this);
+ builder.setTitle(R.string.edit_url_menu);
+ final EditTextDialogBinding dialogBinding = EditTextDialogBinding.inflate(getLayoutInflater());
+ if (downloader != null) {
+ dialogBinding.urlEditText.setText(downloader.getDownloadRequest().getSource());
+ }
+ builder.setView(dialogBinding.getRoot());
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ setLoadingLayout();
+ lookupUrlAndDownload(dialogBinding.urlEditText.getText().toString());
+ });
+ builder.setNegativeButton(R.string.cancel_label, (dialog1, which) -> dialog1.cancel());
+ builder.setOnCancelListener(dialog1 -> {
+ setResult(RESULT_ERROR);
+ finish();
+ });
+ builder.show();
+ }
+
@Subscribe(threadMode = ThreadMode.MAIN)
public void playbackStateChanged(PlayerStatusEvent event) {
boolean isPlayingPreview =
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 10a41057c..3f1c17cdc 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java
@@ -6,7 +6,9 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.os.Environment;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.Menu;
@@ -45,6 +47,7 @@ import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Locale;
/**
* Activity for Opml Import.
@@ -140,15 +143,6 @@ public class OpmlImportActivity extends AppCompatActivity {
return;
}
this.uri = uri;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
- && uri.toString().contains(Environment.getExternalStorageDirectory().toString())) {
- int permission = ActivityCompat.checkSelfPermission(this,
- android.Manifest.permission.READ_EXTERNAL_STORAGE);
- if (permission != PackageManager.PERMISSION_GRANTED) {
- requestPermission();
- return;
- }
- }
startImport();
}
@@ -244,12 +238,29 @@ public class OpmlImportActivity extends AppCompatActivity {
getTitleList());
viewBinding.feedlist.setAdapter(listAdapter);
}, e -> {
+ Log.d(TAG, Log.getStackTraceString(e));
+ String message = e.getMessage() == null ? "" : e.getMessage();
+ if (message.toLowerCase(Locale.ROOT).contains("permission")
+ && Build.VERSION.SDK_INT >= 23) {
+ int permission = ActivityCompat.checkSelfPermission(this,
+ android.Manifest.permission.READ_EXTERNAL_STORAGE);
+ if (permission != PackageManager.PERMISSION_GRANTED) {
+ requestPermission();
+ return;
+ }
+ }
viewBinding.progressBar.setVisibility(View.GONE);
MaterialAlertDialogBuilder alert = new MaterialAlertDialogBuilder(this);
alert.setTitle(R.string.error_label);
- alert.setMessage(getString(R.string.opml_reader_error) + e.getMessage());
- alert.setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
- alert.create().show();
+ String userReadable = getString(R.string.opml_reader_error);
+ String details = e.getMessage();
+ String total = userReadable + "\n\n" + details;
+ SpannableString errorMessage = new SpannableString(total);
+ errorMessage.setSpan(new ForegroundColorSpan(0x88888888),
+ userReadable.length(), total.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ alert.setMessage(errorMessage);
+ alert.setPositiveButton(android.R.string.ok, (dialog, which) -> finish());
+ alert.show();
});
}
}
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 e3473937e..fc371090e 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -567,7 +567,8 @@ public class VideoplayerActivity extends CastEnabledActivity implements SeekBar.
menu.findItem(R.id.disable_sleeptimer_item).setVisible(controller.sleepTimerActive());
menu.findItem(R.id.player_switch_to_audio_only).setVisible(true);
- menu.findItem(R.id.audio_controls).setIcon(R.drawable.ic_sliders);
+
+ menu.findItem(R.id.audio_controls).setVisible(controller.getAudioTracks().size() >= 2);
menu.findItem(R.id.playback_speed).setVisible(true);
menu.findItem(R.id.player_show_chapters).setVisible(true);
return true;
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 8d220c90d..a304ead3c 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -261,7 +261,7 @@ public class NavListAdapter extends RecyclerView.Adapter<NavListAdapter.Holder>
int spaceUsed = itemAccess.getNumberOfDownloadedItems()
- itemAccess.getReclaimableItems();
if (epCacheSize > 0 && spaceUsed >= epCacheSize) {
- holder.count.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_disc_full, 0);
+ holder.count.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_disc_alert, 0);
holder.count.setVisibility(View.VISIBLE);
holder.count.setOnClickListener(v ->
new MaterialAlertDialogBuilder(context)
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java
deleted file mode 100644
index dc8cf65ba..000000000
--- a/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package de.danoeh.antennapod.dialog;
-
-import android.content.Context;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.model.feed.SortOrder;
-
-public abstract class IntraFeedSortDialog {
-
- @Nullable
- protected SortOrder currentSortOrder;
- @NonNull
- protected Context context;
-
- private final String[] sortItems;
- private final SortOrder[] sortValues;
-
- public IntraFeedSortDialog(@NonNull Context context, @Nullable SortOrder sortOrder, @NonNull boolean isLocalFeed) {
- this.context = context;
- this.currentSortOrder = sortOrder;
-
- if (isLocalFeed) {
- sortItems = context.getResources().getStringArray(R.array.local_feed_episodes_sort_options);
- final String[] localSortStringValues =
- context.getResources().getStringArray(R.array.local_feed_episodes_sort_values);
- sortValues = SortOrder.valuesOf(localSortStringValues);
- } else {
- sortItems = context.getResources().getStringArray(R.array.feed_episodes_sort_options);
- final String[] commonSortStringValues =
- context.getResources().getStringArray(R.array.feed_episodes_sort_values);
- sortValues = SortOrder.valuesOf(commonSortStringValues);
- }
- }
-
- public void openDialog() {
- int idxCurrentSort = getCurrentSortOrderIndex();
-
- MaterialAlertDialogBuilder builder =
- new MaterialAlertDialogBuilder(context)
- .setTitle(R.string.sort)
- .setSingleChoiceItems(sortItems, idxCurrentSort, (dialog, idxNewSort) -> {
- updateSort(sortValues[idxNewSort]);
- dialog.dismiss();
- })
- .setNegativeButton(R.string.cancel_label, null);
- builder.create().show();
- }
-
- /**
- * Retrieves index of currentSortOrder index in values array.
- * @return if currentSortOrder is found in array - returns index of that element,
- * otherwise returns 0, the default sort option;
- */
- private int getCurrentSortOrderIndex() {
- for (int i = 0; i < sortValues.length; i++) {
- if (currentSortOrder == sortValues[i]) {
- return i;
- }
- }
- return 0;
- }
-
- protected abstract void updateSort(@NonNull SortOrder sortOrder);
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ItemFilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ItemFilterDialog.java
index 92c143992..359c513af 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ItemFilterDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ItemFilterDialog.java
@@ -18,11 +18,13 @@ import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.button.MaterialButtonToggleGroup;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItemFilterGroup;
+import de.danoeh.antennapod.databinding.FilterDialogBinding;
import de.danoeh.antennapod.databinding.FilterDialogRowBinding;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
@@ -36,24 +38,36 @@ public abstract class ItemFilterDialog extends BottomSheetDialogFragment {
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View layout = inflater.inflate(R.layout.filter_dialog, null, false);
- rows = layout.findViewById(R.id.filter_rows);
+ FilterDialogBinding binding = FilterDialogBinding.bind(layout);
+ rows = binding.filterRows;
FeedItemFilter filter = (FeedItemFilter) getArguments().getSerializable(ARGUMENT_FILTER);
+ //add filter rows
for (FeedItemFilterGroup item : FeedItemFilterGroup.values()) {
- FilterDialogRowBinding binding = FilterDialogRowBinding.inflate(inflater);
- binding.getRoot().addOnButtonCheckedListener(
+ FilterDialogRowBinding rowBinding = FilterDialogRowBinding.inflate(inflater);
+ rowBinding.getRoot().addOnButtonCheckedListener(
(group, checkedId, isChecked) -> onFilterChanged(getNewFilterValues()));
- binding.filterButton1.setText(item.values[0].displayName);
- binding.filterButton1.setTag(item.values[0].filterId);
- binding.filterButton2.setText(item.values[1].displayName);
- binding.filterButton2.setTag(item.values[1].filterId);
- binding.filterButton1.setMaxLines(3);
- binding.filterButton1.setSingleLine(false);
- binding.filterButton2.setMaxLines(3);
- binding.filterButton2.setSingleLine(false);
- rows.addView(binding.getRoot());
+ rowBinding.filterButton1.setText(item.values[0].displayName);
+ rowBinding.filterButton1.setTag(item.values[0].filterId);
+ rowBinding.filterButton2.setText(item.values[1].displayName);
+ rowBinding.filterButton2.setTag(item.values[1].filterId);
+ rowBinding.filterButton1.setMaxLines(3);
+ rowBinding.filterButton1.setSingleLine(false);
+ rowBinding.filterButton2.setMaxLines(3);
+ rowBinding.filterButton2.setSingleLine(false);
+ rows.addView(rowBinding.getRoot(), rows.getChildCount() - 1);
}
+ binding.confirmFiltermenu.setOnClickListener(view1 -> dismiss());
+ binding.resetFiltermenu.setOnClickListener(view1 -> {
+ onFilterChanged(Collections.emptySet());
+ for (int i = 0; i < rows.getChildCount(); i++) {
+ if (rows.getChildAt(i) instanceof MaterialButtonToggleGroup) {
+ ((MaterialButtonToggleGroup) rows.getChildAt(i)).clearChecked();
+ }
+ }
+ });
+
for (String filterId : filter.getValues()) {
if (!TextUtils.isEmpty(filterId)) {
Button button = layout.findViewWithTag(filterId);
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ItemSortDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ItemSortDialog.java
new file mode 100644
index 000000000..cd6cc4b0a
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ItemSortDialog.java
@@ -0,0 +1,104 @@
+package de.danoeh.antennapod.dialog;
+
+import android.app.Dialog;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import com.google.android.material.bottomsheet.BottomSheetDialog;
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.databinding.SortDialogBinding;
+import de.danoeh.antennapod.databinding.SortDialogItemActiveBinding;
+import de.danoeh.antennapod.databinding.SortDialogItemBinding;
+import de.danoeh.antennapod.model.feed.SortOrder;
+
+public class ItemSortDialog extends BottomSheetDialogFragment {
+ protected SortOrder sortOrder;
+ protected SortDialogBinding viewBinding;
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ viewBinding = SortDialogBinding.inflate(inflater);
+ populateList();
+ viewBinding.keepSortedCheckbox.setOnCheckedChangeListener(
+ (buttonView, isChecked) -> ItemSortDialog.this.onSelectionChanged());
+ return viewBinding.getRoot();
+ }
+
+ private void populateList() {
+ viewBinding.gridLayout.removeAllViews();
+ onAddItem(R.string.episode_title, SortOrder.EPISODE_TITLE_A_Z, SortOrder.EPISODE_TITLE_Z_A, true);
+ onAddItem(R.string.feed_title, SortOrder.FEED_TITLE_A_Z, SortOrder.FEED_TITLE_Z_A, true);
+ onAddItem(R.string.duration, SortOrder.DURATION_SHORT_LONG, SortOrder.DURATION_LONG_SHORT, true);
+ onAddItem(R.string.date, SortOrder.DATE_OLD_NEW, SortOrder.DATE_NEW_OLD, false);
+ onAddItem(R.string.size, SortOrder.SIZE_SMALL_LARGE, SortOrder.SIZE_LARGE_SMALL, false);
+ onAddItem(R.string.filename, SortOrder.EPISODE_FILENAME_A_Z, SortOrder.EPISODE_FILENAME_Z_A, true);
+ onAddItem(R.string.random, SortOrder.RANDOM, SortOrder.RANDOM, true);
+ onAddItem(R.string.smart_shuffle, SortOrder.SMART_SHUFFLE_OLD_NEW, SortOrder.SMART_SHUFFLE_NEW_OLD, false);
+ }
+
+ protected void onAddItem(int title, SortOrder ascending, SortOrder descending, boolean ascendingIsDefault) {
+ if (sortOrder == ascending || sortOrder == descending) {
+ SortDialogItemActiveBinding item = SortDialogItemActiveBinding.inflate(
+ getLayoutInflater(), viewBinding.gridLayout, false);
+ SortOrder other;
+ if (ascending == descending) {
+ item.button.setText(title);
+ other = ascending;
+ } else if (sortOrder == ascending) {
+ item.button.setText(getString(title) + "\u00A0▲");
+ other = descending;
+ } else {
+ item.button.setText(getString(title) + "\u00A0▼");
+ other = ascending;
+ }
+ item.button.setOnClickListener(v -> {
+ sortOrder = other;
+ populateList();
+ onSelectionChanged();
+ });
+ viewBinding.gridLayout.addView(item.getRoot());
+ } else {
+ SortDialogItemBinding item = SortDialogItemBinding.inflate(
+ getLayoutInflater(), viewBinding.gridLayout, false);
+ item.button.setText(title);
+ item.button.setOnClickListener(v -> {
+ sortOrder = ascendingIsDefault ? ascending : descending;
+ populateList();
+ onSelectionChanged();
+ });
+ viewBinding.gridLayout.addView(item.getRoot());
+ }
+ }
+
+ protected void onSelectionChanged() {
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.setOnShowListener(dialogInterface -> {
+ BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialogInterface;
+ setupFullHeight(bottomSheetDialog);
+ });
+ return dialog;
+ }
+
+ private void setupFullHeight(BottomSheetDialog bottomSheetDialog) {
+ FrameLayout bottomSheet = bottomSheetDialog.findViewById(R.id.design_bottom_sheet);
+ if (bottomSheet != null) {
+ BottomSheetBehavior<FrameLayout> behavior = BottomSheetBehavior.from(bottomSheet);
+ ViewGroup.LayoutParams layoutParams = bottomSheet.getLayoutParams();
+ bottomSheet.setLayoutParams(layoutParams);
+ behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+ }
+ }
+}
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 a87dccdf5..009f33fe2 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
@@ -10,9 +10,7 @@ import androidx.appcompat.app.AlertDialog;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import androidx.fragment.app.DialogFragment;
import android.widget.Button;
-import android.widget.CheckBox;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import java.util.List;
@@ -37,12 +35,10 @@ public class PlaybackControlsDialog extends DialogFragment {
controller = new PlaybackController(getActivity()) {
@Override
public void loadMediaInfo() {
- setupUi();
setupAudioTracks();
}
};
controller.init();
- setupUi();
}
@Override
@@ -62,15 +58,6 @@ public class PlaybackControlsDialog extends DialogFragment {
return dialog;
}
- private void setupUi() {
- final CheckBox skipSilence = dialog.findViewById(R.id.skipSilence);
- skipSilence.setChecked(UserPreferences.isSkipSilence());
- skipSilence.setOnCheckedChangeListener((buttonView, isChecked) -> {
- UserPreferences.setSkipSilence(isChecked);
- controller.setSkipSilence(isChecked);
- });
- }
-
private void setupAudioTracks() {
List<String> audioTracks = controller.getAudioTracks();
int selectedAudioTrack = controller.getSelectedAudioTrack();
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 cf77c2838..929e6b1ad 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SubscriptionsFilterDialog.java
@@ -1,42 +1,49 @@
package de.danoeh.antennapod.dialog;
-import android.content.Context;
+import android.app.Dialog;
+import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.Button;
+import android.widget.FrameLayout;
import android.widget.LinearLayout;
-
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import com.google.android.material.bottomsheet.BottomSheetDialog;
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import com.google.android.material.button.MaterialButtonToggleGroup;
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
-import org.greenrobot.eventbus.EventBus;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.SubscriptionsFilterGroup;
+import de.danoeh.antennapod.databinding.FilterDialogBinding;
import de.danoeh.antennapod.databinding.FilterDialogRowBinding;
import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.model.feed.SubscriptionsFilter;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
+import org.greenrobot.eventbus.EventBus;
-public class SubscriptionsFilterDialog {
- public static void showDialog(Context context) {
- SubscriptionsFilter subscriptionsFilter = UserPreferences.getSubscriptionsFilter();
- final Set<String> filterValues = new HashSet<>(Arrays.asList(subscriptionsFilter.getValues()));
- MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
- builder.setTitle(context.getString(R.string.pref_filter_feed_title));
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
- LayoutInflater inflater = LayoutInflater.from(context);
- View layout = inflater.inflate(R.layout.filter_dialog, null, false);
- LinearLayout rows = layout.findViewById(R.id.filter_rows);
- builder.setView(layout);
+public class SubscriptionsFilterDialog extends BottomSheetDialogFragment {
+ private LinearLayout rows;
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ SubscriptionsFilter subscriptionsFilter = UserPreferences.getSubscriptionsFilter();
+ FilterDialogBinding dialogBinding = FilterDialogBinding.inflate(inflater);
+ rows = dialogBinding.filterRows;
for (SubscriptionsFilterGroup item : SubscriptionsFilterGroup.values()) {
FilterDialogRowBinding binding = FilterDialogRowBinding.inflate(inflater);
+ binding.getRoot().addOnButtonCheckedListener(
+ (group, checkedId, isChecked) -> updateFilter(getFilterValues()));
binding.buttonGroup.setWeightSum(item.values.length);
binding.filterButton1.setText(item.values[0].displayName);
binding.filterButton1.setTag(item.values[0].filterId);
@@ -50,38 +57,72 @@ public class SubscriptionsFilterDialog {
binding.filterButton1.setSingleLine(false);
binding.filterButton2.setMaxLines(3);
binding.filterButton2.setSingleLine(false);
- rows.addView(binding.getRoot());
+ rows.addView(binding.getRoot(), rows.getChildCount() - 1);
}
+ final Set<String> filterValues = new HashSet<>(Arrays.asList(subscriptionsFilter.getValues()));
for (String filterId : filterValues) {
if (!TextUtils.isEmpty(filterId)) {
- Button button = layout.findViewWithTag(filterId);
+ Button button = dialogBinding.getRoot().findViewWithTag(filterId);
if (button != null) {
((MaterialButtonToggleGroup) button.getParent()).check(button.getId());
}
}
}
- builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
- filterValues.clear();
+ dialogBinding.confirmFiltermenu.setOnClickListener(view -> {
+ updateFilter(getFilterValues());
+ dismiss();
+ });
+ dialogBinding.resetFiltermenu.setOnClickListener(view -> {
+ updateFilter(Collections.emptySet());
for (int i = 0; i < rows.getChildCount(); i++) {
- if (!(rows.getChildAt(i) instanceof MaterialButtonToggleGroup)) {
- continue;
- }
- MaterialButtonToggleGroup group = (MaterialButtonToggleGroup) rows.getChildAt(i);
- if (group.getCheckedButtonId() == View.NO_ID) {
- continue;
- }
- String tag = (String) group.findViewById(group.getCheckedButtonId()).getTag();
- if (tag == null) { // Clear buttons use no tag
- continue;
+ if (rows.getChildAt(i) instanceof MaterialButtonToggleGroup) {
+ ((MaterialButtonToggleGroup) rows.getChildAt(i)).clearChecked();
}
- filterValues.add(tag);
}
- updateFilter(filterValues);
});
- builder.setNegativeButton(R.string.cancel_label, null);
- builder.show();
+ return dialogBinding.getRoot();
+ }
+
+ private Set<String> getFilterValues() {
+ Set<String> filterValues = new HashSet<>();
+ for (int i = 0; i < rows.getChildCount(); i++) {
+ if (!(rows.getChildAt(i) instanceof MaterialButtonToggleGroup)) {
+ continue;
+ }
+ MaterialButtonToggleGroup group = (MaterialButtonToggleGroup) rows.getChildAt(i);
+ if (group.getCheckedButtonId() == View.NO_ID) {
+ continue;
+ }
+ String tag = (String) group.findViewById(group.getCheckedButtonId()).getTag();
+ if (tag == null) { // Clear buttons use no tag
+ continue;
+ }
+ filterValues.add(tag);
+ }
+ return filterValues;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ Dialog dialog = super.onCreateDialog(savedInstanceState);
+ dialog.setOnShowListener(dialogInterface -> {
+ BottomSheetDialog bottomSheetDialog = (BottomSheetDialog) dialogInterface;
+ setupFullHeight(bottomSheetDialog);
+ });
+ return dialog;
+ }
+
+ private void setupFullHeight(BottomSheetDialog bottomSheetDialog) {
+ FrameLayout bottomSheet = bottomSheetDialog.findViewById(R.id.design_bottom_sheet);
+ if (bottomSheet != null) {
+ BottomSheetBehavior<FrameLayout> behavior = BottomSheetBehavior.from(bottomSheet);
+ ViewGroup.LayoutParams layoutParams = bottomSheet.getLayoutParams();
+ bottomSheet.setLayoutParams(layoutParams);
+ behavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+ }
}
private static void updateFilter(Set<String> filterValues) {
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 0367e717b..8de7dce04 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -6,6 +6,8 @@ import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.CheckBox;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.GridLayoutManager;
@@ -94,6 +96,13 @@ public class VariableSpeedDialog extends BottomSheetDialogFragment {
addCurrentSpeedChip.setOnCloseIconClickListener(v -> addCurrentSpeed());
addCurrentSpeedChip.setCloseIconContentDescription(getString(R.string.add_preset));
addCurrentSpeedChip.setOnClickListener(v -> addCurrentSpeed());
+
+ final CheckBox skipSilence = root.findViewById(R.id.skipSilence);
+ skipSilence.setChecked(UserPreferences.isSkipSilence());
+ skipSilence.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ UserPreferences.setSkipSilence(isChecked);
+ controller.setSkipSilence(isChecked);
+ });
return root;
}
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 29be41727..20cdd2718 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -147,6 +147,7 @@ public class AddFeedFragment extends Fragment {
private void addUrl(String url) {
Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, url);
+ intent.putExtra(OnlineFeedViewActivity.ARG_WAS_MANUAL_URL, true);
startActivity(intent);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
index 7061a69f3..ab5039ec2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -1,20 +1,23 @@
package de.danoeh.antennapod.fragment;
-import android.content.Context;
-import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.dialog.AllEpisodesFilterDialog;
+import de.danoeh.antennapod.dialog.ItemSortDialog;
+import de.danoeh.antennapod.event.FeedListUpdateEvent;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.model.feed.SortOrder;
+import de.danoeh.antennapod.storage.preferences.UserPreferences;
import org.apache.commons.lang3.StringUtils;
+import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import java.util.ArrayList;
@@ -26,48 +29,33 @@ import java.util.List;
*/
public class AllEpisodesFragment extends EpisodesListFragment {
public static final String TAG = "EpisodesFragment";
- private static final String PREF_NAME = "PrefAllEpisodesFragment";
- private static final String PREF_FILTER = "filter";
- public static final String PREF_SORT = "prefEpisodesSort";
- private SharedPreferences prefs;
+ public static final String PREF_NAME = "PrefAllEpisodesFragment";
@NonNull
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View root = super.onCreateView(inflater, container, savedInstanceState);
toolbar.inflateMenu(R.menu.episodes);
- inflateSortMenu();
toolbar.setTitle(R.string.episodes_label);
updateToolbar();
updateFilterUi();
- prefs = getActivity().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
txtvInformation.setOnClickListener(
v -> AllEpisodesFilterDialog.newInstance(getFilter()).show(getChildFragmentManager(), null));
return root;
}
- private void inflateSortMenu() {
- MenuItem sortItem = toolbar.getMenu().findItem(R.id.episodes_sort);
- getActivity().getMenuInflater().inflate(R.menu.sort_menu, sortItem.getSubMenu());
-
- // Remove the sorting options that are not needed in this fragment
- toolbar.getMenu().findItem(R.id.sort_episode_title).setVisible(false);
- toolbar.getMenu().findItem(R.id.sort_feed_title).setVisible(false);
- toolbar.getMenu().findItem(R.id.sort_random).setVisible(false);
- toolbar.getMenu().findItem(R.id.sort_smart_shuffle).setVisible(false);
- toolbar.getMenu().findItem(R.id.keep_sorted).setVisible(false);
- }
-
@NonNull
@Override
protected List<FeedItem> loadData() {
- return DBReader.getEpisodes(0, page * EPISODES_PER_PAGE, getFilter(), getSortOrder());
+ return DBReader.getEpisodes(0, page * EPISODES_PER_PAGE, getFilter(),
+ UserPreferences.getAllEpisodesSortOrder());
}
@NonNull
@Override
protected List<FeedItem> loadMoreData(int page) {
- return DBReader.getEpisodes((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE, getFilter(), getSortOrder());
+ return DBReader.getEpisodes((page - 1) * EPISODES_PER_PAGE, EPISODES_PER_PAGE, getFilter(),
+ UserPreferences.getAllEpisodesSortOrder());
}
@Override
@@ -77,8 +65,7 @@ public class AllEpisodesFragment extends EpisodesListFragment {
@Override
protected FeedItemFilter getFilter() {
- SharedPreferences prefs = getActivity().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
- return new FeedItemFilter(prefs.getString(PREF_FILTER, ""));
+ return new FeedItemFilter(UserPreferences.getPrefFilterAllEpisodes());
}
@Override
@@ -108,24 +95,16 @@ public class AllEpisodesFragment extends EpisodesListFragment {
}
onFilterChanged(new AllEpisodesFilterDialog.AllEpisodesFilterChangedEvent(new HashSet<>(filter)));
return true;
- } else {
- SortOrder sortOrder = MenuItemToSortOrderConverter.convert(item);
- if (sortOrder != null) {
- saveSortOrderAndRefresh(sortOrder);
- return true;
- }
+ } else if (item.getItemId() == R.id.episodes_sort) {
+ new AllEpisodesSortDialog().show(getChildFragmentManager().beginTransaction(), "SortDialog");
+ return true;
}
return false;
}
- private void saveSortOrderAndRefresh(SortOrder type) {
- prefs.edit().putString(PREF_SORT, "" + type.code).apply();
- loadItems();
- }
-
@Subscribe
public void onFilterChanged(AllEpisodesFilterDialog.AllEpisodesFilterChangedEvent event) {
- prefs.edit().putString(PREF_FILTER, StringUtils.join(event.filterValues, ",")).apply();
+ UserPreferences.setPrefFilterAllEpisodes(StringUtils.join(event.filterValues, ","));
updateFilterUi();
page = 1;
loadItems();
@@ -144,7 +123,25 @@ public class AllEpisodesFragment extends EpisodesListFragment {
getFilter().showIsFavorite ? R.drawable.ic_star : R.drawable.ic_star_border);
}
- private SortOrder getSortOrder() {
- return SortOrder.fromCodeString(prefs.getString(PREF_SORT, "" + SortOrder.DATE_NEW_OLD.code));
+ public static class AllEpisodesSortDialog extends ItemSortDialog {
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ sortOrder = UserPreferences.getAllEpisodesSortOrder();
+ }
+
+ @Override
+ protected void onAddItem(int title, SortOrder ascending, SortOrder descending, boolean ascendingIsDefault) {
+ if (ascending == SortOrder.DATE_OLD_NEW || ascending == SortOrder.DURATION_SHORT_LONG) {
+ super.onAddItem(title, ascending, descending, ascendingIsDefault);
+ }
+ }
+
+ @Override
+ protected void onSelectionChanged() {
+ super.onSelectionChanged();
+ UserPreferences.setAllEpisodesSortOrder(sortOrder);
+ EventBus.getDefault().post(new FeedListUpdateEvent(0));
+ }
}
}
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 f2a53ab7e..8b25c0e6a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java
@@ -15,25 +15,16 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.google.android.material.appbar.MaterialToolbar;
import androidx.cardview.widget.CardView;
import androidx.fragment.app.Fragment;
import androidx.interpolator.view.animation.FastOutSlowInInterpolator;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;
+import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.elevation.SurfaceColors;
-import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
-import de.danoeh.antennapod.core.util.playback.PlaybackController;
-import de.danoeh.antennapod.dialog.MediaPlayerErrorDialog;
-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;
@@ -44,23 +35,31 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.event.FavoritesEvent;
-import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
-import de.danoeh.antennapod.model.feed.Chapter;
-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.storage.preferences.UserPreferences;
+import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
-import de.danoeh.antennapod.model.playback.Playable;
-import de.danoeh.antennapod.dialog.PlaybackControlsDialog;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.dialog.MediaPlayerErrorDialog;
import de.danoeh.antennapod.dialog.SkipPreferenceDialog;
import de.danoeh.antennapod.dialog.SleepTimerDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
+import de.danoeh.antennapod.event.FavoritesEvent;
+import de.danoeh.antennapod.event.PlayerErrorEvent;
+import de.danoeh.antennapod.event.UnreadItemsUpdateEvent;
+import de.danoeh.antennapod.event.playback.BufferUpdateEvent;
+import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
+import de.danoeh.antennapod.event.playback.PlaybackServiceEvent;
+import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent;
+import de.danoeh.antennapod.event.playback.SpeedChangedEvent;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
+import de.danoeh.antennapod.model.feed.Chapter;
+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.cast.CastEnabledActivity;
+import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.ui.common.PlaybackSpeedIndicatorView;
import de.danoeh.antennapod.view.ChapterSeekBar;
import de.danoeh.antennapod.view.PlayButton;
@@ -503,10 +502,6 @@ public class AudioPlayerFragment extends Fragment implements
if (itemId == R.id.disable_sleeptimer_item || itemId == R.id.set_sleeptimer_item) {
new SleepTimerDialog().show(getChildFragmentManager(), "SleepTimerDialog");
return true;
- } else if (itemId == R.id.audio_controls) {
- PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance();
- dialog.show(getChildFragmentManager(), "playback_controls");
- return true;
} else if (itemId == R.id.open_feed_item) {
if (feedItem != null) {
Intent intent = MainActivity.getIntentToOpenFeed(getContext(), feedItem.getFeedId());
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 730a39189..ec7a35466 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -4,16 +4,14 @@ import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.google.android.material.appbar.MaterialToolbar;
import androidx.fragment.app.Fragment;
+import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.snackbar.Snackbar;
import com.leinardi.android.speeddial.SpeedDialView;
import de.danoeh.antennapod.R;
@@ -25,6 +23,7 @@ import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
+import de.danoeh.antennapod.dialog.ItemSortDialog;
import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.PlayerStatusEvent;
@@ -84,8 +83,6 @@ public class CompletedDownloadsFragment extends Fragment
toolbar = root.findViewById(R.id.toolbar);
toolbar.setTitle(R.string.downloads_label);
toolbar.inflateMenu(R.menu.downloads_completed);
- inflateSortMenu(toolbar);
-
toolbar.setOnMenuItemClickListener(this);
toolbar.setOnLongClickListener(v -> {
recyclerView.scrollToPosition(5);
@@ -148,20 +145,6 @@ public class CompletedDownloadsFragment extends Fragment
return root;
}
- private void inflateSortMenu(MaterialToolbar toolbar) {
- Menu menu = toolbar.getMenu();
- MenuItem downloadsItem = menu.findItem(R.id.downloads_sort);
- MenuInflater menuInflater = getActivity().getMenuInflater();
- menuInflater.inflate(R.menu.sort_menu, downloadsItem.getSubMenu());
-
- // Remove the sorting options that are not needed in this fragment
- menu.findItem(R.id.sort_feed_title).setVisible(false);
- menu.findItem(R.id.sort_random).setVisible(false);
- menu.findItem(R.id.sort_smart_shuffle).setVisible(false);
- menu.findItem(R.id.keep_sorted).setVisible(false);
- menu.findItem(R.id.sort_size).setVisible(true);
- }
-
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putBoolean(KEY_UP_ARROW, displayUpArrow);
@@ -204,21 +187,13 @@ public class CompletedDownloadsFragment extends Fragment
} else if (item.getItemId() == R.id.action_search) {
((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance());
return true;
- } else {
- SortOrder sortOrder = MenuItemToSortOrderConverter.convert(item);
- if (sortOrder != null) {
- setSortOrder(sortOrder);
- return true;
- }
+ } else if (item.getItemId() == R.id.downloads_sort) {
+ new DownloadsSortDialog().show(getChildFragmentManager(), "SortDialog");
+ return true;
}
return false;
}
- private void setSortOrder(SortOrder sortOrder) {
- UserPreferences.setDownloadsSortedOrder(sortOrder);
- loadItems();
- }
-
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(EpisodeDownloadEvent event) {
Set<String> newRunningDownloads = new HashSet<>();
@@ -391,4 +366,27 @@ public class CompletedDownloadsFragment extends Fragment
MenuItemUtils.setOnClickListeners(menu, CompletedDownloadsFragment.this::onContextItemSelected);
}
}
+
+ public static class DownloadsSortDialog extends ItemSortDialog {
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ sortOrder = UserPreferences.getDownloadsSortedOrder();
+ }
+
+ @Override
+ protected void onAddItem(int title, SortOrder ascending, SortOrder descending, boolean ascendingIsDefault) {
+ if (ascending == SortOrder.DATE_OLD_NEW || ascending == SortOrder.DURATION_SHORT_LONG
+ || ascending == SortOrder.EPISODE_TITLE_A_Z || ascending == SortOrder.SIZE_SMALL_LARGE) {
+ super.onAddItem(title, ascending, descending, ascendingIsDefault);
+ }
+ }
+
+ @Override
+ protected void onSelectionChanged() {
+ super.onSelectionChanged();
+ UserPreferences.setDownloadsSortedOrder(sortOrder);
+ EventBus.getDefault().post(DownloadLogEvent.listUpdated());
+ }
+ }
}
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 00d671d36..0cbc23a56 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -2,8 +2,6 @@ package de.danoeh.antennapod.fragment;
import android.content.DialogInterface;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
@@ -13,6 +11,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
+
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import androidx.core.util.Pair;
@@ -20,9 +19,19 @@ import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.snackbar.Snackbar;
import com.leinardi.android.speeddial.SpeedDialView;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
@@ -51,13 +60,6 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
/**
* Shows unread or recently published episodes
@@ -77,6 +79,7 @@ public abstract class EpisodesListFragment extends Fragment
EmptyViewHandler emptyView;
SpeedDialView speedDialView;
MaterialToolbar toolbar;
+ SwipeRefreshLayout swipeRefreshLayout;
SwipeActions swipeActions;
private ProgressBar progressBar;
@@ -180,13 +183,9 @@ public abstract class EpisodesListFragment extends Fragment
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
}
- SwipeRefreshLayout swipeRefreshLayout = root.findViewById(R.id.swipeRefresh);
+ swipeRefreshLayout = root.findViewById(R.id.swipeRefresh);
swipeRefreshLayout.setDistanceToTriggerSync(getResources().getInteger(R.integer.swipe_refresh_distance));
- swipeRefreshLayout.setOnRefreshListener(() -> {
- FeedUpdateManager.runOnceOrAsk(requireContext());
- new Handler(Looper.getMainLooper()).postDelayed(() -> swipeRefreshLayout.setRefreshing(false),
- getResources().getInteger(R.integer.swipe_to_refresh_duration_in_ms));
- });
+ swipeRefreshLayout.setOnRefreshListener(() -> FeedUpdateManager.runOnceOrAsk(requireContext()));
listAdapter = new EpisodeItemListAdapter((MainActivity) getActivity()) {
@Override
@@ -456,9 +455,7 @@ public abstract class EpisodesListFragment extends Fragment
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedUpdateRunningEvent event) {
- if (toolbar.getMenu().findItem(R.id.refresh_item) != null) {
- MenuItemUtils.updateRefreshMenuItem(toolbar.getMenu(), R.id.refresh_item, event.isFeedUpdateRunning);
- }
+ swipeRefreshLayout.setRefreshing(event.isFeedUpdateRunning);
}
@Override
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 b4fa77c75..f62bdaf84 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
@@ -22,25 +22,24 @@ import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import androidx.appcompat.content.res.AppCompatResources;
-import com.google.android.material.appbar.MaterialToolbar;
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.appbar.MaterialToolbar;
+import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
-import com.joanzapata.iconify.Iconify;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.dialog.EditUrlSettingsDialog;
-import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedFunding;
import de.danoeh.antennapod.ui.glide.FastBlurTransformation;
@@ -228,7 +227,8 @@ public class FeedInfoFragment extends Fragment implements MaterialToolbar.OnMenu
txtvAuthorHeader.setText(feed.getAuthor());
}
- txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}");
+ txtvUrl.setText(feed.getDownload_url());
+ txtvUrl.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_paperclip, 0);
if (feed.getPaymentLinks() == null || feed.getPaymentLinks().size() == 0) {
lblSupport.setVisibility(View.GONE);
@@ -263,7 +263,6 @@ public class FeedInfoFragment extends Fragment implements MaterialToolbar.OnMenu
txtvFundingUrl.setText(str.toString());
}
- Iconify.addIcons(txtvUrl);
refreshToolbarState();
}
@@ -290,9 +289,11 @@ public class FeedInfoFragment extends Fragment implements MaterialToolbar.OnMenu
R.string.please_wait_for_data, Toast.LENGTH_LONG);
return false;
}
- boolean handled = FeedMenuHandler.onOptionsItemClicked(getContext(), item, feed);
-
- if (item.getItemId() == R.id.reconnect_local_folder) {
+ if (item.getItemId() == R.id.visit_website_item) {
+ IntentUtils.openInBrowser(getContext(), feed.getLink());
+ } else if (item.getItemId() == R.id.share_item) {
+ ShareUtils.shareFeedLink(getContext(), feed);
+ } else if (item.getItemId() == R.id.reconnect_local_folder) {
MaterialAlertDialogBuilder alert = new MaterialAlertDialogBuilder(getContext());
alert.setMessage(R.string.reconnect_local_folder_warning);
alert.setPositiveButton(android.R.string.ok, (dialog, which) -> {
@@ -304,23 +305,19 @@ public class FeedInfoFragment extends Fragment implements MaterialToolbar.OnMenu
});
alert.setNegativeButton(android.R.string.cancel, null);
alert.show();
- return true;
- }
-
- if (item.getItemId() == R.id.edit_feed_url_item) {
+ } else if (item.getItemId() == R.id.edit_feed_url_item) {
new EditUrlSettingsDialog(getActivity(), feed) {
@Override
protected void setUrl(String url) {
feed.setDownload_url(url);
- txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}");
- Iconify.addIcons(txtvUrl);
+ txtvUrl.setText(feed.getDownload_url());
+ txtvUrl.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_paperclip, 0);
}
}.show();
-
- return true;
+ } else {
+ return false;
}
-
- return handled;
+ return true;
}
private void addLocalFolderResult(final Uri uri) {
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 1c949218a..8020235b9 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -4,8 +4,6 @@ import android.content.Context;
import android.content.res.Configuration;
import android.graphics.LightingColorFilter;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
@@ -15,31 +13,48 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Toast;
+
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
-import com.google.android.material.appbar.MaterialToolbar;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
+
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
+import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.snackbar.Snackbar;
import com.joanzapata.iconify.Iconify;
import com.leinardi.android.speeddial.SpeedDialView;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.FeedItemUtil;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
import de.danoeh.antennapod.core.util.gui.MoreContentListFooterUtil;
import de.danoeh.antennapod.databinding.FeedItemListFragmentBinding;
import de.danoeh.antennapod.databinding.MultiSelectSpeedDialBinding;
import de.danoeh.antennapod.dialog.DownloadLogDetailsDialog;
import de.danoeh.antennapod.dialog.FeedItemFilterDialog;
+import de.danoeh.antennapod.dialog.ItemSortDialog;
import de.danoeh.antennapod.dialog.RemoveFeedDialog;
import de.danoeh.antennapod.dialog.RenameItemDialog;
import de.danoeh.antennapod.event.EpisodeDownloadEvent;
@@ -54,11 +69,11 @@ import de.danoeh.antennapod.event.playback.PlaybackPositionEvent;
import de.danoeh.antennapod.fragment.actions.EpisodeMultiSelectActionHandler;
import de.danoeh.antennapod.fragment.swipeactions.SwipeActions;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
-import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.model.download.DownloadResult;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
+import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.ui.glide.FastBlurTransformation;
import de.danoeh.antennapod.view.ToolbarIconTintManager;
@@ -68,13 +83,6 @@ 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.Validate;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.util.Collections;
-import java.util.List;
/**
* Displays a list of FeedItems.
@@ -183,11 +191,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
EventBus.getDefault().register(this);
viewBinding.swipeRefresh.setDistanceToTriggerSync(getResources().getInteger(R.integer.swipe_refresh_distance));
- viewBinding.swipeRefresh.setOnRefreshListener(() -> {
- FeedUpdateManager.runOnceOrAsk(requireContext(), feed);
- new Handler(Looper.getMainLooper()).postDelayed(() -> viewBinding.swipeRefresh.setRefreshing(false),
- getResources().getInteger(R.integer.swipe_to_refresh_duration_in_ms));
- });
+ viewBinding.swipeRefresh.setOnRefreshListener(() -> FeedUpdateManager.runOnceOrAsk(requireContext(), feed));
loadItems();
@@ -240,8 +244,13 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
return;
}
viewBinding.toolbar.getMenu().findItem(R.id.visit_website_item).setVisible(feed.getLink() != null);
-
- FeedMenuHandler.onPrepareOptionsMenu(viewBinding.toolbar.getMenu(), feed);
+ viewBinding.toolbar.getMenu().findItem(R.id.refresh_complete_item).setVisible(feed.isPaged());
+ if (StringUtils.isBlank(feed.getLink())) {
+ viewBinding.toolbar.getMenu().findItem(R.id.visit_website_item).setVisible(false);
+ }
+ if (feed.isLocalFeed()) {
+ viewBinding.toolbar.getMenu().findItem(R.id.share_item).setVisible(false);
+ }
}
@Override
@@ -260,26 +269,39 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
R.string.please_wait_for_data, Toast.LENGTH_LONG);
return true;
}
- boolean feedMenuHandled = FeedMenuHandler.onOptionsItemClicked(getActivity(), item, feed);
- if (feedMenuHandled) {
- return true;
- }
- final int itemId = item.getItemId();
- if (itemId == R.id.rename_item) {
+ if (item.getItemId() == R.id.visit_website_item) {
+ IntentUtils.openInBrowser(getContext(), feed.getLink());
+ } else if (item.getItemId() == R.id.share_item) {
+ ShareUtils.shareFeedLink(getContext(), feed);
+ } else if (item.getItemId() == R.id.refresh_item) {
+ FeedUpdateManager.runOnceOrAsk(getContext(), feed);
+ } else if (item.getItemId() == R.id.refresh_complete_item) {
+ new Thread(() -> {
+ feed.setNextPageLink(feed.getDownload_url());
+ feed.setPageNr(0);
+ try {
+ DBWriter.resetPagedFeedPage(feed).get();
+ FeedUpdateManager.runOnce(getContext(), feed);
+ } catch (ExecutionException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }).start();
+ } else if (item.getItemId() == R.id.sort_items) {
+ SingleFeedSortDialog.newInstance(feed).show(getChildFragmentManager(), "SortDialog");
+ } else if (item.getItemId() == R.id.rename_item) {
new RenameItemDialog(getActivity(), feed).show();
- return true;
- } else if (itemId == R.id.remove_feed) {
+ } else if (item.getItemId() == R.id.remove_feed) {
RemoveFeedDialog.show(getContext(), feed, () -> {
((MainActivity) getActivity()).loadFragment(UserPreferences.getDefaultPage(), null);
// Make sure fragment is hidden before actually starting to delete
getActivity().getSupportFragmentManager().executePendingTransactions();
});
- return true;
- } else if (itemId == R.id.action_search) {
+ } else if (item.getItemId() == R.id.action_search) {
((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance(feed.getId(), feed.getTitle()));
- return true;
+ } else {
+ return false;
}
- return false;
+ return true;
}
@Override
@@ -407,8 +429,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
if (!event.isFeedUpdateRunning) {
nextPageLoader.getRoot().setVisibility(View.GONE);
}
- MenuItemUtils.updateRefreshMenuItem(viewBinding.toolbar.getMenu(),
- R.id.refresh_item, event.isFeedUpdateRunning);
+ viewBinding.swipeRefresh.setRefreshing(event.isFeedUpdateRunning);
}
private void refreshHeaderView() {
@@ -592,4 +613,45 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem
MenuItemUtils.setOnClickListeners(menu, FeedItemlistFragment.this::onContextItemSelected);
}
}
+
+ public static class SingleFeedSortDialog extends ItemSortDialog {
+ private static final String ARG_FEED_ID = "feedId";
+ private static final String ARG_FEED_IS_LOCAL = "isLocal";
+ private static final String ARG_SORT_ORDER = "sortOrder";
+
+ private static SingleFeedSortDialog newInstance(Feed feed) {
+ Bundle bundle = new Bundle();
+ bundle.putLong(ARG_FEED_ID, feed.getId());
+ bundle.putBoolean(ARG_FEED_IS_LOCAL, feed.isLocalFeed());
+ if (feed.getSortOrder() == null) {
+ bundle.putString(ARG_SORT_ORDER, String.valueOf(SortOrder.DATE_NEW_OLD.code));
+ } else {
+ bundle.putString(ARG_SORT_ORDER, String.valueOf(feed.getSortOrder().code));
+ }
+ SingleFeedSortDialog dialog = new SingleFeedSortDialog();
+ dialog.setArguments(bundle);
+ return dialog;
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ sortOrder = SortOrder.fromCodeString(getArguments().getString(ARG_SORT_ORDER));
+ }
+
+ @Override
+ protected void onAddItem(int title, SortOrder ascending, SortOrder descending, boolean ascendingIsDefault) {
+ if (ascending == SortOrder.DATE_OLD_NEW || ascending == SortOrder.DURATION_SHORT_LONG
+ || ascending == SortOrder.EPISODE_TITLE_A_Z
+ || (getArguments().getBoolean(ARG_FEED_IS_LOCAL) && ascending == SortOrder.EPISODE_FILENAME_A_Z)) {
+ super.onAddItem(title, ascending, descending, ascendingIsDefault);
+ }
+ }
+
+ @Override
+ protected void onSelectionChanged() {
+ super.onSelectionChanged();
+ DBWriter.setFeedItemSortOrder(getArguments().getLong(ARG_FEED_ID), sortOrder);
+ }
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java
index f8bcbb532..497409e70 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/InboxFragment.java
@@ -4,26 +4,25 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.Toast;
-
import androidx.annotation.NonNull;
-
+import androidx.annotation.Nullable;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.dialog.ItemSortDialog;
+import de.danoeh.antennapod.event.FeedListUpdateEvent;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
+import org.greenrobot.eventbus.EventBus;
import java.util.List;
@@ -42,8 +41,6 @@ public class InboxFragment extends EpisodesListFragment {
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View root = super.onCreateView(inflater, container, savedInstanceState);
toolbar.inflateMenu(R.menu.inbox);
- inflateSortMenu();
-
toolbar.setTitle(R.string.inbox_label);
prefs = getActivity().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
updateToolbar();
@@ -83,13 +80,9 @@ public class InboxFragment extends EpisodesListFragment {
showRemoveAllDialog();
}
return true;
- } else {
- SortOrder sortOrder = MenuItemToSortOrderConverter.convert(item);
- if (sortOrder != null) {
- UserPreferences.setInboxSortedOrder(sortOrder);
- loadItems();
- return true;
- }
+ } else if (item.getItemId() == R.id.inbox_sort) {
+ new InboxSortDialog().show(getChildFragmentManager(), "SortDialog");
+ return true;
}
return false;
}
@@ -118,20 +111,6 @@ public class InboxFragment extends EpisodesListFragment {
((MainActivity) getActivity()).showSnackbarAbovePlayer(R.string.removed_all_inbox_msg, Toast.LENGTH_SHORT);
}
- private void inflateSortMenu() {
- Menu menu = toolbar.getMenu();
- MenuItem downloadsItem = menu.findItem(R.id.inbox_sort);
- MenuInflater menuInflater = getActivity().getMenuInflater();
- menuInflater.inflate(R.menu.sort_menu, downloadsItem.getSubMenu());
-
- // Remove the sorting options that are not needed in this fragment
- toolbar.getMenu().findItem(R.id.sort_episode_title).setVisible(false);
- toolbar.getMenu().findItem(R.id.sort_feed_title).setVisible(false);
- toolbar.getMenu().findItem(R.id.sort_random).setVisible(false);
- toolbar.getMenu().findItem(R.id.sort_smart_shuffle).setVisible(false);
- toolbar.getMenu().findItem(R.id.keep_sorted).setVisible(false);
- }
-
private void showRemoveAllDialog() {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext());
builder.setTitle(R.string.remove_all_inbox_label);
@@ -149,4 +128,26 @@ public class InboxFragment extends EpisodesListFragment {
builder.setNegativeButton(R.string.cancel_label, null);
builder.show();
}
+
+ public static class InboxSortDialog extends ItemSortDialog {
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ sortOrder = UserPreferences.getInboxSortedOrder();
+ }
+
+ @Override
+ protected void onAddItem(int title, SortOrder ascending, SortOrder descending, boolean ascendingIsDefault) {
+ if (ascending == SortOrder.DATE_OLD_NEW || ascending == SortOrder.DURATION_SHORT_LONG) {
+ super.onAddItem(title, ascending, descending, ascendingIsDefault);
+ }
+ }
+
+ @Override
+ protected void onSelectionChanged() {
+ super.onSelectionChanged();
+ UserPreferences.setInboxSortedOrder(sortOrder);
+ EventBus.getDefault().post(new FeedListUpdateEvent(0));
+ }
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/MenuItemToSortOrderConverter.java b/app/src/main/java/de/danoeh/antennapod/fragment/MenuItemToSortOrderConverter.java
deleted file mode 100644
index d4150fbdb..000000000
--- a/app/src/main/java/de/danoeh/antennapod/fragment/MenuItemToSortOrderConverter.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package de.danoeh.antennapod.fragment;
-
-import android.view.MenuItem;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.model.feed.SortOrder;
-
-public class MenuItemToSortOrderConverter {
-
- public static SortOrder convert(MenuItem item) {
- final int itemId = item.getItemId();
-
- if (itemId == R.id.sort_episode_title_asc) {
- return SortOrder.EPISODE_TITLE_A_Z;
- } else if (itemId == R.id.sort_episode_title_desc) {
- return SortOrder.EPISODE_TITLE_Z_A;
- } else if (itemId == R.id.sort_date_asc) {
- return SortOrder.DATE_OLD_NEW;
- } else if (itemId == R.id.sort_date_desc) {
- return SortOrder.DATE_NEW_OLD;
- } else if (itemId == R.id.sort_duration_asc) {
- return SortOrder.DURATION_SHORT_LONG;
- } else if (itemId == R.id.sort_duration_desc) {
- return SortOrder.DURATION_LONG_SHORT;
- } else if (itemId == R.id.sort_feed_title_asc) {
- return SortOrder.FEED_TITLE_A_Z;
- } else if (itemId == R.id.sort_feed_title_desc) {
- return SortOrder.FEED_TITLE_Z_A;
- } else if (itemId == R.id.sort_random) {
- return SortOrder.RANDOM;
- } else if (itemId == R.id.sort_smart_shuffle_asc) {
- return SortOrder.SMART_SHUFFLE_OLD_NEW;
- } else if (itemId == R.id.sort_smart_shuffle_desc) {
- return SortOrder.SMART_SHUFFLE_NEW_OLD;
- } else if (itemId == R.id.sort_size_small_large) {
- return SortOrder.SIZE_SMALL_LARGE;
- } else if (itemId == R.id.sort_size_large_small) {
- return SortOrder.SIZE_LARGE_SMALL;
- }
-
- return null;
- }
-
-}
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 eeca181cf..636c0245b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java
@@ -382,7 +382,7 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS
}
} else if (UserPreferences.getSubscriptionsFilter().isEnabled()
&& navAdapter.showSubscriptionList) {
- SubscriptionsFilterDialog.showDialog(requireContext());
+ new SubscriptionsFilterDialog().show(getChildFragmentManager(), "filter");
}
}
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 80933023e..003ee23db 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -4,29 +4,36 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.LayoutInflater;
-import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.TextView;
+
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+
import com.google.android.material.appbar.MaterialToolbar;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
import com.leinardi.android.speeddial.SpeedDialView;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.List;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.EpisodeItemListAdapter;
@@ -39,6 +46,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
+import de.danoeh.antennapod.dialog.ItemSortDialog;
import de.danoeh.antennapod.event.EpisodeDownloadEvent;
import de.danoeh.antennapod.event.FeedItemEvent;
import de.danoeh.antennapod.event.FeedUpdateRunningEvent;
@@ -61,12 +69,6 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.util.List;
-import java.util.Locale;
/**
* Shows all items in the queue.
@@ -81,6 +83,7 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte
private QueueRecyclerAdapter recyclerAdapter;
private EmptyViewHandler emptyView;
private MaterialToolbar toolbar;
+ private SwipeRefreshLayout swipeRefreshLayout;
private boolean displayUpArrow;
private List<FeedItem> queue;
@@ -159,6 +162,8 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte
case MOVED:
return;
}
+ recyclerAdapter.updateDragDropEnabled();
+ refreshToolbarState();
recyclerView.saveScrollPosition(QueueFragment.TAG);
refreshInfoBar();
}
@@ -258,13 +263,11 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte
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.sort_random).setVisible(!keepSorted);
- toolbar.getMenu().findItem(R.id.keep_sorted).setChecked(keepSorted);
}
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedUpdateRunningEvent event) {
- MenuItemUtils.updateRefreshMenuItem(toolbar.getMenu(), R.id.refresh_item, event.isFeedUpdateRunning);
+ swipeRefreshLayout.setRefreshing(event.isFeedUpdateRunning);
}
@Override
@@ -273,6 +276,9 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte
if (itemId == R.id.queue_lock) {
toggleQueueLock();
return true;
+ } else if (itemId == R.id.queue_sort) {
+ new QueueSortDialog().show(getChildFragmentManager().beginTransaction(), "SortDialog");
+ return true;
} else if (itemId == R.id.refresh_item) {
FeedUpdateManager.runOnceOrAsk(requireContext());
return true;
@@ -291,28 +297,9 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte
};
conDialog.createNewDialog().show();
return true;
- } else if (itemId == R.id.keep_sorted) {
- boolean keepSortedOld = UserPreferences.isQueueKeepSorted();
- boolean keepSortedNew = !keepSortedOld;
- UserPreferences.setQueueKeepSorted(keepSortedNew);
- if (keepSortedNew) {
- SortOrder sortOrder = UserPreferences.getQueueKeepSortedOrder();
- DBWriter.reorderQueue(sortOrder, true);
- }
- if (recyclerAdapter != null) {
- recyclerAdapter.updateDragDropEnabled();
- }
- refreshToolbarState();
- return true;
} else if (itemId == R.id.action_search) {
((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance());
return true;
- } else {
- SortOrder sortOrder = MenuItemToSortOrderConverter.convert(item);
- if (sortOrder != null) {
- setSortOrder(sortOrder);
- return true;
- }
}
return false;
}
@@ -359,16 +346,6 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte
}
}
- /**
- * This method is called if the user clicks on a sort order menu item.
- *
- * @param sortOrder New sort order.
- */
- private void setSortOrder(SortOrder sortOrder) {
- UserPreferences.setQueueKeepSortedOrder(sortOrder);
- DBWriter.reorderQueue(sortOrder, true);
- }
-
@Override
public boolean onContextItemSelected(MenuItem item) {
Log.d(TAG, "onContextItemSelected() called with: " + "item = [" + item + "]");
@@ -422,10 +399,6 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte
}
((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow);
toolbar.inflateMenu(R.menu.queue);
-
- MenuItem queueItem = toolbar.getMenu().findItem(R.id.queue_sort);
- MenuInflater menuInflater = getActivity().getMenuInflater();
- menuInflater.inflate(R.menu.sort_menu, queueItem.getSubMenu());
refreshToolbarState();
progressBar = root.findViewById(R.id.progressBar);
progressBar.setVisibility(View.VISIBLE);
@@ -454,13 +427,9 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte
recyclerAdapter.setOnSelectModeListener(this);
recyclerView.setAdapter(recyclerAdapter);
- SwipeRefreshLayout swipeRefreshLayout = root.findViewById(R.id.swipeRefresh);
+ swipeRefreshLayout = root.findViewById(R.id.swipeRefresh);
swipeRefreshLayout.setDistanceToTriggerSync(getResources().getInteger(R.integer.swipe_refresh_distance));
- swipeRefreshLayout.setOnRefreshListener(() -> {
- FeedUpdateManager.runOnceOrAsk(requireContext());
- new Handler(Looper.getMainLooper()).postDelayed(() -> swipeRefreshLayout.setRefreshing(false),
- getResources().getInteger(R.integer.swipe_to_refresh_duration_in_ms));
- });
+ swipeRefreshLayout.setOnRefreshListener(() -> FeedUpdateManager.runOnceOrAsk(requireContext()));
emptyView = new EmptyViewHandler(getContext());
emptyView.attachToRecyclerView(recyclerView);
@@ -507,9 +476,8 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte
}
private void refreshInfoBar() {
- String info = String.format(Locale.getDefault(), "%d%s",
- queue.size(), getString(R.string.episodes_suffix));
- if (queue.size() > 0) {
+ String info = getResources().getQuantityString(R.plurals.num_episodes, queue.size(), queue.size());
+ if (!queue.isEmpty()) {
long timeLeft = 0;
for (FeedItem item : queue) {
float playbackSpeed = 1;
@@ -567,6 +535,42 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte
swipeActions.attachTo(recyclerView);
}
+ public static class QueueSortDialog extends ItemSortDialog {
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater,
+ @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ if (UserPreferences.isQueueKeepSorted()) {
+ sortOrder = UserPreferences.getQueueKeepSortedOrder();
+ }
+ final View view = super.onCreateView(inflater, container, savedInstanceState);
+ viewBinding.keepSortedCheckbox.setVisibility(View.VISIBLE);
+ viewBinding.keepSortedCheckbox.setChecked(UserPreferences.isQueueKeepSorted());
+ // Disable until something gets selected
+ viewBinding.keepSortedCheckbox.setEnabled(UserPreferences.isQueueKeepSorted());
+ return view;
+ }
+
+ @Override
+ protected void onAddItem(int title, SortOrder ascending, SortOrder descending, boolean ascendingIsDefault) {
+ if (ascending != SortOrder.EPISODE_FILENAME_A_Z && ascending != SortOrder.SIZE_SMALL_LARGE) {
+ super.onAddItem(title, ascending, descending, ascendingIsDefault);
+ }
+ }
+
+ @Override
+ protected void onSelectionChanged() {
+ super.onSelectionChanged();
+ viewBinding.keepSortedCheckbox.setEnabled(sortOrder != SortOrder.RANDOM);
+ if (sortOrder == SortOrder.RANDOM) {
+ viewBinding.keepSortedCheckbox.setChecked(false);
+ }
+ UserPreferences.setQueueKeepSorted(viewBinding.keepSortedCheckbox.isChecked());
+ UserPreferences.setQueueKeepSortedOrder(sortOrder);
+ DBWriter.reorderQueue(sortOrder, true);
+ }
+ }
+
private class QueueSwipeActions extends SwipeActions {
// Position tracking whilst dragging
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 93ed4c2c9..e86cd58b9 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -3,8 +3,6 @@ package de.danoeh.antennapod.fragment;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@@ -81,6 +79,7 @@ public class SubscriptionFragment extends Fragment
private EmptyViewHandler emptyView;
private LinearLayout feedsFilteredMsg;
private MaterialToolbar toolbar;
+ private SwipeRefreshLayout swipeRefreshLayout;
private ProgressBar progressBar;
private String displayedFolder = null;
private boolean displayUpArrow;
@@ -166,15 +165,12 @@ public class SubscriptionFragment extends Fragment
});
feedsFilteredMsg = root.findViewById(R.id.feeds_filtered_message);
- feedsFilteredMsg.setOnClickListener((l) -> SubscriptionsFilterDialog.showDialog(requireContext()));
+ feedsFilteredMsg.setOnClickListener((l) ->
+ new SubscriptionsFilterDialog().show(getChildFragmentManager(), "filter"));
- SwipeRefreshLayout swipeRefreshLayout = root.findViewById(R.id.swipeRefresh);
+ swipeRefreshLayout = root.findViewById(R.id.swipeRefresh);
swipeRefreshLayout.setDistanceToTriggerSync(getResources().getInteger(R.integer.swipe_refresh_distance));
- swipeRefreshLayout.setOnRefreshListener(() -> {
- FeedUpdateManager.runOnceOrAsk(requireContext());
- new Handler(Looper.getMainLooper()).postDelayed(() -> swipeRefreshLayout.setRefreshing(false),
- getResources().getInteger(R.integer.swipe_to_refresh_duration_in_ms));
- });
+ swipeRefreshLayout.setOnRefreshListener(() -> FeedUpdateManager.runOnceOrAsk(requireContext()));
speedDialView = root.findViewById(R.id.fabSD);
speedDialView.setOverlayLayout(root.findViewById(R.id.fabSDOverlay));
@@ -211,7 +207,7 @@ public class SubscriptionFragment extends Fragment
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedUpdateRunningEvent event) {
- MenuItemUtils.updateRefreshMenuItem(toolbar.getMenu(), R.id.refresh_item, event.isFeedUpdateRunning);
+ swipeRefreshLayout.setRefreshing(event.isFeedUpdateRunning);
}
@Override
@@ -221,7 +217,7 @@ public class SubscriptionFragment extends Fragment
FeedUpdateManager.runOnceOrAsk(requireContext());
return true;
} else if (itemId == R.id.subscriptions_filter) {
- SubscriptionsFilterDialog.showDialog(requireContext());
+ new SubscriptionsFilterDialog().show(getChildFragmentManager(), "filter");
return true;
} else if (itemId == R.id.subscriptions_sort) {
FeedSortDialog.showDialog(requireContext());
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 66f592af2..0f3320e98 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
@@ -1,8 +1,10 @@
package de.danoeh.antennapod.fragment.preferences;
import android.content.Context;
+import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
+import android.widget.Button;
import android.widget.ListView;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
@@ -64,14 +66,14 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat {
return true;
});
- findPreference(UserPreferences.PREF_COMPACT_NOTIFICATION_BUTTONS)
+ findPreference(UserPreferences.PREF_FULL_NOTIFICATION_BUTTONS)
.setOnPreferenceClickListener(preference -> {
- showNotificationButtonsDialog();
+ showFullNotificationButtonsDialog();
return true;
});
findPreference(UserPreferences.PREF_FILTER_FEED)
.setOnPreferenceClickListener((preference -> {
- SubscriptionsFilterDialog.showDialog(requireContext());
+ new SubscriptionsFilterDialog().show(getChildFragmentManager(), "filter");
return true;
}));
@@ -91,48 +93,85 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat {
}
}
- private void showNotificationButtonsDialog() {
+
+ private void showFullNotificationButtonsDialog() {
final Context context = getActivity();
- final List<Integer> preferredButtons = UserPreferences.getCompactNotificationButtons();
+
+ final List<Integer> preferredButtons = UserPreferences.getFullNotificationButtons();
final String[] allButtonNames = context.getResources().getStringArray(
- R.array.compact_notification_buttons_options);
+ R.array.full_notification_buttons_options);
+ final int[] buttonIDs = {2, 3, 4};
+ final int exactItems = 2;
+ final DialogInterface.OnClickListener completeListener = (dialog, which) ->
+ UserPreferences.setFullNotificationButtons(preferredButtons);
+ final String title = context.getResources().getString(
+ R.string.pref_full_notification_buttons_title);
+
+ showNotificationButtonsDialog(preferredButtons, allButtonNames, buttonIDs, title,
+ exactItems, completeListener
+ );
+ }
+
+ private void showNotificationButtonsDialog(List<Integer> preferredButtons,
+ String[] allButtonNames, int[] buttonIds, String title,
+ int exactItems, DialogInterface.OnClickListener completeListener) {
boolean[] checked = new boolean[allButtonNames.length]; // booleans default to false in java
+ final Context context = getActivity();
+
+ // Clear buttons that are not part of the setting anymore
+ for (int i = preferredButtons.size() - 1; i >= 0; i--) {
+ boolean isValid = false;
+ for (int j = 0; j < checked.length; j++) {
+ if (buttonIds[j] == preferredButtons.get(i)) {
+ isValid = true;
+ }
+ }
+
+ if (!isValid) {
+ preferredButtons.remove(i);
+ }
+ }
+
for(int i=0; i < checked.length; i++) {
- if(preferredButtons.contains(i)) {
+ if (preferredButtons.contains(buttonIds[i])) {
checked[i] = true;
}
}
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context);
- builder.setTitle(String.format(context.getResources().getString(
- R.string.pref_compact_notification_buttons_dialog_title), 2));
+ builder.setTitle(title);
builder.setMultiChoiceItems(allButtonNames, checked, (dialog, which, isChecked) -> {
checked[which] = isChecked;
if (isChecked) {
- if (preferredButtons.size() < 2) {
- preferredButtons.add(which);
- } else {
- // Only allow a maximum of two selections. This is because the notification
- // on the lock screen can only display 3 buttons, and the play/pause button
- // is always included.
- checked[which] = false;
- ListView selectionView = ((AlertDialog) dialog).getListView();
- selectionView.setItemChecked(which, false);
- Snackbar.make(
- selectionView,
- String.format(context.getResources().getString(
- R.string.pref_compact_notification_buttons_dialog_error), 2),
- Snackbar.LENGTH_SHORT).show();
- }
+ preferredButtons.add(buttonIds[which]);
} else {
- preferredButtons.remove((Integer) which);
+ preferredButtons.remove((Integer) buttonIds[which]);
}
});
- builder.setPositiveButton(R.string.confirm_label, (dialog, which) ->
- UserPreferences.setCompactNotificationButtons(preferredButtons));
+ builder.setPositiveButton(R.string.confirm_label, null);
builder.setNegativeButton(R.string.cancel_label, null);
- builder.create().show();
+ final AlertDialog dialog = builder.create();
+
+ dialog.show();
+
+ Button positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
+
+ positiveButton.setOnClickListener(v -> {
+ if (preferredButtons.size() != exactItems) {
+ ListView selectionView = dialog.getListView();
+ Snackbar.make(
+ selectionView,
+ String.format(context.getResources().getString(
+ R.string.pref_compact_notification_buttons_dialog_error_exact), exactItems),
+ Snackbar.LENGTH_SHORT).show();
+
+ } else {
+ completeListener.onClick(dialog, AlertDialog.BUTTON_POSITIVE);
+ dialog.cancel();
+ }
+ }
+ );
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
index 7a9eb1bb5..eac1e9304 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -1,103 +1,32 @@
package de.danoeh.antennapod.menuhandler;
+import android.annotation.SuppressLint;
import android.content.Context;
+import android.content.DialogInterface;
import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
import androidx.annotation.NonNull;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.util.IntentUtils;
-import de.danoeh.antennapod.core.util.ShareUtils;
-import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
-import de.danoeh.antennapod.dialog.IntraFeedSortDialog;
-import de.danoeh.antennapod.model.feed.Feed;
-import de.danoeh.antennapod.model.feed.SortOrder;
-import org.apache.commons.lang3.StringUtils;
-import android.content.DialogInterface;
-import android.annotation.SuppressLint;
import androidx.fragment.app.Fragment;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.dialog.RemoveFeedDialog;
import de.danoeh.antennapod.dialog.RenameItemDialog;
import de.danoeh.antennapod.dialog.TagSettingsDialog;
+import de.danoeh.antennapod.model.feed.Feed;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
+
import java.util.Collections;
import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* Handles interactions with the FeedItemMenu.
*/
-public class FeedMenuHandler {
-
- private FeedMenuHandler(){ }
-
+public abstract class FeedMenuHandler {
private static final String TAG = "FeedMenuHandler";
- public static boolean onPrepareOptionsMenu(Menu menu, Feed selectedFeed) {
- if (selectedFeed == null) {
- return true;
- }
-
- Log.d(TAG, "Preparing options menu");
-
- menu.findItem(R.id.refresh_complete_item).setVisible(selectedFeed.isPaged());
- if (StringUtils.isBlank(selectedFeed.getLink())) {
- menu.findItem(R.id.visit_website_item).setVisible(false);
- }
- if (selectedFeed.isLocalFeed()) {
- // hide complete submenu "Share..." as both sub menu items are not visible
- menu.findItem(R.id.share_item).setVisible(false);
- }
-
- return true;
- }
-
- /**
- * NOTE: This method does not handle clicks on the 'remove feed' - item.
- */
- public static boolean onOptionsItemClicked(final Context context, final MenuItem item, final Feed selectedFeed) {
- final int itemId = item.getItemId();
- if (itemId == R.id.refresh_item) {
- FeedUpdateManager.runOnceOrAsk(context, selectedFeed);
- } else if (itemId == R.id.refresh_complete_item) {
- new Thread(() -> {
- selectedFeed.setNextPageLink(selectedFeed.getDownload_url());
- selectedFeed.setPageNr(0);
- try {
- DBWriter.resetPagedFeedPage(selectedFeed).get();
- FeedUpdateManager.runOnce(context, selectedFeed);
- } catch (ExecutionException | InterruptedException e) {
- throw new RuntimeException(e);
- }
- }).start();
- } else if (itemId == R.id.sort_items) {
- showSortDialog(context, selectedFeed);
- } else if (itemId == R.id.visit_website_item) {
- IntentUtils.openInBrowser(context, selectedFeed.getLink());
- } else if (itemId == R.id.share_item) {
- ShareUtils.shareFeedLink(context, selectedFeed);
- } else {
- return false;
- }
- return true;
- }
-
- private static void showSortDialog(Context context, Feed selectedFeed) {
- IntraFeedSortDialog sortDialog = new IntraFeedSortDialog(context, selectedFeed.getSortOrder(), selectedFeed.isLocalFeed()) {
- @Override
- protected void updateSort(@NonNull SortOrder sortOrder) {
- selectedFeed.setSortOrder(sortOrder);
- DBWriter.setFeedItemSortOrder(selectedFeed.getId(), sortOrder);
- }
- };
- sortDialog.openDialog();
- }
-
public static boolean onMenuItemClicked(@NonNull Fragment fragment, int menuItemId,
@NonNull Feed selectedFeed, Runnable callback) {
@NonNull Context context = fragment.requireContext();
@@ -131,5 +60,4 @@ public class FeedMenuHandler {
}
return true;
}
-
}
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 cc298b38d..3a3063599 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
@@ -6,12 +6,15 @@ import android.view.KeyEvent;
import androidx.core.app.NotificationManagerCompat;
import androidx.preference.PreferenceManager;
+import org.apache.commons.lang3.StringUtils;
+
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
import de.danoeh.antennapod.error.CrashReportWriter;
+import de.danoeh.antennapod.fragment.AllEpisodesFragment;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
import de.danoeh.antennapod.storage.preferences.UserPreferences.EnqueueLocation;
import de.danoeh.antennapod.fragment.QueueFragment;
@@ -149,5 +152,19 @@ public class PreferenceUpgrader {
if (oldVersion < 3020000) {
NotificationManagerCompat.from(context).deleteNotificationChannel("auto_download");
}
+
+ if (oldVersion < 3030000) {
+ SharedPreferences allEpisodesPreferences =
+ context.getSharedPreferences(AllEpisodesFragment.PREF_NAME, Context.MODE_PRIVATE);
+ String oldEpisodeSort = allEpisodesPreferences.getString(UserPreferences.PREF_SORT_ALL_EPISODES, "");
+ if (!StringUtils.isAllEmpty(oldEpisodeSort)) {
+ prefs.edit().putString(UserPreferences.PREF_SORT_ALL_EPISODES, oldEpisodeSort).apply();
+ }
+
+ String oldEpisodeFilter = allEpisodesPreferences.getString("filter", "");
+ if (!StringUtils.isAllEmpty(oldEpisodeFilter)) {
+ prefs.edit().putString(UserPreferences.PREF_FILTER_ALL_EPISODES, oldEpisodeFilter).apply();
+ }
+ }
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/home/HomeFragment.java b/app/src/main/java/de/danoeh/antennapod/ui/home/HomeFragment.java
index fc925aa03..6f43a8ff4 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/home/HomeFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/home/HomeFragment.java
@@ -6,8 +6,6 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
@@ -21,7 +19,6 @@ import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentContainerView;
-import de.danoeh.antennapod.ui.home.sections.EchoSection;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -33,7 +30,6 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.download.FeedUpdateManager;
import de.danoeh.antennapod.databinding.HomeFragmentBinding;
@@ -41,8 +37,10 @@ import de.danoeh.antennapod.event.FeedListUpdateEvent;
import de.danoeh.antennapod.event.FeedUpdateRunningEvent;
import de.danoeh.antennapod.fragment.SearchFragment;
import de.danoeh.antennapod.storage.preferences.UserPreferences;
+import de.danoeh.antennapod.ui.echo.EchoActivity;
import de.danoeh.antennapod.ui.home.sections.AllowNotificationsSection;
import de.danoeh.antennapod.ui.home.sections.DownloadsSection;
+import de.danoeh.antennapod.ui.home.sections.EchoSection;
import de.danoeh.antennapod.ui.home.sections.EpisodesSurpriseSection;
import de.danoeh.antennapod.ui.home.sections.InboxSection;
import de.danoeh.antennapod.ui.home.sections.QueueSection;
@@ -85,11 +83,7 @@ public class HomeFragment extends Fragment implements Toolbar.OnMenuItemClickLis
updateWelcomeScreenVisibility();
viewBinding.swipeRefresh.setDistanceToTriggerSync(getResources().getInteger(R.integer.swipe_refresh_distance));
- viewBinding.swipeRefresh.setOnRefreshListener(() -> {
- FeedUpdateManager.runOnceOrAsk(requireContext());
- new Handler(Looper.getMainLooper()).postDelayed(() -> viewBinding.swipeRefresh.setRefreshing(false),
- getResources().getInteger(R.integer.swipe_to_refresh_duration_in_ms));
- });
+ viewBinding.swipeRefresh.setOnRefreshListener(() -> FeedUpdateManager.runOnceOrAsk(requireContext()));
return viewBinding.getRoot();
}
@@ -104,10 +98,10 @@ public class HomeFragment extends Fragment implements Toolbar.OnMenuItemClickLis
addSection(new AllowNotificationsSection());
}
}
- if (Calendar.getInstance().get(Calendar.MONTH) == Calendar.DECEMBER
- && Calendar.getInstance().get(Calendar.YEAR) == 2023
+ if (Calendar.getInstance().get(Calendar.YEAR) == EchoActivity.RELEASE_YEAR
+ && Calendar.getInstance().get(Calendar.MONTH) == Calendar.DECEMBER
&& Calendar.getInstance().get(Calendar.DAY_OF_MONTH) >= 10
- && prefs.getInt(PREF_HIDE_ECHO, 0) != 2023) {
+ && prefs.getInt(PREF_HIDE_ECHO, 0) != EchoActivity.RELEASE_YEAR) {
addSection(new EchoSection());
}
@@ -153,8 +147,7 @@ public class HomeFragment extends Fragment implements Toolbar.OnMenuItemClickLis
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedUpdateRunningEvent event) {
- MenuItemUtils.updateRefreshMenuItem(viewBinding.toolbar.getMenu(),
- R.id.refresh_item, event.isFeedUpdateRunning);
+ viewBinding.swipeRefresh.setRefreshing(event.isFeedUpdateRunning);
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/ui/home/sections/EchoSection.java b/app/src/main/java/de/danoeh/antennapod/ui/home/sections/EchoSection.java
index 7261c6be4..05b716abb 100644
--- a/app/src/main/java/de/danoeh/antennapod/ui/home/sections/EchoSection.java
+++ b/app/src/main/java/de/danoeh/antennapod/ui/home/sections/EchoSection.java
@@ -32,13 +32,9 @@ public class EchoSection extends Fragment {
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
viewBinding = HomeSectionEchoBinding.inflate(inflater);
- viewBinding.titleLabel.setText(getString(R.string.antennapod_echo_year, 2023));
+ viewBinding.titleLabel.setText(getString(R.string.antennapod_echo_year, EchoActivity.RELEASE_YEAR));
viewBinding.echoButton.setOnClickListener(v -> startActivity(new Intent(getContext(), EchoActivity.class)));
- viewBinding.closeButton.setOnClickListener(v -> {
- getContext().getSharedPreferences(HomeFragment.PREF_NAME, Context.MODE_PRIVATE)
- .edit().putInt(HomeFragment.PREF_HIDE_ECHO, 2023).apply();
- ((MainActivity) getActivity()).loadFragment(HomeFragment.TAG, null);
- });
+ viewBinding.closeButton.setOnClickListener(v -> hideThisYear());
updateVisibility();
return viewBinding.getRoot();
}
@@ -51,7 +47,7 @@ public class EchoSection extends Fragment {
date.set(Calendar.MILLISECOND, 0);
date.set(Calendar.DAY_OF_MONTH, 1);
date.set(Calendar.MONTH, 0);
- date.set(Calendar.YEAR, 2023);
+ date.set(Calendar.YEAR, EchoActivity.RELEASE_YEAR);
return date.getTimeInMillis();
}
@@ -70,8 +66,18 @@ public class EchoSection extends Fragment {
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(totalTime -> viewBinding.getRoot()
- .setVisibility((totalTime >= 3600 * 10) ? View.VISIBLE : View.GONE),
- Throwable::printStackTrace);
+ .subscribe(totalTime -> {
+ boolean shouldShow = (totalTime >= 3600 * 10);
+ viewBinding.getRoot().setVisibility(shouldShow ? View.VISIBLE : View.GONE);
+ if (!shouldShow) {
+ hideThisYear();
+ }
+ }, Throwable::printStackTrace);
+ }
+
+ void hideThisYear() {
+ getContext().getSharedPreferences(HomeFragment.PREF_NAME, Context.MODE_PRIVATE)
+ .edit().putInt(HomeFragment.PREF_HIDE_ECHO, EchoActivity.RELEASE_YEAR).apply();
+ ((MainActivity) getActivity()).loadFragment(HomeFragment.TAG, null);
}
}
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 b4f61a19a..01d4a10f0 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
@@ -100,15 +100,13 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder {
this.item = item;
placeholder.setText(item.getFeed().getTitle());
title.setText(item.getTitle());
- leftPadding.setContentDescription(item.getTitle());
- pubDate.setText(DateFormatter.formatAbbrev(activity, item.getPubDate()));
- pubDate.setContentDescription(DateFormatter.formatForAccessibility(item.getPubDate()));
if (item.isPlayed()) {
- cover.setContentDescription(activity.getString(R.string.is_played));
- cover.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+ leftPadding.setContentDescription(item.getTitle() + ". " + activity.getString(R.string.is_played));
} else {
- cover.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+ leftPadding.setContentDescription(item.getTitle());
}
+ pubDate.setText(DateFormatter.formatAbbrev(activity, item.getPubDate()));
+ pubDate.setContentDescription(DateFormatter.formatForAccessibility(item.getPubDate()));
isInbox.setVisibility(item.isNew() ? View.VISIBLE : View.GONE);
isFavorite.setVisibility(item.isTagged(FeedItem.TAG_FAVORITE) ? View.VISIBLE : View.GONE);
isInQueue.setVisibility(item.isTagged(FeedItem.TAG_QUEUE) ? View.VISIBLE : View.GONE);
diff --git a/app/src/main/res/layout/audio_controls.xml b/app/src/main/res/layout/audio_controls.xml
index 3abb70961..25f38a63f 100644
--- a/app/src/main/res/layout/audio_controls.xml
+++ b/app/src/main/res/layout/audio_controls.xml
@@ -19,19 +19,6 @@
android:visibility="gone"
android:layout_marginBottom="8dp" />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="20dp"
- android:text="@string/audio_effects"
- style="@style/AntennaPod.TextView.ListItemPrimaryTitle" />
-
- <CheckBox
- android:id="@+id/skipSilence"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/pref_skip_silence_title" />
-
</LinearLayout>
</ScrollView>
diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml
index 974823668..c3ca87a5a 100644
--- a/app/src/main/res/layout/feedinfo.xml
+++ b/app/src/main/res/layout/feedinfo.xml
@@ -81,12 +81,13 @@
<TextView
android:id="@+id/txtvUrl"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:maxLines="4"
android:paddingTop="4dp"
android:paddingBottom="4dp"
+ android:drawablePadding="4dp"
tools:background="@android:color/holo_green_dark"
tools:text="http://www.example.com/feed" />
diff --git a/app/src/main/res/layout/filter_dialog.xml b/app/src/main/res/layout/filter_dialog.xml
index d700f0365..e22ea4ffe 100644
--- a/app/src/main/res/layout/filter_dialog.xml
+++ b/app/src/main/res/layout/filter_dialog.xml
@@ -1,15 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+<ScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
<LinearLayout
- android:id="@+id/filter_rows"
+ android:id="@+id/filter_rows"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="24dp"
+ android:paddingTop="24dp"
+ android:paddingRight="24dp"
+ android:paddingBottom="8dp">
+
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:paddingLeft="24dp"
- android:paddingTop="24dp"
- android:paddingRight="24dp"
- android:paddingBottom="8dp">
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/resetFiltermenu"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/reset"
+ style="@style/Widget.MaterialComponents.Button.TextButton" />
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/confirmFiltermenu"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/confirm_label"
+ style="@style/Widget.MaterialComponents.Button.TextButton" />
+
+ </LinearLayout>
+
</LinearLayout>
+
</ScrollView>
diff --git a/app/src/main/res/layout/home_section.xml b/app/src/main/res/layout/home_section.xml
index 82f5c0b5c..c1bac5e91 100644
--- a/app/src/main/res/layout/home_section.xml
+++ b/app/src/main/res/layout/home_section.xml
@@ -56,6 +56,7 @@
android:layout_marginStart="8dp"
android:background="@drawable/bg_pill"
android:paddingHorizontal="8dp"
+ android:paddingBottom="1sp"
android:textAlignment="center"
android:textColor="?attr/colorPrimary"
android:textSize="16sp"
diff --git a/app/src/main/res/layout/nextcloud_auth_dialog.xml b/app/src/main/res/layout/nextcloud_auth_dialog.xml
index c08c5b969..0f25a271f 100644
--- a/app/src/main/res/layout/nextcloud_auth_dialog.xml
+++ b/app/src/main/res/layout/nextcloud_auth_dialog.xml
@@ -1,69 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<ScrollView
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">
+ android:layout_height="wrap_content">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="16dp"
- android:text="@string/synchronization_host_explanation" />
-
- <com.google.android.material.textfield.TextInputLayout
- android:id="@+id/serverUrlTextInput"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginBottom="16dp"
- style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
+ android:padding="16dp"
+ android:orientation="vertical"
+ android:clipToPadding="false">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="16dp"
+ android:text="@string/synchronization_host_explanation" />
- <com.google.android.material.textfield.TextInputEditText
- android:id="@+id/serverUrlText"
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/serverUrlTextInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:hint="@string/synchronization_host_label"
- android:inputType="textNoSuggestions"
- android:lines="1"
- android:imeOptions="actionNext|flagNoFullscreen" />
+ android:layout_marginBottom="16dp"
+ style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox">
- </com.google.android.material.textfield.TextInputLayout>
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/serverUrlText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/synchronization_host_label"
+ android:inputType="textNoSuggestions"
+ android:lines="1"
+ android:imeOptions="actionNext|flagNoFullscreen" />
- <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">
+ </com.google.android.material.textfield.TextInputLayout>
- <ProgressBar
- android:layout_width="wrap_content"
+ <LinearLayout
+ android:id="@+id/loginProgressContainer"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginEnd="8dp"
- android:layout_marginRight="8dp" />
+ 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:text="@string/synchronization_nextcloud_authenticate_browser" />
+ android:visibility="gone"
+ android:textColor="?attr/icon_red"
+ android:layout_marginBottom="16dp" />
- </LinearLayout>
-
- <TextView
- android:id="@+id/errorText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"
- android:textColor="?attr/icon_red"
- android:layout_marginBottom="16dp" />
+ <Button
+ android:id="@+id/chooseHostButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/proceed_to_login_butLabel" />
- <Button
- android:id="@+id/chooseHostButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/proceed_to_login_butLabel" />
+ </LinearLayout>
-</LinearLayout>
+</ScrollView>
diff --git a/app/src/main/res/layout/sort_dialog.xml b/app/src/main/res/layout/sort_dialog.xml
new file mode 100644
index 000000000..88eb61df5
--- /dev/null
+++ b/app/src/main/res/layout/sort_dialog.xml
@@ -0,0 +1,28 @@
+<?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"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <androidx.gridlayout.widget.GridLayout
+ android:id="@+id/gridLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:columnCount="2"
+ app:rowOrderPreserved="false"
+ app:useDefaultMargins="true"
+ app:alignmentMode="alignBounds" />
+
+ <CheckBox
+ android:id="@+id/keepSortedCheckbox"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ android:text="@string/keep_sorted"
+ tools:visibility="visible" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/sort_dialog_item.xml b/app/src/main/res/layout/sort_dialog_item.xml
new file mode 100644
index 000000000..570a66d26
--- /dev/null
+++ b/app/src/main/res/layout/sort_dialog_item.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Button
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/button"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ app:layout_gravity="fill_horizontal|center_vertical"
+ app:layout_columnWeight="1"
+ style="@style/Widget.Material3.Button.OutlinedButton" />
diff --git a/app/src/main/res/layout/sort_dialog_item_active.xml b/app/src/main/res/layout/sort_dialog_item_active.xml
new file mode 100644
index 000000000..829ccdee2
--- /dev/null
+++ b/app/src/main/res/layout/sort_dialog_item_active.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Button
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/button"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ app:layout_gravity="fill_horizontal|center_vertical"
+ app:layout_columnWeight="1"
+ style="@style/Widget.Material3.Button.TonalButton" />
diff --git a/app/src/main/res/layout/speed_select_dialog.xml b/app/src/main/res/layout/speed_select_dialog.xml
index e4d78c3fa..0b9e422a8 100644
--- a/app/src/main/res/layout/speed_select_dialog.xml
+++ b/app/src/main/res/layout/speed_select_dialog.xml
@@ -1,45 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:padding="16dp"
- android:orientation="vertical">
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
<LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
<TextView
- android:text="@string/playback_speed"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- style="@style/AntennaPod.TextView.ListItemPrimaryTitle"/>
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/playback_speed"
+ style="@style/AntennaPod.TextView.ListItemPrimaryTitle" />
<com.google.android.material.chip.Chip
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/add_current_speed_chip"/>
+ android:id="@+id/add_current_speed_chip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
</LinearLayout>
<de.danoeh.antennapod.view.PlaybackSpeedSeekBar
- android:id="@+id/speed_seek_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp">
- </de.danoeh.antennapod.view.PlaybackSpeedSeekBar>
+ android:id="@+id/speed_seek_bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp" />
<TextView
- android:text="@string/speed_presets"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
- android:layout_marginBottom="8dp"/>
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:text="@string/speed_presets"
+ style="@style/AntennaPod.TextView.ListItemPrimaryTitle" />
<androidx.recyclerview.widget.RecyclerView
- android:id="@+id/selected_speeds_grid"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:id="@+id/selected_speeds_grid"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <CheckBox
+ android:id="@+id/skipSilence"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/pref_skip_silence_title" />
-</LinearLayout> \ No newline at end of file
+</LinearLayout>
diff --git a/app/src/main/res/layout/time_dialog.xml b/app/src/main/res/layout/time_dialog.xml
index 263ac0c9c..2b5ae0985 100644
--- a/app/src/main/res/layout/time_dialog.xml
+++ b/app/src/main/res/layout/time_dialog.xml
@@ -1,164 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center"
- android:padding="16dp">
+ android:layout_height="wrap_content">
<LinearLayout
- android:id="@+id/timeSetup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:gravity="center"
+ android:padding="16dp">
<LinearLayout
+ android:id="@+id/timeSetup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:orientation="vertical">
- <EditText
- android:id="@+id/etxtTime"
- android:layout_width="0dp"
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:selectAllOnFocus="true"
- android:layout_margin="8dp"
- android:ems="2"
- android:inputType="number"
- android:maxLength="3" />
+ android:orientation="horizontal">
+
+ <EditText
+ android:id="@+id/etxtTime"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:selectAllOnFocus="true"
+ android:layout_margin="8dp"
+ android:ems="2"
+ android:inputType="number"
+ android:maxLength="3" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/time_minutes"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="8dp" />
+
+ </LinearLayout>
- <TextView
- android:layout_width="wrap_content"
+ <Button
+ android:id="@+id/setSleeptimerButton"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/time_minutes"
- android:layout_marginBottom="8dp"
- android:layout_marginTop="8dp" />
+ android:text="@string/set_sleeptimer_label" />
</LinearLayout>
- <Button
- android:id="@+id/setSleeptimerButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/set_sleeptimer_label" />
-
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/timeDisplay"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="visible">
-
- <TextView
- 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:id="@+id/disableSleeptimerButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/disable_sleeptimer_label" />
-
<LinearLayout
+ android:id="@+id/timeDisplay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:gravity="center_vertical">
+ android:orientation="vertical"
+ android:visibility="visible">
- <Button
- 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" />
+ <TextView
+ 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:id="@+id/extendSleepTenMinutesButton"
- android:layout_width="0dp"
+ android:id="@+id/disableSleeptimerButton"
+ android:layout_width="match_parent"
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" />
+ android:text="@string/disable_sleeptimer_label" />
- <Button
- android:id="@+id/extendSleepTwentyMinutesButton"
- android:layout_width="0dp"
+ <LinearLayout
+ android:layout_width="match_parent"
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" />
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+
+ <Button
+ 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"
+ 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"
+ 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>
-
- <LinearLayout
- 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" />
-
- <CheckBox
- android:id="@+id/cbVibrate"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/timer_vibration_label" />
-
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:weightSum="1">
+ android:orientation="vertical"
+ android:layout_marginTop="8dp">
<CheckBox
- android:id="@+id/chAutoEnable"
- android:layout_width="0dp"
+ android:id="@+id/cbShakeToReset"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/auto_enable_label" />
+ android:text="@string/shake_to_reset_label" />
- <ImageView
- android:id="@+id/changeTimesButton"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:contentDescription="@string/auto_enable_change_times"
- android:background="?attr/selectableItemBackgroundBorderless"
- app:srcCompat="@drawable/ic_settings" />
+ <CheckBox
+ android:id="@+id/cbVibrate"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/timer_vibration_label" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:weightSum="1">
+
+ <CheckBox
+ android:id="@+id/chAutoEnable"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/auto_enable_label" />
+
+ <ImageView
+ android:id="@+id/changeTimesButton"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:contentDescription="@string/auto_enable_change_times"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ app:srcCompat="@drawable/ic_settings" />
+
+ </LinearLayout>
</LinearLayout>
</LinearLayout>
-</LinearLayout>
+</ScrollView>
diff --git a/app/src/main/res/menu/downloads_completed.xml b/app/src/main/res/menu/downloads_completed.xml
index 08179de58..a7753d080 100644
--- a/app/src/main/res/menu/downloads_completed.xml
+++ b/app/src/main/res/menu/downloads_completed.xml
@@ -20,12 +20,8 @@
android:id="@+id/refresh_item"
android:title="@string/refresh_label"
android:menuCategory="container"
- android:icon="@drawable/ic_refresh"
- app:showAsAction="always" />
+ app:showAsAction="never" />
<item
android:id="@+id/downloads_sort"
- android:title="@string/sort">
- <menu></menu>
- </item>
-
+ android:title="@string/sort" />
</menu>
diff --git a/app/src/main/res/menu/episodes.xml b/app/src/main/res/menu/episodes.xml
index 358573c93..4c8e0dbb3 100644
--- a/app/src/main/res/menu/episodes.xml
+++ b/app/src/main/res/menu/episodes.xml
@@ -13,8 +13,7 @@
android:id="@+id/refresh_item"
android:title="@string/refresh_label"
android:menuCategory="container"
- custom:showAsAction="always"
- android:icon="@drawable/ic_refresh"/>
+ custom:showAsAction="never" />
<item
android:id="@+id/filter_items"
@@ -33,8 +32,6 @@
<item
android:id="@+id/episodes_sort"
android:title="@string/sort"
- custom:showAsAction="never">
- <menu />
- </item>
+ custom:showAsAction="never" />
</menu>
diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml
index 2086ff547..0baf434f1 100644
--- a/app/src/main/res/menu/feedlist.xml
+++ b/app/src/main/res/menu/feedlist.xml
@@ -10,10 +10,9 @@
</item>
<item
android:id="@+id/refresh_item"
- android:icon="@drawable/ic_refresh"
android:menuCategory="container"
android:title="@string/refresh_label"
- custom:showAsAction="always">
+ custom:showAsAction="never">
</item>
<item
android:id="@+id/refresh_complete_item"
diff --git a/app/src/main/res/menu/home.xml b/app/src/main/res/menu/home.xml
index f80218c0c..a0bca6d50 100644
--- a/app/src/main/res/menu/home.xml
+++ b/app/src/main/res/menu/home.xml
@@ -12,8 +12,7 @@
android:id="@+id/refresh_item"
android:title="@string/refresh_label"
android:menuCategory="container"
- custom:showAsAction="always"
- android:icon="@drawable/ic_refresh"/>
+ custom:showAsAction="never" />
<item
android:id="@+id/homesettings_items"
diff --git a/app/src/main/res/menu/inbox.xml b/app/src/main/res/menu/inbox.xml
index fba8eefdb..725d6e90e 100644
--- a/app/src/main/res/menu/inbox.xml
+++ b/app/src/main/res/menu/inbox.xml
@@ -12,14 +12,11 @@
android:id="@+id/refresh_item"
android:title="@string/refresh_label"
android:menuCategory="container"
- custom:showAsAction="always"
- android:icon="@drawable/ic_refresh"/>
+ custom:showAsAction="never" />
<item
android:id="@+id/inbox_sort"
- android:title="@string/sort">
- <menu></menu>
- </item>
+ android:title="@string/sort" />
<item
android:id="@+id/remove_all_inbox_item"
diff --git a/app/src/main/res/menu/mediaplayer.xml b/app/src/main/res/menu/mediaplayer.xml
index 5f60fe6f5..a99151ac8 100644
--- a/app/src/main/res/menu/mediaplayer.xml
+++ b/app/src/main/res/menu/mediaplayer.xml
@@ -30,9 +30,9 @@
<item
android:id="@+id/audio_controls"
- android:icon="@drawable/ic_sliders"
android:title="@string/audio_controls"
- custom:showAsAction="always">
+ android:visible="false"
+ custom:showAsAction="never">
</item>
<item
@@ -74,8 +74,9 @@
<item
android:id="@+id/share_item"
+ android:icon="@drawable/ic_share"
android:menuCategory="container"
- custom:showAsAction="never"
+ custom:showAsAction="always"
android:title="@string/share_label">
</item>
</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/queue.xml b/app/src/main/res/menu/queue.xml
index e4bb63808..5f34d0a96 100644
--- a/app/src/main/res/menu/queue.xml
+++ b/app/src/main/res/menu/queue.xml
@@ -12,8 +12,7 @@
android:id="@+id/refresh_item"
android:title="@string/refresh_label"
android:menuCategory="container"
- custom:showAsAction="always"
- android:icon="@drawable/ic_refresh"/>
+ custom:showAsAction="never" />
<item
android:id="@+id/queue_lock"
@@ -23,9 +22,7 @@
<item
android:id="@+id/queue_sort"
- android:title="@string/sort">
- <menu></menu>
- </item>
+ android:title="@string/sort" />
<item
android:id="@+id/clear_queue"
diff --git a/app/src/main/res/menu/sort_menu.xml b/app/src/main/res/menu/sort_menu.xml
deleted file mode 100644
index 96ad4b9f6..000000000
--- a/app/src/main/res/menu/sort_menu.xml
+++ /dev/null
@@ -1,99 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item
- android:id="@+id/sort_date"
- android:title="@string/date">
-
- <menu>
- <item
- android:id="@+id/sort_date_asc"
- android:title="@string/sort_old_new"/>
- <item
- android:id="@+id/sort_date_desc"
- android:title="@string/sort_new_old"/>
- </menu>
- </item>
-
- <item
- android:id="@+id/sort_duration"
- android:title="@string/duration">
-
- <menu>
- <item
- android:id="@+id/sort_duration_asc"
- android:title="@string/sort_short_long"/>
- <item
- android:id="@+id/sort_duration_desc"
- android:title="@string/sort_long_short"/>
- </menu>
- </item>
-
- <item
- android:id="@+id/sort_episode_title"
- android:title="@string/episode_title">
-
- <menu>
- <item
- android:id="@+id/sort_episode_title_asc"
- android:title="@string/sort_a_z"/>
- <item
- android:id="@+id/sort_episode_title_desc"
- android:title="@string/sort_z_a"/>
- </menu>
- </item>
-
- <item
- android:id="@+id/sort_feed_title"
- android:title="@string/feed_title">
-
- <menu>
- <item
- android:id="@+id/sort_feed_title_asc"
- android:title="@string/sort_a_z"/>
- <item
- android:id="@+id/sort_feed_title_desc"
- android:title="@string/sort_z_a"/>
- </menu>
- </item>
-
- <item
- android:id="@+id/sort_random"
- android:title="@string/random">
- </item>
-
- <item
- android:id="@+id/sort_smart_shuffle"
- android:title="@string/smart_shuffle">
-
- <menu>
- <item
- android:id="@+id/sort_smart_shuffle_asc"
- android:title="@string/sort_old_new"/>
- <item
- android:id="@+id/sort_smart_shuffle_desc"
- android:title="@string/sort_new_old"/>
- </menu>
- </item>
-
- <item
- android:id="@+id/sort_size"
- android:title="@string/size"
- android:visible="false">
-
- <menu>
- <item
- android:id="@+id/sort_size_small_large"
- android:title="@string/sort_small_large"/>
- <item
- android:id="@+id/sort_size_large_small"
- android:title="@string/sort_large_small"/>
- </menu>
- </item>
-
- <item
- android:id="@+id/keep_sorted"
- android:title="@string/keep_sorted"
- android:checkable="true" />
-
-</menu>
diff --git a/app/src/main/res/menu/subscriptions.xml b/app/src/main/res/menu/subscriptions.xml
index 845ee92a2..3a2614149 100644
--- a/app/src/main/res/menu/subscriptions.xml
+++ b/app/src/main/res/menu/subscriptions.xml
@@ -15,8 +15,7 @@
android:id="@+id/refresh_item"
android:title="@string/refresh_label"
android:menuCategory="container"
- custom:showAsAction="always"
- android:icon="@drawable/ic_refresh"/>
+ custom:showAsAction="never" />
<item
android:id="@+id/subscriptions_filter"
android:title="@string/filter"
diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml
index 03da6c669..dab3a062c 100644
--- a/app/src/main/res/values/integers.xml
+++ b/app/src/main/res/values/integers.xml
@@ -2,6 +2,5 @@
<resources>
<integer name="subscriptions_default_num_of_columns">3</integer>
<integer name="nav_drawer_screen_size_percent">80</integer>
- <integer name="swipe_to_refresh_duration_in_ms">750</integer>
<integer name="swipe_refresh_distance">300</integer>
</resources> \ No newline at end of file
diff --git a/app/src/main/res/xml/preferences_user_interface.xml b/app/src/main/res/xml/preferences_user_interface.xml
index 733649ce1..5cd25d59f 100644
--- a/app/src/main/res/xml/preferences_user_interface.xml
+++ b/app/src/main/res/xml/preferences_user_interface.xml
@@ -70,9 +70,9 @@
android:summary="@string/pref_persistNotify_sum"
android:title="@string/pref_persistNotify_title"/>
<Preference
- android:key="prefCompactNotificationButtons"
- android:summary="@string/pref_compact_notification_buttons_sum"
- android:title="@string/pref_compact_notification_buttons_title"/>
+ android:key="prefFullNotificationButtons"
+ android:summary="@string/pref_full_notification_buttons_sum"
+ android:title="@string/pref_full_notification_buttons_title"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/behavior">
<de.danoeh.antennapod.preferences.MaterialListPreference
diff --git a/core/src/debug/res/mipmap-hdpi/ic_launcher.png b/core/src/debug/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 6f8022e25..000000000
--- a/core/src/debug/res/mipmap-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/core/src/debug/res/mipmap-hdpi/ic_launcher_background.png b/core/src/debug/res/mipmap-hdpi/ic_launcher_background.png
new file mode 100644
index 000000000..da2b8d47b
--- /dev/null
+++ b/core/src/debug/res/mipmap-hdpi/ic_launcher_background.png
Binary files differ
diff --git a/core/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png b/core/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..dd7c03e27
--- /dev/null
+++ b/core/src/debug/res/mipmap-hdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/core/src/debug/res/mipmap-mdpi/ic_launcher.png b/core/src/debug/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index d542d555f..000000000
--- a/core/src/debug/res/mipmap-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/core/src/debug/res/mipmap-mdpi/ic_launcher_background.png b/core/src/debug/res/mipmap-mdpi/ic_launcher_background.png
new file mode 100644
index 000000000..701d43516
--- /dev/null
+++ b/core/src/debug/res/mipmap-mdpi/ic_launcher_background.png
Binary files differ
diff --git a/core/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png b/core/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..b7a85063f
--- /dev/null
+++ b/core/src/debug/res/mipmap-mdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/core/src/debug/res/mipmap-xhdpi/ic_launcher.png b/core/src/debug/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index a02ec4ca8..000000000
--- a/core/src/debug/res/mipmap-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/core/src/debug/res/mipmap-xhdpi/ic_launcher_background.png b/core/src/debug/res/mipmap-xhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..1adaed041
--- /dev/null
+++ b/core/src/debug/res/mipmap-xhdpi/ic_launcher_background.png
Binary files differ
diff --git a/core/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png b/core/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..4d5a15f39
--- /dev/null
+++ b/core/src/debug/res/mipmap-xhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/core/src/debug/res/mipmap-xxhdpi/ic_launcher.png b/core/src/debug/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 066f9e5a5..000000000
--- a/core/src/debug/res/mipmap-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/core/src/debug/res/mipmap-xxhdpi/ic_launcher_background.png b/core/src/debug/res/mipmap-xxhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..31d4b272e
--- /dev/null
+++ b/core/src/debug/res/mipmap-xxhdpi/ic_launcher_background.png
Binary files differ
diff --git a/core/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png b/core/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..75caa42c9
--- /dev/null
+++ b/core/src/debug/res/mipmap-xxhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/core/src/debug/res/mipmap-xxxhdpi/ic_launcher.png b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 7dbab284c..000000000
--- a/core/src/debug/res/mipmap-xxxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_background.png b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_background.png
new file mode 100644
index 000000000..9edb9ab01
--- /dev/null
+++ b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_background.png
Binary files differ
diff --git a/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..630363954
--- /dev/null
+++ b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
new file mode 100644
index 000000000..d7e12bda3
--- /dev/null
+++ b/core/src/debug/res/mipmap-xxxhdpi/ic_launcher_monochrome.png
Binary files differ
diff --git a/core/src/main/java/de/danoeh/antennapod/core/menuhandler/MenuItemUtils.java b/core/src/main/java/de/danoeh/antennapod/core/menuhandler/MenuItemUtils.java
index 3b9d6a08d..829add126 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/menuhandler/MenuItemUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/menuhandler/MenuItemUtils.java
@@ -3,30 +3,12 @@ package de.danoeh.antennapod.core.menuhandler;
import android.view.Menu;
import android.view.MenuItem;
-import de.danoeh.antennapod.core.R;
-
/**
* Utilities for menu items
*/
public class MenuItemUtils {
/**
- * @param menu The menu that the MenuItem belongs to
- * @param resId The id of the MenuItem
- */
- public static void updateRefreshMenuItem(Menu menu, int resId, boolean isRefreshing) {
- // expand actionview if feeds are being downloaded, collapse otherwise
- MenuItem refreshItem = menu.findItem(resId);
- if (isRefreshing) {
- if (refreshItem.getActionView() == null) {
- refreshItem.setActionView(R.layout.refresh_action_view);
- }
- } else {
- refreshItem.setActionView(null);
- }
- }
-
- /**
* When pressing a context menu item, Android calls onContextItemSelected
* for ALL fragments in arbitrary order, not just for the fragment that the
* context menu was created from. This assigns the listener to every menu item,
diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java
index 08bdd39bc..161af58e1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java
@@ -17,6 +17,8 @@ import de.danoeh.antennapod.core.ClientConfigurator;
public class MediaButtonReceiver extends BroadcastReceiver {
private static final String TAG = "MediaButtonReceiver";
public static final String EXTRA_KEYCODE = "de.danoeh.antennapod.core.service.extra.MediaButtonReceiver.KEYCODE";
+ public static final String EXTRA_CUSTOM_ACTION =
+ "de.danoeh.antennapod.core.service.extra.MediaButtonReceiver.CUSTOM_ACTION";
public static final String EXTRA_SOURCE = "de.danoeh.antennapod.core.service.extra.MediaButtonReceiver.SOURCE";
public static final String EXTRA_HARDWAREBUTTON
= "de.danoeh.antennapod.core.service.extra.MediaButtonReceiver.HARDWAREBUTTON";
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index 5c8dead81..02102db14 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -63,10 +63,12 @@ import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.service.QuickSettingsTileService;
+import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager.SleepTimer;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.FeedSearcher;
import de.danoeh.antennapod.core.sync.queue.SynchronizationQueueSink;
+import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.FeedUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
@@ -84,12 +86,12 @@ import de.danoeh.antennapod.event.playback.SleepTimerUpdatedEvent;
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.model.feed.Chapter;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.model.feed.FeedPreferences;
-import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.playback.base.PlaybackServiceMediaPlayer;
@@ -127,6 +129,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
private static final String CUSTOM_ACTION_REWIND = "action.de.danoeh.antennapod.core.service.rewind";
private static final String CUSTOM_ACTION_CHANGE_PLAYBACK_SPEED =
"action.de.danoeh.antennapod.core.service.changePlaybackSpeed";
+ public static final String CUSTOM_ACTION_NEXT_CHAPTER = "action.de.danoeh.antennapod.core.service.next_chapter";
/**
* Set a max number of episodes to load for Android Auto, otherwise there could be performance issues
@@ -327,7 +330,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (rootHints != null && rootHints.getBoolean(BrowserRoot.EXTRA_RECENT)) {
Bundle extras = new Bundle();
extras.putBoolean(BrowserRoot.EXTRA_RECENT, true);
- return new BrowserRoot(getResources().getString(R.string.recently_played_episodes), extras);
+ Log.d(TAG, "OnGetRoot: Returning BrowserRoot " + R.string.current_playing_episode);
+ return new BrowserRoot(getResources().getString(R.string.current_playing_episode), extras);
}
// Name visible in Android Auto
@@ -408,6 +412,11 @@ public class PlaybackService extends MediaBrowserServiceCompat {
private List<MediaBrowserCompat.MediaItem> loadChildrenSynchronous(@NonNull String parentId) {
List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
if (parentId.equals(getResources().getString(R.string.app_name))) {
+ long currentlyPlaying = PlaybackPreferences.getCurrentPlayerStatus();
+ if (currentlyPlaying == PlaybackPreferences.PLAYER_STATUS_PLAYING
+ || currentlyPlaying == PlaybackPreferences.PLAYER_STATUS_PAUSED) {
+ mediaItems.add(createBrowsableMediaItem(R.string.current_playing_episode, R.drawable.ic_play_48dp, 1));
+ }
mediaItems.add(createBrowsableMediaItem(R.string.queue_label, R.drawable.ic_playlist_play_black,
DBReader.getTotalEpisodeCount(new FeedItemFilter(FeedItemFilter.QUEUED))));
mediaItems.add(createBrowsableMediaItem(R.string.downloads_label, R.drawable.ic_download_black,
@@ -429,11 +438,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
new FeedItemFilter(FeedItemFilter.DOWNLOADED), UserPreferences.getDownloadsSortedOrder());
} else if (parentId.equals(getResources().getString(R.string.episodes_label))) {
feedItems = DBReader.getEpisodes(0, MAX_ANDROID_AUTO_EPISODES_PER_FEED,
- new FeedItemFilter(FeedItemFilter.UNPLAYED), SortOrder.DATE_NEW_OLD);
+ new FeedItemFilter(FeedItemFilter.UNPLAYED), UserPreferences.getAllEpisodesSortOrder());
} else if (parentId.startsWith("FeedId:")) {
long feedId = Long.parseLong(parentId.split(":")[1]);
- feedItems = DBReader.getFeedItemList(DBReader.getFeed(feedId));
- } else if (parentId.equals(getString(R.string.recently_played_episodes))) {
+ Feed feed = DBReader.getFeed(feedId);
+ feedItems = DBReader.getFeedItemList(feed, FeedItemFilter.unfiltered(), feed.getSortOrder());
+ } else if (parentId.equals(getString(R.string.current_playing_episode))) {
Playable playable = PlaybackPreferences.createInstanceFromPreferences(this);
if (playable instanceof FeedMedia) {
feedItems = Collections.singletonList(((FeedMedia) playable).getItem());
@@ -476,9 +486,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
notificationManager.cancel(R.id.notification_streaming_confirmation);
final int keycode = intent.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1);
+ final String customAction = intent.getStringExtra(MediaButtonReceiver.EXTRA_CUSTOM_ACTION);
final boolean hardwareButton = intent.getBooleanExtra(MediaButtonReceiver.EXTRA_HARDWAREBUTTON, false);
Playable playable = intent.getParcelableExtra(PlaybackServiceInterface.EXTRA_PLAYABLE);
- if (keycode == -1 && playable == null) {
+ if (keycode == -1 && playable == null && customAction == null) {
Log.e(TAG, "PlaybackService was started with no arguments");
stateManager.stopService();
return Service.START_NOT_STICKY;
@@ -502,7 +513,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
stateManager.stopService();
return Service.START_NOT_STICKY;
}
- } else {
+ } else if (playable != null) {
stateManager.validStartCommandWasReceived();
boolean allowStreamThisTime = intent.getBooleanExtra(
PlaybackServiceInterface.EXTRA_ALLOW_STREAM_THIS_TIME, false);
@@ -530,6 +541,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
stateManager.stopService();
});
return Service.START_NOT_STICKY;
+ } else {
+ mediaSession.getController().getTransportControls().sendCustomAction(customAction, null);
}
}
@@ -777,6 +790,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onChapterLoaded(Playable media) {
sendNotificationBroadcast(PlaybackServiceInterface.NOTIFICATION_TYPE_RELOAD, 0);
+ updateMediaSession(mediaPlayer.getPlayerStatus());
}
};
@@ -965,7 +979,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (event.isOver()) {
mediaPlayer.pause(true, true);
mediaPlayer.setVolume(1.0f, 1.0f);
- } else if (event.getTimeLeft() < PlaybackServiceTaskManager.SleepTimer.NOTIFICATION_THRESHOLD) {
+ int newPosition = mediaPlayer.getPosition() - (int) SleepTimer.NOTIFICATION_THRESHOLD / 2;
+ newPosition = Math.max(newPosition, 0);
+ seekTo(newPosition);
+ } else if (event.getTimeLeft() < SleepTimer.NOTIFICATION_THRESHOLD) {
final float[] multiplicators = {0.1f, 0.2f, 0.3f, 0.3f, 0.3f, 0.4f, 0.4f, 0.4f, 0.6f, 0.8f};
float multiplicator = multiplicators[Math.max(0, (int) event.getTimeLeft() / 1000)];
Log.d(TAG, "onSleepTimerAlmostExpired: " + multiplicator);
@@ -1244,20 +1261,35 @@ public class PlaybackService extends MediaBrowserServiceCompat {
WearMediaSession.addWearExtrasToAction(fastForwardBuilder);
sessionState.addCustomAction(fastForwardBuilder.build());
- sessionState.addCustomAction(
- new PlaybackStateCompat.CustomAction.Builder(
- CUSTOM_ACTION_CHANGE_PLAYBACK_SPEED,
- getString(R.string.playback_speed),
- R.drawable.ic_notification_playback_speed
+ if (UserPreferences.showPlaybackSpeedOnFullNotification()) {
+ sessionState.addCustomAction(
+ new PlaybackStateCompat.CustomAction.Builder(
+ CUSTOM_ACTION_CHANGE_PLAYBACK_SPEED,
+ getString(R.string.playback_speed),
+ R.drawable.ic_notification_playback_speed
).build()
- );
- sessionState.addCustomAction(
+ );
+ }
+
+ if (UserPreferences.showNextChapterOnFullNotification()) {
+ if (getPlayable() != null && getPlayable().getChapters() != null) {
+ sessionState.addCustomAction(
+ new PlaybackStateCompat.CustomAction.Builder(
+ CUSTOM_ACTION_NEXT_CHAPTER,
+ getString(R.string.next_chapter), R.drawable.ic_notification_next_chapter)
+ .build());
+ }
+ }
+
+ if (UserPreferences.showSkipOnFullNotification()) {
+ sessionState.addCustomAction(
new PlaybackStateCompat.CustomAction.Builder(
- CUSTOM_ACTION_SKIP_TO_NEXT,
- getString(R.string.skip_episode_label),
- R.drawable.ic_notification_skip
+ CUSTOM_ACTION_SKIP_TO_NEXT,
+ getString(R.string.skip_episode_label),
+ R.drawable.ic_notification_skip
).build()
- );
+ );
+ }
WearMediaSession.mediaSessionSetExtraForWear(mediaSession);
@@ -1765,6 +1797,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public void onPlayFromSearch(String query, Bundle extras) {
Log.d(TAG, "onPlayFromSearch query=" + query + " extras=" + extras.toString());
+ if (query.equals("")) {
+ Log.d(TAG, "onPlayFromSearch called with empty query, resuming from the last position");
+ startPlayingFromPreferences();
+ return;
+ }
+
List<FeedItem> results = FeedSearcher.searchFeedItems(query, 0);
if (results.size() > 0 && results.get(0).getMedia() != null) {
FeedMedia media = results.get(0).getMedia();
@@ -1800,6 +1838,26 @@ public class PlaybackService extends MediaBrowserServiceCompat {
seekDelta(-UserPreferences.getRewindSecs() * 1000);
}
+ public void onNextChapter() {
+ List<Chapter> chapters = mediaPlayer.getPlayable().getChapters();
+ if (chapters == null) {
+ // No chapters, just fallback to next episode
+ mediaPlayer.skip();
+ return;
+ }
+
+ int nextChapter = ChapterUtils.getCurrentChapterIndex(
+ mediaPlayer.getPlayable(), mediaPlayer.getPosition()) + 1;
+
+ if (chapters.size() < nextChapter + 1) {
+ // We are on the last chapter, just fallback to the next episode
+ mediaPlayer.skip();
+ return;
+ }
+
+ mediaPlayer.seekTo((int) chapters.get(nextChapter).getStart());
+ }
+
@Override
public void onFastForward() {
Log.d(TAG, "onFastForward()");
@@ -1872,6 +1930,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
onRewind();
} else if (CUSTOM_ACTION_SKIP_TO_NEXT.equals(action)) {
mediaPlayer.skip();
+ } else if (CUSTOM_ACTION_NEXT_CHAPTER.equals(action)) {
+ onNextChapter();
} else if (CUSTOM_ACTION_CHANGE_PLAYBACK_SPEED.equals(action)) {
List<Float> selectedSpeeds = UserPreferences.getPlaybackSpeedArray();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
index fe98dbc8f..471dc7454 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
@@ -175,14 +175,12 @@ public class PlaybackServiceNotificationBuilder {
ArrayList<Integer> compactActionList = new ArrayList<>();
int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction
- // always let them rewind
+
PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_REWIND, numActions);
notification.addAction(R.drawable.ic_notification_fast_rewind, context.getString(R.string.rewind_label),
rewindButtonPendingIntent);
- if (UserPreferences.showRewindOnCompactNotification()) {
- compactActionList.add(numActions);
- }
+ compactActionList.add(numActions);
numActions++;
if (playerStatus == PlayerStatus.PLAYING) {
@@ -205,19 +203,24 @@ public class PlaybackServiceNotificationBuilder {
KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, numActions);
notification.addAction(R.drawable.ic_notification_fast_forward, context.getString(R.string.fast_forward_label),
ffButtonPendingIntent);
- if (UserPreferences.showFastForwardOnCompactNotification()) {
- compactActionList.add(numActions);
- }
+ compactActionList.add(numActions);
numActions++;
- PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_NEXT, numActions);
- notification.addAction(R.drawable.ic_notification_skip, context.getString(R.string.skip_episode_label),
- skipButtonPendingIntent);
- if (UserPreferences.showSkipOnCompactNotification()) {
- compactActionList.add(numActions);
+ if (UserPreferences.showNextChapterOnFullNotification() && playable.getChapters() != null) {
+ PendingIntent nextChapterPendingIntent = getPendingIntentForCustomMediaAction(
+ PlaybackService.CUSTOM_ACTION_NEXT_CHAPTER, numActions);
+ notification.addAction(R.drawable.ic_notification_next_chapter, context.getString(R.string.next_chapter),
+ nextChapterPendingIntent);
+ numActions++;
+ }
+
+ if (UserPreferences.showSkipOnFullNotification()) {
+ PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction(
+ KeyEvent.KEYCODE_MEDIA_NEXT, numActions);
+ notification.addAction(R.drawable.ic_notification_skip, context.getString(R.string.skip_episode_label),
+ skipButtonPendingIntent);
+ numActions++;
}
- numActions++;
PendingIntent stopButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_STOP, numActions);
@@ -242,6 +245,20 @@ public class PlaybackServiceNotificationBuilder {
}
}
+ private PendingIntent getPendingIntentForCustomMediaAction(String action, int requestCode) {
+ Intent intent = new Intent(context, PlaybackService.class);
+ intent.setAction("MediaAction" + action);
+ intent.putExtra(MediaButtonReceiver.EXTRA_CUSTOM_ACTION, action);
+
+ if (Build.VERSION.SDK_INT >= 26) {
+ return PendingIntent.getForegroundService(context, requestCode, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+ } else {
+ return PendingIntent.getService(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
+ }
+ }
+
public void setMediaSessionToken(MediaSessionCompat.Token mediaSessionToken) {
this.mediaSessionToken = mediaSessionToken;
}
@@ -253,4 +270,4 @@ public class PlaybackServiceNotificationBuilder {
public PlayerStatus getPlayerStatus() {
return playerStatus;
}
-} \ No newline at end of file
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java
index dbbfba379..ecfe5f4dd 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java
@@ -69,7 +69,9 @@ public class AutomaticDownloadAlgorithm {
Iterator<FeedItem> it = candidates.iterator();
while (it.hasNext()) {
FeedItem item = it.next();
- if (!item.isAutoDownloadable(System.currentTimeMillis())
+ if (!item.isAutoDownloadEnabled()
+ || item.isDownloaded()
+ || !item.hasMedia()
|| PlaybackStatus.isPlaying(item.getMedia())
|| item.getFeed().isLocalFeed()) {
it.remove();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
index 084b3a7ad..492dff759 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
@@ -15,9 +15,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.comparator.DownloadResultComparator;
-import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
import de.danoeh.antennapod.core.util.comparator.PlaybackCompletionDateComparator;
import de.danoeh.antennapod.model.feed.Chapter;
import de.danoeh.antennapod.model.feed.Feed;
@@ -170,13 +170,18 @@ public final class DBReader {
}
public static List<FeedItem> getFeedItemList(final Feed feed, final FeedItemFilter filter) {
+ return getFeedItemList(feed, filter, SortOrder.DATE_NEW_OLD);
+ }
+
+ public static List<FeedItem> getFeedItemList(final Feed feed, final FeedItemFilter filter, SortOrder sortOrder) {
Log.d(TAG, "getFeedItemList() called with: " + "feed = [" + feed + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
try (Cursor cursor = adapter.getItemsOfFeedCursor(feed, filter)) {
List<FeedItem> items = extractItemlistFromCursor(adapter, cursor);
- Collections.sort(items, new FeedItemPubdateComparator());
+ FeedItemPermutors.getPermutor(sortOrder).reorder(items);
+ feed.setItems(items);
for (FeedItem item : items) {
item.setFeed(feed);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesser.java b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesser.java
index 35d77ae4a..2ff9d8848 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesser.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesser.java
@@ -28,7 +28,7 @@ public class FeedItemDuplicateGuesser {
return titlesLookSimilar(item1, item2)
&& datesLookSimilar(item1, item2)
&& durationsLookSimilar(media1, media2)
- && TextUtils.equals(media1.getMime_type(), media2.getMime_type());
+ && mimeTypeLooksSimilar(media1, media2);
}
private static boolean sameAndNotEmpty(String string1, String string2) {
@@ -52,6 +52,19 @@ public class FeedItemDuplicateGuesser {
return Math.abs(media1.getDuration() - media2.getDuration()) < 10 * 60L * 1000L;
}
+ private static boolean mimeTypeLooksSimilar(FeedMedia media1, FeedMedia media2) {
+ String mimeType1 = media1.getMime_type();
+ String mimeType2 = media2.getMime_type();
+ if (mimeType1 == null || mimeType2 == null) {
+ return true;
+ }
+ if (mimeType1.contains("/") && mimeType2.contains("/")) {
+ mimeType1 = mimeType1.substring(0, mimeType1.indexOf("/"));
+ mimeType2 = mimeType2.substring(0, mimeType2.indexOf("/"));
+ }
+ return TextUtils.equals(mimeType1, mimeType2);
+ }
+
private static boolean titlesLookSimilar(FeedItem item1, FeedItem item2) {
return sameAndNotEmpty(canonicalizeTitle(item1.getTitle()), canonicalizeTitle(item2.getTitle()));
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
index 13f2af762..630507487 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
@@ -10,6 +10,7 @@ import androidx.core.app.ShareCompat;
import androidx.core.content.FileProvider;
import java.io.File;
+import java.net.URLEncoder;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.model.feed.Feed;
@@ -33,12 +34,12 @@ public class ShareUtils {
}
public static void shareFeedLink(Context context, Feed feed) {
- String text = feed.getTitle();
- if (feed.getLink() != null) {
- text += "\n" + feed.getLink();
- }
- text += "\n\n" + context.getResources().getString(R.string.share_rss_address_label)
- + " " + feed.getDownload_url();
+ String text = feed.getTitle()
+ + "\n\n"
+ + "https://antennapod.org/deeplink/subscribe/?url="
+ + URLEncoder.encode(feed.getDownload_url())
+ + "&title="
+ + URLEncoder.encode(feed.getTitle());
shareLink(context, text);
}
diff --git a/core/src/main/res/drawable/ic_disc_alert.xml b/core/src/main/res/drawable/ic_disc_alert.xml
new file mode 100644
index 000000000..6a2c11187
--- /dev/null
+++ b/core/src/main/res/drawable/ic_disc_alert.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="?attr/action_icon_color"
+ android:pathData="M10 14C8.9 14 8 13.1 8 12C8 10.9 8.9 10 10 10C11.1 10 12 10.9 12 12S11.1 14 10 14M10 4C5.6 4 2 7.6 2 12S5.6 20 10 20 18 16.4 18 12 14.4 4 10 4M20 13H22V7H20M20 17H22V15H20V17Z" />
+
+</vector>
diff --git a/core/src/main/res/drawable/ic_disc_full.xml b/core/src/main/res/drawable/ic_disc_full.xml
deleted file mode 100644
index 2aba1bc53..000000000
--- a/core/src/main/res/drawable/ic_disc_full.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="20dp"
- android:height="16dp"
- android:viewportWidth="20"
- android:viewportHeight="16">
- <path
- android:pathData="M17.1 11.5s0-1.7 0-1.7s1.8 0 1.8 0s0 1.7 0 1.7s-1.8 0-1.8 0zm0-8s1.8 0 1.8 0s0 4.5 0 4.5s-1.8 0-1.8 0s0-4.5 0-4.5zm-8.9-2.6c2 0 3.6 0.7 5 2.1s2.1 3 2.1 5s-0.7 3.7-2.1 5s-3 2.1-5 2.1c-1.9 0-3.6-0.7-5-2.1s-2.1-3-2.1-5s0.7-3.6 2.1-5s3.1-2.1 5-2.1zm0 8.9c0.5 0 0.9-0.2 1.3-0.5s0.5-0.8 0.5-1.3s-0.2-0.9-0.5-1.3s-0.8-0.5-1.3-0.5s-0.9 0.2-1.2 0.5s-0.6 0.8-0.6 1.3s0.2 0.9 0.6 1.3s0.7 0.5 1.2 0.5z"
- android:fillColor="?android:attr/textColorPrimary" />
-</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_paperclip.xml b/core/src/main/res/drawable/ic_paperclip.xml
new file mode 100644
index 000000000..dd6673c13
--- /dev/null
+++ b/core/src/main/res/drawable/ic_paperclip.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="12dp"
+ android:width="12dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <group>
+ <path
+ android:fillColor="?attr/action_icon_color"
+ android:pathData="M16.5,6V17.5A4,4 0 0,1 12.5,21.5A4,4 0 0,1 8.5,17.5V5A2.5,2.5 0 0,1 11,2.5A2.5,2.5 0 0,1 13.5,5V15.5A1,1 0 0,1 12.5,16.5A1,1 0 0,1 11.5,15.5V6H10V15.5A2.5,2.5 0 0,0 12.5,18A2.5,2.5 0 0,0 15,15.5V5A4,4 0 0,0 11,1A4,4 0 0,0 7,5V17.5A5.5,5.5 0 0,0 12.5,23A5.5,5.5 0 0,0 18,17.5V6H16.5Z" />
+
+ </group>
+
+</vector>
diff --git a/core/src/main/res/drawable/ic_sliders.xml b/core/src/main/res/drawable/ic_sliders.xml
deleted file mode 100644
index c6f3de7b4..000000000
--- a/core/src/main/res/drawable/ic_sliders.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="?attr/action_icon_color" android:pathData="M3,17V19H9V17H3M3,5V7H13V5H3M13,21V19H21V17H13V15H11V21H13M7,9V11H3V13H7V15H9V9H7M21,13V11H11V13H21M15,9H17V7H21V5H17V3H15V9Z"/>
-</vector>
diff --git a/core/src/main/res/layout/refresh_action_view.xml b/core/src/main/res/layout/refresh_action_view.xml
deleted file mode 100644
index d5b88922e..000000000
--- a/core/src/main/res/layout/refresh_action_view.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<ProgressBar
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_gravity="end"
- android:padding="8dp"
- android:indeterminateOnly="true"/>
diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml
index 50afe630b..7eeab886a 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -246,52 +246,10 @@
<item>DownloadsSection</item>
</string-array>
- <!-- sort for podcast screen, not for queue -->
- <string-array name="feed_episodes_sort_options">
- <item>@string/sort_date_new_old</item>
- <item>@string/sort_date_old_new</item>
- <item>@string/sort_title_a_z</item>
- <item>@string/sort_title_z_a</item>
- <item>@string/sort_duration_short_long</item>
- <item>@string/sort_duration_long_short</item>
- </string-array>
-
- <!-- sort for local feed screen -->
- <string-array name="local_feed_episodes_sort_options">
- <item>@string/sort_date_new_old</item>
- <item>@string/sort_date_old_new</item>
- <item>@string/sort_title_a_z</item>
- <item>@string/sort_title_z_a</item>
- <item>@string/sort_duration_short_long</item>
- <item>@string/sort_duration_long_short</item>
- <item>@string/sort_filename_a_z</item>
- <item>@string/sort_filename_z_a</item>
- </string-array>
-
- <string-array name="feed_episodes_sort_values">
- <item>DATE_NEW_OLD</item>
- <item>DATE_OLD_NEW</item>
- <item>EPISODE_TITLE_A_Z</item>
- <item>EPISODE_TITLE_Z_A</item>
- <item>DURATION_SHORT_LONG</item>
- <item>DURATION_LONG_SHORT</item>
- </string-array>
-
- <string-array name="local_feed_episodes_sort_values">
- <item>DATE_NEW_OLD</item>
- <item>DATE_OLD_NEW</item>
- <item>EPISODE_TITLE_A_Z</item>
- <item>EPISODE_TITLE_Z_A</item>
- <item>DURATION_SHORT_LONG</item>
- <item>DURATION_LONG_SHORT</item>
- <item>EPISODE_FILENAME_A_Z</item>
- <item>EPISODE_FILENAME_Z_A</item>
- </string-array>
-
- <string-array name="compact_notification_buttons_options">
- <item>@string/rewind_label</item>
- <item>@string/fast_forward_label</item>
+ <string-array name="full_notification_buttons_options">
<item>@string/skip_episode_label</item>
+ <item>@string/next_chapter</item>
+ <item>@string/playback_speed</item>
</string-array>
<string-array name="default_page_values">
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
index a08d0897d..62775b84b 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.core.feed;
import de.danoeh.antennapod.model.feed.FeedItem;
-import de.danoeh.antennapod.model.feed.FeedMedia;
import org.junit.Before;
import org.junit.Test;
@@ -11,13 +10,11 @@ import java.util.Date;
import static de.danoeh.antennapod.core.feed.FeedItemMother.anyFeedItemWithImage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
public class FeedItemTest {
private static final String TEXT_LONG = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
private static final String TEXT_SHORT = "Lorem ipsum";
- private static final long ONE_HOUR = 1000L * 3600L;
private FeedItem original;
private FeedItem changedFeedItem;
@@ -139,36 +136,4 @@ public class FeedItemTest {
item.setDescriptionIfLonger(contentEncoded);
assertEquals(TEXT_LONG, item.getDescription());
}
-
- @Test
- public void testAutoDownloadBackoff() {
- FeedItem item = new FeedItem();
- item.setMedia(new FeedMedia(item, "https://example.com/file.mp3", 0, "audio/mpeg"));
-
- long now = ONE_HOUR; // In reality, this is System.currentTimeMillis()
- assertTrue(item.isAutoDownloadable(now));
- item.increaseFailedAutoDownloadAttempts(now);
- assertFalse(item.isAutoDownloadable(now));
-
- now += ONE_HOUR;
- assertTrue(item.isAutoDownloadable(now));
- item.increaseFailedAutoDownloadAttempts(now);
- assertFalse(item.isAutoDownloadable(now));
-
- now += ONE_HOUR;
- assertFalse(item.isAutoDownloadable(now)); // Should backoff, so more than 1 hour needed
-
- now += ONE_HOUR;
- assertTrue(item.isAutoDownloadable(now)); // Now it's enough
- item.increaseFailedAutoDownloadAttempts(now);
- item.increaseFailedAutoDownloadAttempts(now);
- item.increaseFailedAutoDownloadAttempts(now);
-
- now += 1000L * ONE_HOUR;
- assertFalse(item.isAutoDownloadable(now)); // Should have given up
- item.increaseFailedAutoDownloadAttempts(now);
-
- now += 1000L * ONE_HOUR;
- assertFalse(item.isAutoDownloadable(now)); // Still given up
- }
}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesserTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesserTest.java
index ac7cdee1f..f3c993066 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesserTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/FeedItemDuplicateGuesserTest.java
@@ -44,6 +44,9 @@ public class FeedItemDuplicateGuesserTest {
assertFalse(FeedItemDuplicateGuesser.seemDuplicates(
item("id1", "Title", "example.com/episode1", 10, 5 * MINUTES, "audio/*"),
item("id2", "Title", "example.com/episode2", 10, 5 * MINUTES, "video/*")));
+ assertTrue(FeedItemDuplicateGuesser.seemDuplicates(
+ item("id1", "Title", "example.com/episode1", 10, 5 * MINUTES, "audio/mpeg"),
+ item("id2", "Title", "example.com/episode2", 10, 5 * MINUTES, "audio/mp3")));
assertFalse(FeedItemDuplicateGuesser.seemDuplicates(
item("id1", "Title", "example.com/episode1", 5 * DAYS, 5 * MINUTES, "audio/*"),
item("id2", "Title", "example.com/episode2", 2 * DAYS, 5 * MINUTES, "audio/*")));
diff --git a/model/src/main/java/de/danoeh/antennapod/model/feed/FeedItem.java b/model/src/main/java/de/danoeh/antennapod/model/feed/FeedItem.java
index a8570ea4e..be0760bdb 100644
--- a/model/src/main/java/de/danoeh/antennapod/model/feed/FeedItem.java
+++ b/model/src/main/java/de/danoeh/antennapod/model/feed/FeedItem.java
@@ -11,7 +11,6 @@ import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.TimeUnit;
/**
* Item (episode) within a feed.
@@ -65,7 +64,7 @@ public class FeedItem extends FeedComponent implements Serializable {
private transient List<Chapter> chapters;
private String imageUrl;
- private long autoDownload = 1;
+ private boolean autoDownloadEnabled = true;
/**
* Any tags assigned to this item
@@ -82,7 +81,7 @@ public class FeedItem extends FeedComponent implements Serializable {
* */
public FeedItem(long id, String title, String link, Date pubDate, String paymentLink, long feedId,
boolean hasChapters, String imageUrl, int state,
- String itemIdentifier, long autoDownload, String podcastIndexChapterUrl) {
+ String itemIdentifier, boolean autoDownloadEnabled, String podcastIndexChapterUrl) {
this.id = id;
this.title = title;
this.link = link;
@@ -93,7 +92,7 @@ public class FeedItem extends FeedComponent implements Serializable {
this.imageUrl = imageUrl;
this.state = state;
this.itemIdentifier = itemIdentifier;
- this.autoDownload = autoDownload;
+ this.autoDownloadEnabled = autoDownloadEnabled;
this.podcastIndexChapterUrl = podcastIndexChapterUrl;
}
@@ -361,50 +360,11 @@ public class FeedItem extends FeedComponent implements Serializable {
}
public void disableAutoDownload() {
- this.autoDownload = 0;
+ this.autoDownloadEnabled = false;
}
- public long getAutoDownloadAttemptsAndTime() {
- return autoDownload;
- }
-
- public int getFailedAutoDownloadAttempts() {
- // 0: auto download disabled
- // 1: auto download enabled (default)
- // > 1: auto download enabled, timestamp of last failed attempt, last digit denotes number of failed attempts
- if (autoDownload <= 1) {
- return 0;
- }
- int failedAttempts = (int)(autoDownload % 10);
- if (failedAttempts == 0) {
- failedAttempts = 10;
- }
- return failedAttempts;
- }
-
- public void increaseFailedAutoDownloadAttempts(long now) {
- if (autoDownload == 0) {
- return; // Don't re-enable
- }
- int failedAttempts = getFailedAutoDownloadAttempts() + 1;
- if (failedAttempts >= 5) {
- disableAutoDownload(); // giving up
- } else {
- autoDownload = (now / 10) * 10 + failedAttempts;
- }
- }
-
- public boolean isAutoDownloadable(long now) {
- if (media == null || media.isDownloaded() || autoDownload == 0) {
- return false;
- }
- if (autoDownload == 1) {
- return true; // Never failed
- }
- int failedAttempts = getFailedAutoDownloadAttempts();
- long waitingTime = TimeUnit.HOURS.toMillis((long) Math.pow(2, failedAttempts - 1));
- long lastAttempt = (autoDownload / 10) * 10;
- return now >= (lastAttempt + waitingTime);
+ public boolean isAutoDownloadEnabled() {
+ return this.autoDownloadEnabled;
}
public boolean isDownloaded() {
diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBUpgrader.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBUpgrader.java
index e4a1f5a3d..cbac417a9 100644
--- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBUpgrader.java
+++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/DBUpgrader.java
@@ -121,9 +121,9 @@ class DBUpgrader {
}
if (oldVersion <= 14) {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD_ATTEMPTS + " INTEGER");
+ + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD_ENABLED + " INTEGER");
db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " SET " + PodDBAdapter.KEY_AUTO_DOWNLOAD_ATTEMPTS + " = "
+ + " SET " + PodDBAdapter.KEY_AUTO_DOWNLOAD_ENABLED + " = "
+ "(SELECT " + PodDBAdapter.KEY_AUTO_DOWNLOAD_ENABLED
+ " FROM " + PodDBAdapter.TABLE_NAME_FEEDS
+ " WHERE " + PodDBAdapter.TABLE_NAME_FEEDS + "." + PodDBAdapter.KEY_ID
diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java
index 96d80c209..ccef19dc1 100644
--- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java
+++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/PodDBAdapter.java
@@ -95,7 +95,6 @@ public class PodDBAdapter {
public static final String KEY_REASON_DETAILED = "reason_detailed";
public static final String KEY_DOWNLOADSTATUS_TITLE = "title";
public static final String KEY_PLAYBACK_COMPLETION_DATE = "playback_completion_date";
- public static final String KEY_AUTO_DOWNLOAD_ATTEMPTS = "auto_download";
public static final String KEY_AUTO_DOWNLOAD_ENABLED = "auto_download"; // Both tables use the same key
public static final String KEY_KEEP_UPDATED = "keep_updated";
public static final String KEY_AUTO_DELETE_ACTION = "auto_delete_action";
@@ -171,7 +170,7 @@ public class PodDBAdapter {
+ KEY_MEDIA + " INTEGER," + KEY_FEED + " INTEGER,"
+ KEY_HAS_CHAPTERS + " INTEGER," + KEY_ITEM_IDENTIFIER + " TEXT,"
+ KEY_IMAGE_URL + " TEXT,"
- + KEY_AUTO_DOWNLOAD_ATTEMPTS + " INTEGER,"
+ + KEY_AUTO_DOWNLOAD_ENABLED + " INTEGER,"
+ KEY_PODCASTINDEX_CHAPTER_URL + " TEXT)";
private static final String CREATE_TABLE_FEED_MEDIA = "CREATE TABLE "
@@ -259,7 +258,7 @@ public class PodDBAdapter {
+ TABLE_NAME_FEED_ITEMS + "." + KEY_HAS_CHAPTERS + ", "
+ TABLE_NAME_FEED_ITEMS + "." + KEY_ITEM_IDENTIFIER + ", "
+ TABLE_NAME_FEED_ITEMS + "." + KEY_IMAGE_URL + ", "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_AUTO_DOWNLOAD_ATTEMPTS + ", "
+ + TABLE_NAME_FEED_ITEMS + "." + KEY_AUTO_DOWNLOAD_ENABLED + ", "
+ TABLE_NAME_FEED_ITEMS + "." + KEY_PODCASTINDEX_CHAPTER_URL;
private static final String KEYS_FEED_MEDIA =
@@ -652,7 +651,7 @@ public class PodDBAdapter {
}
values.put(KEY_HAS_CHAPTERS, item.getChapters() != null || item.hasChapters());
values.put(KEY_ITEM_IDENTIFIER, item.getItemIdentifier());
- values.put(KEY_AUTO_DOWNLOAD_ATTEMPTS, item.getAutoDownloadAttemptsAndTime());
+ values.put(KEY_AUTO_DOWNLOAD_ENABLED, item.isAutoDownloadEnabled());
values.put(KEY_IMAGE_URL, item.getImageUrl());
values.put(KEY_PODCASTINDEX_CHAPTER_URL, item.getPodcastIndexChapterUrl());
diff --git a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/mapper/FeedItemCursorMapper.java b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/mapper/FeedItemCursorMapper.java
index fcf51e31e..c2c9b89d4 100644
--- a/storage/database/src/main/java/de/danoeh/antennapod/storage/database/mapper/FeedItemCursorMapper.java
+++ b/storage/database/src/main/java/de/danoeh/antennapod/storage/database/mapper/FeedItemCursorMapper.java
@@ -25,7 +25,7 @@ public abstract class FeedItemCursorMapper {
int indexHasChapters = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_HAS_CHAPTERS);
int indexRead = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_READ);
int indexItemIdentifier = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_ITEM_IDENTIFIER);
- int indexAutoDownload = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_AUTO_DOWNLOAD_ATTEMPTS);
+ int indexAutoDownload = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_AUTO_DOWNLOAD_ENABLED);
int indexImageUrl = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_IMAGE_URL);
int indexPodcastIndexChapterUrl = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_PODCASTINDEX_CHAPTER_URL);
@@ -38,11 +38,11 @@ public abstract class FeedItemCursorMapper {
boolean hasChapters = cursor.getInt(indexHasChapters) > 0;
int state = cursor.getInt(indexRead);
String itemIdentifier = cursor.getString(indexItemIdentifier);
- long autoDownload = cursor.getLong(indexAutoDownload);
+ boolean autoDownloadEnabled = cursor.getLong(indexAutoDownload) > 0;
String imageUrl = cursor.getString(indexImageUrl);
String podcastIndexChapterUrl = cursor.getString(indexPodcastIndexChapterUrl);
return new FeedItem(id, title, link, pubDate, paymentLink, feedId,
- hasChapters, imageUrl, state, itemIdentifier, autoDownload, podcastIndexChapterUrl);
+ hasChapters, imageUrl, state, itemIdentifier, autoDownloadEnabled, podcastIndexChapterUrl);
}
}
diff --git a/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java b/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java
index 80a58525d..b454ee5a1 100644
--- a/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java
+++ b/storage/preferences/src/main/java/de/danoeh/antennapod/storage/preferences/UserPreferences.java
@@ -54,7 +54,7 @@ public class UserPreferences {
public static final String PREF_USE_EPISODE_COVER = "prefEpisodeCover";
public static final String PREF_SHOW_TIME_LEFT = "showTimeLeft";
private static final String PREF_PERSISTENT_NOTIFICATION = "prefPersistNotify";
- public static final String PREF_COMPACT_NOTIFICATION_BUTTONS = "prefCompactNotificationButtons";
+ public static final String PREF_FULL_NOTIFICATION_BUTTONS = "prefFullNotificationButtons";
private static final String PREF_SHOW_DOWNLOAD_REPORT = "prefShowDownloadReport";
public static final String PREF_DEFAULT_PAGE = "prefDefaultPage";
public static final String PREF_FILTER_FEED = "prefSubscriptionsFilter";
@@ -67,6 +67,10 @@ public class UserPreferences {
private static final String PREF_DOWNLOADS_SORTED_ORDER = "prefDownloadSortedOrder";
private static final String PREF_INBOX_SORTED_ORDER = "prefInboxSortedOrder";
+ // Episode
+ public static final String PREF_SORT_ALL_EPISODES = "prefEpisodesSort";
+ public static final String PREF_FILTER_ALL_EPISODES = "prefEpisodesFilter";
+
// Playback
public static final String PREF_PAUSE_ON_HEADSET_DISCONNECT = "prefPauseOnHeadsetDisconnect";
public static final String PREF_UNPAUSE_ON_HEADSET_RECONNECT = "prefUnpauseOnHeadsetReconnect";
@@ -123,9 +127,12 @@ public class UserPreferences {
public static final int EPISODE_CLEANUP_DEFAULT = 0;
// Constants
- private static final int NOTIFICATION_BUTTON_REWIND = 0;
- private static final int NOTIFICATION_BUTTON_FAST_FORWARD = 1;
- private static final int NOTIFICATION_BUTTON_SKIP = 2;
+ public static final int NOTIFICATION_BUTTON_REWIND = 0;
+ public static final int NOTIFICATION_BUTTON_FAST_FORWARD = 1;
+ public static final int NOTIFICATION_BUTTON_SKIP = 2;
+
+ public static final int NOTIFICATION_BUTTON_NEXT_CHAPTER = 3;
+ public static final int NOTIFICATION_BUTTON_PLAYBACK_SPEED = 4;
public static final int EPISODE_CACHE_SIZE_UNLIMITED = -1;
public static final int FEED_ORDER_COUNTER = 0;
public static final int FEED_ORDER_ALPHABETICAL = 1;
@@ -191,11 +198,11 @@ public class UserPreferences {
return new ArrayList<>(Arrays.asList(TextUtils.split(hiddenItems, ",")));
}
- public static List<Integer> getCompactNotificationButtons() {
+ public static List<Integer> getFullNotificationButtons() {
String[] buttons = TextUtils.split(
- prefs.getString(PREF_COMPACT_NOTIFICATION_BUTTONS,
- NOTIFICATION_BUTTON_REWIND + "," + NOTIFICATION_BUTTON_FAST_FORWARD),
- ",");
+ prefs.getString(PREF_FULL_NOTIFICATION_BUTTONS,
+ NOTIFICATION_BUTTON_SKIP + "," + NOTIFICATION_BUTTON_PLAYBACK_SPEED), ",");
+
List<Integer> notificationButtons = new ArrayList<>();
for (String button : buttons) {
notificationButtons.add(Integer.parseInt(button));
@@ -204,27 +211,28 @@ public class UserPreferences {
}
/**
- * Helper function to return whether the specified button should be shown on compact
+ * Helper function to return whether the specified button should be shown on full
* notifications.
*
- * @param buttonId Either NOTIFICATION_BUTTON_REWIND, NOTIFICATION_BUTTON_FAST_FORWARD or
- * NOTIFICATION_BUTTON_SKIP.
+ * @param buttonId Either NOTIFICATION_BUTTON_REWIND, NOTIFICATION_BUTTON_FAST_FORWARD,
+ * NOTIFICATION_BUTTON_SKIP, NOTIFICATION_BUTTON_PLAYBACK_SPEED
+ * or NOTIFICATION_BUTTON_NEXT_CHAPTER.
* @return {@code true} if button should be shown, {@code false} otherwise
*/
- private static boolean showButtonOnCompactNotification(int buttonId) {
- return getCompactNotificationButtons().contains(buttonId);
+ private static boolean showButtonOnFullNotification(int buttonId) {
+ return getFullNotificationButtons().contains(buttonId);
}
- public static boolean showRewindOnCompactNotification() {
- return showButtonOnCompactNotification(NOTIFICATION_BUTTON_REWIND);
+ public static boolean showSkipOnFullNotification() {
+ return showButtonOnFullNotification(NOTIFICATION_BUTTON_SKIP);
}
- public static boolean showFastForwardOnCompactNotification() {
- return showButtonOnCompactNotification(NOTIFICATION_BUTTON_FAST_FORWARD);
+ public static boolean showNextChapterOnFullNotification() {
+ return showButtonOnFullNotification(NOTIFICATION_BUTTON_NEXT_CHAPTER);
}
- public static boolean showSkipOnCompactNotification() {
- return showButtonOnCompactNotification(NOTIFICATION_BUTTON_SKIP);
+ public static boolean showPlaybackSpeedOnFullNotification() {
+ return showButtonOnFullNotification(NOTIFICATION_BUTTON_PLAYBACK_SPEED);
}
public static int getFeedOrder() {
@@ -649,10 +657,10 @@ public class UserPreferences {
.apply();
}
- public static void setCompactNotificationButtons(List<Integer> items) {
+ public static void setFullNotificationButtons(List<Integer> items) {
String str = TextUtils.join(",", items);
prefs.edit()
- .putString(PREF_COMPACT_NOTIFICATION_BUTTONS, str)
+ .putString(PREF_FULL_NOTIFICATION_BUTTONS, str)
.apply();
}
@@ -867,4 +875,21 @@ public class UserPreferences {
public static boolean shouldShowSubscriptionTitle() {
return prefs.getBoolean(PREF_SUBSCRIPTION_TITLE, false);
}
+
+ public static void setAllEpisodesSortOrder(SortOrder s) {
+ prefs.edit().putString(PREF_SORT_ALL_EPISODES, "" + s.code).apply();
+ }
+
+ public static SortOrder getAllEpisodesSortOrder() {
+ return SortOrder.fromCodeString(prefs.getString(PREF_SORT_ALL_EPISODES,
+ "" + SortOrder.DATE_NEW_OLD.code));
+ }
+
+ public static String getPrefFilterAllEpisodes() {
+ return prefs.getString(PREF_FILTER_ALL_EPISODES, "");
+ }
+
+ public static void setPrefFilterAllEpisodes(String filter) {
+ prefs.edit().putString(PREF_FILTER_ALL_EPISODES, filter).apply();
+ }
}
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoActivity.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoActivity.java
index 50b34b6e3..758eaeac7 100644
--- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoActivity.java
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/EchoActivity.java
@@ -10,8 +10,8 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
+import android.text.format.DateFormat;
import android.util.Log;
-import android.util.Pair;
import android.view.KeyEvent;
import android.view.View;
import androidx.annotation.NonNull;
@@ -35,6 +35,7 @@ import de.danoeh.antennapod.ui.echo.screens.FinalShareScreen;
import de.danoeh.antennapod.ui.echo.screens.RotatingSquaresScreen;
import de.danoeh.antennapod.ui.echo.screens.StripesScreen;
import de.danoeh.antennapod.ui.echo.screens.WaveformScreen;
+import de.danoeh.antennapod.ui.echo.screens.WavesScreen;
import io.reactivex.Flowable;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
@@ -53,6 +54,7 @@ import java.util.Locale;
import java.util.concurrent.TimeUnit;
public class EchoActivity extends AppCompatActivity {
+ public static final int RELEASE_YEAR = 2023;
private static final String TAG = "EchoActivity";
private static final int NUM_SCREENS = 7;
private static final int SHARE_SIZE = 1000;
@@ -67,6 +69,7 @@ public class EchoActivity extends AppCompatActivity {
private long timeTouchDown;
private long timeLastFrame;
private Disposable disposable;
+ private Disposable disposableFavorite;
private long totalTime = 0;
private int totalActivePodcasts = 0;
@@ -77,7 +80,8 @@ public class EchoActivity extends AppCompatActivity {
private long queueSecondsLeft = 0;
private long timeBetweenReleaseAndPlay = 0;
private long oldestDate = 0;
- private final ArrayList<Pair<String, Drawable>> favoritePods = new ArrayList<>();
+ private final ArrayList<String> favoritePodNames = new ArrayList<>();
+ private final ArrayList<Drawable> favoritePodImages = new ArrayList<>();
@SuppressLint("ClickableViewAccessibility")
@Override
@@ -134,7 +138,7 @@ public class EchoActivity extends AppCompatActivity {
new ShareCompat.IntentBuilder(this)
.setType("image/png")
.addStream(fileUri)
- .setText(getString(R.string.echo_share, 2023))
+ .setText(getString(R.string.echo_share, RELEASE_YEAR))
.setChooserTitle(R.string.share_file_label)
.startChooser();
} catch (Exception e) {
@@ -176,6 +180,9 @@ public class EchoActivity extends AppCompatActivity {
if (disposable != null) {
disposable.dispose();
}
+ if (disposableFavorite != null) {
+ disposableFavorite.dispose();
+ }
}
private void loadScreen(int screen, boolean force) {
@@ -190,7 +197,7 @@ public class EchoActivity extends AppCompatActivity {
switch (currentScreen) {
case 0:
viewBinding.aboveLabel.setText(R.string.echo_intro_your_year);
- viewBinding.largeLabel.setText(String.format(getEchoLanguage(), "%d", 2023));
+ viewBinding.largeLabel.setText(String.format(getEchoLanguage(), "%d", RELEASE_YEAR));
viewBinding.belowLabel.setText(R.string.echo_intro_in_podcasts);
viewBinding.smallLabel.setText(R.string.echo_intro_locally);
currentDrawable = new BubbleScreen(this);
@@ -207,20 +214,27 @@ public class EchoActivity extends AppCompatActivity {
viewBinding.largeLabel.setText(String.format(getEchoLanguage(), "%d", queueSecondsLeft / 3600));
viewBinding.belowLabel.setText(getResources().getQuantityString(
R.plurals.echo_queue_hours_waiting, queueNumEpisodes, queueNumEpisodes));
- int daysUntil2024 = Math.max(356 - Calendar.getInstance().get(Calendar.DAY_OF_YEAR) + 1, 1);
- long secondsPerDay = queueSecondsLeft / daysUntil2024;
+ Calendar dec31 = Calendar.getInstance();
+ dec31.set(Calendar.DAY_OF_MONTH, 31);
+ dec31.set(Calendar.MONTH, Calendar.DECEMBER);
+ int daysUntilNextYear = Math.max(1,
+ dec31.get(Calendar.DAY_OF_YEAR) - Calendar.getInstance().get(Calendar.DAY_OF_YEAR) + 1);
+ long secondsPerDay = queueSecondsLeft / daysUntilNextYear;
String timePerDay = Converter.getDurationStringLocalized(
getLocalizedResources(this, getEchoLanguage()), secondsPerDay * 1000, true);
double hoursPerDay = (double) (secondsPerDay / 3600);
+ int nextYear = RELEASE_YEAR + 1;
if (hoursPerDay < 1.5) {
viewBinding.aboveLabel.setText(R.string.echo_queue_title_clean);
- viewBinding.smallLabel.setText(getString(R.string.echo_queue_hours_clean, timePerDay, 2024));
+ viewBinding.smallLabel.setText(
+ getString(R.string.echo_queue_hours_clean, timePerDay, nextYear));
} else if (hoursPerDay <= 24) {
viewBinding.aboveLabel.setText(R.string.echo_queue_title_many);
- viewBinding.smallLabel.setText(getString(R.string.echo_queue_hours_normal, timePerDay, 2024));
+ viewBinding.smallLabel.setText(
+ getString(R.string.echo_queue_hours_normal, timePerDay, nextYear));
} else {
viewBinding.aboveLabel.setText(R.string.echo_queue_title_many);
- viewBinding.smallLabel.setText(getString(R.string.echo_queue_hours_much, timePerDay, 2024));
+ viewBinding.smallLabel.setText(getString(R.string.echo_queue_hours_much, timePerDay, nextYear));
}
currentDrawable = new StripesScreen(this);
break;
@@ -257,13 +271,14 @@ public class EchoActivity extends AppCompatActivity {
viewBinding.smallLabel.setText(getString(R.string.echo_hoarder_comment_clean,
percentagePlayed, totalActivePodcasts));
}
- currentDrawable = new StripesScreen(this);
+ currentDrawable = new WavesScreen(this);
break;
case 5:
viewBinding.aboveLabel.setText("");
viewBinding.largeLabel.setText(R.string.echo_thanks_large);
if (oldestDate < jan1()) {
- SimpleDateFormat dateFormat = new SimpleDateFormat("MMMM yyyy", getEchoLanguage());
+ String skeleton = DateFormat.getBestDateTimePattern(getEchoLanguage(), "MMMM yyyy");
+ SimpleDateFormat dateFormat = new SimpleDateFormat(skeleton, getEchoLanguage());
String dateFrom = dateFormat.format(new Date(oldestDate));
viewBinding.belowLabel.setText(getString(R.string.echo_thanks_we_are_glad_old, dateFrom));
} else {
@@ -277,7 +292,7 @@ public class EchoActivity extends AppCompatActivity {
viewBinding.largeLabel.setText("");
viewBinding.belowLabel.setText("");
viewBinding.smallLabel.setText("");
- currentDrawable = new FinalShareScreen(this, favoritePods);
+ currentDrawable = new FinalShareScreen(this, favoritePodNames, favoritePodImages);
break;
default: // Keep
}
@@ -312,7 +327,7 @@ public class EchoActivity extends AppCompatActivity {
date.set(Calendar.MILLISECOND, 0);
date.set(Calendar.DAY_OF_MONTH, 1);
date.set(Calendar.MONTH, 0);
- date.set(Calendar.YEAR, 2023);
+ date.set(Calendar.YEAR, RELEASE_YEAR);
return date.getTimeInMillis();
}
@@ -329,25 +344,11 @@ public class EchoActivity extends AppCompatActivity {
Collections.sort(statisticsData.feedTime, (item1, item2) ->
Long.compare(item2.timePlayed, item1.timePlayed));
- favoritePods.clear();
+ favoritePodNames.clear();
for (int i = 0; i < 5 && i < statisticsData.feedTime.size(); i++) {
- BitmapDrawable cover = new BitmapDrawable(getResources(), (Bitmap) null);
- try {
- final int size = SHARE_SIZE / 3;
- final int radius = (i == 0) ? (size / 16) : (size / 8);
- cover = new BitmapDrawable(getResources(), Glide.with(this)
- .asBitmap()
- .load(statisticsData.feedTime.get(i).feed.getImageUrl())
- .apply(new RequestOptions()
- .fitCenter()
- .transform(new RoundedCorners(radius)))
- .submit(size, size)
- .get(1, TimeUnit.SECONDS));
- } catch (Exception e) {
- e.printStackTrace();
- }
- favoritePods.add(new Pair<>(statisticsData.feedTime.get(i).feed.getTitle(), cover));
+ favoritePodNames.add(statisticsData.feedTime.get(i).feed.getTitle());
}
+ loadFavoritePodImages(statisticsData);
totalActivePodcasts = 0;
playedActivePodcasts = 0;
@@ -396,4 +397,37 @@ public class EchoActivity extends AppCompatActivity {
.subscribe(result -> loadScreen(currentScreen, true),
error -> Log.e(TAG, Log.getStackTraceString(error)));
}
+
+ void loadFavoritePodImages(DBReader.StatisticsResult statisticsData) {
+ if (disposableFavorite != null) {
+ disposableFavorite.dispose();
+ }
+ disposableFavorite = Observable.fromCallable(
+ () -> {
+ favoritePodImages.clear();
+ for (int i = 0; i < 5 && i < statisticsData.feedTime.size(); i++) {
+ BitmapDrawable cover = new BitmapDrawable(getResources(), (Bitmap) null);
+ try {
+ final int size = SHARE_SIZE / 3;
+ final int radius = (i == 0) ? (size / 16) : (size / 8);
+ cover = new BitmapDrawable(getResources(), Glide.with(this)
+ .asBitmap()
+ .load(statisticsData.feedTime.get(i).feed.getImageUrl())
+ .apply(new RequestOptions()
+ .fitCenter()
+ .transform(new RoundedCorners(radius)))
+ .submit(size, size)
+ .get(5, TimeUnit.SECONDS));
+ } catch (Exception e) {
+ Log.d(TAG, "Loading cover: " + e.getMessage());
+ }
+ favoritePodImages.add(cover);
+ }
+ return statisticsData;
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> { },
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
}
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/FinalShareScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/FinalShareScreen.java
index 87b9cbd53..4af8941d0 100644
--- a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/FinalShareScreen.java
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/FinalShareScreen.java
@@ -7,9 +7,9 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
-import android.util.Pair;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.res.ResourcesCompat;
+import de.danoeh.antennapod.ui.echo.EchoActivity;
import de.danoeh.antennapod.ui.echo.R;
import java.util.ArrayList;
@@ -19,16 +19,21 @@ public class FinalShareScreen extends BubbleScreen {
private final Paint paintTextMain;
private final Paint paintCoverBorder;
private final String heading;
+ private final String year;
private final Drawable logo;
- private final ArrayList<Pair<String, Drawable>> favoritePods;
+ private final ArrayList<String> favoritePodNames;
+ private final ArrayList<Drawable> favoritePodImages;
private final Typeface typefaceNormal;
private final Typeface typefaceBold;
- public FinalShareScreen(Context context, ArrayList<Pair<String, Drawable>> favoritePods) {
+ public FinalShareScreen(Context context,
+ ArrayList<String> favoritePodNames, ArrayList<Drawable> favoritePodImages) {
super(context);
this.heading = context.getString(R.string.echo_share_heading);
this.logo = AppCompatResources.getDrawable(context, R.drawable.echo);
- this.favoritePods = favoritePods;
+ this.favoritePodNames = favoritePodNames;
+ this.favoritePodImages = favoritePodImages;
+ this.year = String.valueOf(EchoActivity.RELEASE_YEAR);
typefaceNormal = ResourcesCompat.getFont(context, R.font.sarabun_regular);
typefaceBold = ResourcesCompat.getFont(context, R.font.sarabun_semi_bold);
paintTextMain = new Paint();
@@ -49,12 +54,11 @@ public class FinalShareScreen extends BubbleScreen {
paintTextMain.setTextSize(headingSize);
canvas.drawText(heading, innerBoxX + 0.5f * innerBoxSize, innerBoxY + headingSize, paintTextMain);
paintTextMain.setTextSize(0.12f * innerBoxSize);
- canvas.drawText("2023", innerBoxX + 0.8f * innerBoxSize, innerBoxY + 0.25f * innerBoxSize, paintTextMain);
+ canvas.drawText(year, innerBoxX + 0.8f * innerBoxSize, innerBoxY + 0.25f * innerBoxSize, paintTextMain);
- paintTextMain.setTextAlign(Paint.Align.LEFT);
float fontSizePods = innerBoxSize / 18; // First one only
float textY = innerBoxY + 0.62f * innerBoxSize;
- for (int i = 0; i < favoritePods.size(); i++) {
+ for (int i = 0; i < favoritePodNames.size(); i++) {
float coverSize = (i == 0) ? (0.4f * innerBoxSize) : (0.2f * innerBoxSize);
float coverX = COVER_POSITIONS[i][0];
float coverY = COVER_POSITIONS[i][1];
@@ -68,12 +72,20 @@ public class FinalShareScreen extends BubbleScreen {
logo1Pos.inset((int) (0.003f * innerBoxSize), (int) (0.003f * innerBoxSize));
Rect pos = new Rect();
logo1Pos.round(pos);
- favoritePods.get(i).second.setBounds(pos);
- favoritePods.get(i).second.draw(canvas);
+ if (favoritePodImages.size() > i) {
+ favoritePodImages.get(i).setBounds(pos);
+ favoritePodImages.get(i).draw(canvas);
+ } else {
+ canvas.drawText(" ...", pos.left, pos.centerY(), paintTextMain);
+ }
+ paintTextMain.setTextAlign(Paint.Align.CENTER);
paintTextMain.setTextSize(fontSizePods);
- canvas.drawText((i + 1) + ".", innerBoxX, textY, paintTextMain);
- canvas.drawText(favoritePods.get(i).first, innerBoxX + 0.055f * innerBoxSize, textY, paintTextMain);
+ final float numberWidth = 0.06f * innerBoxSize;
+ canvas.drawText((i + 1) + ".", innerBoxX + numberWidth / 2, textY, paintTextMain);
+ paintTextMain.setTextAlign(Paint.Align.LEFT);
+ String ellipsizedTitle = ellipsize(favoritePodNames.get(i), paintTextMain, innerBoxSize - numberWidth);
+ canvas.drawText(ellipsizedTitle, innerBoxX + numberWidth, textY, paintTextMain);
fontSizePods = innerBoxSize / 24; // Starting with second text is smaller
textY += 1.3f * fontSizePods;
paintTextMain.setTypeface(typefaceNormal);
@@ -86,4 +98,14 @@ public class FinalShareScreen extends BubbleScreen {
(int) (innerBoxY + innerBoxSize));
logo.draw(canvas);
}
+
+ String ellipsize(String string, Paint paint, float maxWidth) {
+ if (paint.measureText(string) <= maxWidth) {
+ return string;
+ }
+ while (paint.measureText(string + "…") > maxWidth || string.endsWith(" ")) {
+ string = string.substring(0, string.length() - 1);
+ }
+ return string + "…";
+ }
}
diff --git a/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WavesScreen.java b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WavesScreen.java
new file mode 100644
index 000000000..eb521f0a2
--- /dev/null
+++ b/ui/echo/src/main/java/de/danoeh/antennapod/ui/echo/screens/WavesScreen.java
@@ -0,0 +1,38 @@
+package de.danoeh.antennapod.ui.echo.screens;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import androidx.annotation.NonNull;
+
+public class WavesScreen extends BaseScreen {
+ protected static final int NUM_PARTICLES = 10;
+
+ public WavesScreen(Context context) {
+ super(context);
+ paintParticles.setStyle(Paint.Style.STROKE);
+ for (int i = 0; i < NUM_PARTICLES; i++) {
+ particles.add(new Particle(0, 0, 1.0f * i / NUM_PARTICLES, 0));
+ }
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas) {
+ paintParticles.setStrokeWidth(0.05f * getBounds().height());
+ super.draw(canvas);
+ }
+
+ @Override
+ protected void drawParticle(@NonNull Canvas canvas, Particle p, float width, float height,
+ float innerBoxX, float innerBoxY, float innerBoxSize) {
+ canvas.drawCircle(width / 2, 1.1f * height, (float) (p.positionZ * 1.2f * height), paintParticles);
+ }
+
+ @Override
+ protected void particleTick(Particle p, long timeSinceLastFrame) {
+ p.positionZ += 0.00005 * timeSinceLastFrame;
+ if (p.positionZ > 1f) {
+ p.positionZ -= 1f;
+ }
+ }
+}
diff --git a/ui/i18n/src/main/res/values/strings.xml b/ui/i18n/src/main/res/values/strings.xml
index c1872969a..e40e03035 100644
--- a/ui/i18n/src/main/res/values/strings.xml
+++ b/ui/i18n/src/main/res/values/strings.xml
@@ -26,7 +26,7 @@
<string name="episode_cache_full_message">The episode cache limit has been reached. You can increase the cache size in the Settings.</string>
<string name="years_statistics_label">Years</string>
<string name="notification_pref_fragment">Notifications</string>
- <string name="recently_played_episodes">Recently played episodes</string>
+ <string name="current_playing_episode">Current</string>
<string name="antennapod_echo" translatable="false">AntennaPod Echo</string>
<string name="antennapod_echo_year" translatable="false">AntennaPod Echo %d</string>
@@ -123,7 +123,6 @@
<string name="description_label">Description</string>
<string name="shownotes_label">Shownotes</string>
<string name="shownotes_contentdescription">swipe up to read shownotes</string>
- <string name="episodes_suffix">\u0020episodes</string>
<string name="close_label">Close</string>
<string name="retry_label">Retry</string>
<string name="auto_download_label">Include in auto downloads</string>
@@ -185,7 +184,6 @@
<string name="remove_feed_label">Remove podcast</string>
<string name="share_label">Share</string>
<string name="share_file_label">Share file</string>
- <string name="share_rss_address_label">RSS address:</string>
<string name="feed_delete_confirmation_msg">Please confirm that you want to delete the podcast \"%1$s\", ALL its episodes (including downloaded episodes), and its statistics.</string>
<string name="feed_delete_confirmation_msg_batch">Please confirm that you want to remove the selected podcasts, ALL their episodes (including downloaded episodes), and its statistics.</string>
<string name="feed_delete_confirmation_local_msg">Please confirm that you want to remove the podcast \"%1$s\" and its statistics. The files in the local source folder will not be deleted.</string>
@@ -490,10 +488,9 @@
<string name="pref_expandNotify_sum">This usually expands the notification to show playback buttons.</string>
<string name="pref_persistNotify_title">Persistent playback controls</string>
<string name="pref_persistNotify_sum">Keep notification and lockscreen controls when playback is paused</string>
- <string name="pref_compact_notification_buttons_title">Set compact notification buttons</string>
- <string name="pref_compact_notification_buttons_sum">Change the playback buttons when the notification is collapsed. The play/pause button is always included.</string>
- <string name="pref_compact_notification_buttons_dialog_title">Select a maximum of %1$d items</string>
- <string name="pref_compact_notification_buttons_dialog_error">You can only select a maximum of %1$d items.</string>
+ <string name="pref_compact_notification_buttons_dialog_error_exact">You must select exactly %1$d items.</string>
+ <string name="pref_full_notification_buttons_title">Set notification buttons</string>
+ <string name="pref_full_notification_buttons_sum">Change the playback buttons on the playback notification.</string>
<string name="pref_enqueue_location_title">Enqueue location</string>
<string name="pref_enqueue_location_sum">Add episodes to: %1$s</string>
<string name="enqueue_location_back">Back</string>
@@ -579,7 +576,7 @@
<string name="database_import_summary">Import AntennaPod database from another device</string>
<string name="opml_import_label">OPML import</string>
<string name="opml_add_podcast_label">Import podcast list (OPML)</string>
- <string name="opml_reader_error">An error has occurred while reading the OPML document:</string>
+ <string name="opml_reader_error">An error has occurred while reading the file. Make sure that you have actually selected an OPML file and that the file is valid.</string>
<string name="opml_import_error_no_file">No file selected!</string>
<string name="select_all_label">Select all</string>
<string name="deselect_all_label">Deselect all</string>
@@ -773,25 +770,7 @@
<string name="not_paused">Not paused</string>
<string name="hide_played_episodes_label">Played</string>
<string name="not_played">Not played</string>
-
- <!-- Sort -->
- <string name="sort_title_a_z">Title (A \u2192 Z)</string>
- <string name="sort_title_z_a">Title (Z \u2192 A)</string>
- <string name="sort_date_new_old">Date (New \u2192 Old)</string>
- <string name="sort_date_old_new">Date (Old \u2192 New)</string>
- <string name="sort_duration_short_long">Duration (Short \u2192 Long)</string>
- <string name="sort_duration_long_short">Duration (Long \u2192 Short)</string>
- <string name="sort_filename_a_z">File Name (A \u2192 Z)</string>
- <string name="sort_filename_z_a">File Name (Z \u2192 A)</string>
-
- <string name="sort_a_z">A \u2192 Z</string>
- <string name="sort_z_a">Z \u2192 A</string>
- <string name="sort_new_old">New \u2192 Old</string>
- <string name="sort_old_new">Old \u2192 New</string>
- <string name="sort_short_long">Short \u2192 Long</string>
- <string name="sort_long_short">Long \u2192 Short</string>
- <string name="sort_small_large">Small \u2192 Large</string>
- <string name="sort_large_small">Large \u2192 Small</string>
+ <string name="filename">File name</string>
<!-- Share episode dialog -->
<string name="share_playback_position_dialog_label">Include playback position</string>
@@ -804,7 +783,6 @@
<!-- Audio controls -->
<string name="audio_controls">Audio controls</string>
<string name="playback_speed">Playback speed</string>
- <string name="audio_effects">Audio effects</string>
<string name="player_switch_to_audio_only">Switch to audio only</string>
<!-- proxy settings -->
diff --git a/ui/png-icons/src/main/res/drawable/ic_notification_next_chapter.xml b/ui/png-icons/src/main/res/drawable/ic_notification_next_chapter.xml
new file mode 100644
index 000000000..cb55e93a9
--- /dev/null
+++ b/ui/png-icons/src/main/res/drawable/ic_notification_next_chapter.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="#fff" android:pathData="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4M8,8V16L13,12M14,8V16H16V8" />
+</vector>
diff --git a/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/downloads/DownloadStatisticsListAdapter.java b/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/downloads/DownloadStatisticsListAdapter.java
index 4839aa891..083a87514 100644
--- a/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/downloads/DownloadStatisticsListAdapter.java
+++ b/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/downloads/DownloadStatisticsListAdapter.java
@@ -10,7 +10,6 @@ import de.danoeh.antennapod.ui.statistics.StatisticsListAdapter;
import de.danoeh.antennapod.ui.statistics.feed.FeedStatisticsDialogFragment;
import java.util.List;
-import java.util.Locale;
/**
* Adapter for the download statistics list.
@@ -45,10 +44,10 @@ public class DownloadStatisticsListAdapter extends StatisticsListAdapter {
@Override
protected void onBindFeedViewHolder(StatisticsHolder holder, StatisticsItem item) {
- holder.value.setText(Formatter.formatShortFileSize(context, item.totalDownloadSize)
- + " • "
- + String.format(Locale.getDefault(), "%d%s",
- item.episodesDownloadCount, context.getString(R.string.episodes_suffix)));
+ int numEpisodes = (int) item.episodesDownloadCount;
+ String text = Formatter.formatShortFileSize(context, item.totalDownloadSize);
+ text += " • " + context.getResources().getQuantityString(R.plurals.num_episodes, numEpisodes, numEpisodes);
+ holder.value.setText(text);
holder.itemView.setOnClickListener(v -> {
FeedStatisticsDialogFragment yourDialogFragment = FeedStatisticsDialogFragment.newInstance(
diff --git a/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/subscriptions/PlaybackStatisticsListAdapter.java b/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/subscriptions/PlaybackStatisticsListAdapter.java
index 3936118ca..9b1f234c8 100644
--- a/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/subscriptions/PlaybackStatisticsListAdapter.java
+++ b/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/subscriptions/PlaybackStatisticsListAdapter.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.ui.statistics.subscriptions;
+import android.text.format.DateFormat;
import androidx.fragment.app.Fragment;
import de.danoeh.antennapod.core.storage.StatisticsItem;
import de.danoeh.antennapod.core.util.Converter;
@@ -39,7 +40,8 @@ public class PlaybackStatisticsListAdapter extends StatisticsListAdapter {
if (includeMarkedAsPlayed) {
return context.getString(R.string.statistics_counting_total);
}
- SimpleDateFormat dateFormat = new SimpleDateFormat("MMM yyyy", Locale.getDefault());
+ String skeleton = DateFormat.getBestDateTimePattern(Locale.getDefault(), "MMM yyyy");
+ SimpleDateFormat dateFormat = new SimpleDateFormat(skeleton, Locale.getDefault());
String dateFrom = dateFormat.format(new Date(timeFilterFrom));
// FilterTo is first day of next month => Subtract one day
String dateTo = dateFormat.format(new Date(timeFilterTo - 24L * 3600000L));
diff --git a/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/subscriptions/StatisticsFilterDialog.java b/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/subscriptions/StatisticsFilterDialog.java
index 077883321..e46e48240 100644
--- a/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/subscriptions/StatisticsFilterDialog.java
+++ b/ui/statistics/src/main/java/de/danoeh/antennapod/ui/statistics/subscriptions/StatisticsFilterDialog.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.ui.statistics.subscriptions;
import android.content.Context;
import android.content.SharedPreferences;
+import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.widget.ArrayAdapter;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@@ -114,7 +115,8 @@ public class StatisticsFilterDialog {
date.set(Calendar.DAY_OF_MONTH, 1);
ArrayList<String> names = new ArrayList<>();
ArrayList<Long> timestamps = new ArrayList<>();
- SimpleDateFormat dateFormat = new SimpleDateFormat("MMM yyyy", Locale.getDefault());
+ String skeleton = DateFormat.getBestDateTimePattern(Locale.getDefault(), "MMM yyyy");
+ SimpleDateFormat dateFormat = new SimpleDateFormat(skeleton, Locale.getDefault());
while (date.getTimeInMillis() < System.currentTimeMillis()) {
names.add(dateFormat.format(new Date(date.getTimeInMillis())));
if (!inclusive) {