Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0141225d3 | ||
|
|
026bf6a37b | ||
|
|
c117a334ce | ||
|
|
051520de37 | ||
|
|
b5146e4712 | ||
|
|
4457b6b2c9 | ||
|
|
12edb051c3 | ||
|
|
923e3f32d1 | ||
|
|
5490db90c0 | ||
|
|
cb68d42291 | ||
|
|
ec3a8e80d6 | ||
|
|
eb0960125b | ||
|
|
12b70e2088 | ||
|
|
0c473dea19 | ||
|
|
e71dd7342a | ||
|
|
a765215dda | ||
|
|
edea3027f7 | ||
|
|
305219c3bc | ||
|
|
9c4188fba7 | ||
|
|
0f3124bece | ||
|
|
2614f72a36 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -153,7 +153,7 @@ jobs:
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
if: github.event.inputs.release_tag != ''
|
||||
with:
|
||||
file: ${{ github.workspace }}/V2rayNG/app/build/outputs/apk/*playstore*/release/*.apk
|
||||
file: ${{ github.workspace }}/V2rayNG/app/build/outputs/apk/*/release/*.apk
|
||||
tag: ${{ github.event.inputs.release_tag }}
|
||||
file_glob: true
|
||||
prerelease: true
|
||||
|
||||
Submodule AndroidLibXrayLite updated: b01a18a1ec...f89b8c4585
@@ -12,8 +12,8 @@ android {
|
||||
applicationId = "com.v2ray.ang"
|
||||
minSdk = 21
|
||||
targetSdk = 35
|
||||
versionCode = 672
|
||||
versionName = "1.10.22"
|
||||
versionCode = 678
|
||||
versionName = "1.10.27"
|
||||
multiDexEnabled = true
|
||||
|
||||
val abiFilterList = (properties["ABI_FILTERS"] as? String)?.split(';')
|
||||
|
||||
4
V2rayNG/app/src/fdroid/res/values/strings.xml
Normal file
4
V2rayNG/app/src/fdroid/res/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name" translatable="false">v2rayNG (F-Droid)</string>
|
||||
</resources>
|
||||
31
V2rayNG/app/src/fdroid/res/xml/shortcuts.xml
Normal file
31
V2rayNG/app/src/fdroid/res/xml/shortcuts.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<shortcut
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_qu_switch_24dp"
|
||||
android:shortcutDisabledMessage="@string/app_widget_name"
|
||||
android:shortcutId="shortcuts_switch"
|
||||
android:shortcutLongLabel="@string/app_widget_name"
|
||||
android:shortcutShortLabel="@string/app_widget_name">
|
||||
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetClass="com.v2ray.ang.ui.ScSwitchActivity"
|
||||
android:targetPackage="com.v2ray.ang.fdroid" />
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
</shortcut>
|
||||
<shortcut
|
||||
android:enabled="true"
|
||||
android:icon="@drawable/ic_qu_scan_24dp"
|
||||
android:shortcutDisabledMessage="@string/menu_item_import_config_qrcode"
|
||||
android:shortcutId="shortcuts_scan"
|
||||
android:shortcutLongLabel="@string/menu_item_import_config_qrcode"
|
||||
android:shortcutShortLabel="@string/menu_item_import_config_qrcode">
|
||||
|
||||
<intent
|
||||
android:action="android.intent.action.VIEW"
|
||||
android:targetClass="com.v2ray.ang.ui.ScScannerActivity"
|
||||
android:targetPackage="com.v2ray.ang.fdroid" />
|
||||
<categories android:name="android.shortcut.conversation" />
|
||||
</shortcut>
|
||||
</shortcuts>
|
||||
@@ -195,8 +195,8 @@
|
||||
android:resource="@xml/app_widget_provider" />
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
<action android:name="com.v2ray.ang.action.widget.click" />
|
||||
<action android:name="com.v2ray.ang.action.activity" />
|
||||
<action android:name="${applicationId}.action.widget.click" />
|
||||
<action android:name="${applicationId}.action.activity" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<receiver
|
||||
|
||||
@@ -75,9 +75,9 @@ object AppConfig {
|
||||
const val PROTOCOL_FREEDOM = "freedom"
|
||||
|
||||
/** Broadcast actions. */
|
||||
const val BROADCAST_ACTION_SERVICE = "com.v2ray.ang.action.service"
|
||||
const val BROADCAST_ACTION_ACTIVITY = "com.v2ray.ang.action.activity"
|
||||
const val BROADCAST_ACTION_WIDGET_CLICK = "com.v2ray.ang.action.widget.click"
|
||||
const val BROADCAST_ACTION_SERVICE = "$ANG_PACKAGE.action.service"
|
||||
const val BROADCAST_ACTION_ACTIVITY = "$ANG_PACKAGE.action.activity"
|
||||
const val BROADCAST_ACTION_WIDGET_CLICK = "$ANG_PACKAGE.action.widget.click"
|
||||
|
||||
/** Tasker extras. */
|
||||
const val TASKER_EXTRA_BUNDLE = "com.twofortyfouram.locale.intent.extra.BUNDLE"
|
||||
|
||||
@@ -15,5 +15,6 @@ data class VmessQRCode(
|
||||
var tls: String = "",
|
||||
var sni: String = "",
|
||||
var alpn: String = "",
|
||||
var fp: String = ""
|
||||
var fp: String = "",
|
||||
var insecure: String = ""
|
||||
)
|
||||
@@ -72,10 +72,12 @@ open class FmtBase {
|
||||
if (config.security != AppConfig.TLS && config.security != AppConfig.REALITY) {
|
||||
config.security = null
|
||||
}
|
||||
config.insecure = if (queryParam["allowInsecure"].isNullOrEmpty()) {
|
||||
allowInsecure
|
||||
} else {
|
||||
queryParam["allowInsecure"].orEmpty() == "1"
|
||||
// Support multiple possible query keys for allowInsecure like the C# implementation
|
||||
val allowInsecureKeys = arrayOf("insecure", "allowInsecure", "allow_insecure", "verify")
|
||||
config.insecure = when {
|
||||
allowInsecureKeys.any { queryParam[it] == "1" } -> true
|
||||
allowInsecureKeys.any { queryParam[it] == "0" } -> false
|
||||
else -> allowInsecure
|
||||
}
|
||||
config.sni = queryParam["sni"]
|
||||
config.fingerPrint = queryParam["fp"]
|
||||
@@ -104,6 +106,12 @@ open class FmtBase {
|
||||
config.spiderX.let { if (it.isNotNullEmpty()) dicQuery["spx"] = it.orEmpty() }
|
||||
config.mldsa65Verify.let { if (it.isNotNullEmpty()) dicQuery["pqv"] = it.orEmpty() }
|
||||
config.flow.let { if (it.isNotNullEmpty()) dicQuery["flow"] = it.orEmpty() }
|
||||
// Add two keys for compatibility: "insecure" and "allowInsecure"
|
||||
if (config.security == AppConfig.TLS) {
|
||||
val insecureFlag = if (config.insecure == true) "1" else "0"
|
||||
dicQuery["insecure"] = insecureFlag
|
||||
dicQuery["allowInsecure"] = insecureFlag
|
||||
}
|
||||
|
||||
val networkType = NetworkType.fromString(config.network)
|
||||
dicQuery["type"] = networkType.type
|
||||
|
||||
@@ -34,15 +34,9 @@ object Hysteria2Fmt : FmtBase() {
|
||||
if (!uri.rawQuery.isNullOrEmpty()) {
|
||||
val queryParam = getQueryParam(uri)
|
||||
|
||||
getItemFormQuery(config, queryParam, allowInsecure)
|
||||
|
||||
config.security = queryParam["security"] ?: AppConfig.TLS
|
||||
config.insecure = if (queryParam["insecure"].isNullOrEmpty()) {
|
||||
allowInsecure
|
||||
} else {
|
||||
queryParam["insecure"].orEmpty() == "1"
|
||||
}
|
||||
config.sni = queryParam["sni"]
|
||||
config.alpn = queryParam["alpn"]
|
||||
|
||||
config.obfsPassword = queryParam["obfs-password"]
|
||||
config.portHopping = queryParam["mport"]
|
||||
config.pinSHA256 = queryParam["pinSHA256"]
|
||||
|
||||
@@ -122,7 +122,7 @@ object ShadowsocksFmt : FmtBase() {
|
||||
fun toUri(config: ProfileItem): String {
|
||||
val pw = "${config.method}:${config.password}"
|
||||
|
||||
return toUri(config, Utils.encode(pw), null)
|
||||
return toUri(config, Utils.encode(pw, true), null)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -51,7 +51,7 @@ object SocksFmt : FmtBase() {
|
||||
else
|
||||
":"
|
||||
|
||||
return toUri(config, Utils.encode(pw), null)
|
||||
return toUri(config, Utils.encode(pw, true), null)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,7 @@ object VmessFmt : FmtBase() {
|
||||
return parseVmessStd(str)
|
||||
}
|
||||
|
||||
var allowInsecure = MmkvManager.decodeSettingsBool(AppConfig.PREF_ALLOW_INSECURE, false)
|
||||
val allowInsecure = MmkvManager.decodeSettingsBool(AppConfig.PREF_ALLOW_INSECURE, false)
|
||||
val config = ProfileItem.create(EConfigType.VMESS)
|
||||
|
||||
var result = str.replace(EConfigType.VMESS.protocolScheme, "")
|
||||
@@ -78,12 +78,15 @@ object VmessFmt : FmtBase() {
|
||||
else -> {}
|
||||
}
|
||||
|
||||
config.security = vmessQRCode.tls
|
||||
config.insecure = allowInsecure
|
||||
config.security = vmessQRCode.tls
|
||||
config.sni = vmessQRCode.sni
|
||||
config.fingerPrint = vmessQRCode.fp
|
||||
config.alpn = vmessQRCode.alpn
|
||||
|
||||
config.insecure = when (vmessQRCode.insecure) {
|
||||
"1" -> true
|
||||
"0" -> false
|
||||
else -> allowInsecure
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -132,6 +135,11 @@ object VmessFmt : FmtBase() {
|
||||
vmessQRCode.sni = config.sni.orEmpty()
|
||||
vmessQRCode.fp = config.fingerPrint.orEmpty()
|
||||
vmessQRCode.alpn = config.alpn.orEmpty()
|
||||
vmessQRCode.insecure = when (config.insecure) {
|
||||
true -> "1"
|
||||
false -> "0"
|
||||
else -> ""
|
||||
}
|
||||
|
||||
val json = JsonUtil.toJson(vmessQRCode)
|
||||
return Utils.encode(json)
|
||||
|
||||
@@ -100,8 +100,19 @@ object UpdateCheckerManager {
|
||||
}
|
||||
|
||||
private fun getDownloadUrl(release: GitHubRelease, abi: String): String {
|
||||
return release.assets.find { it.name.contains(abi) }?.browserDownloadUrl
|
||||
?: release.assets.firstOrNull()?.browserDownloadUrl
|
||||
val fDroid = "fdroid"
|
||||
|
||||
val assetsByAbi = release.assets.filter {
|
||||
(it.name.contains(abi, true))
|
||||
}
|
||||
|
||||
val asset = if (BuildConfig.APPLICATION_ID.contains(fDroid, ignoreCase = true)) {
|
||||
assetsByAbi.firstOrNull { it.name.contains(fDroid) }
|
||||
} else {
|
||||
assetsByAbi.firstOrNull { !it.name.contains(fDroid) }
|
||||
}
|
||||
|
||||
return asset?.browserDownloadUrl
|
||||
?: throw IllegalStateException("No compatible APK found")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@ object V2rayConfigManager {
|
||||
)
|
||||
}
|
||||
|
||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_USE_HEV_TUNNEL) == false) {
|
||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_USE_HEV_TUNNEL ,true) == false) {
|
||||
|
||||
// DNS inbound
|
||||
val remoteDns = SettingsManager.getRemoteDnsServers()
|
||||
@@ -623,17 +623,16 @@ object V2rayConfigManager {
|
||||
|
||||
// DNS routing
|
||||
v2rayConfig.routing.rules.add(
|
||||
0, RulesBean(
|
||||
outboundTag = AppConfig.TAG_PROXY,
|
||||
inboundTag = arrayListOf(AppConfig.TAG_DNS),
|
||||
RulesBean(
|
||||
outboundTag = AppConfig.TAG_DIRECT,
|
||||
inboundTag = arrayListOf(AppConfig.TAG_DOMESTIC_DNS),
|
||||
domain = null
|
||||
)
|
||||
)
|
||||
|
||||
v2rayConfig.routing.rules.add(
|
||||
0, RulesBean(
|
||||
outboundTag = AppConfig.TAG_DIRECT,
|
||||
inboundTag = arrayListOf(AppConfig.TAG_DOMESTIC_DNS),
|
||||
RulesBean(
|
||||
outboundTag = AppConfig.TAG_PROXY,
|
||||
inboundTag = arrayListOf(AppConfig.TAG_DNS),
|
||||
domain = null
|
||||
)
|
||||
)
|
||||
|
||||
@@ -295,7 +295,7 @@ class V2RayVpnService : VpnService(), ServiceControl {
|
||||
* Starts the tun2socks process with the appropriate parameters.
|
||||
*/
|
||||
private fun runTun2socks() {
|
||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_USE_HEV_TUNNEL) == true) {
|
||||
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_USE_HEV_TUNNEL, true) == true) {
|
||||
tun2SocksService = TProxyService(
|
||||
context = applicationContext,
|
||||
vpnInterface = mInterface,
|
||||
|
||||
@@ -127,6 +127,9 @@ class AboutActivity : BaseActivity() {
|
||||
"v${BuildConfig.VERSION_NAME} (${SpeedtestManager.getLibVersion()})".also {
|
||||
binding.tvVersion.text = it
|
||||
}
|
||||
BuildConfig.APPLICATION_ID.also {
|
||||
binding.tvAppId.text = it
|
||||
}
|
||||
}
|
||||
|
||||
private fun backupConfiguration(outputZipFilePos: String): Pair<Boolean, String> {
|
||||
|
||||
@@ -42,10 +42,30 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
|
||||
addCustomDividerToRecyclerView(binding.recyclerView, this, R.drawable.custom_divider)
|
||||
|
||||
initList()
|
||||
|
||||
binding.switchPerAppProxy.setOnCheckedChangeListener { _, isChecked ->
|
||||
MmkvManager.encodeSettings(AppConfig.PREF_PER_APP_PROXY, isChecked)
|
||||
}
|
||||
binding.switchPerAppProxy.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_PER_APP_PROXY, false)
|
||||
|
||||
binding.switchBypassApps.setOnCheckedChangeListener { _, isChecked ->
|
||||
MmkvManager.encodeSettings(AppConfig.PREF_BYPASS_APPS, isChecked)
|
||||
}
|
||||
binding.switchBypassApps.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_BYPASS_APPS, false)
|
||||
|
||||
binding.layoutSwitchBypassAppsTips.setOnClickListener {
|
||||
Toasty.info(this, R.string.summary_pref_per_app_proxy, Toast.LENGTH_LONG, true).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initList() {
|
||||
binding.pbWaiting.show()
|
||||
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
binding.pbWaiting.show()
|
||||
val blacklist = MmkvManager.decodeSettingsStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
|
||||
val blacklist =
|
||||
MmkvManager.decodeSettingsStringSet(AppConfig.PREF_PER_APP_PROXY_SET)
|
||||
val apps = withContext(Dispatchers.IO) {
|
||||
val appsList = AppManagerUtil.loadNetworkAppList(this@PerAppProxyActivity)
|
||||
|
||||
@@ -75,26 +95,12 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
appsAll = apps
|
||||
adapter = PerAppProxyAdapter(this@PerAppProxyActivity, apps, blacklist)
|
||||
binding.recyclerView.adapter = adapter
|
||||
binding.pbWaiting.hide()
|
||||
} catch (e: Exception) {
|
||||
binding.pbWaiting.hide()
|
||||
Log.e(ANG_PACKAGE, "Error loading apps", e)
|
||||
} finally {
|
||||
binding.pbWaiting.hide()
|
||||
}
|
||||
}
|
||||
|
||||
binding.switchPerAppProxy.setOnCheckedChangeListener { _, isChecked ->
|
||||
MmkvManager.encodeSettings(AppConfig.PREF_PER_APP_PROXY, isChecked)
|
||||
}
|
||||
binding.switchPerAppProxy.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_PER_APP_PROXY, false)
|
||||
|
||||
binding.switchBypassApps.setOnCheckedChangeListener { _, isChecked ->
|
||||
MmkvManager.encodeSettings(AppConfig.PREF_BYPASS_APPS, isChecked)
|
||||
}
|
||||
binding.switchBypassApps.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_BYPASS_APPS, false)
|
||||
|
||||
binding.layoutSwitchBypassAppsTips.setOnClickListener {
|
||||
Toasty.info(this, R.string.summary_pref_per_app_proxy, Toast.LENGTH_LONG, true).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
@@ -120,14 +126,40 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||
R.id.select_all -> adapter?.let { it ->
|
||||
R.id.select_all -> {
|
||||
selectAllApp()
|
||||
allowPerAppProxy()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.select_proxy_app -> {
|
||||
selectProxyAppAuto()
|
||||
allowPerAppProxy()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.import_proxy_app -> {
|
||||
importProxyApp()
|
||||
allowPerAppProxy()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.export_proxy_app -> {
|
||||
exportProxyApp()
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun selectAllApp() {
|
||||
adapter?.let { it ->
|
||||
val pkgNames = it.apps.map { it.packageName }
|
||||
if (it.blacklist.containsAll(pkgNames)) {
|
||||
it.apps.forEach {
|
||||
@@ -142,27 +174,10 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
}
|
||||
it.notifyDataSetChanged()
|
||||
true
|
||||
} == true
|
||||
|
||||
R.id.select_proxy_app -> {
|
||||
selectProxyApp()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.import_proxy_app -> {
|
||||
importProxyApp()
|
||||
true
|
||||
}
|
||||
|
||||
R.id.export_proxy_app -> {
|
||||
exportProxyApp()
|
||||
true
|
||||
}
|
||||
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun selectProxyApp() {
|
||||
private fun selectProxyAppAuto() {
|
||||
toast(R.string.msg_downloading_content)
|
||||
binding.pbWaiting.show()
|
||||
|
||||
@@ -199,6 +214,10 @@ class PerAppProxyActivity : BaseActivity() {
|
||||
toastSuccess(R.string.toast_success)
|
||||
}
|
||||
|
||||
private fun allowPerAppProxy() {
|
||||
binding.switchPerAppProxy.isChecked = true
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun selectProxyApp(content: String, force: Boolean): Boolean {
|
||||
try {
|
||||
|
||||
@@ -37,7 +37,7 @@ class SettingsActivity : BaseActivity() {
|
||||
|
||||
class SettingsFragment : PreferenceFragmentCompat() {
|
||||
|
||||
private val perAppProxy by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_PER_APP_PROXY) }
|
||||
// private val perAppProxy by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_PER_APP_PROXY) }
|
||||
private val localDns by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_LOCAL_DNS_ENABLED) }
|
||||
private val fakeDns by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_FAKE_DNS_ENABLED) }
|
||||
private val appendHttpProxy by lazy { findPreference<CheckBoxPreference>(AppConfig.PREF_APPEND_HTTP_PROXY) }
|
||||
@@ -74,11 +74,11 @@ class SettingsActivity : BaseActivity() {
|
||||
override fun onCreatePreferences(bundle: Bundle?, s: String?) {
|
||||
addPreferencesFromResource(R.xml.pref_settings)
|
||||
|
||||
perAppProxy?.setOnPreferenceClickListener {
|
||||
startActivity(Intent(activity, PerAppProxyActivity::class.java))
|
||||
perAppProxy?.isChecked = true
|
||||
false
|
||||
}
|
||||
// perAppProxy?.setOnPreferenceClickListener {
|
||||
// startActivity(Intent(activity, PerAppProxyActivity::class.java))
|
||||
// perAppProxy?.isChecked = true
|
||||
// false
|
||||
// }
|
||||
localDns?.setOnPreferenceChangeListener { _, any ->
|
||||
updateLocalDns(any as Boolean)
|
||||
true
|
||||
@@ -227,7 +227,7 @@ class SettingsActivity : BaseActivity() {
|
||||
dnsHosts?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DNS_HOSTS)
|
||||
delayTestUrl?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_DELAY_TEST_URL, AppConfig.DELAY_TEST_URL)
|
||||
|
||||
updateHevTunSettings(MmkvManager.decodeSettingsBool(AppConfig.PREF_USE_HEV_TUNNEL, false))
|
||||
updateHevTunSettings(MmkvManager.decodeSettingsBool(AppConfig.PREF_USE_HEV_TUNNEL, true))
|
||||
hevTunRwTimeout?.summary = MmkvManager.decodeSettingsString(AppConfig.PREF_HEV_TUNNEL_RW_TIMEOUT, AppConfig.HEVTUN_RW_TIMEOUT)
|
||||
|
||||
initSharedPreference()
|
||||
@@ -254,6 +254,7 @@ class SettingsActivity : BaseActivity() {
|
||||
|
||||
listOf(
|
||||
AppConfig.PREF_SNIFFING_ENABLED,
|
||||
AppConfig.PREF_USE_HEV_TUNNEL
|
||||
).forEach { key ->
|
||||
findPreference<CheckBoxPreference>(key)?.isChecked =
|
||||
MmkvManager.decodeSettingsBool(key, true)
|
||||
@@ -269,8 +270,7 @@ class SettingsActivity : BaseActivity() {
|
||||
AppConfig.PREF_DOUBLE_COLUMN_DISPLAY,
|
||||
AppConfig.PREF_PREFER_IPV6,
|
||||
AppConfig.PREF_PROXY_SHARING,
|
||||
AppConfig.PREF_ALLOW_INSECURE,
|
||||
AppConfig.PREF_USE_HEV_TUNNEL
|
||||
AppConfig.PREF_ALLOW_INSECURE
|
||||
).forEach { key ->
|
||||
findPreference<CheckBoxPreference>(key)?.isChecked =
|
||||
MmkvManager.decodeSettingsBool(key, false)
|
||||
@@ -298,8 +298,8 @@ class SettingsActivity : BaseActivity() {
|
||||
|
||||
private fun updateMode(mode: String?) {
|
||||
val vpn = mode == VPN
|
||||
perAppProxy?.isEnabled = vpn
|
||||
perAppProxy?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_PER_APP_PROXY, false)
|
||||
// perAppProxy?.isEnabled = vpn
|
||||
// perAppProxy?.isChecked = MmkvManager.decodeSettingsBool(AppConfig.PREF_PER_APP_PROXY, false)
|
||||
localDns?.isEnabled = vpn
|
||||
fakeDns?.isEnabled = vpn
|
||||
appendHttpProxy?.isEnabled = vpn
|
||||
|
||||
@@ -134,11 +134,16 @@ object Utils {
|
||||
* Encode a string to base64.
|
||||
*
|
||||
* @param text The string to encode.
|
||||
* @param removePadding
|
||||
* @return The base64 encoded string, or an empty string if encoding fails.
|
||||
*/
|
||||
fun encode(text: String): String {
|
||||
fun encode(text: String, removePadding : Boolean = false): String {
|
||||
return try {
|
||||
Base64.encodeToString(text.toByteArray(Charsets.UTF_8), Base64.NO_WRAP)
|
||||
var encoded = Base64.encodeToString(text.toByteArray(Charsets.UTF_8), Base64.NO_WRAP)
|
||||
if (removePadding) {
|
||||
encoded = encoded.trimEnd('=')
|
||||
}
|
||||
encoded
|
||||
} catch (e: Exception) {
|
||||
Log.e(AppConfig.TAG, "Failed to encode text to base64", e)
|
||||
""
|
||||
|
||||
@@ -75,20 +75,20 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
|
||||
AppConfig.PREF_APPEND_HTTP_PROXY,
|
||||
AppConfig.PREF_ALLOW_INSECURE,
|
||||
AppConfig.PREF_PREFER_IPV6,
|
||||
AppConfig.PREF_PER_APP_PROXY,
|
||||
// AppConfig.PREF_PER_APP_PROXY,
|
||||
AppConfig.PREF_BYPASS_APPS,
|
||||
AppConfig.PREF_CONFIRM_REMOVE,
|
||||
AppConfig.PREF_START_SCAN_IMMEDIATE,
|
||||
AppConfig.PREF_DOUBLE_COLUMN_DISPLAY,
|
||||
AppConfig.SUBSCRIPTION_AUTO_UPDATE,
|
||||
AppConfig.PREF_FRAGMENT_ENABLED,
|
||||
AppConfig.PREF_MUX_ENABLED,
|
||||
AppConfig.PREF_USE_HEV_TUNNEL
|
||||
AppConfig.PREF_MUX_ENABLED
|
||||
-> {
|
||||
MmkvManager.encodeSettings(key, sharedPreferences.getBoolean(key, false))
|
||||
}
|
||||
|
||||
AppConfig.PREF_SNIFFING_ENABLED -> {
|
||||
AppConfig.PREF_SNIFFING_ENABLED,
|
||||
AppConfig.PREF_USE_HEV_TUNNEL -> {
|
||||
MmkvManager.encodeSettings(key, sharedPreferences.getBoolean(key, true))
|
||||
}
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/padding_spacing_dp16">
|
||||
|
||||
<TextView
|
||||
@@ -245,6 +245,13 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/title_about"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_app_id"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/title_about"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
<string name="toast_none_data">Ничего нет</string>
|
||||
<string name="toast_incorrect_protocol">Неправильный протокол</string>
|
||||
<string name="toast_decoding_failed">Невозможно декодировать</string>
|
||||
<string name="title_file_chooser">Выберите файл профиля</string>
|
||||
<string name="title_file_chooser">Выберите профиль</string>
|
||||
<string name="toast_require_file_manager">Установите файловый менеджер</string>
|
||||
<string name="server_customize_config">Изменить профиль</string>
|
||||
<string name="toast_config_file_invalid">Неправильный профиль</string>
|
||||
@@ -159,11 +159,11 @@
|
||||
<item>Пропускать</item>
|
||||
</string-array>
|
||||
|
||||
<string name="title_pref_speed_enabled">Отображение скорости</string>
|
||||
<string name="title_pref_speed_enabled">Показывать скорость</string>
|
||||
<string name="summary_pref_speed_enabled">Показывать текущую скорость в уведомлении.\nЗначок будет меняться в зависимости от использования.</string>
|
||||
|
||||
<string name="title_pref_sniffing_enabled">Анализ пакетов</string>
|
||||
<string name="summary_pref_sniffing_enabled">Использовать определение доменных имён в пакетах (по умолчанию включено)</string>
|
||||
<string name="title_pref_sniffing_enabled">Анализировать пакеты</string>
|
||||
<string name="summary_pref_sniffing_enabled">Пытаться определелять доменные имена в пакетах (по умолчанию включено)</string>
|
||||
<string name="title_pref_route_only_enabled">Домен только для маршрутизации</string>
|
||||
<string name="summary_pref_route_only_enabled">Использовать доменное имя только для маршрутизации и сохранять целевой адрес в виде IP.</string>
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
<string name="title_pref_dns_hosts">Узлы DNS (формат: домен:адрес,…)</string>
|
||||
<string name="summary_pref_dns_hosts">домен:адрес,…</string>
|
||||
|
||||
<string name="title_pref_delay_test_url">Сервис проверки времени отклика (HTTP/HTTPS)</string>
|
||||
<string name="title_pref_delay_test_url">Сервис проверки задержки (HTTP/HTTPS)</string>
|
||||
<string name="summary_pref_delay_test_url">URL</string>
|
||||
|
||||
<string name="title_pref_proxy_sharing_enabled">Разрешать подключения из LAN</string>
|
||||
@@ -207,17 +207,17 @@
|
||||
<string name="title_pref_local_dns_port">Порт локальной DNS</string>
|
||||
<string name="summary_pref_local_dns_port">Порт локальной DNS</string>
|
||||
|
||||
<string name="title_pref_confirm_remove">Подтверждение удаления профиля</string>
|
||||
<string name="summary_pref_confirm_remove">Требовать двойное подтверждение удаления профиля</string>
|
||||
<string name="title_pref_confirm_remove">Подтверждать удаление профиля</string>
|
||||
<string name="summary_pref_confirm_remove">Обязательное подтверждение удаления профиля</string>
|
||||
|
||||
<string name="title_pref_start_scan_immediate">Сканирование при запуске</string>
|
||||
<string name="title_pref_start_scan_immediate">Сканировать при запуске</string>
|
||||
<string name="summary_pref_start_scan_immediate">Начинать сканирование сразу при запуске приложения или запускать функцию сканирования камерой или из изображения через панель инструментов</string>
|
||||
|
||||
<string name="title_pref_append_http_proxy">Дополнительный HTTP-прокси</string>
|
||||
<string name="summary_pref_append_http_proxy">HTTP-прокси будет использоваться напрямую (из браузера и других поддерживающих приложений), минуя виртуальный сетевой адаптер (Android 10+)</string>
|
||||
|
||||
<string name="title_pref_double_column_display">Отображение в два столбца</string>
|
||||
<string name="summary_pref_double_column_display">Список профилей выводится в виде двух столбцов, что позволяет показать больше информации на экране. Требуется перезапуск приложения.</string>
|
||||
<string name="title_pref_double_column_display">Профили в два столбца</string>
|
||||
<string name="summary_pref_double_column_display">Список профилей отображается двумя столбцами, что позволяет показать больше информации на экране. Требуется перезапуск приложения.</string>
|
||||
|
||||
<!-- AboutActivity -->
|
||||
<string name="title_pref_feedback">Обратная связь</string>
|
||||
@@ -237,7 +237,7 @@
|
||||
<string name="title_pref_promotion">Содействие</string>
|
||||
<string name="summary_pref_promotion">Содействие, нажмите для получения подробной информации (пожертвование может быть удалено)</string>
|
||||
|
||||
<string name="title_pref_auto_update_subscription">Автоматическое обновление подписок</string>
|
||||
<string name="title_pref_auto_update_subscription">Автоматически обновлять подписки</string>
|
||||
<string name="summary_pref_auto_update_subscription">Автоматическое обновление подписок в фоновом режиме с указанным интервалом. В зависимости от устройства эта функция может работать не всегда.</string>
|
||||
<string name="title_pref_auto_update_interval">Интервал автообновления (минут, не менее 15)</string>
|
||||
|
||||
@@ -273,11 +273,11 @@
|
||||
<string name="sub_setting_pre_profile_tip">Конфигурация должна быть уникальной</string>
|
||||
<string name="title_sub_update">Обновить подписку группы</string>
|
||||
<string name="title_ping_all_server">Проверить профили группы</string>
|
||||
<string name="title_real_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>
|
||||
<string name="title_filter_config">Фильтр профилей</string>
|
||||
<string name="filter_config_all">Все группы</string>
|
||||
<string name="title_del_duplicate_config_count">Удалено дубликатов профилей: %d</string>
|
||||
<string name="title_del_config_count">Удалено профилей: %d</string>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<string name="toast_services_failure">启动服务失败</string>
|
||||
|
||||
<!--ServerActivity-->
|
||||
<string name="title_server">配置文件</string>
|
||||
<string name="title_server">配置项</string>
|
||||
<string name="menu_item_add_config">添加配置</string>
|
||||
<string name="menu_item_save_config">保存配置</string>
|
||||
<string name="menu_item_del_config">删除配置</string>
|
||||
@@ -90,10 +90,10 @@
|
||||
<string name="toast_none_data">没有数据</string>
|
||||
<string name="toast_incorrect_protocol">不正确的协议</string>
|
||||
<string name="toast_decoding_failed">解码失败</string>
|
||||
<string name="title_file_chooser">选择一个配置文件</string>
|
||||
<string name="title_file_chooser">选择一个配置</string>
|
||||
<string name="toast_require_file_manager">请安装一个文件管理器</string>
|
||||
<string name="server_customize_config">自定义配置</string>
|
||||
<string name="toast_config_file_invalid">无效的配置文件</string>
|
||||
<string name="toast_config_file_invalid">无效的配置</string>
|
||||
<string name="server_lab_content">内容</string>
|
||||
<string name="toast_none_data_clipboard">剪贴板中没有数据</string>
|
||||
<string name="toast_invalid_url">无效的网址</string>
|
||||
@@ -205,8 +205,8 @@
|
||||
<string name="title_pref_local_dns_port">本地 DNS 端口</string>
|
||||
<string name="summary_pref_local_dns_port">本地 DNS 端口</string>
|
||||
|
||||
<string name="title_pref_confirm_remove">删除配置文件确认</string>
|
||||
<string name="summary_pref_confirm_remove">删除配置文件是否需要用户二次确认</string>
|
||||
<string name="title_pref_confirm_remove">删除配置确认</string>
|
||||
<string name="summary_pref_confirm_remove">删除配置是否需要用户二次确认</string>
|
||||
|
||||
<string name="title_pref_start_scan_immediate">立即启动扫码</string>
|
||||
<string name="summary_pref_start_scan_immediate">启动时立即打开相机扫描,否则可在工具栏选择扫码或选照片</string>
|
||||
@@ -215,7 +215,7 @@
|
||||
<string name="summary_pref_append_http_proxy">浏览器 / 一些支持的应用 将直接使用 HTTP 代理, 而不经过虚拟网卡设备 (Android 10+)</string>
|
||||
|
||||
<string name="title_pref_double_column_display">启用双列显示</string>
|
||||
<string name="summary_pref_double_column_display">配置文件列表以双列显示,允许在屏幕上显示更多内容。需要重启应用生效。</string>
|
||||
<string name="summary_pref_double_column_display">配置列表以双列显示,允许在屏幕上显示更多内容。需要重启应用生效。</string>
|
||||
|
||||
<!-- AboutActivity -->
|
||||
<string name="title_pref_feedback">反馈</string>
|
||||
@@ -266,16 +266,16 @@
|
||||
<string name="sub_setting_enable">启用更新</string>
|
||||
<string name="sub_auto_update">启用自动更新</string>
|
||||
<string name="sub_allow_insecure_url">允许不安全的 HTTP 地址</string>
|
||||
<string name="sub_setting_pre_profile">前置代理配置文件别名</string>
|
||||
<string name="sub_setting_next_profile">落地代理配置文件別名</string>
|
||||
<string name="sub_setting_pre_profile_tip">请确保配置文件别名存在并唯一</string>
|
||||
<string name="sub_setting_pre_profile">前置代理配置别名</string>
|
||||
<string name="sub_setting_next_profile">落地代理配置別名</string>
|
||||
<string name="sub_setting_pre_profile_tip">请确保配置别名存在并唯一</string>
|
||||
<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>
|
||||
<string name="title_filter_config">过滤配置</string>
|
||||
<string name="filter_config_all">所有分组</string>
|
||||
<string name="title_del_duplicate_config_count">删除 %d 个重复配置</string>
|
||||
|
||||
@@ -366,7 +366,7 @@
|
||||
</string-array>
|
||||
|
||||
<string-array name="vpn_bypass_lan">
|
||||
<item>跟随配置文件</item>
|
||||
<item>跟随配置</item>
|
||||
<item>绕过</item>
|
||||
<item>不绕过</item>
|
||||
</string-array>
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
<string name="toast_services_failure">Start Services Failure</string>
|
||||
|
||||
<!--ServerActivity-->
|
||||
<string name="title_server">Configuration file</string>
|
||||
<string name="menu_item_add_config">Add config</string>
|
||||
<string name="menu_item_save_config">Save config</string>
|
||||
<string name="menu_item_del_config">Delete config</string>
|
||||
<string name="menu_item_import_config_qrcode">Import config from QRcode</string>
|
||||
<string name="menu_item_import_config_clipboard">Import config from Clipboard</string>
|
||||
<string name="menu_item_import_config_local">Import config from locally</string>
|
||||
<string name="title_server">Configuration</string>
|
||||
<string name="menu_item_add_config">Add configuration</string>
|
||||
<string name="menu_item_save_config">Save 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>
|
||||
@@ -91,16 +91,16 @@
|
||||
<string name="toast_none_data">There is nothing</string>
|
||||
<string name="toast_incorrect_protocol">Incorrect protocol</string>
|
||||
<string name="toast_decoding_failed">Decoding failed</string>
|
||||
<string name="title_file_chooser">Select a Config File</string>
|
||||
<string name="title_file_chooser">Select a configuration</string>
|
||||
<string name="toast_require_file_manager">Please install a File Manager.</string>
|
||||
<string name="server_customize_config">Customize Config</string>
|
||||
<string name="toast_config_file_invalid">Invalid Config</string>
|
||||
<string name="server_customize_config">Customize configuration</string>
|
||||
<string name="toast_config_file_invalid">Invalid configuration</string>
|
||||
<string name="server_lab_content">Content</string>
|
||||
<string name="toast_none_data_clipboard">There is no data in the clipboard</string>
|
||||
<string name="toast_invalid_url">Invalid URL</string>
|
||||
<string name="toast_insecure_url_protocol">Please do not use the insecure HTTP protocol subscription address</string>
|
||||
<string name="server_lab_need_inbound">Ensure inbounds port is consistent with the settings</string>
|
||||
<string name="toast_malformed_josn">Config malformed</string>
|
||||
<string name="toast_malformed_josn">Configuration malformed</string>
|
||||
<string name="server_lab_request_host6">Host(SNI)(Optional)</string>
|
||||
<string name="toast_action_not_allowed">Action not allowed</string>
|
||||
<string name="server_obfs_password">Obfs password</string>
|
||||
@@ -211,8 +211,8 @@
|
||||
<string name="title_pref_local_dns_port">Local DNS port</string>
|
||||
<string name="summary_pref_local_dns_port">Local DNS port</string>
|
||||
|
||||
<string name="title_pref_confirm_remove">Delete configuration file confirmation</string>
|
||||
<string name="summary_pref_confirm_remove">Whether to delete the configuration file requires a second confirmation by the user</string>
|
||||
<string name="title_pref_confirm_remove">Delete configuration confirmation</string>
|
||||
<string name="summary_pref_confirm_remove">Whether to delete the configuration requires a second confirmation by the user</string>
|
||||
|
||||
<string name="title_pref_start_scan_immediate">Start scanning immediately</string>
|
||||
<string name="summary_pref_start_scan_immediate">Open the camera to scan immediately at startup, otherwise you can choose to scan the code or select a photo in the toolbar</string>
|
||||
@@ -281,7 +281,7 @@
|
||||
<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 file</string>
|
||||
<string name="title_filter_config">Filter configuration</string>
|
||||
<string name="filter_config_all">All groups</string>
|
||||
<string name="title_del_duplicate_config_count">Delete %d duplicate configurations</string>
|
||||
<string name="title_del_config_count">Delete %d configurations</string>
|
||||
@@ -378,7 +378,7 @@
|
||||
</string-array>
|
||||
|
||||
<string-array name="vpn_bypass_lan">
|
||||
<item>Follow config</item>
|
||||
<item>Follow configuration</item>
|
||||
<item>Bypass</item>
|
||||
<item>Not Bypass</item>
|
||||
</string-array>
|
||||
|
||||
@@ -25,10 +25,10 @@
|
||||
android:summary="@string/summary_pref_prefer_ipv6"
|
||||
android:title="@string/title_pref_prefer_ipv6" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="pref_per_app_proxy"
|
||||
android:summary="@string/summary_pref_per_app_proxy"
|
||||
android:title="@string/title_pref_per_app_proxy" />
|
||||
<!-- <CheckBoxPreference-->
|
||||
<!-- android:key="pref_per_app_proxy"-->
|
||||
<!-- android:summary="@string/summary_pref_per_app_proxy"-->
|
||||
<!-- android:title="@string/title_pref_per_app_proxy" />-->
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="pref_local_dns_enabled"
|
||||
@@ -79,6 +79,7 @@
|
||||
android:title="@string/title_pref_vpn_mtu" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="true"
|
||||
android:key="pref_use_hev_tunnel"
|
||||
android:summary="@string/summary_pref_use_hev_tunnel"
|
||||
android:title="@string/title_pref_use_hev_tunnel" />
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
[versions]
|
||||
agp = "8.12.2"
|
||||
agp = "8.12.3"
|
||||
desugarJdkLibs = "2.1.5"
|
||||
gradleLicensePlugin = "0.9.8"
|
||||
kotlin = "2.2.10"
|
||||
kotlin = "2.2.20"
|
||||
coreKtx = "1.16.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.3.0"
|
||||
espressoCore = "3.7.0"
|
||||
appcompat = "1.7.1"
|
||||
material = "1.12.0"
|
||||
material = "1.13.0"
|
||||
activity = "1.10.1"
|
||||
constraintlayout = "2.2.1"
|
||||
mmkvStatic = "1.3.14"
|
||||
@@ -20,8 +20,8 @@ swiperefreshlayout = "1.1.0"
|
||||
toasty = "1.5.2"
|
||||
editorkit = "2.9.0"
|
||||
core = "3.5.3"
|
||||
workRuntimeKtx = "2.10.3"
|
||||
lifecycleViewmodelKtx = "2.9.2"
|
||||
workRuntimeKtx = "2.10.5"
|
||||
lifecycleViewmodelKtx = "2.9.4"
|
||||
multidex = "2.0.1"
|
||||
mockitoMockitoInline = "5.2.0"
|
||||
flexbox = "3.0.0"
|
||||
|
||||
Reference in New Issue
Block a user