Skip to content

Commit 25393c8

Browse files
authored
Merge pull request #2162 from shadowsocks/preference-1.1
Migrate to androidx.preference 1.1.0
2 parents 723b2b4 + ea98b94 commit 25393c8

30 files changed

+585
-303
lines changed

build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ buildscript {
99
sdkVersion = 28
1010
compileSdkVersion = 28
1111
buildToolsVersion = '28.0.3'
12-
preferencexVersion = '1.0.0'
1312
junitVersion = '4.12'
1413
androidTestVersion = '1.1.1'
1514
androidEspressoVersion = '3.1.1'

core/build.gradle

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,12 @@ dependencies {
4949
api project(':plugin')
5050
api "androidx.lifecycle:lifecycle-extensions:$lifecycleVersion"
5151
api "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion"
52-
api 'androidx.preference:preference:1.0.0'
52+
api 'androidx.preference:preference:1.1.0-alpha05'
5353
api "androidx.room:room-runtime:$roomVersion"
5454
api 'androidx.work:work-runtime-ktx:2.0.1'
5555
api 'com.crashlytics.sdk.android:crashlytics:2.10.0'
5656
api 'com.google.firebase:firebase-config:17.0.0'
5757
api 'com.google.firebase:firebase-core:16.0.9'
58-
api "com.takisoft.preferencex:preferencex:$preferencexVersion"
5958
api 'dnsjava:dnsjava:2.1.8'
6059
api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1'
6160
api 'org.connectbot.jsocks:jsocks:1.0.0'

core/src/main/java/com/github/shadowsocks/VpnRequestActivity.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ class VpnRequestActivity : AppCompatActivity() {
6363
}
6464

6565
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
66+
if (requestCode != REQUEST_CONNECT) return super.onActivityResult(requestCode, resultCode, data)
6667
if (resultCode == RESULT_OK) Core.startService() else {
6768
Toast.makeText(this, R.string.vpn_permission_denied, Toast.LENGTH_LONG).show()
6869
Crashlytics.log(Log.ERROR, TAG, "Failed to start VpnService from onActivityResult: $data")

core/src/main/java/com/github/shadowsocks/bg/LocalDnsService.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ package com.github.shadowsocks.bg
2323
import com.github.shadowsocks.Core.app
2424
import com.github.shadowsocks.acl.Acl
2525
import com.github.shadowsocks.core.R
26+
import com.github.shadowsocks.net.HostsFile
2627
import com.github.shadowsocks.net.LocalDnsServer
2728
import com.github.shadowsocks.net.Socks5Endpoint
2829
import com.github.shadowsocks.net.Subnet
2930
import com.github.shadowsocks.preference.DataStore
31+
import com.github.shadowsocks.utils.Key
3032
import kotlinx.coroutines.CoroutineScope
3133
import java.net.InetSocketAddress
3234
import java.net.URI
@@ -49,7 +51,8 @@ object LocalDnsService {
4951
val dns = URI("dns://${profile.remoteDns}")
5052
LocalDnsServer(this::resolver,
5153
Socks5Endpoint(dns.host, if (dns.port < 0) 53 else dns.port),
52-
DataStore.proxyAddress).apply {
54+
DataStore.proxyAddress,
55+
HostsFile(DataStore.publicStore.getString(Key.hosts) ?: "")).apply {
5356
tcp = !profile.udpdns
5457
when (profile.route) {
5558
Acl.BYPASS_CHN, Acl.BYPASS_LAN_CHN, Acl.GFWLIST, Acl.CUSTOM_RULES -> {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*******************************************************************************
2+
* *
3+
* Copyright (C) 2019 by Max Lv <[email protected]> *
4+
* Copyright (C) 2019 by Mygod Studio <[email protected]> *
5+
* *
6+
* This program is free software: you can redistribute it and/or modify *
7+
* it under the terms of the GNU General Public License as published by *
8+
* the Free Software Foundation, either version 3 of the License, or *
9+
* (at your option) any later version. *
10+
* *
11+
* This program is distributed in the hope that it will be useful, *
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14+
* GNU General Public License for more details. *
15+
* *
16+
* You should have received a copy of the GNU General Public License *
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
18+
* *
19+
*******************************************************************************/
20+
21+
package com.github.shadowsocks.net
22+
23+
import com.github.shadowsocks.utils.computeIfAbsentCompat
24+
import com.github.shadowsocks.utils.parseNumericAddress
25+
import java.net.InetAddress
26+
27+
class HostsFile(input: String = "") {
28+
private val map = mutableMapOf<String, MutableSet<InetAddress>>()
29+
init {
30+
for (line in input.lineSequence()) {
31+
val entries = line.substringBefore('#').splitToSequence(' ', '\t').filter { it.isNotEmpty() }
32+
val address = entries.firstOrNull()?.parseNumericAddress() ?: continue
33+
for (hostname in entries.drop(1)) map.computeIfAbsentCompat(hostname) { LinkedHashSet(1) }.add(address)
34+
}
35+
}
36+
37+
val configuredHostnames get() = map.size
38+
fun resolve(hostname: String) = map[hostname]?.shuffled() ?: emptyList()
39+
}

core/src/main/java/com/github/shadowsocks/net/LocalDnsServer.kt

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ import java.nio.channels.SocketChannel
4343
* https://github.com/shadowsocks/overture/tree/874f22613c334a3b78e40155a55479b7b69fee04
4444
*/
4545
class LocalDnsServer(private val localResolver: suspend (String) -> Array<InetAddress>,
46-
private val remoteDns: Socks5Endpoint, private val proxy: SocketAddress) : CoroutineScope {
46+
private val remoteDns: Socks5Endpoint,
47+
private val proxy: SocketAddress,
48+
private val hosts: HostsFile) : CoroutineScope {
4749
/**
4850
* Forward all requests to remote and ignore localResolver.
4951
*/
@@ -70,7 +72,18 @@ class LocalDnsServer(private val localResolver: suspend (String) -> Array<InetAd
7072
if (request.header.getFlag(Flags.RD.toInt())) header.setFlag(Flags.RD.toInt())
7173
request.question?.also { addRecord(it, Section.QUESTION) }
7274
}
75+
76+
private fun cookDnsResponse(request: Message, results: Iterable<InetAddress>) =
77+
ByteBuffer.wrap(prepareDnsResponse(request).apply {
78+
header.setFlag(Flags.RA.toInt()) // recursion available
79+
for (address in results) addRecord(when (address) {
80+
is Inet4Address -> ARecord(question.name, DClass.IN, TTL, address)
81+
is Inet6Address -> AAAARecord(question.name, DClass.IN, TTL, address)
82+
else -> throw IllegalStateException("Unsupported address $address")
83+
}, Section.ANSWER)
84+
}.toWire())
7385
}
86+
7487
private val monitor = ChannelMonitor()
7588

7689
override val coroutineContext = SupervisorJob() + CoroutineExceptionHandler { _, t -> printLog(t) }
@@ -101,10 +114,16 @@ class LocalDnsServer(private val localResolver: suspend (String) -> Array<InetAd
101114
return supervisorScope {
102115
val remote = async { withTimeout(TIMEOUT) { forward(packet) } }
103116
try {
104-
if (forwardOnly || request.header.opcode != Opcode.QUERY) return@supervisorScope remote.await()
117+
if (request.header.opcode != Opcode.QUERY) return@supervisorScope remote.await()
105118
val question = request.question
106119
if (question?.type != Type.A) return@supervisorScope remote.await()
107120
val host = question.name.toString(true)
121+
val hostsResults = hosts.resolve(host)
122+
if (hostsResults.isNotEmpty()) {
123+
remote.cancel()
124+
return@supervisorScope cookDnsResponse(request, hostsResults)
125+
}
126+
if (forwardOnly) return@supervisorScope remote.await()
108127
if (remoteDomainMatcher?.containsMatchIn(host) == true) return@supervisorScope remote.await()
109128
val localResults = try {
110129
withTimeout(TIMEOUT) { GlobalScope.async(Dispatchers.IO) { localResolver(host) }.await() }
@@ -117,14 +136,7 @@ class LocalDnsServer(private val localResolver: suspend (String) -> Array<InetAd
117136
if (localResults.isEmpty()) return@supervisorScope remote.await()
118137
if (localIpMatcher.isEmpty() || localIpMatcher.any { subnet -> localResults.any(subnet::matches) }) {
119138
remote.cancel()
120-
ByteBuffer.wrap(prepareDnsResponse(request).apply {
121-
header.setFlag(Flags.RA.toInt()) // recursion available
122-
for (address in localResults) addRecord(when (address) {
123-
is Inet4Address -> ARecord(question.name, DClass.IN, TTL, address)
124-
is Inet6Address -> AAAARecord(question.name, DClass.IN, TTL, address)
125-
else -> throw IllegalStateException("Unsupported address $address")
126-
}, Section.ANSWER)
127-
}.toWire())
139+
cookDnsResponse(request, localResults.asIterable())
128140
} else remote.await()
129141
} catch (e: Exception) {
130142
remote.cancel()

core/src/main/java/com/github/shadowsocks/preference/DataStore.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ object DataStore : OnPreferenceDataStoreChangeListener {
4242
publicStore.registerChangeListener(this)
4343
}
4444

45-
override fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String?) {
45+
override fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String) {
4646
when (key) {
4747
Key.id -> if (directBootAware) DirectBoot.update()
4848
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*******************************************************************************
2+
* *
3+
* Copyright (C) 2019 by Max Lv <[email protected]> *
4+
* Copyright (C) 2019 by Mygod Studio <[email protected]> *
5+
* *
6+
* This program is free software: you can redistribute it and/or modify *
7+
* it under the terms of the GNU General Public License as published by *
8+
* the Free Software Foundation, either version 3 of the License, or *
9+
* (at your option) any later version. *
10+
* *
11+
* This program is distributed in the hope that it will be useful, *
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14+
* GNU General Public License for more details. *
15+
* *
16+
* You should have received a copy of the GNU General Public License *
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
18+
* *
19+
*******************************************************************************/
20+
21+
package com.github.shadowsocks.preference
22+
23+
import android.graphics.Typeface
24+
import android.text.InputFilter
25+
import android.view.inputmethod.EditorInfo
26+
import android.widget.EditText
27+
import androidx.preference.EditTextPreference
28+
29+
object EditTextPreferenceModifiers {
30+
object Monospace : EditTextPreference.OnBindEditTextListener {
31+
override fun onBindEditText(editText: EditText) {
32+
editText.typeface = Typeface.MONOSPACE
33+
}
34+
}
35+
36+
object Port : EditTextPreference.OnBindEditTextListener {
37+
private val portLengthFilter = arrayOf(InputFilter.LengthFilter(5))
38+
39+
override fun onBindEditText(editText: EditText) {
40+
editText.inputType = EditorInfo.TYPE_CLASS_NUMBER
41+
editText.filters = portLengthFilter
42+
editText.setSingleLine()
43+
}
44+
}
45+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*******************************************************************************
2+
* *
3+
* Copyright (C) 2019 by Max Lv <[email protected]> *
4+
* Copyright (C) 2019 by Mygod Studio <[email protected]> *
5+
* *
6+
* This program is free software: you can redistribute it and/or modify *
7+
* it under the terms of the GNU General Public License as published by *
8+
* the Free Software Foundation, either version 3 of the License, or *
9+
* (at your option) any later version. *
10+
* *
11+
* This program is distributed in the hope that it will be useful, *
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14+
* GNU General Public License for more details. *
15+
* *
16+
* You should have received a copy of the GNU General Public License *
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
18+
* *
19+
*******************************************************************************/
20+
21+
package com.github.shadowsocks.preference
22+
23+
import androidx.preference.EditTextPreference
24+
import androidx.preference.Preference
25+
import com.github.shadowsocks.core.R
26+
import com.github.shadowsocks.net.HostsFile
27+
28+
object HostsSummaryProvider : Preference.SummaryProvider<EditTextPreference> {
29+
override fun provideSummary(preference: EditTextPreference?): CharSequence {
30+
val count = HostsFile(preference!!.text ?: "").configuredHostnames
31+
return preference.context.resources.getQuantityString(R.plurals.hosts_summary, count, count)
32+
}
33+
}

core/src/main/java/com/github/shadowsocks/preference/OnPreferenceDataStoreChangeListener.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ package com.github.shadowsocks.preference
2323
import androidx.preference.PreferenceDataStore
2424

2525
interface OnPreferenceDataStoreChangeListener {
26-
fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String?)
26+
fun onPreferenceDataStoreChanged(store: PreferenceDataStore, key: String)
2727
}

0 commit comments

Comments
 (0)