From c6edf77fc29de6d50b3be25da5aec1ff42f1554c Mon Sep 17 00:00:00 2001 From: Mats Wahlberg Date: Sat, 20 Jun 2020 15:25:21 +0200 Subject: Fix #1560 for some remaining platforms still affected Added extra check in onStartCommand() for PlaybackService to check if eventTime or downTime are positive (which seems to indicate the intent was generated by a hardware button press instead of a touchscreen tap, like the lockscreen playback controls). --- .../danoeh/antennapod/core/receiver/MediaButtonReceiver.java | 7 +++++++ .../antennapod/core/service/playback/PlaybackService.java | 12 ++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') 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 b683f849c..abee9d8d3 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 @@ -15,6 +15,7 @@ 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_SOURCE = "de.danoeh.antennapod.core.service.extra.MediaButtonReceiver.SOURCE"; + public static final String EXTRA_HARDWAREBUTTON = "de.danoeh.antennapod.core.service.extra.MediaButtonReceiver.HARDWAREBUTTON"; public static final String NOTIFY_BUTTON_RECEIVER = "de.danoeh.antennapod.NOTIFY_BUTTON_RECEIVER"; @@ -30,6 +31,12 @@ public class MediaButtonReceiver extends BroadcastReceiver { Intent serviceIntent = new Intent(context, PlaybackService.class); serviceIntent.putExtra(EXTRA_KEYCODE, event.getKeyCode()); serviceIntent.putExtra(EXTRA_SOURCE, event.getSource()); + //detect if this is a hardware button press + if (event.getEventTime() > 0 || event.getDownTime() > 0) { + serviceIntent.putExtra(EXTRA_HARDWAREBUTTON, true); + } else { + serviceIntent.putExtra(EXTRA_HARDWAREBUTTON, false); + } ContextCompat.startForegroundService(context, serviceIntent); } 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 585510ef8..db1ddc888 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 @@ -450,6 +450,7 @@ public class PlaybackService extends MediaBrowserServiceCompat { notificationManager.cancel(R.id.notification_streaming_confirmation); final int keycode = intent.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1); + final boolean hardwareButton = intent.getBooleanExtra(MediaButtonReceiver.EXTRA_HARDWAREBUTTON, false); final boolean castDisconnect = intent.getBooleanExtra(EXTRA_CAST_DISCONNECT, false); Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE); if (keycode == -1 && playable == null && !castDisconnect) { @@ -463,8 +464,15 @@ public class PlaybackService extends MediaBrowserServiceCompat { stateManager.stopForeground(true); } else { if (keycode != -1) { - Log.d(TAG, "Received media button event"); - boolean handled = handleKeycode(keycode, true); + boolean notificationButton; + if (hardwareButton) { + Log.d(TAG, "Received hardware button event"); + notificationButton = false; + } else { + Log.d(TAG, "Received media button event"); + notificationButton = true; + } + boolean handled = handleKeycode(keycode, notificationButton); if (!handled && !stateManager.hasReceivedValidStartCommand()) { stateManager.stopService(); return Service.START_NOT_STICKY; -- cgit v1.2.3 From 806b1f48ecf93f93e548980a87c0e6495591f596 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sat, 19 Sep 2020 21:20:15 +0200 Subject: Make ProviderInstaller synchronous again See #4077 for details: It is fast and the old method sometimes does not install the provider early enough Reverts #3946 and #4285 --- .../core/service/ProviderInstallerInterceptor.java | 18 ------------------ .../core/service/download/AntennapodHttpClient.java | 2 -- 2 files changed, 20 deletions(-) delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/service/ProviderInstallerInterceptor.java (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/ProviderInstallerInterceptor.java b/core/src/main/java/de/danoeh/antennapod/core/service/ProviderInstallerInterceptor.java deleted file mode 100644 index 4fa1fc3d7..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/service/ProviderInstallerInterceptor.java +++ /dev/null @@ -1,18 +0,0 @@ -package de.danoeh.antennapod.core.service; - -import androidx.annotation.NonNull; -import okhttp3.Interceptor; -import okhttp3.Response; - -import java.io.IOException; - -public class ProviderInstallerInterceptor implements Interceptor { - public static Runnable installer = () -> { }; - - @Override - @NonNull - public Response intercept(Chain chain) throws IOException { - installer.run(); - return chain.proceed(chain.request()); - } -} diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java index d0484f2a2..e0c23bdac 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java @@ -32,7 +32,6 @@ import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.service.ProviderInstallerInterceptor; import de.danoeh.antennapod.core.service.UserAgentInterceptor; import de.danoeh.antennapod.core.storage.DBWriter; import okhttp3.Cache; @@ -117,7 +116,6 @@ public class AntennapodHttpClient { } return response; }); - builder.interceptors().add(new ProviderInstallerInterceptor()); builder.interceptors().add(new BasicAuthorizationInterceptor()); builder.networkInterceptors().add(new UserAgentInterceptor()); -- cgit v1.2.3 From 43d36c7560eb018a2e450466d75c1dbbf136a961 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sun, 20 Sep 2020 11:52:09 +0200 Subject: Fixed crash when item has no feed Apparently, there are ways to end up with items that have no feed. This hotfix prevents the app from crashing but it does not solve the reason for items without feeds. --- core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java | 1 + 1 file changed, 1 insertion(+) (limited to 'core/src/main/java/de/danoeh/antennapod') 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 c30f46315..7330a6c80 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 @@ -154,6 +154,7 @@ public final class DBReader { Feed feed = feedIndex.get(item.getFeedId()); if (feed == null) { Log.w(TAG, "No match found for item with ID " + item.getId() + ". Feed ID was " + item.getFeedId()); + feed = new Feed("", "", "Error: Item without feed"); } item.setFeed(feed); } -- cgit v1.2.3 From ff116ccd8c3f47de709d207008d9c38ff3ee2d75 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sun, 20 Sep 2020 12:24:11 +0200 Subject: Fixed items without pubdate getting assigned the current date on every single refresh --- .../java/de/danoeh/antennapod/core/feed/Feed.java | 2 +- .../service/download/handler/FeedParserTask.java | 8 -------- .../antennapod/core/storage/PodDBAdapter.java | 6 ++++++ .../util/comparator/FeedItemPubdateComparator.java | 22 +++++++++++++--------- 4 files changed, 20 insertions(+), 18 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java index 0889e5182..3657bcdc6 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java @@ -352,7 +352,7 @@ public class Feed extends FeedFile implements ImageResource { Date mostRecentDate = new Date(0); FeedItem mostRecentItem = null; for (FeedItem item : items) { - if (item.getPubDate().after(mostRecentDate)) { + if (item.getPubDate() != null && item.getPubDate().after(mostRecentDate)) { mostRecentDate = item.getPubDate(); mostRecentItem = item; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java index c50162788..18c5fce27 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java @@ -18,7 +18,6 @@ import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; import java.io.File; import java.io.IOException; -import java.util.Date; import java.util.concurrent.Callable; public class FeedParserTask implements Callable { @@ -104,13 +103,6 @@ public class FeedParserTask implements Callable { if (item.getTitle() == null) { throw new InvalidFeedException("Item has no title: " + item); } - if (item.getPubDate() == null) { - Log.e(TAG, "Item has no pubDate. Using current time as pubDate"); - if (item.getTitle() != null) { - Log.e(TAG, "Title of invalid item: " + item.getTitle()); - } - item.setPubDate(new Date()); - } } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java index 8a3f44e18..4b585d796 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java @@ -21,6 +21,7 @@ import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Set; @@ -593,6 +594,11 @@ public class PodDBAdapter { * @return the id of the entry */ private long setFeedItem(FeedItem item, boolean saveFeed) { + if (item.getId() == 0 && item.getPubDate() == null) { + Log.e(TAG, "Newly saved item has no pubDate. Using current date as pubDate"); + item.setPubDate(new Date()); + } + ContentValues values = new ContentValues(); values.put(KEY_TITLE, item.getTitle()); values.put(KEY_LINK, item.getLink()); diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java index 51fe2da78..ad81a1d17 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java @@ -4,16 +4,20 @@ import java.util.Comparator; import de.danoeh.antennapod.core.feed.FeedItem; -/** Compares the pubDate of two FeedItems for sorting*/ +/** + * Compares the pubDate of two FeedItems for sorting. + */ public class FeedItemPubdateComparator implements Comparator { - /** Returns a new instance of this comparator in reverse order. - public static FeedItemPubdateComparator newInstance() { - FeedItemPubdateComparator - }*/ - @Override - public int compare(FeedItem lhs, FeedItem rhs) { - return rhs.getPubDate().compareTo(lhs.getPubDate()); - } + /** + * Returns a new instance of this comparator in reverse order. + */ + @Override + public int compare(FeedItem lhs, FeedItem rhs) { + if (rhs.getPubDate() == null || lhs.getPubDate() == null) { + return 0; + } + return rhs.getPubDate().compareTo(lhs.getPubDate()); + } } -- cgit v1.2.3 From eb3993f776da330741ef9ef6522a5b20bd45f20b Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sun, 20 Sep 2020 23:13:28 +0200 Subject: New notification icon --- .../core/service/playback/PlaybackServiceNotificationBuilder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') 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 4bb88200a..6a892cc1c 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 @@ -85,7 +85,7 @@ public class PlaybackServiceNotificationBuilder { private Bitmap getDefaultIcon() { if (defaultIcon == null) { - defaultIcon = getBitmap(context, R.drawable.notification_default_large_icon); + defaultIcon = getBitmap(context, R.mipmap.ic_launcher); } return defaultIcon; } @@ -136,7 +136,7 @@ public class PlaybackServiceNotificationBuilder { notification.setContentIntent(getPlayerActivityPendingIntent()); notification.setWhen(0); - notification.setSmallIcon(R.drawable.ic_antenna); + notification.setSmallIcon(R.drawable.ic_notification); notification.setOngoing(false); notification.setOnlyAlertOnce(true); notification.setPriority(UserPreferences.getNotifyPriority()); -- cgit v1.2.3 From e82702655e5b2b5a4c5fe2a3d9276f052571ac68 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Mon, 21 Sep 2020 09:32:07 +0200 Subject: Updated widget icon --- .../de/danoeh/antennapod/core/service/PlayerWidgetJobService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java b/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java index f39ac0df8..7bf1a5df1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java @@ -132,7 +132,7 @@ public class PlayerWidgetJobService extends SafeJobIntentService { views.setImageViewBitmap(R.id.imgvCover, icon); } catch (Throwable tr) { Log.e(TAG, "Error loading the media icon for the widget", tr); - views.setImageViewResource(R.id.imgvCover, R.mipmap.ic_launcher_foreground); + views.setImageViewResource(R.id.imgvCover, R.mipmap.ic_launcher_round); } views.setTextViewText(R.id.txtvTitle, media.getEpisodeTitle()); @@ -171,7 +171,7 @@ public class PlayerWidgetJobService extends SafeJobIntentService { views.setViewVisibility(R.id.txtvProgress, View.GONE); views.setViewVisibility(R.id.txtvTitle, View.GONE); views.setViewVisibility(R.id.txtNoPlaying, View.VISIBLE); - views.setImageViewResource(R.id.imgvCover, R.mipmap.ic_launcher_foreground); + views.setImageViewResource(R.id.imgvCover, R.mipmap.ic_launcher_round); views.setImageViewResource(R.id.butPlay, R.drawable.ic_av_play_white_48dp); } -- cgit v1.2.3 From c339e278135b4f2bca4256891913bb87da04c314 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Mon, 21 Sep 2020 20:35:11 +0200 Subject: Detect network cellular instead of network metered Cellular networks may be unmetered, or Wi-Fi networks may be metered --- .../danoeh/antennapod/core/util/NetworkUtils.java | 172 ++++++++++++--------- 1 file changed, 103 insertions(+), 69 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') 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 a9e46e42c..8cca2f28f 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 @@ -2,9 +2,12 @@ package de.danoeh.antennapod.core.util; import android.content.Context; import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import android.os.Build; import androidx.core.net.ConnectivityManagerCompat; import android.text.TextUtils; import android.util.Log; @@ -29,65 +32,65 @@ import okhttp3.Response; public class NetworkUtils { private NetworkUtils(){} - private static final String TAG = NetworkUtils.class.getSimpleName(); - - private static Context context; - - public static void init(Context context) { - 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); - 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 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; - } - } 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; - } - } - Log.d(TAG, "Network for auto-dl is not available"); - return false; - } + private static final String TAG = NetworkUtils.class.getSimpleName(); + + private static Context context; + + public static void init(Context context) { + 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); + 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 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; + } + } 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; + } + } + Log.d(TAG, "Network for auto-dl is not available"); + return false; + } public static boolean networkAvailable() { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); @@ -96,7 +99,7 @@ public class NetworkUtils { } public static boolean isEpisodeDownloadAllowed() { - return UserPreferences.isAllowMobileEpisodeDownload() || !NetworkUtils.isNetworkMetered(); + return UserPreferences.isAllowMobileEpisodeDownload() || !NetworkUtils.isNetworkRestricted(); } public static boolean isEpisodeHeadDownloadAllowed() { @@ -106,22 +109,53 @@ public class NetworkUtils { } public static boolean isImageAllowed() { - return UserPreferences.isAllowMobileImages() || !NetworkUtils.isNetworkMetered(); + return UserPreferences.isAllowMobileImages() || !NetworkUtils.isNetworkRestricted(); } public static boolean isStreamingAllowed() { - return UserPreferences.isAllowMobileStreaming() || !NetworkUtils.isNetworkMetered(); + return UserPreferences.isAllowMobileStreaming() || !NetworkUtils.isNetworkRestricted(); } public static boolean isFeedRefreshAllowed() { - return UserPreferences.isAllowMobileFeedRefresh() || !NetworkUtils.isNetworkMetered(); + return UserPreferences.isAllowMobileFeedRefresh() || !NetworkUtils.isNetworkRestricted(); + } + + public static boolean isNetworkRestricted() { + return isNetworkMetered() || isNetworkCellular(); } - private static boolean isNetworkMetered() { - ConnectivityManager connManager = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); + private static boolean isNetworkMetered() { + ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); return ConnectivityManagerCompat.isActiveNetworkMetered(connManager); - } + } + + private static boolean isNetworkCellular() { + ConnectivityManager connManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + if (Build.VERSION.SDK_INT >= 23) { + Network network = connManager.getActiveNetwork(); + if (network == null) { + return false; // Nothing connected + } + NetworkInfo info = connManager.getNetworkInfo(network); + if (info == null) { + return true; // Better be safe than sorry + } + NetworkCapabilities capabilities = connManager.getNetworkCapabilities(network); + if (capabilities == null) { + return true; // Better be safe than sorry + } + return capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); + } else { + // if the default network is a VPN, + // this method will return the NetworkInfo for one of its underlying networks + NetworkInfo info = connManager.getActiveNetworkInfo(); + if (info == null) { + return false; // Nothing connected + } + //noinspection deprecation + return info.getType() == ConnectivityManager.TYPE_MOBILE; + } + } /** * Returns the SSID of the wifi connection, or null if there is no wifi. @@ -135,7 +169,7 @@ public class NetworkUtils { return null; } - public static Single getFeedMediaSizeObservable(FeedMedia media) { + public static Single getFeedMediaSizeObservable(FeedMedia media) { return Single.create((SingleOnSubscribe) emitter -> { if (!NetworkUtils.isEpisodeHeadDownloadAllowed()) { emitter.onSuccess(0L); @@ -188,7 +222,7 @@ public class NetworkUtils { DBWriter.setFeedMedia(media); }) .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); + .observeOn(AndroidSchedulers.mainThread()); } } -- cgit v1.2.3 From 3bb2e2ae17ca848ce1a24b48066210ce36f29965 Mon Sep 17 00:00:00 2001 From: Tony Tam Date: Tue, 18 Feb 2020 06:58:08 -0800 Subject: Add custom rewind and fast forward for Android Auto --- .../core/service/playback/PlaybackService.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'core/src/main/java/de/danoeh/antennapod') 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 bf59b0ffd..8a48ed571 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 @@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.service.playback; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; +import android.app.UiModeManager; import android.bluetooth.BluetoothA2dp; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -10,6 +11,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.res.Configuration; import android.graphics.Bitmap; import android.media.AudioManager; import android.media.MediaPlayer; @@ -1094,6 +1096,20 @@ public class PlaybackService extends MediaBrowserServiceCompat { capabilities = capabilities | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; } + UiModeManager uiModeManager = (UiModeManager) getApplicationContext().getSystemService(Context.UI_MODE_SERVICE); + if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) { + sessionState.addCustomAction( + new PlaybackStateCompat.CustomAction.Builder( + CUSTOM_ACTION_REWIND, + getString(R.string.rewind_label), R.drawable.ic_notification_fast_rewind) + .build()); + sessionState.addCustomAction( + new PlaybackStateCompat.CustomAction.Builder( + CUSTOM_ACTION_FAST_FORWARD, + getString(R.string.fast_forward_label), R.drawable.ic_notification_fast_forward) + .build()); + } + sessionState.setActions(capabilities); flavorHelper.sessionStateAddActionForWear(sessionState, -- cgit v1.2.3 From f54076ca587e89c8af1bfb3d4a8e5bb442405e85 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Tue, 29 Sep 2020 12:00:40 +0200 Subject: Swallow undeliverable RxJava exceptions AntennaPod threads might throw NPEs after disposing because we set controllers to null. --- .../util/exception/RxJavaErrorHandlerSetup.java | 24 ---------------------- 1 file changed, 24 deletions(-) delete mode 100644 core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java b/core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java deleted file mode 100644 index 223104d2e..000000000 --- a/core/src/main/java/de/danoeh/antennapod/core/util/exception/RxJavaErrorHandlerSetup.java +++ /dev/null @@ -1,24 +0,0 @@ -package de.danoeh.antennapod.core.util.exception; - -import android.util.Log; -import io.reactivex.exceptions.UndeliverableException; -import io.reactivex.plugins.RxJavaPlugins; - -public class RxJavaErrorHandlerSetup { - - private RxJavaErrorHandlerSetup() { - - } - - public static void setupRxJavaErrorHandler() { - RxJavaPlugins.setErrorHandler(e -> { - if (e instanceof UndeliverableException) { - // Probably just disposed because the fragment was left - Log.d("RxJavaErrorHandler", "Ignored exception: " + Log.getStackTraceString(e)); - return; - } - Thread.currentThread().getUncaughtExceptionHandler() - .uncaughtException(Thread.currentThread(), e); - }); - } -} -- cgit v1.2.3 From 4f86047a24bdfd1cdb730487f828d5b24331d6d4 Mon Sep 17 00:00:00 2001 From: Mats Wahlberg Date: Wed, 22 Apr 2020 16:22:48 +0200 Subject: Enable TLSv1.3 and harden protocols and cipher suites for Free builds The Free build bundles a modern Conscrypt which means TLSv1.3 is always guaranteed no matter android version. So it can always be enabled. Since it also provides modern cipher suites, there is no need to enable older protocols than TLSv1.2 (that is: SSLv3, TLSv1.0 and TLSv1.1 which are all now deprecated). And the support for modern cipher suites also means there is no need to explicitly enable the following (obsolete+unsafe) ciphers suites: * TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA * TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA on Android API < 21 (Android < 5.0). No changes are made to the Play builds (since the available security provider can't be guaranteed to support modern protocols and cipher suites). --- .../service/download/AntennapodHttpClient.java | 43 ++++++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java index e0c23bdac..fc971cd59 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java @@ -34,6 +34,7 @@ import javax.net.ssl.X509TrustManager; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.UserAgentInterceptor; import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.core.util.Flavors; import okhttp3.Cache; import okhttp3.CipherSuite; import okhttp3.ConnectionSpec; @@ -149,7 +150,17 @@ public class AntennapodHttpClient { }); } } - if (Build.VERSION.SDK_INT < 21) { + + // The Free flavor bundles a modern conscrypt (security provider), so CustomSslSocketFactory + // is only used to make sure that modern protocols (TLSv1.3 and TLSv1.2) are enabled and + // that old, deprecated, protocols (like SSLv3, TLSv1.0 and TLSv1.1) are disabled. + if (Flavors.FLAVOR == Flavors.FREE) { + builder.sslSocketFactory(new CustomSslSocketFactory(), trustManager()); + } + // The Play flavor can not be assumed to have a modern security provider, so for Android + // older than 5.0 CustomSslSocketFactory is used to enable all possible protocols (modern + // and deprecated). And we explicitly enable deprecated cipher suites disabled by default. + else if (Build.VERSION.SDK_INT < 21) { builder.sslSocketFactory(new CustomSslSocketFactory(), trustManager()); // workaround for Android 4.x for certain web sites. @@ -178,6 +189,9 @@ public class AntennapodHttpClient { } } + /** + * Reimplements default trust manager (required for calling sslSocketFactory). + */ private static X509TrustManager trustManager() { try { TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( @@ -199,13 +213,27 @@ public class AntennapodHttpClient { AntennapodHttpClient.cacheDirectory = cacheDirectory; } + /** + * Used to disable deprecated protocols and explicitly enable TLSv1.3 and TLSv1.2, or to enable + * all protocols (including deprecated) up to TLSv1.2, depending on build flavor (Free or Play). + */ private static class CustomSslSocketFactory extends SSLSocketFactory { private SSLSocketFactory factory; public CustomSslSocketFactory() { try { - SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); + SSLContext sslContext; + + // Free flavor (bundles modern conscrypt): support for TLSv1.3 is guaranteed. + if (Flavors.FLAVOR == Flavors.FREE) { + sslContext = SSLContext.getInstance("TLSv1.3"); + } + // Play flavor (security provider can vary): only TLSv1.2 is guaranteed. + else { + sslContext = SSLContext.getInstance("TLSv1.2"); + } + sslContext.init(null, null, null); factory= sslContext.getSocketFactory(); } catch(GeneralSecurityException e) { @@ -260,7 +288,16 @@ public class AntennapodHttpClient { } private void configureSocket(SSLSocket s) { - s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" } ); + // Free flavor (bundles modern conscrypt): TLSv1.3 and modern cipher suites are + // guaranteed. Protocols older than TLSv1.2 are now deprecated and can be disabled. + if (Flavors.FLAVOR == Flavors.FREE) { + s.setEnabledProtocols(new String[] { "TLSv1.3", "TLSv1.2" } ); + } + // Play flavor (security provider can vary): only TLSv1.2 is guaranteed, supported + // cipher suites may vary. Old protocols might be necessary to keep things working. + else { + s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" } ); + } } } -- cgit v1.2.3 From 3271d7628fd62d3e26c150ae26fc095d24c6fa37 Mon Sep 17 00:00:00 2001 From: Mats Wahlberg Date: Sun, 26 Apr 2020 16:57:08 +0200 Subject: Fixed circle static-analysis error No code changes, but formatting with comments around else statements needed to be clearer, also some whitespaces should have been avoided... --- .../service/download/AntennapodHttpClient.java | 37 ++++++++++------------ 1 file changed, 17 insertions(+), 20 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java index fc971cd59..807af0a3f 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java @@ -151,16 +151,15 @@ public class AntennapodHttpClient { } } - // The Free flavor bundles a modern conscrypt (security provider), so CustomSslSocketFactory - // is only used to make sure that modern protocols (TLSv1.3 and TLSv1.2) are enabled and - // that old, deprecated, protocols (like SSLv3, TLSv1.0 and TLSv1.1) are disabled. if (Flavors.FLAVOR == Flavors.FREE) { + // The Free flavor bundles a modern conscrypt (security provider), so CustomSslSocketFactory + // is only used to make sure that modern protocols (TLSv1.3 and TLSv1.2) are enabled and + // that old, deprecated, protocols (like SSLv3, TLSv1.0 and TLSv1.1) are disabled. builder.sslSocketFactory(new CustomSslSocketFactory(), trustManager()); - } - // The Play flavor can not be assumed to have a modern security provider, so for Android - // older than 5.0 CustomSslSocketFactory is used to enable all possible protocols (modern - // and deprecated). And we explicitly enable deprecated cipher suites disabled by default. - else if (Build.VERSION.SDK_INT < 21) { + } else if (Build.VERSION.SDK_INT < 21) { + // The Play flavor can not be assumed to have a modern security provider, so for Android + // older than 5.0 CustomSslSocketFactory is used to enable all possible protocols (modern + // and deprecated). And we explicitly enable deprecated cipher suites disabled by default. builder.sslSocketFactory(new CustomSslSocketFactory(), trustManager()); // workaround for Android 4.x for certain web sites. @@ -225,12 +224,11 @@ public class AntennapodHttpClient { try { SSLContext sslContext; - // Free flavor (bundles modern conscrypt): support for TLSv1.3 is guaranteed. if (Flavors.FLAVOR == Flavors.FREE) { + // Free flavor (bundles modern conscrypt): support for TLSv1.3 is guaranteed. sslContext = SSLContext.getInstance("TLSv1.3"); - } - // Play flavor (security provider can vary): only TLSv1.2 is guaranteed. - else { + } else { + // Play flavor (security provider can vary): only TLSv1.2 is guaranteed. sslContext = SSLContext.getInstance("TLSv1.2"); } @@ -288,15 +286,14 @@ public class AntennapodHttpClient { } private void configureSocket(SSLSocket s) { - // Free flavor (bundles modern conscrypt): TLSv1.3 and modern cipher suites are - // guaranteed. Protocols older than TLSv1.2 are now deprecated and can be disabled. if (Flavors.FLAVOR == Flavors.FREE) { - s.setEnabledProtocols(new String[] { "TLSv1.3", "TLSv1.2" } ); - } - // Play flavor (security provider can vary): only TLSv1.2 is guaranteed, supported - // cipher suites may vary. Old protocols might be necessary to keep things working. - else { - s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" } ); + // Free flavor (bundles modern conscrypt): TLSv1.3 and modern cipher suites are + // guaranteed. Protocols older than TLSv1.2 are now deprecated and can be disabled. + s.setEnabledProtocols(new String[] { "TLSv1.3", "TLSv1.2" }); + } else { + // Play flavor (security provider can vary): only TLSv1.2 is guaranteed, supported + // cipher suites may vary. Old protocols might be necessary to keep things working. + s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" }); } } -- cgit v1.2.3 From 7513c6e1ad1c2ef0f1b1f2d050dc61ea6cab73f9 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Thu, 1 Oct 2020 10:48:53 +0200 Subject: Work around a Lint crash https://issuetracker.google.com/issues/124861181 --- .../antennapod/core/service/playback/PlaybackServiceTaskManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java index 883ba6023..0a908020a 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java @@ -102,7 +102,7 @@ public class PlaybackServiceTaskManager { private synchronized void loadQueue() { if (!isQueueLoaderActive()) { - queueFuture = schedExecutor.submit(DBReader::getQueue); + queueFuture = schedExecutor.submit(() -> DBReader.getQueue()); } } -- cgit v1.2.3 From 74d18b747adcf90be3ab9b79c6d1813d5b174043 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Thu, 1 Oct 2020 10:52:27 +0200 Subject: Cancel ChapterLoader correctly --- .../playback/PlaybackServiceTaskManager.java | 26 +++++++++------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java index 0a908020a..9ef34cdac 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java @@ -8,6 +8,7 @@ import androidx.annotation.NonNull; import android.util.Log; import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; +import io.reactivex.disposables.Disposable; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -57,7 +58,7 @@ public class PlaybackServiceTaskManager { private ScheduledFuture widgetUpdaterFuture; private ScheduledFuture sleepTimerFuture; private volatile Future> queueFuture; - private volatile Future chapterLoaderFuture; + private volatile Disposable chapterLoaderFuture; private SleepTimer sleepTimer; @@ -289,28 +290,19 @@ public class PlaybackServiceTaskManager { } } - private synchronized void cancelChapterLoader() { - if (isChapterLoaderActive()) { - chapterLoaderFuture.cancel(true); - } - } - - private synchronized boolean isChapterLoaderActive() { - return chapterLoaderFuture != null && !chapterLoaderFuture.isDone(); - } - /** * Starts a new thread that loads the chapter marks from a playable object. If another chapter loader is already active, * it will be cancelled first. * On completion, the callback's onChapterLoaded method will be called. */ public synchronized void startChapterLoader(@NonNull final Playable media) { - if (isChapterLoaderActive()) { - cancelChapterLoader(); + if (chapterLoaderFuture != null) { + chapterLoaderFuture.dispose(); + chapterLoaderFuture = null; } if (media.getChapters() == null) { - Completable.create(emitter -> { + chapterLoaderFuture = Completable.create(emitter -> { media.loadChapterMarks(); emitter.onComplete(); }) @@ -330,7 +322,11 @@ public class PlaybackServiceTaskManager { cancelWidgetUpdater(); disableSleepTimer(); cancelQueueLoader(); - cancelChapterLoader(); + + if (chapterLoaderFuture != null) { + chapterLoaderFuture.dispose(); + chapterLoaderFuture = null; + } } /** -- cgit v1.2.3 From 0c2720625cf8ee685b3706bcaca76c446cfa3d6b Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Thu, 1 Oct 2020 11:06:01 +0200 Subject: Enabled Lint for core module --- .../java/de/danoeh/antennapod/core/preferences/UserPreferences.java | 4 ++-- core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java index bcbc041a6..5700bb9a0 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java @@ -249,7 +249,7 @@ public class UserPreferences { public static void setFeedOrder(String selected) { prefs.edit() .putString(PREF_DRAWER_FEED_ORDER, selected) - .commit(); + .apply(); } public static int getFeedCounterSetting() { @@ -1054,7 +1054,7 @@ public class UserPreferences { public static void setFeedFilter(String value) { prefs.edit() .putString(PREF_FILTER_FEED, value) - .commit(); + .apply(); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java index 16e2825b4..4f2417b7d 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java @@ -257,7 +257,6 @@ public final class DBTasks { EventBus.getDefault().post(new MessageEvent(context.getString(R.string.error_file_not_found))); } - @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public static List enqueueFeedItemsToDownload(final Context context, List items) throws InterruptedException, ExecutionException { List itemsToEnqueue = new ArrayList<>(); -- cgit v1.2.3 From 5a10728aa97e41f857bf219d30866ac192712a7d Mon Sep 17 00:00:00 2001 From: Connectety Date: Thu, 1 Oct 2020 12:59:03 +0200 Subject: Delete private constructor in FeedPreferences which is only used by other consturctor (#4477) --- .../main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java index 2a2568f28..5ffee0d62 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java @@ -38,11 +38,8 @@ public class FeedPreferences { private int feedSkipEnding; public FeedPreferences(long feedID, boolean autoDownload, AutoDeleteAction auto_delete_action, VolumeAdaptionSetting volumeAdaptionSetting, String username, String password) { - this(feedID, autoDownload, true, auto_delete_action, volumeAdaptionSetting, username, password, new FeedFilter(), SPEED_USE_GLOBAL); - } - - private FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated, AutoDeleteAction auto_delete_action, VolumeAdaptionSetting volumeAdaptionSetting, String username, String password, @NonNull FeedFilter filter, float feedPlaybackSpeed) { - this(feedID, autoDownload, true, auto_delete_action, volumeAdaptionSetting, username, password, new FeedFilter(), feedPlaybackSpeed, 0, 0); + this(feedID, autoDownload, true, auto_delete_action, volumeAdaptionSetting, + username, password, new FeedFilter(), SPEED_USE_GLOBAL, 0, 0); } private FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated, AutoDeleteAction auto_delete_action, VolumeAdaptionSetting volumeAdaptionSetting, String username, String password, @NonNull FeedFilter filter, float feedPlaybackSpeed, int feedSkipIntro, int feedSkipEnding) { -- cgit v1.2.3 From 923be5bfdd8168325079968df8fb14de4f7b59b4 Mon Sep 17 00:00:00 2001 From: Bo Jacobs Date: Thu, 1 Oct 2020 23:49:34 +0200 Subject: Improve search feature by using keywords instead of full match (#4478) --- .../antennapod/core/storage/PodDBAdapter.java | 84 +++++++++++++++------- 1 file changed, 59 insertions(+), 25 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java index 4b585d796..e552cc180 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java @@ -20,7 +20,6 @@ import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; -import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Locale; @@ -1207,12 +1206,17 @@ public class PodDBAdapter { * Uses DatabaseUtils to escape a search query and removes ' at the * beginning and the end of the string returned by the escape method. */ - private String prepareSearchQuery(String query) { - StringBuilder builder = new StringBuilder(); - DatabaseUtils.appendEscapedSQLString(builder, query); - builder.deleteCharAt(0); - builder.deleteCharAt(builder.length() - 1); - return builder.toString(); + private String[] prepareSearchQuery(String query) { + String[] queryWords = query.split("\\s+"); + for (int i = 0; i < queryWords.length; ++i) { + StringBuilder builder = new StringBuilder(); + DatabaseUtils.appendEscapedSQLString(builder, queryWords[i]); + builder.deleteCharAt(0); + builder.deleteCharAt(builder.length() - 1); + queryWords[i] = builder.toString(); + } + + return queryWords; } /** @@ -1222,7 +1226,7 @@ public class PodDBAdapter { * @return A cursor with all search results in SEL_FI_EXTRA selection. */ public Cursor searchItems(long feedID, String searchQuery) { - String preparedQuery = prepareSearchQuery(searchQuery); + String[] queryWords = prepareSearchQuery(searchQuery); String queryFeedId; if (feedID != 0) { @@ -1233,14 +1237,28 @@ public class PodDBAdapter { queryFeedId = "1 = 1"; } - String query = SELECT_FEED_ITEMS_AND_MEDIA_WITH_DESCRIPTION - + " WHERE " + queryFeedId + " AND (" - + KEY_DESCRIPTION + " LIKE '%" + preparedQuery + "%' OR " - + KEY_CONTENT_ENCODED + " LIKE '%" + preparedQuery + "%' OR " - + KEY_TITLE + " LIKE '%" + preparedQuery + "%'" - + ") ORDER BY " + KEY_PUBDATE + " DESC " - + "LIMIT 300"; - return db.rawQuery(query, null); + String queryStart = SELECT_FEED_ITEMS_AND_MEDIA_WITH_DESCRIPTION + + " WHERE " + queryFeedId + " AND ("; + StringBuilder sb = new StringBuilder(queryStart); + + for (int i = 0; i < queryWords.length; i++) { + sb + .append("(") + .append(KEY_DESCRIPTION + " LIKE '%").append(queryWords[i]) + .append("%' OR ") + .append(KEY_CONTENT_ENCODED).append(" LIKE '%").append(queryWords[i]) + .append("%' OR ") + .append(KEY_TITLE).append(" LIKE '%").append(queryWords[i]) + .append("%') "); + + if (i != queryWords.length - 1) { + sb.append("AND "); + } + } + + sb.append(") ORDER BY " + KEY_PUBDATE + " DESC LIMIT 300"); + + return db.rawQuery(sb.toString(), null); } /** @@ -1249,15 +1267,31 @@ public class PodDBAdapter { * @return A cursor with all search results in SEL_FI_EXTRA selection. */ public Cursor searchFeeds(String searchQuery) { - String preparedQuery = prepareSearchQuery(searchQuery); - String query = "SELECT * FROM " + TABLE_NAME_FEEDS + " WHERE " - + KEY_TITLE + " LIKE '%" + preparedQuery + "%' OR " - + KEY_CUSTOM_TITLE + " LIKE '%" + preparedQuery + "%' OR " - + KEY_AUTHOR + " LIKE '%" + preparedQuery + "%' OR " - + KEY_DESCRIPTION + " LIKE '%" + preparedQuery + "%' " - + "ORDER BY " + KEY_TITLE + " ASC " - + "LIMIT 300"; - return db.rawQuery(query, null); + String[] queryWords = prepareSearchQuery(searchQuery); + + String queryStart = "SELECT * FROM " + TABLE_NAME_FEEDS + " WHERE "; + StringBuilder sb = new StringBuilder(queryStart); + + for (int i = 0; i < queryWords.length; i++) { + sb + .append("(") + .append(KEY_TITLE).append(" LIKE '%").append(queryWords[i]) + .append("%' OR ") + .append(KEY_CUSTOM_TITLE).append(" LIKE '%").append(queryWords[i]) + .append("%' OR ") + .append(KEY_AUTHOR).append(" LIKE '%").append(queryWords[i]) + .append("%' OR ") + .append(KEY_DESCRIPTION).append(" LIKE '%").append(queryWords[i]) + .append("%') "); + + if (i != queryWords.length - 1) { + sb.append("AND "); + } + } + + sb.append("ORDER BY " + KEY_TITLE + " ASC LIMIT 300"); + + return db.rawQuery(sb.toString(), null); } /** -- cgit v1.2.3 From 449880287bcd87f9059838ec29447c56823104de Mon Sep 17 00:00:00 2001 From: Patrick Kennedy Date: Sun, 4 Oct 2020 19:18:45 +0100 Subject: Add plural default resource for download_report_content string. (#4492) --- .../core/service/download/DownloadServiceNotification.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java index 64666d25d..975bc3cb3 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java @@ -153,7 +153,11 @@ public class DownloadServiceNotification { iconId = R.drawable.ic_notification_sync_error; intent = ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(context); id = R.id.notification_download_report; - content = String.format(context.getString(R.string.download_report_content), successfulDownloads, failedDownloads); + content = context.getResources() + .getQuantityString(R.plurals.download_report_content, + successfulDownloads, + successfulDownloads, + failedDownloads); } NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelId); -- cgit v1.2.3 From 2e0a191d5c45fb1d7fc24144db11f070a866bc5f Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Mon, 5 Oct 2020 12:44:47 +0200 Subject: Ship our own CA certificates for old devices --- .../service/download/AntennapodHttpClient.java | 175 +++------------------ .../core/service/download/HttpDownloader.java | 1 - .../antennapod/core/ssl/BackportCaCerts.java | 73 +++++++++ .../antennapod/core/ssl/BackportTrustManager.java | 58 +++++++ .../core/ssl/CompositeX509TrustManager.java | 60 +++++++ .../antennapod/core/ssl/NoV1SslSocketFactory.java | 100 ++++++++++++ 6 files changed, 313 insertions(+), 154 deletions(-) create mode 100644 core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java create mode 100644 core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java create mode 100644 core/src/main/java/de/danoeh/antennapod/core/ssl/CompositeX509TrustManager.java create mode 100644 core/src/main/java/de/danoeh/antennapod/core/ssl/NoV1SslSocketFactory.java (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java index 807af0a3f..a01b3cb52 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java @@ -1,38 +1,14 @@ package de.danoeh.antennapod.core.service.download; import android.os.Build; -import androidx.annotation.NonNull; import android.text.TextUtils; import android.util.Log; - -import de.danoeh.antennapod.core.service.BasicAuthorizationInterceptor; - -import java.io.File; -import java.io.IOException; -import java.net.CookieManager; -import java.net.CookiePolicy; -import java.net.HttpURLConnection; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.Socket; -import java.net.SocketAddress; -import java.security.GeneralSecurityException; -import java.security.KeyStore; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocket; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import javax.net.ssl.X509TrustManager; - +import androidx.annotation.NonNull; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.service.BasicAuthorizationInterceptor; import de.danoeh.antennapod.core.service.UserAgentInterceptor; +import de.danoeh.antennapod.core.ssl.BackportTrustManager; +import de.danoeh.antennapod.core.ssl.NoV1SslSocketFactory; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.Flavors; import okhttp3.Cache; @@ -46,6 +22,19 @@ import okhttp3.Request; import okhttp3.Response; import okhttp3.internal.http.StatusLine; +import javax.net.ssl.X509TrustManager; +import java.io.File; +import java.net.CookieManager; +import java.net.CookiePolicy; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; + /** * Provides access to a HttpClient singleton. */ @@ -155,17 +144,15 @@ public class AntennapodHttpClient { // The Free flavor bundles a modern conscrypt (security provider), so CustomSslSocketFactory // is only used to make sure that modern protocols (TLSv1.3 and TLSv1.2) are enabled and // that old, deprecated, protocols (like SSLv3, TLSv1.0 and TLSv1.1) are disabled. - builder.sslSocketFactory(new CustomSslSocketFactory(), trustManager()); + X509TrustManager trustManager = BackportTrustManager.create(); + builder.sslSocketFactory(new NoV1SslSocketFactory(trustManager), trustManager); } else if (Build.VERSION.SDK_INT < 21) { - // The Play flavor can not be assumed to have a modern security provider, so for Android - // older than 5.0 CustomSslSocketFactory is used to enable all possible protocols (modern - // and deprecated). And we explicitly enable deprecated cipher suites disabled by default. - builder.sslSocketFactory(new CustomSslSocketFactory(), trustManager()); + X509TrustManager trustManager = BackportTrustManager.create(); + builder.sslSocketFactory(new NoV1SslSocketFactory(trustManager), trustManager); // workaround for Android 4.x for certain web sites. // see: https://github.com/square/okhttp/issues/4053#issuecomment-402579554 - List cipherSuites = new ArrayList<>(); - cipherSuites.addAll(ConnectionSpec.MODERN_TLS.cipherSuites()); + List cipherSuites = new ArrayList<>(ConnectionSpec.MODERN_TLS.cipherSuites()); cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA); cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA); @@ -178,125 +165,7 @@ public class AntennapodHttpClient { return builder; } - /** - * Closes expired connections. This method should be called by the using class once has finished its work with - * the HTTP client. - */ - public static synchronized void cleanup() { - if (httpClient != null) { - // does nothing at the moment - } - } - - /** - * Reimplements default trust manager (required for calling sslSocketFactory). - */ - private static X509TrustManager trustManager() { - try { - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( - TrustManagerFactory.getDefaultAlgorithm()); - trustManagerFactory.init((KeyStore) null); - TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); - if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { - throw new IllegalStateException("Unexpected default trust managers:" - + Arrays.toString(trustManagers)); - } - return (X509TrustManager) trustManagers[0]; - } catch (Exception e) { - Log.e(TAG, Log.getStackTraceString(e)); - return null; - } - } - public static void setCacheDirectory(File cacheDirectory) { AntennapodHttpClient.cacheDirectory = cacheDirectory; } - - /** - * Used to disable deprecated protocols and explicitly enable TLSv1.3 and TLSv1.2, or to enable - * all protocols (including deprecated) up to TLSv1.2, depending on build flavor (Free or Play). - */ - private static class CustomSslSocketFactory extends SSLSocketFactory { - - private SSLSocketFactory factory; - - public CustomSslSocketFactory() { - try { - SSLContext sslContext; - - if (Flavors.FLAVOR == Flavors.FREE) { - // Free flavor (bundles modern conscrypt): support for TLSv1.3 is guaranteed. - sslContext = SSLContext.getInstance("TLSv1.3"); - } else { - // Play flavor (security provider can vary): only TLSv1.2 is guaranteed. - sslContext = SSLContext.getInstance("TLSv1.2"); - } - - sslContext.init(null, null, null); - factory= sslContext.getSocketFactory(); - } catch(GeneralSecurityException e) { - e.printStackTrace(); - } - } - - @Override - public String[] getDefaultCipherSuites() { - return factory.getDefaultCipherSuites(); - } - - @Override - public String[] getSupportedCipherSuites() { - return factory.getSupportedCipherSuites(); - } - - public Socket createSocket() throws IOException { - SSLSocket result = (SSLSocket) factory.createSocket(); - configureSocket(result); - return result; - } - - public Socket createSocket(String var1, int var2) throws IOException { - SSLSocket result = (SSLSocket) factory.createSocket(var1, var2); - configureSocket(result); - return result; - } - - public Socket createSocket(Socket var1, String var2, int var3, boolean var4) throws IOException { - SSLSocket result = (SSLSocket) factory.createSocket(var1, var2, var3, var4); - configureSocket(result); - return result; - } - - public Socket createSocket(InetAddress var1, int var2) throws IOException { - SSLSocket result = (SSLSocket) factory.createSocket(var1, var2); - configureSocket(result); - return result; - } - - public Socket createSocket(String var1, int var2, InetAddress var3, int var4) throws IOException { - SSLSocket result = (SSLSocket) factory.createSocket(var1, var2, var3, var4); - configureSocket(result); - return result; - } - - public Socket createSocket(InetAddress var1, int var2, InetAddress var3, int var4) throws IOException { - SSLSocket result = (SSLSocket) factory.createSocket(var1, var2, var3, var4); - configureSocket(result); - return result; - } - - private void configureSocket(SSLSocket s) { - if (Flavors.FLAVOR == Flavors.FREE) { - // Free flavor (bundles modern conscrypt): TLSv1.3 and modern cipher suites are - // guaranteed. Protocols older than TLSv1.2 are now deprecated and can be disabled. - s.setEnabledProtocols(new String[] { "TLSv1.3", "TLSv1.2" }); - } else { - // Play flavor (security provider can vary): only TLSv1.2 is guaranteed, supported - // cipher suites may vary. Old protocols might be necessary to keep things working. - s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" }); - } - } - - } - } diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java index e65e6fe26..4072772ac 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java @@ -259,7 +259,6 @@ public class HttpDownloader extends Downloader { onFail(DownloadError.ERROR_CONNECTION_ERROR, request.getSource()); } finally { IOUtils.closeQuietly(out); - AntennapodHttpClient.cleanup(); IOUtils.closeQuietly(responseBody); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java new file mode 100644 index 000000000..720d6a9d9 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java @@ -0,0 +1,73 @@ +package de.danoeh.antennapod.core.ssl; + +public class BackportCaCerts { + public static final String SECTIGO_USER_TRUST = "-----BEGIN CERTIFICATE-----\n" + + "MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB\n" + + "iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n" + + "cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n" + + "BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw\n" + + "MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV\n" + + "BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU\n" + + "aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy\n" + + "dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n" + + "AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B\n" + + "3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY\n" + + "tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/\n" + + "Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2\n" + + "VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT\n" + + "79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6\n" + + "c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT\n" + + "Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l\n" + + "c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee\n" + + "UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE\n" + + "Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd\n" + + "BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G\n" + + "A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF\n" + + "Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO\n" + + "VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3\n" + + "ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs\n" + + "8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR\n" + + "iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze\n" + + "Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ\n" + + "XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/\n" + + "qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB\n" + + "VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB\n" + + "L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG\n" + + "jjxDah2nGN59PRbxYvnKkKj9\n" + + "-----END CERTIFICATE-----\n"; + + public static final String COMODO = "-----BEGIN CERTIFICATE-----\n" + + "MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB\n" + + "hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n" + + "A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV\n" + + "BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5\n" + + "MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT\n" + + "EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR\n" + + "Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh\n" + + "dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR\n" + + "6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X\n" + + "pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC\n" + + "9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV\n" + + "/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf\n" + + "Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z\n" + + "+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w\n" + + "qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah\n" + + "SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC\n" + + "u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf\n" + + "Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq\n" + + "crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E\n" + + "FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB\n" + + "/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl\n" + + "wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM\n" + + "4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV\n" + + "2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna\n" + + "FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ\n" + + "CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK\n" + + "boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke\n" + + "jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL\n" + + "S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb\n" + + "QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl\n" + + "0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB\n" + + "NVOFBkpdn627G190\n" + + "-----END CERTIFICATE-----"; +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java new file mode 100644 index 000000000..b8fe950b2 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java @@ -0,0 +1,58 @@ +package de.danoeh.antennapod.core.ssl; + +import android.util.Log; + +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509TrustManager; +import java.io.ByteArrayInputStream; +import java.nio.charset.Charset; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateFactory; +import java.util.ArrayList; +import java.util.List; + +/** + * SSL trust manager that allows old Android systems to use modern certificates. + */ +public class BackportTrustManager { + private static final String TAG = "BackportTrustManager"; + + private static X509TrustManager getSystemTrustManager(KeyStore keystore) { + TrustManagerFactory factory; + try { + factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + factory.init(keystore); + for (TrustManager manager : factory.getTrustManagers()) { + if (manager instanceof X509TrustManager) { + return (X509TrustManager) manager; + } + } + } catch (NoSuchAlgorithmException | KeyStoreException e) { + e.printStackTrace(); + } + throw new IllegalStateException("Unexpected default trust managers"); + } + + public static X509TrustManager create() { + try { + KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); + keystore.load(null); // Clear + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + keystore.setCertificateEntry("BACKPORT_COMODO_ROOT_CA", cf.generateCertificate( + new ByteArrayInputStream(BackportCaCerts.COMODO.getBytes(Charset.forName("UTF-8"))))); + keystore.setCertificateEntry("SECTIGO_USER_TRUST_CA", cf.generateCertificate( + new ByteArrayInputStream(BackportCaCerts.SECTIGO_USER_TRUST.getBytes(Charset.forName("UTF-8"))))); + + List managers = new ArrayList<>(); + managers.add(getSystemTrustManager(keystore)); + managers.add(getSystemTrustManager(null)); + return new CompositeX509TrustManager(managers); + } catch (Exception e) { + Log.e(TAG, Log.getStackTraceString(e)); + return null; + } + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/ssl/CompositeX509TrustManager.java b/core/src/main/java/de/danoeh/antennapod/core/ssl/CompositeX509TrustManager.java new file mode 100644 index 000000000..7af96a492 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/ssl/CompositeX509TrustManager.java @@ -0,0 +1,60 @@ +package de.danoeh.antennapod.core.ssl; + +import javax.net.ssl.X509TrustManager; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Represents an ordered list of {@link X509TrustManager}s with additive trust. If any one of the composed managers + * trusts a certificate chain, then it is trusted by the composite manager. + * Based on https://stackoverflow.com/a/16229909 + */ +public class CompositeX509TrustManager implements X509TrustManager { + private final List trustManagers; + + public CompositeX509TrustManager(List trustManagers) { + this.trustManagers = trustManagers; + } + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + CertificateException reason = null; + for (X509TrustManager trustManager : trustManagers) { + try { + trustManager.checkClientTrusted(chain, authType); + return; // someone trusts them. success! + } catch (CertificateException e) { + // maybe someone else will trust them + reason = e; + } + } + throw reason; + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + CertificateException reason = null; + for (X509TrustManager trustManager : trustManagers) { + try { + trustManager.checkServerTrusted(chain, authType); + return; // someone trusts them. success! + } catch (CertificateException e) { + // maybe someone else will trust them + reason = e; + } + } + throw reason; + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + List certificates = new ArrayList<>(); + for (X509TrustManager trustManager : trustManagers) { + certificates.addAll(Arrays.asList(trustManager.getAcceptedIssuers())); + } + return certificates.toArray(new X509Certificate[0]); + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/ssl/NoV1SslSocketFactory.java b/core/src/main/java/de/danoeh/antennapod/core/ssl/NoV1SslSocketFactory.java new file mode 100644 index 000000000..96a42f22d --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/ssl/NoV1SslSocketFactory.java @@ -0,0 +1,100 @@ +package de.danoeh.antennapod.core.ssl; + +import de.danoeh.antennapod.core.util.Flavors; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.security.GeneralSecurityException; + +/** + * SSLSocketFactory that does not use TLS 1.0 + * This fixes issues with old Android versions that abort if the server does not know TLS 1.0 + */ +public class NoV1SslSocketFactory extends SSLSocketFactory { + private SSLSocketFactory factory; + + public NoV1SslSocketFactory(TrustManager trustManager) { + try { + SSLContext sslContext; + + if (Flavors.FLAVOR == Flavors.FREE) { + // Free flavor (bundles modern conscrypt): support for TLSv1.3 is guaranteed. + sslContext = SSLContext.getInstance("TLSv1.3"); + } else { + // Play flavor (security provider can vary): only TLSv1.2 is guaranteed. + sslContext = SSLContext.getInstance("TLSv1.2"); + } + + sslContext.init(null, new TrustManager[] {trustManager}, null); + factory = sslContext.getSocketFactory(); + } catch (GeneralSecurityException e) { + e.printStackTrace(); + } + } + + @Override + public String[] getDefaultCipherSuites() { + return factory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return factory.getSupportedCipherSuites(); + } + + public Socket createSocket() throws IOException { + SSLSocket result = (SSLSocket) factory.createSocket(); + configureSocket(result); + return result; + } + + public Socket createSocket(String var1, int var2) throws IOException { + SSLSocket result = (SSLSocket) factory.createSocket(var1, var2); + configureSocket(result); + return result; + } + + public Socket createSocket(Socket var1, String var2, int var3, boolean var4) throws IOException { + SSLSocket result = (SSLSocket) factory.createSocket(var1, var2, var3, var4); + configureSocket(result); + return result; + } + + public Socket createSocket(InetAddress var1, int var2) throws IOException { + SSLSocket result = (SSLSocket) factory.createSocket(var1, var2); + configureSocket(result); + return result; + } + + public Socket createSocket(String var1, int var2, InetAddress var3, int var4) throws IOException { + SSLSocket result = (SSLSocket) factory.createSocket(var1, var2, var3, var4); + configureSocket(result); + return result; + } + + public Socket createSocket(InetAddress var1, int var2, InetAddress var3, int var4) throws IOException { + SSLSocket result = (SSLSocket) factory.createSocket(var1, var2, var3, var4); + configureSocket(result); + return result; + } + + private void configureSocket(SSLSocket s) { + if (Flavors.FLAVOR == Flavors.FREE) { + // Free flavor (bundles modern conscrypt): TLSv1.3 and modern cipher suites are + // guaranteed. Protocols older than TLSv1.2 are now deprecated and can be disabled. + s.setEnabledProtocols(new String[] { "TLSv1.3", "TLSv1.2" }); + } else { + // Play flavor (security provider can vary): only TLSv1.2 is guaranteed, supported + // cipher suites may vary. Old protocols might be necessary to keep things working. + + // TLS 1.0 is enabled by default on some old systems, which causes connection errors. + // This disables that. + s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" }); + } + } +} \ No newline at end of file -- cgit v1.2.3 From 9833bd753b440c300cfdeccf73fb94226488910c Mon Sep 17 00:00:00 2001 From: Chetan Pawar <36985543+chetan882777@users.noreply.github.com> Date: Fri, 9 Oct 2020 12:35:07 +0530 Subject: Replacing deprecated default constructor of handler with constructor that accepts Looper (#4513) --- .../de/danoeh/antennapod/core/service/download/DownloadService.java | 3 ++- .../antennapod/core/service/playback/PlaybackServiceTaskManager.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java index e44aa716a..f1b35fe23 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java @@ -10,6 +10,7 @@ import android.content.IntentFilter; import android.os.Binder; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; import android.text.TextUtils; import android.util.Log; @@ -178,7 +179,7 @@ public class DownloadService extends Service { public void onCreate() { Log.d(TAG, "Service started"); isRunning = true; - handler = new Handler(); + handler = new Handler(Looper.getMainLooper()); notificationManager = new DownloadServiceNotification(this); IntentFilter cancelDownloadReceiverFilter = new IntentFilter(); diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java index 9ef34cdac..1cf665f12 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java @@ -343,7 +343,7 @@ public class PlaybackServiceTaskManager { if (Looper.myLooper() == Looper.getMainLooper()) { // Called in main thread => ExoPlayer is used // Run on ui thread even if called from schedExecutor - Handler handler = new Handler(); + Handler handler = new Handler(Looper.getMainLooper()); return () -> handler.post(runnable); } else { return runnable; @@ -370,7 +370,7 @@ public class PlaybackServiceTaskManager { if (UserPreferences.useExoplayer() && Looper.myLooper() == Looper.getMainLooper()) { // Run callbacks in main thread so they can call ExoPlayer methods themselves - this.handler = new Handler(); + this.handler = new Handler(Looper.getMainLooper()); } else { this.handler = null; } -- cgit v1.2.3 From 983c0a464e265fca8f5a6bdc2e8b14951506472e Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Wed, 14 Oct 2020 16:56:15 +0200 Subject: Set cache-control to no-cache The previous value, no-store, did not allow CDNs to do their work. --- .../java/de/danoeh/antennapod/core/service/download/HttpDownloader.java | 1 + 1 file changed, 1 insertion(+) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java index 4072772ac..ef86c9024 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java @@ -71,6 +71,7 @@ public class HttpDownloader extends Downloader { // set header explicitly so that okhttp doesn't do transparent gzip Log.d(TAG, "addHeader(\"Accept-Encoding\", \"identity\")"); httpReq.addHeader("Accept-Encoding", "identity"); + httpReq.cacheControl(new CacheControl.Builder().noCache().build()); // noStore breaks CDNs } if (!TextUtils.isEmpty(request.getLastModified())) { -- cgit v1.2.3 From e9285fba2f9e446702a0a5aff14bb0a475773951 Mon Sep 17 00:00:00 2001 From: asdoi <36813904+asdoi@users.noreply.github.com> Date: Thu, 15 Oct 2020 20:22:37 +0000 Subject: Clarified wording of compact notification setting (#4384) --- .../core/service/playback/PlaybackServiceNotificationBuilder.java | 1 + 1 file changed, 1 insertion(+) (limited to 'core/src/main/java/de/danoeh/antennapod') 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 0ff8b94d6..632ac07ea 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 @@ -139,6 +139,7 @@ public class PlaybackServiceNotificationBuilder { notification.setSmallIcon(R.drawable.ic_notification); notification.setOngoing(false); notification.setOnlyAlertOnce(true); + notification.setShowWhen(false); notification.setPriority(UserPreferences.getNotifyPriority()); notification.setVisibility(NotificationCompat.VISIBILITY_PUBLIC); notification.setColor(NotificationCompat.COLOR_DEFAULT); -- cgit v1.2.3 From dc7e2bebbb2b1cfa0b43bc89877418f3dd76e3d3 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Wed, 14 Oct 2020 20:26:47 +0200 Subject: Load queue in background thread --- .../de/danoeh/antennapod/core/service/playback/PlaybackService.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'core/src/main/java/de/danoeh/antennapod') 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 00d4e2e90..8677ea030 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 @@ -84,6 +84,7 @@ import io.reactivex.Observable; import io.reactivex.Single; 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; @@ -315,7 +316,10 @@ public class PlaybackService extends MediaBrowserServiceCompat { } } emitter.onSuccess(queueItems); - }).subscribe(queueItems -> mediaSession.setQueue(queueItems), Throwable::printStackTrace); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(queueItems -> mediaSession.setQueue(queueItems), Throwable::printStackTrace); flavorHelper.initializeMediaPlayer(PlaybackService.this); mediaSession.setActive(true); -- cgit v1.2.3 From e334b9cad6c627d5793898c110e7e0ad57613c20 Mon Sep 17 00:00:00 2001 From: asdoi <36813904+asdoi@users.noreply.github.com> Date: Fri, 16 Oct 2020 11:56:43 +0000 Subject: Extended subscriptions filter (#4502) --- .../antennapod/core/feed/SubscriptionsFilter.java | 106 +++++++++++++++++++++ .../core/feed/SubscriptionsFilterGroup.java | 30 ++++++ .../core/preferences/UserPreferences.java | 15 ++- .../danoeh/antennapod/core/storage/DBReader.java | 13 +-- 4 files changed, 147 insertions(+), 17 deletions(-) create mode 100644 core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilter.java create mode 100644 core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilterGroup.java (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilter.java new file mode 100644 index 000000000..93f098ecf --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilter.java @@ -0,0 +1,106 @@ +package de.danoeh.antennapod.core.feed; + +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.List; + +import de.danoeh.antennapod.core.util.LongIntMap; + +public class SubscriptionsFilter { + private static final String divider = ","; + + private final String[] properties; + + private boolean showIfCounterGreaterZero = false; + + private boolean showAutoDownloadEnabled = false; + private boolean showAutoDownloadDisabled = false; + + private boolean showUpdatedEnabled = false; + private boolean showUpdatedDisabled = false; + + public SubscriptionsFilter(String properties) { + this(TextUtils.split(properties, divider)); + } + + + public SubscriptionsFilter(String[] properties) { + this.properties = properties; + for (String property : properties) { + // see R.arrays.feed_filter_values + switch (property) { + case "counter_greater_zero": + showIfCounterGreaterZero = true; + break; + case "enabled_auto_download": + showAutoDownloadEnabled = true; + break; + case "disabled_auto_download": + showAutoDownloadDisabled = true; + break; + case "enabled_updates": + showUpdatedEnabled = true; + break; + case "disabled_updates": + showUpdatedDisabled = true; + break; + default: + break; + } + } + } + + public boolean isEnabled() { + return properties.length > 0; + } + + /** + * Run a list of feed items through the filter. + */ + public List filter(List items, LongIntMap feedCounters) { + if (properties.length == 0) { + return items; + } + + List result = new ArrayList<>(); + + for (Feed item : items) { + FeedPreferences itemPreferences = item.getPreferences(); + + // If the item does not meet a requirement, skip it. + if (showAutoDownloadEnabled && !itemPreferences.getAutoDownload()) { + continue; + } else if (showAutoDownloadDisabled && itemPreferences.getAutoDownload()) { + continue; + } + + if (showUpdatedEnabled && !itemPreferences.getKeepUpdated()) { + continue; + } else if (showUpdatedDisabled && itemPreferences.getKeepUpdated()) { + continue; + } + + // If the item reaches here, it meets all criteria (except counter > 0) + result.add(item); + } + + if (showIfCounterGreaterZero) { + for (int i = result.size() - 1; i >= 0; i--) { + if (feedCounters.get(result.get(i).getId()) <= 0) { + result.remove(i); + } + } + } + + return result; + } + + public String[] getValues() { + return properties.clone(); + } + + public String serialize() { + return TextUtils.join(divider, getValues()); + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilterGroup.java b/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilterGroup.java new file mode 100644 index 000000000..7db0456a0 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilterGroup.java @@ -0,0 +1,30 @@ +package de.danoeh.antennapod.core.feed; + +import de.danoeh.antennapod.core.R; + +public enum SubscriptionsFilterGroup { + COUNTER_GREATER_ZERO(new ItemProperties(R.string.subscriptions_counter_greater_zero, "counter_greater_zero")), + AUTO_DOWNLOAD(new ItemProperties(R.string.auto_downloaded, "enabled_auto_download"), + new ItemProperties(R.string.not_auto_downloaded, "disabled_auto_download")), + UPDATED(new ItemProperties(R.string.kept_updated, "enabled_updates"), + new ItemProperties(R.string.not_kept_updated, "disabled_updates")); + + + public final ItemProperties[] values; + + SubscriptionsFilterGroup(ItemProperties... values) { + this.values = values; + } + + public static class ItemProperties { + + public final int displayName; + public final String filterId; + + public ItemProperties(int displayName, String filterId) { + this.displayName = displayName; + this.filterId = filterId; + } + + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java index 5700bb9a0..1dc4b6093 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java +++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java @@ -32,6 +32,7 @@ import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.core.R; import de.danoeh.antennapod.core.feed.MediaType; +import de.danoeh.antennapod.core.feed.SubscriptionsFilter; import de.danoeh.antennapod.core.service.download.ProxyConfig; import de.danoeh.antennapod.core.storage.APCleanupAlgorithm; import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm; @@ -66,7 +67,7 @@ public class UserPreferences { private static final String PREF_SHOW_AUTO_DOWNLOAD_REPORT = "prefShowAutoDownloadReport"; public static final String PREF_BACK_BUTTON_BEHAVIOR = "prefBackButtonBehavior"; private static final String PREF_BACK_BUTTON_GO_TO_PAGE = "prefBackButtonGoToPage"; - public static final String PREF_FILTER_FEED = "prefFeedFilter"; + public static final String PREF_FILTER_FEED = "prefSubscriptionsFilter"; public static final String PREF_QUEUE_KEEP_SORTED = "prefQueueKeepSorted"; public static final String PREF_QUEUE_KEEP_SORTED_ORDER = "prefQueueKeepSortedOrder"; @@ -149,8 +150,6 @@ public class UserPreferences { public static final int FEED_COUNTER_SHOW_UNPLAYED = 2; public static final int FEED_COUNTER_SHOW_NONE = 3; public static final int FEED_COUNTER_SHOW_DOWNLOADED = 4; - public static final int FEED_FILTER_NONE = 0; - public static final int FEED_FILTER_COUNTER_ZERO = 1; private static Context context; private static SharedPreferences prefs; @@ -1046,14 +1045,14 @@ public class UserPreferences { .apply(); } - public static int getFeedFilter() { - String value = prefs.getString(PREF_FILTER_FEED, "" + FEED_FILTER_NONE); - return Integer.parseInt(value); + public static SubscriptionsFilter getSubscriptionsFilter() { + String value = prefs.getString(PREF_FILTER_FEED, ""); + return new SubscriptionsFilter(value); } - public static void setFeedFilter(String value) { + public static void setSubscriptionsFilter(SubscriptionsFilter value) { prefs.edit() - .putString(PREF_FILTER_FEED, value) + .putString(PREF_FILTER_FEED, value.serialize()) .apply(); } 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 140fde93b..c8368506b 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 @@ -19,6 +19,7 @@ import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedPreferences; +import de.danoeh.antennapod.core.feed.SubscriptionsFilter; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.util.LongIntMap; @@ -795,6 +796,7 @@ public final class DBReader { Log.d(TAG, "getNavDrawerData() called with: " + ""); PodDBAdapter adapter = PodDBAdapter.getInstance(); adapter.open(); + List feeds = getFeedList(adapter); long[] feedIds = new long[feeds.size()]; for (int i = 0; i < feeds.size(); i++) { @@ -802,15 +804,8 @@ public final class DBReader { } final LongIntMap feedCounters = adapter.getFeedCounters(feedIds); - int feedFilter = UserPreferences.getFeedFilter(); - if (feedFilter == UserPreferences.FEED_FILTER_COUNTER_ZERO) { - for (int i = feeds.size() - 1; i >= 0; i--) { - if (feedCounters.get(feeds.get(i).getId()) <= 0) { - feedCounters.delete(feeds.get(i).getId()); - feeds.remove(i); - } - } - } + SubscriptionsFilter subscriptionsFilter = UserPreferences.getSubscriptionsFilter(); + feeds = subscriptionsFilter.filter(getFeedList(adapter), feedCounters); Comparator comparator; int feedOrder = UserPreferences.getFeedOrder(); -- cgit v1.2.3 From 4df751a018ec6a06915b58c0f52189c17017de84 Mon Sep 17 00:00:00 2001 From: Jake Douglas Date: Sat, 17 Oct 2020 22:47:20 +0200 Subject: Add error message when database export does not have enough space (#4451) --- .../de/danoeh/antennapod/core/storage/DatabaseExporter.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java index 234c01b20..271babc6e 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java @@ -5,6 +5,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.net.Uri; import android.os.ParcelFileDescriptor; +import android.text.format.Formatter; import android.util.Log; import de.danoeh.antennapod.core.R; import org.apache.commons.io.FileUtils; @@ -53,7 +54,16 @@ public class DatabaseExporter { if (currentDB.exists()) { src = new FileInputStream(currentDB).getChannel(); dst = outFileStream.getChannel(); - dst.transferFrom(src, 0, src.size()); + long srcSize = src.size(); + dst.transferFrom(src, 0, srcSize); + + long newDstSize = dst.size(); + if (newDstSize != srcSize) { + throw new IOException(String.format( + "Unable to write entire database. Expected to write %s, but wrote %s.", + Formatter.formatShortFileSize(context, srcSize), + Formatter.formatShortFileSize(context, newDstSize))); + } } else { throw new IOException("Can not access current database"); } -- cgit v1.2.3 From 2ef464ad93525e93e74f1a8f298059acc6977d8d Mon Sep 17 00:00:00 2001 From: Tony Tam <149837+tonytamsf@users.noreply.github.com> Date: Sat, 17 Oct 2020 13:50:08 -0700 Subject: Discovery filter by country & hide discovery on first subscribe screen (#4515) --- .../danoeh/antennapod/core/event/DiscoveryDefaultUpdateEvent.java | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 core/src/main/java/de/danoeh/antennapod/core/event/DiscoveryDefaultUpdateEvent.java (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DiscoveryDefaultUpdateEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/DiscoveryDefaultUpdateEvent.java new file mode 100644 index 000000000..f7757935a --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/event/DiscoveryDefaultUpdateEvent.java @@ -0,0 +1,6 @@ +package de.danoeh.antennapod.core.event; + +public class DiscoveryDefaultUpdateEvent { + public DiscoveryDefaultUpdateEvent() { + } +} -- cgit v1.2.3 From 4971bb52873839acc6d00d12730c1d36cf0ea394 Mon Sep 17 00:00:00 2001 From: DAMLA YILDIZ <56313500+damlayildiz@users.noreply.github.com> Date: Sat, 17 Oct 2020 23:54:34 +0300 Subject: New channel for synchronization errors (#4545) --- .../main/java/de/danoeh/antennapod/core/sync/SyncService.java | 2 +- .../de/danoeh/antennapod/core/util/gui/NotificationUtils.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'core/src/main/java/de/danoeh/antennapod') 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 4c89ebc19..16130ac9b 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 @@ -491,7 +491,7 @@ public class SyncService extends Worker { PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), R.id.pending_intent_sync_error, intent, PendingIntent.FLAG_UPDATE_CURRENT); Notification notification = new NotificationCompat.Builder(getApplicationContext(), - NotificationUtils.CHANNEL_ID_ERROR) + NotificationUtils.CHANNEL_ID_SYNC_ERROR) .setContentTitle(getApplicationContext().getString(R.string.gpodnetsync_error_title)) .setContentText(description) .setContentIntent(pendingIntent) diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java index f546ca019..ddbe68938 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java @@ -14,6 +14,7 @@ public class NotificationUtils { public static final String CHANNEL_ID_DOWNLOADING = "downloading"; public static final String CHANNEL_ID_PLAYING = "playing"; public static final String CHANNEL_ID_ERROR = "error"; + public static final String CHANNEL_ID_SYNC_ERROR = "sync_error"; public static final String CHANNEL_ID_AUTO_DOWNLOAD = "auto_download"; public static void createChannels(Context context) { @@ -27,6 +28,7 @@ public class NotificationUtils { mNotificationManager.createNotificationChannel(createChannelDownloading(context)); mNotificationManager.createNotificationChannel(createChannelPlaying(context)); mNotificationManager.createNotificationChannel(createChannelError(context)); + mNotificationManager.createNotificationChannel(createChannelSyncError(context)); mNotificationManager.createNotificationChannel(createChannelAutoDownload(context)); } } @@ -65,6 +67,14 @@ public class NotificationUtils { return mChannel; } + @RequiresApi(api = Build.VERSION_CODES.O) + private static NotificationChannel createChannelSyncError(Context c) { + NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID_SYNC_ERROR, + c.getString(R.string.notification_channel_sync_error), NotificationManager.IMPORTANCE_HIGH); + notificationChannel.setDescription(c.getString(R.string.notification_channel_sync_error_description)); + return notificationChannel; + } + @RequiresApi(api = Build.VERSION_CODES.O) private static NotificationChannel createChannelAutoDownload(Context c) { NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_AUTO_DOWNLOAD, -- cgit v1.2.3 From 91f9c19baeb0349f6b11bb967ea22e58f6f935ee Mon Sep 17 00:00:00 2001 From: Ezequiel Date: Thu, 22 Oct 2020 08:37:03 -0300 Subject: Remove episode from queue when gpodder notifies as played (#4562) --- core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'core/src/main/java/de/danoeh/antennapod') 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 16130ac9b..1f5d9b75f 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 @@ -39,6 +39,7 @@ import de.danoeh.antennapod.core.sync.model.ISyncService; import de.danoeh.antennapod.core.sync.model.SubscriptionChanges; import de.danoeh.antennapod.core.sync.model.SyncServiceException; import de.danoeh.antennapod.core.sync.model.UploadChangesResponse; +import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.URLChecker; import de.danoeh.antennapod.core.util.gui.NotificationUtils; import io.reactivex.Completable; @@ -456,7 +457,7 @@ public class SyncService extends Worker { break; } } - + LongList queueToBeRemoved = new LongList(); List updatedItems = new ArrayList<>(); for (EpisodeAction action : mostRecentPlayAction.values()) { FeedItem playItem = DBReader.getFeedItemByUrl(action.getPodcast(), action.getEpisode()); @@ -467,10 +468,12 @@ public class SyncService extends Worker { if (playItem.getMedia().hasAlmostEnded()) { Log.d(TAG, "Marking as played"); playItem.setPlayed(true); + queueToBeRemoved.add(playItem.getId()); } updatedItems.add(playItem); } } + DBWriter.removeQueueItem(getApplicationContext(), false, queueToBeRemoved.toArray()); DBWriter.setItemList(updatedItems); } -- cgit v1.2.3 From 469a1615db8295e8ccd2b3c76a31c47af7ffe407 Mon Sep 17 00:00:00 2001 From: Niffler Date: Thu, 22 Oct 2020 23:04:00 +0200 Subject: Update view after episode deletion when downloaded filter is enabled (#4560) --- core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'core/src/main/java/de/danoeh/antennapod') diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java index d34e23506..e8e478a86 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java @@ -129,4 +129,8 @@ public class FeedItemFilter { return mProperties.clone(); } + public boolean isShowDownloaded() { + return showDownloaded; + } + } -- cgit v1.2.3 From 69a3c56d247e0f32c156c49a396c989d746ba96e Mon Sep 17 00:00:00 2001 From: avirajrsingh <69088913+avirajrsingh@users.noreply.github.com> Date: Sat, 24 Oct 2020 13:56:56 +0530 Subject: [Statistics]Add count of episodes stocked on device (#4581) --- .../src/main/java/de/danoeh/antennapod/core/storage/DBReader.java | 4 +++- .../java/de/danoeh/antennapod/core/storage/StatisticsItem.java | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') 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 c8368506b..fd8ca116d 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 @@ -746,6 +746,7 @@ public final class DBReader { long episodesStarted = 0; long episodesStartedIncludingMarked = 0; long totalDownloadSize = 0; + long episodesDownloadCount = 0; List items = getFeed(feed.getId()).getItems(); for (FeedItem item : items) { FeedMedia media = item.getMedia(); @@ -773,13 +774,14 @@ public final class DBReader { if (media.isDownloaded()) { totalDownloadSize = totalDownloadSize + media.getSize(); + episodesDownloadCount++; } episodes++; } feedTime.add(new StatisticsItem( feed, feedTotalTime, feedPlayedTime, feedPlayedTimeCountAll, episodes, - episodesStarted, episodesStartedIncludingMarked, totalDownloadSize)); + episodesStarted, episodesStartedIncludingMarked, totalDownloadSize, episodesDownloadCount)); } adapter.close(); diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java b/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java index f96af185b..18a5403a7 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java @@ -36,9 +36,14 @@ public class StatisticsItem { */ public final long totalDownloadSize; + /** + * Stores the number of episodes downloaded. + */ + public final long episodesDownloadCount; + public StatisticsItem(Feed feed, long time, long timePlayed, long timePlayedCountAll, long episodes, long episodesStarted, long episodesStartedIncludingMarked, - long totalDownloadSize) { + long totalDownloadSize, long episodesDownloadCount) { this.feed = feed; this.time = time; this.timePlayed = timePlayed; @@ -47,5 +52,6 @@ public class StatisticsItem { this.episodesStarted = episodesStarted; this.episodesStartedIncludingMarked = episodesStartedIncludingMarked; this.totalDownloadSize = totalDownloadSize; + this.episodesDownloadCount = episodesDownloadCount; } } -- cgit v1.2.3 From cd100aae15538415dbc896d59732e4b9710c89d1 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sat, 24 Oct 2020 11:38:31 +0200 Subject: Improve performance and memory usage of 'recent pubdate' feed sort option --- .../de/danoeh/antennapod/core/storage/DBReader.java | 21 ++++----------------- .../antennapod/core/storage/PodDBAdapter.java | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 17 deletions(-) (limited to 'core/src/main/java/de/danoeh/antennapod') 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 c8368506b..5b226ef92 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 @@ -850,24 +850,11 @@ public final class DBReader { } }; } else { + final Map recentPubDates = adapter.getMostRecentItemDates(); comparator = (lhs, rhs) -> { - if (lhs.getItems() == null || lhs.getItems().size() == 0) { - List items = DBReader.getFeedItemList(lhs); - lhs.setItems(items); - } - if (rhs.getItems() == null || rhs.getItems().size() == 0) { - List items = DBReader.getFeedItemList(rhs); - rhs.setItems(items); - } - if (lhs.getMostRecentItem() == null) { - return 1; - } else if (rhs.getMostRecentItem() == null) { - return -1; - } else { - Date d1 = lhs.getMostRecentItem().getPubDate(); - Date d2 = rhs.getMostRecentItem().getPubDate(); - return d2.compareTo(d1); - } + long dateLhs = recentPubDates.containsKey(lhs.getId()) ? recentPubDates.get(lhs.getId()) : 0; + long dateRhs = recentPubDates.containsKey(rhs.getId()) ? recentPubDates.get(rhs.getId()) : 0; + return Long.compare(dateRhs, dateLhs); }; } diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java index e552cc180..775485880 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java @@ -21,8 +21,10 @@ import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; import java.util.Date; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Set; import de.danoeh.antennapod.core.feed.Chapter; @@ -1189,6 +1191,25 @@ public class PodDBAdapter { return conditionalFeedCounterRead(whereRead, feedIds); } + public final Map getMostRecentItemDates() { + final String query = "SELECT " + KEY_FEED + "," + + " MAX(" + TABLE_NAME_FEED_ITEMS + "." + KEY_PUBDATE + ") AS most_recent_pubdate" + + " FROM " + TABLE_NAME_FEED_ITEMS + + " GROUP BY " + KEY_FEED; + + Cursor c = db.rawQuery(query, null); + Map result = new HashMap<>(); + if (c.moveToFirst()) { + do { + long feedId = c.getLong(0); + long date = c.getLong(1); + result.put(feedId, date); + } while (c.moveToNext()); + } + c.close(); + return result; + } + public final int getNumberOfDownloadedEpisodes() { final String query = "SELECT COUNT(DISTINCT " + KEY_ID + ") AS count FROM " + TABLE_NAME_FEED_MEDIA + " WHERE " + KEY_DOWNLOADED + " > 0"; -- cgit v1.2.3