Unverified Commit 141a3076 authored by Mygod's avatar Mygod Committed by GitHub

Deprecate hosts (#2502)

parent 198434ea
...@@ -35,10 +35,8 @@ import com.github.shadowsocks.aidl.IShadowsocksServiceCallback ...@@ -35,10 +35,8 @@ import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import com.github.shadowsocks.aidl.TrafficStats import com.github.shadowsocks.aidl.TrafficStats
import com.github.shadowsocks.core.R import com.github.shadowsocks.core.R
import com.github.shadowsocks.net.DnsResolverCompat import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.net.HostsFile
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.Action import com.github.shadowsocks.utils.Action
import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.broadcastReceiver import com.github.shadowsocks.utils.broadcastReceiver
import com.github.shadowsocks.utils.readableMessage import com.github.shadowsocks.utils.readableMessage
import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.analytics.FirebaseAnalytics
...@@ -239,7 +237,7 @@ object BaseService { ...@@ -239,7 +237,7 @@ object BaseService {
val isVpnService get() = false val isVpnService get() = false
suspend fun startProcesses(hosts: HostsFile) { suspend fun startProcesses() {
val configRoot = (if (Build.VERSION.SDK_INT < 24 || app.getSystemService<UserManager>() val configRoot = (if (Build.VERSION.SDK_INT < 24 || app.getSystemService<UserManager>()
?.isUserUnlocked != false) app else Core.deviceStorage).noBackupFilesDir ?.isUserUnlocked != false) app else Core.deviceStorage).noBackupFilesDir
val udpFallback = data.udpFallback val udpFallback = data.udpFallback
...@@ -315,7 +313,6 @@ object BaseService { ...@@ -315,7 +313,6 @@ object BaseService {
listOfNotNull(data.proxy, data.udpFallback).forEach { it.trafficMonitor?.persistStats(it.profile.id) } listOfNotNull(data.proxy, data.udpFallback).forEach { it.trafficMonitor?.persistStats(it.profile.id) }
suspend fun preInit() { } suspend fun preInit() { }
suspend fun getActiveNetwork() = if (Build.VERSION.SDK_INT >= 23) Core.connectivity.activeNetwork else null
suspend fun resolver(host: String) = DnsResolverCompat.resolveOnActiveNetwork(host) suspend fun resolver(host: String) = DnsResolverCompat.resolveOnActiveNetwork(host)
suspend fun rawResolver(query: ByteArray) = DnsResolverCompat.resolveRawOnActiveNetwork(query) suspend fun rawResolver(query: ByteArray) = DnsResolverCompat.resolveRawOnActiveNetwork(query)
suspend fun openConnection(url: URL) = url.openConnection() suspend fun openConnection(url: URL) = url.openConnection()
...@@ -355,9 +352,8 @@ object BaseService { ...@@ -355,9 +352,8 @@ object BaseService {
try { try {
Executable.killAll() // clean up old processes Executable.killAll() // clean up old processes
preInit() preInit()
val hosts = HostsFile(DataStore.publicStore.getString(Key.hosts) ?: "") proxy.init(this@Interface)
proxy.init(this@Interface, hosts) data.udpFallback?.init(this@Interface)
data.udpFallback?.init(this@Interface, hosts)
if (profile.route == Acl.CUSTOM_RULES) try { if (profile.route == Acl.CUSTOM_RULES) try {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
Acl.customRules.flatten(10, this@Interface::openConnection).also { Acl.customRules.flatten(10, this@Interface::openConnection).also {
...@@ -372,7 +368,7 @@ object BaseService { ...@@ -372,7 +368,7 @@ object BaseService {
Timber.w(it) Timber.w(it)
stopRunner(false, it.readableMessage) stopRunner(false, it.readableMessage)
} }
startProcesses(hosts) startProcesses()
proxy.scheduleUpdate() proxy.scheduleUpdate()
data.udpFallback?.scheduleUpdate() data.udpFallback?.scheduleUpdate()
......
...@@ -26,8 +26,6 @@ import com.github.shadowsocks.Core ...@@ -26,8 +26,6 @@ import com.github.shadowsocks.Core
import com.github.shadowsocks.acl.Acl import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.acl.AclSyncer import com.github.shadowsocks.acl.AclSyncer
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.net.HostsFile
import com.github.shadowsocks.plugin.PluginConfiguration import com.github.shadowsocks.plugin.PluginConfiguration
import com.github.shadowsocks.plugin.PluginManager import com.github.shadowsocks.plugin.PluginManager
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
...@@ -52,7 +50,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro ...@@ -52,7 +50,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
val plugin by lazy { PluginManager.init(PluginConfiguration(profile.plugin ?: "")) } val plugin by lazy { PluginManager.init(PluginConfiguration(profile.plugin ?: "")) }
private var scheduleConfigUpdate = false private var scheduleConfigUpdate = false
suspend fun init(service: BaseService.Interface, hosts: HostsFile) { suspend fun init(service: BaseService.Interface) {
if (profile.isSponsored) { if (profile.isSponsored) {
scheduleConfigUpdate = true scheduleConfigUpdate = true
val mdg = MessageDigest.getInstance("SHA-1") val mdg = MessageDigest.getInstance("SHA-1")
...@@ -86,23 +84,10 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro ...@@ -86,23 +84,10 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
// it's hard to resolve DNS on a specific interface so we'll do it here // it's hard to resolve DNS on a specific interface so we'll do it here
if (profile.host.parseNumericAddress() == null) { if (profile.host.parseNumericAddress() == null) {
profile.host = hosts.resolve(profile.host).run { profile.host = try {
if (isEmpty()) try { service.resolver(profile.host).firstOrNull()
service.resolver(profile.host).firstOrNull() } catch (_: IOException) {
} catch (_: IOException) { null
null
} else {
val network = service.getActiveNetwork() ?: throw UnknownHostException()
val hasIpv4 = DnsResolverCompat.haveIpv4(network)
val hasIpv6 = DnsResolverCompat.haveIpv6(network)
firstOrNull {
when (it) {
is Inet4Address -> hasIpv4
is Inet6Address -> hasIpv6
else -> error(it)
}
}
}
}?.hostAddress ?: throw UnknownHostException() }?.hostAddress ?: throw UnknownHostException()
} }
} }
......
...@@ -23,7 +23,6 @@ package com.github.shadowsocks.bg ...@@ -23,7 +23,6 @@ package com.github.shadowsocks.bg
import android.app.Service import android.app.Service
import android.content.Intent import android.content.Intent
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.net.HostsFile
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import java.io.File import java.io.File
...@@ -57,9 +56,9 @@ redsocks { ...@@ -57,9 +56,9 @@ redsocks {
File(applicationInfo.nativeLibraryDir, Executable.REDSOCKS).absolutePath, "-c", "redsocks.conf")) File(applicationInfo.nativeLibraryDir, Executable.REDSOCKS).absolutePath, "-c", "redsocks.conf"))
} }
override suspend fun startProcesses(hosts: HostsFile) { override suspend fun startProcesses() {
startRedsocksDaemon() startRedsocksDaemon()
super.startProcesses(hosts) super.startProcesses()
} }
override fun onDestroy() { override fun onDestroy() {
......
...@@ -34,7 +34,10 @@ import com.github.shadowsocks.Core ...@@ -34,7 +34,10 @@ import com.github.shadowsocks.Core
import com.github.shadowsocks.VpnRequestActivity import com.github.shadowsocks.VpnRequestActivity
import com.github.shadowsocks.acl.Acl import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.core.R import com.github.shadowsocks.core.R
import com.github.shadowsocks.net.* import com.github.shadowsocks.net.ConcurrentLocalSocketListener
import com.github.shadowsocks.net.DefaultNetworkListener
import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.net.Subnet
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.closeQuietly import com.github.shadowsocks.utils.closeQuietly
...@@ -137,7 +140,6 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -137,7 +140,6 @@ class VpnService : BaseVpnService(), BaseService.Interface {
} }
override suspend fun preInit() = DefaultNetworkListener.start(this) { underlyingNetwork = it } override suspend fun preInit() = DefaultNetworkListener.start(this) { underlyingNetwork = it }
override suspend fun getActiveNetwork() = DefaultNetworkListener.get()
override suspend fun resolver(host: String) = DnsResolverCompat.resolve(DefaultNetworkListener.get(), host) override suspend fun resolver(host: String) = DnsResolverCompat.resolve(DefaultNetworkListener.get(), host)
override suspend fun rawResolver(query: ByteArray) = override suspend fun rawResolver(query: ByteArray) =
// no need to listen for network here as this is only used for forwarding local DNS queries. // no need to listen for network here as this is only used for forwarding local DNS queries.
...@@ -145,9 +147,9 @@ class VpnService : BaseVpnService(), BaseService.Interface { ...@@ -145,9 +147,9 @@ class VpnService : BaseVpnService(), BaseService.Interface {
DnsResolverCompat.resolveRaw(underlyingNetwork ?: throw IOException("no network"), query) DnsResolverCompat.resolveRaw(underlyingNetwork ?: throw IOException("no network"), query)
override suspend fun openConnection(url: URL) = DefaultNetworkListener.get().openConnection(url) override suspend fun openConnection(url: URL) = DefaultNetworkListener.get().openConnection(url)
override suspend fun startProcesses(hosts: HostsFile) { override suspend fun startProcesses() {
worker = ProtectWorker().apply { start() } worker = ProtectWorker().apply { start() }
super.startProcesses(hosts) super.startProcesses()
sendFd(startVpn()) sendFd(startVpn())
} }
......
...@@ -29,14 +29,10 @@ import android.os.CancellationSignal ...@@ -29,14 +29,10 @@ import android.os.CancellationSignal
import android.os.Looper import android.os.Looper
import android.system.ErrnoException import android.system.ErrnoException
import android.system.Os import android.system.Os
import android.system.OsConstants
import com.github.shadowsocks.Core import com.github.shadowsocks.Core
import com.github.shadowsocks.utils.closeQuietly
import com.github.shadowsocks.utils.int import com.github.shadowsocks.utils.int
import com.github.shadowsocks.utils.parseNumericAddress
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.xbill.DNS.* import org.xbill.DNS.*
import timber.log.Timber
import java.io.FileDescriptor import java.io.FileDescriptor
import java.io.IOException import java.io.IOException
import java.net.Inet4Address import java.net.Inet4Address
...@@ -58,42 +54,6 @@ sealed class DnsResolverCompat { ...@@ -58,42 +54,6 @@ sealed class DnsResolverCompat {
} }
} }
/**
* Based on: https://android.googlesource.com/platform/frameworks/base/+/9f97f97/core/java/android/net/util/DnsUtils.java#341
*/
private val address4 = "8.8.8.8".parseNumericAddress()!!
private val address6 = "2000::".parseNumericAddress()!!
suspend fun haveIpv4(network: Network) = checkConnectivity(network, OsConstants.AF_INET, address4)
suspend fun haveIpv6(network: Network) = checkConnectivity(network, OsConstants.AF_INET6, address6)
private suspend fun checkConnectivity(network: Network, domain: Int, addr: InetAddress) = try {
val socket = Os.socket(domain, OsConstants.SOCK_DGRAM, OsConstants.IPPROTO_UDP)
try {
instance.bindSocket(network, socket)
instance.connectUdp(socket, addr)
} finally {
socket.closeQuietly()
}
true
} catch (e: IOException) {
if ((e.cause as? ErrnoException)?.errno == OsConstants.EPERM) checkConnectivity(network, addr) else false
} catch (_: ErrnoException) {
false
} catch (e: ReflectiveOperationException) {
check(Build.VERSION.SDK_INT < 23)
Timber.w(e)
checkConnectivity(network, addr)
}
private fun checkConnectivity(network: Network, addr: InetAddress): Boolean {
return Core.connectivity.getLinkProperties(network)?.routes?.any {
try {
it.matches(addr)
} catch (e: RuntimeException) {
Timber.w(e)
false
}
} == true
}
override fun bindSocket(network: Network, socket: FileDescriptor) = instance.bindSocket(network, socket) override fun bindSocket(network: Network, socket: FileDescriptor) = instance.bindSocket(network, socket)
override suspend fun resolve(network: Network, host: String) = instance.resolve(network, host) override suspend fun resolve(network: Network, host: String) = instance.resolve(network, host)
override suspend fun resolveOnActiveNetwork(host: String) = instance.resolveOnActiveNetwork(host) override suspend fun resolveOnActiveNetwork(host: String) = instance.resolveOnActiveNetwork(host)
......
/*******************************************************************************
* *
* Copyright (C) 2019 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2019 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.net
import com.github.shadowsocks.utils.parseNumericAddress
import java.net.InetAddress
class HostsFile(input: String = "") {
private val map = mutableMapOf<String, MutableSet<InetAddress>>()
init {
for (line in input.lineSequence()) {
val entries = line.substringBefore('#').splitToSequence(' ', '\t').filter { it.isNotEmpty() }
val address = entries.firstOrNull()?.parseNumericAddress() ?: continue
for (hostname in entries.drop(1)) map.computeIfAbsent(hostname) { LinkedHashSet(1) }.add(address)
}
}
val configuredHostnames get() = map.size
fun resolve(hostname: String) = map[hostname]?.shuffled() ?: emptyList()
}
/*******************************************************************************
* *
* Copyright (C) 2019 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2019 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.preference
import androidx.preference.EditTextPreference
import androidx.preference.Preference
import com.github.shadowsocks.core.R
import com.github.shadowsocks.net.HostsFile
object HostsSummaryProvider : Preference.SummaryProvider<EditTextPreference> {
override fun provideSummary(preference: EditTextPreference?): CharSequence {
val count = HostsFile(preference!!.text ?: "").configuredHostnames
return preference.context.resources.getQuantityString(R.plurals.hosts_summary, count, count)
}
}
...@@ -64,7 +64,6 @@ object Key { ...@@ -64,7 +64,6 @@ object Key {
const val dirty = "profileDirty" const val dirty = "profileDirty"
const val hosts = "hosts"
const val assetUpdateTime = "assetUpdateTime" const val assetUpdateTime = "assetUpdateTime"
// TV specific values // TV specific values
......
...@@ -20,8 +20,6 @@ ...@@ -20,8 +20,6 @@
package com.github.shadowsocks package com.github.shadowsocks
import android.app.Activity
import android.content.Intent
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
...@@ -30,24 +28,14 @@ import androidx.preference.Preference ...@@ -30,24 +28,14 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
import com.github.shadowsocks.bg.BaseService import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.plugin.showAllowingStateLoss
import com.github.shadowsocks.preference.BrowsableEditTextPreferenceDialogFragment
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.preference.EditTextPreferenceModifiers import com.github.shadowsocks.preference.EditTextPreferenceModifiers
import com.github.shadowsocks.preference.HostsSummaryProvider
import com.github.shadowsocks.utils.DirectBoot import com.github.shadowsocks.utils.DirectBoot
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.readableMessage
import com.github.shadowsocks.utils.remove import com.github.shadowsocks.utils.remove
import com.github.shadowsocks.widget.MainListListener import com.github.shadowsocks.widget.MainListListener
class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() { class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() {
companion object {
private const val REQUEST_BROWSE = 1
}
private val hosts by lazy { findPreference<EditTextPreference>(Key.hosts)!! }
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.preferenceDataStore = DataStore.publicStore preferenceManager.preferenceDataStore = DataStore.publicStore
DataStore.initGlobal() DataStore.initGlobal()
...@@ -63,8 +51,6 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() { ...@@ -63,8 +51,6 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() {
true true
} else canToggleLocked.remove() } else canToggleLocked.remove()
hosts.setOnBindEditTextListener(EditTextPreferenceModifiers.Monospace)
hosts.summaryProvider = HostsSummaryProvider
val serviceMode = findPreference<Preference>(Key.serviceMode)!! val serviceMode = findPreference<Preference>(Key.serviceMode)!!
val portProxy = findPreference<EditTextPreference>(Key.portProxy)!! val portProxy = findPreference<EditTextPreference>(Key.portProxy)!!
portProxy.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) portProxy.setOnBindEditTextListener(EditTextPreferenceModifiers.Port)
...@@ -78,7 +64,6 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() { ...@@ -78,7 +64,6 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() {
} }
val listener: (BaseService.State) -> Unit = { val listener: (BaseService.State) -> Unit = {
val stopped = it == BaseService.State.Stopped val stopped = it == BaseService.State.Stopped
hosts.isEnabled = stopped
serviceMode.isEnabled = stopped serviceMode.isEnabled = stopped
portProxy.isEnabled = stopped portProxy.isEnabled = stopped
portLocalDns.isEnabled = stopped portLocalDns.isEnabled = stopped
...@@ -96,29 +81,6 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() { ...@@ -96,29 +81,6 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() {
listView.setOnApplyWindowInsetsListener(MainListListener) listView.setOnApplyWindowInsetsListener(MainListListener)
} }
override fun onDisplayPreferenceDialog(preference: Preference?) {
if (preference == hosts) BrowsableEditTextPreferenceDialogFragment().apply {
setKey(hosts.key)
setTargetFragment(this@GlobalSettingsPreferenceFragment, REQUEST_BROWSE)
}.showAllowingStateLoss(parentFragmentManager, hosts.key) else super.onDisplayPreferenceDialog(preference)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_BROWSE -> {
if (resultCode != Activity.RESULT_OK) return
val activity = activity as MainActivity
try {
// we read and persist all its content here to avoid content URL permission issues
hosts.text = activity.contentResolver.openInputStream(data!!.data!!)!!.bufferedReader().readText()
} catch (e: Exception) {
activity.snackbar(e.readableMessage).show()
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
override fun onDestroy() { override fun onDestroy() {
MainActivity.stateListener = null MainActivity.stateListener = null
super.onDestroy() super.onDestroy()
......
/*******************************************************************************
* *
* Copyright (C) 2019 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2019 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.preference
import android.content.ActivityNotFoundException
import android.content.Intent
import androidx.appcompat.app.AlertDialog
import androidx.core.os.bundleOf
import androidx.preference.EditTextPreferenceDialogFragmentCompat
import com.github.shadowsocks.MainActivity
import com.github.shadowsocks.R
class BrowsableEditTextPreferenceDialogFragment : EditTextPreferenceDialogFragmentCompat() {
fun setKey(key: String) {
arguments = bundleOf(Pair(ARG_KEY, key))
}
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
super.onPrepareDialogBuilder(builder)
builder.setNeutralButton(R.string.browse) { _, _ ->
val activity = activity as MainActivity
try {
targetFragment!!.startActivityForResult(Intent(Intent.ACTION_GET_CONTENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
}, targetRequestCode)
return@setNeutralButton
} catch (_: ActivityNotFoundException) { } catch (_: SecurityException) { }
activity.snackbar(activity.getString(R.string.file_manager_missing)).show()
}
}
}
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M21,5c-1.11,-0.35 -2.33,-0.5 -3.5,-0.5c-1.95,0 -4.05,0.4 -5.5,1.5c-1.45,-1.1 -3.55,-1.5 -5.5,-1.5S2.45,4.9 1,6v14.65c0,0.25 0.25,0.5 0.5,0.5c0.1,0 0.15,-0.05 0.25,-0.05C3.1,20.45 5.05,20 6.5,20c1.95,0 4.05,0.4 5.5,1.5c1.35,-0.85 3.8,-1.5 5.5,-1.5c1.65,0 3.35,0.3 4.75,1.05c0.1,0.05 0.15,0.05 0.25,0.05c0.25,0 0.5,-0.25 0.5,-0.5V6C22.4,5.55 21.75,5.25 21,5zM21,18.5c-1.1,-0.35 -2.3,-0.5 -3.5,-0.5c-1.7,0 -4.15,0.65 -5.5,1.5V8c1.35,-0.85 3.8,-1.5 5.5,-1.5c1.2,0 2.4,0.15 3.5,0.5V18.5z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M17.5,10.5c0.88,0 1.73,0.09 2.5,0.26V9.24C19.21,9.09 18.36,9 17.5,9c-1.7,0 -3.24,0.29 -4.5,0.83v1.66C14.13,10.85 15.7,10.5 17.5,10.5z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M13,12.49v1.66c1.13,-0.64 2.7,-0.99 4.5,-0.99c0.88,0 1.73,0.09 2.5,0.26V11.9c-0.79,-0.15 -1.64,-0.24 -2.5,-0.24C15.8,11.66 14.26,11.96 13,12.49z"/>
<path
android:fillColor="@android:color/white"
android:pathData="M17.5,14.33c-1.7,0 -3.24,0.29 -4.5,0.83v1.66c1.13,-0.64 2.7,-0.99 4.5,-0.99c0.88,0 1.73,0.09 2.5,0.26v-1.52C19.21,14.41 18.36,14.33 17.5,14.33z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto" <PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto"
app:initialExpandedChildrenCount="3"> app:initialExpandedChildrenCount="2">
<SwitchPreference <SwitchPreference
app:key="isAutoConnect" app:key="isAutoConnect"
app:icon="@drawable/ic_communication_phonelink_ring" app:icon="@drawable/ic_communication_phonelink_ring"
...@@ -11,10 +11,6 @@ ...@@ -11,10 +11,6 @@
app:icon="@drawable/ic_action_lock" app:icon="@drawable/ic_action_lock"
app:summary="@string/direct_boot_aware_summary" app:summary="@string/direct_boot_aware_summary"
app:title="@string/direct_boot_aware"/> app:title="@string/direct_boot_aware"/>
<EditTextPreference
app:key="hosts"
app:icon="@drawable/ic_maps_menu_book"
app:title="hosts"/>
<com.takisoft.preferencex.SimpleMenuPreference <com.takisoft.preferencex.SimpleMenuPreference
app:key="serviceMode" app:key="serviceMode"
......
...@@ -44,7 +44,6 @@ import com.github.shadowsocks.database.ProfileManager ...@@ -44,7 +44,6 @@ import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.net.HttpsTest import com.github.shadowsocks.net.HttpsTest
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.preference.EditTextPreferenceModifiers import com.github.shadowsocks.preference.EditTextPreferenceModifiers
import com.github.shadowsocks.preference.HostsSummaryProvider
import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener import com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.datas import com.github.shadowsocks.utils.datas
...@@ -58,13 +57,11 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo ...@@ -58,13 +57,11 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
private const val REQUEST_CONNECT = 1 private const val REQUEST_CONNECT = 1
private const val REQUEST_REPLACE_PROFILES = 2 private const val REQUEST_REPLACE_PROFILES = 2
private const val REQUEST_EXPORT_PROFILES = 3 private const val REQUEST_EXPORT_PROFILES = 3
private const val REQUEST_HOSTS = 4
} }
private lateinit var fab: ListPreference private lateinit var fab: ListPreference
private lateinit var stats: Preference private lateinit var stats: Preference
private lateinit var controlImport: Preference private lateinit var controlImport: Preference
private lateinit var hosts: EditTextPreference
private lateinit var serviceMode: Preference private lateinit var serviceMode: Preference
private lateinit var shareOverLan: Preference private lateinit var shareOverLan: Preference
private lateinit var portProxy: EditTextPreference private lateinit var portProxy: EditTextPreference
...@@ -111,7 +108,6 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo ...@@ -111,7 +108,6 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
this.state = state this.state = state
val stopped = state == BaseService.State.Stopped val stopped = state == BaseService.State.Stopped
controlImport.isEnabled = stopped controlImport.isEnabled = stopped
hosts.isEnabled = stopped
serviceMode.isEnabled = stopped serviceMode.isEnabled = stopped
shareOverLan.isEnabled = stopped shareOverLan.isEnabled = stopped
portProxy.isEnabled = stopped portProxy.isEnabled = stopped
...@@ -148,8 +144,6 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo ...@@ -148,8 +144,6 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
true true
} }
hosts = findPreference(Key.hosts)!!
hosts.summaryProvider = HostsSummaryProvider
serviceMode = findPreference(Key.serviceMode)!! serviceMode = findPreference(Key.serviceMode)!!
shareOverLan = findPreference(Key.shareOverLan)!! shareOverLan = findPreference(Key.shareOverLan)!!
portProxy = findPreference(Key.portProxy)!! portProxy = findPreference(Key.portProxy)!!
...@@ -245,11 +239,6 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo ...@@ -245,11 +239,6 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
else -> super.onPreferenceTreeClick(preference) else -> super.onPreferenceTreeClick(preference)
} }
override fun onDisplayPreferenceDialog(preference: Preference?) {
if (preference != hosts || startFilesForResult(Intent(Intent.ACTION_GET_CONTENT).setType("*/*"), REQUEST_HOSTS))
super.onDisplayPreferenceDialog(preference)
}
private fun startFilesForResult(intent: Intent, requestCode: Int): Boolean { private fun startFilesForResult(intent: Intent, requestCode: Int): Boolean {
try { try {
startActivityForResult(intent.addCategory(Intent.CATEGORY_OPENABLE), requestCode) startActivityForResult(intent.addCategory(Intent.CATEGORY_OPENABLE), requestCode)
...@@ -291,16 +280,6 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo ...@@ -291,16 +280,6 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
Toast.makeText(context, e.readableMessage, Toast.LENGTH_SHORT).show() Toast.makeText(context, e.readableMessage, Toast.LENGTH_SHORT).show()
} }
} }
REQUEST_HOSTS -> {
if (resultCode != Activity.RESULT_OK) return
val context = requireContext()
try {
// we read and persist all its content here to avoid content URL permission issues
hosts.text = context.contentResolver.openInputStream(data!!.data!!)!!.bufferedReader().readText()
} catch (e: Exception) {
Toast.makeText(context, e.readableMessage, Toast.LENGTH_SHORT).show()
}
}
else -> super.onActivityResult(requestCode, resultCode, data) else -> super.onActivityResult(requestCode, resultCode, data)
} }
} }
......
...@@ -20,14 +20,11 @@ ...@@ -20,14 +20,11 @@
<PreferenceCategory <PreferenceCategory
app:key="settings" app:key="settings"
app:title="@string/settings" app:title="@string/settings"
app:initialExpandedChildrenCount="2"> app:initialExpandedChildrenCount="1">
<SwitchPreference <SwitchPreference
app:key="isAutoConnect" app:key="isAutoConnect"
app:summary="@string/auto_connect_summary" app:summary="@string/auto_connect_summary"
app:title="@string/auto_connect"/> app:title="@string/auto_connect"/>
<EditTextPreference
app:key="hosts"
app:title="hosts"/>
<ListPreference <ListPreference
app:key="serviceMode" app:key="serviceMode"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment