Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c28fffbdb | ||
|
|
96c86b2441 | ||
|
|
15373f8d44 | ||
|
|
30b56c97ea | ||
|
|
d081d06903 | ||
|
|
090851ed3b | ||
|
|
9352f2dfe7 | ||
|
|
9a2643ec2c | ||
|
|
5c2d548dee | ||
|
|
2fa8bc7f41 | ||
|
|
94527ea460 | ||
|
|
9259789fb9 | ||
|
|
8db642e721 | ||
|
|
fca0e87b6c | ||
|
|
b88f836616 | ||
|
|
c3bbd09ef3 | ||
|
|
ea7ede033d | ||
|
|
956443ef28 |
@@ -7,9 +7,14 @@ env:
|
||||
global:
|
||||
- GOROOT_BOOTSTRAP=$GOROOT
|
||||
|
||||
before_cache:
|
||||
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
|
||||
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.gradle
|
||||
- $HOME/.gradle/caches/
|
||||
- $HOME/.gradle/wrapper/
|
||||
|
||||
install:
|
||||
- pushd $ANDROID_HOME
|
||||
@@ -25,4 +30,4 @@ install:
|
||||
>/dev/null
|
||||
|
||||
script:
|
||||
- ./gradlew assembleDebug
|
||||
- ./gradlew assembleDebug check
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
@@ -43,8 +45,6 @@ task goClean(type: Exec) {
|
||||
args "-c", "src/overture/clean.bash"
|
||||
}
|
||||
|
||||
import org.apache.tools.ant.taskdefs.condition.Os
|
||||
|
||||
tasks.whenTaskAdded { task ->
|
||||
if ((task.name == 'generateJsonModelDebug' ||
|
||||
task.name == 'generateJsonModelRelease') &&
|
||||
|
||||
@@ -9,7 +9,7 @@ def getCurrentFlavor() {
|
||||
.matcher(getGradle().getStartParameter().getTaskRequests().toString())
|
||||
if (matcher.find()) return matcher.group(1).toLowerCase() else {
|
||||
println "NO MATCH FOUND"
|
||||
return ""
|
||||
return "debug"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@ android {
|
||||
applicationId "com.github.shadowsocks"
|
||||
minSdkVersion rootProject.minSdkVersion
|
||||
targetSdkVersion rootProject.sdkVersion
|
||||
versionCode 200
|
||||
versionName "4.4.0"
|
||||
versionCode 201
|
||||
versionName "4.4.1"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary true
|
||||
resConfigs "fa", "ja", "ko", "ru", "zh-rCN", "zh-rTW"
|
||||
@@ -36,9 +36,6 @@ android {
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
sourceSets.main.jniLibs.srcDirs +=
|
||||
new File(project(':core').buildDir, "intermediates/bundles/${getCurrentFlavor()}/jni")
|
||||
sourceSets.main.jniLibs.srcDirs += new File(project(':core').projectDir, "src/overture/bin")
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
<issue id="InvalidPackage">
|
||||
<ignore regexp="ormlite-core" />
|
||||
</issue>
|
||||
<issue id="ImpliedQuantity" severity="warning" />
|
||||
<issue id="ExtraTranslation" severity="warning" />
|
||||
<issue id="MissingTranslation" severity="informational" />
|
||||
</lint>
|
||||
@@ -0,0 +1,41 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* 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.acl
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import java.io.StringReader
|
||||
|
||||
class AclTest {
|
||||
companion object {
|
||||
private const val INPUT1 = """[proxy_all]
|
||||
[bypass_list]
|
||||
1.0.1.0/24
|
||||
[proxy_list]
|
||||
(^|\.)4tern\.com${'$'}
|
||||
"""
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse() {
|
||||
Assert.assertEquals(INPUT1, Acl().fromReader(StringReader(INPUT1)).toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* 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.database
|
||||
|
||||
import android.net.Uri
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class ProfileTest {
|
||||
@Test
|
||||
fun parsing() {
|
||||
val results = Profile.findAll("garble ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4#example-server garble")
|
||||
.toList()
|
||||
Assert.assertEquals(1, results.size)
|
||||
Assert.assertEquals(Uri.parse("ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888#example-server"),
|
||||
results.single().toUri())
|
||||
}
|
||||
}
|
||||
@@ -4742,7 +4742,7 @@ mangaapi.manhuaren.com
|
||||
^(.*\.)?android\.com$
|
||||
^(.*\.)?androidify\.com$
|
||||
^(.*\.)?appspot\.com$
|
||||
^(.*\.)?blogspot(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?blogspot\.[a-zA-Z0-9-]{1,63}[a-zA-Z0-9-]{1,2}$
|
||||
^(.*\.)?certificate-transparency\.org$
|
||||
^(.*\.)?chrome\.com$
|
||||
^(.*\.)?chromecast\.com$
|
||||
@@ -4765,8 +4765,8 @@ mangaapi.manhuaren.com
|
||||
^(.*\.)?gmail\.com$
|
||||
^(.*\.)?gmodules\.com$
|
||||
^(.*\.)?goo\.gl$
|
||||
^(.*\.)?google(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?googleapis(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?google\.[a-zA-Z0-9-]{1,63}[a-zA-Z0-9-]{1,2}$
|
||||
^(.*\.)?googleapis\.[a-zA-Z0-9-]{1,63}[a-zA-Z0-9-]{1,2}$
|
||||
^(.*\.)?googleapps\.com$
|
||||
^(.*\.)?googleartproject\.com$
|
||||
^(.*\.)?googleblog\.com$
|
||||
|
||||
@@ -4756,7 +4756,7 @@ mangaapi.manhuaren.com
|
||||
^(.*\.)?android\.com$
|
||||
^(.*\.)?androidify\.com$
|
||||
^(.*\.)?appspot\.com$
|
||||
^(.*\.)?blogspot(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?blogspot\.[a-zA-Z0-9-]{1,63}[a-zA-Z0-9-]{1,2}$
|
||||
^(.*\.)?certificate-transparency\.org$
|
||||
^(.*\.)?chrome\.com$
|
||||
^(.*\.)?chromecast\.com$
|
||||
@@ -4779,8 +4779,8 @@ mangaapi.manhuaren.com
|
||||
^(.*\.)?gmail\.com$
|
||||
^(.*\.)?gmodules\.com$
|
||||
^(.*\.)?goo\.gl$
|
||||
^(.*\.)?google(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?googleapis(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?google\.[a-zA-Z0-9-]{1,63}[a-zA-Z0-9-]{1,2}$
|
||||
^(.*\.)?googleapis\.[a-zA-Z0-9-]{1,63}[a-zA-Z0-9-]{1,2}$
|
||||
^(.*\.)?googleapps\.com$
|
||||
^(.*\.)?googleartproject\.com$
|
||||
^(.*\.)?googleblog\.com$
|
||||
|
||||
@@ -320,7 +320,7 @@ mangaapi.manhuaren.com
|
||||
^(.*\.)?android\.com$
|
||||
^(.*\.)?androidify\.com$
|
||||
^(.*\.)?appspot\.com$
|
||||
^(.*\.)?blogspot(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?blogspot\.[a-zA-Z0-9-]{1,63}[a-zA-Z0-9-]{1,2}$
|
||||
^(.*\.)?certificate-transparency\.org$
|
||||
^(.*\.)?chrome\.com$
|
||||
^(.*\.)?chromecast\.com$
|
||||
@@ -343,8 +343,8 @@ mangaapi.manhuaren.com
|
||||
^(.*\.)?gmail\.com$
|
||||
^(.*\.)?gmodules\.com$
|
||||
^(.*\.)?goo\.gl$
|
||||
^(.*\.)?google(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?googleapis(\.(?!-)[a-zA-Z0-9-]{1,63}(?<!-)){1,2}$
|
||||
^(.*\.)?google\.[a-zA-Z0-9-]{1,63}[a-zA-Z0-9-]{1,2}$
|
||||
^(.*\.)?googleapis\.[a-zA-Z0-9-]{1,63}[a-zA-Z0-9-]{1,2}$
|
||||
^(.*\.)?googleapps\.com$
|
||||
^(.*\.)?googleartproject\.com$
|
||||
^(.*\.)?googleblog\.com$
|
||||
|
||||
@@ -180,7 +180,9 @@ class AppManager : AppCompatActivity(), Toolbar.OnMenuItemClickListener {
|
||||
setContentView(R.layout.layout_apps)
|
||||
toolbar = findViewById(R.id.toolbar)
|
||||
toolbar.setTitle(R.string.proxied_apps)
|
||||
toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_material)
|
||||
val arr = obtainStyledAttributes(intArrayOf(R.attr.homeAsUpIndicator))
|
||||
toolbar.navigationIcon = arr.getDrawable(0)
|
||||
arr.recycle()
|
||||
toolbar.setNavigationOnClickListener {
|
||||
val intent = parentActivityIntent
|
||||
if (shouldUpRecreateTask(intent) || isTaskRoot)
|
||||
|
||||
@@ -76,7 +76,8 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
|
||||
}
|
||||
|
||||
@SuppressLint("ValidFragment")
|
||||
private class QRCodeDialog() : DialogFragment() {
|
||||
class QRCodeDialog() : DialogFragment() {
|
||||
|
||||
constructor(url: String) : this() {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_URL, url)
|
||||
@@ -104,9 +105,11 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
adapter?.setNdefPushMessage(null, activity)
|
||||
adapter = null
|
||||
super.onDetach()
|
||||
val activity = activity
|
||||
if (activity != null && !activity.isFinishing && !activity.isDestroyed)
|
||||
adapter?.setNdefPushMessage(null, activity)
|
||||
adapter = null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.content.Intent
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.PersistableBundle
|
||||
import android.support.v4.content.pm.ShortcutInfoCompat
|
||||
import android.support.v4.content.pm.ShortcutManagerCompat
|
||||
import android.support.v4.graphics.drawable.IconCompat
|
||||
@@ -34,8 +33,8 @@ import com.github.shadowsocks.aidl.IShadowsocksService
|
||||
import com.github.shadowsocks.bg.BaseService
|
||||
|
||||
class QuickToggleShortcut : Activity(), ShadowsocksConnection.Interface {
|
||||
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
|
||||
super.onCreate(savedInstanceState, persistentState)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (intent.action == Intent.ACTION_CREATE_SHORTCUT) {
|
||||
setResult(Activity.RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this,
|
||||
ShortcutInfoCompat.Builder(this, "toggle")
|
||||
|
||||
@@ -54,7 +54,9 @@ class ScannerActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
|
||||
setContentView(R.layout.layout_scanner)
|
||||
val toolbar = findViewById<Toolbar>(R.id.toolbar)
|
||||
toolbar.title = title
|
||||
toolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_material)
|
||||
val arr = obtainStyledAttributes(intArrayOf(R.attr.homeAsUpIndicator))
|
||||
toolbar.navigationIcon = arr.getDrawable(0)
|
||||
arr.recycle()
|
||||
toolbar.setNavigationOnClickListener { navigateUp() }
|
||||
scannerView = findViewById(R.id.scanner)
|
||||
if (Build.VERSION.SDK_INT >= 25) getSystemService(ShortcutManager::class.java).reportShortcutUsed("scan")
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
package com.github.shadowsocks
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.KeyguardManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
@@ -27,15 +28,13 @@ import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.net.VpnService
|
||||
import android.os.Bundle
|
||||
import android.os.PersistableBundle
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.util.Log
|
||||
import com.github.shadowsocks.App.Companion.app
|
||||
import com.github.shadowsocks.aidl.IShadowsocksService
|
||||
import com.github.shadowsocks.bg.BaseService
|
||||
import com.github.shadowsocks.utils.broadcastReceiver
|
||||
|
||||
class VpnRequestActivity : AppCompatActivity(), ShadowsocksConnection.Interface {
|
||||
class VpnRequestActivity : Activity(), ShadowsocksConnection.Interface {
|
||||
companion object {
|
||||
private const val TAG = "VpnRequestActivity"
|
||||
private const val REQUEST_CONNECT = 1
|
||||
@@ -43,8 +42,8 @@ class VpnRequestActivity : AppCompatActivity(), ShadowsocksConnection.Interface
|
||||
|
||||
private var receiver: BroadcastReceiver? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
|
||||
super.onCreate(savedInstanceState, persistentState)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (!BaseService.usingVpnMode) {
|
||||
finish()
|
||||
return
|
||||
|
||||
@@ -91,10 +91,8 @@ object BaseService {
|
||||
}
|
||||
var closeReceiverRegistered = false
|
||||
|
||||
private fun state() = state
|
||||
|
||||
val binder = object : IShadowsocksService.Stub() {
|
||||
override fun getState(): Int = state()
|
||||
override fun getState(): Int = this@Data.state
|
||||
override fun getProfileName(): String = profile?.formattedName ?: "Idle"
|
||||
|
||||
override fun registerCallback(cb: IShadowsocksServiceCallback) {
|
||||
@@ -182,6 +180,24 @@ object BaseService {
|
||||
}
|
||||
File(app.filesDir, "shadowsocks.json").bufferedWriter().use { it.write(config.toString()) }
|
||||
}
|
||||
|
||||
val aclFile: File? get() {
|
||||
val route = profile!!.route
|
||||
return if (route == Acl.ALL) null else
|
||||
Acl.getFile(if (route == Acl.CUSTOM_RULES) Acl.CUSTOM_RULES_FLATTENED else route)
|
||||
}
|
||||
|
||||
fun changeState(s: Int, msg: String? = null) {
|
||||
if (state == s && msg == null) return
|
||||
if (callbacks.registeredCallbackCount > 0) app.handler.post {
|
||||
val n = callbacks.beginBroadcast()
|
||||
for (i in 0 until n) try {
|
||||
callbacks.getBroadcastItem(i).stateChanged(s, binder.profileName, msg)
|
||||
} catch (_: Exception) { } // ignore
|
||||
callbacks.finishBroadcast()
|
||||
}
|
||||
state = s
|
||||
}
|
||||
}
|
||||
interface Interface {
|
||||
val tag: String
|
||||
@@ -221,7 +237,7 @@ object BaseService {
|
||||
"-t", "600",
|
||||
"-c", "shadowsocks.json"))
|
||||
|
||||
val acl = getAclFile()
|
||||
val acl = data.aclFile
|
||||
if (acl != null) {
|
||||
cmd += "--acl"
|
||||
cmd += acl.absolutePath
|
||||
@@ -232,28 +248,6 @@ object BaseService {
|
||||
data.sslocalProcess = GuardedProcess(cmd).start()
|
||||
}
|
||||
|
||||
fun getAclFile(): File? {
|
||||
val route = data.profile!!.route
|
||||
return if (route == Acl.ALL) null else
|
||||
Acl.getFile(if (route == Acl.CUSTOM_RULES) Acl.CUSTOM_RULES_FLATTENED else route)
|
||||
}
|
||||
|
||||
fun getListFile(): File? {
|
||||
val aclFile = getAclFile() ?: return null
|
||||
val listFile = File(aclFile.absolutePath + ".lst")
|
||||
val list = StringBuilder()
|
||||
aclFile.bufferedReader().useLines {
|
||||
for (line in it) {
|
||||
if (line.startsWith("^(.*\\.)?")) {
|
||||
list.append(line.substring(8).replace("\\","").replace("$",""))
|
||||
list.append("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
listFile.bufferedWriter().use { it.write(list.toString()) }
|
||||
return listFile
|
||||
}
|
||||
|
||||
fun createNotification(): ServiceNotification
|
||||
|
||||
fun startRunner() {
|
||||
@@ -270,14 +264,14 @@ object BaseService {
|
||||
|
||||
fun stopRunner(stopService: Boolean, msg: String? = null) {
|
||||
// channge the state
|
||||
changeState(STOPPING)
|
||||
val data = data
|
||||
data.changeState(STOPPING)
|
||||
|
||||
app.track(tag, "stop")
|
||||
|
||||
killProcesses()
|
||||
|
||||
// clean up recevier
|
||||
val data = data
|
||||
this as Service
|
||||
if (data.closeReceiverRegistered) {
|
||||
unregisterReceiver(data.closeReceiver)
|
||||
@@ -295,7 +289,7 @@ object BaseService {
|
||||
data.trafficMonitorThread = null
|
||||
|
||||
// change the state
|
||||
changeState(STOPPED, msg)
|
||||
data.changeState(STOPPED, msg)
|
||||
|
||||
// stop the service if nothing has bound to it
|
||||
if (stopService) stopSelf()
|
||||
@@ -309,8 +303,9 @@ object BaseService {
|
||||
val data = data
|
||||
if (data.state != STOPPED) return Service.START_NOT_STICKY
|
||||
val profile = app.currentProfile
|
||||
this as Context
|
||||
if (profile == null) {
|
||||
stopRunner(true)
|
||||
stopRunner(true, getString(R.string.profile_empty))
|
||||
return Service.START_NOT_STICKY
|
||||
}
|
||||
data.profile = profile
|
||||
@@ -327,17 +322,16 @@ object BaseService {
|
||||
filter.addAction(Action.RELOAD)
|
||||
filter.addAction(Intent.ACTION_SHUTDOWN)
|
||||
filter.addAction(Action.CLOSE)
|
||||
(this as Context).registerReceiver(data.closeReceiver, filter)
|
||||
registerReceiver(data.closeReceiver, filter)
|
||||
data.closeReceiverRegistered = true
|
||||
}
|
||||
|
||||
data.notification = createNotification()
|
||||
app.track(tag, "start")
|
||||
|
||||
changeState(CONNECTING)
|
||||
data.changeState(CONNECTING)
|
||||
|
||||
thread {
|
||||
this as Context
|
||||
try {
|
||||
if (profile.host == "198.199.101.152") {
|
||||
val client = OkHttpClient.Builder()
|
||||
@@ -385,7 +379,7 @@ object BaseService {
|
||||
|
||||
if (profile.route !in arrayOf(Acl.ALL, Acl.CUSTOM_RULES)) AclSyncJob.schedule(profile.route)
|
||||
|
||||
changeState(CONNECTED)
|
||||
data.changeState(CONNECTED)
|
||||
} catch (_: UnknownHostException) {
|
||||
stopRunner(true, getString(R.string.invalid_server))
|
||||
} catch (_: VpnService.NullConnectionException) {
|
||||
@@ -397,19 +391,6 @@ object BaseService {
|
||||
}
|
||||
return Service.START_NOT_STICKY
|
||||
}
|
||||
|
||||
fun changeState(s: Int, msg: String? = null) {
|
||||
val data = instances[this]!!
|
||||
if (data.state == s && msg == null) return
|
||||
if (data.callbacks.registeredCallbackCount > 0) app.handler.post {
|
||||
val n = data.callbacks.beginBroadcast()
|
||||
for (i in 0 until n) try {
|
||||
data.callbacks.getBroadcastItem(i).stateChanged(s, data.binder.profileName, msg)
|
||||
} catch (_: Exception) { } // ignore
|
||||
data.callbacks.finishBroadcast()
|
||||
}
|
||||
data.state = s
|
||||
}
|
||||
}
|
||||
|
||||
private val instances = WeakHashMap<Interface, Data>()
|
||||
|
||||
@@ -44,6 +44,7 @@ object LocalDnsService {
|
||||
|
||||
override fun startNativeProcesses() {
|
||||
super.startNativeProcesses()
|
||||
val data = data
|
||||
val profile = data.profile!!
|
||||
|
||||
fun makeDns(name: String, address: String, edns: Boolean = true): JSONObject {
|
||||
@@ -89,7 +90,7 @@ object LocalDnsService {
|
||||
.put("PrimaryDNS", localDns)
|
||||
.put("AlternativeDNS", remoteDns)
|
||||
.put("IPNetworkFile", "china_ip_list.txt")
|
||||
.put("DomainFile", getListFile()!!.absolutePath)
|
||||
.put("DomainFile", data.aclFile!!.absolutePath)
|
||||
Acl.CHINALIST -> config
|
||||
.put("PrimaryDNS", localDns)
|
||||
.put("AlternativeDNS", remoteDns)
|
||||
|
||||
@@ -53,7 +53,7 @@ class KeyValuePair() {
|
||||
get() = if (valueType == TYPE_LONG) ByteBuffer.wrap(value).long else null
|
||||
val string: String?
|
||||
get() = if (valueType == TYPE_STRING) String(value) else null
|
||||
val stringSet: MutableSet<String>?
|
||||
val stringSet: Set<String>?
|
||||
get() = if (valueType == TYPE_STRING_SET) {
|
||||
val buffer = ByteBuffer.wrap(value)
|
||||
val result = HashSet<String>()
|
||||
@@ -95,7 +95,7 @@ class KeyValuePair() {
|
||||
this.value = value.toByteArray()
|
||||
return this
|
||||
}
|
||||
fun put(value: MutableSet<String>): KeyValuePair {
|
||||
fun put(value: Set<String>): KeyValuePair {
|
||||
valueType = TYPE_STRING_SET
|
||||
val stream = ByteArrayOutputStream()
|
||||
for (v in value) {
|
||||
|
||||
@@ -27,7 +27,7 @@ import com.github.shadowsocks.R
|
||||
import com.github.shadowsocks.database.ProfileManager
|
||||
import com.twofortyfouram.locale.api.Intent as ApiIntent
|
||||
|
||||
class Settings(bundle: Bundle) {
|
||||
class Settings(bundle: Bundle?) {
|
||||
companion object {
|
||||
private val KEY_SWITCH_ON = "switch_on"
|
||||
private val KEY_PROFILE_ID = "profile_id"
|
||||
@@ -35,8 +35,8 @@ class Settings(bundle: Bundle) {
|
||||
fun fromIntent(intent: Intent) = Settings(intent.getBundleExtra(ApiIntent.EXTRA_BUNDLE))
|
||||
}
|
||||
|
||||
var switchOn: Boolean = bundle.getBoolean(KEY_SWITCH_ON, true)
|
||||
var profileId: Int = bundle.getInt(KEY_PROFILE_ID, -1)
|
||||
var switchOn: Boolean = bundle?.getBoolean(KEY_SWITCH_ON, true) ?: true
|
||||
var profileId: Int = bundle?.getInt(KEY_PROFILE_ID, -1) ?: -1
|
||||
|
||||
fun toIntent(context: Context): Intent {
|
||||
val bundle = Bundle()
|
||||
|
||||
@@ -7,7 +7,6 @@ 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.TypedValue
|
||||
import com.github.shadowsocks.App.Companion.app
|
||||
import java.lang.reflect.InvocationTargetException
|
||||
import java.net.InetAddress
|
||||
@@ -15,7 +14,11 @@ 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)
|
||||
try {
|
||||
InetAddress::class.java.getMethod("parseNumericAddress", String::class.java)
|
||||
} catch (_: NoSuchMethodException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
private val fieldChildFragmentManager by lazy {
|
||||
val field = Fragment::class.java.getDeclaredField("mChildFragmentManager")
|
||||
@@ -24,11 +27,12 @@ private val fieldChildFragmentManager by lazy {
|
||||
}
|
||||
|
||||
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 String.parseNumericAddress(): InetAddress =
|
||||
if (parseNumericAddressMethod == null) InetAddress.getByName(this) else 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
|
||||
|
||||
|
Before Width: | Height: | Size: 221 B |
|
After Width: | Height: | Size: 198 B |
|
Before Width: | Height: | Size: 806 B |
|
After Width: | Height: | Size: 400 B |
|
Before Width: | Height: | Size: 175 B |
|
After Width: | Height: | Size: 156 B |
|
Before Width: | Height: | Size: 556 B |
|
After Width: | Height: | Size: 298 B |
|
Before Width: | Height: | Size: 257 B |
|
After Width: | Height: | Size: 228 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 510 B |
|
Before Width: | Height: | Size: 347 B |
|
After Width: | Height: | Size: 284 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 748 B |
|
Before Width: | Height: | Size: 436 B |
|
After Width: | Height: | Size: 364 B |
|
Before Width: | Height: | Size: 277 KiB |
|
After Width: | Height: | Size: 110 KiB |
@@ -6,6 +6,7 @@
|
||||
<Spinner android:id="@+id/template_selector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:labelFor="@+id/content"
|
||||
android:entries="@array/acl_rule_templates"/>
|
||||
<EditText android:id="@+id/content"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/background_header"/>
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/drawer_title"
|
||||
<TextView android:id="@+id/drawer_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
android:ellipsize="marquee"
|
||||
android:paddingEnd="72dip"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
android:layout_height="wrap_content"
|
||||
tools:ignore="RtlSymmetry"/>
|
||||
<com.github.shadowsocks.widget.BoundedGridLayout android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:bounded_width="288dp"
|
||||
|
||||
@@ -1,34 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item android:id="@+id/selection"
|
||||
android:title="@string/action_selection"
|
||||
android:icon="@drawable/abc_ic_menu_selectall_mtrl_alpha"
|
||||
android:alphabeticShortcut="s"
|
||||
android:numericShortcut="0"
|
||||
android:visible="false"
|
||||
app:showAsAction="always">
|
||||
<item
|
||||
android:id="@+id/selection"
|
||||
android:alphabeticShortcut="s"
|
||||
android:icon="?attr/actionModeSelectAllDrawable"
|
||||
android:numericShortcut="0"
|
||||
android:title="@string/action_selection"
|
||||
android:visible="false"
|
||||
app:showAsAction="always">
|
||||
<menu>
|
||||
<item android:id="@+id/action_select_all"
|
||||
android:title="@android:string/selectAll"
|
||||
android:alphabeticShortcut="a"
|
||||
android:numericShortcut="1"
|
||||
app:showAsAction="always"/>
|
||||
<item android:id="@+id/action_cut"
|
||||
android:title="@android:string/cut"
|
||||
android:alphabeticShortcut="t"
|
||||
android:numericShortcut="2"
|
||||
app:showAsAction="always"/>
|
||||
<item android:id="@+id/action_copy"
|
||||
android:title="@android:string/copy"
|
||||
android:alphabeticShortcut="c"
|
||||
android:numericShortcut="3"
|
||||
app:showAsAction="always"/>
|
||||
<item android:id="@+id/action_delete"
|
||||
android:title="@string/delete"
|
||||
android:alphabeticShortcut="d"
|
||||
android:numericShortcut="4"
|
||||
app:showAsAction="always"/>
|
||||
<item
|
||||
android:id="@+id/action_select_all"
|
||||
android:alphabeticShortcut="a"
|
||||
android:numericShortcut="1"
|
||||
android:title="@android:string/selectAll"
|
||||
app:showAsAction="always"/>
|
||||
<item
|
||||
android:id="@+id/action_cut"
|
||||
android:alphabeticShortcut="t"
|
||||
android:numericShortcut="2"
|
||||
android:title="@android:string/cut"
|
||||
app:showAsAction="always"/>
|
||||
<item
|
||||
android:id="@+id/action_copy"
|
||||
android:alphabeticShortcut="c"
|
||||
android:numericShortcut="3"
|
||||
android:title="@android:string/copy"
|
||||
app:showAsAction="always"/>
|
||||
<item
|
||||
android:id="@+id/action_delete"
|
||||
android:alphabeticShortcut="d"
|
||||
android:numericShortcut="4"
|
||||
android:title="@string/delete"
|
||||
app:showAsAction="always"/>
|
||||
</menu>
|
||||
</item>
|
||||
<item android:title="@string/action_add_rule"
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.github.shadowsocks
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* @see [Testing documentation](http://d.android.com/tools/testing)
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, (2 + 2).toLong())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* 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.database
|
||||
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
|
||||
class KeyValuePairTest {
|
||||
@Test
|
||||
fun putAndGet() {
|
||||
val kvp = KeyValuePair()
|
||||
Assert.assertEquals(true, kvp.put(true).boolean)
|
||||
Assert.assertEquals(3f, kvp.put(3f).float)
|
||||
Assert.assertEquals(3, kvp.put(3).int)
|
||||
Assert.assertEquals(3L, kvp.put(3L).long)
|
||||
Assert.assertEquals("3", kvp.put("3").string)
|
||||
val set = (0 until 3).map(Int::toString).toSet()
|
||||
Assert.assertEquals(set, kvp.put(set).stringSet)
|
||||
Assert.assertEquals(null, kvp.boolean)
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
package com.github.shadowsocks.utils
|
||||
|
||||
import com.github.shadowsocks.utils.Subnet
|
||||
import org.junit.Assert
|
||||
import org.junit.Test
|
||||
import java.net.InetAddress
|
||||
|
||||
object SubnetTest {
|
||||
class SubnetTest {
|
||||
@Test
|
||||
fun parsingAndEquals() {
|
||||
Assert.assertEquals(Subnet(InetAddress.getByName("1.10.11.12"), 25), Subnet.fromString("1.10.11.12/25"))
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<lint>
|
||||
<issue id="ImpliedQuantity" severity="warning" />
|
||||
<issue id="ExtraTranslation" severity="warning" />
|
||||
<issue id="MissingTranslation" severity="informational" />
|
||||
</lint>
|
||||