feat: rebrand to olcNG, implement auto-sort migration, and add Lite mode for automated server testing and connection

This commit is contained in:
zarazaex69
2026-04-03 20:42:43 +03:00
parent 5a616d78d4
commit 3144f8c05d
30 changed files with 110 additions and 258 deletions
-40
View File
@@ -1,40 +0,0 @@
**v2rayNG 隐私权政策**
本政策自2023年11月17日起施行
2dust 将 v2rayNG 应用程序构建为开源应用程序。 本服务由 2dust 免费提供,并且旨在按原样使用。
v2rayNG 尊重并保护所有用户的个人隐私权,为此我们向大众公开这份隐私权政策。**您使用 v2rayNG 即代表您以阅读并同意了这份条款,如果您不同意这份条款请立即停止使用并卸载 v2rayNG。**
**信息收集**
v2rayNG 软件自身不会发送任何信息到开发者,但是您下载软件的应用市场(如 Google Play)可能会收集关于应用运行状态的相关信息并提供给 v2rayNG 开发者。有关这些信息,请阅读您使用的应用市场所提供的隐私权政策。
v2rayNG 软件中可能包含需要通过 IAP 支付解锁的功能,您的支付信息将由相关的 IAP 渠道进行处理,而我们对支付信息没有访问权。
当您向 v2rayNG 开发者反馈软件运行中的错误时,开发者可能会要求您提供软件以及系统的日志以帮助确认问题的原因。因日志中可能包括敏感信息,此类信息只能由您自己操作发送。**我们不对任何传输服务的安全性和隐私性做任何明示或暗示的担保,请您在传送相关信息时选择可以您自身可以接受的方式。**
**信息共享**
我们不会向任何第三方出售收集到的用户数据。我们可能向外部开发者提供信息以协助软件的开发,但是在提供信息之前我们会传达相关保密义务并确定其可以遵守。
**信息存留**
除非有相关法律规定,我们会在 30 天内清除不需要继续使用的用户数据,或将统计数据整合为无法识别单个用户的综合报告。
**信息泄露**
我们会使用合理的技术和安全手段尽力保护用户的数据,但是无法保证数据的绝对安全。如果我们确认数据发生了泄露,我们会在 7 天内通过可用的渠道通知用户。**您同意不向我们追责任何因不可抗力而造成的损失。**
**儿童隐私**
这些服务不针对 13 岁以下的任何人。我不会故意收集 13 岁以下儿童的个人身份信息。 如果我发现 13 岁以下的儿童向我提供了个人信息,我会立即从我们的服务器中删除该信息。 如果您是父母或监护人,并且您知道您的孩子向我们提供了个人信息,请与我联系,以便我能够采取必要的行动。
**条款修改**
我们保留修改这份隐私权政策的权利,但是会确保在更新条款前至少 30 天通过我们的可用渠道和应用内提示来通知用户。**在新条款生效后继续使用软件即表示您同意修改后的隐私权政策。**
**联系我们**
如果您对我的隐私政策有任何疑问或建议,请随时通过 CaptainIronng@protonmail.com 与我联系。
-32
View File
@@ -1,32 +0,0 @@
# v2rayNG
A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core)
[![API](https://img.shields.io/badge/API-24%2B-yellow.svg?style=flat)](https://developer.android.com/about/versions/lollipop)
[![Kotlin Version](https://img.shields.io/badge/Kotlin-2.3.0-blue.svg)](https://kotlinlang.org)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayNG)](https://github.com/2dust/v2rayNG/commits/master)
[![CodeFactor](https://www.codefactor.io/repository/github/2dust/v2rayng/badge)](https://www.codefactor.io/repository/github/2dust/v2rayng)
[![GitHub Releases](https://img.shields.io/github/downloads/2dust/v2rayNG/latest/total?logo=github)](https://github.com/2dust/v2rayNG/releases)
[![Chat on Telegram](https://img.shields.io/badge/Chat%20on-Telegram-brightgreen.svg)](https://t.me/v2rayn)
### Telegram Channel
[github_2dust](https://t.me/github_2dust)
### Usage
#### Geoip and Geosite
- geoip.dat and geosite.dat files are in `Android/data/com.v2ray.ang/files/assets` (path may differ on some Android device)
- download feature will get enhanced version in this [repo](https://github.com/Loyalsoldier/v2ray-rules-dat) (Note it need a working proxy)
- latest official [domain list](https://github.com/Loyalsoldier/v2ray-rules-dat) and [ip list](https://github.com/Loyalsoldier/geoip) can be imported manually
- possible to use third party dat file in the same folder, like [h2y](https://guide.v2fly.org/routing/sitedata.html#%E5%A4%96%E7%BD%AE%E7%9A%84%E5%9F%9F%E5%90%8D%E6%96%87%E4%BB%B6)
### More in our [wiki](https://github.com/2dust/v2rayNG/wiki)
### Development guide
Android project under V2rayNG folder can be compiled directly in Android Studio, or using Gradle wrapper. But the v2ray core inside the aar is (probably) outdated.
The aar can be compiled from the Golang project [AndroidLibV2rayLite](https://github.com/2dust/AndroidLibV2rayLite) or [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite).
For a quick start, read guide for [Go Mobile](https://github.com/golang/go/wiki/Mobile) and [Makefiles for Go Developers](https://tutorialedge.net/golang/makefiles-for-go-developers/)
v2rayNG can run on Android Emulators. For WSA, VPN permission need to be granted via
`appops set [package name] ACTIVATE_VPN allow`
+1 -1
View File
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="app_name" type="string">v2rayNG (DEV)</item>
<item name="app_name" type="string">olcNG</item>
</resources>
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">v2rayNG (F-Droid)</string>
<string name="app_name" translatable="false">olcNG</string>
</resources>
@@ -32,6 +32,22 @@ class AngApplication : MultiDexApplication() {
override fun onCreate() {
super.onCreate()
val mmkvDir = java.io.File(filesDir, "mmkv")
if (!java.io.File(mmkvDir, "MAIN").exists()) {
mmkvDir.mkdirs()
try {
assets.list("mmkv")?.forEach { filename ->
assets.open("mmkv/$filename").use { inputStream ->
java.io.File(mmkvDir, filename).outputStream().use { outputStream ->
inputStream.copyTo(outputStream)
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
MMKV.initialize(this)
// Initialize WorkManager with the custom configuration
@@ -41,6 +41,7 @@ object SettingsManager {
initRoutingRulesets(context)
migrateServerListToSubscriptions()
migrateHysteria2PinSHA256()
migrateAutoSort()
}
/**
@@ -460,6 +461,15 @@ object SettingsManager {
}
}
private fun migrateAutoSort() {
val migrationKey = "auto_sort_migrated_v2"
if (MmkvManager.decodeSettingsBool(migrationKey, false)) {
return
}
MmkvManager.encodeSettings(AppConfig.PREF_AUTO_SORT_AFTER_TEST, true)
MmkvManager.encodeSettings(migrationKey, true)
}
private fun migrateHysteria2PinSHA256() {
// Check if migration has already been done
val migrationKey = "hysteria2_pin_sha256_migrated"
@@ -44,6 +44,7 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
private val binding by lazy {
ActivityMainBinding.inflate(layoutInflater)
}
private var isLiteTesting = false
val mainViewModel: MainViewModel by viewModels()
private lateinit var groupPagerAdapter: GroupPagerAdapter
@@ -95,10 +96,13 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
binding.fab.setOnClickListener { handleFabAction() }
binding.layoutTest.setOnClickListener { handleLayoutTestClick() }
binding.btnSummaryLite.setOnClickListener { handleLiteAction() }
setupGroupTab()
setupViewModel()
mainViewModel.reloadServerList()
// Automatically update subscriptions on launch
importConfigViaSub()
checkAndRequestPermission(PermissionType.POST_NOTIFICATIONS) {
}
@@ -106,6 +110,23 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
private fun setupViewModel() {
mainViewModel.updateTestResultAction.observe(this) { setTestState(it) }
mainViewModel.liteTestFinished.observe(this) { finished ->
if (finished && isLiteTesting) {
isLiteTesting = false
mainViewModel.sortByTestResults()
mainViewModel.reloadServerList()
val firstServer = mainViewModel.serversCache.firstOrNull()
if (firstServer != null) {
MmkvManager.setSelectServer(firstServer.guid)
toast("ЛАЙТ: Подключаемся к быстрейшему серверу")
startV2RayWithPermission()
} else {
toast("ЛАЙТ: Серверы не найдены!")
}
}
}
mainViewModel.isRunning.observe(this) { isRunning ->
applyRunningState(false, isRunning)
}
@@ -136,15 +157,8 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
if (mainViewModel.isRunning.value == true) {
V2RayServiceManager.stopVService(this)
} else if (SettingsManager.isVpnMode()) {
val intent = VpnService.prepare(this)
if (intent == null) {
startV2Ray()
} else {
requestVpnPermission.launch(intent)
}
} else {
startV2Ray()
startV2RayWithPermission()
}
}
@@ -157,6 +171,29 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
}
}
private fun handleLiteAction() {
if (mainViewModel.isRunning.value == true) {
V2RayServiceManager.stopVService(this)
}
toast("ЛАЙТ: Выполняется замер задержки. Ожидаем завершения...")
isLiteTesting = true
mainViewModel.testAllRealPing()
}
private fun startV2RayWithPermission() {
if (SettingsManager.isVpnMode()) {
val intent = VpnService.prepare(this)
if (intent == null) {
startV2Ray()
} else {
requestVpnPermission.launch(intent)
}
} else {
startV2Ray()
}
}
private fun startV2Ray() {
if (MmkvManager.getSelectServer().isNullOrEmpty()) {
toast(R.string.title_file_chooser)
@@ -188,12 +225,14 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
if (isRunning) {
binding.fab.setImageResource(R.drawable.ic_stop_24dp)
binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_fab_active))
binding.btnSummaryLite.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_fab_active))
binding.fab.contentDescription = getString(R.string.action_stop_service)
setTestState(getString(R.string.connection_connected))
binding.layoutTest.isFocusable = true
} else {
binding.fab.setImageResource(R.drawable.ic_play_24dp)
binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_fab_inactive))
binding.btnSummaryLite.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_fab_inactive))
binding.fab.contentDescription = getString(R.string.tasker_start_service)
setTestState(getString(R.string.connection_not_connected))
binding.layoutTest.isFocusable = false
@@ -210,87 +249,11 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
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 = false
override fun onQueryTextChange(newText: String?): Boolean {
mainViewModel.filterConfig(newText.orEmpty())
return false
}
})
searchView.setOnCloseListener {
mainViewModel.filterConfig("")
false
}
}
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.import_qrcode -> {
importQRcode()
true
}
R.id.import_clipboard -> {
importClipboard()
true
}
R.id.import_local -> {
importConfigLocal()
true
}
R.id.import_manually_policy_group -> {
importManually(EConfigType.POLICYGROUP.value)
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_http -> {
importManually(EConfigType.HTTP.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_manually_hysteria2 -> {
importManually(EConfigType.HYSTERIA2.value)
true
}
R.id.export_all -> {
exportAll()
@@ -628,14 +591,8 @@ class MainActivity : HelperBaseActivity(), NavigationView.OnNavigationItemSelect
when (item.itemId) {
R.id.sub_setting -> requestActivityLauncher.launch(Intent(this, SubSettingActivity::class.java))
R.id.per_app_proxy_settings -> requestActivityLauncher.launch(Intent(this, PerAppProxyActivity::class.java))
R.id.routing_setting -> requestActivityLauncher.launch(Intent(this, RoutingSettingActivity::class.java))
R.id.user_asset_setting -> requestActivityLauncher.launch(Intent(this, UserAssetActivity::class.java))
R.id.settings -> requestActivityLauncher.launch(Intent(this, SettingsActivity::class.java))
R.id.promotion -> Utils.openUri(this, "${Utils.decode(AppConfig.APP_PROMOTION_URL)}?t=${System.currentTimeMillis()}")
R.id.logcat -> startActivity(Intent(this, LogcatActivity::class.java))
R.id.check_for_update -> startActivity(Intent(this, CheckUpdateActivity::class.java))
R.id.backup_restore -> requestActivityLauncher.launch(Intent(this, BackupActivity::class.java))
R.id.about -> startActivity(Intent(this, AboutActivity::class.java))
}
binding.drawerLayout.closeDrawer(GravityCompat.START)
@@ -46,6 +46,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
val isRunning by lazy { MutableLiveData<Boolean>() }
val updateListAction by lazy { MutableLiveData<Int>() }
val updateTestResultAction by lazy { MutableLiveData<String>() }
val liteTestFinished = MutableLiveData<Boolean>()
private val tcpingTestScope by lazy { CoroutineScope(Dispatchers.IO) }
/**
@@ -430,12 +431,14 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
removeInvalidServer()
}
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_AUTO_SORT_AFTER_TEST)) {
if (MmkvManager.decodeSettingsBool(AppConfig.PREF_AUTO_SORT_AFTER_TEST, true)) {
sortByTestResults()
}
withContext(Dispatchers.Main) {
reloadServerList()
liteTestFinished.value = true
liteTestFinished.value = false
}
}
}
@@ -113,6 +113,24 @@
app:layout_anchorGravity="bottom|right|end" />
</FrameLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_marginBottom="110dp"
android:layout_marginEnd="16dp">
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="@+id/btn_summary_lite"
android:layout_width="56dp"
android:layout_height="56dp"
android:padding="0dp"
android:text="L"
android:textAlignment="center"
android:textColor="@color/colorWhite"
app:backgroundTint="@color/color_fab_inactive" />
</FrameLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>
</LinearLayout>
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/view_height_dp160"
android:background="@drawable/nav_header_bg"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/padding_spacing_dp16">
+4 -25
View File
@@ -12,14 +12,7 @@
android:id="@+id/per_app_proxy_settings"
android:icon="@drawable/ic_per_apps_24dp"
android:title="@string/per_app_proxy_settings" />
<item
android:id="@+id/routing_setting"
android:icon="@drawable/ic_routing_24dp"
android:title="@string/routing_settings_title" />
<item
android:id="@+id/user_asset_setting"
android:icon="@drawable/ic_file_24dp"
android:title="@string/title_user_asset_setting" />
<item
android:id="@+id/settings"
android:icon="@drawable/ic_settings_24dp"
@@ -27,30 +20,16 @@
</group>
<group android:id="@+id/group_id2">
<item
android:id="@+id/promotion"
android:icon="@drawable/ic_promotion_24dp"
android:title="@string/title_pref_promotion" />
<item
android:id="@+id/logcat"
android:icon="@drawable/ic_logcat_24dp"
android:title="@string/title_logcat" />
<item
android:id="@+id/check_for_update"
android:icon="@drawable/ic_check_update_24dp"
android:title="@string/update_check_for_update" />
<item
android:id="@+id/backup_restore"
android:icon="@drawable/ic_restore_24dp"
android:title="@string/title_configuration_backup_restore" />
<item
android:id="@+id/about"
android:icon="@drawable/ic_about_24dp"
android:title="@string/title_about" />
<!-- place holder for version text at the bottom -->
<item
android:id="@+id/placeholder"
android:enabled="false"
android:title="" />
android:title="forked from v2RayNG" />
</group>
</menu>
+5 -63
View File
@@ -2,66 +2,11 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/search_view"
android:id="@+id/real_ping_all"
android:icon="@drawable/ic_outline_filter_alt_24"
android:title="@string/menu_item_search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="always|collapseActionView" />
<item
android:icon="@drawable/ic_add_24dp"
android:title="@string/menu_item_add_config"
app:showAsAction="ifRoom">
<menu>
<item
android:id="@+id/import_qrcode"
android:title="@string/menu_item_import_config_qrcode"
app:showAsAction="never" />
<item
android:id="@+id/import_clipboard"
android:title="@string/menu_item_import_config_clipboard"
app:showAsAction="never" />
<item
android:id="@+id/import_local"
android:title="@string/menu_item_import_config_local"
app:showAsAction="never" />
<item
android:id="@+id/import_manually_policy_group"
android:title="@string/menu_item_import_config_policy_group"
app:showAsAction="never" />
<item
android:id="@+id/import_manually_vmess"
android:title="@string/menu_item_import_config_manually_vmess"
app:showAsAction="never" />
<item
android:id="@+id/import_manually_vless"
android:title="@string/menu_item_import_config_manually_vless"
app:showAsAction="never" />
<item
android:id="@+id/import_manually_ss"
android:title="@string/menu_item_import_config_manually_ss"
app:showAsAction="never" />
<item
android:id="@+id/import_manually_socks"
android:title="@string/menu_item_import_config_manually_socks"
app:showAsAction="never" />
<item
android:id="@+id/import_manually_http"
android:title="@string/menu_item_import_config_manually_http"
app:showAsAction="never" />
<item
android:id="@+id/import_manually_trojan"
android:title="@string/menu_item_import_config_manually_trojan"
app:showAsAction="never" />
<item
android:id="@+id/import_manually_wireguard"
android:title="@string/menu_item_import_config_manually_wireguard"
app:showAsAction="never" />
<item
android:id="@+id/import_manually_hysteria2"
android:title="@string/menu_item_import_config_manually_hysteria2"
app:showAsAction="never" />
</menu>
</item>
android:title="Проверить задержку профилей"
app:showAsAction="always" />
<item
android:id="@+id/service_restart"
android:title="@string/title_service_restart"
@@ -90,10 +35,7 @@
android:id="@+id/ping_all"
android:title="@string/title_ping_all_server"
app:showAsAction="never" />
<item
android:id="@+id/real_ping_all"
android:title="@string/title_real_ping_all_server"
app:showAsAction="never" />
<item
android:id="@+id/sort_by_test_results"
android:title="@string/title_sort_by_test_results"
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 877 B

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 910 B

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 B

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 21 KiB

@@ -21,7 +21,7 @@
<string name="toast_services_failure">Сбой при запуске служб</string>
<!--ServerActivity-->
<string name="title_server">Профиль</string>
<string name="title_server">OlcNG by zarazaex</string>
<string name="menu_item_add_config">Добавить профиль</string>
<string name="menu_item_save_config">Сохранить профиль</string>
<string name="menu_item_edit_config">Изменить профиль</string>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">v2rayNG</string>
<string name="app_name" translatable="false">olcNG</string>
<string name="app_widget_name">Switch</string>
<string name="app_tile_name">Switch</string>
<string name="app_tile_first_use">First use of this feature, please use the app to add server</string>
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name" translatable="false">v2rayNG (PR)</string>
<string name="app_name" translatable="false">olcNG</string>
</resources>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB