diff --git a/app/src/debug/java/org/schabi/newpipe/DebugApp.kt b/app/src/debug/java/org/schabi/newpipe/DebugApp.kt
index 70b9ec2807b..24a0bfa053b 100644
--- a/app/src/debug/java/org/schabi/newpipe/DebugApp.kt
+++ b/app/src/debug/java/org/schabi/newpipe/DebugApp.kt
@@ -26,7 +26,8 @@ class DebugApp : App() {
override fun getDownloader(): Downloader {
val downloader = DownloaderImpl.init(
OkHttpClient.Builder()
- .addNetworkInterceptor(StethoInterceptor())
+ .addNetworkInterceptor(StethoInterceptor()),
+ this
)
setCookiesToDownloader(downloader)
return downloader
diff --git a/app/src/main/java/org/schabi/newpipe/App.kt b/app/src/main/java/org/schabi/newpipe/App.kt
index 3ca259528ac..c31cdbcef40 100644
--- a/app/src/main/java/org/schabi/newpipe/App.kt
+++ b/app/src/main/java/org/schabi/newpipe/App.kt
@@ -136,7 +136,7 @@ open class App :
}.build()
protected open fun getDownloader(): Downloader {
- val downloader = DownloaderImpl.init(null)
+ val downloader = DownloaderImpl.init(null, this)
setCookiesToDownloader(downloader)
return downloader
}
diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java
index 74a2cab5176..761f79a4ebc 100644
--- a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java
+++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java
@@ -11,6 +11,7 @@
import org.schabi.newpipe.extractor.downloader.Request;
import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
+import org.schabi.newpipe.util.ProxyManager;
import org.schabi.newpipe.util.InfoCache;
import java.io.IOException;
@@ -57,11 +58,18 @@ public OkHttpClient getClient() {
* It's recommended to call exactly once in the entire lifetime of the application.
*
* @param builder if null, default builder will be used
+ * @param context the context to use
* @return a new instance of {@link DownloaderImpl}
*/
- public static DownloaderImpl init(@Nullable final OkHttpClient.Builder builder) {
- instance = new DownloaderImpl(
- builder != null ? builder : new OkHttpClient.Builder());
+ public static DownloaderImpl init(@Nullable final OkHttpClient.Builder builder,
+ final Context context) {
+ final OkHttpClient.Builder builderToUse = builder != null ? builder
+ : new OkHttpClient.Builder();
+ final ProxyManager proxyManager = new ProxyManager(context);
+ if (proxyManager.isProxyEnabled()) {
+ builderToUse.proxy(proxyManager.getProxy());
+ }
+ instance = new DownloaderImpl(builderToUse);
return instance;
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/datasource/YoutubeHttpDataSource.java b/app/src/main/java/org/schabi/newpipe/player/datasource/YoutubeHttpDataSource.java
index 4cdb649a350..44c0f845889 100644
--- a/app/src/main/java/org/schabi/newpipe/player/datasource/YoutubeHttpDataSource.java
+++ b/app/src/main/java/org/schabi/newpipe/player/datasource/YoutubeHttpDataSource.java
@@ -20,6 +20,7 @@
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isWebEmbeddedPlayerStreamingUrl;
import static java.lang.Math.min;
+import android.content.Context;
import android.net.Uri;
import androidx.annotation.NonNull;
@@ -45,6 +46,7 @@
import com.google.common.net.HttpHeaders;
import org.schabi.newpipe.DownloaderImpl;
+import org.schabi.newpipe.util.ProxyManager;
import java.io.IOException;
import java.io.InputStream;
@@ -54,6 +56,7 @@
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
+import java.net.Proxy;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
@@ -84,6 +87,7 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
*/
public static final class Factory implements HttpDataSource.Factory {
+ private final Context context;
private final RequestProperties defaultRequestProperties;
@Nullable
@@ -100,8 +104,10 @@ public static final class Factory implements HttpDataSource.Factory {
/**
* Creates an instance.
+ * @param context the context to use
*/
- public Factory() {
+ public Factory(final Context context) {
+ this.context = context;
defaultRequestProperties = new RequestProperties();
connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MILLIS;
readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS;
@@ -220,7 +226,6 @@ public Factory setContentTypePredicate(
*
The default is {@code null}.
*
*
See {@link DataSource#addTransferListener(TransferListener)}.
- *
* @param transferListenerToUse The listener that will be used.
* @return This factory.
*/
@@ -247,6 +252,7 @@ public Factory setKeepPostFor302Redirects(final boolean keepPostFor302RedirectsV
@Override
public YoutubeHttpDataSource createDataSource() {
final YoutubeHttpDataSource dataSource = new YoutubeHttpDataSource(
+ context,
connectTimeoutMs,
readTimeoutMs,
allowCrossProtocolRedirects,
@@ -272,6 +278,7 @@ public YoutubeHttpDataSource createDataSource() {
private static final String YOUTUBE_BASE_URL = "https://www.youtube.com";
private static final byte[] POST_BODY = new byte[] {0x78, 0};
+ private final Context context;
private final boolean allowCrossProtocolRedirects;
private final boolean rangeParameterEnabled;
private final boolean rnParameterEnabled;
@@ -299,7 +306,8 @@ public YoutubeHttpDataSource createDataSource() {
private long requestNumber;
@SuppressWarnings("checkstyle:ParameterNumber")
- private YoutubeHttpDataSource(final int connectTimeoutMillis,
+ private YoutubeHttpDataSource(final Context context,
+ final int connectTimeoutMillis,
final int readTimeoutMillis,
final boolean allowCrossProtocolRedirects,
final boolean rangeParameterEnabled,
@@ -308,6 +316,7 @@ private YoutubeHttpDataSource(final int connectTimeoutMillis,
@Nullable final Predicate contentTypePredicate,
final boolean keepPostFor302Redirects) {
super(true);
+ this.context = context;
this.connectTimeoutMillis = connectTimeoutMillis;
this.readTimeoutMillis = readTimeoutMillis;
this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
@@ -708,6 +717,11 @@ private HttpURLConnection makeConnection(
* @return an {@link HttpURLConnection} created with the {@code url}
*/
private HttpURLConnection openConnection(@NonNull final URL url) throws IOException {
+ final ProxyManager proxyManager = new ProxyManager(context);
+ final Proxy proxy = proxyManager.getProxy();
+ if (proxy != null) {
+ return (HttpURLConnection) url.openConnection(proxy);
+ }
return (HttpURLConnection) url.openConnection();
}
@@ -1006,4 +1020,3 @@ public int hashCode() {
}
}
}
-
diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java
index 506b643fed4..dd1b2f0281b 100644
--- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java
+++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java
@@ -18,12 +18,10 @@
import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultDataSource;
-import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
import com.google.android.exoplayer2.upstream.TransferListener;
import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
import com.google.android.exoplayer2.upstream.cache.SimpleCache;
-import org.schabi.newpipe.DownloaderImpl;
import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeOtfDashManifestCreator;
import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubePostLiveStreamDvrDashManifestCreator;
import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeProgressiveDashManifestCreator;
@@ -86,20 +84,22 @@ public PlayerDataSource(final Context context,
// make sure the static cache was created: needed by CacheFactories below
instantiateCacheIfNeeded(context);
- // generic data source factories use DefaultHttpDataSource.Factory
+ // generic data source factories now use YoutubeHttpDataSource.Factory to support proxies
+ final YoutubeHttpDataSource.Factory youtubeHttpDataSourceFactory =
+ getYoutubeHttpDataSourceFactory(context, false, false);
cachelessDataSourceFactory = new DefaultDataSource.Factory(context,
- new DefaultHttpDataSource.Factory().setUserAgent(DownloaderImpl.USER_AGENT))
+ youtubeHttpDataSourceFactory)
.setTransferListener(transferListener);
cacheDataSourceFactory = new CacheFactory(context, transferListener, cache,
- new DefaultHttpDataSource.Factory().setUserAgent(DownloaderImpl.USER_AGENT));
+ youtubeHttpDataSourceFactory);
// YouTube-specific data source factories use getYoutubeHttpDataSourceFactory()
ytHlsCacheDataSourceFactory = new CacheFactory(context, transferListener, cache,
- getYoutubeHttpDataSourceFactory(false, false));
+ getYoutubeHttpDataSourceFactory(context, false, false));
ytDashCacheDataSourceFactory = new CacheFactory(context, transferListener, cache,
- getYoutubeHttpDataSourceFactory(true, true));
+ getYoutubeHttpDataSourceFactory(context, true, true));
ytProgressiveDashCacheDataSourceFactory = new CacheFactory(context, transferListener, cache,
- getYoutubeHttpDataSourceFactory(false, true));
+ getYoutubeHttpDataSourceFactory(context, false, true));
// set the maximum size to manifest creators
YoutubeProgressiveDashManifestCreator.getCache().setMaximumSize(MAX_MANIFEST_CACHE_SIZE);
@@ -198,9 +198,10 @@ private static DefaultDashChunkSource.Factory getDefaultDashChunkSourceFactory(
}
private static YoutubeHttpDataSource.Factory getYoutubeHttpDataSourceFactory(
+ final Context context,
final boolean rangeParameterEnabled,
final boolean rnParameterEnabled) {
- return new YoutubeHttpDataSource.Factory()
+ return new YoutubeHttpDataSource.Factory(context)
.setRangeParameterEnabled(rangeParameterEnabled)
.setRnParameterEnabled(rnParameterEnabled);
}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/ProxySettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ProxySettingsFragment.java
new file mode 100644
index 00000000000..8bf99d6f15d
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/settings/ProxySettingsFragment.java
@@ -0,0 +1,70 @@
+package org.schabi.newpipe.settings;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import org.schabi.newpipe.R;
+import org.schabi.newpipe.util.NavigationHelper;
+
+/**
+ * A fragment that displays proxy settings.
+ */
+public class ProxySettingsFragment extends BasePreferenceFragment {
+
+ private boolean preferencesChanged = false;
+ private SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener;
+
+ @Override
+ public void onCreatePreferences(@Nullable final Bundle savedInstanceState,
+ @Nullable final String rootKey) {
+ //addPreferencesFromResource(R.xml.proxy_settings);
+ addPreferencesFromResourceRegistry();
+ preferenceChangeListener = (sharedPreferences, key) -> {
+ preferencesChanged = true;
+ };
+ getPreferenceScreen().getSharedPreferences()
+ .registerOnSharedPreferenceChangeListener(preferenceChangeListener);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (preferencesChanged && getActivity() != null && !getActivity().isFinishing()) {
+ showRestartDialog();
+ }
+ }
+
+ private void showRestartDialog() {
+ // Show Alert Dialogue
+ final Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ builder.setMessage(R.string.restart_app_message);
+ builder.setTitle(R.string.restart_app_title);
+ builder.setCancelable(true);
+ builder.setPositiveButton(R.string.ok, (dialogInterface, i) -> {
+ // Restarts the app
+ if (activity == null) {
+ return;
+ }
+ NavigationHelper.restartApp(activity);
+ });
+ builder.setNegativeButton(R.string.cancel, (dialogInterface, i) -> {
+ });
+ final android.app.AlertDialog alertDialog = builder.create();
+ alertDialog.show();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (preferenceChangeListener != null && getPreferenceScreen() != null) {
+ getPreferenceScreen().getSharedPreferences()
+ .unregisterOnSharedPreferenceChangeListener(preferenceChangeListener);
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java
index 06e0a7c1eae..43ccdb2a4c4 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java
@@ -42,6 +42,7 @@ private SettingsResourceRegistry() {
add(VideoAudioSettingsFragment.class, R.xml.video_audio_settings);
add(ExoPlayerSettingsFragment.class, R.xml.exoplayer_settings);
add(BackupRestoreSettingsFragment.class, R.xml.backup_restore_settings);
+ add(ProxySettingsFragment.class, R.xml.proxy_settings);
}
private SettingRegistryEntry add(
diff --git a/app/src/main/java/org/schabi/newpipe/util/ProxyManager.java b/app/src/main/java/org/schabi/newpipe/util/ProxyManager.java
new file mode 100644
index 00000000000..cec665ba7ab
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/util/ProxyManager.java
@@ -0,0 +1,79 @@
+package org.schabi.newpipe.util;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import androidx.preference.PreferenceManager;
+
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+
+/**
+ * A class to manage proxy settings.
+ */
+public class ProxyManager {
+
+ private final SharedPreferences sharedPreferences;
+
+ /**
+ * Creates a new ProxyManager.
+ * @param context the context to use
+ */
+ public ProxyManager(final Context context) {
+ this.sharedPreferences =
+ PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
+ }
+
+ /**
+ * Checks if the proxy is enabled.
+ * @return true if the proxy is enabled, false otherwise
+ */
+ public boolean isProxyEnabled() {
+ return sharedPreferences.getBoolean("use_proxy", false);
+ }
+
+ /**
+ * Gets the proxy host.
+ * @return the proxy host
+ */
+ public String getProxyHost() {
+ return sharedPreferences.getString("proxy_host", "127.0.0.1");
+ }
+
+ /**
+ * Gets the proxy port.
+ * @return the proxy port
+ */
+ public int getProxyPort() {
+ final String portString = sharedPreferences.getString("proxy_port", "1080");
+ try {
+ return Integer.parseInt(portString);
+ } catch (final NumberFormatException e) {
+ return 1080;
+ }
+ }
+
+ /**
+ * Gets the proxy type.
+ * @return the proxy type
+ */
+ public Proxy.Type getProxyType() {
+ final String type = sharedPreferences.getString("proxy_type", "SOCKS");
+ if ("SOCKS".equals(type)) {
+ return Proxy.Type.SOCKS;
+ } else {
+ return Proxy.Type.HTTP;
+ }
+ }
+
+ /**
+ * Gets the proxy.
+ * @return the proxy, or null if the proxy is not enabled
+ */
+ public Proxy getProxy() {
+ if (!isProxyEnabled()) {
+ return null;
+ }
+ return new Proxy(getProxyType(), new InetSocketAddress(getProxyHost(), getProxyPort()));
+ }
+}
diff --git a/app/src/main/java/us/shandian/giga/get/DownloadMission.java b/app/src/main/java/us/shandian/giga/get/DownloadMission.java
index 54340ce5dca..77cfb0bab98 100644
--- a/app/src/main/java/us/shandian/giga/get/DownloadMission.java
+++ b/app/src/main/java/us/shandian/giga/get/DownloadMission.java
@@ -1,5 +1,6 @@
package us.shandian.giga.get;
+import android.content.Context;
import android.os.Handler;
import android.system.ErrnoException;
import android.system.OsConstants;
@@ -17,6 +18,7 @@
import java.io.Serializable;
import java.net.ConnectException;
import java.net.HttpURLConnection;
+import java.net.Proxy;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
@@ -25,6 +27,7 @@
import javax.net.ssl.SSLException;
+import org.schabi.newpipe.util.ProxyManager;
import org.schabi.newpipe.streams.io.StoredFileHelper;
import us.shandian.giga.postprocessing.Postprocessing;
import us.shandian.giga.service.DownloadManagerService;
@@ -34,7 +37,7 @@
public class DownloadMission extends Mission {
private static final long serialVersionUID = 6L;// last bump: 07 october 2019
-
+ private final Context context;
static final int BUFFER_SIZE = 64 * 1024;
static final int BLOCK_SIZE = 512 * 1024;
@@ -153,9 +156,10 @@ public class DownloadMission extends Mission {
public transient Thread[] threads = new Thread[0];
public transient Thread init = null;
- public DownloadMission(String[] urls, StoredFileHelper storage, char kind, Postprocessing psInstance) {
+ public DownloadMission(final Context context, String[] urls, StoredFileHelper storage, char kind, Postprocessing psInstance) {
if (Objects.requireNonNull(urls).length < 1)
throw new IllegalArgumentException("urls array is empty");
+ this.context = context;
this.urls = urls;
this.kind = kind;
this.offsets = new long[urls.length];
@@ -164,6 +168,7 @@ public DownloadMission(String[] urls, StoredFileHelper storage, char kind, Postp
this.storage = storage;
this.psAlgorithm = psInstance;
+
if (DEBUG && psInstance == null && urls.length > 1) {
Log.w(TAG, "mission created with multiple urls ¿missing post-processing algorithm?");
}
@@ -219,7 +224,14 @@ HttpURLConnection openConnection(boolean headRequest, long rangeStart, long rang
}
HttpURLConnection openConnection(String url, boolean headRequest, long rangeStart, long rangeEnd) throws IOException {
- HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
+ final ProxyManager proxyManager = new ProxyManager(context);
+ final Proxy proxy = proxyManager.getProxy();
+ final HttpURLConnection conn;
+ if (proxy != null) {
+ conn = (HttpURLConnection) new URL(url).openConnection(proxy);
+ } else {
+ conn = (HttpURLConnection) new URL(url).openConnection();
+ }
conn.setInstanceFollowRedirects(true);
conn.setRequestProperty("User-Agent", DownloaderImpl.USER_AGENT);
conn.setRequestProperty("Accept", "*/*");
diff --git a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java
index 76da18b2df0..65c8b623a9a 100755
--- a/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java
+++ b/app/src/main/java/us/shandian/giga/service/DownloadManagerService.java
@@ -408,7 +408,7 @@ private void startMission(Intent intent) {
else
ps = Postprocessing.getAlgorithm(psName, psArgs, streamInfo);
- final DownloadMission mission = new DownloadMission(urls, storage, kind, ps);
+ final DownloadMission mission = new DownloadMission(this, urls, storage, kind, ps);
mission.threadCount = threads;
mission.source = streamInfo.getUrl();
mission.nearLength = nearLength;
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 5bacb22ef7c..21e6923862c 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -34,6 +34,14 @@
Внешний видеоплеер
Внешний аудиоплеер
Воспроизведение в фоновом режиме
+ Настройки прокси
+ Использовать прокси
+ Перенаправлять трафик через прокси
+ Хост прокси
+ Имя хоста или IP-адрес прокси
+ Порт прокси
+ Номер порта прокси
+ Введите номер порта прокси
Тема
Тёмная
Светлая
@@ -646,7 +654,7 @@
Миниатюра над полосой прокрутки
Автору видео понравилось это
Пометить проигранным
- Серверные предложения поиска
+ Серверные предложения поиска
Локальные предложения поиска
- Удалена %1$s загрузка
@@ -866,6 +874,8 @@
Во время воспроизведения получена ошибка HTTP 403 от сервера, вероятно, вызванная блокировкой IP-адреса или проблемами деобфускации URL-адреса потоковой передачи
%1$s отказался предоставить данные, запросив логин для подтверждения, что запросчик не бот.\n\nВозможно, ваш IP-адрес временно заблокирован %1$s. Вы можете подождать некоторое время или переключиться на другой IP-адрес (например, включив/выключив VPN или переключившись с Wi-Fi на мобильный интернет).
Этот контент недоступен для выбранной страны контента.\n\nИзмените свой выбор в разделе «Настройки > Контент > Страна контента по умолчанию».
+ Перезапуск приложения
+ Настройки прокси изменены. Для применения изменений требуется перезапуск приложения.
В августе 2025 года, Google анонсировала, что с сентября 2026 года, установка приложений потребует верификации разработчика для всех Android приложений на сертифицированных устройствах, включая те, которые были установлены не через Play Store. Поскольку разработчики NewPipe не согласны с этим требованием, NewPipe перестанет работать на сертифицированных Android устройствах после этой даты.
Подробнее
Решение
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3ec84bfffbb..d3f23b7b8c4 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -159,6 +159,23 @@
Player notification
Configure current playing stream notification
Backup and restore
+ Proxy Settings
+ Use proxy
+ Redirect traffic through a proxy
+ Proxy host
+ Hostname or IP address of the proxy
+ Proxy port
+ Port number of the proxy
+ Enter the proxy port number
+ Proxy type
+
+ - HTTP
+ - SOCKS
+
+
+ - HTTP
+ - SOCKS
+
Playing in background
Playing in popup mode
Content
@@ -897,6 +914,8 @@
HTTP error 403 received from server while playing, likely caused by an IP ban or streaming URL deobfuscation issues
%1$s refused to provide data, asking for a login to confirm the requester is not a bot.\n\nYour IP might have been temporarily banned by %1$s, you can wait some time or switch to a different IP (for example by turning on/off a VPN, or by switching from WiFi to mobile data).\n\nPlease see this FAQ entry for more information.
This content is not available for the currently selected content country.\n\nChange your selection from \"Settings > Content > Default content country\".
+ Restart application
+ The proxy settings have been changed. A restart of the application is required for the changes to take effect.
In August 2025, Google announced that as of September 2026, installing apps will require developer verification for all Android apps on certified devices, including those installed outside of the Play Store. Since the developers of NewPipe do not agree to this requirement, NewPipe will no longer work on certified Android devices after that time.
Details
Solution
diff --git a/app/src/main/res/xml/main_settings.xml b/app/src/main/res/xml/main_settings.xml
index 5f96989f979..bdb02355fd8 100644
--- a/app/src/main/res/xml/main_settings.xml
+++ b/app/src/main/res/xml/main_settings.xml
@@ -16,6 +16,12 @@
android:title="@string/settings_category_downloads_title"
app:iconSpaceReserved="false" />
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file