From 9983ea25d20016b5eb5635e157aa92ffe8e6cd53 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Tue, 13 Aug 2024 21:17:27 +0800 Subject: [PATCH 01/20] Performance optimization --- .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 15 +--- .../kotlin/com/v2ray/ang/util/MmkvManager.kt | 16 +++- .../com/v2ray/ang/viewmodel/MainViewModel.kt | 84 +++++++++++++++++-- 3 files changed, 94 insertions(+), 21 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt index 023d2b726..25c7784ba 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -291,11 +291,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } R.id.export_all -> { - if (AngConfigManager.shareNonCustomConfigsToClipboard(this, mainViewModel.serverList) == 0) { - toast(R.string.toast_success) - } else { - toast(R.string.toast_failure) - } + mainViewModel.exportAllServer() true } @@ -317,8 +313,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList R.id.del_all_config -> { AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) .setPositiveButton(android.R.string.ok) { _, _ -> - MmkvManager.removeAllServer() - mainViewModel.reloadServerList() + mainViewModel.removeAllServer() } .setNegativeButton(android.R.string.no) {_, _ -> //do noting @@ -340,8 +335,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList R.id.del_invalid_config -> { AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) .setPositiveButton(android.R.string.ok) { _, _ -> - MmkvManager.removeInvalidServer() - mainViewModel.reloadServerList() + mainViewModel.removeInvalidServer() } .setNegativeButton(android.R.string.no) {_, _ -> //do noting @@ -350,8 +344,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList true } R.id.sort_by_test_results -> { - MmkvManager.sortByTestResults() - mainViewModel.reloadServerList() + mainViewModel.sortByTestResults() true } else -> super.onOptionsItemSelected(item) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt index b81c56fa0..97ab05f69 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt @@ -191,11 +191,19 @@ object MmkvManager { serverAffStorage?.clearAll() } - fun removeInvalidServer() { - serverAffStorage?.allKeys()?.forEach { key -> - decodeServerAffiliationInfo(key)?.let { aff -> + fun removeInvalidServer(guid: String) { + if (guid.isNotEmpty()) { + decodeServerAffiliationInfo(guid)?.let { aff -> if (aff.testDelayMillis < 0L) { - removeServer(key) + removeServer(guid) + } + } + } else { + serverAffStorage?.allKeys()?.forEach { key -> + decodeServerAffiliationInfo(key)?.let { aff -> + if (aff.testDelayMillis < 0L) { + removeServer(key) + } } } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt index d0d4e589d..04bd836bb 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -23,6 +23,7 @@ import com.v2ray.ang.dto.ServerConfig import com.v2ray.ang.dto.ServersCache import com.v2ray.ang.dto.V2rayConfig import com.v2ray.ang.extension.toast +import com.v2ray.ang.util.AngConfigManager import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.MmkvManager.KEY_ANG_CONFIGS @@ -170,6 +171,29 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } } + + fun exportAllServer() { + viewModelScope.launch(Dispatchers.Default) { + val serverListCopy = + if (subscriptionId.isNullOrEmpty()) { + serverList + } else { + serversCache.map { it.guid }.toList() + } + + val ret = AngConfigManager.shareNonCustomConfigsToClipboard( + getApplication(), + serverListCopy + ) + launch(Dispatchers.Main) { + if (ret == 0) + getApplication().toast(R.string.toast_success) + else + getApplication().toast(R.string.toast_failure) + } + } + } + fun testAllTcping() { tcpingTestScope.coroutineContext[Job]?.cancelChildren() SpeedtestUtil.closeAllTcpSockets() @@ -256,14 +280,20 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { fun removeDuplicateServer() { viewModelScope.launch(Dispatchers.Default) { + val serversCacheCopy = mutableListOf>() + for (it in serversCache) { + val config = MmkvManager.decodeServerConfig(it.guid) ?: continue + serversCacheCopy.add(Pair(it.guid, config)) + } + val deleteServer = mutableListOf() - serversCache.forEachIndexed { index, it -> - val outbound = MmkvManager.decodeServerConfig(it.guid)?.getProxyOutbound() - serversCache.forEachIndexed { index2, it2 -> + serversCacheCopy.forEachIndexed { index, it -> + val outbound = it.second.getProxyOutbound() + serversCacheCopy.forEachIndexed { index2, it2 -> if (index2 > index) { - val outbound2 = MmkvManager.decodeServerConfig(it2.guid)?.getProxyOutbound() - if (outbound == outbound2 && !deleteServer.contains(it2.guid)) { - deleteServer.add(it2.guid) + val outbound2 = it2.second.getProxyOutbound() + if (outbound == outbound2 && !deleteServer.contains(it2.first)) { + deleteServer.add(it2.first) } } } @@ -284,6 +314,48 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } } + fun removeAllServer() { + viewModelScope.launch(Dispatchers.Default) { + if (subscriptionId.isNullOrEmpty()) { + MmkvManager.removeAllServer() + } else { + val serversCopy = serversCache.toList() + for (item in serversCopy) { + MmkvManager.removeServer(item.guid) + } + } + launch(Dispatchers.Main) { + reloadServerList() + } + } + } + + fun removeInvalidServer() { + viewModelScope.launch(Dispatchers.Default) { + if (subscriptionId.isNullOrEmpty()) { + MmkvManager.removeInvalidServer("") + } else { + val serversCopy = serversCache.toList() + for (item in serversCopy) { + MmkvManager.removeInvalidServer(item.guid) + } + } + launch(Dispatchers.Main) { + reloadServerList() + } + } + } + + fun sortByTestResults() { + viewModelScope.launch(Dispatchers.Default) { + MmkvManager.sortByTestResults() + launch(Dispatchers.Main) { + reloadServerList() + } + } + } + + fun copyAssets(assets: AssetManager) { val extFolder = Utils.userAssetPath(getApplication()) viewModelScope.launch(Dispatchers.Default) { From 7f6a526b25ff46ed855c0c432aba185f6fece15c Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 14 Aug 2024 10:46:42 +0800 Subject: [PATCH 02/20] Add profile filtering https://github.com/2dust/v2rayNG/issues/3471 --- .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 21 ++++++++++++++++++- .../com/v2ray/ang/viewmodel/MainViewModel.kt | 13 +++++++++--- V2rayNG/app/src/main/res/menu/menu_main.xml | 6 ++++++ 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt index 25c7784ba..d12c9cf19 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -18,6 +18,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.ActionBarDrawerToggle import androidx.appcompat.app.AlertDialog +import androidx.appcompat.widget.SearchView import androidx.core.content.ContextCompat import androidx.core.view.GravityCompat import androidx.core.view.isVisible @@ -232,7 +233,25 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.menu_main, menu) - return true + + val searchItem = menu.findItem(R.id.search_view) + if (searchItem != null) { + val searchView = searchItem.actionView as SearchView + searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String?): Boolean { + mainViewModel.filterConfig(query.orEmpty()) + return false + } + + override fun onQueryTextChange(newText: String?): Boolean = false + }) + + searchView.setOnCloseListener { + mainViewModel.filterConfig("") + false + } + } + return super.onCreateOptionsMenu(menu) } override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt index 04bd836bb..284d3582d 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -61,7 +61,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { var serverList = MmkvManager.decodeServerList() var subscriptionId: String = settingsStorage.decodeString(AppConfig.CACHE_SUBSCRIPTION_ID, "")?:"" - //var keywordFilter: String = settingsStorage.decodeString(AppConfig.CACHE_KEYWORD_FILTER, "")?:"" + var keywordFilter: String = settingsStorage.decodeString(AppConfig.CACHE_KEYWORD_FILTER, "")?:"" private set val serversCache = mutableListOf() val isRunning by lazy { MutableLiveData() } @@ -166,8 +166,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { continue } -// if (keywordFilter.isEmpty() || config.remarks.contains(keywordFilter)) { - serversCache.add(ServersCache(guid, profile)) + if (keywordFilter.isEmpty() || profile.remarks.contains(keywordFilter)) { + serversCache.add(ServersCache(guid, profile)) + } } } @@ -382,6 +383,12 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } } + fun filterConfig(keyword: String) { + keywordFilter = keyword + settingsStorage.encode(AppConfig.CACHE_KEYWORD_FILTER, keywordFilter) + reloadServerList() + } + private val mMsgReceiver = object : BroadcastReceiver() { override fun onReceive(ctx: Context?, intent: Intent?) { when (intent?.getIntExtra("key", 0)) { diff --git a/V2rayNG/app/src/main/res/menu/menu_main.xml b/V2rayNG/app/src/main/res/menu/menu_main.xml index 0db248692..8edaf8a10 100644 --- a/V2rayNG/app/src/main/res/menu/menu_main.xml +++ b/V2rayNG/app/src/main/res/menu/menu_main.xml @@ -1,6 +1,12 @@ + Date: Wed, 14 Aug 2024 15:06:21 +0800 Subject: [PATCH 03/20] logic optimization https://github.com/2dust/v2rayNG/issues/3470 --- .../kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt | 14 +++++++++----- .../main/kotlin/com/v2ray/ang/util/MmkvManager.kt | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt index c7b71e598..83cc4a76a 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt @@ -22,7 +22,7 @@ class UrlSchemeActivity : BaseActivity() { if (action == Intent.ACTION_SEND) { if ("text/plain" == type) { intent.getStringExtra(Intent.EXTRA_TEXT)?.let { - parseUri(it) + parseUri(it, null) } } } else if (action == Intent.ACTION_VIEW) { @@ -30,13 +30,13 @@ class UrlSchemeActivity : BaseActivity() { "install-config" -> { val uri: Uri? = intent.data val shareUrl = uri?.getQueryParameter("url") ?: "" - parseUri(shareUrl) + parseUri(shareUrl, uri?.fragment) } "install-sub" -> { val uri: Uri? = intent.data val shareUrl = uri?.getQueryParameter("url") ?: "" - parseUri(shareUrl) + parseUri(shareUrl, uri?.fragment) } else -> { @@ -53,15 +53,19 @@ class UrlSchemeActivity : BaseActivity() { } } - private fun parseUri(uriString: String?) { + private fun parseUri(uriString: String?, fragment: String?) { if (uriString.isNullOrEmpty()) { return } Log.d("UrlScheme", uriString) - val decodedUrl = URLDecoder.decode(uriString, "UTF-8") + var decodedUrl = URLDecoder.decode(uriString, "UTF-8") val uri = Uri.parse(decodedUrl) if (uri != null) { + if (uri.fragment.isNullOrEmpty() && !fragment.isNullOrEmpty()) { + decodedUrl += "#${fragment}" + } + Log.d("UrlScheme-decodedUrl", decodedUrl) val (count, countSub) = AngConfigManager.importBatchConfig(decodedUrl, "", false) if (count + countSub > 0) { toast(R.string.import_subscription_success) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt index 97ab05f69..29bcfa058 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt @@ -147,7 +147,7 @@ object MmkvManager { } val uri = URI(Utils.fixIllegalUrl(url)) val subItem = SubscriptionItem() - subItem.remarks = Utils.urlDecode(uri.fragment ?: "import sub") + subItem.remarks = uri.fragment ?: "import sub" subItem.url = url subStorage?.encode(Utils.getUuid(), Gson().toJson(subItem)) return 1 From 828a39331baced1e0394fa0048bdc5c195a73417 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 14 Aug 2024 17:55:41 +0800 Subject: [PATCH 04/20] Add a progress bar --- .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 57 +++++++-- .../com/v2ray/ang/ui/SubEditActivity.kt | 19 ++- .../com/v2ray/ang/viewmodel/MainViewModel.kt | 121 +++++++----------- 3 files changed, 103 insertions(+), 94 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt index d12c9cf19..659482714 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -128,7 +128,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList initGroupTab() setupViewModel() - mainViewModel.copyAssets(assets) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { RxPermissions(this) @@ -175,6 +174,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } } mainViewModel.startListenBroadcast() + mainViewModel.copyAssets(assets) } private fun initGroupTab() { @@ -310,7 +310,18 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } R.id.export_all -> { - mainViewModel.exportAllServer() + binding.pbWaiting.show() + lifecycleScope.launch(Dispatchers.IO) { + val ret = mainViewModel.exportAllServer() + launch(Dispatchers.Main) { + if (ret == 0) + toast(R.string.toast_success) + else + toast(R.string.toast_failure) + binding.pbWaiting.hide() + } + } + true } @@ -332,7 +343,14 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList R.id.del_all_config -> { AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) .setPositiveButton(android.R.string.ok) { _, _ -> - mainViewModel.removeAllServer() + binding.pbWaiting.show() + lifecycleScope.launch(Dispatchers.IO) { + mainViewModel.removeAllServer() + launch(Dispatchers.Main) { + mainViewModel.reloadServerList() + binding.pbWaiting.hide() + } + } } .setNegativeButton(android.R.string.no) {_, _ -> //do noting @@ -343,18 +361,34 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList R.id.del_duplicate_config-> { AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) .setPositiveButton(android.R.string.ok) { _, _ -> - mainViewModel.removeDuplicateServer() + binding.pbWaiting.show() + lifecycleScope.launch(Dispatchers.IO) { + val ret = mainViewModel.removeDuplicateServer() + launch(Dispatchers.Main) { + mainViewModel.reloadServerList() + toast(getString(R.string.title_del_duplicate_config_count, ret)) + binding.pbWaiting.hide() + } + } } - .setNegativeButton(android.R.string.no) {_, _ -> + .setNegativeButton(android.R.string.no) { _, _ -> //do noting } .show() true } + R.id.del_invalid_config -> { AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) .setPositiveButton(android.R.string.ok) { _, _ -> - mainViewModel.removeInvalidServer() + binding.pbWaiting.show() + lifecycleScope.launch(Dispatchers.IO) { + mainViewModel.removeInvalidServer() + launch(Dispatchers.Main) { + mainViewModel.reloadServerList() + binding.pbWaiting.hide() + } + } } .setNegativeButton(android.R.string.no) {_, _ -> //do noting @@ -363,7 +397,14 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList true } R.id.sort_by_test_results -> { - mainViewModel.sortByTestResults() + binding.pbWaiting.show() + lifecycleScope.launch(Dispatchers.IO) { + mainViewModel.sortByTestResults() + launch(Dispatchers.Main) { + mainViewModel.reloadServerList() + binding.pbWaiting.hide() + } + } true } else -> super.onOptionsItemSelected(item) @@ -451,7 +492,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList //dialog.dismiss() binding.pbWaiting.hide() } - } + } } private fun importConfigCustomClipboard() diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt index 8106aedb7..1f52f6d28 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt @@ -5,22 +5,17 @@ import android.text.TextUtils import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.AlertDialog -import androidx.work.Constraints -import androidx.work.ExistingPeriodicWorkPolicy -import androidx.work.NetworkType -import androidx.work.PeriodicWorkRequest -import androidx.work.multiprocess.RemoteWorkManager +import androidx.lifecycle.lifecycleScope import com.google.gson.Gson import com.tencent.mmkv.MMKV -import com.v2ray.ang.AngApplication import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivitySubEditBinding import com.v2ray.ang.dto.SubscriptionItem import com.v2ray.ang.extension.toast -import com.v2ray.ang.service.SubscriptionUpdater import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.Utils -import java.util.concurrent.TimeUnit +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch class SubEditActivity : BaseActivity() { private val binding by lazy {ActivitySubEditBinding.inflate(layoutInflater)} @@ -106,8 +101,12 @@ class SubEditActivity : BaseActivity() { if (editSubId.isNotEmpty()) { AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) .setPositiveButton(android.R.string.ok) { _, _ -> - MmkvManager.removeSubscription(editSubId) - finish() + lifecycleScope.launch(Dispatchers.IO) { + MmkvManager.removeSubscription(editSubId) + launch(Dispatchers.Main) { + finish() + } + } } .setNegativeButton(android.R.string.no) {_, _ -> // do nothing diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt index 284d3582d..cb9a736b7 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -173,28 +173,22 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } - fun exportAllServer() { - viewModelScope.launch(Dispatchers.Default) { - val serverListCopy = - if (subscriptionId.isNullOrEmpty()) { - serverList - } else { - serversCache.map { it.guid }.toList() - } - - val ret = AngConfigManager.shareNonCustomConfigsToClipboard( - getApplication(), - serverListCopy - ) - launch(Dispatchers.Main) { - if (ret == 0) - getApplication().toast(R.string.toast_success) - else - getApplication().toast(R.string.toast_failure) + fun exportAllServer() : Int { + val serverListCopy = + if (subscriptionId.isNullOrEmpty()) { + serverList + } else { + serversCache.map { it.guid }.toList() } - } + + val ret = AngConfigManager.shareNonCustomConfigsToClipboard( + getApplication(), + serverListCopy + ) + return ret } + fun testAllTcping() { tcpingTestScope.coroutineContext[Job]?.cancelChildren() SpeedtestUtil.closeAllTcpSockets() @@ -279,81 +273,56 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { return -1 } - fun removeDuplicateServer() { - viewModelScope.launch(Dispatchers.Default) { - val serversCacheCopy = mutableListOf>() - for (it in serversCache) { - val config = MmkvManager.decodeServerConfig(it.guid) ?: continue - serversCacheCopy.add(Pair(it.guid, config)) - } + fun removeDuplicateServer() : Int { + val serversCacheCopy = mutableListOf>() + for (it in serversCache) { + val config = MmkvManager.decodeServerConfig(it.guid) ?: continue + serversCacheCopy.add(Pair(it.guid, config)) + } - val deleteServer = mutableListOf() - serversCacheCopy.forEachIndexed { index, it -> - val outbound = it.second.getProxyOutbound() - serversCacheCopy.forEachIndexed { index2, it2 -> - if (index2 > index) { - val outbound2 = it2.second.getProxyOutbound() - if (outbound == outbound2 && !deleteServer.contains(it2.first)) { - deleteServer.add(it2.first) - } + val deleteServer = mutableListOf() + serversCacheCopy.forEachIndexed { index, it -> + val outbound = it.second.getProxyOutbound() + serversCacheCopy.forEachIndexed { index2, it2 -> + if (index2 > index) { + val outbound2 = it2.second.getProxyOutbound() + if (outbound == outbound2 && !deleteServer.contains(it2.first)) { + deleteServer.add(it2.first) } } } - for (it in deleteServer) { - MmkvManager.removeServer(it) - } - launch(Dispatchers.Main) { - reloadServerList() - getApplication().toast( - getApplication().getString( - R.string.title_del_duplicate_config_count, - deleteServer.count() - ) - ) - } - } + for (it in deleteServer) { + MmkvManager.removeServer(it) + } + + return deleteServer.count() } fun removeAllServer() { - viewModelScope.launch(Dispatchers.Default) { - if (subscriptionId.isNullOrEmpty()) { - MmkvManager.removeAllServer() - } else { - val serversCopy = serversCache.toList() - for (item in serversCopy) { - MmkvManager.removeServer(item.guid) - } - } - launch(Dispatchers.Main) { - reloadServerList() + if (subscriptionId.isNullOrEmpty()) { + MmkvManager.removeAllServer() + } else { + val serversCopy = serversCache.toList() + for (item in serversCopy) { + MmkvManager.removeServer(item.guid) } } } fun removeInvalidServer() { - viewModelScope.launch(Dispatchers.Default) { - if (subscriptionId.isNullOrEmpty()) { - MmkvManager.removeInvalidServer("") - } else { - val serversCopy = serversCache.toList() - for (item in serversCopy) { - MmkvManager.removeInvalidServer(item.guid) - } - } - launch(Dispatchers.Main) { - reloadServerList() + if (subscriptionId.isNullOrEmpty()) { + MmkvManager.removeInvalidServer("") + } else { + val serversCopy = serversCache.toList() + for (item in serversCopy) { + MmkvManager.removeInvalidServer(item.guid) } } } fun sortByTestResults() { - viewModelScope.launch(Dispatchers.Default) { - MmkvManager.sortByTestResults() - launch(Dispatchers.Main) { - reloadServerList() - } - } + MmkvManager.sortByTestResults() } From df4e232087ce59b6af052d61ced38bd1d1223c56 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 14 Aug 2024 19:58:32 +0800 Subject: [PATCH 05/20] Code Optimization --- .../kotlin/com/v2ray/ang/AngApplication.kt | 3 - .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 122 ++++++++++-------- .../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 41 +++--- .../kotlin/com/v2ray/ang/util/MmkvManager.kt | 6 +- .../com/v2ray/ang/viewmodel/MainViewModel.kt | 44 ++----- 5 files changed, 107 insertions(+), 109 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt index 6416d201d..516fe1ed4 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt @@ -17,9 +17,6 @@ class AngApplication : MultiDexApplication(), Configuration.Provider { application = this } - //var firstRun = false - // private set - override fun onCreate() { super.onCreate() diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt index 659482714..51b3fe691 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -28,7 +28,6 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.navigation.NavigationView import com.google.android.material.tabs.TabLayout import com.tbruyelle.rxpermissions3.RxPermissions -import com.tencent.mmkv.MMKV import com.v2ray.ang.AppConfig import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivityMainBinding @@ -55,8 +54,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } private val adapter by lazy { MainRecyclerAdapter(this) } - private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) } - private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } private val requestVpnPermission = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { if (it.resultCode == RESULT_OK) { startV2Ray() @@ -91,7 +88,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList binding.fab.setOnClickListener { if (mainViewModel.isRunning.value == true) { Utils.stopVService(this) - } else if ((settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN") == "VPN") { + } else if ((MmkvManager.settingsStorage?.decodeString(AppConfig.PREF_MODE) ?: "VPN") == "VPN") { val intent = VpnService.prepare(this) if (intent == null) { startV2Ray() @@ -121,7 +118,8 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList val toggle = ActionBarDrawerToggle( - this, binding.drawerLayout, binding.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close) + this, binding.drawerLayout, binding.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close + ) binding.drawerLayout.addDrawerListener(toggle) toggle.syncState() binding.navView.setNavigationItemSelectedListener(this) @@ -202,7 +200,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList fun startV2Ray() { if (isNetworkConnected) { - if (mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) { + if (MmkvManager.mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) { return } V2RayServiceManager.startV2Ray(this) @@ -216,10 +214,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList Utils.stopVService(this) } Observable.timer(500, TimeUnit.MILLISECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { - startV2Ray() - } + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + startV2Ray() + } } public override fun onResume() { @@ -259,46 +257,57 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList importQRcode(true) true } + R.id.import_clipboard -> { importClipboard() true } + R.id.import_manually_vmess -> { importManually(EConfigType.VMESS.value) true } + R.id.import_manually_vless -> { importManually(EConfigType.VLESS.value) true } + R.id.import_manually_ss -> { importManually(EConfigType.SHADOWSOCKS.value) true } + R.id.import_manually_socks -> { importManually(EConfigType.SOCKS.value) true } + R.id.import_manually_trojan -> { importManually(EConfigType.TROJAN.value) true } + R.id.import_manually_wireguard -> { importManually(EConfigType.WIREGUARD.value) true } + R.id.import_config_custom_clipboard -> { importConfigCustomClipboard() true } + R.id.import_config_custom_local -> { importConfigCustomLocal() true } + R.id.import_config_custom_url -> { importConfigCustomUrlClipboard() true } + R.id.import_config_custom_url_scan -> { importQRcode(false) true @@ -342,23 +351,24 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList R.id.del_all_config -> { AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) - .setPositiveButton(android.R.string.ok) { _, _ -> - binding.pbWaiting.show() - lifecycleScope.launch(Dispatchers.IO) { - mainViewModel.removeAllServer() - launch(Dispatchers.Main) { - mainViewModel.reloadServerList() - binding.pbWaiting.hide() - } + .setPositiveButton(android.R.string.ok) { _, _ -> + binding.pbWaiting.show() + lifecycleScope.launch(Dispatchers.IO) { + mainViewModel.removeAllServer() + launch(Dispatchers.Main) { + mainViewModel.reloadServerList() + binding.pbWaiting.hide() } } - .setNegativeButton(android.R.string.no) {_, _ -> - //do noting - } - .show() + } + .setNegativeButton(android.R.string.no) { _, _ -> + //do noting + } + .show() true } - R.id.del_duplicate_config-> { + + R.id.del_duplicate_config -> { AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) .setPositiveButton(android.R.string.ok) { _, _ -> binding.pbWaiting.show() @@ -390,12 +400,13 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } } } - .setNegativeButton(android.R.string.no) {_, _ -> + .setNegativeButton(android.R.string.no) { _, _ -> //do noting } .show() true } + R.id.sort_by_test_results -> { binding.pbWaiting.show() lifecycleScope.launch(Dispatchers.IO) { @@ -407,10 +418,11 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } true } + else -> super.onOptionsItemSelected(item) } - private fun importManually(createConfigType : Int) { + private fun importManually(createConfigType: Int) { startActivity( Intent() .putExtra("createConfigType", createConfigType) @@ -429,16 +441,16 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList // .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), requestCode) // } catch (e: Exception) { RxPermissions(this) - .request(Manifest.permission.CAMERA) - .subscribe { - if (it) - if (forConfig) - scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java)) - else - scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java)) + .request(Manifest.permission.CAMERA) + .subscribe { + if (it) + if (forConfig) + scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java)) else - toast(R.string.toast_permission_denied) - } + scanQRCodeForUrlToCustomConfig.launch(Intent(this, ScannerActivity::class.java)) + else + toast(R.string.toast_permission_denied) + } // } return true } @@ -569,7 +581,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList /** * import config from sub */ - private fun importConfigViaSub() : Boolean { + private fun importConfigViaSub(): Boolean { // val dialog = AlertDialog.Builder(this) // .setView(LayoutProgressBinding.inflate(layoutInflater).root) // .setCancelable(false) @@ -625,19 +637,19 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList Manifest.permission.READ_EXTERNAL_STORAGE } RxPermissions(this) - .request(permission) - .subscribe { - if (it) { - try { - contentResolver.openInputStream(uri).use { input -> - importCustomizeConfig(input?.bufferedReader()?.readText()) - } - } catch (e: Exception) { - e.printStackTrace() + .request(permission) + .subscribe { + if (it) { + try { + contentResolver.openInputStream(uri).use { input -> + importCustomizeConfig(input?.bufferedReader()?.readText()) } - } else - toast(R.string.toast_permission_denied) - } + } catch (e: Exception) { + e.printStackTrace() + } + } else + toast(R.string.toast_permission_denied) + } } /** @@ -685,27 +697,33 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } - override fun onNavigationItemSelected(item: MenuItem): Boolean { // Handle navigation view item clicks here. when (item.itemId) { R.id.sub_setting -> { - requestSubSettingActivity.launch(Intent(this,SubSettingActivity::class.java)) + requestSubSettingActivity.launch(Intent(this, SubSettingActivity::class.java)) } + R.id.settings -> { - startActivity(Intent(this, SettingsActivity::class.java) - .putExtra("isRunning", mainViewModel.isRunning.value == true)) + startActivity( + Intent(this, SettingsActivity::class.java) + .putExtra("isRunning", mainViewModel.isRunning.value == true) + ) } + R.id.user_asset_setting -> { startActivity(Intent(this, UserAssetActivity::class.java)) } + R.id.promotion -> { Utils.openUri(this, "${Utils.decode(AppConfig.PromotionUrl)}?t=${System.currentTimeMillis()}") } + R.id.logcat -> { startActivity(Intent(this, LogcatActivity::class.java)) } - R.id.about-> { + + R.id.about -> { startActivity(Intent(this, AboutActivity::class.java)) } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt index cc9a062f3..b1e891125 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt @@ -10,7 +10,6 @@ import androidx.appcompat.app.AlertDialog import androidx.core.content.ContextCompat import androidx.recyclerview.widget.RecyclerView import com.google.gson.Gson -import com.tencent.mmkv.MMKV import com.v2ray.ang.AngApplication.Companion.application import com.v2ray.ang.AppConfig import com.v2ray.ang.R @@ -38,9 +37,6 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter by lazy { mActivity.resources.getStringArray(R.array.share_method) } @@ -71,13 +67,13 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter { holder.itemMainBinding.tvType.text = profile.configType.name } + else -> { holder.itemMainBinding.tvType.text = profile.configType.name.lowercase() } @@ -114,6 +112,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter { if (AngConfigManager.share2Clipboard(mActivity, guid) == 0) { mActivity.toast(R.string.toast_success) @@ -121,6 +120,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter shareFullContent(guid) else -> mActivity.toast("else") } @@ -132,7 +132,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter removeServer(guid, position) } - .setNegativeButton(android.R.string.no) {_, _ -> + .setNegativeButton(android.R.string.no) { _, _ -> //do noting } .show() @@ -159,9 +159,9 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter MainViewHolder(ItemRecyclerMainBinding.inflate(LayoutInflater.from(parent.context), parent, false)) + else -> FooterViewHolder(ItemRecyclerFooterBinding.inflate(LayoutInflater.from(parent.context), parent, false)) } @@ -231,14 +232,14 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter { val json = mainStorage?.decodeString(KEY_ANG_CONFIGS) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt index cb9a736b7..517e2d8a6 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -12,7 +12,6 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import com.google.gson.Gson -import com.tencent.mmkv.MMKV import com.v2ray.ang.AngApplication import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig.ANG_PACKAGE @@ -40,34 +39,15 @@ import java.io.FileOutputStream import java.util.Collections class MainViewModel(application: Application) : AndroidViewModel(application) { - private val mainStorage by lazy { - MMKV.mmkvWithID( - MmkvManager.ID_MAIN, - MMKV.MULTI_PROCESS_MODE - ) - } - private val serverRawStorage by lazy { - MMKV.mmkvWithID( - MmkvManager.ID_SERVER_RAW, - MMKV.MULTI_PROCESS_MODE - ) - } - private val settingsStorage by lazy { - MMKV.mmkvWithID( - MmkvManager.ID_SETTING, - MMKV.MULTI_PROCESS_MODE - ) - } + private var serverList = MmkvManager.decodeServerList() + var subscriptionId: String = MmkvManager.settingsStorage.decodeString(AppConfig.CACHE_SUBSCRIPTION_ID, "") ?: "" - var serverList = MmkvManager.decodeServerList() - var subscriptionId: String = settingsStorage.decodeString(AppConfig.CACHE_SUBSCRIPTION_ID, "")?:"" - var keywordFilter: String = settingsStorage.decodeString(AppConfig.CACHE_KEYWORD_FILTER, "")?:"" - private set + //var keywordFilter: String = MmkvManager.settingsStorage.decodeString(AppConfig.CACHE_KEYWORD_FILTER, "")?:"" + private var keywordFilter = "" val serversCache = mutableListOf() val isRunning by lazy { MutableLiveData() } val updateListAction by lazy { MutableLiveData() } val updateTestResultAction by lazy { MutableLiveData() } - private val tcpingTestScope by lazy { CoroutineScope(Dispatchers.IO) } fun startListenBroadcast() { @@ -121,7 +101,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java) config.remarks = config.fullConfig?.remarks ?: System.currentTimeMillis().toString() val key = MmkvManager.encodeServerConfig("", config) - serverRawStorage?.encode(key, server) + MmkvManager.serverRawStorage?.encode(key, server) serverList.add(0, key) val profile = ProfileItem( configType = config.configType, @@ -142,7 +122,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { fun swapServer(fromPosition: Int, toPosition: Int) { Collections.swap(serverList, fromPosition, toPosition) Collections.swap(serversCache, fromPosition, toPosition) - mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList)) + MmkvManager.mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList)) } @Synchronized @@ -173,7 +153,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } - fun exportAllServer() : Int { + fun exportAllServer(): Int { val serverListCopy = if (subscriptionId.isNullOrEmpty()) { serverList @@ -242,12 +222,12 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { fun subscriptionIdChanged(id: String) { if (subscriptionId != id) { subscriptionId = id - settingsStorage.encode(AppConfig.CACHE_SUBSCRIPTION_ID, subscriptionId) + MmkvManager.settingsStorage.encode(AppConfig.CACHE_SUBSCRIPTION_ID, subscriptionId) reloadServerList() } } - fun getSubscriptions(context: Context) : Pair?, MutableList?> { + fun getSubscriptions(context: Context): Pair?, MutableList?> { val subscriptions = MmkvManager.decodeSubscriptions() if (subscriptionId.isNotEmpty() && !subscriptions.map { it.first }.contains(subscriptionId) @@ -273,7 +253,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { return -1 } - fun removeDuplicateServer() : Int { + fun removeDuplicateServer(): Int { val serversCacheCopy = mutableListOf>() for (it in serversCache) { val config = MmkvManager.decodeServerConfig(it.guid) ?: continue @@ -354,7 +334,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { fun filterConfig(keyword: String) { keywordFilter = keyword - settingsStorage.encode(AppConfig.CACHE_KEYWORD_FILTER, keywordFilter) + MmkvManager.settingsStorage.encode(AppConfig.CACHE_KEYWORD_FILTER, keywordFilter) reloadServerList() } @@ -395,4 +375,4 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } } } -} +} \ No newline at end of file From e17e566daa06876f3fa805e6d9cb3bc94836700b Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Wed, 14 Aug 2024 20:16:26 +0800 Subject: [PATCH 06/20] Update subscriptions based on grouping https://github.com/2dust/v2rayNG/issues/3445 --- .../main/kotlin/com/v2ray/ang/ui/MainActivity.kt | 2 +- .../kotlin/com/v2ray/ang/util/AngConfigManager.kt | 2 +- .../com/v2ray/ang/viewmodel/MainViewModel.kt | 15 +++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt index 51b3fe691..23fa92129 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -589,7 +589,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList binding.pbWaiting.show() lifecycleScope.launch(Dispatchers.IO) { - val count = AngConfigManager.updateConfigViaSubAll() + val count = mainViewModel.updateConfigViaSubAll() delay(500L) launch(Dispatchers.Main) { if (count > 0) { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt index 0b60190a3..60317225c 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt @@ -535,7 +535,7 @@ object AngConfigManager { return count } - private fun updateConfigViaSub(it: Pair): Int { + fun updateConfigViaSub(it: Pair): Int { try { if (TextUtils.isEmpty(it.first) || TextUtils.isEmpty(it.second.remarks) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt index 517e2d8a6..98700451f 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -20,12 +20,15 @@ import com.v2ray.ang.dto.EConfigType import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.ServerConfig import com.v2ray.ang.dto.ServersCache +import com.v2ray.ang.dto.SubscriptionItem import com.v2ray.ang.dto.V2rayConfig import com.v2ray.ang.extension.toast import com.v2ray.ang.util.AngConfigManager +import com.v2ray.ang.util.AngConfigManager.updateConfigViaSub import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.MmkvManager.KEY_ANG_CONFIGS +import com.v2ray.ang.util.MmkvManager.subStorage import com.v2ray.ang.util.SpeedtestUtil import com.v2ray.ang.util.Utils import com.v2ray.ang.util.V2rayConfigUtil @@ -152,6 +155,18 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } } + fun updateConfigViaSubAll(): Int { + if (subscriptionId.isNullOrEmpty()) { + return AngConfigManager.updateConfigViaSubAll() + } else { + val json = subStorage?.decodeString(subscriptionId) + if (!json.isNullOrBlank()) { + return updateConfigViaSub(Pair(subscriptionId, Gson().fromJson(json, SubscriptionItem::class.java))) + } else { + return 0 + } + } + } fun exportAllServer(): Int { val serverListCopy = From 92900c3f74f4e663e273729ab4ddee61c59c2aac Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 15 Aug 2024 09:54:42 +0800 Subject: [PATCH 07/20] Optimize text descriptions --- .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 2 +- .../app/src/main/res/values-ar/strings.xml | 1 + .../app/src/main/res/values-bn/strings.xml | 1 + .../app/src/main/res/values-fa/strings.xml | 1 + .../app/src/main/res/values-ru/strings.xml | 1 + .../app/src/main/res/values-vi/strings.xml | 1 + .../src/main/res/values-zh-rCN/strings.xml | 17 +++++++++-------- .../src/main/res/values-zh-rTW/strings.xml | 17 +++++++++-------- V2rayNG/app/src/main/res/values/strings.xml | 19 ++++++++++--------- 9 files changed, 34 insertions(+), 26 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt index 23fa92129..f6af32804 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -389,7 +389,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } R.id.del_invalid_config -> { - AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) + AlertDialog.Builder(this).setMessage(R.string.del_invalid_config_comfirm) .setPositiveButton(android.R.string.ok) { _, _ -> binding.pbWaiting.show() lifecycleScope.launch(Dispatchers.IO) { diff --git a/V2rayNG/app/src/main/res/values-ar/strings.xml b/V2rayNG/app/src/main/res/values-ar/strings.xml index 27cbba7a3..4f31bde96 100644 --- a/V2rayNG/app/src/main/res/values-ar/strings.xml +++ b/V2rayNG/app/src/main/res/values-ar/strings.xml @@ -37,6 +37,7 @@ استيراد تكوين مخصص من عنوان URL استيراد تكوين مخصص مسح عنوان URL تأكيد الحذف؟ + Please test before deleting! Confirm delete ? ملاحظات العنوان المنفذ diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml index 6accba407..ea85d329b 100644 --- a/V2rayNG/app/src/main/res/values-bn/strings.xml +++ b/V2rayNG/app/src/main/res/values-bn/strings.xml @@ -37,6 +37,7 @@ URL থেকে কাস্টম কনফিগারেশন আমদানি করুন কাস্টম কনফিগারেশন স্ক্যান URL আমদানি করুন মুছে ফেলুন নিশ্চিত করুন? + Please test before deleting! Confirm delete ? মন্তব্য ঠিকানা পোর্ট diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index 9c7a7182b..01d859dd7 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -36,6 +36,7 @@ کانفیگ سفارشی را از طریق نشانی اینترنتی وارد کنید نشانی اینترنتی اسکن کانفیگ سفارشی را وارد کنید حذف شود؟ + Please test before deleting! Confirm delete ? ملاحظات نشانی پورت diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 1e9bfa2ee..65c2d8e0e 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -36,6 +36,7 @@ Импорт из URL Импорт сканированием URL Подтверждаете удаление? + Please test before deleting! Confirm delete ? Описание Адрес Порт diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml index 45f4625ad..62c7362dd 100644 --- a/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -36,6 +36,7 @@ Nhập cấu hình tùy chỉnh từ URL Nhập cấu hình tùy chỉnh quét URL Xác nhận xóa? + Please test before deleting! Confirm delete ? Tên cấu hình Địa chỉ Cổng diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml index 6b5df57b7..6c36130b9 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -36,6 +36,7 @@ 剪贴板URL导入自定义配置 扫描URL导入自定义配置 确认删除? + 删除前请先测试!确认删除? 别名(remarks) 地址(address) 端口(port) @@ -223,22 +224,22 @@ 复制 清除 服务重启 - 删除全部配置 - 删除重复配置 - 删除无效配置(先测试) - 导出全部(非自定义)配置至剪贴板 + 删除当前组配置 + 删除当前组重复配置 + 删除当前组无效配置 + 导出当前组配置至剪贴板 订阅分组设置 备注 可选地址(url) 启用更新 启用自动更新 - 更新订阅 - 测试全部配置Tcping - 测试全部配置真连接 + 更新当前组订阅 + 测试当前组配置Tcping + 测试当前组配置真连接 Geo 资源文件 按测试结果排序 过滤配置文件 - 所有订阅分组 + 所有分组 删除 %d 个重复配置 启动服务 diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml index 81c3e4265..b28dd3aa6 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -36,6 +36,7 @@ 從 URL 匯入自訂配置 掃描 URL 匯入自訂配置 確定刪除? + 刪除前請先測試!確認刪除? 備註 位址 @@ -224,22 +225,22 @@ 複製 清除 重啟服務 - 刪除全部配置 - 刪除重複配置 - 刪除無效配置 (先偵測) - 匯出全部 (非自訂) 配置至剪貼簿 + 刪除目前群組配置 + 刪除目前群組重複配置 + 刪除目前群組無效配置 + 匯出目前群組配置至剪貼簿 訂閱分組設定 備註 Optional URL 啟用更新 啟用自動更新 - 更新訂閱 - 偵測所有配置 Tcping - 偵測所有配置真延遲 + 更新目前群組訂閱 + 偵測目前群組配置 Tcping + 偵測目前群組配置真延遲 Geo 資源檔案 依偵測結果排序 過濾配置 - 所有訂閱分組 + 所有分組 Delete %d duplicate configurations 啟動服務 diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index 9a598fc1a..4727bf1dc 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -36,7 +36,8 @@ Import custom config from locally Import custom config from URL Import custom config scan URL - Confirm delete? + Confirm delete ? + Please test before deleting! Confirm delete ? remarks address port @@ -229,22 +230,22 @@ Copy Clear Service restart - Delete all config - Delete duplicate config - Delete invalid config(Test first) - Export non-custom configs to clipboard + Delete current group configuration + Delete current group duplicate configuration + Delete current group invalid configuration + Export current group non-custom configs to clipboard Subscription group setting remarks Optional URL Enable update Enable automatic update - Update subscription - Tcping all configuration - Real delay all configuration + Update current group subscription + Tcping current group configuration + Real delay current group configuration Geo asset files Sorting by test results Filter configuration file - All subscription groups + All groups Delete %d duplicate configurations Start Service From 214d9e1c53af6f4236ab54dc35b55477c791690c Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Thu, 15 Aug 2024 09:58:44 +0800 Subject: [PATCH 08/20] up 1.8.37 --- V2rayNG/app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index 86b5d64f6..16ad2d81d 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 34 - versionCode = 580 - versionName = "1.8.36" + versionCode = 582 + versionName = "1.8.37" multiDexEnabled = true splits { abi { From a3de44cd0a9894ee96e718d96d9b0c021a6ee5ac Mon Sep 17 00:00:00 2001 From: solokot Date: Thu, 15 Aug 2024 12:06:11 +0300 Subject: [PATCH 09/20] Update Russian translation (#3482) --- .../app/src/main/res/values-ru/strings.xml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 65c2d8e0e..7619e002d 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -36,7 +36,7 @@ Импорт из URL Импорт сканированием URL Подтверждаете удаление? - Please test before deleting! Confirm delete ? + Выполните проверку перед удалением! Подтверждаете удаление? Описание Адрес Порт @@ -64,8 +64,8 @@ Сид KCP Служба gRPC TLS - Fingerprint - Alpn + Отпечаток + ALPN Разрешать небезопасные SNI Адрес @@ -77,7 +77,7 @@ Шифрование Поток PublicKey - ShortId + ShortID SpiderX SecretKey Reserved (необязательно) @@ -227,18 +227,18 @@ Копировать Очистить Перезапуск службы - Удалить все профили - Удалить дубликаты профилей - Удалить нерабочие профили (после проверки) - Экспорт всех профилей в буфер обмена + Удалить профили группы + Удалить дубликаты профилей группы + Удалить нерабочие профили группы + Экспорт профилей группы в буфер обмена Группы Название URL (необязательно) Использовать обновление Использовать автоматическое обновление - Обновить подписку - Проверка доступности профилей - Время отклика профилей + Обновить подписку группы + Проверка профилей группы + Время отклика профилей группы Файлы георесурсов Сортировка по результатам теста Фильтр групп From c8ba5d727e9f56c42715d5150dc7047c94b87329 Mon Sep 17 00:00:00 2001 From: Tamim Hossain <132823494+CodeWithTamim@users.noreply.github.com> Date: Thu, 15 Aug 2024 15:07:25 +0600 Subject: [PATCH 10/20] Updated WorkManager (#3483) * Updated WorkManager Updated the WorkManager library to latest version and also updated the code for its initialization. * Updated WorkManager Updated the WorkManager library to latest version and also updated the code for its initialization. --- V2rayNG/app/build.gradle.kts | 1 - .../kotlin/com/v2ray/ang/AngApplication.kt | 25 +++++++++++-------- V2rayNG/gradle/libs.versions.toml | 2 +- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index 16ad2d81d..1fb6789fc 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -130,7 +130,6 @@ dependencies { implementation(libs.language.json) implementation(libs.quickie.bundled) implementation(libs.core) - // Updating these 2 dependencies may cause some errors. Be careful. implementation(libs.work.runtime.ktx) implementation(libs.work.multiprocess) } \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt index 516fe1ed4..0a900e0da 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt @@ -3,21 +3,30 @@ package com.v2ray.ang import android.content.Context import androidx.multidex.MultiDexApplication import androidx.work.Configuration +import androidx.work.WorkManager import com.tencent.mmkv.MMKV import com.v2ray.ang.util.Utils -class AngApplication : MultiDexApplication(), Configuration.Provider { - companion object { +class AngApplication : MultiDexApplication() +{ + companion object + { //const val PREF_LAST_VERSION = "pref_last_version" lateinit var application: AngApplication } - override fun attachBaseContext(base: Context?) { + override fun attachBaseContext(base: Context?) + { super.attachBaseContext(base) application = this } - override fun onCreate() { + private val workManagerConfiguration: Configuration = Configuration.Builder() + .setDefaultProcessName("${BuildConfig.APPLICATION_ID}:bg") + .build() + + override fun onCreate() + { super.onCreate() // LeakCanary.install(this) @@ -31,11 +40,7 @@ class AngApplication : MultiDexApplication(), Configuration.Provider { MMKV.initialize(this) Utils.setNightMode(application) - } - - override fun getWorkManagerConfiguration(): Configuration { - return Configuration.Builder() - .setDefaultProcessName("${BuildConfig.APPLICATION_ID}:bg") - .build() + // Initialize WorkManager with the custom configuration + WorkManager.initialize(this, workManagerConfiguration) } } diff --git a/V2rayNG/gradle/libs.versions.toml b/V2rayNG/gradle/libs.versions.toml index 31f2101c8..685754e4a 100644 --- a/V2rayNG/gradle/libs.versions.toml +++ b/V2rayNG/gradle/libs.versions.toml @@ -24,7 +24,7 @@ rxjava = "3.1.8" rxpermissions = "0.12" toastcompat = "1.1.0" viewpager2 = "1.1.0" -workRuntimeKtx = "2.8.1" +workRuntimeKtx = "2.9.1" [libraries] activity-ktx = { module = "androidx.activity:activity-ktx", version.ref = "activityKtx" } From b2235d4c38e607ac8e04ee56156da37a498bd58f Mon Sep 17 00:00:00 2001 From: Tamim Hossain <132823494+CodeWithTamim@users.noreply.github.com> Date: Fri, 16 Aug 2024 08:09:53 +0600 Subject: [PATCH 11/20] Fix issue #3475 (#3485) Fix issue where the label in the tile and widget was not displaying as `v2rayNG` for Russian language users --- V2rayNG/app/src/main/res/values-ru/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 7619e002d..b862e53bf 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -1,7 +1,7 @@ - Переключить - Переключить + v2rayNG + v2rayNG Первое использование этой функции, пожалуйста, используйте приложение, чтобы добавить сервер Открыть панель навигации Закрыть панель навигации From b5451e9d3d091ec2e7523525187bb83eeb5b82b1 Mon Sep 17 00:00:00 2001 From: Tamim Hossain <132823494+CodeWithTamim@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:46:13 +0600 Subject: [PATCH 12/20] Update getSerializableExtra usage for Android 13 or later (#3489) Adjusted the usage of `getSerializableExtra` to handle its deprecation in Android 13 (API level 33) and later. Implemented the method requiring class type specification for retrieving `Pair` in newer API levels, while maintaining compatibility with older versions using the legacy approach. This change ensures proper functionality across all supported Android versions. --- .../main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt index 98700451f..f7bd84e14 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -383,7 +383,11 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } AppConfig.MSG_MEASURE_CONFIG_SUCCESS -> { - val resultPair = intent.getSerializableExtra("content") as Pair + val resultPair: Pair = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + intent.getSerializableExtra("content", Pair::class.java) as Pair + } else { + intent.getSerializableExtra("content") as Pair + } MmkvManager.encodeServerTestDelayMillis(resultPair.first, resultPair.second) updateListAction.value = getPosition(resultPair.first) } From b37d8c23694c4894209462ca2502556a4d21b078 Mon Sep 17 00:00:00 2001 From: Tamim Hossain <132823494+CodeWithTamim@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:49:29 +0600 Subject: [PATCH 13/20] Refactor null handling with .orEmpty() for better readability (#3490) Replaced null handling using `?: ""` with `.orEmpty()` for improved readability. The `.orEmpty()` extension function provides a more concise and expressive way to handle null strings by returning an empty string if the original string is null. This change enhances code clarity and consistency. --- .../kotlin/com/v2ray/ang/dto/V2rayConfig.kt | 24 +++++++++---------- .../kotlin/com/v2ray/ang/extension/_Ext.kt | 2 +- .../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 4 ++-- .../com/v2ray/ang/ui/UrlSchemeActivity.kt | 4 ++-- .../com/v2ray/ang/util/AngConfigManager.kt | 2 +- .../main/kotlin/com/v2ray/ang/util/Utils.kt | 2 +- .../com/v2ray/ang/util/V2rayConfigUtil.kt | 20 ++++++++-------- .../com/v2ray/ang/util/fmt/ShadowsocksFmt.kt | 6 ++--- .../com/v2ray/ang/util/fmt/TrojanFmt.kt | 18 +++++++------- .../kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt | 24 +++++++++---------- .../kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt | 8 +++---- .../com/v2ray/ang/util/fmt/WireguardFmt.kt | 4 ++-- .../com/v2ray/ang/viewmodel/MainViewModel.kt | 2 +- 13 files changed, 60 insertions(+), 60 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt index e15f5e4a5..e42b46bf8 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt @@ -249,14 +249,14 @@ data class V2rayConfig( tcpSetting.header.type = HTTP if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(path)) { val requestObj = TcpSettingsBean.HeaderBean.RequestBean() - requestObj.headers.Host = (host ?: "").split(",").map { it.trim() }.filter { it.isNotEmpty() } - requestObj.path = (path ?: "").split(",").map { it.trim() }.filter { it.isNotEmpty() } + requestObj.headers.Host = (host .orEmpty()).split(",").map { it.trim() }.filter { it.isNotEmpty() } + requestObj.path = (path .orEmpty()).split(",").map { it.trim() }.filter { it.isNotEmpty() } tcpSetting.header.request = requestObj sni = requestObj.headers.Host?.getOrNull(0) ?: sni } } else { tcpSetting.header.type = "none" - sni = host ?: "" + sni = host .orEmpty() } tcpSettings = tcpSetting } @@ -272,21 +272,21 @@ data class V2rayConfig( } "ws" -> { val wssetting = WsSettingsBean() - wssetting.headers.Host = host ?: "" + wssetting.headers.Host = host .orEmpty() sni = wssetting.headers.Host wssetting.path = path ?: "/" wsSettings = wssetting } "httpupgrade" -> { val httpupgradeSetting = HttpupgradeSettingsBean() - httpupgradeSetting.host = host ?: "" + httpupgradeSetting.host = host .orEmpty() sni = httpupgradeSetting.host httpupgradeSetting.path = path ?: "/" httpupgradeSettings = httpupgradeSetting } "splithttp" -> { val splithttpSetting = SplithttpSettingsBean() - splithttpSetting.host = host ?: "" + splithttpSetting.host = host .orEmpty() sni = splithttpSetting.host splithttpSetting.path = path ?: "/" splithttpSettings = splithttpSetting @@ -294,7 +294,7 @@ data class V2rayConfig( "h2", "http" -> { network = "h2" val h2Setting = HttpSettingsBean() - h2Setting.host = (host ?: "").split(",").map { it.trim() }.filter { it.isNotEmpty() } + h2Setting.host = (host .orEmpty()).split(",").map { it.trim() }.filter { it.isNotEmpty() } sni = h2Setting.host.getOrNull(0) ?: sni h2Setting.path = path ?: "/" httpSettings = h2Setting @@ -302,18 +302,18 @@ data class V2rayConfig( "quic" -> { val quicsetting = QuicSettingBean() quicsetting.security = quicSecurity ?: "none" - quicsetting.key = key ?: "" + quicsetting.key = key .orEmpty() quicsetting.header.type = headerType ?: "none" quicSettings = quicsetting } "grpc" -> { val grpcSetting = GrpcSettingsBean() grpcSetting.multiMode = mode == "multi" - grpcSetting.serviceName = serviceName ?: "" - grpcSetting.authority = authority ?: "" + grpcSetting.serviceName = serviceName .orEmpty() + grpcSetting.authority = authority .orEmpty() grpcSetting.idle_timeout = 60 grpcSetting.health_check_timeout = 20 - sni = authority ?: "" + sni = authority .orEmpty() grpcSettings = grpcSetting } } @@ -451,7 +451,7 @@ data class V2rayConfig( "grpc" -> { val grpcSetting = streamSettings?.grpcSettings ?: return null listOf(if (grpcSetting.multiMode == true) "multi" else "gun", - grpcSetting.authority ?: "", + grpcSetting.authority .orEmpty(), grpcSetting.serviceName) } else -> null diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt index 403774ac8..38e352ecb 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt @@ -54,7 +54,7 @@ val URLConnection.responseLength: Long } val URI.idnHost: String - get() = host?.replace("[", "")?.replace("]", "") ?: "" + get() = host?.replace("[", "")?.replace("]", "") .orEmpty() fun String.removeWhiteSpace(): String = replace("\\s+".toRegex(), "") diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt index b1e891125..9c8127611 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt @@ -61,7 +61,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter { val uri: Uri? = intent.data - val shareUrl = uri?.getQueryParameter("url") ?: "" + val shareUrl = uri?.getQueryParameter("url") .orEmpty() parseUri(shareUrl, uri?.fragment) } "install-sub" -> { val uri: Uri? = intent.data - val shareUrl = uri?.getQueryParameter("url") ?: "" + val shareUrl = uri?.getQueryParameter("url") .orEmpty() parseUri(shareUrl, uri?.fragment) } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt index 60317225c..2d557aa2a 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt @@ -434,7 +434,7 @@ object AngConfigManager { val removedSelectedServer = if (!TextUtils.isEmpty(subid) && !append) { MmkvManager.decodeServerConfig( - mainStorage?.decodeString(KEY_SELECTED_SERVER) ?: "" + mainStorage?.decodeString(KEY_SELECTED_SERVER) .orEmpty() )?.let { if (it.subscriptionId == subid) { return@let it diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt index 87a2f7c0c..6d7a51d42 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt @@ -97,7 +97,7 @@ object Utils { * base64 decode */ fun decode(text: String?): String { - return tryDecodeBase64(text) ?: text?.trimEnd('=')?.let { tryDecodeBase64(it) } ?: "" + return tryDecodeBase64(text) ?: text?.trimEnd('=')?.let { tryDecodeBase64(it) } .orEmpty() } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt index d8209ba69..59038feaa 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt @@ -188,25 +188,25 @@ object V2rayConfigUtil { routingUserRule( settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED) - ?: "", TAG_BLOCKED, v2rayConfig + .orEmpty(), TAG_BLOCKED, v2rayConfig ) if (routingMode == ERoutingMode.GLOBAL_DIRECT.value) { routingUserRule( settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) - ?: "", TAG_DIRECT, v2rayConfig + .orEmpty(), TAG_DIRECT, v2rayConfig ) routingUserRule( settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) - ?: "", TAG_PROXY, v2rayConfig + .orEmpty(), TAG_PROXY, v2rayConfig ) } else { routingUserRule( settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) - ?: "", TAG_PROXY, v2rayConfig + .orEmpty(), TAG_PROXY, v2rayConfig ) routingUserRule( settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) - ?: "", TAG_DIRECT, v2rayConfig + .orEmpty(), TAG_DIRECT, v2rayConfig ) } @@ -352,11 +352,11 @@ object V2rayConfigUtil { val geositeCn = arrayListOf("geosite:cn") val proxyDomain = userRule2Domain( settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) - ?: "" + .orEmpty() ) val directDomain = userRule2Domain( settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) - ?: "" + .orEmpty() ) // fakedns with all domains to make it always top priority v2rayConfig.dns.servers?.add( @@ -430,7 +430,7 @@ object V2rayConfigUtil { val remoteDns = Utils.getRemoteDnsServers() val proxyDomain = userRule2Domain( settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_AGENT) - ?: "" + .orEmpty() ) remoteDns.forEach { servers.add(it) @@ -450,7 +450,7 @@ object V2rayConfigUtil { val domesticDns = Utils.getDomesticDnsServers() val directDomain = userRule2Domain( settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_DIRECT) - ?: "" + .orEmpty() ) val routingMode = settingsStorage?.decodeString(AppConfig.PREF_ROUTING_MODE) ?: ERoutingMode.BYPASS_LAN_MAINLAND.value @@ -494,7 +494,7 @@ object V2rayConfigUtil { //block dns val blkDomain = userRule2Domain( settingsStorage?.decodeString(AppConfig.PREF_V2RAY_ROUTING_BLOCKED) - ?: "" + .orEmpty() ) if (blkDomain.size > 0) { hosts.putAll(blkDomain.map { it to "127.0.0.1" }) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt index d0710ddee..b1d9f532d 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt @@ -67,7 +67,7 @@ object ShadowsocksFmt { private fun tryResolveResolveSip002(str: String, config: ServerConfig): Boolean { try { val uri = URI(Utils.fixIllegalUrl(str)) - config.remarks = Utils.urlDecode(uri.fragment ?: "") + config.remarks = Utils.urlDecode(uri.fragment .orEmpty()) val method: String val password: String @@ -88,7 +88,7 @@ object ShadowsocksFmt { password = base64Decode.substringAfter(":") } - val query = Utils.urlDecode(uri.query ?: "") + val query = Utils.urlDecode(uri.query .orEmpty()) if (query != "") { val queryPairs = HashMap() val pairs = query.split(";") @@ -137,7 +137,7 @@ object ShadowsocksFmt { } if ("tls" in queryPairs) { config.outboundBean?.streamSettings?.populateTlsSettings( - "tls", false, sni ?: "", null, null, null, null, null + "tls", false, sni .orEmpty(), null, null, null, null, null ) } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt index 87b210b66..294dd0f4e 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt @@ -24,7 +24,7 @@ object TrojanFmt { val config = ServerConfig.create(EConfigType.TROJAN) val uri = URI(Utils.fixIllegalUrl(str)) - config.remarks = Utils.urlDecode(uri.fragment ?: "") + config.remarks = Utils.urlDecode(uri.fragment .orEmpty()) var flow = "" var fingerprint = config.outboundBean?.streamSettings?.tlsSettings?.fingerprint @@ -55,19 +55,19 @@ object TrojanFmt { queryParam["serviceName"], queryParam["authority"] ) - fingerprint = queryParam["fp"] ?: "" - allowInsecure = if ((queryParam["allowInsecure"] ?: "") == "1") true else allowInsecure + fingerprint = queryParam["fp"] .orEmpty() + allowInsecure = if ((queryParam["allowInsecure"] .orEmpty()) == "1") true else allowInsecure config.outboundBean?.streamSettings?.populateTlsSettings( queryParam["security"] ?: V2rayConfig.TLS, allowInsecure, - queryParam["sni"] ?: sni ?: "", + queryParam["sni"] ?: sni .orEmpty(), fingerprint, queryParam["alpn"], null, null, null ) - flow = queryParam["flow"] ?: "" + flow = queryParam["flow"] .orEmpty() } config.outboundBean?.settings?.servers?.get(0)?.let { server -> server.address = uri.idnHost @@ -102,16 +102,16 @@ object TrojanFmt { Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty() } if (!TextUtils.isEmpty(tlsSetting.fingerprint)) { - dicQuery["fp"] = tlsSetting.fingerprint ?: "" + dicQuery["fp"] = tlsSetting.fingerprint .orEmpty() } if (!TextUtils.isEmpty(tlsSetting.publicKey)) { - dicQuery["pbk"] = tlsSetting.publicKey ?: "" + dicQuery["pbk"] = tlsSetting.publicKey .orEmpty() } if (!TextUtils.isEmpty(tlsSetting.shortId)) { - dicQuery["sid"] = tlsSetting.shortId ?: "" + dicQuery["sid"] = tlsSetting.shortId .orEmpty() } if (!TextUtils.isEmpty(tlsSetting.spiderX)) { - dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX ?: "") + dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX .orEmpty()) } } dicQuery["type"] = diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt index 5e445d3c9..7955d018a 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt @@ -30,13 +30,13 @@ object VlessFmt { val streamSetting = config.outboundBean?.streamSettings ?: return null - config.remarks = Utils.urlDecode(uri.fragment ?: "") + config.remarks = Utils.urlDecode(uri.fragment .orEmpty()) config.outboundBean.settings?.vnext?.get(0)?.let { vnext -> vnext.address = uri.idnHost vnext.port = uri.port vnext.users[0].id = uri.userInfo vnext.users[0].encryption = queryParam["encryption"] ?: "none" - vnext.users[0].flow = queryParam["flow"] ?: "" + vnext.users[0].flow = queryParam["flow"] .orEmpty() } val sni = streamSetting.populateTransportSettings( @@ -51,16 +51,16 @@ object VlessFmt { queryParam["serviceName"], queryParam["authority"] ) - allowInsecure = if ((queryParam["allowInsecure"] ?: "") == "1") true else allowInsecure + allowInsecure = if ((queryParam["allowInsecure"] .orEmpty()) == "1") true else allowInsecure streamSetting.populateTlsSettings( - queryParam["security"] ?: "", + queryParam["security"] .orEmpty(), allowInsecure, queryParam["sni"] ?: sni, - queryParam["fp"] ?: "", + queryParam["fp"] .orEmpty(), queryParam["alpn"], - queryParam["pbk"] ?: "", - queryParam["sid"] ?: "", - queryParam["spx"] ?: "" + queryParam["pbk"] .orEmpty(), + queryParam["sid"] .orEmpty(), + queryParam["spx"] .orEmpty() ) return config @@ -93,16 +93,16 @@ object VlessFmt { Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty() } if (!TextUtils.isEmpty(tlsSetting.fingerprint)) { - dicQuery["fp"] = tlsSetting.fingerprint ?: "" + dicQuery["fp"] = tlsSetting.fingerprint .orEmpty() } if (!TextUtils.isEmpty(tlsSetting.publicKey)) { - dicQuery["pbk"] = tlsSetting.publicKey ?: "" + dicQuery["pbk"] = tlsSetting.publicKey .orEmpty() } if (!TextUtils.isEmpty(tlsSetting.shortId)) { - dicQuery["sid"] = tlsSetting.shortId ?: "" + dicQuery["sid"] = tlsSetting.shortId .orEmpty() } if (!TextUtils.isEmpty(tlsSetting.spiderX)) { - dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX ?: "") + dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX .orEmpty()) } } dicQuery["type"] = diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt index 55e30b29f..da15a12a6 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt @@ -121,7 +121,7 @@ object VmessFmt { val streamSetting = config.outboundBean?.streamSettings ?: return null - config.remarks = Utils.urlDecode(uri.fragment ?: "") + config.remarks = Utils.urlDecode(uri.fragment .orEmpty()) config.outboundBean.settings?.vnext?.get(0)?.let { vnext -> vnext.address = uri.idnHost vnext.port = uri.port @@ -143,12 +143,12 @@ object VmessFmt { queryParam["authority"] ) - allowInsecure = if ((queryParam["allowInsecure"] ?: "") == "1") true else allowInsecure + allowInsecure = if ((queryParam["allowInsecure"] .orEmpty()) == "1") true else allowInsecure streamSetting.populateTlsSettings( - queryParam["security"] ?: "", + queryParam["security"] .orEmpty(), allowInsecure, queryParam["sni"] ?: sni, - queryParam["fp"] ?: "", + queryParam["fp"] .orEmpty(), queryParam["alpn"], null, null, diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt index 84efeb0ec..c711c85d1 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt @@ -13,7 +13,7 @@ object WireguardFmt { val uri = URI(Utils.fixIllegalUrl(str)) if (uri.rawQuery != null) { val config = ServerConfig.create(EConfigType.WIREGUARD) - config.remarks = Utils.urlDecode(uri.fragment ?: "") + config.remarks = Utils.urlDecode(uri.fragment .orEmpty()) val queryParam = uri.rawQuery.split("&") .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } } @@ -24,7 +24,7 @@ object WireguardFmt { (queryParam["address"] ?: AppConfig.WIREGUARD_LOCAL_ADDRESS_V4).removeWhiteSpace() .split(",") - wireguard.peers?.get(0)?.publicKey = queryParam["publickey"] ?: "" + wireguard.peers?.get(0)?.publicKey = queryParam["publickey"] .orEmpty() wireguard.peers?.get(0)?.endpoint = Utils.getIpv6Address(uri.idnHost) + ":${uri.port}" wireguard.mtu = Utils.parseInt(queryParam["mtu"] ?: AppConfig.WIREGUARD_LOCAL_MTU) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt index f7bd84e14..e24017598 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -43,7 +43,7 @@ import java.util.Collections class MainViewModel(application: Application) : AndroidViewModel(application) { private var serverList = MmkvManager.decodeServerList() - var subscriptionId: String = MmkvManager.settingsStorage.decodeString(AppConfig.CACHE_SUBSCRIPTION_ID, "") ?: "" + var subscriptionId: String = MmkvManager.settingsStorage.decodeString(AppConfig.CACHE_SUBSCRIPTION_ID, "").orEmpty() //var keywordFilter: String = MmkvManager.settingsStorage.decodeString(AppConfig.CACHE_KEYWORD_FILTER, "")?:"" private var keywordFilter = "" From 8b806fe0bed62ac756f7a42724f18e3b04bb324a Mon Sep 17 00:00:00 2001 From: Tamim Hossain <132823494+CodeWithTamim@users.noreply.github.com> Date: Fri, 16 Aug 2024 15:50:04 +0600 Subject: [PATCH 14/20] Fix logcat flush blocking call on the wrong dispatcher (#3491) * Fix logcat flush blocking call on the wrong dispatcher Moved the blocking `process.waitFor()` call to `Dispatchers.IO` to avoid potential thread starvation on `Dispatchers.Default`. This change ensures that the I/O-bound operation does not interfere with CPU-bound tasks, improving overall performance and responsiveness. * Fix logcat flush blocking call on the wrong dispatcher Moved the blocking `process.waitFor()` call to `Dispatchers.IO` to avoid potential thread starvation on `Dispatchers.Default`. This change ensures that the I/O-bound operation does not interfere with CPU-bound tasks, improving overall performance and responsiveness. --- .../kotlin/com/v2ray/ang/ui/LogcatActivity.kt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/LogcatActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/LogcatActivity.kt index bd35637e0..5ae0f3571 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/LogcatActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/LogcatActivity.kt @@ -1,8 +1,8 @@ package com.v2ray.ang.ui +import android.os.Bundle import android.os.Handler import android.os.Looper -import android.os.Bundle import android.text.method.ScrollingMovementMethod import android.view.Menu import android.view.MenuItem @@ -14,11 +14,9 @@ import com.v2ray.ang.databinding.ActivityLogcatBinding import com.v2ray.ang.extension.toast import com.v2ray.ang.util.Utils import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch - +import kotlinx.coroutines.withContext import java.io.IOException -import java.util.LinkedHashSet class LogcatActivity : BaseActivity() { private val binding by lazy { @@ -44,8 +42,10 @@ class LogcatActivity : BaseActivity() { val lst = LinkedHashSet() lst.add("logcat") lst.add("-c") - val process = Runtime.getRuntime().exec(lst.toTypedArray()) - process.waitFor() + withContext(Dispatchers.IO) { + val process = Runtime.getRuntime().exec(lst.toTypedArray()) + process.waitFor() + } } val lst = LinkedHashSet() lst.add("logcat") @@ -54,7 +54,9 @@ class LogcatActivity : BaseActivity() { lst.add("time") lst.add("-s") lst.add("GoLog,tun2socks,${ANG_PACKAGE},AndroidRuntime,System.err") - val process = Runtime.getRuntime().exec(lst.toTypedArray()) + val process = withContext(Dispatchers.IO) { + Runtime.getRuntime().exec(lst.toTypedArray()) + } // val bufferedReader = BufferedReader( // InputStreamReader(process.inputStream)) // val allText = bufferedReader.use(BufferedReader::readText) From 7f3c6b4665dfda66db949e5028b7e543220d8d60 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 16 Aug 2024 17:59:36 +0800 Subject: [PATCH 15/20] Bug fix --- .../kotlin/com/v2ray/ang/dto/V2rayConfig.kt | 18 +++++++++--------- .../kotlin/com/v2ray/ang/extension/_Ext.kt | 2 +- .../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 4 ++-- .../com/v2ray/ang/ui/UrlSchemeActivity.kt | 4 ++-- .../com/v2ray/ang/util/AngConfigManager.kt | 2 +- .../main/kotlin/com/v2ray/ang/util/Utils.kt | 2 +- .../com/v2ray/ang/util/fmt/ShadowsocksFmt.kt | 2 +- .../kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt | 12 ++++++------ .../kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt | 18 +++++++++--------- .../kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt | 4 ++-- .../com/v2ray/ang/util/fmt/WireguardFmt.kt | 2 +- 11 files changed, 35 insertions(+), 35 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt index e42b46bf8..4224debe4 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt @@ -256,7 +256,7 @@ data class V2rayConfig( } } else { tcpSetting.header.type = "none" - sni = host .orEmpty() + sni = host.orEmpty() } tcpSettings = tcpSetting } @@ -272,21 +272,21 @@ data class V2rayConfig( } "ws" -> { val wssetting = WsSettingsBean() - wssetting.headers.Host = host .orEmpty() + wssetting.headers.Host = host.orEmpty() sni = wssetting.headers.Host wssetting.path = path ?: "/" wsSettings = wssetting } "httpupgrade" -> { val httpupgradeSetting = HttpupgradeSettingsBean() - httpupgradeSetting.host = host .orEmpty() + httpupgradeSetting.host = host.orEmpty() sni = httpupgradeSetting.host httpupgradeSetting.path = path ?: "/" httpupgradeSettings = httpupgradeSetting } "splithttp" -> { val splithttpSetting = SplithttpSettingsBean() - splithttpSetting.host = host .orEmpty() + splithttpSetting.host = host.orEmpty() sni = splithttpSetting.host splithttpSetting.path = path ?: "/" splithttpSettings = splithttpSetting @@ -302,18 +302,18 @@ data class V2rayConfig( "quic" -> { val quicsetting = QuicSettingBean() quicsetting.security = quicSecurity ?: "none" - quicsetting.key = key .orEmpty() + quicsetting.key = key.orEmpty() quicsetting.header.type = headerType ?: "none" quicSettings = quicsetting } "grpc" -> { val grpcSetting = GrpcSettingsBean() grpcSetting.multiMode = mode == "multi" - grpcSetting.serviceName = serviceName .orEmpty() - grpcSetting.authority = authority .orEmpty() + grpcSetting.serviceName = serviceName.orEmpty() + grpcSetting.authority = authority.orEmpty() grpcSetting.idle_timeout = 60 grpcSetting.health_check_timeout = 20 - sni = authority .orEmpty() + sni = authority.orEmpty() grpcSettings = grpcSetting } } @@ -451,7 +451,7 @@ data class V2rayConfig( "grpc" -> { val grpcSetting = streamSettings?.grpcSettings ?: return null listOf(if (grpcSetting.multiMode == true) "multi" else "gun", - grpcSetting.authority .orEmpty(), + grpcSetting.authority.orEmpty(), grpcSetting.serviceName) } else -> null diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt index 38e352ecb..e39d16f26 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt @@ -54,7 +54,7 @@ val URLConnection.responseLength: Long } val URI.idnHost: String - get() = host?.replace("[", "")?.replace("]", "") .orEmpty() + get() = host?.replace("[", "")?.replace("]", "").orEmpty() fun String.removeWhiteSpace(): String = replace("\\s+".toRegex(), "") diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt index 9c8127611..c0141c9bd 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt @@ -61,7 +61,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter { val uri: Uri? = intent.data - val shareUrl = uri?.getQueryParameter("url") .orEmpty() + val shareUrl = uri?.getQueryParameter("url").orEmpty() parseUri(shareUrl, uri?.fragment) } "install-sub" -> { val uri: Uri? = intent.data - val shareUrl = uri?.getQueryParameter("url") .orEmpty() + val shareUrl = uri?.getQueryParameter("url").orEmpty() parseUri(shareUrl, uri?.fragment) } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt index 2d557aa2a..37bf1fd87 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt @@ -434,7 +434,7 @@ object AngConfigManager { val removedSelectedServer = if (!TextUtils.isEmpty(subid) && !append) { MmkvManager.decodeServerConfig( - mainStorage?.decodeString(KEY_SELECTED_SERVER) .orEmpty() + mainStorage?.decodeString(KEY_SELECTED_SERVER).orEmpty() )?.let { if (it.subscriptionId == subid) { return@let it diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt index 6d7a51d42..a01ee24ae 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt @@ -97,7 +97,7 @@ object Utils { * base64 decode */ fun decode(text: String?): String { - return tryDecodeBase64(text) ?: text?.trimEnd('=')?.let { tryDecodeBase64(it) } .orEmpty() + return tryDecodeBase64(text) ?: text?.trimEnd('=')?.let { tryDecodeBase64(it) }.orEmpty() } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt index b1d9f532d..749e67d39 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt @@ -137,7 +137,7 @@ object ShadowsocksFmt { } if ("tls" in queryPairs) { config.outboundBean?.streamSettings?.populateTlsSettings( - "tls", false, sni .orEmpty(), null, null, null, null, null + "tls", false, sni.orEmpty(), null, null, null, null, null ) } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt index 294dd0f4e..90fa94124 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt @@ -55,19 +55,19 @@ object TrojanFmt { queryParam["serviceName"], queryParam["authority"] ) - fingerprint = queryParam["fp"] .orEmpty() + fingerprint = queryParam["fp"].orEmpty() allowInsecure = if ((queryParam["allowInsecure"] .orEmpty()) == "1") true else allowInsecure config.outboundBean?.streamSettings?.populateTlsSettings( queryParam["security"] ?: V2rayConfig.TLS, allowInsecure, - queryParam["sni"] ?: sni .orEmpty(), + queryParam["sni"] ?: sni.orEmpty(), fingerprint, queryParam["alpn"], null, null, null ) - flow = queryParam["flow"] .orEmpty() + flow = queryParam["flow"].orEmpty() } config.outboundBean?.settings?.servers?.get(0)?.let { server -> server.address = uri.idnHost @@ -102,13 +102,13 @@ object TrojanFmt { Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty() } if (!TextUtils.isEmpty(tlsSetting.fingerprint)) { - dicQuery["fp"] = tlsSetting.fingerprint .orEmpty() + dicQuery["fp"] = tlsSetting.fingerprint.orEmpty() } if (!TextUtils.isEmpty(tlsSetting.publicKey)) { - dicQuery["pbk"] = tlsSetting.publicKey .orEmpty() + dicQuery["pbk"] = tlsSetting.publicKey.orEmpty() } if (!TextUtils.isEmpty(tlsSetting.shortId)) { - dicQuery["sid"] = tlsSetting.shortId .orEmpty() + dicQuery["sid"] = tlsSetting.shortId.orEmpty() } if (!TextUtils.isEmpty(tlsSetting.spiderX)) { dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX .orEmpty()) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt index 7955d018a..3196305a4 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt @@ -36,7 +36,7 @@ object VlessFmt { vnext.port = uri.port vnext.users[0].id = uri.userInfo vnext.users[0].encryption = queryParam["encryption"] ?: "none" - vnext.users[0].flow = queryParam["flow"] .orEmpty() + vnext.users[0].flow = queryParam["flow"].orEmpty() } val sni = streamSetting.populateTransportSettings( @@ -53,14 +53,14 @@ object VlessFmt { ) allowInsecure = if ((queryParam["allowInsecure"] .orEmpty()) == "1") true else allowInsecure streamSetting.populateTlsSettings( - queryParam["security"] .orEmpty(), + queryParam["security"].orEmpty(), allowInsecure, queryParam["sni"] ?: sni, - queryParam["fp"] .orEmpty(), + queryParam["fp"].orEmpty(), queryParam["alpn"], - queryParam["pbk"] .orEmpty(), - queryParam["sid"] .orEmpty(), - queryParam["spx"] .orEmpty() + queryParam["pbk"].orEmpty(), + queryParam["sid"].orEmpty(), + queryParam["spx"].orEmpty() ) return config @@ -93,13 +93,13 @@ object VlessFmt { Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty() } if (!TextUtils.isEmpty(tlsSetting.fingerprint)) { - dicQuery["fp"] = tlsSetting.fingerprint .orEmpty() + dicQuery["fp"] = tlsSetting.fingerprint.orEmpty() } if (!TextUtils.isEmpty(tlsSetting.publicKey)) { - dicQuery["pbk"] = tlsSetting.publicKey .orEmpty() + dicQuery["pbk"] = tlsSetting.publicKey.orEmpty() } if (!TextUtils.isEmpty(tlsSetting.shortId)) { - dicQuery["sid"] = tlsSetting.shortId .orEmpty() + dicQuery["sid"] = tlsSetting.shortId.orEmpty() } if (!TextUtils.isEmpty(tlsSetting.spiderX)) { dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX .orEmpty()) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt index da15a12a6..313700a6c 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt @@ -145,10 +145,10 @@ object VmessFmt { allowInsecure = if ((queryParam["allowInsecure"] .orEmpty()) == "1") true else allowInsecure streamSetting.populateTlsSettings( - queryParam["security"] .orEmpty(), + queryParam["security"].orEmpty(), allowInsecure, queryParam["sni"] ?: sni, - queryParam["fp"] .orEmpty(), + queryParam["fp"].orEmpty(), queryParam["alpn"], null, null, diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt index c711c85d1..8375870a7 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt @@ -24,7 +24,7 @@ object WireguardFmt { (queryParam["address"] ?: AppConfig.WIREGUARD_LOCAL_ADDRESS_V4).removeWhiteSpace() .split(",") - wireguard.peers?.get(0)?.publicKey = queryParam["publickey"] .orEmpty() + wireguard.peers?.get(0)?.publicKey = queryParam["publickey"].orEmpty() wireguard.peers?.get(0)?.endpoint = Utils.getIpv6Address(uri.idnHost) + ":${uri.port}" wireguard.mtu = Utils.parseInt(queryParam["mtu"] ?: AppConfig.WIREGUARD_LOCAL_MTU) From f040fa5c083cc0fef6fd043a929c500eea881658 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Fri, 16 Aug 2024 18:12:53 +0800 Subject: [PATCH 16/20] Bug fix https://github.com/2dust/v2rayNG/issues/3488 --- .../kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt index 1bd7275f3..f760b3f31 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt @@ -8,7 +8,7 @@ import android.widget.Toast import androidx.appcompat.app.AlertDialog import com.blacksquircle.ui.editorkit.utils.EditorTheme import com.blacksquircle.ui.language.json.JsonLanguage -import com.google.gson.* +import com.google.gson.Gson import com.tencent.mmkv.MMKV import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivityServerCustomConfigBinding @@ -89,7 +89,7 @@ class ServerCustomConfigActivity : BaseActivity() { } val config = MmkvManager.decodeServerConfig(editGuid) ?: ServerConfig.create(EConfigType.CUSTOM) - config.remarks = v2rayConfig.remarks ?: binding.etRemarks.text.toString().trim() + config.remarks = if (binding.etRemarks.text.isNullOrEmpty()) v2rayConfig.remarks.orEmpty() else binding.etRemarks.text.toString() config.fullConfig = v2rayConfig MmkvManager.encodeServerConfig(editGuid, config) From fee0a016d8b356da58aaccd03e86c6072da534a2 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 17 Aug 2024 14:58:48 +0800 Subject: [PATCH 17/20] Optimized search --- .../src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt | 8 ++++---- .../kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt | 11 +++++++---- V2rayNG/app/src/main/res/menu/menu_main.xml | 4 ++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt index f6af32804..8a78473d8 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -236,12 +236,12 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList if (searchItem != null) { val searchView = searchItem.actionView as SearchView searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { - override fun onQueryTextSubmit(query: String?): Boolean { - mainViewModel.filterConfig(query.orEmpty()) + override fun onQueryTextSubmit(query: String?): Boolean = false + + override fun onQueryTextChange(newText: String?): Boolean { + mainViewModel.filterConfig(newText.orEmpty()) return false } - - override fun onQueryTextChange(newText: String?): Boolean = false }) searchView.setOnCloseListener { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt index e24017598..43c3a9c38 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -46,7 +46,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { var subscriptionId: String = MmkvManager.settingsStorage.decodeString(AppConfig.CACHE_SUBSCRIPTION_ID, "").orEmpty() //var keywordFilter: String = MmkvManager.settingsStorage.decodeString(AppConfig.CACHE_KEYWORD_FILTER, "")?:"" - private var keywordFilter = "" + var keywordFilter = "" val serversCache = mutableListOf() val isRunning by lazy { MutableLiveData() } val updateListAction by lazy { MutableLiveData() } @@ -170,7 +170,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { fun exportAllServer(): Int { val serverListCopy = - if (subscriptionId.isNullOrEmpty()) { + if (subscriptionId.isNullOrEmpty() && keywordFilter.isNullOrEmpty()) { serverList } else { serversCache.map { it.guid }.toList() @@ -295,7 +295,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } fun removeAllServer() { - if (subscriptionId.isNullOrEmpty()) { + if (subscriptionId.isNullOrEmpty() && keywordFilter.isNullOrEmpty()) { MmkvManager.removeAllServer() } else { val serversCopy = serversCache.toList() @@ -306,7 +306,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } fun removeInvalidServer() { - if (subscriptionId.isNullOrEmpty()) { + if (subscriptionId.isNullOrEmpty() && keywordFilter.isNullOrEmpty()) { MmkvManager.removeInvalidServer("") } else { val serversCopy = serversCache.toList() @@ -348,6 +348,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } fun filterConfig(keyword: String) { + if (keyword == keywordFilter) { + return + } keywordFilter = keyword MmkvManager.settingsStorage.encode(AppConfig.CACHE_KEYWORD_FILTER, keywordFilter) reloadServerList() diff --git a/V2rayNG/app/src/main/res/menu/menu_main.xml b/V2rayNG/app/src/main/res/menu/menu_main.xml index 8edaf8a10..5bcba5e7e 100644 --- a/V2rayNG/app/src/main/res/menu/menu_main.xml +++ b/V2rayNG/app/src/main/res/menu/menu_main.xml @@ -3,10 +3,10 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> + app:showAsAction="always|collapseActionView"/> Date: Sat, 17 Aug 2024 12:59:21 +0600 Subject: [PATCH 18/20] Removed internet check (#3494) Removed the internet connection check before connecting as per the request in issue #3486 --- .../kotlin/com/v2ray/ang/extension/_Ext.kt | 18 +----------------- .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 10 +++------- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt index e39d16f26..87fa3260f 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt @@ -56,20 +56,4 @@ val URLConnection.responseLength: Long val URI.idnHost: String get() = host?.replace("[", "")?.replace("]", "").orEmpty() -fun String.removeWhiteSpace(): String = replace("\\s+".toRegex(), "") - -val Context.isNetworkConnected: Boolean - get() { - val manager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) - manager.getNetworkCapabilities(manager.activeNetwork)?.let { - it.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || - it.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) || - it.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) || - it.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) || - it.hasTransport(NetworkCapabilities.TRANSPORT_VPN) - } ?: false - else - @Suppress("DEPRECATION") - manager.activeNetworkInfo?.isConnectedOrConnecting == true - } \ No newline at end of file +fun String.removeWhiteSpace(): String = replace("\\s+".toRegex(), "") \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt index 8a78473d8..20d4df330 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -199,14 +199,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } fun startV2Ray() { - if (isNetworkConnected) { - if (MmkvManager.mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) { - return - } - V2RayServiceManager.startV2Ray(this) - } else { - ToastCompat.makeText(this, getString(R.string.connection_test_fail), Toast.LENGTH_LONG).show() + if (MmkvManager.mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) { + return } + V2RayServiceManager.startV2Ray(this) } fun restartV2Ray() { From 51adca8568745ce94431cb200c7b9ba46a70f356 Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 17 Aug 2024 19:40:04 +0800 Subject: [PATCH 19/20] Format code --- V2rayNG/app/build.gradle.kts | 9 +- V2rayNG/app/src/main/AndroidManifest.xml | 92 ++- .../ang/helper/ItemTouchHelperAdapter.java | 4 +- .../helper/SimpleItemTouchHelperCallback.java | 3 +- .../kotlin/com/v2ray/ang/AngApplication.kt | 12 +- .../main/kotlin/com/v2ray/ang/AppConfig.kt | 1 + .../main/kotlin/com/v2ray/ang/dto/AppInfo.kt | 12 +- .../kotlin/com/v2ray/ang/dto/EConfigType.kt | 2 +- .../kotlin/com/v2ray/ang/dto/ERoutingMode.kt | 2 +- .../kotlin/com/v2ray/ang/dto/ServerConfig.kt | 46 +- .../kotlin/com/v2ray/ang/dto/ServersCache.kt | 6 +- .../kotlin/com/v2ray/ang/dto/V2rayConfig.kt | 678 ++++++++++-------- .../kotlin/com/v2ray/ang/dto/VmessQRCode.kt | 32 +- .../kotlin/com/v2ray/ang/extension/_Ext.kt | 2 - .../com/v2ray/ang/receiver/WidgetProvider.kt | 16 +- .../com/v2ray/ang/service/QSTileService.kt | 5 + .../v2ray/ang/service/SubscriptionUpdater.kt | 1 - .../ang/service/V2RayProxyOnlyService.kt | 2 +- .../v2ray/ang/service/V2RayServiceManager.kt | 123 ++-- .../com/v2ray/ang/service/V2RayTestService.kt | 7 +- .../com/v2ray/ang/service/V2RayVpnService.kt | 55 +- .../kotlin/com/v2ray/ang/ui/AboutActivity.kt | 2 +- .../kotlin/com/v2ray/ang/ui/BaseActivity.kt | 3 +- .../com/v2ray/ang/ui/FragmentAdapter.kt | 2 +- .../kotlin/com/v2ray/ang/ui/LogcatActivity.kt | 2 + .../kotlin/com/v2ray/ang/ui/MainActivity.kt | 1 - .../com/v2ray/ang/ui/MainRecyclerAdapter.kt | 5 +- .../com/v2ray/ang/ui/PerAppProxyActivity.kt | 69 +- .../com/v2ray/ang/ui/PerAppProxyAdapter.kt | 13 +- .../v2ray/ang/ui/RoutingSettingsActivity.kt | 2 +- .../v2ray/ang/ui/RoutingSettingsFragment.kt | 44 +- .../com/v2ray/ang/ui/ScScannerActivity.kt | 14 +- .../com/v2ray/ang/ui/ScSwitchActivity.kt | 4 +- .../com/v2ray/ang/ui/ScannerActivity.kt | 13 +- .../kotlin/com/v2ray/ang/ui/ServerActivity.kt | 20 +- .../ang/ui/ServerCustomConfigActivity.kt | 18 +- .../com/v2ray/ang/ui/SettingsActivity.kt | 8 +- .../com/v2ray/ang/ui/SubEditActivity.kt | 24 +- .../kotlin/com/v2ray/ang/ui/TaskerActivity.kt | 22 +- .../com/v2ray/ang/ui/UserAssetActivity.kt | 59 +- .../com/v2ray/ang/ui/UserAssetUrlActivity.kt | 4 +- .../com/v2ray/ang/util/AngConfigManager.kt | 2 +- .../main/kotlin/com/v2ray/ang/util/Utils.kt | 25 +- .../com/v2ray/ang/util/fmt/ShadowsocksFmt.kt | 4 +- .../com/v2ray/ang/util/fmt/TrojanFmt.kt | 8 +- .../kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt | 6 +- .../kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt | 4 +- .../com/v2ray/ang/util/fmt/WireguardFmt.kt | 2 +- .../res/drawable-night/ic_action_done.xml | 12 +- .../main/res/drawable-night/ic_add_24dp.xml | 10 +- .../src/main/res/drawable-night/ic_copy.xml | 10 +- .../res/drawable-night/ic_delete_24dp.xml | 10 +- .../drawable-night/ic_description_24dp.xml | 10 +- .../main/res/drawable-night/ic_edit_24dp.xml | 10 +- .../main/res/drawable-night/ic_file_24dp.xml | 6 +- .../res/drawable-night/ic_logcat_24dp.xml | 6 +- .../ic_outline_filter_alt_24.xml | 13 +- .../res/drawable-night/ic_promotion_24dp.xml | 4 +- .../main/res/drawable-night/ic_save_24dp.xml | 10 +- .../main/res/drawable-night/ic_share_24dp.xml | 10 +- .../res/drawable-night/ic_telegram_24dp.xml | 6 +- .../src/main/res/drawable/ic_action_done.xml | 12 +- .../app/src/main/res/drawable/ic_add_24dp.xml | 10 +- V2rayNG/app/src/main/res/drawable/ic_copy.xml | 10 +- .../src/main/res/drawable/ic_delete_24dp.xml | 10 +- .../main/res/drawable/ic_description_24dp.xml | 10 +- .../src/main/res/drawable/ic_edit_24dp.xml | 10 +- .../src/main/res/drawable/ic_file_24dp.xml | 6 +- .../src/main/res/drawable/ic_logcat_24dp.xml | 18 +- .../res/drawable/ic_outline_filter_alt_24.xml | 13 +- .../main/res/drawable/ic_promotion_24dp.xml | 12 +- .../src/main/res/drawable/ic_save_24dp.xml | 10 +- .../src/main/res/drawable/ic_share_24dp.xml | 10 +- .../main/res/drawable/ic_source_code_24dp.xml | 11 +- .../app/src/main/res/layout/activity_main.xml | 6 +- .../app/src/main/res/layout/activity_none.xml | 3 +- .../res/layout/activity_routing_settings.xml | 1 - .../layout/activity_server_custom_config.xml | 10 +- .../layout/activity_server_shadowsocks.xml | 4 +- .../res/layout/activity_server_trojan.xml | 2 +- .../main/res/layout/activity_server_vless.xml | 12 +- .../main/res/layout/activity_server_vmess.xml | 2 +- .../res/layout/activity_server_wireguard.xml | 3 + .../main/res/layout/activity_sub_setting.xml | 3 +- .../res/layout/activity_user_asset_url.xml | 112 +-- .../main/res/layout/dialog_config_filter.xml | 2 +- .../src/main/res/layout/layout_progress.xml | 10 +- .../app/src/main/res/layout/nav_header.xml | 1 - .../res/layout/preference_with_help_link.xml | 12 +- .../app/src/main/res/layout/tls_layout.xml | 18 +- V2rayNG/app/src/main/res/menu/menu_asset.xml | 2 +- .../src/main/res/menu/menu_bypass_list.xml | 2 +- V2rayNG/app/src/main/res/menu/menu_drawer.xml | 9 +- V2rayNG/app/src/main/res/menu/menu_main.xml | 2 +- .../main/res/mipmap-anydpi-v26/ic_banner.xml | 6 +- .../res/mipmap-anydpi-v26/ic_launcher.xml | 4 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 4 +- .../app/src/main/res/values-ar/strings.xml | 4 +- V2rayNG/app/src/main/res/values/arrays.xml | 2 +- V2rayNG/app/src/main/res/values/attrs.xml | 1 + V2rayNG/app/src/main/res/xml/cache_paths.xml | 4 +- .../java/com/v2ray/ang/ExampleUnitTest.java | 4 +- .../kotlin/com/v2ray/ang/ExampleUnitTest.kt | 66 +- 103 files changed, 1149 insertions(+), 911 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index 1fb6789fc..ffb2c46cc 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -68,12 +68,9 @@ android { "universal" output.outputFileName = "v2rayNG_${variant.versionName}_${abi}.apk" - if(versionCodes.containsKey(abi)) - { + if (versionCodes.containsKey(abi)) { output.versionCodeOverride = (1000000 * versionCodes[abi]!!).plus(variant.versionCode) - } - else - { + } else { return@forEach } } @@ -92,7 +89,7 @@ android { } dependencies { - implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar","*.jar")))) + implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar", "*.jar")))) testImplementation(libs.junit) implementation(libs.flexbox) diff --git a/V2rayNG/app/src/main/AndroidManifest.xml b/V2rayNG/app/src/main/AndroidManifest.xml index 42cfc2618..d5301680a 100644 --- a/V2rayNG/app/src/main/AndroidManifest.xml +++ b/V2rayNG/app/src/main/AndroidManifest.xml @@ -8,18 +8,29 @@ android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" - android:xlargeScreens="true"/> + android:xlargeScreens="true" /> - + - - - - + + + + - + @@ -31,7 +42,7 @@ android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" android:minSdkVersion="34" /> - + @@ -53,12 +64,14 @@ android:theme="@style/AppThemeDayNight.NoActionBar"> + - + + @@ -119,12 +132,14 @@ - - - - - - + + + + + + + + - + - - + android:process=":RunSoLibV2RayDaemon"> + android:exported="true" + android:name=".receiver.WidgetProvider" + android:process=":RunSoLibV2RayDaemon"> + android:name="android.appwidget.provider" + android:resource="@xml/app_widget_provider" /> @@ -180,13 +196,13 @@ + android:exported="true" + android:name=".service.QSTileService" + android:icon="@drawable/ic_stat_name" + android:label="@string/app_tile_name" + android:foregroundServiceType="specialUse" + android:permission="android.permission.BIND_QUICK_SETTINGS_TILE" + android:process=":RunSoLibV2RayDaemon"> @@ -208,7 +224,7 @@ + android:process=":RunSoLibV2RayDaemon"> @@ -234,7 +250,7 @@ android:grantUriPermissions="true"> + android:resource="@xml/cache_paths" /> diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/ItemTouchHelperAdapter.java b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/ItemTouchHelperAdapter.java index 2de0c74ac..566feffcc 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/ItemTouchHelperAdapter.java +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/ItemTouchHelperAdapter.java @@ -16,8 +16,8 @@ package com.v2ray.ang.helper; -import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.RecyclerView; /** * Interface to listen for a move or dismissal event from a {@link ItemTouchHelper.Callback}. @@ -36,7 +36,6 @@ public interface ItemTouchHelperAdapter { * @param fromPosition The start position of the moved item. * @param toPosition Then resolved position of the moved item. * @return True if the item was moved to the new adapter position. - * * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder) * @see RecyclerView.ViewHolder#getAdapterPosition() */ @@ -52,7 +51,6 @@ public interface ItemTouchHelperAdapter { * adjusting the underlying data to reflect this removal. * * @param position The position of the item dismissed. - * * @see RecyclerView#getAdapterPositionFor(RecyclerView.ViewHolder) * @see RecyclerView.ViewHolder#getAdapterPosition() */ diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/SimpleItemTouchHelperCallback.java b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/SimpleItemTouchHelperCallback.java index 97fceab73..e3686921b 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/SimpleItemTouchHelperCallback.java +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/SimpleItemTouchHelperCallback.java @@ -17,9 +17,10 @@ package com.v2ray.ang.helper; import android.graphics.Canvas; + import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.ItemTouchHelper; +import androidx.recyclerview.widget.RecyclerView; import org.jetbrains.annotations.NotNull; diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt index 0a900e0da..a07e7f994 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AngApplication.kt @@ -7,16 +7,13 @@ import androidx.work.WorkManager import com.tencent.mmkv.MMKV import com.v2ray.ang.util.Utils -class AngApplication : MultiDexApplication() -{ - companion object - { +class AngApplication : MultiDexApplication() { + companion object { //const val PREF_LAST_VERSION = "pref_last_version" lateinit var application: AngApplication } - override fun attachBaseContext(base: Context?) - { + override fun attachBaseContext(base: Context?) { super.attachBaseContext(base) application = this } @@ -25,8 +22,7 @@ class AngApplication : MultiDexApplication() .setDefaultProcessName("${BuildConfig.APPLICATION_ID}:bg") .build() - override fun onCreate() - { + override fun onCreate() { super.onCreate() // LeakCanary.install(this) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt index 786b1db70..b564a41b1 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt @@ -140,6 +140,7 @@ object AppConfig { const val RAY_NG_CHANNEL_NAME = "V2rayNG Background Service" const val SUBSCRIPTION_UPDATE_CHANNEL = "subscription_update_channel" const val SUBSCRIPTION_UPDATE_CHANNEL_NAME = "Subscription Update Service" + /** Protocols Scheme **/ const val VMESS = "vmess://" const val CUSTOM = "" diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/AppInfo.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/AppInfo.kt index f99655a85..219e35e96 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/AppInfo.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/AppInfo.kt @@ -2,8 +2,10 @@ package com.v2ray.ang.dto import android.graphics.drawable.Drawable -data class AppInfo(val appName: String, - val packageName: String, - val appIcon: Drawable, - val isSystemApp: Boolean, - var isSelected: Int) \ No newline at end of file +data class AppInfo( + val appName: String, + val packageName: String, + val appIcon: Drawable, + val isSystemApp: Boolean, + var isSelected: Int +) \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt index 3db803de2..0cde22f0e 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/EConfigType.kt @@ -6,7 +6,7 @@ import com.v2ray.ang.AppConfig enum class EConfigType(val value: Int, val protocolScheme: String) { VMESS(1, AppConfig.VMESS), CUSTOM(2, AppConfig.CUSTOM), - SHADOWSOCKS(3,AppConfig.SHADOWSOCKS), + SHADOWSOCKS(3, AppConfig.SHADOWSOCKS), SOCKS(4, AppConfig.SOCKS), VLESS(5, AppConfig.VLESS), TROJAN(6, AppConfig.TROJAN), diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ERoutingMode.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ERoutingMode.kt index 96d773974..8d882ed66 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ERoutingMode.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ERoutingMode.kt @@ -1,6 +1,6 @@ package com.v2ray.ang.dto -enum class ERoutingMode(val value: String ) { +enum class ERoutingMode(val value: String) { GLOBAL_PROXY("0"), BYPASS_LAN("1"), BYPASS_MAINLAND("2"), diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt index 1999c9770..f2a91e2e8 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt @@ -1,50 +1,64 @@ package com.v2ray.ang.dto -import com.v2ray.ang.AppConfig.TAG_PROXY import com.v2ray.ang.AppConfig.TAG_BLOCKED import com.v2ray.ang.AppConfig.TAG_DIRECT +import com.v2ray.ang.AppConfig.TAG_PROXY import com.v2ray.ang.util.Utils data class ServerConfig( - val configVersion: Int = 3, - val configType: EConfigType, - var subscriptionId: String = "", - val addedTime: Long = System.currentTimeMillis(), - var remarks: String = "", - val outboundBean: V2rayConfig.OutboundBean? = null, - var fullConfig: V2rayConfig? = null + val configVersion: Int = 3, + val configType: EConfigType, + var subscriptionId: String = "", + val addedTime: Long = System.currentTimeMillis(), + var remarks: String = "", + val outboundBean: V2rayConfig.OutboundBean? = null, + var fullConfig: V2rayConfig? = null ) { companion object { fun create(configType: EConfigType): ServerConfig { - when(configType) { + when (configType) { EConfigType.VMESS, EConfigType.VLESS -> return ServerConfig( configType = configType, outboundBean = V2rayConfig.OutboundBean( protocol = configType.name.lowercase(), settings = V2rayConfig.OutboundBean.OutSettingsBean( - vnext = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean( - users = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean())))), - streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean())) + vnext = listOf( + V2rayConfig.OutboundBean.OutSettingsBean.VnextBean( + users = listOf(V2rayConfig.OutboundBean.OutSettingsBean.VnextBean.UsersBean()) + ) + ) + ), + streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean() + ) + ) + EConfigType.CUSTOM -> return ServerConfig(configType = configType) + EConfigType.SHADOWSOCKS, EConfigType.SOCKS, EConfigType.TROJAN -> return ServerConfig( configType = configType, outboundBean = V2rayConfig.OutboundBean( protocol = configType.name.lowercase(), settings = V2rayConfig.OutboundBean.OutSettingsBean( - servers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.ServersBean())), - streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean())) + servers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.ServersBean()) + ), + streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean() + ) + ) + EConfigType.WIREGUARD -> return ServerConfig( configType = configType, - outboundBean = V2rayConfig.OutboundBean( + outboundBean = V2rayConfig.OutboundBean( protocol = configType.name.lowercase(), settings = V2rayConfig.OutboundBean.OutSettingsBean( secretKey = "", peers = listOf(V2rayConfig.OutboundBean.OutSettingsBean.WireGuardBean()) - ))) + ) + ) + ) } } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServersCache.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServersCache.kt index 82cea587e..7ea3e6a30 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServersCache.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServersCache.kt @@ -1,4 +1,6 @@ package com.v2ray.ang.dto -data class ServersCache(val guid: String, - val profile: ProfileItem) \ No newline at end of file +data class ServersCache( + val guid: String, + val profile: ProfileItem +) \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt index 4224debe4..13cbab0ee 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt @@ -10,21 +10,22 @@ import com.google.gson.reflect.TypeToken import java.lang.reflect.Type data class V2rayConfig( - var remarks: String? = null, - var stats: Any? = null, - val log: LogBean, - var policy: PolicyBean?, - val inbounds: ArrayList, - var outbounds: ArrayList, - var dns: DnsBean, - val routing: RoutingBean, - val api: Any? = null, - val transport: Any? = null, - val reverse: Any? = null, - var fakedns: Any? = null, - val browserForwarder: Any? = null, - var observatory: Any? = null, - var burstObservatory: Any? = null) { + var remarks: String? = null, + var stats: Any? = null, + val log: LogBean, + var policy: PolicyBean?, + val inbounds: ArrayList, + var outbounds: ArrayList, + var dns: DnsBean, + val routing: RoutingBean, + val api: Any? = null, + val transport: Any? = null, + val reverse: Any? = null, + var fakedns: Any? = null, + val browserForwarder: Any? = null, + var observatory: Any? = null, + var burstObservatory: Any? = null +) { companion object { const val DEFAULT_PORT = 443 const val DEFAULT_SECURITY = "auto" @@ -36,210 +37,261 @@ data class V2rayConfig( const val HTTP = "http" } - data class LogBean(val access: String, - val error: String, - var loglevel: String?, - val dnsLog: Boolean? = null) + data class LogBean( + val access: String, + val error: String, + var loglevel: String?, + val dnsLog: Boolean? = null + ) data class InboundBean( - var tag: String, - var port: Int, - var protocol: String, - var listen: String? = null, - val settings: Any? = null, - val sniffing: SniffingBean?, - val streamSettings: Any? = null, - val allocate: Any? = null) { - - data class InSettingsBean(val auth: String? = null, - val udp: Boolean? = null, - val userLevel: Int? = null, - val address: String? = null, - val port: Int? = null, - val network: String? = null) - - data class SniffingBean(var enabled: Boolean, - val destOverride: ArrayList, - val metadataOnly: Boolean? = null, - var routeOnly: Boolean? = null) + var tag: String, + var port: Int, + var protocol: String, + var listen: String? = null, + val settings: Any? = null, + val sniffing: SniffingBean?, + val streamSettings: Any? = null, + val allocate: Any? = null + ) { + + data class InSettingsBean( + val auth: String? = null, + val udp: Boolean? = null, + val userLevel: Int? = null, + val address: String? = null, + val port: Int? = null, + val network: String? = null + ) + + data class SniffingBean( + var enabled: Boolean, + val destOverride: ArrayList, + val metadataOnly: Boolean? = null, + var routeOnly: Boolean? = null + ) } - data class OutboundBean(var tag: String = "proxy", - var protocol: String, - var settings: OutSettingsBean? = null, - var streamSettings: StreamSettingsBean? = null, - val proxySettings: Any? = null, - val sendThrough: String? = null, - var mux: MuxBean? = MuxBean(false)) { - - data class OutSettingsBean(var vnext: List? = null, - var fragment: FragmentBean? = null, - var servers: List? = null, - /*Blackhole*/ - var response: Response? = null, - /*DNS*/ - val network: String? = null, - var address: Any? = null, - val port: Int? = null, - /*Freedom*/ - var domainStrategy: String? = null, - val redirect: String? = null, - val userLevel: Int? = null, - /*Loopback*/ - val inboundTag: String? = null, - /*Wireguard*/ - var secretKey: String? = null, - val peers: List? = null, - var reserved: List? = null, - var mtu :Int? = null - ) { + data class OutboundBean( + var tag: String = "proxy", + var protocol: String, + var settings: OutSettingsBean? = null, + var streamSettings: StreamSettingsBean? = null, + val proxySettings: Any? = null, + val sendThrough: String? = null, + var mux: MuxBean? = MuxBean(false) + ) { - data class VnextBean(var address: String = "", - var port: Int = DEFAULT_PORT, - var users: List) { + data class OutSettingsBean( + var vnext: List? = null, + var fragment: FragmentBean? = null, + var servers: List? = null, + /*Blackhole*/ + var response: Response? = null, + /*DNS*/ + val network: String? = null, + var address: Any? = null, + val port: Int? = null, + /*Freedom*/ + var domainStrategy: String? = null, + val redirect: String? = null, + val userLevel: Int? = null, + /*Loopback*/ + val inboundTag: String? = null, + /*Wireguard*/ + var secretKey: String? = null, + val peers: List? = null, + var reserved: List? = null, + var mtu: Int? = null + ) { - data class UsersBean(var id: String = "", - var alterId: Int? = null, - var security: String = DEFAULT_SECURITY, - var level: Int = DEFAULT_LEVEL, - var encryption: String = "", - var flow: String = "") + data class VnextBean( + var address: String = "", + var port: Int = DEFAULT_PORT, + var users: List + ) { + + data class UsersBean( + var id: String = "", + var alterId: Int? = null, + var security: String = DEFAULT_SECURITY, + var level: Int = DEFAULT_LEVEL, + var encryption: String = "", + var flow: String = "" + ) } - data class FragmentBean(var packets: String? = null, - var length: String? = null, - var interval: String? = null) - - data class ServersBean(var address: String = "", - var method: String = "chacha20-poly1305", - var ota: Boolean = false, - var password: String = "", - var port: Int = DEFAULT_PORT, - var level: Int = DEFAULT_LEVEL, - val email: String? = null, - var flow: String? = null, - val ivCheck: Boolean? = null, - var users: List? = null) { - - - data class SocksUsersBean(var user: String = "", - var pass: String = "", - var level: Int = DEFAULT_LEVEL) + data class FragmentBean( + var packets: String? = null, + var length: String? = null, + var interval: String? = null + ) + + data class ServersBean( + var address: String = "", + var method: String = "chacha20-poly1305", + var ota: Boolean = false, + var password: String = "", + var port: Int = DEFAULT_PORT, + var level: Int = DEFAULT_LEVEL, + val email: String? = null, + var flow: String? = null, + val ivCheck: Boolean? = null, + var users: List? = null + ) { + + + data class SocksUsersBean( + var user: String = "", + var pass: String = "", + var level: Int = DEFAULT_LEVEL + ) } data class Response(var type: String) - data class WireGuardBean(var publicKey: String = "", - var endpoint: String = "") + data class WireGuardBean( + var publicKey: String = "", + var endpoint: String = "" + ) } - data class StreamSettingsBean(var network: String = DEFAULT_NETWORK, - var security: String = "", - var tcpSettings: TcpSettingsBean? = null, - var kcpSettings: KcpSettingsBean? = null, - var wsSettings: WsSettingsBean? = null, - var httpupgradeSettings: HttpupgradeSettingsBean? = null, - var splithttpSettings: SplithttpSettingsBean? = null, - var httpSettings: HttpSettingsBean? = null, - var tlsSettings: TlsSettingsBean? = null, - var quicSettings: QuicSettingBean? = null, - var realitySettings: TlsSettingsBean? = null, - var grpcSettings: GrpcSettingsBean? = null, - val dsSettings: Any? = null, - var sockopt: SockoptBean? = null + data class StreamSettingsBean( + var network: String = DEFAULT_NETWORK, + var security: String = "", + var tcpSettings: TcpSettingsBean? = null, + var kcpSettings: KcpSettingsBean? = null, + var wsSettings: WsSettingsBean? = null, + var httpupgradeSettings: HttpupgradeSettingsBean? = null, + var splithttpSettings: SplithttpSettingsBean? = null, + var httpSettings: HttpSettingsBean? = null, + var tlsSettings: TlsSettingsBean? = null, + var quicSettings: QuicSettingBean? = null, + var realitySettings: TlsSettingsBean? = null, + var grpcSettings: GrpcSettingsBean? = null, + val dsSettings: Any? = null, + var sockopt: SockoptBean? = null ) { - data class TcpSettingsBean(var header: HeaderBean = HeaderBean(), - val acceptProxyProtocol: Boolean? = null) { - data class HeaderBean(var type: String = "none", - var request: RequestBean? = null, - var response: Any? = null) { - data class RequestBean(var path: List = ArrayList(), - var headers: HeadersBean = HeadersBean(), - val version: String? = null, - val method: String? = null) { - data class HeadersBean(var Host: List? = ArrayList(), - @SerializedName("User-Agent") - val userAgent: List? = null, - @SerializedName("Accept-Encoding") - val acceptEncoding: List? = null, - val Connection: List? = null, - val Pragma: String? = null) + data class TcpSettingsBean( + var header: HeaderBean = HeaderBean(), + val acceptProxyProtocol: Boolean? = null + ) { + data class HeaderBean( + var type: String = "none", + var request: RequestBean? = null, + var response: Any? = null + ) { + data class RequestBean( + var path: List = ArrayList(), + var headers: HeadersBean = HeadersBean(), + val version: String? = null, + val method: String? = null + ) { + data class HeadersBean( + var Host: List? = ArrayList(), + @SerializedName("User-Agent") + val userAgent: List? = null, + @SerializedName("Accept-Encoding") + val acceptEncoding: List? = null, + val Connection: List? = null, + val Pragma: String? = null + ) } } } - data class KcpSettingsBean(var mtu: Int = 1350, - var tti: Int = 50, - var uplinkCapacity: Int = 12, - var downlinkCapacity: Int = 100, - var congestion: Boolean = false, - var readBufferSize: Int = 1, - var writeBufferSize: Int = 1, - var header: HeaderBean = HeaderBean(), - var seed: String? = null) { + data class KcpSettingsBean( + var mtu: Int = 1350, + var tti: Int = 50, + var uplinkCapacity: Int = 12, + var downlinkCapacity: Int = 100, + var congestion: Boolean = false, + var readBufferSize: Int = 1, + var writeBufferSize: Int = 1, + var header: HeaderBean = HeaderBean(), + var seed: String? = null + ) { data class HeaderBean(var type: String = "none") } - data class WsSettingsBean(var path: String = "", - var headers: HeadersBean = HeadersBean(), - val maxEarlyData: Int? = null, - val useBrowserForwarding: Boolean? = null, - val acceptProxyProtocol: Boolean? = null) { + data class WsSettingsBean( + var path: String = "", + var headers: HeadersBean = HeadersBean(), + val maxEarlyData: Int? = null, + val useBrowserForwarding: Boolean? = null, + val acceptProxyProtocol: Boolean? = null + ) { data class HeadersBean(var Host: String = "") } - data class HttpupgradeSettingsBean(var path: String = "", - var host: String = "", - val acceptProxyProtocol: Boolean? = null) - - data class SplithttpSettingsBean(var path: String = "", - var host: String = "", - val maxUploadSize: Int? = null, - val maxConcurrentUploads: Int? = null) - data class HttpSettingsBean(var host: List = ArrayList(), - var path: String = "") - - data class SockoptBean(var TcpNoDelay: Boolean? = null, - var tcpKeepAliveIdle: Int? = null, - var tcpFastOpen: Boolean? = null, - var tproxy: String? = null, - var mark: Int? = null, - var dialerProxy: String? = null) - - data class TlsSettingsBean(var allowInsecure: Boolean = false, - var serverName: String = "", - val alpn: List? = null, - val minVersion: String? = null, - val maxVersion: String? = null, - val preferServerCipherSuites: Boolean? = null, - val cipherSuites: String? = null, - val fingerprint: String? = null, - val certificates: List? = null, - val disableSystemRoot: 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", - var key: String = "", - var header: HeaderBean = HeaderBean()) { + data class HttpupgradeSettingsBean( + var path: String = "", + var host: String = "", + val acceptProxyProtocol: Boolean? = null + ) + + data class SplithttpSettingsBean( + var path: String = "", + var host: String = "", + val maxUploadSize: Int? = null, + val maxConcurrentUploads: Int? = null + ) + + data class HttpSettingsBean( + var host: List = ArrayList(), + var path: String = "" + ) + + data class SockoptBean( + var TcpNoDelay: Boolean? = null, + var tcpKeepAliveIdle: Int? = null, + var tcpFastOpen: Boolean? = null, + var tproxy: String? = null, + var mark: Int? = null, + var dialerProxy: String? = null + ) + + data class TlsSettingsBean( + var allowInsecure: Boolean = false, + var serverName: String = "", + val alpn: List? = null, + val minVersion: String? = null, + val maxVersion: String? = null, + val preferServerCipherSuites: Boolean? = null, + val cipherSuites: String? = null, + val fingerprint: String? = null, + val certificates: List? = null, + val disableSystemRoot: 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", + var key: String = "", + var header: HeaderBean = HeaderBean() + ) { data class HeaderBean(var type: String = "none") } - data class GrpcSettingsBean(var serviceName: String = "", - var authority: String? = null, - var multiMode: Boolean? = null, - var idle_timeout: Int? = null, - var health_check_timeout: Int? = null - ) - - fun populateTransportSettings(transport: String, headerType: String?, host: String?, path: String?, seed: String?, - quicSecurity: String?, key: String?, mode: String?, serviceName: String?, - authority: String?): String { + data class GrpcSettingsBean( + var serviceName: String = "", + var authority: String? = null, + var multiMode: Boolean? = null, + var idle_timeout: Int? = null, + var health_check_timeout: Int? = null + ) + + fun populateTransportSettings( + transport: String, headerType: String?, host: String?, path: String?, seed: String?, + quicSecurity: String?, key: String?, mode: String?, serviceName: String?, + authority: String? + ): String { var sni = "" network = transport when (network) { @@ -249,8 +301,8 @@ data class V2rayConfig( tcpSetting.header.type = HTTP if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(path)) { val requestObj = TcpSettingsBean.HeaderBean.RequestBean() - requestObj.headers.Host = (host .orEmpty()).split(",").map { it.trim() }.filter { it.isNotEmpty() } - requestObj.path = (path .orEmpty()).split(",").map { it.trim() }.filter { it.isNotEmpty() } + requestObj.headers.Host = (host.orEmpty()).split(",").map { it.trim() }.filter { it.isNotEmpty() } + requestObj.path = (path.orEmpty()).split(",").map { it.trim() }.filter { it.isNotEmpty() } tcpSetting.header.request = requestObj sni = requestObj.headers.Host?.getOrNull(0) ?: sni } @@ -260,6 +312,7 @@ data class V2rayConfig( } tcpSettings = tcpSetting } + "kcp" -> { val kcpsetting = KcpSettingsBean() kcpsetting.header.type = headerType ?: "none" @@ -270,6 +323,7 @@ data class V2rayConfig( } kcpSettings = kcpsetting } + "ws" -> { val wssetting = WsSettingsBean() wssetting.headers.Host = host.orEmpty() @@ -277,6 +331,7 @@ data class V2rayConfig( wssetting.path = path ?: "/" wsSettings = wssetting } + "httpupgrade" -> { val httpupgradeSetting = HttpupgradeSettingsBean() httpupgradeSetting.host = host.orEmpty() @@ -284,6 +339,7 @@ data class V2rayConfig( httpupgradeSetting.path = path ?: "/" httpupgradeSettings = httpupgradeSetting } + "splithttp" -> { val splithttpSetting = SplithttpSettingsBean() splithttpSetting.host = host.orEmpty() @@ -291,14 +347,16 @@ data class V2rayConfig( splithttpSetting.path = path ?: "/" splithttpSettings = splithttpSetting } + "h2", "http" -> { network = "h2" val h2Setting = HttpSettingsBean() - h2Setting.host = (host .orEmpty()).split(",").map { it.trim() }.filter { it.isNotEmpty() } + h2Setting.host = (host.orEmpty()).split(",").map { it.trim() }.filter { it.isNotEmpty() } sni = h2Setting.host.getOrNull(0) ?: sni h2Setting.path = path ?: "/" httpSettings = h2Setting } + "quic" -> { val quicsetting = QuicSettingBean() quicsetting.security = quicSecurity ?: "none" @@ -306,6 +364,7 @@ data class V2rayConfig( quicsetting.header.type = headerType ?: "none" quicSettings = quicsetting } + "grpc" -> { val grpcSetting = GrpcSettingsBean() grpcSetting.multiMode = mode == "multi" @@ -320,17 +379,19 @@ data class V2rayConfig( return sni } - fun populateTlsSettings(streamSecurity: String, allowInsecure: Boolean, sni: String, fingerprint: String?, alpns: String?, - publicKey: String?, shortId: String?, spiderX: String?) { + fun populateTlsSettings( + streamSecurity: String, allowInsecure: Boolean, sni: String, fingerprint: String?, alpns: String?, + publicKey: String?, shortId: String?, spiderX: String? + ) { security = streamSecurity val tlsSetting = TlsSettingsBean( - allowInsecure = allowInsecure, - serverName = sni, - fingerprint = fingerprint, - alpn = if (alpns.isNullOrEmpty()) null else alpns.split(",").map { it.trim() }.filter { it.isNotEmpty() }, - publicKey = publicKey, - shortId = shortId, - spiderX = spiderX + allowInsecure = allowInsecure, + serverName = sni, + fingerprint = fingerprint, + alpn = if (alpns.isNullOrEmpty()) null else alpns.split(",").map { it.trim() }.filter { it.isNotEmpty() }, + publicKey = publicKey, + shortId = shortId, + spiderX = spiderX ) if (security == TLS) { tlsSettings = tlsSetting @@ -342,18 +403,22 @@ data class V2rayConfig( } } - data class MuxBean(var enabled: Boolean, - var concurrency: Int = 8, - var xudpConcurrency: Int = 8, - var xudpProxyUDP443: String = "",) + data class MuxBean( + var enabled: Boolean, + var concurrency: Int = 8, + var xudpConcurrency: Int = 8, + var xudpProxyUDP443: String = "", + ) fun getServerAddress(): String? { if (protocol.equals(EConfigType.VMESS.name, true) - || protocol.equals(EConfigType.VLESS.name, true)) { + || protocol.equals(EConfigType.VLESS.name, true) + ) { return settings?.vnext?.get(0)?.address } else if (protocol.equals(EConfigType.SHADOWSOCKS.name, true) - || protocol.equals(EConfigType.SOCKS.name, true) - || protocol.equals(EConfigType.TROJAN.name, true)) { + || protocol.equals(EConfigType.SOCKS.name, true) + || protocol.equals(EConfigType.TROJAN.name, true) + ) { return settings?.servers?.get(0)?.address } else if (protocol.equals(EConfigType.WIREGUARD.name, true)) { return settings?.peers?.get(0)?.endpoint?.substringBeforeLast(":") @@ -363,11 +428,13 @@ data class V2rayConfig( fun getServerPort(): Int? { if (protocol.equals(EConfigType.VMESS.name, true) - || protocol.equals(EConfigType.VLESS.name, true)) { + || protocol.equals(EConfigType.VLESS.name, true) + ) { return settings?.vnext?.get(0)?.port } else if (protocol.equals(EConfigType.SHADOWSOCKS.name, true) - || protocol.equals(EConfigType.SOCKS.name, true) - || protocol.equals(EConfigType.TROJAN.name, true)) { + || protocol.equals(EConfigType.SOCKS.name, true) + || protocol.equals(EConfigType.TROJAN.name, true) + ) { return settings?.servers?.get(0)?.port } else if (protocol.equals(EConfigType.WIREGUARD.name, true)) { return settings?.peers?.get(0)?.endpoint?.substringAfterLast(":")?.toInt() @@ -377,10 +444,12 @@ data class V2rayConfig( fun getPassword(): String? { if (protocol.equals(EConfigType.VMESS.name, true) - || protocol.equals(EConfigType.VLESS.name, true)) { + || protocol.equals(EConfigType.VLESS.name, true) + ) { return settings?.vnext?.get(0)?.users?.get(0)?.id } else if (protocol.equals(EConfigType.SHADOWSOCKS.name, true) - || protocol.equals(EConfigType.TROJAN.name, true)) { + || protocol.equals(EConfigType.TROJAN.name, true) + ) { return settings?.servers?.get(0)?.password } else if (protocol.equals(EConfigType.SOCKS.name, true)) { return settings?.servers?.get(0)?.users?.get(0)?.pass @@ -401,59 +470,84 @@ data class V2rayConfig( fun getTransportSettingDetails(): List? { if (protocol.equals(EConfigType.VMESS.name, true) - || protocol.equals(EConfigType.VLESS.name, true) - || protocol.equals(EConfigType.TROJAN.name, true) - || protocol.equals(EConfigType.SHADOWSOCKS.name, true)) { + || protocol.equals(EConfigType.VLESS.name, true) + || protocol.equals(EConfigType.TROJAN.name, true) + || protocol.equals(EConfigType.SHADOWSOCKS.name, true) + ) { val transport = streamSettings?.network ?: return null return when (transport) { "tcp" -> { val tcpSetting = streamSettings?.tcpSettings ?: return null - listOf(tcpSetting.header.type, - tcpSetting.header.request?.headers?.Host?.joinToString().orEmpty(), - tcpSetting.header.request?.path?.joinToString().orEmpty()) + listOf( + tcpSetting.header.type, + tcpSetting.header.request?.headers?.Host?.joinToString().orEmpty(), + tcpSetting.header.request?.path?.joinToString().orEmpty() + ) } + "kcp" -> { val kcpSetting = streamSettings?.kcpSettings ?: return null - listOf(kcpSetting.header.type, - "", - kcpSetting.seed.orEmpty()) + listOf( + kcpSetting.header.type, + "", + kcpSetting.seed.orEmpty() + ) } + "ws" -> { val wsSetting = streamSettings?.wsSettings ?: return null - listOf("", - wsSetting.headers.Host, - wsSetting.path) + listOf( + "", + wsSetting.headers.Host, + wsSetting.path + ) } + "httpupgrade" -> { val httpupgradeSetting = streamSettings?.httpupgradeSettings ?: return null - listOf("", + listOf( + "", httpupgradeSetting.host, - httpupgradeSetting.path) + httpupgradeSetting.path + ) } + "splithttp" -> { val splithttpSetting = streamSettings?.splithttpSettings ?: return null - listOf("", + listOf( + "", splithttpSetting.host, - splithttpSetting.path) + splithttpSetting.path + ) } + "h2" -> { val h2Setting = streamSettings?.httpSettings ?: return null - listOf("", - h2Setting.host.joinToString(), - h2Setting.path) + listOf( + "", + h2Setting.host.joinToString(), + h2Setting.path + ) } + "quic" -> { val quicSetting = streamSettings?.quicSettings ?: return null - listOf(quicSetting.header.type, - quicSetting.security, - quicSetting.key) + listOf( + quicSetting.header.type, + quicSetting.security, + quicSetting.key + ) } + "grpc" -> { val grpcSetting = streamSettings?.grpcSettings ?: return null - listOf(if (grpcSetting.multiMode == true) "multi" else "gun", - grpcSetting.authority.orEmpty(), - grpcSetting.serviceName) + listOf( + if (grpcSetting.multiMode == true) "multi" else "gun", + grpcSetting.authority.orEmpty(), + grpcSetting.serviceName + ) } + else -> null } } @@ -461,59 +555,69 @@ data class V2rayConfig( } } - data class DnsBean(var servers: ArrayList? = null, - var hosts: Map? = null, - val clientIp: String? = null, - val disableCache: Boolean? = null, - val queryStrategy: String? = null, - val tag: String? = null + data class DnsBean( + var servers: ArrayList? = null, + var hosts: Map? = null, + val clientIp: String? = null, + val disableCache: Boolean? = null, + val queryStrategy: String? = null, + val tag: String? = null ) { - data class ServersBean(var address: String = "", - var port: Int? = null, - var domains: List? = null, - var expectIPs: List? = null, - val clientIp: String? = null) + data class ServersBean( + var address: String = "", + var port: Int? = null, + var domains: List? = null, + var expectIPs: List? = null, + val clientIp: String? = null + ) } - data class RoutingBean(var domainStrategy: String, - var domainMatcher: String? = null, - var rules: ArrayList, - val balancers: List? = null) { + data class RoutingBean( + var domainStrategy: String, + var domainMatcher: String? = null, + var rules: ArrayList, + val balancers: List? = null + ) { data class RulesBean( - var ip: ArrayList? = null, - var domain: ArrayList? = null, - var outboundTag: String = "", - var balancerTag: String? = null, - var port: String? = null, - val sourcePort: String? = null, - val network: String? = null, - val source: List? = null, - val user: List? = null, - var inboundTag: List? = null, - val protocol: List? = null, - val attrs: String? = null, - val domainMatcher: String? = null + var ip: ArrayList? = null, + var domain: ArrayList? = null, + var outboundTag: String = "", + var balancerTag: String? = null, + var port: String? = null, + val sourcePort: String? = null, + val network: String? = null, + val source: List? = null, + val user: List? = null, + var inboundTag: List? = null, + val protocol: List? = null, + val attrs: String? = null, + val domainMatcher: String? = null ) } - data class PolicyBean(var levels: Map, - var system: Any? = null) { + data class PolicyBean( + var levels: Map, + var system: Any? = null + ) { data class LevelBean( - var handshake: Int? = null, - var connIdle: Int? = null, - var uplinkOnly: Int? = null, - var downlinkOnly: Int? = null, - val statsUserUplink: Boolean? = null, - val statsUserDownlink: Boolean? = null, - var bufferSize: Int? = null) + var handshake: Int? = null, + var connIdle: Int? = null, + var uplinkOnly: Int? = null, + var downlinkOnly: Int? = null, + val statsUserUplink: Boolean? = null, + val statsUserDownlink: Boolean? = null, + var bufferSize: Int? = null + ) } - data class FakednsBean(var ipPool: String = "198.18.0.0/15", - var poolSize: Int = 10000) // roughly 10 times smaller than total ip pool + data class FakednsBean( + var ipPool: String = "198.18.0.0/15", + var poolSize: Int = 10000 + ) // roughly 10 times smaller than total ip pool fun getProxyOutbound(): OutboundBean? { - outbounds?.forEach { outbound -> + outbounds.forEach { outbound -> EConfigType.entries.forEach { if (outbound.protocol.equals(it.name, true)) { return outbound @@ -525,13 +629,13 @@ data class V2rayConfig( fun toPrettyPrinting(): String { return GsonBuilder() - .setPrettyPrinting() - .disableHtmlEscaping() - .registerTypeAdapter( // custom serialiser is needed here since JSON by default parse number as Double, core will fail to start - object : TypeToken() {}.type, - JsonSerializer { src: Double?, _: Type?, _: JsonSerializationContext? -> JsonPrimitive(src?.toInt()) } - ) - .create() - .toJson(this) + .setPrettyPrinting() + .disableHtmlEscaping() + .registerTypeAdapter( // custom serialiser is needed here since JSON by default parse number as Double, core will fail to start + object : TypeToken() {}.type, + JsonSerializer { src: Double?, _: Type?, _: JsonSerializationContext? -> JsonPrimitive(src?.toInt()) } + ) + .create() + .toJson(this) } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/VmessQRCode.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/VmessQRCode.kt index 54b7dd742..779c7e3ce 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/VmessQRCode.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/VmessQRCode.kt @@ -1,17 +1,19 @@ package com.v2ray.ang.dto -data class VmessQRCode(var v: String = "", - var ps: String = "", - var add: String = "", - var port: String = "", - var id: String = "", - var aid: String = "0", - var scy: String = "", - var net: String = "", - var type: String = "", - var host: String = "", - var path: String = "", - var tls: String = "", - var sni: String = "", - var alpn: String = "", - var fp: String = "") \ No newline at end of file +data class VmessQRCode( + var v: String = "", + var ps: String = "", + var add: String = "", + var port: String = "", + var id: String = "", + var aid: String = "0", + var scy: String = "", + var net: String = "", + var type: String = "", + var host: String = "", + var path: String = "", + var tls: String = "", + var sni: String = "", + var alpn: String = "", + var fp: String = "" +) \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt index 87fa3260f..f4c3a6f42 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/extension/_Ext.kt @@ -1,8 +1,6 @@ package com.v2ray.ang.extension import android.content.Context -import android.net.ConnectivityManager -import android.net.NetworkCapabilities import android.os.Build import android.widget.Toast import com.v2ray.ang.AngApplication diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt index 52dacf048..5aa6c2de0 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/receiver/WidgetProvider.kt @@ -35,7 +35,8 @@ class WidgetProvider : AppWidgetProvider() { PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT } else { PendingIntent.FLAG_UPDATE_CURRENT - }) + } + ) remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent) if (isRunning) { remoteViews.setInt(R.id.image_switch, "setImageResource", R.drawable.ic_stop_24dp) @@ -65,12 +66,17 @@ class WidgetProvider : AppWidgetProvider() { AppWidgetManager.getInstance(context)?.let { manager -> when (intent.getIntExtra("key", 0)) { AppConfig.MSG_STATE_RUNNING, AppConfig.MSG_STATE_START_SUCCESS -> { - updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)), - true) + updateWidgetBackground( + context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)), + true + ) } + AppConfig.MSG_STATE_NOT_RUNNING, AppConfig.MSG_STATE_START_FAILURE, AppConfig.MSG_STATE_STOP_SUCCESS -> { - updateWidgetBackground(context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)), - false) + updateWidgetBackground( + context, manager, manager.getAppWidgetIds(ComponentName(context, WidgetProvider::class.java)), + false + ) } } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt index ecbae70e5..1c9afe6cb 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/QSTileService.kt @@ -58,6 +58,7 @@ class QSTileService : TileService() { Tile.STATE_INACTIVE -> { Utils.startVServiceFromToggle(this) } + Tile.STATE_ACTIVE -> { Utils.stopVService(this) } @@ -74,15 +75,19 @@ class QSTileService : TileService() { AppConfig.MSG_STATE_RUNNING -> { context?.setState(Tile.STATE_ACTIVE) } + AppConfig.MSG_STATE_NOT_RUNNING -> { context?.setState(Tile.STATE_INACTIVE) } + AppConfig.MSG_STATE_START_SUCCESS -> { context?.setState(Tile.STATE_ACTIVE) } + AppConfig.MSG_STATE_START_FAILURE -> { context?.setState(Tile.STATE_INACTIVE) } + AppConfig.MSG_STATE_STOP_SUCCESS -> { context?.setState(Tile.STATE_INACTIVE) } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/SubscriptionUpdater.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/SubscriptionUpdater.kt index a1e8a5ac7..449b8835c 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/SubscriptionUpdater.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/SubscriptionUpdater.kt @@ -21,7 +21,6 @@ import com.v2ray.ang.util.Utils object SubscriptionUpdater { - class UpdateTask(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayProxyOnlyService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayProxyOnlyService.kt index d3f6a2967..4d387a45f 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayProxyOnlyService.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayProxyOnlyService.kt @@ -49,7 +49,7 @@ class V2RayProxyOnlyService : Service(), ServiceControl { @RequiresApi(Build.VERSION_CODES.N) override fun attachBaseContext(newBase: Context?) { val context = newBase?.let { - MyContextWrapper.wrap(newBase, Utils.getLocale()) + MyContextWrapper.wrap(newBase, Utils.getLocale()) } super.attachBaseContext(context) } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt index 4324f45c6..1f6dfa6f2 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayServiceManager.kt @@ -27,14 +27,14 @@ import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.Utils import com.v2ray.ang.util.V2rayConfigUtil import go.Seq +import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.disposables.Disposable import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import libv2ray.Libv2ray import libv2ray.V2RayPoint import libv2ray.V2RayVPNServiceSupportsSet -import io.reactivex.rxjava3.core.Observable -import io.reactivex.rxjava3.disposables.Disposable import java.lang.ref.SoftReference import kotlin.math.min @@ -206,18 +206,23 @@ object V2RayServiceManager { MessageUtil.sendMsg2UI(serviceControl.getService(), AppConfig.MSG_STATE_NOT_RUNNING, "") } } + AppConfig.MSG_UNREGISTER_CLIENT -> { // nothing to do } + AppConfig.MSG_STATE_START -> { // nothing to do } + AppConfig.MSG_STATE_STOP -> { serviceControl.stopService() } + AppConfig.MSG_STATE_RESTART -> { startV2rayPoint() } + AppConfig.MSG_MEASURE_DELAY -> { measureV2rayDelay() } @@ -228,6 +233,7 @@ object V2RayServiceManager { Log.d(ANG_PACKAGE, "SCREEN_OFF, stop querying stats") stopSpeedNotification() } + Intent.ACTION_SCREEN_ON -> { Log.d(ANG_PACKAGE, "SCREEN_ON, start querying stats") startSpeedNotification() @@ -270,46 +276,52 @@ object V2RayServiceManager { private fun showNotification() { val service = serviceControl?.get()?.getService() ?: return val startMainIntent = Intent(service, MainActivity::class.java) - val contentPendingIntent = PendingIntent.getActivity(service, - NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent, + val contentPendingIntent = PendingIntent.getActivity( + service, + NOTIFICATION_PENDING_INTENT_CONTENT, startMainIntent, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT } else { PendingIntent.FLAG_UPDATE_CURRENT - }) + } + ) val stopV2RayIntent = Intent(AppConfig.BROADCAST_ACTION_SERVICE) stopV2RayIntent.`package` = ANG_PACKAGE stopV2RayIntent.putExtra("key", AppConfig.MSG_STATE_STOP) - val stopV2RayPendingIntent = PendingIntent.getBroadcast(service, - NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent, + val stopV2RayPendingIntent = PendingIntent.getBroadcast( + service, + NOTIFICATION_PENDING_INTENT_STOP_V2RAY, stopV2RayIntent, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT } else { PendingIntent.FLAG_UPDATE_CURRENT - }) + } + ) val channelId = - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - createNotificationChannel() - } else { - // If earlier version channel ID is not used - // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) - "" - } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel() + } else { + // If earlier version channel ID is not used + // https://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html#NotificationCompat.Builder(android.content.Context) + "" + } mBuilder = NotificationCompat.Builder(service, channelId) - .setSmallIcon(R.drawable.ic_stat_name) - .setContentTitle(currentConfig?.remarks) - .setPriority(NotificationCompat.PRIORITY_MIN) - .setOngoing(true) - .setShowWhen(false) - .setOnlyAlertOnce(true) - .setContentIntent(contentPendingIntent) - .addAction(R.drawable.ic_delete_24dp, - service.getString(R.string.notification_action_stop_v2ray), - stopV2RayPendingIntent) + .setSmallIcon(R.drawable.ic_stat_name) + .setContentTitle(currentConfig?.remarks) + .setPriority(NotificationCompat.PRIORITY_MIN) + .setOngoing(true) + .setShowWhen(false) + .setOnlyAlertOnce(true) + .setContentIntent(contentPendingIntent) + .addAction( + R.drawable.ic_delete_24dp, + service.getString(R.string.notification_action_stop_v2ray), + stopV2RayPendingIntent + ) //.build() //mBuilder?.setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) //取消震动,铃声其他都不好使 @@ -321,8 +333,10 @@ object V2RayServiceManager { private fun createNotificationChannel(): String { val channelId = AppConfig.RAY_NG_CHANNEL_ID val channelName = AppConfig.RAY_NG_CHANNEL_NAME - val chan = NotificationChannel(channelId, - channelName, NotificationManager.IMPORTANCE_HIGH) + val chan = NotificationChannel( + channelId, + channelName, NotificationManager.IMPORTANCE_HIGH + ) chan.lightColor = Color.DKGRAY chan.importance = NotificationManager.IMPORTANCE_NONE chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE @@ -363,40 +377,43 @@ object V2RayServiceManager { private fun startSpeedNotification() { if (mDisposable == null && - v2rayPoint.isRunning && - settingsStorage?.decodeBool(AppConfig.PREF_SPEED_ENABLED) == true) { + v2rayPoint.isRunning && + settingsStorage?.decodeBool(AppConfig.PREF_SPEED_ENABLED) == true + ) { var lastZeroSpeed = false val outboundTags = currentConfig?.getAllOutboundTags() outboundTags?.remove(TAG_DIRECT) mDisposable = Observable.interval(3, java.util.concurrent.TimeUnit.SECONDS) - .subscribe { - val queryTime = System.currentTimeMillis() - val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0 - var proxyTotal = 0L - val text = StringBuilder() - outboundTags?.forEach { - val up = v2rayPoint.queryStats(it, AppConfig.UPLINK) - val down = v2rayPoint.queryStats(it, AppConfig.DOWNLINK) - if (up + down > 0) { - appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds) - proxyTotal += up + down - } + .subscribe { + val queryTime = System.currentTimeMillis() + val sinceLastQueryInSeconds = (queryTime - lastQueryTime) / 1000.0 + var proxyTotal = 0L + val text = StringBuilder() + outboundTags?.forEach { + val up = v2rayPoint.queryStats(it, AppConfig.UPLINK) + val down = v2rayPoint.queryStats(it, AppConfig.DOWNLINK) + if (up + down > 0) { + appendSpeedString(text, it, up / sinceLastQueryInSeconds, down / sinceLastQueryInSeconds) + proxyTotal += up + down } - val directUplink = v2rayPoint.queryStats(TAG_DIRECT, AppConfig.UPLINK) - val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, AppConfig.DOWNLINK) - val zeroSpeed = proxyTotal == 0L && directUplink == 0L && directDownlink == 0L - if (!zeroSpeed || !lastZeroSpeed) { - if (proxyTotal == 0L) { - appendSpeedString(text, outboundTags?.firstOrNull(), 0.0, 0.0) - } - appendSpeedString(text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds, - directDownlink / sinceLastQueryInSeconds) - updateNotification(text.toString(), proxyTotal, directDownlink + directUplink) + } + val directUplink = v2rayPoint.queryStats(TAG_DIRECT, AppConfig.UPLINK) + val directDownlink = v2rayPoint.queryStats(TAG_DIRECT, AppConfig.DOWNLINK) + val zeroSpeed = proxyTotal == 0L && directUplink == 0L && directDownlink == 0L + if (!zeroSpeed || !lastZeroSpeed) { + if (proxyTotal == 0L) { + appendSpeedString(text, outboundTags?.firstOrNull(), 0.0, 0.0) } - lastZeroSpeed = zeroSpeed - lastQueryTime = queryTime + appendSpeedString( + text, TAG_DIRECT, directUplink / sinceLastQueryInSeconds, + directDownlink / sinceLastQueryInSeconds + ) + updateNotification(text.toString(), proxyTotal, directDownlink + directUplink) } + lastZeroSpeed = zeroSpeed + lastQueryTime = queryTime + } } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayTestService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayTestService.kt index d84140007..6d6dc0aa9 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayTestService.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayTestService.kt @@ -10,7 +10,11 @@ import com.v2ray.ang.util.MessageUtil import com.v2ray.ang.util.SpeedtestUtil import com.v2ray.ang.util.Utils import go.Seq -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.asCoroutineDispatcher +import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.launch import libv2ray.Libv2ray import java.util.concurrent.Executors @@ -32,6 +36,7 @@ class V2RayTestService : Service() { MessageUtil.sendMsg2UI(this@V2RayTestService, MSG_MEASURE_CONFIG_SUCCESS, Pair(contentPair.first, result)) } } + MSG_MEASURE_CONFIG_CANCEL -> { realTestScope.coroutineContext[Job]?.cancelChildren() } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt index d3f675a68..c5f3ea997 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/service/V2RayVpnService.kt @@ -4,7 +4,13 @@ import android.app.Service import android.content.Context import android.content.Intent import android.content.pm.PackageManager -import android.net.* +import android.net.ConnectivityManager +import android.net.LocalSocket +import android.net.LocalSocketAddress +import android.net.Network +import android.net.NetworkCapabilities +import android.net.NetworkRequest +import android.net.VpnService import android.os.Build import android.os.ParcelFileDescriptor import android.os.StrictMode @@ -19,7 +25,6 @@ import com.v2ray.ang.util.MyContextWrapper import com.v2ray.ang.util.Utils import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import java.io.File import java.lang.ref.SoftReference @@ -54,9 +59,9 @@ class V2RayVpnService : VpnService(), ServiceControl { @delegate:RequiresApi(Build.VERSION_CODES.P) private val defaultNetworkRequest by lazy { NetworkRequest.Builder() - .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .build() + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .build() } private val connectivity by lazy { getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager } @@ -140,11 +145,11 @@ class V2RayVpnService : VpnService(), ServiceControl { builder.addDnsServer(PRIVATE_VLAN4_ROUTER) } else { Utils.getVpnDnsServers() - .forEach { - if (Utils.isPureIpAddress(it)) { - builder.addDnsServer(it) - } + .forEach { + if (Utils.isPureIpAddress(it)) { + builder.addDnsServer(it) } + } } builder.setSession(V2RayServiceManager.currentConfig?.remarks.orEmpty()) @@ -197,14 +202,16 @@ class V2RayVpnService : VpnService(), ServiceControl { private fun runTun2socks() { val socksPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_SOCKS_PORT), AppConfig.PORT_SOCKS.toInt()) - val cmd = arrayListOf(File(applicationContext.applicationInfo.nativeLibraryDir, TUN2SOCKS).absolutePath, - "--netif-ipaddr", PRIVATE_VLAN4_ROUTER, - "--netif-netmask", "255.255.255.252", - "--socks-server-addr", "127.0.0.1:${socksPort}", - "--tunmtu", VPN_MTU.toString(), - "--sock-path", "sock_path",//File(applicationContext.filesDir, "sock_path").absolutePath, - "--enable-udprelay", - "--loglevel", "notice") + val cmd = arrayListOf( + File(applicationContext.applicationInfo.nativeLibraryDir, TUN2SOCKS).absolutePath, + "--netif-ipaddr", PRIVATE_VLAN4_ROUTER, + "--netif-netmask", "255.255.255.252", + "--socks-server-addr", "127.0.0.1:${socksPort}", + "--tunmtu", VPN_MTU.toString(), + "--sock-path", "sock_path",//File(applicationContext.filesDir, "sock_path").absolutePath, + "--enable-udprelay", + "--loglevel", "notice" + ) if (settingsStorage?.decodeBool(AppConfig.PREF_PREFER_IPV6) == true) { cmd.add("--netif-ip6addr") @@ -221,14 +228,14 @@ class V2RayVpnService : VpnService(), ServiceControl { val proBuilder = ProcessBuilder(cmd) proBuilder.redirectErrorStream(true) process = proBuilder - .directory(applicationContext.filesDir) - .start() + .directory(applicationContext.filesDir) + .start() Thread(Runnable { - Log.d(packageName,"$TUN2SOCKS check") + Log.d(packageName, "$TUN2SOCKS check") process.waitFor() - Log.d(packageName,"$TUN2SOCKS exited") + Log.d(packageName, "$TUN2SOCKS exited") if (isRunning) { - Log.d(packageName,"$TUN2SOCKS restart") + Log.d(packageName, "$TUN2SOCKS restart") runTun2socks() } }).start() @@ -275,7 +282,7 @@ class V2RayVpnService : VpnService(), ServiceControl { // val emptyInfo = VpnNetworkInfo() // val info = loadVpnNetworkInfo(configName, emptyInfo)!! + (lastNetworkInfo ?: emptyInfo) // saveVpnNetworkInfo(configName, info) - isRunning = false; + isRunning = false if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { try { connectivity.unregisterNetworkCallback(defaultNetworkCallback) @@ -328,7 +335,7 @@ class V2RayVpnService : VpnService(), ServiceControl { @RequiresApi(Build.VERSION_CODES.N) override fun attachBaseContext(newBase: Context?) { val context = newBase?.let { - MyContextWrapper.wrap(newBase, Utils.getLocale()) + MyContextWrapper.wrap(newBase, Utils.getLocale()) } super.attachBaseContext(context) } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/AboutActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/AboutActivity.kt index 8d2682a0d..8f0f1bf0d 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/AboutActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/AboutActivity.kt @@ -21,7 +21,7 @@ import java.text.SimpleDateFormat import java.util.Locale class AboutActivity : BaseActivity() { - private val binding by lazy {ActivityAboutBinding.inflate(layoutInflater)} + private val binding by lazy { ActivityAboutBinding.inflate(layoutInflater) } private val extDir by lazy { File(Utils.backupPath(this)) } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/BaseActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/BaseActivity.kt index db9e1d3f9..8a2c7fda8 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/BaseActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/BaseActivity.kt @@ -28,13 +28,14 @@ abstract class BaseActivity : AppCompatActivity() { onBackPressedDispatcher.onBackPressed() true } + else -> super.onOptionsItemSelected(item) } @RequiresApi(Build.VERSION_CODES.N) override fun attachBaseContext(newBase: Context?) { val context = newBase?.let { - MyContextWrapper.wrap(newBase, Utils.getLocale()) + MyContextWrapper.wrap(newBase, Utils.getLocale()) } super.attachBaseContext(context) } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/FragmentAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/FragmentAdapter.kt index d9d129928..deeb43c31 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/FragmentAdapter.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/FragmentAdapter.kt @@ -5,7 +5,7 @@ import androidx.fragment.app.FragmentActivity import androidx.viewpager2.adapter.FragmentStateAdapter class FragmentAdapter(fragmentActivity: FragmentActivity, private val mFragments: List) : - FragmentStateAdapter(fragmentActivity) { + FragmentStateAdapter(fragmentActivity) { override fun createFragment(position: Int): Fragment { return mFragments[position] diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/LogcatActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/LogcatActivity.kt index 5ae0f3571..c71294eb0 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/LogcatActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/LogcatActivity.kt @@ -84,10 +84,12 @@ class LogcatActivity : BaseActivity() { toast(R.string.toast_success) true } + R.id.clear_all -> { logcat(true) true } + else -> super.onOptionsItemSelected(item) } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt index 20d4df330..8dd873de2 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -32,7 +32,6 @@ import com.v2ray.ang.AppConfig import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivityMainBinding import com.v2ray.ang.dto.EConfigType -import com.v2ray.ang.extension.isNetworkConnected import com.v2ray.ang.extension.toast import com.v2ray.ang.helper.SimpleItemTouchHelperCallback import com.v2ray.ang.service.V2RayServiceManager diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt index c0141c9bd..4d7ebc1fb 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt @@ -29,8 +29,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Observable import java.util.concurrent.TimeUnit -class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter() - , ItemTouchHelperAdapter { +class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter(), ItemTouchHelperAdapter { companion object { private const val VIEW_TYPE_ITEM = 1 private const val VIEW_TYPE_FOOTER = 2 @@ -95,7 +94,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter - if (blacklist.contains(one.packageName)) { - one.isSelected = 1 - } else { - one.isSelected = 0 - } + .subscribeOn(Schedulers.io()) + .map { + if (blacklist != null) { + it.forEach { one -> + if (blacklist.contains(one.packageName)) { + one.isSelected = 1 + } else { + one.isSelected = 0 } - val comparator = Comparator { p1, p2 -> - when { - p1.isSelected > p2.isSelected -> -1 - p1.isSelected == p2.isSelected -> 0 - else -> 1 - } - } - it.sortedWith(comparator) - } else { - val comparator = object : Comparator { - val collator = Collator.getInstance() - override fun compare(o1: AppInfo, o2: AppInfo) = collator.compare(o1.appName, o2.appName) + } + val comparator = Comparator { p1, p2 -> + when { + p1.isSelected > p2.isSelected -> -1 + p1.isSelected == p2.isSelected -> 0 + else -> 1 } - it.sortedWith(comparator) } + it.sortedWith(comparator) + } else { + val comparator = object : Comparator { + val collator = Collator.getInstance() + override fun compare(o1: AppInfo, o2: AppInfo) = collator.compare(o1.appName, o2.appName) + } + it.sortedWith(comparator) } + } // .map { // val comparator = object : Comparator { // val collator = Collator.getInstance() @@ -79,13 +79,13 @@ class PerAppProxyActivity : BaseActivity() { // } // it.sortedWith(comparator) // } - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { - appsAll = it - adapter = PerAppProxyAdapter(this, it, blacklist) - binding.recyclerView.adapter = adapter - binding.pbWaiting.visibility = View.GONE - } + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { + appsAll = it + adapter = PerAppProxyAdapter(this, it, blacklist) + binding.recyclerView.adapter = adapter + binding.pbWaiting.visibility = View.GONE + } /*** recycler_view.addOnScrollListener(object : RecyclerView.OnScrollListener() { var dst = 0 @@ -218,18 +218,22 @@ class PerAppProxyActivity : BaseActivity() { it.notifyDataSetChanged() true } ?: false + R.id.select_proxy_app -> { selectProxyApp() true } + R.id.import_proxy_app -> { importProxyApp() true } + R.id.export_proxy_app -> { exportProxyApp() true } + else -> super.onOptionsItemSelected(item) } @@ -324,7 +328,8 @@ class PerAppProxyActivity : BaseActivity() { if (key.isNotEmpty()) { appsAll?.forEach { if (it.appName.uppercase().indexOf(key) >= 0 - || it.packageName.uppercase().indexOf(key) >= 0) { + || it.packageName.uppercase().indexOf(key) >= 0 + ) { apps.add(it) } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/PerAppProxyAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/PerAppProxyAdapter.kt index 040e9b9c2..7e5dcddde 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/PerAppProxyAdapter.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/PerAppProxyAdapter.kt @@ -1,16 +1,15 @@ package com.v2ray.ang.ui import android.view.LayoutInflater -import androidx.recyclerview.widget.RecyclerView import android.view.View import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView import com.v2ray.ang.R import com.v2ray.ang.databinding.ItemRecyclerBypassListBinding import com.v2ray.ang.dto.AppInfo -import java.util.* class PerAppProxyAdapter(val activity: BaseActivity, val apps: List, blacklist: MutableSet?) : - RecyclerView.Adapter() { + RecyclerView.Adapter() { companion object { private const val VIEW_TYPE_HEADER = 0 @@ -34,8 +33,10 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List, bl return when (viewType) { VIEW_TYPE_HEADER -> { val view = View(ctx) - view.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ctx.resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 0) + view.layoutParams = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ctx.resources.getDimensionPixelSize(R.dimen.bypass_list_header_height) * 0 + ) BaseViewHolder(view) } // VIEW_TYPE_ITEM -> AppViewHolder(ctx.layoutInflater @@ -51,7 +52,7 @@ class PerAppProxyAdapter(val activity: BaseActivity, val apps: List, bl open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) inner class AppViewHolder(private val itemBypassBinding: ItemRecyclerBypassListBinding) : BaseViewHolder(itemBypassBinding.root), - View.OnClickListener { + View.OnClickListener { private val inBlacklist: Boolean get() = blacklist.contains(appInfo.packageName) private lateinit var appInfo: AppInfo diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsActivity.kt index 9eab7935c..c8d84ab0c 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsActivity.kt @@ -1,10 +1,10 @@ package com.v2ray.ang.ui import android.os.Bundle -import com.v2ray.ang.R import androidx.fragment.app.Fragment import com.google.android.material.tabs.TabLayoutMediator import com.v2ray.ang.AppConfig +import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivityRoutingSettingsBinding class RoutingSettingsActivity : BaseActivity() { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsFragment.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsFragment.kt index 02a690d0e..4904a69c5 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsFragment.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingsFragment.kt @@ -1,15 +1,19 @@ package com.v2ray.ang.ui import android.Manifest -import androidx.appcompat.app.AppCompatActivity.RESULT_OK import android.content.Intent import android.os.Bundle import android.text.TextUtils -import android.view.* +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity.RESULT_OK import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope -import androidx.preference.PreferenceManager import com.tbruyelle.rxpermissions3.RxPermissions import com.tencent.mmkv.MMKV import com.v2ray.ang.AppConfig @@ -24,14 +28,17 @@ import kotlinx.coroutines.launch class RoutingSettingsFragment : Fragment() { private val binding by lazy { FragmentRoutingSettingsBinding.inflate(layoutInflater) } + companion object { private const val routing_arg = "routing_arg" } - private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View { // Inflate the layout for this fragment return binding.root// inflater.inflate(R.layout.fragment_routing_settings, container, false) } @@ -63,22 +70,27 @@ class RoutingSettingsFragment : Fragment() { saveRouting() true } + R.id.del_routing -> { binding.etRoutingContent.text = null true } + R.id.scan_replace -> { scanQRcode(true) true } + R.id.scan_append -> { scanQRcode(false) true } + R.id.default_rules -> { setDefaultRules() true } + else -> super.onOptionsItemSelected(item) } @@ -95,16 +107,16 @@ class RoutingSettingsFragment : Fragment() { // .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), requestCode) // } catch (e: Exception) { RxPermissions(requireActivity()) - .request(Manifest.permission.CAMERA) - .subscribe { - if (it) - if (forReplace) - scanQRCodeForReplace.launch(Intent(activity, ScannerActivity::class.java)) - else - scanQRCodeForAppend.launch(Intent(activity, ScannerActivity::class.java)) + .request(Manifest.permission.CAMERA) + .subscribe { + if (it) + if (forReplace) + scanQRCodeForReplace.launch(Intent(activity, ScannerActivity::class.java)) else - activity?.toast(R.string.toast_permission_denied) - } + scanQRCodeForAppend.launch(Intent(activity, ScannerActivity::class.java)) + else + activity?.toast(R.string.toast_permission_denied) + } // } return true } @@ -130,9 +142,11 @@ class RoutingSettingsFragment : Fragment() { AppConfig.PREF_V2RAY_ROUTING_AGENT -> { tag = AppConfig.TAG_PROXY } + AppConfig.PREF_V2RAY_ROUTING_DIRECT -> { tag = AppConfig.TAG_DIRECT } + AppConfig.PREF_V2RAY_ROUTING_BLOCKED -> { tag = AppConfig.TAG_BLOCKED } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScScannerActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScScannerActivity.kt index c85085537..17090b75d 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScScannerActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScScannerActivity.kt @@ -19,13 +19,13 @@ class ScScannerActivity : BaseActivity() { fun importQRcode(): Boolean { RxPermissions(this) - .request(Manifest.permission.CAMERA) - .subscribe { - if (it) - scanQRCode.launch(Intent(this, ScannerActivity::class.java)) - else - toast(R.string.toast_permission_denied) - } + .request(Manifest.permission.CAMERA) + .subscribe { + if (it) + scanQRCode.launch(Intent(this, ScannerActivity::class.java)) + else + toast(R.string.toast_permission_denied) + } return true } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt index 3d3101efe..7f536971b 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScSwitchActivity.kt @@ -1,9 +1,9 @@ package com.v2ray.ang.ui -import com.v2ray.ang.R -import com.v2ray.ang.util.Utils import android.os.Bundle +import com.v2ray.ang.R import com.v2ray.ang.service.V2RayServiceManager +import com.v2ray.ang.util.Utils class ScSwitchActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScannerActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScannerActivity.kt index c38d53625..029f023b1 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScannerActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScannerActivity.kt @@ -1,11 +1,10 @@ package com.v2ray.ang.ui import android.Manifest -import androidx.appcompat.app.AppCompatActivity -import android.os.Bundle import android.content.Intent import android.graphics.BitmapFactory import android.os.Build +import android.os.Bundle import android.view.Menu import android.view.MenuItem import androidx.activity.result.contract.ActivityResultContracts @@ -20,7 +19,7 @@ import io.github.g00fy2.quickie.QRResult import io.github.g00fy2.quickie.ScanCustomCode import io.github.g00fy2.quickie.config.ScannerConfig -class ScannerActivity : BaseActivity(){ +class ScannerActivity : BaseActivity() { private val scanQrCode = registerForActivityResult(ScanCustomCode(), ::handleResult) private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } @@ -33,7 +32,7 @@ class ScannerActivity : BaseActivity(){ } } - private fun launchScan(){ + private fun launchScan() { scanQrCode.launch( ScannerConfig.build { setHapticSuccessFeedback(true) // enable (default) or disable haptic feedback when a barcode was detected @@ -44,7 +43,7 @@ class ScannerActivity : BaseActivity(){ } private fun handleResult(result: QRResult) { - if (result is QRResult.QRSuccess ) { + if (result is QRResult.QRSuccess) { finished(result.content.rawValue.orEmpty()) } else { finish() @@ -54,7 +53,7 @@ class ScannerActivity : BaseActivity(){ private fun finished(text: String) { val intent = Intent() intent.putExtra("SCAN_RESULT", text) - setResult(AppCompatActivity.RESULT_OK, intent) + setResult(RESULT_OK, intent) finish() } @@ -68,6 +67,7 @@ class ScannerActivity : BaseActivity(){ launchScan() true } + R.id.select_photo -> { val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { Manifest.permission.READ_MEDIA_IMAGES @@ -88,6 +88,7 @@ class ScannerActivity : BaseActivity(){ } true } + else -> super.onOptionsItemSelected(item) } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt index 0b5391bb5..fbfe8d1eb 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt @@ -533,16 +533,16 @@ class ServerActivity : BaseActivity() { val spiderX = et_spider_x?.text?.toString()?.trim() ?: return var sni = streamSetting.populateTransportSettings( - transport = networks[network], - headerType = transportTypes(networks[network])[type], - host = requestHost, - path = path, - seed = path, - quicSecurity = requestHost, - key = path, - mode = transportTypes(networks[network])[type], - serviceName = path, - authority = requestHost, + transport = networks[network], + headerType = transportTypes(networks[network])[type], + host = requestHost, + path = path, + seed = path, + quicSecurity = requestHost, + key = path, + mode = transportTypes(networks[network])[type], + serviceName = path, + authority = requestHost, ) if (sniField.isNotBlank()) { sni = sniField diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt index f760b3f31..3df4d3a04 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerCustomConfigActivity.kt @@ -105,14 +105,14 @@ class ServerCustomConfigActivity : BaseActivity() { private fun deleteServer(): Boolean { if (editGuid.isNotEmpty()) { AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) - .setPositiveButton(android.R.string.ok) { _, _ -> - MmkvManager.removeServer(editGuid) - finish() - } - .setNegativeButton(android.R.string.no) {_, _ -> - // do nothing - } - .show() + .setPositiveButton(android.R.string.ok) { _, _ -> + MmkvManager.removeServer(editGuid) + finish() + } + .setNegativeButton(android.R.string.no) { _, _ -> + // do nothing + } + .show() } return true } @@ -139,10 +139,12 @@ class ServerCustomConfigActivity : BaseActivity() { deleteServer() true } + R.id.save_config -> { saveServer() true } + else -> super.onOptionsItemSelected(item) } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt index a025909a1..305ed2741 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt @@ -184,7 +184,7 @@ class SettingsActivity : BaseActivity() { localDns?.isChecked = settingsStorage.getBoolean(AppConfig.PREF_LOCAL_DNS_ENABLED, false) fakeDns?.isChecked = settingsStorage.getBoolean(AppConfig.PREF_FAKE_DNS_ENABLED, false) localDnsPort?.summary = settingsStorage.decodeString(AppConfig.PREF_LOCAL_DNS_PORT, AppConfig.PORT_LOCAL_DNS) - vpnDns?.summary = settingsStorage.decodeString(AppConfig.PREF_VPN_DNS, AppConfig.DNS_VPN) + vpnDns?.summary = settingsStorage.decodeString(AppConfig.PREF_VPN_DNS, AppConfig.DNS_VPN) updateMux(settingsStorage.getBoolean(AppConfig.PREF_MUX_ENABLED, false)) mux?.isChecked = settingsStorage.getBoolean(AppConfig.PREF_MUX_ENABLED, false) @@ -198,7 +198,8 @@ class SettingsActivity : BaseActivity() { fragmentInterval?.summary = settingsStorage.decodeString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20") autoUpdateCheck?.isChecked = settingsStorage.getBoolean(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false) - autoUpdateInterval?.summary = settingsStorage.decodeString(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL,AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL) + autoUpdateInterval?.summary = + settingsStorage.decodeString(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL, AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL) autoUpdateInterval?.isEnabled = settingsStorage.getBoolean(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false) socksPort?.summary = settingsStorage.decodeString(AppConfig.PREF_SOCKS_PORT, AppConfig.PORT_SOCKS) @@ -350,12 +351,15 @@ class SettingsActivity : BaseActivity() { updateFragmentInterval(settingsStorage.decodeString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20")) } } + private fun updateFragmentPackets(value: String?) { fragmentPackets?.summary = value.toString() } + private fun updateFragmentLength(value: String?) { fragmentLength?.summary = value.toString() } + private fun updateFragmentInterval(value: String?) { fragmentInterval?.summary = value.toString() } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt index 1f52f6d28..d9546f34b 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt @@ -18,7 +18,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch class SubEditActivity : BaseActivity() { - private val binding by lazy {ActivitySubEditBinding.inflate(layoutInflater)} + private val binding by lazy { ActivitySubEditBinding.inflate(layoutInflater) } var del_config: MenuItem? = null var save_config: MenuItem? = null @@ -100,18 +100,18 @@ class SubEditActivity : BaseActivity() { private fun deleteServer(): Boolean { if (editSubId.isNotEmpty()) { AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) - .setPositiveButton(android.R.string.ok) { _, _ -> - lifecycleScope.launch(Dispatchers.IO) { - MmkvManager.removeSubscription(editSubId) - launch(Dispatchers.Main) { - finish() - } + .setPositiveButton(android.R.string.ok) { _, _ -> + lifecycleScope.launch(Dispatchers.IO) { + MmkvManager.removeSubscription(editSubId) + launch(Dispatchers.Main) { + finish() } } - .setNegativeButton(android.R.string.no) {_, _ -> - // do nothing - } - .show() + } + .setNegativeButton(android.R.string.no) { _, _ -> + // do nothing + } + .show() } return true } @@ -133,10 +133,12 @@ class SubEditActivity : BaseActivity() { deleteServer() true } + R.id.save_config -> { saveServer() true } + else -> super.onOptionsItemSelected(item) } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/TaskerActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/TaskerActivity.kt index 872607d3e..edf352f7a 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/TaskerActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/TaskerActivity.kt @@ -1,18 +1,16 @@ package com.v2ray.ang.ui -import androidx.appcompat.app.AppCompatActivity -import android.os.Bundle -import android.view.View -import android.widget.ArrayAdapter -import android.widget.ListView -import java.util.ArrayList -import com.v2ray.ang.R import android.content.Intent +import android.os.Bundle import android.text.TextUtils import android.view.Menu import android.view.MenuItem +import android.view.View +import android.widget.ArrayAdapter +import android.widget.ListView import com.tencent.mmkv.MMKV import com.v2ray.ang.AppConfig +import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivityTaskerBinding import com.v2ray.ang.util.MmkvManager @@ -39,8 +37,10 @@ class TaskerActivity : BaseActivity() { lstGuid.add(key) } } - val adapter = ArrayAdapter(this, - android.R.layout.simple_list_item_single_choice, lstData) + val adapter = ArrayAdapter( + this, + android.R.layout.simple_list_item_single_choice, lstData + ) listview = findViewById(R.id.listview) as ListView listview?.adapter = adapter @@ -88,7 +88,7 @@ class TaskerActivity : BaseActivity() { intent.putExtra(AppConfig.TASKER_EXTRA_BUNDLE, extraBundle) intent.putExtra(AppConfig.TASKER_EXTRA_STRING_BLURB, blurb) - setResult(AppCompatActivity.RESULT_OK, intent) + setResult(RESULT_OK, intent) finish() } @@ -103,10 +103,12 @@ class TaskerActivity : BaseActivity() { R.id.del_config -> { true } + R.id.save_config -> { confirmFinish() true } + else -> super.onOptionsItemSelected(item) } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetActivity.kt index 9cd441cd1..5a8f814e2 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetActivity.kt @@ -83,6 +83,7 @@ class UserAssetActivity : BaseActivity() { startActivity(intent) true } + R.id.download_file -> { downloadGeoFiles() true @@ -100,24 +101,24 @@ class UserAssetActivity : BaseActivity() { RxPermissions(this) .request(permission) .subscribe { - if (it) { - val intent = Intent(Intent.ACTION_GET_CONTENT) - intent.type = "*/*" - intent.addCategory(Intent.CATEGORY_OPENABLE) - - try { - chooseFile.launch( - Intent.createChooser( - intent, - getString(R.string.title_file_chooser) + if (it) { + val intent = Intent(Intent.ACTION_GET_CONTENT) + intent.type = "*/*" + intent.addCategory(Intent.CATEGORY_OPENABLE) + + try { + chooseFile.launch( + Intent.createChooser( + intent, + getString(R.string.title_file_chooser) + ) ) - ) - } catch (ex: android.content.ActivityNotFoundException) { - toast(R.string.toast_require_file_manager) - } - } else - toast(R.string.toast_permission_denied) - } + } catch (ex: android.content.ActivityNotFoundException) { + toast(R.string.toast_require_file_manager) + } + } else + toast(R.string.toast_permission_denied) + } } private val chooseFile = @@ -237,15 +238,18 @@ class UserAssetActivity : BaseActivity() { conn?.disconnect() } } + private fun addBuiltInGeoItems(assets: List>): List> { val list = mutableListOf>() builtInGeoFiles .filter { geoFile -> assets.none { it.second.remarks == geoFile } } - .forEach { - list.add(Utils.getUuid() to AssetUrlItem( - it, - AppConfig.GeoUrl + it - )) + .forEach { + list.add( + Utils.getUuid() to AssetUrlItem( + it, + AppConfig.GeoUrl + it + ) + ) } return list + assets @@ -257,14 +261,15 @@ class UserAssetActivity : BaseActivity() { ItemRecyclerUserAssetBinding.inflate( LayoutInflater.from(parent.context), parent, - false) + false + ) ) } @SuppressLint("SetTextI18n") override fun onBindViewHolder(holder: UserAssetViewHolder, position: Int) { - var assets = MmkvManager.decodeAssetUrls(); - assets = addBuiltInGeoItems(assets); + var assets = MmkvManager.decodeAssetUrls() + assets = addBuiltInGeoItems(assets) val item = assets.getOrNull(position) ?: return // file with name == item.second.remarks val file = extDir.listFiles()?.find { it.name == item.second.remarks } @@ -300,8 +305,8 @@ class UserAssetActivity : BaseActivity() { } override fun getItemCount(): Int { - var assets = MmkvManager.decodeAssetUrls(); - assets = addBuiltInGeoItems(assets); + var assets = MmkvManager.decodeAssetUrls() + assets = addBuiltInGeoItems(assets) return assets.size } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetUrlActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetUrlActivity.kt index 1d5d66f58..63dba839a 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetUrlActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetUrlActivity.kt @@ -112,7 +112,7 @@ class UserAssetUrlActivity : BaseActivity() { MmkvManager.removeAssetUrl(editAssetId) finish() } - .setNegativeButton(android.R.string.no) {_, _ -> + .setNegativeButton(android.R.string.no) { _, _ -> // do nothing } .show() @@ -137,10 +137,12 @@ class UserAssetUrlActivity : BaseActivity() { deleteServer() true } + R.id.save_config -> { saveServer() true } + else -> super.onOptionsItemSelected(item) } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt index 37bf1fd87..beda150fe 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt @@ -386,7 +386,7 @@ object AngConfigManager { // } // } - fun importBatchConfig(server: String?, subid: String, append: Boolean): Pair { + fun importBatchConfig(server: String?, subid: String, append: Boolean): Pair { var count = parseBatchConfig(Utils.decode(server), subid, append) if (count <= 0) { count = parseBatchConfig(server, subid, append) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt index a01ee24ae..7be5024a6 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/Utils.kt @@ -140,7 +140,7 @@ object Utils { } fun getVpnDnsServers(): List { - val vpnDns = settingsStorage?.decodeString(AppConfig.PREF_VPN_DNS)?:AppConfig.DNS_VPN + val vpnDns = settingsStorage?.decodeString(AppConfig.PREF_VPN_DNS) ?: AppConfig.DNS_VPN return vpnDns.split(",").filter { isPureIpAddress(it) } // allow empty, in that case dns will use system default } @@ -204,7 +204,8 @@ object Utils { } fun isIpv4Address(value: String): Boolean { - val regV4 = Regex("^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$") + val regV4 = + Regex("^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$") return regV4.matches(value) } @@ -214,7 +215,8 @@ object Utils { addr = addr.drop(1) addr = addr.dropLast(addr.count() - addr.lastIndexOf("]")) } - val regV6 = Regex("^((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7}$") + val regV6 = + Regex("^((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*::((?:[0-9A-Fa-f]{1,4}))?((?::[0-9A-Fa-f]{1,4}))*|((?:[0-9A-Fa-f]{1,4}))((?::[0-9A-Fa-f]{1,4})){7}$") return regV6.matches(addr) } @@ -300,8 +302,7 @@ object Utils { * readTextFromAssets */ fun readTextFromAssets(context: Context?, fileName: String): String { - if(context == null) - { + if (context == null) { return "" } val content = context.assets.open(fileName).bufferedReader().use { @@ -314,7 +315,7 @@ object Utils { if (context == null) return "" val extDir = context.getExternalFilesDir(AppConfig.DIR_ASSETS) - ?: return context.getDir(AppConfig.DIR_ASSETS, 0).absolutePath + ?: return context.getDir(AppConfig.DIR_ASSETS, 0).absolutePath return extDir.absolutePath } @@ -370,8 +371,10 @@ object Utils { conn.setRequestProperty("Connection", "close") conn.setRequestProperty("User-agent", "v2rayNG/${BuildConfig.VERSION_NAME}") url.userInfo?.let { - conn.setRequestProperty("Authorization", - "Basic ${encode(urlDecode(it))}") + conn.setRequestProperty( + "Authorization", + "Basic ${encode(urlDecode(it))}" + ) } conn.useCaches = false return conn.inputStream.use { @@ -393,7 +396,7 @@ object Utils { } fun getIpv6Address(address: String?): String { - if(address == null){ + if (address == null) { return "" } return if (isIpv6Address(address) && !address.contains('[') && !address.contains(']')) { @@ -427,8 +430,8 @@ object Utils { fun fixIllegalUrl(str: String): String { return str - .replace(" ","%20") - .replace("|","%7C") + .replace(" ", "%20") + .replace("|", "%7C") } fun removeWhiteSpace(str: String?): String? { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt index 749e67d39..40e068ee0 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/ShadowsocksFmt.kt @@ -67,7 +67,7 @@ object ShadowsocksFmt { private fun tryResolveResolveSip002(str: String, config: ServerConfig): Boolean { try { val uri = URI(Utils.fixIllegalUrl(str)) - config.remarks = Utils.urlDecode(uri.fragment .orEmpty()) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) val method: String val password: String @@ -88,7 +88,7 @@ object ShadowsocksFmt { password = base64Decode.substringAfter(":") } - val query = Utils.urlDecode(uri.query .orEmpty()) + val query = Utils.urlDecode(uri.query.orEmpty()) if (query != "") { val queryPairs = HashMap() val pairs = query.split(";") diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt index 90fa94124..cc76272ec 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/TrojanFmt.kt @@ -19,12 +19,12 @@ object TrojanFmt { ) } - fun parseTrojan(str: String): ServerConfig? { + fun parseTrojan(str: String): ServerConfig { var allowInsecure = settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false val config = ServerConfig.create(EConfigType.TROJAN) val uri = URI(Utils.fixIllegalUrl(str)) - config.remarks = Utils.urlDecode(uri.fragment .orEmpty()) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) var flow = "" var fingerprint = config.outboundBean?.streamSettings?.tlsSettings?.fingerprint @@ -56,7 +56,7 @@ object TrojanFmt { queryParam["authority"] ) fingerprint = queryParam["fp"].orEmpty() - allowInsecure = if ((queryParam["allowInsecure"] .orEmpty()) == "1") true else allowInsecure + allowInsecure = if ((queryParam["allowInsecure"].orEmpty()) == "1") true else allowInsecure config.outboundBean?.streamSettings?.populateTlsSettings( queryParam["security"] ?: V2rayConfig.TLS, allowInsecure, @@ -111,7 +111,7 @@ object TrojanFmt { dicQuery["sid"] = tlsSetting.shortId.orEmpty() } if (!TextUtils.isEmpty(tlsSetting.spiderX)) { - dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX .orEmpty()) + dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX.orEmpty()) } } dicQuery["type"] = diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt index 3196305a4..ef4424014 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VlessFmt.kt @@ -30,7 +30,7 @@ object VlessFmt { val streamSetting = config.outboundBean?.streamSettings ?: return null - config.remarks = Utils.urlDecode(uri.fragment .orEmpty()) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) config.outboundBean.settings?.vnext?.get(0)?.let { vnext -> vnext.address = uri.idnHost vnext.port = uri.port @@ -51,7 +51,7 @@ object VlessFmt { queryParam["serviceName"], queryParam["authority"] ) - allowInsecure = if ((queryParam["allowInsecure"] .orEmpty()) == "1") true else allowInsecure + allowInsecure = if ((queryParam["allowInsecure"].orEmpty()) == "1") true else allowInsecure streamSetting.populateTlsSettings( queryParam["security"].orEmpty(), allowInsecure, @@ -102,7 +102,7 @@ object VlessFmt { dicQuery["sid"] = tlsSetting.shortId.orEmpty() } if (!TextUtils.isEmpty(tlsSetting.spiderX)) { - dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX .orEmpty()) + dicQuery["spx"] = Utils.urlEncode(tlsSetting.spiderX.orEmpty()) } } dicQuery["type"] = diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt index 313700a6c..d191c6d02 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/VmessFmt.kt @@ -121,7 +121,7 @@ object VmessFmt { val streamSetting = config.outboundBean?.streamSettings ?: return null - config.remarks = Utils.urlDecode(uri.fragment .orEmpty()) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) config.outboundBean.settings?.vnext?.get(0)?.let { vnext -> vnext.address = uri.idnHost vnext.port = uri.port @@ -143,7 +143,7 @@ object VmessFmt { queryParam["authority"] ) - allowInsecure = if ((queryParam["allowInsecure"] .orEmpty()) == "1") true else allowInsecure + allowInsecure = if ((queryParam["allowInsecure"].orEmpty()) == "1") true else allowInsecure streamSetting.populateTlsSettings( queryParam["security"].orEmpty(), allowInsecure, diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt index 8375870a7..34fb2effc 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/fmt/WireguardFmt.kt @@ -13,7 +13,7 @@ object WireguardFmt { val uri = URI(Utils.fixIllegalUrl(str)) if (uri.rawQuery != null) { val config = ServerConfig.create(EConfigType.WIREGUARD) - config.remarks = Utils.urlDecode(uri.fragment .orEmpty()) + config.remarks = Utils.urlDecode(uri.fragment.orEmpty()) val queryParam = uri.rawQuery.split("&") .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } } diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_action_done.xml b/V2rayNG/app/src/main/res/drawable-night/ic_action_done.xml index 33a117f67..c3fb8a471 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_action_done.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_action_done.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:fillColor="#FFFFFFFF" + android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z" /> diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_add_24dp.xml b/V2rayNG/app/src/main/res/drawable-night/ic_add_24dp.xml index b9b8eca8b..d29480261 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_add_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_add_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" /> diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_copy.xml b/V2rayNG/app/src/main/res/drawable-night/ic_copy.xml index e50927b22..9cafc8523 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_copy.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_copy.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z" /> diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_delete_24dp.xml b/V2rayNG/app/src/main/res/drawable-night/ic_delete_24dp.xml index ab38bb6d6..4c803eca9 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_delete_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_delete_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8.46,11.88l1.41,-1.41L12,12.59l2.12,-2.12 1.41,1.41L13.41,14l2.12,2.12 -1.41,1.41L12,15.41l-2.12,2.12 -1.41,-1.41L10.59,14l-2.13,-2.12zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z" /> diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_description_24dp.xml b/V2rayNG/app/src/main/res/drawable-night/ic_description_24dp.xml index 7e0d28e3e..6f55b8de3 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_description_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_description_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,18L8,18v-2h8v2zM16,14L8,14v-2h8v2zM13,9L13,3.5L18.5,9L13,9z" /> diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_edit_24dp.xml b/V2rayNG/app/src/main/res/drawable-night/ic_edit_24dp.xml index 35a774a51..85d32985f 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_edit_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_edit_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" /> diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_file_24dp.xml b/V2rayNG/app/src/main/res/drawable-night/ic_file_24dp.xml index 3ddbb298c..30595842a 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_file_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_file_24dp.xml @@ -3,7 +3,7 @@ android:height="24dp" android:viewportWidth="1024" android:viewportHeight="1024"> - + diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_logcat_24dp.xml b/V2rayNG/app/src/main/res/drawable-night/ic_logcat_24dp.xml index c60b60624..1c1a9c94c 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_logcat_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_logcat_24dp.xml @@ -5,11 +5,11 @@ android:viewportHeight="1024"> + android:fillColor="#FFFFFFFF" /> + android:fillColor="#FFFFFFFF" /> + android:fillColor="#FFFFFFFF" /> diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_outline_filter_alt_24.xml b/V2rayNG/app/src/main/res/drawable-night/ic_outline_filter_alt_24.xml index 6cb82eff7..9d0d59105 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_outline_filter_alt_24.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_outline_filter_alt_24.xml @@ -1,5 +1,10 @@ - - + + diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_promotion_24dp.xml b/V2rayNG/app/src/main/res/drawable-night/ic_promotion_24dp.xml index e5c48df84..014a5021f 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_promotion_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_promotion_24dp.xml @@ -5,8 +5,8 @@ android:viewportHeight="1024"> + android:fillColor="#FFFFFFFF" /> + android:fillColor="#FFFFFFFF" /> diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_save_24dp.xml b/V2rayNG/app/src/main/res/drawable-night/ic_save_24dp.xml index a7a81a25d..47a925197 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_save_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_save_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z" /> diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_share_24dp.xml b/V2rayNG/app/src/main/res/drawable-night/ic_share_24dp.xml index 904066636..b09d63e85 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_share_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_share_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z" /> diff --git a/V2rayNG/app/src/main/res/drawable-night/ic_telegram_24dp.xml b/V2rayNG/app/src/main/res/drawable-night/ic_telegram_24dp.xml index 983735107..b35b623c6 100644 --- a/V2rayNG/app/src/main/res/drawable-night/ic_telegram_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable-night/ic_telegram_24dp.xml @@ -3,7 +3,7 @@ android:height="24dp" android:viewportWidth="1024" android:viewportHeight="1024"> - + diff --git a/V2rayNG/app/src/main/res/drawable/ic_action_done.xml b/V2rayNG/app/src/main/res/drawable/ic_action_done.xml index 5635bead9..bda675f14 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_action_done.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_action_done.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:fillColor="#FF000000" + android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z" /> diff --git a/V2rayNG/app/src/main/res/drawable/ic_add_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_add_24dp.xml index 0258249cc..fedd077d8 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_add_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_add_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z" /> diff --git a/V2rayNG/app/src/main/res/drawable/ic_copy.xml b/V2rayNG/app/src/main/res/drawable/ic_copy.xml index 8a894a3bc..380c8783e 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_copy.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_copy.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z" /> diff --git a/V2rayNG/app/src/main/res/drawable/ic_delete_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_delete_24dp.xml index 2f5557afd..c3027b2e3 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_delete_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_delete_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2L18,7L6,7v12zM8.46,11.88l1.41,-1.41L12,12.59l2.12,-2.12 1.41,1.41L13.41,14l2.12,2.12 -1.41,1.41L12,15.41l-2.12,2.12 -1.41,-1.41L10.59,14l-2.13,-2.12zM15.5,4l-1,-1h-5l-1,1L5,4v2h14L19,4z" /> diff --git a/V2rayNG/app/src/main/res/drawable/ic_description_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_description_24dp.xml index 38c33351c..a30bc6cab 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_description_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_description_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,18L8,18v-2h8v2zM16,14L8,14v-2h8v2zM13,9L13,3.5L18.5,9L13,9z" /> diff --git a/V2rayNG/app/src/main/res/drawable/ic_edit_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_edit_24dp.xml index 2ab2fb753..5505cc775 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_edit_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_edit_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z" /> diff --git a/V2rayNG/app/src/main/res/drawable/ic_file_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_file_24dp.xml index cb5193f6a..c14b248f3 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_file_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_file_24dp.xml @@ -3,7 +3,7 @@ android:height="24dp" android:viewportWidth="1024" android:viewportHeight="1024"> - + diff --git a/V2rayNG/app/src/main/res/drawable/ic_logcat_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_logcat_24dp.xml index 86562d04f..b8a544efc 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_logcat_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_logcat_24dp.xml @@ -3,13 +3,13 @@ android:height="24dp" android:viewportWidth="1024" android:viewportHeight="1024"> - - - + + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_outline_filter_alt_24.xml b/V2rayNG/app/src/main/res/drawable/ic_outline_filter_alt_24.xml index d574422c1..3a12237a7 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_outline_filter_alt_24.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_outline_filter_alt_24.xml @@ -1,5 +1,10 @@ - - + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_promotion_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_promotion_24dp.xml index 58505a6bf..079a21973 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_promotion_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_promotion_24dp.xml @@ -3,10 +3,10 @@ android:height="24dp" android:viewportWidth="1024" android:viewportHeight="1024"> - - + + diff --git a/V2rayNG/app/src/main/res/drawable/ic_save_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_save_24dp.xml index a561d632a..0651fcc6c 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_save_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_save_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z" /> diff --git a/V2rayNG/app/src/main/res/drawable/ic_share_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_share_24dp.xml index e3fe874d6..338d95ad5 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_share_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_share_24dp.xml @@ -1,9 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z" /> diff --git a/V2rayNG/app/src/main/res/drawable/ic_source_code_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_source_code_24dp.xml index 73879a2a4..dc13d1c8b 100644 --- a/V2rayNG/app/src/main/res/drawable/ic_source_code_24dp.xml +++ b/V2rayNG/app/src/main/res/drawable/ic_source_code_24dp.xml @@ -1,4 +1,9 @@ - - + + diff --git a/V2rayNG/app/src/main/res/layout/activity_main.xml b/V2rayNG/app/src/main/res/layout/activity_main.xml index 25c6189e2..7c5ff6e3f 100644 --- a/V2rayNG/app/src/main/res/layout/activity_main.xml +++ b/V2rayNG/app/src/main/res/layout/activity_main.xml @@ -51,7 +51,7 @@ app:tabTextAppearance="@style/TabLayoutTextStyle" app:tabIndicatorFullWidth="false" android:layout_height="wrap_content" - android:layout_width="match_parent"/> + android:layout_width="match_parent" /> + android:textAppearance="@style/TextAppearance.AppCompat.Small" /> @@ -120,7 +120,7 @@ android:layout_gravity="start" app:headerLayout="@layout/nav_header" app:itemIconTint="@color/colorAccent" - app:menu="@menu/menu_drawer" > + app:menu="@menu/menu_drawer"> diff --git a/V2rayNG/app/src/main/res/layout/activity_none.xml b/V2rayNG/app/src/main/res/layout/activity_none.xml index d76816989..c7d9548b3 100644 --- a/V2rayNG/app/src/main/res/layout/activity_none.xml +++ b/V2rayNG/app/src/main/res/layout/activity_none.xml @@ -1,5 +1,4 @@ - + android:layout_height="match_parent"> diff --git a/V2rayNG/app/src/main/res/layout/activity_routing_settings.xml b/V2rayNG/app/src/main/res/layout/activity_routing_settings.xml index 7052cc5f8..a77a80620 100644 --- a/V2rayNG/app/src/main/res/layout/activity_routing_settings.xml +++ b/V2rayNG/app/src/main/res/layout/activity_routing_settings.xml @@ -1,7 +1,6 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="top|start" + android:id="@+id/editor" + android:layout_marginTop="@dimen/layout_margin_top_height" /> diff --git a/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml b/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml index 1cf580106..926a09f3e 100644 --- a/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml +++ b/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml @@ -205,9 +205,9 @@ android:inputType="text" /> - + + - - + + android:id="@+id/et_security" + android:layout_width="match_parent" + android:layout_height="@dimen/edit_height" + android:inputType="text" + android:text="none" /> @@ -225,7 +225,7 @@ android:inputType="text" /> - + - + + + + + tools:listitem="@layout/item_recycler_sub_setting" /> \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/activity_user_asset_url.xml b/V2rayNG/app/src/main/res/layout/activity_user_asset_url.xml index bc3780093..15247c42e 100644 --- a/V2rayNG/app/src/main/res/layout/activity_user_asset_url.xml +++ b/V2rayNG/app/src/main/res/layout/activity_user_asset_url.xml @@ -1,86 +1,86 @@ - - + android:layout_height="match_parent" + tools:context=".ui.UserAssetUrlActivity"> + android:orientation="vertical"> + android:orientation="vertical" + android:padding="@dimen/layout_margin_top_height"> - - + android:orientation="vertical"> - + + - + android:layout_marginTop="@dimen/layout_margin_top_height" + android:orientation="vertical"> - + - + - + - + android:layout_marginTop="@dimen/layout_margin_top_height" + android:orientation="vertical"> + - - + + - - + - + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml b/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml index 115371dfe..1c043f4f0 100644 --- a/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml +++ b/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml @@ -18,7 +18,7 @@ + android:layout_height="@dimen/edit_height" /> diff --git a/V2rayNG/app/src/main/res/layout/layout_progress.xml b/V2rayNG/app/src/main/res/layout/layout_progress.xml index 78ff04c7c..8c6994203 100644 --- a/V2rayNG/app/src/main/res/layout/layout_progress.xml +++ b/V2rayNG/app/src/main/res/layout/layout_progress.xml @@ -1,11 +1,11 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="center_vertical" + android:orientation="horizontal" + android:padding="16dp"> diff --git a/V2rayNG/app/src/main/res/layout/tls_layout.xml b/V2rayNG/app/src/main/res/layout/tls_layout.xml index dcbf2e17b..2f29d7828 100644 --- a/V2rayNG/app/src/main/res/layout/tls_layout.xml +++ b/V2rayNG/app/src/main/res/layout/tls_layout.xml @@ -1,10 +1,9 @@ - + android:orientation="vertical"> + + + + + android:orientation="vertical"> + + + + app:showAsAction="ifRoom"> + app:showAsAction="always" /> - + + android:id="@+id/user_asset_setting" + android:icon="@drawable/ic_file_24dp" + android:title="@string/title_user_asset_setting" /> diff --git a/V2rayNG/app/src/main/res/menu/menu_main.xml b/V2rayNG/app/src/main/res/menu/menu_main.xml index 5bcba5e7e..7894237ba 100644 --- a/V2rayNG/app/src/main/res/menu/menu_main.xml +++ b/V2rayNG/app/src/main/res/menu/menu_main.xml @@ -6,7 +6,7 @@ android:icon="@drawable/ic_outline_filter_alt_24" android:title="@string/menu_item_search" app:actionViewClass="androidx.appcompat.widget.SearchView" - app:showAsAction="always|collapseActionView"/> + app:showAsAction="always|collapseActionView" /> - + - + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/V2rayNG/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index e202e4bed..c46314ada 100644 --- a/V2rayNG/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/V2rayNG/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,6 +1,6 @@ - - + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/V2rayNG/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 036d09bc5..c9ad5f98f 100644 --- a/V2rayNG/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/V2rayNG/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/values-ar/strings.xml b/V2rayNG/app/src/main/res/values-ar/strings.xml index 4f31bde96..7370804c6 100644 --- a/V2rayNG/app/src/main/res/values-ar/strings.xml +++ b/V2rayNG/app/src/main/res/values-ar/strings.xml @@ -42,7 +42,7 @@ العنوان المنفذ المعرف - AlterId + AlterId الأمان الشبكة @@ -110,7 +110,7 @@ الملف غير موجود الملاحظات موجودة بالفعل الإجراء غير مسموح به - + جار التحميل بحث diff --git a/V2rayNG/app/src/main/res/values/arrays.xml b/V2rayNG/app/src/main/res/values/arrays.xml index 684c44a5a..721aae042 100644 --- a/V2rayNG/app/src/main/res/values/arrays.xml +++ b/V2rayNG/app/src/main/res/values/arrays.xml @@ -49,7 +49,7 @@ gun multi - + diff --git a/V2rayNG/app/src/main/res/values/attrs.xml b/V2rayNG/app/src/main/res/values/attrs.xml index 52069cfb1..0df1370f9 100644 --- a/V2rayNG/app/src/main/res/values/attrs.xml +++ b/V2rayNG/app/src/main/res/values/attrs.xml @@ -1,5 +1,6 @@ + diff --git a/V2rayNG/app/src/main/res/xml/cache_paths.xml b/V2rayNG/app/src/main/res/xml/cache_paths.xml index f9649fc75..c782c2836 100644 --- a/V2rayNG/app/src/main/res/xml/cache_paths.xml +++ b/V2rayNG/app/src/main/res/xml/cache_paths.xml @@ -1,4 +1,6 @@ - + diff --git a/V2rayNG/app/src/test/java/com/v2ray/ang/ExampleUnitTest.java b/V2rayNG/app/src/test/java/com/v2ray/ang/ExampleUnitTest.java index 5c6cdaa64..3e28d6402 100644 --- a/V2rayNG/app/src/test/java/com/v2ray/ang/ExampleUnitTest.java +++ b/V2rayNG/app/src/test/java/com/v2ray/ang/ExampleUnitTest.java @@ -1,9 +1,9 @@ package com.v2ray.ang; -import org.junit.Test; - import static org.junit.Assert.assertEquals; +import org.junit.Test; + /** * To work on unit tests, switch the Test Artifact in the Build Variants view. */ diff --git a/V2rayNG/app/src/test/kotlin/com/v2ray/ang/ExampleUnitTest.kt b/V2rayNG/app/src/test/kotlin/com/v2ray/ang/ExampleUnitTest.kt index 8e7961ffe..7cda0075e 100644 --- a/V2rayNG/app/src/test/kotlin/com/v2ray/ang/ExampleUnitTest.kt +++ b/V2rayNG/app/src/test/kotlin/com/v2ray/ang/ExampleUnitTest.kt @@ -1,39 +1,41 @@ -import org.junit.Assert.* -import org.junit.Test import com.v2ray.ang.util.Utils +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test class UtilTest { - @Test - fun test_parseInt() { - assertEquals(Utils.parseInt("1234"), 1234) - } - - @Test - fun test_isIpAddress() { - assertFalse(Utils.isIpAddress("114.113.112.266")) - assertFalse(Utils.isIpAddress("666.666.666.666")) - assertFalse(Utils.isIpAddress("256.0.0.0" )) - assertFalse(Utils.isIpAddress("::ffff:127.0.0.0.1" )) - assertFalse(Utils.isIpAddress("baidu.com")) - assertFalse(Utils.isIpAddress("")) + @Test + fun test_parseInt() { + assertEquals(Utils.parseInt("1234"), 1234) + } + + @Test + fun test_isIpAddress() { + assertFalse(Utils.isIpAddress("114.113.112.266")) + assertFalse(Utils.isIpAddress("666.666.666.666")) + assertFalse(Utils.isIpAddress("256.0.0.0")) + assertFalse(Utils.isIpAddress("::ffff:127.0.0.0.1")) + assertFalse(Utils.isIpAddress("baidu.com")) + assertFalse(Utils.isIpAddress("")) - assertTrue(Utils.isIpAddress("127.0.0.1" )) - assertTrue(Utils.isIpAddress("127.0.0.1:80" )) - assertTrue(Utils.isIpAddress("0.0.0.0/0" )) - assertTrue(Utils.isIpAddress("::1" )) - assertTrue(Utils.isIpAddress("[::1]:80" )) - assertTrue(Utils.isIpAddress("2605:2700:0:3::4713:93e3" )) - assertTrue(Utils.isIpAddress("[2605:2700:0:3::4713:93e3]:80" )) - assertTrue(Utils.isIpAddress("::ffff:192.168.173.22" )) - assertTrue(Utils.isIpAddress("[::ffff:192.168.173.22]:80" )) - assertTrue(Utils.isIpAddress("1::" )) - assertTrue(Utils.isIpAddress("::" )) - assertTrue(Utils.isIpAddress("::/0" )) - assertTrue(Utils.isIpAddress("10.24.56.0/24" )) - assertTrue(Utils.isIpAddress("2001:4321::1" )) - assertTrue(Utils.isIpAddress("240e:1234:abcd:12::6666" )) - assertTrue(Utils.isIpAddress("240e:1234:abcd:12::/64" )) - } + assertTrue(Utils.isIpAddress("127.0.0.1")) + assertTrue(Utils.isIpAddress("127.0.0.1:80")) + assertTrue(Utils.isIpAddress("0.0.0.0/0")) + assertTrue(Utils.isIpAddress("::1")) + assertTrue(Utils.isIpAddress("[::1]:80")) + assertTrue(Utils.isIpAddress("2605:2700:0:3::4713:93e3")) + assertTrue(Utils.isIpAddress("[2605:2700:0:3::4713:93e3]:80")) + assertTrue(Utils.isIpAddress("::ffff:192.168.173.22")) + assertTrue(Utils.isIpAddress("[::ffff:192.168.173.22]:80")) + assertTrue(Utils.isIpAddress("1::")) + assertTrue(Utils.isIpAddress("::")) + assertTrue(Utils.isIpAddress("::/0")) + assertTrue(Utils.isIpAddress("10.24.56.0/24")) + assertTrue(Utils.isIpAddress("2001:4321::1")) + assertTrue(Utils.isIpAddress("240e:1234:abcd:12::6666")) + assertTrue(Utils.isIpAddress("240e:1234:abcd:12::/64")) + } } From b8939763d4f67d5c28950d18da3ced9da88bbb6d Mon Sep 17 00:00:00 2001 From: 2dust <31833384+2dust@users.noreply.github.com> Date: Sat, 17 Aug 2024 19:46:20 +0800 Subject: [PATCH 20/20] up 1.8.38 --- V2rayNG/app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/V2rayNG/app/build.gradle.kts b/V2rayNG/app/build.gradle.kts index ffb2c46cc..852561e5a 100644 --- a/V2rayNG/app/build.gradle.kts +++ b/V2rayNG/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "com.v2ray.ang" minSdk = 21 targetSdk = 34 - versionCode = 582 - versionName = "1.8.37" + versionCode = 583 + versionName = "1.8.38" multiDexEnabled = true splits { abi {