diff --git a/README.md b/README.md
index 2fc8fd32..6e877e72 100644
--- a/README.md
+++ b/README.md
@@ -2,44 +2,69 @@
Global Proxy App for Android System
-ProxyDroid is distributed under GPLv3 with many other open source software,
+ProxyDroid is distributed under GPLv3 with many other open source software,
here is a list of them:
- * cntlm - authentication proxy: http://cntlm.sourceforge.net/
* redsocks - transparent socks redirector: http://darkk.net.ru/redsocks/
+ * tun2socks - VPN-based transparent proxy
* netfilter/iptables - NAT module: http://www.netfilter.org/
- * transproxy - transparent proxy for HTTP: http://transproxy.sourceforge.net/
- * stunnel - multiplatform SSL tunneling proxy: http://www.stunnel.org/
-## TRAVIS CI STATUS
+## PREREQUISITES
-[](http://travis-ci.org/madeye/proxydroid)
+* JDK 11+
+* Android Studio or Gradle 8.1+
+* Android SDK (compileSdk 33)
+* Android NDK 25.1.8937393
+* CMake 3.22.1
-[Nightly Builds](http://buildbot.sinaapp.com)
+## BUILD
-## PREREQUISITES
+### Using Android Studio
-* JDK 1.6+
-* Maven 3.0.5
-* Android SDK r17+
-* Android NDK r8+
+1. Open the project in Android Studio
+2. Sync Gradle files
+3. Build the project using `Build > Make Project`
-* Local Maven Dependencies
+### Using Command Line
- Use Maven Android SDK Deployer to install all android related dependencies.
+```bash
+./gradlew assembleDebug
+```
- ```bash
- git clone https://github.com/mosabua/maven-android-sdk-deployer.git
- pushd maven-android-sdk-deployer
- export ANDROID_HOME=/path/to/android/sdk
- mvn install -P 4.1
- popd
- ```
+For release build:
-## BUILD
+```bash
+./gradlew assembleRelease
+```
-Invoke the building like this
+## PROJECT STRUCTURE
-```bash
- mvn clean install
```
+app/
+├── src/main/
+│ ├── java/org/proxydroid/ # Kotlin source files
+│ │ ├── ProxyDroid.kt # Main activity
+│ │ ├── ProxyDroidService.kt
+│ │ ├── ProxyDroidVpnService.kt
+│ │ ├── AppManager.kt
+│ │ ├── Profile.kt
+│ │ └── utils/ # Utility classes
+│ └── cpp/ # Native code
+│ ├── exec/ # Native exec helper
+│ ├── libevent/ # libevent library
+│ ├── redsocks/ # redsocks proxy
+│ └── tun2socks/ # tun2socks VPN helper
+└── build.gradle
+```
+
+## SUPPORTED ARCHITECTURES
+
+* armeabi-v7a
+* arm64-v8a
+* x86
+* x86_64
+
+## REQUIREMENTS
+
+* Minimum SDK: 21 (Android 5.0)
+* Target SDK: 33 (Android 13)
diff --git a/app/build.gradle b/app/build.gradle
index 6cebd071..2dcdc74f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,5 +1,6 @@
plugins {
id 'com.android.application'
+ id 'org.jetbrains.kotlin.android'
}
android {
@@ -35,6 +36,10 @@ android {
targetCompatibility JavaVersion.VERSION_11
}
+ kotlinOptions {
+ jvmTarget = '11'
+ }
+
externalNativeBuild {
cmake {
version '3.22.1'
@@ -51,6 +56,8 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.core:core-ktx:1.10.1'
// Exclude JUnit from json-simple (it pulls junit as compile dependency)
implementation('com.googlecode.json-simple:json-simple:1.1.1') {
exclude group: 'junit', module: 'junit'
diff --git a/app/src/androidTest/java/org/proxydroid/ExampleInstrumentedTest.java b/app/src/androidTest/java/org/proxydroid/ExampleInstrumentedTest.java
deleted file mode 100644
index c1665d6c..00000000
--- a/app/src/androidTest/java/org/proxydroid/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.proxydroid;
-
-import android.content.Context;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see Testing documentation
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
- @Test
- public void useAppContext() {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
- assertEquals("org.proxydroid", appContext.getPackageName());
- }
-}
diff --git a/app/src/androidTest/java/org/proxydroid/ExampleInstrumentedTest.kt b/app/src/androidTest/java/org/proxydroid/ExampleInstrumentedTest.kt
new file mode 100644
index 00000000..59c38909
--- /dev/null
+++ b/app/src/androidTest/java/org/proxydroid/ExampleInstrumentedTest.kt
@@ -0,0 +1,16 @@
+package org.proxydroid
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.Assert.*
+
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("org.proxydroid", appContext.packageName)
+ }
+}
diff --git a/app/src/main/java/com/btr/proxy/selector/pac/PacProxySelector.java b/app/src/main/java/com/btr/proxy/selector/pac/PacProxySelector.java
deleted file mode 100644
index 16b782b2..00000000
--- a/app/src/main/java/com/btr/proxy/selector/pac/PacProxySelector.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package com.btr.proxy.selector.pac;
-
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.List;
-
-import android.util.Log;
-
-/*****************************************************************************
- * ProxySelector that will use a PAC script to find an proxy for a given URI.
- *
- * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
- ****************************************************************************/
-public class PacProxySelector {
-
- // private static final String PAC_PROXY = "PROXY";
- private static final String PAC_SOCKS = "SOCKS";
- private static final String PAC_DIRECT = "DIRECT";
- private static final String PAC_HTTPS = "HTTPS";
-
- private final static String TAG = "ProxyDroid.PAC";
-
- private PacScriptParser pacScriptParser;
-
- /*************************************************************************
- * Constructor
- *
- * @param pacSource
- * the source for the PAC file.
- ************************************************************************/
-
- public PacProxySelector(PacScriptSource pacSource) {
- super();
- selectEngine(pacSource);
- }
-
- /*************************************************************************
- * Selects one of the available PAC parser engines.
- *
- * @param pacSource
- * to use as input.
- ************************************************************************/
-
- private void selectEngine(PacScriptSource pacSource) {
- try {
- this.pacScriptParser = new RhinoPacScriptParser(pacSource);
- } catch (Exception e) {
- Log.e(TAG, "PAC parser error.", e);
- }
- }
-
- /*************************************************************************
- * select
- *
- * @see java.net.ProxySelector#select(java.net.URI)
- ************************************************************************/
- public List select(URI uri) {
- if (uri == null || uri.getHost() == null) {
- throw new IllegalArgumentException("URI must not be null.");
- }
-
- // Fix for Java 1.6.16 where we get a infinite loop because
- // URL.connect(Proxy.NO_PROXY) does not work as expected.
- PacScriptSource scriptSource = this.pacScriptParser.getScriptSource();
- if (String.valueOf(scriptSource).contains(uri.getHost())) {
- return null;
- }
-
- return findProxy(uri);
- }
-
- /*************************************************************************
- * Evaluation of the given URL with the PAC-file.
- *
- * Two cases can be handled here: DIRECT Fetch the object directly from the
- * content HTTP server denoted by its URL PROXY name:port Fetch the object
- * via the proxy HTTP server at the given location (name and port)
- *
- * @param uri
- * URI to be evaluated.
- * @return Proxy-object list as result of the evaluation.
- ************************************************************************/
-
- private List findProxy(URI uri) {
- try {
- List proxies = new ArrayList();
- String parseResult = this.pacScriptParser.evaluate(uri.toString(),
- uri.getHost());
- String[] proxyDefinitions = parseResult.split("[;]");
- for (String proxyDef : proxyDefinitions) {
- if (proxyDef.trim().length() > 0) {
- proxies.add(buildProxyFromPacResult(proxyDef));
- }
- }
- return proxies;
- } catch (ProxyEvaluationException e) {
- Log.e(TAG, "PAC resolving error.", e);
- return null;
- }
- }
-
- /*************************************************************************
- * The proxy evaluator will return a proxy string. This method will take
- * this string and build a matching Proxy for it.
- *
- * @param pacResult
- * the result from the PAC parser.
- * @return a Proxy
- ************************************************************************/
-
- private Proxy buildProxyFromPacResult(String pacResult) {
- if (pacResult == null || pacResult.trim().length() < 6) {
- return Proxy.NO_PROXY;
- }
- String proxyDef = pacResult.trim();
- if (proxyDef.toUpperCase().startsWith(PAC_DIRECT)) {
- return Proxy.NO_PROXY;
- }
-
- // Check proxy type.
- String type = Proxy.TYPE_HTTP;
- if (proxyDef.toUpperCase().startsWith(PAC_SOCKS)) {
- type = Proxy.TYPE_SOCKS5;
- }
- if (proxyDef.toUpperCase().startsWith(PAC_HTTPS)) {
- type = Proxy.TYPE_HTTPS;
- }
-
- String host = proxyDef.substring(6);
- Integer port = 80;
- if (type.equals(Proxy.TYPE_HTTPS)) {
- port = 443;
- }
-
- // Split port from host
- int indexOfPort = host.indexOf(':');
- if (indexOfPort != -1) {
- port = Integer.parseInt(host.substring(indexOfPort + 1).trim());
- host = host.substring(0, indexOfPort).trim();
- }
-
- return new Proxy(host, port, type);
- }
-}
diff --git a/app/src/main/java/com/btr/proxy/selector/pac/PacProxySelector.kt b/app/src/main/java/com/btr/proxy/selector/pac/PacProxySelector.kt
new file mode 100644
index 00000000..468fdef0
--- /dev/null
+++ b/app/src/main/java/com/btr/proxy/selector/pac/PacProxySelector.kt
@@ -0,0 +1,89 @@
+package com.btr.proxy.selector.pac
+
+import android.util.Log
+import java.net.URI
+
+class PacProxySelector(pacSource: PacScriptSource) {
+
+ companion object {
+ private const val PAC_SOCKS = "SOCKS"
+ private const val PAC_DIRECT = "DIRECT"
+ private const val PAC_HTTPS = "HTTPS"
+ private const val TAG = "ProxyDroid.PAC"
+ }
+
+ private var pacScriptParser: PacScriptParser? = null
+
+ init {
+ selectEngine(pacSource)
+ }
+
+ private fun selectEngine(pacSource: PacScriptSource) {
+ try {
+ pacScriptParser = RhinoPacScriptParser(pacSource)
+ } catch (e: Exception) {
+ Log.e(TAG, "PAC parser error.", e)
+ }
+ }
+
+ fun select(uri: URI): List? {
+ if (uri.host == null) {
+ throw IllegalArgumentException("URI must not be null.")
+ }
+
+ val scriptSource = pacScriptParser?.getScriptSource()
+ if (scriptSource.toString().contains(uri.host)) {
+ return null
+ }
+
+ return findProxy(uri)
+ }
+
+ private fun findProxy(uri: URI): List? {
+ return try {
+ val proxies = mutableListOf()
+ val parseResult = pacScriptParser?.evaluate(uri.toString(), uri.host) ?: return null
+ val proxyDefinitions = parseResult.split(";")
+
+ for (proxyDef in proxyDefinitions) {
+ if (proxyDef.trim().isNotEmpty()) {
+ proxies.add(buildProxyFromPacResult(proxyDef))
+ }
+ }
+ proxies
+ } catch (e: ProxyEvaluationException) {
+ Log.e(TAG, "PAC resolving error.", e)
+ null
+ }
+ }
+
+ private fun buildProxyFromPacResult(pacResult: String?): Proxy {
+ if (pacResult == null || pacResult.trim().length < 6) {
+ return Proxy.NO_PROXY
+ }
+
+ val proxyDef = pacResult.trim()
+ if (proxyDef.uppercase().startsWith(PAC_DIRECT)) {
+ return Proxy.NO_PROXY
+ }
+
+ var type = Proxy.TYPE_HTTP
+ if (proxyDef.uppercase().startsWith(PAC_SOCKS)) {
+ type = Proxy.TYPE_SOCKS5
+ }
+ if (proxyDef.uppercase().startsWith(PAC_HTTPS)) {
+ type = Proxy.TYPE_HTTPS
+ }
+
+ var host = proxyDef.substring(6)
+ var port = if (type == Proxy.TYPE_HTTPS) 443 else 80
+
+ val indexOfPort = host.indexOf(':')
+ if (indexOfPort != -1) {
+ port = host.substring(indexOfPort + 1).trim().toInt()
+ host = host.substring(0, indexOfPort).trim()
+ }
+
+ return Proxy(host, port, type)
+ }
+}
diff --git a/app/src/main/java/com/btr/proxy/selector/pac/PacScriptMethods.java b/app/src/main/java/com/btr/proxy/selector/pac/PacScriptMethods.java
deleted file mode 100644
index 66ef714a..00000000
--- a/app/src/main/java/com/btr/proxy/selector/pac/PacScriptMethods.java
+++ /dev/null
@@ -1,647 +0,0 @@
-package com.btr.proxy.selector.pac;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-import java.util.TimeZone;
-
-import android.util.Log;
-
-/***************************************************************************
- * Implementation of PAC JavaScript functions.
- *
- * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
- ***************************************************************************
- */
-public class PacScriptMethods implements ScriptMethods {
-
- public static final String OVERRIDE_LOCAL_IP = "com.btr.proxy.pac.overrideLocalIP";
-
- private final static String GMT = "GMT";
-
- private final static List DAYS = Collections
- .unmodifiableList(Arrays.asList("SUN", "MON", "TUE", "WED", "THU",
- "FRI", "SAT"));
-
- private final static List MONTH = Collections
- .unmodifiableList(Arrays.asList("JAN", "FEB", "MAR", "APR", "MAY",
- "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"));
-
- private Calendar currentTime;
-
- private final static String TAG = "ProxyDroid.PAC";
-
- /*************************************************************************
- * Constructor
- ************************************************************************/
-
- public PacScriptMethods() {
- super();
- }
-
- /*************************************************************************
- * isPlainHostName
- *
- * @see com.btr.proxy.selector.pac.ScriptMethods#isPlainHostName(java.lang.String)
- ************************************************************************/
-
- @Override
- public boolean isPlainHostName(String host) {
- return host.indexOf(".") < 0;
- }
-
- /*************************************************************************
- * Tests if an URL is in a given domain.
- *
- * @param host
- * is the host name from the URL.
- * @param domain
- * is the domain name to test the host name against.
- * @return true if the domain of host name matches.
- ************************************************************************/
-
- @Override
- public boolean dnsDomainIs(String host, String domain) {
- return host.endsWith(domain);
- }
-
- /*************************************************************************
- * Is true if the host name matches exactly the specified host name, or if
- * there is no domain name part in the host name, but the unqualified host
- * name matches.
- *
- * @param host
- * the host name from the URL.
- * @param domain
- * fully qualified host name with domain to match against.
- * @return true if matches else false.
- ************************************************************************/
-
- @Override
- public boolean localHostOrDomainIs(String host, String domain) {
- return domain.startsWith(host);
- }
-
- /*************************************************************************
- * Tries to resolve the host name. Returns true if succeeds.
- *
- * @param host
- * is the host name from the URL.
- * @return true if resolvable else false.
- ************************************************************************/
-
- @Override
- public boolean isResolvable(String host) {
- try {
- InetAddress.getByName(host).getHostAddress();
- return true;
- } catch (UnknownHostException ex) {
- Log.e(TAG, "Hostname not resolveable " + host);
- // Not resolvable
- }
- return false;
- }
-
- /*************************************************************************
- * Returns true if the IP address of the host matches the specified IP
- * address pattern. Pattern and mask specification is done the same way as
- * for SOCKS configuration.
- *
- * Example: isInNet(host, "198.95.0.0", "255.255.0.0") is true if the IP
- * address of the host matches 198.95.*.*.
- *
- * @param host
- * a DNS host name, or IP address. If a host name is passed, it
- * will be resolved into an IP address by this function.
- * @param pattern
- * an IP address pattern in the dot-separated format.
- * @param mask
- * mask for the IP address pattern informing which parts of the
- * IP address should be matched against. 0 means ignore, 255
- * means match.
- * @return true if it matches else false.
- ************************************************************************/
-
- @Override
- public boolean isInNet(String host, String pattern, String mask) {
- long lhost = parseIpAddressToLong(host);
- long lpattern = parseIpAddressToLong(pattern);
- long lmask = parseIpAddressToLong(mask);
- boolean result = (lhost & lmask) == lpattern;
- return result;
- }
-
- /*************************************************************************
- * Convert a string representation of a IP to a long.
- *
- * @param address
- * to convert.
- * @return the address as long.
- ************************************************************************/
-
- private long parseIpAddressToLong(String address) {
- long result = 0;
- String[] parts = address.split("\\.");
- long shift = 24;
- for (String part : parts) {
- long lpart = Long.parseLong(part);
-
- result |= (lpart << shift);
- shift -= 8;
- }
- return result;
- }
-
- /*************************************************************************
- * Resolves the given DNS host name into an IP address, and returns it in
- * the dot separated format as a string.
- *
- * @param host
- * the host to resolve.
- * @return the resolved IP, empty string if not resolvable.
- ************************************************************************/
-
- @Override
- public String dnsResolve(String host) {
- try {
- return InetAddress.getByName(host).getHostAddress();
- } catch (UnknownHostException e) {
- Log.e(TAG, "DNS name not resolvable " + host);
- // Not resolvable.
- }
- return "";
- }
-
- /*************************************************************************
- * Returns the IP address of the host that the process is running on, as a
- * string in the dot-separated integer format.
- *
- * @return an IP as string.
- ************************************************************************/
-
- @Override
- public String myIpAddress() {
- try {
- String overrideIP = System.getProperty(OVERRIDE_LOCAL_IP);
- if (overrideIP != null && overrideIP.trim().length() > 0) {
- return overrideIP.trim();
- }
- return InetAddress.getLocalHost().getHostAddress();
- } catch (UnknownHostException e) {
- Log.e(TAG, "Local address not resolvable.");
- return "";
- }
- }
-
- /*************************************************************************
- * Returns the number of DNS domain levels (number of dots) in the host
- * name.
- *
- * @param host
- * is the host name from the URL.
- * @return number of DNS domain levels.
- ************************************************************************/
-
- @Override
- public int dnsDomainLevels(String host) {
- int count = 0;
- int startPos = 0;
- while ((startPos = host.indexOf(".", startPos + 1)) > -1) {
- count++;
- }
- return count;
- }
-
- /*************************************************************************
- * Returns true if the string matches the specified shell expression.
- * Actually, currently the patterns are shell expressions, not regular
- * expressions.
- *
- * @param str
- * is any string to compare (e.g. the URL, or the host name).
- * @param shexp
- * is a shell expression to compare against.
- * @return true if the string matches, else false.
- ************************************************************************/
-
- @Override
- public boolean shExpMatch(String str, String shexp) {
- StringTokenizer tokenizer = new StringTokenizer(shexp, "*");
- int startPos = 0;
- while (tokenizer.hasMoreTokens()) {
- String token = tokenizer.nextToken();
- // 07.05.2009 Incorrect? first token can be startsWith and last one
- // can be endsWith
- int temp = str.indexOf(token, startPos);
- if (temp == -1) {
- return false;
- } else {
- startPos = temp + token.length();
- }
- }
- return true;
- }
-
- /*************************************************************************
- * Only the first parameter is mandatory. Either the second, the third, or
- * both may be left out. If only one parameter is present, the function
- * yields a true value on the weekday that the parameter represents. If the
- * string "GMT" is specified as a second parameter, times are taken to be in
- * GMT, otherwise in local time zone. If both wd1 and wd2 are defined, the
- * condition is true if the current weekday is in between those two
- * weekdays. Bounds are inclusive. If the "GMT" parameter is specified,
- * times are taken to be in GMT, otherwise the local time zone is used.
- *
- * @param wd1
- * weekday 1 is one of SUN MON TUE WED THU FRI SAT
- * @param wd2
- * weekday 2 is one of SUN MON TUE WED THU FRI SAT
- * @param gmt
- * "GMT" for gmt time format else "undefined"
- * @return true if current day matches the criteria.
- ************************************************************************/
-
- @Override
- public boolean weekdayRange(String wd1, String wd2, String gmt) {
- boolean useGmt = GMT.equalsIgnoreCase(wd2) || GMT.equalsIgnoreCase(gmt);
- Calendar cal = getCurrentTime(useGmt);
-
- int currentDay = cal.get(Calendar.DAY_OF_WEEK) - 1;
- int from = DAYS.indexOf(wd1 == null ? null : wd1.toUpperCase());
- int to = DAYS.indexOf(wd2 == null ? null : wd2.toUpperCase());
- if (to == -1) {
- to = from;
- }
-
- if (to < from) {
- return currentDay >= from || currentDay <= to;
- } else {
- return currentDay >= from && currentDay <= to;
- }
- }
-
- /*************************************************************************
- * Sets a calendar with the current time. If this is set all date and time
- * based methods will use this calendar to determine the current time
- * instead of the real time. This is only be used by unit tests and is not
- * part of the public API.
- *
- * @param cal
- * a Calendar to set.
- ************************************************************************/
-
- public void setCurrentTime(Calendar cal) {
- this.currentTime = cal;
- }
-
- /*************************************************************************
- * Gets a calendar set to the current time. This is used by the date and
- * time based methods.
- *
- * @param useGmt
- * flag to indicate if the calendar is to be created in GMT time
- * or local time.
- * @return a Calendar set to the current time.
- ************************************************************************/
-
- private Calendar getCurrentTime(boolean useGmt) {
- if (this.currentTime != null) { // Only used for unit tests
- return (Calendar) this.currentTime.clone();
- }
- return Calendar.getInstance(useGmt ? TimeZone.getTimeZone(GMT)
- : TimeZone.getDefault());
- }
-
- /*************************************************************************
- * Only the first parameter is mandatory. All other parameters can be left
- * out therefore the meaning of the parameters changes. The method
- * definition shows the version with the most possible parameters filled.
- * The real meaning of the parameters is guessed from it's value. If "from"
- * and "to" are specified then the bounds are inclusive. If the "GMT"
- * parameter is specified, times are taken to be in GMT, otherwise the local
- * time zone is used.
- *
- * @param day1
- * is the day of month between 1 and 31 (as an integer).
- * @param month1
- * one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
- * @param year1
- * is the full year number, for example 1995 (but not 95).
- * Integer.
- * @param day2
- * is the day of month between 1 and 31 (as an integer).
- * @param month2
- * one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
- * @param year2
- * is the full year number, for example 1995 (but not 95).
- * Integer.
- * @param gmt
- * "GMT" for gmt time format else "undefined"
- * @return true if the current date matches the given range.
- ************************************************************************/
-
- @Override
- public boolean dateRange(Object day1, Object month1, Object year1,
- Object day2, Object month2, Object year2, Object gmt) {
-
- // Guess the parameter meanings.
- Map params = new HashMap();
- parseDateParam(params, day1);
- parseDateParam(params, month1);
- parseDateParam(params, year1);
- parseDateParam(params, day2);
- parseDateParam(params, month2);
- parseDateParam(params, year2);
- parseDateParam(params, gmt);
-
- // Get current date
- boolean useGmt = params.get("gmt") != null;
- Calendar cal = getCurrentTime(useGmt);
- Date current = cal.getTime();
-
- // Build the "from" date
- if (params.get("day1") != null) {
- cal.set(Calendar.DAY_OF_MONTH, params.get("day1"));
- }
- if (params.get("month1") != null) {
- cal.set(Calendar.MONTH, params.get("month1"));
- }
- if (params.get("year1") != null) {
- cal.set(Calendar.YEAR, params.get("year1"));
- }
- Date from = cal.getTime();
-
- // Build the "to" date
- Date to;
- if (params.get("day2") != null) {
- cal.set(Calendar.DAY_OF_MONTH, params.get("day2"));
- }
- if (params.get("month2") != null) {
- cal.set(Calendar.MONTH, params.get("month2"));
- }
- if (params.get("year2") != null) {
- cal.set(Calendar.YEAR, params.get("year2"));
- }
- to = cal.getTime();
-
- // Need to increment to the next month?
- if (to.before(from)) {
- cal.add(Calendar.MONTH, +1);
- to = cal.getTime();
- }
- // Need to increment to the next year?
- if (to.before(from)) {
- cal.add(Calendar.YEAR, +1);
- cal.add(Calendar.MONTH, -1);
- to = cal.getTime();
- }
-
- return current.compareTo(from) >= 0 && current.compareTo(to) <= 0;
- }
-
- /*************************************************************************
- * Try to guess the type of the given parameter and put it into the params
- * map.
- *
- * @param params
- * a map to put the parsed parameters into.
- * @param value
- * to parse and specify the type for.
- ************************************************************************/
-
- private void parseDateParam(Map params, Object value) {
- if (value instanceof Number) {
- int n = ((Number) value).intValue();
- if (n <= 31) {
- // Its a day
- if (params.get("day1") == null) {
- params.put("day1", n);
- } else {
- params.put("day2", n);
- }
- } else {
- // Its a year
- if (params.get("year1") == null) {
- params.put("year1", n);
- } else {
- params.put("year2", n);
- }
- }
- }
-
- if (value instanceof String) {
- int n = MONTH.indexOf(((String) value).toUpperCase());
- if (n > -1) {
- // Its a month
- if (params.get("month1") == null) {
- params.put("month1", n);
- } else {
- params.put("month2", n);
- }
- }
- }
-
- if (GMT.equalsIgnoreCase(String.valueOf(value))) {
- params.put("gmt", 1);
- }
- }
-
- /*************************************************************************
- * Some parameters can be left out therefore the meaning of the parameters
- * changes. The method definition shows the version with the most possible
- * parameters filled. The real meaning of the parameters is guessed from
- * it's value. If "from" and "to" are specified then the bounds are
- * inclusive. If the "GMT" parameter is specified, times are taken to be in
- * GMT, otherwise the local time zone is used.
- *
- *
- *
- * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
- ****************************************************************************/
-
-public class RhinoPacScriptParser extends ScriptableObject implements
- PacScriptParser {
-
- private static final long serialVersionUID = 1L;
-
- private final static String TAG = "ProxyDroid.PAC";
-
- // Define some PAC script functions. These functions are not part of ECMA.
- private static final String[] JS_FUNCTION_NAMES = { "shExpMatch",
- "dnsResolve", "isResolvable", "isInNet", "dnsDomainIs",
- "isPlainHostName", "myIpAddress", "dnsDomainLevels",
- "localHostOrDomainIs", "weekdayRange", "dateRange", "timeRange" };
-
- private Scriptable scope;
- private PacScriptSource source;
- private static final PacScriptMethods SCRIPT_METHODS = new PacScriptMethods();
-
- /*************************************************************************
- * Constructor
- *
- * @param source
- * the source for the PAC script.
- * @throws ProxyEvaluationException
- * on error.
- ************************************************************************/
-
- public RhinoPacScriptParser(PacScriptSource source)
- throws ProxyEvaluationException {
- super();
- this.source = source;
-
- setupEngine();
- }
-
- /*************************************************************************
- * Initializes the JavaScript engine.
- *
- * @throws ProxyEvaluationException
- * on error.
- ************************************************************************/
-
- public void setupEngine() throws ProxyEvaluationException {
-
- Context context = new ContextFactory().enterContext();
- try {
- defineFunctionProperties(JS_FUNCTION_NAMES,
- RhinoPacScriptParser.class, ScriptableObject.DONTENUM);
- } catch (Exception e) {
- Log.e(TAG, "JS Engine setup error.", e);
- throw new ProxyEvaluationException(e.getMessage(), e);
- }
-
- this.scope = context.initStandardObjects(this);
- }
-
- /***************************************************************************
- * Gets the source of the PAC script used by this parser.
- *
- * @return a PacScriptSource.
- **************************************************************************/
-
- @Override
- public PacScriptSource getScriptSource() {
- return this.source;
- }
-
- /*************************************************************************
- * Evaluates the given URL and host against the PAC script.
- *
- * @param url
- * the URL to evaluate.
- * @param host
- * the host name part of the URL.
- * @return the script result.
- * @throws ProxyEvaluationException
- * on execution error.
- ************************************************************************/
-
- @Override
- public String evaluate(String url, String host)
- throws ProxyEvaluationException {
- try {
- // FindProxyForURL function signature
- StringBuilder script = new StringBuilder(
- this.source.getScriptContent());
- String evalMethod = " ;FindProxyForURL (\"" + url + "\",\"" + host
- + "\")";
- script.append(evalMethod);
-
- Context context = Context.enter();
- context.setOptimizationLevel(-1);
- try {
- Object result = context.evaluateString(this.scope,
- script.toString(), "userPacFile", 1, null);
-
- return Context.toString(result);
- } finally {
- Context.exit();
- }
- } catch (Exception e) {
- Log.e(TAG, "JS evaluation error.", e);
- throw new ProxyEvaluationException(
- "Error while executing PAC script: " + e.getMessage(), e);
- }
- }
-
- /*************************************************************************
- * getClassName See also
- * org.mozilla.javascript.ScriptableObject#getClassName()
- ************************************************************************/
- @Override
- public String getClassName() {
- return getClass().getSimpleName();
- }
-
- // ***************************************************************************
- // Defining PAC script methods needed in JS
- // ***************************************************************************
-
- /*************************************************************************
- * Tests if the given name is a plain host name without a domain name.
- *
- * @param host
- * the host name from the URL (excluding port number)
- * @return true if there is no domain name in the host name (no dots).
- ************************************************************************/
-
- public static boolean isPlainHostName(String host) {
- return SCRIPT_METHODS.isPlainHostName(host);
- }
-
- /*************************************************************************
- * Tests if an URL is in a given domain.
- *
- * @param host
- * is the host name from the URL.
- * @param domain
- * is the domain name to test the host name against.
- * @return true if the domain of host name matches.
- ************************************************************************/
-
- public static boolean dnsDomainIs(String host, String domain) {
- return SCRIPT_METHODS.dnsDomainIs(host, domain);
- }
-
- /*************************************************************************
- * Is true if the host name matches exactly the specified host name, or if
- * there is no domain name part in the host name, but the unqualified host
- * name matches.
- *
- * @param host
- * the host name from the URL.
- * @param domain
- * fully qualified host name with domain to match against.
- * @return true if matches else false.
- ************************************************************************/
-
- public static boolean localHostOrDomainIs(String host, String domain) {
- return SCRIPT_METHODS.localHostOrDomainIs(host, domain);
- }
-
- /*************************************************************************
- * Tries to resolve the host name. Returns true if succeeds.
- *
- * @param host
- * is the host name from the URL.
- * @return true if resolvable else false.
- ************************************************************************/
-
- public static boolean isResolvable(String host) {
- return SCRIPT_METHODS.isResolvable(host);
- }
-
- /*************************************************************************
- * Returns true if the IP address of the host matches the specified IP
- * address pattern. Pattern and mask specification is done the same way as
- * for SOCKS configuration.
- *
- * Example: isInNet(host, "198.95.0.0", "255.255.0.0") is true if the IP
- * address of the host matches 198.95.*.*.
- *
- * @param host
- * a DNS host name, or IP address. If a host name is passed, it
- * will be resolved into an IP address by this function.
- * @param pattern
- * an IP address pattern in the dot-separated format.
- * @param mask
- * mask for the IP address pattern informing which parts of the
- * IP address should be matched against. 0 means ignore, 255
- * means match.
- * @return true if it matches else false.
- ************************************************************************/
-
- public static boolean isInNet(String host, String pattern, String mask) {
- return SCRIPT_METHODS.isInNet(host, pattern, mask);
- }
-
- /*************************************************************************
- * Resolves the given DNS host name into an IP address, and returns it in
- * the dot separated format as a string.
- *
- * @param host
- * the host to resolve.
- * @return the resolved IP, empty string if not resolvable.
- ************************************************************************/
-
- public static String dnsResolve(String host) {
- return SCRIPT_METHODS.dnsResolve(host);
- }
-
- /*************************************************************************
- * Returns the IP address of the host that the process is running on, as a
- * string in the dot-separated integer format.
- *
- * @return an IP as string.
- ************************************************************************/
-
- public static String myIpAddress() {
- return SCRIPT_METHODS.myIpAddress();
- }
-
- /*************************************************************************
- * Returns the number of DNS domain levels (number of dots) in the host
- * name.
- *
- * @param host
- * is the host name from the URL.
- * @return number of DNS domain levels.
- ************************************************************************/
-
- public static int dnsDomainLevels(String host) {
- return SCRIPT_METHODS.dnsDomainLevels(host);
- }
-
- /*************************************************************************
- * Returns true if the string matches the specified shell expression.
- * Actually, currently the patterns are shell expressions, not regular
- * expressions.
- *
- * @param str
- * is any string to compare (e.g. the URL, or the host name).
- * @param shexp
- * is a shell expression to compare against.
- * @return true if the string matches, else false.
- ************************************************************************/
-
- public static boolean shExpMatch(String str, String shexp) {
- return SCRIPT_METHODS.shExpMatch(str, shexp);
- }
-
- /*************************************************************************
- * Only the first parameter is mandatory. Either the second, the third, or
- * both may be left out. If only one parameter is present, the function
- * yields a true value on the weekday that the parameter represents. If the
- * string "GMT" is specified as a second parameter, times are taken to be in
- * GMT, otherwise in local time zone. If both wd1 and wd2 are defined, the
- * condition is true if the current weekday is in between those two
- * weekdays. Bounds are inclusive. If the "GMT" parameter is specified,
- * times are taken to be in GMT, otherwise the local time zone is used.
- *
- * @param wd1
- * weekday 1 is one of SUN MON TUE WED THU FRI SAT
- * @param wd2
- * weekday 2 is one of SUN MON TUE WED THU FRI SAT
- * @param gmt
- * "GMT" for gmt time format else "undefined"
- * @return true if current day matches the criteria.
- ************************************************************************/
-
- public static boolean weekdayRange(String wd1, String wd2, String gmt) {
- return SCRIPT_METHODS.weekdayRange(wd1, wd2, gmt);
- }
-
- /*************************************************************************
- * Sets a calendar with the current time. If this is set all date and time
- * based methods will use this calendar to determine the current time
- * instead of the real time. This is only be used by unit tests and is not
- * part of the public API.
- *
- * @param cal
- * a Calendar to set.
- ************************************************************************/
-
- static void setCurrentTime(Calendar cal) {
- SCRIPT_METHODS.setCurrentTime(cal);
- }
-
- /*************************************************************************
- * Only the first parameter is mandatory. All other parameters can be left
- * out therefore the meaning of the parameters changes. The method
- * definition shows the version with the most possible parameters filled.
- * The real meaning of the parameters is guessed from it's value. If "from"
- * and "to" are specified then the bounds are inclusive. If the "GMT"
- * parameter is specified, times are taken to be in GMT, otherwise the local
- * time zone is used.
- *
- * @param day1
- * is the day of month between 1 and 31 (as an integer).
- * @param month1
- * one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
- * @param year1
- * is the full year number, for example 1995 (but not 95).
- * Integer.
- * @param day2
- * is the day of month between 1 and 31 (as an integer).
- * @param month2
- * one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
- * @param year2
- * is the full year number, for example 1995 (but not 95).
- * Integer.
- * @param gmt
- * "GMT" for gmt time format else "undefined"
- * @return true if the current date matches the given range.
- ************************************************************************/
-
- public static boolean dateRange(Object day1, Object month1, Object year1,
- Object day2, Object month2, Object year2, Object gmt) {
- return SCRIPT_METHODS.dateRange(day1, month1, year1, day2, month2,
- year2, gmt);
- }
-
- /*************************************************************************
- * Some parameters can be left out therefore the meaning of the parameters
- * changes. The method definition shows the version with the most possible
- * parameters filled. The real meaning of the parameters is guessed from
- * it's value. If "from" and "to" are specified then the bounds are
- * inclusive. If the "GMT" parameter is specified, times are taken to be in
- * GMT, otherwise the local time zone is used.
- *
- *
- *
- * @param hour1
- * is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)
- * @param min1
- * minutes from 0 to 59.
- * @param sec1
- * seconds from 0 to 59.
- * @param hour2
- * is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)
- * @param min2
- * minutes from 0 to 59.
- * @param sec2
- * seconds from 0 to 59.
- * @param gmt
- * "GMT" for gmt time format else "undefined"
- * @return true if the current time matches the given range.
- ************************************************************************/
-
- public static boolean timeRange(Object hour1, Object min1, Object sec1,
- Object hour2, Object min2, Object sec2, Object gmt) {
- return SCRIPT_METHODS.timeRange(hour1, min1, sec1, hour2, min2, sec2,
- gmt);
- }
-
-}
diff --git a/app/src/main/java/com/btr/proxy/selector/pac/RhinoPacScriptParser.kt b/app/src/main/java/com/btr/proxy/selector/pac/RhinoPacScriptParser.kt
new file mode 100644
index 00000000..8065452c
--- /dev/null
+++ b/app/src/main/java/com/btr/proxy/selector/pac/RhinoPacScriptParser.kt
@@ -0,0 +1,112 @@
+package com.btr.proxy.selector.pac
+
+import android.util.Log
+import org.mozilla.javascript.Context
+import org.mozilla.javascript.ContextFactory
+import org.mozilla.javascript.Scriptable
+import org.mozilla.javascript.ScriptableObject
+import java.util.*
+
+class RhinoPacScriptParser(private val source: PacScriptSource) : ScriptableObject(), PacScriptParser {
+
+ companion object {
+ private const val serialVersionUID = 1L
+ private const val TAG = "ProxyDroid.PAC"
+
+ private val JS_FUNCTION_NAMES = arrayOf(
+ "shExpMatch", "dnsResolve", "isResolvable", "isInNet", "dnsDomainIs",
+ "isPlainHostName", "myIpAddress", "dnsDomainLevels", "localHostOrDomainIs",
+ "weekdayRange", "dateRange", "timeRange"
+ )
+
+ private val SCRIPT_METHODS = PacScriptMethods()
+
+ @JvmStatic
+ fun isPlainHostName(host: String): Boolean = SCRIPT_METHODS.isPlainHostName(host)
+
+ @JvmStatic
+ fun dnsDomainIs(host: String, domain: String): Boolean = SCRIPT_METHODS.dnsDomainIs(host, domain)
+
+ @JvmStatic
+ fun localHostOrDomainIs(host: String, domain: String): Boolean = SCRIPT_METHODS.localHostOrDomainIs(host, domain)
+
+ @JvmStatic
+ fun isResolvable(host: String): Boolean = SCRIPT_METHODS.isResolvable(host)
+
+ @JvmStatic
+ fun isInNet(host: String, pattern: String, mask: String): Boolean = SCRIPT_METHODS.isInNet(host, pattern, mask)
+
+ @JvmStatic
+ fun dnsResolve(host: String): String = SCRIPT_METHODS.dnsResolve(host)
+
+ @JvmStatic
+ fun myIpAddress(): String = SCRIPT_METHODS.myIpAddress()
+
+ @JvmStatic
+ fun dnsDomainLevels(host: String): Int = SCRIPT_METHODS.dnsDomainLevels(host)
+
+ @JvmStatic
+ fun shExpMatch(str: String, shexp: String): Boolean = SCRIPT_METHODS.shExpMatch(str, shexp)
+
+ @JvmStatic
+ fun weekdayRange(wd1: String, wd2: String?, gmt: String?): Boolean = SCRIPT_METHODS.weekdayRange(wd1, wd2, gmt)
+
+ @JvmStatic
+ fun setCurrentTime(cal: Calendar?) {
+ SCRIPT_METHODS.setCurrentTime(cal)
+ }
+
+ @JvmStatic
+ fun dateRange(day1: Any?, month1: Any?, year1: Any?, day2: Any?, month2: Any?, year2: Any?, gmt: Any?): Boolean {
+ return SCRIPT_METHODS.dateRange(day1, month1, year1, day2, month2, year2, gmt)
+ }
+
+ @JvmStatic
+ fun timeRange(hour1: Any?, min1: Any?, sec1: Any?, hour2: Any?, min2: Any?, sec2: Any?, gmt: Any?): Boolean {
+ return SCRIPT_METHODS.timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt)
+ }
+ }
+
+ private var scope: Scriptable? = null
+
+ init {
+ setupEngine()
+ }
+
+ @Throws(ProxyEvaluationException::class)
+ fun setupEngine() {
+ val context = ContextFactory().enterContext()
+ try {
+ defineFunctionProperties(JS_FUNCTION_NAMES, RhinoPacScriptParser::class.java, ScriptableObject.DONTENUM)
+ } catch (e: Exception) {
+ Log.e(TAG, "JS Engine setup error.", e)
+ throw ProxyEvaluationException(e.message, e)
+ }
+ scope = context.initStandardObjects(this)
+ }
+
+ override fun getScriptSource(): PacScriptSource = source
+
+ @Throws(ProxyEvaluationException::class)
+ override fun evaluate(url: String, host: String): String {
+ try {
+ val script = StringBuilder(source.getScriptContent())
+ val evalMethod = " ;FindProxyForURL (\"$url\",\"$host\")"
+ script.append(evalMethod)
+
+ val context = Context.enter()
+ context.optimizationLevel = -1
+ try {
+ val result = context.evaluateString(scope, script.toString(), "userPacFile", 1, null)
+ return Context.toString(result)
+ } finally {
+ Context.exit()
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "JS evaluation error.", e)
+ throw ProxyEvaluationException("Error while executing PAC script: ${e.message}", e)
+ }
+ }
+
+ override fun getClassName(): String = javaClass.simpleName
+}
diff --git a/app/src/main/java/com/btr/proxy/selector/pac/ScriptMethods.java b/app/src/main/java/com/btr/proxy/selector/pac/ScriptMethods.java
deleted file mode 100644
index b2bcdb40..00000000
--- a/app/src/main/java/com/btr/proxy/selector/pac/ScriptMethods.java
+++ /dev/null
@@ -1,265 +0,0 @@
-package com.btr.proxy.selector.pac;
-
-/***************************************************************************
- * Defines the public interface for PAC scripts.
- *
- * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
- ***************************************************************************/
-public interface ScriptMethods {
-
- public boolean isPlainHostName(String host);
-
- /*************************************************************************
- * Tests if an URL is in a given domain.
- *
- * @param host
- * is the host name from the URL.
- * @param domain
- * is the domain name to test the host name against.
- * @return true if the domain of host name matches.
- ************************************************************************/
-
- public boolean dnsDomainIs(String host, String domain);
-
- /*************************************************************************
- * Is true if the host name matches exactly the specified host name, or if
- * there is no domain name part in the host name, but the unqualified host
- * name matches.
- *
- * @param host
- * the host name from the URL.
- * @param domain
- * fully qualified host name with domain to match against.
- * @return true if matches else false.
- ************************************************************************/
-
- public boolean localHostOrDomainIs(String host, String domain);
-
- /*************************************************************************
- * Tries to resolve the host name. Returns true if succeeds.
- *
- * @param host
- * is the host name from the URL.
- * @return true if resolvable else false.
- ************************************************************************/
-
- public boolean isResolvable(String host);
-
- /*************************************************************************
- * Tries to resolve the host name. Returns true if succeeds to resolve the
- * host to an IPv4 or IPv6 address.
- *
- * @param host
- * is the host name from the URL.
- * @return true if resolvable else false.
- ************************************************************************/
-
- public boolean isResolvableEx(String host);
-
- /*************************************************************************
- * Returns true if the IP address of the host matches the specified IP
- * address pattern. Pattern and mask specification is done the same way as
- * for SOCKS configuration.
- *
- * Example: isInNet(host, "198.95.0.0", "255.255.0.0") is true if the IP
- * address of the host matches 198.95.*.*.
- *
- * @param host
- * a DNS host name, or IP address. If a host name is passed, it
- * will be resolved into an IP address by this function.
- * @param pattern
- * an IP address pattern in the dot-separated format.
- * @param mask
- * mask for the IP address pattern informing which parts of the
- * IP address should be matched against. 0 means ignore, 255
- * means match.
- * @return true if it matches else false.
- ************************************************************************/
-
- public boolean isInNet(String host, String pattern, String mask);
-
- /*************************************************************************
- * Extension of the isInNet method to support IPv6.
- *
- * @param ipAddress
- * an IP4 or IP6 address
- * @param ipPrefix
- * A string containing colon delimited IP prefix with top n bits
- * specified in the bit field (i.e. 3ffe:8311:ffff::/48 or
- * 123.112.0.0/16).
- * @return true if the host is in the given subnet, else false.
- ************************************************************************/
-
- public boolean isInNetEx(String ipAddress, String ipPrefix);
-
- /*************************************************************************
- * Resolves the given DNS host name into an IP address, and returns it in
- * the dot separated format as a string.
- *
- * @param host
- * the host to resolve.
- * @return the resolved IP, empty string if not resolvable.
- ************************************************************************/
-
- public String dnsResolve(String host);
-
- /*************************************************************************
- * @param host
- * the host to resolve
- * @return a semicolon separated list of IP6 and IP4 addresses the host name
- * resolves to, empty string if not resolvable.
- ************************************************************************/
-
- public String dnsResolveEx(String host);
-
- /*************************************************************************
- * Returns the IP address of the host that the process is running on, as a
- * string in the dot-separated integer format.
- *
- * @return an IP as string.
- ************************************************************************/
-
- public String myIpAddress();
-
- /*************************************************************************
- * Returns a list of IP4 and IP6 addresses of the host that the process is
- * running on. The list is separated with semicolons.
- *
- * @return the list, empty string if not available.
- ************************************************************************/
-
- public String myIpAddressEx();
-
- /*************************************************************************
- * Returns the number of DNS domain levels (number of dots) in the host
- * name.
- *
- * @param host
- * is the host name from the URL.
- * @return number of DNS domain levels.
- ************************************************************************/
-
- public int dnsDomainLevels(String host);
-
- /*************************************************************************
- * Returns true if the string matches the specified shell expression.
- * Actually, currently the patterns are shell expressions, not regular
- * expressions.
- *
- * @param str
- * is any string to compare (e.g. the URL, or the host name).
- * @param shexp
- * is a shell expression to compare against.
- * @return true if the string matches, else false.
- ************************************************************************/
-
- public boolean shExpMatch(String str, String shexp);
-
- /*************************************************************************
- * Only the first parameter is mandatory. Either the second, the third, or
- * both may be left out. If only one parameter is present, the function
- * yields a true value on the weekday that the parameter represents. If the
- * string "GMT" is specified as a second parameter, times are taken to be in
- * GMT, otherwise in local time zone. If both wd1 and wd2 are defined, the
- * condition is true if the current weekday is in between those two
- * weekdays. Bounds are inclusive. If the "GMT" parameter is specified,
- * times are taken to be in GMT, otherwise the local time zone is used.
- *
- * @param wd1
- * weekday 1 is one of SUN MON TUE WED THU FRI SAT
- * @param wd2
- * weekday 2 is one of SUN MON TUE WED THU FRI SAT
- * @param gmt
- * "GMT" for gmt time format else "undefined"
- * @return true if current day matches the criteria.
- ************************************************************************/
-
- public boolean weekdayRange(String wd1, String wd2, String gmt);
-
- /*************************************************************************
- * Only the first parameter is mandatory. All other parameters can be left
- * out therefore the meaning of the parameters changes. The method
- * definition shows the version with the most possible parameters filled.
- * The real meaning of the parameters is guessed from it's value. If "from"
- * and "to" are specified then the bounds are inclusive. If the "GMT"
- * parameter is specified, times are taken to be in GMT, otherwise the local
- * time zone is used.
- *
- * @param day1
- * is the day of month between 1 and 31 (as an integer).
- * @param month1
- * one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
- * @param year1
- * is the full year number, for example 1995 (but not 95).
- * Integer.
- * @param day2
- * is the day of month between 1 and 31 (as an integer).
- * @param month2
- * one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
- * @param year2
- * is the full year number, for example 1995 (but not 95).
- * Integer.
- * @param gmt
- * "GMT" for gmt time format else "undefined"
- * @return true if the current date matches the given range.
- ************************************************************************/
-
- public boolean dateRange(Object day1, Object month1, Object year1,
- Object day2, Object month2, Object year2, Object gmt);
-
- /*************************************************************************
- * Some parameters can be left out therefore the meaning of the parameters
- * changes. The method definition shows the version with the most possible
- * parameters filled. The real meaning of the parameters is guessed from
- * it's value. If "from" and "to" are specified then the bounds are
- * inclusive. If the "GMT" parameter is specified, times are taken to be in
- * GMT, otherwise the local time zone is used.
- *
- *
- *
- * @param hour1
- * is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)
- * @param min1
- * minutes from 0 to 59.
- * @param sec1
- * seconds from 0 to 59.
- * @param hour2
- * is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)
- * @param min2
- * minutes from 0 to 59.
- * @param sec2
- * seconds from 0 to 59.
- * @param gmt
- * "GMT" for gmt time format else "undefined"
- * @return true if the current time matches the given range.
- ************************************************************************/
-
- public boolean timeRange(Object hour1, Object min1, Object sec1,
- Object hour2, Object min2, Object sec2, Object gmt);
-
- /*************************************************************************
- * Sorts a list of IP4 and IP6 addresses. Separated by semicolon. Dual
- * addresses first, then IPv6 and last IPv4.
- *
- * @param ipAddressList
- * the address list.
- * @return the sorted list, empty string if sort is not possible
- ************************************************************************/
-
- public String sortIpAddressList(String ipAddressList);
-
- /*************************************************************************
- * Gets the version of the PAC extension that is available.
- *
- * @return the extension version, currently 1.0
- ************************************************************************/
-
- public String getClientVersion();
-
-}
diff --git a/app/src/main/java/com/btr/proxy/selector/pac/ScriptMethods.kt b/app/src/main/java/com/btr/proxy/selector/pac/ScriptMethods.kt
new file mode 100644
index 00000000..543eceb1
--- /dev/null
+++ b/app/src/main/java/com/btr/proxy/selector/pac/ScriptMethods.kt
@@ -0,0 +1,22 @@
+package com.btr.proxy.selector.pac
+
+interface ScriptMethods {
+ fun isPlainHostName(host: String): Boolean
+ fun dnsDomainIs(host: String, domain: String): Boolean
+ fun localHostOrDomainIs(host: String, domain: String): Boolean
+ fun isResolvable(host: String): Boolean
+ fun isResolvableEx(host: String): Boolean
+ fun isInNet(host: String, pattern: String, mask: String): Boolean
+ fun isInNetEx(ipAddress: String, ipPrefix: String): Boolean
+ fun dnsResolve(host: String): String
+ fun dnsResolveEx(host: String): String
+ fun myIpAddress(): String
+ fun myIpAddressEx(): String
+ fun dnsDomainLevels(host: String): Int
+ fun shExpMatch(str: String, shexp: String): Boolean
+ fun weekdayRange(wd1: String, wd2: String?, gmt: String?): Boolean
+ fun dateRange(day1: Any?, month1: Any?, year1: Any?, day2: Any?, month2: Any?, year2: Any?, gmt: Any?): Boolean
+ fun timeRange(hour1: Any?, min1: Any?, sec1: Any?, hour2: Any?, min2: Any?, sec2: Any?, gmt: Any?): Boolean
+ fun sortIpAddressList(ipAddressList: String): String
+ fun getClientVersion(): String
+}
diff --git a/app/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java b/app/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java
deleted file mode 100644
index 1bbfeb10..00000000
--- a/app/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java
+++ /dev/null
@@ -1,186 +0,0 @@
-package com.btr.proxy.selector.pac;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.Proxy;
-import java.net.URISyntaxException;
-import java.net.URL;
-
-import android.util.Log;
-
-/*****************************************************************************
- * Script source that will load the content of a PAC file from an webserver. The
- * script content is cached once it was downloaded.
- *
- * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
- ****************************************************************************/
-
-public class UrlPacScriptSource implements PacScriptSource {
-
- private final static String TAG = "ProxyDroid.PAC";
-
- private final String scriptUrl;
- private String scriptContent;
- private long expireAtMillis;
-
- /*************************************************************************
- * Constructor
- *
- * @param url
- * the URL to download the script from.
- ************************************************************************/
-
- public UrlPacScriptSource(String url) {
- super();
- this.expireAtMillis = 0;
- this.scriptUrl = url;
- }
-
- /*************************************************************************
- * getScriptContent
- *
- * @see com.btr.proxy.selector.pac.PacScriptSource#getScriptContent()
- ************************************************************************/
-
- @Override
- public synchronized String getScriptContent() throws IOException {
- if (this.scriptContent == null
- || (this.expireAtMillis > 0 && this.expireAtMillis > System
- .currentTimeMillis())) {
- try {
- if (this.scriptUrl.startsWith("file:/")
- || this.scriptUrl.indexOf(":/") == -1) {
- this.scriptContent = readPacFileContent(this.scriptUrl);
- } else {
- this.scriptContent = downloadPacContent(this.scriptUrl);
- }
- } catch (IOException e) {
- Log.e(TAG, "Loading script failed.", e);
- this.scriptContent = "";
- throw e;
- }
- }
- return this.scriptContent;
- }
-
- /*************************************************************************
- * Reads a PAC script from a local file.
- *
- * @param scriptUrl
- * @return the content of the script file.
- * @throws IOException
- * @throws URISyntaxException
- ************************************************************************/
-
- private String readPacFileContent(String scriptUrl) throws IOException {
- try {
- File file = null;
- if (scriptUrl.indexOf(":/") == -1) {
- file = new File(scriptUrl);
- } else {
- file = new File(new URL(scriptUrl).toURI());
- }
- BufferedReader r = new BufferedReader(new FileReader(file));
- StringBuilder result = new StringBuilder();
- try {
- String line;
- while ((line = r.readLine()) != null) {
- result.append(line).append("\n");
- }
- } finally {
- r.close();
- }
- return result.toString();
- } catch (Exception e) {
- Log.e(TAG, "File reading error.", e);
- throw new IOException(e.getMessage());
- }
- }
-
- /*************************************************************************
- * Downloads the script from a webserver.
- *
- * @param url
- * the URL to the script file.
- * @return the script content.
- * @throws IOException
- * on read error.
- ************************************************************************/
-
- private String downloadPacContent(String url) throws IOException {
- if (url == null) {
- throw new IOException("Invalid PAC script URL: null");
- }
-
- HttpURLConnection con = (HttpURLConnection) new URL(url)
- .openConnection(Proxy.NO_PROXY);
- con.setConnectTimeout(15 * 1000);
- con.setReadTimeout(20 * 1000);
- con.setInstanceFollowRedirects(true);
- con.setRequestProperty("accept",
- "application/x-ns-proxy-autoconfig, */*;q=0.8");
-
- if (con.getResponseCode() != 200) {
- throw new IOException("Server returned: " + con.getResponseCode()
- + " " + con.getResponseMessage());
- }
-
- // Read expire date.
- this.expireAtMillis = con.getExpiration();
-
- String charsetName = parseCharsetFromHeader(con.getContentType());
- BufferedReader r = new BufferedReader(new InputStreamReader(
- con.getInputStream(), charsetName));
- try {
- StringBuilder result = new StringBuilder();
- try {
- String line;
- while ((line = r.readLine()) != null) {
- result.append(line).append("\n");
- }
- } finally {
- r.close();
- con.disconnect();
- }
- return result.toString();
- } finally {
- r.close();
- }
- }
-
- /*************************************************************************
- * Response Content-Type could be something like this:
- * application/x-ns-proxy-autoconfig; charset=UTF-8
- *
- * @param contentType
- * header field.
- * @return the extracted charset if set else a default charset.
- ************************************************************************/
-
- String parseCharsetFromHeader(String contentType) {
- String result = "ISO-8859-1";
- if (contentType != null) {
- String[] paramList = contentType.split(";");
- for (String param : paramList) {
- if (param.toLowerCase().trim().startsWith("charset")
- && param.indexOf("=") != -1) {
- result = param.substring(param.indexOf("=") + 1).trim();
- }
- }
- }
- return result;
- }
-
- /***************************************************************************
- * @see java.lang.Object#toString()
- **************************************************************************/
- @Override
- public String toString() {
- return this.scriptUrl;
- }
-
-}
diff --git a/app/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.kt b/app/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.kt
new file mode 100644
index 00000000..70d42114
--- /dev/null
+++ b/app/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.kt
@@ -0,0 +1,110 @@
+package com.btr.proxy.selector.pac
+
+import android.util.Log
+import java.io.BufferedReader
+import java.io.File
+import java.io.FileReader
+import java.io.IOException
+import java.io.InputStreamReader
+import java.net.HttpURLConnection
+import java.net.URL
+
+class UrlPacScriptSource(private val scriptUrl: String) : PacScriptSource {
+
+ companion object {
+ private const val TAG = "ProxyDroid.PAC"
+ }
+
+ private var scriptContent: String? = null
+ private var expireAtMillis: Long = 0
+
+ @Synchronized
+ @Throws(IOException::class)
+ override fun getScriptContent(): String {
+ if (scriptContent == null || (expireAtMillis > 0 && expireAtMillis > System.currentTimeMillis())) {
+ try {
+ scriptContent = if (scriptUrl.startsWith("file:/") || !scriptUrl.contains(":/")) {
+ readPacFileContent(scriptUrl)
+ } else {
+ downloadPacContent(scriptUrl)
+ }
+ } catch (e: IOException) {
+ Log.e(TAG, "Loading script failed.", e)
+ scriptContent = ""
+ throw e
+ }
+ }
+ return scriptContent ?: ""
+ }
+
+ @Throws(IOException::class)
+ private fun readPacFileContent(scriptUrl: String): String {
+ try {
+ val file = if (!scriptUrl.contains(":/")) {
+ File(scriptUrl)
+ } else {
+ File(URL(scriptUrl).toURI())
+ }
+
+ BufferedReader(FileReader(file)).use { r ->
+ val result = StringBuilder()
+ var line: String?
+ while (r.readLine().also { line = it } != null) {
+ result.append(line).append("\n")
+ }
+ return result.toString()
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "File reading error.", e)
+ throw IOException(e.message)
+ }
+ }
+
+ @Throws(IOException::class)
+ private fun downloadPacContent(url: String?): String {
+ if (url == null) {
+ throw IOException("Invalid PAC script URL: null")
+ }
+
+ val con = URL(url).openConnection(java.net.Proxy.NO_PROXY) as HttpURLConnection
+ con.connectTimeout = 15 * 1000
+ con.readTimeout = 20 * 1000
+ con.instanceFollowRedirects = true
+ con.setRequestProperty("accept", "application/x-ns-proxy-autoconfig, */*;q=0.8")
+
+ if (con.responseCode != 200) {
+ throw IOException("Server returned: ${con.responseCode} ${con.responseMessage}")
+ }
+
+ expireAtMillis = con.expiration
+
+ val charsetName = parseCharsetFromHeader(con.contentType)
+ BufferedReader(InputStreamReader(con.inputStream, charsetName)).use { r ->
+ val result = StringBuilder()
+ try {
+ var line: String?
+ while (r.readLine().also { line = it } != null) {
+ result.append(line).append("\n")
+ }
+ } finally {
+ con.disconnect()
+ }
+ return result.toString()
+ }
+ }
+
+ internal fun parseCharsetFromHeader(contentType: String?): String {
+ var result = "ISO-8859-1"
+ if (contentType != null) {
+ val paramList = contentType.split(";")
+ for (param in paramList) {
+ if (param.lowercase().trim().startsWith("charset") && param.contains("=")) {
+ result = param.substring(param.indexOf("=") + 1).trim()
+ }
+ }
+ }
+ return result
+ }
+
+ override fun toString(): String = scriptUrl
+}
diff --git a/app/src/main/java/com/ksmaze/android/preference/ListPreferenceMultiSelect.java b/app/src/main/java/com/ksmaze/android/preference/ListPreferenceMultiSelect.java
deleted file mode 100644
index 131dbb66..00000000
--- a/app/src/main/java/com/ksmaze/android/preference/ListPreferenceMultiSelect.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/* proxydroid - Global / Individual Proxy App for Android
- * Copyright (C) 2011 K's Maze
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- */
-
-package com.ksmaze.android.preference;
-
-import android.app.AlertDialog.Builder;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.util.AttributeSet;
-
-/**
- * A {@link Preference} that displays a list of entries as a dialog and allows
- * multiple selections
- *
- * This preference will store a string into the SharedPreferences. This string
- * will be the values selected from the {@link #setEntryValues(CharSequence[])}
- * array.
- *
- */
-public class ListPreferenceMultiSelect extends ListPreference {
- // Need to make sure the SEPARATOR is unique and weird enough that it
- // doesn't match one of the entries.
- // Not using any fancy symbols because this is interpreted as a regex for
- // splitting strings.
- private static final String SEPARATOR = " , ";
-
- private boolean[] mClickedDialogEntryIndices;
-
- public ListPreferenceMultiSelect(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- mClickedDialogEntryIndices = new boolean[getEntries().length];
- }
-
- @Override
- public void setEntries(CharSequence[] entries) {
- super.setEntries(entries);
- mClickedDialogEntryIndices = new boolean[entries.length];
- }
-
- public ListPreferenceMultiSelect(Context context) {
- this(context, null);
- }
-
- @Override
- protected void onPrepareDialogBuilder(Builder builder) {
- CharSequence[] entries = getEntries();
- CharSequence[] entryValues = getEntryValues();
-
- if (entries == null || entryValues == null
- || entries.length != entryValues.length) {
- throw new IllegalStateException(
- "ListPreference requires an entries array and an entryValues array which are both the same length");
- }
-
- restoreCheckedEntries();
- builder.setMultiChoiceItems(entries, mClickedDialogEntryIndices,
- new DialogInterface.OnMultiChoiceClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which,
- boolean val) {
- mClickedDialogEntryIndices[which] = val;
- }
- });
- }
-
- public static String[] parseStoredValue(CharSequence val) {
- if (val == null)
- return null;
- if ("".equals(val))
- return null;
- else
- return ((String) val).split(SEPARATOR);
- }
-
- private void restoreCheckedEntries() {
- CharSequence[] entryValues = getEntryValues();
-
- String[] vals = parseStoredValue(getValue());
- if (vals != null) {
- for (String val1 : vals) {
- String val = val1.trim();
- for (int i = 0; i < entryValues.length; i++) {
- CharSequence entry = entryValues[i];
- if (entry.equals(val)) {
- mClickedDialogEntryIndices[i] = true;
- break;
- }
- }
- }
- }
- }
-
- @Override
- protected void onDialogClosed(boolean positiveResult) {
- // super.onDialogClosed(positiveResult);
-
- CharSequence[] entryValues = getEntryValues();
- if (positiveResult && entryValues != null) {
- StringBuffer value = new StringBuffer();
- for (int i = 0; i < entryValues.length; i++) {
- if (mClickedDialogEntryIndices[i]) {
- value.append(entryValues[i]).append(SEPARATOR);
- }
- }
-
- if (callChangeListener(value)) {
- String val = value.toString();
- if (val.length() > 0)
- val = val.substring(0, val.length() - SEPARATOR.length());
- setValue(val);
- }
- }
- }
-}
diff --git a/app/src/main/java/com/ksmaze/android/preference/ListPreferenceMultiSelect.kt b/app/src/main/java/com/ksmaze/android/preference/ListPreferenceMultiSelect.kt
new file mode 100644
index 00000000..11900e77
--- /dev/null
+++ b/app/src/main/java/com/ksmaze/android/preference/ListPreferenceMultiSelect.kt
@@ -0,0 +1,98 @@
+/* proxydroid - Global / Individual Proxy App for Android
+ * Copyright (C) 2011 K's Maze
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package com.ksmaze.android.preference
+
+import android.app.AlertDialog
+import android.content.Context
+import android.content.DialogInterface
+import android.preference.ListPreference
+import android.util.AttributeSet
+
+class ListPreferenceMultiSelect @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null
+) : ListPreference(context, attrs) {
+
+ companion object {
+ private const val SEPARATOR = " , "
+
+ @JvmStatic
+ fun parseStoredValue(value: CharSequence?): Array? {
+ if (value == null || value.toString().isEmpty()) {
+ return null
+ }
+ return value.toString().split(SEPARATOR).toTypedArray()
+ }
+ }
+
+ private var mClickedDialogEntryIndices: BooleanArray = BooleanArray(entries?.size ?: 0)
+
+ override fun setEntries(entries: Array?) {
+ super.setEntries(entries)
+ mClickedDialogEntryIndices = BooleanArray(entries?.size ?: 0)
+ }
+
+ override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
+ val entries = entries
+ val entryValues = entryValues
+
+ require(!(entries == null || entryValues == null || entries.size != entryValues.size)) {
+ "ListPreference requires an entries array and an entryValues array which are both the same length"
+ }
+
+ restoreCheckedEntries()
+ builder.setMultiChoiceItems(entries, mClickedDialogEntryIndices) { _, which, isChecked ->
+ mClickedDialogEntryIndices[which] = isChecked
+ }
+ }
+
+ private fun restoreCheckedEntries() {
+ val entryValues = entryValues ?: return
+ val vals = parseStoredValue(value) ?: return
+
+ for (v in vals) {
+ val trimmedVal = v.trim()
+ for (i in entryValues.indices) {
+ if (entryValues[i] == trimmedVal) {
+ mClickedDialogEntryIndices[i] = true
+ break
+ }
+ }
+ }
+ }
+
+ override fun onDialogClosed(positiveResult: Boolean) {
+ val entryValues = entryValues
+ if (positiveResult && entryValues != null) {
+ val valueBuilder = StringBuilder()
+ for (i in entryValues.indices) {
+ if (mClickedDialogEntryIndices[i]) {
+ valueBuilder.append(entryValues[i]).append(SEPARATOR)
+ }
+ }
+
+ if (callChangeListener(valueBuilder)) {
+ var finalValue = valueBuilder.toString()
+ if (finalValue.isNotEmpty()) {
+ finalValue = finalValue.substring(0, finalValue.length - SEPARATOR.length)
+ }
+ value = finalValue
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/org/proxydroid/AppManager.java b/app/src/main/java/org/proxydroid/AppManager.java
deleted file mode 100644
index 485632da..00000000
--- a/app/src/main/java/org/proxydroid/AppManager.java
+++ /dev/null
@@ -1,463 +0,0 @@
-/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
-/* See LICENSE for licensing information */
-
-package org.proxydroid;
-
-import android.app.Activity;
-import android.app.ProgressDialog;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.PixelFormat;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.preference.PreferenceManager;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.WindowManager;
-import android.widget.AbsListView;
-import android.widget.AbsListView.OnScrollListener;
-import android.widget.ArrayAdapter;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
-import android.widget.ImageView;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import org.proxydroid.utils.ImageLoader;
-import org.proxydroid.utils.ImageLoaderFactory;
-
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.StringTokenizer;
-import java.util.Vector;
-
-public class AppManager extends AppCompatActivity implements OnCheckedChangeListener,
- OnClickListener {
-
- private ProxyedApp[] apps = null;
-
- private ListView listApps;
-
- private AppManager mAppManager;
-
- private TextView overlay;
-
- private ProgressDialog pd = null;
- private ListAdapter adapter;
-
- private ImageLoader dm;
-
- private static final int MSG_LOAD_START = 1;
- private static final int MSG_LOAD_FINISH = 2;
-
- public final static String PREFS_KEY_PROXYED = "Proxyed";
-
- private boolean appsLoaded = false;
-
- final Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_LOAD_START:
- pd = ProgressDialog.show(AppManager.this, "",
- getString(R.string.loading), true, true);
- break;
- case MSG_LOAD_FINISH:
-
- listApps.setAdapter(adapter);
-
- listApps.setOnScrollListener(new OnScrollListener() {
-
- boolean visible;
-
- @Override
- public void onScrollStateChanged(AbsListView view,
- int scrollState) {
- visible = true;
- if (scrollState == ListView.OnScrollListener.SCROLL_STATE_IDLE) {
- overlay.setVisibility(View.INVISIBLE);
- }
- }
-
- @Override
- public void onScroll(AbsListView view,
- int firstVisibleItem, int visibleItemCount,
- int totalItemCount) {
- if (visible) {
- String name = apps[firstVisibleItem].getName();
- if (name != null && name.length() > 1)
- overlay.setText(apps[firstVisibleItem]
- .getName().substring(0, 1));
- else
- overlay.setText("*");
- overlay.setVisibility(View.VISIBLE);
- }
- }
- });
-
- if (pd != null) {
- pd.dismiss();
- pd = null;
- }
- break;
- }
- super.handleMessage(msg);
- }
- };
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- // app icon in action bar clicked; go home
-// Intent intent = new Intent(this, ProxyDroid.class);
-// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-// startActivity(intent);
- finish();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- this.setContentView(R.layout.layout_apps);
-
- dm = ImageLoaderFactory.getImageLoader(this);
-
- this.overlay = (TextView) View.inflate(this, R.layout.overlay, null);
- getWindowManager()
- .addView(
- overlay,
- new WindowManager.LayoutParams(
- LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_APPLICATION,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
- PixelFormat.TRANSLUCENT));
-
- mAppManager = this;
-
- }
-
- /** Called when the activity is closed. */
- @Override
- public void onDestroy() {
-
- getWindowManager().removeView(overlay);
-
- super.onDestroy();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
-
- new Thread() {
-
- @Override
- public void run() {
- handler.sendEmptyMessage(MSG_LOAD_START);
-
- listApps = (ListView) findViewById(R.id.applistview);
-
- if (!appsLoaded)
- loadApps();
- handler.sendEmptyMessage(MSG_LOAD_FINISH);
- }
- }.start();
-
- }
-
- private void loadApps() {
- getApps(this);
-
- Arrays.sort(apps, new Comparator() {
- @Override
- public int compare(ProxyedApp o1, ProxyedApp o2) {
- if (o1 == null || o2 == null || o1.getName() == null
- || o2.getName() == null)
- return 1;
- if (o1.isProxyed() == o2.isProxyed())
- return o1.getName().compareTo(o2.getName());
- if (o1.isProxyed())
- return -1;
- return 1;
- }
- });
-
- final LayoutInflater inflater = getLayoutInflater();
-
- adapter = new ArrayAdapter(this, R.layout.layout_apps_item,
- R.id.itemtext, apps) {
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ListEntry entry;
- if (convertView == null) {
- // Inflate a new view
- convertView = inflater.inflate(R.layout.layout_apps_item,
- parent, false);
- entry = new ListEntry();
- entry.icon = (ImageView) convertView
- .findViewById(R.id.itemicon);
- entry.box = (CheckBox) convertView
- .findViewById(R.id.itemcheck);
- entry.text = (TextView) convertView
- .findViewById(R.id.itemtext);
-
- entry.text.setOnClickListener(mAppManager);
-
- convertView.setTag(entry);
-
- entry.box.setOnCheckedChangeListener(mAppManager);
- } else {
- // Convert an existing view
- entry = (ListEntry) convertView.getTag();
- }
-
- final ProxyedApp app = apps[position];
-
- entry.icon.setTag(app.getUid());
-
- dm.DisplayImage(app.getUid(),
- (Activity) convertView.getContext(), entry.icon);
-
- entry.text.setText(app.getName());
-
- final CheckBox box = entry.box;
- box.setTag(app);
- box.setChecked(app.isProxyed());
-
- entry.text.setTag(box);
-
- return convertView;
- }
- };
-
- appsLoaded = true;
-
- }
-
- private static class ListEntry {
- private CheckBox box;
- private TextView text;
- private ImageView icon;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see android.app.Activity#onStop()
- */
- @Override
- protected void onStop() {
- super.onStop();
-
- // Log.d(getClass().getName(),"Exiting Preferences");
- }
-
- public static ProxyedApp[] getProxyedApps(Context context, boolean self) {
-
- SharedPreferences prefs = PreferenceManager
- .getDefaultSharedPreferences(context);
-
- String tordAppString = prefs.getString(PREFS_KEY_PROXYED, "");
- String[] tordApps;
-
- StringTokenizer st = new StringTokenizer(tordAppString, "|");
- tordApps = new String[st.countTokens()];
- int tordIdx = 0;
- while (st.hasMoreTokens()) {
- tordApps[tordIdx++] = st.nextToken();
- }
-
- Arrays.sort(tordApps);
-
- // else load the apps up
- PackageManager pMgr = context.getPackageManager();
-
- List lAppInfo = pMgr.getInstalledApplications(0);
-
- Iterator itAppInfo = lAppInfo.iterator();
-
- Vector vectorApps = new Vector();
-
- ApplicationInfo aInfo = null;
-
- int appIdx = 0;
-
- while (itAppInfo.hasNext()) {
- aInfo = itAppInfo.next();
-
- // ignore all system apps
- if (aInfo.uid < 10000)
- continue;
-
- ProxyedApp app = new ProxyedApp();
-
- app.setUid(aInfo.uid);
-
- app.setUsername(pMgr.getNameForUid(app.getUid()));
-
- // check if this application is allowed
- if (aInfo.packageName != null
- && aInfo.packageName.equals("org.proxydroid")) {
- if (self)
- app.setProxyed(true);
- } else if (Arrays.binarySearch(tordApps, app.getUsername()) >= 0) {
- app.setProxyed(true);
- } else {
- app.setProxyed(false);
- }
-
- if (app.isProxyed())
- vectorApps.add(app);
-
- }
-
- ProxyedApp[] apps = new ProxyedApp[vectorApps.size()];
- vectorApps.toArray(apps);
- return apps;
- }
-
- public void getApps(Context context) {
-
- SharedPreferences prefs = PreferenceManager
- .getDefaultSharedPreferences(context);
-
- String tordAppString = prefs.getString(PREFS_KEY_PROXYED, "");
- String[] tordApps;
-
- StringTokenizer st = new StringTokenizer(tordAppString, "|");
- tordApps = new String[st.countTokens()];
- int tordIdx = 0;
- while (st.hasMoreTokens()) {
- tordApps[tordIdx++] = st.nextToken();
- }
-
- Arrays.sort(tordApps);
-
- Vector vectorApps = new Vector();
-
- // else load the apps up
- PackageManager pMgr = context.getPackageManager();
-
- List lAppInfo = pMgr.getInstalledApplications(0);
-
- Iterator itAppInfo = lAppInfo.iterator();
-
- ApplicationInfo aInfo = null;
-
- while (itAppInfo.hasNext()) {
- aInfo = itAppInfo.next();
-
- // ignore system apps
- if (aInfo.uid < 10000)
- continue;
-
- if (aInfo.processName == null)
- continue;
- if (pMgr.getApplicationLabel(aInfo) == null
- || pMgr.getApplicationLabel(aInfo).toString().equals(""))
- continue;
- if (pMgr.getApplicationIcon(aInfo) == null)
- continue;
-
- ProxyedApp tApp = new ProxyedApp();
-
- tApp.setEnabled(aInfo.enabled);
- tApp.setUid(aInfo.uid);
- tApp.setUsername(pMgr.getNameForUid(tApp.getUid()));
- tApp.setProcname(aInfo.processName);
- tApp.setName(pMgr.getApplicationLabel(aInfo).toString());
-
- // check if this application is allowed
- if (Arrays.binarySearch(tordApps, tApp.getUsername()) >= 0) {
- tApp.setProxyed(true);
- } else {
- tApp.setProxyed(false);
- }
-
- vectorApps.add(tApp);
- }
-
- apps = new ProxyedApp[vectorApps.size()];
- vectorApps.toArray(apps);
-
- }
-
- public void saveAppSettings(Context context) {
- if (apps == null)
- return;
-
- SharedPreferences prefs = PreferenceManager
- .getDefaultSharedPreferences(this);
-
- // final SharedPreferences prefs =
- // context.getSharedPreferences(PREFS_KEY, 0);
-
- StringBuilder tordApps = new StringBuilder();
-
- for (int i = 0; i < apps.length; i++) {
- if (apps[i].isProxyed()) {
- tordApps.append(apps[i].getUsername());
- tordApps.append("|");
- }
- }
-
- Editor edit = prefs.edit();
- edit.putString(PREFS_KEY_PROXYED, tordApps.toString());
- edit.commit();
-
- }
-
- /**
- * Called an application is check/unchecked
- */
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- final ProxyedApp app = (ProxyedApp) buttonView.getTag();
- if (app != null) {
- app.setProxyed(isChecked);
- }
-
- saveAppSettings(this);
-
- }
-
- @Override
- public void onClick(View v) {
-
- CheckBox cbox = (CheckBox) v.getTag();
-
- final ProxyedApp app = (ProxyedApp) cbox.getTag();
- if (app != null) {
- app.setProxyed(!app.isProxyed());
- cbox.setChecked(app.isProxyed());
- }
-
- saveAppSettings(this);
-
- }
-
-}
diff --git a/app/src/main/java/org/proxydroid/AppManager.kt b/app/src/main/java/org/proxydroid/AppManager.kt
new file mode 100644
index 00000000..b9506e49
--- /dev/null
+++ b/app/src/main/java/org/proxydroid/AppManager.kt
@@ -0,0 +1,278 @@
+/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
+/* See LICENSE for licensing information */
+
+package org.proxydroid
+
+import android.app.Activity
+import android.app.ProgressDialog
+import android.content.Context
+import android.graphics.PixelFormat
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowManager
+import android.widget.*
+import androidx.appcompat.app.AppCompatActivity
+import android.preference.PreferenceManager
+import org.proxydroid.utils.ImageLoader
+import org.proxydroid.utils.ImageLoaderFactory
+import java.util.*
+
+class AppManager : AppCompatActivity(), CompoundButton.OnCheckedChangeListener, View.OnClickListener {
+
+ private var apps: Array? = null
+ private lateinit var listApps: ListView
+ private lateinit var overlay: TextView
+ private var pd: ProgressDialog? = null
+ private var adapter: ListAdapter? = null
+ private lateinit var dm: ImageLoader
+ private var appsLoaded = false
+
+ companion object {
+ private const val MSG_LOAD_START = 1
+ private const val MSG_LOAD_FINISH = 2
+ const val PREFS_KEY_PROXYED = "Proxyed"
+
+ @JvmStatic
+ fun getProxyedApps(context: Context, self: Boolean): Array {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ val tordAppString = prefs.getString(PREFS_KEY_PROXYED, "") ?: ""
+ val st = StringTokenizer(tordAppString, "|")
+ val tordApps = Array(st.countTokens()) { st.nextToken() }
+ Arrays.sort(tordApps)
+
+ val pMgr = context.packageManager
+ val lAppInfo = pMgr.getInstalledApplications(0)
+ val vectorApps = Vector()
+
+ for (aInfo in lAppInfo) {
+ if (aInfo.uid < 10000) continue
+
+ val app = ProxyedApp().apply {
+ uid = aInfo.uid
+ username = pMgr.getNameForUid(uid)
+ isProxyed = when {
+ aInfo.packageName == "org.proxydroid" -> self
+ username != null && Arrays.binarySearch(tordApps, username) >= 0 -> true
+ else -> false
+ }
+ }
+
+ if (app.isProxyed) {
+ vectorApps.add(app)
+ }
+ }
+
+ return vectorApps.toTypedArray()
+ }
+ }
+
+ private val handler = object : Handler(Looper.getMainLooper()) {
+ override fun handleMessage(msg: Message) {
+ when (msg.what) {
+ MSG_LOAD_START -> {
+ pd = ProgressDialog.show(this@AppManager, "", getString(R.string.loading), true, true)
+ }
+ MSG_LOAD_FINISH -> {
+ listApps.adapter = adapter
+ listApps.setOnScrollListener(object : AbsListView.OnScrollListener {
+ var visible = false
+
+ override fun onScrollStateChanged(view: AbsListView, scrollState: Int) {
+ visible = true
+ if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) {
+ overlay.visibility = View.INVISIBLE
+ }
+ }
+
+ override fun onScroll(view: AbsListView, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) {
+ if (visible && apps != null && firstVisibleItem < apps!!.size) {
+ val name = apps!![firstVisibleItem].name
+ overlay.text = if (name != null && name.length > 1) name.substring(0, 1) else "*"
+ overlay.visibility = View.VISIBLE
+ }
+ }
+ })
+
+ pd?.dismiss()
+ pd = null
+ }
+ }
+ super.handleMessage(msg)
+ }
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ return when (item.itemId) {
+ android.R.id.home -> {
+ finish()
+ true
+ }
+ else -> super.onOptionsItemSelected(item)
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ setContentView(R.layout.layout_apps)
+
+ dm = ImageLoaderFactory.getImageLoader(this)
+
+ overlay = View.inflate(this, R.layout.overlay, null) as TextView
+ windowManager.addView(
+ overlay,
+ WindowManager.LayoutParams(
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_APPLICATION,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
+ PixelFormat.TRANSLUCENT
+ )
+ )
+ }
+
+ override fun onDestroy() {
+ windowManager.removeView(overlay)
+ super.onDestroy()
+ }
+
+ override fun onResume() {
+ super.onResume()
+
+ Thread {
+ handler.sendEmptyMessage(MSG_LOAD_START)
+ listApps = findViewById(R.id.applistview)
+ if (!appsLoaded) loadApps()
+ handler.sendEmptyMessage(MSG_LOAD_FINISH)
+ }.start()
+ }
+
+ private fun loadApps() {
+ getApps(this)
+
+ apps?.sortWith { o1, o2 ->
+ when {
+ o1 == null || o2 == null || o1.name == null || o2.name == null -> 1
+ o1.isProxyed == o2.isProxyed -> o1.name!!.compareTo(o2.name!!)
+ o1.isProxyed -> -1
+ else -> 1
+ }
+ }
+
+ val inflater = layoutInflater
+
+ adapter = object : ArrayAdapter(this, R.layout.layout_apps_item, R.id.itemtext, apps!!) {
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val entry: ListEntry
+ val view: View
+
+ if (convertView == null) {
+ view = inflater.inflate(R.layout.layout_apps_item, parent, false)
+ entry = ListEntry(
+ view.findViewById(R.id.itemicon),
+ view.findViewById(R.id.itemcheck),
+ view.findViewById(R.id.itemtext)
+ )
+ entry.text.setOnClickListener(this@AppManager)
+ view.tag = entry
+ entry.box.setOnCheckedChangeListener(this@AppManager)
+ } else {
+ view = convertView
+ entry = view.tag as ListEntry
+ }
+
+ val app = apps!![position]
+ entry.icon.tag = app.uid
+ dm.displayImage(app.uid, view.context as Activity, entry.icon)
+ entry.text.text = app.name
+ entry.box.tag = app
+ entry.box.isChecked = app.isProxyed
+ entry.text.tag = entry.box
+
+ return view
+ }
+ }
+
+ appsLoaded = true
+ }
+
+ private data class ListEntry(
+ val icon: ImageView,
+ val box: CheckBox,
+ val text: TextView
+ )
+
+ override fun onStop() {
+ super.onStop()
+ }
+
+ private fun getApps(context: Context) {
+ val prefs = PreferenceManager.getDefaultSharedPreferences(context)
+ val tordAppString = prefs.getString(PREFS_KEY_PROXYED, "") ?: ""
+ val st = StringTokenizer(tordAppString, "|")
+ val tordApps = Array(st.countTokens()) { st.nextToken() }
+ Arrays.sort(tordApps)
+
+ val vectorApps = Vector()
+ val pMgr = context.packageManager
+ val lAppInfo = pMgr.getInstalledApplications(0)
+
+ for (aInfo in lAppInfo) {
+ if (aInfo.uid < 10000) continue
+ if (aInfo.processName == null) continue
+ val label = pMgr.getApplicationLabel(aInfo)
+ if (label == null || label.toString().isEmpty()) continue
+ if (pMgr.getApplicationIcon(aInfo) == null) continue
+
+ val tApp = ProxyedApp().apply {
+ isEnabled = aInfo.enabled
+ uid = aInfo.uid
+ username = pMgr.getNameForUid(uid)
+ procname = aInfo.processName
+ name = label.toString()
+ isProxyed = username != null && Arrays.binarySearch(tordApps, username) >= 0
+ }
+ vectorApps.add(tApp)
+ }
+
+ apps = vectorApps.toTypedArray()
+ }
+
+ fun saveAppSettings(context: Context) {
+ val currentApps = apps ?: return
+ val prefs = PreferenceManager.getDefaultSharedPreferences(this)
+
+ val tordApps = StringBuilder()
+ for (app in currentApps) {
+ if (app.isProxyed) {
+ tordApps.append(app.username)
+ tordApps.append("|")
+ }
+ }
+
+ prefs.edit().putString(PREFS_KEY_PROXYED, tordApps.toString()).apply()
+ }
+
+ override fun onCheckedChanged(buttonView: CompoundButton, isChecked: Boolean) {
+ val app = buttonView.tag as? ProxyedApp
+ app?.isProxyed = isChecked
+ saveAppSettings(this)
+ }
+
+ override fun onClick(v: View) {
+ val cbox = v.tag as CheckBox
+ val app = cbox.tag as? ProxyedApp
+ app?.let {
+ it.isProxyed = !it.isProxyed
+ cbox.isChecked = it.isProxyed
+ }
+ saveAppSettings(this)
+ }
+
+}
diff --git a/app/src/main/java/org/proxydroid/BypassListActivity.java b/app/src/main/java/org/proxydroid/BypassListActivity.java
deleted file mode 100644
index 56f1ca59..00000000
--- a/app/src/main/java/org/proxydroid/BypassListActivity.java
+++ /dev/null
@@ -1,533 +0,0 @@
-/* proxydroid - Global / Individual Proxy App for Android
- * Copyright (C) 2011 Max Lv
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- *
- * ___====-_ _-====___
- * _--^^^#####// \\#####^^^--_
- * _-^##########// ( ) \\##########^-_
- * -############// |\^^/| \\############-
- * _/############// (@::@) \\############\_
- * /#############(( \\// ))#############\
- * -###############\\ (oo) //###############-
- * -#################\\ / VV \ //#################-
- * -###################\\/ \//###################-
- * _#/|##########/\######( /\ )######/\##########|\#_
- * |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
- * ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
- * ` ` ` ` / | | | | \ ' ' ' '
- * ( | | | | )
- * __\ | | | | /__
- * (vvv(VVV)(VVV)vvv)
- *
- * HERE BE DRAGONS
- *
- */
-
-package org.proxydroid;
-
-import android.app.AlertDialog;
-import android.app.ProgressDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.preference.PreferenceManager;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AdapterView.OnItemLongClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.EditText;
-import android.widget.ListAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import org.proxydroid.utils.Constraints;
-import org.proxydroid.utils.Utils;
-
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-
-public class BypassListActivity extends AppCompatActivity implements
- OnClickListener, OnItemClickListener, OnItemLongClickListener {
-
- private static final String TAG = BypassListActivity.class.getName();
-
- private static final int MSG_ERR_ADDR = 0;
- private static final int MSG_ADD_ADDR = 1;
- private static final int MSG_EDIT_ADDR = 2;
- private static final int MSG_DEL_ADDR = 3;
- private static final int MSG_PRESET_ADDR = 4;
- private static final int MSG_IMPORT_ADDR = 5;
- private static final int MSG_EXPORT_ADDR = 6;
-
- private ListAdapter adapter;
- private ArrayList bypassList;
- private Profile profile = new Profile();
-
- final Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- String addr;
- switch (msg.what) {
- case MSG_ERR_ADDR:
- Toast.makeText(BypassListActivity.this, R.string.err_addr,
- Toast.LENGTH_LONG).show();
- break;
- case MSG_ADD_ADDR:
- if (msg.obj == null)
- return;
- addr = (String) msg.obj;
- bypassList.add(addr);
- break;
- case MSG_EDIT_ADDR:
- if (msg.obj == null)
- return;
- addr = (String) msg.obj;
- bypassList.set(msg.arg1, addr);
- break;
- case MSG_DEL_ADDR:
- bypassList.remove(msg.arg1);
- break;
- case MSG_PRESET_ADDR:
- String[] list = Constraints.PRESETS[msg.arg1];
- reset(list);
- return;
- case MSG_EXPORT_ADDR:
- if (msg.obj == null)
- return;
- Toast.makeText(BypassListActivity.this,
- getString(R.string.exporting) + " " + (String) msg.obj,
- Toast.LENGTH_LONG).show();
- return;
- }
- refreshList();
- super.handleMessage(msg);
- }
- };
-
- @Override
- public void onClick(View arg0) {
- int id = arg0.getId();
- if (id == R.id.addBypassAddr) {
- editAddr(MSG_ADD_ADDR, -1);
- } else if (id == R.id.presetBypassAddr) {
- presetAddr();
- } else if (id == R.id.importBypassAddr) {
- importAddr();
- } else if (id == R.id.exportBypassAddr) {
- exportAddr();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- // app icon in action bar clicked; go home
-// Intent intent = new Intent(this, ProxyDroid.class);
-// intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-// startActivity(intent);
- finish();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
-
- super.onCreate(savedInstanceState);
-
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- setContentView(R.layout.bypass_list);
- TextView addButton = (TextView) findViewById(R.id.addBypassAddr);
- addButton.setOnClickListener(this);
-
- TextView presetButton = (TextView) findViewById(R.id.presetBypassAddr);
- presetButton.setOnClickListener(this);
-
- TextView importButton = (TextView) findViewById(R.id.importBypassAddr);
- importButton.setOnClickListener(this);
-
- TextView exportButton = (TextView) findViewById(R.id.exportBypassAddr);
- exportButton.setOnClickListener(this);
-
- refreshList();
- }
-
- @Override
- public void onItemClick(AdapterView> parent, View view, int position,
- long id) {
- editAddr(MSG_EDIT_ADDR, position);
- }
-
- @Override
- public boolean onItemLongClick(AdapterView> parent, View view,
- int position, long id) {
- delAddr(position);
- return true;
- }
-
- private void presetAddr() {
- AlertDialog ad = new AlertDialog.Builder(this)
- .setTitle(R.string.preset_button)
- .setNegativeButton(R.string.alert_dialog_cancel,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int whichButton) {
- /* User clicked Cancel so do some stuff */
- }
- })
- .setSingleChoiceItems(R.array.presets_list, -1,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int which) {
- if (which >= 0
- && which < Constraints.PRESETS.length) {
- Message msg = new Message();
- msg.what = MSG_PRESET_ADDR;
- msg.arg1 = which;
- handler.sendMessage(msg);
- }
- dialog.dismiss();
- }
- }).create();
- ad.show();
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == Constraints.IMPORT_REQUEST) {
- if (resultCode == RESULT_OK) {
- if (data == null)
- return;
- final String path = data.getStringExtra(Constraints.FILE_PATH);
- if (path == null || path.equals(""))
- return;
-
- final ProgressDialog pd = ProgressDialog.show(this, "",
- getString(R.string.importing), true, true);
-
- final Handler h = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- refreshList();
- if (pd != null) {
- pd.dismiss();
- }
- }
- };
-
- new Thread() {
- @Override
- public void run() {
- FileInputStream input;
- try {
- input = new FileInputStream(path);
- BufferedReader br = new BufferedReader(
- new InputStreamReader(input));
- bypassList.clear();
- while (true) {
- String line = br.readLine();
- if (line == null)
- break;
- String addr = Profile.validateAddr(line);
- if (addr != null)
- bypassList.add(addr);
- }
- br.close();
- input.close();
- } catch (FileNotFoundException e) {
- Log.e(TAG, "error to open file", e);
- } catch (IOException e) {
- Log.e(TAG, "error to read file", e);
- }
- h.sendEmptyMessage(MSG_IMPORT_ADDR);
- }
- }.start();
- }
- }
- }
-
- private void importAddr() {
- startActivityForResult(new Intent(this, FileChooser.class),
- Constraints.IMPORT_REQUEST);
- }
-
- private void exportAddr() {
- if (profile == null)
- return;
-
- LayoutInflater factory = LayoutInflater.from(this);
- final View textEntryView = factory.inflate(
- R.layout.alert_dialog_text_entry, null);
- final EditText path = (EditText) textEntryView
- .findViewById(R.id.text_edit);
-
- path.setText(Utils.getDataPath(this) + "/" + profile.getHost() + ".opt");
-
- AlertDialog ad = new AlertDialog.Builder(this)
- .setTitle(R.string.export_button)
- .setView(textEntryView)
- .setPositiveButton(R.string.alert_dialog_ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int whichButton) {
- if (path.getText() == null
- || path.getText().toString() == null)
- dialog.dismiss();
- new Thread() {
- @Override
- public void run() {
-
- FileOutputStream output;
- try {
- File file = new File(path.getText()
- .toString());
- if (!file.exists())
- file.createNewFile();
-
- output = new FileOutputStream(file);
- BufferedOutputStream bw = new BufferedOutputStream(
- output);
- for (String addr : bypassList) {
- addr = Profile
- .validateAddr(addr);
- if (addr != null)
- bw.write((addr + "\n")
- .getBytes());
- }
-
- bw.flush();
- bw.close();
- output.flush();
- output.close();
- } catch (FileNotFoundException e) {
- Log.e(TAG, "error to open file", e);
- } catch (IOException e) {
- Log.e(TAG, "error to write file", e);
- }
-
- Message msg = new Message();
- msg.what = MSG_EXPORT_ADDR;
- msg.obj = path.getText().toString();
- handler.sendMessage(msg);
- }
- }.start();
-
- }
- })
- .setNegativeButton(R.string.alert_dialog_cancel,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int whichButton) {
- /* User clicked cancel so do some stuff */
- }
- }).create();
- ad.show();
- }
-
- private void delAddr(final int idx) {
-
- final String addr = bypassList.get(idx);
-
- AlertDialog ad = new AlertDialog.Builder(this)
- .setTitle(addr)
- .setMessage(R.string.bypass_del_text)
- .setPositiveButton(R.string.alert_dialog_ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int whichButton) {
- /* User clicked OK so do some stuff */
- Message msg = new Message();
- msg.what = MSG_DEL_ADDR;
- msg.arg1 = idx;
- msg.obj = addr;
- handler.sendMessage(msg);
- }
- })
- .setNegativeButton(R.string.alert_dialog_cancel,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int whichButton) {
- /* User clicked Cancel so do some stuff */
- }
- }).create();
-
- ad.show();
- }
-
- private void editAddr(final int msg, final int idx) {
- LayoutInflater factory = LayoutInflater.from(this);
- final View textEntryView = factory.inflate(
- R.layout.alert_dialog_text_entry, null);
- final EditText addrText = (EditText) textEntryView
- .findViewById(R.id.text_edit);
-
- if (msg == MSG_EDIT_ADDR)
- addrText.setText(bypassList.get(idx));
- else if (msg == MSG_ADD_ADDR)
- addrText.setText("0.0.0.0/0");
-
- AlertDialog ad = new AlertDialog.Builder(this)
- .setTitle(R.string.bypass_edit_title)
- .setView(textEntryView)
- .setPositiveButton(R.string.alert_dialog_ok,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int whichButton) {
- /* User clicked OK so do some stuff */
-
- new Thread() {
- @Override
- public void run() {
- EditText addrText = (EditText) textEntryView
- .findViewById(R.id.text_edit);
- String addr = addrText.getText()
- .toString();
- addr = Profile.validateAddr(addr);
- if (addr != null) {
- Message m = new Message();
- m.what = msg;
- m.arg1 = idx;
- m.obj = addr;
- handler.sendMessage(m);
- } else {
- handler.sendEmptyMessage(MSG_ERR_ADDR);
- }
- }
- }.start();
-
- }
- })
- .setNegativeButton(R.string.alert_dialog_cancel,
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,
- int whichButton) {
-
- /* User clicked cancel so do some stuff */
- }
- }).create();
- ad.show();
- }
-
- private void reset(final String[] list) {
-
- final ProgressDialog pd = ProgressDialog.show(this, "",
- getString(R.string.reseting), true, true);
-
- final Handler h = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- refreshList();
- if (pd != null) {
- pd.dismiss();
- }
- }
- };
-
- new Thread() {
- @Override
- public void run() {
- bypassList.clear();
- for (String addr : list) {
- addr = Profile.validateAddr(addr);
- if (addr != null)
- bypassList.add(addr);
- }
- h.sendEmptyMessage(0);
- }
- }.start();
- }
-
- private void refreshList() {
-
- SharedPreferences settings = PreferenceManager
- .getDefaultSharedPreferences(this);
-
- profile.getProfile(settings);
-
- if (bypassList != null) {
- profile.setBypassAddrs(Profile.encodeAddrs(bypassList
- .toArray(new String[bypassList.size()])));
- profile.setProfile(settings);
- }
-
- String[] addrs = Profile.decodeAddrs(profile.getBypassAddrs());
- bypassList = new ArrayList();
-
- for (String addr : addrs) {
- bypassList.add(addr);
- // Log.d(TAG, addr);
- }
-
- final LayoutInflater inflater = getLayoutInflater();
-
- adapter = new ArrayAdapter(this, R.layout.bypass_list_item,
- R.id.bypasslistItemText, bypassList) {
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- String addr;
- if (convertView == null) {
- // Inflate a new view
- convertView = inflater.inflate(R.layout.bypass_list_item,
- parent, false);
- }
-
- TextView item = (TextView) convertView
- .findViewById(R.id.bypasslistItemText);
- addr = bypassList.get(position);
- if (addr != null)
- item.setText(addr);
-
- return convertView;
- }
- };
-
- ListView list = (ListView) findViewById(R.id.BypassListView);
- list.setAdapter(adapter);
- list.setOnItemClickListener(this);
- list.setOnItemLongClickListener(this);
- }
-}
diff --git a/app/src/main/java/org/proxydroid/BypassListActivity.kt b/app/src/main/java/org/proxydroid/BypassListActivity.kt
new file mode 100644
index 00000000..38e44b6b
--- /dev/null
+++ b/app/src/main/java/org/proxydroid/BypassListActivity.kt
@@ -0,0 +1,337 @@
+/* proxydroid - Global / Individual Proxy App for Android
+ * Copyright (C) 2011 Max Lv
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.proxydroid
+
+import android.app.AlertDialog
+import android.app.ProgressDialog
+import android.content.Intent
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.widget.*
+import androidx.appcompat.app.AppCompatActivity
+import android.preference.PreferenceManager
+import org.proxydroid.utils.Constraints
+import org.proxydroid.utils.Utils
+import java.io.*
+
+class BypassListActivity : AppCompatActivity(), View.OnClickListener,
+ AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {
+
+ companion object {
+ private val TAG = BypassListActivity::class.java.name
+ private const val MSG_ERR_ADDR = 0
+ private const val MSG_ADD_ADDR = 1
+ private const val MSG_EDIT_ADDR = 2
+ private const val MSG_DEL_ADDR = 3
+ private const val MSG_PRESET_ADDR = 4
+ private const val MSG_IMPORT_ADDR = 5
+ private const val MSG_EXPORT_ADDR = 6
+ }
+
+ private var adapter: ListAdapter? = null
+ private var bypassList: ArrayList = ArrayList()
+ private val profile = Profile()
+
+ private val handler = object : Handler(Looper.getMainLooper()) {
+ override fun handleMessage(msg: Message) {
+ when (msg.what) {
+ MSG_ERR_ADDR -> {
+ Toast.makeText(this@BypassListActivity, R.string.err_addr, Toast.LENGTH_LONG).show()
+ }
+ MSG_ADD_ADDR -> {
+ val addr = msg.obj as? String ?: return
+ bypassList.add(addr)
+ }
+ MSG_EDIT_ADDR -> {
+ val addr = msg.obj as? String ?: return
+ bypassList[msg.arg1] = addr
+ }
+ MSG_DEL_ADDR -> {
+ bypassList.removeAt(msg.arg1)
+ }
+ MSG_PRESET_ADDR -> {
+ val list = Constraints.PRESETS[msg.arg1]
+ reset(list)
+ return
+ }
+ MSG_EXPORT_ADDR -> {
+ val path = msg.obj as? String ?: return
+ Toast.makeText(this@BypassListActivity, "${getString(R.string.exporting)} $path", Toast.LENGTH_LONG).show()
+ return
+ }
+ }
+ refreshList()
+ super.handleMessage(msg)
+ }
+ }
+
+ override fun onClick(arg0: View) {
+ when (arg0.id) {
+ R.id.addBypassAddr -> editAddr(MSG_ADD_ADDR, -1)
+ R.id.presetBypassAddr -> presetAddr()
+ R.id.importBypassAddr -> importAddr()
+ R.id.exportBypassAddr -> exportAddr()
+ }
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ return when (item.itemId) {
+ android.R.id.home -> {
+ finish()
+ true
+ }
+ else -> super.onOptionsItemSelected(item)
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ setContentView(R.layout.bypass_list)
+
+ findViewById(R.id.addBypassAddr).setOnClickListener(this)
+ findViewById(R.id.presetBypassAddr).setOnClickListener(this)
+ findViewById(R.id.importBypassAddr).setOnClickListener(this)
+ findViewById(R.id.exportBypassAddr).setOnClickListener(this)
+
+ refreshList()
+ }
+
+ override fun onItemClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
+ editAddr(MSG_EDIT_ADDR, position)
+ }
+
+ override fun onItemLongClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long): Boolean {
+ delAddr(position)
+ return true
+ }
+
+ private fun presetAddr() {
+ AlertDialog.Builder(this)
+ .setTitle(R.string.preset_button)
+ .setNegativeButton(R.string.alert_dialog_cancel) { _, _ -> }
+ .setSingleChoiceItems(R.array.presets_list, -1) { dialog, which ->
+ if (which >= 0 && which < Constraints.PRESETS.size) {
+ val msg = Message.obtain().apply {
+ what = MSG_PRESET_ADDR
+ arg1 = which
+ }
+ handler.sendMessage(msg)
+ }
+ dialog.dismiss()
+ }
+ .create()
+ .show()
+ }
+
+ @Deprecated("Deprecated in Java")
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ if (requestCode == Constraints.IMPORT_REQUEST && resultCode == RESULT_OK) {
+ val path = data?.getStringExtra(Constraints.FILE_PATH)
+ if (path.isNullOrEmpty()) return
+
+ val pd = ProgressDialog.show(this, "", getString(R.string.importing), true, true)
+
+ val h = object : Handler(Looper.getMainLooper()) {
+ override fun handleMessage(msg: Message) {
+ refreshList()
+ pd?.dismiss()
+ }
+ }
+
+ Thread {
+ try {
+ FileInputStream(path).use { input ->
+ BufferedReader(InputStreamReader(input)).use { br ->
+ bypassList.clear()
+ var line: String?
+ while (br.readLine().also { line = it } != null) {
+ Profile.validateAddr(line)?.let { bypassList.add(it) }
+ }
+ }
+ }
+ } catch (e: FileNotFoundException) {
+ Log.e(TAG, "error to open file", e)
+ } catch (e: IOException) {
+ Log.e(TAG, "error to read file", e)
+ }
+ h.sendEmptyMessage(MSG_IMPORT_ADDR)
+ }.start()
+ }
+ }
+
+ private fun importAddr() {
+ startActivityForResult(Intent(this, FileChooser::class.java), Constraints.IMPORT_REQUEST)
+ }
+
+ private fun exportAddr() {
+ val factory = LayoutInflater.from(this)
+ val textEntryView = factory.inflate(R.layout.alert_dialog_text_entry, null)
+ val path = textEntryView.findViewById(R.id.text_edit)
+
+ path.setText("${Utils.getDataPath(this)}/${profile.host}.opt")
+
+ AlertDialog.Builder(this)
+ .setTitle(R.string.export_button)
+ .setView(textEntryView)
+ .setPositiveButton(R.string.alert_dialog_ok) { _, _ ->
+ val pathText = path.text?.toString() ?: return@setPositiveButton
+
+ Thread {
+ try {
+ val file = File(pathText)
+ if (!file.exists()) file.createNewFile()
+
+ FileOutputStream(file).use { output ->
+ BufferedOutputStream(output).use { bw ->
+ for (addr in bypassList) {
+ Profile.validateAddr(addr)?.let {
+ bw.write("$it\n".toByteArray())
+ }
+ }
+ bw.flush()
+ }
+ output.flush()
+ }
+ } catch (e: FileNotFoundException) {
+ Log.e(TAG, "error to open file", e)
+ } catch (e: IOException) {
+ Log.e(TAG, "error to write file", e)
+ }
+
+ val msg = Message.obtain().apply {
+ what = MSG_EXPORT_ADDR
+ obj = pathText
+ }
+ handler.sendMessage(msg)
+ }.start()
+ }
+ .setNegativeButton(R.string.alert_dialog_cancel) { _, _ -> }
+ .create()
+ .show()
+ }
+
+ private fun delAddr(idx: Int) {
+ val addr = bypassList[idx]
+
+ AlertDialog.Builder(this)
+ .setTitle(addr)
+ .setMessage(R.string.bypass_del_text)
+ .setPositiveButton(R.string.alert_dialog_ok) { _, _ ->
+ val msg = Message.obtain().apply {
+ what = MSG_DEL_ADDR
+ arg1 = idx
+ obj = addr
+ }
+ handler.sendMessage(msg)
+ }
+ .setNegativeButton(R.string.alert_dialog_cancel) { _, _ -> }
+ .create()
+ .show()
+ }
+
+ private fun editAddr(msgType: Int, idx: Int) {
+ val factory = LayoutInflater.from(this)
+ val textEntryView = factory.inflate(R.layout.alert_dialog_text_entry, null)
+ val addrText = textEntryView.findViewById(R.id.text_edit)
+
+ when (msgType) {
+ MSG_EDIT_ADDR -> addrText.setText(bypassList[idx])
+ MSG_ADD_ADDR -> addrText.setText("0.0.0.0/0")
+ }
+
+ AlertDialog.Builder(this)
+ .setTitle(R.string.bypass_edit_title)
+ .setView(textEntryView)
+ .setPositiveButton(R.string.alert_dialog_ok) { _, _ ->
+ Thread {
+ val addr = addrText.text.toString()
+ val validated = Profile.validateAddr(addr)
+ if (validated != null) {
+ val msg = Message.obtain().apply {
+ what = msgType
+ arg1 = idx
+ obj = validated
+ }
+ handler.sendMessage(msg)
+ } else {
+ handler.sendEmptyMessage(MSG_ERR_ADDR)
+ }
+ }.start()
+ }
+ .setNegativeButton(R.string.alert_dialog_cancel) { _, _ -> }
+ .create()
+ .show()
+ }
+
+ private fun reset(list: Array) {
+ val pd = ProgressDialog.show(this, "", getString(R.string.reseting), true, true)
+
+ val h = object : Handler(Looper.getMainLooper()) {
+ override fun handleMessage(msg: Message) {
+ refreshList()
+ pd?.dismiss()
+ }
+ }
+
+ Thread {
+ bypassList.clear()
+ for (addr in list) {
+ Profile.validateAddr(addr)?.let { bypassList.add(it) }
+ }
+ h.sendEmptyMessage(0)
+ }.start()
+ }
+
+ private fun refreshList() {
+ val settings = PreferenceManager.getDefaultSharedPreferences(this)
+ profile.getProfile(settings)
+
+ if (bypassList.isNotEmpty()) {
+ profile.bypassAddrs = Profile.encodeAddrs(bypassList.toTypedArray())
+ profile.setProfile(settings)
+ }
+
+ val addrs = Profile.decodeAddrs(profile.bypassAddrs)
+ bypassList = ArrayList(addrs.toList())
+
+ val inflater = layoutInflater
+
+ adapter = object : ArrayAdapter(this, R.layout.bypass_list_item, R.id.bypasslistItemText, bypassList) {
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
+ val view = convertView ?: inflater.inflate(R.layout.bypass_list_item, parent, false)
+ val item = view.findViewById(R.id.bypasslistItemText)
+ bypassList.getOrNull(position)?.let { item.text = it }
+ return view
+ }
+ }
+
+ val list = findViewById(R.id.BypassListView)
+ list.adapter = adapter
+ list.onItemClickListener = this
+ list.onItemLongClickListener = this
+ }
+}
diff --git a/app/src/main/java/org/proxydroid/ConnectivityBroadcastReceiver.java b/app/src/main/java/org/proxydroid/ConnectivityBroadcastReceiver.java
deleted file mode 100644
index 7b732b81..00000000
--- a/app/src/main/java/org/proxydroid/ConnectivityBroadcastReceiver.java
+++ /dev/null
@@ -1,235 +0,0 @@
-/* proxydroid - Global / Individual Proxy App for Android
- * Copyright (C) 2011 Max Lv
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- *
- * ___====-_ _-====___
- * _--^^^#####// \\#####^^^--_
- * _-^##########// ( ) \\##########^-_
- * -############// |\^^/| \\############-
- * _/############// (@::@) \\############\_
- * /#############(( \\// ))#############\
- * -###############\\ (oo) //###############-
- * -#################\\ / VV \ //#################-
- * -###################\\/ \//###################-
- * _#/|##########/\######( /\ )######/\##########|\#_
- * |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
- * ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
- * ` ` ` ` / | | | | \ ' ' ' '
- * ( | | | | )
- * __\ | | | | /__
- * (vvv(VVV)(VVV)vvv)
- *
- * HERE BE DRAGONS
- *
- */
-
-package org.proxydroid;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.os.Handler;
-import android.preference.PreferenceManager;
-
-import com.ksmaze.android.preference.ListPreferenceMultiSelect;
-
-import org.proxydroid.utils.Constraints;
-import org.proxydroid.utils.Utils;
-
-public class ConnectivityBroadcastReceiver extends BroadcastReceiver {
-
- private Handler mHandler = new Handler();
-
- private static final String TAG = "ConnectivityBroadcastReceiver";
-
- @Override
- public void onReceive(final Context context, final Intent intent) {
-
- if (Utils.isConnecting()) return;
-
- if (!intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) return;
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
-
- // only switching profiles when needed
- ConnectivityManager manager = (ConnectivityManager) context
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo networkInfo = manager.getActiveNetworkInfo();
-
- if (networkInfo != null) {
- if (networkInfo.getState() == NetworkInfo.State.CONNECTING
- || networkInfo.getState() == NetworkInfo.State.DISCONNECTING
- || networkInfo.getState() == NetworkInfo.State.UNKNOWN)
- return;
- } else {
- if (!Utils.isWorking()) return;
- }
-
- SharedPreferences settings = PreferenceManager
- .getDefaultSharedPreferences(context);
- Profile mProfile = new Profile();
- mProfile.getProfile(settings);
-
- // Store current settings first
- String oldProfile = settings.getString("profile", "1");
- Editor ed = settings.edit();
- ed.putString(oldProfile, mProfile.toString());
- ed.commit();
-
- // Load all profiles
- String[] profileValues = settings.getString("profileValues", "").split(
- "\\|");
- String curSSID = null;
- String lastSSID = settings.getString("lastSSID", "-1");
- boolean autoConnect = false;
-
- // Test on each profile
- for (String profile : profileValues) {
- String profileString = settings.getString(profile, "");
- mProfile.decodeJson(profileString);
- curSSID = onlineSSID(context, mProfile.getSsid(), mProfile.getExcludedSsid());
- if (mProfile.isAutoConnect() && curSSID != null) {
- // Enable auto connect
- autoConnect = true;
-
- // XXX: Switch profile first
- ed = settings.edit();
- ed.putString("profile", profile);
- ed.commit();
-
- // Then switch profile values
- mProfile.setProfile(settings);
- break;
- }
- }
-
- if (networkInfo == null) {
- if (!lastSSID.equals(Constraints.ONLY_3G)
- && !lastSSID.equals(Constraints.WIFI_AND_3G)
- && !lastSSID.equals(Constraints.ONLY_WIFI)) {
- if (Utils.isWorking()) {
- context.stopService(new Intent(context,
- ProxyDroidService.class));
- }
- }
- } else {
- // no network available now
- if (networkInfo.getState() != NetworkInfo.State.CONNECTED)
- return;
-
- if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
- // if no last SSID, should give up here
- if (!lastSSID.equals("-1")) {
- // get WIFI info
- WifiManager wm = (WifiManager) context
- .getSystemService(Context.WIFI_SERVICE);
- WifiInfo wInfo = wm.getConnectionInfo();
- if (wInfo != null) {
- // compare with the current SSID
- String current = wInfo.getSSID();
- if (current != null) current = current.replace("\"", "");
- if (current != null && !current.equals(lastSSID)) {
- // need to switch profile, so stop service first
- if (Utils.isWorking())
- context.stopService(new Intent(context,
- ProxyDroidService.class));
- }
- }
- }
- } else {
- // still satisfy the last trigger
- if (!lastSSID.equals(Constraints.ONLY_3G)
- && !lastSSID.equals(Constraints.WIFI_AND_3G)) {
- if (Utils.isWorking())
- context.stopService(new Intent(context,
- ProxyDroidService.class));
- }
- }
- }
-
- if (autoConnect) {
- if (!Utils.isWorking()) {
- ProxyDroidReceiver pdr = new ProxyDroidReceiver();
- ed = settings.edit();
- ed.putString("lastSSID", curSSID);
- ed.commit();
- Utils.setConnecting(true);
- pdr.onReceive(context, intent);
- }
- }
- }
- });
- }
-
- public String onlineSSID(Context context, String ssid, String excludedSsid) {
- String ssids[] = ListPreferenceMultiSelect.parseStoredValue(ssid);
- String excludedSsids[] = ListPreferenceMultiSelect.parseStoredValue(excludedSsid);
- if (ssids == null)
- return null;
- if (ssids.length < 1)
- return null;
- ConnectivityManager manager = (ConnectivityManager) context
- .getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo networkInfo = manager.getActiveNetworkInfo();
- if (networkInfo == null)
- return null;
- if (networkInfo.getType() != ConnectivityManager.TYPE_WIFI) {
- for (String item : ssids) {
- if (Constraints.WIFI_AND_3G.equals(item))
- return item;
- if (Constraints.ONLY_3G.equals(item))
- return item;
- }
- return null;
- }
- WifiManager wm = (WifiManager) context
- .getSystemService(Context.WIFI_SERVICE);
- WifiInfo wInfo = wm.getConnectionInfo();
- if (wInfo == null || wInfo.getSSID() == null)
- return null;
- String current = wInfo.getSSID();
- if (current == null || "".equals(current))
- return null;
- current = current.replace("\"", "");
-
- if (excludedSsids != null) {
- for (String item : excludedSsids) {
- if (current.equals(item)) {
- return null; // Never connect proxy on excluded ssid
- }
- }
- }
-
- for (String item : ssids) {
- if (Constraints.WIFI_AND_3G.equals(item))
- return item;
- if (Constraints.ONLY_WIFI.equals(item))
- return item;
- if (current.equals(item))
- return item;
- }
- return null;
- }
-
-}
diff --git a/app/src/main/java/org/proxydroid/ConnectivityBroadcastReceiver.kt b/app/src/main/java/org/proxydroid/ConnectivityBroadcastReceiver.kt
new file mode 100644
index 00000000..b45d0fcb
--- /dev/null
+++ b/app/src/main/java/org/proxydroid/ConnectivityBroadcastReceiver.kt
@@ -0,0 +1,174 @@
+/* proxydroid - Global / Individual Proxy App for Android
+ * Copyright (C) 2011 Max Lv
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.proxydroid
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.net.NetworkInfo
+import android.net.wifi.WifiManager
+import android.os.Handler
+import android.os.Looper
+import android.preference.PreferenceManager
+import com.ksmaze.android.preference.ListPreferenceMultiSelect
+import org.proxydroid.utils.Constraints
+import org.proxydroid.utils.Utils
+
+class ConnectivityBroadcastReceiver : BroadcastReceiver() {
+
+ private val handler = Handler(Looper.getMainLooper())
+
+ companion object {
+ private const val TAG = "ConnectivityBroadcastReceiver"
+ }
+
+ override fun onReceive(context: Context, intent: Intent) {
+ if (Utils.isConnecting()) return
+ if (intent.action != ConnectivityManager.CONNECTIVITY_ACTION) return
+
+ handler.post {
+ val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ val networkInfo = manager.activeNetworkInfo
+
+ if (networkInfo != null) {
+ if (networkInfo.state == NetworkInfo.State.CONNECTING ||
+ networkInfo.state == NetworkInfo.State.DISCONNECTING ||
+ networkInfo.state == NetworkInfo.State.UNKNOWN
+ ) {
+ return@post
+ }
+ } else {
+ if (!Utils.isWorking()) return@post
+ }
+
+ val settings = PreferenceManager.getDefaultSharedPreferences(context)
+ val profile = Profile()
+ profile.getProfile(settings)
+
+ // Store current settings first
+ val oldProfile = settings.getString("profile", "1") ?: "1"
+ settings.edit().putString(oldProfile, profile.toString()).apply()
+
+ // Load all profiles
+ val profileValues = settings.getString("profileValues", "")?.split("\\|".toRegex()) ?: emptyList()
+ var curSSID: String? = null
+ val lastSSID = settings.getString("lastSSID", "-1") ?: "-1"
+ var autoConnect = false
+
+ // Test on each profile
+ for (profileId in profileValues) {
+ if (profileId.isEmpty()) continue
+ val profileString = settings.getString(profileId, "") ?: ""
+ profile.decodeJson(profileString)
+ curSSID = onlineSSID(context, profile.ssid, profile.excludedSsid)
+ if (profile.isAutoConnect && curSSID != null) {
+ autoConnect = true
+ settings.edit().putString("profile", profileId).apply()
+ profile.setProfile(settings)
+ break
+ }
+ }
+
+ if (networkInfo == null) {
+ if (lastSSID != Constraints.ONLY_3G &&
+ lastSSID != Constraints.WIFI_AND_3G &&
+ lastSSID != Constraints.ONLY_WIFI
+ ) {
+ if (Utils.isWorking()) {
+ context.stopService(Intent(context, ProxyDroidService::class.java))
+ }
+ }
+ } else {
+ if (networkInfo.state != NetworkInfo.State.CONNECTED) return@post
+
+ if (networkInfo.type == ConnectivityManager.TYPE_WIFI) {
+ if (lastSSID != "-1") {
+ val wm = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
+ val wInfo = wm.connectionInfo
+ if (wInfo != null) {
+ var current = wInfo.ssid
+ current = current?.replace("\"", "")
+ if (current != null && current != lastSSID) {
+ if (Utils.isWorking()) {
+ context.stopService(Intent(context, ProxyDroidService::class.java))
+ }
+ }
+ }
+ }
+ } else {
+ if (lastSSID != Constraints.ONLY_3G && lastSSID != Constraints.WIFI_AND_3G) {
+ if (Utils.isWorking()) {
+ context.stopService(Intent(context, ProxyDroidService::class.java))
+ }
+ }
+ }
+ }
+
+ if (autoConnect) {
+ if (!Utils.isWorking()) {
+ val pdr = ProxyDroidReceiver()
+ settings.edit().putString("lastSSID", curSSID).apply()
+ Utils.setConnecting(true)
+ pdr.onReceive(context, intent)
+ }
+ }
+ }
+ }
+
+ fun onlineSSID(context: Context, ssid: String, excludedSsid: String): String? {
+ val ssids = ListPreferenceMultiSelect.parseStoredValue(ssid) ?: return null
+ val excludedSsids = ListPreferenceMultiSelect.parseStoredValue(excludedSsid)
+
+ if (ssids.isEmpty()) return null
+
+ val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ val networkInfo = manager.activeNetworkInfo ?: return null
+
+ if (networkInfo.type != ConnectivityManager.TYPE_WIFI) {
+ for (item in ssids) {
+ if (Constraints.WIFI_AND_3G == item) return item
+ if (Constraints.ONLY_3G == item) return item
+ }
+ return null
+ }
+
+ val wm = context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
+ val wInfo = wm.connectionInfo
+ if (wInfo?.ssid == null) return null
+
+ var current = wInfo.ssid
+ if (current.isNullOrEmpty()) return null
+ current = current.replace("\"", "")
+
+ if (excludedSsids != null) {
+ for (item in excludedSsids) {
+ if (current == item) {
+ return null // Never connect proxy on excluded ssid
+ }
+ }
+ }
+
+ for (item in ssids) {
+ if (Constraints.WIFI_AND_3G == item) return item
+ if (Constraints.ONLY_WIFI == item) return item
+ if (current == item) return item
+ }
+ return null
+ }
+}
diff --git a/app/src/main/java/org/proxydroid/DomainValidator.java b/app/src/main/java/org/proxydroid/DomainValidator.java
deleted file mode 100644
index 4f8e51a9..00000000
--- a/app/src/main/java/org/proxydroid/DomainValidator.java
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.proxydroid;
-
-import org.proxydroid.utils.RegexValidator;
-
-import java.io.Serializable;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- *
- * Domain name validation routines.
- *
- *
- *
- * This validator provides methods for validating Internet domain names and
- * top-level domains.
- *
- *
- *
- * Domain names are evaluated according to the standards RFC1034, section 3, and RFC1123, section 2.1. No
- * accomodation is provided for the specialized needs of other applications; if
- * the domain name has been URL-encoded, for example, validation will fail even
- * though the equivalent plaintext version of the same name would have passed.
- *
- *
- *
- * Validation is also provided for top-level domains (TLDs) as defined and
- * maintained by the Internet Assigned Numbers Authority (IANA):
- *
- * (NOTE: This class does not provide IP address lookup for domain names
- * or methods to ensure that a given domain name matches a specific IP; see
- * {@link java.net.InetAddress} for that functionality.)
- *
- *
- * @version $Revision$ $Date$
- * @since Validator 1.4
- */
-public class DomainValidator implements Serializable {
-
- // Regular expression strings for hostnames (derived from RFC2396 and RFC
- // 1123)
- private static final String DOMAIN_LABEL_REGEX = "\\p{Alnum}(?>[\\p{Alnum}-]*\\p{Alnum})*";
- private static final String TOP_LABEL_REGEX = "\\p{Alpha}{2,}";
- private static final String DOMAIN_NAME_REGEX = "^(?:" + DOMAIN_LABEL_REGEX
- + "\\.)+" + "(" + TOP_LABEL_REGEX + ")$";
-
- /**
- * Singleton instance of this validator.
- */
- private static final DomainValidator DOMAIN_VALIDATOR = new DomainValidator();
-
- /**
- * RegexValidator for matching domains.
- */
- private final RegexValidator domainRegex = new RegexValidator(
- DOMAIN_NAME_REGEX);
-
- /**
- * Returns the singleton instance of this validator.
- *
- * @return the singleton instance of this validator
- */
- public static DomainValidator getInstance() {
- return DOMAIN_VALIDATOR;
- }
-
- /** Private constructor. */
- private DomainValidator() {
- }
-
- /**
- * Returns true if the specified String parses as a valid
- * domain name with a recognized top-level domain. The parsing is
- * case-sensitive.
- *
- * @param domain
- * the parameter to check for domain name syntax
- * @return true if the parameter is a valid domain name
- */
- public boolean isValid(String domain) {
- String[] groups = domainRegex.match(domain);
- if (groups != null && groups.length > 0) {
- return isValidTld(groups[0]);
- } else {
- return false;
- }
- }
-
- /**
- * Returns true if the specified String matches any
- * IANA-defined top-level domain. Leading dots are ignored if present. The
- * search is case-sensitive.
- *
- * @param tld
- * the parameter to check for TLD status
- * @return true if the parameter is a TLD
- */
- public boolean isValidTld(String tld) {
- return isValidInfrastructureTld(tld) || isValidGenericTld(tld)
- || isValidCountryCodeTld(tld);
- }
-
- /**
- * Returns true if the specified String matches any
- * IANA-defined infrastructure top-level domain. Leading dots are ignored if
- * present. The search is case-sensitive.
- *
- * @param iTld
- * the parameter to check for infrastructure TLD status
- * @return true if the parameter is an infrastructure TLD
- */
- public boolean isValidInfrastructureTld(String iTld) {
- return INFRASTRUCTURE_TLD_LIST.contains(chompLeadingDot(iTld
- .toLowerCase()));
- }
-
- /**
- * Returns true if the specified String matches any
- * IANA-defined generic top-level domain. Leading dots are ignored if
- * present. The search is case-sensitive.
- *
- * @param gTld
- * the parameter to check for generic TLD status
- * @return true if the parameter is a generic TLD
- */
- public boolean isValidGenericTld(String gTld) {
- return GENERIC_TLD_LIST.contains(chompLeadingDot(gTld.toLowerCase()));
- }
-
- /**
- * Returns true if the specified String matches any
- * IANA-defined country code top-level domain. Leading dots are ignored if
- * present. The search is case-sensitive.
- *
- * @param ccTld
- * the parameter to check for country code TLD status
- * @return true if the parameter is a country code TLD
- */
- public boolean isValidCountryCodeTld(String ccTld) {
- return COUNTRY_CODE_TLD_LIST.contains(chompLeadingDot(ccTld
- .toLowerCase()));
- }
-
- private String chompLeadingDot(String str) {
- if (str.startsWith(".")) {
- return str.substring(1);
- } else {
- return str;
- }
- }
-
- // ---------------------------------------------
- // ----- TLDs defined by IANA
- // ----- Authoritative and comprehensive list at:
- // ----- http://data.iana.org/TLD/tlds-alpha-by-domain.txt
-
- private static final String[] INFRASTRUCTURE_TLDS = new String[] { "arpa", // internet
- // infrastructure
- "root" // diagnostic marker for non-truncated root zone
- };
-
- private static final String[] GENERIC_TLDS = new String[] { "aero", // air
- // transport
- // industry
- "asia", // Pan-Asia/Asia Pacific
- "biz", // businesses
- "cat", // Catalan linguistic/cultural community
- "com", // commercial enterprises
- "coop", // cooperative associations
- "info", // informational sites
- "jobs", // Human Resource managers
- "mobi", // mobile products and services
- "museum", // museums, surprisingly enough
- "name", // individuals' sites
- "net", // internet support infrastructure/business
- "org", // noncommercial organizations
- "pro", // credentialed professionals and entities
- "tel", // contact data for businesses and individuals
- "travel", // entities in the travel industry
- "gov", // United States Government
- "edu", // accredited postsecondary US education entities
- "mil", // United States Military
- "int" // organizations established by international treaty
- };
-
- private static final String[] COUNTRY_CODE_TLDS = new String[] { "ac", // Ascension
- // Island
- "ad", // Andorra
- "ae", // United Arab Emirates
- "af", // Afghanistan
- "ag", // Antigua and Barbuda
- "ai", // Anguilla
- "al", // Albania
- "am", // Armenia
- "an", // Netherlands Antilles
- "ao", // Angola
- "aq", // Antarctica
- "ar", // Argentina
- "as", // American Samoa
- "at", // Austria
- "au", // Australia (includes Ashmore and Cartier Islands and Coral
- // Sea Islands)
- "aw", // Aruba
- "ax", // 脙鈥and
- "az", // Azerbaijan
- "ba", // Bosnia and Herzegovina
- "bb", // Barbados
- "bd", // Bangladesh
- "be", // Belgium
- "bf", // Burkina Faso
- "bg", // Bulgaria
- "bh", // Bahrain
- "bi", // Burundi
- "bj", // Benin
- "bm", // Bermuda
- "bn", // Brunei Darussalam
- "bo", // Bolivia
- "br", // Brazil
- "bs", // Bahamas
- "bt", // Bhutan
- "bv", // Bouvet Island
- "bw", // Botswana
- "by", // Belarus
- "bz", // Belize
- "ca", // Canada
- "cc", // Cocos (Keeling) Islands
- "cd", // Democratic Republic of the Congo (formerly Zaire)
- "cf", // Central African Republic
- "cg", // Republic of the Congo
- "ch", // Switzerland
- "ci", // C脙麓te d'Ivoire
- "ck", // Cook Islands
- "cl", // Chile
- "cm", // Cameroon
- "cn", // China, mainland
- "co", // Colombia
- "cr", // Costa Rica
- "cu", // Cuba
- "cv", // Cape Verde
- "cx", // Christmas Island
- "cy", // Cyprus
- "cz", // Czech Republic
- "de", // Germany
- "dj", // Djibouti
- "dk", // Denmark
- "dm", // Dominica
- "do", // Dominican Republic
- "dz", // Algeria
- "ec", // Ecuador
- "ee", // Estonia
- "eg", // Egypt
- "er", // Eritrea
- "es", // Spain
- "et", // Ethiopia
- "eu", // European Union
- "fi", // Finland
- "fj", // Fiji
- "fk", // Falkland Islands
- "fm", // Federated States of Micronesia
- "fo", // Faroe Islands
- "fr", // France
- "ga", // Gabon
- "gb", // Great Britain (United Kingdom)
- "gd", // Grenada
- "ge", // Georgia
- "gf", // French Guiana
- "gg", // Guernsey
- "gh", // Ghana
- "gi", // Gibraltar
- "gl", // Greenland
- "gm", // The Gambia
- "gn", // Guinea
- "gp", // Guadeloupe
- "gq", // Equatorial Guinea
- "gr", // Greece
- "gs", // South Georgia and the South Sandwich Islands
- "gt", // Guatemala
- "gu", // Guam
- "gw", // Guinea-Bissau
- "gy", // Guyana
- "hk", // Hong Kong
- "hm", // Heard Island and McDonald Islands
- "hn", // Honduras
- "hr", // Croatia (Hrvatska)
- "ht", // Haiti
- "hu", // Hungary
- "id", // Indonesia
- "ie", // Ireland (脙鈥癷re)
- "il", // Israel
- "im", // Isle of Man
- "in", // India
- "io", // British Indian Ocean Territory
- "iq", // Iraq
- "ir", // Iran
- "is", // Iceland
- "it", // Italy
- "je", // Jersey
- "jm", // Jamaica
- "jo", // Jordan
- "jp", // Japan
- "ke", // Kenya
- "kg", // Kyrgyzstan
- "kh", // Cambodia (Khmer)
- "ki", // Kiribati
- "km", // Comoros
- "kn", // Saint Kitts and Nevis
- "kp", // North Korea
- "kr", // South Korea
- "kw", // Kuwait
- "ky", // Cayman Islands
- "kz", // Kazakhstan
- "la", // Laos (currently being marketed as the official domain for
- // Los Angeles)
- "lb", // Lebanon
- "lc", // Saint Lucia
- "li", // Liechtenstein
- "lk", // Sri Lanka
- "lr", // Liberia
- "ls", // Lesotho
- "lt", // Lithuania
- "lu", // Luxembourg
- "lv", // Latvia
- "ly", // Libya
- "ma", // Morocco
- "mc", // Monaco
- "md", // Moldova
- "me", // Montenegro
- "mg", // Madagascar
- "mh", // Marshall Islands
- "mk", // Republic of Macedonia
- "ml", // Mali
- "mm", // Myanmar
- "mn", // Mongolia
- "mo", // Macau
- "mp", // Northern Mariana Islands
- "mq", // Martinique
- "mr", // Mauritania
- "ms", // Montserrat
- "mt", // Malta
- "mu", // Mauritius
- "mv", // Maldives
- "mw", // Malawi
- "mx", // Mexico
- "my", // Malaysia
- "mz", // Mozambique
- "na", // Namibia
- "nc", // New Caledonia
- "ne", // Niger
- "nf", // Norfolk Island
- "ng", // Nigeria
- "ni", // Nicaragua
- "nl", // Netherlands
- "no", // Norway
- "np", // Nepal
- "nr", // Nauru
- "nu", // Niue
- "nz", // New Zealand
- "om", // Oman
- "pa", // Panama
- "pe", // Peru
- "pf", // French Polynesia With Clipperton Island
- "pg", // Papua New Guinea
- "ph", // Philippines
- "pk", // Pakistan
- "pl", // Poland
- "pm", // Saint-Pierre and Miquelon
- "pn", // Pitcairn Islands
- "pr", // Puerto Rico
- "ps", // Palestinian territories (PA-controlled West Bank and Gaza
- // Strip)
- "pt", // Portugal
- "pw", // Palau
- "py", // Paraguay
- "qa", // Qatar
- "re", // R脙漏union
- "ro", // Romania
- "rs", // Serbia
- "ru", // Russia
- "rw", // Rwanda
- "sa", // Saudi Arabia
- "sb", // Solomon Islands
- "sc", // Seychelles
- "sd", // Sudan
- "se", // Sweden
- "sg", // Singapore
- "sh", // Saint Helena
- "si", // Slovenia
- "sj", // Svalbard and Jan Mayen Islands Not in use (Norwegian
- // dependencies; see .no)
- "sk", // Slovakia
- "sl", // Sierra Leone
- "sm", // San Marino
- "sn", // Senegal
- "so", // Somalia
- "sr", // Suriname
- "st", // S脙拢o Tom脙漏 and Pr脙颅ncipe
- "su", // Soviet Union (deprecated)
- "sv", // El Salvador
- "sy", // Syria
- "sz", // Swaziland
- "tc", // Turks and Caicos Islands
- "td", // Chad
- "tf", // French Southern and Antarctic Lands
- "tg", // Togo
- "th", // Thailand
- "tj", // Tajikistan
- "tk", // Tokelau
- "tl", // East Timor (deprecated old code)
- "tm", // Turkmenistan
- "tn", // Tunisia
- "to", // Tonga
- "tp", // East Timor
- "tr", // Turkey
- "tt", // Trinidad and Tobago
- "tv", // Tuvalu
- "tw", // Taiwan, Republic of China
- "tz", // Tanzania
- "ua", // Ukraine
- "ug", // Uganda
- "uk", // United Kingdom
- "um", // United States Minor Outlying Islands
- "us", // United States of America
- "uy", // Uruguay
- "uz", // Uzbekistan
- "va", // Vatican City State
- "vc", // Saint Vincent and the Grenadines
- "ve", // Venezuela
- "vg", // British Virgin Islands
- "vi", // U.S. Virgin Islands
- "vn", // Vietnam
- "vu", // Vanuatu
- "wf", // Wallis and Futuna
- "ws", // Samoa (formerly Western Samoa)
- "ye", // Yemen
- "yt", // Mayotte
- "yu", // Serbia and Montenegro (originally Yugoslavia)
- "za", // South Africa
- "zm", // Zambia
- "zw", // Zimbabwe
- };
-
- private static final List INFRASTRUCTURE_TLD_LIST = Arrays
- .asList(INFRASTRUCTURE_TLDS);
- private static final List GENERIC_TLD_LIST = Arrays.asList(GENERIC_TLDS);
- private static final List COUNTRY_CODE_TLD_LIST = Arrays
- .asList(COUNTRY_CODE_TLDS);
-}
diff --git a/app/src/main/java/org/proxydroid/DomainValidator.kt b/app/src/main/java/org/proxydroid/DomainValidator.kt
new file mode 100644
index 00000000..8e69c025
--- /dev/null
+++ b/app/src/main/java/org/proxydroid/DomainValidator.kt
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.proxydroid
+
+import org.proxydroid.utils.RegexValidator
+import java.io.Serializable
+
+class DomainValidator private constructor() : Serializable {
+
+ companion object {
+ private const val DOMAIN_LABEL_REGEX = "\\p{Alnum}(?>[\\p{Alnum}-]*\\p{Alnum})*"
+ private const val TOP_LABEL_REGEX = "\\p{Alpha}{2,}"
+ private const val DOMAIN_NAME_REGEX = "^(?:$DOMAIN_LABEL_REGEX\\.)+($TOP_LABEL_REGEX)$"
+
+ @JvmStatic
+ val instance: DomainValidator = DomainValidator()
+
+ private val INFRASTRUCTURE_TLDS = arrayOf("arpa", "root")
+
+ private val GENERIC_TLDS = arrayOf(
+ "aero", "asia", "biz", "cat", "com", "coop", "info", "jobs", "mobi",
+ "museum", "name", "net", "org", "pro", "tel", "travel", "gov", "edu", "mil", "int"
+ )
+
+ private val COUNTRY_CODE_TLDS = arrayOf(
+ "ac", "ad", "ae", "af", "ag", "ai", "al", "am", "an", "ao", "aq", "ar", "as", "at",
+ "au", "aw", "ax", "az", "ba", "bb", "bd", "be", "bf", "bg", "bh", "bi", "bj", "bm",
+ "bn", "bo", "br", "bs", "bt", "bv", "bw", "by", "bz", "ca", "cc", "cd", "cf", "cg",
+ "ch", "ci", "ck", "cl", "cm", "cn", "co", "cr", "cu", "cv", "cx", "cy", "cz", "de",
+ "dj", "dk", "dm", "do", "dz", "ec", "ee", "eg", "er", "es", "et", "eu", "fi", "fj",
+ "fk", "fm", "fo", "fr", "ga", "gb", "gd", "ge", "gf", "gg", "gh", "gi", "gl", "gm",
+ "gn", "gp", "gq", "gr", "gs", "gt", "gu", "gw", "gy", "hk", "hm", "hn", "hr", "ht",
+ "hu", "id", "ie", "il", "im", "in", "io", "iq", "ir", "is", "it", "je", "jm", "jo",
+ "jp", "ke", "kg", "kh", "ki", "km", "kn", "kp", "kr", "kw", "ky", "kz", "la", "lb",
+ "lc", "li", "lk", "lr", "ls", "lt", "lu", "lv", "ly", "ma", "mc", "md", "me", "mg",
+ "mh", "mk", "ml", "mm", "mn", "mo", "mp", "mq", "mr", "ms", "mt", "mu", "mv", "mw",
+ "mx", "my", "mz", "na", "nc", "ne", "nf", "ng", "ni", "nl", "no", "np", "nr", "nu",
+ "nz", "om", "pa", "pe", "pf", "pg", "ph", "pk", "pl", "pm", "pn", "pr", "ps", "pt",
+ "pw", "py", "qa", "re", "ro", "rs", "ru", "rw", "sa", "sb", "sc", "sd", "se", "sg",
+ "sh", "si", "sj", "sk", "sl", "sm", "sn", "so", "sr", "st", "su", "sv", "sy", "sz",
+ "tc", "td", "tf", "tg", "th", "tj", "tk", "tl", "tm", "tn", "to", "tp", "tr", "tt",
+ "tv", "tw", "tz", "ua", "ug", "uk", "um", "us", "uy", "uz", "va", "vc", "ve", "vg",
+ "vi", "vn", "vu", "wf", "ws", "ye", "yt", "yu", "za", "zm", "zw"
+ )
+
+ private val INFRASTRUCTURE_TLD_LIST = INFRASTRUCTURE_TLDS.toList()
+ private val GENERIC_TLD_LIST = GENERIC_TLDS.toList()
+ private val COUNTRY_CODE_TLD_LIST = COUNTRY_CODE_TLDS.toList()
+ }
+
+ private val domainRegex = RegexValidator(DOMAIN_NAME_REGEX)
+
+ fun isValid(domain: String): Boolean {
+ val groups = domainRegex.match(domain)
+ return if (groups != null && groups.isNotEmpty()) {
+ isValidTld(groups[0])
+ } else {
+ false
+ }
+ }
+
+ fun isValidTld(tld: String): Boolean {
+ return isValidInfrastructureTld(tld) || isValidGenericTld(tld) || isValidCountryCodeTld(tld)
+ }
+
+ fun isValidInfrastructureTld(iTld: String): Boolean {
+ return INFRASTRUCTURE_TLD_LIST.contains(chompLeadingDot(iTld.lowercase()))
+ }
+
+ fun isValidGenericTld(gTld: String): Boolean {
+ return GENERIC_TLD_LIST.contains(chompLeadingDot(gTld.lowercase()))
+ }
+
+ fun isValidCountryCodeTld(ccTld: String): Boolean {
+ return COUNTRY_CODE_TLD_LIST.contains(chompLeadingDot(ccTld.lowercase()))
+ }
+
+ private fun chompLeadingDot(str: String): String {
+ return if (str.startsWith(".")) str.substring(1) else str
+ }
+}
diff --git a/app/src/main/java/org/proxydroid/Exec.java b/app/src/main/java/org/proxydroid/Exec.java
deleted file mode 100644
index e3e23c30..00000000
--- a/app/src/main/java/org/proxydroid/Exec.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.proxydroid;
-
-import java.io.FileDescriptor;
-
-/**
- * Utility methods for creating and managing a subprocess.
- *
- * Note: The native methods access a package-private java.io.FileDescriptor
- * field to get and set the raw Linux file descriptor. This might break if the
- * implementation of java.io.FileDescriptor is changed.
- */
-
-public class Exec {
- static {
- java.lang.System.loadLibrary("exec");
- }
-
- /**
- * Close a given file descriptor.
- */
- public static native void close(FileDescriptor fd);
-
- /**
- * Create a subprocess. Differs from java.lang.ProcessBuilder in that a pty
- * is used to communicate with the subprocess.
- *
- * Callers are responsible for calling Exec.close() on the returned file
- * descriptor.
- *
- * @param rdt Whether redirect stdout and stderr
- * @param cmd The command to execute.
- * @param args An array of arguments to the command.
- * @param envVars An array of strings of the form "VAR=value" to be added to the
- * environment of the process.
- * @param scripts The scripts to execute.
- * @param processId A one-element array to which the process ID of the started
- * process will be written.
- * @return File descriptor
- */
- public static native FileDescriptor createSubprocess(int rdt, String cmd,
- String[] args, String[] envVars,
- String scripts, int[] processId);
-
- /**
- * Send SIGHUP to a process group.
- */
- public static native void hangupProcessGroup(int processId);
-
- /**
- * Causes the calling thread to wait for the process associated with the
- * receiver to finish executing.
- *
- * @return The exit value of the Process being waited on
- */
- public static native int waitFor(int processId);
-}
diff --git a/app/src/main/java/org/proxydroid/Exec.kt b/app/src/main/java/org/proxydroid/Exec.kt
new file mode 100644
index 00000000..be1ca2b9
--- /dev/null
+++ b/app/src/main/java/org/proxydroid/Exec.kt
@@ -0,0 +1,45 @@
+/* proxydroid - Global / Individual Proxy App for Android
+ * Copyright (C) 2011 Max Lv
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package org.proxydroid
+
+import java.io.FileDescriptor
+
+object Exec {
+ init {
+ System.loadLibrary("exec")
+ }
+
+ @JvmStatic
+ external fun close(fd: FileDescriptor)
+
+ @JvmStatic
+ external fun createSubprocess(
+ rdt: Int,
+ cmd: String,
+ args: Array?,
+ envVars: Array?,
+ scripts: String?,
+ processId: IntArray
+ ): FileDescriptor
+
+ @JvmStatic
+ external fun hangupProcessGroup(processId: Int)
+
+ @JvmStatic
+ external fun waitFor(processId: Int): Int
+}
diff --git a/app/src/main/java/org/proxydroid/FileArrayAdapter.java b/app/src/main/java/org/proxydroid/FileArrayAdapter.java
deleted file mode 100644
index b7742c18..00000000
--- a/app/src/main/java/org/proxydroid/FileArrayAdapter.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package org.proxydroid;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.TextView;
-
-import org.proxydroid.utils.Option;
-
-import java.util.List;
-
-public class FileArrayAdapter extends ArrayAdapter