diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt index b61def972..df865d804 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt @@ -188,6 +188,7 @@ object DataStore : OnPreferenceDataStoreChangeListener { var requireTransproxy by configurationStore.boolean(Key.REQUIRE_TRANSPROXY) var transproxyMode by configurationStore.stringToInt(Key.TRANSPROXY_MODE) var connectionTestURL by configurationStore.string(Key.CONNECTION_TEST_URL) { CONNECTION_TEST_URL } + var connectionTestConcurrent by configurationStore.int("connectionTestConcurrent") { 5 } var alwaysShowAddress by configurationStore.boolean(Key.ALWAYS_SHOW_ADDRESS) var appTrafficStatistics by configurationStore.boolean(Key.APP_TRAFFIC_STATISTICS) diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt index 4905d1911..49ed29e5f 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt @@ -786,6 +786,7 @@ class ConfigurationFragment @JvmOverloads constructor( if (DataStore.serviceState.started) SagerNet.stopService() } + @OptIn(DelicateCoroutinesApi::class) @Suppress("EXPERIMENTAL_API_USAGE") fun pingTest(icmpPing: Boolean) { stopService() @@ -816,8 +817,11 @@ class ConfigurationFragment @JvmOverloads constructor( } test.proxyN = profilesUnfiltered.size val profiles = ConcurrentLinkedQueue(profilesUnfiltered) - val testPool = newFixedThreadPoolContext(5, "Connection test pool") - repeat(5) { + val testPool = newFixedThreadPoolContext( + DataStore.connectionTestConcurrent, + "Connection test pool" + ) + repeat(DataStore.connectionTestConcurrent) { testJobs.add(launch(testPool) { while (isActive) { val profile = profiles.poll() ?: break @@ -986,7 +990,7 @@ class ConfigurationFragment @JvmOverloads constructor( val profiles = ConcurrentLinkedQueue(profilesUnfiltered) val urlTest = UrlTest() // note: this is NOT in bg process - repeat(5) { + repeat(DataStore.connectionTestConcurrent) { testJobs.add(launch { while (isActive) { val profile = profiles.poll() ?: break diff --git a/app/src/main/java/io/nekohasekai/sagernet/widget/LinkOrContentPreference.kt b/app/src/main/java/io/nekohasekai/sagernet/widget/LinkOrContentPreference.kt index 5c0ea8110..0d257c726 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/widget/LinkOrContentPreference.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/widget/LinkOrContentPreference.kt @@ -44,7 +44,7 @@ class LinkOrContentPreference : EditTextPreference { init { - dialogLayoutResource = R.layout.layout_link_dialog + dialogLayoutResource = R.layout.layout_urltest_preference_dialog setOnBindEditTextListener { val linkLayout = it.rootView.findViewById(R.id.input_layout) diff --git a/app/src/main/java/io/nekohasekai/sagernet/widget/LinkPreference.kt b/app/src/main/java/io/nekohasekai/sagernet/widget/LinkPreference.kt deleted file mode 100644 index 84b9a5163..000000000 --- a/app/src/main/java/io/nekohasekai/sagernet/widget/LinkPreference.kt +++ /dev/null @@ -1,112 +0,0 @@ -/****************************************************************************** - * * - * Copyright (C) 2021 by nekohasekai * - * * - * 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 . * - * * - ******************************************************************************/ - -package io.nekohasekai.sagernet.widget - -import android.content.Context -import android.util.AttributeSet -import androidx.core.widget.addTextChangedListener -import com.google.android.material.textfield.TextInputLayout -import com.takisoft.preferencex.EditTextPreference -import io.nekohasekai.sagernet.R -import io.nekohasekai.sagernet.ktx.app -import io.nekohasekai.sagernet.ktx.readableMessage -import okhttp3.HttpUrl.Companion.toHttpUrl - -class LinkPreference : EditTextPreference { - - var defaultValue: String? = null - - constructor(context: Context) : this(context, null) - - constructor( - context: Context, - attrs: AttributeSet?, - ) : this(context, attrs, com.takisoft.preferencex.R.attr.editTextPreferenceStyle) - - constructor( - context: Context, - attrs: AttributeSet?, - defStyleAttr: Int, - ) : this(context, attrs, defStyleAttr, 0) - - constructor( - context: Context, - attrs: AttributeSet?, - defStyleAttr: Int, - defStyleRes: Int, - ) : super(context, attrs, defStyleAttr, defStyleRes) { - val a = context.obtainStyledAttributes( - attrs, R.styleable.Preference, defStyleAttr, defStyleRes - ) - if (a.hasValue(androidx.preference.R.styleable.Preference_defaultValue)) { - defaultValue = onGetDefaultValue( - a, androidx.preference.R.styleable.Preference_defaultValue - )?.toString() - } else if (a.hasValue(androidx.preference.R.styleable.Preference_android_defaultValue)) { - defaultValue = onGetDefaultValue( - a, androidx.preference.R.styleable.Preference_android_defaultValue - )?.toString() - } - } - - init { - dialogLayoutResource = R.layout.layout_link_dialog - - setOnBindEditTextListener { - val linkLayout = it.rootView.findViewById(R.id.input_layout) - fun validate() { - val link = it.text - if (link.isBlank()) { - linkLayout.isErrorEnabled = false - return - } - try { - val url = link.toString().toHttpUrl() - if ("http".equals(url.scheme, true)) { - linkLayout.error = app.getString(R.string.cleartext_http_warning) - linkLayout.isErrorEnabled = true - } else { - linkLayout.isErrorEnabled = false - } - } catch (e: Exception) { - linkLayout.error = e.readableMessage - linkLayout.isErrorEnabled = true - } - } - validate() - it.addTextChangedListener { - validate() - } - } - - setOnPreferenceChangeListener { _, newValue -> - if ((newValue as String).isBlank()) { - text = defaultValue - false - } else try { - newValue.toHttpUrl() - true - } catch (ignored: Exception) { - false - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/res/layout/layout_link_dialog.xml b/app/src/main/res/layout/layout_urltest_preference_dialog.xml similarity index 61% rename from app/src/main/res/layout/layout_link_dialog.xml rename to app/src/main/res/layout/layout_urltest_preference_dialog.xml index d5dc440ce..d1c2b9efd 100644 --- a/app/src/main/res/layout/layout_link_dialog.xml +++ b/app/src/main/res/layout/layout_urltest_preference_dialog.xml @@ -10,18 +10,6 @@ android:layout_height="wrap_content" android:orientation="vertical"> - - + + + + + + + + + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 6dce70484..7f699b7ce 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -485,4 +485,5 @@ 在通知中显示组名 重置连接 删除重复的服务器 + 测试并发 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 631ec97cb..576292bad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -525,5 +525,6 @@ Anyone can write advanced plugins, which can control Matsuri. please download an Show group name in in notification Reset Connections Remove duplicate servers + Test concurrency \ No newline at end of file diff --git a/app/src/main/res/xml/global_preferences.xml b/app/src/main/res/xml/global_preferences.xml index b00eb3969..d605b5262 100644 --- a/app/src/main/res/xml/global_preferences.xml +++ b/app/src/main/res/xml/global_preferences.xml @@ -236,7 +236,7 @@ -