Compare commits

..
18 Commits
Author SHA1 Message Date
Max Lv 8c28fffbdb Bump version 2017-12-15 01:41:49 -08:00
Max Lv 96c86b2441 Make QR code dialog public #1500 2017-12-14 20:04:37 -08:00
Mygod 15373f8d44 Add missing lint ignores 2017-12-14 15:34:45 -08:00
Mygod 30b56c97ea Add more tests, fix lint errors and add check to travis 2017-12-14 14:55:00 -08:00
Mygod d081d06903 Replace png with lossless webp 2017-12-14 14:53:11 -08:00
Mygod 090851ed3b Refine Travis cache 2017-12-14 13:07:01 -08:00
Mygod 9352f2dfe7 Fix SubnetTest 2017-12-14 12:41:09 -08:00
Mygod 9a2643ec2c Add missing error message 2017-12-14 12:00:13 -08:00
Mygod 5c2d548dee Reduce the number of default methods 2017-12-14 12:00:13 -08:00
Mygod 2fa8bc7f41 Refine #1500 2017-12-14 12:00:13 -08:00
Mygod 94527ea460 Refine #1497 2017-12-14 12:00:13 -08:00
Mygod 9259789fb9 Refine #1481 2017-12-14 12:00:13 -08:00
Mygod 8db642e721 Refine build.gradle format 2017-12-14 12:00:13 -08:00
Max Lv fca0e87b6c Update GFW List 2017-12-14 00:48:54 -08:00
Max Lv b88f836616 Refine #1489 2017-12-13 23:35:10 +08:00
Max Lv c3bbd09ef3 Fix #1500 2017-12-13 22:32:37 +08:00
Max Lv ea7ede033d Fix #1497 2017-12-13 02:06:20 -08:00
Max Lv 956443ef28 Fix the shortcut #1481 2017-12-12 23:45:22 -08:00
48 changed files with 254 additions and 142 deletions
+7 -2
View File
@@ -7,9 +7,14 @@ env:
global: global:
- GOROOT_BOOTSTRAP=$GOROOT - GOROOT_BOOTSTRAP=$GOROOT
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/
cache: cache:
directories: directories:
- $HOME/.gradle - $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
install: install:
- pushd $ANDROID_HOME - pushd $ANDROID_HOME
@@ -25,4 +30,4 @@ install:
>/dev/null >/dev/null
script: script:
- ./gradlew assembleDebug - ./gradlew assembleDebug check
+2 -2
View File
@@ -1,3 +1,5 @@
import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
android { android {
@@ -43,8 +45,6 @@ task goClean(type: Exec) {
args "-c", "src/overture/clean.bash" args "-c", "src/overture/clean.bash"
} }
import org.apache.tools.ant.taskdefs.condition.Os
tasks.whenTaskAdded { task -> tasks.whenTaskAdded { task ->
if ((task.name == 'generateJsonModelDebug' || if ((task.name == 'generateJsonModelDebug' ||
task.name == 'generateJsonModelRelease') && task.name == 'generateJsonModelRelease') &&
+3 -6
View File
@@ -9,7 +9,7 @@ def getCurrentFlavor() {
.matcher(getGradle().getStartParameter().getTaskRequests().toString()) .matcher(getGradle().getStartParameter().getTaskRequests().toString())
if (matcher.find()) return matcher.group(1).toLowerCase() else { if (matcher.find()) return matcher.group(1).toLowerCase() else {
println "NO MATCH FOUND" println "NO MATCH FOUND"
return "" return "debug"
} }
} }
@@ -20,8 +20,8 @@ android {
applicationId "com.github.shadowsocks" applicationId "com.github.shadowsocks"
minSdkVersion rootProject.minSdkVersion minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.sdkVersion targetSdkVersion rootProject.sdkVersion
versionCode 200 versionCode 201
versionName "4.4.0" versionName "4.4.1"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary true vectorDrawables.useSupportLibrary true
resConfigs "fa", "ja", "ko", "ru", "zh-rCN", "zh-rTW" resConfigs "fa", "ja", "ko", "ru", "zh-rCN", "zh-rTW"
@@ -36,9 +36,6 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
lintOptions {
abortOnError false
}
sourceSets.main.jniLibs.srcDirs += sourceSets.main.jniLibs.srcDirs +=
new File(project(':core').buildDir, "intermediates/bundles/${getCurrentFlavor()}/jni") new File(project(':core').buildDir, "intermediates/bundles/${getCurrentFlavor()}/jni")
sourceSets.main.jniLibs.srcDirs += new File(project(':core').projectDir, "src/overture/bin") sourceSets.main.jniLibs.srcDirs += new File(project(':core').projectDir, "src/overture/bin")
+9
View File
@@ -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())
}
}
+3 -3
View File
@@ -4742,7 +4742,7 @@ mangaapi.manhuaren.com
^(.*\.)?android\.com$ ^(.*\.)?android\.com$
^(.*\.)?androidify\.com$ ^(.*\.)?androidify\.com$
^(.*\.)?appspot\.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$ ^(.*\.)?certificate-transparency\.org$
^(.*\.)?chrome\.com$ ^(.*\.)?chrome\.com$
^(.*\.)?chromecast\.com$ ^(.*\.)?chromecast\.com$
@@ -4765,8 +4765,8 @@ mangaapi.manhuaren.com
^(.*\.)?gmail\.com$ ^(.*\.)?gmail\.com$
^(.*\.)?gmodules\.com$ ^(.*\.)?gmodules\.com$
^(.*\.)?goo\.gl$ ^(.*\.)?goo\.gl$
^(.*\.)?google(\.(?!-)[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}(?<!-)){1,2}$ ^(.*\.)?googleapis\.[a-zA-Z0-9-]{1,63}[a-zA-Z0-9-]{1,2}$
^(.*\.)?googleapps\.com$ ^(.*\.)?googleapps\.com$
^(.*\.)?googleartproject\.com$ ^(.*\.)?googleartproject\.com$
^(.*\.)?googleblog\.com$ ^(.*\.)?googleblog\.com$
@@ -4756,7 +4756,7 @@ mangaapi.manhuaren.com
^(.*\.)?android\.com$ ^(.*\.)?android\.com$
^(.*\.)?androidify\.com$ ^(.*\.)?androidify\.com$
^(.*\.)?appspot\.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$ ^(.*\.)?certificate-transparency\.org$
^(.*\.)?chrome\.com$ ^(.*\.)?chrome\.com$
^(.*\.)?chromecast\.com$ ^(.*\.)?chromecast\.com$
@@ -4779,8 +4779,8 @@ mangaapi.manhuaren.com
^(.*\.)?gmail\.com$ ^(.*\.)?gmail\.com$
^(.*\.)?gmodules\.com$ ^(.*\.)?gmodules\.com$
^(.*\.)?goo\.gl$ ^(.*\.)?goo\.gl$
^(.*\.)?google(\.(?!-)[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}(?<!-)){1,2}$ ^(.*\.)?googleapis\.[a-zA-Z0-9-]{1,63}[a-zA-Z0-9-]{1,2}$
^(.*\.)?googleapps\.com$ ^(.*\.)?googleapps\.com$
^(.*\.)?googleartproject\.com$ ^(.*\.)?googleartproject\.com$
^(.*\.)?googleblog\.com$ ^(.*\.)?googleblog\.com$
+3 -3
View File
@@ -320,7 +320,7 @@ mangaapi.manhuaren.com
^(.*\.)?android\.com$ ^(.*\.)?android\.com$
^(.*\.)?androidify\.com$ ^(.*\.)?androidify\.com$
^(.*\.)?appspot\.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$ ^(.*\.)?certificate-transparency\.org$
^(.*\.)?chrome\.com$ ^(.*\.)?chrome\.com$
^(.*\.)?chromecast\.com$ ^(.*\.)?chromecast\.com$
@@ -343,8 +343,8 @@ mangaapi.manhuaren.com
^(.*\.)?gmail\.com$ ^(.*\.)?gmail\.com$
^(.*\.)?gmodules\.com$ ^(.*\.)?gmodules\.com$
^(.*\.)?goo\.gl$ ^(.*\.)?goo\.gl$
^(.*\.)?google(\.(?!-)[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}(?<!-)){1,2}$ ^(.*\.)?googleapis\.[a-zA-Z0-9-]{1,63}[a-zA-Z0-9-]{1,2}$
^(.*\.)?googleapps\.com$ ^(.*\.)?googleapps\.com$
^(.*\.)?googleartproject\.com$ ^(.*\.)?googleartproject\.com$
^(.*\.)?googleblog\.com$ ^(.*\.)?googleblog\.com$
@@ -180,7 +180,9 @@ class AppManager : AppCompatActivity(), Toolbar.OnMenuItemClickListener {
setContentView(R.layout.layout_apps) setContentView(R.layout.layout_apps)
toolbar = findViewById(R.id.toolbar) toolbar = findViewById(R.id.toolbar)
toolbar.setTitle(R.string.proxied_apps) 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 { toolbar.setNavigationOnClickListener {
val intent = parentActivityIntent val intent = parentActivityIntent
if (shouldUpRecreateTask(intent) || isTaskRoot) if (shouldUpRecreateTask(intent) || isTaskRoot)
@@ -76,7 +76,8 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
} }
@SuppressLint("ValidFragment") @SuppressLint("ValidFragment")
private class QRCodeDialog() : DialogFragment() { class QRCodeDialog() : DialogFragment() {
constructor(url: String) : this() { constructor(url: String) : this() {
val bundle = Bundle() val bundle = Bundle()
bundle.putString(KEY_URL, url) bundle.putString(KEY_URL, url)
@@ -104,9 +105,11 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
} }
override fun onDetach() { override fun onDetach() {
adapter?.setNdefPushMessage(null, activity)
adapter = null
super.onDetach() 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.content.pm.ShortcutManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.PersistableBundle
import android.support.v4.content.pm.ShortcutInfoCompat import android.support.v4.content.pm.ShortcutInfoCompat
import android.support.v4.content.pm.ShortcutManagerCompat import android.support.v4.content.pm.ShortcutManagerCompat
import android.support.v4.graphics.drawable.IconCompat import android.support.v4.graphics.drawable.IconCompat
@@ -34,8 +33,8 @@ import com.github.shadowsocks.aidl.IShadowsocksService
import com.github.shadowsocks.bg.BaseService import com.github.shadowsocks.bg.BaseService
class QuickToggleShortcut : Activity(), ShadowsocksConnection.Interface { class QuickToggleShortcut : Activity(), ShadowsocksConnection.Interface {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState, persistentState) super.onCreate(savedInstanceState)
if (intent.action == Intent.ACTION_CREATE_SHORTCUT) { if (intent.action == Intent.ACTION_CREATE_SHORTCUT) {
setResult(Activity.RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this, setResult(Activity.RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this,
ShortcutInfoCompat.Builder(this, "toggle") ShortcutInfoCompat.Builder(this, "toggle")
@@ -54,7 +54,9 @@ class ScannerActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
setContentView(R.layout.layout_scanner) setContentView(R.layout.layout_scanner)
val toolbar = findViewById<Toolbar>(R.id.toolbar) val toolbar = findViewById<Toolbar>(R.id.toolbar)
toolbar.title = title 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() } toolbar.setNavigationOnClickListener { navigateUp() }
scannerView = findViewById(R.id.scanner) scannerView = findViewById(R.id.scanner)
if (Build.VERSION.SDK_INT >= 25) getSystemService(ShortcutManager::class.java).reportShortcutUsed("scan") if (Build.VERSION.SDK_INT >= 25) getSystemService(ShortcutManager::class.java).reportShortcutUsed("scan")
@@ -20,6 +20,7 @@
package com.github.shadowsocks package com.github.shadowsocks
import android.app.Activity
import android.app.KeyguardManager import android.app.KeyguardManager
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
@@ -27,15 +28,13 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.net.VpnService import android.net.VpnService
import android.os.Bundle import android.os.Bundle
import android.os.PersistableBundle
import android.support.v7.app.AppCompatActivity
import android.util.Log import android.util.Log
import com.github.shadowsocks.App.Companion.app import com.github.shadowsocks.App.Companion.app
import com.github.shadowsocks.aidl.IShadowsocksService import com.github.shadowsocks.aidl.IShadowsocksService
import com.github.shadowsocks.bg.BaseService import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.utils.broadcastReceiver import com.github.shadowsocks.utils.broadcastReceiver
class VpnRequestActivity : AppCompatActivity(), ShadowsocksConnection.Interface { class VpnRequestActivity : Activity(), ShadowsocksConnection.Interface {
companion object { companion object {
private const val TAG = "VpnRequestActivity" private const val TAG = "VpnRequestActivity"
private const val REQUEST_CONNECT = 1 private const val REQUEST_CONNECT = 1
@@ -43,8 +42,8 @@ class VpnRequestActivity : AppCompatActivity(), ShadowsocksConnection.Interface
private var receiver: BroadcastReceiver? = null private var receiver: BroadcastReceiver? = null
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState, persistentState) super.onCreate(savedInstanceState)
if (!BaseService.usingVpnMode) { if (!BaseService.usingVpnMode) {
finish() finish()
return return
@@ -91,10 +91,8 @@ object BaseService {
} }
var closeReceiverRegistered = false var closeReceiverRegistered = false
private fun state() = state
val binder = object : IShadowsocksService.Stub() { 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 getProfileName(): String = profile?.formattedName ?: "Idle"
override fun registerCallback(cb: IShadowsocksServiceCallback) { override fun registerCallback(cb: IShadowsocksServiceCallback) {
@@ -182,6 +180,24 @@ object BaseService {
} }
File(app.filesDir, "shadowsocks.json").bufferedWriter().use { it.write(config.toString()) } 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 { interface Interface {
val tag: String val tag: String
@@ -221,7 +237,7 @@ object BaseService {
"-t", "600", "-t", "600",
"-c", "shadowsocks.json")) "-c", "shadowsocks.json"))
val acl = getAclFile() val acl = data.aclFile
if (acl != null) { if (acl != null) {
cmd += "--acl" cmd += "--acl"
cmd += acl.absolutePath cmd += acl.absolutePath
@@ -232,28 +248,6 @@ object BaseService {
data.sslocalProcess = GuardedProcess(cmd).start() 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 createNotification(): ServiceNotification
fun startRunner() { fun startRunner() {
@@ -270,14 +264,14 @@ object BaseService {
fun stopRunner(stopService: Boolean, msg: String? = null) { fun stopRunner(stopService: Boolean, msg: String? = null) {
// channge the state // channge the state
changeState(STOPPING) val data = data
data.changeState(STOPPING)
app.track(tag, "stop") app.track(tag, "stop")
killProcesses() killProcesses()
// clean up recevier // clean up recevier
val data = data
this as Service this as Service
if (data.closeReceiverRegistered) { if (data.closeReceiverRegistered) {
unregisterReceiver(data.closeReceiver) unregisterReceiver(data.closeReceiver)
@@ -295,7 +289,7 @@ object BaseService {
data.trafficMonitorThread = null data.trafficMonitorThread = null
// change the state // change the state
changeState(STOPPED, msg) data.changeState(STOPPED, msg)
// stop the service if nothing has bound to it // stop the service if nothing has bound to it
if (stopService) stopSelf() if (stopService) stopSelf()
@@ -309,8 +303,9 @@ object BaseService {
val data = data val data = data
if (data.state != STOPPED) return Service.START_NOT_STICKY if (data.state != STOPPED) return Service.START_NOT_STICKY
val profile = app.currentProfile val profile = app.currentProfile
this as Context
if (profile == null) { if (profile == null) {
stopRunner(true) stopRunner(true, getString(R.string.profile_empty))
return Service.START_NOT_STICKY return Service.START_NOT_STICKY
} }
data.profile = profile data.profile = profile
@@ -327,17 +322,16 @@ object BaseService {
filter.addAction(Action.RELOAD) filter.addAction(Action.RELOAD)
filter.addAction(Intent.ACTION_SHUTDOWN) filter.addAction(Intent.ACTION_SHUTDOWN)
filter.addAction(Action.CLOSE) filter.addAction(Action.CLOSE)
(this as Context).registerReceiver(data.closeReceiver, filter) registerReceiver(data.closeReceiver, filter)
data.closeReceiverRegistered = true data.closeReceiverRegistered = true
} }
data.notification = createNotification() data.notification = createNotification()
app.track(tag, "start") app.track(tag, "start")
changeState(CONNECTING) data.changeState(CONNECTING)
thread { thread {
this as Context
try { try {
if (profile.host == "198.199.101.152") { if (profile.host == "198.199.101.152") {
val client = OkHttpClient.Builder() val client = OkHttpClient.Builder()
@@ -385,7 +379,7 @@ object BaseService {
if (profile.route !in arrayOf(Acl.ALL, Acl.CUSTOM_RULES)) AclSyncJob.schedule(profile.route) if (profile.route !in arrayOf(Acl.ALL, Acl.CUSTOM_RULES)) AclSyncJob.schedule(profile.route)
changeState(CONNECTED) data.changeState(CONNECTED)
} catch (_: UnknownHostException) { } catch (_: UnknownHostException) {
stopRunner(true, getString(R.string.invalid_server)) stopRunner(true, getString(R.string.invalid_server))
} catch (_: VpnService.NullConnectionException) { } catch (_: VpnService.NullConnectionException) {
@@ -397,19 +391,6 @@ object BaseService {
} }
return Service.START_NOT_STICKY 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>() private val instances = WeakHashMap<Interface, Data>()
@@ -44,6 +44,7 @@ object LocalDnsService {
override fun startNativeProcesses() { override fun startNativeProcesses() {
super.startNativeProcesses() super.startNativeProcesses()
val data = data
val profile = data.profile!! val profile = data.profile!!
fun makeDns(name: String, address: String, edns: Boolean = true): JSONObject { fun makeDns(name: String, address: String, edns: Boolean = true): JSONObject {
@@ -89,7 +90,7 @@ object LocalDnsService {
.put("PrimaryDNS", localDns) .put("PrimaryDNS", localDns)
.put("AlternativeDNS", remoteDns) .put("AlternativeDNS", remoteDns)
.put("IPNetworkFile", "china_ip_list.txt") .put("IPNetworkFile", "china_ip_list.txt")
.put("DomainFile", getListFile()!!.absolutePath) .put("DomainFile", data.aclFile!!.absolutePath)
Acl.CHINALIST -> config Acl.CHINALIST -> config
.put("PrimaryDNS", localDns) .put("PrimaryDNS", localDns)
.put("AlternativeDNS", remoteDns) .put("AlternativeDNS", remoteDns)
@@ -53,7 +53,7 @@ class KeyValuePair() {
get() = if (valueType == TYPE_LONG) ByteBuffer.wrap(value).long else null get() = if (valueType == TYPE_LONG) ByteBuffer.wrap(value).long else null
val string: String? val string: String?
get() = if (valueType == TYPE_STRING) String(value) else null get() = if (valueType == TYPE_STRING) String(value) else null
val stringSet: MutableSet<String>? val stringSet: Set<String>?
get() = if (valueType == TYPE_STRING_SET) { get() = if (valueType == TYPE_STRING_SET) {
val buffer = ByteBuffer.wrap(value) val buffer = ByteBuffer.wrap(value)
val result = HashSet<String>() val result = HashSet<String>()
@@ -95,7 +95,7 @@ class KeyValuePair() {
this.value = value.toByteArray() this.value = value.toByteArray()
return this return this
} }
fun put(value: MutableSet<String>): KeyValuePair { fun put(value: Set<String>): KeyValuePair {
valueType = TYPE_STRING_SET valueType = TYPE_STRING_SET
val stream = ByteArrayOutputStream() val stream = ByteArrayOutputStream()
for (v in value) { for (v in value) {
@@ -27,7 +27,7 @@ import com.github.shadowsocks.R
import com.github.shadowsocks.database.ProfileManager import com.github.shadowsocks.database.ProfileManager
import com.twofortyfouram.locale.api.Intent as ApiIntent import com.twofortyfouram.locale.api.Intent as ApiIntent
class Settings(bundle: Bundle) { class Settings(bundle: Bundle?) {
companion object { companion object {
private val KEY_SWITCH_ON = "switch_on" private val KEY_SWITCH_ON = "switch_on"
private val KEY_PROFILE_ID = "profile_id" 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)) fun fromIntent(intent: Intent) = Settings(intent.getBundleExtra(ApiIntent.EXTRA_BUNDLE))
} }
var switchOn: Boolean = bundle.getBoolean(KEY_SWITCH_ON, true) var switchOn: Boolean = bundle?.getBoolean(KEY_SWITCH_ON, true) ?: true
var profileId: Int = bundle.getInt(KEY_PROFILE_ID, -1) var profileId: Int = bundle?.getInt(KEY_PROFILE_ID, -1) ?: -1
fun toIntent(context: Context): Intent { fun toIntent(context: Context): Intent {
val bundle = Bundle() val bundle = Bundle()
@@ -7,7 +7,6 @@ import android.os.Build
import android.support.v4.app.Fragment import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager import android.support.v4.app.FragmentManager
import android.support.v7.util.SortedList import android.support.v7.util.SortedList
import android.util.TypedValue
import com.github.shadowsocks.App.Companion.app import com.github.shadowsocks.App.Companion.app
import java.lang.reflect.InvocationTargetException import java.lang.reflect.InvocationTargetException
import java.net.InetAddress 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 isNumericMethod by lazy { InetAddress::class.java.getMethod("isNumeric", String::class.java) }
private val parseNumericAddressMethod by lazy { 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 { private val fieldChildFragmentManager by lazy {
val field = Fragment::class.java.getDeclaredField("mChildFragmentManager") 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.isNumericAddress(): Boolean = isNumericMethod.invoke(null, this) as Boolean
fun String.parseNumericAddress(): InetAddress = try { fun String.parseNumericAddress(): InetAddress =
parseNumericAddressMethod.invoke(null, this) as InetAddress if (parseNumericAddressMethod == null) InetAddress.getByName(this) else try {
} catch (exc: InvocationTargetException) { parseNumericAddressMethod!!.invoke(null, this) as InetAddress
throw exc.cause ?: exc } catch (exc: InvocationTargetException) {
} throw exc.cause ?: exc
}
fun parsePort(str: String?, default: Int, min: Int = 1025): Int { fun parsePort(str: String?, default: Int, min: Int = 1025): Int {
val x = str?.toIntOrNull() ?: default val x = str?.toIntOrNull() ?: default
Binary file not shown.

Before

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 806 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 436 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

@@ -6,6 +6,7 @@
<Spinner android:id="@+id/template_selector" <Spinner android:id="@+id/template_selector"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:labelFor="@+id/content"
android:entries="@array/acl_rule_templates"/> android:entries="@array/acl_rule_templates"/>
<EditText android:id="@+id/content" <EditText android:id="@+id/content"
android:layout_width="match_parent" android:layout_width="match_parent"
+1 -2
View File
@@ -10,8 +10,7 @@
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:src="@drawable/background_header"/> android:src="@drawable/background_header"/>
<TextView xmlns:android="http://schemas.android.com/apk/res/android" <TextView android:id="@+id/drawer_title"
android:id="@+id/drawer_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_centerVertical="true" android:layout_centerVertical="true"
+2 -1
View File
@@ -26,7 +26,8 @@
android:ellipsize="marquee" android:ellipsize="marquee"
android:paddingEnd="72dip" android:paddingEnd="72dip"
android:layout_width="wrap_content" 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" <com.github.shadowsocks.widget.BoundedGridLayout android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:bounded_width="288dp" app:bounded_width="288dp"
+32 -27
View File
@@ -1,34 +1,39 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:id="@+id/selection" <item
android:title="@string/action_selection" android:id="@+id/selection"
android:icon="@drawable/abc_ic_menu_selectall_mtrl_alpha" android:alphabeticShortcut="s"
android:alphabeticShortcut="s" android:icon="?attr/actionModeSelectAllDrawable"
android:numericShortcut="0" android:numericShortcut="0"
android:visible="false" android:title="@string/action_selection"
app:showAsAction="always"> android:visible="false"
app:showAsAction="always">
<menu> <menu>
<item android:id="@+id/action_select_all" <item
android:title="@android:string/selectAll" android:id="@+id/action_select_all"
android:alphabeticShortcut="a" android:alphabeticShortcut="a"
android:numericShortcut="1" android:numericShortcut="1"
app:showAsAction="always"/> android:title="@android:string/selectAll"
<item android:id="@+id/action_cut" app:showAsAction="always"/>
android:title="@android:string/cut" <item
android:alphabeticShortcut="t" android:id="@+id/action_cut"
android:numericShortcut="2" android:alphabeticShortcut="t"
app:showAsAction="always"/> android:numericShortcut="2"
<item android:id="@+id/action_copy" android:title="@android:string/cut"
android:title="@android:string/copy" app:showAsAction="always"/>
android:alphabeticShortcut="c" <item
android:numericShortcut="3" android:id="@+id/action_copy"
app:showAsAction="always"/> android:alphabeticShortcut="c"
<item android:id="@+id/action_delete" android:numericShortcut="3"
android:title="@string/delete" android:title="@android:string/copy"
android:alphabeticShortcut="d" app:showAsAction="always"/>
android:numericShortcut="4" <item
app:showAsAction="always"/> android:id="@+id/action_delete"
android:alphabeticShortcut="d"
android:numericShortcut="4"
android:title="@string/delete"
app:showAsAction="always"/>
</menu> </menu>
</item> </item>
<item android:title="@string/action_add_rule" <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 package com.github.shadowsocks.utils
import com.github.shadowsocks.utils.Subnet
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import java.net.InetAddress import java.net.InetAddress
object SubnetTest { class SubnetTest {
@Test @Test
fun parsingAndEquals() { fun parsingAndEquals() {
Assert.assertEquals(Subnet(InetAddress.getByName("1.10.11.12"), 25), Subnet.fromString("1.10.11.12/25")) Assert.assertEquals(Subnet(InetAddress.getByName("1.10.11.12"), 25), Subnet.fromString("1.10.11.12/25"))
+6
View File
@@ -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>