Compare commits

...

50 Commits

Author SHA1 Message Date
2dust
bdbd0b154e up 1.8.5 2023-05-07 15:35:42 +08:00
2dust
653137ec58 bug fixes 2023-05-07 15:34:11 +08:00
2dust
39f65850be Merge pull request #2258 from Amir-yazdanmanesh/master
Fix persian translation
2023-05-07 15:19:33 +08:00
2dust
2d8db97417 Merge branch 'master' into master 2023-05-07 15:19:25 +08:00
2dust
17a6f80798 Merge pull request #2255 from hadi-norouzi/feature/share_sub
Feature/share sub
2023-05-07 15:18:28 +08:00
2dust
381fe859ff Merge pull request #2254 from hadi-norouzi/master
Fix status bar and navigation bar, remove FooterView TouchHelper
2023-05-07 15:14:04 +08:00
am.yazdanmanesh
e2bdf17b82 Fix persian translation 2023-05-05 21:54:18 +03:30
Hadi Norouzi
d20afc6801 Share subscription implemented 2023-05-04 18:03:50 +03:30
Hadi Norouzi
54046a27e6 Fix fa strings and test layout direction 2023-05-04 17:04:35 +03:30
Hadi Norouzi
36c000d18a Fix status bar and navigation bar, remove FooterView TouchHelper 2023-05-04 16:34:41 +03:30
2dust
2f3c2cf4d5 up 1.8.4 2023-04-29 20:46:28 +08:00
2dust
892358d5d8 Merge pull request #2227 from AlirezaIvaz/master
Add themed icon support
2023-04-27 09:40:08 +08:00
Alireza Ivaz
6dced903cd Add themed icon support 2023-04-26 17:05:11 +03:30
2dust
f8a98a426e Merge pull request #2213 from solokot/master
Updated Russian translation
2023-04-22 14:40:57 +08:00
solokot
011506e99f Updated Russian translation 2023-04-21 21:33:22 +03:00
2dust
966151d3fe bug fixes 2023-04-19 19:45:11 +08:00
2dust
247f4db77e up 1.8.3 2023-04-18 09:51:49 +08:00
2dust
9a661bc401 Use Google code scanner 2023-04-17 10:28:34 +08:00
2dust
f5f1b3816c Refactor createQRCode 2023-04-16 19:58:51 +08:00
2dust
3971c9badc use Exception 2023-04-16 19:56:44 +08:00
2dust
390fbf046b Merge pull request #2130 from pouriaksrvi/master
[New Feature] UX enhancement - Reversed the order of server list
2023-04-06 08:28:54 +08:00
Programmer
ec7ba59528 Bug Fix - Fixed the order of subscription import
Subscription server list should not be reversed when it is being imported. Fixed this problem.
2023-04-03 04:12:01 +03:30
Programmer
838941c6a4 Merge branch '2dust:master' into master 2023-04-03 04:00:52 +03:30
2dust
a0ec764b64 Merge pull request #2142 from solokot/master
Updated Russian translation
2023-04-02 09:32:29 +08:00
2dust
26f1f7099b up 1.8.2 2023-04-01 18:08:11 +08:00
2dust
613e9ac8bf Add permission POST_NOTIFICATIONS and READ_MEDIA_IMAGES 2023-03-31 13:48:40 +08:00
2dust
ea979f02b5 update gradle 2023-03-30 20:45:37 +08:00
2dust
383f55f809 update org.jetbrains.kotlin.android to 1.8.0 2023-03-30 13:21:36 +08:00
2dust
ce61f177dc Adjust build gradle 2023-03-30 11:54:15 +08:00
2dust
6a64157537 update kotlinx-coroutines to 1.6.4 2023-03-29 16:15:43 +08:00
2dust
de83302c8a update buildtoolsversion & targetSdkversion to 33 2023-03-29 15:54:35 +08:00
2dust
68553d3807 update kotlin version to 1.7.20 2023-03-29 15:25:54 +08:00
solokot
4cfe3394b1 Updated Russian translation 2023-03-29 09:35:28 +03:00
Programmer
ae9aa75ba0 New Feature
Fixed incorrect default order of server list as described in the issue https://github.com/2dust/v2rayNG/issues/2116
The order was problematic for users who add new servers more frequently
2023-03-26 06:27:52 +00:00
2dust
3d7ed12d4b Added the function of deleting duplicate configurations 2023-03-25 21:16:21 +08:00
2dust
f70be5bce9 routing domainMatcher is set to null 2023-03-25 18:10:29 +08:00
2dust
405667697e REALITY show false 2023-03-25 18:09:53 +08:00
2dust
3aeda7de81 Merge pull request #2122 from justlovediaodiao/master
fix a crash when adding custom config
2023-03-24 09:15:01 +08:00
yangxing
8a775d662a fix a crash when adding empty custom config 2023-03-23 22:25:07 +08:00
2dust
ec0ccbca76 up 1.8.1 2023-03-21 17:17:51 +08:00
2dust
6cdcbb0096 Add REALITY share link 2023-03-21 10:04:03 +08:00
2dust
79e3881704 hide alpn in reality 2023-03-21 09:19:21 +08:00
2dust
4cf2d429f0 Adjust string 2023-03-21 09:18:46 +08:00
2dust
fa51952bfa up 1.8.0 2023-03-10 11:17:25 +08:00
2dust
b2c4c0a67e Adjust string 2023-03-10 10:00:34 +08:00
2dust
cf2c58637b Merge pull request #2036 from yuhan6665/reality
Add reality and remove legacy xtls settings
2023-02-21 20:26:58 +08:00
yuhan6665
a2c262441e Add reality and remove legacy xtls settings
No sharing support for Reality yet
2023-02-20 21:25:33 -05:00
2dust
237be79680 up 1.7.38 2023-02-08 20:07:30 +08:00
2dust
441a64b8c6 add more fingerprint 2023-02-08 20:07:12 +08:00
2dust
2128488658 up 1.7.37 (rollback core to 1.7.2) 2023-02-03 12:04:39 +08:00
42 changed files with 613 additions and 295 deletions

View File

