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:
|
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
|
||||||
|
|||||||
@@ -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') &&
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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$
|
^(.*\.)?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$
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
|
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"
|
<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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"))
|
||||||
|
|||||||
@@ -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>
|
||||||