() {
11 | private var mActivity: LogcatActivity = activity
12 |
13 |
14 | override fun getItemCount() = mActivity.logsets.size
15 |
16 | override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
17 | try {
18 | val log = mActivity.logsets[position]
19 | if (log.isEmpty()) {
20 | holder.itemSubSettingBinding.logTag.text = ""
21 | holder.itemSubSettingBinding.logContent.text = ""
22 | } else {
23 | val content = log.split("):", limit = 2)
24 | holder.itemSubSettingBinding.logTag.text = content.first().split("(", limit = 2).first().trim()
25 | holder.itemSubSettingBinding.logContent.text = if (content.count() > 1) content.last().trim() else ""
26 | }
27 | } catch (e: Exception) {
28 | Log.e(AppConfig.TAG, "Error binding log view data", e)
29 | }
30 | }
31 |
32 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
33 | return MainViewHolder(
34 | ItemRecyclerLogcatBinding.inflate(
35 | LayoutInflater.from(parent.context),
36 | parent,
37 | false
38 | )
39 | )
40 | }
41 |
42 | class MainViewHolder(val itemSubSettingBinding: ItemRecyclerLogcatBinding) : RecyclerView.ViewHolder(itemSubSettingBinding.root)
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/drawable/ic_promotion_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/layout/dialog_config_filter.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
17 |
18 |
24 |
25 |
26 |
27 |
32 |
33 |
37 |
38 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/drawable-night/ic_promotion_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
16 |
17 |
22 |
23 |
27 |
28 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScScannerActivity.kt:
--------------------------------------------------------------------------------
1 | package com.v2ray.ang.ui
2 |
3 | import android.Manifest
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import androidx.activity.result.contract.ActivityResultContracts
7 | import com.v2ray.ang.R
8 | import com.v2ray.ang.extension.toast
9 | import com.v2ray.ang.extension.toastError
10 | import com.v2ray.ang.extension.toastSuccess
11 | import com.v2ray.ang.handler.AngConfigManager
12 |
13 | class ScScannerActivity : BaseActivity() {
14 |
15 | private val requestCameraPermissionLauncher = registerForActivityResult(
16 | ActivityResultContracts.RequestPermission()
17 | ) { isGranted: Boolean ->
18 | if (isGranted) {
19 | scanQRCode.launch(Intent(this, ScannerActivity::class.java))
20 | } else {
21 | toast(R.string.toast_permission_denied)
22 | finish()
23 | }
24 | }
25 |
26 | override fun onCreate(savedInstanceState: Bundle?) {
27 | super.onCreate(savedInstanceState)
28 | setContentView(R.layout.activity_none)
29 | importQRcode()
30 | }
31 |
32 | private fun importQRcode(): Boolean {
33 | requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA)
34 | return true
35 | }
36 |
37 | private val scanQRCode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
38 | if (it.resultCode == RESULT_OK) {
39 | val scanResult = it.data?.getStringExtra("SCAN_RESULT").orEmpty()
40 | val (count, countSub) = AngConfigManager.importBatchConfig(scanResult, "", false)
41 |
42 | if (count + countSub > 0) {
43 | toastSuccess(R.string.toast_success)
44 | } else {
45 | toastError(R.string.toast_failure)
46 | }
47 |
48 | startActivity(Intent(this, MainActivity::class.java))
49 | }
50 | finish()
51 | }
52 | }
--------------------------------------------------------------------------------
/fastlane/metadata/android/en-US/full_description.txt:
--------------------------------------------------------------------------------
1 | A V2Ray client for Android, support Xray core and v2fly core
2 |
3 | Telegram Channel
4 |
5 | github_2dust
6 |
7 | Usage
8 |
9 | Geoip and Geosite
10 |
11 |
12 | - geoip.dat and geosite.dat files are in
Android/data/com.v2ray.ang/files/assets (path may differ on some Android device)
13 | - download feature will get enhanced version in this repo (Note it need a working proxy)
14 | - latest official domain list and ip list can be imported manually
15 | - possible to use third party dat file in the same folder, like h2y
16 |
17 |
18 | More in our wiki
19 |
20 | Development guide
21 |
22 | 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.
23 | The aar can be compiled from the Golang project AndroidLibV2rayLite or AndroidLibXrayLite.
24 | For a quick start, read guide for Go Mobile and Makefiles for Go Developers
25 |
26 | v2rayNG can run on Android Emulators. For WSA, VPN permission need to be granted via
27 | appops set [package name] ACTIVATE_VPN allow
28 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/dto/VpnInterfaceAddressConfig.kt:
--------------------------------------------------------------------------------
1 | package com.v2ray.ang.dto
2 |
3 | /**
4 | * VPN interface address configuration enum class
5 | * Defines predefined IPv4 and IPv6 address pairs for VPN TUN interface configuration.
6 | * Each option provides client and router addresses to establish point-to-point VPN tunnels.
7 | */
8 | enum class VpnInterfaceAddressConfig(
9 | val displayName: String,
10 | val ipv4Client: String,
11 | val ipv4Router: String,
12 | val ipv6Client: String,
13 | val ipv6Router: String
14 | ) {
15 | OPTION_1("10.10.14.x", "10.10.14.1", "10.10.14.2", "fc00::10:10:14:1", "fc00::10:10:14:2"),
16 | OPTION_2("10.1.0.x", "10.1.0.1", "10.1.0.2", "fc00::10:1:0:1", "fc00::10:1:0:2"),
17 | OPTION_3("10.0.0.x", "10.0.0.1", "10.0.0.2", "fc00::10:0:0:1", "fc00::10:0:0:2"),
18 | OPTION_4("172.31.0.x", "172.31.0.1", "172.31.0.2", "fc00::172:31:0:1", "fc00::172:31:0:2"),
19 | OPTION_5("172.20.0.x", "172.20.0.1", "172.20.0.2", "fc00::172:20:0:1", "fc00::172:20:0:2"),
20 | OPTION_6("172.16.0.x", "172.16.0.1", "172.16.0.2", "fc00::172:16:0:1", "fc00::172:16:0:2"),
21 | OPTION_7("192.168.100.x", "192.168.100.1", "192.168.100.2", "fc00::192:168:100:1", "fc00::192:168:100:2");
22 |
23 | companion object {
24 | /**
25 | * Retrieves the VPN interface address configuration based on the specified index.
26 | *
27 | * @param index The configuration index (0-based) corresponding to user selection
28 | * @return The VpnInterfaceAddressConfig instance at the specified index,
29 | * or OPTION_1 (default) if the index is out of bounds
30 | */
31 | fun getConfigByIndex(index: Int): VpnInterfaceAddressConfig {
32 | return if (index in values().indices) {
33 | values()[index]
34 | } else {
35 | OPTION_1 // Default to the first configuration
36 | }
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/NativePlugin.kt:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * *
3 | * Copyright (C) 2021 by nekohasekai *
4 | * Copyright (C) 2021 by Max Lv *
5 | * Copyright (C) 2021 by Mygod Studio *
6 | * *
7 | * This program is free software: you can redistribute it and/or modify *
8 | * it under the terms of the GNU General Public License as published by *
9 | * the Free Software Foundation, either version 3 of the License, or *
10 | * (at your option) any later version. *
11 | * *
12 | * This program is distributed in the hope that it will be useful, *
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 | * GNU General Public License for more details. *
16 | * *
17 | * You should have received a copy of the GNU General Public License *
18 | * along with this program. If not, see . *
19 | * *
20 | ******************************************************************************/
21 |
22 | package com.v2ray.ang.plugin
23 |
24 | import android.content.pm.ResolveInfo
25 |
26 | class NativePlugin(resolveInfo: ResolveInfo) : ResolvedPlugin(resolveInfo) {
27 | init {
28 | check(resolveInfo.providerInfo != null)
29 | }
30 |
31 | override val componentInfo get() = resolveInfo.providerInfo!!
32 | }
33 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/layout/activity_tasker.xml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
15 |
16 |
24 |
25 |
33 |
34 |
35 |
36 |
41 |
42 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/layout/item_recycler_bypass_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
23 |
24 |
30 |
31 |
38 |
39 |
40 |
47 |
48 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/PluginContract.kt:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * *
3 | * Copyright (C) 2021 by nekohasekai *
4 | * *
5 | * This program is free software: you can redistribute it and/or modify *
6 | * it under the terms of the GNU General Public License as published by *
7 | * the Free Software Foundation, either version 3 of the License, or *
8 | * (at your option) any later version. *
9 | * *
10 | * This program is distributed in the hope that it will be useful, *
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 | * GNU General Public License for more details. *
14 | * *
15 | * You should have received a copy of the GNU General Public License *
16 | * along with this program. If not, see . *
17 | * *
18 | ******************************************************************************/
19 |
20 | package com.v2ray.ang.plugin
21 |
22 | object PluginContract {
23 |
24 | const val ACTION_NATIVE_PLUGIN = "io.nekohasekai.sagernet.plugin.ACTION_NATIVE_PLUGIN"
25 | const val EXTRA_ENTRY = "io.nekohasekai.sagernet.plugin.EXTRA_ENTRY"
26 | const val METADATA_KEY_ID = "io.nekohasekai.sagernet.plugin.id"
27 | const val METADATA_KEY_EXECUTABLE_PATH = "io.nekohasekai.sagernet.plugin.executable_path"
28 | const val METHOD_GET_EXECUTABLE = "sagernet:getExecutable"
29 |
30 | const val COLUMN_PATH = "path"
31 | const val COLUMN_MODE = "mode"
32 | const val SCHEME = "plugin"
33 | }
34 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/assets/custom_routing_white:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "remarks": "阻断udp443",
4 | "outboundTag": "block",
5 | "port": "443",
6 | "network": "udp"
7 | },
8 | {
9 | "remarks": "代理Google",
10 | "outboundTag": "proxy",
11 | "domain": [
12 | "geosite:google"
13 | ]
14 | },
15 | {
16 | "remarks": "绕过局域网IP",
17 | "outboundTag": "direct",
18 | "ip": [
19 | "geoip:private"
20 | ]
21 | },
22 | {
23 | "remarks": "绕过局域网域名",
24 | "outboundTag": "direct",
25 | "domain": [
26 | "geosite:private"
27 | ]
28 | },
29 | {
30 | "remarks": "绕过中国公共DNSIP",
31 | "outboundTag": "direct",
32 | "ip": [
33 | "223.5.5.5",
34 | "223.6.6.6",
35 | "2400:3200::1",
36 | "2400:3200:baba::1",
37 | "119.29.29.29",
38 | "1.12.12.12",
39 | "120.53.53.53",
40 | "2402:4e00::",
41 | "2402:4e00:1::",
42 | "180.76.76.76",
43 | "2400:da00::6666",
44 | "114.114.114.114",
45 | "114.114.115.115",
46 | "114.114.114.119",
47 | "114.114.115.119",
48 | "114.114.114.110",
49 | "114.114.115.110",
50 | "180.184.1.1",
51 | "180.184.2.2",
52 | "101.226.4.6",
53 | "218.30.118.6",
54 | "123.125.81.6",
55 | "140.207.198.6",
56 | "1.2.4.8",
57 | "210.2.4.8",
58 | "52.80.66.66",
59 | "117.50.22.22",
60 | "2400:7fc0:849e:200::4",
61 | "2404:c2c0:85d8:901::4",
62 | "117.50.10.10",
63 | "52.80.52.52",
64 | "2400:7fc0:849e:200::8",
65 | "2404:c2c0:85d8:901::8",
66 | "117.50.60.30",
67 | "52.80.60.30"
68 | ]
69 | },
70 | {
71 | "remarks": "绕过中国公共DNS域名",
72 | "outboundTag": "direct",
73 | "domain": [
74 | "domain:alidns.com",
75 | "domain:doh.pub",
76 | "domain:dot.pub",
77 | "domain:360.cn",
78 | "domain:onedns.net"
79 | ]
80 | },
81 | {
82 | "remarks": "绕过中国IP",
83 | "outboundTag": "direct",
84 | "ip": [
85 | "geoip:cn"
86 | ]
87 | },
88 | {
89 | "remarks": "绕过中国域名",
90 | "outboundTag": "direct",
91 | "domain": [
92 | "geosite:cn"
93 | ]
94 | }
95 | ]
96 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/menu/menu_drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
53 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/helper/ItemTouchHelperAdapter.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015 Paul Burke
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.v2ray.ang.helper
17 |
18 | /**
19 | * Interface to listen for a move or dismissal event from a [ItemTouchHelper.Callback].
20 | *
21 | * @author Paul Burke (ipaulpro)
22 | */
23 | interface ItemTouchHelperAdapter {
24 | /**
25 | * Called when an item has been dragged far enough to trigger a move. This is called every time
26 | * an item is shifted, and **not** at the end of a "drop" event.
27 | *
28 | * Implementations should call [RecyclerView.Adapter.notifyItemMoved] after
29 | * adjusting the underlying data to reflect this move.
30 | *
31 | * @param fromPosition The start position of the moved item.
32 | * @param toPosition Then resolved position of the moved item.
33 | * @return True if the item was moved to the new adapter position.
34 | * @see RecyclerView.getAdapterPositionFor
35 | * @see RecyclerView.ViewHolder.getAdapterPosition
36 | */
37 | fun onItemMove(fromPosition: Int, toPosition: Int): Boolean
38 |
39 |
40 | fun onItemMoveCompleted()
41 |
42 | /**
43 | * Called when an item has been dismissed by a swipe.
44 | *
45 | * Implementations should call [RecyclerView.Adapter.notifyItemRemoved] after
46 | * adjusting the underlying data to reflect this removal.
47 | *
48 | * @param position The position of the item dismissed.
49 | * @see RecyclerView.getAdapterPositionFor
50 | * @see RecyclerView.ViewHolder.getAdapterPosition
51 | */
52 | fun onItemDismiss(position: Int)
53 | }
54 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/assets/v2ray_config.json:
--------------------------------------------------------------------------------
1 | {
2 | "stats":{},
3 | "log": {
4 | "loglevel": "warning"
5 | },
6 | "policy":{
7 | "levels": {
8 | "8": {
9 | "handshake": 4,
10 | "connIdle": 300,
11 | "uplinkOnly": 1,
12 | "downlinkOnly": 1
13 | }
14 | },
15 | "system": {
16 | "statsOutboundUplink": true,
17 | "statsOutboundDownlink": true
18 | }
19 | },
20 | "inbounds": [{
21 | "tag": "socks",
22 | "port": 10808,
23 | "protocol": "socks",
24 | "settings": {
25 | "auth": "noauth",
26 | "udp": true,
27 | "userLevel": 8
28 | },
29 | "sniffing": {
30 | "enabled": true,
31 | "destOverride": [
32 | "http",
33 | "tls"
34 | ]
35 | }
36 | },
37 | {
38 | "tag": "http",
39 | "port": 10809,
40 | "protocol": "http",
41 | "settings": {
42 | "userLevel": 8
43 | }
44 | }
45 | ],
46 | "outbounds": [{
47 | "tag": "proxy",
48 | "protocol": "vmess",
49 | "settings": {
50 | "vnext": [
51 | {
52 | "address": "v2ray.cool",
53 | "port": 10086,
54 | "users": [
55 | {
56 | "id": "a3482e88-686a-4a58-8126-99c9df64b7bf",
57 | "alterId": 0,
58 | "security": "auto",
59 | "level": 8
60 | }
61 | ]
62 | }
63 | ],
64 | "servers": [
65 | {
66 | "address": "v2ray.cool",
67 | "method": "chacha20",
68 | "ota": false,
69 | "password": "123456",
70 | "port": 10086,
71 | "level": 8
72 | }
73 | ]
74 | },
75 | "streamSettings": {
76 | "network": "tcp"
77 | },
78 | "mux": {
79 | "enabled": false
80 | }
81 | },
82 | {
83 | "protocol": "freedom",
84 | "settings": {
85 | "domainStrategy": "UseIP"
86 | },
87 | "tag": "direct"
88 | },
89 | {
90 | "protocol": "blackhole",
91 | "tag": "block",
92 | "settings": {
93 | "response": {
94 | "type": "http"
95 | }
96 | }
97 | }
98 | ],
99 | "routing": {
100 | "domainStrategy": "AsIs",
101 | "rules": []
102 | },
103 | "dns": {
104 | "hosts": {},
105 | "servers": []
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/drawable/ic_settings_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # v2rayNG
2 |
3 | A V2Ray client for Android, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core)
4 |
5 | [](https://developer.android.com/about/versions/lollipop)
6 | [](https://kotlinlang.org)
7 | [](https://github.com/2dust/v2rayNG/commits/master)
8 | [](https://www.codefactor.io/repository/github/2dust/v2rayng)
9 | [](https://github.com/2dust/v2rayNG/releases)
10 | [](https://t.me/v2rayn)
11 |
12 | ### Telegram Channel
13 | [github_2dust](https://t.me/github_2dust)
14 |
15 | ### Usage
16 |
17 | #### Geoip and Geosite
18 | - geoip.dat and geosite.dat files are in `Android/data/com.v2ray.ang/files/assets` (path may differ on some Android device)
19 | - download feature will get enhanced version in this [repo](https://github.com/Loyalsoldier/v2ray-rules-dat) (Note it need a working proxy)
20 | - latest official [domain list](https://github.com/Loyalsoldier/v2ray-rules-dat) and [ip list](https://github.com/Loyalsoldier/geoip) can be imported manually
21 | - 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)
22 |
23 | ### More in our [wiki](https://github.com/2dust/v2rayNG/wiki)
24 |
25 | ### Development guide
26 |
27 | 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.
28 | The aar can be compiled from the Golang project [AndroidLibV2rayLite](https://github.com/2dust/AndroidLibV2rayLite) or [AndroidLibXrayLite](https://github.com/2dust/AndroidLibXrayLite).
29 | 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/)
30 |
31 | v2rayNG can run on Android Emulators. For WSA, VPN permission need to be granted via
32 | `appops set [package name] ACTIVATE_VPN allow`
33 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/plugin/Plugin.kt:
--------------------------------------------------------------------------------
1 | /******************************************************************************
2 | * *
3 | * Copyright (C) 2021 by nekohasekai *
4 | * Copyright (C) 2021 by Max Lv *
5 | * Copyright (C) 2021 by Mygod Studio *
6 | * *
7 | * This program is free software: you can redistribute it and/or modify *
8 | * it under the terms of the GNU General Public License as published by *
9 | * the Free Software Foundation, either version 3 of the License, or *
10 | * (at your option) any later version. *
11 | * *
12 | * This program is distributed in the hope that it will be useful, *
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 | * GNU General Public License for more details. *
16 | * *
17 | * You should have received a copy of the GNU General Public License *
18 | * along with this program. If not, see . *
19 | * *
20 | ******************************************************************************/
21 |
22 | package com.v2ray.ang.plugin
23 |
24 | import android.graphics.drawable.Drawable
25 |
26 | abstract class Plugin {
27 | abstract val id: String
28 | abstract val label: CharSequence
29 | abstract val version: Int
30 | abstract val versionName: String
31 | open val icon: Drawable? get() = null
32 | open val defaultConfig: String? get() = null
33 | open val packageName: String get() = ""
34 | open val directBootAware: Boolean get() = true
35 |
36 | override fun equals(other: Any?): Boolean {
37 | if (this === other) return true
38 | if (javaClass != other?.javaClass) return false
39 | return id == (other as Plugin).id
40 | }
41 |
42 | override fun hashCode() = id.hashCode()
43 | }
44 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/drawable-night/ic_settings_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/helper/CustomDividerItemDecoration.kt:
--------------------------------------------------------------------------------
1 | package com.v2ray.ang.helper
2 |
3 | import android.graphics.Canvas
4 | import android.graphics.Rect
5 | import android.graphics.drawable.Drawable
6 | import android.view.View
7 | import androidx.recyclerview.widget.RecyclerView
8 |
9 | class CustomDividerItemDecoration(
10 | private val divider: Drawable,
11 | private val orientation: Int
12 | ) : RecyclerView.ItemDecoration() {
13 |
14 | override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
15 | if (orientation == RecyclerView.VERTICAL) {
16 | drawVerticalDividers(canvas, parent)
17 | } else {
18 | drawHorizontalDividers(canvas, parent)
19 | }
20 | }
21 |
22 | private fun drawVerticalDividers(canvas: Canvas, parent: RecyclerView) {
23 | val left = parent.paddingLeft
24 | val right = parent.width - parent.paddingRight
25 |
26 | val childCount = parent.childCount
27 | for (i in 0 until childCount - 1) {
28 | val child = parent.getChildAt(i)
29 | val params = child.layoutParams as RecyclerView.LayoutParams
30 |
31 | val top = child.bottom + params.bottomMargin
32 | val bottom = top + divider.intrinsicHeight
33 |
34 | divider.setBounds(left, top, right, bottom)
35 | divider.draw(canvas)
36 | }
37 | }
38 |
39 | private fun drawHorizontalDividers(canvas: Canvas, parent: RecyclerView) {
40 | val top = parent.paddingTop
41 | val bottom = parent.height - parent.paddingBottom
42 |
43 | val childCount = parent.childCount
44 | for (i in 0 until childCount - 1) {
45 | val child = parent.getChildAt(i)
46 | val params = child.layoutParams as RecyclerView.LayoutParams
47 |
48 | val left = child.right + params.rightMargin
49 | val right = left + divider.intrinsicWidth
50 |
51 | divider.setBounds(left, top, right, bottom)
52 | divider.draw(canvas)
53 | }
54 | }
55 |
56 | override fun getItemOffsets(
57 | outRect: Rect,
58 | view: View,
59 | parent: RecyclerView,
60 | state: RecyclerView.State
61 | ) {
62 | if (orientation == RecyclerView.VERTICAL) {
63 | outRect.set(0, 0, 0, divider.intrinsicHeight)
64 | } else {
65 | outRect.set(0, 0, divider.intrinsicWidth, 0)
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/layout/activity_server_socks.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
16 |
17 |
22 |
23 |
27 |
28 |
29 |
34 |
35 |
36 |
37 |
42 |
43 |
47 |
48 |
53 |
54 |
55 |
56 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/test/java/com/v2ray/ang/UtilsTest.kt:
--------------------------------------------------------------------------------
1 | package com.v2ray.ang
2 |
3 | import com.v2ray.ang.util.Utils
4 | import org.junit.Assert.assertEquals
5 | import org.junit.Assert.assertFalse
6 | import org.junit.Assert.assertTrue
7 | import org.junit.Test
8 |
9 | /**
10 | * Example local unit test, which will execute on the development machine (host).
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | class UtilsTest {
15 |
16 | @Test
17 | fun test_parseInt() {
18 | assertEquals(Utils.parseInt("1234"), 1234)
19 | }
20 |
21 | @Test
22 | fun test_isIpAddress() {
23 | assertFalse(Utils.isIpAddress("114.113.112.266"))
24 | assertFalse(Utils.isIpAddress("666.666.666.666"))
25 | assertFalse(Utils.isIpAddress("256.0.0.0"))
26 | assertFalse(Utils.isIpAddress("::ffff:127.0.0.0.1"))
27 | assertFalse(Utils.isIpAddress("baidu.com"))
28 | assertFalse(Utils.isIpAddress(""))
29 |
30 | assertTrue(Utils.isIpAddress("127.0.0.1"))
31 | assertTrue(Utils.isIpAddress("127.0.0.1:80"))
32 | assertTrue(Utils.isIpAddress("0.0.0.0/0"))
33 | assertTrue(Utils.isIpAddress("::1"))
34 | assertTrue(Utils.isIpAddress("[::1]:80"))
35 | assertTrue(Utils.isIpAddress("2605:2700:0:3::4713:93e3"))
36 | assertTrue(Utils.isIpAddress("[2605:2700:0:3::4713:93e3]:80"))
37 | assertTrue(Utils.isIpAddress("::ffff:192.168.173.22"))
38 | assertTrue(Utils.isIpAddress("[::ffff:192.168.173.22]:80"))
39 | assertTrue(Utils.isIpAddress("1::"))
40 | assertTrue(Utils.isIpAddress("::"))
41 | assertTrue(Utils.isIpAddress("::/0"))
42 | assertTrue(Utils.isIpAddress("10.24.56.0/24"))
43 | assertTrue(Utils.isIpAddress("2001:4321::1"))
44 | assertTrue(Utils.isIpAddress("240e:1234:abcd:12::6666"))
45 | assertTrue(Utils.isIpAddress("240e:1234:abcd:12::/64"))
46 | }
47 |
48 | @Test
49 | fun test_IsIpInCidr() {
50 | assertTrue(Utils.isIpInCidr("192.168.1.1", "192.168.1.0/24"))
51 | assertTrue(Utils.isIpInCidr("192.168.1.254", "192.168.1.0/24"))
52 | assertFalse(Utils.isIpInCidr("192.168.2.1", "192.168.1.0/24"))
53 |
54 | assertTrue(Utils.isIpInCidr("10.0.0.0", "10.0.0.0/8"))
55 | assertTrue(Utils.isIpInCidr("10.255.255.255", "10.0.0.0/8"))
56 | assertFalse(Utils.isIpInCidr("11.0.0.0", "10.0.0.0/8"))
57 |
58 | assertFalse(Utils.isIpInCidr("invalid-ip", "192.168.1.0/24"))
59 | assertFalse(Utils.isIpInCidr("192.168.1.1", "invalid-cidr"))
60 | }
61 |
62 | }
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/util/MessageUtil.kt:
--------------------------------------------------------------------------------
1 | package com.v2ray.ang.util
2 |
3 | import android.content.ComponentName
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.util.Log
7 | import com.v2ray.ang.AppConfig
8 | import com.v2ray.ang.service.V2RayTestService
9 | import java.io.Serializable
10 |
11 | object MessageUtil {
12 |
13 |
14 | /**
15 | * Sends a message to the service.
16 | *
17 | * @param ctx The context.
18 | * @param what The message identifier.
19 | * @param content The message content.
20 | */
21 | fun sendMsg2Service(ctx: Context, what: Int, content: Serializable) {
22 | sendMsg(ctx, AppConfig.BROADCAST_ACTION_SERVICE, what, content)
23 | }
24 |
25 | /**
26 | * Sends a message to the UI.
27 | *
28 | * @param ctx The context.
29 | * @param what The message identifier.
30 | * @param content The message content.
31 | */
32 | fun sendMsg2UI(ctx: Context, what: Int, content: Serializable) {
33 | sendMsg(ctx, AppConfig.BROADCAST_ACTION_ACTIVITY, what, content)
34 | }
35 |
36 | /**
37 | * Sends a message to the test service.
38 | *
39 | * @param ctx The context.
40 | * @param what The message identifier.
41 | * @param content The message content.
42 | */
43 | fun sendMsg2TestService(ctx: Context, what: Int, content: Serializable) {
44 | try {
45 | val intent = Intent()
46 | intent.component = ComponentName(ctx, V2RayTestService::class.java)
47 | intent.putExtra("key", what)
48 | intent.putExtra("content", content)
49 | ctx.startService(intent)
50 | } catch (e: Exception) {
51 | Log.e(AppConfig.TAG, "Failed to send message to test service", e)
52 | }
53 | }
54 |
55 | /**
56 | * Sends a message with the specified action.
57 | *
58 | * @param ctx The context.
59 | * @param action The action string.
60 | * @param what The message identifier.
61 | * @param content The message content.
62 | */
63 | private fun sendMsg(ctx: Context, action: String, what: Int, content: Serializable) {
64 | try {
65 | val intent = Intent()
66 | intent.action = action
67 | intent.`package` = AppConfig.ANG_PACKAGE
68 | intent.putExtra("key", what)
69 | intent.putExtra("content", content)
70 | ctx.sendBroadcast(intent)
71 | } catch (e: Exception) {
72 | Log.e(AppConfig.TAG, "Failed to send message with action: $action", e)
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/service/V2RayProxyOnlyService.kt:
--------------------------------------------------------------------------------
1 | package com.v2ray.ang.service
2 |
3 | import android.app.Service
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.os.Build
7 | import android.os.IBinder
8 | import androidx.annotation.RequiresApi
9 | import com.v2ray.ang.handler.SettingsManager
10 | import com.v2ray.ang.handler.V2RayServiceManager
11 | import com.v2ray.ang.util.MyContextWrapper
12 | import java.lang.ref.SoftReference
13 |
14 | class V2RayProxyOnlyService : Service(), ServiceControl {
15 | /**
16 | * Initializes the service.
17 | */
18 | override fun onCreate() {
19 | super.onCreate()
20 | V2RayServiceManager.serviceControl = SoftReference(this)
21 | }
22 |
23 | /**
24 | * Handles the start command for the service.
25 | * @param intent The intent.
26 | * @param flags The flags.
27 | * @param startId The start ID.
28 | * @return The start mode.
29 | */
30 | override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
31 | V2RayServiceManager.startCoreLoop()
32 | return START_STICKY
33 | }
34 |
35 | /**
36 | * Destroys the service.
37 | */
38 | override fun onDestroy() {
39 | super.onDestroy()
40 | V2RayServiceManager.stopCoreLoop()
41 | }
42 |
43 | /**
44 | * Gets the service instance.
45 | * @return The service instance.
46 | */
47 | override fun getService(): Service {
48 | return this
49 | }
50 |
51 | /**
52 | * Starts the service.
53 | */
54 | override fun startService() {
55 | // do nothing
56 | }
57 |
58 | /**
59 | * Stops the service.
60 | */
61 | override fun stopService() {
62 | stopSelf()
63 | }
64 |
65 | /**
66 | * Protects the VPN socket.
67 | * @param socket The socket to protect.
68 | * @return True if the socket is protected, false otherwise.
69 | */
70 | override fun vpnProtect(socket: Int): Boolean {
71 | return true
72 | }
73 |
74 | /**
75 | * Binds the service.
76 | * @param intent The intent.
77 | * @return The binder.
78 | */
79 | override fun onBind(intent: Intent?): IBinder? {
80 | return null
81 | }
82 |
83 | /**
84 | * Attaches the base context to the service.
85 | * @param newBase The new base context.
86 | */
87 | @RequiresApi(Build.VERSION_CODES.N)
88 | override fun attachBaseContext(newBase: Context?) {
89 | val context = newBase?.let {
90 | MyContextWrapper.wrap(newBase, SettingsManager.getLocale())
91 | }
92 | super.attachBaseContext(context)
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/util/JsonUtil.kt:
--------------------------------------------------------------------------------
1 | package com.v2ray.ang.util
2 |
3 | import android.util.Log
4 | import com.google.gson.Gson
5 | import com.google.gson.GsonBuilder
6 | import com.google.gson.JsonObject
7 | import com.google.gson.JsonParser
8 | import com.google.gson.JsonPrimitive
9 | import com.google.gson.JsonSerializationContext
10 | import com.google.gson.JsonSerializer
11 | import com.google.gson.reflect.TypeToken
12 | import com.v2ray.ang.AppConfig
13 | import java.lang.reflect.Type
14 |
15 | object JsonUtil {
16 | private var gson = Gson()
17 |
18 | /**
19 | * Converts an object to its JSON representation.
20 | *
21 | * @param src The object to convert.
22 | * @return The JSON representation of the object.
23 | */
24 | fun toJson(src: Any?): String {
25 | return gson.toJson(src)
26 | }
27 |
28 | /**
29 | * Parses a JSON string into an object of the specified class.
30 | *
31 | * @param src The JSON string to parse.
32 | * @param cls The class of the object to parse into.
33 | * @return The parsed object.
34 | */
35 | fun fromJson(src: String, cls: Class): T {
36 | return gson.fromJson(src, cls)
37 | }
38 |
39 | /**
40 | * Converts an object to its pretty-printed JSON representation.
41 | *
42 | * @param src The object to convert.
43 | * @return The pretty-printed JSON representation of the object, or null if the object is null.
44 | */
45 | fun toJsonPretty(src: Any?): String? {
46 | if (src == null)
47 | return null
48 | val gsonPre = GsonBuilder()
49 | .setPrettyPrinting()
50 | .disableHtmlEscaping()
51 | .registerTypeAdapter( // custom serializer is needed here since JSON by default parse number as Double, core will fail to start
52 | object : TypeToken() {}.type,
53 | JsonSerializer { src: Double?, _: Type?, _: JsonSerializationContext? ->
54 | JsonPrimitive(
55 | src?.toInt()
56 | )
57 | }
58 | )
59 | .create()
60 | return gsonPre.toJson(src)
61 | }
62 |
63 | /**
64 | * Parses a JSON string into a JsonObject.
65 | *
66 | * @param src The JSON string to parse.
67 | * @return The parsed JsonObject, or null if parsing fails.
68 | */
69 | fun parseString(src: String?): JsonObject? {
70 | if (src == null)
71 | return null
72 | try {
73 | return JsonParser.parseString(src).getAsJsonObject()
74 | } catch (e: Exception) {
75 | Log.e(AppConfig.TAG, "Failed to parse JSON string", e)
76 | return null
77 | }
78 | }
79 | }
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/layout/activity_server_vmess.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
16 |
17 |
22 |
23 |
27 |
28 |
33 |
34 |
35 |
36 |
41 |
42 |
46 |
47 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/layout/layout_address_port.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
17 |
18 |
19 |
24 |
25 |
29 |
30 |
35 |
36 |
37 |
38 |
43 |
44 |
48 |
49 |
54 |
55 |
56 |
57 |
62 |
63 |
67 |
68 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/res/layout/activity_server_shadowsocks.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
14 |
15 |
16 |
17 |
22 |
23 |
27 |
28 |
33 |
34 |
35 |
36 |
37 |
42 |
43 |
47 |
48 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/handler/SubscriptionUpdater.kt:
--------------------------------------------------------------------------------
1 | package com.v2ray.ang.handler
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.NotificationChannel
5 | import android.app.NotificationManager
6 | import android.content.Context
7 | import android.os.Build
8 | import android.util.Log
9 | import androidx.core.app.NotificationCompat
10 | import androidx.core.app.NotificationManagerCompat
11 | import androidx.work.CoroutineWorker
12 | import androidx.work.WorkerParameters
13 | import com.v2ray.ang.AppConfig
14 | import com.v2ray.ang.R
15 |
16 | object SubscriptionUpdater {
17 |
18 | class UpdateTask(context: Context, params: WorkerParameters) :
19 | CoroutineWorker(context, params) {
20 |
21 | private val notificationManager = NotificationManagerCompat.from(applicationContext)
22 | private val notification =
23 | NotificationCompat.Builder(applicationContext, AppConfig.SUBSCRIPTION_UPDATE_CHANNEL)
24 | .setWhen(0)
25 | .setTicker("Update")
26 | .setContentTitle(context.getString(R.string.title_pref_auto_update_subscription))
27 | .setSmallIcon(R.drawable.ic_stat_name)
28 | .setCategory(NotificationCompat.CATEGORY_SERVICE)
29 | .setPriority(NotificationCompat.PRIORITY_DEFAULT)
30 |
31 | /**
32 | * Performs the subscription update work.
33 | * @return The result of the work.
34 | */
35 | @SuppressLint("MissingPermission")
36 | override suspend fun doWork(): Result {
37 | Log.i(AppConfig.TAG, "subscription automatic update starting")
38 |
39 | val subs = MmkvManager.decodeSubscriptions().filter { it.second.autoUpdate }
40 |
41 | for (sub in subs) {
42 | val subItem = sub.second
43 |
44 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
45 | notification.setChannelId(AppConfig.SUBSCRIPTION_UPDATE_CHANNEL)
46 | val channel =
47 | NotificationChannel(
48 | AppConfig.SUBSCRIPTION_UPDATE_CHANNEL,
49 | AppConfig.SUBSCRIPTION_UPDATE_CHANNEL_NAME,
50 | NotificationManager.IMPORTANCE_MIN
51 | )
52 | notificationManager.createNotificationChannel(channel)
53 | }
54 | notificationManager.notify(3, notification.build())
55 | Log.i(AppConfig.TAG, "subscription automatic update: ---${subItem.remarks}")
56 | AngConfigManager.updateConfigViaSub(Pair(sub.first, subItem))
57 | notification.setContentText("Updating ${subItem.remarks}")
58 | }
59 | notificationManager.cancel(3)
60 | return Result.success()
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.v2ray.ang.ui
2 |
3 | import android.content.Context
4 | import android.os.Build
5 | import android.os.Bundle
6 | import android.view.MenuItem
7 | import androidx.annotation.RequiresApi
8 | import androidx.appcompat.app.AppCompatActivity
9 | import androidx.core.content.ContextCompat
10 | import androidx.core.view.WindowCompat
11 | import androidx.recyclerview.widget.DividerItemDecoration
12 | import androidx.recyclerview.widget.RecyclerView
13 | import com.v2ray.ang.handler.SettingsManager
14 | import com.v2ray.ang.helper.CustomDividerItemDecoration
15 | import com.v2ray.ang.util.MyContextWrapper
16 | import com.v2ray.ang.util.Utils
17 |
18 |
19 | abstract class BaseActivity : AppCompatActivity() {
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | supportActionBar?.setDisplayHomeAsUpEnabled(true)
23 | if (!Utils.getDarkModeStatus(this)) {
24 | WindowCompat.getInsetsController(window, window.decorView).apply {
25 | isAppearanceLightStatusBars = true
26 | }
27 | }
28 | }
29 |
30 | override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
31 | android.R.id.home -> {
32 | // Handles the home button press by delegating to the onBackPressedDispatcher.
33 | // This ensures consistent back navigation behavior.
34 | onBackPressedDispatcher.onBackPressed()
35 | true
36 | }
37 |
38 | else -> super.onOptionsItemSelected(item)
39 | }
40 |
41 | @RequiresApi(Build.VERSION_CODES.N)
42 | override fun attachBaseContext(newBase: Context?) {
43 | super.attachBaseContext(MyContextWrapper.wrap(newBase ?: return, SettingsManager.getLocale()))
44 | }
45 |
46 | /**
47 | * Adds a custom divider to a RecyclerView.
48 | *
49 | * @param recyclerView The target RecyclerView to which the divider will be added.
50 | * @param context The context used to access resources.
51 | * @param drawableResId The resource ID of the drawable to be used as the divider.
52 | * @param orientation The orientation of the divider (DividerItemDecoration.VERTICAL or DividerItemDecoration.HORIZONTAL).
53 | */
54 | fun addCustomDividerToRecyclerView(recyclerView: RecyclerView, context: Context?, drawableResId: Int, orientation: Int = DividerItemDecoration.VERTICAL) {
55 | // Get the drawable from resources
56 | val drawable = ContextCompat.getDrawable(context!!, drawableResId)
57 | requireNotNull(drawable) { "Drawable resource not found" }
58 |
59 | // Create a DividerItemDecoration with the specified orientation
60 | val dividerItemDecoration = CustomDividerItemDecoration(drawable, orientation)
61 |
62 | // Add the divider to the RecyclerView
63 | recyclerView.addItemDecoration(dividerItemDecoration)
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/V2rayNG/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/SocksFmt.kt:
--------------------------------------------------------------------------------
1 | package com.v2ray.ang.fmt
2 |
3 | import com.v2ray.ang.dto.EConfigType
4 | import com.v2ray.ang.dto.ProfileItem
5 | import com.v2ray.ang.dto.V2rayConfig.OutboundBean
6 | import com.v2ray.ang.extension.idnHost
7 | import com.v2ray.ang.extension.isNotNullEmpty
8 | import com.v2ray.ang.handler.V2rayConfigManager
9 | import com.v2ray.ang.util.Utils
10 | import java.net.URI
11 |
12 | object SocksFmt : FmtBase() {
13 | /**
14 | * Parses a Socks URI string into a ProfileItem object.
15 | *
16 | * @param str the Socks URI string to parse
17 | * @return the parsed ProfileItem object, or null if parsing fails
18 | */
19 | fun parse(str: String): ProfileItem? {
20 | val config = ProfileItem.create(EConfigType.SOCKS)
21 |
22 | val uri = URI(Utils.fixIllegalUrl(str))
23 | if (uri.idnHost.isEmpty()) return null
24 | if (uri.port <= 0) return null
25 |
26 | config.remarks = Utils.urlDecode(uri.fragment.orEmpty()).let { if (it.isEmpty()) "none" else it }
27 | config.server = uri.idnHost
28 | config.serverPort = uri.port.toString()
29 |
30 | if (uri.userInfo?.isEmpty() == false) {
31 | val result = Utils.decode(uri.userInfo).split(":", limit = 2)
32 | if (result.count() == 2) {
33 | config.username = result.first()
34 | config.password = result.last()
35 | }
36 | }
37 |
38 | return config
39 | }
40 |
41 | /**
42 | * Converts a ProfileItem object to a URI string.
43 | *
44 | * @param config the ProfileItem object to convert
45 | * @return the converted URI string
46 | */
47 | fun toUri(config: ProfileItem): String {
48 | val pw =
49 | if (config.username.isNotNullEmpty())
50 | "${config.username}:${config.password}"
51 | else
52 | ":"
53 |
54 | return toUri(config, Utils.encode(pw, true), null)
55 | }
56 |
57 | /**
58 | * Converts a ProfileItem object to an OutboundBean object.
59 | *
60 | * @param profileItem the ProfileItem object to convert
61 | * @return the converted OutboundBean object, or null if conversion fails
62 | */
63 | fun toOutbound(profileItem: ProfileItem): OutboundBean? {
64 | val outboundBean = V2rayConfigManager.createInitOutbound(EConfigType.SOCKS)
65 |
66 | outboundBean?.settings?.servers?.first()?.let { server ->
67 | server.address = getServerAddress(profileItem)
68 | server.port = profileItem.serverPort.orEmpty().toInt()
69 | if (profileItem.username.isNotNullEmpty()) {
70 | val socksUsersBean = OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean()
71 | socksUsersBean.user = profileItem.username.orEmpty()
72 | socksUsersBean.pass = profileItem.password.orEmpty()
73 | server.users = listOf(socksUsersBean)
74 | }
75 | }
76 |
77 | return outboundBean
78 | }
79 | }
--------------------------------------------------------------------------------