@@ -1,8 +1,7 @@
apply plugin: 'com.android.application' plugins {
apply plugin: 'kotlin-android' id 'com.android.application'
id 'org.jetbrains.kotlin.android'
Properties props = new Properties() }
props.load(new FileInputStream(new File('local.properties')))
android { android {
compileSdkVersion Integer.parseInt("$compileSdkVer") compileSdkVersion Integer.parseInt("$compileSdkVer")
@@ -18,25 +17,8 @@ android {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion Integer.parseInt("$targetSdkVer") targetSdkVersion Integer.parseInt("$targetSdkVer")
multiDexEnabled true multiDexEnabled true
versionCode 500 versionCode 516
versionName "1.7.36" versionName "1.8.5"
}
if (props["sign"]) {
signingConfigs {
release {
storeFile file("../key.jks")
keyAlias 'ang'
keyPassword '123456'
storePassword '123456'
}
debug {
storeFile file("../key.jks")
keyAlias 'ang'
keyPassword '123456'
storePassword '123456'
}
}
} }
buildTypes { buildTypes {
@@ -44,19 +26,12 @@ android {
minifyEnabled false minifyEnabled false
zipAlignEnabled false zipAlignEnabled false
shrinkResources false shrinkResources false
if (props["sign"]) {
signingConfig signingConfigs.release
}
ndk.abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' ndk.abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
// proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
debug { debug {
minifyEnabled false minifyEnabled false
zipAlignEnabled false zipAlignEnabled false
shrinkResources false shrinkResources false
if (props["sign"]) {
signingConfig signingConfigs.release
}
ndk.abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a' ndk.abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
} }
} }
@@ -98,6 +73,8 @@ android {
buildFeatures { buildFeatures {
viewBinding true viewBinding true
} }
namespace 'com.v2ray.ang'
testNamespace 'com.v2ray.angTest'
} }
dependencies { dependencies {
@@ -107,45 +84,36 @@ dependencies {
// Androidx // Androidx
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.appcompat:appcompat:1.4.2' implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.6.1' implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.cardview:cardview:1.0.0' implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.preference:preference-ktx:1.2.0' implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.recyclerview:recyclerview:1.3.0'
implementation 'androidx.fragment:fragment-ktx:1.5.2' implementation 'androidx.fragment:fragment-ktx:1.5.6'
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01' implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
// Androidx ktx // Androidx ktx
implementation 'androidx.activity:activity-ktx:1.5.1' implementation 'androidx.activity:activity-ktx:1.7.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
//kotlin //kotlin
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion" implementation "org.jetbrains.kotlin:kotlin-reflect:1.8.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"
implementation 'com.tencent:mmkv-static:1.2.12' implementation 'com.tencent:mmkv-static:1.2.15'
implementation 'com.google.code.gson:gson:2.8.9' implementation 'com.google.code.gson:gson:2.10.1'
implementation 'io.reactivex:rxjava:1.3.4' implementation 'io.reactivex:rxjava:1.3.8'
implementation 'io.reactivex:rxandroid:1.2.1' implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar' implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'
implementation 'me.dm7.barcodescanner:core:1.9.8'
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar' implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
implementation 'me.drakeet.support:toastcompat:1.1.0' implementation 'me.drakeet.support:toastcompat:1.1.0'
implementation 'com.blacksquircle.ui:editorkit:2.1.1' implementation 'com.blacksquircle.ui:editorkit:2.1.1'
implementation 'com.blacksquircle.ui:language-base:2.1.1' implementation 'com.blacksquircle.ui:language-base:2.1.1'
implementation 'com.blacksquircle.ui:language-json:2.1.1' implementation 'com.blacksquircle.ui:language-json:2.1.1'
} implementation 'io.github.g00fy2.quickie:quickie-bundled:1.6.0'
implementation 'com.google.zxing:core:3.5.1'
//buildscript { }
// repositories {
// google()
// mavenCentral()
// maven { url 'https://maven.google.com' }
// maven { url 'https://jitpack.io' }
// }
//}

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools">
package="com.v2ray.ang">
<supports-screens <supports-screens
android:anyDensity="true" android:anyDensity="true"
@@ -24,6 +23,9 @@
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> --> <!-- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<application <application
android:name=".AngApplication" android:name=".AngApplication"

View File

@@ -39,6 +39,7 @@ object AppConfig {
const val PREF_PER_APP_PROXY_SET = "pref_per_app_proxy_set" const val PREF_PER_APP_PROXY_SET = "pref_per_app_proxy_set"
const val PREF_BYPASS_APPS = "pref_bypass_apps" const val PREF_BYPASS_APPS = "pref_bypass_apps"
const val PREF_CONFIRM_REMOVE = "pref_confirm_remove" const val PREF_CONFIRM_REMOVE = "pref_confirm_remove"
const val PREF_START_SCAN_IMMEDIATE = "pref_start_scan_immediate"
const val HTTP_PROTOCOL: String = "http://" const val HTTP_PROTOCOL: String = "http://"
const val HTTPS_PROTOCOL: String = "https://" const val HTTPS_PROTOCOL: String = "https://"

View File

@@ -29,7 +29,7 @@ data class V2rayConfig(
const val DEFAULT_NETWORK = "tcp" const val DEFAULT_NETWORK = "tcp"
const val TLS = "tls" const val TLS = "tls"
const val XTLS = "xtls" const val REALITY = "reality"
const val HTTP = "http" const val HTTP = "http"
} }
@@ -130,7 +130,7 @@ data class V2rayConfig(
var httpSettings: HttpSettingsBean? = null, var httpSettings: HttpSettingsBean? = null,
var tlsSettings: TlsSettingsBean? = null, var tlsSettings: TlsSettingsBean? = null,
var quicSettings: QuicSettingBean? = null, var quicSettings: QuicSettingBean? = null,
var xtlsSettings: TlsSettingsBean? = null, var realitySettings: TlsSettingsBean? = null,
var grpcSettings: GrpcSettingsBean? = null, var grpcSettings: GrpcSettingsBean? = null,
val dsSettings: Any? = null, val dsSettings: Any? = null,
val sockopt: Any? = null val sockopt: Any? = null
@@ -189,7 +189,12 @@ data class V2rayConfig(
val fingerprint: String? = null, val fingerprint: String? = null,
val certificates: List<Any>? = null, val certificates: List<Any>? = null,
val disableSystemRoot: Boolean? = null, val disableSystemRoot: Boolean? = null,
val enableSessionResumption: Boolean? = null) val enableSessionResumption: Boolean? = null,
// REALITY settings
val show: Boolean = false,
var publicKey: String? = null,
var shortId: String? = null,
var spiderX: String? = null)
data class QuicSettingBean(var security: String = "none", data class QuicSettingBean(var security: String = "none",
var key: String = "", var key: String = "",
@@ -265,20 +270,24 @@ data class V2rayConfig(
return sni return sni
} }
fun populateTlsSettings(streamSecurity: String, allowInsecure: Boolean, sni: String, fingerprint: String?, alpns: String?) { fun populateTlsSettings(streamSecurity: String, allowInsecure: Boolean, sni: String, fingerprint: String?, alpns: String?,
publicKey: String?, shortId: String?, spiderX: String?) {
security = streamSecurity security = streamSecurity
val tlsSetting = TlsSettingsBean( val tlsSetting = TlsSettingsBean(
allowInsecure = allowInsecure, allowInsecure = allowInsecure,
serverName = sni, serverName = sni,
fingerprint = fingerprint, fingerprint = fingerprint,
alpn = if (alpns.isNullOrEmpty()) null else alpns.split(",").map { it.trim() }.filter { it.isNotEmpty() } alpn = if (alpns.isNullOrEmpty()) null else alpns.split(",").map { it.trim() }.filter { it.isNotEmpty() },
publicKey = publicKey,
shortId = shortId,
spiderX = spiderX
) )
if (security == TLS) { if (security == TLS) {
tlsSettings = tlsSetting tlsSettings = tlsSetting
xtlsSettings = null realitySettings = null
} else if (security == XTLS) { } else if (security == REALITY) {
tlsSettings = null tlsSettings = null
xtlsSettings = tlsSetting realitySettings = tlsSetting
} }
} }
} }
@@ -438,7 +447,7 @@ data class V2rayConfig(
var poolSize: Int = 10000) // roughly 10 times smaller than total ip pool var poolSize: Int = 10000) // roughly 10 times smaller than total ip pool
fun getProxyOutbound(): OutboundBean? { fun getProxyOutbound(): OutboundBean? {
outbounds.forEach { outbound -> outbounds?.forEach { outbound ->
EConfigType.values().forEach { EConfigType.values().forEach {
if (outbound.protocol.equals(it.name, true)) { if (outbound.protocol.equals(it.name, true)) {
return outbound return outbound

View File

@@ -4,7 +4,6 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.text.TextUtils import android.text.TextUtils
import com.google.zxing.WriterException
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.service.V2RayServiceManager import com.v2ray.ang.service.V2RayServiceManager
@@ -34,7 +33,7 @@ class TaskerReceiver : BroadcastReceiver() {
} else { } else {
Utils.stopVService(context) Utils.stopVService(context)
} }
} catch (e: WriterException) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
} }

View File

@@ -14,6 +14,7 @@ import android.text.TextUtils
import android.view.KeyEvent import android.view.KeyEvent
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.os.Build
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
@@ -107,6 +108,15 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
setupViewModel() setupViewModel()
copyAssets() copyAssets()
migrateLegacy() migrateLegacy()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
RxPermissions(this)
.request(Manifest.permission.POST_NOTIFICATIONS)
.subscribe {
if (!it)
toast(R.string.toast_permission_denied)
}
}
} }
private fun setupViewModel() { private fun setupViewModel() {
@@ -297,7 +307,14 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
.show() .show()
true true
} }
R.id.del_duplicate_config-> {
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ ->
mainViewModel.removeDuplicateServer()
}
.show()
true
}
R.id.del_invalid_config -> { R.id.del_invalid_config -> {
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ -> .setPositiveButton(android.R.string.ok) { _, _ ->
@@ -541,8 +558,13 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
* read content from uri * read content from uri
*/ */
private fun readContentFromUri(uri: Uri) { private fun readContentFromUri(uri: Uri) {
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
RxPermissions(this) RxPermissions(this)
.request(Manifest.permission.READ_EXTERNAL_STORAGE) .request(permission)
.subscribe { .subscribe {
if (it) { if (it) {
try { try {
@@ -620,11 +642,13 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
} }
} }
@Deprecated("Deprecated in Java")
override fun onBackPressed() { override fun onBackPressed() {
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) { if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.drawerLayout.closeDrawer(GravityCompat.START) binding.drawerLayout.closeDrawer(GravityCompat.START)
} else { } else {
super.onBackPressed() //super.onBackPressed()
onBackPressedDispatcher.onBackPressed()
} }
} }

View File

@@ -228,7 +228,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
BaseViewHolder(itemMainBinding.root), ItemTouchHelperViewHolder BaseViewHolder(itemMainBinding.root), ItemTouchHelperViewHolder
class FooterViewHolder(val itemFooterBinding: ItemRecyclerFooterBinding) : class FooterViewHolder(val itemFooterBinding: ItemRecyclerFooterBinding) :
BaseViewHolder(itemFooterBinding.root), ItemTouchHelperViewHolder BaseViewHolder(itemFooterBinding.root)
override fun onItemDismiss(position: Int) { override fun onItemDismiss(position: Int) {
val guid = mActivity.mainViewModel.serversCache.getOrNull(position)?.guid ?: return val guid = mActivity.mainViewModel.serversCache.getOrNull(position)?.guid ?: return

View File

@@ -3,57 +3,62 @@ package com.v2ray.ang.ui
import android.Manifest import android.Manifest
import android.app.Activity import android.app.Activity
import android.os.Bundle import android.os.Bundle
import com.google.zxing.Result
import me.dm7.barcodescanner.zxing.ZXingScannerView
import android.content.Intent import android.content.Intent
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.os.Build
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import com.google.zxing.BarcodeFormat
import com.tbruyelle.rxpermissions.RxPermissions import com.tbruyelle.rxpermissions.RxPermissions
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.QRCodeDecoder import com.v2ray.ang.util.QRCodeDecoder
import io.github.g00fy2.quickie.QRResult
import io.github.g00fy2.quickie.ScanCustomCode
import io.github.g00fy2.quickie.config.ScannerConfig
class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler { class ScannerActivity : BaseActivity(){
private var mScannerView: ZXingScannerView? = null private val scanQrCode = registerForActivityResult(ScanCustomCode(), ::handleResult)
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
public override fun onCreate(savedInstanceState: Bundle?) { public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
mScannerView = ZXingScannerView(this) // Programmatically initialize the scanner view
mScannerView?.setAutoFocus(true) if (settingsStorage?.decodeBool(AppConfig.PREF_START_SCAN_IMMEDIATE) == true) {
val formats = ArrayList<BarcodeFormat>() launchScan()
formats.add(BarcodeFormat.QR_CODE) }
mScannerView?.setFormats(formats)
setContentView(mScannerView) // Set the scanner view as the content view
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
} }
public override fun onResume() { public override fun onResume() {
super.onResume() super.onResume()
mScannerView!!.setResultHandler(this) // Register ourselves as a handler for scan results.
mScannerView!!.startCamera() // Start camera on resume
} }
public override fun onPause() { public override fun onPause() {
super.onPause() super.onPause()
mScannerView!!.stopCamera() // Stop camera on pause
} }
override fun handleResult(rawResult: Result) { private fun launchScan(){
// Do something with the result here scanQrCode.launch(
// Log.v(FragmentActivity.TAG, rawResult.text) // Prints scan results ScannerConfig.build {
// Log.v(FragmentActivity.TAG, rawResult.barcodeFormat.toString()) // Prints the scan format (qrcode, pdf417 etc.) setHapticSuccessFeedback(true) // enable (default) or disable haptic feedback when a barcode was detected
setShowTorchToggle(true) // show or hide (default) torch/flashlight toggle button
setShowCloseButton(true) // show or hide (default) close button
}
)
}
finished(rawResult.text) private fun handleResult(result: QRResult) {
if (result is QRResult.QRSuccess ) {
// If you would like to resume scanning, call this method below: finished(result.content.rawValue)
// mScannerView!!.resumeCameraPreview(this) } else {
finish()
}
} }
private fun finished(text: String) { private fun finished(text: String) {
@@ -69,19 +74,28 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
} }
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.scan_code -> {
launchScan()
true
}
R.id.select_photo -> { R.id.select_photo -> {
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
RxPermissions(this) RxPermissions(this)
.request(Manifest.permission.READ_EXTERNAL_STORAGE) .request(permission)
.subscribe { .subscribe {
if (it) { if (it) {
try { try {
showFileChooser() showFileChooser()
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
} else } else
toast(R.string.toast_permission_denied) toast(R.string.toast_permission_denied)
} }
true true
} }
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)

View File

@@ -15,6 +15,7 @@ import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.dto.ServerConfig import com.v2ray.ang.dto.ServerConfig
import com.v2ray.ang.dto.V2rayConfig import com.v2ray.ang.dto.V2rayConfig
import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_PORT import com.v2ray.ang.dto.V2rayConfig.Companion.DEFAULT_PORT
import com.v2ray.ang.dto.V2rayConfig.Companion.TLS
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.MmkvManager.ID_MAIN import com.v2ray.ang.util.MmkvManager.ID_MAIN
@@ -84,14 +85,24 @@ class ServerActivity : BaseActivity() {
private val sp_security: Spinner? by lazy { findViewById(R.id.sp_security) } private val sp_security: Spinner? by lazy { findViewById(R.id.sp_security) }
private val sp_stream_security: Spinner? by lazy { findViewById(R.id.sp_stream_security) } private val sp_stream_security: Spinner? by lazy { findViewById(R.id.sp_stream_security) }
private val sp_allow_insecure: Spinner? by lazy { findViewById(R.id.sp_allow_insecure) } private val sp_allow_insecure: Spinner? by lazy { findViewById(R.id.sp_allow_insecure) }
private val container_allow_insecure: LinearLayout? by lazy { findViewById(R.id.l5) }
private val et_sni: EditText? by lazy { findViewById(R.id.et_sni) } private val et_sni: EditText? by lazy { findViewById(R.id.et_sni) }
private val container_sni: LinearLayout? by lazy { findViewById(R.id.l2) }
private val sp_stream_fingerprint: Spinner? by lazy { findViewById(R.id.sp_stream_fingerprint) } //uTLS private val sp_stream_fingerprint: Spinner? by lazy { findViewById(R.id.sp_stream_fingerprint) } //uTLS
private val container_fingerprint: LinearLayout? by lazy { findViewById(R.id.l3) }
private val sp_network: Spinner? by lazy { findViewById(R.id.sp_network) } private val sp_network: Spinner? by lazy { findViewById(R.id.sp_network) }
private val sp_header_type: Spinner? by lazy { findViewById(R.id.sp_header_type) } private val sp_header_type: Spinner? by lazy { findViewById(R.id.sp_header_type) }
private val sp_header_type_title: TextView? by lazy { findViewById(R.id.sp_header_type_title) } private val sp_header_type_title: TextView? by lazy { findViewById(R.id.sp_header_type_title) }
private val et_request_host: EditText? by lazy { findViewById(R.id.et_request_host) } private val et_request_host: EditText? by lazy { findViewById(R.id.et_request_host) }
private val et_path: EditText? by lazy { findViewById(R.id.et_path) } private val et_path: EditText? by lazy { findViewById(R.id.et_path) }
private val sp_stream_alpn: Spinner? by lazy { findViewById(R.id.sp_stream_alpn) } //uTLS private val sp_stream_alpn: Spinner? by lazy { findViewById(R.id.sp_stream_alpn) } //uTLS
private val container_alpn: LinearLayout? by lazy { findViewById(R.id.l4) }
private val et_public_key: EditText? by lazy { findViewById(R.id.et_public_key) }
private val container_public_key: LinearLayout? by lazy { findViewById(R.id.l6) }
private val et_short_id: EditText? by lazy { findViewById(R.id.et_short_id) }
private val container_short_id: LinearLayout? by lazy { findViewById(R.id.l7) }
private val et_spider_x: EditText? by lazy { findViewById(R.id.et_spider_x) }
private val container_spider_x: LinearLayout? by lazy { findViewById(R.id.l8) }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -105,6 +116,7 @@ class ServerActivity : BaseActivity() {
EConfigType.SOCKS -> setContentView(R.layout.activity_server_socks) EConfigType.SOCKS -> setContentView(R.layout.activity_server_socks)
EConfigType.VLESS -> setContentView(R.layout.activity_server_vless) EConfigType.VLESS -> setContentView(R.layout.activity_server_vless)
EConfigType.TROJAN -> setContentView(R.layout.activity_server_trojan) EConfigType.TROJAN -> setContentView(R.layout.activity_server_trojan)
else -> setContentView(R.layout.activity_server_vmess)
} }
sp_network?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { sp_network?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
@@ -126,6 +138,38 @@ class ServerActivity : BaseActivity() {
// do nothing // do nothing
} }
} }
sp_stream_security?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
if (streamSecuritys[position].isBlank()) {
container_sni?.visibility = View.GONE
container_fingerprint?.visibility = View.GONE
container_alpn?.visibility = View.GONE
container_allow_insecure?.visibility = View.GONE
container_public_key?.visibility = View.GONE
container_short_id?.visibility = View.GONE
container_spider_x?.visibility = View.GONE
} else {
container_sni?.visibility = View.VISIBLE
container_fingerprint?.visibility = View.VISIBLE
container_alpn?.visibility = View.VISIBLE
if (streamSecuritys[position] == TLS) {
container_allow_insecure?.visibility = View.VISIBLE
container_public_key?.visibility = View.GONE
container_short_id?.visibility = View.GONE
container_spider_x?.visibility = View.GONE
} else {
container_allow_insecure?.visibility = View.GONE
container_alpn?.visibility = View.GONE
container_public_key?.visibility = View.VISIBLE
container_short_id?.visibility = View.VISIBLE
container_spider_x?.visibility = View.VISIBLE
}
}
}
override fun onNothingSelected(p0: AdapterView<*>?) {
// do nothing
}
}
if (config != null) { if (config != null) {
bindingServer(config) bindingServer(config)
} else { } else {
@@ -169,13 +213,11 @@ class ServerActivity : BaseActivity() {
val streamSecurity = Utils.arrayFind(streamSecuritys, streamSetting.security) val streamSecurity = Utils.arrayFind(streamSecuritys, streamSetting.security)
if (streamSecurity >= 0) { if (streamSecurity >= 0) {
sp_stream_security?.setSelection(streamSecurity) sp_stream_security?.setSelection(streamSecurity)
(streamSetting.tlsSettings?: streamSetting.xtlsSettings)?.let { tlsSetting -> (streamSetting.tlsSettings?: streamSetting.realitySettings)?.let { tlsSetting ->
val allowinsecure = Utils.arrayFind(allowinsecures, tlsSetting.allowInsecure.toString()) container_sni?.visibility = View.VISIBLE
if (allowinsecure >= 0) { container_fingerprint?.visibility = View.VISIBLE
sp_allow_insecure?.setSelection(allowinsecure) container_alpn?.visibility = View.VISIBLE
}
et_sni?.text = Utils.getEditable(tlsSetting.serverName) et_sni?.text = Utils.getEditable(tlsSetting.serverName)
tlsSetting.fingerprint?.let { tlsSetting.fingerprint?.let {
val utlsIndex = Utils.arrayFind(uTlsItems, tlsSetting.fingerprint) val utlsIndex = Utils.arrayFind(uTlsItems, tlsSetting.fingerprint)
sp_stream_fingerprint?.setSelection(utlsIndex) sp_stream_fingerprint?.setSelection(utlsIndex)
@@ -184,7 +226,33 @@ class ServerActivity : BaseActivity() {
val alpnIndex = Utils.arrayFind(alpns, Utils.removeWhiteSpace(tlsSetting.alpn.joinToString())!!) val alpnIndex = Utils.arrayFind(alpns, Utils.removeWhiteSpace(tlsSetting.alpn.joinToString())!!)
sp_stream_alpn?.setSelection(alpnIndex) sp_stream_alpn?.setSelection(alpnIndex)
} }
if (streamSetting.tlsSettings != null) {
container_allow_insecure?.visibility = View.VISIBLE
val allowinsecure = Utils.arrayFind(allowinsecures, tlsSetting.allowInsecure.toString())
if (allowinsecure >= 0) {
sp_allow_insecure?.setSelection(allowinsecure)
}
container_public_key?.visibility = View.GONE
container_short_id?.visibility = View.GONE
container_spider_x?.visibility = View.GONE
} else { // reality settings
container_public_key?.visibility = View.VISIBLE
et_public_key?.text = Utils.getEditable(tlsSetting.publicKey.orEmpty())
container_short_id?.visibility = View.VISIBLE
et_short_id?.text = Utils.getEditable(tlsSetting.shortId.orEmpty())
container_spider_x?.visibility = View.VISIBLE
et_spider_x?.text = Utils.getEditable(tlsSetting.spiderX.orEmpty())
container_allow_insecure?.visibility = View.GONE
}
}
if (streamSetting.tlsSettings == null && streamSetting.realitySettings == null) {
container_sni?.visibility = View.GONE
container_fingerprint?.visibility = View.GONE
container_alpn?.visibility = View.GONE
container_allow_insecure?.visibility = View.GONE
container_public_key?.visibility = View.GONE
container_short_id?.visibility = View.GONE
container_spider_x?.visibility = View.GONE
} }
} }
val network = Utils.arrayFind(networks, streamSetting.network) val network = Utils.arrayFind(networks, streamSetting.network)
@@ -304,12 +372,6 @@ class ServerActivity : BaseActivity() {
} }
} else if (config.configType == EConfigType.TROJAN) { } else if (config.configType == EConfigType.TROJAN) {
server.password = et_id.text.toString().trim() server.password = et_id.text.toString().trim()
server.flow =
if (streamSecuritys[sp_stream_security?.selectedItemPosition ?: 0] == V2rayConfig.XTLS) {
flows[sp_flow?.selectedItemPosition ?: 0]
} else {
""
}
} }
} }
@@ -321,8 +383,11 @@ class ServerActivity : BaseActivity() {
val sniField = et_sni?.text?.toString()?.trim() ?: return val sniField = et_sni?.text?.toString()?.trim() ?: return
val allowInsecureField = sp_allow_insecure?.selectedItemPosition ?: return val allowInsecureField = sp_allow_insecure?.selectedItemPosition ?: return
val streamSecurity = sp_stream_security?.selectedItemPosition ?: return val streamSecurity = sp_stream_security?.selectedItemPosition ?: return
var utlsIndex = sp_stream_fingerprint?.selectedItemPosition ?: return val utlsIndex = sp_stream_fingerprint?.selectedItemPosition ?: return
var alpnIndex = sp_stream_alpn?.selectedItemPosition ?: return val alpnIndex = sp_stream_alpn?.selectedItemPosition ?: return
val publicKey = et_public_key?.text?.toString()?.trim() ?: return
val shortId = et_short_id?.text?.toString()?.trim() ?: return
val spiderX = et_spider_x?.text?.toString()?.trim() ?: return
var sni = streamSetting.populateTransportSettings( var sni = streamSetting.populateTransportSettings(
transport = networks[network], transport = networks[network],
@@ -344,18 +409,32 @@ class ServerActivity : BaseActivity() {
allowinsecures[allowInsecureField].toBoolean() allowinsecures[allowInsecureField].toBoolean()
} }
streamSetting.populateTlsSettings(streamSecuritys[streamSecurity], allowInsecure, sni, uTlsItems[utlsIndex], alpns[alpnIndex]) streamSetting.populateTlsSettings(
streamSecurity = streamSecuritys[streamSecurity],
allowInsecure = allowInsecure,
sni = sni,
fingerprint = uTlsItems[utlsIndex],
alpns = alpns[alpnIndex],
publicKey = publicKey,
shortId = shortId,
spiderX = spiderX
)
} }
private fun transportTypes(network: String?): Array<out String> { private fun transportTypes(network: String?): Array<out String> {
return if (network == "tcp") { return when (network) {
tcpTypes "tcp" -> {
} else if (network == "kcp" || network == "quic") { tcpTypes
kcpAndQuicTypes }
} else if (network == "grpc") { "kcp", "quic" -> {
grpcModes kcpAndQuicTypes
} else { }
arrayOf("---") "grpc" -> {
grpcModes
}
else -> {
arrayOf("---")
}
} }
} }
@@ -364,7 +443,7 @@ class ServerActivity : BaseActivity() {
*/ */
private fun deleteServer(): Boolean { private fun deleteServer(): Boolean {
if (editGuid.isNotEmpty()) { if (editGuid.isNotEmpty()) {
if (editGuid != mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER)) { if (editGuid != mainStorage?.decodeString(KEY_SELECTED_SERVER)) {
if (settingsStorage?.decodeBool(AppConfig.PREF_CONFIRM_REMOVE) == true) { if (settingsStorage?.decodeBool(AppConfig.PREF_CONFIRM_REMOVE) == true) {
AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm)
.setPositiveButton(android.R.string.ok) { _, _ -> .setPositiveButton(android.R.string.ok) { _, _ ->

View File

@@ -2,20 +2,34 @@ package com.v2ray.ang.ui
import android.content.Intent import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import com.google.gson.Gson import com.google.gson.Gson
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.databinding.ItemQrcodeBinding
import com.v2ray.ang.databinding.ItemRecyclerSubSettingBinding import com.v2ray.ang.databinding.ItemRecyclerSubSettingBinding
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.QRCodeDecoder
import com.v2ray.ang.util.Utils
class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter<SubSettingRecyclerAdapter.MainViewHolder>() { class SubSettingRecyclerAdapter(val activity: SubSettingActivity) :
RecyclerView.Adapter<SubSettingRecyclerAdapter.MainViewHolder>() {
private var mActivity: SubSettingActivity = activity private var mActivity: SubSettingActivity = activity
private val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) } private val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) }
private val share_method: Array<out String> by lazy {
mActivity.resources.getStringArray(R.array.share_sub_method)
}
override fun getItemCount() = mActivity.subscriptions.size override fun getItemCount() = mActivity.subscriptions.size
override fun onBindViewHolder(holder: MainViewHolder, position: Int) { override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
@@ -31,7 +45,8 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
holder.itemView.setBackgroundColor(Color.TRANSPARENT) holder.itemView.setBackgroundColor(Color.TRANSPARENT)
holder.itemSubSettingBinding.layoutEdit.setOnClickListener { holder.itemSubSettingBinding.layoutEdit.setOnClickListener {
mActivity.startActivity(Intent(mActivity, SubEditActivity::class.java) mActivity.startActivity(
Intent(mActivity, SubEditActivity::class.java)
.putExtra("subId", subId) .putExtra("subId", subId)
) )
} }
@@ -40,11 +55,51 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
subStorage?.encode(subId, Gson().toJson(subItem)) subStorage?.encode(subId, Gson().toJson(subItem))
notifyItemChanged(position) notifyItemChanged(position)
} }
if (TextUtils.isEmpty(subItem.url)) {
holder.itemSubSettingBinding.layoutShare.visibility = View.INVISIBLE
} else {
holder.itemSubSettingBinding.layoutShare.setOnClickListener {
AlertDialog.Builder(mActivity)
.setItems(share_method.asList().toTypedArray()) { _, i ->
try {
when (i) {
0 -> {
val ivBinding =
ItemQrcodeBinding.inflate(LayoutInflater.from(mActivity))
ivBinding.ivQcode.setImageBitmap(
QRCodeDecoder.createQRCode(
subItem.url
)
)
AlertDialog.Builder(mActivity).setView(ivBinding.root).show()
}
1 -> {
Utils.setClipboard(mActivity, subItem.url)
}
else -> mActivity.toast("else")
}
} catch (e: Exception) {
e.printStackTrace()
}
}.show()
}
}
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
return MainViewHolder(ItemRecyclerSubSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false)) return MainViewHolder(
ItemRecyclerSubSettingBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
} }
class MainViewHolder(val itemSubSettingBinding: ItemRecyclerSubSettingBinding) : RecyclerView.ViewHolder(itemSubSettingBinding.root) class MainViewHolder(val itemSubSettingBinding: ItemRecyclerSubSettingBinding) :
RecyclerView.ViewHolder(itemSubSettingBinding.root)
} }

View File

@@ -11,7 +11,6 @@ import android.content.Intent
import android.text.TextUtils import android.text.TextUtils
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import com.google.zxing.WriterException
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
import com.v2ray.ang.databinding.ActivityTaskerBinding import com.v2ray.ang.databinding.ActivityTaskerBinding
@@ -65,7 +64,7 @@ class TaskerActivity : BaseActivity() {
listview?.setItemChecked(pos, true) listview?.setItemChecked(pos, true)
} }
} }
} catch (e: WriterException) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }

View File

@@ -3,7 +3,6 @@ package com.v2ray.ang.ui
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import com.google.zxing.WriterException
import com.v2ray.ang.R import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityLogcatBinding import com.v2ray.ang.databinding.ActivityLogcatBinding
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
@@ -44,7 +43,7 @@ class UrlSchemeActivity : BaseActivity() {
} }
startActivity(Intent(this, MainActivity::class.java)) startActivity(Intent(this, MainActivity::class.java))
finish() finish()
} catch (e: WriterException) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
} }
} }

View File

@@ -4,6 +4,7 @@ import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.OpenableColumns import android.provider.OpenableColumns
import android.util.Log import android.util.Log
@@ -75,7 +76,14 @@ class UserAssetActivity : BaseActivity() {
} }
private fun showFileChooser() { private fun showFileChooser() {
RxPermissions(this).request(Manifest.permission.READ_EXTERNAL_STORAGE).subscribe { val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
RxPermissions(this)
.request(permission)
.subscribe {
if (it) { if (it) {
val intent = Intent(Intent.ACTION_GET_CONTENT) val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "*/*" intent.type = "*/*"
@@ -91,7 +99,8 @@ class UserAssetActivity : BaseActivity() {
} catch (ex: android.content.ActivityNotFoundException) { } catch (ex: android.content.ActivityNotFoundException) {
toast(R.string.toast_require_file_manager) toast(R.string.toast_require_file_manager)
} }
} } else
toast(R.string.toast_permission_denied)
} }
} }

View File

@@ -145,7 +145,7 @@ object AngConfigManager {
} }
var fingerprint = streamSetting.tlsSettings?.fingerprint var fingerprint = streamSetting.tlsSettings?.fingerprint
streamSetting.populateTlsSettings(vmessBean.streamSecurity, allowInsecure, streamSetting.populateTlsSettings(vmessBean.streamSecurity, allowInsecure,
vmessBean.sni.ifBlank { sni }, fingerprint, null) vmessBean.sni.ifBlank { sni }, fingerprint, null, null, null, null)
} }
} }
val key = MmkvManager.encodeServerConfig(vmessBean.guid, config) val key = MmkvManager.encodeServerConfig(vmessBean.guid, config)
@@ -221,7 +221,8 @@ object AngConfigManager {
val fingerprint = vmessQRCode.fp ?: streamSetting.tlsSettings?.fingerprint val fingerprint = vmessQRCode.fp ?: streamSetting.tlsSettings?.fingerprint
streamSetting.populateTlsSettings(vmessQRCode.tls, allowInsecure, streamSetting.populateTlsSettings(vmessQRCode.tls, allowInsecure,
if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni, fingerprint, vmessQRCode.alpn) if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni,
fingerprint, vmessQRCode.alpn, null, null, null)
} }
} }
} else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) { } else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) {
@@ -305,11 +306,13 @@ object AngConfigManager {
queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"], queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"],
queryParam["mode"], queryParam["serviceName"]) queryParam["mode"], queryParam["serviceName"])
fingerprint = queryParam["fp"] ?: "" fingerprint = queryParam["fp"] ?: ""
config.outboundBean?.streamSettings?.populateTlsSettings(queryParam["security"] ?: TLS, allowInsecure, queryParam["sni"] ?: sni!!, fingerprint, queryParam["alpn"]) config.outboundBean?.streamSettings?.populateTlsSettings(queryParam["security"] ?: TLS,
allowInsecure, queryParam["sni"] ?: sni!!, fingerprint, queryParam["alpn"],
null, null, null)
flow = queryParam["flow"] ?: "" flow = queryParam["flow"] ?: ""
} else { } else {
config.outboundBean?.streamSettings?.populateTlsSettings(TLS, allowInsecure, "",
config.outboundBean?.streamSettings?.populateTlsSettings(TLS, allowInsecure, "", fingerprint, null) fingerprint, null, null, null, null)
} }
config.outboundBean?.settings?.servers?.get(0)?.let { server -> config.outboundBean?.settings?.servers?.get(0)?.let { server ->
@@ -339,7 +342,11 @@ object AngConfigManager {
queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"], queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"],
queryParam["mode"], queryParam["serviceName"]) queryParam["mode"], queryParam["serviceName"])
fingerprint = queryParam["fp"] ?: "" fingerprint = queryParam["fp"] ?: ""
streamSetting.populateTlsSettings(queryParam["security"] ?: "", allowInsecure, queryParam["sni"] ?: sni, fingerprint, queryParam["alpn"]) val pbk = queryParam["pbk"] ?: ""
val sid = queryParam["sid"] ?: ""
val spx = Utils.urlDecode(queryParam["spx"] ?: "")
streamSetting.populateTlsSettings(queryParam["security"] ?: "", allowInsecure,
queryParam["sni"] ?: sni, fingerprint, queryParam["alpn"], pbk, sid, spx)
} }
if (config == null){ if (config == null){
return R.string.toast_incorrect_protocol return R.string.toast_incorrect_protocol
@@ -384,7 +391,8 @@ object AngConfigManager {
queryParam["host"]?.split("|")?.get(0) ?: "", queryParam["host"]?.split("|")?.get(0) ?: "",
queryParam["path"]?.takeIf { it.trim() != "/" } ?: "", queryParam["seed"], queryParam["security"], queryParam["path"]?.takeIf { it.trim() != "/" } ?: "", queryParam["seed"], queryParam["security"],
queryParam["key"], queryParam["mode"], queryParam["serviceName"]) queryParam["key"], queryParam["mode"], queryParam["serviceName"])
streamSetting.populateTlsSettings(if (tls) TLS else "", allowInsecure, sni, fingerprint, null) streamSetting.populateTlsSettings(if (tls) TLS else "", allowInsecure, sni, fingerprint, null,
null, null, null)
true true
}.getOrElse { false } }.getOrElse { false }
} }
@@ -529,7 +537,7 @@ object AngConfigManager {
} }
dicQuery["security"] = streamSetting.security.ifEmpty { "none" } dicQuery["security"] = streamSetting.security.ifEmpty { "none" }
(streamSetting.tlsSettings?: streamSetting.xtlsSettings)?.let { tlsSetting -> (streamSetting.tlsSettings?: streamSetting.realitySettings)?.let { tlsSetting ->
if (!TextUtils.isEmpty(tlsSetting.serverName)) { if (!TextUtils.isEmpty(tlsSetting.serverName)) {
dicQuery["sni"] = tlsSetting.serverName dicQuery["sni"] = tlsSetting.serverName
} }
@@ -539,6 +547,15 @@ object AngConfigManager {
if (!TextUtils.isEmpty(tlsSetting.fingerprint)) { if (!TextUtils.isEmpty(tlsSetting.fingerprint)) {
dicQuery["fp"] = tlsSetting.fingerprint!! dicQuery["fp"] = tlsSetting.fingerprint!!
} }
if (!TextUtils.isEmpty(tlsSetting.publicKey)) {
dicQuery["pbk"] = tlsSetting.publicKey!!
}
if (!TextUtils.isEmpty(tlsSetting.shortId)) {
dicQuery["sid"] = tlsSetting.shortId!!
}
if (!TextUtils.isEmpty(tlsSetting.spiderX)) {
dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX!!)
}
} }
dicQuery["type"] = streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK } dicQuery["type"] = streamSetting.network.ifEmpty { V2rayConfig.DEFAULT_NETWORK }
@@ -653,7 +670,7 @@ object AngConfigManager {
if (TextUtils.isEmpty(conf)) { if (TextUtils.isEmpty(conf)) {
return null return null
} }
return Utils.createQRCode(conf) return QRCodeDecoder.createQRCode(conf)
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@@ -739,6 +756,7 @@ object AngConfigManager {
var count = 0 var count = 0
servers.lines() servers.lines()
.reversed()
.forEach { .forEach {
val resId = importConfig(it, subid, removedSelectedServer) val resId = importConfig(it, subid, removedSelectedServer)
if (resId == 0) { if (resId == 0) {

View File

@@ -46,7 +46,7 @@ object MmkvManager {
serverStorage?.encode(key, Gson().toJson(config)) serverStorage?.encode(key, Gson().toJson(config))
val serverList = decodeServerList() val serverList = decodeServerList()
if (!serverList.contains(key)) { if (!serverList.contains(key)) {
serverList.add(key) serverList.add(0, key)
mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList)) mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
if (mainStorage?.decodeString(KEY_SELECTED_SERVER).isNullOrBlank()) { if (mainStorage?.decodeString(KEY_SELECTED_SERVER).isNullOrBlank()) {
mainStorage?.encode(KEY_SELECTED_SERVER, key) mainStorage?.encode(KEY_SELECTED_SERVER, key)

View File

@@ -5,6 +5,7 @@ import android.graphics.BitmapFactory
import com.google.zxing.* import com.google.zxing.*
import com.google.zxing.common.GlobalHistogramBinarizer import com.google.zxing.common.GlobalHistogramBinarizer
import com.google.zxing.common.HybridBinarizer import com.google.zxing.common.HybridBinarizer
import com.google.zxing.qrcode.QRCodeWriter
import java.util.* import java.util.*
/** /**
@@ -13,6 +14,36 @@ import java.util.*
object QRCodeDecoder { object QRCodeDecoder {
val HINTS: MutableMap<DecodeHintType, Any?> = EnumMap(DecodeHintType::class.java) val HINTS: MutableMap<DecodeHintType, Any?> = EnumMap(DecodeHintType::class.java)
/**
* create qrcode using zxing
*/
fun createQRCode(text: String, size: Int = 800): Bitmap? {
try {
val hints = HashMap<EncodeHintType, String>()
hints[EncodeHintType.CHARACTER_SET] = "utf-8"
val bitMatrix = QRCodeWriter().encode(text,
BarcodeFormat.QR_CODE, size, size, hints)
val pixels = IntArray(size * size)
for (y in 0 until size) {
for (x in 0 until size) {
if (bitMatrix.get(x, y)) {
pixels[y * size + x] = 0xff000000.toInt()
} else {
pixels[y * size + x] = 0xffffffff.toInt()
}
}
}
val bitmap = Bitmap.createBitmap(size, size,
Bitmap.Config.ARGB_8888)
bitmap.setPixels(pixels, 0, size, 0, 0, size, size)
return bitmap
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
/** /**
* 同步解析本地图片二维码。该方法是耗时操作,请在子线程中调用。 * 同步解析本地图片二维码。该方法是耗时操作,请在子线程中调用。
* *

View File

@@ -4,13 +4,7 @@ import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.text.Editable import android.text.Editable
import android.util.Base64 import android.util.Base64
import com.google.zxing.WriterException
import android.graphics.Bitmap
import com.google.zxing.BarcodeFormat
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.EncodeHintType
import java.util.* import java.util.*
import kotlin.collections.HashMap
import android.content.ClipData import android.content.ClipData
import android.content.Intent import android.content.Intent
import android.content.res.Configuration.UI_MODE_NIGHT_MASK import android.content.res.Configuration.UI_MODE_NIGHT_MASK
@@ -171,36 +165,6 @@ object Utils {
return ret return ret
} }
/**
* create qrcode using zxing
*/
fun createQRCode(text: String, size: Int = 800): Bitmap? {
try {
val hints = HashMap<EncodeHintType, String>()
hints[EncodeHintType.CHARACTER_SET] = "utf-8"
val bitMatrix = QRCodeWriter().encode(text,
BarcodeFormat.QR_CODE, size, size, hints)
val pixels = IntArray(size * size)
for (y in 0 until size) {
for (x in 0 until size) {
if (bitMatrix.get(x, y)) {
pixels[y * size + x] = 0xff000000.toInt()
} else {
pixels[y * size + x] = 0xffffffff.toInt()
}
}
}
val bitmap = Bitmap.createBitmap(size, size,
Bitmap.Config.ARGB_8888)
bitmap.setPixels(pixels, 0, size, 0, 0, size, size)
return bitmap
} catch (e: WriterException) {
e.printStackTrace()
return null
}
}
/** /**
* is ip address * is ip address
*/ */
@@ -274,7 +238,7 @@ object Utils {
if (value != null && Patterns.WEB_URL.matcher(value).matches() || URLUtil.isValidUrl(value)) { if (value != null && Patterns.WEB_URL.matcher(value).matches() || URLUtil.isValidUrl(value)) {
return true return true
} }
} catch (e: WriterException) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return false return false
} }

View File

@@ -151,7 +151,7 @@ object V2rayConfigUtil {
v2rayConfig.routing.domainStrategy = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY) v2rayConfig.routing.domainStrategy = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY)
?: "IPIfNonMatch" ?: "IPIfNonMatch"
v2rayConfig.routing.domainMatcher = "mph" // v2rayConfig.routing.domainMatcher = "mph"
val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: ERoutingMode.GLOBAL_PROXY.value val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: ERoutingMode.GLOBAL_PROXY.value
// Hardcode googleapis.cn // Hardcode googleapis.cn

View File

@@ -74,8 +74,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java) config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java)
val key = MmkvManager.encodeServerConfig("", config) val key = MmkvManager.encodeServerConfig("", config)
serverRawStorage?.encode(key, server) serverRawStorage?.encode(key, server)
serverList.add(key) serverList.add(0, key)
serversCache.add(ServersCache(key,config)) serversCache.add(0, ServersCache(key,config))
} }
fun swapServer(fromPosition: Int, toPosition: Int) { fun swapServer(fromPosition: Int, toPosition: Int) {
@@ -201,6 +201,27 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
return -1 return -1
} }
fun removeDuplicateServer() {
val deleteServer = mutableListOf<String>()
serversCache.forEachIndexed { index, it ->
val outbound = it.config.getProxyOutbound()
serversCache.forEachIndexed { index2, it2 ->
if(index2 > index){
val outbound2 = it2.config.getProxyOutbound()
if( outbound == outbound2 && !deleteServer.contains(it2.guid))
{
deleteServer.add(it2.guid)
}
}
}
}
for(it in deleteServer){
MmkvManager.removeServer(it)
}
reloadServerList()
getApplication<AngApplication>().toast(getApplication<AngApplication>().getString(R.string.title_del_duplicate_config_count, deleteServer.count()))
}
private val mMsgReceiver = object : BroadcastReceiver() { private val mMsgReceiver = object : BroadcastReceiver() {
override fun onReceive(ctx: Context?, intent: Intent?) { override fun onReceive(ctx: Context?, intent: Intent?) {
when (intent?.getIntExtra("key", 0)) { when (intent?.getIntExtra("key", 0)) {

View File

@@ -50,7 +50,8 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
AppConfig.PREF_PREFER_IPV6, AppConfig.PREF_PREFER_IPV6,
AppConfig.PREF_PER_APP_PROXY, AppConfig.PREF_PER_APP_PROXY,
AppConfig.PREF_BYPASS_APPS, AppConfig.PREF_BYPASS_APPS,
AppConfig.PREF_CONFIRM_REMOVE, -> { AppConfig.PREF_CONFIRM_REMOVE,
AppConfig.PREF_START_SCAN_IMMEDIATE, -> {
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false)) settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
} }
AppConfig.PREF_SNIFFING_ENABLED -> { AppConfig.PREF_SNIFFING_ENABLED -> {

View File

@@ -50,7 +50,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/connection_test_height" android:layout_height="@dimen/connection_test_height"
android:background="?attr/colorPrimary" android:background="?attr/colorPrimary"
android:gravity="center|left" android:gravity="center|start"
android:nextFocusRight="@+id/fab" android:nextFocusRight="@+id/fab"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"

View File

@@ -98,24 +98,6 @@
</LinearLayout> </LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_margin_top_height"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_flow" />
<Spinner
android:id="@+id/sp_flow"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:entries="@array/flows" />
</LinearLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@@ -16,7 +16,8 @@
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent"
tools:listitem="@layout/item_recycler_sub_setting"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout> </RelativeLayout>

View File

@@ -73,12 +73,13 @@
android:gravity="center" android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:padding="@dimen/layout_margin_spacing" android:padding="@dimen/layout_margin_spacing"
android:visibility="invisible"> >
<ImageView <ImageView
android:layout_width="@dimen/png_height" android:layout_width="@dimen/png_height"
android:layout_height="@dimen/png_height" android:layout_height="@dimen/png_height"
android:src="@drawable/ic_share_black_24dp" /> android:src="@drawable/ic_share_black_24dp"
app:tint="?attr/colorMainText"/>
</LinearLayout> </LinearLayout>

View File

@@ -104,5 +104,64 @@
android:layout_height="@dimen/edit_height" android:layout_height="@dimen/edit_height"
android:entries="@array/allowinsecures" /> android:entries="@array/allowinsecures" />
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/l6"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_horizontal_margin"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_public_key" />
<EditText
android:id="@+id/et_public_key"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="text"
android:nextFocusDown="@+id/sp_stream_fingerprint" />
</LinearLayout>
<LinearLayout
android:id="@+id/l7"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_horizontal_margin"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_short_id" />
<EditText
android:id="@+id/et_short_id"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="text"
android:nextFocusDown="@+id/sp_stream_fingerprint" />
</LinearLayout>
<LinearLayout
android:id="@+id/l8"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_horizontal_margin"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/server_lab_spider_x" />
<EditText
android:id="@+id/et_spider_x"
android:layout_width="match_parent"
android:layout_height="@dimen/edit_height"
android:inputType="text"
android:nextFocusDown="@+id/sp_stream_fingerprint" />
</LinearLayout>
</LinearLayout> </LinearLayout>

View File

@@ -73,6 +73,11 @@
android:icon="@drawable/ic_delete_white_24dp" android:icon="@drawable/ic_delete_white_24dp"
android:title="@string/title_del_all_config" android:title="@string/title_del_all_config"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/del_duplicate_config"
android:icon="@drawable/ic_delete_white_24dp"
android:title="@string/title_del_duplicate_config"
app:showAsAction="never" />
<item <item
android:id="@+id/del_invalid_config" android:id="@+id/del_invalid_config"
android:icon="@drawable/ic_delete_white_24dp" android:icon="@drawable/ic_delete_white_24dp"

View File

@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/scan_code"
android:icon="@drawable/ic_scan_black_24dp"
android:title=""
app:showAsAction="always" />
<item <item
android:id="@+id/select_photo" android:id="@+id/select_photo"
android:icon="@drawable/ic_image_photo" android:icon="@drawable/ic_image_photo"

View File

@@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/> <background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/> <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_foreground" />
</adaptive-icon> </adaptive-icon>

View File

@@ -48,9 +48,7 @@
<string name="server_lab_mode_type">حالت gRPC</string> <string name="server_lab_mode_type">حالت gRPC</string>
<string name="server_lab_request_host">درخواست میزبان (میزبان/میزبان ws/ میزبان h2)/امنیت QUIC</string> <string name="server_lab_request_host">درخواست میزبان (میزبان/میزبان ws/ میزبان h2)/امنیت QUIC</string>
<string name="server_lab_path">مسیر (مسیر ws/ مسیر h2) کلید QUIC/دانه kcp/نام‌خدمات gRPC</string> <string name="server_lab_path">مسیر (مسیر ws/ مسیر h2) کلید QUIC/دانه kcp/نام‌خدمات gRPC</string>
<string name="server_lab_stream_security">tls</string> <string name="server_lab_stream_security">TLS</string>
<string name="server_lab_stream_fingerprint" translatable="false">uTLS</string>
<string name="server_lab_stream_alpn" translatable="false">alpn</string>
<string name="server_lab_allow_insecure">allowInsecure</string> <string name="server_lab_allow_insecure">allowInsecure</string>
<string name="server_lab_sni">SNI</string> <string name="server_lab_sni">SNI</string>
<string name="server_lab_address3">نشانی</string> <string name="server_lab_address3">نشانی</string>
@@ -149,24 +147,29 @@
<string name="title_pref_confirm_remove">تایید حذف پرونده پیکربندی</string> <string name="title_pref_confirm_remove">تایید حذف پرونده پیکربندی</string>
<string name="summary_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>
<string name="title_pref_feedback">بازخورد</string> <string name="title_pref_feedback">بازخورد</string>
<string name="summary_pref_feedback">بهبودهای بازخورد یا اشکالات در گیت‌هاب</string> <string name="summary_pref_feedback">بهبودهای بازخورد یا اشکالات در گیت‌هاب</string>
<string name="summary_pref_tg_group">عضویت در گروه تلگرام</string> <string name="summary_pref_tg_group">عضویت در گروه تلگرام</string>
<string name="toast_tg_app_not_found">برنامه تلگرام پیدا نشد</string> <string name="toast_tg_app_not_found">برنامه تلگرام پیدا نشد</string>
<string name="title_pref_promotion">تبلیغات،</string> <string name="title_pref_promotion">تبلیغات</string>
<string name="summary_pref_promotion">تبلیغات، برای جزئیات بیشتر کلیک کنید (کمک مالی کنید تا حذف شود)</string> <string name="summary_pref_promotion">تبلیغات، برای جزئیات بیشتر کلیک کنید (کمک مالی کنید تا حذف شود)</string>
<string name="title_core_loglevel">سطح گزارشات</string> <string name="title_core_loglevel">سطح گزارشات</string>
<string name="title_mode">حالت</string> <string name="title_mode">حالت</string>
<string name="title_mode_help">برای راهنمایی بیشتر روی این متن، کلیک کنید</string> <string name="title_mode_help">برای راهنمایی بیشتر روی این متن، کلیک کنید</string>
<string name="title_language">زبان</string> <string name="title_language">زبان</string>
<string name="title_ui_settings">تنظیمات رابط کاربری</string>
<string name="title_logcat">گزارشات</string> <string name="title_logcat">گزارشات</string>
<string name="logcat_copy">کپی</string> <string name="logcat_copy">کپی</string>
<string name="logcat_clear">پاک کردن</string> <string name="logcat_clear">پاک کردن</string>
<string name="title_service_restart">راه‌اندازی مجدد خدمات</string> <string name="title_service_restart">راه‌اندازی مجدد خدمات</string>
<string name="title_del_all_config">حذف تمام پیکربندی</string> <string name="title_del_all_config">حذف تمام پیکربندی</string>
<string name="title_del_duplicate_config">حذف کانفیگ های تکراری</string>
<string name="title_del_invalid_config">تنظیمات نامعتبر را حذف کنید (ابتدا آزمایش کنید)</string> <string name="title_del_invalid_config">تنظیمات نامعتبر را حذف کنید (ابتدا آزمایش کنید)</string>
<string name="title_export_all">خروجی گرفتن پیکربندی‌های غیرسفارشی در کلیپ‌بورد</string> <string name="title_export_all">خروجی گرفتن پیکربندی‌های غیرسفارشی در کلیپ‌بورد</string>
<string name="title_sub_setting">تنظیمات گروه‌ی اشتراک</string> <string name="title_sub_setting">تنظیمات گروه‌ی اشتراک</string>
@@ -178,8 +181,9 @@
<string name="title_real_ping_all_server">تاخیر واقعی همه پیکربندی</string> <string name="title_real_ping_all_server">تاخیر واقعی همه پیکربندی</string>
<string name="title_user_asset_setting">پرونده‌های دارایی جغرافیا</string> <string name="title_user_asset_setting">پرونده‌های دارایی جغرافیا</string>
<string name="title_sort_by_test_results">مرتب‌سازی بر اساس نتایج آزمایش</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="filter_config_all">همه گروه‌های اشتراک</string>
<string name="title_del_duplicate_config_count">حذف %d کانفیگ تکراری</string>
<string name="tasker_start_service">شروع خدمات</string> <string name="tasker_start_service">شروع خدمات</string>
<string name="tasker_setting_confirm">تایید</string> <string name="tasker_setting_confirm">تایید</string>
@@ -207,6 +211,11 @@
<item>خروجی گرفتن پیکربندی کامل در کلیپ‌بورد</item> <item>خروجی گرفتن پیکربندی کامل در کلیپ‌بورد</item>
</string-array> </string-array>
<string-array name="share_sub_method">
<item>QRcode</item>
<item>خروجی گرفتن در کلیپ‌بورد</item>
</string-array>
<string-array name="routing_tag"> <string-array name="routing_tag">
<item>نشانی اینترنتی یا آی‌پی پروکسی</item> <item>نشانی اینترنتی یا آی‌پی پروکسی</item>
<item>نشانی اینترنتی یا آی‌پی مستقیم</item> <item>نشانی اینترنتی یا آی‌پی مستقیم</item>

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppThemeDayNight" parent="Theme.AppCompat.DayNight.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="colorMainBg">@color/colorBg</item>
<item name="colorMainText">@color/colorText</item>
<item name="android:statusBarColor">@color/colorPrimary</item>
<item name="android:navigationBarColor">@color/colorPrimary</item>
</style>
</resources>

View File

@@ -49,8 +49,8 @@
<string name="server_lab_request_host">Запрос узла (WS/H2) / Шифрование QUIC</string> <string name="server_lab_request_host">Запрос узла (WS/H2) / Шифрование QUIC</string>
<string name="server_lab_path">Путь (WS/H2) / Ключ QUIC / Сид KCP / Сервис gRPC</string> <string name="server_lab_path">Путь (WS/H2) / Ключ QUIC / Сид KCP / Сервис gRPC</string>
<string name="server_lab_stream_security">TLS</string> <string name="server_lab_stream_security">TLS</string>
<string name="server_lab_stream_fingerprint">uTLS</string> <string name="server_lab_stream_fingerprint" translatable="false">Fingerprint</string>
<string name="server_lab_stream_alpn" translatable="false">alpn</string> <string name="server_lab_stream_alpn" translatable="false">Alpn</string>
<string name="server_lab_allow_insecure">Разрешать небезопасные</string> <string name="server_lab_allow_insecure">Разрешать небезопасные</string>
<string name="server_lab_sni">SNI</string> <string name="server_lab_sni">SNI</string>
<string name="server_lab_address3">Адрес</string> <string name="server_lab_address3">Адрес</string>
@@ -61,6 +61,9 @@
<string name="server_lab_security4">Пользователь (необязательно)</string> <string name="server_lab_security4">Пользователь (необязательно)</string>
<string name="server_lab_encryption">Шифрование</string> <string name="server_lab_encryption">Шифрование</string>
<string name="server_lab_flow">Поток</string> <string name="server_lab_flow">Поток</string>
<string name="server_lab_public_key" translatable="false">PublicKey</string>
<string name="server_lab_short_id" translatable="false">ShortId</string>
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
<string name="toast_success">Успешно</string> <string name="toast_success">Успешно</string>
<string name="toast_failure">Ошибка</string> <string name="toast_failure">Ошибка</string>
<string name="toast_none_data">Ничего нет</string> <string name="toast_none_data">Ничего нет</string>
@@ -149,6 +152,9 @@
<string name="title_pref_confirm_remove">Подтверждение удаления профиля</string> <string name="title_pref_confirm_remove">Подтверждение удаления профиля</string>
<string name="summary_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>
<string name="title_pref_feedback">Обратная связь</string> <string name="title_pref_feedback">Обратная связь</string>
<string name="summary_pref_feedback">Предложить улучшение или сообщить об ошибке на GitHub</string> <string name="summary_pref_feedback">Предложить улучшение или сообщить об ошибке на GitHub</string>
<string name="summary_pref_tg_group">Присоединиться к группе в Telegram</string> <string name="summary_pref_tg_group">Присоединиться к группе в Telegram</string>
@@ -161,12 +167,14 @@
<string name="title_mode">Режим</string> <string name="title_mode">Режим</string>
<string name="title_mode_help">Нажмите для получения дополнительной информации</string> <string name="title_mode_help">Нажмите для получения дополнительной информации</string>
<string name="title_language">Язык</string> <string name="title_language">Язык</string>
<string name="title_ui_settings">Настройки интерфейса</string>
<string name="title_logcat">Системный журнал</string> <string name="title_logcat">Системный журнал</string>
<string name="logcat_copy">Копировать</string> <string name="logcat_copy">Копировать</string>
<string name="logcat_clear">Очистить</string> <string name="logcat_clear">Очистить</string>
<string name="title_service_restart">Перезапуск службы</string> <string name="title_service_restart">Перезапуск службы</string>
<string name="title_del_all_config">Удалить все профили</string> <string name="title_del_all_config">Удалить все профили</string>
<string name="title_del_duplicate_config">Удалить дубликаты профилей</string>
<string name="title_del_invalid_config">Удалить сбойные профили (после проверки)</string> <string name="title_del_invalid_config">Удалить сбойные профили (после проверки)</string>
<string name="title_export_all">Экспорт всех профилей в буфер обмена</string> <string name="title_export_all">Экспорт всех профилей в буфер обмена</string>
<string name="title_sub_setting">Группы</string> <string name="title_sub_setting">Группы</string>
@@ -180,6 +188,7 @@
<string name="title_sort_by_test_results">Сортировка по результатам теста</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="filter_config_all">Все группы</string>
<string name="title_del_duplicate_config_count">Удалено дубликатов профилей: %d</string>
<string name="tasker_start_service">Запуск службы</string> <string name="tasker_start_service">Запуск службы</string>
<string name="tasker_setting_confirm">Подтвердить</string> <string name="tasker_setting_confirm">Подтвердить</string>
@@ -207,6 +216,11 @@
<item>Экспорт всего профиля в буфер обмена</item> <item>Экспорт всего профиля в буфер обмена</item>
</string-array> </string-array>
<string-array name="share_sub_method">
<item>QR-код</item>
<item>Экспорт в буфер обмена</item>
</string-array>
<string-array name="routing_tag"> <string-array name="routing_tag">
<item>Проксируемые</item> <item>Проксируемые</item>
<item>Прямые</item> <item>Прямые</item>

View File

@@ -146,6 +146,9 @@
<string name="title_pref_confirm_remove">Hiển thị thông báo xác nhận xoá cấu hình</string> <string name="title_pref_confirm_remove">Hiển thị thông báo xác nhận xoá cấu hình</string>
<string name="summary_pref_confirm_remove">Hiển thị thông báo xác nhận xoá cấu hình khi bạn xoá một cấu hình.</string> <string name="summary_pref_confirm_remove">Hiển thị thông báo xác nhận xoá cấu hình khi bạn xoá một cấu hình.</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>
<string name="title_pref_feedback">Phản hồi lỗi</string> <string name="title_pref_feedback">Phản hồi lỗi</string>
<string name="summary_pref_feedback">Phản hồi cải tiến hoặc lỗi lên GitHub</string> <string name="summary_pref_feedback">Phản hồi cải tiến hoặc lỗi lên GitHub</string>
<string name="summary_pref_tg_group">Tham gia nhóm Telegram</string> <string name="summary_pref_tg_group">Tham gia nhóm Telegram</string>
@@ -158,12 +161,14 @@
<string name="title_mode">Chế độ kết nối</string> <string name="title_mode">Chế độ kết nối</string>
<string name="title_mode_help">Nhấn vào đây nếu bạn cần trợ giúp!</string> <string name="title_mode_help">Nhấn vào đây nếu bạn cần trợ giúp!</string>
<string name="title_language">Ngôn ngữ</string> <string name="title_language">Ngôn ngữ</string>
<string name="title_ui_settings">UI settings</string>
<string name="title_logcat">Nhật ký hoạt động</string> <string name="title_logcat">Nhật ký hoạt động</string>
<string name="logcat_copy">Sao chép nhật ký</string> <string name="logcat_copy">Sao chép nhật ký</string>
<string name="logcat_clear">Xoá nhật ký</string> <string name="logcat_clear">Xoá nhật ký</string>
<string name="title_service_restart">Kết nối lại v2rayNG</string> <string name="title_service_restart">Kết nối lại v2rayNG</string>
<string name="title_del_all_config">Xoá tất cả cấu hình</string> <string name="title_del_all_config">Xoá tất cả cấu hình</string>
<string name="title_del_duplicate_config">Delete duplicate config</string>
<string name="title_del_invalid_config">Xoá cấu hình lỗi (Kiểm tra trước)</string> <string name="title_del_invalid_config">Xoá cấu hình lỗi (Kiểm tra trước)</string>
<string name="title_export_all">Xuất và sao chép tất cả cấu hình</string> <string name="title_export_all">Xuất và sao chép tất cả cấu hình</string>
<string name="title_sub_setting">Các gói đăng ký</string> <string name="title_sub_setting">Các gói đăng ký</string>
@@ -177,6 +182,7 @@
<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_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> <string name="title_filter_config">Lọc cấu hình theo các gói đăng ký</string>
<string name="filter_config_all">Hiển thị tất cả các gói đăng ký</string> <string name="filter_config_all">Hiển thị tất cả các gói đăng ký</string>
<string name="title_del_duplicate_config_count">Delete %d duplicate configurations</string>
<string name="tasker_start_service">Bắt đầu v2rayNG</string> <string name="tasker_start_service">Bắt đầu v2rayNG</string>
<string name="tasker_setting_confirm">Xác nhận</string> <string name="tasker_setting_confirm">Xác nhận</string>
@@ -204,6 +210,11 @@
<item>Sao chép thành cấu hình tùy chỉnh</item> <item>Sao chép thành cấu hình tùy chỉnh</item>
</string-array> </string-array>
<string-array name="share_sub_method">
<item>Xuất ra mã QR (Chụp màn hình để lưu)</item>
<item>Sao chép cấu hình này</item>
</string-array>
<string-array name="routing_tag"> <string-array name="routing_tag">
<item>Proxy URL hoặc IP</item> <item>Proxy URL hoặc IP</item>
<item>Direct URL hoặc IP</item> <item>Direct URL hoặc IP</item>

View File

@@ -44,10 +44,10 @@
<string name="server_lab_network">传输协议(network)</string> <string name="server_lab_network">传输协议(network)</string>
<string name="server_lab_more_function">底层传输方式(transport)</string> <string name="server_lab_more_function">底层传输方式(transport)</string>
<string name="server_lab_head_type">伪装类型(type)</string> <string name="server_lab_head_type">伪装类型(type)</string>
<string name="server_lab_mode_type">gRPC 传输模式 (mode)</string> <string name="server_lab_mode_type">gRPC 传输模式(mode)</string>
<string name="server_lab_request_host">伪装域名(host)(host/ws host/h2 host)/QUIC 加密方式</string> <string name="server_lab_request_host">伪装域名(host)(host/ws host/h2 host)/QUIC 加密方式</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC 加密密钥/kcp seed/gRPC serviceName</string> <string name="server_lab_path">path(ws path/h2 path)/QUIC 加密密钥/kcp seed/gRPC serviceName</string>
<string name="server_lab_stream_security">传输层安全(tls)</string> <string name="server_lab_stream_security">传输层安全(TLS)</string>
<string name="server_lab_allow_insecure">跳过证书验证(allowInsecure)</string> <string name="server_lab_allow_insecure">跳过证书验证(allowInsecure)</string>
<string name="server_lab_sni">SNI</string> <string name="server_lab_sni">SNI</string>
<string name="server_lab_address3">服务器地址</string> <string name="server_lab_address3">服务器地址</string>
@@ -56,7 +56,7 @@
<string name="server_lab_security3">加密方式</string> <string name="server_lab_security3">加密方式</string>
<string name="server_lab_id4">密码(可选)</string> <string name="server_lab_id4">密码(可选)</string>
<string name="server_lab_security4">用户名(可选)</string> <string name="server_lab_security4">用户名(可选)</string>
<string name="server_lab_encryption">加密(encryption)</string> <string name="server_lab_encryption">加密方式(encryption)</string>
<string name="server_lab_flow">流控(flow)</string> <string name="server_lab_flow">流控(flow)</string>
<string name="toast_success">成功</string> <string name="toast_success">成功</string>
<string name="toast_failure">失败</string> <string name="toast_failure">失败</string>
@@ -146,6 +146,9 @@
<string name="title_pref_confirm_remove">删除配置文件确认</string> <string name="title_pref_confirm_remove">删除配置文件确认</string>
<string name="summary_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>
<string name="title_pref_feedback">反馈</string> <string name="title_pref_feedback">反馈</string>
<string name="summary_pref_feedback">反馈改进或漏洞至 GitHub</string> <string name="summary_pref_feedback">反馈改进或漏洞至 GitHub</string>
<string name="summary_pref_tg_group">加入Telegram Group</string> <string name="summary_pref_tg_group">加入Telegram Group</string>
@@ -158,12 +161,14 @@
<string name="title_mode">模式</string> <string name="title_mode">模式</string>
<string name="title_mode_help">点此查看更多帮助</string> <string name="title_mode_help">点此查看更多帮助</string>
<string name="title_language">语言</string> <string name="title_language">语言</string>
<string name="title_ui_settings">用户界面设置</string>
<string name="title_logcat">Logcat</string> <string name="title_logcat">Logcat</string>
<string name="logcat_copy">复制</string> <string name="logcat_copy">复制</string>
<string name="logcat_clear">清除</string> <string name="logcat_clear">清除</string>
<string name="title_service_restart">服务重启</string> <string name="title_service_restart">服务重启</string>
<string name="title_del_all_config">删除全部配置</string> <string name="title_del_all_config">删除全部配置</string>
<string name="title_del_duplicate_config">删除重复配置</string>
<string name="title_del_invalid_config">删除无效配置(先测试)</string> <string name="title_del_invalid_config">删除无效配置(先测试)</string>
<string name="title_export_all">导出全部(非自定义)配置至剪贴板</string> <string name="title_export_all">导出全部(非自定义)配置至剪贴板</string>
<string name="title_sub_setting">订阅分组设置</string> <string name="title_sub_setting">订阅分组设置</string>
@@ -177,6 +182,7 @@
<string name="title_sort_by_test_results">按测试结果排序</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="filter_config_all">所有订阅分组</string>
<string name="title_del_duplicate_config_count">删除 %d 个重复配置</string>
<string name="tasker_start_service">启动服务</string> <string name="tasker_start_service">启动服务</string>
<string name="tasker_setting_confirm">确定</string> <string name="tasker_setting_confirm">确定</string>
@@ -203,6 +209,12 @@
<item>导出至剪贴板</item> <item>导出至剪贴板</item>
<item>导出完整配置至剪贴板</item> <item>导出完整配置至剪贴板</item>
</string-array> </string-array>
<string-array name="share_sub_method">
<item>二维码</item>
<item>导出至剪贴板</item>
</string-array>
share_method share_method
<string-array name="routing_tag"> <string-array name="routing_tag">
<item>代理的网址或IP</item> <item>代理的网址或IP</item>

View File

@@ -47,7 +47,7 @@
<string name="server_lab_mode_type">gRPC 傳輸模式 (mode)</string> <string name="server_lab_mode_type">gRPC 傳輸模式 (mode)</string>
<string name="server_lab_request_host">要求主機 (host)(host/ws host/h2 host)/QUIC 加密方式</string> <string name="server_lab_request_host">要求主機 (host)(host/ws host/h2 host)/QUIC 加密方式</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC 加密金鑰/kcp seed/gRPC serviceName</string> <string name="server_lab_path">path(ws path/h2 path)/QUIC 加密金鑰/kcp seed/gRPC serviceName</string>
<string name="server_lab_stream_security">傳輸層安全 (tls)</string> <string name="server_lab_stream_security">傳輸層安全 (TLS)</string>
<string name="server_lab_allow_insecure">跳過憑證驗證 (allowInsecure)</string> <string name="server_lab_allow_insecure">跳過憑證驗證 (allowInsecure)</string>
<string name="server_lab_sni">SNI</string> <string name="server_lab_sni">SNI</string>
<string name="server_lab_address3">伺服器位址</string> <string name="server_lab_address3">伺服器位址</string>
@@ -146,6 +146,9 @@
<string name="title_pref_confirm_remove">刪除配置文件確認</string> <string name="title_pref_confirm_remove">刪除配置文件確認</string>
<string name="summary_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>
<string name="title_pref_feedback">意見回饋</string> <string name="title_pref_feedback">意見回饋</string>
<string name="summary_pref_feedback">前往 GitHub 回報錯誤</string> <string name="summary_pref_feedback">前往 GitHub 回報錯誤</string>
<string name="summary_pref_tg_group">加入 Telegram 群組</string> <string name="summary_pref_tg_group">加入 Telegram 群組</string>
@@ -158,12 +161,14 @@
<string name="title_mode">模式</string> <string name="title_mode">模式</string>
<string name="title_mode_help">輕觸以檢視說明</string> <string name="title_mode_help">輕觸以檢視說明</string>
<string name="title_language">語言</string> <string name="title_language">語言</string>
<string name="title_ui_settings">用戶界面設置</string>
<string name="title_logcat">Logcat</string> <string name="title_logcat">Logcat</string>
<string name="logcat_copy">複製</string> <string name="logcat_copy">複製</string>
<string name="logcat_clear">清除</string> <string name="logcat_clear">清除</string>
<string name="title_service_restart">服務重啟</string> <string name="title_service_restart">服務重啟</string>
<string name="title_del_all_config">刪除全部組態</string> <string name="title_del_all_config">刪除全部組態</string>
<string name="title_del_duplicate_config">刪除重複組態</string>
<string name="title_del_invalid_config">刪除無效組態 (先偵測)</string> <string name="title_del_invalid_config">刪除無效組態 (先偵測)</string>
<string name="title_export_all">匯出全部 (非自訂) 組態至剪貼簿</string> <string name="title_export_all">匯出全部 (非自訂) 組態至剪貼簿</string>
<string name="title_sub_setting">訂閱分組設定</string> <string name="title_sub_setting">訂閱分組設定</string>
@@ -177,6 +182,7 @@
<string name="title_sort_by_test_results">依偵測結果排序</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="filter_config_all">所有訂閱分組</string>
<string name="title_del_duplicate_config_count">Delete %d duplicate configurations</string>
<string name="tasker_start_service">啟動服務</string> <string name="tasker_start_service">啟動服務</string>
<string name="tasker_setting_confirm">確定</string> <string name="tasker_setting_confirm">確定</string>
@@ -204,6 +210,11 @@
<item>匯出完整組態至剪貼簿</item> <item>匯出完整組態至剪貼簿</item>
</string-array> </string-array>
<string-array name="share_sub_method">
<item>QR Code</item>
<item>匯出至剪貼簿</item>
</string-array>
<string-array name="routing_tag"> <string-array name="routing_tag">
<item>Proxy URL 或 IP</item> <item>Proxy URL 或 IP</item>
<item>直接連線 URL 或 IP</item> <item>直接連線 URL 或 IP</item>

View File

@@ -57,13 +57,14 @@
<string-array name="streamsecurityxs" translatable="false"> <string-array name="streamsecurityxs" translatable="false">
<item></item> <item></item>
<item>tls</item> <item>tls</item>
<item>xtls</item> <item>reality</item>
</string-array> </string-array>
<string-array name="streamsecurity_utls" translatable="false"> <string-array name="streamsecurity_utls" translatable="false">
<item></item> <item></item>
<item>chrome</item> <item>chrome</item>
<item>firefox</item> <item>firefox</item>
<item>safari</item>
<item>ios</item> <item>ios</item>
<item>android</item> <item>android</item>
<item>edge</item> <item>edge</item>
@@ -115,12 +116,6 @@
<string-array name="flows" translatable="false"> <string-array name="flows" translatable="false">
<item></item> <item></item>
<item>xtls-rprx-origin</item>
<item>xtls-rprx-origin-udp443</item>
<item>xtls-rprx-direct</item>
<item>xtls-rprx-direct-udp443</item>
<item>xtls-rprx-splice</item>
<item>xtls-rprx-splice-udp443</item>
<item>xtls-rprx-vision</item> <item>xtls-rprx-vision</item>
<item>xtls-rprx-vision-udp443</item> <item>xtls-rprx-vision-udp443</item>
</string-array> </string-array>

View File

@@ -48,9 +48,9 @@
<string name="server_lab_mode_type">gRPC mode</string> <string name="server_lab_mode_type">gRPC mode</string>
<string name="server_lab_request_host">request host(host/ws host/h2 host)/QUIC security</string> <string name="server_lab_request_host">request host(host/ws host/h2 host)/QUIC security</string>
<string name="server_lab_path">path(ws path/h2 path)/QUIC key/kcp seed/gRPC serviceName</string> <string name="server_lab_path">path(ws path/h2 path)/QUIC key/kcp seed/gRPC serviceName</string>
<string name="server_lab_stream_security">tls</string> <string name="server_lab_stream_security">TLS</string>
<string name="server_lab_stream_fingerprint" translatable="false">uTLS</string> <string name="server_lab_stream_fingerprint" translatable="false">Fingerprint</string>
<string name="server_lab_stream_alpn" translatable="false">alpn</string> <string name="server_lab_stream_alpn" translatable="false">Alpn</string>
<string name="server_lab_allow_insecure">allowInsecure</string> <string name="server_lab_allow_insecure">allowInsecure</string>
<string name="server_lab_sni">SNI</string> <string name="server_lab_sni">SNI</string>
<string name="server_lab_address3">address</string> <string name="server_lab_address3">address</string>
@@ -61,6 +61,9 @@
<string name="server_lab_security4">User(Optional)</string> <string name="server_lab_security4">User(Optional)</string>
<string name="server_lab_encryption">encryption</string> <string name="server_lab_encryption">encryption</string>
<string name="server_lab_flow">flow</string> <string name="server_lab_flow">flow</string>
<string name="server_lab_public_key" translatable="false">PublicKey</string>
<string name="server_lab_short_id" translatable="false">ShortId</string>
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
<string name="toast_success">Success</string> <string name="toast_success">Success</string>
<string name="toast_failure">Failure</string> <string name="toast_failure">Failure</string>
<string name="toast_none_data">There is nothing</string> <string name="toast_none_data">There is nothing</string>
@@ -151,6 +154,9 @@
<string name="title_pref_confirm_remove">Delete configuration file confirmation</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="summary_pref_confirm_remove">Whether to delete the configuration file 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>
<string name="title_pref_feedback">Feedback</string> <string name="title_pref_feedback">Feedback</string>
<string name="summary_pref_feedback">Feedback enhancements or bugs to GitHub</string> <string name="summary_pref_feedback">Feedback enhancements or bugs to GitHub</string>
<string name="summary_pref_tg_group">Join Telegram Group</string> <string name="summary_pref_tg_group">Join Telegram Group</string>
@@ -163,12 +169,14 @@
<string name="title_mode">Mode</string> <string name="title_mode">Mode</string>
<string name="title_mode_help">Click me for more help</string> <string name="title_mode_help">Click me for more help</string>
<string name="title_language">Language</string> <string name="title_language">Language</string>
<string name="title_ui_settings">UI settings</string>
<string name="title_logcat">Logcat</string> <string name="title_logcat">Logcat</string>
<string name="logcat_copy">Copy</string> <string name="logcat_copy">Copy</string>
<string name="logcat_clear">Clear</string> <string name="logcat_clear">Clear</string>
<string name="title_service_restart">Service restart</string> <string name="title_service_restart">Service restart</string>
<string name="title_del_all_config">Delete all config</string> <string name="title_del_all_config">Delete all config</string>
<string name="title_del_duplicate_config">Delete duplicate config</string>
<string name="title_del_invalid_config">Delete invalid config(Test first)</string> <string name="title_del_invalid_config">Delete invalid config(Test first)</string>
<string name="title_export_all">Export non-custom configs to clipboard</string> <string name="title_export_all">Export non-custom configs to clipboard</string>
<string name="title_sub_setting">Subscription group setting</string> <string name="title_sub_setting">Subscription group setting</string>
@@ -182,6 +190,7 @@
<string name="title_sort_by_test_results">Sorting by test results</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 file</string>
<string name="filter_config_all">All subscription groups</string> <string name="filter_config_all">All subscription groups</string>
<string name="title_del_duplicate_config_count">Delete %d duplicate configurations</string>
<string name="tasker_start_service">Start Service</string> <string name="tasker_start_service">Start Service</string>
<string name="tasker_setting_confirm">Confirm</string> <string name="tasker_setting_confirm">Confirm</string>
@@ -209,6 +218,11 @@
<item>Export full configuration to clipboard</item> <item>Export full configuration to clipboard</item>
</string-array> </string-array>
<string-array name="share_sub_method">
<item>QRcode</item>
<item>Export to clipboard</item>
</string-array>
<string-array name="routing_tag"> <string-array name="routing_tag">
<item>proxy URL or IP</item> <item>proxy URL or IP</item>
<item>direct URL or IP</item> <item>direct URL or IP</item>

View File

@@ -6,6 +6,8 @@
<item name="colorAccent">@color/colorAccent</item> <item name="colorAccent">@color/colorAccent</item>
<item name="colorMainBg">@color/colorBg</item> <item name="colorMainBg">@color/colorBg</item>
<item name="colorMainText">@color/colorText</item> <item name="colorMainText">@color/colorText</item>
<item name="android:statusBarColor">@color/colorPrimary</item>
<item name="android:navigationBarColor">@color/colorPrimary</item>
</style> </style>
<style name="AppThemeDayNight.NoActionBar" parent="AppThemeDayNight"> <style name="AppThemeDayNight.NoActionBar" parent="AppThemeDayNight">

View File

@@ -124,6 +124,20 @@
android:summary="%s" android:summary="%s"
android:title="@string/title_mode" /> android:title="@string/title_mode" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/title_ui_settings">
<CheckBoxPreference
android:key="pref_confirm_remove"
android:summary="@string/summary_pref_confirm_remove"
android:title="@string/title_pref_confirm_remove" />
<CheckBoxPreference
android:key="pref_start_scan_immediate"
android:summary="@string/summary_pref_start_scan_immediate"
android:title="@string/title_pref_start_scan_immediate" />
<ListPreference <ListPreference
android:defaultValue="auto" android:defaultValue="auto"
android:entries="@array/language_select" android:entries="@array/language_select"
@@ -132,10 +146,5 @@
android:summary="%s" android:summary="%s"
android:title="@string/title_language" /> android:title="@string/title_language" />
<CheckBoxPreference
android:key="pref_confirm_remove"
android:summary="@string/summary_pref_confirm_remove"
android:title="@string/title_pref_confirm_remove" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View File

@@ -1,31 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
buildscript { id 'com.android.application' version '7.4.2' apply false
repositories { id 'com.android.library' version '7.4.2' apply false
google() id 'org.jetbrains.kotlin.android' version '1.8.0' apply false
mavenCentral() }
maven { url 'https://maven.google.com' }
maven { url 'https://jitpack.io' }
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
mavenCentral()
maven { url 'https://maven.google.com' }
maven { url 'https://jitpack.io' }
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

View File

@@ -1,22 +1,10 @@
## Project-wide Gradle settings.
# buildToolsVer=33.0.2
# For more details on how to configure your build environment visit compileSdkVer=33
# http://www.gradle.org/docs/current/userguide/build_environment.html targetSdkVer=33
#
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx1024m -XX:MaxPermSize=256m
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Fri Jun 02 14:08:42 CST 2017
kotlinVersion=1.6.21
buildToolsVer=31.0.0
compileSdkVer=31
targetSdkVer=31
kotlin.incremental=true kotlin.incremental=true
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
kotlin.code.style=official
android.nonTransitiveRClass=true
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip

View File

@@ -1 +1,17 @@
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
jcenter()
}
}
rootProject.name = "V2rayNG"
include ':app' include ':app'