Initial draft for porting this project to Gradle + Kotlin
This is one of my stupidest idea ever.
This commit is contained in:
@@ -1,155 +0,0 @@
|
||||
package com.github.shadowsocks.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* Commandline objects help handling command lines specifying processes to
|
||||
* execute.
|
||||
*
|
||||
* The class can be used to define a command line as nested elements or as a
|
||||
* helper to define a command line by an application.
|
||||
* <p>
|
||||
* <code>
|
||||
* <someelement><br>
|
||||
* <acommandline executable="/executable/to/run"><br>
|
||||
* <argument value="argument 1" /><br>
|
||||
* <argument line="argument_1 argument_2 argument_3" /><br>
|
||||
* <argument value="argument 4" /><br>
|
||||
* </acommandline><br>
|
||||
* </someelement><br>
|
||||
* </code>
|
||||
*
|
||||
* Based on: https://github.com/apache/ant/blob/588ce1f/src/main/org/apache/tools/ant/types/Commandline.java
|
||||
*
|
||||
* Adds support for escape character '\'.
|
||||
*
|
||||
* @author Mygod
|
||||
*/
|
||||
public final class Commandline {
|
||||
private Commandline() { }
|
||||
|
||||
/**
|
||||
* Quote the parts of the given array in way that makes them
|
||||
* usable as command line arguments.
|
||||
* @param args the list of arguments to quote.
|
||||
* @return empty string for null or no command, else every argument split
|
||||
* by spaces and quoted by quoting rules.
|
||||
*/
|
||||
public static String toString(Iterable<String> args) {
|
||||
// empty path return empty string
|
||||
if (args == null) {
|
||||
return "";
|
||||
}
|
||||
// path containing one or more elements
|
||||
final StringBuilder result = new StringBuilder();
|
||||
for (String arg : args) {
|
||||
if (result.length() > 0) result.append(' ');
|
||||
for (int j = 0; j < arg.length(); ++j) {
|
||||
char ch = arg.charAt(j);
|
||||
switch (ch) {
|
||||
case ' ': case '\\': case '"': case '\'': result.append('\\'); // intentionally no break
|
||||
default: result.append(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
/**
|
||||
* Quote the parts of the given array in way that makes them
|
||||
* usable as command line arguments.
|
||||
* @param args the list of arguments to quote.
|
||||
* @return empty string for null or no command, else every argument split
|
||||
* by spaces and quoted by quoting rules.
|
||||
*/
|
||||
public static String toString(String[] args) {
|
||||
return toString(Arrays.asList(args)); // thanks to Java, arrays aren't iterable
|
||||
}
|
||||
|
||||
/**
|
||||
* Crack a command line.
|
||||
* @param toProcess the command line to process.
|
||||
* @return the command line broken into strings.
|
||||
* An empty or null toProcess parameter results in a zero sized array.
|
||||
*/
|
||||
public static String[] translateCommandline(String toProcess) {
|
||||
if (toProcess == null || toProcess.length() == 0) {
|
||||
//no command? no string
|
||||
return new String[0];
|
||||
}
|
||||
// parse with a simple finite state machine
|
||||
|
||||
final int normal = 0;
|
||||
final int inQuote = 1;
|
||||
final int inDoubleQuote = 2;
|
||||
int state = normal;
|
||||
final StringTokenizer tok = new StringTokenizer(toProcess, "\\\"\' ", true);
|
||||
final ArrayList<String> result = new ArrayList<>();
|
||||
final StringBuilder current = new StringBuilder();
|
||||
boolean lastTokenHasBeenQuoted = false;
|
||||
boolean lastTokenIsSlash = false;
|
||||
|
||||
while (tok.hasMoreTokens()) {
|
||||
String nextTok = tok.nextToken();
|
||||
switch (state) {
|
||||
case inQuote:
|
||||
if ("\'".equals(nextTok)) {
|
||||
lastTokenHasBeenQuoted = true;
|
||||
state = normal;
|
||||
} else {
|
||||
current.append(nextTok);
|
||||
}
|
||||
break;
|
||||
case inDoubleQuote:
|
||||
if ("\"".equals(nextTok)) {
|
||||
if (lastTokenIsSlash) {
|
||||
current.append(nextTok);
|
||||
lastTokenIsSlash = false;
|
||||
} else {
|
||||
lastTokenHasBeenQuoted = true;
|
||||
state = normal;
|
||||
}
|
||||
} else if ("\\".equals(nextTok)) {
|
||||
if (lastTokenIsSlash) {
|
||||
current.append(nextTok);
|
||||
lastTokenIsSlash = false;
|
||||
} else lastTokenIsSlash = true;
|
||||
} else {
|
||||
if (lastTokenIsSlash) {
|
||||
current.append("\\"); // unescaped
|
||||
lastTokenIsSlash = false;
|
||||
}
|
||||
current.append(nextTok);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (lastTokenIsSlash) {
|
||||
current.append(nextTok);
|
||||
lastTokenIsSlash = false;
|
||||
} else if ("\\".equals(nextTok)) lastTokenIsSlash = true; else if ("\'".equals(nextTok)) {
|
||||
state = inQuote;
|
||||
} else if ("\"".equals(nextTok)) {
|
||||
state = inDoubleQuote;
|
||||
} else if (" ".equals(nextTok)) {
|
||||
if (lastTokenHasBeenQuoted || current.length() != 0) {
|
||||
result.add(current.toString());
|
||||
current.setLength(0);
|
||||
}
|
||||
} else {
|
||||
current.append(nextTok);
|
||||
}
|
||||
lastTokenHasBeenQuoted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lastTokenHasBeenQuoted || current.length() != 0) {
|
||||
result.add(current.toString());
|
||||
}
|
||||
if (state == inQuote || state == inDoubleQuote) {
|
||||
throw new IllegalArgumentException("unbalanced quotes in " + toProcess);
|
||||
}
|
||||
if (lastTokenIsSlash) throw new IllegalArgumentException("escape character following nothing in " + toProcess);
|
||||
return result.toArray(new String[result.size()]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
|
||||
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
package com.github.shadowsocks.utils
|
||||
|
||||
import java.util.ArrayList
|
||||
import java.util.Arrays
|
||||
import java.util.StringTokenizer
|
||||
|
||||
/**
|
||||
* Commandline objects help handling command lines specifying processes to
|
||||
* execute.
|
||||
*
|
||||
* The class can be used to define a command line as nested elements or as a
|
||||
* helper to define a command line by an application.
|
||||
*
|
||||
*
|
||||
* `
|
||||
* <someelement><br></br>
|
||||
* <acommandline executable="/executable/to/run"><br></br>
|
||||
* <argument value="argument 1" /><br></br>
|
||||
* <argument line="argument_1 argument_2 argument_3" /><br></br>
|
||||
* <argument value="argument 4" /><br></br>
|
||||
* </acommandline><br></br>
|
||||
* </someelement><br></br>
|
||||
` *
|
||||
*
|
||||
* Based on: https://github.com/apache/ant/blob/588ce1f/src/main/org/apache/tools/ant/types/Commandline.java
|
||||
*
|
||||
* Adds support for escape character '\'.
|
||||
*/
|
||||
object Commandline {
|
||||
|
||||
/**
|
||||
* Quote the parts of the given array in way that makes them
|
||||
* usable as command line arguments.
|
||||
* @param args the list of arguments to quote.
|
||||
* @return empty string for null or no command, else every argument split
|
||||
* by spaces and quoted by quoting rules.
|
||||
*/
|
||||
fun toString(args: Iterable<String>?): String {
|
||||
// empty path return empty string
|
||||
if (args == null) {
|
||||
return ""
|
||||
}
|
||||
// path containing one or more elements
|
||||
val result = StringBuilder()
|
||||
for (arg in args) {
|
||||
if (result.isNotEmpty()) result.append(' ')
|
||||
(0 until arg.length)
|
||||
.map { arg[it] }
|
||||
.forEach {
|
||||
when (it) {
|
||||
' ', '\\', '"', '\'' -> {
|
||||
result.append('\\') // intentionally no break
|
||||
result.append(it)
|
||||
}
|
||||
else -> result.append(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote the parts of the given array in way that makes them
|
||||
* usable as command line arguments.
|
||||
* @param args the list of arguments to quote.
|
||||
* @return empty string for null or no command, else every argument split
|
||||
* by spaces and quoted by quoting rules.
|
||||
*/
|
||||
fun toString(args: Array<String>): String {
|
||||
return toString(Arrays.asList(*args)) // thanks to Java, arrays aren't iterable
|
||||
}
|
||||
|
||||
/**
|
||||
* Crack a command line.
|
||||
* @param toProcess the command line to process.
|
||||
* @return the command line broken into strings.
|
||||
* An empty or null toProcess parameter results in a zero sized array.
|
||||
*/
|
||||
fun translateCommandline(toProcess: String?): Array<String> {
|
||||
if (toProcess == null || toProcess.isEmpty()) {
|
||||
//no command? no string
|
||||
return arrayOf()
|
||||
}
|
||||
// parse with a simple finite state machine
|
||||
|
||||
val normal = 0
|
||||
val inQuote = 1
|
||||
val inDoubleQuote = 2
|
||||
var state = normal
|
||||
val tok = StringTokenizer(toProcess, "\\\"\' ", true)
|
||||
val result = ArrayList<String>()
|
||||
val current = StringBuilder()
|
||||
var lastTokenHasBeenQuoted = false
|
||||
var lastTokenIsSlash = false
|
||||
|
||||
while (tok.hasMoreTokens()) {
|
||||
val nextTok = tok.nextToken()
|
||||
when (state) {
|
||||
inQuote -> if ("\'" == nextTok) {
|
||||
lastTokenHasBeenQuoted = true
|
||||
state = normal
|
||||
} else {
|
||||
current.append(nextTok)
|
||||
}
|
||||
inDoubleQuote -> if ("\"" == nextTok) {
|
||||
if (lastTokenIsSlash) {
|
||||
current.append(nextTok)
|
||||
lastTokenIsSlash = false
|
||||
} else {
|
||||
lastTokenHasBeenQuoted = true
|
||||
state = normal
|
||||
}
|
||||
} else if ("\\" == nextTok) {
|
||||
lastTokenIsSlash = if (lastTokenIsSlash) {
|
||||
current.append(nextTok)
|
||||
false
|
||||
} else
|
||||
true
|
||||
} else {
|
||||
if (lastTokenIsSlash) {
|
||||
current.append("\\") // unescaped
|
||||
lastTokenIsSlash = false
|
||||
}
|
||||
current.append(nextTok)
|
||||
}
|
||||
else -> {
|
||||
if (lastTokenIsSlash) {
|
||||
current.append(nextTok)
|
||||
lastTokenIsSlash = false
|
||||
} else if ("\\" == nextTok)
|
||||
lastTokenIsSlash = true
|
||||
else if ("\'" == nextTok) {
|
||||
state = inQuote
|
||||
} else if ("\"" == nextTok) {
|
||||
state = inDoubleQuote
|
||||
} else if (" " == nextTok) {
|
||||
if (lastTokenHasBeenQuoted || current.isNotEmpty()) {
|
||||
result.add(current.toString())
|
||||
current.setLength(0)
|
||||
}
|
||||
} else {
|
||||
current.append(nextTok)
|
||||
}
|
||||
lastTokenHasBeenQuoted = false
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastTokenHasBeenQuoted || current.isNotEmpty()) {
|
||||
result.add(current.toString())
|
||||
}
|
||||
if (state == inQuote || state == inDoubleQuote) {
|
||||
throw IllegalArgumentException("unbalanced quotes in " + toProcess)
|
||||
}
|
||||
if (lastTokenIsSlash) throw IllegalArgumentException("escape character following nothing in " + toProcess)
|
||||
return result.toTypedArray()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
|
||||
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
package com.github.shadowsocks.utils
|
||||
|
||||
object Key {
|
||||
const val PROFILE = "profile.db"
|
||||
|
||||
const val id = "profileId"
|
||||
const val name = "profileName"
|
||||
|
||||
const val individual = "Proxyed"
|
||||
|
||||
const val serviceMode = "serviceMode"
|
||||
const val modeProxy = "proxy"
|
||||
const val modeVpn = "vpn"
|
||||
const val modeTransproxy = "transproxy"
|
||||
const val portProxy = "portProxy"
|
||||
const val portLocalDns = "portLocalDns"
|
||||
const val portTransproxy = "portTransproxy"
|
||||
|
||||
const val route = "route"
|
||||
|
||||
const val isAutoConnect = "isAutoConnect"
|
||||
|
||||
const val proxyApps = "isProxyApps"
|
||||
const val bypass = "isBypassApps"
|
||||
const val udpdns = "isUdpDns"
|
||||
const val ipv6 = "isIpv6"
|
||||
|
||||
const val host = "proxy"
|
||||
const val password = "sitekey"
|
||||
const val method = "encMethod"
|
||||
const val remotePort = "remotePortNum"
|
||||
const val remoteDns = "remoteDns"
|
||||
|
||||
const val plugin = "plugin"
|
||||
const val pluginConfigure = "plugin.configure"
|
||||
|
||||
const val dirty = "profileDirty"
|
||||
|
||||
const val tfo = "tcp_fastopen"
|
||||
const val assetUpdateTime = "assetUpdateTime"
|
||||
}
|
||||
|
||||
object Action {
|
||||
const val SERVICE = "com.github.shadowsocks.SERVICE"
|
||||
const val CLOSE = "com.github.shadowsocks.CLOSE"
|
||||
const val RELOAD = "com.github.shadowsocks.RELOAD"
|
||||
|
||||
const val EXTRA_PROFILE_ID = "com.github.shadowsocks.EXTRA_PROFILE_ID"
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright 2010 Brave New Software Project, Inc.
|
||||
*
|
||||
* 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 com.github.shadowsocks.utils;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.LinkProperties;
|
||||
import android.os.Build;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
public class Dns {
|
||||
public static String getDnsResolver(Context context) throws Exception {
|
||||
Collection<InetAddress> dnsResolvers = getDnsResolvers(context);
|
||||
if (dnsResolvers.isEmpty()) {
|
||||
throw new Exception("Couldn't find an active DNS resolver");
|
||||
}
|
||||
String dnsResolver = dnsResolvers.iterator().next().toString();
|
||||
if (dnsResolver.startsWith("/")) {
|
||||
dnsResolver = dnsResolver.substring(1);
|
||||
}
|
||||
return dnsResolver;
|
||||
}
|
||||
|
||||
private static Collection<InetAddress> getDnsResolvers(Context context) throws Exception {
|
||||
ArrayList<InetAddress> addresses = new ArrayList<InetAddress>();
|
||||
ConnectivityManager connectivityManager =
|
||||
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
Class<?> LinkPropertiesClass = Class.forName("android.net.LinkProperties");
|
||||
Method getActiveLinkPropertiesMethod = ConnectivityManager.class.getMethod("getActiveLinkProperties");
|
||||
Object linkProperties = getActiveLinkPropertiesMethod.invoke(connectivityManager);
|
||||
if (linkProperties != null) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
Method getDnsesMethod = LinkPropertiesClass.getMethod("getDnses");
|
||||
Collection<?> dnses = (Collection<?>)getDnsesMethod.invoke(linkProperties);
|
||||
for (Object dns : dnses) {
|
||||
addresses.add((InetAddress)dns);
|
||||
}
|
||||
} else {
|
||||
addresses.addAll(((LinkProperties) linkProperties).getDnsServers());
|
||||
}
|
||||
}
|
||||
|
||||
return addresses;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
|
||||
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
package com.github.shadowsocks.utils
|
||||
|
||||
import java.net.InetAddress
|
||||
import java.util.*
|
||||
|
||||
class Subnet(val address: InetAddress, val prefixSize: Int) : Comparable<Subnet> {
|
||||
companion object {
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun fromString(value: String): Subnet {
|
||||
val parts = value.split('/')
|
||||
val addr = parts[0].parseNumericAddress()
|
||||
return when (parts.size) {
|
||||
1 -> Subnet(addr, addr.address.size shl 3)
|
||||
2 -> Subnet(addr, parts[1].toInt())
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val addressLength get() = address.address.size shl 3
|
||||
|
||||
init {
|
||||
if (prefixSize < 0 || prefixSize > addressLength) throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
override fun toString(): String =
|
||||
if (prefixSize == addressLength) address.hostAddress else address.hostAddress + '/' + prefixSize
|
||||
|
||||
private fun Byte.unsigned() = toInt() and 0xFF
|
||||
override fun compareTo(other: Subnet): Int {
|
||||
val addrThis = address.address
|
||||
val addrThat = other.address.address
|
||||
var result = addrThis.size.compareTo(addrThat.size) // IPv4 address goes first
|
||||
if (result != 0) return result
|
||||
for ((x, y) in addrThis zip addrThat) {
|
||||
result = x.unsigned().compareTo(y.unsigned()) // undo sign extension of signed byte
|
||||
if (result != 0) return result
|
||||
}
|
||||
return prefixSize.compareTo(other.prefixSize)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
val that = other as? Subnet
|
||||
return address == that?.address && prefixSize == that.prefixSize
|
||||
}
|
||||
override fun hashCode(): Int = Objects.hash(address, prefixSize)
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
|
||||
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
|
||||
* *
|
||||
* 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 <http://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
|
||||
package com.github.shadowsocks.utils
|
||||
|
||||
import eu.chainfire.libsuperuser.Shell
|
||||
import java.io.File
|
||||
|
||||
object TcpFastOpen {
|
||||
/**
|
||||
* Is kernel version >= 3.7.1.
|
||||
*/
|
||||
val supported by lazy {
|
||||
val match = """^(\d+)\.(\d+)\.(\d+)""".toRegex().find(System.getProperty("os.version"))
|
||||
if (match == null) false else when (match.groupValues[1].toInt()) {
|
||||
in Int.MIN_VALUE .. 2 -> false
|
||||
3 -> when (match.groupValues[2].toInt()) {
|
||||
in Int.MIN_VALUE .. 6 -> false
|
||||
7 -> match.groupValues[3].toInt() >= 1
|
||||
else -> true
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
val sendEnabled: Boolean get() {
|
||||
val file = File("/proc/sys/net/ipv4/tcp_fastopen")
|
||||
return file.canRead() && file.bufferedReader().use { it.readText() }.trim().toInt() and 1 > 0
|
||||
}
|
||||
|
||||
fun enabled(value: Boolean): String? = if (sendEnabled == value) null else Shell.run("su", arrayOf(
|
||||
"if echo " + (if (value) 3 else 0) + " > /proc/sys/net/ipv4/tcp_fastopen; then",
|
||||
" echo Success.",
|
||||
"else",
|
||||
" echo Failed.",
|
||||
"fi"), null, true)?.joinToString("\n")
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.github.shadowsocks.utils
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
import android.os.Build
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.app.FragmentManager
|
||||
import android.support.v7.util.SortedList
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.Toast
|
||||
import com.github.shadowsocks.App.Companion.app
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.net.InetAddress
|
||||
import java.net.URLConnection
|
||||
|
||||
private val isNumericMethod by lazy { InetAddress::class.java.getMethod("isNumeric", String::class.java) }
|
||||
private val parseNumericAddressMethod by lazy {
|
||||
InetAddress::class.java.getMethod("parseNumericAddress", String::class.java)
|
||||
}
|
||||
private val fieldChildFragmentManager by lazy {
|
||||
val field = Fragment::class.java.getDeclaredField("mChildFragmentManager")
|
||||
field.isAccessible = true
|
||||
field
|
||||
}
|
||||
|
||||
fun String.isNumericAddress(): Boolean = isNumericMethod.invoke(null, this) as Boolean
|
||||
fun String.parseNumericAddress(): InetAddress = try {
|
||||
parseNumericAddressMethod.invoke(null, this) as InetAddress
|
||||
} catch (exc: InvocationTargetException) {
|
||||
throw exc.cause ?: exc
|
||||
}
|
||||
|
||||
fun parsePort(str: String?, default: Int, min: Int = 1025): Int {
|
||||
val x = str?.toIntOrNull() ?: default
|
||||
return if (x < min || x > 65535) default else x
|
||||
}
|
||||
|
||||
fun broadcastReceiver(callback: (Context, Intent) -> Unit): BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) = callback(context, intent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for kotlin.concurrent.thread that tracks uncaught exceptions.
|
||||
*/
|
||||
fun thread(start: Boolean = true, isDaemon: Boolean = false, contextClassLoader: ClassLoader? = null,
|
||||
name: String? = null, priority: Int = -1, block: () -> Unit): Thread {
|
||||
val thread = kotlin.concurrent.thread(false, isDaemon, contextClassLoader, name, priority, block)
|
||||
thread.setUncaughtExceptionHandler(app::track)
|
||||
if (start) thread.start()
|
||||
return thread
|
||||
}
|
||||
|
||||
val URLConnection.responseLength: Long
|
||||
get() = if (Build.VERSION.SDK_INT >= 24) contentLengthLong else contentLength.toLong()
|
||||
|
||||
/**
|
||||
* Based on: http://stackoverflow.com/a/21026866/2245107
|
||||
*/
|
||||
fun Toast.position(view: View, window: Window, offsetX: Int = 0, offsetY: Int = 0): Toast {
|
||||
val rect = Rect()
|
||||
window.decorView.getWindowVisibleDisplayFrame(rect)
|
||||
val viewLocation = IntArray(2)
|
||||
view.getLocationInWindow(viewLocation)
|
||||
val metrics = DisplayMetrics()
|
||||
window.windowManager.defaultDisplay.getMetrics(metrics)
|
||||
val toastView = this.view
|
||||
toastView.measure(View.MeasureSpec.makeMeasureSpec(metrics.widthPixels, View.MeasureSpec.UNSPECIFIED),
|
||||
View.MeasureSpec.makeMeasureSpec(metrics.heightPixels, View.MeasureSpec.UNSPECIFIED))
|
||||
setGravity(Gravity.START or Gravity.TOP,
|
||||
viewLocation[0] - rect.left + (view.width - toastView.measuredWidth) / 2 + offsetX,
|
||||
viewLocation[1] - rect.top + view.height + offsetY)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on: https://stackoverflow.com/a/15656428/2245107
|
||||
*/
|
||||
var Fragment.childFragManager: FragmentManager?
|
||||
get() = childFragmentManager
|
||||
set(value) = fieldChildFragmentManager.set(this, value)
|
||||
|
||||
private class SortedListIterable<out T>(private val list: SortedList<T>) : Iterable<T> {
|
||||
override fun iterator(): Iterator<T> = SortedListIterator(list)
|
||||
}
|
||||
private class SortedListIterator<out T>(private val list: SortedList<T>) : Iterator<T> {
|
||||
private var count = 0
|
||||
override fun hasNext() = count < list.size()
|
||||
override fun next(): T = list[count++]
|
||||
}
|
||||
fun <T> SortedList<T>.asIterable(): Iterable<T> = SortedListIterable(this)
|
||||
Reference in New Issue
Block a user