Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bde37e38a7 | ||
|
|
c401d63d2f | ||
|
|
52416dd43d | ||
|
|
59bd7128ae | ||
|
|
87f16467bb | ||
|
|
bed0fd00bd | ||
|
|
0672af98f8 | ||
|
|
2341eceb65 | ||
|
|
ec5f7245bf | ||
|
|
a46b0d58eb | ||
|
|
d1262d169b | ||
|
|
4a85c95b02 | ||
|
|
a368927b9f | ||
|
|
80feb69af5 | ||
|
|
c09f0d5787 | ||
|
|
ff667546b8 | ||
|
|
6a255cdfa4 | ||
|
|
2b5784df6f | ||
|
|
a9665dd2d8 | ||
|
|
1e52877e93 | ||
|
|
a7a70b448f | ||
|
|
3e52aeb804 | ||
|
|
4b4f5d145b | ||
|
|
34d8329c8a | ||
|
|
2e7ae732aa | ||
|
|
424287e258 | ||
|
|
40d03bbb96 | ||
|
|
d768694445 | ||
|
|
56c7f2ef69 | ||
|
|
3839b0c59c | ||
|
|
77cab14ae8 | ||
|
|
b0dbd4c7ca | ||
|
|
50ca2e0e69 | ||
|
|
0bf8beda94 | ||
|
|
4d334929c9 | ||
|
|
ccfdf096f9 | ||
|
|
b4f2af2778 | ||
|
|
32b9e4855c | ||
|
|
bdbce5147e | ||
|
|
a4833506ae | ||
|
|
d4e8072248 | ||
|
|
207ca93735 | ||
|
|
99307ab8f0 | ||
|
|
1ec23a7b39 | ||
|
|
685f9e220c | ||
|
|
e77b7eb52e | ||
|
|
29014704e0 | ||
|
|
b3570d9c0b | ||
|
|
6e427cee82 | ||
|
|
0a1b6d00d9 | ||
|
|
00ffe66f36 | ||
|
|
63661fbdaa | ||
|
|
4d4a4543c5 | ||
|
|
2349805968 |
@@ -8,8 +8,8 @@ android {
|
|||||||
buildToolsVersion "$buildToolsVer"
|
buildToolsVersion "$buildToolsVer"
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
targetCompatibility = "8"
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
sourceCompatibility = "8"
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
@@ -17,8 +17,8 @@ android {
|
|||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion Integer.parseInt("$targetSdkVer")
|
targetSdkVersion Integer.parseInt("$targetSdkVer")
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
versionCode 516
|
versionCode 523
|
||||||
versionName "1.8.5"
|
versionName "1.8.9"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -36,6 +36,21 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// flavorDimensions "versions"
|
||||||
|
//
|
||||||
|
// productFlavors {
|
||||||
|
// dev {
|
||||||
|
// applicationIdSuffix = ".dev"
|
||||||
|
// versionNameSuffix = "-dev"
|
||||||
|
// }
|
||||||
|
// pre_release {
|
||||||
|
// applicationIdSuffix = ".pre"
|
||||||
|
// versionNameSuffix = "-pre-release"
|
||||||
|
// }
|
||||||
|
// prod {
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
jniLibs.srcDirs = ['libs']
|
jniLibs.srcDirs = ['libs']
|
||||||
@@ -72,6 +87,7 @@ android {
|
|||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
|
buildConfig true
|
||||||
}
|
}
|
||||||
namespace 'com.v2ray.ang'
|
namespace 'com.v2ray.ang'
|
||||||
testNamespace 'com.v2ray.angTest'
|
testNamespace 'com.v2ray.angTest'
|
||||||
@@ -85,16 +101,16 @@ dependencies {
|
|||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
implementation 'com.google.android.material:material:1.8.0'
|
implementation 'com.google.android.material:material:1.9.0'
|
||||||
implementation 'androidx.cardview:cardview:1.0.0'
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.5.6'
|
implementation 'androidx.fragment:fragment-ktx:1.5.7'
|
||||||
implementation 'androidx.multidex:multidex:2.0.1'
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta01'
|
||||||
|
|
||||||
// Androidx ktx
|
// Androidx ktx
|
||||||
implementation 'androidx.activity:activity-ktx:1.7.0'
|
implementation 'androidx.activity:activity-ktx:1.7.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'
|
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.1'
|
||||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1'
|
||||||
@@ -111,9 +127,9 @@ dependencies {
|
|||||||
implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'
|
implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'
|
||||||
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
|
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
|
||||||
implementation 'me.drakeet.support:toastcompat:1.1.0'
|
implementation 'me.drakeet.support:toastcompat:1.1.0'
|
||||||
implementation 'com.blacksquircle.ui:editorkit:2.1.1'
|
implementation 'com.blacksquircle.ui:editorkit:2.8.0'
|
||||||
implementation 'com.blacksquircle.ui:language-base:2.1.1'
|
implementation 'com.blacksquircle.ui:language-base:2.8.0'
|
||||||
implementation 'com.blacksquircle.ui:language-json:2.1.1'
|
implementation 'com.blacksquircle.ui:language-json:2.8.0'
|
||||||
implementation 'io.github.g00fy2.quickie:quickie-bundled:1.6.0'
|
implementation 'io.github.g00fy2.quickie:quickie-bundled:1.6.0'
|
||||||
implementation 'com.google.zxing:core:3.5.1'
|
implementation 'com.google.zxing:core:3.5.1'
|
||||||
}
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package com.v2ray.ang;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import android.test.ApplicationTestCase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
|
|
||||||
*/
|
|
||||||
public class ApplicationTest extends ApplicationTestCase<Application> {
|
|
||||||
public ApplicationTest() {
|
|
||||||
super(Application.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
4
V2rayNG/app/src/dev/res/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<item name="app_name" type="string">v2rayNG (DEV)</item>
|
||||||
|
</resources>
|
||||||
@@ -9,8 +9,12 @@
|
|||||||
android:largeScreens="true"
|
android:largeScreens="true"
|
||||||
android:xlargeScreens="true"/>
|
android:xlargeScreens="true"/>
|
||||||
|
|
||||||
|
<uses-sdk android:minSdkVersion="21" tools:overrideLibrary="com.blacksquircle.ui.editorkit"/>
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
<uses-feature android:name="android.hardware.camera" android:required="false"/>
|
||||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
|
||||||
|
<uses-feature android:name="android.software.leanback" android:required="false" />
|
||||||
|
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />
|
||||||
|
|
||||||
<!-- https://developer.android.com/about/versions/11/privacy/package-visibility -->
|
<!-- https://developer.android.com/about/versions/11/privacy/package-visibility -->
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||||
@@ -31,6 +35,7 @@
|
|||||||
android:name=".AngApplication"
|
android:name=".AngApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:banner="@mipmap/ic_banner"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppThemeDayNight"
|
android:theme="@style/AppThemeDayNight"
|
||||||
@@ -45,6 +50,7 @@
|
|||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
<category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
|
||||||
@@ -109,8 +115,9 @@
|
|||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
<data android:scheme="v2rayng"
|
<data android:scheme="v2rayng"/>
|
||||||
android:host="install-config" />
|
<data android:host="install-config"/>
|
||||||
|
<data android:host="install-sub"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ package com.v2ray.ang
|
|||||||
* App Config Const
|
* App Config Const
|
||||||
*/
|
*/
|
||||||
object AppConfig {
|
object AppConfig {
|
||||||
const val ANG_PACKAGE = "com.v2ray.ang"
|
const val ANG_PACKAGE = BuildConfig.APPLICATION_ID
|
||||||
const val DIR_ASSETS = "assets"
|
const val DIR_ASSETS = "assets"
|
||||||
|
|
||||||
// legacy
|
// legacy
|
||||||
|
|||||||
@@ -38,12 +38,18 @@ class WidgetProvider : AppWidgetProvider() {
|
|||||||
})
|
})
|
||||||
remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent)
|
remoteViews.setOnClickPendingIntent(R.id.layout_switch, pendingIntent)
|
||||||
if (isRunning) {
|
if (isRunning) {
|
||||||
|
if (!Utils.getDarkModeStatus(context)) {
|
||||||
|
remoteViews.setInt(R.id.image_switch, "setImageResource", R.drawable.ic_stat_name)
|
||||||
|
}
|
||||||
remoteViews.setInt(
|
remoteViews.setInt(
|
||||||
R.id.layout_switch,
|
R.id.layout_switch,
|
||||||
"setBackgroundResource",
|
"setBackgroundResource",
|
||||||
R.drawable.ic_rounded_corner_theme
|
R.drawable.ic_rounded_corner_active
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
if (!Utils.getDarkModeStatus(context)) {
|
||||||
|
remoteViews.setInt(R.id.image_switch, "setImageResource", R.drawable.ic_stat_name_black)
|
||||||
|
}
|
||||||
remoteViews.setInt(
|
remoteViews.setInt(
|
||||||
R.id.layout_switch,
|
R.id.layout_switch,
|
||||||
"setBackgroundResource",
|
"setBackgroundResource",
|
||||||
|
|||||||
@@ -2,13 +2,25 @@ package com.v2ray.ang.ui
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.view.WindowCompat
|
||||||
import com.v2ray.ang.util.MyContextWrapper
|
import com.v2ray.ang.util.MyContextWrapper
|
||||||
import com.v2ray.ang.util.Utils
|
import com.v2ray.ang.util.Utils
|
||||||
|
|
||||||
abstract class BaseActivity : AppCompatActivity() {
|
abstract class BaseActivity : AppCompatActivity() {
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
if (!Utils.getDarkModeStatus(this)) {
|
||||||
|
WindowCompat.getInsetsController(window, window.decorView).apply {
|
||||||
|
isAppearanceLightStatusBars = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
|
||||||
android.R.id.home -> {
|
android.R.id.home -> {
|
||||||
onBackPressed()
|
onBackPressed()
|
||||||
|
|||||||
@@ -25,13 +25,12 @@ class LogcatActivity : BaseActivity() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityLogcatBinding.inflate(layoutInflater)
|
binding = ActivityLogcatBinding.inflate(layoutInflater)
|
||||||
val view = binding.root
|
val view = binding.root
|
||||||
setContentView(view)
|
setContentView(view)
|
||||||
|
|
||||||
title = getString(R.string.title_logcat)
|
title = getString(R.string.title_logcat)
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
logcat(false)
|
logcat(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,11 +131,17 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
mainViewModel.isRunning.observe(this) { isRunning ->
|
mainViewModel.isRunning.observe(this) { isRunning ->
|
||||||
adapter.isRunning = isRunning
|
adapter.isRunning = isRunning
|
||||||
if (isRunning) {
|
if (isRunning) {
|
||||||
binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.colorSelected))
|
if (!Utils.getDarkModeStatus(this)) {
|
||||||
|
binding.fab.setImageResource(R.drawable.ic_stat_name)
|
||||||
|
}
|
||||||
|
binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_fab_orange))
|
||||||
setTestState(getString(R.string.connection_connected))
|
setTestState(getString(R.string.connection_connected))
|
||||||
binding.layoutTest.isFocusable = true
|
binding.layoutTest.isFocusable = true
|
||||||
} else {
|
} else {
|
||||||
binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.colorUnselected))
|
if (!Utils.getDarkModeStatus(this)) {
|
||||||
|
binding.fab.setImageResource(R.drawable.ic_stat_name)
|
||||||
|
}
|
||||||
|
binding.fab.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(this, R.color.color_fab_grey))
|
||||||
setTestState(getString(R.string.connection_not_connected))
|
setTestState(getString(R.string.connection_not_connected))
|
||||||
binding.layoutTest.isFocusable = false
|
binding.layoutTest.isFocusable = false
|
||||||
}
|
}
|
||||||
@@ -613,13 +619,14 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_BUTTON_B) {
|
||||||
moveTaskToBack(false)
|
moveTaskToBack(false)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return super.onKeyDown(keyCode, event)
|
return super.onKeyDown(keyCode, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun showCircle() {
|
fun showCircle() {
|
||||||
binding.fabProgressCircle.show()
|
binding.fabProgressCircle.show()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,8 +41,6 @@ class PerAppProxyActivity : BaseActivity() {
|
|||||||
val view = binding.root
|
val view = binding.root
|
||||||
setContentView(view)
|
setContentView(view)
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
val dividerItemDecoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
|
val dividerItemDecoration = DividerItemDecoration(this, LinearLayoutManager.VERTICAL)
|
||||||
binding.recyclerView.addItemDecoration(dividerItemDecoration)
|
binding.recyclerView.addItemDecoration(dividerItemDecoration)
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class RoutingSettingsActivity : BaseActivity() {
|
|||||||
setContentView(view)
|
setContentView(view)
|
||||||
|
|
||||||
title = getString(R.string.title_pref_routing_custom)
|
title = getString(R.string.title_pref_routing_custom)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
val fragments = ArrayList<Fragment>()
|
val fragments = ArrayList<Fragment>()
|
||||||
fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_AGENT))
|
fragments.add(RoutingSettingsFragment().newInstance(AppConfig.PREF_V2RAY_ROUTING_AGENT))
|
||||||
|
|||||||
@@ -31,16 +31,6 @@ class ScannerActivity : BaseActivity(){
|
|||||||
if (settingsStorage?.decodeBool(AppConfig.PREF_START_SCAN_IMMEDIATE) == true) {
|
if (settingsStorage?.decodeBool(AppConfig.PREF_START_SCAN_IMMEDIATE) == true) {
|
||||||
launchScan()
|
launchScan()
|
||||||
}
|
}
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
}
|
|
||||||
|
|
||||||
public override fun onPause() {
|
|
||||||
super.onPause()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchScan(){
|
private fun launchScan(){
|
||||||
|
|||||||
@@ -175,7 +175,6 @@ class ServerActivity : BaseActivity() {
|
|||||||
} else {
|
} else {
|
||||||
clearServer()
|
clearServer()
|
||||||
}
|
}
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.view.Menu
|
|||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import com.blacksquircle.ui.editorkit.utils.EditorTheme
|
||||||
import com.blacksquircle.ui.language.json.JsonLanguage
|
import com.blacksquircle.ui.language.json.JsonLanguage
|
||||||
import com.google.gson.*
|
import com.google.gson.*
|
||||||
import com.tencent.mmkv.MMKV
|
import com.tencent.mmkv.MMKV
|
||||||
@@ -38,6 +39,9 @@ class ServerCustomConfigActivity : BaseActivity() {
|
|||||||
setContentView(view)
|
setContentView(view)
|
||||||
title = getString(R.string.title_server)
|
title = getString(R.string.title_server)
|
||||||
|
|
||||||
|
if (!Utils.getDarkModeStatus(this)) {
|
||||||
|
binding.editor.colorScheme = EditorTheme.INTELLIJ_LIGHT
|
||||||
|
}
|
||||||
binding.editor.language = JsonLanguage()
|
binding.editor.language = JsonLanguage()
|
||||||
val config = MmkvManager.decodeServerConfig(editGuid)
|
val config = MmkvManager.decodeServerConfig(editGuid)
|
||||||
if (config != null) {
|
if (config != null) {
|
||||||
@@ -45,7 +49,6 @@ class ServerCustomConfigActivity : BaseActivity() {
|
|||||||
} else {
|
} else {
|
||||||
clearServer()
|
clearServer()
|
||||||
}
|
}
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ class SettingsActivity : BaseActivity() {
|
|||||||
|
|
||||||
title = getString(R.string.title_settings)
|
title = getString(R.string.title_settings)
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
settingsViewModel.startListenPreferenceChange()
|
settingsViewModel.startListenPreferenceChange()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ class SubEditActivity : BaseActivity() {
|
|||||||
} else {
|
} else {
|
||||||
clearServer()
|
clearServer()
|
||||||
}
|
}
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ class SubSettingActivity : BaseActivity() {
|
|||||||
binding.recyclerView.setHasFixedSize(true)
|
binding.recyclerView.setHasFixedSize(true)
|
||||||
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
||||||
binding.recyclerView.adapter = adapter
|
binding.recyclerView.adapter = adapter
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
|||||||
@@ -3,10 +3,12 @@ package com.v2ray.ang.ui
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import com.v2ray.ang.AppConfig
|
||||||
import com.v2ray.ang.R
|
import com.v2ray.ang.R
|
||||||
import com.v2ray.ang.databinding.ActivityLogcatBinding
|
import com.v2ray.ang.databinding.ActivityLogcatBinding
|
||||||
import com.v2ray.ang.extension.toast
|
import com.v2ray.ang.extension.toast
|
||||||
import com.v2ray.ang.util.AngConfigManager
|
import com.v2ray.ang.util.AngConfigManager
|
||||||
|
import java.net.URLDecoder
|
||||||
|
|
||||||
class UrlSchemeActivity : BaseActivity() {
|
class UrlSchemeActivity : BaseActivity() {
|
||||||
private lateinit var binding: ActivityLogcatBinding
|
private lateinit var binding: ActivityLogcatBinding
|
||||||
@@ -17,34 +19,68 @@ class UrlSchemeActivity : BaseActivity() {
|
|||||||
val view = binding.root
|
val view = binding.root
|
||||||
setContentView(view)
|
setContentView(view)
|
||||||
|
|
||||||
var shareUrl: String = ""
|
|
||||||
try {
|
try {
|
||||||
intent?.apply {
|
intent.apply {
|
||||||
when (action) {
|
if (action == Intent.ACTION_SEND) {
|
||||||
Intent.ACTION_SEND -> {
|
if ("text/plain" == type) {
|
||||||
if ("text/plain" == type) {
|
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
|
||||||
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
|
val uri = Uri.parse(it)
|
||||||
shareUrl = it
|
if (uri.scheme?.startsWith(AppConfig.HTTPS_PROTOCOL) == true || uri.scheme?.startsWith(
|
||||||
|
AppConfig.HTTP_PROTOCOL
|
||||||
|
) == true
|
||||||
|
) {
|
||||||
|
val name = uri.getQueryParameter("name") ?: "Subscription"
|
||||||
|
importSubscription(it, name)
|
||||||
|
} else {
|
||||||
|
importConfig(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Intent.ACTION_VIEW -> {
|
} else if (action == Intent.ACTION_VIEW) {
|
||||||
val uri: Uri? = intent.data
|
when (data?.host) {
|
||||||
shareUrl = uri?.getQueryParameter("url")!!
|
"install-config" -> {
|
||||||
|
val uri: Uri? = intent.data
|
||||||
|
val shareUrl: String = uri?.getQueryParameter("url")!!
|
||||||
|
toast(shareUrl)
|
||||||
|
importConfig(shareUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
"install-sub" -> {
|
||||||
|
val uri: Uri? = intent.data
|
||||||
|
val url = uri?.getQueryParameter("url")!!
|
||||||
|
val name = uri.getQueryParameter("name") ?: "Subscription"
|
||||||
|
importSubscription(url, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
toast(R.string.toast_failure)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
toast(shareUrl)
|
|
||||||
val count = AngConfigManager.importBatchConfig(shareUrl, "", false)
|
|
||||||
if (count > 0) {
|
|
||||||
toast(R.string.toast_success)
|
|
||||||
} else {
|
|
||||||
toast(R.string.toast_failure)
|
|
||||||
}
|
|
||||||
startActivity(Intent(this, MainActivity::class.java))
|
startActivity(Intent(this, MainActivity::class.java))
|
||||||
finish()
|
finish()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun importSubscription(url: String, name: String) {
|
||||||
|
val decodedUrl = URLDecoder.decode(url, "UTF-8")
|
||||||
|
|
||||||
|
val check = AngConfigManager.importSubscription(name, decodedUrl)
|
||||||
|
if (check) toast(R.string.import_subscription_success) else toast(R.string.import_subscription_failure)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun importConfig(shareUrl: String) {
|
||||||
|
val count = AngConfigManager.importBatchConfig(shareUrl, "", false)
|
||||||
|
if (count > 0) {
|
||||||
|
toast(R.string.toast_success)
|
||||||
|
} else {
|
||||||
|
toast(R.string.toast_failure)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -49,7 +49,6 @@ class UserAssetActivity : BaseActivity() {
|
|||||||
val view = binding.root
|
val view = binding.root
|
||||||
setContentView(view)
|
setContentView(view)
|
||||||
title = getString(R.string.title_user_asset_setting)
|
title = getString(R.string.title_user_asset_setting)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
|
|
||||||
binding.recyclerView.setHasFixedSize(true)
|
binding.recyclerView.setHasFixedSize(true)
|
||||||
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
binding.recyclerView.layoutManager = LinearLayoutManager(this)
|
||||||
|
|||||||
@@ -20,11 +20,27 @@ import com.v2ray.ang.util.MmkvManager.KEY_SELECTED_SERVER
|
|||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import com.v2ray.ang.extension.idnHost
|
import com.v2ray.ang.extension.idnHost
|
||||||
|
import com.v2ray.ang.extension.toast
|
||||||
|
|
||||||
object AngConfigManager {
|
object AngConfigManager {
|
||||||
private val mainStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_MAIN, MMKV.MULTI_PROCESS_MODE) }
|
private val mainStorage by lazy {
|
||||||
private val serverRawStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SERVER_RAW, MMKV.MULTI_PROCESS_MODE) }
|
MMKV.mmkvWithID(
|
||||||
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
|
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 val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) }
|
private val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -82,8 +98,14 @@ object AngConfigManager {
|
|||||||
).forEach { key ->
|
).forEach { key ->
|
||||||
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
|
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
|
||||||
}
|
}
|
||||||
settingsStorage?.encode(AppConfig.PREF_SNIFFING_ENABLED, sharedPreferences.getBoolean(AppConfig.PREF_SNIFFING_ENABLED, true))
|
settingsStorage?.encode(
|
||||||
settingsStorage?.encode(AppConfig.PREF_PER_APP_PROXY_SET, sharedPreferences.getStringSet(AppConfig.PREF_PER_APP_PROXY_SET, setOf()))
|
AppConfig.PREF_SNIFFING_ENABLED,
|
||||||
|
sharedPreferences.getBoolean(AppConfig.PREF_SNIFFING_ENABLED, true)
|
||||||
|
)
|
||||||
|
settingsStorage?.encode(
|
||||||
|
AppConfig.PREF_PER_APP_PROXY_SET,
|
||||||
|
sharedPreferences.getStringSet(AppConfig.PREF_PER_APP_PROXY_SET, setOf())
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun migrateVmessBean(angConfig: AngConfig, sharedPreferences: SharedPreferences) {
|
private fun migrateVmessBean(angConfig: AngConfig, sharedPreferences: SharedPreferences) {
|
||||||
@@ -125,7 +147,8 @@ object AngConfigManager {
|
|||||||
if (TextUtils.isEmpty(vmessBean.security) && TextUtils.isEmpty(vmessBean.id)) {
|
if (TextUtils.isEmpty(vmessBean.security) && TextUtils.isEmpty(vmessBean.id)) {
|
||||||
server.users = null
|
server.users = null
|
||||||
} else {
|
} else {
|
||||||
val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
|
val socksUsersBean =
|
||||||
|
V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
|
||||||
socksUsersBean.user = vmessBean.security
|
socksUsersBean.user = vmessBean.security
|
||||||
socksUsersBean.pass = vmessBean.id
|
socksUsersBean.pass = vmessBean.id
|
||||||
server.users = listOf(socksUsersBean)
|
server.users = listOf(socksUsersBean)
|
||||||
@@ -135,17 +158,27 @@ object AngConfigManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
config.outboundBean?.streamSettings?.let { streamSetting ->
|
config.outboundBean?.streamSettings?.let { streamSetting ->
|
||||||
val sni = streamSetting.populateTransportSettings(vmessBean.network, vmessBean.headerType,
|
val sni = streamSetting.populateTransportSettings(
|
||||||
vmessBean.requestHost, vmessBean.path, vmessBean.path, vmessBean.requestHost, vmessBean.path,
|
vmessBean.network,
|
||||||
vmessBean.headerType, vmessBean.path)
|
vmessBean.headerType,
|
||||||
|
vmessBean.requestHost,
|
||||||
|
vmessBean.path,
|
||||||
|
vmessBean.path,
|
||||||
|
vmessBean.requestHost,
|
||||||
|
vmessBean.path,
|
||||||
|
vmessBean.headerType,
|
||||||
|
vmessBean.path
|
||||||
|
)
|
||||||
val allowInsecure = if (vmessBean.allowInsecure.isBlank()) {
|
val allowInsecure = if (vmessBean.allowInsecure.isBlank()) {
|
||||||
settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
|
settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false
|
||||||
} else {
|
} else {
|
||||||
vmessBean.allowInsecure.toBoolean()
|
vmessBean.allowInsecure.toBoolean()
|
||||||
}
|
}
|
||||||
var fingerprint = streamSetting.tlsSettings?.fingerprint
|
var fingerprint = streamSetting.tlsSettings?.fingerprint
|
||||||
streamSetting.populateTlsSettings(vmessBean.streamSecurity, allowInsecure,
|
streamSetting.populateTlsSettings(
|
||||||
vmessBean.sni.ifBlank { sni }, fingerprint, null, null, null, null)
|
vmessBean.streamSecurity, allowInsecure,
|
||||||
|
vmessBean.sni.ifBlank { sni }, fingerprint, null, null, null, null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val key = MmkvManager.encodeServerConfig(vmessBean.guid, config)
|
val key = MmkvManager.encodeServerConfig(vmessBean.guid, config)
|
||||||
@@ -168,14 +201,21 @@ object AngConfigManager {
|
|||||||
/**
|
/**
|
||||||
* import config form qrcode or...
|
* import config form qrcode or...
|
||||||
*/
|
*/
|
||||||
private fun importConfig(str: String?, subid: String, removedSelectedServer: ServerConfig?): Int {
|
private fun importConfig(
|
||||||
|
str: String?,
|
||||||
|
subid: String,
|
||||||
|
removedSelectedServer: ServerConfig?
|
||||||
|
): Int {
|
||||||
try {
|
try {
|
||||||
if (str == null || TextUtils.isEmpty(str)) {
|
if (str == null || TextUtils.isEmpty(str)) {
|
||||||
return R.string.toast_none_data
|
return R.string.toast_none_data
|
||||||
}
|
}
|
||||||
|
|
||||||
//maybe sub
|
//maybe sub
|
||||||
if (TextUtils.isEmpty(subid) && (str.startsWith(HTTP_PROTOCOL) || str.startsWith(HTTPS_PROTOCOL))) {
|
if (TextUtils.isEmpty(subid) && (str.startsWith(HTTP_PROTOCOL) || str.startsWith(
|
||||||
|
HTTPS_PROTOCOL
|
||||||
|
))
|
||||||
|
) {
|
||||||
MmkvManager.importUrlAsSubscription(str)
|
MmkvManager.importUrlAsSubscription(str)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -201,9 +241,9 @@ object AngConfigManager {
|
|||||||
val vmessQRCode = Gson().fromJson(result, VmessQRCode::class.java)
|
val vmessQRCode = Gson().fromJson(result, VmessQRCode::class.java)
|
||||||
// Although VmessQRCode fields are non null, looks like Gson may still create null fields
|
// Although VmessQRCode fields are non null, looks like Gson may still create null fields
|
||||||
if (TextUtils.isEmpty(vmessQRCode.add)
|
if (TextUtils.isEmpty(vmessQRCode.add)
|
||||||
|| TextUtils.isEmpty(vmessQRCode.port)
|
|| TextUtils.isEmpty(vmessQRCode.port)
|
||||||
|| TextUtils.isEmpty(vmessQRCode.id)
|
|| TextUtils.isEmpty(vmessQRCode.id)
|
||||||
|| TextUtils.isEmpty(vmessQRCode.net)
|
|| TextUtils.isEmpty(vmessQRCode.net)
|
||||||
) {
|
) {
|
||||||
return R.string.toast_incorrect_protocol
|
return R.string.toast_incorrect_protocol
|
||||||
}
|
}
|
||||||
@@ -213,16 +253,28 @@ object AngConfigManager {
|
|||||||
vnext.address = vmessQRCode.add
|
vnext.address = vmessQRCode.add
|
||||||
vnext.port = Utils.parseInt(vmessQRCode.port)
|
vnext.port = Utils.parseInt(vmessQRCode.port)
|
||||||
vnext.users[0].id = vmessQRCode.id
|
vnext.users[0].id = vmessQRCode.id
|
||||||
vnext.users[0].security = if (TextUtils.isEmpty(vmessQRCode.scy)) DEFAULT_SECURITY else vmessQRCode.scy
|
vnext.users[0].security =
|
||||||
|
if (TextUtils.isEmpty(vmessQRCode.scy)) DEFAULT_SECURITY else vmessQRCode.scy
|
||||||
vnext.users[0].alterId = Utils.parseInt(vmessQRCode.aid)
|
vnext.users[0].alterId = Utils.parseInt(vmessQRCode.aid)
|
||||||
}
|
}
|
||||||
val sni = streamSetting.populateTransportSettings(vmessQRCode.net, vmessQRCode.type, vmessQRCode.host,
|
val sni = streamSetting.populateTransportSettings(
|
||||||
vmessQRCode.path, vmessQRCode.path, vmessQRCode.host, vmessQRCode.path, vmessQRCode.type, vmessQRCode.path)
|
vmessQRCode.net,
|
||||||
|
vmessQRCode.type,
|
||||||
|
vmessQRCode.host,
|
||||||
|
vmessQRCode.path,
|
||||||
|
vmessQRCode.path,
|
||||||
|
vmessQRCode.host,
|
||||||
|
vmessQRCode.path,
|
||||||
|
vmessQRCode.type,
|
||||||
|
vmessQRCode.path
|
||||||
|
)
|
||||||
|
|
||||||
val fingerprint = vmessQRCode.fp ?: streamSetting.tlsSettings?.fingerprint
|
val fingerprint = vmessQRCode.fp ?: streamSetting.tlsSettings?.fingerprint
|
||||||
streamSetting.populateTlsSettings(vmessQRCode.tls, allowInsecure,
|
streamSetting.populateTlsSettings(
|
||||||
if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni,
|
vmessQRCode.tls, allowInsecure,
|
||||||
fingerprint, vmessQRCode.alpn, null, null, null)
|
if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni,
|
||||||
|
fingerprint, vmessQRCode.alpn, null, null, null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) {
|
} else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) {
|
||||||
@@ -232,7 +284,8 @@ object AngConfigManager {
|
|||||||
val indexSplit = result.indexOf("#")
|
val indexSplit = result.indexOf("#")
|
||||||
if (indexSplit > 0) {
|
if (indexSplit > 0) {
|
||||||
try {
|
try {
|
||||||
config.remarks = Utils.urlDecode(result.substring(indexSplit + 1, result.length))
|
config.remarks =
|
||||||
|
Utils.urlDecode(result.substring(indexSplit + 1, result.length))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
@@ -243,13 +296,17 @@ object AngConfigManager {
|
|||||||
//part decode
|
//part decode
|
||||||
val indexS = result.indexOf("@")
|
val indexS = result.indexOf("@")
|
||||||
result = if (indexS > 0) {
|
result = if (indexS > 0) {
|
||||||
Utils.decode(result.substring(0, indexS)) + result.substring(indexS, result.length)
|
Utils.decode(result.substring(0, indexS)) + result.substring(
|
||||||
|
indexS,
|
||||||
|
result.length
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
Utils.decode(result)
|
Utils.decode(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)/?$".toRegex()
|
val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)/?$".toRegex()
|
||||||
val match = legacyPattern.matchEntire(result) ?: return R.string.toast_incorrect_protocol
|
val match = legacyPattern.matchEntire(result)
|
||||||
|
?: return R.string.toast_incorrect_protocol
|
||||||
|
|
||||||
config.outboundBean?.settings?.servers?.get(0)?.let { server ->
|
config.outboundBean?.settings?.servers?.get(0)?.let { server ->
|
||||||
server.address = match.groupValues[3].removeSurrounding("[", "]")
|
server.address = match.groupValues[3].removeSurrounding("[", "]")
|
||||||
@@ -264,7 +321,8 @@ object AngConfigManager {
|
|||||||
config = ServerConfig.create(EConfigType.SOCKS)
|
config = ServerConfig.create(EConfigType.SOCKS)
|
||||||
if (indexSplit > 0) {
|
if (indexSplit > 0) {
|
||||||
try {
|
try {
|
||||||
config.remarks = Utils.urlDecode(result.substring(indexSplit + 1, result.length))
|
config.remarks =
|
||||||
|
Utils.urlDecode(result.substring(indexSplit + 1, result.length))
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
@@ -275,18 +333,23 @@ object AngConfigManager {
|
|||||||
//part decode
|
//part decode
|
||||||
val indexS = result.indexOf("@")
|
val indexS = result.indexOf("@")
|
||||||
if (indexS > 0) {
|
if (indexS > 0) {
|
||||||
result = Utils.decode(result.substring(0, indexS)) + result.substring(indexS, result.length)
|
result = Utils.decode(result.substring(0, indexS)) + result.substring(
|
||||||
|
indexS,
|
||||||
|
result.length
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
result = Utils.decode(result)
|
result = Utils.decode(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
val legacyPattern = "^(.*):(.*)@(.+?):(\\d+?)$".toRegex()
|
val legacyPattern = "^(.*):(.*)@(.+?):(\\d+?)$".toRegex()
|
||||||
val match = legacyPattern.matchEntire(result) ?: return R.string.toast_incorrect_protocol
|
val match =
|
||||||
|
legacyPattern.matchEntire(result) ?: return R.string.toast_incorrect_protocol
|
||||||
|
|
||||||
config.outboundBean?.settings?.servers?.get(0)?.let { server ->
|
config.outboundBean?.settings?.servers?.get(0)?.let { server ->
|
||||||
server.address = match.groupValues[3].removeSurrounding("[", "]")
|
server.address = match.groupValues[3].removeSurrounding("[", "]")
|
||||||
server.port = match.groupValues[4].toInt()
|
server.port = match.groupValues[4].toInt()
|
||||||
val socksUsersBean = V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
|
val socksUsersBean =
|
||||||
|
V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
|
||||||
socksUsersBean.user = match.groupValues[1].lowercase()
|
socksUsersBean.user = match.groupValues[1].lowercase()
|
||||||
socksUsersBean.pass = match.groupValues[2]
|
socksUsersBean.pass = match.groupValues[2]
|
||||||
server.users = listOf(socksUsersBean)
|
server.users = listOf(socksUsersBean)
|
||||||
@@ -302,17 +365,29 @@ object AngConfigManager {
|
|||||||
val queryParam = uri.rawQuery.split("&")
|
val queryParam = uri.rawQuery.split("&")
|
||||||
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
|
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
|
||||||
|
|
||||||
val sni = config.outboundBean?.streamSettings?.populateTransportSettings(queryParam["type"] ?: "tcp", queryParam["headerType"],
|
val sni = config.outboundBean?.streamSettings?.populateTransportSettings(
|
||||||
queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"],
|
queryParam["type"] ?: "tcp",
|
||||||
queryParam["mode"], queryParam["serviceName"])
|
queryParam["headerType"],
|
||||||
|
queryParam["host"],
|
||||||
|
queryParam["path"],
|
||||||
|
queryParam["seed"],
|
||||||
|
queryParam["quicSecurity"],
|
||||||
|
queryParam["key"],
|
||||||
|
queryParam["mode"],
|
||||||
|
queryParam["serviceName"]
|
||||||
|
)
|
||||||
fingerprint = queryParam["fp"] ?: ""
|
fingerprint = queryParam["fp"] ?: ""
|
||||||
config.outboundBean?.streamSettings?.populateTlsSettings(queryParam["security"] ?: TLS,
|
config.outboundBean?.streamSettings?.populateTlsSettings(
|
||||||
allowInsecure, queryParam["sni"] ?: sni!!, fingerprint, queryParam["alpn"],
|
queryParam["security"] ?: TLS,
|
||||||
null, null, null)
|
allowInsecure, queryParam["sni"] ?: sni!!, fingerprint, queryParam["alpn"],
|
||||||
|
null, null, null
|
||||||
|
)
|
||||||
flow = queryParam["flow"] ?: ""
|
flow = queryParam["flow"] ?: ""
|
||||||
} else {
|
} else {
|
||||||
config.outboundBean?.streamSettings?.populateTlsSettings(TLS, allowInsecure, "",
|
config.outboundBean?.streamSettings?.populateTlsSettings(
|
||||||
fingerprint, null, null, null, null)
|
TLS, allowInsecure, "",
|
||||||
|
fingerprint, null, null, null, null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
config.outboundBean?.settings?.servers?.get(0)?.let { server ->
|
config.outboundBean?.settings?.servers?.get(0)?.let { server ->
|
||||||
@@ -335,27 +410,41 @@ object AngConfigManager {
|
|||||||
vnext.port = uri.port
|
vnext.port = uri.port
|
||||||
vnext.users[0].id = uri.userInfo
|
vnext.users[0].id = uri.userInfo
|
||||||
vnext.users[0].encryption = queryParam["encryption"] ?: "none"
|
vnext.users[0].encryption = queryParam["encryption"] ?: "none"
|
||||||
vnext.users[0].flow =queryParam["flow"] ?: ""
|
vnext.users[0].flow = queryParam["flow"] ?: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
val sni = streamSetting.populateTransportSettings(queryParam["type"] ?: "tcp", queryParam["headerType"],
|
val sni = streamSetting.populateTransportSettings(
|
||||||
queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"],
|
queryParam["type"] ?: "tcp",
|
||||||
queryParam["mode"], queryParam["serviceName"])
|
queryParam["headerType"],
|
||||||
|
queryParam["host"],
|
||||||
|
queryParam["path"],
|
||||||
|
queryParam["seed"],
|
||||||
|
queryParam["quicSecurity"],
|
||||||
|
queryParam["key"],
|
||||||
|
queryParam["mode"],
|
||||||
|
queryParam["serviceName"]
|
||||||
|
)
|
||||||
fingerprint = queryParam["fp"] ?: ""
|
fingerprint = queryParam["fp"] ?: ""
|
||||||
val pbk = queryParam["pbk"] ?: ""
|
val pbk = queryParam["pbk"] ?: ""
|
||||||
val sid = queryParam["sid"] ?: ""
|
val sid = queryParam["sid"] ?: ""
|
||||||
val spx = Utils.urlDecode(queryParam["spx"] ?: "")
|
val spx = Utils.urlDecode(queryParam["spx"] ?: "")
|
||||||
streamSetting.populateTlsSettings(queryParam["security"] ?: "", allowInsecure,
|
streamSetting.populateTlsSettings(
|
||||||
queryParam["sni"] ?: sni, fingerprint, queryParam["alpn"], pbk, sid, spx)
|
queryParam["security"] ?: "", allowInsecure,
|
||||||
|
queryParam["sni"] ?: sni, fingerprint, queryParam["alpn"], pbk, sid, spx
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (config == null){
|
if (config == null) {
|
||||||
return R.string.toast_incorrect_protocol
|
return R.string.toast_incorrect_protocol
|
||||||
}
|
}
|
||||||
config.subscriptionId = subid
|
config.subscriptionId = subid
|
||||||
val guid = MmkvManager.encodeServerConfig("", config)
|
val guid = MmkvManager.encodeServerConfig("", config)
|
||||||
if (removedSelectedServer != null &&
|
if (removedSelectedServer != null &&
|
||||||
config.getProxyOutbound()?.getServerAddress() == removedSelectedServer.getProxyOutbound()?.getServerAddress() &&
|
config.getProxyOutbound()
|
||||||
config.getProxyOutbound()?.getServerPort() == removedSelectedServer.getProxyOutbound()?.getServerPort()) {
|
?.getServerAddress() == removedSelectedServer.getProxyOutbound()
|
||||||
|
?.getServerAddress() &&
|
||||||
|
config.getProxyOutbound()
|
||||||
|
?.getServerPort() == removedSelectedServer.getProxyOutbound()?.getServerPort()
|
||||||
|
) {
|
||||||
mainStorage?.encode(KEY_SELECTED_SERVER, guid)
|
mainStorage?.encode(KEY_SELECTED_SERVER, guid)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -365,14 +454,18 @@ object AngConfigManager {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun tryParseNewVmess(uriString: String, config: ServerConfig, allowInsecure: Boolean): Boolean {
|
private fun tryParseNewVmess(
|
||||||
|
uriString: String,
|
||||||
|
config: ServerConfig,
|
||||||
|
allowInsecure: Boolean
|
||||||
|
): Boolean {
|
||||||
return runCatching {
|
return runCatching {
|
||||||
val uri = URI(uriString)
|
val uri = URI(uriString)
|
||||||
check(uri.scheme == "vmess")
|
check(uri.scheme == "vmess")
|
||||||
val (_, protocol, tlsStr, uuid, alterId) =
|
val (_, protocol, tlsStr, uuid, alterId) =
|
||||||
Regex("(tcp|http|ws|kcp|quic|grpc)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})")
|
Regex("(tcp|http|ws|kcp|quic|grpc)(\\+tls)?:([0-9a-z]{8}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{4}-[0-9a-z]{12})")
|
||||||
.matchEntire(uri.userInfo)?.groupValues
|
.matchEntire(uri.userInfo)?.groupValues
|
||||||
?: error("parse user info fail.")
|
?: error("parse user info fail.")
|
||||||
val tls = tlsStr.isNotBlank()
|
val tls = tlsStr.isNotBlank()
|
||||||
val queryParam = uri.rawQuery.split("&")
|
val queryParam = uri.rawQuery.split("&")
|
||||||
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
|
.associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } }
|
||||||
@@ -387,12 +480,19 @@ object AngConfigManager {
|
|||||||
vnext.users[0].alterId = alterId.toInt()
|
vnext.users[0].alterId = alterId.toInt()
|
||||||
}
|
}
|
||||||
var fingerprint = streamSetting.tlsSettings?.fingerprint
|
var fingerprint = streamSetting.tlsSettings?.fingerprint
|
||||||
val sni = streamSetting.populateTransportSettings(protocol, queryParam["type"],
|
val sni = streamSetting.populateTransportSettings(protocol,
|
||||||
queryParam["host"]?.split("|")?.get(0) ?: "",
|
queryParam["type"],
|
||||||
queryParam["path"]?.takeIf { it.trim() != "/" } ?: "", queryParam["seed"], queryParam["security"],
|
queryParam["host"]?.split("|")?.get(0) ?: "",
|
||||||
queryParam["key"], queryParam["mode"], queryParam["serviceName"])
|
queryParam["path"]?.takeIf { it.trim() != "/" } ?: "",
|
||||||
streamSetting.populateTlsSettings(if (tls) TLS else "", allowInsecure, sni, fingerprint, null,
|
queryParam["seed"],
|
||||||
null, null, null)
|
queryParam["security"],
|
||||||
|
queryParam["key"],
|
||||||
|
queryParam["mode"],
|
||||||
|
queryParam["serviceName"])
|
||||||
|
streamSetting.populateTlsSettings(
|
||||||
|
if (tls) TLS else "", allowInsecure, sni, fingerprint, null,
|
||||||
|
null, null, null
|
||||||
|
)
|
||||||
true
|
true
|
||||||
}.getOrElse { false }
|
}.getOrElse { false }
|
||||||
}
|
}
|
||||||
@@ -480,12 +580,16 @@ object AngConfigManager {
|
|||||||
vmessQRCode.add = outbound.getServerAddress().orEmpty()
|
vmessQRCode.add = outbound.getServerAddress().orEmpty()
|
||||||
vmessQRCode.port = outbound.getServerPort().toString()
|
vmessQRCode.port = outbound.getServerPort().toString()
|
||||||
vmessQRCode.id = outbound.getPassword().orEmpty()
|
vmessQRCode.id = outbound.getPassword().orEmpty()
|
||||||
vmessQRCode.aid = outbound.settings?.vnext?.get(0)?.users?.get(0)?.alterId.toString()
|
vmessQRCode.aid =
|
||||||
vmessQRCode.scy = outbound.settings?.vnext?.get(0)?.users?.get(0)?.security.toString()
|
outbound.settings?.vnext?.get(0)?.users?.get(0)?.alterId.toString()
|
||||||
|
vmessQRCode.scy =
|
||||||
|
outbound.settings?.vnext?.get(0)?.users?.get(0)?.security.toString()
|
||||||
vmessQRCode.net = streamSetting.network
|
vmessQRCode.net = streamSetting.network
|
||||||
vmessQRCode.tls = streamSetting.security
|
vmessQRCode.tls = streamSetting.security
|
||||||
vmessQRCode.sni = streamSetting.tlsSettings?.serverName.orEmpty()
|
vmessQRCode.sni = streamSetting.tlsSettings?.serverName.orEmpty()
|
||||||
vmessQRCode.alpn = Utils.removeWhiteSpace(streamSetting.tlsSettings?.alpn?.joinToString()).orEmpty()
|
vmessQRCode.alpn =
|
||||||
|
Utils.removeWhiteSpace(streamSetting.tlsSettings?.alpn?.joinToString())
|
||||||
|
.orEmpty()
|
||||||
vmessQRCode.fp = streamSetting.tlsSettings?.fingerprint.orEmpty()
|
vmessQRCode.fp = streamSetting.tlsSettings?.fingerprint.orEmpty()
|
||||||
outbound.getTransportSettingDetails()?.let { transportDetails ->
|
outbound.getTransportSettingDetails()?.let { transportDetails ->
|
||||||
vmessQRCode.type = transportDetails[0]
|
vmessQRCode.type = transportDetails[0]
|
||||||
@@ -495,25 +599,34 @@ object AngConfigManager {
|
|||||||
val json = Gson().toJson(vmessQRCode)
|
val json = Gson().toJson(vmessQRCode)
|
||||||
Utils.encode(json)
|
Utils.encode(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
EConfigType.CUSTOM, EConfigType.WIREGUARD -> ""
|
EConfigType.CUSTOM, EConfigType.WIREGUARD -> ""
|
||||||
EConfigType.SHADOWSOCKS -> {
|
EConfigType.SHADOWSOCKS -> {
|
||||||
val remark = "#" + Utils.urlEncode(config.remarks)
|
val remark = "#" + Utils.urlEncode(config.remarks)
|
||||||
val pw = Utils.encode("${outbound.getSecurityEncryption()}:${outbound.getPassword()}")
|
val pw =
|
||||||
val url = String.format("%s@%s:%s",
|
Utils.encode("${outbound.getSecurityEncryption()}:${outbound.getPassword()}")
|
||||||
pw,
|
val url = String.format(
|
||||||
Utils.getIpv6Address(outbound.getServerAddress()!!),
|
"%s@%s:%s",
|
||||||
outbound.getServerPort())
|
|
||||||
url + remark
|
|
||||||
}
|
|
||||||
EConfigType.SOCKS -> {
|
|
||||||
val remark = "#" + Utils.urlEncode(config.remarks)
|
|
||||||
val pw = Utils.encode("${outbound.settings?.servers?.get(0)?.users?.get(0)?.user}:${outbound.getPassword()}")
|
|
||||||
val url = String.format("%s@%s:%s",
|
|
||||||
pw,
|
pw,
|
||||||
Utils.getIpv6Address(outbound.getServerAddress()!!),
|
Utils.getIpv6Address(outbound.getServerAddress()!!),
|
||||||
outbound.getServerPort())
|
outbound.getServerPort()
|
||||||
|
)
|
||||||
url + remark
|
url + remark
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EConfigType.SOCKS -> {
|
||||||
|
val remark = "#" + Utils.urlEncode(config.remarks)
|
||||||
|
val pw =
|
||||||
|
Utils.encode("${outbound.settings?.servers?.get(0)?.users?.get(0)?.user}:${outbound.getPassword()}")
|
||||||
|
val url = String.format(
|
||||||
|
"%s@%s:%s",
|
||||||
|
pw,
|
||||||
|
Utils.getIpv6Address(outbound.getServerAddress()!!),
|
||||||
|
outbound.getServerPort()
|
||||||
|
)
|
||||||
|
url + remark
|
||||||
|
}
|
||||||
|
|
||||||
EConfigType.VLESS,
|
EConfigType.VLESS,
|
||||||
EConfigType.TROJAN -> {
|
EConfigType.TROJAN -> {
|
||||||
val remark = "#" + Utils.urlEncode(config.remarks)
|
val remark = "#" + Utils.urlEncode(config.remarks)
|
||||||
@@ -537,12 +650,14 @@ object AngConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dicQuery["security"] = streamSetting.security.ifEmpty { "none" }
|
dicQuery["security"] = streamSetting.security.ifEmpty { "none" }
|
||||||
(streamSetting.tlsSettings?: streamSetting.realitySettings)?.let { tlsSetting ->
|
(streamSetting.tlsSettings
|
||||||
|
?: streamSetting.realitySettings)?.let { tlsSetting ->
|
||||||
if (!TextUtils.isEmpty(tlsSetting.serverName)) {
|
if (!TextUtils.isEmpty(tlsSetting.serverName)) {
|
||||||
dicQuery["sni"] = tlsSetting.serverName
|
dicQuery["sni"] = tlsSetting.serverName
|
||||||
}
|
}
|
||||||
if (!tlsSetting.alpn.isNullOrEmpty() && tlsSetting.alpn.isNotEmpty()) {
|
if (!tlsSetting.alpn.isNullOrEmpty() && tlsSetting.alpn.isNotEmpty()) {
|
||||||
dicQuery["alpn"] = Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty()
|
dicQuery["alpn"] =
|
||||||
|
Utils.removeWhiteSpace(tlsSetting.alpn.joinToString()).orEmpty()
|
||||||
}
|
}
|
||||||
if (!TextUtils.isEmpty(tlsSetting.fingerprint)) {
|
if (!TextUtils.isEmpty(tlsSetting.fingerprint)) {
|
||||||
dicQuery["fp"] = tlsSetting.fingerprint!!
|
dicQuery["fp"] = tlsSetting.fingerprint!!
|
||||||
@@ -567,12 +682,14 @@ object AngConfigManager {
|
|||||||
dicQuery["host"] = Utils.urlEncode(transportDetails[1])
|
dicQuery["host"] = Utils.urlEncode(transportDetails[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"kcp" -> {
|
"kcp" -> {
|
||||||
dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" }
|
dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" }
|
||||||
if (!TextUtils.isEmpty(transportDetails[2])) {
|
if (!TextUtils.isEmpty(transportDetails[2])) {
|
||||||
dicQuery["seed"] = Utils.urlEncode(transportDetails[2])
|
dicQuery["seed"] = Utils.urlEncode(transportDetails[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"ws" -> {
|
"ws" -> {
|
||||||
if (!TextUtils.isEmpty(transportDetails[1])) {
|
if (!TextUtils.isEmpty(transportDetails[1])) {
|
||||||
dicQuery["host"] = Utils.urlEncode(transportDetails[1])
|
dicQuery["host"] = Utils.urlEncode(transportDetails[1])
|
||||||
@@ -581,6 +698,7 @@ object AngConfigManager {
|
|||||||
dicQuery["path"] = Utils.urlEncode(transportDetails[2])
|
dicQuery["path"] = Utils.urlEncode(transportDetails[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"http", "h2" -> {
|
"http", "h2" -> {
|
||||||
dicQuery["type"] = "http"
|
dicQuery["type"] = "http"
|
||||||
if (!TextUtils.isEmpty(transportDetails[1])) {
|
if (!TextUtils.isEmpty(transportDetails[1])) {
|
||||||
@@ -590,11 +708,13 @@ object AngConfigManager {
|
|||||||
dicQuery["path"] = Utils.urlEncode(transportDetails[2])
|
dicQuery["path"] = Utils.urlEncode(transportDetails[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
"quic" -> {
|
"quic" -> {
|
||||||
dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" }
|
dicQuery["headerType"] = transportDetails[0].ifEmpty { "none" }
|
||||||
dicQuery["quicSecurity"] = Utils.urlEncode(transportDetails[1])
|
dicQuery["quicSecurity"] = Utils.urlEncode(transportDetails[1])
|
||||||
dicQuery["key"] = Utils.urlEncode(transportDetails[2])
|
dicQuery["key"] = Utils.urlEncode(transportDetails[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
"grpc" -> {
|
"grpc" -> {
|
||||||
dicQuery["mode"] = transportDetails[0]
|
dicQuery["mode"] = transportDetails[0]
|
||||||
dicQuery["serviceName"] = transportDetails[2]
|
dicQuery["serviceName"] = transportDetails[2]
|
||||||
@@ -602,13 +722,15 @@ object AngConfigManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val query = "?" + dicQuery.toList().joinToString(
|
val query = "?" + dicQuery.toList().joinToString(
|
||||||
separator = "&",
|
separator = "&",
|
||||||
transform = { it.first + "=" + it.second })
|
transform = { it.first + "=" + it.second })
|
||||||
|
|
||||||
val url = String.format("%s@%s:%s",
|
val url = String.format(
|
||||||
outbound.getPassword(),
|
"%s@%s:%s",
|
||||||
Utils.getIpv6Address(outbound.getServerAddress()!!),
|
outbound.getPassword(),
|
||||||
outbound.getServerPort())
|
Utils.getIpv6Address(outbound.getServerAddress()!!),
|
||||||
|
outbound.getServerPort()
|
||||||
|
)
|
||||||
url + query + remark
|
url + query + remark
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -756,17 +878,34 @@ object AngConfigManager {
|
|||||||
|
|
||||||
var count = 0
|
var count = 0
|
||||||
servers.lines()
|
servers.lines()
|
||||||
.reversed()
|
.reversed()
|
||||||
.forEach {
|
.forEach {
|
||||||
val resId = importConfig(it, subid, removedSelectedServer)
|
val resId = importConfig(it, subid, removedSelectedServer)
|
||||||
if (resId == 0) {
|
if (resId == 0) {
|
||||||
count++
|
count++
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return count
|
return count
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun importSubscription(remark: String, url: String, enabled: Boolean = true): Boolean {
|
||||||
|
val subId = Utils.getUuid()
|
||||||
|
val subItem = SubscriptionItem()
|
||||||
|
|
||||||
|
|
||||||
|
subItem.remarks = remark
|
||||||
|
subItem.url = url
|
||||||
|
subItem.enabled = enabled
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(subItem.remarks) || TextUtils.isEmpty(subItem.url)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
subStorage?.encode(subId, Gson().toJson(subItem))
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,9 @@ import android.util.Base64
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
|
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
|
||||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
|
import android.content.res.Configuration.UI_MODE_NIGHT_NO
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.LocaleList
|
import android.os.LocaleList
|
||||||
@@ -177,7 +178,7 @@ object Utils {
|
|||||||
//CIDR
|
//CIDR
|
||||||
if (addr.indexOf("/") > 0) {
|
if (addr.indexOf("/") > 0) {
|
||||||
val arr = addr.split("/")
|
val arr = addr.split("/")
|
||||||
if (arr.count() == 2 && Integer.parseInt(arr[1]) > 0) {
|
if (arr.count() == 2 && Integer.parseInt(arr[1]) > -1) {
|
||||||
addr = arr[0]
|
addr = arr[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,7 +356,7 @@ object Utils {
|
|||||||
|
|
||||||
fun getDarkModeStatus(context: Context): Boolean {
|
fun getDarkModeStatus(context: Context): Boolean {
|
||||||
val mode = context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK
|
val mode = context.resources.configuration.uiMode and UI_MODE_NIGHT_MASK
|
||||||
return mode == UI_MODE_NIGHT_YES
|
return mode != UI_MODE_NIGHT_NO
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIpv6Address(address: String): String {
|
fun getIpv6Address(address: String): String {
|
||||||
@@ -399,5 +400,9 @@ object Utils {
|
|||||||
return URL(url.protocol, IDN.toASCII(url.host, IDN.ALLOW_UNASSIGNED), url.port, url.file)
|
return URL(url.protocol, IDN.toASCII(url.host, IDN.ALLOW_UNASSIGNED), url.port, url.file)
|
||||||
.toExternalForm()
|
.toExternalForm()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isTv(context: Context): Boolean =
|
||||||
|
context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
V2rayNG/app/src/main/res/drawable-hdpi/ic_stat_name_black.png
Normal file
|
After Width: | Height: | Size: 526 B |
BIN
V2rayNG/app/src/main/res/drawable-hdpi/ic_stat_name_grey.png
Normal file
|
After Width: | Height: | Size: 651 B |
BIN
V2rayNG/app/src/main/res/drawable-mdpi/ic_stat_name_black.png
Normal file
|
After Width: | Height: | Size: 386 B |
BIN
V2rayNG/app/src/main/res/drawable-mdpi/ic_stat_name_grey.png
Normal file
|
After Width: | Height: | Size: 476 B |
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
|
||||||
|
</vector>
|
||||||
9
V2rayNG/app/src/main/res/drawable-night/ic_edit_24dp.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
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"/>
|
||||||
|
</vector>
|
||||||
9
V2rayNG/app/src/main/res/drawable-night/ic_fab_check.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFF"
|
||||||
|
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#fff"
|
||||||
|
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
||||||
|
</vector>
|
||||||
9
V2rayNG/app/src/main/res/drawable-night/ic_info_24dp.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-6h2v6zm0,-8h-2V7h2v2z" />
|
||||||
|
</vector>
|
||||||
BIN
V2rayNG/app/src/main/res/drawable-night/nav_header_bg.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
V2rayNG/app/src/main/res/drawable-xhdpi/ic_stat_name_black.png
Normal file
|
After Width: | Height: | Size: 679 B |
BIN
V2rayNG/app/src/main/res/drawable-xhdpi/ic_stat_name_grey.png
Normal file
|
After Width: | Height: | Size: 855 B |
BIN
V2rayNG/app/src/main/res/drawable-xxhdpi/ic_stat_name_black.png
Normal file
|
After Width: | Height: | Size: 984 B |
BIN
V2rayNG/app/src/main/res/drawable-xxhdpi/ic_stat_name_grey.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
V2rayNG/app/src/main/res/drawable-xxxhdpi/ic_stat_name_black.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
V2rayNG/app/src/main/res/drawable-xxxhdpi/ic_stat_name_grey.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
@@ -4,6 +4,6 @@
|
|||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FFFFFFFF"
|
android:fillColor="#FF000000"
|
||||||
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
|
android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
9
V2rayNG/app/src/main/res/drawable/ic_add_24dp.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z" />
|
||||||
|
</vector>
|
||||||
9
V2rayNG/app/src/main/res/drawable/ic_copy.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
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"/>
|
||||||
|
</vector>
|
||||||
@@ -4,6 +4,6 @@
|
|||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FFF"
|
android:fillColor="#000"
|
||||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
|
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
9
V2rayNG/app/src/main/res/drawable/ic_feedback_24dp.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM13,14h-2v-2h2v2zM13,10h-2L11,6h2v4z"/>
|
||||||
|
</vector>
|
||||||
34
V2rayNG/app/src/main/res/drawable/ic_file_24dp.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:pathData="M11,23h8a3,3 0,0 0,3 -3V8L15,1H7A3,3 0,0 0,4 4V9"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M15,5a3,3 0,0 0,3 3h4L15,1Z"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M2,17L10,17"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M6,13L6,21"
|
||||||
|
android:strokeLineJoin="round"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:strokeColor="#000"
|
||||||
|
android:strokeLineCap="round"/>
|
||||||
|
</vector>
|
||||||
@@ -4,6 +4,6 @@
|
|||||||
android:viewportWidth="24.0"
|
android:viewportWidth="24.0"
|
||||||
android:viewportHeight="24.0">
|
android:viewportHeight="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#fff"
|
android:fillColor="#000"
|
||||||
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
android:pathData="M21,19V5c0,-1.1 -0.9,-2 -2,-2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2zM8.5,13.5l2.5,3.01L14.5,12l4.5,6H5l3.5,-4.5z"/>
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
9
V2rayNG/app/src/main/res/drawable/ic_logcat_24dp.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M7,6h10l-5.01,6.3L7,6zM4.25,5.61C6.27,8.2 10,13 10,13v6c0,0.55 0.45,1 1,1h2c0.55,0 1,-0.45 1,-1v-6c0,0 3.72,-4.8 5.74,-7.39C20.25,4.95 19.78,4 18.95,4H5.04C4.21,4 3.74,4.95 4.25,5.61z"/>
|
||||||
|
</vector>
|
||||||
19
V2rayNG/app/src/main/res/drawable/ic_qu_scan_24dp.xml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="48"
|
||||||
|
android:viewportHeight="48">
|
||||||
|
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/colorBg"
|
||||||
|
android:pathData="M24,24m-22,0a22,22 0,1 1,44 0a22,22 0,1 1,-44 0Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/colorText"
|
||||||
|
android:pathData="M24,21.8 C25.7673,21.8,27.2,23.2327,27.2,25 C27.2,26.7673,25.7673,28.2,24,28.2
|
||||||
|
C22.2327,28.2,20.8,26.7673,20.8,25 C20.8,23.2327,22.2327,21.8,24,21.8 Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="@color/colorText"
|
||||||
|
android:pathData="M21,15 L19.17,17 L16,17 A2,2,0,0,0,14,19 L14,31 A2,2,0,0,0,16,33 L32,33
|
||||||
|
A2,2,0,0,0,34,31 L34,19 A2,2,0,0,0,32,17 L28.83,17 L27,15 Z M24,30
|
||||||
|
A5,5,0,1,1,29,25 A5,5,0,0,1,24,30 Z" />
|
||||||
|
</vector>
|
||||||
18
V2rayNG/app/src/main/res/drawable/ic_qu_switch_24dp.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/ic_shortcut_background"
|
||||||
|
android:left="2dp"
|
||||||
|
android:top="2dp"
|
||||||
|
android:right="2dp"
|
||||||
|
android:bottom="2dp" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/ic_stat_name_black"
|
||||||
|
android:left="12dp"
|
||||||
|
android:top="12dp"
|
||||||
|
android:right="12dp"
|
||||||
|
android:bottom="12dp" />
|
||||||
|
|
||||||
|
</layer-list>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<solid android:color="#009963"/>
|
<solid android:color="@color/color_fab_orange"/>
|
||||||
<corners android:radius="20dp"/>
|
<corners android:radius="20dp"/>
|
||||||
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||||
</shape>
|
</shape>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<solid android:color="@color/colorUnselected"/>
|
<solid android:color="@color/color_switch_fab_grey"/>
|
||||||
<corners android:radius="20dp"/>
|
<corners android:radius="20dp"/>
|
||||||
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
<padding android:left="0dp" android:top="0dp" android:right="0dp" android:bottom="0dp" />
|
||||||
</shape>
|
</shape>
|
||||||
|
|||||||
9
V2rayNG/app/src/main/res/drawable/ic_save_24dp.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
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"/>
|
||||||
|
</vector>
|
||||||
@@ -5,14 +5,14 @@
|
|||||||
android:viewportHeight="48">
|
android:viewportHeight="48">
|
||||||
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="#999999"
|
android:fillColor="#050505"
|
||||||
android:pathData="M24,24m-22,0a22,22 0,1 1,44 0a22,22 0,1 1,-44 0Z" />
|
android:pathData="M24,24m-22,0a22,22 0,1 1,44 0a22,22 0,1 1,-44 0Z" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="#ffffff"
|
android:fillColor="#FFFFFF"
|
||||||
android:pathData="M24,21.8 C25.7673,21.8,27.2,23.2327,27.2,25 C27.2,26.7673,25.7673,28.2,24,28.2
|
android:pathData="M24,21.8 C25.7673,21.8,27.2,23.2327,27.2,25 C27.2,26.7673,25.7673,28.2,24,28.2
|
||||||
C22.2327,28.2,20.8,26.7673,20.8,25 C20.8,23.2327,22.2327,21.8,24,21.8 Z" />
|
C22.2327,28.2,20.8,26.7673,20.8,25 C20.8,23.2327,22.2327,21.8,24,21.8 Z" />
|
||||||
<path
|
<path
|
||||||
android:fillColor="#ffffff"
|
android:fillColor="#FFFFFF"
|
||||||
android:pathData="M21,15 L19.17,17 L16,17 A2,2,0,0,0,14,19 L14,31 A2,2,0,0,0,16,33 L32,33
|
android:pathData="M21,15 L19.17,17 L16,17 A2,2,0,0,0,14,19 L14,31 A2,2,0,0,0,16,33 L32,33
|
||||||
A2,2,0,0,0,34,31 L34,19 A2,2,0,0,0,32,17 L28.83,17 L27,15 Z M24,30
|
A2,2,0,0,0,34,31 L34,19 A2,2,0,0,0,32,17 L28.83,17 L27,15 Z M24,30
|
||||||
A5,5,0,1,1,29,25 A5,5,0,0,1,24,30 Z" />
|
A5,5,0,1,1,29,25 A5,5,0,0,1,24,30 Z" />
|
||||||
10
V2rayNG/app/src/main/res/drawable/ic_select_all_24dp.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:width="24dp">
|
||||||
|
<path
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:pathData="M3,5h2L5,3c-1.1,0 -2,0.9 -2,2zM3,13h2v-2L3,11v2zM7,21h2v-2L7,19v2zM3,9h2L5,7L3,7v2zM13,3h-2v2h2L13,3zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2L3,19c0,1.1 0.9,2 2,2zM3,17h2v-2L3,15v2zM9,3L7,3v2h2L9,3zM11,21h2v-2h-2v2zM19,13h2v-2h-2v2zM19,21c1.1,0 2,-0.9 2,-2h-2v2zM19,9h2L21,7h-2v2zM19,17h2v-2h-2v2zM15,21h2v-2h-2v2zM15,5h2L17,3h-2v2zM7,17h10L17,7L7,7v10zM9,9h6v6L9,15L9,9z" />
|
||||||
|
</vector>
|
||||||
9
V2rayNG/app/src/main/res/drawable/ic_settings_24dp.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
|
||||||
|
</vector>
|
||||||
@@ -2,6 +2,6 @@
|
|||||||
<shape
|
<shape
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:shape="oval">
|
android:shape="oval">
|
||||||
<solid android:color="@color/colorUnselected" />
|
<solid android:color="@color/colorBg" />
|
||||||
<size android:width="44dp" android:height="44dp" />
|
<size android:width="44dp" android:height="44dp" />
|
||||||
</shape>
|
</shape>
|
||||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
BIN
V2rayNG/app/src/main/res/font/montserrat_thin.ttf
Normal file
@@ -3,7 +3,8 @@
|
|||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:padding="10dp"
|
android:paddingStart="10dp"
|
||||||
|
android:paddingEnd="10dp"
|
||||||
tools:context=".ui.MainActivity">
|
tools:context=".ui.MainActivity">
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
|
|||||||
@@ -13,8 +13,7 @@
|
|||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:theme="@style/AppTheme.AppBarOverlay">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
@@ -65,7 +64,7 @@
|
|||||||
android:paddingStart="16dp"
|
android:paddingStart="16dp"
|
||||||
android:text="@string/connection_test_pending"
|
android:text="@string/connection_test_pending"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||||
android:textColor="@color/colorWhite" />
|
android:textColor="@color/colorText" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -106,6 +105,7 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="start"
|
android:layout_gravity="start"
|
||||||
app:headerLayout="@layout/nav_header"
|
app:headerLayout="@layout/nav_header"
|
||||||
|
app:itemIconTint="@color/colorAccent"
|
||||||
app:menu="@menu/menu_drawer" >
|
app:menu="@menu/menu_drawer" >
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/server_lab_need_inbound"
|
android:text="@string/server_lab_need_inbound"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Tooltip"
|
android:textAppearance="@style/TextAppearance.AppCompat.Tooltip"
|
||||||
android:textColor="@color/fab_orange_dark" />
|
android:textColor="@color/color_secondary" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/routing_settings_tips"
|
android:text="@string/routing_settings_tips"
|
||||||
android:textColor="@color/fab_orange_dark" />
|
android:textColor="@color/color_secondary" />
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/et_routing_content"
|
android:id="@+id/et_routing_content"
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="@color/colorPrimary"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true">
|
android:focusable="true">
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/summary_pref_promotion"
|
android:text="@string/summary_pref_promotion"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Tooltip"
|
android:textAppearance="@style/TextAppearance.AppCompat.Tooltip"
|
||||||
android:textColor="@color/fab_orange_dark" />
|
android:textColor="@color/color_secondary" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -19,7 +19,8 @@
|
|||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:nextFocusRight="@+id/layout_share"
|
android:nextFocusRight="@+id/layout_share"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="@color/colorPrimary"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/server_height"
|
android:layout_height="@dimen/server_height"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layout_indicator"
|
android:id="@+id/layout_indicator"
|
||||||
android:layout_width="10dp"
|
android:layout_width="6dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:orientation="vertical" />
|
android:orientation="vertical" />
|
||||||
@@ -38,7 +39,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingStart="5dp">
|
android:paddingStart="9dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tv_name"
|
android:id="@+id/tv_name"
|
||||||
@@ -84,7 +85,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||||
android:textColor="@color/colorSubscription"
|
android:textColor="@color/color_secondary"
|
||||||
android:textSize="10sp"
|
android:textSize="10sp"
|
||||||
tools:text="Sub" />
|
tools:text="Sub" />
|
||||||
|
|
||||||
@@ -121,7 +122,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="@dimen/png_height"
|
android:layout_height="@dimen/png_height"
|
||||||
android:src="@drawable/ic_share_black_24dp"
|
android:src="@drawable/ic_share_24dp"
|
||||||
app:tint="?attr/colorMainText" />
|
app:tint="?attr/colorMainText" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -140,7 +141,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
android:layout_height="@dimen/png_height"
|
android:layout_height="@dimen/png_height"
|
||||||
android:src="@drawable/ic_edit_black_24dp"
|
android:src="@drawable/ic_edit_24dp"
|
||||||
app:tint="?attr/colorMainText" />
|
app:tint="?attr/colorMainText" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -159,7 +160,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
android:layout_height="@dimen/png_height"
|
android:layout_height="@dimen/png_height"
|
||||||
android:src="@drawable/ic_delete_black_24dp"
|
android:src="@drawable/ic_delete_24dp"
|
||||||
app:tint="?attr/colorMainText" />
|
app:tint="?attr/colorMainText" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -178,7 +179,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:lines="1"
|
android:lines="1"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||||
android:textColor="@color/fab_orange_dark"
|
android:textColor="@color/color_fab_orange"
|
||||||
android:textSize="10sp" />
|
android:textSize="10sp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
android:id="@+id/info_container"
|
android:id="@+id/info_container"
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="@color/colorPrimary"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground"
|
||||||
android:nextFocusRight="@+id/layout_edit"
|
android:nextFocusRight="@+id/layout_edit"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="@dimen/server_height"
|
android:layout_height="@dimen/server_height"
|
||||||
@@ -28,7 +29,7 @@
|
|||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/chk_enable"
|
android:id="@+id/chk_enable"
|
||||||
android:layout_width="10dp"
|
android:layout_width="6dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:orientation="vertical" />
|
android:orientation="vertical" />
|
||||||
@@ -37,7 +38,7 @@
|
|||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:paddingStart="5dp"
|
android:paddingStart="9dp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@@ -78,7 +79,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
android:layout_height="@dimen/png_height"
|
android:layout_height="@dimen/png_height"
|
||||||
android:src="@drawable/ic_share_black_24dp"
|
android:src="@drawable/ic_share_24dp"
|
||||||
app:tint="?attr/colorMainText"/>
|
app:tint="?attr/colorMainText"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -98,7 +99,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
android:layout_height="@dimen/png_height"
|
android:layout_height="@dimen/png_height"
|
||||||
android:src="@drawable/ic_edit_black_24dp"
|
android:src="@drawable/ic_edit_24dp"
|
||||||
app:tint="?attr/colorMainText" />
|
app:tint="?attr/colorMainText" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -16,6 +16,8 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
|
android:background="@color/colorPrimary"
|
||||||
|
android:foreground="?android:attr/selectableItemBackground"
|
||||||
android:padding="@dimen/nav_header_vertical_spacing">
|
android:padding="@dimen/nav_header_vertical_spacing">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -52,7 +54,7 @@
|
|||||||
<ImageView
|
<ImageView
|
||||||
android:layout_width="@dimen/png_height"
|
android:layout_width="@dimen/png_height"
|
||||||
android:layout_height="@dimen/png_height"
|
android:layout_height="@dimen/png_height"
|
||||||
android:src="@drawable/ic_delete_black_24dp"
|
android:src="@drawable/ic_delete_24dp"
|
||||||
app:tint="?attr/colorMainText" />
|
app:tint="?attr/colorMainText" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -14,12 +14,13 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
|
android:fontFamily="@font/montserrat_thin"
|
||||||
|
android:gravity="center"
|
||||||
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
||||||
android:text="@string/app_name"
|
android:text="@string/app_name"
|
||||||
android:textColor="@color/white"
|
android:textAppearance="@style/TextAppearance.AppCompat.Display1"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
|
android:textColor="@color/colorText" />
|
||||||
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?android:attr/actionBarSize">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
app:layout_scrollFlags="scroll|enterAlways" />
|
|
||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<com.google.android.material.navigation.NavigationView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/nav_view"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="start"
|
|
||||||
app:headerLayout="@layout/nav_header"
|
|
||||||
app:menu="@menu/menu_drawer" />
|
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
|
android:id="@+id/image_switch"
|
||||||
android:layout_width="40dp"
|
android:layout_width="40dp"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"
|
||||||
android:padding="10dp"
|
android:padding="10dp"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/del_config"
|
android:id="@+id/del_config"
|
||||||
android:icon="@drawable/ic_delete_white_24dp"
|
android:icon="@drawable/ic_delete_24dp"
|
||||||
android:title="@string/menu_item_del_config"
|
android:title="@string/menu_item_del_config"
|
||||||
app:showAsAction="always" />
|
app:showAsAction="always" />
|
||||||
<item
|
<item
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
<item
|
<item
|
||||||
android:id="@+id/add_config"
|
android:id="@+id/add_config"
|
||||||
android:icon="@drawable/ic_add_white_24dp"
|
android:icon="@drawable/ic_add_24dp"
|
||||||
android:title="@string/menu_item_add_config"
|
android:title="@string/menu_item_add_config"
|
||||||
app:showAsAction="always" />
|
app:showAsAction="always" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/del_config"
|
android:id="@+id/del_config"
|
||||||
android:icon="@drawable/ic_delete_white_24dp"
|
android:icon="@drawable/ic_delete_24dp"
|
||||||
android:title="@string/menu_item_del_config"
|
android:title="@string/menu_item_del_config"
|
||||||
app:showAsAction="always" />
|
app:showAsAction="always" />
|
||||||
<item
|
<item
|
||||||
|
|||||||