2 * Copyright © 2016-2022 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser Android <https://www.stoutner.com/privacy-browser-android>.
6 * Privacy Browser Android is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Browser Android is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Browser Android. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacybrowser.helpers
22 import android.content.Context
23 import android.content.Intent
24 import android.net.Uri
25 import android.view.View
27 import androidx.preference.PreferenceManager
28 import androidx.webkit.ProxyConfig
29 import androidx.webkit.ProxyController
30 import androidx.webkit.WebViewFeature
32 import com.stoutner.privacybrowser.R
33 import com.stoutner.privacybrowser.activities.MainWebViewActivity
35 import com.google.android.material.snackbar.Snackbar
37 import java.lang.Exception
38 import java.lang.IllegalArgumentException
39 import java.net.InetSocketAddress
41 import java.net.SocketAddress
45 // Define the public static companion object constants.
46 const val NONE = "None"
49 const val CUSTOM = "Custom"
50 const val ORBOT_STATUS_ON = "ON"
53 fun setProxy(context: Context, activityView: View, proxyMode: String) {
54 // Create a proxy config builder.
55 val proxyConfigBuilder = ProxyConfig.Builder()
57 // Run the commands that correlate to the proxy mode.
60 // Add the proxy to the builder. The proxy config builder can use a SOCKS proxy.
61 proxyConfigBuilder.addProxyRule("socks://localhost:9050")
63 // Ask Orbot to connect if its current status is not `"ON"`.
64 if (MainWebViewActivity.orbotStatus != ORBOT_STATUS_ON) {
65 // Create an intent to request Orbot to start.
66 val orbotIntent = Intent("org.torproject.android.intent.action.START")
68 // Send the intent to the Orbot package.
69 orbotIntent.setPackage("org.torproject.android")
71 // Request a status response be sent back to this package.
72 orbotIntent.putExtra("org.torproject.android.intent.extra.PACKAGE_NAME", context.packageName)
75 context.sendBroadcast(orbotIntent)
80 // Add the proxy to the builder.
81 proxyConfigBuilder.addProxyRule("http://localhost:4444")
85 // Get a handle for the shared preferences.
86 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
88 // Get the custom proxy URL string.
89 val customProxyUrlString = sharedPreferences.getString(context.getString(R.string.proxy_custom_url_key), context.getString(R.string.proxy_custom_url_default_value))
91 // Parse the custom proxy URL.
93 // Add the proxy to the builder.
94 proxyConfigBuilder.addProxyRule(customProxyUrlString!!)
95 } catch (exception: Exception) { // The custom proxy URL is invalid.
96 // Display a Snackbar.
97 Snackbar.make(activityView, R.string.custom_proxy_invalid, Snackbar.LENGTH_LONG).show()
102 // Apply the proxy settings
103 if (WebViewFeature.isFeatureSupported(WebViewFeature.PROXY_OVERRIDE)) {
104 // Convert the proxy config builder into a proxy config.
105 val proxyConfig = proxyConfigBuilder.build()
107 // Get the proxy controller.
108 val proxyController = ProxyController.getInstance()
110 // Apply the proxy settings.
111 if (proxyMode == NONE) { // Remove the proxy. A default executor and runnable are used.
112 proxyController.clearProxyOverride({}, {})
113 } else { // Apply the proxy.
115 // Apply the proxy. A default executor and runnable are used.
116 proxyController.setProxyOverride(proxyConfig, {}, {})
117 } catch (exception: IllegalArgumentException) { // The proxy config is invalid.
118 // Display a Snackbar.
119 Snackbar.make(activityView, R.string.custom_proxy_invalid, Snackbar.LENGTH_LONG).show()
125 fun getCurrentProxy(context: Context): Proxy {
126 // Get the proxy according to the current proxy mode.
127 val proxy = when (MainWebViewActivity.proxyMode) {
129 // Use localhost port 9050 as the socket address.
130 val torSocketAddress: SocketAddress = InetSocketAddress.createUnresolved("localhost", 9050)
132 // Create a SOCKS proxy.
133 Proxy(Proxy.Type.SOCKS, torSocketAddress)
137 // Use localhost port 4444 as the socket address.
138 val i2pSocketAddress: SocketAddress = InetSocketAddress.createUnresolved("localhost", 4444)
140 // Create an HTTP proxy.
141 Proxy(Proxy.Type.HTTP, i2pSocketAddress)
145 // Get the shared preferences.
146 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
148 // Get the custom proxy URL string.
149 val customProxyUrlString = sharedPreferences.getString(context.getString(R.string.proxy_custom_url_key), context.getString(R.string.proxy_custom_url_default_value))
151 // Parse the custom proxy URL.
153 // Convert the custom proxy URL string to a URI.
154 val customProxyUri = Uri.parse(customProxyUrlString)
156 // Get the custom socket address.
157 val customSocketAddress: SocketAddress = InetSocketAddress.createUnresolved(customProxyUri.host, customProxyUri.port)
159 // Get the custom proxy scheme.
160 val customProxyScheme = customProxyUri.scheme
162 // Create a proxy according to the scheme.
163 if (customProxyScheme != null && customProxyScheme.startsWith("socks")) { // A SOCKS proxy is specified.
164 // Create a SOCKS proxy.
165 Proxy(Proxy.Type.SOCKS, customSocketAddress)
166 } else { // A SOCKS proxy is not specified.
167 // Create an HTTP proxy.
168 Proxy(Proxy.Type.HTTP, customSocketAddress)
170 } catch (exception: Exception) { // The custom proxy cannot be parsed.
171 // Disable the proxy.
177 // Create a direct proxy.