Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ba7721091 | |||
| 98bb9235aa | |||
| 855b17c2ee | |||
| 3d946b66ab | |||
| 81baa8ccf6 | |||
| 3a944a7aeb | |||
| 37723c3fca |
@@ -139,3 +139,4 @@ app.*.symbols
|
|||||||
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||||
!/dev/ci/**/Gemfile.lock
|
!/dev/ci/**/Gemfile.lock
|
||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
|
/Android-kotlin-Code/core/.cxx
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
- Android 7.0+ (recommend)
|
- Android 7.0+ (recommend)
|
||||||
- `armeabi-v7a` , `arm64-v8a`, `x86` or `x86_64` Architecture
|
- `armeabi-v7a` , `arm64-v8a`, `x86` or `x86_64` Architecture
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Build
|
### Build
|
||||||
|
|
||||||
1. Update submodules
|
1. Update submodules
|
||||||
@@ -67,6 +69,15 @@ Please read 服务器配置+Kotlin-Android 文档教程.docx
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
```
|
||||||
|
测试的 URL 配置地址: https://vungles.com/api/test/config
|
||||||
|
测试账号:
|
||||||
|
binance3980@gmail.com
|
||||||
|
Tinkl123
|
||||||
|
```
|
||||||
|
|
||||||
上面字段的意思:
|
上面字段的意思:
|
||||||
以下是您提供的 JSON 配置文件的字段解释:
|
以下是您提供的 JSON 配置文件的字段解释:
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ android {
|
|||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
|
// 设置cmake版本
|
||||||
|
// version = "3.31.6" //macos下不需要指定 cmake 的版本信息
|
||||||
path = file("src/main/cpp/CMakeLists.txt")
|
path = file("src/main/cpp/CMakeLists.txt")
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,35 @@
|
|||||||
cmake_minimum_required(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
|
||||||
# 获取git hash
|
# 获取git hash
|
||||||
|
|
||||||
message(STATUS "CMAKE_CURRENT_SOURCE_DIR= ${CMAKE_CURRENT_SOURCE_DIR}")
|
message(STATUS "CMAKE_CURRENT_SOURCE_DIR= ${CMAKE_CURRENT_SOURCE_DIR}")
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND git submodule foreach git log -1 --format=%H
|
COMMAND git submodule foreach git log -1 --format=%H
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
OUTPUT_VARIABLE COMMIT_HASH
|
OUTPUT_VARIABLE COMMIT_HASH
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 判断是否获取到了git hash值(规避如因项目目录非git repository等原因产生的获取异常而中断build)
|
||||||
|
string(LENGTH ${COMMIT_HASH} OUTPUT_VARIABLE COMMIT_HASH_LENGTH)
|
||||||
|
string(COMPARE LESS ${COMMIT_HASH_LENGTH} "40" OUTPUT_VARIABLE GIT_HASH_COMPARISON_RESULT)
|
||||||
|
if(NOT ${GIT_HASH_COMPARISON_RESULT})
|
||||||
string(REPLACE "\n" ";" COMMIT_HASH "${COMMIT_HASH}")
|
string(REPLACE "\n" ";" COMMIT_HASH "${COMMIT_HASH}")
|
||||||
list(GET COMMIT_HASH 1 COMMIT_HASH)
|
list(GET COMMIT_HASH 1 COMMIT_HASH)
|
||||||
string (REGEX REPLACE "[\n\t\r]" "" COMMIT_HASH ${COMMIT_HASH})
|
string (REGEX REPLACE "[\n\t\r]" "" COMMIT_HASH ${COMMIT_HASH})
|
||||||
string(SUBSTRING ${COMMIT_HASH} 0 7 COMMIT_HASH)
|
#string(SUBSTRING ${COMMIT_HASH} 0 7 COMMIT_HASH)
|
||||||
message(STATUS "git hash= ${COMMIT_HASH}")
|
message(STATUS "git hash= ${COMMIT_HASH}")
|
||||||
|
|
||||||
# 获取分支名称
|
# 获取git分支名称
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND git submodule foreach git branch -r --contains ${COMMIT_HASH}
|
COMMAND git submodule foreach git branch -r --contains ${COMMIT_HASH}
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
OUTPUT_VARIABLE CURRENT_BRANCH
|
OUTPUT_VARIABLE CURRENT_BRANCH
|
||||||
)
|
)
|
||||||
|
|
||||||
string(REPLACE "\n" ";" CURRENT_BRANCH "${CURRENT_BRANCH}")
|
string(REPLACE "\n" ";" CURRENT_BRANCH "${CURRENT_BRANCH}")
|
||||||
list(GET CURRENT_BRANCH 1 CURRENT_BRANCH)
|
list(GET CURRENT_BRANCH 1 CURRENT_BRANCH)
|
||||||
string (REGEX REPLACE "origin/" "" CURRENT_BRANCH ${CURRENT_BRANCH} "")
|
string (REGEX REPLACE "origin/" "" CURRENT_BRANCH ${CURRENT_BRANCH} "")
|
||||||
string (REGEX REPLACE "[\n\t\r]" "" CURRENT_BRANCH ${CURRENT_BRANCH} "")
|
string (REGEX REPLACE "[\n\t\r]" "" CURRENT_BRANCH ${CURRENT_BRANCH} "")
|
||||||
#string(SUBSTRING ${CURRENT_BRANCH} 0 8 CURRENT_BRANCH)
|
string(SUBSTRING ${CURRENT_BRANCH} 0 8 CURRENT_BRANCH)
|
||||||
message(STATUS "git current branch = ${CURRENT_BRANCH}")
|
message(STATUS "git current branch = ${CURRENT_BRANCH}")
|
||||||
|
|
||||||
# 获取生成时间
|
# 获取生成时间
|
||||||
@@ -38,10 +43,15 @@ message(STATUS "version info = ${GIT_VERSION}")
|
|||||||
|
|
||||||
# 去除空格
|
# 去除空格
|
||||||
string(REGEX REPLACE "[ ]+" "" GIT_VERSION "${GIT_VERSION}")
|
string(REGEX REPLACE "[ ]+" "" GIT_VERSION "${GIT_VERSION}")
|
||||||
|
else()
|
||||||
|
message(AUTHOR_WARNING "Unable to get git hash, but build will continue.")
|
||||||
|
|
||||||
|
endif()
|
||||||
|
|
||||||
# 保存变量到文件
|
# 保存变量到文件
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/version.h @ONLY)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_SOURCE_DIR}/version.h @ONLY)
|
||||||
|
|
||||||
|
#设置项目名称与语言类别(必)
|
||||||
project(clash-bridge C)
|
project(clash-bridge C)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* 当前编译core版本号
|
* 当前编译core版本号
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define GIT_VERSION _920e728_241219
|
#define GIT_VERSION
|
||||||
#define make_Str(x) #x
|
#define make_Str(x) #x
|
||||||
#define make_String(x) make_Str(x)
|
#define make_String(x) make_Str(x)
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,6 @@ dependencies {
|
|||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
|
|
||||||
implementation("com.github.bumptech.glide:glide:4.16.0")
|
implementation("com.github.bumptech.glide:glide:4.16.0")
|
||||||
annotationProcessor("com.github.bumptech.glide:compiler:4.15.1")
|
kapt("com.github.bumptech.glide:compiler:4.15.1")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+47
-51
@@ -5,7 +5,7 @@ import android.content.res.Configuration
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.WindowManager
|
// import android.view.WindowManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
@@ -20,16 +20,16 @@ import com.github.kr328.clash.design.adapter.PlanDataAdapter
|
|||||||
import com.github.kr328.clash.design.adapter.TicketDetailAdapter
|
import com.github.kr328.clash.design.adapter.TicketDetailAdapter
|
||||||
import com.github.kr328.clash.design.adapter.TicketsDataAdapter
|
import com.github.kr328.clash.design.adapter.TicketsDataAdapter
|
||||||
import com.github.kr328.clash.design.databinding.ActivityBaseListBinding
|
import com.github.kr328.clash.design.databinding.ActivityBaseListBinding
|
||||||
import com.github.kr328.clash.design.databinding.DesignSettingsCommonBinding
|
// import com.github.kr328.clash.design.databinding.DesignSettingsCommonBinding
|
||||||
import com.github.kr328.clash.design.ui.DayNight
|
import com.github.kr328.clash.design.ui.DayNight
|
||||||
import com.github.kr328.clash.design.ui.Surface
|
import com.github.kr328.clash.design.ui.Surface
|
||||||
import com.github.kr328.clash.design.util.applyFrom
|
import com.github.kr328.clash.design.util.applyFrom
|
||||||
import com.github.kr328.clash.design.util.layoutInflater
|
// import com.github.kr328.clash.design.util.layoutInflater
|
||||||
import com.github.kr328.clash.design.util.resolveThemedBoolean
|
import com.github.kr328.clash.design.util.resolveThemedBoolean
|
||||||
import com.github.kr328.clash.design.util.resolveThemedColor
|
import com.github.kr328.clash.design.util.resolveThemedColor
|
||||||
import com.github.kr328.clash.design.util.root
|
import com.github.kr328.clash.design.util.root
|
||||||
import com.github.kr328.clash.design.util.setOnInsertsChangedListener
|
import com.github.kr328.clash.design.util.setOnInsertsChangedListener
|
||||||
import com.github.kr328.clash.design.view.ActivityBarLayout
|
// import com.github.kr328.clash.design.view.ActivityBarLayout
|
||||||
import com.github.kr328.clash.network.OrderData
|
import com.github.kr328.clash.network.OrderData
|
||||||
import com.github.kr328.clash.network.PlanData
|
import com.github.kr328.clash.network.PlanData
|
||||||
import com.github.kr328.clash.network.TicketMessage
|
import com.github.kr328.clash.network.TicketMessage
|
||||||
@@ -190,65 +190,61 @@ abstract class BaseListActivity<T> : AppCompatActivity() {
|
|||||||
this.dayNight = DayNight.Night //dayNight
|
this.dayNight = DayNight.Night //dayNight
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化列表数据。
|
||||||
|
* 如果数据为空,显示提示信息;否则,在UI线程上更新适配器数据。
|
||||||
|
*
|
||||||
|
* @param data 要显示的数据列表,可能为null
|
||||||
|
*/
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
private fun initList(data: List<T>?) {
|
private fun initList(data: List<T>?) {
|
||||||
|
|
||||||
if (data.isNullOrEmpty()) {
|
if (data.isNullOrEmpty()) {
|
||||||
|
showEmptyDataToast()
|
||||||
|
} else {
|
||||||
|
updateAdapterDataOnUiThread(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在UI线程上显示"没有更多数据"的Toast提示。
|
||||||
|
*/
|
||||||
|
private fun showEmptyDataToast() {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
Toast.makeText(this, "没有更多数据", Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, "没有更多数据", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
// 更新列表
|
|
||||||
|
/**
|
||||||
|
* 在UI线程上更新适配器数据。
|
||||||
|
* 这个方法作为一个中间层,避免在runOnUiThread的lambda中直接捕获外部变量。
|
||||||
|
*
|
||||||
|
* @param data 要更新到适配器的数据列表
|
||||||
|
*/
|
||||||
|
private fun updateAdapterDataOnUiThread(data: List<T>) {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
// 更新适配器的数据
|
doUpdateAdapterData(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行实际的适配器数据更新操作。
|
||||||
|
* 根据适配器类型,将数据设置到相应的适配器中,并通知数据集变化。
|
||||||
|
*
|
||||||
|
* @param data 要更新到适配器的数据列表
|
||||||
|
*/
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
private fun doUpdateAdapterData(data: List<T>) {
|
||||||
when (adapter) {
|
when (adapter) {
|
||||||
is PlanDataAdapter -> {
|
is PlanDataAdapter -> (adapter as PlanDataAdapter).setData(data as List<PlanData>)
|
||||||
// 使用 PlanDataAdapter 类型进行操作
|
is OrdersDataAdapter -> (adapter as OrdersDataAdapter).setData(data as List<OrderData>)
|
||||||
val planDataAdapter = adapter as PlanDataAdapter
|
is TicketsDataAdapter -> (adapter as TicketsDataAdapter).setData(data as List<TicketsData>)
|
||||||
// 执行 PlanDataAdapter 的操作
|
is TicketDetailAdapter -> (adapter as TicketDetailAdapter).setData(data as List<TicketMessage>)
|
||||||
planDataAdapter.setData(data as List<PlanData>)
|
else -> println("Unknown adapter type.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
is OrdersDataAdapter -> {
|
|
||||||
// 使用 PlanDataAdapter 类型进行操作
|
|
||||||
val ordersDataAdapter = adapter as OrdersDataAdapter
|
|
||||||
// 执行 PlanDataAdapter 的操作
|
|
||||||
ordersDataAdapter.setData(data as List<OrderData>)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
is TicketsDataAdapter->{
|
|
||||||
val ordersDataAdapter = adapter as TicketsDataAdapter
|
|
||||||
ordersDataAdapter.setData(data as List<TicketsData>)
|
|
||||||
}
|
|
||||||
|
|
||||||
is TicketDetailAdapter ->{
|
|
||||||
|
|
||||||
val ordersDataAdapter = adapter as TicketDetailAdapter
|
|
||||||
ordersDataAdapter.setData(data as List<TicketMessage>)
|
|
||||||
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
// 处理不匹配的适配器类型
|
|
||||||
println( "Unknown adapter type.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val subscriptionAdapter = adapter as? PlanDataAdapter
|
|
||||||
|
|
||||||
if ( subscriptionAdapter != null ){
|
|
||||||
subscriptionAdapter.setData(data as List<PlanData>) // 假设你实现了一个 setData 方法来更新适配器的数据
|
|
||||||
}
|
|
||||||
|
|
||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
private fun updateList(data: List<T>?) {
|
private fun updateList(data: List<T>?) {
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@ object ApiClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
object ApiClientConfig {
|
object ApiClientConfig {
|
||||||
private const val ConfigURL = "https://api.xxxx.com/api/" // BASE 请求配置文件地址 见说明文档
|
private const val ConfigURL = "https://vungles.com/api/test/" // BASE 请求配置文件地址 见说明文档
|
||||||
//const val ConfigNodeURL = "https://api.xxxx.com/api/parseyamlclash.php?target=clashmeta&url=" //通过香港服务器转接一次Clash转化的订阅地址
|
//const val ConfigNodeURL = "https://api.xxxx.com/api/parseyamlclash.php?target=clashmeta&url=" //通过香港服务器转接一次Clash转化的订阅地址
|
||||||
const val ConfigNodeURL = ""
|
const val ConfigNodeURL = ""
|
||||||
|
|
||||||
|
|||||||
@@ -194,8 +194,8 @@
|
|||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="0dp"
|
||||||
android:layout_marginHorizontal="16dp"/>
|
android:layout_marginHorizontal="0dp"/>
|
||||||
</androidx.cardview.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
<!-- TabLayout作为页指示器 -->
|
<!-- TabLayout作为页指示器 -->
|
||||||
<com.google.android.material.tabs.TabLayout
|
<com.google.android.material.tabs.TabLayout
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||||
# Specifies the JVM arguments used for the daemon process.
|
# Specifies the JVM arguments used for the daemon process.
|
||||||
# The setting is particularly useful for tweaking memory settings.
|
# The setting is particularly useful for tweaking memory settings.
|
||||||
org.gradle.jvmargs=-Xmx4g -XX:+UseZGC -Dfile.encoding=UTF-8
|
#org.gradle.jvmargs=-Xmx4g -XX:+UseZGC -Dfile.encoding=UTF-8
|
||||||
|
org.gradle.jvmargs=-Xmx4g -XX:+UseG1GC
|
||||||
# When configured, Gradle will run in incubating parallel mode.
|
# When configured, Gradle will run in incubating parallel mode.
|
||||||
# This option should only be used with decoupled projects. More details, visit
|
# This option should only be used with decoupled projects. More details, visit
|
||||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||||
|
|||||||
@@ -4,5 +4,7 @@
|
|||||||
# Location of the SDK. This is only used by Gradle.
|
# Location of the SDK. This is only used by Gradle.
|
||||||
# For customization when using a Version Control System, please read the
|
# For customization when using a Version Control System, please read the
|
||||||
# header note.
|
# header note.
|
||||||
#Wed Dec 11 11:56:02 CST 2024
|
#Fri Jul 18 13:36:50 CST 2025
|
||||||
sdk.dir=/Volumes/TOSHIBA/androidstudio/sdk
|
#cmake.dir=/Volumes/WD/androidstudio/sdk/cmake/3.22.1
|
||||||
|
# C\:\\Users\\Administrator\\AppData\\Local\\Android\\Sdk\\cmake\\3.31.6
|
||||||
|
sdk.dir=/Volumes/WD/androidstudio/sdk
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ dependencyResolutionManagement {
|
|||||||
library("androidx-activity", "androidx.activity:activity:$activity")
|
library("androidx-activity", "androidx.activity:activity:$activity")
|
||||||
library("androidx-fragment", "androidx.fragment:fragment:$fragment")
|
library("androidx-fragment", "androidx.fragment:fragment:$fragment")
|
||||||
library("androidx-appcompat", "androidx.appcompat:appcompat:$appcompat")
|
library("androidx-appcompat", "androidx.appcompat:appcompat:$appcompat")
|
||||||
library("androidx-appcompat", "androidx.appcompat:appcompat-resources:$appcompatres")
|
library("androidx-appcompat-resources", "androidx.appcompat:appcompat-resources:$appcompatres")
|
||||||
library("androidx-coordinator", "androidx.coordinatorlayout:coordinatorlayout:$coordinator")
|
library("androidx-coordinator", "androidx.coordinatorlayout:coordinatorlayout:$coordinator")
|
||||||
library("androidx-recyclerview", "androidx.recyclerview:recyclerview:$recyclerview")
|
library("androidx-recyclerview", "androidx.recyclerview:recyclerview:$recyclerview")
|
||||||
library("androidx-viewpager", "androidx.viewpager2:viewpager2:$viewpager")
|
library("androidx-viewpager", "androidx.viewpager2:viewpager2:$viewpager")
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
@@ -3,6 +3,7 @@
|
|||||||
The original UUVPN was built using Flutter, which led to increasing challenges in maintaining the codebase due to performance issues, subpar UI quality, and a lack of stability. To address these concerns, UUVPN has been completely redeveloped for native performance on iOS/macOS and Android, utilizing the powerful SINGBOX and CLASH cores.
|
The original UUVPN was built using Flutter, which led to increasing challenges in maintaining the codebase due to performance issues, subpar UI quality, and a lack of stability. To address these concerns, UUVPN has been completely redeveloped for native performance on iOS/macOS and Android, utilizing the powerful SINGBOX and CLASH cores.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
@@ -18,6 +19,9 @@ The original UUVPN was built using Flutter, which led to increasing challenges i
|
|||||||
- V2Board Compatibility: Fully supports V2Board protocols, offering smooth integration for backend management.
|
- V2Board Compatibility: Fully supports V2Board protocols, offering smooth integration for backend management.
|
||||||
- Refined UI: A completely redesigned interface with a modern look and feel, providing a user-friendly experience.
|
- Refined UI: A completely redesigned interface with a modern look and feel, providing a user-friendly experience.
|
||||||
|
|
||||||
|
### Server :
|
||||||
|
- https://github.com/cedar2025/Xboard
|
||||||
|
|
||||||
|
|
||||||
### Download iOS and android
|
### Download iOS and android
|
||||||
- [iOS TestFlight (Beta)](https://t.me/dcgzeus)
|
- [iOS TestFlight (Beta)](https://t.me/dcgzeus)
|
||||||
|
|||||||
Reference in New Issue
Block a user