summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.md50
-rw-r--r--.github/ISSUE_TEMPLATE/bug_report.yml78
-rw-r--r--.github/ISSUE_TEMPLATE/feature_request.md31
-rw-r--r--.github/ISSUE_TEMPLATE/feature_request.yml48
-rw-r--r--.github/workflows/android-emulator.yml4
-rw-r--r--app/src/main/AndroidManifest.xml62
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java50
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java2
-rw-r--r--app/src/main/res/xml/network_security_config.xml9
-rw-r--r--common.gradle2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/NoHttpStringLoader.java39
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java106
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java65
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java4
-rw-r--r--core/src/main/res/drawable/ic_download_black.xml9
-rw-r--r--net/sync/gpoddernet/src/main/java/de/danoeh/antennapod/net/sync/gpoddernet/GpodnetService.java8
-rw-r--r--parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java9
-rw-r--r--parser/feed/src/test/java/de/danoeh/antennapod/parser/feed/element/namespace/RssParserTest.java8
-rw-r--r--parser/feed/src/test/resources/feed-rss-testUnsupportedElements.xml14
-rw-r--r--ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/MainActivityStarter.java5
-rw-r--r--ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/VideoPlayerActivityStarter.java4
33 files changed, 401 insertions, 299 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index f1d96dc7a..000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,50 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve existing features
-labels: 'Type: Possible bug'
----
-
-# Checklist
-<!-- Place an x in the boxes to tick them: [x] -->
-
-- [ ] I have used the search function to see if someone else has already submitted the same bug report.
-- [ ] I will describe the problem with as much detail as possible.
-- [ ] If the bug only to occurs with a certain podcast, I will include the URL of that podcast.
-
-# System info
-<!-- The following information is very important to fill out because some bugs may only occur on certain devices or versions of Android. -->
-
-**App version**: x.y.z
-<!-- The latest version may be different depending on your device. You can find the version in AntennaPod's settings. -->
-
-**App source**: Google Play / F-Droid / ...
-<!-- Please delete irrelevant answer or fill in the blank -->
-
-**Android version**: 5.x (Please mention if you are using a custom rom!)
-
-**Device model**:
-
-# Bug description
-
-**Steps to reproduce**:
-1. This
-2. Then that
-3. Then this
-4. Etc.
-
-**Expected behaviour**:
-<!-- After following the steps, what did you think AntennaPod would do? -->
-
-**Current behaviour**:
-<!-- What did AntennaPod do instead? Screenshots might help. Usually, you can take a screenshot of your smartphone by pressing *Power* + *Volume down* for a few seconds. -->
-
-**First occurred**: (e.g. about x days/weeks ago)
-
-**Environment**:
-<!-- Settings you have changed (e.g. Auto Download, changed media player). "Unusual" devices you use (e.g. Bluetooth headphones). -->
-
-**Stacktrace/Logcat**:
-<!-- If you are experiencing a crash, including the stacktrace will likely get it fixed sooner. AntennaPod has an `export logs` feature for this. -->
-```
-[if available]
-```
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 000000000..96c33d973
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,78 @@
+name: Bug report
+description: Create a report to help us improve existing features
+labels: ["Type: Possible bug"]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this bug report!
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: Checklist
+ options:
+ - label: I have used the search function to see if someone else has already submitted the same bug report.
+ required: true
+ - label: I will describe the problem with as much detail as possible.
+ required: true
+ - label: If the bug only to occurs with a certain podcast, I will include the URL of that podcast.
+ required: true
+ - type: input
+ id: version
+ attributes:
+ label: App version
+ description: The latest version is different on each device, so we need the actual version number found on the settings screen.
+ placeholder: x.y.z
+ validations:
+ required: true
+ - type: dropdown
+ id: source
+ attributes:
+ label: Where did you get the app from
+ multiple: false
+ options:
+ - Google Play
+ - F-Droid
+ - Other
+ validations:
+ required: true
+ - type: input
+ id: android_version
+ attributes:
+ label: Android version
+ description: Please mention if you are using a custom rom!
+ validations:
+ required: true
+ - type: input
+ id: device
+ attributes:
+ label: Device model
+ - type: input
+ id: first
+ attributes:
+ label: First occurred
+ placeholder: about x days/weeks ago
+ - type: textarea
+ id: steps
+ attributes:
+ label: Steps to reproduce
+ placeholder: |
+ 1. This
+ 2. Then that
+ 3. Then this
+ 4. Etc.
+ - type: textarea
+ id: expected
+ attributes:
+ label: Expected behaviour
+ description: After following the steps, what did you think AntennaPod would do?
+ - type: textarea
+ id: current
+ attributes:
+ label: Current behaviour
+ description: What did AntennaPod do instead? Screenshots might help. Usually, you can take a screenshot of your smartphone by pressing *Power* + *Volume down* for a few seconds.
+ - type: textarea
+ id: logs
+ attributes:
+ label: Logs
+ description: If you are experiencing a crash, including the stacktrace will likely get it fixed sooner. AntennaPod has an `export logs` feature for this.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 24f2f5772..000000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,31 +0,0 @@
----
-name: Feature request
-about: Request a new feature or enhancement
-
----
-
-# Checklist
-<!-- Place an x in the boxes to tick them: [x] -->
-
-- [ ] I have used the search function to see if someone else has already submitted the same feature request.
-- [ ] I will only create one feature request per issue.
-- [ ] I will describe the problem with as much detail as possible.
-
-# System info
-
-**App version**: x.y.z
-<!-- The latest version may be different depending on your device. You can find the version in AntennaPod's settings. -->
-
-**App source**: Google Play / F-Droid / ...
-<!-- Please delete irrelevant answer or fill in the blank -->
-
-# Feature description
-
-**Problem you may be having, or feature you want**:
-<!-- Give a brief explanation about the problem that may currently exist -->
-
-**Suggested solution**:
-<!-- Describe how your requested feature solves this problem. Try to be as specific as possible. Please not only explain what the feature does, but also how. -->
-
-**Screenshots / Drawings / Technical details**:
-<!-- If your request is about (or includes) changing or extending the UI, describe what the UI would look like and how the user would interact with it. -->
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 000000000..58ac86f8b
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,48 @@
+name: Feature request
+description: Request a new feature or enhancement
+body:
+ - type: checkboxes
+ id: checklist
+ attributes:
+ label: Checklist
+ options:
+ - label: I have used the search function to see if someone else has already submitted the same feature request.
+ required: true
+ - label: I will describe the problem with as much detail as possible.
+ required: true
+ - label: This request contains only one single feature, **not** a list of multiple (related) features.
+ required: true
+ - type: input
+ id: version
+ attributes:
+ label: App version
+ description: The latest version is different on each device, so we need the actual version number found on the settings screen.
+ placeholder: x.y.z
+ validations:
+ required: true
+ - type: dropdown
+ id: source
+ attributes:
+ label: Where did you get the app from
+ multiple: false
+ options:
+ - Google Play
+ - F-Droid
+ - Other
+ validations:
+ required: true
+ - type: textarea
+ id: problem
+ attributes:
+ label: Problem you may be having, or feature you want
+ description: Give a brief explanation about the problem that may currently exist
+ - type: textarea
+ id: solution
+ attributes:
+ label: Suggested solution
+ description: Describe how your requested feature solves this problem. Try to be as specific as possible. Please not only explain what the feature does, but also how.
+ - type: textarea
+ id: screenshots
+ attributes:
+ label: Screenshots / Drawings / Technical details
+ description: If your request is about (or includes) changing or extending the UI, describe what the UI would look like and how the user would interact with it.
diff --git a/.github/workflows/android-emulator.yml b/.github/workflows/android-emulator.yml
index eed69911a..c8e66e14f 100644
--- a/.github/workflows/android-emulator.yml
+++ b/.github/workflows/android-emulator.yml
@@ -9,11 +9,11 @@ jobs:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v2
- - name: Set up JDK 8
+ - name: Set up JDK 11
uses: actions/setup-java@v2
with:
distribution: 'adopt'
- java-version: '8'
+ java-version: '11'
- name: Wrapper validation
uses: gradle/wrapper-validation-action@v1
- name: Build with Gradle
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 47648f9d3..ff0ec6873 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -40,7 +40,8 @@
android:supportsRtl="true"
android:logo="@mipmap/ic_launcher"
android:resizeableActivity="true"
- android:allowAudioPlaybackCapture="true">
+ android:allowAudioPlaybackCapture="true"
+ android:networkSecurityConfig="@xml/network_security_config">
<meta-data android:name="android.webkit.WebView.MetricsOptOut"
@@ -65,15 +66,14 @@
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <action android:name="android.media.action.MEDIA_PLAY_FROM_SEARCH" />
+ <action android:name="android.intent.action.MUSIC_PLAYER" />
- <intent-filter>
- <action android:name=
- "android.media.action.MEDIA_PLAY_FROM_SEARCH" />
- <category android:name=
- "android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ <category android:name="android.intent.category.APP_MUSIC" />
+ <category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
@@ -98,13 +98,6 @@
android:host="antennapod.org"
android:pathPrefix="/deeplink/main"
android:scheme="https" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.VIEW" />
-
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.BROWSABLE" />
-
<data
android:host="antennapod.org"
android:pathPrefix="/deeplink/search"
@@ -144,11 +137,7 @@
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
- </intent-filter>
- <intent-filter>
<action android:name="de.danoeh.antennapod.FORCE_WIDGET_UPDATE"/>
- </intent-filter>
- <intent-filter>
<action android:name="de.danoeh.antennapod.STOP_WIDGET_UPDATE"/>
</intent-filter>
<meta-data
@@ -165,6 +154,7 @@
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
+ <action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
@@ -172,27 +162,15 @@
<data android:mimeType="text/xml"/>
<data android:mimeType="text/x-opml"/>
<data android:mimeType="application/xml"/>
- <data android:mimeType="application/octet-stream"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
-
- <data android:host="*"/>
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.SEND"/>
-
- <category android:name="android.intent.category.DEFAULT"/>
- <category android:name="android.intent.category.BROWSABLE"/>
-
- <data android:mimeType="text/xml"/>
- <data android:mimeType="text/plain"/>
- <data android:mimeType="text/x-opml"/>
- <data android:mimeType="application/xml"/>
- <data android:mimeType="application/octet-stream"/>
-
<data android:scheme="http"/>
<data android:scheme="https"/>
+
+ <data android:host="*"/>
+ <data android:pathPattern=".*.xml" />
+ <data android:pathPattern=".*.opml" />
</intent-filter>
</activity>
<activity
@@ -315,6 +293,18 @@
</intent-filter>
<intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+
+ <data android:pathPattern="/.*/podcast/.*" />
+ <data android:host="podcasts.apple.com" />
+ <data android:scheme="http" />
+ <data android:scheme="https" />
+ </intent-filter>
+
+ <intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
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 ec9e20dea..f0ca5a2cb 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -19,6 +19,7 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.TextView;
+import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -60,7 +61,6 @@ import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.danoeh.antennapod.discovery.PodcastSearcherRegistry;
import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.model.feed.FeedPreferences;
-import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.model.playback.RemoteMedia;
import de.danoeh.antennapod.parser.feed.UnsupportedFeedtypeException;
import io.reactivex.Maybe;
@@ -101,6 +101,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
private Feed feed;
private String selectedDownloadUrl;
private Downloader downloader;
+ private String username = null;
+ private String password = null;
private boolean isPaused;
private boolean didPressSubscribe = false;
@@ -144,12 +146,11 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
if (feedUrl.contains("subscribeonandroid.com")) {
feedUrl = feedUrl.replaceFirst("((www.)?(subscribeonandroid.com/))", "");
}
- if (savedInstanceState == null) {
- lookupUrlAndDownload(feedUrl, null, null);
- } else {
- lookupUrlAndDownload(feedUrl, savedInstanceState.getString("username"),
- savedInstanceState.getString("password"));
+ if (savedInstanceState != null) {
+ username = savedInstanceState.getString("username");
+ password = savedInstanceState.getString("password");
}
+ lookupUrlAndDownload(feedUrl);
}
}
@@ -210,10 +211,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- if (feed != null && feed.getPreferences() != null) {
- outState.putString("username", feed.getPreferences().getUsername());
- outState.putString("password", feed.getPreferences().getPassword());
- }
+ outState.putString("username", username);
+ outState.putString("password", password);
}
private void resetIntent(String url) {
@@ -242,25 +241,21 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
return super.onOptionsItemSelected(item);
}
- private void lookupUrlAndDownload(String url, String username, String password) {
+ private void lookupUrlAndDownload(String url) {
download = PodcastSearcherRegistry.lookupUrl(url)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
- .subscribe(lookedUpUrl -> startFeedDownload(lookedUpUrl, username, password),
+ .subscribe(this::startFeedDownload,
error -> {
showNoPodcastFoundError();
Log.e(TAG, Log.getStackTraceString(error));
});
}
- private void startFeedDownload(String url, String username, String password) {
+ private void startFeedDownload(String url) {
Log.d(TAG, "Starting feed download");
url = URLChecker.prepareURL(url);
feed = new Feed(url, null);
- if (username != null && password != null) {
- feed.setPreferences(new FeedPreferences(0, false, FeedPreferences.AutoDeleteAction.GLOBAL,
- VolumeAdaptionSetting.OFF, username, password));
- }
String fileUrl = new File(getExternalCacheDir(),
FileNameGenerator.generateFileName(feed.getDownload_url())).toString();
feed.setFile_url(fileUrl);
@@ -288,6 +283,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
parseFeed();
} else if (status.getReason() == DownloadError.ERROR_UNAUTHORIZED) {
if (!isFinishing() && !isPaused) {
+ if (username != null && password != null) {
+ Toast.makeText(this, R.string.download_error_unauthorized, Toast.LENGTH_LONG).show();
+ }
dialog = new FeedViewAuthenticationDialog(OnlineFeedViewActivity.this,
R.string.authentication_notification_title,
downloader.getDownloadRequest().getSource()).create();
@@ -637,21 +635,17 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
if (urls.size() == 1) {
// Skip dialog and display the item directly
resetIntent(urls.get(0));
- startFeedDownload(urls.get(0), null, null);
+ startFeedDownload(urls.get(0));
return true;
}
- final ArrayAdapter<String> adapter = new ArrayAdapter<>(OnlineFeedViewActivity.this, R.layout.ellipsize_start_listitem, R.id.txtvTitle, titles);
+ final ArrayAdapter<String> adapter = new ArrayAdapter<>(OnlineFeedViewActivity.this,
+ R.layout.ellipsize_start_listitem, R.id.txtvTitle, titles);
DialogInterface.OnClickListener onClickListener = (dialog, which) -> {
String selectedUrl = urls.get(which);
dialog.dismiss();
resetIntent(selectedUrl);
- FeedPreferences prefs = feed.getPreferences();
- if(prefs != null) {
- startFeedDownload(selectedUrl, prefs.getUsername(), prefs.getPassword());
- } else {
- startFeedDownload(selectedUrl, null, null);
- }
+ startFeedDownload(selectedUrl);
};
AlertDialog.Builder ab = new AlertDialog.Builder(OnlineFeedViewActivity.this)
@@ -674,7 +668,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
private final String feedUrl;
FeedViewAuthenticationDialog(Context context, int titleRes, String feedUrl) {
- super(context, titleRes, true, null, null);
+ super(context, titleRes, true, username, password);
this.feedUrl = feedUrl;
}
@@ -686,7 +680,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
@Override
protected void onConfirmed(String username, String password) {
- startFeedDownload(feedUrl, username, password);
+ OnlineFeedViewActivity.this.username = username;
+ OnlineFeedViewActivity.this.password = password;
+ startFeedDownload(feedUrl);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java
index 938bb5931..590b7c897 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.config;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
import de.danoeh.antennapod.R;
@@ -24,7 +25,8 @@ public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks {
args.putInt(DownloadsFragment.ARG_SELECTED_TAB, DownloadsFragment.POS_LOG);
intent.putExtra(MainActivity.EXTRA_FRAGMENT_ARGS, args);
return PendingIntent.getActivity(context,
- R.id.pending_intent_download_service_notification, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ R.id.pending_intent_download_service_notification, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
@Override
@@ -33,7 +35,8 @@ public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks {
activityIntent.setAction("request" + request.getFeedfileId());
activityIntent.putExtra(DownloadAuthenticationActivity.ARG_DOWNLOAD_REQUEST, request);
return PendingIntent.getActivity(context.getApplicationContext(),
- R.id.pending_intent_download_service_auth, activityIntent, PendingIntent.FLAG_ONE_SHOT);
+ R.id.pending_intent_download_service_auth, activityIntent,
+ PendingIntent.FLAG_ONE_SHOT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
@Override
@@ -43,15 +46,15 @@ public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks {
Bundle args = new Bundle();
args.putInt(DownloadsFragment.ARG_SELECTED_TAB, DownloadsFragment.POS_LOG);
intent.putExtra(MainActivity.EXTRA_FRAGMENT_ARGS, args);
- return PendingIntent.getActivity(context, R.id.pending_intent_download_service_report,
- intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getActivity(context, R.id.pending_intent_download_service_report, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
@Override
public PendingIntent getAutoDownloadReportNotificationContentIntent(Context context) {
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, QueueFragment.TAG);
- return PendingIntent.getActivity(context, R.id.pending_intent_download_service_autodownload_report,
- intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getActivity(context, R.id.pending_intent_download_service_autodownload_report, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java
index 6e894176f..5f3dd5f61 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/ItunesPodcastSearcher.java
@@ -17,9 +17,12 @@ import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class ItunesPodcastSearcher implements PodcastSearcher {
private static final String ITUNES_API_URL = "https://itunes.apple.com/search?media=podcast&term=%s";
+ private static final String PATTERN_BY_ID = ".*/podcasts\\.apple\\.com/.*/podcast/.*/id(\\d+).*";
public ItunesPodcastSearcher() {
}
@@ -70,9 +73,12 @@ public class ItunesPodcastSearcher implements PodcastSearcher {
@Override
public Single<String> lookupUrl(String url) {
+ Pattern pattern = Pattern.compile(PATTERN_BY_ID);
+ Matcher matcher = pattern.matcher(url);
+ final String lookupUrl = matcher.find() ? ("https://itunes.apple.com/lookup?id=" + matcher.group(1)) : url;
return Single.create(emitter -> {
OkHttpClient client = AntennapodHttpClient.getHttpClient();
- Request.Builder httpReq = new Request.Builder().url(url);
+ Request.Builder httpReq = new Request.Builder().url(lookupUrl);
try {
Response response = client.newCall(httpReq.build()).execute();
if (response.isSuccessful()) {
@@ -92,7 +98,7 @@ public class ItunesPodcastSearcher implements PodcastSearcher {
@Override
public boolean urlNeedsLookup(String url) {
- return url.contains("itunes.apple.com");
+ return url.contains("itunes.apple.com") || url.matches(PATTERN_BY_ID);
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index d4c243676..7f561e583 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -14,7 +14,6 @@ import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.text.TextUtilsCompat;
import androidx.core.util.ObjectsCompat;
@@ -224,12 +223,6 @@ public class ItemFragment extends Fragment {
}
@Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- load();
- }
-
- @Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
@@ -240,6 +233,7 @@ public class ItemFragment extends Fragment {
}
};
controller.init();
+ load();
}
@Override
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 13a093be3..8591cf42e 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -247,8 +247,9 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
() -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
private void refreshToolbarState() {
- toolbar.getMenu().findItem(R.id.queue_lock).setChecked(UserPreferences.isQueueLocked());
boolean keepSorted = UserPreferences.isQueueKeepSorted();
+ toolbar.getMenu().findItem(R.id.queue_lock).setChecked(UserPreferences.isQueueLocked());
+ toolbar.getMenu().findItem(R.id.queue_lock).setVisible(!keepSorted);
toolbar.getMenu().findItem(R.id.queue_sort_random).setVisible(!keepSorted);
toolbar.getMenu().findItem(R.id.queue_keep_sorted).setChecked(keepSorted);
isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(toolbar.getMenu(),
@@ -635,11 +636,6 @@ public class QueueFragment extends Fragment implements Toolbar.OnMenuItemClickLi
}
@Override
- public boolean isItemViewSwipeEnabled() {
- return !UserPreferences.isQueueLocked();
- }
-
- @Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
// Check if drag finished
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
index af502ce13..c2c5adc9a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
@@ -101,7 +101,7 @@ public abstract class PodcastListFragment extends Fragment {
}, error -> {
gridView.setVisibility(View.GONE);
progressBar.setVisibility(View.GONE);
- txtvError.setText(getString(R.string.error_msg_prefix) + error.getMessage());
+ txtvError.setText(error.getMessage());
txtvError.setVisibility(View.VISIBLE);
butRetry.setVisibility(View.VISIBLE);
Log.e(TAG, Log.getStackTraceString(error));
diff --git a/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java b/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
index 1075117dd..2ea15005a 100644
--- a/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
+++ b/app/src/main/java/de/danoeh/antennapod/receiver/ConnectivityActionReceiver.java
@@ -22,7 +22,7 @@ public class ConnectivityActionReceiver extends BroadcastReceiver {
Log.d(TAG, "Received intent");
ClientConfig.initialize(context);
- if (NetworkUtils.autodownloadNetworkAvailable()) {
+ if (NetworkUtils.isAutoDownloadAllowed()) {
Log.d(TAG, "auto-dl network available, starting auto-download");
DBTasks.autodownloadUndownloadedItems(context);
} else { // if new network is Wi-Fi, finish ongoing downloads,
diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml
new file mode 100644
index 000000000..d4c3fc996
--- /dev/null
+++ b/app/src/main/res/xml/network_security_config.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config xmlns:tools="http://schemas.android.com/tools">
+ <base-config cleartextTrafficPermitted="true" tools:ignore="InsecureBaseConfiguration">
+ <trust-anchors>
+ <certificates src="user" tools:ignore="AcceptsUserCertificates"/>
+ <certificates src="system" />
+ </trust-anchors>
+ </base-config>
+</network-security-config>
diff --git a/common.gradle b/common.gradle
index 0300ed534..8063952ce 100644
--- a/common.gradle
+++ b/common.gradle
@@ -1,5 +1,5 @@
android {
- compileSdkVersion 30
+ compileSdkVersion 31
defaultConfig {
minSdkVersion 16
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
index defe6c9f8..9b06d2138 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
@@ -11,7 +11,6 @@ import com.bumptech.glide.Registry;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.load.DecodeFormat;
import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory;
-import com.bumptech.glide.load.model.StringLoader;
import com.bumptech.glide.module.AppGlideModule;
import de.danoeh.antennapod.model.feed.EmbeddedChapterImage;
@@ -43,7 +42,7 @@ public class ApGlideModule extends AppGlideModule {
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
registry.replace(String.class, InputStream.class, new MetadataRetrieverLoader.Factory(context));
registry.append(String.class, InputStream.class, new ApOkHttpUrlLoader.Factory());
- registry.append(String.class, InputStream.class, new StringLoader.StreamFactory());
+ registry.append(String.class, InputStream.class, new NoHttpStringLoader.StreamFactory());
registry.append(EmbeddedChapterImage.class, ByteBuffer.class, new ChapterImageModelLoader.Factory());
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/NoHttpStringLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/NoHttpStringLoader.java
new file mode 100644
index 000000000..9cda3b1aa
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/NoHttpStringLoader.java
@@ -0,0 +1,39 @@
+package de.danoeh.antennapod.core.glide;
+
+import android.net.Uri;
+import androidx.annotation.NonNull;
+import com.bumptech.glide.load.model.ModelLoader;
+import com.bumptech.glide.load.model.ModelLoaderFactory;
+import com.bumptech.glide.load.model.MultiModelLoaderFactory;
+import com.bumptech.glide.load.model.StringLoader;
+
+import java.io.InputStream;
+
+/**
+ * StringLoader that does not handle http/https urls. Used to avoid fallback to StringLoader when
+ * AntennaPod blocks mobile image loading.
+ */
+public final class NoHttpStringLoader extends StringLoader<InputStream> {
+
+ public static class StreamFactory implements ModelLoaderFactory<String, InputStream> {
+ @NonNull
+ @Override
+ public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
+ return new NoHttpStringLoader(multiFactory.build(Uri.class, InputStream.class));
+ }
+
+ @Override
+ public void teardown() {
+ // Do nothing.
+ }
+ }
+
+ public NoHttpStringLoader(ModelLoader<Uri, InputStream> uriLoader) {
+ super(uriLoader);
+ }
+
+ @Override
+ public boolean handles(@NonNull String model) {
+ return !model.startsWith("http") && super.handles(model);
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java
index 63e005927..f7ed049cd 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java
@@ -8,6 +8,7 @@ import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
+import android.os.Build;
import android.util.Log;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
@@ -68,7 +69,8 @@ public class NewEpisodesNotification {
intent.setComponent(new ComponentName(context, "de.danoeh.antennapod.activity.MainActivity"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra("fragment_feed_id", feed.getId());
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
+ (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
Notification notification = new NotificationCompat.Builder(
context, NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS)
@@ -93,7 +95,8 @@ public class NewEpisodesNotification {
intent.setComponent(new ComponentName(context, "de.danoeh.antennapod.activity.MainActivity"));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
intent.putExtra("fragment_tag", "EpisodesFragment");
- PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
+ PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent,
+ (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
Notification notificationGroupSummary = new NotificationCompat.Builder(
context, NotificationUtils.CHANNEL_ID_EPISODE_NOTIFICATIONS)
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 3465d952d..c969923d4 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
@@ -35,6 +35,7 @@ import android.view.SurfaceHolder;
import android.webkit.URLUtil;
import android.widget.Toast;
+import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.core.app.NotificationCompat;
@@ -286,7 +287,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
ComponentName eventReceiver = new ComponentName(getApplicationContext(), MediaButtonReceiver.class);
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setComponent(eventReceiver);
- PendingIntent buttonReceiverIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent buttonReceiverIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 31 ? PendingIntent.FLAG_MUTABLE : 0));
mediaSession = new MediaSessionCompat(getApplicationContext(), TAG, eventReceiver, buttonReceiverIntent);
setSessionToken(mediaSession.getSessionToken());
@@ -366,30 +368,22 @@ public class PlaybackService extends MediaBrowserServiceCompat {
.subscribe(queueItems -> mediaSession.setQueue(queueItems), Throwable::printStackTrace);
}
- private MediaBrowserCompat.MediaItem createBrowsableMediaItemForRoot() {
+ private MediaBrowserCompat.MediaItem createBrowsableMediaItem(
+ @StringRes int title, @DrawableRes int icon, int numEpisodes) {
Uri uri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
- .authority(getResources().getResourcePackageName(R.drawable.ic_playlist_black))
- .appendPath(getResources().getResourceTypeName(R.drawable.ic_playlist_black))
- .appendPath(getResources().getResourceEntryName(R.drawable.ic_playlist_black))
+ .authority(getResources().getResourcePackageName(icon))
+ .appendPath(getResources().getResourceTypeName(icon))
+ .appendPath(getResources().getResourceEntryName(icon))
.build();
- String subtitle = "";
- try {
- int count = taskManager.getQueue().size();
- subtitle = getResources().getQuantityString(R.plurals.num_episodes, count, count);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
MediaDescriptionCompat description = new MediaDescriptionCompat.Builder()
.setIconUri(uri)
- .setMediaId(getResources().getString(R.string.queue_label))
- .setTitle(getResources().getString(R.string.queue_label))
- .setSubtitle(subtitle)
+ .setMediaId(getResources().getString(title))
+ .setTitle(getResources().getString(title))
+ .setSubtitle(getResources().getQuantityString(R.plurals.num_episodes, numEpisodes, numEpisodes))
.build();
- return new MediaBrowserCompat.MediaItem(description,
- MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
+ return new MediaBrowserCompat.MediaItem(description, MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
}
private MediaBrowserCompat.MediaItem createBrowsableMediaItemForFeed(Feed feed) {
@@ -421,46 +415,47 @@ public class PlaybackService extends MediaBrowserServiceCompat {
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(() -> { }, Throwable::printStackTrace);
+ .subscribe(
+ () -> {
+ }, e -> {
+ e.printStackTrace();
+ result.sendResult(null);
+ });
}
- private List<MediaBrowserCompat.MediaItem> loadChildrenSynchronous(@NonNull String parentId) {
+ private List<MediaBrowserCompat.MediaItem> loadChildrenSynchronous(@NonNull String parentId)
+ throws InterruptedException {
List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
if (parentId.equals(getResources().getString(R.string.app_name))) {
- // Root List
- try {
- if (!(taskManager.getQueue().isEmpty())) {
- mediaItems.add(createBrowsableMediaItemForRoot());
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ mediaItems.add(createBrowsableMediaItem(R.string.queue_label, R.drawable.ic_playlist_black,
+ taskManager.getQueue().size()));
+ mediaItems.add(createBrowsableMediaItem(R.string.downloads_label, R.drawable.ic_download_black,
+ DBReader.getDownloadedItems().size()));
List<Feed> feeds = DBReader.getFeedList();
for (Feed feed : feeds) {
mediaItems.add(createBrowsableMediaItemForFeed(feed));
}
- } else if (parentId.equals(getResources().getString(R.string.queue_label))) {
- // Child List
- try {
- for (FeedItem feedItem : taskManager.getQueue()) {
- FeedMedia media = feedItem.getMedia();
- if (media != null) {
- mediaItems.add(media.getMediaItem());
- }
- }
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
+ return mediaItems;
+ }
+
+ List<FeedItem> feedItems;
+ if (parentId.equals(getResources().getString(R.string.queue_label))) {
+ feedItems = taskManager.getQueue();
+ } else if (parentId.equals(getResources().getString(R.string.downloads_label))) {
+ feedItems = DBReader.getDownloadedItems();
} else if (parentId.startsWith("FeedId:")) {
long feedId = Long.parseLong(parentId.split(":")[1]);
- List<FeedItem> feedItems = DBReader.getFeedItemList(DBReader.getFeed(feedId));
- int count = 0;
- for (FeedItem feedItem : feedItems) {
- if (feedItem.getMedia() != null && feedItem.getMedia().getMediaItem() != null) {
- mediaItems.add(feedItem.getMedia().getMediaItem());
- if (++count >= MAX_ANDROID_AUTO_EPISODES_PER_FEED) {
- break;
- }
+ feedItems = DBReader.getFeedItemList(DBReader.getFeed(feedId));
+ } else {
+ Log.e(TAG, "Parent ID not found: " + parentId);
+ return null;
+ }
+ int count = 0;
+ for (FeedItem feedItem : feedItems) {
+ if (feedItem.getMedia() != null && feedItem.getMedia().getMediaItem() != null) {
+ mediaItems.add(feedItem.getMedia().getMediaItem());
+ if (++count >= MAX_ANDROID_AUTO_EPISODES_PER_FEED) {
+ break;
}
}
}
@@ -598,10 +593,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
PendingIntent pendingIntentAllowThisTime;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
pendingIntentAllowThisTime = PendingIntent.getForegroundService(this,
- R.id.pending_intent_allow_stream_this_time, intentAllowThisTime, PendingIntent.FLAG_UPDATE_CURRENT);
+ R.id.pending_intent_allow_stream_this_time, intentAllowThisTime,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntentAllowThisTime = PendingIntent.getService(this,
- R.id.pending_intent_allow_stream_this_time, intentAllowThisTime, PendingIntent.FLAG_UPDATE_CURRENT);
+ R.id.pending_intent_allow_stream_this_time, intentAllowThisTime, PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
Intent intentAlwaysAllow = new Intent(intentAllowThisTime);
@@ -610,10 +607,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
PendingIntent pendingIntentAlwaysAllow;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
pendingIntentAlwaysAllow = PendingIntent.getForegroundService(this,
- R.id.pending_intent_allow_stream_always, intentAlwaysAllow, PendingIntent.FLAG_UPDATE_CURRENT);
+ R.id.pending_intent_allow_stream_always, intentAlwaysAllow,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntentAlwaysAllow = PendingIntent.getService(this,
- R.id.pending_intent_allow_stream_always, intentAlwaysAllow, PendingIntent.FLAG_UPDATE_CURRENT);
+ R.id.pending_intent_allow_stream_always, intentAlwaysAllow, PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this,
@@ -1302,7 +1301,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (stateManager.hasReceivedValidStartCommand()) {
mediaSession.setSessionActivity(PendingIntent.getActivity(this, R.id.pending_intent_player_activity,
- PlaybackService.getPlayerActivityIntent(this), PendingIntent.FLAG_UPDATE_CURRENT));
+ PlaybackService.getPlayerActivityIntent(this), PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 31 ? PendingIntent.FLAG_MUTABLE : 0)));
try {
mediaSession.setMetadata(builder.build());
} catch (OutOfMemoryError e) {
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 e7dea192a..5aee8c24c 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
@@ -170,7 +170,8 @@ public class PlaybackServiceNotificationBuilder {
private PendingIntent getPlayerActivityPendingIntent() {
return PendingIntent.getActivity(context, R.id.pending_intent_player_activity,
- PlaybackService.getPlayerActivityIntent(context), PendingIntent.FLAG_UPDATE_CURRENT);
+ PlaybackService.getPlayerActivityIntent(context), PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
private void addActions(NotificationCompat.Builder notification, MediaSessionCompat.Token mediaSessionToken,
@@ -183,7 +184,8 @@ public class PlaybackServiceNotificationBuilder {
Intent stopCastingIntent = new Intent(context, PlaybackService.class);
stopCastingIntent.putExtra(PlaybackService.EXTRA_CAST_DISCONNECT, true);
PendingIntent stopCastingPendingIntent = PendingIntent.getService(context,
- numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
notification.addAction(R.drawable.ic_notification_cast_off,
context.getString(R.string.cast_disconnect_label),
stopCastingPendingIntent);
@@ -252,9 +254,11 @@ public class PlaybackServiceNotificationBuilder {
intent.putExtra(MediaButtonReceiver.EXTRA_KEYCODE, keycodeValue);
if (Build.VERSION.SDK_INT >= 26) {
- return PendingIntent.getForegroundService(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getForegroundService(context, requestCode, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
} else {
- return PendingIntent.getService(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getService(context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
}
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 b5202d79c..0dc57e0af 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
@@ -35,7 +35,7 @@ public class AutomaticDownloadAlgorithm {
return () -> {
// true if we should auto download based on network status
- boolean networkShouldAutoDl = NetworkUtils.autodownloadNetworkAvailable()
+ boolean networkShouldAutoDl = NetworkUtils.isAutoDownloadAllowed()
&& UserPreferences.isEnableAutodownload();
// true if we should auto download based on power status
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java
index 35b60ca4b..e6496bb7d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java
@@ -5,6 +5,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -302,7 +303,8 @@ public class SyncService extends Worker {
Intent intent = getApplicationContext().getPackageManager().getLaunchIntentForPackage(
getApplicationContext().getPackageName());
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(),
- R.id.pending_intent_sync_error, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ R.id.pending_intent_sync_error, intent, PendingIntent.FLAG_UPDATE_CURRENT
+ | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
Notification notification = new NotificationCompat.Builder(getApplicationContext(),
NotificationUtils.CHANNEL_ID_SYNC_ERROR)
.setContentTitle(getApplicationContext().getString(R.string.gpodnetsync_error_title))
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java
index e5f60d64b..09161ca7b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java
@@ -9,6 +9,7 @@ import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import de.danoeh.antennapod.model.feed.FeedItem;
@@ -77,25 +78,22 @@ public class FeedItemPermutors {
@NonNull
private static Date pubDate(@Nullable FeedItem item) {
- return (item != null && item.getPubDate() != null) ?
- item.getPubDate() : new Date(0);
+ return (item != null && item.getPubDate() != null) ? item.getPubDate() : new Date(0);
}
@NonNull
private static String itemTitle(@Nullable FeedItem item) {
- return (item != null && item.getTitle() != null) ?
- item.getTitle() : "";
+ return (item != null && item.getTitle() != null) ? item.getTitle().toLowerCase(Locale.getDefault()) : "";
}
private static int duration(@Nullable FeedItem item) {
- return (item != null && item.getMedia() != null) ?
- item.getMedia().getDuration() : 0;
+ return (item != null && item.getMedia() != null) ? item.getMedia().getDuration() : 0;
}
@NonNull
private static String feedTitle(@Nullable FeedItem item) {
- return (item != null && item.getFeed() != null && item.getFeed().getTitle() != null) ?
- item.getFeed().getTitle() : "";
+ return (item != null && item.getFeed() != null && item.getFeed().getTitle() != null)
+ ? item.getFeed().getTitle().toLowerCase(Locale.getDefault()) : "";
}
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
index 12f1e98f9..06d5d67a3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
@@ -40,56 +40,23 @@ public class NetworkUtils {
NetworkUtils.context = context;
}
- /**
- * Returns true if the device is connected to Wi-Fi and the Wi-Fi filter for
- * automatic downloads is disabled or the device is connected to a Wi-Fi
- * network that is on the 'selected networks' list of the Wi-Fi filter for
- * automatic downloads and false otherwise.
- * */
- public static boolean autodownloadNetworkAvailable() {
- ConnectivityManager cm = (ConnectivityManager) context
- .getSystemService(Context.CONNECTIVITY_SERVICE);
+ public static boolean isAutoDownloadAllowed() {
+ ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
- if (networkInfo != null) {
- if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
- Log.d(TAG, "Device is connected to Wi-Fi");
- if (networkInfo.isConnected()) {
- if (!UserPreferences.isEnableAutodownloadWifiFilter()) {
- Log.d(TAG, "Auto-dl filter is disabled");
- return true;
- } else {
- WifiManager wm = (WifiManager) context.getApplicationContext()
- .getSystemService(Context.WIFI_SERVICE);
- WifiInfo wifiInfo = wm.getConnectionInfo();
- List<String> selectedNetworks = Arrays
- .asList(UserPreferences
- .getAutodownloadSelectedNetworks());
- if (selectedNetworks.contains(Integer.toString(wifiInfo
- .getNetworkId()))) {
- Log.d(TAG, "Current network is on the selected networks list");
- return true;
- }
- }
- }
- } else if (networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET) {
- Log.d(TAG, "Device is connected to Ethernet");
- if (networkInfo.isConnected()) {
- return true;
- }
+ if (networkInfo == null) {
+ return false;
+ }
+ if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+ if (UserPreferences.isEnableAutodownloadWifiFilter()) {
+ return isInAllowedWifiNetwork();
} else {
- if (!UserPreferences.isAllowMobileAutoDownload()) {
- Log.d(TAG, "Auto Download not enabled on Mobile");
- return false;
- }
- if (networkInfo.isRoaming()) {
- Log.d(TAG, "Roaming on foreign network");
- return false;
- }
- return true;
+ return !isNetworkMetered();
}
+ } else if (networkInfo.getType() == ConnectivityManager.TYPE_ETHERNET) {
+ return true;
+ } else {
+ return UserPreferences.isAllowMobileAutoDownload() || !NetworkUtils.isNetworkRestricted();
}
- Log.d(TAG, "Network for auto-dl is not available");
- return false;
}
public static boolean networkAvailable() {
@@ -157,6 +124,12 @@ public class NetworkUtils {
}
}
+ private static boolean isInAllowedWifiNetwork() {
+ WifiManager wm = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ List<String> selectedNetworks = Arrays.asList(UserPreferences.getAutodownloadSelectedNetworks());
+ return selectedNetworks.contains(Integer.toString(wm.getConnectionInfo().getNetworkId()));
+ }
+
/**
* Returns the SSID of the wifi connection, or <code>null</code> if there is no wifi.
*/
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
index 649e97c42..11a3ad9b3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
@@ -12,8 +12,11 @@ import android.util.Log;
import android.util.Pair;
import android.view.SurfaceHolder;
import androidx.annotation.NonNull;
+import de.danoeh.antennapod.core.event.playback.PlaybackPositionEvent;
import de.danoeh.antennapod.core.event.playback.PlaybackServiceEvent;
import de.danoeh.antennapod.core.event.playback.SpeedChangedEvent;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
@@ -420,6 +423,11 @@ public abstract class PlaybackController {
public void seekTo(int time) {
if (playbackService != null) {
playbackService.seekTo(time);
+ } else if (getMedia() instanceof FeedMedia) {
+ FeedMedia media = (FeedMedia) getMedia();
+ media.setPosition(time);
+ DBWriter.setFeedItem(media.getItem());
+ EventBus.getDefault().post(new PlaybackPositionEvent(time, getMedia().getDuration()));
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java
index cecd4b3b6..62d56521c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java
@@ -7,6 +7,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
+import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
@@ -212,7 +213,8 @@ public abstract class WidgetUpdater {
startingIntent.setAction(MediaButtonReceiver.NOTIFY_BUTTON_RECEIVER);
startingIntent.putExtra(Intent.EXTRA_KEY_EVENT, event);
- return PendingIntent.getBroadcast(context, eventCode, startingIntent, 0);
+ return PendingIntent.getBroadcast(context, eventCode, startingIntent,
+ (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
private static String getProgressString(int position, int duration, float speed) {
diff --git a/core/src/main/res/drawable/ic_download_black.xml b/core/src/main/res/drawable/ic_download_black.xml
new file mode 100644
index 000000000..eba137a59
--- /dev/null
+++ b/core/src/main/res/drawable/ic_download_black.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#000000"
+ android:pathData="M18,15v3H6v-3H4v3c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-3H18zM17,11l-1.41,-1.41L13,12.17V4h-2v8.17L8.41,9.59L7,11l5,5L17,11z"/>
+</vector>
diff --git a/net/sync/gpoddernet/src/main/java/de/danoeh/antennapod/net/sync/gpoddernet/GpodnetService.java b/net/sync/gpoddernet/src/main/java/de/danoeh/antennapod/net/sync/gpoddernet/GpodnetService.java
index 439a528b7..21a362a40 100644
--- a/net/sync/gpoddernet/src/main/java/de/danoeh/antennapod/net/sync/gpoddernet/GpodnetService.java
+++ b/net/sync/gpoddernet/src/main/java/de/danoeh/antennapod/net/sync/gpoddernet/GpodnetService.java
@@ -588,7 +588,13 @@ public class GpodnetService implements ISyncService {
e.printStackTrace();
}
}
- throw new GpodnetServiceBadStatusCodeException("Bad response code: " + responseCode, responseCode);
+ if (responseCode >= 500) {
+ throw new GpodnetServiceBadStatusCodeException("Gpodder.net is currently unavailable (code "
+ + responseCode + ")", responseCode);
+ } else {
+ throw new GpodnetServiceBadStatusCodeException("Unable to connect to Gpodder.net (code "
+ + responseCode + ": " + response.message() + ")", responseCode);
+ }
}
}
}
diff --git a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java
index a49cd16dd..9ac77a5e6 100644
--- a/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java
+++ b/parser/feed/src/main/java/de/danoeh/antennapod/parser/feed/namespace/Rss20.java
@@ -39,14 +39,12 @@ public class Rss20 extends Namespace {
private static final String ENC_TYPE = "type";
@Override
- public SyndElement handleElementStart(String localName, HandlerState state,
- Attributes attributes) {
- if (ITEM.equals(localName)) {
+ public SyndElement handleElementStart(String localName, HandlerState state, Attributes attributes) {
+ if (ITEM.equals(localName) && CHANNEL.equals(state.getTagstack().lastElement().getName())) {
state.setCurrentItem(new FeedItem());
state.getItems().add(state.getCurrentItem());
state.getCurrentItem().setFeed(state.getFeed());
-
- } else if (ENCLOSURE.equals(localName)) {
+ } else if (ENCLOSURE.equals(localName) && ITEM.equals(state.getTagstack().peek().getName())) {
String type = attributes.getValue(ENC_TYPE);
String url = attributes.getValue(ENC_URL);
@@ -72,7 +70,6 @@ public class Rss20 extends Namespace {
FeedMedia media = new FeedMedia(state.getCurrentItem(), url, size, type);
state.getCurrentItem().setMedia(media);
}
-
}
return new SyndElement(localName, this);
}
diff --git a/parser/feed/src/test/java/de/danoeh/antennapod/parser/feed/element/namespace/RssParserTest.java b/parser/feed/src/test/java/de/danoeh/antennapod/parser/feed/element/namespace/RssParserTest.java
index 8f8942d7b..88ac5c731 100644
--- a/parser/feed/src/test/java/de/danoeh/antennapod/parser/feed/element/namespace/RssParserTest.java
+++ b/parser/feed/src/test/java/de/danoeh/antennapod/parser/feed/element/namespace/RssParserTest.java
@@ -96,4 +96,12 @@ public class RssParserTest {
assertTrue(TextUtils.isEmpty(feed.getPaymentLinks().get(2).content));
assertEquals("https://example.com/funding3", feed.getPaymentLinks().get(2).url);
}
+
+ @Test
+ public void testUnsupportedElements() throws Exception {
+ File feedFile = FeedParserTestHelper.getFeedFile("feed-rss-testUnsupportedElements.xml");
+ Feed feed = FeedParserTestHelper.runFeedParser(feedFile);
+ assertEquals(1, feed.getItems().size());
+ assertEquals("item-0", feed.getItems().get(0).getTitle());
+ }
}
diff --git a/parser/feed/src/test/resources/feed-rss-testUnsupportedElements.xml b/parser/feed/src/test/resources/feed-rss-testUnsupportedElements.xml
new file mode 100644
index 000000000..f21ca7ebd
--- /dev/null
+++ b/parser/feed/src/test/resources/feed-rss-testUnsupportedElements.xml
@@ -0,0 +1,14 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
+ <channel>
+ <title>title</title>
+ <item>
+ <title>item-0</title>
+ </item>
+ <unsupported-element>
+ <item>
+ <title>item-1</title>
+ </item>
+ </unsupported-element>
+ </channel>
+</rss>
diff --git a/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/MainActivityStarter.java b/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/MainActivityStarter.java
index 33f96f141..88c0378c1 100644
--- a/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/MainActivityStarter.java
+++ b/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/MainActivityStarter.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.ui.appstartintent;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.os.Build;
/**
* Launches the main activity of the app with specific arguments.
@@ -26,8 +27,8 @@ public class MainActivityStarter {
}
public PendingIntent getPendingIntent() {
- return PendingIntent.getActivity(context, R.id.pending_intent_player_activity,
- getIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getActivity(context, R.id.pending_intent_player_activity, getIntent(),
+ PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
public void start() {
diff --git a/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/VideoPlayerActivityStarter.java b/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/VideoPlayerActivityStarter.java
index 7536d34b6..53f8719de 100644
--- a/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/VideoPlayerActivityStarter.java
+++ b/ui/app-start-intent/src/main/java/de/danoeh/antennapod/ui/appstartintent/VideoPlayerActivityStarter.java
@@ -28,8 +28,8 @@ public class VideoPlayerActivityStarter {
}
public PendingIntent getPendingIntent() {
- return PendingIntent.getActivity(context, R.id.pending_intent_video_player,
- getIntent(), PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getActivity(context, R.id.pending_intent_video_player, getIntent(),
+ PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0));
}
public void start() {