As mention in this StackOverFlow, there is no legal or direct way/method to change your webview proxy settings programmatically. But it’s possible to use java reflection to change ProxyHost value from android.net.http.RequestQueue class. It’s private value and there is no setters for it, so reflection seems to be the only possible variant.
Its works for me as using the following modified method, and tested up Android 9.
@SuppressLint("NewApi") @SuppressWarnings("all") private static boolean setProxyKKPlus(WebView webView, String host, int port, final String user, final String password, String applicationClassName) { Log.d(LOG_TAG, "Setting proxy with >= 4.4 API."); Context appContext = webView.getContext().getApplicationContext(); System.setProperty("http.proxyHost", host); System.setProperty("http.proxyPort", port + ""); System.setProperty("https.proxyHost", host); System.setProperty("https.proxyPort", port + ""); Authenticator.setDefault( new Authenticator() { @Override public PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication( user, password.toCharArray()); } } ); System.setProperty("http.proxyUser", user); System.setProperty("http.proxyPassword", password); System.setProperty("https.proxyUser", user); System.setProperty("https.proxyPassword", password ); try { Class applictionCls = Class.forName(applicationClassName); Field loadedApkField = applictionCls.getField("mLoadedApk"); loadedApkField.setAccessible(true); Object loadedApk = loadedApkField.get(appContext); Class loadedApkCls = Class.forName("android.app.LoadedApk"); Field receiversField = loadedApkCls.getDeclaredField("mReceivers"); receiversField.setAccessible(true); ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk); for (Object receiverMap : receivers.values()) { for (Object rec : ((ArrayMap) receiverMap).keySet()) { Class clazz = rec.getClass(); if (clazz.getName().contains("ProxyChangeListener")) { Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, Intent.class); Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); onReceiveMethod.invoke(rec, appContext, intent); } } } Log.d(LOG_TAG, "Setting proxy with >= 4.4 API successful!"); return true; } catch (ClassNotFoundException e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); String exceptionAsString = sw.toString(); Log.v(LOG_TAG, e.getMessage()); Log.v(LOG_TAG, exceptionAsString); } catch (NoSuchFieldException e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); String exceptionAsString = sw.toString(); Log.v(LOG_TAG, e.getMessage()); Log.v(LOG_TAG, exceptionAsString); } catch (IllegalAccessException e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); String exceptionAsString = sw.toString(); Log.v(LOG_TAG, e.getMessage()); Log.v(LOG_TAG, exceptionAsString); } catch (IllegalArgumentException e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); String exceptionAsString = sw.toString(); Log.v(LOG_TAG, e.getMessage()); Log.v(LOG_TAG, exceptionAsString); } catch (NoSuchMethodException e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); String exceptionAsString = sw.toString(); Log.v(LOG_TAG, e.getMessage()); Log.v(LOG_TAG, exceptionAsString); } catch (InvocationTargetException e) { StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); String exceptionAsString = sw.toString(); Log.v(LOG_TAG, e.getMessage()); Log.v(LOG_TAG, exceptionAsString); } return false; }
Now we need to using above method to set the webview instance proxy, so create setProxy method as below.
public static boolean setProxy(WebView webview, String host, int port, String applicationClassName, String user, String password) { return setProxyKKPlus(webview, host, port, user, password, applicationClassName); }
And to load the url into the webview instance we will use the method loadUrl
public void loadUrl(WebView view, String url, String proxyUserName, String proxyPassword){ UsernamePasswordCredentials creds= new UsernamePasswordCredentials(proxyUserName, proxyPassword); Header credHeader = BasicScheme.authenticate(creds, "UTF-8", true); Map<String, String> header = new HashMap<String, String>(); header.put(credHeader.getName(), credHeader.getValue()); view.loadUrl(url, header); }
In normal browsing scenario, if using authenticated proxy, for the first time you request an URL, there are a pop up form that fires before starting loading the requested URL.
ie Firefox popup form with label “Proxy Authorization Required” and “Description: Authorization is required for access to this proxy” or some thing like the below image.
and so we need to enter the correct authentication information username and password, then we can pass through this proxy and loading the URL.
Now we will do that programatically by extend WebViewClient
as following and using onReceivedHttpAuthRequest
method
class ProxyAuthWebViewClient extends WebViewClient { String proxyUserName; String proxyPassword; public ProxyAuthWebViewClient(String proxyUserName, String proxyPassword){ this.proxyUserName = proxyUserName; this.proxyPassword = proxyPassword; } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { loadUrl(view, url, proxyUserName, proxyPassword); return true ; } @Override public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { view.loadUrl(url); //reload the url. handler.proceed(user,password); } }
Then the main entry method will be like
public class MainActivity extends AppCompatActivity { -------------------------- -------------------------- WebView webview; String applicationClassName="android.app.Application"; String user = "passnews010"; String password = "@ProxyHP4TH1048"; String proxyServer = "213.219.38.192"; int proxyPort = 8080; ------------------------- ------------------------- protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ------------------------- ------------------------- //Set Webview instance webview=findViewById(R.id.webview); WebSettings webSettings = webview.getSettings(); CookieManager.getInstance().setAcceptCookie(true); webview.getSettings().setJavaScriptEnabled(true); webview.getSettings().setSupportZoom(true); setProxy(webview, proxyServer, proxyPort, applicationClassName, user, password); webview.setWebViewClient(new ProxyAuthWebViewClient(user,password)); loadUrl(webview,"https://ipinfo.io/",user,password); ------------------------- ------------------------- }
In the Android Studio build.gradle(Module:app)
file we need to add useLibrary 'org.apache.http.legacy'
as
android { compileSdkVersion 29 buildToolsVersion "29.0.3" defaultConfig { applicationId "news.pass.app.alummah" minSdkVersion 21 targetSdkVersion 29 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" manifestPlaceholders = [ onesignal_app_id: '86e59daf-b90c-4849-9603-d822d564523c', // Project number pulled from dashboard, local value is ignored. onesignal_google_project_number: 'REMOTE' ] } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } useLibrary 'org.apache.http.legacy' }
Resources:
- StackOverFlow: WebView android proxy
- StackOverFlow: Using webview and proxy with authentication
- StackOverFlow: Android-WebView’s onReceivedHttpAuthRequest() not called again
- StackOverFlow: Android Webview set proxy programmatically Kitkat