Add policy group config type and UI support

Introduces a new POLICYGROUP config type for server grouping and selection. Adds ServerGroupActivity and related UI, updates menu options, and removes legacy intelligent selection logic. Refactors config management and filtering to support policy groups, and updates translations and resources accordingly.
This commit is contained in:
2dust
2025-12-30 16:16:32 +08:00
parent 47be68c52f
commit 4812f067db
32 changed files with 473 additions and 248 deletions
+4
View File
@@ -79,6 +79,10 @@
android:name=".ui.ServerCustomConfigActivity"
android:exported="false"
android:windowSoftInputMode="stateUnchanged" />
<activity
android:name=".ui.ServerGroupActivity"
android:exported="false"
android:windowSoftInputMode="stateUnchanged" />
<activity
android:name=".ui.SettingsActivity"
android:exported="false" />
@@ -58,7 +58,6 @@ object AppConfig {
const val PREF_DELAY_TEST_URL = "pref_delay_test_url"
const val PREF_LOGLEVEL = "pref_core_loglevel"
const val PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD = "pref_outbound_domain_resolve_method"
const val PREF_INTELLIGENT_SELECTION_METHOD = "pref_intelligent_selection_method"
const val PREF_MODE = "pref_mode"
const val PREF_IS_BOOTED = "pref_is_booted"
const val PREF_CHECK_UPDATE_PRE_RELEASE = "pref_check_update_pre_release"
@@ -14,7 +14,8 @@ enum class EConfigType(val value: Int, val protocolScheme: String) {
// TUIC(8, AppConfig.TUIC),
HYSTERIA2(9, AppConfig.HYSTERIA2),
HTTP(10, AppConfig.HTTP);
HTTP(10, AppConfig.HTTP),
POLICYGROUP (101, AppConfig.CUSTOM);
companion object {
fun fromInt(value: Int) = entries.firstOrNull { it.value == value }
@@ -59,6 +59,10 @@ data class ProfileItem(
var bandwidthDown: String? = null,
var bandwidthUp: String? = null,
var policyGroupType: String? = null,
var policyGroupSubscriptionId: String? = null,
var policyGroupFilter: String? = null,
) {
companion object {
fun create(configType: EConfigType): ProfileItem {
@@ -11,7 +11,6 @@ data class SubscriptionItem(
var prevProfile: String? = null,
var nextProfile: String? = null,
var filter: String? = null,
var intelligentSelectionFilter: String? = null,
var allowInsecureUrl: Boolean = false,
var userAgent: String? = null,
)
@@ -148,6 +148,7 @@ object AngConfigManager {
EConfigType.TROJAN -> TrojanFmt.toUri(config)
EConfigType.WIREGUARD -> WireguardFmt.toUri(config)
EConfigType.HYSTERIA2 -> Hysteria2Fmt.toUri(config)
EConfigType.POLICYGROUP -> ""
}
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to share config for GUID: $guid", e)
@@ -491,31 +492,4 @@ object AngConfigManager {
MmkvManager.encodeSubscription("", subItem)
return 1
}
/**
* Creates an intelligent selection configuration based on multiple server configurations.
*
* @param context The application context used for configuration generation.
* @param guidList The list of server GUIDs to be included in the intelligent selection.
* Each GUID represents a server configuration that will be combined.
* @param subid The subscription ID to associate with the generated configuration.
* This helps organize the configuration under a specific subscription.
* @return The GUID key of the newly created intelligent selection configuration,
* or null if the operation fails (e.g., empty guidList or configuration parsing error).
*/
fun createIntelligentSelection(
context: Context,
guidList: List<String>,
subid: String
): String? {
if (guidList.isEmpty()) {
return null
}
val result = V2rayConfigManager.genV2rayConfig(context, guidList) ?: return null
val config = CustomFmt.parse(JsonUtil.toJson(result)) ?: return null
config.subscriptionId = subid
val key = MmkvManager.encodeServerConfig("", config)
MmkvManager.encodeServerRaw(key, JsonUtil.toJsonPretty(result) ?: "")
return key
}
}
@@ -5,7 +5,6 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.util.Log
import androidx.core.content.ContextCompat
import com.v2ray.ang.AppConfig
@@ -17,7 +16,6 @@ import com.v2ray.ang.service.ServiceControl
import com.v2ray.ang.service.V2RayProxyOnlyService
import com.v2ray.ang.service.V2RayVpnService
import com.v2ray.ang.util.MessageUtil
import com.v2ray.ang.handler.PluginServiceManager
import com.v2ray.ang.util.Utils
import go.Seq
import kotlinx.coroutines.CoroutineScope
@@ -100,6 +98,7 @@ object V2RayServiceManager {
val guid = MmkvManager.getSelectServer() ?: return
val config = MmkvManager.decodeServerConfig(guid) ?: return
if (config.configType != EConfigType.CUSTOM
&& config.configType != EConfigType.POLICYGROUP
&& !Utils.isValidUrl(config.server)
&& !Utils.isPureIpAddress(config.server.orEmpty())
) return
@@ -44,6 +44,8 @@ object V2rayConfigManager {
val config = MmkvManager.decodeServerConfig(guid) ?: return ConfigResult(false)
return if (config.configType == EConfigType.CUSTOM) {
getV2rayCustomConfig(guid, config)
} else if (config.configType == EConfigType.POLICYGROUP) {
getV2rayGroupConfig(context, guid, config)
} else {
getV2rayNormalConfig(context, guid, config)
}
@@ -53,27 +55,6 @@ object V2rayConfigManager {
}
}
/**
* Generates a V2ray configuration from multiple server profiles.
*
* @param context The context of the caller.
* @param guidList A list of server GUIDs to be included in the generated configuration.
* Each GUID represents a unique server profile stored in the system.
* @return A V2rayConfig object containing the combined configuration of all specified servers,
* or null if the operation fails (e.g., no valid configurations found, parsing errors)
*/
fun genV2rayConfig(context: Context, guidList: List<String>): V2rayConfig? {
try {
val configList = guidList.mapNotNull { guid ->
MmkvManager.decodeServerConfig(guid)
}
return genV2rayMultipleConfig(context, configList)
} catch (e: Exception) {
Log.e(AppConfig.TAG, "Failed to generate V2ray config", e)
return null
}
}
/**
* Retrieves the speedtest V2ray configuration for the given GUID.
*
@@ -86,6 +67,9 @@ object V2rayConfigManager {
val config = MmkvManager.decodeServerConfig(guid) ?: return ConfigResult(false)
return if (config.configType == EConfigType.CUSTOM) {
getV2rayCustomConfig(guid, config)
} else if (config.configType == EConfigType.POLICYGROUP) {
// The number of policy groups will not be very large, so no special handling is needed.
getV2rayGroupConfig(context, guid, config)
} else {
getV2rayNormalConfig4Speedtest(context, guid, config)
}
@@ -107,6 +91,50 @@ object V2rayConfigManager {
return ConfigResult(true, guid, raw)
}
/**
* Retrieves the group V2ray configuration.
*
* @param context The context in which the function is called.
* @param guid The unique identifier for the V2ray configuration.
* @param config The profile item containing the configuration details.
* @return A ConfigResult object containing the result of the configuration retrieval.
*/
private fun getV2rayGroupConfig(context: Context, guid: String, config: ProfileItem): ConfigResult {
val result = ConfigResult(false)
val serverList = MmkvManager.decodeServerList()
val configList = serverList
.mapNotNull { id -> MmkvManager.decodeServerConfig(id) }
.filter { profile ->
val subscriptionId = config.policyGroupSubscriptionId
if (subscriptionId.isNullOrBlank()) {
true
} else {
profile.subscriptionId == subscriptionId
}
}
.filter { profile ->
val filter = config.policyGroupFilter
if (filter.isNullOrBlank()) {
true
} else {
try {
Regex(filter).containsMatchIn(profile.remarks)
} catch (e: Exception) {
profile.remarks.contains(filter)
}
}
}
val v2rayConfig = getV2rayMultipleConfig(context, config, configList) ?: return result
result.status = true
result.content = JsonUtil.toJsonPretty(v2rayConfig) ?: ""
result.guid = guid
return result
}
/**
* Retrieves the normal V2ray configuration.
*
@@ -164,21 +192,13 @@ object V2rayConfigManager {
return result
}
private fun genV2rayMultipleConfig(context: Context, configList: List<ProfileItem>): V2rayConfig? {
private fun getV2rayMultipleConfig(context: Context, config: ProfileItem, configList: List<ProfileItem>): V2rayConfig? {
val validConfigs = configList.asSequence().filter { it.server.isNotNullEmpty() }
.filter { !Utils.isPureIpAddress(it.server!!) || Utils.isValidUrl(it.server!!) }
.filter { it.configType != EConfigType.CUSTOM }
.filter { it.configType != EConfigType.HYSTERIA2 }
.filter { config ->
if (config.subscriptionId.isEmpty()) {
return@filter true
}
val subItem = MmkvManager.decodeSubscription(config.subscriptionId)
if (subItem?.intelligentSelectionFilter.isNullOrEmpty() || config.remarks.isEmpty()) {
return@filter true
}
Regex(pattern = subItem.intelligentSelectionFilter.orEmpty()).containsMatchIn(input = config.remarks)
}.toList()
.filter { it.configType != EConfigType.POLICYGROUP }
.toList()
if (validConfigs.isEmpty()) {
Log.w(AppConfig.TAG, "All configs are invalid")
@@ -187,16 +207,7 @@ object V2rayConfigManager {
val v2rayConfig = initV2rayConfig(context) ?: return null
v2rayConfig.log.loglevel = MmkvManager.decodeSettingsString(AppConfig.PREF_LOGLEVEL) ?: "warning"
val subIds = configList.map { it.subscriptionId }.toHashSet()
val remarks = if (subIds.size == 1 && subIds.first().isNotEmpty()) {
val sub = MmkvManager.decodeSubscription(subIds.first())
(sub?.remarks ?: "") + context.getString(R.string.intelligent_selection)
} else {
context.getString(R.string.intelligent_selection)
}
v2rayConfig.remarks = remarks
v2rayConfig.remarks = config.remarks
getInbounds(v2rayConfig)
@@ -220,12 +231,12 @@ object V2rayConfigManager {
getDns(v2rayConfig)
getBalance(v2rayConfig)
getBalance(v2rayConfig, config)
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED) == true) {
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_LOCAL_DNS_ENABLED)) {
getCustomLocalDns(v2rayConfig)
}
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED) != true) {
if (!MmkvManager.decodeSettingsBool(AppConfig.PREF_SPEED_ENABLED)) {
v2rayConfig.stats = null
v2rayConfig.policy = null
}
@@ -469,7 +480,7 @@ object V2rayConfigManager {
)
}
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_USE_HEV_TUNNEL ,true) == false) {
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_USE_HEV_TUNNEL, true) == false) {
// DNS inbound
val remoteDns = SettingsManager.getRemoteDnsServers()
@@ -850,8 +861,7 @@ object V2rayConfigManager {
*
* @param v2rayConfig The V2ray configuration object to be modified with balancing settings
*/
private fun getBalance(v2rayConfig: V2rayConfig)
{
private fun getBalance(v2rayConfig: V2rayConfig, config: ProfileItem) {
try {
v2rayConfig.routing.rules.forEach { rule ->
if (rule.outboundTag == "proxy") {
@@ -860,7 +870,7 @@ object V2rayConfigManager {
}
}
if (MmkvManager.decodeSettingsString(AppConfig.PREF_INTELLIGENT_SELECTION_METHOD, "0") == "0") {
if (config.policyGroupType == "0") {
val balancer = V2rayConfig.RoutingBean.BalancerBean(
tag = "proxy-round",
selector = listOf("proxy-"),
@@ -1054,6 +1064,7 @@ object V2rayConfigManager {
EConfigType.WIREGUARD -> WireguardFmt.toOutbound(profileItem)
EConfigType.HYSTERIA2 -> null
EConfigType.HTTP -> HttpFmt.toOutbound(profileItem)
EConfigType.POLICYGROUP -> null
}
}
@@ -1104,6 +1115,7 @@ object V2rayConfigManager {
)
EConfigType.CUSTOM -> null
EConfigType.POLICYGROUP -> null
}
}
@@ -313,6 +313,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
true
}
R.id.import_manually_policy_group -> {
importManually(EConfigType.POLICYGROUP.value)
true
}
R.id.import_manually_vmess -> {
importManually(EConfigType.VMESS.value)
true
@@ -370,14 +375,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
true
}
R.id.intelligent_selection_all -> {
if (MmkvManager.decodeSettingsString(AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD, "1") != "0") {
toast(getString(R.string.pre_resolving_domain))
}
mainViewModel.createIntelligentSelectionAll()
true
}
R.id.service_restart -> {
restartV2Ray()
true
@@ -413,12 +410,20 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
}
private fun importManually(createConfigType: Int) {
startActivity(
Intent()
.putExtra("createConfigType", createConfigType)
.putExtra("subscriptionId", mainViewModel.subscriptionId)
.setClass(this, ServerActivity::class.java)
)
if (createConfigType == EConfigType.POLICYGROUP.value) {
startActivity(
Intent()
.putExtra("subscriptionId", mainViewModel.subscriptionId)
.setClass(this, ServerGroupActivity::class.java)
)
} else {
startActivity(
Intent()
.putExtra("createConfigType", createConfigType)
.putExtra("subscriptionId", mainViewModel.subscriptionId)
.setClass(this, ServerActivity::class.java)
)
}
}
/**
@@ -57,7 +57,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
if (holder is MainViewHolder) {
val guid = mActivity.mainViewModel.serversCache[position].guid
val profile = mActivity.mainViewModel.serversCache[position].profile
val isCustom = profile.configType == EConfigType.CUSTOM
val isCustom = profile.configType == EConfigType.CUSTOM || profile.configType == EConfigType.POLICYGROUP
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
@@ -144,14 +144,18 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
*/
private fun getAddress(profile: ProfileItem): String {
// Hide xxx:xxx:***/xxx.xxx.xxx.***
return "${
profile.server?.let {
if (it.contains(":"))
it.split(":").take(2).joinToString(":", postfix = ":***")
else
it.split('.').dropLast(1).joinToString(".", postfix = ".***")
}
} : ${profile.serverPort}"
val server = profile.server
val port = profile.serverPort
if (server.isNullOrBlank() && port.isNullOrBlank()) return ""
val addrPart = server?.let {
if (it.contains(":"))
it.split(":").take(2).joinToString(":", postfix = ":***")
else
it.split('.').dropLast(1).joinToString(".", postfix = ".***")
} ?: ""
return "$addrPart : ${port ?: ""}"
}
/**
@@ -250,6 +254,8 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
.putExtra("createConfigType", profile.configType.value)
if (profile.configType == EConfigType.CUSTOM) {
mActivity.startActivity(intent.setClass(mActivity, ServerCustomConfigActivity::class.java))
} else if (profile.configType == EConfigType.POLICYGROUP) {
mActivity.startActivity(intent.setClass(mActivity, ServerGroupActivity::class.java))
} else {
mActivity.startActivity(intent.setClass(mActivity, ServerActivity::class.java))
}
@@ -134,9 +134,9 @@ class ServerActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
title = getString(R.string.title_server)
val config = MmkvManager.decodeServerConfig(editGuid)
title = (config?.configType ?: createConfigType).toString()
when (config?.configType ?: createConfigType) {
EConfigType.VMESS -> setContentView(R.layout.activity_server_vmess)
EConfigType.CUSTOM -> return
@@ -147,6 +147,7 @@ class ServerActivity : BaseActivity() {
EConfigType.TROJAN -> setContentView(R.layout.activity_server_trojan)
EConfigType.WIREGUARD -> setContentView(R.layout.activity_server_wireguard)
EConfigType.HYSTERIA2 -> setContentView(R.layout.activity_server_hysteria2)
EConfigType.POLICYGROUP -> return
}
sp_network?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
@@ -32,7 +32,7 @@ class ServerCustomConfigActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
title = getString(R.string.title_server)
title = EConfigType.CUSTOM.toString()
if (!Utils.getDarkModeStatus(this)) {
binding.editor.colorScheme = EditorTheme.INTELLIJ_LIGHT
@@ -0,0 +1,165 @@
package com.v2ray.ang.ui
import android.os.Bundle
import android.text.TextUtils
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AlertDialog
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityServerGroupBinding
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ProfileItem
import com.v2ray.ang.extension.toast
import com.v2ray.ang.extension.toastSuccess
import android.widget.ArrayAdapter
import com.v2ray.ang.handler.MmkvManager
import com.v2ray.ang.util.Utils
class ServerGroupActivity : BaseActivity() {
private val binding by lazy { ActivityServerGroupBinding.inflate(layoutInflater) }
private val editGuid by lazy { intent.getStringExtra("guid").orEmpty() }
private val isRunning by lazy {
intent.getBooleanExtra("isRunning", false)
&& editGuid.isNotEmpty()
&& editGuid == MmkvManager.getSelectServer()
}
private val subscriptionId by lazy {
intent.getStringExtra("subscriptionId")
}
private val subIds = mutableListOf<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
title = EConfigType.POLICYGROUP.toString()
val config = MmkvManager.decodeServerConfig(editGuid)
populateSubscriptionSpinner()
if (config != null) {
bindingServer(config)
} else {
clearServer()
}
}
/**
* Binding selected server config
*/
private fun bindingServer(config: ProfileItem): Boolean {
binding.etRemarks.text = Utils.getEditable(config.remarks)
binding.etPolicyGroupFilter.text = Utils.getEditable(config.policyGroupFilter)
val type = config.policyGroupType?.toInt() ?: 0
binding.spPolicyGroupType.setSelection(type)
val pos = subIds.indexOf(config.policyGroupSubscriptionId ?: "").let { if (it >= 0) it else 0 }
binding.spPolicyGroupSubId.setSelection(pos)
return true
}
/**
* clear or init server config
*/
private fun clearServer(): Boolean {
binding.etRemarks.text = null
binding.etPolicyGroupFilter.text = null
return true
}
/**
* save server config
*/
private fun saveServer(): Boolean {
if (TextUtils.isEmpty(binding.etRemarks.text.toString())) {
toast(R.string.server_lab_remarks)
return false
}
val config = MmkvManager.decodeServerConfig(editGuid) ?: ProfileItem.create(EConfigType.POLICYGROUP)
config.remarks = binding.etRemarks.text.toString().trim()
config.policyGroupFilter = binding.etPolicyGroupFilter.text.toString().trim()
config.policyGroupType = binding.spPolicyGroupType.selectedItemPosition.toString()
val selPos = binding.spPolicyGroupSubId.selectedItemPosition
config.policyGroupSubscriptionId = if (selPos >= 0 && selPos < subIds.size) subIds[selPos] else null
if (config.subscriptionId.isEmpty() && !subscriptionId.isNullOrEmpty()) {
config.subscriptionId = subscriptionId.orEmpty()
}
MmkvManager.encodeServerConfig(editGuid, config)
toastSuccess(R.string.toast_success)
finish()
return true
}
/**
* save server config
*/
private fun deleteServer(): Boolean {
if (editGuid.isNotEmpty()) {
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
MmkvManager.removeServer(editGuid)
finish()
}
.setNegativeButton(android.R.string.cancel) { _, _ ->
// do nothing
}
.show()
}
return true
}
private fun populateSubscriptionSpinner() {
val subs = MmkvManager.decodeSubscriptions()
val displayList = mutableListOf(getString(R.string.filter_config_all)) //none
subIds.clear()
subIds.add("") // index 0 => All
subs.forEach { (id, item) ->
val name = when {
item.remarks.isNotBlank() -> item.remarks
else -> id
}
displayList.add(name)
subIds.add(id)
}
val subAdapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, displayList)
subAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.spPolicyGroupSubId.adapter = subAdapter
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.action_server, menu)
val delButton = menu.findItem(R.id.del_config)
val saveButton = menu.findItem(R.id.save_config)
if (editGuid.isNotEmpty()) {
if (isRunning) {
delButton?.isVisible = false
saveButton?.isVisible = false
}
} else {
delButton?.isVisible = false
}
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.del_config -> {
deleteServer()
true
}
R.id.save_config -> {
saveServer()
true
}
else -> super.onOptionsItemSelected(item)
}
}
@@ -285,7 +285,6 @@ class SettingsActivity : BaseActivity() {
AppConfig.PREF_UI_MODE_NIGHT,
AppConfig.PREF_LOGLEVEL,
AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD,
AppConfig.PREF_INTELLIGENT_SELECTION_METHOD,
AppConfig.PREF_MODE,
AppConfig.PREF_HEV_TUNNEL_LOGLEVEL
).forEach { key ->
@@ -46,7 +46,6 @@ class SubEditActivity : BaseActivity() {
binding.etUrl.text = Utils.getEditable(subItem.url)
binding.etUserAgent.text = Utils.getEditable(subItem.userAgent)
binding.etFilter.text = Utils.getEditable(subItem.filter)
binding.etIntelligentSelectionFilter.text = Utils.getEditable(subItem.intelligentSelectionFilter)
binding.chkEnable.isChecked = subItem.enabled
binding.autoUpdateCheck.isChecked = subItem.autoUpdate
binding.allowInsecureUrl.isChecked = subItem.allowInsecureUrl
@@ -62,7 +61,6 @@ class SubEditActivity : BaseActivity() {
binding.etRemarks.text = null
binding.etUrl.text = null
binding.etFilter.text = null
binding.etIntelligentSelectionFilter.text = null
binding.chkEnable.isChecked = true
binding.etPreProfile.text = null
binding.etNextProfile.text = null
@@ -79,7 +77,6 @@ class SubEditActivity : BaseActivity() {
subItem.url = binding.etUrl.text.toString()
subItem.userAgent = binding.etUserAgent.text.toString()
subItem.filter = binding.etFilter.text.toString()
subItem.intelligentSelectionFilter = binding.etIntelligentSelectionFilter.text.toString()
subItem.enabled = binding.chkEnable.isChecked
subItem.autoUpdate = binding.autoUpdateCheck.isChecked
subItem.prevProfile = binding.etPreProfile.text.toString()
@@ -384,29 +384,6 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
MmkvManager.encodeServerList(serverList)
}
/**
* Creates an intelligent selection configuration containing all currently filtered servers.
*/
fun createIntelligentSelectionAll() {
viewModelScope.launch(Dispatchers.IO) {
val key = AngConfigManager.createIntelligentSelection(
getApplication<AngApplication>(),
serversCache.map { it.guid }.toList(),
subscriptionId
)
launch(Dispatchers.Main) {
if (key.isNullOrEmpty()) {
getApplication<AngApplication>().toastError(R.string.toast_failure)
} else {
getApplication<AngApplication>().toastSuccess(R.string.toast_success)
MmkvManager.setSelectServer(key)
reloadServerList()
}
}
}
}
/**
* Initializes assets.
* @param assets The asset manager.
@@ -51,7 +51,6 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
AppConfig.PREF_SOCKS_PORT,
AppConfig.PREF_LOGLEVEL,
AppConfig.PREF_OUTBOUND_DOMAIN_RESOLVE_METHOD,
AppConfig.PREF_INTELLIGENT_SELECTION_METHOD,
AppConfig.PREF_LANGUAGE,
AppConfig.PREF_UI_MODE_NIGHT,
AppConfig.PREF_ROUTING_DOMAIN_STRATEGY,
@@ -9,12 +9,12 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:orientation="vertical"
android:padding="@dimen/padding_spacing_dp16">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/padding_spacing_dp8"
android:orientation="vertical">
<TextView
@@ -28,7 +28,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/padding_spacing_dp8"
android:layout_marginTop="@dimen/padding_spacing_dp16"
android:orientation="vertical">
<TextView
@@ -41,7 +41,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/padding_spacing_dp8"
android:layout_marginTop="@dimen/padding_spacing_dp16"
android:orientation="vertical">
<TextView
@@ -60,12 +60,12 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_spacing_dp16"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/padding_spacing_dp8"
android:text="@string/server_lab_content"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
@@ -73,7 +73,6 @@
android:id="@+id/editor"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/padding_spacing_dp16"
android:gravity="top|start" />
</LinearLayout>
@@ -0,0 +1,106 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".ui.ServerCustomConfigActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/padding_spacing_dp16">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_server"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_spacing_dp16"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_remarks" />
<EditText
android:id="@+id/et_remarks"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_spacing_dp16"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_policy_group_type" />
<Spinner
android:id="@+id/sp_policy_group_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_spacing_dp8"
android:layout_marginBottom="@dimen/padding_spacing_dp8"
android:entries="@array/policy_group_type" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_spacing_dp16"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_policy_group_subscription_id" />
<Spinner
android:id="@+id/sp_policy_group_sub_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_spacing_dp8"
android:layout_marginBottom="@dimen/padding_spacing_dp8"
android:entries="@array/policy_group_type" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_spacing_dp16"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/title_policy_group_subscription_filter" />
<EditText
android:id="@+id/et_policy_group_filter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text" />
</LinearLayout>
</LinearLayout>
</ScrollView>
@@ -112,25 +112,6 @@
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/padding_spacing_dp16"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sub_setting_intelligent_selection_filter" />
<EditText
android:id="@+id/et_intelligent_selection_filter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="text" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
+4 -4
View File
@@ -24,6 +24,10 @@
android:id="@+id/import_local"
android:title="@string/menu_item_import_config_local"
app:showAsAction="never" />
<item
android:id="@+id/import_manually_policy_group"
android:title="@string/menu_item_import_config_policy_group"
app:showAsAction="never" />
<item
android:id="@+id/import_manually_vmess"
android:title="@string/menu_item_import_config_manually_vmess"
@@ -82,10 +86,6 @@
android:icon="@drawable/ic_share_24dp"
android:title="@string/title_export_all"
app:showAsAction="never" />
<item
android:id="@+id/intelligent_selection_all"
android:title="@string/title_create_intelligent_selection_all_server"
app:showAsAction="never" />
<item
android:id="@+id/ping_all"
android:title="@string/title_ping_all_server"
@@ -29,6 +29,7 @@
<string name="menu_item_import_config_qrcode">استيراد التكوين من رمز الاستجابة السريعة (QRcode)</string>
<string name="menu_item_import_config_clipboard">استيراد التكوين من الحافظة</string>
<string name="menu_item_import_config_local">Import config from locally</string>
<string name="menu_item_import_config_policy_group">Add [Policy group]</string>
<string name="menu_item_import_config_manually_vmess">الكتابة يدويًا [VMess]</string>
<string name="menu_item_import_config_manually_vless">الكتابة يدويًا [VLESS]</string>
<string name="menu_item_import_config_manually_ss">الكتابة يدويًا [Shadowsocks]</string>
@@ -278,7 +279,6 @@
<string name="title_sub_update">تحديث الاشتراك (أول خطوة)</string>
<string name="title_ping_all_server">Tcping لجميع الإعدادات</string>
<string name="title_real_ping_all_server"> اختبر جميع الإعدادات (3)</string>
<string name="title_create_intelligent_selection_all_server">Creating Intelligent Selection Current Group Configuration</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_sort_by_test_results">الفرز حسب نتائج الاختبار (5)</string>
<string name="title_filter_config">تصفية ملف التكوين</string>
@@ -332,6 +332,10 @@
<string name="update_check_pre_release">Check Pre-release</string>
<string name="update_checking_for_update">Checking for update…</string>
<string name="title_policy_group_type">Policy group type</string>
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<string-array name="share_method">
<item>رمز استجابة سريعة (QRcode)</item>
<item>تصدير إلى الحافظة</item>
@@ -373,10 +377,7 @@
<item>Resolve and add to DNS Hosts</item>
<item>Resolve and replace domain</item>
</string-array>
<string name="intelligent_selection">Intelligent Selection</string>
<string name="sub_setting_intelligent_selection_filter">Remarks Intelligent Selection regular filter</string>
<string name="title_intelligent_selection_method">Intelligent Selection Method</string>
<string-array name="intelligent_selection_method">
<string-array name="policy_group_type">
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
@@ -29,6 +29,7 @@
<string name="menu_item_import_config_qrcode">QR কোড থেকে কনফিগারেশন আমদানি করুন</string>
<string name="menu_item_import_config_clipboard">ক্লিপবোর্ড থেকে কনফিগারেশন আমদানি করুন</string>
<string name="menu_item_import_config_local">Import config from locally</string>
<string name="menu_item_import_config_policy_group">Add [Policy group]</string>
<string name="menu_item_import_config_manually_vmess">ম্যানুয়ালি টাইপ করুন [VMess]</string>
<string name="menu_item_import_config_manually_vless">ম্যানুয়ালি টাইপ করুন [VLESS]</string>
<string name="menu_item_import_config_manually_ss">ম্যানুয়ালি টাইপ করুন [Shadowsocks]</string>
@@ -278,7 +279,6 @@
<string name="title_sub_update">সাবস্ক্রিপশন আপডেট</string>
<string name="title_ping_all_server">সব কনফিগারেশন TCPing</string>
<string name="title_real_ping_all_server">সব কনফিগারেশন প্রকৃত বিলম্ব</string>
<string name="title_create_intelligent_selection_all_server">Creating Intelligent Selection Current Group Configuration</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_sort_by_test_results">টেস্ট ফলাফল দ্বারা সাজানো</string>
<string name="title_filter_config">কনফিগারেশন ফাইল ফিল্টার করুন</string>
@@ -331,6 +331,10 @@
<string name="update_check_pre_release">Check Pre-release</string>
<string name="update_checking_for_update">Checking for update…</string>
<string name="title_policy_group_type">Policy group type</string>
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<string-array name="share_method">
<item>QR কোড</item>
<item>ক্লিপবোর্ডে রপ্তানি করুন</item>
@@ -378,10 +382,7 @@
<item>Resolve and add to DNS Hosts</item>
<item>Resolve and replace domain</item>
</string-array>
<string name="intelligent_selection">Intelligent Selection</string>
<string name="sub_setting_intelligent_selection_filter">Remarks Intelligent Selection regular filter</string>
<string name="title_intelligent_selection_method">Intelligent Selection Method</string>
<string-array name="intelligent_selection_method">
<string-array name="policy_group_type">
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
@@ -29,6 +29,7 @@
<string name="menu_item_import_config_qrcode">و من ٱووردن کانفیگ ز QRcode</string>
<string name="menu_item_import_config_clipboard">و من ٱووردن کانفیگ ز کلیپ بورد</string>
<string name="menu_item_import_config_local">و من ٱووردن کانفیگ ز مهلی</string>
<string name="menu_item_import_config_policy_group">Add [Policy group]</string>
<string name="menu_item_import_config_manually_vmess">هؽل دستی[VMess]</string>
<string name="menu_item_import_config_manually_vless">هؽل دستی[VLESS]</string>
<string name="menu_item_import_config_manually_ss">هؽل دستی[Shadowsocks]</string>
@@ -278,7 +279,6 @@
<string name="title_sub_update">ورۊ کردن اشتراک بونکۊ سکویی</string>
<string name="title_ping_all_server">Tcping کانفیگا بونکۊ سکویی</string>
<string name="title_real_ping_all_server">تئخیر واقعی کانفیگا بونکۊ سکویی</string>
<string name="title_create_intelligent_selection_all_server">وورکل پسند هۊشمند کانفیگ بونکۊ سکویی</string>
<string name="title_user_asset_setting">فایلا بونچک جوقرافیایی</string>
<string name="title_sort_by_test_results">ترتیب و ری نتیجه یل آزمایش</string>
<string name="title_filter_config">فیلتر کردن کانفیگا</string>
@@ -340,6 +340,10 @@
<string name="update_check_pre_release">واجۊری نوسخه یل پؽش ز تیجنیڌن</string>
<string name="update_checking_for_update">ورۊ رسۊوی ن هونی واجۊری اکونه...</string>
<string name="title_policy_group_type">Policy group type</string>
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<string-array name="share_method">
<item>QRcode</item>
<item>و در کشیڌن من کلیپ بورد</item>
@@ -388,10 +392,7 @@
<item>هل وو ٱووردن و میزبووݩ یل دامنه DNS</item>
<item>هل وو جایونی دامنه</item>
</string-array>
<string name="intelligent_selection">پسند هۊشمند</string>
<string name="sub_setting_intelligent_selection_filter">گوڌنا دیاری پسند هۊشمند فیلتر مئمۊلی</string>
<string name="title_intelligent_selection_method">بارت پسند هۊشمند</string>
<string-array name="intelligent_selection_method">
<string-array name="policy_group_type">
<item>کم ترین پینگ</item>
<item>کم ترین بار(لود)</item>
</string-array>
@@ -29,6 +29,7 @@
<string name="menu_item_import_config_qrcode">کانفیگ را از QRcode وارد کنید</string>
<string name="menu_item_import_config_clipboard">کانفیگ را از کلیپ ‌بورد وارد کنید</string>
<string name="menu_item_import_config_local">کانفیگ را از محلی وارد کنید</string>
<string name="menu_item_import_config_policy_group">Add [Policy group]</string>
<string name="menu_item_import_config_manually_vmess">تایپ دستی[VMESS]</string>
<string name="menu_item_import_config_manually_vless">تایپ دستی[VLESS]</string>
<string name="menu_item_import_config_manually_ss">تایپ دستی[SHADOWSOCKS]</string>
@@ -275,7 +276,6 @@
<string name="title_sub_update">به‌روزرسانی گروه فعلی اشتراک</string>
<string name="title_ping_all_server">TCPING کانفیگ های گروه فعلی</string>
<string name="title_real_ping_all_server">تاخیر واقعی کانفیگ های گروه فعلی</string>
<string name="title_create_intelligent_selection_all_server">ایجاد کانفیگ انتخاب هوشمند برای گروه فعلی</string>
<string name="title_user_asset_setting">فایل های منبع جغرافیایی</string>
<string name="title_sort_by_test_results">مرتب‌ سازی بر اساس نتایج آزمایش</string>
<string name="title_filter_config">فیلتر کردن کانفیگ‌ها</string>
@@ -337,6 +337,10 @@
<string name="update_check_pre_release">بررسی نسخه پیش از انتشار</string>
<string name="update_checking_for_update">در حال بررسی برای به‌روزرسانی…</string>
<string name="title_policy_group_type">Policy group type</string>
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<string-array name="share_method">
<item>QRcode</item>
<item>خروجی گرفتن در کلیپ‌ بورد</item>
@@ -387,10 +391,7 @@
<item>Resolve and add to DNS Hosts</item>
<item>Resolve and replace domain</item>
</string-array>
<string name="intelligent_selection">انتخاب هوشمند</string>
<string name="sub_setting_intelligent_selection_filter">فیلتر نام مستعار برای انتخاب هوشمند</string>
<string name="title_intelligent_selection_method">روش انتخاب هوشمند</string>
<string-array name="intelligent_selection_method">
<string-array name="policy_group_type">
<item>کمترین پینگ</item>
<item>کمترین بار(لود)</item>
</string-array>
@@ -29,6 +29,7 @@
<string name="menu_item_import_config_qrcode">Импорт из QR-кода</string>
<string name="menu_item_import_config_clipboard">Импорт из буфера обмена</string>
<string name="menu_item_import_config_local">Импорт из файла</string>
<string name="menu_item_import_config_policy_group">Add [Policy group]</string>
<string name="menu_item_import_config_manually_vmess">Ручной ввод VMess</string>
<string name="menu_item_import_config_manually_vless">Ручной ввод VLESS</string>
<string name="menu_item_import_config_manually_ss">Ручной ввод Shadowsocks</string>
@@ -277,7 +278,6 @@
<string name="title_sub_update">Обновить подписку группы</string>
<string name="title_ping_all_server">Проверить профили группы</string>
<string name="title_real_ping_all_server">Проверить задержку профилей группы</string>
<string name="title_create_intelligent_selection_all_server">Создать конфигурацию умного выбора</string>
<string name="title_user_asset_setting">Файлы ресурсов</string>
<string name="title_sort_by_test_results">Сортировать по результатам теста</string>
<string name="title_filter_config">Фильтр профилей</string>
@@ -339,6 +339,10 @@
<string name="update_check_pre_release">Искать предварительный выпуск</string>
<string name="update_checking_for_update">Проверка обновления…</string>
<string name="title_policy_group_type">Policy group type</string>
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<string-array name="share_method">
<item>QR-код</item>
<item>Экспорт в буфер обмена</item>
@@ -387,10 +391,7 @@
<item>Определять и добавлять к узлам DNS</item>
<item>Определять и заменять домен</item>
</string-array>
<string name="intelligent_selection">Умный выбор</string>
<string name="sub_setting_intelligent_selection_filter">Название фильтра умного выбора</string>
<string name="title_intelligent_selection_method">Принцип умного выбора</string>
<string-array name="intelligent_selection_method">
<string-array name="policy_group_type">
<item>Наименьшая задержка</item>
<item>Наименьшая нагрузка</item>
</string-array>
@@ -29,6 +29,7 @@
<string name="menu_item_import_config_qrcode">Nhập cấu hình từ mã QR</string>
<string name="menu_item_import_config_clipboard">Nhập cấu hình từ Clipboard</string>
<string name="menu_item_import_config_local">Import config from locally</string>
<string name="menu_item_import_config_policy_group">Add [Policy group]</string>
<string name="menu_item_import_config_manually_vmess">Nhập thủ công [VMess]</string>
<string name="menu_item_import_config_manually_vless">Nhập thủ công [VLESS]</string>
<string name="menu_item_import_config_manually_ss">Nhập thủ công [ShadowSocks]</string>
@@ -278,7 +279,6 @@
<string name="title_sub_update">Cập nhật các gói đăng ký</string>
<string name="title_ping_all_server">Ping tất cả máy chủ</string>
<string name="title_real_ping_all_server">Kiểm tra HTTP tất cả máy chủ</string>
<string name="title_create_intelligent_selection_all_server">Creating Intelligent Selection Current Group Configuration</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_sort_by_test_results">Sắp xếp lại theo lần kiểm tra cuối cùng</string>
<string name="title_filter_config">Lọc cấu hình theo các gói đăng ký</string>
@@ -333,6 +333,10 @@
<string name="update_check_pre_release">Check Pre-release</string>
<string name="update_checking_for_update">Checking for update…</string>
<string name="title_policy_group_type">Policy group type</string>
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<string-array name="share_method">
<item>Xuất ra mã QR (Chụp màn hình để lưu)</item>
<item>Sao chép vào Clipboard</item>
@@ -375,10 +379,7 @@
<item>Resolve and add to DNS Hosts</item>
<item>Resolve and replace domain</item>
</string-array>
<string name="intelligent_selection">Intelligent Selection</string>
<string name="sub_setting_intelligent_selection_filter">Remarks Intelligent Selection regular filter</string>
<string name="title_intelligent_selection_method">Intelligent Selection Method</string>
<string-array name="intelligent_selection_method">
<string-array name="policy_group_type">
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
@@ -29,14 +29,15 @@
<string name="menu_item_import_config_qrcode">扫描二维码</string>
<string name="menu_item_import_config_clipboard">从剪贴板导入</string>
<string name="menu_item_import_config_local">从本地导入</string>
<string name="menu_item_import_config_manually_vmess">手动输入 [VMess]</string>
<string name="menu_item_import_config_manually_vless">手动输入 [VLESS]</string>
<string name="menu_item_import_config_manually_ss">手动输入 [Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">手动输入 [SOCKS]</string>
<string name="menu_item_import_config_manually_http">手动输入 [HTTP]</string>
<string name="menu_item_import_config_manually_trojan">手动输入 [Trojan]</string>
<string name="menu_item_import_config_manually_wireguard">手动输入 [Wireguard]</string>
<string name="menu_item_import_config_manually_hysteria2">手动输入 [Hysteria2]</string>
<string name="menu_item_import_config_policy_group">添加 [策略组]</string>
<string name="menu_item_import_config_manually_vmess">添加 [VMess]</string>
<string name="menu_item_import_config_manually_vless">添加 [VLESS]</string>
<string name="menu_item_import_config_manually_ss">添加 [Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">添加 [SOCKS]</string>
<string name="menu_item_import_config_manually_http">添加 [HTTP]</string>
<string name="menu_item_import_config_manually_trojan">添加 [Trojan]</string>
<string name="menu_item_import_config_manually_wireguard">添加 [Wireguard]</string>
<string name="menu_item_import_config_manually_hysteria2">添加 [Hysteria2]</string>
<string name="del_config_comfirm">确认删除?</string>
<string name="del_invalid_config_comfirm">删除前请先测试!确认删除?</string>
<string name="server_lab_remarks">别名 (remarks)</string>
@@ -275,7 +276,6 @@
<string name="title_sub_update">更新当前组订阅</string>
<string name="title_ping_all_server">测试当前组配置 Tcping</string>
<string name="title_real_ping_all_server">测试当前组配置真连接</string>
<string name="title_create_intelligent_selection_all_server">生成当前组智能选择配置</string>
<string name="title_user_asset_setting">资源文件</string>
<string name="title_sort_by_test_results">按测试结果排序</string>
<string name="title_filter_config">过滤配置</string>
@@ -331,6 +331,10 @@
<string name="update_check_pre_release">检查 Pre-release</string>
<string name="update_checking_for_update">正在检查更新中…</string>
<string name="title_policy_group_type">策略组类型</string>
<string name="title_policy_group_subscription_id">来自订阅分组</string>
<string name="title_policy_group_subscription_filter">别名正则过滤</string>
<string-array name="share_method">
<item>二维码</item>
<item>导出至剪贴板</item>
@@ -379,10 +383,7 @@
<item>解析后添加至 DNS Hosts</item>
<item>解析后替换原域名</item>
</string-array>
<string name="intelligent_selection">智能选择</string>
<string name="sub_setting_intelligent_selection_filter">别名智能选择正则过滤</string>
<string name="title_intelligent_selection_method">智能选择方式</string>
<string-array name="intelligent_selection_method">
<string-array name="policy_group_type">
<item>最低延迟</item>
<item>最稳定</item>
</string-array>
@@ -29,14 +29,15 @@
<string name="menu_item_import_config_qrcode">從 QR Code 匯入設定</string>
<string name="menu_item_import_config_clipboard">從剪貼簿匯入設定</string>
<string name="menu_item_import_config_local">從本地匯入</string>
<string name="menu_item_import_config_manually_vmess">手動鍵入 [VMess]</string>
<string name="menu_item_import_config_manually_vless">手動鍵入 [VLESS]</string>
<string name="menu_item_import_config_manually_ss">手動鍵入 [Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">手動鍵入 [SOCKS]</string>
<string name="menu_item_import_config_manually_http">手動鍵入 [HTTP]</string>
<string name="menu_item_import_config_manually_trojan">手動鍵入 [Trojan]</string>
<string name="menu_item_import_config_manually_wireguard">手動鍵入 [Wireguard]</string>
<string name="menu_item_import_config_manually_hysteria2">手動鍵入 [Hysteria2]</string>
<string name="menu_item_import_config_policy_group">新增 [策略組]</string>
<string name="menu_item_import_config_manually_vmess">新增 [VMess]</string>
<string name="menu_item_import_config_manually_vless">新增 [VLESS]</string>
<string name="menu_item_import_config_manually_ss">新增 [Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">新增 [SOCKS]</string>
<string name="menu_item_import_config_manually_http">新增 [HTTP]</string>
<string name="menu_item_import_config_manually_trojan">新增 [Trojan]</string>
<string name="menu_item_import_config_manually_wireguard">新增 [Wireguard]</string>
<string name="menu_item_import_config_manually_hysteria2">新增 [Hysteria2]</string>
<string name="del_config_comfirm">確定刪除?</string>
<string name="del_invalid_config_comfirm">刪除前請先測試!確認刪除?</string>
<string name="server_lab_remarks">備註</string>
@@ -276,7 +277,6 @@
<string name="title_sub_update">更新目前群組訂閱</string>
<string name="title_ping_all_server">偵測目前群組設定 Tcping</string>
<string name="title_real_ping_all_server">偵測目前群組設定真延遲</string>
<string name="title_create_intelligent_selection_all_server">創建目前群組智慧選擇</string>
<string name="title_user_asset_setting">資源檔案</string>
<string name="title_sort_by_test_results">依偵測結果排序</string>
<string name="title_filter_config">過濾設定</string>
@@ -331,6 +331,10 @@
<string name="update_check_pre_release">檢查 Pre-release</string>
<string name="update_checking_for_update">正在檢查更新中…</string>
<string name="title_policy_group_type">策略群組類型</string>
<string name="title_policy_group_subscription_id">來自訂閱分組</string>
<string name="title_policy_group_subscription_filter">別名正規過濾</string>
<string-array name="share_method">
<item>二維碼</item>
<item>匯出至剪貼簿</item>
@@ -379,10 +383,7 @@
<item>解析後加入 DNS Hosts</item>
<item>解析後替換原網域名稱</item>
</string-array>
<string name="intelligent_selection">智慧選擇</string>
<string name="sub_setting_intelligent_selection_filter">別名智慧選擇正則過濾</string>
<string name="title_intelligent_selection_method">智慧選擇方案</string>
<string-array name="intelligent_selection_method">
<string-array name="policy_group_type">
<item>最低延遲</item>
<item>最少卡頓</item>
</string-array>
@@ -215,9 +215,4 @@
<item>2</item>
</string-array>
<string-array name="intelligent_selection_method_value" translatable="false">
<item>0</item>
<item>1</item>
</string-array>
</resources>
+19 -16
View File
@@ -27,17 +27,18 @@
<string name="menu_item_save_config">Save configuration</string>
<string name="menu_item_edit_config">Edit configuration</string>
<string name="menu_item_del_config">Delete configuration</string>
<string name="menu_item_import_config_qrcode">Import configuration from QRcode</string>
<string name="menu_item_import_config_clipboard">Import configuration from Clipboard</string>
<string name="menu_item_import_config_local">Import configuration from locally</string>
<string name="menu_item_import_config_manually_vmess">Type manually[VMess]</string>
<string name="menu_item_import_config_manually_vless">Type manually[VLESS]</string>
<string name="menu_item_import_config_manually_ss">Type manually[Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">Type manually[SOCKS]</string>
<string name="menu_item_import_config_manually_http">Type manually[HTTP]</string>
<string name="menu_item_import_config_manually_trojan">Type manually[Trojan]</string>
<string name="menu_item_import_config_manually_wireguard">Type manually[Wireguard]</string>
<string name="menu_item_import_config_manually_hysteria2">Type manually[Hysteria2]</string>
<string name="menu_item_import_config_qrcode">Import from QRcode</string>
<string name="menu_item_import_config_clipboard">Import from Clipboard</string>
<string name="menu_item_import_config_local">Import from locally</string>
<string name="menu_item_import_config_policy_group">Add [Policy group]</string>
<string name="menu_item_import_config_manually_vmess">Add [VMess]</string>
<string name="menu_item_import_config_manually_vless">Add [VLESS]</string>
<string name="menu_item_import_config_manually_ss">Add [Shadowsocks]</string>
<string name="menu_item_import_config_manually_socks">Add [SOCKS]</string>
<string name="menu_item_import_config_manually_http">Add [HTTP]</string>
<string name="menu_item_import_config_manually_trojan">Add [Trojan]</string>
<string name="menu_item_import_config_manually_wireguard">Add [Wireguard]</string>
<string name="menu_item_import_config_manually_hysteria2">Add [Hysteria2]</string>
<string name="del_config_comfirm">Confirm delete ?</string>
<string name="del_invalid_config_comfirm">Please test before deleting! Confirm delete ?</string>
<string name="server_lab_remarks">remarks</string>
@@ -281,7 +282,6 @@
<string name="title_sub_update">Update current group subscription</string>
<string name="title_ping_all_server">Tcping current group configuration</string>
<string name="title_real_ping_all_server">Real delay current group configuration</string>
<string name="title_create_intelligent_selection_all_server">Creating Intelligent Selection Current Group Configuration</string>
<string name="title_user_asset_setting">Asset files</string>
<string name="title_sort_by_test_results">Sorting by test results</string>
<string name="title_filter_config">Filter configuration</string>
@@ -343,6 +343,10 @@
<string name="update_check_pre_release">Check Pre-release</string>
<string name="update_checking_for_update">Checking for update…</string>
<string name="title_policy_group_type">Policy group type</string>
<string name="title_policy_group_subscription_id">From subscription group</string>
<string name="title_policy_group_subscription_filter">Remarks regular filter</string>
<string-array name="share_method">
<item>QRcode</item>
<item>Export to clipboard</item>
@@ -391,13 +395,12 @@
<item>Resolve and add to DNS Hosts</item>
<item>Resolve and replace domain</item>
</string-array>
<string name="intelligent_selection">Intelligent Selection</string>
<string name="sub_setting_intelligent_selection_filter">Remarks Intelligent Selection regular filter</string>
<string name="title_intelligent_selection_method">Intelligent Selection Method</string>
<string-array name="intelligent_selection_method">
<string-array name="policy_group_type">
<item>Least Ping</item>
<item>Least Load</item>
</string-array>
<string name="pre_resolving_domain">Pre-resolving domain…</string>
</resources>
@@ -256,14 +256,6 @@
android:summary="%s"
android:title="@string/title_outbound_domain_resolve_method" />
<ListPreference
android:defaultValue="0"
android:entries="@array/intelligent_selection_method"
android:entryValues="@array/intelligent_selection_method_value"
android:key="pref_intelligent_selection_method"
android:summary="%s"
android:title="@string/title_intelligent_selection_method" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/title_advanced">