diff --git a/app/src/debug/java/org/schabi/newpipe/DebugApp.kt b/app/src/debug/java/org/schabi/newpipe/DebugApp.kt
index 70b9ec280..24a0bfa05 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.java b/app/src/main/java/org/schabi/newpipe/App.java
index cf41aad46..92cb0f2a0 100644
--- a/app/src/main/java/org/schabi/newpipe/App.java
+++ b/app/src/main/java/org/schabi/newpipe/App.java
@@ -141,7 +141,7 @@ public class App extends Application {
}
protected Downloader getDownloader() {
- final DownloaderImpl downloader = DownloaderImpl.init(null);
+ final DownloaderImpl 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 041e91396..59ab0b61d 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.Downloader;
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;
@@ -52,11 +53,18 @@ public final class DownloaderImpl extends Downloader {
* 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 4cdb649a3..44c0f8458 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
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.collect.Sets;
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.lang.reflect.Method;
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 final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
/**
* 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 final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
*
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 final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
@Override
public YoutubeHttpDataSource createDataSource() {
final YoutubeHttpDataSource dataSource = new YoutubeHttpDataSource(
+ context,
connectTimeoutMs,
readTimeoutMs,
allowCrossProtocolRedirects,
@@ -272,6 +278,7 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
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 final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
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 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
@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 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
* @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 final class YoutubeHttpDataSource extends BaseDataSource implements HttpD
}
}
}
-
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 506b643fe..dd1b2f028 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.DefaultSsChunkSource
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 class PlayerDataSource {
// 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 @@ public class PlayerDataSource {
}
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 000000000..8bf99d6f1
--- /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 06e0a7c1e..43ccdb2a4 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 @@ public final class 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 000000000..cec665ba7
--- /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/org/schabi/newpipe/util/image/PicassoHelper.java b/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java
index 4b116bdf9..ef513064d 100644
--- a/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/image/PicassoHelper.java
@@ -23,6 +23,7 @@ import com.squareup.picasso.Transformation;
import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.Image;
+import org.schabi.newpipe.util.ProxyManager;
import java.io.File;
import java.io.IOException;
@@ -49,12 +50,17 @@ public final class PicassoHelper {
public static void init(final Context context) {
picassoCache = new LruCache(10 * 1024 * 1024);
- picassoDownloaderClient = new OkHttpClient.Builder()
+ final ProxyManager proxyManager = new ProxyManager(context);
+ final OkHttpClient.Builder builder = new OkHttpClient.Builder()
.cache(new okhttp3.Cache(new File(context.getExternalCacheDir(), "picasso"),
50L * 1024L * 1024L))
// this should already be the default timeout in OkHttp3, but just to be sure...
- .callTimeout(15, TimeUnit.SECONDS)
- .build();
+ .callTimeout(15, TimeUnit.SECONDS);
+
+ if (proxyManager.isProxyEnabled()) {
+ builder.proxy(proxyManager.getProxy());
+ }
+ picassoDownloaderClient = builder.build();
picassoInstance = new Picasso.Builder(context)
.memoryCache(picassoCache) // memory cache
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 54340ce5d..77cfb0bab 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.InterruptedIOException;
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 java.util.Objects;
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 @@ import static org.schabi.newpipe.BuildConfig.DEBUG;
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 class DownloadMission extends Mission {
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 @@ public class DownloadMission extends Mission {
}
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 76da18b2d..65c8b623a 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 @@ public class DownloadManagerService extends Service {
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 fc32c0da3..bc21d9120 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -34,6 +34,14 @@
Внешний видеоплеер
Внешний аудиоплеер
Воспроизведение в фоновом режиме
+ Настройки прокси
+ Использовать прокси
+ Перенаправлять трафик через прокси
+ Хост прокси
+ Имя хоста или IP-адрес прокси
+ Порт прокси
+ Номер порта прокси
+ Введите номер порта прокси
Тема
Тёмная
Светлая
@@ -868,4 +876,6 @@
Во время воспроизведения получена ошибка HTTP 403 от сервера, вероятно, вызванная блокировкой IP-адреса или проблемами деобфускации URL-адреса потоковой передачи
%1$s отказался предоставить данные, запросив логин для подтверждения, что запросчик не бот.\n\nВозможно, ваш IP-адрес временно заблокирован %1$s. Вы можете подождать некоторое время или переключиться на другой IP-адрес (например, включив/выключив VPN или переключившись с Wi-Fi на мобильный интернет).
Этот контент недоступен для выбранной страны контента.\n\nИзмените свой выбор в разделе «Настройки > Контент > Страна контента по умолчанию».
+ Перезапуск приложения
+ Настройки прокси изменены. Для применения изменений требуется перезапуск приложения.
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9095fe927..4df1e1497 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
@@ -882,4 +899,6 @@
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).
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.
diff --git a/app/src/main/res/xml/main_settings.xml b/app/src/main/res/xml/main_settings.xml
index 5f96989f9..bdb02355f 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