- } else { // The old proxy method must be used, either because an old WebView is installed or because the API == 19;
- // Get a handle for the shared preferences.
- val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
-
- // Check to make sure a SOCKS proxy is not selected.
- if ((proxyMode == CUSTOM) &&
- sharedPreferences.getString(context.getString(R.string.proxy_custom_url_key), context.getString(R.string.proxy_custom_url_default_value))!!.startsWith("socks://")) {
- // Display a Snackbar.
- Snackbar.make(activityView, R.string.socks_proxies_do_not_work_on_kitkat, Snackbar.LENGTH_LONG).show()
- } else { // Use reflection to apply the new proxy values.
- try {
- // Get the application and APK classes.
- val applicationClass = Class.forName("android.app.Application")
-
- // Suppress the lint warning that reflection may not always work in the future and on all devices.
- @SuppressLint("PrivateApi") val loadedApkClass = Class.forName("android.app.LoadedApk")
-
- // Get the declared fields. Suppress the lint that it is discouraged to access private APIs.
- @SuppressLint("DiscouragedPrivateApi") val methodLoadedApkField = applicationClass.getDeclaredField("mLoadedApk")
- @SuppressLint("DiscouragedPrivateApi") val methodReceiversField = loadedApkClass.getDeclaredField("mReceivers")
-
- // Allow the values to be changed.
- methodLoadedApkField.isAccessible = true
- methodReceiversField.isAccessible = true
-
- // Get the APK object.
- val methodLoadedApkObject = methodLoadedApkField[context]
-
- // Get an array map of the receivers.
- val receivers = methodReceiversField[methodLoadedApkObject] as ArrayMap<*, *>
-
- // Set the proxy.
- for (receiverMap in receivers.values) {
- for (receiver in (receiverMap as ArrayMap<*, *>).keys) {
- // Get the receiver class.
- // `Class<*>`, which is an `unbounded wildcard parameterized type`, must be used instead of `Class`, which is a `raw type`. Otherwise, `receiveClass.getDeclaredMethod()` is unhappy.
- val receiverClass: Class<*> = receiver.javaClass
-
- // Apply the new proxy settings to any classes whose names contain `ProxyChangeListener`.
- if (receiverClass.name.contains("ProxyChangeListener")) {
- // Get the `onReceive` method from the class.
- val onReceiveMethod = receiverClass.getDeclaredMethod("onReceive", Context::class.java, Intent::class.java)
-
- // Create a proxy change intent.
- val proxyChangeIntent = Intent(android.net.Proxy.PROXY_CHANGE_ACTION)
-
- // Set the proxy for API >= 21.
- if (Build.VERSION.SDK_INT >= 21) {
- // Get the proxy info class.
- val proxyInfoClass = Class.forName("android.net.ProxyInfo")
-
- // Get the build direct proxy method from the proxy info class.
- val buildDirectProxyMethod = proxyInfoClass.getMethod("buildDirectProxy", String::class.java, Integer.TYPE)
-
- // Populate a proxy info object with the new proxy information.
- val proxyInfoObject = buildDirectProxyMethod.invoke(proxyInfoClass, proxyHost, Integer.valueOf(proxyPort))
-
- // Add the proxy info object into the proxy change intent.
- proxyChangeIntent.putExtra("proxy", proxyInfoObject as Parcelable)
- }
-
- // Pass the proxy change intent to the `onReceive` method of the receiver class.
- onReceiveMethod.invoke(receiver, context, proxyChangeIntent)
- }
- }
- }
- } catch (exception: ClassNotFoundException) {
- // Do nothing.
- } catch (exception: NoSuchFieldException) {
- // Do nothing.
- } catch (exception: IllegalAccessException) {
- // Do nothing.
- } catch (exception: NoSuchMethodException) {
- // Do nothing.
- } catch (exception: InvocationTargetException) {
- // Do nothing.
- }
- }