From 4c24d1c29a726a67eabf86a9f9866e5daeca4ae8 Mon Sep 17 00:00:00 2001 From: Mats Wahlberg Date: Tue, 21 Apr 2020 17:41:24 +0200 Subject: [PATCH 01/16] Bundle Conscrypt security provider for Free builds This fixes protocol and cipher errors on older versions of android without requiring Google API/Services (which are non-free) to replace the security provider from the OS. No changes are made to Play builds. The value of conscryptVersion in build.gradle should be updated regularly to keep the bundled version of conscrypt up to date (or changed to "latest.release", which will cause issues with verifying reproducible builds). Fixes: #2814 (for users of free builds) --- build.gradle | 6 ++++++ core/build.gradle | 3 ++- .../java/de/danoeh/antennapod/core/ClientConfig.java | 10 +++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 01af13b4e..8e1efd6cb 100644 --- a/build.gradle +++ b/build.gradle @@ -67,6 +67,12 @@ project.ext { iconifyVersion = "2.2.2" audioPlayerVersion = "v2.0.0" + // Only used for free builds. This version should be updated regularly. + conscryptVersion = "2.4.0" + // Alternatively one can just use: + // conscryptVersion = "latest.release" + // but it will mess up reproducible builds. + // Google Play build wearableSupportVersion = "2.6.0" diff --git a/core/build.gradle b/core/build.gradle index 4c7ef5a0a..ee7441550 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -88,7 +88,8 @@ dependencies { api "com.google.android.support:wearable:$wearableSupportVersion" compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion" } else { - System.out.println("core: free build hack, skipping some dependencies") + System.out.println("core: free build hack, skipping some dependencies and bundling conscrypt ("+"$conscryptVersion"+")") + implementation "org.conscrypt:conscrypt-android:$conscryptVersion" } testImplementation "org.awaitility:awaitility:$awaitilityVersion" diff --git a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java index 824a4fae3..aff8081e2 100644 --- a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java +++ b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java @@ -1,6 +1,13 @@ package de.danoeh.antennapod.core; import android.content.Context; +import java.security.Security; + +/* + * If you get an error here ("package org.conscrypt does not exist"), you are probably doing a free + * build and didn't pass -PfreeBuild to gradle (e.g. ./gradlew assembleFreeRelease -PfreeBuild). + */ +import org.conscrypt.Conscrypt; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.SleepTimerPreferences; @@ -55,6 +62,7 @@ public class ClientConfig { } private static void installSslProvider(Context context) { - // ProviderInstaller is a closed-source Google library + // Insert bundled conscrypt as highest security provider (overrides OS version). + Security.insertProviderAt(Conscrypt.newProvider(), 1); } } From 3f0420544a87a974a04b957606a2ace8f9b96e47 Mon Sep 17 00:00:00 2001 From: Mats Wahlberg Date: Tue, 21 Apr 2020 21:33:27 +0200 Subject: [PATCH 02/16] Updated circleci debug and release jobs to explicitly build Play flavors Changed the gradlew build targets assembleRelease to assemblePlayRelease and assembleDebug to assemblePlayDebug, because the old targets causes files for the free builds to get compiled when not needed. It's unnecessary and also done without -PfreeBuild which gives build errors. These are also the targets used in makeRelease.sh, so the workflow should better match the expected. --- .circleci/config.yml | 4 ++-- .../free/java/de/danoeh/antennapod/core/ClientConfig.java | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ccdd5aaee..a0cf32f37 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -38,7 +38,7 @@ workflows: build-steps: - run: name: Build debug - command: ./gradlew assembleDebug -PdisablePreDex + command: ./gradlew assemblePlayDebug -PdisablePreDex - run: name: Execute debug unit tests command: ./gradlew :core:testPlayDebugUnitTest -PdisablePreDex @@ -47,7 +47,7 @@ workflows: build-steps: - run: name: Build release - command: ./gradlew assembleRelease -PdisablePreDex + command: ./gradlew assemblePlayRelease -PdisablePreDex - run: name: Execute release unit tests command: ./gradlew :core:testPlayReleaseUnitTest -PdisablePreDex diff --git a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java index aff8081e2..6aa9982bc 100644 --- a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java +++ b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java @@ -5,7 +5,10 @@ import java.security.Security; /* * If you get an error here ("package org.conscrypt does not exist"), you are probably doing a free - * build and didn't pass -PfreeBuild to gradle (e.g. ./gradlew assembleFreeRelease -PfreeBuild). + * build and didn't pass "-PfreeBuild" to gradle (e.g. "./gradlew assembleFreeRelease -PfreeBuild"). + * + * If you are doing a non-free build using "assembleRelease" or "assembleDebug" and get this error, + * use "assemblePlayRelease" or "assemblePlayDebug" instead (e.g. "./gradlew assemblePlayRelease"). */ import org.conscrypt.Conscrypt; From 4f86047a24bdfd1cdb730487f828d5b24331d6d4 Mon Sep 17 00:00:00 2001 From: Mats Wahlberg Date: Wed, 22 Apr 2020 16:22:48 +0200 Subject: [PATCH 03/16] 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). --- .../download/AntennapodHttpClient.java | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) 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" } ); + } } } From 3271d7628fd62d3e26c150ae26fc095d24c6fa37 Mon Sep 17 00:00:00 2001 From: Mats Wahlberg Date: Sun, 26 Apr 2020 16:57:08 +0200 Subject: [PATCH 04/16] 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... --- .../download/AntennapodHttpClient.java | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) 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" }); } } From 30268d73d1a309467b2d183ec276fd4ff672b386 Mon Sep 17 00:00:00 2001 From: Mats Wahlberg Date: Sat, 20 Jun 2020 12:25:16 +0200 Subject: [PATCH 05/16] Cleaned up string to println in core/build.gradle Variable can be used and gets replaced directly in the string without using '+' to concatenate strings. --- core/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle b/core/build.gradle index ee7441550..e2ee3a0bd 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -88,7 +88,7 @@ dependencies { api "com.google.android.support:wearable:$wearableSupportVersion" compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion" } else { - System.out.println("core: free build hack, skipping some dependencies and bundling conscrypt ("+"$conscryptVersion"+")") + System.out.println("core: free build hack, skipping some dependencies and bundling conscrypt ($conscryptVersion)") implementation "org.conscrypt:conscrypt-android:$conscryptVersion" } From 468acab8f45bc076806fe7f6bfde3585526f19ca Mon Sep 17 00:00:00 2001 From: Mats Wahlberg Date: Sat, 4 Jul 2020 16:21:32 +0200 Subject: [PATCH 06/16] Removed comment about automatically choosing Conscrypt version There are drawbacks to using conscryptVersion = "latest.release", hopefully the version will be kept up to date manually instead. --- build.gradle | 3 --- 1 file changed, 3 deletions(-) diff --git a/build.gradle b/build.gradle index 8e1efd6cb..048d2f3e1 100644 --- a/build.gradle +++ b/build.gradle @@ -69,9 +69,6 @@ project.ext { // Only used for free builds. This version should be updated regularly. conscryptVersion = "2.4.0" - // Alternatively one can just use: - // conscryptVersion = "latest.release" - // but it will mess up reproducible builds. // Google Play build wearableSupportVersion = "2.6.0" From 06d212b911af1ce54e03c63bad69b99e938cb430 Mon Sep 17 00:00:00 2001 From: Mats Wahlberg Date: Fri, 2 Oct 2020 14:39:02 +0200 Subject: [PATCH 07/16] Use freeImplementation for including conscrypt in Free builds. This removes the need for the -PfreeBuild flag to gradle, and makes assemblePlay and assembleDebug build all flavours without errors again. Changed circleci config back accordingly and removed comment about -PfreeBuild. Based on #4457. --- .circleci/config.yml | 4 ++-- core/build.gradle | 6 ++++-- .../free/java/de/danoeh/antennapod/core/ClientConfig.java | 8 -------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index a0cf32f37..ccdd5aaee 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -38,7 +38,7 @@ workflows: build-steps: - run: name: Build debug - command: ./gradlew assemblePlayDebug -PdisablePreDex + command: ./gradlew assembleDebug -PdisablePreDex - run: name: Execute debug unit tests command: ./gradlew :core:testPlayDebugUnitTest -PdisablePreDex @@ -47,7 +47,7 @@ workflows: build-steps: - run: name: Build release - command: ./gradlew assemblePlayRelease -PdisablePreDex + command: ./gradlew assembleRelease -PdisablePreDex - run: name: Execute release unit tests command: ./gradlew :core:testPlayReleaseUnitTest -PdisablePreDex diff --git a/core/build.gradle b/core/build.gradle index e2ee3a0bd..71ad30ac1 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -88,10 +88,12 @@ dependencies { api "com.google.android.support:wearable:$wearableSupportVersion" compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion" } else { - System.out.println("core: free build hack, skipping some dependencies and bundling conscrypt ($conscryptVersion)") - implementation "org.conscrypt:conscrypt-android:$conscryptVersion" + System.out.println("core: free build hack, skipping some dependencies") } + // bundle conscrypt with free builds + freeImplementation "org.conscrypt:conscrypt-android:$conscryptVersion" + testImplementation "org.awaitility:awaitility:$awaitilityVersion" testImplementation 'junit:junit:4.13' testImplementation 'org.mockito:mockito-core:1.10.19' diff --git a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java index 6aa9982bc..37905b556 100644 --- a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java +++ b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java @@ -2,14 +2,6 @@ package de.danoeh.antennapod.core; import android.content.Context; import java.security.Security; - -/* - * If you get an error here ("package org.conscrypt does not exist"), you are probably doing a free - * build and didn't pass "-PfreeBuild" to gradle (e.g. "./gradlew assembleFreeRelease -PfreeBuild"). - * - * If you are doing a non-free build using "assembleRelease" or "assembleDebug" and get this error, - * use "assemblePlayRelease" or "assemblePlayDebug" instead (e.g. "./gradlew assemblePlayRelease"). - */ import org.conscrypt.Conscrypt; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; From 2e0a191d5c45fb1d7fc24144db11f070a866bc5f Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Mon, 5 Oct 2020 12:44:47 +0200 Subject: [PATCH 08/16] Ship our own CA certificates for old devices --- .../download/AntennapodHttpClient.java | 175 +++--------------- .../core/service/download/HttpDownloader.java | 1 - .../antennapod/core/ssl/BackportCaCerts.java | 73 ++++++++ .../core/ssl/BackportTrustManager.java | 58 ++++++ .../core/ssl/CompositeX509TrustManager.java | 60 ++++++ .../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 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 From 3e771c664f6705ac557d5d418132f992f2fb68be Mon Sep 17 00:00:00 2001 From: Darrell Date: Wed, 7 Oct 2020 18:58:19 +0100 Subject: [PATCH 09/16] Fixed progress bar being highlighted on touch (#4511) Signed-off-by: Darrell --- app/src/main/res/layout/audioplayer_fragment.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/audioplayer_fragment.xml b/app/src/main/res/layout/audioplayer_fragment.xml index 5b4e26b69..a5e2f82ce 100644 --- a/app/src/main/res/layout/audioplayer_fragment.xml +++ b/app/src/main/res/layout/audioplayer_fragment.xml @@ -67,7 +67,8 @@ android:max="500" tools:progress="100" android:layout_marginLeft="8dp" - android:layout_marginRight="8dp" /> + android:layout_marginRight="8dp" + android:clickable="true"/> Date: Sun, 11 Oct 2020 13:05:02 -0300 Subject: [PATCH 10/16] Fix 'Visit website' button crashes app if there is no browser installed (#4520) --- .../adapter/actionbutton/VisitWebsiteActionButton.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java index 46712a666..7b8659968 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/VisitWebsiteActionButton.java @@ -8,6 +8,7 @@ import androidx.annotation.AttrRes; import androidx.annotation.StringRes; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.util.IntentUtils; public class VisitWebsiteActionButton extends ItemActionButton { @@ -29,8 +30,7 @@ public class VisitWebsiteActionButton extends ItemActionButton { @Override public void onClick(Context context) { - Uri uri = Uri.parse(item.getLink()); - context.startActivity(new Intent(Intent.ACTION_VIEW, uri)); + IntentUtils.openInBrowser(context, item.getLink()); } @Override From 7f7863b7be12dbdf8bfa038f6282bc1b0f3d6ab6 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Sun, 11 Oct 2020 19:26:15 +0200 Subject: [PATCH 11/16] Generate unique view IDs --- .../antennapod/activity/MainActivity.java | 24 +++++++++++++++++++ .../fragment/ItemPagerFragment.java | 14 ++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java index eaa423708..fffe5be60 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -19,6 +19,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.Toolbar; +import androidx.core.view.ViewCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; @@ -67,6 +68,7 @@ public class MainActivity extends CastEnabledActivity { public static final String EXTRA_FEED_ID = "fragment_feed_id"; public static final String EXTRA_OPEN_PLAYER = "open_player"; public static final String EXTRA_REFRESH_ON_START = "refresh_on_start"; + public static final String KEY_GENERATED_VIEW_ID = "generated_view_id"; private DrawerLayout drawerLayout; private View navDrawer; @@ -88,6 +90,9 @@ public class MainActivity extends CastEnabledActivity { public void onCreate(Bundle savedInstanceState) { lastTheme = UserPreferences.getNoTitleTheme(); setTheme(lastTheme); + if (savedInstanceState != null) { + ensureGeneratedViewIdGreaterThan(savedInstanceState.getInt(KEY_GENERATED_VIEW_ID, 0)); + } super.onCreate(savedInstanceState); StorageUtils.checkStorageAvailability(this); setContentView(R.layout.main); @@ -132,6 +137,25 @@ public class MainActivity extends CastEnabledActivity { sheetBehavior.setBottomSheetCallback(bottomSheetCallback); } + /** + * ViewCompat.generateViewId stores the current ID in a static variable. + * When the process is killed, the variable gets reset. + * This makes sure that we do not get ID collisions + * and therefore errors when trying to restore state from another view. + */ + @SuppressWarnings("StatementWithEmptyBody") + private void ensureGeneratedViewIdGreaterThan(int minimum) { + while (ViewCompat.generateViewId() <= minimum) { + // Generate new IDs + } + } + + @Override + protected void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(KEY_GENERATED_VIEW_ID, ViewCompat.generateViewId()); + } + private BottomSheetBehavior.BottomSheetCallback bottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java index c198ce258..b6e4190e8 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java @@ -38,6 +38,8 @@ import io.reactivex.schedulers.Schedulers; public class ItemPagerFragment extends Fragment { private static final String ARG_FEEDITEMS = "feeditems"; private static final String ARG_FEEDITEM_POS = "feeditem_pos"; + private static final String KEY_PAGER_ID = "pager_id"; + private ViewPager2 pager; /** * Creates a new instance of an ItemPagerFragment. @@ -77,12 +79,16 @@ public class ItemPagerFragment extends Fragment { feedItems = getArguments().getLongArray(ARG_FEEDITEMS); int feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS); - ViewPager2 pager = layout.findViewById(R.id.pager); + pager = layout.findViewById(R.id.pager); // FragmentStatePagerAdapter documentation: // > When using FragmentStatePagerAdapter the host ViewPager must have a valid ID set. // When opening multiple ItemPagerFragments by clicking "item" -> "visit podcast" -> "item" -> etc, // the ID is no longer unique and FragmentStatePagerAdapter does not display any pages. int newId = ViewCompat.generateViewId(); + if (savedInstanceState != null && savedInstanceState.getInt(KEY_PAGER_ID, 0) != 0) { + // Restore state by using the same ID as before. ID collisions are prevented in MainActivity. + newId = savedInstanceState.getInt(KEY_PAGER_ID, 0); + } pager.setId(newId); pager.setAdapter(new ItemPagerAdapter(this)); pager.setCurrentItem(feedItemPos, false); @@ -99,6 +105,12 @@ public class ItemPagerFragment extends Fragment { return layout; } + @Override + public void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(KEY_PAGER_ID, pager.getId()); + } + @Override public void onDestroyView() { super.onDestroyView(); From bf73ebbd1af4894fd7e080c50c03bd1b4426265b Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Tue, 13 Oct 2020 09:41:35 +0200 Subject: [PATCH 12/16] Updated Google Play metadata --- .tx/config | 8 +-- .../play/listings/de-DE/full-description.txt | 4 +- .../play/listings/et/full-description.txt | 6 +- .../play/listings/fr-FR/full-description.txt | 4 +- .../play/listings/gl-ES/full-description.txt | 12 ++-- .../play/listings/hu-HU/full-description.txt | 4 +- .../play/listings/it-IT/full-description.txt | 26 ++++----- .../play/listings/iw-IL/full-description.txt | 57 ++++++++----------- .../play/listings/lt/full-description.txt | 31 ++++++++++ .../play/listings/lt/short-description.txt | 1 + app/src/main/play/listings/lt/title.txt | 1 + .../play/listings/nl-NL/full-description.txt | 4 +- .../play/listings/pt-BR/full-description.txt | 31 ++++++++++ .../play/listings/pt-BR/short-description.txt | 1 + app/src/main/play/listings/pt-BR/title.txt | 1 + .../play/listings/pt-PT/full-description.txt | 31 ++++++++++ .../play/listings/pt-PT/short-description.txt | 1 + app/src/main/play/listings/pt-PT/title.txt | 1 + .../play/listings/ru-RU/full-description.txt | 18 +++--- .../play/listings/sv-SE/full-description.txt | 4 +- .../play/listings/zh-CN/full-description.txt | 4 +- .../play/listings/zh-TW/full-description.txt | 31 ++++++++++ .../play/listings/zh-TW/short-description.txt | 1 + app/src/main/play/listings/zh-TW/title.txt | 1 + 24 files changed, 202 insertions(+), 81 deletions(-) create mode 100644 app/src/main/play/listings/lt/full-description.txt create mode 100644 app/src/main/play/listings/lt/short-description.txt create mode 100644 app/src/main/play/listings/lt/title.txt create mode 100644 app/src/main/play/listings/pt-BR/full-description.txt create mode 100644 app/src/main/play/listings/pt-BR/short-description.txt create mode 100644 app/src/main/play/listings/pt-BR/title.txt create mode 100644 app/src/main/play/listings/pt-PT/full-description.txt create mode 100644 app/src/main/play/listings/pt-PT/short-description.txt create mode 100644 app/src/main/play/listings/pt-PT/title.txt create mode 100644 app/src/main/play/listings/zh-TW/full-description.txt create mode 100644 app/src/main/play/listings/zh-TW/short-description.txt create mode 100644 app/src/main/play/listings/zh-TW/title.txt diff --git a/.tx/config b/.tx/config index 458d18a63..43e05555a 100644 --- a/.tx/config +++ b/.tx/config @@ -43,7 +43,7 @@ trans.ar = app/src/main/play/listings/ar/full-description.txt trans.az = app/src/main/play/listings/az-AZ/full-description.txt trans.bg = app/src/main/play/listings/bg/full-description.txt trans.ca = app/src/main/play/listings/ca/full-description.txt -trans.cs = app/src/main/play/listings/cs-CZ/full-description.txt +trans.cs_CZ = app/src/main/play/listings/cs-CZ/full-description.txt trans.da = app/src/main/play/listings/da-DK/full-description.txt trans.de = app/src/main/play/listings/de-DE/full-description.txt trans.el = app/src/main/play/listings/el-GR/full-description.txt @@ -65,7 +65,7 @@ trans.lt = app/src/main/play/listings/lt/full-description.txt trans.nl = app/src/main/play/listings/nl-NL/full-description.txt trans.pl_PL = app/src/main/play/listings/pl-PL/full-description.txt trans.pt_BR = app/src/main/play/listings/pt-BR/full-description.txt -trans.pt_PT = app/src/main/play/listings/pt-PT/full-description.txt +trans.pt = app/src/main/play/listings/pt-PT/full-description.txt trans.ro_RO = app/src/main/play/listings/ro/full-description.txt trans.ru_RU = app/src/main/play/listings/ru-RU/full-description.txt trans.sl_SI = app/src/main/play/listings/sl/full-description.txt @@ -83,7 +83,7 @@ trans.ar = app/src/main/play/listings/ar/short-description.txt trans.az = app/src/main/play/listings/az-AZ/short-description.txt trans.bg = app/src/main/play/listings/bg/short-description.txt trans.ca = app/src/main/play/listings/ca/short-description.txt -trans.cs = app/src/main/play/listings/cs-CZ/short-description.txt +trans.cs_CZ = app/src/main/play/listings/cs-CZ/short-description.txt trans.da = app/src/main/play/listings/da-DK/short-description.txt trans.de = app/src/main/play/listings/de-DE/short-description.txt trans.el = app/src/main/play/listings/el-GR/short-description.txt @@ -105,7 +105,7 @@ trans.lt = app/src/main/play/listings/lt/short-description.txt trans.nl = app/src/main/play/listings/nl-NL/short-description.txt trans.pl_PL = app/src/main/play/listings/pl-PL/short-description.txt trans.pt_BR = app/src/main/play/listings/pt-BR/short-description.txt -trans.pt_PT = app/src/main/play/listings/pt-PT/short-description.txt +trans.pt = app/src/main/play/listings/pt-PT/short-description.txt trans.ro_RO = app/src/main/play/listings/ro/short-description.txt trans.ru_RU = app/src/main/play/listings/ru-RU/short-description.txt trans.sl_SI = app/src/main/play/listings/sl/short-description.txt diff --git a/app/src/main/play/listings/de-DE/full-description.txt b/app/src/main/play/listings/de-DE/full-description.txt index 5365983bb..b342dcdee 100644 --- a/app/src/main/play/listings/de-DE/full-description.txt +++ b/app/src/main/play/listings/de-DE/full-description.txt @@ -24,8 +24,8 @@ AntennaPod ist von Podcast-Enthusiasten gemacht und frei im wahrsten Sinne des W Tritt der AntennaPod-Community bei! AntennaPod wird aktiv von Freiwilligen weiterentwickelt. Auch du kannst bei der Entwicklung mit Quellcode oder Kommentaren mitwirken! -Wir verwenden GitHub für Funktionswünsche, Fehlerberichte und zur Beteiligung an der Entwicklung: -https://www.github.com/AntennaPod/AntennaPod +Unsere freundlichen Freiwilligen im Forum stehen dir bei allen Fragen zur Seite. Gerne kannst du dich dort auch über neue Funktionen und über Podcasting generell austauschen. +https://forum.antennapod.org/ Mit Transifex kannst du uns beim Übersetzen helfen: https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/et/full-description.txt b/app/src/main/play/listings/et/full-description.txt index 446d16ded..ebcc26476 100644 --- a/app/src/main/play/listings/et/full-description.txt +++ b/app/src/main/play/listings/et/full-description.txt @@ -1,4 +1,4 @@ -AntennaPod on taskuhäälingu haldur ja esitaja, millega saad kohese ligipääsu miljonitele tasuta ja tasulistele saadetele nii ise- kui suurtegijatelt nagu BBC, NPR ja CNN. Lisa, impordi ja ekspordi nende voogusid ilma muredeta kasutades iTunes'i taskuhäälingute andmebaasi, OPML fail või lihtsalt RSS URL-e. +AntennaPod on taskuhäälingu haldur ja esitaja, millega saad kohe ligipääsu miljonitele tasuta ja tasulistele saadetele nii ise- kui suurtegijatelt nagu BBC, NPR ja CNN. Lisa, impordi ja ekspordi nende voogusid ilma probleemideta, kasutades iTunes'i taskuhäälingute andmebaasi, OPML faili või lihtsalt RSS URL-e. Laadi alla, voogedasta või lisa kuulamise järjekorda saateid ning naudi neid täpselt nii, nagu sina tahad, kasutades muudetavat taasesituse kiirust, peatükkide tuge ja unetaimerit. Säästa pingutuselt, aku kestuselt ja mobiilse andmeside kasutuselt võimekate automatiseerimise vahenditega saadete allalaadimiseks (määra millal, mis sagedusega ja millistest WiFi võrkudes) ning kustutamiseks (vastavalt sinu lemmikutele ja viivituse sätetele). @@ -24,8 +24,8 @@ Kuna AntennaPod on tehtud taskuhäälingu entusiastide poolt, on AntennaPod vaba Liitu AntennaPodi kogukonnaga! AntennaPod on vabatahtlike poolt aktiivselt arendamisel. Ka sina võid anda oma osa, kirjutades koodi või andes tagasisidet! -GitHub on koht, kus esitada uute võimaluste soove, vearaporteid ja kooditäiendusi: -https://www.github.com/AntennaPod/AntennaPod +Meie sõbralikud foorumi liikmed aitavad sind meeleldi leida vastuse kõigile küsimustele. Oled oodatud arutama nii äpi võimalusi kui taskuhäälinguid üldiselt. +https://forum.antennapod.org/ Transifex on koht, kus saab aidata tõlgetega: https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/fr-FR/full-description.txt b/app/src/main/play/listings/fr-FR/full-description.txt index 7e763023b..911381a95 100644 --- a/app/src/main/play/listings/fr-FR/full-description.txt +++ b/app/src/main/play/listings/fr-FR/full-description.txt @@ -24,8 +24,8 @@ Conçu par des fans de podcast, AntennaPod est gratuit, open source et sans publ Rejoignez la communauté d'AntennaPod ! AntennaPod est développé activement par des volontaires. Vous pouvez aussi contribuer avec du code, des traductions ou des commentaires ! -Rendez-vous sur GitHub pour demander de nouvelles options, signaler des bugs ou pour contribuer au développement : -https://www.github.com/AntennaPod/AntennaPod +Les utilisateurs de notre forum seront heureux de répondre à vos questions. Rejoignez-nous pour discuter des fonctionnalités et des podcasts. +https://forum.antennapod.org/ Rendez-vous sur Transifex pour aider la traduction : https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/gl-ES/full-description.txt b/app/src/main/play/listings/gl-ES/full-description.txt index bf9471ad2..432677e8d 100644 --- a/app/src/main/play/listings/gl-ES/full-description.txt +++ b/app/src/main/play/listings/gl-ES/full-description.txt @@ -1,6 +1,6 @@ AntennaPod é un xestor de podcast e reprodutor que che permite acceder a millóns de contidos gratuítos e de pagamento, desde produtores independentes a grandes empresas de contidos como BBC, NPR ou CNN. Engade, importa e exporta as súas fontes sen complicacións utilizando a base de datos de iTunes, ficheiros OPML ou URL de RSS. -Descarga, difunde ou pon e cola episodios e desfrutaos do xeito que prefiras, con velocidade de reprodución axustable, soporte de capítulos e apagado programable. -De xeito doado, aforrando batería e datos móbiles e con ferramentas potentes para a automatización das descargas (indicando horarios, intervalos e redes WiFi) e borrado de ficheiros (baseado nos favoritos e axustes temporais). +Descarga, difunde ou pon e cola episodios e desfrútaos do xeito que prefiras, con velocidade de reprodución axustable, soporte de capítulos e apagado programable. +De xeito doado, aforrando batería e datos móbiles, con ferramentas potentes para a automatización das descargas (indicando horarios, intervalos e redes WiFi) e borrado de ficheiros (baseado nos favoritos e axustes temporais). Feita por entusiastas do podcast, AntennaPod é libre en todos os sentidos da palabra: código aberto, gratuíto e sen anuncios. @@ -17,15 +17,15 @@ Feita por entusiastas do podcast, AntennaPod é libre en todos os sentidos da pa Xestiona o sistema • Toma o control das descargas automáticas: escolle fontes, exclúe redes móbiles, redes Wifi específicas, que o móbil esté cargando ou establece horarios para a descarga -• Xestiona o almacenaxe establecendo a número de episodios gardados, borrado intelixente e escollendo a localización preferida. +• Xestiona a almacenaxe establecendo a número de episodios gardados, borrado intelixente e escollendo a localización preferida. • Adáptase ao teu entorno utilizando os decorados claro ou escuro • Respalda as túas subscricións con gPodder.net e a exportación OPML Participa na comunidade AntennaPod! -AntennaPod está baixo continuo desenvolvemento grazas a voluntarios. Vostede tamén pode contribuir, con código ou con comentarios! +AntennaPod está baixo continuo desenvolvemento grazas a persoas voluntarias. Ti tamén podes contribuír, con código ou con comentarios! -Podes solicitar novas características utilizando GitHub, así como informe de fallos ou contribuír ao código: -https://www.github.com/AntennaPod/AntennaPod +No noso foro estamos encantados de poder axudarche con calquera dúbida que teñas. Convidámoste a comentar a aplicación e o podcasting en xeral. +https://forum.antennapod.org/ As traducións fanse en Transifex: https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/hu-HU/full-description.txt b/app/src/main/play/listings/hu-HU/full-description.txt index 708ad5b1e..09fd8d1ba 100644 --- a/app/src/main/play/listings/hu-HU/full-description.txt +++ b/app/src/main/play/listings/hu-HU/full-description.txt @@ -24,8 +24,8 @@ Podcast-rajongók készítésében, az AntennaPod teljesen szabad, nyílt forrá Csatlakozzon az AntennaPod közösséghez! Az AntennaPodot aktívan fejlesztik az önkéntesek. Ön is közreműködhet: kóddal vagy megjegyzésekkel! -A GitHub a funkciókérések, hibajelentések és a kódbeküldés helye: -https://www.github.com/AntennaPod/AntennaPod +A barátságos fórumtagjaink örömmel segítenek minden felmerülő kérdésben. Meghívjuk, hogy beszélgessen a funkciókról, valamint általánosságban a podcastolásról. +https://forum.antennapod.org/ A Transifexen segíthet a fordításokban: https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/it-IT/full-description.txt b/app/src/main/play/listings/it-IT/full-description.txt index 047a2278b..5e75f9420 100644 --- a/app/src/main/play/listings/it-IT/full-description.txt +++ b/app/src/main/play/listings/it-IT/full-description.txt @@ -1,14 +1,14 @@ -AntennaPod è un riproduttore e gestore di podcast che permette l'accesso immediato a milioni di podcast gratuiti e a pagamento, dai podcaster indipendenti alle più grandi emittenti come BBC, NPR e CNN. Aggiungi, importa e esporta in modo semplice usando il database di podcast di iTunes, un file OPML o i semplici URL RSS. -Scarica, ascolta in streaming o accoda gli episodi e goditeli come preferisci grazie alla velocità di riproduzione modificabile, al supporto ai capitoli e al timer di spegnimento. -Risparmia fatica, carica della batteria e dati con i potenti controlli automatizzati per il download degli episodi (orari specifici, intervalli di tempo e reti WiFi selezionate) e l'eliminazione degli episodi (gestione dei preferiti e i tempi di cancellazione). +AntennaPod è un riproduttore e gestore di podcast che permette l'accesso immediato a milioni di podcast gratuiti e a pagamento, dai podcaster indipendenti alle grandi emittenti come BBC, NPR e CNN. Aggiungi, importa ed esporta i feed in modo semplice tramite il database di iTunes, i file OPML o i semplici indirizzi RSS. +Scarica, accoda o ascolta in streaming gli episodi e goditeli come preferisci grazie alla velocità di riproduzione modificabile, al supporto ai capitoli e al timer di spegnimento. +Risparmia fatica, carica della batteria e dati grazie ai potenti controlli automatizzati per il download degli episodi (orari specifici, intervalli di tempo e reti WiFi specifiche) e l'eliminazione degli episodi (gestione dei preferiti e tempi di cancellazione). -Creato da appassionati di podcast, AntennaPod è libero in tutti i sensi: open source, gratuito e senza pubblicità. +Creato da appassionati di podcast, AntennaPod è libero in tutti i sensi: open source, gratuito e senza pubblicità. Importa, organizza e riproduci -• Gestisci la riproduzione ovunque: widget nella schermata home, tendina delle notifiche, cuffie e controlli bluetooth. +• Gestisci la riproduzione ovunque: widget nella schermata home, tendina delle notifiche, cuffie e controlli Bluetooth. • Aggiungi e importa le sottoscrizioni tramite iTunes e gPodder, o anche tramite file OPML, collegamenti RSS o Atom. -• Goditi l'ascolto nel modo che preferisci grazie alla velocità di riproduzione modificabile, il supporto ai capitoli, la memorizzazione dello stato di riproduzione e un timer di spegnimento avanzato (scuoti per riavviare o riduci il volume) -• Accedi ai feed e agli episodi protetti tramite utente e password +• Goditi l'ascolto nel modo che preferisci grazie alla velocità di riproduzione modificabile, al supporto ai capitoli, alla memorizzazione dello stato di riproduzione e al timer di spegnimento avanzato (scuoti per riavviare o riduci il volume) +• Accedi tramite utente e password ai feed e agli episodi riservati Tieni traccia, condividi & ricerca • Tieni traccia dei migliori episodi di sempre segnandoli come preferiti @@ -16,16 +16,16 @@ Creato da appassionati di podcast, AntennaPod è libero in tutti i sensi: open • Condividi gli episodi e i feed tramite opzioni avanzate di condivisione via social o email, i servizi di gPodder.net o l'esportazione OPML. Controlla il sistema -• Gestisci il download automatico: puoi selezionare i feed, escludere le reti mobili, utilizzare reti WiFi specifiche, impostare il download solo se in carica e scaricare in orari o intervalli selezionati. -• Gestisci lo spazio impostando l'occupazione massima degli episodi, la cancellazione intelligente e il percorso in cui scaricare i file. -• Adattati alle tue preferenze grazie ai temi chiaro o scuro. -• Effettua il backup delle sottoscrizioni grazie all'integrazione di gPodder.net e all'esportazione in OPML +• Gestisci il download automatico: puoi selezionare i feed, escludere le reti mobili, utilizzare reti WiFi specifiche, impostare il download solo quando in carica e scaricare ad orari o intervalli selezionati. +• Gestisci lo spazio impostando il numero massimo di episodi da tenere, la cancellazione intelligente e il percorso in cui scaricare i file. +• Adattalo alle tue preferenze grazie ai temi chiaro o scuro. +• Effettua il backup delle sottoscrizioni grazie all'integrazione con gPodder.net e all'esportazione in OPML. Partecipa alla community di AntennaPod! AntennaPod è sviluppato da volontari. Anche tu puoi contribuire, con il codice o con dei commenti! -GitHub è la piattaforma usata per le richieste di funzionalità, le segnalazioni di bug o i contributi al codice: -https://www.github.com/AntennaPod/AntennaPod +I simpatici membri del nostro forum sono felici di rispondere a qualsiasi domanda tu possa avere. Ti invitiamo anche a discutere di funzionalità e podcasting. +https://forum.antennapod.org/ Transifex è dove puoi contribuire alla traduzione: https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/iw-IL/full-description.txt b/app/src/main/play/listings/iw-IL/full-description.txt index 58895c50e..20cc56675 100644 --- a/app/src/main/play/listings/iw-IL/full-description.txt +++ b/app/src/main/play/listings/iw-IL/full-description.txt @@ -1,42 +1,31 @@ -היישומון אנטנה־פּוֹד הוא נגן ומנהל פודקאסטים שמעניק לך גישה ישירה למיליונים של פודקאסטים בחינם ובתשלום, החל ממגישי פודקאסטים עצמאיים ועד למפיצים גדולים כגון BBC,‏ NPR ו־CNN. ניתן להוסיף, לייבא ולייצא את ההזנות שלהם בקלות יחסית באמצעות מסד נתוני הפודקאסטים של iTunes, קובצי OPML או כתובות של RSS. מאפשר לך לחסוך במאמץ, סוללה ותקשורת נתונים עם פקדי אוטומציה להורדה של פרקים (לפי זמנים, הפרשי זמן ורשתות אלחוטיות) ומחיקה של פרקים (על בסיס הגדרות המועדפים וההשהיה שלך).
-אבל הכי חשוב: ניתן להוריד, להזרים או לסדר רשימות של פרקים וליהנות מהם בכל דרך שמתאימה לך עם מהירויות נגינה משתנות, תמיכה במקטעים ומתזמן שינה +היישומון אנטנה־פּוֹד הוא נגן ומנהל פודקאסטים שמעניק לך גישה ישירה למיליונים של פודקאסטים בחינם ובתשלום, החל ממגישי פודקאסטים עצמאיים ועד למפיצים גדולים כגון BBC,‏ NPR ו־CNN. ניתן להוסיף, לייבא ולייצא את ההזנות שלהם בקלות יחסית באמצעות מסד נתוני הפודקאסטים של iTunes, קובצי OPML או כתובות של RSS. +ניתן להוריד, להזרים או לסדר רשימות של פרקים וליהנות מהם בכל דרך שמתאימה לך עם מהירויות נגינה משתנות, תמיכה במקטעים ומתזמן שינה +ניתן לחסוך במאמץ, סוללה וניצולת חבילת נתונים עם בקרת אוטומציה מתקדמת להורדת פרקים (תוך הגדרת זמנים, מרווחים ורשתות אלחוטיות) ומחיקת פרקים (על בסיס המועדפים והגדרות ההשהיה שלך). -מיוצרת על ידי חובבי פודקאסטים, אנטנהפוד הינה תוכנה חינמית בכל מובן המילה: קוד פתוח, ללא עלות וללא פרסומות. +היישומון אנטנהפּוֹד, שנוצר על ידי חובבי פודקאסטים, הוא חופשי במלוא מובן המילה: קוד פתוח, ללא עלות, ללא פרסומות. -כל התכונות:
-ייבוא, ארגון ונגינה
-• ניתן להוסיף ולייבא הזנות דרך הספריות של iTunes ושל gPodder.net, קובצי OPML וקישורי RSS או Atom
-• ניתן לנהל את הנגינה מכל מקום: וידג׳ט על מסך הבית, התרעות המערכת ופקדי שקע אוזניות ובלוטות׳
-• פשוט ליהנות בדרך שלך עם מהירות נגינה משתנה, תמיכה במקטעים (MP3, VorbisComment ו־Podlove), שמירת מיקום הנגינה ומתזמן שינה מתקדם (ניתן לנער כדי לאפס, להנמיך את עצמך השמע ולהאט את מהירות הנגינה)
-• גישה להזנות ולפרקים המוגנים בססמה
-• ניתן להשתמש בעימודי ההזנות שלנו (www.podlove.org/paged-feeds) +ייבוא, ארגון והשמעה +• ניתן לנהל את הנגינה מכל מקום: וידג׳ט על מסך הבית, התרעות המערכת ופקדי שקע אוזניות ובלוטות׳ +• ניתן להוסיף ולייבא הזנות דרך הספריות של iTunes ושל gPodder.net, קובצי OPML וקישורי RSS או Atom +• פשוט ליהנות בדרך שלך עם מהירות נגינה משתנה, תמיכה במקטעים, שמירת מיקום הנגינה ומתזמן שינה מתקדם (ניתן לנער כדי לאפס, להנמיך את עצמת השמע) +• גישה להזנות ולפרקים המוגנים בססמה -מעקב, שיתוף והבעת הערכה
-• מעקב אחר הטובים שבטובים על ידי סימון פרקים כמועדפים
-• ניתן לאתר פרק אחד דרך היסטוריית הנגינה או על ידי חיפוש (כותרות והערות פרק)
-• ניתן לשתף פרקים והזנות דרך אפשרויות מתקדמות ברשתות חברתיות ודוא״ל, שירותי gPodder.net ודרך ייצוא OPML
+מעקב, שיתוף והערכה +• מעקב אחר הטובים שבטובים על ידי סימון פרקים כמועדפים +• ניתן לאתר פרק אחד דרך היסטוריית הנגינה או על ידי חיפוש כותרות והערות פרק +• ניתן לשתף פרקים והזנות דרך אפשרויות מתקדמות ברשתות חברתיות ודוא״ל, שירותי gPodder.net ודרך ייצוא OPML -שליטה במערכת
-• ניתן לשלוט על הורדה אוטומטית: לבחור הזנות, להחריג רשתות סלולריות, לבחור רשתות אלחוטיות מסוימות, לדרוש מהטלפון להיות בטעינה ולהגדיר מועדים או מרווחי זמן
-• ניתן לנהל את האחסון על ידי הגדרת כמות הפרקים שנשמרים במטמון, מחיקה חכמה (בהתבסס על המועדפים ומצב הנגינה שלך) ובחירת המיקום המועדף עליך
-• ניתן להשתמש באנטנה־פּוֹד בשפה שלך (HE, EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)
-• התאמה לסביבה שלך באמצעות ערכות עיצוב בהירה וכהה
-• גיבוי התמיכה שלך עם שילוב מול gPodder.net וייצוא של OPML +שליטה במערכת +• ניתן לשלוט על הורדה אוטומטית: לבחור הזנות, להחריג רשתות סלולריות, לבחור רשתות אלחוטיות מסוימות, לדרוש מהטלפון להיות בטעינה ולהגדיר מועדים או מרווחי זמן +• ניתן לנהל את האחסון על ידי הגדרת כמות הפרקים שנשמרים במטמון, מחיקה חכמה (בהתבסס על המועדפים ומצב הנגינה שלך) ובחירת המיקום המועדף עליך +• התאמה לסביבה שלך באמצעות ערכות עיצוב בהירה וכהה +• גיבוי המינויים שלך עם שילוב מול gPodder.net וייצוא של OPML -מזמינים אותך להצטרף לקהילת אנטנה־פּוֹד!
+מזמינים אותך להצטרף עוד היום לקהילת אנטנהפּוֹד! את תהליכי הפיתוח הפעילים של אנטנה־פּוֹד מובילים מתנדבים. ניתן לתרום גם כן, עם קוד או עם הערה! -GitHub הוא המקום בו אנו מרכזים את בקשות התכונות, דיווחי התקלות ותרומות הקוד:
-https://www.github.com/AntennaPod/AntennaPod +חברי הפורום החביבים שלנו שמחים לסייע בכל שאלה שעשויה לצוץ לך. מזמינים אותך לדון בתכונות של היישומון ועל פודקאסטים בכלל. +https://forum.antennapod.org/ -הקבוצה שלנו ב־Google היא המקום לשתף את הרעיונות שלך, רגעי הפודקאסט המועדפים עליך ואת הערכתך לכל המתנדבים:
-https://groups.google.com/forum/#!forum/antennapod - -יש לך שאלה או שמעניין אותך לתת לנו משוב? -https://twitter.com/@AntennaPod - -Transifex הוא המקום לסייע בתרגומים:
-https://www.transifex.com/antennapod/antennapod - -מזמינים אותך לחקור את תכנית הבדיקות שלנו כדי לקבל את התכונות העדכניות ביותר לפני כולם:
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file +Transifex הוא המקום לסייע עם התרגומים: +https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/lt/full-description.txt b/app/src/main/play/listings/lt/full-description.txt new file mode 100644 index 000000000..161322aa2 --- /dev/null +++ b/app/src/main/play/listings/lt/full-description.txt @@ -0,0 +1,31 @@ +„AntennaPod“ yra tinklalaidžių tvarkytuvė ir leistuvė, įgalinanti prieigą prie milijonų nemokamų ir mokamų tinklalaidžių, nuo nepriklausomų kūrėjų iki didžiųjų leidyklų, tokių kaip „BBC“, „NPR“ ir „CNN“. Be vargo pridėkite, importuokite ir eksportuokite sklaidos kanalus naudodamiesi „iTunes“ tinklalaidžių duomenų baze, OPML failais ar RSS kanalais. +Atsisiųskite, klausykitės iš karto ar dėkite epizodus į eilę ir mėgaukitės jais pasirinkę atkūrimo spartą, naudodamiesi skyrių palaikymu bei miego laikmačiu. +Sutaupykite pastangų, baterijos energijos ir suvartotų mobiliųjų duomenų naudodamiesi galingais automatizacijos valdikliais epizodų atsiuntimui (nurodykite laiką, intervalą ir „WiFi“ tinklus) ir epizodų trynimui (remiantis Jūsų mėgstamaisiais ir atidėjimo nustatymais). + +Sukurtas tinklalaidžių entuziastų, „AntennaPod“ yra laisvas visomis prasmėmis: atvirojo kodo, be mokesčių, be reklamų. + +Importuokite, tvarkykite ir atkurkite +• Valdykite atkūrimą bet kur: pradžios ekrane, programų pranešimuose, ausinių ar „Bluetooth“ valdikliu +• Pridėkite ar importuokite sklaidos kanalus iš „iTunes“ ir „gPodder.net“, OPML failų, RSS ar Atom nuorodų +• Mėgaukitės klausydamiesi taip, kaip Jums patinka, naudodamiesi derinama atkūrimo sparta, skyrių palaikymu, išsaugota atkūrimo pozicija ir miego laikmačiu (pakračius nustatomas iš naujo, mažinamas garsis) +• Pasiekite slaptažodžiu apsaugotus sklaidos kanalus ir epizodus + +Sekite, dalinkitės ir branginkite +• Kaupkite geriausius žymėdami epizodus kaip mėgstamus +• Raskite norimą epizodą pasinaudoję atkūrimo istorija ar paieška pavadinimuose ir laidų užrašuose +• Dalinkitės epizodais ir sklaidos kanalais per socialinius tinklus, el. paštu, „gPodder.net“ ir OPML failus + +Valdykite sistemą +• Valdykite automatinį atsiuntimą: pasirinkite sklaidos kanalus, neleiskite atsiuntimų mobiliuoju ryšiu, apibrėžkite leistinus belaidžius tinklus, reikalaukite, jog atsiuntimai būtų vykdomi įkrovos metu, nurodykite atsiuntimų dienos metą ar intervalą +• Valdykite laikmenos naudojimą nurodant podėlyje laikomų epizodų kiekį, išmanų trynimą ir nurodant norimą saugojimo vietą +• Pritaikykite savo aplinkai pasirinkę šviesią ar tamsią temą +• Kurkite prenumeratų atsargines kopijas „gPodder.net“ ar OPML failais + +Prisijunkite prie AntennaPod bendruomenės! +„AntennaPod“ vysto savanoriai. Ir Jūs galite prisidėti, nuo atsiliepimų iki programinio kodo! + +Draugiški mūsų forumo nariai pasiruošę padėti visais turimais klausimais. Kviečiame diskutuoti apie programėlės funkcijas bei bendrai apie tinklalaides. +https://forum.antennapod.org/ + +Prie vertimų galite prisidėti „Transifex“: +https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/lt/short-description.txt b/app/src/main/play/listings/lt/short-description.txt new file mode 100644 index 000000000..0a0752647 --- /dev/null +++ b/app/src/main/play/listings/lt/short-description.txt @@ -0,0 +1 @@ +Patogi naudoti, lanksti atvirojo kodo tinklalaidžių tvarkytuvė bei leistuvė \ No newline at end of file diff --git a/app/src/main/play/listings/lt/title.txt b/app/src/main/play/listings/lt/title.txt new file mode 100644 index 000000000..31552f353 --- /dev/null +++ b/app/src/main/play/listings/lt/title.txt @@ -0,0 +1 @@ +AntennaPod \ No newline at end of file diff --git a/app/src/main/play/listings/nl-NL/full-description.txt b/app/src/main/play/listings/nl-NL/full-description.txt index 851f0b34e..9fedfaffe 100644 --- a/app/src/main/play/listings/nl-NL/full-description.txt +++ b/app/src/main/play/listings/nl-NL/full-description.txt @@ -24,8 +24,8 @@ AntennaPod is gemaakt door podcast-enthousiastelingen. Het team van vrijwilliger Doe mee met de AntennaPod-gemeenschap! AntennaPod wordt regelmatig geüpdatet door vrijwilligers. En jij kan ook helpen, met code of commentaar! -Je kunt op GitHub terecht voor foutmeldingen, verzoekjes voor nieuwe functies en bijdragen aan de code: -https://www.github.com/AntennaPod/AntennaPod +De vriendelijke leden van ons forum helpen je graag met welke vraag dan ook. Deel gerust ook ideëen voor nieuwe functies of podcasts in het algemeen. +https://forum.antennapod.org/ Transifex is de beste plek om te helpen met vertalen: https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/pt-BR/full-description.txt b/app/src/main/play/listings/pt-BR/full-description.txt new file mode 100644 index 000000000..2c852b464 --- /dev/null +++ b/app/src/main/play/listings/pt-BR/full-description.txt @@ -0,0 +1,31 @@ +AntennaPod é um tocador e gerenciador de podcasts que lhe oferece acesso a milhões de podcasts gratuitos ou pagos, de podcasters independentes ou dos principais publicadores de conteúdo como BBC, NPR e CNN. Adicione, importe e exporte seus feeds facilmente usando a biblioteca do iTunes, arquivos OPML ou URLs RSS. +Baixe, transmita ou enfileire episódios e curta-os como quiser com velocidades de reprodução ajustáveis, suporte a capítulos e um temporizador. +Economize esforço, bateria e uso de dados móveis com avançados recursos para automatizar o download de episódios (especificar horários, intervalos e redes WiFi) e a sua remoção (com base em seus favoritos e configurações de atraso). + +Feito por entusiastas de podcast, o AntennaPod é gratuito em todos os sentidos da palavra: código aberto, sem custos, sem anúncios. + +Importe, organize e toque +• Gerencie suas reproduções de qualquer lugar: widget da tela inicial, notificações de sistema e fone de ouvido e controles bluetooth
+• Adicione and importe feeds pelo iTunes e diretorios gPodder.net, arquivos OPML e links RSS ou Atom
+• Ouça do seu jeito com velocidade de reprodução ajustável, suporte a capítulos (MP3, VorbisComment e Podlove), marcador da posição de reprodução e um despertador avançado (chacoalhe para reiniciar, volume reduzido e reprodução desacelerada)
+• Acesse feeds e episódios protegidos por senha + +Acompanhe, compartilhe e aprecie +• Guarde o melhor do melhor marcando episódios como favoritos
+• Encontre aquele episódio específico através do histórico de execução ou pelo sistema de busca (através de títulos e anotações)
+• Compartilhe episódios e feeds através de opções em redes sociais, email, os serviços da gPodder.net e exportação OPML
+ +Controle o sistema +• Tenha controle sobre a automação dos downloads: escolha feeds, exclua redes móveis, selecione redes específicas de WiFi, exija que o telefone esteja sendo carregado e defina horários ou intervalos
+• Gerencie o armazenamento configurando a quantidade de episódios em cache, exclusão inteligente dos episódios (baseada nos seus favoritos e status de reprodução)
+• Adapte-se ao ambiente utilizando os temas claro ou escuro
+• Faça backup das suas inscrições com a integração ao gPodder.net e exportação de OPML + +Junte-se à comunidade do AntennaPod! +O AntennaPod está sob constante desenvolvimento através de voluntários. Você também pode contribuir, com código ou um comentário! + +Nossos simpáticos membros do fórum ficarão felizes em ajudar com todas as perguntas que você tiver. Você está convidado a discutir sobre funcionalidades e podcasts em geral também. +https://forum.antennapod.org/ + +Transifex é o lugar para ajudar com as traduções:
+https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/pt-BR/short-description.txt b/app/src/main/play/listings/pt-BR/short-description.txt new file mode 100644 index 000000000..8f3301cc3 --- /dev/null +++ b/app/src/main/play/listings/pt-BR/short-description.txt @@ -0,0 +1 @@ +Um player de podcasts fácil de usar, flexível e de código aberto \ No newline at end of file diff --git a/app/src/main/play/listings/pt-BR/title.txt b/app/src/main/play/listings/pt-BR/title.txt new file mode 100644 index 000000000..31552f353 --- /dev/null +++ b/app/src/main/play/listings/pt-BR/title.txt @@ -0,0 +1 @@ +AntennaPod \ No newline at end of file diff --git a/app/src/main/play/listings/pt-PT/full-description.txt b/app/src/main/play/listings/pt-PT/full-description.txt new file mode 100644 index 000000000..6774d87f8 --- /dev/null +++ b/app/src/main/play/listings/pt-PT/full-description.txt @@ -0,0 +1,31 @@ +AntennaPod é um gestor de podcasts que lhe permite aceder a milhões de podcasts, gratuitos ou pagos, sejam eles publicadores independentes ou grandes cadeias tais como as estações BBC, NPR e CNN. A adição de fontes é muito fácil através do serviço iTunes, ficheiros OPML e fontes RSS. +Pode descarregar, emitir ou colocar episódios na fila de reprodução ao seu gosto, pode utilizar velocidades variáveis de reprodução, tem suporte a capítulos e um temporizador. +Poupe tempo, economize bateria e dados móveis através dos mecanismos de controlo de descargas de episódios (possibilidade de especificar intervalos ou horas para as descargas e redes Wi-Fi) e de eliminação de episódios (de acordo com as suas preferências). + +Criado por entusiastas de podcasts, AntennaPod é livre em todos os sentidos da palavra: open source, gratuito e sem publicidade. + +Importação, organização e reprodução +• Gestão de podcasts através do widget, barra de notificações e controlos de auriculares ou bluetooth +• Adicione e importe fontes existentes nos diretórios iTunes e gPodder.net, ficheiros OPML e ligações ATOM e RSS +• Velocidade variável de reprodução, suporte a capítulos, memorização da posição de reprodução e um temporizador avançado (agite para repor, baixar e aumentar o volume) +• Acesso a fontes e episódios protegidos por palavra-passe + +Monitorizar, partilhar e apreciar +• Monitorize os seus episódio preferidos marcando-os como favoritos +• Localize um episódio no histórico de reprodução ou através de uma pesquisa por título ou notas +• Partilhe episódios e fontes nas redes sociais, por e-mail, no diretório gPodder.net ou através de ficheiros OPML + +Controlo de sistema +• Controle todas as descargas automáticas: escolha as fontes, exclua redes móveis, especifique as redes Wi-Fi, indique se o telefone deve estar a ser carregado e defina as horas ou intervalos das descargas +• Gestão do armazenamento através da cache de episódios, da eliminação inteligente e selecionando a localização dos dados +• Adapte-se ao seu ambiente através dos temas claro ou escuro +• Guarde as suas subscrições com a integração gPodder.net ou através da exportação OPML + +Integrar a comunidade AntennaPod! +O AntennaPod é desenvolvido por voluntários. Você também pode contribuir na programação ou reportando os erros encontrados! + +Os membros do nosso fórum podem ajudar-vos em relação às vossas dúvidas. Sinta-se à vontade para propor funcionalidades ou simplesmente falar connosco. +https://forum.antennapod.org/ + +Transifex é o local certo para ajudar a traduzir a aplicação: +https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/pt-PT/short-description.txt b/app/src/main/play/listings/pt-PT/short-description.txt new file mode 100644 index 000000000..c558abaca --- /dev/null +++ b/app/src/main/play/listings/pt-PT/short-description.txt @@ -0,0 +1 @@ +Gestor e reprodutor de podcasts simples, flexível e open souce \ No newline at end of file diff --git a/app/src/main/play/listings/pt-PT/title.txt b/app/src/main/play/listings/pt-PT/title.txt new file mode 100644 index 000000000..31552f353 --- /dev/null +++ b/app/src/main/play/listings/pt-PT/title.txt @@ -0,0 +1 @@ +AntennaPod \ No newline at end of file diff --git a/app/src/main/play/listings/ru-RU/full-description.txt b/app/src/main/play/listings/ru-RU/full-description.txt index d3d8f2d8f..5efe8f284 100644 --- a/app/src/main/play/listings/ru-RU/full-description.txt +++ b/app/src/main/play/listings/ru-RU/full-description.txt @@ -1,4 +1,4 @@ -AntennaPod — менеджер и проигрыватель подкастов, который обеспечит Вас мгновенным доступом к миллионам бесплатных и платных подкастов, как от независимых подкастеров, так и крупных издательских домов. С лёгкостью добавляйте, импортируйте и экспортируйте их каналы используя каталог подкастов iTunes, файлы OPML или адреса каналов RSS. +AntennaPod — менеджер и проигрыватель подкастов, который обеспечит вас мгновенным доступом к миллионам бесплатных и платных подкастов, как от независимых подкастеров, так и крупных издательских домов. С легкостью добавляйте, импортируйте и экспортируйте их каналы используя каталог подкастов iTunes, файлы OPML или адреса каналов RSS. Загружайте, транслируйте по сети или добавляйте выпуски в очередь и наслаждайтесь ими так, как вам нравится, с регулируемой скоростью воспроизведения, поддержкой оглавления и таймером сна. Экономьте время, заряд батареи и мобильный трафик при помощи мощных средств автоматизации загрузки выпусков (фильтрация, указание времени и интервалов, а также сетей WiFi) и их удаления (избранные и настройки хранения). @@ -7,8 +7,8 @@ AntennaPod — менеджер и проигрыватель подкастов Импортируйте, систематизируйте и слушайте • Всевозможное управление воспроизведением: виджетом, системным уведомлением и кнопками проводных и беспроводных гарнитур • Добавление и импорт каналов через каталоги iTunes и gPodder.net, файлы OPML и ссылки на каналы в RSS или Atom -• Приятное Вам прослушивание с регулировкой скорости воспроизведения, оглавлениями, запоминанием места воспроизведения и продвинутым таймером сна (со сбросом при встряхивании, снижением громкости) -• Доступ к каналам и выпускам защищённым паролем +• Приятное вам прослушивание с регулировкой скорости воспроизведения, оглавлениями, запоминанием места воспроизведения и продвинутым таймером сна (со сбросом при встряхивании, снижением громкости) +• Доступ к каналам и выпускам, защищенным паролем Отслеживайте, делитесь и благодарите • Отслеживайте лучших из лучших, помещая выпуски в избранное @@ -16,16 +16,16 @@ AntennaPod — менеджер и проигрыватель подкастов • Разнообразие способов поделиться выпусками и каналами через социальные сети и e-mail, услуги gPodder.net и экспорт в OPML Управляйте системой -• Управление автоматической загрузкой: выбор отдельных каналов, запрет на использование мобильных сетей, выбор определённых точек доступа WiFi, только во время зарядки телефона и в заданное время или интервалы -• Управление хранением путём задания количества выпусков в кэше, автоудаление и выбор предпочтительного размещения -• Приспосабливается к Вашему окружению посредством светлого или тёмного оформления -• Резервирование Ваших подписок путём интеграции с gPodder.net и экпорта в OPML +• Управление автоматической загрузкой: выбор отдельных каналов, запрет на использование мобильных сетей, выбор определенных точек доступа WiFi, только во время зарядки телефона и в заданное время или интервалы +• Управление хранением: ограничение количества кешируемых выпусков, автоудаление и выбор расположения файлов +• Приспосабливается к вашему окружению посредством светлого или темного оформления +• Резервирование ваших подписок путем интеграции с gPodder.net и экпорта в OPML Присоединяйтесь к сообществу AntennaPod! AntennaPod постоянно развивается силами добровольцев. Вы тоже можете сделать свой вклад при помощи кода или комментария! -Посещайте GitHub для запроса новых возможностей, уведомления об ошибках и внесения кода: -https://www.github.com/AntennaPod/AntennaPod +Участники нашего дружелюбного форума рады помочь вам с любым вопросом. Мы приглашаем вас обсудить возможности приложения и подкастинг вообще. +https://forum.antennapod.org/ Помогайте с переводом приложения на Transifex: https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/sv-SE/full-description.txt b/app/src/main/play/listings/sv-SE/full-description.txt index 5965f45bc..2846f4c22 100644 --- a/app/src/main/play/listings/sv-SE/full-description.txt +++ b/app/src/main/play/listings/sv-SE/full-description.txt @@ -24,8 +24,8 @@ Gjord av podcastenthusiaster, AntennaPod är fri i alla ordets bemärkelser: öp Gå med i AntennaPods gemenskap! AntennaPod är under aktiv utveckling av volontärer. Du kan också bidra, med kod eller kommentarer! -GitHub är platsen att gå till för att be om funktioner, skapa buggrapporter eller bidra med kod: -https://www.github.com/AntennaPod/AntennaPod +Våra vänliga forummedlemmar hjälper glatt till med alla frågor du har. Du inbjuds också att diskutera funktioner och podcasting generellt. +https://forum.antennapod.org/ Transifex är platsen att gå till för att hjälpa till med översättningen: https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/zh-CN/full-description.txt b/app/src/main/play/listings/zh-CN/full-description.txt index 993e7e18c..f467bc671 100644 --- a/app/src/main/play/listings/zh-CN/full-description.txt +++ b/app/src/main/play/listings/zh-CN/full-description.txt @@ -24,8 +24,8 @@ AntennaPod是一个播客管理器和播放器,可以让你即时访问数以 加入AntennaPod社区 志愿者正在积极开发 AntennaPod 。您也可以通过代码或评论做出贡献! -前往Github请求功能、报告错误和贡献代码 -https://www.github.com/AntennaPod/AntennaPod +我们友好的论坛成员乐意为您的每个问题提供帮助。 我们还邀请您讨论功能和播客。 +https://forum.antennapod.org/ 要帮忙翻译请前往Transifex https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/zh-TW/full-description.txt b/app/src/main/play/listings/zh-TW/full-description.txt new file mode 100644 index 000000000..97144018e --- /dev/null +++ b/app/src/main/play/listings/zh-TW/full-description.txt @@ -0,0 +1,31 @@ +AntennaPod 是 Podcast 訂閱管理工具。從百家爭鳴的獨立 Postcasters 到各立山頭的大廠如 BBC、CNN、NPR 等,讓數百萬免費或付費的 Podcast 唾手可得。您可以使用 iTunes 的 Podcast 資料庫、OPML 檔或RSS 網址等方式,輕鬆新增、匯入或匯出資料來源。 +將各 Podcast 劇集下載、串流播放或放入隊列,然後可以調整播放速度、跳躍章節、設定睡眠時間等等,用您喜愛的方式聆聽享受。 +用強大的自動下載設定指定時間、頻率、WiFi 下載,並且可以依據最愛劇集與延遲設定等方式刪除已經聽過的劇集。省力、省電、省數據。 + +AntennaPod 由 Podcast 愛好者鼎力製作,免費、無廣告、開放原始碼,給您最全面的自由。 + +匯入、管理、播放 +• 從主畫面小工具、系統通知、耳機或藍牙控制器隨處播放 +• 利用 iTunes、gPodder.net 目錄、OPML 檔、RSS 或 Atom 連結匯入、新增 Podcast +• 擁有自訂播放速度、支援章節、記憶播放進度、設定睡眠時間等功能,享受自由聆聽的樂趣 +• 支援以密碼保護的 Podcast + +保留、分享 +• 將特定一集設定為最愛,永久留存 +• 自播放紀錄、標題或附註等處搜尋特定一集 +• 以社群網站、電子郵件、gPodder.net 或匯出 OPML 等方式分享 Podcast + +一手掌控 +• 選擇特定 Podcast、只用特定 WiFi 下載、僅在充電時下載、設定更新頻率與次數等等機制,完整控制自動下載功能 +• 設定暫存集數上限、智慧刪除、設定儲存位置等機制,方便管理設備空間 +• 根據系統環境採用明亮或暗夜佈景 +• 以整合的 gPodder.net 服務或匯出 OPML 來備份訂閱清單 + +加入 AntennaPod 社群! +AntennaPod 由志工熱情製作,您也可以加入 -- 幫忙寫程式或給意見都非常歡迎! + +若您遇上使用問題,論壇上的社群成員都樂意提供協助。您也可以加入討論未來功能、其他 Podcast 相關的事情。 +https://forum.antennapod.org/ + +協助翻譯請至 Transifex: +https://www.transifex.com/antennapod/antennapod \ No newline at end of file diff --git a/app/src/main/play/listings/zh-TW/short-description.txt b/app/src/main/play/listings/zh-TW/short-description.txt new file mode 100644 index 000000000..2606389d1 --- /dev/null +++ b/app/src/main/play/listings/zh-TW/short-description.txt @@ -0,0 +1 @@ +易用、靈活的開源 podcast 管理及播放器 \ No newline at end of file diff --git a/app/src/main/play/listings/zh-TW/title.txt b/app/src/main/play/listings/zh-TW/title.txt new file mode 100644 index 000000000..31552f353 --- /dev/null +++ b/app/src/main/play/listings/zh-TW/title.txt @@ -0,0 +1 @@ +AntennaPod \ No newline at end of file From 983c0a464e265fca8f5a6bdc2e8b14951506472e Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Wed, 14 Oct 2020 16:56:15 +0200 Subject: [PATCH 13/16] Set cache-control to no-cache The previous value, no-store, did not allow CDNs to do their work. --- .../danoeh/antennapod/core/service/download/HttpDownloader.java | 1 + 1 file changed, 1 insertion(+) 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())) { From 959f29c8b8cd26f7f9ddc774ce7c27e1c872cd39 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Tue, 13 Oct 2020 22:49:06 +0200 Subject: [PATCH 14/16] Ensure that search query is readable --- .../fragment/FeedItemlistFragment.java | 9 ++++++--- .../antennapod/menuhandler/MenuItemUtils.java | 2 ++ .../view/ToolbarIconTintManager.java | 20 ++++++++++++++----- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java index 67433166c..ab9a867d0 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java @@ -247,7 +247,12 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem @Override public boolean onOptionsItemSelected(MenuItem item) { - if (!super.onOptionsItemSelected(item)) { + if (item.getItemId() == R.id.action_search) { + item.getActionView().post(() -> iconTintManager.updateTint()); + } + if (super.onOptionsItemSelected(item)) { + return true; + } else { if (feed == null) { ((MainActivity) getActivity()).showSnackbarAbovePlayer( R.string.please_wait_for_data, Toast.LENGTH_LONG); @@ -298,8 +303,6 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage()); return true; } - } else { - return true; } } diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java index 76091327d..ff98ac609 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java @@ -8,6 +8,7 @@ import androidx.appcompat.widget.SearchView; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.util.ThemeUtils; import de.danoeh.antennapod.fragment.SearchFragment; /** @@ -33,6 +34,7 @@ public class MenuItemUtils extends de.danoeh.antennapod.core.menuhandler.MenuIte public static void setupSearchItem(Menu menu, MainActivity activity, long feedId) { MenuItem searchItem = menu.findItem(R.id.action_search); final SearchView sv = (SearchView) searchItem.getActionView(); + sv.setBackgroundColor(ThemeUtils.getColorFromAttr(activity, android.R.attr.windowBackground)); sv.setQueryHint(activity.getString(R.string.search_label)); sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override diff --git a/app/src/main/java/de/danoeh/antennapod/view/ToolbarIconTintManager.java b/app/src/main/java/de/danoeh/antennapod/view/ToolbarIconTintManager.java index dcf8ff20d..37d8db03e 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/ToolbarIconTintManager.java +++ b/app/src/main/java/de/danoeh/antennapod/view/ToolbarIconTintManager.java @@ -1,7 +1,9 @@ package de.danoeh.antennapod.view; import android.content.Context; -import android.graphics.PorterDuff; +import android.graphics.PorterDuff.Mode; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; import android.view.ContextThemeWrapper; import androidx.appcompat.widget.Toolbar; import androidx.core.view.ViewCompat; @@ -33,12 +35,20 @@ public abstract class ToolbarIconTintManager implements AppBarLayout.OnOffsetCha public void updateTint() { if (isTinted) { doTint(new ContextThemeWrapper(context, R.style.Theme_AntennaPod_Dark)); - toolbar.getNavigationIcon().setColorFilter(0xffffffff, PorterDuff.Mode.SRC_ATOP); - toolbar.getOverflowIcon().setColorFilter(0xffffffff, PorterDuff.Mode.SRC_ATOP); + safeSetColorFilter(toolbar.getNavigationIcon(), new PorterDuffColorFilter(0xffffffff, Mode.SRC_ATOP)); + safeSetColorFilter(toolbar.getOverflowIcon(), new PorterDuffColorFilter(0xffffffff, Mode.SRC_ATOP)); + safeSetColorFilter(toolbar.getCollapseIcon(), new PorterDuffColorFilter(0xffffffff, Mode.SRC_ATOP)); } else { doTint(context); - toolbar.getNavigationIcon().clearColorFilter(); - toolbar.getOverflowIcon().clearColorFilter(); + safeSetColorFilter(toolbar.getNavigationIcon(), null); + safeSetColorFilter(toolbar.getOverflowIcon(), null); + safeSetColorFilter(toolbar.getCollapseIcon(), null); + } + } + + private void safeSetColorFilter(Drawable icon, PorterDuffColorFilter filter) { + if (icon != null) { + icon.setColorFilter(filter); } } From 2c5372eb31a9e9048912cfa45089902184d57364 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Wed, 14 Oct 2020 20:12:24 +0200 Subject: [PATCH 15/16] Updated translations --- core/src/main/res/values-cs/strings.xml | 95 +++++++++++++++++++-- core/src/main/res/values-eu/strings.xml | 5 ++ core/src/main/res/values-fa/strings.xml | 4 + core/src/main/res/values-pl/strings.xml | 6 +- core/src/main/res/values-pt-rBR/strings.xml | 31 +++++++ core/src/main/res/values-zh-rTW/strings.xml | 28 +++--- 6 files changed, 148 insertions(+), 21 deletions(-) diff --git a/core/src/main/res/values-cs/strings.xml b/core/src/main/res/values-cs/strings.xml index 2aa11889d..ec6f20941 100644 --- a/core/src/main/res/values-cs/strings.xml +++ b/core/src/main/res/values-cs/strings.xml @@ -40,7 +40,7 @@ Otevřít menu Zavřít menu Nastavení panelu - Řadit dle počtu + Řadit dle čítače Řadit abecedně Řadit dle data zveřejnění Řadit podle počtu poslechnutých epizod @@ -110,12 +110,21 @@ %d dnů po dokončení %d dnů po dokončení + + %d vybrána + %d vybrány + %d vybráno + %d vybráno + Načítají se další… Označit vše jako poslechnuté Všechny epizody označeny jako poslechnuté Potvrďte prosím, že chcete označit všechny vybrané epizody jako poslechnuté. Potvrďte prosím, že chcete označit všechny epizody tohoto podcastu jako poslechnuté. + Odstranit příznak „nová“ ze všech epizod + Příznak „nová“ odstraněn ze všech epizod + Potvrďte prosím, že chcete odstranit příznak „nová“ ze všech epizod. Informace o zdroji Zobrazit nastavení podcastu Informace o podcastu @@ -132,6 +141,7 @@ Sdílet URL souboru s časovou značkou Potvrďte prosím, že chcete smazat podcast „%1$s“ a VŠECHNY jeho epizody (včetně stažených epizod). Odstraňování podcastu + Aktualizovat celý podcast Výběr více položek Vybrat všechny výše Vybrat všechny níže @@ -168,10 +178,13 @@ %d epizod staženo. %d epizod staženo. + Odstranit příznak „nová“ + Příznak „nová“ odstraněn Označit jako poslechnuté Označeno jako poslechnuté Označit jako poslechnuté Označeno jako poslechnuté + Pro přeskočení na pozice musíte epizodu přehrát %d epizoda označena jako přehraná %d epizody označeny jako přehrané @@ -180,9 +193,27 @@ Označit jako neposlechnuté Označit jako nepřečtené + + %d epizoda označena jako neposlechnutá + %d epizody označeny jako neposlechnuté + %d epizod označeno jako neposlechnuté + %d epizod označeno jako neposlechnuté + Přidat do fronty Přidáno do fronty + + %d epizoda přidána do fronty + %d epizody přidány do fronty + %d epizod přidáno do fronty + %d epizod přidáno do fronty + Odebrat z fronty + + %d epizoda odebrána z fronty + %d epizody odebrány z fronty + %d epizod odebráno z fronty + %d epizod odebráno z fronty + Přidat k oblíbeným Přidáno k oblíbeným Odebrat z obíbených @@ -233,6 +264,7 @@ Kanál Soubor Nastala chyba při pokusu o stažení souboru:\u0020 + Nebyl poskytnut žádný podcast, co by mohl být zobrazen. Vyžadováno ověření Zdroj který jste vybrali vyžaduje zadání uživatelského jména a hesla Potvrdit mobilní stahování @@ -285,13 +317,17 @@ Stáhnout modul Modul není nainstalován + Pro správnou funkci proměnlivé rychlosti přehrávání je doporučeno povolit vestavěný přehrávač Sonic. Rychlosti přehrávání Povolit Sonic Žádné epizody ve frontě + Přidejte epizodu stažením nebo dlouhým dotykem a volbou „Přidat do fronty“. Tato epizoda neobsahuje žádné poznámky. Neběží žádná stahování + Můžete stáhnout epizody tohoto podcastu z obrazovky s jeho detaily. Žádné stažené epizody + Můžete stáhnout epizody tohoto podcastu z obrazovky s jeho detaily. Žádné záznamy o stahování Až proběhnou nějaká stahování, tak se záznamy o nich objeví zde. Žádná historie @@ -304,6 +340,7 @@ Epizody si můžete přidat mezi oblíbené dlouhým dotykem. Žádné kapitoly Tato epizoda nemá žádné kapitoly. + Žádené sbírky Pro přidání podcastu do sbírky se dotkněte ikonky plus níže. Úložiště @@ -317,6 +354,7 @@ Importovat/Exportovat zálohovat, obnovit Vzhled + Externí elementy Přerušení Ovládání přehrávání Vyhledávání… @@ -328,6 +366,8 @@ Při odpojení sluchátek nebo bluetooth připojení pozastavit přehrávání. Pokračovat v přehrávání po připojení sluchátek Pokračovat v přehrávání po připojení bluetooth + Tlačítko rychle vpřed přeskakuje + Stisk tlačítka rychle vpřed (FF) na připojeném zařízení Bluetooth přeskočí na další epizodu místo rychlého přetočení vpřed. Tlačítko zpět restartuje Po stlačení hardwarového tlačítka pro posun zpět místo přetočení vpřed restartovat přehrávání aktuální epizody Po přehrání položky z fronty přejít automaticky na další @@ -337,10 +377,14 @@ Chytré označování jako poslechnuté Neodstraňovat epizody při jejich přeskočení Nemazat přeskočené epizody + Nemazat epizody, které jsou mezi oblíbenými. Nemazat oblíbené epizody Přehrávání + Ovládání tlačítky sluchátek, přeskakování, fronta Síť + Četnost aktualizací, ovládání stahování, mobilní data Aktualizovat interval nebo čas v průběhu dne + Zvolte čas mezi aktualizacemi nebo čas v rámci dne, kdy proběhne automatická aktualizace podcastů Můžete nastavit interval jako třeba \"každé 2 hodiny\", nastavit specifický čas v průběhu dne jako \"7:00\" nebo úplně vypnout automatické aktualizace.\n\nMějte na paměti: Časy aktualizací nejsou přesné. Možná zaznamenáte krátká zpoždění. Vypnout Nastavit interval @@ -351,17 +395,24 @@ Sluchátka odpojena Sluchátka připojena Bluetooth připojeno + Upřednostnit streamování + Zobrazit tlačítko streamovat místo tlačítka stáhnout v seznamech. Mobilní aktualizace + Vyberte, co by mělo být povoleno přes mobilní data Obnovit podcast + Obrázky podcastů/epizod Automatické stahování + Stahování epizod Streamování Uživatelské rozhraní + Vzhled, poradí ve sbírce, uzamčená obrazovka Vybrat motiv Změnit navigační panel Upravit zobrazení položek v navigačním panelu. Nastavit pořadí sbírek Upravit pořadí vašich sbírek - Nastavit počítadlo sbírek + Nastavit čítač sbírek + Změnit informaci zobrazenou čítačem sbírek. Též ovlivňuje řazení, je-li nastaveno na „podle čítače“. Změnit vzhled AntennaPod. Automatické stahování Nastavení automatického stahování epizod. @@ -371,6 +422,9 @@ Povolit automatické stahování i pokud není baterie nabíjena Paralelní stahování Historie epizod + Celkový počet epizod stažených na zařízení. Automatické stahování se zastaví při dosažení této hodnoty. + Použít obrázek epizody + Použít obrázek přímo z epizody, pokud je k dispozici. Není-li tato možnost zaškrtnuta, tak se vžy použije obrázek podcastu. Použít systémové téma Světlý Tmavý @@ -387,9 +441,10 @@ Změní přihlašovací údaje k vašemu gpodder.net účtu. Synchronizovat ihned Synchronizovat odběr a změny stavu epizody s gpodder.net. + Synchronizovat vše ihned Synchronizovat všechny odběry a stav epizod s gpodder.net. %1$s z přístroje %2$s]]> - Zobrazovat upozornění na chyby synchronizace + Zobrazovat oznámení o chybách synchronizace Toto nastavení se netýká chyb přihlášení. Rychlosti přehrávání Přizpůsobení rychlosti je dostupné pro přehrávání zvuku různými rychlostmi @@ -398,16 +453,20 @@ Přeskočit úvod a závěr. Přeskočit posledních Přeskočit prvních + Přeskočeno posledních %d sekund + Přeskočeno prvních %d sekund Upravit informace o médiu vzhledem k rychlosti přehrávání. Zobrazené délka a pozice jsou upravené vzhledem k rychlosti přehrávání Délka času posunu vpřed + Upravit o kolik sekund se přeskočí dopředu při stisku tlačítka rychle vpřed (FF). Délka času posunu zpět + Upravit o kolik sekund se přeskočí zpět při stisku tlačítka přetočit zpět (RW). Nastavit hostname Použít přednastaveného hosta - Vysoká priorita pro upozornění + Vysoká priorita pro oznámení Toto obvykle přidá tlačítka ovládání přehrávání do zpráv upozornění Pevné ovládání přehrávání - Zachovat upozornění a ovládání na obrazovce uzamčení i při pozastaveném přehrávání. + Zachovat oznámení a ovládání na obrazovce uzamčení i při pozastaveném přehrávání. Nastavení tlačítek uzamčené obrazovky Změnit tlačítka ovládání na obrazovce uzamčení. Tlačítka přehrát/pozastavit jsou vždy zobrazena. Vybrat maximálně %1$d položek @@ -416,7 +475,9 @@ Nastavit pozadí uzamčené obrazovky na obrázek aktuální epizody. Jako vedlejší efekt zobrazí toto nastavení obrázek i v aplikacích třetích stran. Zobrazit report stahování Pokud selže stahování, vygenerovat report zobrazující detaily o chybě. - Verze Androidu nižší než 4.1 nepodporují rozšířená upozornění. + Zobrazovat hlášení automatického stahování + Zobrazovat oznámení o automaticky stažených epizodách. + Verze Androidu nižší než 4.1 nepodporují rozšířená oznámení. Pozice přidávání do fronty Přidávat epizody na: %1$s konec @@ -425,7 +486,9 @@ Vypnuto Velikost odkládací paměti obrázků Velikost diskové paměti pro obrázky. + Uživatelské fórum Nahlásit chybu + Otevřít systém pro sledování a hlášení chyb (bug tracker) Exportovat záznamy Zkopírovat do schránky Zkopírováno do schránky @@ -490,6 +553,7 @@ Databáze OPML HTML + Ukažte své sbírky přátelům Přenést sbírky do jiné podcastové aplikace Importovat vaše sbírky z jiné podcastové aplikace Přenést sbírky, poslechnuté epizody a frontu do aplikace AntennaPod na jiném zařízení @@ -582,6 +646,7 @@ Vybrat umístění dat Vyberte prosím váš výchozí datový adresář. AntennaPod vytvoří všechny potřebné podadresáře. Ke změně datového adresáře je vyžadován přístup k externímu úložišti + %1$s z %2$s zdarma Vytvořit adresář \"%1$s\"? Nový adresář vytvořen Nelze zapisovat do adresáře @@ -600,6 +665,9 @@ Pro aktivování změn nastavení bylo třeba restartovat aplikaci AntennaPod. Odebírat + Přidává se do sbírky… + Spustit ukázku + Zastavit ukázku Posunout zpět Posunout vpřed @@ -613,6 +681,9 @@ Epizoda je označená jako oblíbená Tahem změnit pozici této položky Načíst další stranu + Přehodit stránku + Pozice: %1$s + Vykonat Ověření Změnit uživatelské jméno a heslo pro tento podcast a jeho epizody. @@ -638,6 +709,7 @@ Prohledávat gpodder.net Objevit více » + Vyhledávání poskytuje %1$s Filtr Vše @@ -716,11 +788,22 @@ Přijímač zaznamenal závažnou chybu Chyba přehrávání médií. Přeskakuji... + Je vyžadována činnost z vaší strany + Zobrazuje se, pokud je požadována činnost z vaší strany. Například je-li potřeba zadat heslo. Stahuji + Zobrazuje se v průběhu stahování. + Přehrává se + Umožňuje ovládat přehrávání. Toto je to hlavní oznámení, které uvidité při přehrávání podcastu. + Chyby Zobrazuje se, když se něco nepovedlo. Například pokud selhalo stahování anebo synchronizace gpodder. + Automatické stahování + Zobrazuje se po automatickém stažení epizod. + Nastavení widgetu + Vytvořit widget Průhlednost + Nastavení úspěšně aktualizováno Vypadá to, že častěji streamujete než stahujete. Chcete zobrazovat tlačítko streamovat v seznamu epizod? Vypadá to, že častěji stahujete než streamujete. Chcete zobrazovat tlačítko stáhnout v seznamu epizod? diff --git a/core/src/main/res/values-eu/strings.xml b/core/src/main/res/values-eu/strings.xml index 6c0973739..e4b4fe4b6 100644 --- a/core/src/main/res/values-eu/strings.xml +++ b/core/src/main/res/values-eu/strings.xml @@ -509,8 +509,11 @@ Honi buruz AntennaPod bertsioa + Laguntzaileak + Denek lagun dezakete gure foroan kodea, itzulpenak edo erabiltzaileei laguntza emanez. Garatzaileak Itzultzaileak + Esker bereziak Pribatutasun politika Baimenak AntennaPod-ek beste software ezin hobeak erabiltzen ditu @@ -554,6 +557,8 @@ Aukeratu inportatzeko fitxategia Inportazio arrakastatsua.\n\nSakatu OK, AntennaPod berrabiarazteko Datu-basea AntennaPod-en bertsio berriago batekin esportatu zen. Uneko aplikazioak ez daki nola inportatu. + Gogokoak esportatu + Gorde diren gogokoak esportatu artxibatzeko Ezarri tenporizadore bat Desgaitu tenporizadorea diff --git a/core/src/main/res/values-fa/strings.xml b/core/src/main/res/values-fa/strings.xml index a418d834c..0ac3b684d 100644 --- a/core/src/main/res/values-fa/strings.xml +++ b/core/src/main/res/values-fa/strings.xml @@ -85,6 +85,7 @@ تنظیمات جدید بارگیری خودکار به طور خودکار بر قسمت‌های جدید اعمال خواهد شد.\nآیا می‌خواهید بر قسمت‌هایی قبلاً منتشر شده هم اعمال شود؟ حذف خودکار قسمت کم کردن صدا + برای قسمت‌های این خوراک، صدا کم شود: %1$s خاموش سبک سنگین @@ -204,6 +205,7 @@ غیر فعال کردن بارگیری خودکار تنظیم مجدد موقعیت پخش مورد حذف شده است + چیزی انتخاب نشده است موفقیت‌آمیز بارگیری معوق @@ -324,6 +326,8 @@ حذف خودکار قسمت، درون‌ریزی، برون‌ریزی پروژه صف + هم‌گام‌سازی + با کمک gpodder.net با دستگاه‌های دیگر هم‌گام کنید اتوماسیون جزئیات وارد/صادر کرد diff --git a/core/src/main/res/values-pl/strings.xml b/core/src/main/res/values-pl/strings.xml index 1043b58d8..f5fa5e310 100644 --- a/core/src/main/res/values-pl/strings.xml +++ b/core/src/main/res/values-pl/strings.xml @@ -323,7 +323,7 @@ Brak odcinków w kolejce Dodaj odcinek, pobierając go, lub przytrzymaj dłużej na odcinku i wybierz \"Dodaj do kolejki\". - Ten epizod nie ma notatek. + Ten odcinek nie ma notatek. Brak aktywnych pobierań Możesz pobrać odcinki z ekranu informacji o podcaście Brak pobranych odcinków @@ -528,8 +528,10 @@ O... Wersja AntennaPod + Kontrybutorzy Twórcy Tłumacze + Specjalne podziękowania Polityka prywatności Licencje AntennaPod używa różnego świetnego oprogramowania/bibliotek @@ -573,6 +575,8 @@ Wybierz plik do Importowania Import zakończony powodzeniem.\n\nNaciśnij OK aby zrestartować AntennaPod Baza danych została eksportowana z nowszej wersji AntennaPod. Twoja wersja nie potrafi obsłużyć tego pliku. + Eksport ulubionych + Ulubione wyeksportowano do pliku Ustaw czas do wyłączenia Wyłącz wyłącznik czasowy diff --git a/core/src/main/res/values-pt-rBR/strings.xml b/core/src/main/res/values-pt-rBR/strings.xml index 1d36dc272..59e3af044 100644 --- a/core/src/main/res/values-pt-rBR/strings.xml +++ b/core/src/main/res/values-pt-rBR/strings.xml @@ -384,6 +384,7 @@ Download de episódios Streaming Interface com usuário + Aparência, pedidos de assinatura, tela de bloqueio Selecionar tema Configurar itens da Gaveta de Navegação Escolher quais itens irão aparecer na gaveta de navegação. @@ -400,6 +401,10 @@ Permitir download automático enquanto a bateria não está carregando Downloads paralelos Cache de episódios + Número total de episódios baixados em cache no dispositivo. O download automático será suspenso se esse número for atingido. + Usar capa do episódio + Use a capa específica do episódio sempre que disponível. Se desmarcado, o aplicativo sempre usará a imagem da capa do podcast. + Usar tema do sistema Claro Escuro Preto (preparado para AMOLED) @@ -415,12 +420,22 @@ Alterar informações de login da sua conta gpodder.net Sincronizar agora Sincronizar as alterações de estado da inscrição e de episódios com o gpodder.net. + Forçar sincronização completa Sincronizar os estados das inscrições e episódios com o gpodder.net. %1$s com o dispositivo %2$s]]> Exibir notificações de erros de sincronismo Essa configuração não se aplica a erros de autenticação. Velocidades de Reprodução Personalize as velocidades variáveis de reprodução de áudio. + A velocidade a ser usada ao iniciar a reprodução de áudio para episódios neste podcast + Salto automático + Pule as introduções e os créditos finais. + Pular últimos + Pular primeiros + Pulou últimos %d segundos + Pulou os primeiros %d segundos + Ajuste as informações da mídia para a velocidade de reprodução + A posição exibida e a duração são adaptadas à velocidade de reprodução Tempo de avanço rápido Personalize os segundos para avançar quando o botão avanço rápido for clicado Tempo de retroceder @@ -439,15 +454,29 @@ Configurar o plano de fundo da tela de bloqueio para a imagem do episódio atual. Como um efeito colateral, também ira mostrar imagens de aplicativos de terceiros. Mostrar Relatório de Downloads Se os downloads falharem, gerar um relatório que mostra os detalhes da falha. + Mostrar relatório de downloads automáticos + Mostra uma notificação para episódios baixados automaticamente. Versões do Android inferiores a 4.1 não suportam notificações expansíveis + Local da fila + Adicionar episódios para: %1$s + Final + Início + Depois do episódio atual Desabilitado Tamanho da Imagem em Cache Tamanho do cache de disco para imagens. + Fórum de usuários + Reportar um bug + Abrir registro de bugs + Exportar logs + Copiar para clipboard + Copiado para clipboard Experimental Selecione qual reprodutor de mídia usar para reproduzir os arquivos Valor atual: %1$s Proxy Configurar um proxy da rede + Perguntas mais frequentes Nenhum navegador web encontrado. Suporte ao Chromecast Habilitar o suporte para reprodução remota de mídia em dispositivos Cast (como Chromecast, Caixa de som ou Android TV) @@ -455,6 +484,8 @@ Enfileirar os baixados Adicionar episódios baixados à fila Reprodutor próprio do Android + Alterar para ExoPlayer + Alterado para ExoPlayer Pular silêncio no áudio Após fechar vídeo Comportamento ao parar a reprodução de vídeo diff --git a/core/src/main/res/values-zh-rTW/strings.xml b/core/src/main/res/values-zh-rTW/strings.xml index 9d809f88d..0b3f88797 100644 --- a/core/src/main/res/values-zh-rTW/strings.xml +++ b/core/src/main/res/values-zh-rTW/strings.xml @@ -40,8 +40,8 @@ 打開選單 關閉選單 側邊選單設定 - 按數量排序 - 按字母表排序 + 按計數器排序 + 按字母排序 按發布日期排序 按已播放的集數排序 新增及未播放集數 @@ -319,7 +319,7 @@ 同步 利用 gpodder.net 與其他裝置同步 自動化 - 詳情 + 細節 匯入/匯出 備份, 還原, backup, restore, export, import, 匯出, 匯入 外觀 @@ -351,10 +351,10 @@ 播放 耳機線控、快轉時間、待播清單 網路 - 更新週期、下載控制、行動數據 + 更新週期、下載控制、行動網路 更新週期 設定 Podcast 節目清單的更新週期 - 您可以設定「每 2 小時」這類的週期或指定「每天早上 7 點」這種每日定時,也也可以停用 自動更新。\n\n請注意:這裡的時間並非十分精準,可能會有些許延遲。 + 您可以設定「每 2 小時」這類的週期或指定「每天早上 7 點」這種每日定時,也可以停用 自動更新。\n\n請注意:這裡的時間並非十分精準,可能會有些許延遲。 停用 設定週期 設定每日定時 @@ -366,20 +366,20 @@ 連上藍牙時繼續播放 偏好串流 在清單中顯示串流播放按鈕取代下載鈕 - 行動網路更新 + 允許以行動網路… 選擇以行動網路連線時可以做的事 更新 Podcast - 封面 + 更新封面圖 自動下載 下載單集 串流播放 使用者介面 - 外觀、訂閱順序、鎖定畫面 + 外觀、訂閱排序、鎖定畫面 選擇主題 設定側邊選單 調整側邊選單裡要顯示的項目 - 設定訂閱順序 - 更改您的訂閱順序 + 設定訂閱排序方式 + 更改您訂閱頻道的排序方式 設定訂閱計數器 調整訂閱計數器中要顯示的東西,同時也會在排序方式設定為「計數」時影響排序。 更改 AntennaPod 的外觀 @@ -394,12 +394,12 @@ 在本機中可以暫存的集數,若達上限則將停止自動下載。 使用單集的封面圖 在單集有專屬封面的情況下使用該封面圖。如果取消,則一律使用 Podcast 的封面圖。 - 使用系統主題 + 依據系統設定 淡色 深色 黑色 (適用 AMOLED 螢幕) 無限 - 時數 + 小時 小時 手動 登入 @@ -490,7 +490,7 @@ 離開前需要確認 確定關閉 AntennaPod? 重按返回鍵以離開 - 前往頁面... + 前往頁面… 選擇頁面 刪除時同步自待播清單中移除 刪除某單集後,也自動將其從待播清單中移除 @@ -693,7 +693,7 @@ 標題 (A \u2192 Z) 標題 (Z \u2192 A) 日期 (新 \u2192 舊) - 標題 (舊 \u2192 新) + 日期 (舊 \u2192 新) 單集時間 (短 \u2192 長) 單集時間 (長 \u2192 短) A \u2192 Z From ee4b2c05545b81ef154a5802479793e8eaf29af1 Mon Sep 17 00:00:00 2001 From: ByteHamster Date: Wed, 14 Oct 2020 20:14:11 +0200 Subject: [PATCH 16/16] Bumped version to 2.0.2 --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 903323edc..d713ea8fd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,8 +14,8 @@ android { // "1.2.3-SNAPSHOT" -> 1020300 // "1.2.3-RC4" -> 1020304 // "1.2.3" -> 1020395 - versionCode 2000195 - versionName "2.0.1" + versionCode 2000295 + versionName "2.0.2" multiDexEnabled false vectorDrawables.useSupportLibrary true