93 Commits

Author SHA1 Message Date
admin da527b57af Fix: 真正修复双启动页 - 移除 windowBackground 覆盖,让系统启动窗口独占
Build Debug / Build Android Debug APK (push) Successful in 4m14s
Build iOS / Build iOS IPA (push) Successful in 11m46s
Build UUVPN / Build Android APK (push) Has been skipped
Build UUVPN / Build iOS IPA (push) Has been skipped
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
之前同时设置 windowBackground 和 windowSplashScreenAnimatedIcon 导致两个画面都显示 appstore.png,仍可见两个启动页。现在 SplashTheme 不再覆盖 windowBackground,仅由系统启动窗口显示 appstore.png,Activity 启动后直接进入主界面。
2026-07-07 13:32:05 +08:00
admin 307f3c454a Fix: 修复双启动页问题 - 系统启动窗口显示 appstore.png
Build Debug / Build Android Debug APK (push) Successful in 5m19s
Build iOS / Build iOS IPA (push) Successful in 12m21s
Build UUVPN / Build Android APK (push) Has been skipped
Build UUVPN / Build iOS IPA (push) Has been skipped
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
- 创建 SplashTheme,windowBackground 设为 appstore.png 居中白底

- 创建 values-v31/themes.xml 适配 Android 12+ Splash Screen API

- MainActivity 应用 SplashTheme,BaseActivity.onCreate 切换回 BootstrapTheme

- 移除 MainActivity 中 ActivitySplashBinding 第二启动页
2026-07-07 13:22:15 +08:00
admin b3057a72bb Fix: Use correct Docker-OSX startup with KVM and SSH
Build Debug / Build Android Debug APK (push) Successful in 5m47s
Build iOS / Build iOS IPA (push) Successful in 11m47s
Build UUVPN / Build Android APK (push) Has been skipped
Build UUVPN / Build iOS IPA (push) Has been skipped
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-05 12:07:24 +08:00
admin 7a95af02b9 Update: Improve Docker-OSX handling and add VM status check
Build Debug / Build Android Debug APK (push) Successful in 3m54s
Build iOS / Build iOS IPA (push) Successful in 3m57s
Build UUVPN / Build Android APK (push) Has been skipped
Build UUVPN / Build iOS IPA (push) Has been skipped
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-05 04:38:46 +08:00
admin 161d5dbcaf Fix: Use correct Docker-OSX image tag (latest instead of sonoma)
Build Debug / Build Android Debug APK (push) Successful in 4m1s
Build iOS / Build iOS IPA (push) Successful in 2m47s
Build UUVPN / Build Android APK (push) Has been skipped
Build UUVPN / Build iOS IPA (push) Has been skipped
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-05 04:05:43 +08:00
admin 23b8260d87 Trigger rebuild after LFS push
Build Debug / Build Android Debug APK (push) Successful in 4m27s
Build iOS / Build iOS IPA (push) Successful in 1m46s
Build UUVPN / Build Android APK (push) Has been skipped
Build UUVPN / Build iOS IPA (push) Has been skipped
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-05 03:48:45 +08:00
admin 9cce230f1f Fix: Correct Libbox.xcframework path (iOS-SwiftUI-Code/Libbox.xcframework, not uuvpn/Libbox.xcframework)
Build Debug / Build Android Debug APK (push) Successful in 4m9s
Build iOS / Build iOS IPA (push) Successful in 1m47s
Build UUVPN / Build Android APK (push) Has been skipped
Build UUVPN / Build iOS IPA (push) Has been skipped
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-05 03:35:34 +08:00
admin 3185a0c66d Fix: Remove --verbose flag, add fallback LFS fetch/checkout, improve directory checking
Build Debug / Build Android Debug APK (push) Successful in 4m3s
Build iOS / Build iOS IPA (push) Successful in 1m47s
Build UUVPN / Build Android APK (push) Has been skipped
Build UUVPN / Build iOS IPA (push) Has been skipped
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-05 03:26:11 +08:00
admin 759fdccc96 Fix: Use hardcoded token for LFS authentication and add verbose output
Build Debug / Build Android Debug APK (push) Successful in 4m17s
Build iOS / Build iOS IPA (push) Successful in 33s
Build UUVPN / Build Android APK (push) Has been skipped
Build UUVPN / Build iOS IPA (push) Has been skipped
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-05 03:14:00 +08:00
admin 470c2eca39 Fix: LFS check should not fail the build
Build Debug / Build Android Debug APK (push) Successful in 5m10s
Build iOS / Build iOS IPA (push) Successful in 1m10s
Build UUVPN / Build Android APK (push) Has been skipped
Build UUVPN / Build iOS IPA (push) Has been skipped
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-05 03:11:08 +08:00
admin 52ac80a037 Fix: Remove qemu dependency, use docker directly for Alpine Linux
Build Debug / Build Android Debug APK (push) Successful in 5m45s
Build iOS / Build iOS IPA (push) Failing after 1m11s
Build UUVPN / Build Android APK (push) Has been skipped
Build UUVPN / Build iOS IPA (push) Has been skipped
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-05 02:44:21 +08:00
admin 4f3bcf4e52 Split workflows: Create build-ios.yml and disable Android build in main workflow
Build Debug / Build Android Debug APK (push) Failing after 15m5s
Build iOS / Build iOS IPA (push) Failing after 2s
Build UUVPN / Build Android APK (push) Has been skipped
Build UUVPN / Build iOS IPA (push) Has been skipped
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-04 21:13:48 +08:00
admin 8b4ea9646d Fix: Use apk for Alpine Linux in iOS build dependencies
Build Debug / Build Android Debug APK (push) Successful in 3m42s
Build UUVPN / Build Android APK (push) Failing after 2m59s
Build UUVPN / Build iOS IPA (push) Failing after 1s
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-04 21:01:21 +08:00
admin a911f25a28 WIP: Add real iOS build steps with Docker-OSX and LFS check
Build Debug / Build Android Debug APK (push) Successful in 4m24s
Build UUVPN / Build Android APK (push) Successful in 3m8s
Build UUVPN / Build iOS IPA (push) Failing after 1s
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-04 20:37:32 +08:00
admin 7b500dde68 Fix: Remove cleanup and simplify notification steps for Gitea act compatibility
Build Debug / Build Android Debug APK (push) Successful in 4m23s
Build UUVPN / Build Android APK (push) Successful in 2m48s
Build UUVPN / Build iOS IPA (push) Successful in 1m9s
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-04 20:21:10 +08:00
admin dc294c2eec WIP: Add Docker-OSX test for iOS build (experimental)
Build Debug / Build Android Debug APK (push) Successful in 3m48s
Build UUVPN / Build Android APK (push) Successful in 2m39s
Build UUVPN / Build iOS IPA (push) Successful in 1m44s
Build UUVPN / Cleanup old artifacts (push) Failing after 1s
Build UUVPN / Create GitHub Release (push) Has been skipped
Build UUVPN / Send Notification (push) Successful in 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
2026-07-04 19:04:47 +08:00
admin 425dd00208 Fix: Add LFS authentication for iOS build
Build Debug / Build Android Debug APK (push) Successful in 5m53s
Build UUVPN / Build Android APK (push) Successful in 2m52s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 18:59:15 +08:00
admin 5eaa8c2b49 Fix: Replace upload-artifact action in build-debug.yml with simple file copy
Build Debug / Build Android Debug APK (push) Successful in 3m53s
Build UUVPN / Build Android APK (push) Successful in 2m48s
Build UUVPN / Cleanup old artifacts (push) Failing after 1s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 14:36:29 +08:00
admin 6bbbba0343 Fix: Replace upload-artifact action with simple file copy (Gitea act compatibility)
Build Debug / Build Android Debug APK (push) Failing after 4m58s
Build UUVPN / Build Android APK (push) Successful in 3m15s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 14:28:34 +08:00
admin 2305f3d6d3 Fix: List APK files before upload and use correct flavor output path
Build Debug / Build Android Debug APK (push) Failing after 4m40s
Build UUVPN / Build Android APK (push) Failing after 3m2s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 14:08:05 +08:00
admin e42e2dac79 Fix: Install gcompat for NDK clang on Alpine Linux and remove container config
Build Debug / Build Android Debug APK (push) Failing after 1m26s
Build UUVPN / Build Android APK (push) Failing after 9m52s
Build UUVPN / Cleanup old artifacts (push) Failing after 1s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 13:49:02 +08:00
admin c876251950 Fix: Use Ubuntu 22.04 container for Android build (glibc required for NDK clang)
Build Debug / Build Android Debug APK (push) Failing after 1m36s
Build UUVPN / Build Android APK (push) Failing after 32s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 13:35:17 +08:00
admin 04f3fadb62 Fix: Use assembleAlphaDebug/Release without app: prefix
Build Debug / Build Android Debug APK (push) Failing after 1m33s
Build UUVPN / Build Android APK (push) Failing after 1m32s
Build UUVPN / Cleanup old artifacts (push) Failing after 1s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 12:37:48 +08:00
admin fd89f2a965 Fix: Use assembleDebug/assembleRelease without flavor prefix
Build Debug / Build Android Debug APK (push) Failing after 1m30s
Build UUVPN / Build Android APK (push) Failing after 1m37s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 11:30:53 +08:00
admin 227e7e4ab9 Fix: Use correct Gradle task names (assembleAlphaDebug/Release instead of assembleMeta-Alpha)
Build Debug / Build Android Debug APK (push) Failing after 1m30s
Build UUVPN / Build Android APK (push) Failing after 1m23s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 10:47:33 +08:00
admin 7f6ca27e19 Fix: Install curl and create toolcache directory before downloading Go
Build Debug / Build Android Debug APK (push) Failing after 2m45s
Build UUVPN / Build Android APK (push) Failing after 1m21s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 10:28:53 +08:00
admin 9f17517c20 Fix: Always download Go 1.23 directly to ensure correct version for build
Build Debug / Build Android Debug APK (push) Failing after 1m10s
Build UUVPN / Build Android APK (push) Failing after 1m10s
Build UUVPN / Cleanup old artifacts (push) Failing after 1s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 10:14:29 +08:00
admin 8c1483da96 Fix: Allow LFS pull to fail for Android build (doesn't need iOS framework)
Build Debug / Build Android Debug APK (push) Failing after 5m43s
Build UUVPN / Build Android APK (push) Failing after 1m16s
Build UUVPN / Cleanup old artifacts (push) Failing after 1s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 01:01:15 +08:00
admin a3c82260f2 Fix: Enable Alpine community repository for openjdk17-jdk installation
Build Debug / Build Android Debug APK (push) Failing after 1m8s
Build UUVPN / Build Android APK (push) Failing after 1m9s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 00:58:57 +08:00
admin b94558069b Fix: Use openjdk17-jdk package name for Alpine Linux
Build Debug / Build Android Debug APK (push) Failing after 1m7s
Build UUVPN / Build Android APK (push) Failing after 1m9s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 00:53:31 +08:00
admin 849515fb0e Fix: Add Alpine Linux (apk) support for Java 17 and Go installation
Build Debug / Build Android Debug APK (push) Failing after 1m8s
Build UUVPN / Build Android APK (push) Failing after 1m9s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-04 00:47:36 +08:00
admin 4acdff0477 Fix: Use kankanjiao CI/CD style environment setup with RUNNER_TOOL_CACHE
Build Debug / Build Android Debug APK (push) Failing after 1m9s
Build UUVPN / Build Android APK (push) Failing after 1m9s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-03 21:26:51 +08:00
admin 9d266f282c Fix: Remove container, install JDK/Go directly via apt-get on ubuntu runner
Build Debug / Build Android Debug APK (push) Failing after 2s
Build UUVPN / Build Android APK (push) Failing after 1s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-03 19:07:16 +08:00
admin 5e3a8f70f7 Fix: Use 'ubuntu' runner label instead of 'ubuntu-latest'
Build Debug / Build Android Debug APK (push) Failing after 1m10s
Build UUVPN / Build Android APK (push) Failing after 1m10s
Build UUVPN / Cleanup old artifacts (push) Failing after 1s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-03 18:54:43 +08:00
admin cecdbd1b01 Fix: Install Git LFS before checkout, pull LFS files after checkout
Build Debug / Build Android Debug APK (push) Failing after 1m10s
Build UUVPN / Build Android APK (push) Failing after 1m9s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-03 18:53:40 +08:00
admin 606b3aca90 Fix: Use node:20-bullseye container for CI/CD (includes Node.js)
Build Debug / Build Android Debug APK (push) Failing after 18s
Build UUVPN / Build Android APK (push) Failing after 6s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-03 18:32:24 +08:00
admin c418800664 Fix: Install curl and sudo first, then use apt without sudo
Build Debug / Build Android Debug APK (push) Failing after 1s
Build UUVPN / Build Android APK (push) Failing after 1s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-03 18:24:55 +08:00
admin fe12e9ce33 Fix: Install Node.js and Git LFS in CI/CD workflow
Build Debug / Build Android Debug APK (push) Failing after 2s
Build UUVPN / Build Android APK (push) Failing after 1s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-03 18:20:06 +08:00
admin ebf60d4ce6 Fix: Remove lfs option from checkout and use manual Git LFS setup
Build Debug / Build Android Debug APK (push) Failing after 2s
Build UUVPN / Build Android APK (push) Failing after 2s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-03 18:11:10 +08:00
admin 7773749c53 Configure Git LFS for Libbox.xcframework and update workflows
Build Debug / Build Android Debug APK (push) Failing after 1m31s
Build UUVPN / Build Android APK (push) Failing after 2s
Build UUVPN / Cleanup old artifacts (push) Failing after 0s
Build Debug / Build iOS Debug App (push) Has been cancelled
Build UUVPN / Build iOS IPA (push) Has been cancelled
Build UUVPN / Create GitHub Release (push) Has been cancelled
Build UUVPN / Upload to TestFlight (push) Has been cancelled
Build UUVPN / Send Notification (push) Has been cancelled
2026-07-03 16:54:36 +08:00
admin 8eb287d32d Configure Git LFS for Libbox.xcframework binary files 2026-07-03 16:53:49 +08:00
admin aa1dfe0e4b Fix: GitHub Actions workflow syntax error - secrets cannot be used in if condition directly 2026-07-03 16:25:35 +08:00
admin d0b07f42de Docs: Add API field type documentation 2026-07-03 13:46:34 +08:00
admin b9fc411999 Fix: Handle sell field type inconsistency (Boolean or Int) 2026-07-03 13:44:40 +08:00
admin 56c4caeaf3 Fix: PlanData.sell should be Boolean type 2026-07-03 07:15:50 +08:00
admin 012e71cc10 Fix: Add null safety for u and d fields in MainActivity 2026-07-03 05:58:41 +08:00
admin 18f5c7093a Fix: Add null check for cached subscribe data and update SubscribeData fields 2026-07-03 05:51:03 +08:00
admin 103e7eb984 Fix: Change PlanData.sell type from Boolean to Int (API returns number) 2026-07-03 05:44:32 +08:00
admin d2131f1f9c Fix: Update PlanData to match API response (Boolean fields, add device_limit/sell) 2026-07-03 03:27:02 +08:00
admin 6ba16654b1 Fix: Retrofit baseUrl must end with / 2026-07-03 03:03:18 +08:00
admin bc19fe5ef5 Fix: Add x86/x86_64 ABI support for emulator compatibility 2026-07-03 02:40:01 +08:00
admin 7ebe87e276 Enable Android debug build and update ApiClient config URL 2026-07-03 00:59:33 +08:00
admin 14d955951e Fix: Bump swift-tools-version to 5.9 for tvOS v17 support 2026-07-02 23:05:52 +08:00
admin 621e34cc6a Fix: Use local Swift Package (LibboxLocal) with binaryTarget for Libbox.xcframework 2026-07-02 22:50:37 +08:00
admin 20b794122c Fix: Restore original modulemap format and remove SWIFT_INCLUDE_PATHS 2026-07-02 22:19:14 +08:00
admin 1cad70e0b2 CI: Add Libbox module diagnostics and improve error logging 2026-07-02 21:50:36 +08:00
admin c4c0cb3a7a Fix: Update Libbox module.modulemap with umbrella header for Xcode 26 compatibility 2026-07-02 20:16:15 +08:00
admin 0703c3aa07 Fix: Add SWIFT_INCLUDE_PATHS for Libbox module discovery 2026-07-02 20:07:50 +08:00
admin 999ee48e6b Fix: Remove bridging header and restore import Libbox - try module import approach 2026-07-02 20:01:59 +08:00
admin 61d17ccbe2 Fix: Use correct framework import syntax in bridging header 2026-07-02 19:37:49 +08:00
admin 882f74100e Fix: Remove import Libbox statements - use bridging header instead 2026-07-02 19:30:28 +08:00
admin 7e267072f3 Fix: Add HEADER_SEARCH_PATHS for Libbox headers in bridging header 2026-07-02 19:26:42 +08:00
admin 1d0acc8f38 Fix: Disable SWIFT_ENABLE_EXPLICIT_MODULES for Xcode 26 compatibility 2026-07-02 19:13:54 +08:00
admin df0aa6b9f9 Fix: Improve bridging header with conditional include 2026-07-02 19:05:23 +08:00
admin b8e2955ebe Fix: Add bridging header for Libbox.xcframework to enable Swift import 2026-07-02 18:53:45 +08:00
admin dc0c6a0e09 Fix: Use local Libbox.xcframework instead of Swift Package dependency 2026-07-02 18:38:53 +08:00
admin 5b044334ee Fix: Add sing-box-lib Swift Package for Libbox (solves 'No Such module Libbox' issue - see GitHub Issue #37) 2026-07-02 17:54:17 +08:00
admin 3a96b19e0c Fix: Add FRAMEWORK_SEARCH_PATHS to Library target to find Libbox.xcframework (complete fix for iOS build) 2026-07-02 16:58:33 +08:00
admin 31a8a2ba06 Fix: Add Libbox.xcframework to Library target's linked frameworks (fixes iOS build issue) 2026-07-02 16:46:59 +08:00
admin c98b5df051 Docs: Add detailed iOS build issue diagnosis report with root cause and fix instructions 2026-07-02 16:39:34 +08:00
admin a0e3b69ac5 Debug: Add detailed verification of Libbox.xcframework binary file before build to diagnose download issue 2026-07-02 16:31:10 +08:00
admin 5b8e815fe6 Fix: Enable Git LFS to download large Libbox.xcframework binary files (60MB) 2026-07-02 13:43:54 +08:00
admin a2523209b8 Debug: Add verification step to check if Libbox.xcframework exists before build 2026-07-02 12:42:12 +08:00
admin f374e2ce3c Fix: Use iOS Simulator OS 26.2 (26.3 not available, only 26.2, 26.4.1, 26.5) 2026-07-02 12:37:54 +08:00
admin f2ef91036a Fix: Use Xcode 26.3 (26.4 does not exist) and matching simulator OS 2026-07-02 12:28:50 +08:00
admin 4c70cd5f12 Fix: Remove invalid xcodebuild options for Xcode 26.4 2026-07-02 12:17:51 +08:00
admin 63ce949edc Fix: Remove continue-on-error to properly detect build failures 2026-07-02 12:00:03 +08:00
admin 2080366d66 Fix: Add detailed build result checking and DerivedData inspection 2026-07-02 11:54:50 +08:00
admin 15e607dfc6 Fix: Disable Android build temporarily, fix iOS simulator app path detection 2026-07-02 11:43:50 +08:00
admin 7ba0cec04f Fix: Improve iOS app file search with multiple fallback methods and verbose output 2026-07-02 11:39:28 +08:00
admin 73a95e2c7a Fix: Use Xcode 26.4 and iPhone Air simulator matching available versions 2026-07-02 11:10:58 +08:00
admin 26c1cd4bcb Fix: Switch to macos-latest runner with Xcode 16.4 for better availability 2026-07-02 10:56:44 +08:00
admin ec225e72bd Fix: Add skip validation flags and list schemes for better error diagnosis 2026-07-02 10:37:27 +08:00
admin eca0f8a08f Fix: Use macOS-13 runner with Xcode 15.0 for better compatibility 2026-07-02 10:27:59 +08:00
admin 1b78c4b61c Fix: Use specific iOS simulator device (iPhone Air, OS 26.5) 2026-07-02 10:13:50 +08:00
admin 4fa894245c Fix: Use latest-stable Xcode and generic iOS simulator 2026-07-02 09:49:50 +08:00
admin 97f69f1a9c Fix: Use iPhone 16 simulator for Xcode 16 2026-07-02 01:47:04 +08:00
admin 839a1627fd Fix: Update Xcode to 16.0 (minimum available on GitHub Actions) 2026-07-02 01:36:33 +08:00
admin 91b6620347 Fix: Use correct Gradle task assembleDebug 2026-07-02 01:00:46 +08:00
admin e954bcc430 Fix: Update JDK to 17 and Xcode to 15.2 2026-07-02 00:00:31 +08:00
admin 51a30184d7 Fix: Update Xcode version to 15.2 2026-07-01 23:59:34 +08:00
admin 267865c61d Add simplified debug build workflow 2026-07-01 23:52:41 +08:00
admin c590135d68 Add CI/CD configuration and API documentation 2026-07-01 21:40:53 +08:00
3631 changed files with 623343 additions and 71544 deletions
+10
View File
@@ -0,0 +1,10 @@
# Git LFS 配置 - iOS 大文件
# Libbox.xcframework 二进制文件
*.xcframework/**/*.framework/Versions/A/Libbox filter=lfs diff=lfs merge=lfs -text
*.xcframework/**/*.framework/Libbox filter=lfs diff=lfs merge=lfs -text
# 其他大型二进制文件
*.a filter=lfs diff=lfs merge=lfs -text
*.dylib filter=lfs diff=lfs merge=lfs -text
*.framework/Versions/A/* filter=lfs diff=lfs merge=lfs -text
iOS-SwiftUI-Code/Libbox.xcframework/**/*.framework/Versions/A/Libbox filter=lfs diff=lfs merge=lfs -text
iOS-SwiftUI-Code/Libbox.xcframework/**/*.framework/Libbox filter=lfs diff=lfs merge=lfs -text
+38
View File
@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
+254
View File
@@ -0,0 +1,254 @@
# CI/CD 配置检查报告
## ✅ 检查完成
已完成对 GitHub Actions CI/CD 配置文件的全面检查。
---
## 📁 已创建的文件
| 文件 | 路径 | 说明 |
|------|------|------|
| build.yml | `.github/workflows/build.yml` | 主工作流配置文件 |
| README.md | `.github/workflows/README.md` | 详细配置说明文档 |
| QUICKSTART.md | `.github/workflows/QUICKSTART.md` | 快速入门指南 |
| setup-secrets.sh | `.github/workflows/setup-secrets.sh` | macOS/Linux 配置脚本 |
| setup-secrets.ps1 | `.github/workflows/setup-secrets.ps1` | Windows 配置脚本 |
---
## 🔧 已修复的问题
### 问题 1: iOS 签名身份 Secret 名称不一致
- **位置**: build.yml 第 218 行
- **问题**: 使用了 `IOS_SIGNING_identity` (小写 identity)
- **修复**: 改为 `IOS_SIGNING_IDENTITY` (全大写,符合命名规范)
### 问题 2: 条件判断过于复杂
- **位置**: 多处
- **问题**: 使用了 `startsWith(github.ref, 'refs/tags/v') || github.event.inputs.release_type == 'release'` 复杂条件
- **修复**: 简化为 `startsWith(github.ref, 'refs/tags/v')`,只在创建标签时触发 Release 构建
---
## ✅ 配置验证清单
### 工作流触发条件
- ✅ Push 到 main 分支 - 触发构建
- ✅ Push 到 develop 分支 - 触发 Debug 构建
- ✅ Pull Request - 触发 Debug 构建
- ✅ 创建版本标签 (v*) - 触发 Release 构建
- ✅ 手动触发 - 可选择构建平台和类型
### Android 构建步骤
- ✅ 检出代码和子模块
- ✅ 安装 JDK 11
- ✅ 安装 Go 1.23
- ✅ 安装 Android SDK
- ✅ 创建 local.properties
- ✅ 配置签名密钥 (Release)
- ✅ 构建 APK
- ✅ 上传构建产物
### iOS 构建步骤
- ✅ 检出代码和子模块
- ✅ 安装 Xcode 15.0
- ✅ 安装开发者证书 (Release)
- ✅ 安装 Provisioning Profile (Release)
- ✅ 解析 Swift Package 依赖
- ✅ 构建 Debug 版本 (模拟器)
- ✅ 构建并归档 Release 版本
- ✅ 导出 IPA
- ✅ 上传构建产物
### 发布步骤
- ✅ 下载 Android APK
- ✅ 下载 iOS IPA
- ✅ 创建 GitHub Release
- ✅ 上传文件到 Release
- ✅ 上传到 TestFlight (Beta 和正式版)
- ✅ 发送 Telegram 通知
- ✅ 清理旧的构建产物
---
## ⚠️ 使用前必须配置的 Secrets
### Android 构建 (发布版本必需)
| Secret 名称 | 说明 | 如何获取 |
|------------|------|---------|
| `ANDROID_KEYSTORE_BASE64` | 签名密钥库的 Base64 编码 | 使用 base64 命令转换 |
| `ANDROID_KEYSTORE_PASSWORD` | 密钥库密码 | 创建密钥库时设置 |
| `ANDROID_KEY_ALIAS` | 密钥别名 | 创建密钥库时设置 |
| `ANDROID_KEY_PASSWORD` | 密钥密码 | 创建密钥库时设置 |
**注意**: Debug 构建不需要配置这些 Secrets。
### iOS 构建 (发布版本必需)
| Secret 名称 | 说明 | 如何获取 |
|------------|------|---------|
| `IOS_CERTIFICATES_P12` | 开发者证书的 Base64 编码 | 从钥匙串导出 .p12 文件 |
| `IOS_CERTIFICATES_PASSWORD` | 证书密码 | 导出时设置 |
| `IOS_TEAM_ID` | Apple Team ID | Apple Developer 账号 |
| `IOS_SIGNING_IDENTITY` | 签名身份 | 如: "Apple Distribution: Name (TEAM_ID)" |
| `IOS_PROVISIONING_PROFILE` | Provisioning Profile 的 Base64 编码 | Apple Developer 网站下载 |
**注意**: Debug 构建不需要配置这些 Secrets。
### TestFlight 上传 (可选)
| Secret 名称 | 说明 | 如何获取 |
|------------|------|---------|
| `APP_STORE_CONNECT_ISSUER_ID` | Issuer ID | App Store Connect API Keys |
| `APP_STORE_CONNECT_API_KEY_ID` | API Key ID | 创建 API Key 时获得 |
| `APP_STORE_CONNECT_API_PRIVATE_KEY` | API 私钥 | 下载的 .p8 文件内容 |
### 通知 (可选)
| Secret 名称 | 说明 | 如何获取 |
|------------|------|---------|
| `TELEGRAM_BOT_TOKEN` | Bot Token | 通过 @BotFather 创建 |
| `TELEGRAM_CHAT_ID` | Chat ID | 邀请 Bot 后获取 |
---
## 🚀 快速开始步骤
### 1. 配置 Secrets
**Windows 用户**:
```powershell
cd D:\Code\UUVPN\.github\workflows
.\setup-secrets.ps1
```
**macOS/Linux 用户**:
```bash
cd /path/to/UUVPN/.github/workflows
chmod +x setup-secrets.sh
./setup-secrets.sh
```
### 2. 推送代码测试
```bash
# 推送到 develop 分支触发 Debug 构建
git checkout -b develop
git push origin develop
# 查看 Actions 页面确认构建成功
```
### 3. 创建发布版本
```bash
# 创建 Alpha 版本
git tag v1.0.0-alpha
git push origin v1.0.0-alpha
# 创建 Beta 版本 (会自动上传到 TestFlight)
git tag v1.0.0-beta
git push origin v1.0.0-beta
# 创建正式版本
git tag v1.0.0
git push origin v1.0.0
```
---
## 📊 预期行为
### Debug 构建 (develop 分支或 PR)
**Android**:
- ✅ 使用 Debug 签名
- ✅ 构建 `app-meta-alpha-debug.apk`
- ✅ 上传到 Artifacts (保留 30 天)
**iOS**:
- ✅ 无签名
- ✅ 构建模拟器版本
- ✅ 上传 .app 文件到 Artifacts (保留 7 天)
### Release 构建 (创建标签)
**Android**:
- ✅ 使用自定义签名密钥
- ✅ 构建 Release APK
- ✅ 重命名为 `UUVPN-Android-v1.0.0.apk`
- ✅ 上传到 GitHub Release
**iOS**:
- ✅ 使用开发者证书签名
- ✅ 导出 IPA 文件
- ✅ 重命名为 `UUVPN-iOS-v1.0.0.ipa`
- ✅ 上传到 GitHub Release
- ✅ Beta 和正式版上传到 TestFlight
---
## ⏱️ 预计构建时间
| 平台 | Debug 构建 | Release 构建 |
|------|-----------|-------------|
| Android | 5-10 分钟 | 10-15 分钟 |
| iOS | 15-20 分钟 | 20-30 分钟 |
**总计**: 完整构建约 25-45 分钟
---
## 💰 成本估算
GitHub Actions 免费账户每月 2000 分钟:
| 构建类型 | 消耗时间 | 每月可构建次数 |
|---------|---------|--------------|
| 仅 Android | ~10 分钟 | ~200 次 |
| 仅 iOS | ~200 分钟 (按 10 倍计) | ~10 次 |
| Android + iOS | ~210 分钟 | ~9 次 |
**建议**: 节省 macOS runner 时间,只在需要时构建 iOS 版本。
---
## 🎯 下一步行动
1.**配置 Secrets**
- 使用配置脚本或手动配置
- 至少配置 Debug 构建所需的 Secrets
2.**测试 Debug 构建**
- 推送代码到 develop 分支
- 确认构建成功
3.**配置发布 Secrets**
- 配置 Android 签名密钥
- 配置 iOS 证书和 Profile
4.**创建第一个版本**
- 创建标签触发 Release 构建
- 验证 GitHub Release 创建成功
5.**配置 TestFlight** (可选)
- 配置 App Store Connect API Keys
- 验证自动上传到 TestFlight
---
## 📞 获取帮助
如遇到问题,请查看:
1. [GitHub Actions 日志](https://github.com/你的用户名/UUVPN/actions)
2. [配置文档](.github/workflows/README.md)
3. [快速入门](.github/workflows/QUICKSTART.md)
4. [项目 Issues](https://github.com/nicolastinkl/UUVPN/issues)
---
**配置检查完成! 🎉**
+363
View File
@@ -0,0 +1,363 @@
# GitHub Actions CI/CD 快速入门指南
本指南帮助你快速配置和使用 UUVPN 的自动构建系统。
---
## 🚀 5 分钟快速开始
### 第一步: 准备工作
确保你已拥有:
- ✅ GitHub 账号
- ✅ UUVPN 代码仓库的写权限
- ✅ Apple Developer 账号 (iOS 构建必需)
- ✅ Android 签名密钥库 (Android Release 构建必需)
### 第二步: 安装 GitHub CLI
**Windows**:
```powershell
winget install GitHub.cli
```
**macOS**:
```bash
brew install gh
```
**Linux**:
```bash
# Debian/Ubuntu
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update
sudo apt install gh
```
### 第三步: 登录 GitHub
```bash
gh auth login
```
按照提示选择:
- GitHub.com
- HTTPS
- 使用浏览器登录或粘贴 token
### 第四步: 配置 Secrets
#### 方法一: 使用自动配置脚本 (推荐)
**Windows**:
```powershell
cd D:\Code\UUVPN\.github\workflows
.\setup-secrets.ps1
```
**macOS/Linux**:
```bash
cd /path/to/UUVPN/.github/workflows
chmod +x setup-secrets.sh
./setup-secrets.sh
```
#### 方法二: 手动配置
在 GitHub 仓库页面:
1. 进入 `Settings``Secrets and variables``Actions`
2. 点击 `New repository secret`
3. 添加所需的 secrets (详见下方)
### 第五步: 触发构建
```bash
# 推送代码触发构建
git push origin main
# 或创建标签触发发布
git tag v1.0.0-alpha
git push origin v1.0.0-alpha
```
---
## 📋 必需的 Secrets 清单
### Android 构建
| Secret | 必需 | 说明 |
|--------|------|------|
| `ANDROID_KEYSTORE_BASE64` | ✅ | 签名密钥库的 Base64 编码 |
| `ANDROID_KEYSTORE_PASSWORD` | ✅ | 密钥库密码 |
| `ANDROID_KEY_ALIAS` | ✅ | 密钥别名 |
| `ANDROID_KEY_PASSWORD` | ✅ | 密钥密码 |
### iOS 构建
| Secret | 必需 | 说明 |
|--------|------|------|
| `IOS_CERTIFICATES_P12` | ✅ | 开发者证书 Base64 编码 |
| `IOS_CERTIFICATES_PASSWORD` | ✅ | 证书密码 |
| `IOS_TEAM_ID` | ✅ | Apple Team ID |
| `IOS_SIGNING_IDENTITY` | ✅ | 签名身份 |
| `IOS_PROVISIONING_PROFILE` | ✅ | Provisioning Profile Base64 |
### TestFlight 上传 (可选)
| Secret | 必需 | 说明 |
|--------|------|------|
| `APP_STORE_CONNECT_ISSUER_ID` | ⭐ | App Store Connect Issuer ID |
| `APP_STORE_CONNECT_API_KEY_ID` | ⭐ | API Key ID |
| `APP_STORE_CONNECT_API_PRIVATE_KEY` | ⭐ | API 私钥 |
### 通知 (可选)
| Secret | 必需 | 说明 |
|--------|------|------|
| `TELEGRAM_BOT_TOKEN` | | Telegram Bot Token |
| `TELEGRAM_CHAT_ID` | | Telegram Chat ID |
---
## 🔧 详细配置步骤
### Android 签名密钥库
#### 1. 创建密钥库 (如果没有)
```bash
keytool -genkey -v \
-keystore release.keystore \
-alias uuvpn \
-keyalg RSA \
-keysize 2048 \
-validity 10000
```
按提示输入:
- 密钥库密码
- 个人信息
- 密钥密码
#### 2. 转换为 Base64
**macOS/Linux**:
```bash
base64 -i release.keystore -o release.keystore.base64
```
**Windows PowerShell**:
```powershell
[Convert]::ToBase64String([IO.File]::ReadAllBytes("release.keystore")) | Out-File release.keystore.base64
```
#### 3. 添加到 GitHub Secrets
复制 `release.keystore.base64` 文件内容,添加为 `ANDROID_KEYSTORE_BASE64` secret。
### iOS 开发者证书
#### 1. 导出证书
1. 打开 "钥匙串访问" (Keychain Access)
2. 找到 "Apple Distribution" 证书
3. 右键 → 导出
4. 选择 .p12 格式
5. 设置密码
#### 2. 转换为 Base64
**macOS**:
```bash
base64 -i certificate.p12 -o certificate.p12.base64
```
#### 3. 下载 Provisioning Profile
1. 登录 [Apple Developer](https://developer.apple.com/)
2. 进入 Certificates, Identifiers & Profiles
3. 下载对应的 Provisioning Profile
#### 4. 转换为 Base64
```bash
base64 -i uuvpn.mobileprovision -o uuvpn.mobileprovision.base64
```
### App Store Connect API Key
#### 1. 创建 API Key
1. 登录 [App Store Connect](https://appstoreconnect.apple.com/)
2. 进入 Users and Access → Keys
3. 点击 "Generate API Key"
4. 选择 "App Manager" 权限
5. 下载 .p8 文件
#### 2. 记录信息
- **Issuer ID**: 在 Keys 页面顶部
- **Key ID**: 创建后显示
- **Private Key**: 下载的 .p8 文件内容
---
## 🎯 构建类型说明
### Debug 构建
**触发条件**:
- Pull Request
- 推送到 develop 分支
**特点**:
- 无需签名配置
- 快速构建
- 不发布到 Release
### Release 构建
**触发条件**:
- 推送到 main 分支
- 创建版本标签
**特点**:
- 需要签名配置
- 自动发布到 GitHub Releases
- 可选上传到 TestFlight
### 版本标签格式
```bash
# Alpha 版本 (预发布)
git tag v1.0.0-alpha
git push origin v1.0.0-alpha
# Beta 版本 (预发布,上传 TestFlight)
git tag v1.0.0-beta
git push origin v1.0.0-beta
# 正式版本 (发布,上传 TestFlight)
git tag v1.0.0
git push origin v1.0.0
```
---
## 📥 下载构建产物
### 方法一: Actions Artifacts
1. 进入 GitHub 仓库
2. 点击 "Actions" 标签
3. 选择具体的 workflow 运行记录
4. 在页面底部 "Artifacts" 区域下载
### 方法二: GitHub Releases
1. 进入 GitHub 仓库
2. 点击 "Releases" 标签
3. 找到对应版本
4. 下载 APK 或 IPA 文件
---
## 🛠️ 故障排查
### 构建失败
1. **查看日志**:
- 进入 Actions 页面
- 点击失败的步骤
- 查看详细错误信息
2. **常见错误**:
- ❌ 签名错误: 检查 Secrets 配置
- ❌ 证书过期: 更新证书和 Profile
- ❌ 构建超时: 优化构建配置
### iOS 签名失败
检查清单:
- [ ] 证书未过期
- [ ] Provisioning Profile 包含正确 App ID
- [ ] Team ID 正确
- [ ] 证书和 Profile 匹配
### TestFlight 上传失败
检查清单:
- [ ] API Key 有 App Manager 权限
- [ ] App ID 在 App Store Connect 已创建
- [ ] 版本号未重复
---
## 💡 最佳实践
### 1. 分支策略
```
main (稳定版本)
develop (开发版本)
feature/* (功能分支)
```
### 2. 版本号规范
使用语义化版本:
- `v1.0.0-alpha`: 内部测试版本
- `v1.0.0-beta`: 公开测试版本
- `v1.0.0`: 正式发布版本
### 3. 节省构建时间
- 只在需要时构建 iOS (macOS runner 费用高)
- 使用缓存加速构建
- 手动触发选择性构建
### 4. 安全建议
- 定期更新签名密钥
- 使用最小权限原则
- 不要在代码中硬编码敏感信息
---
## 📞 获取帮助
### 文档
- [GitHub Actions 文档](https://docs.github.com/en/actions)
- [详细配置说明](./README.md)
- [API 配置指南](../../API配置指南.md)
### 支持
- 查看仓库 [Issues](https://github.com/nicolastinkl/UUVPN/issues)
- Telegram: @fastlink
---
## ✅ 配置验证清单
完成配置后,检查以下内容:
- [ ] 已安装 GitHub CLI
- [ ] 已登录 GitHub CLI
- [ ] 已配置 Android Secrets (如需 Android 构建)
- [ ] 已配置 iOS Secrets (如需 iOS 构建)
- [ ] 已配置 App Store Connect API (如需 TestFlight)
- [ ] 已推送代码触发构建
- [ ] 构建成功
- [ ] 可以下载构建产物
---
**祝你构建顺利! 🎉**
+525
View File
@@ -0,0 +1,525 @@
# GitHub Actions CI/CD 配置说明
本文档说明如何配置和使用 UUVPN 的 GitHub Actions 自动构建流程。
---
## 📋 目录
- [功能特性](#功能特性)
- [前置要求](#前置要求)
- [配置 Secrets](#配置-secrets)
- [使用方法](#使用方法)
- [构建产物](#构建产物)
- [自定义配置](#自定义配置)
- [常见问题](#常见问题)
---
## 功能特性
### ✅ 自动构建
- **Android APK**: 支持 Debug 和 Release 构建
- **iOS IPA**: 支持 Debug 和 Release 构建
- **多架构支持**: Android 支持 armeabi-v7a, arm64-v8a, x86, x86_64
### ✅ 自动发布
- **GitHub Releases**: 自动创建 Release 并上传构建产物
- **TestFlight**: 自动上传 iOS 版本到 TestFlight
- **Telegram 通知**: 构建完成后发送通知
### ✅ 触发条件
- 推送到 main/develop 分支
- 创建版本标签 (v*)
- Pull Request
- 手动触发
---
## 前置要求
### Android 构建
1. **JDK 11**: 已在 Actions 中自动配置
2. **Android SDK**: 已在 Actions 中自动配置
3. **Go 1.23**: 用于编译核心库
4. **签名密钥**: Release 构建需要签名密钥库
### iOS 构建
1. **Apple Developer 账号**: $99/年
2. **Xcode 15.0+**: 已在 Actions 中自动配置
3. **开发者证书**: .p12 格式
4. **Provisioning Profile**: .mobileprovision 文件
5. **App Store Connect API Key**: 用于上传到 TestFlight
---
## 配置 Secrets
在 GitHub 仓库中配置以下 Secrets:
### Android Secrets
| Secret 名称 | 描述 | 获取方式 |
|------------|------|---------|
| `ANDROID_KEYSTORE_BASE64` | 签名密钥库的 Base64 编码 | 见下方说明 |
| `ANDROID_KEYSTORE_PASSWORD` | 密钥库密码 | 创建密钥库时设置 |
| `ANDROID_KEY_ALIAS` | 密钥别名 | 创建密钥库时设置 |
| `ANDROID_KEY_PASSWORD` | 密钥密码 | 创建密钥库时设置 |
**生成 ANDROID_KEYSTORE_BASE64**:
```bash
# 在本地执行
keytool -genkey -v -keystore release.keystore -alias uuvpn -keyalg RSA -keysize 2048 -validity 10000
# 转换为 Base64
base64 -i release.keystore -o release.keystore.base64
# 或者使用 PowerShell
[Convert]::ToBase64String([IO.File]::ReadAllBytes("release.keystore")) | Out-File release.keystore.base64
```
### iOS Secrets
| Secret 名称 | 描述 | 获取方式 |
|------------|------|---------|
| `IOS_CERTIFICATES_P12` | 开发者证书的 Base64 编码 | 见下方说明 |
| `IOS_CERTIFICATES_PASSWORD` | 证书密码 | 导出证书时设置 |
| `IOS_SIGNING_IDENTITY` | 签名身份 | 如: "Apple Distribution: Your Name (TEAM_ID)" |
| `IOS_PROVISIONING_PROFILE` | Provisioning Profile 的 Base64 编码 | 见下方说明 |
| `IOS_TEAM_ID` | Apple Team ID | 在 Apple Developer 账号中查看 |
**生成 IOS_CERTIFICATES_P12**:
1. 在 Keychain Access 中导出证书
2. 选择 "Apple Distribution" 证书
3. 右键 → 导出 → 选择 .p12 格式
4. 设置密码
5. 转换为 Base64:
```bash
base64 -i certificate.p12 -o certificate.p12.base64
# 或者使用 PowerShell
[Convert]::ToBase64String([IO.File]::ReadAllBytes("certificate.p12")) | Out-File certificate.p12.base64
```
**生成 IOS_PROVISIONING_PROFILE**:
1. 在 Apple Developer 网站下载 Provisioning Profile
2. 转换为 Base64:
```bash
base64 -i uuvpn.mobileprovision -o uuvpn.mobileprovision.base64
# 或者使用 PowerShell
[Convert]::ToBase64String([IO.File]::ReadAllBytes("uuvpn.mobileprovision")) | Out-File uuvpn.mobileprovision.base64
```
### App Store Connect Secrets
| Secret 名称 | 描述 | 获取方式 |
|------------|------|---------|
| `APP_STORE_CONNECT_ISSUER_ID` | App Store Connect API Issuer ID | 在 App Store Connect 中创建 API Key |
| `APP_STORE_CONNECT_API_KEY_ID` | App Store Connect API Key ID | 创建 API Key 时获得 |
| `APP_STORE_CONNECT_API_PRIVATE_KEY` | App Store Connect API 私钥 | 创建 API Key 时下载的 .p8 文件内容 |
**获取方式**:
1. 登录 [App Store Connect](https://appstoreconnect.apple.com/)
2. 进入 Users and Access → Keys
3. 点击 "Generate API Key"
4. 选择 "App Manager" 权限
5. 下载 .p8 文件并保存私钥内容
### 通知 Secrets (可选)
| Secret 名称 | 描述 | 获取方式 |
|------------|------|---------|
| `TELEGRAM_BOT_TOKEN` | Telegram Bot Token | 通过 @BotFather 创建 Bot |
| `TELEGRAM_CHAT_ID` | Telegram 群组/频道 ID | 邀请 Bot 到群组后获取 |
**获取方式**:
1. 在 Telegram 中找到 @BotFather
2. 发送 `/newbot` 创建新 Bot
3. 获取 Bot Token
4. 创建群组或频道,邀请 Bot
5. 获取 Chat ID: `https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates`
---
## 使用方法
### 方法一: 自动触发
#### 推送到分支
```bash
# 推送到 main 分支,触发构建
git push origin main
# 推送到 develop 分支,触发 Debug 构建
git push origin develop
```
#### 创建版本标签
```bash
# 创建 Alpha 版本
git tag v1.0.0-alpha
git push origin v1.0.0-alpha
# 创建 Beta 版本
git tag v1.0.0-beta
git push origin v1.0.0-beta
# 创建正式版本
git tag v1.0.0
git push origin v1.0.0
```
#### Pull Request
创建 Pull Request 到 main 分支会自动触发 Debug 构建。
### 方法二: 手动触发
1. 进入 GitHub 仓库
2. 点击 "Actions" 标签
3. 选择 "Build UUVPN" workflow
4. 点击 "Run workflow"
5. 选择构建选项:
- `build_android`: 是否构建 Android APK
- `build_ios`: 是否构建 iOS IPA
- `release_type`: 构建类型 (alpha/beta/release)
6. 点击 "Run workflow" 按钮
---
## 构建产物
### Android 构建产物
构建完成后,可以在以下位置找到 APK:
1. **Actions Artifacts**: 每个 workflow 运行都会保存 APK artifact
2. **GitHub Releases**: 创建标签后,APK 会自动上传到 Release
**APK 文件命名**:
- Debug: `app-meta-alpha-debug.apk`
- Release: `UUVPN-Android-v1.0.0.apk`
### iOS 构建产物
构建完成后,可以在以下位置找到 IPA:
1. **Actions Artifacts**: 每个 workflow 运行都会保存 IPA artifact
2. **GitHub Releases**: 创建标签后,IPA 会自动上传到 Release
3. **TestFlight**: Beta 和正式版本会自动上传到 TestFlight
**IPA 文件命名**:
- Release: `UUVPN-iOS-v1.0.0.ipa`
---
## 自定义配置
### 修改 Android 构建变体
编辑 `.github/workflows/build.yml` 中的构建命令:
```yaml
- name: Build Android APK
run: |
cd Android-kotlin-Code
# 修改为你需要的构建变体
./gradlew app:assembleMeta-BetaRelease --stacktrace
```
### 修改 iOS Scheme
编辑 `.github/workflows/build.yml` 中的 scheme:
```yaml
- name: Build iOS App
run: |
cd iOS-SwiftUI-Code
xcodebuild build \
-project uuvpn.xcodeproj \
-scheme SFT \ # 修改为你需要的 scheme
-sdk iphoneos \
...
```
### 添加构建缓存
在 workflow 中添加缓存以加速构建:
```yaml
- name: Cache Gradle packages
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Cache Swift packages
uses: actions/cache@v4
with:
path: |
~/Library/Caches/org.swift.swiftpm
.build
key: ${{ runner.os }}-swift-${{ hashFiles('**/Package.resolved') }}
restore-keys: |
${{ runner.os }}-swift-
```
### 添加自定义构建步骤
在 workflow 中添加自定义步骤:
```yaml
- name: Custom build step
run: |
# 你的自定义命令
echo "Running custom build step"
```
---
## 工作流程说明
### 完整构建流程
```
┌─────────────────┐
│ 触发构建 │
└────────┬────────┘
├─────────────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 构建 Android │ │ 构建 iOS │
│ APK │ │ IPA │
└────────┬────────┘ └────────┬────────┘
│ │
└──────────┬──────────┘
┌─────────────────────┐
│ 创建 GitHub Release │
│ (仅标签触发) │
└──────────┬──────────┘
├─────────────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 上传 APK 到 │ │ 上传 IPA 到 │
│ GitHub Release │ │ TestFlight │
└─────────────────┘ └─────────────────┘
┌─────────────────────┐
│ 发送通知 │
└─────────────────────┘
```
### 构建类型说明
| 触发条件 | Android | iOS | 发布 |
|---------|---------|-----|------|
| Push to develop | Debug APK | Debug App | 否 |
| Pull Request | Debug APK | Debug App | 否 |
| Push to main | Release APK | Release IPA | 否 |
| Tag v*-alpha | Release APK | Release IPA | GitHub Release (Pre-release) |
| Tag v*-beta | Release APK | Release IPA | GitHub Release + TestFlight |
| Tag v* | Release APK | Release IPA | GitHub Release + TestFlight |
---
## 常见问题
### Q1: Android 构建失败,提示找不到签名密钥?
**A**: 检查以下内容:
- 确认已配置 `ANDROID_KEYSTORE_BASE64` secret
- 确认 Base64 编码正确
- 确认密钥库密码、别名和密钥密码正确
### Q2: iOS 构建失败,提示签名错误?
**A**: 检查以下内容:
- 确认证书未过期
- 确认 Provisioning Profile 包含正确的 App ID
- 确认 Team ID 正确
- 确认证书和 Provisioning Profile 匹配
### Q3: iOS 构建时间过长?
**A**: iOS 构建通常需要 10-20 分钟,这是正常的。可以:
- 使用缓存加速构建
- 只在需要时构建 iOS 版本
- 使用手动触发选择性构建
### Q4: 如何跳过某个平台的构建?
**A**: 使用手动触发时,取消勾选对应平台的构建选项。
### Q5: 构建产物在哪里下载?
**A**:
1. 进入 Actions 页面
2. 点击具体的 workflow 运行记录
3. 在页面底部 "Artifacts" 区域下载
### Q6: 如何查看构建日志?
**A**:
1. 进入 Actions 页面
2. 点击具体的 workflow 运行记录
3. 点击左侧的 job 名称
4. 展开各个步骤查看详细日志
### Q7: TestFlight 上传失败?
**A**: 检查以下内容:
- 确认 App Store Connect API Key 配置正确
- 确认 API Key 有 App Manager 权限
- 确认 App ID 在 App Store Connect 中已创建
- 确认版本号未重复
### Q8: 如何修改构建触发条件?
**A**: 编辑 `.github/workflows/build.yml``on` 部分:
```yaml
on:
push:
branches:
- main
- develop
- feature/* # 添加更多分支
tags:
- 'v*'
pull_request:
branches:
- main
- develop
```
---
## 进阶配置
### 添加代码质量检查
在构建前添加代码检查:
```yaml
jobs:
lint:
name: Code Quality Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Android Lint
run: |
cd Android-kotlin-Code
./gradlew lint
- name: Swift Lint
run: |
brew install swiftlint
cd iOS-SwiftUI-Code
swiftlint
```
### 添加单元测试
```yaml
jobs:
test:
name: Run Tests
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Android Tests
run: |
cd Android-kotlin-Code
./gradlew test
- name: iOS Tests
run: |
cd iOS-SwiftUI-Code
xcodebuild test \
-project uuvpn.xcodeproj \
-scheme SFI \
-destination 'platform=iOS Simulator,name=iPhone 15'
```
### 添加多版本构建
```yaml
strategy:
matrix:
xcode-version: ['14.3', '15.0', '15.2']
include:
- xcode-version: '14.3'
ios-deployment-target: '14.0'
- xcode-version: '15.0'
ios-deployment-target: '15.0'
- xcode-version: '15.2'
ios-deployment-target: '16.0'
```
---
## 成本估算
### GitHub Actions 免费额度
- **总时长**: 每月 2000 分钟 (免费账户)
- **macOS runner**: 按 10 倍计算
- **Linux runner**: 按 1 倍计算
### 实际消耗估算
| 构建类型 | Android | iOS | 总计 |
|---------|---------|-----|------|
| Debug 构建 | ~5 分钟 | ~100 分钟 | ~105 分钟 |
| Release 构建 | ~10 分钟 | ~150 分钟 | ~160 分钟 |
| 完整流程 | ~15 分钟 | ~200 分钟 | ~215 分钟 |
### 建议
- 免费账户每月可进行约 **9-10 次完整构建**
- 建议只在必要时构建 iOS 版本
- 使用手动触发选择性构建
- 考虑升级 GitHub Pro 获得更多额度
---
## 总结
通过 GitHub Actions CI/CD 配置,你可以:
✅ 无需本地 Mac 设备即可构建 iOS 应用
✅ 自动化构建和发布流程
✅ 支持 Android 和 iOS 双平台
✅ 自动上传到 TestFlight 和 GitHub Releases
✅ 接收构建通知
配置好 Secrets 后,只需推送代码或创建标签,即可自动完成构建和发布!
+304
View File
@@ -0,0 +1,304 @@
name: Build Debug
on:
push:
branches:
- develop
pull_request:
branches:
- main
- develop
workflow_dispatch:
jobs:
# ==================== Android Debug 构建 ====================
build-android-debug:
name: Build Android Debug APK
runs-on: ubuntu
env:
RUNNER_TOOL_CACHE: /toolcache
steps:
- name: Install Node.js
run: |
export PATH="/toolcache/bin:$PATH"
if ! command -v node &> /dev/null; then
if command -v apk &> /dev/null; then
apk add --no-cache nodejs npm
else
curl -fsSL https://nodejs.org/dist/v20.0.0/node-v20.0.0-linux-x64.tar.xz | tar -xJ -C /tmp
mkdir -p /toolcache
cp -r /tmp/node-v20.0.0-linux-x64/{bin,lib,share} /toolcache/
fi
fi
node --version
- name: Install Git LFS
run: |
if command -v apk &> /dev/null; then
apk add --no-cache git-lfs
elif command -v apt-get &> /dev/null; then
apt-get update && apt-get install -y git-lfs
else
curl -sL https://github.com/git-lfs/git-lfs/releases/download/v3.5.1/git-lfs-linux-amd64-v3.5.1.tar.gz | tar xz -C /tmp
mv /tmp/git-lfs-3.5.1/git-lfs /toolcache/bin/
fi
git lfs install
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Pull Git LFS files
run: |
git lfs install
git lfs pull || echo "LFS pull failed, continuing without LFS files (Android build doesn't need them)"
- name: Install Java 17
run: |
if command -v apk &> /dev/null; then
# Enable community repository for openjdk17-jdk
sed -i 's/^#.*\/community\/.*$/\0/' /etc/apk/repositories || true
echo "https://dl-cdn.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories
apk update
apk add --no-cache openjdk17-jdk
echo "JAVA_HOME=/usr/lib/jvm/java-17-openjdk" >> $GITHUB_ENV
elif command -v apt-get &> /dev/null; then
apt-get update && apt-get install -y openjdk-17-jdk
echo "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64" >> $GITHUB_ENV
else
curl -sL https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9/OpenJDK17U-jdk_x64_linux_hotspot_17.0.9_9.tar.gz | tar xz -C /toolcache
echo "JAVA_HOME=/toolcache/jdk-17.0.9+9" >> $GITHUB_ENV
echo "/toolcache/jdk-17.0.9+9/bin" >> $GITHUB_PATH
fi
java -version
- name: Install Go 1.23
run: |
# Create toolcache directory if not exists
mkdir -p /toolcache
# Install curl if not available (Alpine uses apk)
if ! command -v curl &> /dev/null; then
if command -v apk &> /dev/null; then
apk add --no-cache curl
fi
fi
# Download Go 1.23 directly to ensure correct version
curl -sL https://go.dev/dl/go1.23.0.linux-amd64.tar.gz | tar xz -C /toolcache
echo "/toolcache/go/bin" >> $GITHUB_PATH
echo "GOROOT=/toolcache/go" >> $GITHUB_ENV
/toolcache/go/bin/go version
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Grant execute permission for gradlew
run: |
cd Android-kotlin-Code
chmod +x gradlew
- name: Create local.properties
run: |
cd Android-kotlin-Code
echo "sdk.dir=$ANDROID_HOME" > local.properties
- name: Build Android Debug APK
run: |
cd Android-kotlin-Code
./gradlew app:assembleDebug --stacktrace
- name: List APK files
run: |
echo "Listing APK files..."
find Android-kotlin-Code/app/build/outputs/apk -name "*.apk" -type f || echo "No APK files found"
ls -la Android-kotlin-Code/app/build/outputs/apk/ || true
- name: Copy APK to artifacts
run: |
mkdir -p /tmp/artifacts
find Android-kotlin-Code/app/build/outputs/apk -name "*.apk" -type f -exec cp {} /tmp/artifacts/ \;
ls -la /tmp/artifacts/
echo "APK files copied to /tmp/artifacts"
# ==================== iOS Debug 构建 ====================
build-ios-debug:
name: Build iOS Debug App
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Setup Git LFS
run: |
git lfs install
git lfs pull
- name: Verify Libbox framework before build
run: |
echo "========================================"
echo "Checking Libbox.xcframework..."
echo "========================================"
cd iOS-SwiftUI-Code
if [ -d "Libbox.xcframework" ]; then
echo "✅ Libbox.xcframework directory exists"
ls -lh Libbox.xcframework/
# 检查模拟器版本
if [ -d "Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A" ]; then
echo "✅ Simulator framework structure exists"
# 检查二进制文件
LIBBOX_BINARY="Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Libbox"
if [ -f "$LIBBOX_BINARY" ]; then
echo "✅ Libbox binary file exists"
# 获取文件大小 (macOS 和 Linux 兼容)
FILE_SIZE=$(stat -f%z "$LIBBOX_BINARY" 2>/dev/null || stat -c%s "$LIBBOX_BINARY" 2>/dev/null)
echo "Binary file size: $FILE_SIZE bytes ($(( FILE_SIZE / 1024 / 1024 )) MB)"
# 检查文件是否是真实的二进制文件 (不是 Git placeholder)
if [ "$FILE_SIZE" -lt 1000 ]; then
echo "❌ ERROR: File size is too small! This is likely a Git placeholder"
echo "File content (first 200 bytes):"
head -c 200 "$LIBBOX_BINARY"
echo ""
echo "This means Git did not download the actual binary file."
exit 1
else
echo "✅ File size is normal, binary appears to be downloaded correctly"
echo "File type:"
file "$LIBBOX_BINARY" || echo "Binary file exists"
fi
else
echo "❌ ERROR: Libbox binary file NOT found at: $LIBBOX_BINARY"
echo "Listing directory contents:"
ls -la Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/ || true
exit 1
fi
else
echo "❌ ERROR: Simulator framework structure NOT found"
echo "Listing Libbox.xcframework contents:"
find Libbox.xcframework -type d -name "*.framework" || true
exit 1
fi
else
echo "❌ ERROR: Libbox.xcframework directory NOT found!"
echo "Listing iOS-SwiftUI-Code directory:"
ls -la
exit 1
fi
- name: Setup Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: '26.3'
- name: Show Xcode version
run: |
xcodebuild -version
swift --version
- name: Resolve Swift Package Dependencies
run: |
cd iOS-SwiftUI-Code
xcodebuild -resolvePackageDependencies -project uuvpn.xcodeproj -scheme SFI
- name: List available schemes
run: |
cd iOS-SwiftUI-Code
xcodebuild -list -project uuvpn.xcodeproj
- name: Diagnose Libbox module
run: |
cd iOS-SwiftUI-Code
echo "=== Libbox.xcframework Info.plist ==="
cat Libbox.xcframework/Info.plist
echo ""
echo "=== Simulator module.modulemap ==="
cat Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Modules/module.modulemap
echo ""
echo "=== Simulator Headers ==="
ls -la Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Headers/
echo ""
echo "=== Check for Swift module interface ==="
find Libbox.xcframework/ios-arm64_x86_64-simulator -name "*.swiftmodule" -o -name "*.swiftinterface" 2>/dev/null || echo "No Swift module interface found"
echo ""
echo "=== Check Modules directory ==="
ls -la Libbox.xcframework/ios-arm64_x86_64-simulator/Libbox.framework/Versions/A/Modules/
- name: Build iOS Debug App
id: build
run: |
cd iOS-SwiftUI-Code
xcodebuild clean build \
-project uuvpn.xcodeproj \
-scheme SFI \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone Air,OS=26.2' \
-configuration Debug \
CODE_SIGN_IDENTITY="" \
CODE_SIGNING_REQUIRED=NO \
2>&1 | tee build_output.log
# 检查构建结果
if grep -q "BUILD SUCCEEDED" build_output.log; then
echo "✅ BUILD SUCCEEDED"
echo "build_status=succeeded" >> $GITHUB_OUTPUT
else
echo "❌ BUILD FAILED"
echo "=== Error lines from build log ==="
grep -i "error:" build_output.log || echo "No 'error:' lines found"
echo ""
echo "=== Last 100 lines of build log ==="
tail -100 build_output.log
echo "build_status=failed" >> $GITHUB_OUTPUT
exit 1
fi
- name: Find and upload iOS Debug App
if: success()
run: |
cd iOS-SwiftUI-Code
# 查找 .app 文件
echo "Searching for .app files in DerivedData..."
APP_PATH=$(find ~/Library/Developer/Xcode/DerivedData -name "SFI.app" -type d | head -n 1)
if [ -z "$APP_PATH" ]; then
APP_PATH=$(find ~/Library/Developer/Xcode/DerivedData -name "*.app" -type d | head -n 1)
fi
if [ -n "$APP_PATH" ] && [ -d "$APP_PATH" ]; then
echo "✅ Found app at: $APP_PATH"
echo "App size:"
du -sh "$APP_PATH"
# 复制到 build 目录
mkdir -p build
cp -R "$APP_PATH" build/
# 创建 zip 文件
cd build
APP_NAME=$(basename "$APP_PATH")
zip -r ios-debug-app.zip "$APP_NAME"
echo "✅ Created zip file: ios-debug-app.zip"
ls -lh ios-debug-app.zip
else
echo "❌ No .app file found in DerivedData"
exit 1
fi
- name: Upload iOS Debug App
uses: actions/upload-artifact@v4
with:
name: ios-debug-app
path: iOS-SwiftUI-Code/build/ios-debug-app.zip
retention-days: 7
+283
View File
@@ -0,0 +1,283 @@
name: Build iOS
on:
push:
branches:
- develop
tags:
- 'v*'
workflow_dispatch:
inputs:
build_debug:
description: 'Build Debug IPA'
required: false
default: true
type: boolean
build_release:
description: 'Build Release IPA'
required: false
default: false
type: boolean
env:
IOS_XCODE_VERSION: "16.0"
IOS_DEPLOYMENT_TARGET: "15.0"
jobs:
build-ios:
name: Build iOS IPA
runs-on: ubuntu
env:
RUNNER_TOOL_CACHE: /toolcache
steps:
- name: Install dependencies (Alpine)
run: |
# 添加 community 仓库
echo "https://dl-cdn.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories
apk update
# 安装基础依赖
apk add --no-cache \
wget \
curl \
git-lfs \
bash \
python3 \
py3-pip \
docker \
openrc \
sshpass \
sshfs
# 启动 docker 服务
rc-service docker start || true
docker --version || echo "Docker not available"
- name: Check virtualization support
run: |
echo "========================================"
echo "Checking virtualization support..."
echo "========================================"
# 检查 KVM
ls -la /dev/kvm 2>/dev/null || echo "KVM device not found"
cat /proc/cpuinfo | grep -E "vmx|svm" | head -5 || echo "No hardware virtualization detected"
echo ""
echo "Docker version:"
docker --version || echo "Docker not found"
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Setup Git LFS
run: |
git lfs install
# 配置 LFS 认证
git config --global lfs.url "https://admin:73ffc0f4391a26bb327fbed379d63b5a087e5f10@git.viaeon.com/admin/UUVPN.git/info/lfs"
echo "Attempting LFS pull..."
git lfs pull 2>&1 || echo "LFS pull failed, trying alternative methods..."
# 如果 LFS pull 失败,尝试直接 fetch 和 checkout
git lfs fetch origin 2>&1 || echo "LFS fetch failed"
git lfs checkout 2>&1 || echo "LFS checkout failed"
- name: Check if Libbox framework exists
id: check_lfs
run: |
echo "========================================"
echo "Checking LFS files..."
echo "========================================"
echo "Libbox.xcframework contents:"
ls -la iOS-SwiftUI-Code/Libbox.xcframework/ 2>/dev/null || echo "Directory not found or empty"
# 检查关键文件是否是真正的二进制文件(不是 LFS 指针)
if [ -f "iOS-SwiftUI-Code/Libbox.xcframework/ios-arm64/Libbox.framework/Libbox" ]; then
file_size=$(stat -c%s "iOS-SwiftUI-Code/Libbox.xcframework/ios-arm64/Libbox.framework/Libbox" 2>/dev/null || stat -f%z "iOS-SwiftUI-Code/Libbox.xcframework/ios-arm64/Libbox.framework/Libbox" 2>/dev/null)
if [ "$file_size" -gt 100 ]; then
echo "lfs_ready=true" >> $GITHUB_OUTPUT
echo "✅ LFS files downloaded successfully (file size: ${file_size} bytes)"
else
echo "lfs_ready=false" >> $GITHUB_OUTPUT
echo "❌ LFS files are pointers, not actual binaries"
fi
else
echo "lfs_ready=false" >> $GITHUB_OUTPUT
echo "❌ LFS files not available"
fi
exit 0
- name: Pull Docker-OSX image
if: ${{ steps.check_lfs.outputs.lfs_ready == 'true' }}
run: |
echo "Pulling Docker-OSX image..."
docker pull sickcodes/docker-osx:latest || echo "Failed to pull image"
- name: Start macOS VM (Sonoma)
if: ${{ steps.check_lfs.outputs.lfs_ready == 'true' }}
run: |
echo "========================================"
echo "Starting macOS Sonoma VM with Docker-OSX..."
echo "========================================"
echo ""
echo "⚠️ 注意: 首次启动需要下载 macOS 安装镜像 (~15GB)"
echo "⚠️ 这可能需要 30-60 分钟"
echo ""
# 使用正确的 Docker-OSX 启动命令
# 注意: 需要 --device /dev/kvm 来传递 KVM 设备
# 注意: 需要 -it 交互式模式
# 注意: Sonoma 需要特殊的 CPU 配置
# 检查 KVM 是否可用
if [ -e "/dev/kvm" ]; then
echo "✅ KVM is available"
KVM_DEVICE="--device /dev/kvm"
else
echo "❌ KVM not available, will use QEMU emulation (very slow)"
KVM_DEVICE=""
fi
# 启动 macOS Sonoma 容器
# 使用 -d 后台运行,并设置环境变量
docker run -d \
--name macos-vm \
$KVM_DEVICE \
--privileged \
-p 50922:10022 \
-e RAM=8192 \
-e SMP=4 \
-e CPU='Haswell-noTSX' \
-e CPUID_FLAGS='kvm=on,vendor=GenuineIntel,+invtsc,vmware-cpuid-freq=on' \
-e GENERATE_UNIQUE=true \
-e MASTER_PLIST_URL='https://raw.githubusercontent.com/sickcodes/osx-serial-generator/master/config-custom-sonoma.plist' \
-e SHORTNAME=sonoma \
-e DISPLAY=dummy \
sickcodes/docker-osx:latest || echo "Failed to start VM"
echo ""
echo "VM started in background"
echo "Waiting for macOS to boot (this may take several minutes)..."
# 等待 SSH 可用
max_wait=600 # 10 分钟
wait_count=0
while [ $wait_count -lt $max_wait ]; do
if sshpass -p "user" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=5 -p 50922 user@localhost "echo 'SSH ready'" 2>/dev/null; then
echo "✅ SSH is ready!"
break
fi
echo "Waiting for SSH... ($wait_count/$max_wait)"
sleep 10
wait_count=$((wait_count + 10))
done
if [ $wait_count -ge $max_wait ]; then
echo "❌ SSH not available after $max_wait seconds"
docker logs macos-vm 2>/dev/null | tail -50 || echo "No VM logs"
fi
- name: Check macOS VM status
if: ${{ steps.check_lfs.outputs.lfs_ready == 'true' }}
run: |
echo "========================================"
echo "macOS VM Status"
echo "========================================"
# 通过 SSH 连接到 macOS
echo "Checking macOS via SSH..."
# 尝试连接 SSH
sshpass -p "user" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p 50922 user@localhost "uname -a" 2>/dev/null || echo "Cannot connect via SSH"
# 检查 xcodebuild
sshpass -p "user" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p 50922 user@localhost "xcodebuild -version" 2>/dev/null || echo "xcodebuild not found"
# 检查容器日志
echo ""
echo "Container logs (last 30 lines):"
docker logs macos-vm 2>/dev/null | tail -30 || echo "No VM logs"
- name: Build iOS app (attempt)
if: ${{ steps.check_lfs.outputs.lfs_ready == 'true' }}
run: |
echo "========================================"
echo "Attempting iOS build..."
echo "========================================"
# 通过 SSHFS 挂载 macOS 文件系统,或者通过 SSH 直接执行命令
# 默认用户: user, 密码: user
# 检查 SSH 是否可用
if sshpass -p "user" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 -p 50922 user@localhost "echo 'SSH ready'" 2>/dev/null; then
echo "✅ SSH connection available"
# 将代码复制到 macOS
echo "Copying code to macOS..."
sshpass -p "user" scp -o StrictHostKeyChecking=no -P 50922 -r iOS-SwiftUI-Code user@localhost:/tmp/ 2>/dev/null || echo "Failed to copy code"
# 检查代码是否存在
sshpass -p "user" ssh -o StrictHostKeyChecking=no -p 50922 user@localhost "ls -la /tmp/iOS-SwiftUI-Code/" 2>/dev/null || echo "Code not copied"
# 尝试构建
echo ""
echo "Building iOS app..."
sshpass -p "user" ssh -o StrictHostKeyChecking=no -p 50922 user@localhost "
cd /tmp/iOS-SwiftUI-Code || exit 1
# 检查 xcodebuild
xcodebuild -version || echo 'xcodebuild not found'
# 尝试构建
xcodebuild \
-project uuvpn.xcodeproj \
-scheme SFI \
-sdk iphoneos \
-configuration Debug \
-derivedDataPath build \
CODE_SIGN_IDENTITY='' \
CODE_SIGNING_REQUIRED=NO \
build 2>&1 || echo 'Build failed'
" 2>&1 || echo "SSH command failed"
else
echo "❌ SSH not available, cannot build"
echo ""
echo "Docker-OSX 问题总结:"
echo "1. 首次启动需要下载 macOS 安装镜像 (~15GB)"
echo "2. 需要等待 macOS 安装完成 (可能需要 1 小时+)"
echo "3. 需要手动安装 Xcode"
echo ""
echo "建议:"
echo "- 使用 GitHub Actions 的托管 macOS runner (免费)"
echo "- 或者预先准备好 macOS 虚拟机镜像"
fi
- name: Cleanup
if: always()
run: |
echo "Cleaning up..."
docker stop macos-vm 2>/dev/null || true
docker rm macos-vm 2>/dev/null || true
- name: Build summary
run: |
echo "========================================"
echo "iOS Build Summary"
echo "========================================"
echo "LFS ready: ${{ steps.check_lfs.outputs.lfs_ready }}"
echo ""
if [ "${{ steps.check_lfs.outputs.lfs_ready }}" != "true" ]; then
echo "❌ Cannot build iOS without Libbox.xcframework"
else
echo "✅ LFS files available"
echo "Build was attempted - check logs above for results"
fi
+307
View File
@@ -0,0 +1,307 @@
name: Build UUVPN
on:
push:
branches:
- main
- develop
tags:
- 'v*'
pull_request:
branches:
- main
workflow_dispatch:
inputs:
build_android:
description: 'Build Android APK'
required: false
default: true
type: boolean
build_ios:
description: 'Build iOS IPA'
required: false
default: true
type: boolean
release_type:
description: 'Release type'
required: false
default: 'alpha'
type: choice
options:
- alpha
- beta
- release
env:
# Android 配置
ANDROID_COMPILE_SDK: "33"
ANDROID_BUILD_TOOLS: "33.0.0"
ANDROID_TARGET_SDK: "33"
ANDROID_JDK_VERSION: "17"
# iOS 配置
IOS_XCODE_VERSION: "16.0"
IOS_DEPLOYMENT_TARGET: "15.0"
jobs:
# ==================== Android 构建 ====================
build-android:
name: Build Android APK
runs-on: ubuntu
# 暂时禁用 Android 构建,使用 build-android.yml
if: ${{ false && github.event.inputs.build_android != 'false' }}
env:
RUNNER_TOOL_CACHE: /toolcache
steps:
- name: Install Node.js
run: |
export PATH="/toolcache/bin:$PATH"
if ! command -v node &> /dev/null; then
if command -v apk &> /dev/null; then
apk add --no-cache nodejs npm
else
curl -fsSL https://nodejs.org/dist/v20.0.0/node-v20.0.0-linux-x64.tar.xz | tar -xJ -C /tmp
mkdir -p /toolcache
cp -r /tmp/node-v20.0.0-linux-x64/{bin,lib,share} /toolcache/
fi
fi
node --version
- name: Install Git LFS
run: |
if command -v apk &> /dev/null; then
apk add --no-cache git-lfs
elif command -v apt-get &> /dev/null; then
apt-get update && apt-get install -y git-lfs
else
curl -sL https://github.com/git-lfs/git-lfs/releases/download/v3.5.1/git-lfs-linux-amd64-v3.5.1.tar.gz | tar xz -C /tmp
mv /tmp/git-lfs-3.5.1/git-lfs /toolcache/bin/
fi
git lfs install
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Pull Git LFS files
run: |
git lfs pull || echo "LFS pull failed, continuing without LFS files (Android build doesn't need them)"
- name: Install Java 17
run: |
if command -v apk &> /dev/null; then
# Enable community repository for openjdk17-jdk
echo "https://dl-cdn.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories
apk update
apk add --no-cache openjdk17-jdk
echo "JAVA_HOME=/usr/lib/jvm/java-17-openjdk" >> $GITHUB_ENV
elif command -v apt-get &> /dev/null; then
apt-get update && apt-get install -y openjdk-17-jdk
echo "JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64" >> $GITHUB_ENV
else
curl -sL https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.9%2B9/OpenJDK17U-jdk_x64_linux_hotspot_17.0.9_9.tar.gz | tar xz -C /toolcache
echo "JAVA_HOME=/toolcache/jdk-17.0.9+9" >> $GITHUB_ENV
echo "/toolcache/jdk-17.0.9+9/bin" >> $GITHUB_PATH
fi
java -version
- name: Install Go 1.23
run: |
mkdir -p /toolcache
if ! command -v curl &> /dev/null; then
if command -v apk &> /dev/null; then
apk add --no-cache curl
fi
fi
curl -sL https://go.dev/dl/go1.23.0.linux-amd64.tar.gz | tar xz -C /toolcache
echo "/toolcache/go/bin" >> $GITHUB_PATH
echo "GOROOT=/toolcache/go" >> $GITHUB_ENV
/toolcache/go/bin/go version
- name: Install gcompat (for NDK on Alpine)
run: |
# Install gcompat for running glibc binaries on Alpine (required for NDK clang)
if command -v apk &> /dev/null; then
apk add --no-cache gcompat libstdc++
fi
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Grant execute permission for gradlew
run: |
cd Android-kotlin-Code
chmod +x gradlew
- name: Create local.properties
run: |
cd Android-kotlin-Code
echo "sdk.dir=$ANDROID_HOME" > local.properties
- name: Create signing.properties (Debug)
if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/develop' }}
run: |
cd Android-kotlin-Code
echo "keystore.path=debug.keystore" > signing.properties
echo "keystore.password=android" >> signing.properties
echo "key.alias=androiddebugkey" >> signing.properties
echo "key.password=android" >> signing.properties
- name: Create signing.properties (Release)
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
cd Android-kotlin-Code
echo "keystore.path=release.keystore" > signing.properties
echo "keystore.password=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" >> signing.properties
echo "key.alias=${{ secrets.ANDROID_KEY_ALIAS }}" >> signing.properties
echo "key.password=${{ secrets.ANDROID_KEY_PASSWORD }}" >> signing.properties
- name: Decode Android keystore
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
cd Android-kotlin-Code
echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 -d > release.keystore
- name: Build Android Debug APK
if: ${{ github.event_name == 'pull_request' || github.ref == 'refs/heads/develop' }}
run: |
cd Android-kotlin-Code
./gradlew assembleAlphaDebug --stacktrace
- name: Build Android Release APK
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
cd Android-kotlin-Code
./gradlew assembleAlphaRelease --stacktrace
- name: Build Android APK (Default)
if: ${{ github.event_name == 'push' && !startsWith(github.ref, 'refs/tags/v') && github.ref != 'refs/heads/develop' }}
run: |
cd Android-kotlin-Code
./gradlew assembleAlphaRelease --stacktrace
- name: Upload Android APK Artifact
run: |
# Copy APK to a known location for artifact upload
mkdir -p /tmp/artifacts
find Android-kotlin-Code/app/build/outputs/apk -name "*.apk" -type f -exec cp {} /tmp/artifacts/ \;
ls -la /tmp/artifacts/
echo "APK files copied to /tmp/artifacts"
- name: Get APK filename
id: apk_name
run: |
if [ "${{ startsWith(github.ref, 'refs/tags/v') }}" = "true" ]; then
APK_NAME="UUVPN-Android-${{ github.ref_name }}.apk"
else
APK_NAME="UUVPN-Android-${{ github.sha }}.apk"
fi
echo "apk_name=$APK_NAME" >> $GITHUB_OUTPUT
# Find the APK file
APK_PATH=$(find Android-kotlin-Code/app/build/outputs/apk -name "*.apk" -type f | head -1)
echo "apk_path=$APK_PATH" >> $GITHUB_OUTPUT
echo "Found APK: $APK_PATH"
- name: Rename APK for release
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
run: |
cd Android-kotlin-Code/app/build/outputs/apk
find . -name "*.apk" -type f -exec mv {} "${{ steps.apk_name.outputs.apk_name }}" \;
# ==================== iOS 构建 (Docker-OSX) ====================
# 已移至 build-ios.yml
build-ios:
name: Build iOS IPA
runs-on: ubuntu
if: ${{ false }}
steps:
- name: Disabled
run: echo "iOS build moved to build-ios.yml"
# ==================== 发布到 GitHub Releases ====================
release:
name: Create GitHub Release
needs: [build-android]
runs-on: ubuntu-latest
if: ${{ startsWith(github.ref, 'refs/tags/v') }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Download Android APK
uses: actions/download-artifact@v4
with:
name: android-apk-${{ github.sha }}
path: artifacts/android
- name: Get version from tag
id: get_version
run: |
VERSION=${{ github.ref_name }}
echo "version=$VERSION" >> $GITHUB_OUTPUT
- name: Create Release Notes
id: release_notes
run: |
cat > release_notes.md << EOF
# UUVPN ${{ steps.get_version.outputs.version }}
## 🎉 新版本发布
### 📱 Android 版本
- 支持 Android 5.0+ (推荐 Android 7.0+)
- 支持 armeabi-v7a, arm64-v8a, x86, x86_64 架构
- 基于 CLASH (mihomo) 核心
### 🍎 iOS 版本
- 支持 iOS 15.0+
- 基于 SINGBOX 核心
- 支持 iPhone 和 iPad
## 📥 下载说明
### Android
下载 APK 文件并安装,首次安装需要授予必要权限。
### iOS
IPA 文件需要通过 TestFlight 或第三方工具安装。
## 🔄 更新内容
请查看 [Commits](https://github.com/${{ github.repository }}/commits/${{ github.ref_name }}) 了解详细更新内容。
## 📝 问题反馈
如遇到问题,请在 [Issues](https://github.com/${{ github.repository }}/issues) 中反馈。
EOF
- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ github.ref_name }}
name: UUVPN ${{ steps.get_version.outputs.version }}
body_path: release_notes.md
draft: false
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') }}
files: |
artifacts/android/*.apk
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ==================== 通知 ====================
notify:
name: Send Notification
needs: [build-android]
runs-on: ubuntu
if: ${{ always() && github.event_name == 'push' }}
steps:
- name: Send Telegram Notification
if: ${{ secrets.TELEGRAM_BOT_TOKEN != '' }}
run: |
echo "Telegram notification would be sent here"
echo "Build status: ${{ needs.build-android.result }}"
+199
View File
@@ -0,0 +1,199 @@
# UUVPN GitHub Secrets 配置脚本 (PowerShell)
# 用于快速配置 GitHub Actions 所需的 Secrets
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "UUVPN GitHub Secrets 配置工具" -ForegroundColor Cyan
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
# 检查是否安装了 GitHub CLI
if (-not (Get-Command gh -ErrorAction SilentlyContinue)) {
Write-Host "❌ 错误: 未安装 GitHub CLI" -ForegroundColor Red
Write-Host "请先安装 GitHub CLI: https://cli.github.com/"
exit 1
}
# 检查是否已登录 GitHub
try {
$authStatus = gh auth status 2>&1
if ($LASTEXITCODE -ne 0) {
Write-Host "❌ 错误: 未登录 GitHub CLI" -ForegroundColor Red
Write-Host "请先运行: gh auth login"
exit 1
}
} catch {
Write-Host "❌ 错误: 未登录 GitHub CLI" -ForegroundColor Red
Write-Host "请先运行: gh auth login"
exit 1
}
# 获取仓库信息
try {
$remoteUrl = git remote get-url origin
if ($remoteUrl -match "github\.com[:/](.+?)\.git") {
$repo = $matches[1]
} else {
Write-Host "❌ 错误: 无法获取仓库信息" -ForegroundColor Red
Write-Host "请确保在仓库目录中运行此脚本"
exit 1
}
} catch {
Write-Host "❌ 错误: 无法获取仓库信息" -ForegroundColor Red
Write-Host "请确保在仓库目录中运行此脚本"
exit 1
}
Write-Host "✅ 当前仓库: $repo" -ForegroundColor Green
Write-Host ""
# 配置 Android Secrets
Write-Host "📱 配置 Android Secrets" -ForegroundColor Yellow
Write-Host "----------------------------------------"
$configAndroid = Read-Host "是否配置 Android 签名密钥? (y/n)"
if ($configAndroid -eq "y") {
$keystorePath = Read-Host "密钥库文件路径 (默认: release.keystore)"
if ([string]::IsNullOrWhiteSpace($keystorePath)) {
$keystorePath = "release.keystore"
}
if (-not (Test-Path $keystorePath)) {
Write-Host "❌ 错误: 密钥库文件不存在: $keystorePath" -ForegroundColor Red
Write-Host "请先创建密钥库:"
Write-Host "keytool -genkey -v -keystore release.keystore -alias uuvpn -keyalg RSA -keysize 2048 -validity 10000"
exit 1
}
$keystorePassword = Read-Host "密钥库密码" -AsSecureString
$keystorePasswordPlain = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($keystorePassword)
)
$keyAlias = Read-Host "密钥别名 (默认: uuvpn)"
if ([string]::IsNullOrWhiteSpace($keyAlias)) {
$keyAlias = "uuvpn"
}
$keyPassword = Read-Host "密钥密码" -AsSecureString
$keyPasswordPlain = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($keyPassword)
)
# Base64 编码密钥库
$keystoreBytes = [IO.File]::ReadAllBytes($keystorePath)
$keystoreBase64 = [Convert]::ToBase64String($keystoreBytes)
Write-Host "正在设置 Android Secrets..." -ForegroundColor Cyan
$keystoreBase64 | gh secret set ANDROID_KEYSTORE_BASE64 --repo $repo
$keystorePasswordPlain | gh secret set ANDROID_KEYSTORE_PASSWORD --repo $repo
$keyAlias | gh secret set ANDROID_KEY_ALIAS --repo $repo
$keyPasswordPlain | gh secret set ANDROID_KEY_PASSWORD --repo $repo
Write-Host "✅ Android Secrets 配置完成" -ForegroundColor Green
}
Write-Host ""
# 配置 iOS Secrets
Write-Host "🍎 配置 iOS Secrets" -ForegroundColor Yellow
Write-Host "----------------------------------------"
$configIOS = Read-Host "是否配置 iOS 签名证书? (y/n)"
if ($configIOS -eq "y") {
$certPath = Read-Host "证书文件路径 (.p12)"
if (-not (Test-Path $certPath)) {
Write-Host "❌ 错误: 证书文件不存在: $certPath" -ForegroundColor Red
exit 1
}
$certPassword = Read-Host "证书密码" -AsSecureString
$certPasswordPlain = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($certPassword)
)
$teamId = Read-Host "Team ID"
$signingIdentity = Read-Host "签名身份 (如: Apple Distribution: Your Name (TEAM_ID))"
$profilePath = Read-Host "Provisioning Profile 文件路径 (.mobileprovision)"
if (-not (Test-Path $profilePath)) {
Write-Host "❌ 错误: Provisioning Profile 文件不存在: $profilePath" -ForegroundColor Red
exit 1
}
# Base64 编码
$certBytes = [IO.File]::ReadAllBytes($certPath)
$certBase64 = [Convert]::ToBase64String($certBytes)
$profileBytes = [IO.File]::ReadAllBytes($profilePath)
$profileBase64 = [Convert]::ToBase64String($profileBytes)
Write-Host "正在设置 iOS Secrets..." -ForegroundColor Cyan
$certBase64 | gh secret set IOS_CERTIFICATES_P12 --repo $repo
$certPasswordPlain | gh secret set IOS_CERTIFICATES_PASSWORD --repo $repo
$teamId | gh secret set IOS_TEAM_ID --repo $repo
$signingIdentity | gh secret set IOS_SIGNING_IDENTITY --repo $repo
$profileBase64 | gh secret set IOS_PROVISIONING_PROFILE --repo $repo
Write-Host "✅ iOS Secrets 配置完成" -ForegroundColor Green
}
Write-Host ""
# 配置 App Store Connect Secrets
Write-Host "🚀 配置 App Store Connect Secrets" -ForegroundColor Yellow
Write-Host "----------------------------------------"
$configASC = Read-Host "是否配置 App Store Connect API? (y/n)"
if ($configASC -eq "y") {
$issuerId = Read-Host "Issuer ID"
$apiKeyId = Read-Host "API Key ID"
$p8Path = Read-Host "API Private Key 文件路径 (.p8)"
if (-not (Test-Path $p8Path)) {
Write-Host "❌ 错误: API Private Key 文件不存在: $p8Path" -ForegroundColor Red
exit 1
}
# 读取私钥内容
$apiKey = Get-Content $p8Path -Raw
Write-Host "正在设置 App Store Connect Secrets..." -ForegroundColor Cyan
$issuerId | gh secret set APP_STORE_CONNECT_ISSUER_ID --repo $repo
$apiKeyId | gh secret set APP_STORE_CONNECT_API_KEY_ID --repo $repo
$apiKey | gh secret set APP_STORE_CONNECT_API_PRIVATE_KEY --repo $repo
Write-Host "✅ App Store Connect Secrets 配置完成" -ForegroundColor Green
}
Write-Host ""
# 配置通知 Secrets
Write-Host "📢 配置通知 Secrets" -ForegroundColor Yellow
Write-Host "----------------------------------------"
$configTelegram = Read-Host "是否配置 Telegram 通知? (y/n)"
if ($configTelegram -eq "y") {
$botToken = Read-Host "Bot Token"
$chatId = Read-Host "Chat ID"
Write-Host "正在设置 Telegram Secrets..." -ForegroundColor Cyan
$botToken | gh secret set TELEGRAM_BOT_TOKEN --repo $repo
$chatId | gh secret set TELEGRAM_CHAT_ID --repo $repo
Write-Host "✅ Telegram Secrets 配置完成" -ForegroundColor Green
}
Write-Host ""
Write-Host "========================================" -ForegroundColor Cyan
Write-Host "✅ 配置完成!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Cyan
Write-Host ""
Write-Host "当前配置的 Secrets:"
gh secret list --repo $repo
Write-Host ""
Write-Host "下一步:" -ForegroundColor Yellow
Write-Host "1. 推送代码到仓库触发构建"
Write-Host "2. 查看构建状态: https://github.com/$repo/actions"
Write-Host ""
+171
View File
@@ -0,0 +1,171 @@
#!/bin/bash
# UUVPN GitHub Secrets 配置脚本
# 用于快速配置 GitHub Actions 所需的 Secrets
set -e
echo "========================================"
echo "UUVPN GitHub Secrets 配置工具"
echo "========================================"
echo ""
# 检查是否安装了 GitHub CLI
if ! command -v gh &> /dev/null; then
echo "❌ 错误: 未安装 GitHub CLI"
echo "请先安装 GitHub CLI: https://cli.github.com/"
exit 1
fi
# 检查是否已登录 GitHub
if ! gh auth status &> /dev/null; then
echo "❌ 错误: 未登录 GitHub CLI"
echo "请先运行: gh auth login"
exit 1
fi
# 获取仓库信息
REPO=$(git remote get-url origin | sed -E 's|.*github\.com[:/](.*)\.git|\1|')
if [ -z "$REPO" ]; then
echo "❌ 错误: 无法获取仓库信息"
echo "请确保在仓库目录中运行此脚本"
exit 1
fi
echo "✅ 当前仓库: $REPO"
echo ""
# 配置 Android Secrets
echo "📱 配置 Android Secrets"
echo "----------------------------------------"
read -p "是否配置 Android 签名密钥? (y/n): " config_android
if [ "$config_android" = "y" ]; then
read -p "密钥库文件路径 (默认: release.keystore): " keystore_path
keystore_path=${keystore_path:-"release.keystore"}
if [ ! -f "$keystore_path" ]; then
echo "❌ 错误: 密钥库文件不存在: $keystore_path"
echo "请先创建密钥库:"
echo "keytool -genkey -v -keystore release.keystore -alias uuvpn -keyalg RSA -keysize 2048 -validity 10000"
exit 1
fi
read -sp "密钥库密码: " keystore_password
echo ""
read -p "密钥别名 (默认: uuvpn): " key_alias
key_alias=${key_alias:-"uuvpn"}
read -sp "密钥密码: " key_password
echo ""
# Base64 编码密钥库
keystore_base64=$(base64 -i "$keystore_path")
echo "正在设置 Android Secrets..."
gh secret set ANDROID_KEYSTORE_BASE64 --repo "$REPO" --body "$keystore_base64"
gh secret set ANDROID_KEYSTORE_PASSWORD --repo "$REPO" --body "$keystore_password"
gh secret set ANDROID_KEY_ALIAS --repo "$REPO" --body "$key_alias"
gh secret set ANDROID_KEY_PASSWORD --repo "$REPO" --body "$key_password"
echo "✅ Android Secrets 配置完成"
fi
echo ""
# 配置 iOS Secrets
echo "🍎 配置 iOS Secrets"
echo "----------------------------------------"
read -p "是否配置 iOS 签名证书? (y/n): " config_ios
if [ "$config_ios" = "y" ]; then
read -p "证书文件路径 (.p12): " cert_path
if [ ! -f "$cert_path" ]; then
echo "❌ 错误: 证书文件不存在: $cert_path"
exit 1
fi
read -sp "证书密码: " cert_password
echo ""
read -p "Team ID: " team_id
read -p "签名身份 (如: Apple Distribution: Your Name (TEAM_ID)): " signing_identity
read -p "Provisioning Profile 文件路径 (.mobileprovision): " profile_path
if [ ! -f "$profile_path" ]; then
echo "❌ 错误: Provisioning Profile 文件不存在: $profile_path"
exit 1
fi
# Base64 编码
cert_base64=$(base64 -i "$cert_path")
profile_base64=$(base64 -i "$profile_path")
echo "正在设置 iOS Secrets..."
gh secret set IOS_CERTIFICATES_P12 --repo "$REPO" --body "$cert_base64"
gh secret set IOS_CERTIFICATES_PASSWORD --repo "$REPO" --body "$cert_password"
gh secret set IOS_TEAM_ID --repo "$REPO" --body "$team_id"
gh secret set IOS_SIGNING_IDENTITY --repo "$REPO" --body "$signing_identity"
gh secret set IOS_PROVISIONING_PROFILE --repo "$REPO" --body "$profile_base64"
echo "✅ iOS Secrets 配置完成"
fi
echo ""
# 配置 App Store Connect Secrets
echo "🚀 配置 App Store Connect Secrets"
echo "----------------------------------------"
read -p "是否配置 App Store Connect API? (y/n): " config_asc
if [ "$config_asc" = "y" ]; then
read -p "Issuer ID: " issuer_id
read -p "API Key ID: " api_key_id
read -p "API Private Key 文件路径 (.p8): " p8_path
if [ ! -f "$p8_path" ]; then
echo "❌ 错误: API Private Key 文件不存在: $p8_path"
exit 1
fi
# 读取私钥内容
api_private_key=$(cat "$p8_path")
echo "正在设置 App Store Connect Secrets..."
gh secret set APP_STORE_CONNECT_ISSUER_ID --repo "$REPO" --body "$issuer_id"
gh secret set APP_STORE_CONNECT_API_KEY_ID --repo "$REPO" --body "$api_key_id"
gh secret set APP_STORE_CONNECT_API_PRIVATE_KEY --repo "$REPO" --body "$api_private_key"
echo "✅ App Store Connect Secrets 配置完成"
fi
echo ""
# 配置通知 Secrets
echo "📢 配置通知 Secrets"
echo "----------------------------------------"
read -p "是否配置 Telegram 通知? (y/n): " config_telegram
if [ "$config_telegram" = "y" ]; then
read -p "Bot Token: " bot_token
read -p "Chat ID: " chat_id
echo "正在设置 Telegram Secrets..."
gh secret set TELEGRAM_BOT_TOKEN --repo "$REPO" --body "$bot_token"
gh secret set TELEGRAM_CHAT_ID --repo "$REPO" --body "$chat_id"
echo "✅ Telegram Secrets 配置完成"
fi
echo ""
echo "========================================"
echo "✅ 配置完成!"
echo "========================================"
echo ""
echo "当前配置的 Secrets:"
gh secret list --repo "$REPO"
echo ""
echo "下一步:"
echo "1. 推送代码到仓库触发构建"
echo "2. 查看构建状态: https://github.com/$REPO/actions"
echo ""
+4
View File
@@ -1,6 +1,9 @@
app-profile.apk
.DS_Store
# Exclude kankanjiao project
/kankanjiao/
# Do not remove or rename entries in this file, only add new ones
# See https://github.com/flutter/flutter/issues/128635 for more context.
@@ -139,3 +142,4 @@ app.*.symbols
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
!/dev/ci/**/Gemfile.lock
!.vscode/settings.json
/Android-kotlin-Code/core/.cxx
+636
View File
@@ -0,0 +1,636 @@
# UUVPN API 端点配置指南
本文档详细说明如何配置 UUVPN 的 API 端点,包括 Android 和 iOS 平台的配置方法。
---
## 目录
- [API 配置概述](#api-配置概述)
- [配置字段说明](#配置字段说明)
- [Android 配置方法](#android-配置方法)
- [iOS 配置方法](#ios-配置方法)
- [测试与验证](#测试与验证)
- [常见问题](#常见问题)
---
## API 配置概述
UUVPN 需要配置一个远程 API 端点,用于获取服务器配置信息。该配置采用 JSON 格式,包含 V2Board 面板的 API 地址、支付链接、客服支持等信息。
### 配置 URL 格式
```
https://your-domain.com/api/config.php
```
### 配置返回格式
```json
{
"baseURL": "https://api.0008.uk/api/v1/",
"baseDYURL": "https://api.gooapis.com/api/vpnnodes.php",
"mainregisterURL": "https://lelian.app/#/register?code=",
"paymentURL": "https://your-payment-gateway.com/pay",
"telegramurl": "https://t.me/fastlink",
"kefuurl": "https://gooapis.com/fastlink/",
"websiteURL": "https://gooapis.com/fastlink/",
"crisptoken": "5546c6ea-4b1e-41bc-80e4-4b6648cbca76",
"banners": [
"https://image.gooapis.com/api/images/12-11-56.png",
"https://image.gooapis.com/api/images/12-44-57.png",
"https://image.gooapis.com/api/images/12-47-03.png"
],
"message": "OK",
"code": 1
}
```
---
## 配置字段说明
### 1. baseURL
- **类型**: String
- **必填**: 是
- **描述**: V2Board 面板的主要 API 端点
- **示例**: `https://api.0008.uk/api/v1/`
- **用途**: 所有与 V2Board 服务器的 API 请求都将基于此 URL,包括:
- 用户登录/注册
- 获取节点列表
- 获取订阅信息
- 用户信息管理
### 2. baseDYURL
- **类型**: String
- **必填**: 是
- **描述**: VPN 节点测试端点
- **示例**: `https://api.gooapis.com/api/vpnnodes.php`
- **用途**: 用于测试 VPN 节点的连通性和速度,通常用于应用内的节点测速功能
### 3. mainregisterURL
- **类型**: String
- **必填**: 是
- **描述**: 用户注册页面 URL(带邀请码参数)
- **示例**: `https://lelian.app/#/register?code=`
- **用途**: 引导用户注册,支持邀请码系统
- **注意**: URL 末尾需要保留 `code=` 参数,应用会自动填充邀请码
### 4. paymentURL
- **类型**: String
- **必填**: 是
- **描述**: 支付网关 URL
- **示例**: `https://your-payment-gateway.com/pay`
- **用途**:
- **正常模式** (URL 长度 > 3): 显示外部支付选项,启用订阅功能
- **审核模式** (URL 长度 ≤ 3): 隐藏外部支付,遵守 App Store 指南
- **iOS 重要说明**:
- 正常值: `https://payment.example.com/pay` (显示支付功能)
- 审核值: `xxx``xx` (隐藏支付功能,用于 App Store 审核)
### 5. telegramurl
- **类型**: String
- **必填**: 否
- **描述**: Telegram 支持频道/群组链接
- **示例**: `https://t.me/fastlink`
- **用途**: 提供用户 Telegram 客服支持入口
### 6. kefuurl
- **类型**: String
- **必填**: 否
- **描述**: 在线客服页面 URL
- **示例**: `https://gooapis.com/fastlink/`
- **用途**: 提供网页版在线客服入口
### 7. websiteURL
- **类型**: String
- **必填**: 否
- **描述**: 官方网站 URL
- **示例**: `https://gooapis.com/fastlink/`
- **用途**: 应用内关于页面或官网链接
### 8. crisptoken
- **类型**: String
- **必填**: 否
- **描述**: Crisp 聊天服务认证令牌
- **示例**: `5546c6ea-4b1e-41bc-80e4-4b6648cbca76`
- **用途**: 集成 Crisp 实时聊天功能
- **获取方式**: 在 [Crisp 官网](https://crisp.chat/) 注册后获取
### 9. banners
- **类型**: Array of Strings
- **必填**: 否
- **描述**: 首页横幅图片 URL 数组
- **示例**:
```json
[
"https://image.gooapis.com/api/images/12-11-56.png",
"https://image.gooapis.com/api/images/12-44-57.png"
]
```
- **用途**: 应用首页轮播图展示
- **建议尺寸**: 750x300 像素
- **格式支持**: PNG, JPG, WebP
### 10. message
- **类型**: String
- **必填**: 是
- **描述**: API 响应状态消息
- **示例**: `OK`
- **用途**: 标识配置请求是否成功
### 11. code
- **类型**: Integer
- **必填**: 是
- **描述**: API 响应状态码
- **示例**: `1` (成功) 或 `0` (失败)
- **用途**: 应用根据此字段判断配置是否有效
---
## Android 配置方法
### 方法一: 修改配置文件 (推荐)
1. **定位配置文件**
在 Android 项目中找到以下文件:
```
Android-kotlin-Code/app/src/main/java/com/xxx/xxx/config/ApiConfig.kt
```
2. **修改配置 URL**
找到 `configURL` 或类似的配置常量:
```kotlin
const val CONFIG_URL = "https://your-domain.com/api/config.php"
```
3. **重新编译 APK**
```bash
./gradlew app:assembleMeta-AlphaRelease
```
### 方法二: 动态配置
如果支持动态配置,可以在应用启动时请求配置 URL:
```kotlin
// 在 Application 或 MainActivity 中
ConfigManager.init(this, "https://your-domain.com/api/config.php")
```
### Android API 请求示例
```kotlin
// ApiService.kt
interface ApiService {
@GET
suspend fun getConfig(@Url url: String): Response<ConfigResponse>
}
data class ConfigResponse(
val baseURL: String,
val baseDYURL: String,
val mainregisterURL: String,
val paymentURL: String,
val telegramurl: String,
val kefuurl: String,
val websiteURL: String,
val crisptoken: String,
val banners: List<String>,
val message: String,
val code: Int
)
```
---
## iOS 配置方法
### 方法一: 修改 StoreManager.swift (推荐)
1. **定位配置文件**
在 iOS 项目中找到:
```
iOS-SwiftUI-Code/ApplicationLibrary/Service/StoreManager.swift
```
2. **修改配置 URL**
找到 `configURL` 常量并修改:
```swift
public let configURL = "https://your-domain.com/api/config.php"
```
3. **重新编译应用**
在 Xcode 中按 `Cmd + B` 编译项目
### 方法二: 使用 Info.plist
1. **在 Info.plist 中添加配置**
```xml
<key>ConfigURL</key>
<string>https://your-domain.com/api/config.php</string>
```
2. **在代码中读取**
```swift
if let configURL = Bundle.main.infoDictionary?["ConfigURL"] as? String {
// 使用配置 URL
}
```
### iOS 网络请求示例
```swift
// StoreManager.swift
func fetchConfig() async throws -> ConfigResponse {
guard let url = URL(string: configURL) else {
throw URLError(.badURL)
}
var request = URLRequest(url: url)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue(Bundle.main.bundleIdentifier ?? "", forHTTPHeaderField: "bid")
request.addValue(appVersion, forHTTPHeaderField: "appver")
let (data, _) = try await URLSession.shared.data(for: request)
return try JSONDecoder().decode(ConfigResponse.self, from: data)
}
// 数据模型
struct ConfigResponse: Codable {
let baseURL: String
let baseDYURL: String
let mainregisterURL: String
let paymentURL: String
let telegramurl: String
let kefuurl: String
let websiteURL: String
let crisptoken: String
let banners: [String]
let message: String
let code: Int
}
```
---
## 测试与验证
### 测试配置地址
项目提供了测试配置地址供开发调试使用:
```
https://vungles.com/api/test/config
```
**测试账号**:
- 邮箱: `binance3980@gmail.com`
- 密码: `Tinkl123`
### 验证配置是否正确
1. **浏览器测试**
在浏览器中访问你的配置 URL,确认返回正确的 JSON 格式
2. **Postman 测试**
使用 Postman 发送 GET 请求,验证响应内容
3. **应用内测试**
- 启动应用
- 检查是否能正常加载首页横幅图
- 测试登录/注册功能
- 验证节点列表加载
### 错误排查
如果配置加载失败:
1. **检查 URL 是否正确**
```
https://your-domain.com/api/config.php
```
2. **检查 HTTPS 证书**
- 确保证书有效且未过期
- 使用可信 CA 签发的证书
3. **检查 JSON 格式**
- 使用 JSON 格式化工具验证
- 确保所有必填字段都存在
4. **检查 CORS 配置** (Web 端)
- 服务器需要配置正确的 CORS 头
---
## 部署配置服务器
### 方案一: 静态 JSON 文件 (推荐)
将配置 JSON 存储在 CDN 或对象存储服务:
1. **创建配置文件**
创建 `config.json`:
```json
{
"baseURL": "https://api.yourdomain.com/api/v1/",
"baseDYURL": "https://api.yourdomain.com/api/vpnnodes.php",
"mainregisterURL": "https://yourdomain.com/#/register?code=",
"paymentURL": "https://pay.yourdomain.com/",
"telegramurl": "https://t.me/yourchannel",
"kefuurl": "https://yourdomain.com/support/",
"websiteURL": "https://yourdomain.com/",
"crisptoken": "your-crisp-token",
"banners": [
"https://cdn.yourdomain.com/banner1.png"
],
"message": "OK",
"code": 1
}
```
2. **上传到对象存储**
- 阿里云 OSS
- 腾讯云 COS
- AWS S3
- Cloudflare R2
3. **启用 HTTPS**
确保存储桶启用 HTTPS 访问
### 方案二: 动态 API 接口
使用后端服务动态返回配置:
**PHP 示例**:
```php
<?php
// config.php
header('Content-Type: application/json');
$config = [
'baseURL' => 'https://api.yourdomain.com/api/v1/',
'baseDYURL' => 'https://api.yourdomain.com/api/vpnnodes.php',
'mainregisterURL' => 'https://yourdomain.com/#/register?code=',
'paymentURL' => 'https://pay.yourdomain.com/',
'telegramurl' => 'https://t.me/yourchannel',
'kefuurl' => 'https://yourdomain.com/support/',
'websiteURL' => 'https://yourdomain.com/',
'crisptoken' => 'your-crisp-token',
'banners' => [
'https://cdn.yourdomain.com/banner1.png',
'https://cdn.yourdomain.com/banner2.png'
],
'message' => 'OK',
'code' => 1
];
echo json_encode($config, JSON_PRETTY_PRINT);
?>
```
**Node.js 示例**:
```javascript
// config.js
const express = require('express');
const app = express();
app.get('/api/config', (req, res) => {
res.json({
baseURL: 'https://api.yourdomain.com/api/v1/',
baseDYURL: 'https://api.yourdomain.com/api/vpnnodes.php',
mainregisterURL: 'https://yourdomain.com/#/register?code=',
paymentURL: 'https://pay.yourdomain.com/',
telegramurl: 'https://t.me/yourchannel',
kefuurl: 'https://yourdomain.com/support/',
websiteURL: 'https://yourdomain.com/',
crisptoken: 'your-crisp-token',
banners: [
'https://cdn.yourdomain.com/banner1.png'
],
message: 'OK',
code: 1
});
});
app.listen(3000, () => {
console.log('Config server running on port 3000');
});
```
---
## 安全建议
### 1. 使用 HTTPS
- 所有 API 端点必须使用 HTTPS
- 使用可信 CA 签发的 SSL 证书
- 禁用 HTTP 访问
### 2. 请求验证
在请求头中添加验证信息:
```kotlin
// Android
request.addHeader("bid", BuildConfig.APPLICATION_ID)
request.addHeader("appver", BuildConfig.VERSION_NAME)
request.addHeader("device", android.os.Build.MODEL)
```
```swift
// iOS
request.addValue(Bundle.main.bundleIdentifier ?? "", forHTTPHeaderField: "bid")
request.addValue(appVersion, forHTTPHeaderField: "appver")
request.addValue(UIDevice.current.model, forHTTPHeaderField: "device")
```
### 3. 服务器端验证
在服务器端验证请求来源:
```php
<?php
$bid = $_SERVER['HTTP_BID'] ?? '';
$appver = $_SERVER['HTTP_APPVER'] ?? '';
// 验证 Bundle ID
$allowedBids = ['com.yourcompany.uuvpn', 'com.yourcompany.uuvpn.dev'];
if (!in_array($bid, $allowedBids)) {
http_response_code(403);
exit(json_encode(['code' => 0, 'message' => 'Unauthorized']));
}
// 返回配置
// ...
?>
```
### 4. 敏感信息保护
- 不要在配置文件中存储密钥或密码
- 使用 token 而非明文密码
- 定期更新 Crisp token
---
## iOS App Store 审核配置
### 审核模式配置
iOS 应用提交 App Store 审核时,需要隐藏外部支付链接:
```json
{
"baseURL": "https://api.yourdomain.com/api/v1/",
"baseDYURL": "https://api.yourdomain.com/api/vpnnodes.php",
"mainregisterURL": "https://yourdomain.com/#/register?code=",
"paymentURL": "xx",
"telegramurl": "https://t.me/yourchannel",
"kefuurl": "https://yourdomain.com/support/",
"websiteURL": "https://yourdomain.com/",
"crisptoken": "your-crisp-token",
"banners": [],
"message": "OK",
"code": 1
}
```
**关键点**:
- `paymentURL` 设置为 `"xx"` 或 `"xxx"` (长度 ≤ 3)
- `banners` 设置为空数组,避免显示包含支付信息的横幅
### 审核通过后配置
审核通过后,将配置改回正常模式:
```json
{
"paymentURL": "https://pay.yourdomain.com/",
"banners": [
"https://cdn.yourdomain.com/banner1.png"
]
}
```
---
## 常见问题
### Q1: 修改配置后应用没有更新怎么办?
**A**: 清除应用缓存或重新安装应用
**Android**:
```bash
adb shell pm clear com.yourcompany.uuvpn
```
**iOS**: 卸载重装应用
### Q2: 配置请求超时怎么办?
**A**: 检查以下内容:
- 服务器是否正常运行
- HTTPS 证书是否有效
- 网络连接是否正常
- 考虑使用 CDN 加速
### Q3: 如何在不重新编译的情况下更改配置?
**A**: 配置是从远程 URL 动态获取的,只需修改服务器端的配置 JSON 即可,应用会在下次启动时自动获取最新配置
### Q4: 能否使用多个配置 URL?
**A**: 可以,通过在不同的编译变体中使用不同的配置 URL:
**Android**:
```kotlin
object ApiConfig {
val CONFIG_URL = when (BuildConfig.BUILD_TYPE) {
"debug" -> "https://dev.yourdomain.com/api/config.php"
"release" -> "https://yourdomain.com/api/config.php"
else -> "https://yourdomain.com/api/config.php"
}
}
```
**iOS**:
```swift
#if DEBUG
let configURL = "https://dev.yourdomain.com/api/config.php"
#else
let configURL = "https://yourdomain.com/api/config.php"
#endif
```
### Q5: 配置加载失败应用会崩溃吗?
**A**: 不会,应用应该有默认配置作为后备:
```kotlin
// Android
fun getConfig(): ConfigResponse {
return try {
apiService.getConfig(configURL)
} catch (e: Exception) {
// 返回默认配置
getDefaultConfig()
}
}
```
```swift
// iOS
func loadConfig() async {
do {
config = try await fetchConfig()
} catch {
// 使用默认配置
config = getDefaultConfig()
}
}
```
---
## 总结
配置 API 端点是 UUVPN 应用的核心步骤,正确的配置能确保应用正常工作:
1. ✅ 准备好 V2Board 服务器
2. ✅ 创建配置 JSON 文件
3. ✅ 部署到 HTTPS 服务器
4. ✅ 在应用中配置 URL
5. ✅ 测试验证配置
6. ✅ 针对iOS审核特殊处理
如有问题,请参考 [GitHub Issues](https://github.com/nicolastinkl/UUVPN/issues) 或联系技术支持。
+4
View File
@@ -0,0 +1,4 @@
[submodule "clash-foss"]
path = core/src/foss/golang/clash
url = https://github.com/MetaCubeX/mihomo
branch = Meta
+144
View File
@@ -0,0 +1,144 @@
## UUVPN
[https://youtube.com/shorts/zI1hrpFJbtg?feature=share](https://youtube.com/shorts/zI1hrpFJbtg?feature=share)
### Requirement
- Android 5.0+ (minimum)
- Android 7.0+ (recommend)
- `armeabi-v7a` , `arm64-v8a`, `x86` or `x86_64` Architecture
### Build
1. Update submodules
```bash
git submodule update --init --recursive
```
2. Install **OpenJDK 11**, **Android SDK**, **CMake** and **Golang**
3. Create `local.properties` in project root with
```properties
sdk.dir=/path/to/android-sdk
```
4. Create `signing.properties` in project root with
```properties
keystore.path=/path/to/keystore/file
keystore.password=<key store password>
key.alias=<key alias>
key.password=<key password>
```
5. Build
```bash
./gradlew app:assembleMeta-AlphaRelease
```
6. 教程:修改 API 后台地址,api请求内容如下 , 可选增加订阅地址转换地址,方便调用。
Please read 服务器配置+Kotlin-Android 文档教程.docx
![](images_05_20_11.png)
```
返回值可以直接写在阿里云oss 中,速度更快
{
"baseURL": "https://api.0008.uk/api/v1/",
"baseDYURL": "https://api.gooapis.com/api/vpnnodes.php",
"mainregisterURL": "https://lelian.app/#/register?code=",
"paymentURL": "xxxxx",
"telegramurl": "https://t.me/fastlink",
"kefuurl": "https://gooapis.com/fastlink/",
"websiteURL": "https://gooapis.com/fastlink/",
"crisptoken": "5546c6ea-4b1e-41bc-80e4-4b6648cbca76",
"banners": [
"https://image.gooapis.com/api/images/12-11-56.png",
"https://image.gooapis.com/api/images/12-44-57.png",
"https://image.gooapis.com/api/images/12-47-03.png"
],
"message": "OK",
"code": 1
}
```
![alt text](telegram-cloud-photo-size-5-6327623898581027724-y.jpg)
```
测试的 URL 配置地址: https://vungles.com/api/test/config
测试账号:
binance3980@gmail.com
Tinkl123
```
上面字段的意思:
以下是您提供的 JSON 配置文件的字段解释:
### 1. **baseURL**
- **描述**:基础 API URL,用于访问主要的 API 端点。
- **示例值**`https://api.0008.uk/api/v1/`
- **用途**:所有与服务器的 API 请求都将基于此 URL 进行。例如,可以在此基础上附加不同的 API 路径来访问特定资源。
### 2. **baseDYURL**
- **描述**:另一个 API URL,与 `baseURL` 不同,用于访问特定的 VPN默认测试节点。
- **示例值**`https://api.gooapis.com/api/vpnnodes.php`
- **用途**:通常用于请求特定的 VPN 节点数据或其他与代理/网络连接相关的信息。
### 3. **mainregisterURL**
- **描述**:用户注册页面推广的 URL,通常包含一个唯一的代码参数,用于推广注册过程。
- **示例值**`https://lelian.app/#/register?code=`
- **用途**:这个 URL 用于引导用户进行注册,`code` 参数可能是用户的注册邀请码或其他标识符。
### 4. **paymentURL**
- **描述**:支付相关的 URL,可能指向支付网关或交易页面。
- **示例值**`xxxxx`
- **用途**:用于支付的跳转链接,具体地址没有提供。
### 5. **telegramurl**
- **描述**:指向 Telegram 频道或群组的 URL。
- **示例值**`https://t.me/fastlink`
- **用途**:用户可以通过此链接访问 Telegram 频道,可能用于接收通知、更新或客服支持。
### 6. **kefuurl**
- **描述**:客服支持页面的 URL。
- **示例值**`https://gooapis.com/fastlink/`
- **用途**:提供给用户访问的在线客服页面,通常是支持问题解答和帮助解决问题的页面。
### 7. **websiteURL**
- **描述**:指向网站首页的 URL。
- **示例值**`https://gooapis.com/fastlink/`
- **用途**:指向官方网站,用户可以通过该链接访问相关产品或服务的详细信息。
### 8. **crisptoken**
- **描述**:一个唯一的令牌,通常用于身份验证或授权。
- **示例值**`5546c6ea-4b1e-41bc-80e4-4b6648cbca76`
- **用途**:令牌用于验证请求的合法性,防止未经授权的访问。可能用于 API 请求中传递身份信息。
### 9. **banners**
- **描述**:一个包含多个图片 URL 的数组,通常用于在网页或应用中展示横幅广告或图片。
- **示例值**
```json
[
"https://image.gooapis.com/api/images/12-11-56.png",
"https://image.gooapis.com/api/images/12-44-57.png",
"https://image.gooapis.com/api/images/12-47-03.png"
]
```
- **用途**:这些链接指向可以展示在前端界面上的横幅图片,可以是广告、通知、促销等。
### 10. **message**
- **描述**:一个消息字段,通常用于描述当前操作或请求的状态。
- **示例值**`OK`
- **用途**:这是 API 响应中的状态信息,`OK` 表示操作成功。
### 11. **code**
- **描述**:一个数字代码,表示响应的状态。
- **示例值**`1`
- **用途**:通常用于表示请求是否成功,`1` 可能表示操作成功,其他数字可能表示错误或特定的状态。
+33 -2
View File
@@ -6,11 +6,13 @@ plugins {
kotlin("android")
kotlin("kapt")
id("com.android.application")
// kotlin("jvm") version "1.8.10" // 使用兼容的 Kotlin 版本
}
dependencies {
compileOnly(project(":hideapi"))
implementation(project(":core"))
implementation(project(":service"))
implementation(project(":design"))
@@ -24,6 +26,25 @@ dependencies {
implementation(libs.androidx.coordinator)
implementation(libs.androidx.recyclerview)
implementation(libs.google.material)
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")
// https://mvnrepository.com/artifact/androidx.appcompat/appcompat
// implementation(files("libs/appcompat-1.7.0.aar"))
// implementation("im.crisp:crisp-sdk:2.0.5")
// implementation("im.crisp:crisp-sdk:2.0.5")
// If you're using AndroidX
//implementation("androidx.multidex:multidex:2.0.1")
// If you're not using AndroidX
//implementation ("com.android.support:multidex:1.0.3")
// implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
}
tasks.getByName("clean", type = Delete::class) {
@@ -78,4 +99,14 @@ afterEvaluate {
tasks.getByName("clean", type = Delete::class) {
delete(file(geoFilesDownloadDir))
}
}
android {
buildTypes {
getByName("release") {
signingConfig = signingConfigs.getByName("debug")
}
getByName("debug") {
signingConfig = signingConfigs.getByName("debug")
}
}
}
Binary file not shown.
@@ -9,11 +9,14 @@
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<!-- 网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
@@ -21,6 +24,7 @@
<application
android:name=".MainApplication"
android:allowBackup="true"
android:usesCleartextTraffic="true"
android:banner="@mipmap/ic_launcher"
android:fullBackupContent="@xml/full_backup_content"
android:icon="@mipmap/ic_launcher"
@@ -38,12 +42,15 @@
android:name="releaseCode"
android:value="@integer/release_code" />
<activity
android:name=".MainActivity"
android:configChanges="uiMode"
android:exported="true"
android:label="@string/launch_name"
android:launchMode="singleTop">
android:launchMode="singleTop"
android:theme="@style/SplashTheme" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -66,9 +73,9 @@
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="clash"/>
<data android:scheme="clashmeta"/>
<data android:host="install-config"/>
<data android:scheme="clash-----------"/>
<data android:scheme="clashmeta-----------"/>
<data android:host="install-config-----------"/>
</intent-filter>
<intent-filter>
<action android:name="com.github.metacubex.clash.meta.action.START_CLASH" />
@@ -83,6 +90,9 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity
android:name=".ApkBrokenActivity"
android:configChanges="uiMode"
@@ -170,14 +180,109 @@
android:exported="false"
android:label="@string/files" />
<activity
android:name=".ProfileActivity"
android:configChanges="uiMode"
android:exported="false"
android:label="设置中心"
/>
<activity
android:name=".LoginActivity"
android:configChanges="uiMode"
android:exported="false"
/>
<activity
android:name=".SplashActivity"
android:configChanges="uiMode"
android:exported="true" />
<activity
android:name=".PlansActivity"
android:configChanges="uiMode"
android:exported="false"
android:label="购买订阅"
/>
<activity
android:name=".MyOrdersActivity"
android:configChanges="uiMode"
android:exported="false"
android:label="我的订单"
/>
<activity
android:name=".ConfigOrderActivity"
android:configChanges="uiMode"
android:exported="false"
android:label="配置订阅"
/>
<activity
android:name=".SubmitOrderActivity"
android:configChanges="uiMode"
android:exported="false"
android:label="订单详情"
/>
<activity
android:name=".GongdanActivity"
android:configChanges="uiMode"
android:exported="false"
android:label="我的工单"
/>
<activity
android:name=".NewGongdanActivity"
android:configChanges="uiMode"
android:exported="false"
android:label="新建工单"
/>
<activity
android:name=".H5WebActivity"
android:configChanges="uiMode"
android:exported="false"
/>
<activity
android:name=".TicketDetailActivity"
android:configChanges="uiMode"
android:exported="false"
android:label=""
/>
<activity
android:name=".RegisterActivity"
android:configChanges="uiMode"
android:exported="false"
android:label="注册"
/>
<activity
android:name=".InvitationActivity"
android:configChanges="uiMode"
android:exported="false"
android:label="我的邀请"
/>
<service
android:name=".LogcatService"
android:exported="false"
android:label="@string/clash_logcat" />
<service
android:name=".TileService"
android:exported="true"
android:icon="@drawable/ic_logo_service"
android:foregroundServiceType="connectedDevice"
android:label="@string/launch_name"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
{"v":"5.9.3","fr":29.9700012207031,"ip":0,"op":60.0000024438501,"w":1920,"h":1080,"nm":"1_Loading_Line","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Blue Line","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[960,539,0],"ix":2,"l":2},"a":{"a":0,"k":[0,-1,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[800,800],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.212477396049,0.540471813725,0.898039215686,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":80,"ix":5},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-1],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.9],"y":[0]},"t":15,"s":[0]},{"t":60.0000024438501,"s":[99.9]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.1],"y":[1]},"o":{"x":[0.9],"y":[0]},"t":0,"s":[0.01]},{"t":45.0000018328876,"s":[100]}],"ix":2},"o":{"a":0,"k":-1,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":150.000006109625,"st":0,"ct":1,"bm":0}],"markers":[{"tm":60.0000024438501,"cm":"END (No Loop)","dr":0}]}
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

@@ -84,6 +84,7 @@ abstract class BaseActivity<D : Design<*>> : AppCompatActivity(),
}
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.BootstrapTheme)
super.onCreate(savedInstanceState)
applyDayNight()
@@ -189,11 +190,14 @@ abstract class BaseActivity<D : Design<*>> : AppCompatActivity(),
}
private fun applyDayNight(config: Configuration = resources.configuration) {
val dayNight = queryDayNight(config)
// val dayNight = theme.applyStyle(R.style.AppThemeLight, true) //默认白天模式
/*
val dayNight = queryDayNight(config)
when (dayNight) {
DayNight.Night -> theme.applyStyle(R.style.AppThemeDark, true)
DayNight.Day -> theme.applyStyle(R.style.AppThemeLight, true)
}
}*/
window.isAllowForceDarkCompat = false
window.isSystemBarsTranslucentCompat = true
@@ -209,7 +213,7 @@ abstract class BaseActivity<D : Design<*>> : AppCompatActivity(),
window.isLightNavigationBarCompat = resolveThemedBoolean(android.R.attr.windowLightNavigationBar)
}
this.dayNight = dayNight
this.dayNight = DayNight.Night //dayNight
}
enum class Event {
@@ -0,0 +1,47 @@
package com.github.kr328.clash
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.common.util.ticker
import com.github.kr328.clash.design.ConfigOrderDesign
import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.design.ProfileDesign
import com.github.kr328.clash.network.PlanData
import com.github.kr328.clash.utity.LoadingDialog
import kotlinx.coroutines.isActive
import kotlinx.coroutines.selects.select
import java.util.concurrent.TimeUnit
//ConfigOrderDesign
class ConfigOrderActivity : BaseActivity<ConfigOrderDesign>() {
override suspend fun main() {
// 获取 Intent 并接收传递的 PlanData 对象
// 使用 Parcelable 获取对象
val planData = intent.getSerializableExtra("planData") as? PlanData
val design = ConfigOrderDesign(this)
setContentDesign(design)
design.fillData(planData)
val ticker = ticker(TimeUnit.SECONDS.toMillis(1))
while (isActive) {
select<Unit> {
events.onReceive {
println("events : " + it.name)
}
design.requests.onReceive {
println("requests : " + it.name)
when (it) {
ConfigOrderDesign.Request.SubmitOrder -> {
startActivity(SubmitOrderActivity::class.intent.putExtra("trade_no",design.trade_no))
}
}
}
}
}
}
}
@@ -0,0 +1,157 @@
package com.github.kr328.clash
import android.app.Activity
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.view.Gravity
import android.widget.Button
import android.widget.FrameLayout
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.RecyclerView
import com.github.kr328.clash.design.BaseListActivity
import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.design.adapter.TicketsDataAdapter
import com.github.kr328.clash.design.util.showCustomDialog
import com.github.kr328.clash.network.ApiClient
import com.github.kr328.clash.network.ApiService
import com.github.kr328.clash.network.TicketsData
import com.github.kr328.clash.network.safeApiRequestCall
import com.github.kr328.clash.utity.LoadingDialog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class GongdanActivity : BaseListActivity<TicketsData>() {
override fun createAdapter(): RecyclerView.Adapter<*> {
return TicketsDataAdapter(onItemClick = { planData ->
// 在这里处理 item 点击事件
val intent = Intent(this, TicketDetailActivity::class.java)
intent.putExtra("ticketid", planData.id ?: 0)
startActivity(intent)
}, onCloseItemClick = { planData ->
showCustomDialog(
title = "提示",
message = "确定关闭当前工单吗?",
positiveButtonText = "确定",
negativeButtonText = "取消",
onPositiveClick = {
// 执行确认操作
// startActivity(PlansActivity::class.intent)
CoroutineScope(Dispatchers.IO).launch {
val apiService = ApiClient.retrofit.create(ApiService::class.java)
val fieldMap = mutableMapOf<String, Int>()
fieldMap.put("id",planData.id ?: 0)
safeApiRequestCall {
apiService.closeTicket(
PreferenceManager.loginauthData,
request = fieldMap
)
}.let {
withContext(Dispatchers.Main) {
LoadingDialog.hide()
}
if (it != null && it.isSuccessful) {
withContext(Dispatchers.Main) {
Toast.makeText(this@GongdanActivity, "关闭成功", Toast.LENGTH_SHORT).show()
}
refereshData()
}else{
withContext(Dispatchers.Main) {
Toast.makeText(
this@GongdanActivity,
"关闭失败",
Toast.LENGTH_SHORT
).show()
}
}
}
}
},
onNegativeClick = {
// 执行取消操作
}
)
})
}
override fun onCreate() {
// Create the top-right icon
val iconView = Button(this)
iconView.setText("新建工单")
iconView.layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT,
Gravity.TOP or Gravity.END
).apply {
marginEnd = 16 // Add margin to the right
topMargin = 16 // Add margin to the top
}
// Set the background tint programmatically
/* ViewCompat.setBackgroundTintList(
iconView,
ContextCompat.getColorStateList(this, R.color.blue)
)
*/
val colorStateList: ColorStateList = ColorStateList(
arrayOf<IntArray>(intArrayOf(android.R.attr.state_pressed)), intArrayOf(
R.color.blue
)
)
iconView.setBackgroundTintList(colorStateList)
iconView.setOnClickListener {
val intent = Intent(this@GongdanActivity, NewGongdanActivity::class.java)
//startActivity(intent)
resultLauncher.launch(intent)
}
mainBinding?.activityBarLayout?.addView(iconView)
}
private val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
val data = result.data
val resultData = data?.getStringExtra("resultKey")
// Process the result
println("registerForActivityResult : resultData : ${resultData} ")
if (resultData == "shuaxin"){
refereshData()
}
}
}
override suspend fun loadData(page: Int, callback: (List<TicketsData>?, Throwable?) -> Unit) {
// android:backgroundTint="@color/blue"
// android:background="@drawable/rounded_button_background"
val apiService = ApiClient.retrofit.create(ApiService::class.java)
safeApiRequestCall { apiService.getTickets(PreferenceManager.loginauthData)}.let {
if (it != null && it.isSuccessful) {
callback(it.body()?.data,null)
}else{
callback(null, Throwable("请求失败"))
}
}
}
}
@@ -0,0 +1,37 @@
package com.github.kr328.clash
import android.content.Intent
import com.github.kr328.clash.design.H5WebViewDesign
import com.github.kr328.clash.design.PreferenceManager
class H5WebActivity : BaseActivity<H5WebViewDesign>() {
override suspend fun main() {
val design = H5WebViewDesign(this) {
startActivity(Intent(Intent.ACTION_VIEW).setData(it))
//Uri.parse(PreferenceManager.getConfigFromPreferences(this@ProfileActivity)?.telegramurl)
}
setContentDesign(design)
val url = intent.getStringExtra("url")
if (url != null && url.length > 1){
title = url
design.refereshApplyFrom()
design.breaseURL(url)
}else{
PreferenceManager.getConfigFromPreferences(this@H5WebActivity)?.kefuurl?.let {
design.breaseURL(
it
)
}
}
// while (isActive) {
// events.receive()
// }
}
}
@@ -6,14 +6,14 @@ import kotlinx.coroutines.isActive
class HelpActivity : BaseActivity<HelpDesign>() {
override suspend fun main() {
val design = HelpDesign(this) {
startActivity(Intent(Intent.ACTION_VIEW).setData(it))
}
setContentDesign(design)
while (isActive) {
events.receive()
}
// val design = HelpDesign(this) {
// startActivity(Intent(Intent.ACTION_VIEW).setData(it))
// }
//
// setContentDesign(design)
//
// while (isActive) {
// events.receive()
// }
}
}
@@ -0,0 +1,24 @@
package com.github.kr328.clash
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.common.util.ticker
import com.github.kr328.clash.design.InvitationDesign
import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.design.ProfileDesign
import kotlinx.coroutines.isActive
import kotlinx.coroutines.selects.select
import java.util.concurrent.TimeUnit
class InvitationActivity : BaseActivity<InvitationDesign>() {
override suspend fun main() {
PreferenceManager.init(this)
val design = InvitationDesign(this)
setContentDesign(design)
design.requestdataDisible()
}
}
@@ -0,0 +1,267 @@
package com.github.kr328.clash
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import android.text.InputType
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.github.kr328.clash.common.compat.isAllowForceDarkCompat
import com.github.kr328.clash.common.compat.isLightNavigationBarCompat
import com.github.kr328.clash.common.compat.isLightStatusBarsCompat
import com.github.kr328.clash.common.compat.isSystemBarsTranslucentCompat
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.design.databinding.ActivityLoginBinding
import com.github.kr328.clash.design.databinding.ActivityProfileBinding
import com.github.kr328.clash.design.ui.DayNight
import com.github.kr328.clash.design.ui.ToastDuration
import com.github.kr328.clash.design.util.layoutInflater
import com.github.kr328.clash.design.util.resolveThemedBoolean
import com.github.kr328.clash.design.util.resolveThemedColor
import com.github.kr328.clash.design.util.root
import com.github.kr328.clash.network.ApiClient
import com.github.kr328.clash.network.ApiClientConfig
import com.github.kr328.clash.network.ApiService
import com.github.kr328.clash.network.ConfigResponse
import com.github.kr328.clash.network.LoginRequest
import com.github.kr328.clash.network.LoginResponse
import com.github.kr328.clash.network.safeApiCall
import com.github.kr328.clash.network.safeApiRequestCall
import com.github.kr328.clash.utity.LoadingDialog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONException
import org.json.JSONObject
class LoginActivity : AppCompatActivity() {
private lateinit var apiService: ApiService
private var dayNight: DayNight = DayNight.Day
@Deprecated("Deprecated in Java")
override fun onBackPressed() {
// 禁用返回键
// 不调用 super.onBackPressed()
}
private var isPasswordVisible = false
private var configRetryCount = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityLoginBinding
.inflate(this.layoutInflater, this.root, false)
setContentView(binding.root)
applyDayNight()
PreferenceManager.init(this)
binding.togglePasswordVisibility.setOnClickListener {
isPasswordVisible = !isPasswordVisible
if (isPasswordVisible) {
// 显示明文密码
binding.loginPasswordEditText.inputType = InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
binding.togglePasswordVisibility.setImageResource(R.drawable.visibility_24px) // 更改图标为显示状态
} else {
// 显示星号密码
binding.loginPasswordEditText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_PASSWORD
binding.togglePasswordVisibility.setImageResource(R.drawable.visibility_off_24px) // 更改图标为隐藏状态
}
// 将光标移动到文本的末尾
binding.loginPasswordEditText.setSelection(binding.loginPasswordEditText.text.length)
}
binding.loginLoginButton.setOnClickListener {
val email = binding.loginEmailEditText.text.toString()
val password = binding.loginPasswordEditText.text.toString()
val isAgreementChecked = binding.loginAgreementCheckBox.isChecked
if (email.isEmpty() || password.isEmpty()) {
Toast.makeText(this, "请输入邮箱和密码", Toast.LENGTH_SHORT).show()
} else if (!isAgreementChecked) {
Toast.makeText(this, "请同意隐私政策和用户协议", Toast.LENGTH_SHORT).show()
} else {
// Hide the keyboard
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val currentFocusView = currentFocus
if (currentFocusView != null) {
inputMethodManager.hideSoftInputFromWindow(currentFocusView.windowToken, 0)
}
performLogin(email, password)
}
}
binding.loginForgotPassword.setOnClickListener {
// Navigate to Forgot Password screen or show dialog
}
binding.loginRegister.setOnClickListener {
// Navigate to Register screen
//startActivity(RegisterActivity::class::intent)
// startActivity(RegisterActivity::class::intent)
val intent = Intent(this@LoginActivity, RegisterActivity::class.java)
startActivity(intent)
// finish() // It
}
apiService = ApiClientConfig.retrofit.create(ApiService::class.java)
//获取 Config 数据
getconfig()
}
private fun getconfig(){
runOnUiThread {
LoadingDialog.show(this, "正在初始化...")
}
CoroutineScope(Dispatchers.IO).launch {
safeApiCall {apiService.getConfig()}.let {
if (it != null) {
if (it.code == 1 ) {
withContext(Dispatchers.Main) {
LoadingDialog.hide()
}
PreferenceManager.saveConfigToPreferences( it)
//重新初始化 ApiClint
}
}else{
configRetryCount++
if (configRetryCount < 3) {
getconfig()
} else {
withContext(Dispatchers.Main) {
LoadingDialog.hide()
Toast.makeText(this@LoginActivity, "初始化失败,请检查配置是否正确", Toast.LENGTH_LONG).show()
}
}
}
}
}
}
/**
* withContext(Dispatchers.Main) {
* if (response.isSuccessful) {
* val userData = response.body()
* // 更新 UI
* } else {
* Toast.makeText(this@MainActivity, "Failed to fetch user data", Toast.LENGTH_SHORT).show()
* }
* }
* */
private fun applyDayNight(config: Configuration = resources.configuration) {
// val dayNight = theme.applyStyle(R.style.AppThemeLight, true) //默认白天模式
/*
val dayNight = queryDayNight(config)
when (dayNight) {
DayNight.Night -> theme.applyStyle(R.style.AppThemeDark, true)
DayNight.Day -> theme.applyStyle(R.style.AppThemeLight, true)
}*/
window.isAllowForceDarkCompat = false
window.isSystemBarsTranslucentCompat = true
window.statusBarColor = resolveThemedColor(android.R.attr.statusBarColor)
window.navigationBarColor = resolveThemedColor(android.R.attr.navigationBarColor)
if (Build.VERSION.SDK_INT >= 23) {
window.isLightStatusBarsCompat = resolveThemedBoolean(android.R.attr.windowLightStatusBar)
}
if (Build.VERSION.SDK_INT >= 27) {
window.isLightNavigationBarCompat = resolveThemedBoolean(android.R.attr.windowLightNavigationBar)
}
this.dayNight = DayNight.Night //dayNight
}
private fun performLogin(email: String, password: String) {
// Perform login logic here, possibly calling an API
// Show the loading indicator with a custom message
LoadingDialog.show(this, "正在登录...")
val apiServiceApp = ApiClient.retrofit.create(ApiService::class.java)
//获取 Config 数据
CoroutineScope(Dispatchers.IO).launch {
safeApiRequestCall {
apiServiceApp.loginUser(LoginRequest(email,password))}.let {
withContext(Dispatchers.Main) {
LoadingDialog.hide()
}
if (it != null &&
it.isSuccessful) {
val response: LoginResponse? = it.body()
println(response )
println(it.errorBody()?.string() ?: "")
if (response?.data != null){
PreferenceManager.loginemail = email
PreferenceManager.loginToken = response.data?.token ?: ""
PreferenceManager.loginauthData = response.data?.auth_data ?: ""
PreferenceManager.isLoginin = true
// saveLoginMailToken(this@LoginActivity,email)
// saveLoginToken(this@LoginActivity, response.data.token ?: "")
// saveLoginAuthData(this@LoginActivity, response.data.auth_data ?: "")
// saveLoginStatus(this@LoginActivity,true)
//获取用户信息
withContext(Dispatchers.Main) {
val intent = Intent(this@LoginActivity, MainActivity::class.java)
intent.flags =
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
finish() // It
}
}
}else{
// Handle error response, even if it's 422
try {
val errorResponse = it?.errorBody()?.string() ?: ""
print(errorResponse)
val errorJson = JSONObject(errorResponse)
val message = errorJson.optString("message")
// Process the error message or show it to the user
if (!message.isNullOrEmpty()) {
withContext(Dispatchers.Main) {
Toast.makeText(this@LoginActivity, "登录失败:${message}", Toast.LENGTH_LONG).show()
}
}
println( "Message: $message, ")
} catch (e: JSONException) {
// Handle JSON parsing error
withContext(Dispatchers.Main) {
Toast.makeText(this@LoginActivity, "登录失败:${e.message}", Toast.LENGTH_LONG).show()
}
}
}
}
}
}
}
@@ -1,10 +1,12 @@
package com.github.kr328.clash
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.widget.PopupMenu
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.github.kr328.clash.common.log.Log
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.common.util.setUUID
import com.github.kr328.clash.common.util.ticker
import com.github.kr328.clash.core.Clash
import com.github.kr328.clash.design.MainDesign
@@ -15,27 +17,54 @@ import com.github.kr328.clash.util.withClash
import com.github.kr328.clash.util.withProfile
import com.github.kr328.clash.core.bridge.*
import com.github.kr328.clash.core.model.FetchStatus
import com.github.kr328.clash.core.model.ProxySort
import com.github.kr328.clash.core.model.TunnelState
import com.github.kr328.clash.design.NewProfileDesign
import com.github.kr328.clash.design.PreferenceLiveData
import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.design.R
import com.github.kr328.clash.design.component.ProxyMenu
import com.github.kr328.clash.design.dialog.requestModelTextInput
import com.github.kr328.clash.design.dialog.withModelProgressBar
import com.github.kr328.clash.design.network.APIGlobalObject
import com.github.kr328.clash.design.util.ValidatorHttpUrl
import com.github.kr328.clash.design.util.root
import com.github.kr328.clash.design.util.showCustomDialog
import com.github.kr328.clash.design.util.showExceptionToast
import com.github.kr328.clash.network.ApiClient
import com.github.kr328.clash.network.ApiClientConfig
import com.github.kr328.clash.network.ApiService
import com.github.kr328.clash.network.ConfigResponse
import com.github.kr328.clash.network.LoginRequest
import com.github.kr328.clash.network.SubscribeData
import com.github.kr328.clash.network.SubscribeResponse
import com.github.kr328.clash.network.safeApiCall
import com.github.kr328.clash.network.safeApiRequestCall
import com.github.kr328.clash.service.ProfileManager
import com.github.kr328.clash.service.model.Profile
import com.github.kr328.clash.service.remote.IRemoteService
import com.github.kr328.clash.service.util.PreferenceManagerServer
import com.github.kr328.clash.utity.LoadingDialog
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.selects.select
import kotlinx.coroutines.withContext
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.concurrent.timer
class MainActivity : BaseActivity<MainDesign>() {
private lateinit var apiService: ApiService
private var subData: SubscribeData? = null
private val selectnodeName: LiveData<String> by lazy {
PreferenceLiveData(this)
@@ -44,100 +73,372 @@ class MainActivity : BaseActivity<MainDesign>() {
override suspend fun main() {
val design = MainDesign(this)
PreferenceManager.init(this)
if (PreferenceManager.isLoginin){
setContentDesign(design)
setContentDesign(design)
design.fetch()
design.startBannsers()
design.fetch()
design.startBannsers()
selectnodeName.observe(this, androidx.lifecycle.Observer { newValue ->
})
//1 : 查询 config 信息
val ticker = ticker(TimeUnit.SECONDS.toMillis(1))
apiService = ApiClientConfig.retrofit.create(ApiService::class.java)
//获取 Config 数据
// CoroutineScope(Dispatchers.IO).launch { }
val currentTime = System.currentTimeMillis()
val cacheExpiryTime = 10 * 60 * 1000 // 10 minutes in milliseconds
// 如果缓存数据存在且未超过 10 分钟,使用缓存数据;否则,重新请求数据
if ( PreferenceManager.cached_data != null && (currentTime - PreferenceManager.cache_timestamp) <= cacheExpiryTime) {
// 使用缓存数据
val gson = Gson()
val configResponse = gson.fromJson(PreferenceManager.cached_data, ConfigResponse::class.java)
println(configResponse)
println("Using cached data: ${PreferenceManager.cached_data}")
println("Using cached_userSubscritedata : ${PreferenceManager.cached_userSubscritedata}")
if (PreferenceManager.cached_userSubscritedata.length > 0){
val subcache = gson.fromJson(PreferenceManager.cached_userSubscritedata, SubscribeResponse::class.java)
subData = subcache.data
//存储到本地
val plainid = subcache.data.plan_id ?: 0
// 或者检查使用量
val guoqi = (subcache.data.u ?: 0) + (subcache.data.d ?: 0) > (subcache.data.transfer_enable ?: 0) && (subcache.data.transfer_enable ?: 0) > 0
if ( plainid == 0 || guoqi ) {
//提示到期
//删除所有节点
withProfile {
var allProfiles = queryAll()
println(allProfiles)
//删除所有节点
if (allProfiles.isNotEmpty() && allProfiles.count() > 0){
allProfiles.forEach {
delete(it.uuid)
println("Deleted ${it.uuid} ${it.name}: ${it.source}")
}
}
}
//删除所有节点文件
withContext(Dispatchers.Main) {
showCustomDialog(
title = "提示",
message = "您的订阅已到期,请续费订阅开启服务",
positiveButtonText = "确定",
negativeButtonText = "取消",
onPositiveClick = {
// 执行确认操作
startActivity(PlansActivity::class.intent)
},
onNegativeClick = {
// 执行取消操作
}
)
}
while (isActive) {
select<Unit> {
events.onReceive {
when (it) {
Event.ActivityStart,
Event.ServiceRecreated,
Event.ClashStop, Event.ClashStart,
Event.ProfileLoaded, Event.ProfileChanged -> design.fetch()
else -> Unit
}
}
design.requests.onReceive {
when (it) {
MainDesign.Request.ToggleStatus -> {
if (clashRunning)
stopClashService()
else
design.startClash()
}
MainDesign.Request.OpenProxy ->
startActivity(ProxyActivity::class.intent)
MainDesign.Request.OpenProfiles ->
startActivity(ProfilesActivity::class.intent)
MainDesign.Request.OpenProviders ->
startActivity(ProvidersActivity::class.intent)
MainDesign.Request.OpenLogs ->
startActivity(LogsActivity::class.intent)
MainDesign.Request.OpenSettings ->
startActivity(SettingsActivity::class.intent)
MainDesign.Request.OpenHelp ->
startActivity(HelpActivity::class.intent)
MainDesign.Request.OpenAbout ->
design.showAbout(queryAppVersionName())
MainDesign.Request.OpenSettingsDIY ->
startActivity(NetworkSettingsActivity::class.intent)
MainDesign.Request.OpenSettingsKEFU ->
startActivity(HelpActivity::class.intent)
MainDesign.Request.OpenModeDirect ->
withClash {
val o = queryOverride(Clash.OverrideSlot.Session)
o.mode = TunnelState.Mode.Direct
patchOverride(Clash.OverrideSlot.Session, o)
println("subData ${subData}")
}else{
// 缓存已过期,重新请求数据
println("Cache expired, requesting new data")
// 重新请求数据
//requestNewData()
LoadingDialog.show(this, "正在更新节点数据...")
configRequesting()
}
selectnodeName.observe(this, androidx.lifecycle.Observer { newValue ->
})
val ticker = ticker(TimeUnit.SECONDS.toMillis(1))
while (isActive) {
select<Unit> {
events.onReceive {
when (it) {
Event.ActivityStart,
Event.ServiceRecreated,
Event.ClashStop, Event.ClashStart,
Event.ProfileLoaded, Event.ProfileChanged -> design.fetch()
else -> Unit
}
}
design.requests.onReceive {
when (it) {
MainDesign.Request.ToggleStatus -> {
if (clashRunning)
stopClashService()
else
design.startClash()
}
MainDesign.Request.OpenProxy ->
startActivity(ProxyActivity::class.intent)
MainDesign.Request.OpenProfiles ->
startActivity(ProfilesActivity::class.intent)
MainDesign.Request.OpenProviders ->
startActivity(ProvidersActivity::class.intent)
MainDesign.Request.OpenLogs ->
startActivity(LogsActivity::class.intent)
MainDesign.Request.OpenSettings ->
startActivity(SettingsActivity::class.intent)
MainDesign.Request.OpenHelp ->
startActivity(HelpActivity::class.intent)
MainDesign.Request.OpenAbout ->
design.showAbout(queryAppVersionName())
MainDesign.Request.OpenSettingsDIY ->
startActivity(ProfileActivity::class.intent)
MainDesign.Request.OpenSettingsKEFU ->
startActivity(PlansActivity::class.intent)
MainDesign.Request.OpenModeDirect ->
withClash {
PreferenceManager.modeName = TunnelState.Mode.Direct.name
val override = queryOverride(Clash.OverrideSlot.Session)
override.mode = TunnelState.Mode.Direct
patchOverride(Clash.OverrideSlot.Session, override)
}
MainDesign.Request.OpenModeGlobal -> withClash {
CoroutineScope(Dispatchers.Main).launch {
PreferenceManager.modeName = TunnelState.Mode.Global.name
LoadingDialog.show(this@MainActivity, "正在切换中...")
val names = withClash { queryProxyGroupNames(uiStore.proxyExcludeNotSelectable) }
withClash {
val o = queryOverride(Clash.OverrideSlot.Session)
o.mode = TunnelState.Mode.Global
patchOverride(Clash.OverrideSlot.Session, o)
}
startServer()
}
}
MainDesign.Request.OpenModeRule -> withClash {
CoroutineScope(Dispatchers.Main).launch {
PreferenceManager.modeName = TunnelState.Mode.Rule.name
LoadingDialog.show(this@MainActivity, "正在切换中...")
withClash {
val o = queryOverride(Clash.OverrideSlot.Session)
o.mode = TunnelState.Mode.Rule
patchOverride(Clash.OverrideSlot.Session, o)
}
startServer()
}
}
MainDesign.Request.OpenModeGlobal -> withClash {
withClash {
val o = queryOverride(Clash.OverrideSlot.Session)
o.mode = TunnelState.Mode.Global
patchOverride(Clash.OverrideSlot.Session, o)
MainDesign.Request.OpenModeMenu -> {
design.OpenModeMenu()
}
}
MainDesign.Request.OpenModeRule -> withClash {
withClash {
val o = queryOverride(Clash.OverrideSlot.Session)
o.mode = TunnelState.Mode.Direct
patchOverride(Clash.OverrideSlot.Session, o)
MainDesign.Request.OpenChangeMode -> {
design.OpenModeMenu()
}
MainDesign.Request.OpenModeSheet ->{
design.OpenModeSheet()
}
}
}
if (clashRunning) {
ticker.onReceive {
design.fetchTraffic()
}
}
}
if (clashRunning) {
ticker.onReceive {
design.fetchTraffic()
}
}else{
startActivity(LoginActivity::class.intent)
finish()
}
}
private suspend fun configRequesting(retryCount: Int = 0){
val gson = Gson()
val currentTime = System.currentTimeMillis()
/*apiService.getConfig().let {
if (it.isSuccessful) {
val response: ConfigResponse = it.body() ?: throw Exception("Response body is null")
if (response.code == 1 ) {
PreferenceManager.saveConfigToPreferences(response)
//更新缓存数据
PreferenceManager.cache_timestamp = currentTime
PreferenceManager.cached_data = gson.toJson( it.body())
println("API Response: ${gson.toJson( it.body())}")
//重新初始化 ApiClint
//2 : 获取订阅节点信息
performSubscrite()
}
} else {
println("API Error: ${it.errorBody()}")
configRequesting()
}
}
*/
safeApiCall {apiService.getConfig()}.let {
if (it !=null){
if (it.code == 1 ) {
PreferenceManager.saveConfigToPreferences(it)
//更新缓存数据
PreferenceManager.cache_timestamp = currentTime
PreferenceManager.cached_data = gson.toJson( it)
println("API Response: ${it}")
//重新初始化 ApiClint
//2 : 获取订阅节点信息
performSubscrite()
}
}else{
if (retryCount < 3) {
configRequesting(retryCount + 1)
} else {
withContext(Dispatchers.Main) {
LoadingDialog.hide()
showCustomDialog(
title = "提示",
message = "初始化失败,请检查配置是否正确",
positiveButtonText = "确定",
negativeButtonText = "",
onPositiveClick = {
},
onNegativeClick = {
}
)
}
}
}
}
}
private fun performSubscrite(){
val apiServiceApp = ApiClient.retrofit.create(ApiService::class.java)
//获取 Config 数据
CoroutineScope(Dispatchers.IO).launch {
safeApiRequestCall {
apiServiceApp.getSubscribe(PreferenceManager.loginauthData)}.let {
withContext(Dispatchers.Main) {
LoadingDialog.hide()
}
if (it != null &&
it.isSuccessful) {
println(" performSubscrite : ${it.body()?.data}")
subData = it.body()?.data
val gson = Gson()
PreferenceManager.cached_userSubscritedata = gson.toJson( it.body())
APIGlobalObject.subData = it.body()?.data
//存储到本地
val plainid = it.body()?.data?.plan_id ?: 0
// 或者检查使用量
val guoqi = (it.body()?.data?.u ?: 0) + (it.body()?.data?.d ?: 0) > (it.body()?.data?.transfer_enable ?: 0) && (it.body()?.data?.transfer_enable ?: 0) > 0
if ( plainid == 0 || guoqi ) {
//提示到期
//删除所有节点
withProfile {
var allProfiles = queryAll()
println(allProfiles)
if (allProfiles.isNotEmpty() && allProfiles.count() > 1){
allProfiles.forEach { delete(it.uuid) }
//删除所有节点
}
}
//删除所有节点文件
withContext(Dispatchers.Main) {
showCustomDialog(
title = "提示",
message = "您的订阅已到期,请续费订阅开启服务",
positiveButtonText = "确定",
negativeButtonText = "取消",
onPositiveClick = {
// 执行确认操作
startActivity(PlansActivity::class.intent)
},
onNegativeClick = {
// 执行取消操作
}
)
}
}else{
//更新本地节点
withProfile {
var profileActive = queryActive()
if (profileActive != null){
try {
println(">>>>>>>>> 更新节点订阅地址:${profileActive.uuid} ${profileActive.source}")
update(profileActive.uuid)
}catch (e: Exception){
println(">>>>>>>>> 更新节点订阅地址 catch${e.message}")
}
finally {
println(">>>>>>>>> 更新节点订阅地址 完成 finally")
}
}
}
}
}
}
}
}
private suspend fun MainDesign.fetch() {
setClashRunning(clashRunning)
val state = withClash {
queryTunnelState()
}
println("运行时查询系统mode模式: ${state}")
val providers = withClash {
queryProviders()
}
android.util.Log.i("MainActivity","fetch Info: ${state.mode.name} ${providers.count()}")
if (providers.isNotEmpty()) {
providers.forEach { android.util.Log.i("MainActivity", "The element is ${it.name}") }
println("模式providers ${providers}")
providers.forEach { println("The element is ${it.name}") }
}
setMode(state.mode)
@@ -145,24 +446,27 @@ class MainActivity : BaseActivity<MainDesign>() {
withProfile {
setProfileName(queryActive()?.name)
setSelectNodeName(PreferenceManager.selectnodeName)
setSelectNodeName(PreferenceManager.selectnodeName )
}
}
//查询上传速度和下载速度
private suspend fun MainDesign.fetchTraffic() {
withClash {
setForwarded(queryTrafficTotal())
// setForwarded(queryTrafficTotal())
setUploadedSeep(queryTrafficTotal())
setDownloadedSeep(queryTrafficTotal())
}
}
private suspend fun MainDesign.startClash() {
//删除所有节点
withProfile {
var allProfiles = queryAll()
if (allProfiles.isNotEmpty() && allProfiles.count() > 1){
if (allProfiles.isNotEmpty() && allProfiles.count() > 1){ //删除超过多余的 1 个节点之外的节点
allProfiles.forEach { delete(it.uuid) }
//删除所有节点
}
@@ -180,26 +484,123 @@ class MainActivity : BaseActivity<MainDesign>() {
// }
val activeProfile = withProfile { queryActive() }
println("当前订阅文件:${activeProfile}")
if (activeProfile == null ) { //|| !activeProfile.imported
if (activeProfile == null || !activeProfile.imported) {
showToast("正在请求节点数据....", ToastDuration.Long) {
}
LoadingDialog.show(this@MainActivity, "正在请求节点数据...")
// showToast("正在请求节点数据....", ToastDuration.Long)
//create(Profile.Type.Url, "VPN")
withProfile{
val uuid: UUID = create(Profile.Type.Url,"VPN","https://api.0009.uk/api/v1/client/subscribe?token=5652e3eec20c553047dbe3fcf8754090")
commit(uuid){
android.util.Log.i("commit",">> ${it.progress} ${it.action} ${it.args.toString()} ")
if (it.action == FetchStatus.Action.Verifying){
// 在协程中启动 activeProfile 调用
CoroutineScope(Dispatchers.IO).launch {
activeProfile()
println(">>>>>>>>>${subData}")
//https://hongxingdl.com/hxvip?token=bc2f2839ccc608f34d6a6fc90b6e7f15
//https://hongxingdl.com/api/v1/client/subscribe?token=bc2f2839ccc608f34d6a6fc90b6e7f15
//"https://api.0009.uk/api/v1/client/subscribe?token=5652e3eec20c553047dbe3fcf8754090"
if (subData?.subscribe_url != null && subData?.plan?.name != null && subData?.plan?.id != null) {
var suburl = subData?.subscribe_url ?: ""
var encodeURL = java.net.URLEncoder.encode(suburl, "utf-8")
encodeURL = encodeURL.replace("?","%3F")
var newULR =suburl
println(">>>>>>>>> "+suburl)
if( ApiClientConfig.ConfigNodeURL.length > 3){
newULR = ApiClientConfig.ConfigNodeURL + encodeURL
}
println(">>>>>>>>> "+newULR)
//val uuid: UUID = create(Profile.Type.Url,subData?.plan?.name ?: "VPN",newULR)
launch {
val uuid2 = withProfile {
val type = Profile.Type.Url
val name = subData?.plan?.name ?: "VPN"
create(type, name).also {
patch(it, name, newULR, 0)
}
}
withProcessing { updateStatus ->
withProfile {
try {
commit(uuid2) {
launch {
activeProfile()
updateStatus(it)
}
}
} catch (e: Exception) {
LoadingDialog.hide()
showExceptionToast("初始化节点数据失败,请检查网络重试")
}
coroutineScope {
}
}
}
/* coroutineScope {
commit(uuid2) {
println(">>>>>>>>>> ${it.progress} ${it.action} ${it.args.toString()} ${it} ")
if (it.action == FetchStatus.Action.Verifying){
// 在协程中启动 activeProfile 调用
CoroutineScope(Dispatchers.IO).launch {
activeProfile()
//自动开启订阅
startServer()
}
}
}
}*/
}
/* commit(uuid){
println(">> ${it.progress} ${it.action} ${it.args.toString()} ")
if (it.action == FetchStatus.Action.Verifying){
// 在协程中启动 activeProfile 调用
CoroutineScope(Dispatchers.IO).launch {
activeProfile()
//自动开启订阅
startServer()
}
}
}*/
}else{
//重新请求网络
performSubscrite()
}
/*
if (subData == null){
withContext(Dispatchers.Main){
LoadingDialog.hide()
}
performSubscrite()
}else{
val plainid =subData?.plan_id ?: 0
// 或者检查使用量
val guoqi = (subData?.u ?: 0) + (subData?.d ?: 0) > (subData?.transfer_enable ?: 0) && (subData?.transfer_enable ?: 0) > 0
if ( plainid == 0 || guoqi ) {
//提示到期
performSubscrite()
}
}
*/
}
}else{
@@ -209,9 +610,46 @@ class MainActivity : BaseActivity<MainDesign>() {
// }
// }
android.util.Log.i("activeProfile",">> ${activeProfile.type.name} ${activeProfile.uuid} ${activeProfile.imported} ")
println("当前订阅文件>> ${activeProfile.type.name} ${activeProfile.uuid} ${activeProfile.imported} ")
withProfile {
activeProfile.uuid
}
startServer()
}
}
suspend fun withProcessing(executeTask: suspend (suspend (FetchStatus) -> Unit) -> Unit) {
try {
executeTask {
// applyFrom(it)
println("applyFrom ${it.progress} ${it}")
if (it.action == FetchStatus.Action.Verifying) {
//验证通过
CoroutineScope(Dispatchers.IO).launch {
//选中节点
activeProfile()
//自动开启订阅
startServer()
}
}else{
//请求中
}
}
} finally {
}
}
private suspend fun startServer(){
//读取配置文件
val vpnRequest = startClashService()
@@ -223,13 +661,104 @@ class MainActivity : BaseActivity<MainDesign>() {
)
if (result.resultCode == RESULT_OK)
{
startClashService()
withContext(Dispatchers.Main){
LoadingDialog.hide()
}
}
}
println("缓存配置模式:${PreferenceManager.modeName}")
CoroutineScope(Dispatchers.Main).launch {
// Delay for 3 seconds
//第一次全部使用全局模式连接
delay(500)
withClash {
// Query the current mode
val override = queryOverride(Clash.OverrideSlot.Session)
// Toggle between Rule and Global modes
if (PreferenceManager.modeName == TunnelState.Mode.Global.name){
override.mode = TunnelState.Mode.Global
}else{
override.mode = TunnelState.Mode.Rule //第一次默认使用智能 模式连接
}
// Apply the new mode`
patchOverride(Clash.OverrideSlot.Session, override)
//模式切换完毕
// Optional: Log or display the current mode for verification
println("模式切换 Clash mode is now set to: ${override.mode}")
delay(500)
//如果切换全局模式,默认 直接主动选择节点 根据缓存来
//if (override.mode == TunnelState.Mode.Global){
val names = queryProxyGroupNames(uiStore.proxyExcludeNotSelectable)
println("模式切换>>>>>>>>>>>>>"+names)
if ( names.size > 0){
var progxys = queryProxyGroup(names[0], ProxySort.Default)
if (progxys.proxies.first().title.length > 0){
println("模式切换>>>>>>>>>>>>>: "+progxys.proxies.last())
if ( progxys.proxies.size > 2)
{
val zutuolist = progxys.proxies.filter { it.title == "自动选择" }
var autoNode = ""
if (PreferenceManager.selectnodeName.length > 0){
autoNode = PreferenceManager.selectnodeName
} else{
if (zutuolist.size > 0){
//说明存在自动选择
autoNode = "自动选择"
}else{
autoNode = progxys.proxies.first().title
}
PreferenceManager.selectnodeName = autoNode // progxys.proxies.last().title
}
patchSelector(names.first(),autoNode) // progxys.proxies.last().title
withContext(Dispatchers.Main){
design?.setSelectNodeName(PreferenceManager.selectnodeName)
}
}
}
}
// }
withContext(Dispatchers.Main){
LoadingDialog.hide()
}
}
}
} catch (e: Exception) {
design?.showToast(R.string.unable_to_start_vpn, ToastDuration.Long)
withContext(Dispatchers.Main){
LoadingDialog.hide()
}
//design?.showToast(R.string.unable_to_start_vpn, ToastDuration.Long)
}
}
private suspend fun activeProfile(){
withProfile {
setActive(queryAll().first())
@@ -238,7 +767,7 @@ class MainActivity : BaseActivity<MainDesign>() {
}
private suspend fun queryAppVersionName(): String {
return withContext(Dispatchers.IO) {
packageManager.getPackageInfo(packageName, 0).versionName + "\n" + Bridge.nativeCoreVersion().replace("_", "-")
packageManager.getPackageInfo(packageName, 0).versionName// + "\n" + Bridge.nativeCoreVersion().replace("_", "-")
}
}
@@ -5,7 +5,9 @@ import android.content.Context
import com.github.kr328.clash.common.Global
import com.github.kr328.clash.common.compat.currentProcessName
import com.github.kr328.clash.common.log.Log
import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.remote.Remote
import com.github.kr328.clash.service.util.PreferenceManagerServer
import com.github.kr328.clash.service.util.sendServiceRecreated
import com.github.kr328.clash.util.clashDir
import java.io.File
@@ -20,7 +22,11 @@ class MainApplication : Application() {
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
PreferenceManagerServer.init(this)
PreferenceManager.init(this)
Global.init(this)
}
override fun onCreate() {
@@ -0,0 +1,37 @@
package com.github.kr328.clash
import androidx.recyclerview.widget.RecyclerView
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.design.BaseListActivity
import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.design.adapter.OrdersDataAdapter
import com.github.kr328.clash.design.adapter.PlanDataAdapter
import com.github.kr328.clash.network.ApiClient
import com.github.kr328.clash.network.ApiService
import com.github.kr328.clash.network.OrderData
import com.github.kr328.clash.network.PlanData
import com.github.kr328.clash.network.safeApiRequestCall
class MyOrdersActivity : BaseListActivity<OrderData>() {
override fun createAdapter(): RecyclerView.Adapter<*> {
return OrdersDataAdapter(){ item ->
startActivity(SubmitOrderActivity::class.intent.putExtra("trade_no",item.trade_no))
}
}
override fun onCreate() {}
override suspend fun loadData(page: Int, callback: (List<OrderData>?, Throwable?) -> Unit) {
val apiService = ApiClient.retrofit.create(ApiService::class.java)
safeApiRequestCall { apiService.getOrders(PreferenceManager.loginauthData)}.let {
if (it!= null && it.isSuccessful) {
callback(it.body()?.data,null)
}else{
callback(null, Throwable("请求失败"))
}
}
}
}
@@ -1,16 +1,20 @@
package com.github.kr328.clash
import android.content.pm.PackageManager
import com.github.kr328.clash.common.util.componentName
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.design.NetworkSettingsDesign
import com.github.kr328.clash.design.model.Behavior
import com.github.kr328.clash.service.store.ServiceStore
import kotlinx.coroutines.isActive
import kotlinx.coroutines.selects.select
class NetworkSettingsActivity : BaseActivity<NetworkSettingsDesign>() {
class NetworkSettingsActivity : BaseActivity<NetworkSettingsDesign>(), Behavior {
override suspend fun main() {
val design = NetworkSettingsDesign(
this,
uiStore,
this,
ServiceStore(this),
clashRunning,
)
@@ -36,4 +40,25 @@ class NetworkSettingsActivity : BaseActivity<NetworkSettingsDesign>() {
}
}
override var autoRestart: Boolean
get() {
val status = packageManager.getComponentEnabledSetting(
RestartReceiver::class.componentName
)
return status == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
}
set(value) {
val status = if (value)
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
else
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
packageManager.setComponentEnabledSetting(
RestartReceiver::class.componentName,
status,
PackageManager.DONT_KILL_APP,
)
}
}
@@ -0,0 +1,51 @@
package com.github.kr328.clash
import android.app.Activity
import android.content.Intent
import com.github.kr328.clash.common.util.ticker
import com.github.kr328.clash.design.HelpDesign
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
import kotlinx.coroutines.selects.select
import kotlinx.coroutines.withContext
import java.util.concurrent.TimeUnit
class NewGongdanActivity : BaseActivity<HelpDesign>() {
override suspend fun main() {
val design = HelpDesign(this) {
startActivity(Intent(Intent.ACTION_VIEW).setData(it))
}
setContentDesign(design)
//
// while (isActive) {
// events.receive()
// }
//val ticker = ticker(TimeUnit.SECONDS.toMillis(1))
while (isActive) {
select<Unit> {
events.onReceive {
println("events : " + it.name)
}
design.requests.onReceive {
println("requests : " + it.name)
when (it) {
HelpDesign.Request.CommitSuccess -> {
withContext(Dispatchers.Main) {
val resultIntent = Intent()
resultIntent.putExtra("resultKey", "shuaxin")
setResult(Activity.RESULT_OK, resultIntent)
finish()
}
}
}
}
}
}
}
}
@@ -0,0 +1,48 @@
package com.github.kr328.clash
import android.content.Intent
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.design.BaseListActivity
import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.design.adapter.PlanDataAdapter
import com.github.kr328.clash.design.view.ActivityBarLayout
import com.github.kr328.clash.network.ApiClient
import com.github.kr328.clash.network.ApiService
import com.github.kr328.clash.network.PlanData
import com.github.kr328.clash.network.safeApiRequestCall
class PlansActivity : BaseListActivity<PlanData>() {
override fun createAdapter(): RecyclerView.Adapter<*> {
return PlanDataAdapter(){ planData ->
// 在这里处理 item 点击事件
val intent = Intent(this, ConfigOrderActivity::class.java)
intent.putExtra("planData", planData) // 使用 Serializable 传递对象
startActivity(intent)
}
}
override fun onCreate() {
//
setRightButtonVisible {
startActivity(MyOrdersActivity::class.intent)
}
}
override suspend fun loadData(page: Int, callback: (List<PlanData>?, Throwable?) -> Unit) {
val apiService = ApiClient.retrofit.create(ApiService::class.java)
safeApiRequestCall { apiService.getplans(PreferenceManager.loginauthData)}.let {
if (it != null && it.isSuccessful) {
callback(it.body()?.data,null)
}else{
callback(null, Throwable("请求失败"))
}
}
}
}
@@ -0,0 +1,133 @@
package com.github.kr328.clash
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.PersistableBundle
import androidx.appcompat.app.AppCompatActivity
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.common.util.ticker
import com.github.kr328.clash.core.bridge.Bridge
import com.github.kr328.clash.design.MainDesign
import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.design.ProfileDesign
import com.github.kr328.clash.design.ui.ToastDuration
import com.github.kr328.clash.design.util.showCustomDialog
import com.github.kr328.clash.util.stopClashService
import com.github.kr328.clash.util.withProfile
import com.github.kr328.clash.utity.LoadingDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.selects.select
import kotlinx.coroutines.withContext
import java.util.concurrent.TimeUnit
class ProfileActivity : BaseActivity<ProfileDesign>() {
private suspend fun queryAppVersionName(): String {
return withContext(Dispatchers.IO) {
packageManager.getPackageInfo(packageName, 0).versionName //+ "\n" + Bridge.nativeCoreVersion().replace("_", "-")
}
}
override suspend fun main() {
PreferenceManager.init(this)
val design = ProfileDesign(this)
setContentDesign(design)
design.requestdataDisible()
val ticker = ticker(TimeUnit.SECONDS.toMillis(1))
design.queryVersion(queryAppVersionName())
while (isActive) {
select<Unit> {
events.onReceive {
println("events : " + it.name)
}
design.requests.onReceive {
println("requests : " + it.name)
when (it) {
ProfileDesign.Request.OpenBuyPlan -> startActivity(PlansActivity::class.intent)
ProfileDesign.Request.OpenMyOrders -> startActivity(MyOrdersActivity::class.intent)
ProfileDesign.Request.OpenMyBlance -> startActivity(SubmitOrderActivity::class.intent)
ProfileDesign.Request.OpenMyInvites ->startActivity(InvitationActivity::class.intent)
ProfileDesign.Request.OpenCustomView ->{ startActivity(H5WebActivity::class.intent) }
/*try {
withContext(Dispatchers.Main) {
val intent = Intent(Intent.ACTION_VIEW)
intent.addCategory(Intent.CATEGORY_BROWSABLE);
intent.setData(Uri.parse(PreferenceManager.getConfigFromPreferences(this@ProfileActivity)?.telegramurl))
startActivity(intent)
}
} catch (e: Exception) {
design.showToast("当前手机未安装浏览器",ToastDuration.Long)
} */
ProfileDesign.Request.OpenLogout -> {
showCustomDialog(
title = "提示",
message = "确定要退出吗?",
positiveButtonText = "确定",
negativeButtonText = "取消",
onPositiveClick = {
LoadingDialog.show(this@ProfileActivity, "正在退出...")
// 执行确认操作
stopClashService()
// 清理缓存,清除节点信息,切换界面
launch {
withProfile {
var allProfiles = queryAll()
println(allProfiles)
if (allProfiles.isNotEmpty() && allProfiles.count() > 0) {
allProfiles.forEach {
delete(it.uuid)
println("删除节点:${it.source}")
}
//删除所有节点
}
}
}
PreferenceManager.clearData()
//2s 后执行
// 在协程作用域中执行
GlobalScope.launch(Dispatchers.Main) {
delay(2000) // 协程延迟2000毫秒(2秒)
println("协程中的延迟执行")
LoadingDialog.hide()
startActivity(LoginActivity::class.intent)
finish()
}
},
onNegativeClick = {
// 执行取消操作
}
)
}
ProfileDesign.Request.OpenGSettings -> {
//查看是否有更新
design.showToast("当前版本是最新版本",ToastDuration.Long)
}// startActivity(NetworkSettingsActivity::class.intent)
ProfileDesign.Request.OpenMyGongdan ->startActivity(GongdanActivity::class.intent)
ProfileDesign.Request.OpenCustomViewIPtest -> startActivity(H5WebActivity::class.intent.putExtra("url","https://ipcelou.com"))
ProfileDesign.Request.OpenCustomViewSpeed -> startActivity(H5WebActivity::class.intent.putExtra("url","https://fast.com/"))
}
}
}
}
}
}
@@ -95,6 +95,9 @@ class PropertiesActivity : BaseActivity<PropertiesDesign>() {
coroutineScope {
commit(profile.uuid) {
launch {
withProfile {
setActive(queryAll().first())
}
updateStatus(it)
}
}
@@ -1,5 +1,7 @@
package com.github.kr328.clash
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.core.Clash
@@ -8,11 +10,15 @@ import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.design.ProxyDesign
import com.github.kr328.clash.design.model.ProxyState
import com.github.kr328.clash.util.withClash
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.selects.select
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import kotlinx.coroutines.withContext
class ProxyActivity : BaseActivity<ProxyDesign>() {
override suspend fun main() {
@@ -22,6 +28,8 @@ class ProxyActivity : BaseActivity<ProxyDesign>() {
val unorderedStates = names.indices.map { names[it] to states[it] }.toMap()
val reloadLock = Semaphore(10)
setTitle("选择节点")
val design = ProxyDesign(
this,
mode,
@@ -31,7 +39,11 @@ class ProxyActivity : BaseActivity<ProxyDesign>() {
setContentDesign(design)
design.requests.send(ProxyDesign.Request.ReloadAll)
//刷新所以Groups
//design.requests.send(ProxyDesign.Request.ReloadAll)
design.requests.send(ProxyDesign.Request.Reload(0))
while (isActive) {
select<Unit> {
@@ -65,11 +77,52 @@ class ProxyActivity : BaseActivity<ProxyDesign>() {
}
is ProxyDesign.Request.Reload -> {
launch {
design.urlTesting = false
val group = reloadLock.withPermit {
withClash {
queryProxyGroup(names[it.index], uiStore.proxySort)
}
}
var state = states[it.index]
println(">>>>>>>>>>>> ${names}")
println(">>>>>>>>>>>> ${it.index} group: "+group + " ${group.type.name} ${group.proxies.size}")
if (group.proxies.first().name == "GLOBAL"){
println(">>>>>>>>>>>> GLOBAL ")
}
if (PreferenceManager.selectnodeName.length > 0){
state.now = PreferenceManager.selectnodeName //group.now
}else{
state.now = group.now
}
println(">>>>>>>>>>>>unorderedStates : ${unorderedStates}")
println(">>>>>>>>>>>> state: "+state)
//PreferenceManager.selectnodeName = state.now //刷新目前使用的节点名称
// >>>>>>>>>>>> state: ProxyState(now=?)
// >>>>>>>>>>>> 0 group: 距离下次重置剩余:18 天 Selector
// >>>>>>>>>>>>{代理=ProxyState(now=距离下次重置剩余:18 天), 电报=ProxyState(now=?), 油管=ProxyState(now=?), 奈飞=ProxyState(now=?), b站=ProxyState(now=?), 规则外路由选择=ProxyState(now=?)}
design.updateGroup(
it.index,
group.proxies,
group.type == Proxy.Type.Selector,
state,
unorderedStates
)
design.finishreferesh()
/*val group = reloadLock.withPermit {
withClash {
queryProxyGroup(names[it.index], uiStore.proxySort)
}
}
val state = states[it.index]
state.now = group.now
@@ -80,7 +133,7 @@ class ProxyActivity : BaseActivity<ProxyDesign>() {
group.type == Proxy.Type.Selector,
state,
unorderedStates
)
) */
}
}
is ProxyDesign.Request.Select -> {
@@ -88,9 +141,11 @@ class ProxyActivity : BaseActivity<ProxyDesign>() {
patchSelector(names[it.index], it.name)
states[it.index].now = it.name
}
Log.i("selectnodeName","${it.name}")
PreferenceManager.selectnodeName = it.name
design.requestRedrawVisible()
launch {
finish()
}
}
is ProxyDesign.Request.UrlTest -> {
launch {
@@ -102,7 +157,7 @@ class ProxyActivity : BaseActivity<ProxyDesign>() {
}
}
is ProxyDesign.Request.PatchMode -> {
design.showModeSwitchTips()
// design.showModeSwitchTips()
withClash {
val o = queryOverride(Clash.OverrideSlot.Session)
@@ -116,5 +171,21 @@ class ProxyActivity : BaseActivity<ProxyDesign>() {
}
}
}
// 使用协程延迟 500ms 后执行
/* GlobalScope.launch(Dispatchers.Main) {
delay(1500) // 延迟 500ms
launch {
withClash {
healthCheck(names[0])
}
design.requests.send(ProxyDesign.Request.Reload(0))
}
}*/
}
}
@@ -0,0 +1,217 @@
package com.github.kr328.clash
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import com.github.kr328.clash.common.util.intent
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.github.kr328.clash.common.compat.isAllowForceDarkCompat
import com.github.kr328.clash.common.compat.isLightNavigationBarCompat
import com.github.kr328.clash.common.compat.isLightStatusBarsCompat
import com.github.kr328.clash.common.compat.isSystemBarsTranslucentCompat
import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.design.databinding.ActivityLoginBinding
import com.github.kr328.clash.design.databinding.ActivityProfileBinding
import com.github.kr328.clash.design.databinding.ActivityRegisterBinding
import com.github.kr328.clash.design.ui.DayNight
import com.github.kr328.clash.design.util.layoutInflater
import com.github.kr328.clash.design.util.resolveThemedBoolean
import com.github.kr328.clash.design.util.resolveThemedColor
import com.github.kr328.clash.design.util.root
import com.github.kr328.clash.network.ApiClient
import com.github.kr328.clash.network.ApiClientConfig
import com.github.kr328.clash.network.ApiService
import com.github.kr328.clash.network.ConfigResponse
import com.github.kr328.clash.network.LoginRequest
import com.github.kr328.clash.network.LoginResponse
import com.github.kr328.clash.network.safeApiRequestCall
import com.github.kr328.clash.utity.LoadingDialog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONException
import org.json.JSONObject
class RegisterActivity : AppCompatActivity() {
private lateinit var apiService: ApiService
private var dayNight: DayNight = DayNight.Day
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityRegisterBinding
.inflate(this.layoutInflater, this.root, false)
setContentView(binding.root)
applyDayNight()
PreferenceManager.init(this)
binding.loginLoginButton.setOnClickListener {
val email = binding.loginEmailEditText.text.toString()
val password = binding.loginPasswordEditText.text.toString()
val password2 = binding.loginPasswordEditText2.text.toString()
val isAgreementChecked = binding.loginAgreementCheckBox.isChecked
if (email.isEmpty() || password.isEmpty() || password2.isEmpty()) {
Toast.makeText(this, "请输入邮箱和密码", Toast.LENGTH_SHORT).show()
} else if (!isAgreementChecked) {
Toast.makeText(this, "请同意隐私政策和用户协议", Toast.LENGTH_SHORT).show()
} else {
// Hide the keyboard
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val currentFocusView = currentFocus
if (currentFocusView != null) {
inputMethodManager.hideSoftInputFromWindow(currentFocusView.windowToken, 0)
}
performLogin(email, password)
}
}
binding.loginForgotPassword.setOnClickListener {
// Navigate to Forgot Password screen or show dialog
}
binding.loginRegister.setOnClickListener {
// Navigate to Register screen
finish()
}
apiService = ApiClientConfig.retrofit.create(ApiService::class.java)
//获取 Config 数据
CoroutineScope(Dispatchers.IO).launch {
safeApiRequestCall { apiService.getConfig()}.let {
if (it != null && it.isSuccessful) {
val response: ConfigResponse = it.body() ?: throw Exception("Response body is null")
if (response.code == 1 ) {
PreferenceManager.saveConfigToPreferences( response)
//重新初始化 ApiClint
}
println("API Response: ${it.body()}")
} else {
println("API Error: 请求失败")
}
}
}
}
/**
* withContext(Dispatchers.Main) {
* if (response.isSuccessful) {
* val userData = response.body()
* // 更新 UI
* } else {
* Toast.makeText(this@MainActivity, "Failed to fetch user data", Toast.LENGTH_SHORT).show()
* }
* }
* */
private fun applyDayNight(config: Configuration = resources.configuration) {
//val dayNight = theme.applyStyle(R.style.AppThemeLight, true) //默认白天模式
/*
val dayNight = queryDayNight(config)
when (dayNight) {
DayNight.Night -> theme.applyStyle(R.style.AppThemeDark, true)
DayNight.Day -> theme.applyStyle(R.style.AppThemeLight, true)
}*/
window.isAllowForceDarkCompat = false
window.isSystemBarsTranslucentCompat = true
window.statusBarColor = resolveThemedColor(android.R.attr.statusBarColor)
window.navigationBarColor = resolveThemedColor(android.R.attr.navigationBarColor)
if (Build.VERSION.SDK_INT >= 23) {
window.isLightStatusBarsCompat = resolveThemedBoolean(android.R.attr.windowLightStatusBar)
}
if (Build.VERSION.SDK_INT >= 27) {
window.isLightNavigationBarCompat = resolveThemedBoolean(android.R.attr.windowLightNavigationBar)
}
this.dayNight = DayNight.Night //dayNight
}
private fun performLogin(email: String, password: String) {
// Perform login logic here, possibly calling an API
// Show the loading indicator with a custom message
LoadingDialog.show(this, "正在注册...")
val apiServiceApp = ApiClient.retrofit.create(ApiService::class.java)
//获取 Config 数据
CoroutineScope(Dispatchers.IO).launch {
safeApiRequestCall {
apiServiceApp.registerUser(LoginRequest(email,password))}.let {
withContext(Dispatchers.Main) {
LoadingDialog.hide()
}
if (it != null &&
it.isSuccessful) {
println("API Response: ${it.code()} ${it.body()} ${it.raw().body}")
val response: LoginResponse = it.body() ?: throw Exception("Response body is null")
if (response.data != null){
PreferenceManager.loginemail = email
PreferenceManager.loginToken = response.data?.token ?: ""
PreferenceManager.loginauthData = response.data?.auth_data ?: ""
PreferenceManager.isLoginin = true
// saveLoginMailToken(this@LoginActivity,email)
// saveLoginToken(this@LoginActivity, response.data.token ?: "")
// saveLoginAuthData(this@LoginActivity, response.data.auth_data ?: "")
// saveLoginStatus(this@LoginActivity,true)
//获取用户信息
withContext(Dispatchers.Main) {
val intent = Intent(this@RegisterActivity, MainActivity::class.java)
intent.flags =
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
finish() // It
}
}
}else{
// Handle error response, even if it's 422
try {
val errorResponse = it?.errorBody()?.string() ?: ""
val errorJson = JSONObject(errorResponse)
val message = errorJson.optString("message")
// Process the error message or show it to the user
if (!message.isNullOrEmpty()) {
withContext(Dispatchers.Main) {
Toast.makeText(this@RegisterActivity, "注册失败:${message}", Toast.LENGTH_LONG).show()
}
}
println( "Message: $message, ")
} catch (e: JSONException) {
// Handle JSON parsing error
withContext(Dispatchers.Main) {
Toast.makeText(this@RegisterActivity, "注册失败:${e.message}", Toast.LENGTH_LONG).show()
}
}
}
}
}
}
}
@@ -0,0 +1,36 @@
package com.github.kr328.clash
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import androidx.appcompat.app.AppCompatActivity
import com.github.kr328.clash.common.util.intent
import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.design.databinding.ActivityLoginBinding
import com.github.kr328.clash.design.databinding.ActivitySplashBinding
import com.github.kr328.clash.design.util.root
import com.github.kr328.clash.network.ApiService
class SplashActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivitySplashBinding
.inflate(this.layoutInflater, this.root, false)
setContentView(binding.root)
PreferenceManager.init(this)
Handler(Looper.getMainLooper()).postDelayed({
// Hide the loading indicator
startActivity(LoginActivity::class.intent)
finish()
}, 1000) // Simulating a network delay
}
}
@@ -0,0 +1,16 @@
package com.github.kr328.clash
import com.github.kr328.clash.design.SubmitOrderViewDesign
class SubmitOrderActivity : BaseActivity<SubmitOrderViewDesign>() {
override suspend fun main() {
val design = SubmitOrderViewDesign(this)
setContentDesign(design)
//
val trade_no = intent.getStringExtra("trade_no")
design.fetchTradeInfo(trade_no)
}
}
@@ -0,0 +1,258 @@
package com.github.kr328.clash
import android.graphics.Color
import android.graphics.Rect
import android.provider.Settings.Global
import android.view.Gravity
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.recyclerview.widget.RecyclerView
import com.github.kr328.clash.design.BaseListActivity
import com.github.kr328.clash.design.PreferenceManager
import com.github.kr328.clash.design.adapter.TicketDetailAdapter
import com.github.kr328.clash.design.adapter.TicketsDataAdapter
import com.github.kr328.clash.design.ui.ToastDuration
import com.github.kr328.clash.network.ApiClient
import com.github.kr328.clash.network.ApiService
import com.github.kr328.clash.network.PublicResponse
import com.github.kr328.clash.network.TicketMessage
import com.github.kr328.clash.network.safeApiRequestCall
import com.google.gson.Gson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.net.URLEncoder
class TicketDetailActivity : BaseListActivity<TicketMessage>() {
override fun createAdapter(): RecyclerView.Adapter<*> {
return TicketDetailAdapter(){ planData ->
// 在这里处理 item 点击事件
}
}
override fun onCreate() {
}
fun encodeString(input: String): String {
val encoded = StringBuilder()
input.forEach { char ->
when {
char.isWhitespace() -> {
// 编码空格为 %20
encoded.append("")
}
char.isLetterOrDigit() || char.toInt() in 0x4E00..0x9FFF -> {
// 保留字母、数字和中文字符
encoded.append(char)
}
else -> {
// 其他字符使用 URLEncoder 编码
encoded.append(URLEncoder.encode(char.toString(), "UTF-8"))
}
}
}
return encoded.toString()
}
fun onCreateInputView() {
// 将 dp 转换为 px 的实用函数
fun dpToPx(dp: Int): Int {
return (dp * resources.displayMetrics.density).toInt()
}
// 创建顶部分割线
val topDivider = View(this).apply {
layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
dpToPx(1) // 设置分割线高度为 1dp
) .apply {
gravity = Gravity.TOP // 将分割线置于顶部
}
setBackgroundColor(ContextCompat.getColor(this@TicketDetailActivity, android.R.color.darker_gray)) // 设置分割线颜色
}
val rootLayout = FrameLayout(this).apply {
layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
)
addView(topDivider) // 将分割线添加到根布局
}
// Create the input container layout programmatically
// Create the input container layout programmatically
val inputContainer = LinearLayout(this).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(8, 8, 8, 8)
// setBackgroundColor(ContextCompat.getColor(this@TicketDetailActivity, android.R.color.white))
layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT,
Gravity.BOTTOM // Aligns the input container to the bottom
)
setBackgroundColor(ContextCompat.getColor(this@TicketDetailActivity, R.color.white))
}
// Create the EditText for message input
val editText = EditText(this).apply {
hint = "输入内容回复工单..."
setHintTextColor(ContextCompat.getColor(this@TicketDetailActivity, R.color.gray)) // Set the placeholder color
// Utility function to convert dp to pixels
fun dpToPx2(dp: Int): Int {
return (dp * resources.displayMetrics.density).toInt()
}
textSize = 14f
// Set other EditText properties
setTextColor(Color.BLACK) // Text color
inputType = android.text.InputType.TYPE_TEXT_FLAG_MULTI_LINE
setTextColor(ContextCompat.getColor(this@TicketDetailActivity, R.color.black))
maxLines = 1
// background = ContextCompat.getDrawable(this@TicketDetailActivity, R.drawable.rounded_button_background)
// 设置内边距为 4dp(上下左右)
// val padding = dpToPx(8)
// setPadding(padding,0 , padding, 0)
layoutParams = LinearLayout.LayoutParams(
0,
LinearLayout.LayoutParams.WRAP_CONTENT,
1f // Weight to make it fill available width
)
}
// Create the Send Button
val sendButton = Button(this).apply {
text = "回复"
setTextColor(ContextCompat.getColor(this@TicketDetailActivity, android.R.color.white)) // Set text color for better contrast
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
}
// Set the background tint programmatically
ViewCompat.setBackgroundTintList(
sendButton,
ContextCompat.getColorStateList(this, R.color.blue)
)
// Add EditText and Button to the input container
inputContainer.addView(editText)
inputContainer.addView(sendButton)
sendButton.setOnClickListener {
val message = editText.text.toString()
if (message.isNullOrEmpty()){
}else{
val fieldMap = mutableMapOf<String, String>()
val messageProperty1 = encodeString(message)
val ticketid = intent.getIntExtra("ticketid",0)
fieldMap.put("message",""+messageProperty1)
fieldMap.put("id",""+ticketid)
GlobalScope.launch {
val apiService = ApiClient.retrofit.create(ApiService::class.java)
safeApiRequestCall { apiService.replyTicket(PreferenceManager.loginauthData, fieldMap)}.let {
if (it != null && it.isSuccessful) {
withContext(Dispatchers.Main) {
Toast.makeText(this@TicketDetailActivity, "回复成功", Toast.LENGTH_SHORT).show()
editText.setText("")
}
refereshData()
}else{
val errorinfo = it?.errorBody()?.string()
if (errorinfo != null){
//解析错错误信息
val gson = Gson()
val submitResponse = gson.fromJson(errorinfo, PublicResponse::class.java)
if (submitResponse?.message != null){
withContext(Dispatchers.Main) {
Toast.makeText(this@TicketDetailActivity, "回复失败: ${submitResponse.message}", Toast.LENGTH_SHORT).show()
}
}
}else{
withContext(Dispatchers.Main) {
Toast.makeText(this@TicketDetailActivity, "回复失败", Toast.LENGTH_SHORT).show()
}
}
}
}
}
}
}
// 将分割线添加到输入容器顶部
rootLayout.addView(inputContainer)
// rootLayout.addView(topDivider) // 将分割线添加到根布局
// Add the input container to the root layout
mainBinding?.mainview?.addView(rootLayout)
rootLayout.viewTreeObserver.addOnGlobalLayoutListener {
val rect = Rect()
rootLayout.getWindowVisibleDisplayFrame(rect)
val screenHeight = rootLayout.rootView.height
val keypadHeight = screenHeight - rect.bottom
val params = inputContainer.layoutParams as FrameLayout.LayoutParams
if (keypadHeight > screenHeight * 0.15) {
// Keyboard is visible; adjust inputContainer to stay above it
params.bottomMargin = keypadHeight
} else {
// Keyboard is hidden; reset inputContainer to bottom
params.bottomMargin = 0
}
inputContainer.layoutParams = params
}
}
override suspend fun loadData(page: Int, callback: (List<TicketMessage>?, Throwable?) -> Unit) {
val ticketid = intent.getIntExtra("ticketid",0)
val apiService = ApiClient.retrofit.create(ApiService::class.java)
safeApiRequestCall { apiService.getTicketsByTID(PreferenceManager.loginauthData,ticketid)}.let {
if (it != null && it.isSuccessful) {
withContext(Dispatchers.Main) {
referTitle(it.body()?.data?.subject ?: "")
}
if(it.body()?.data?.status == 0){
withContext(Dispatchers.Main) {
onCreateInputView()
}
}
callback(it.body()?.data?.message,null)
}else{
callback(null, Throwable("请求失败"))
}
}
}
}
@@ -8,7 +8,7 @@ object SystemLogcat {
"Go",
"DEBUG",
"AndroidRuntime",
"ClashMetaForAndroid",
"UUVPN",
"LwIP",
)
@@ -5,6 +5,8 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import androidx.core.content.ContextCompat
import com.github.kr328.clash.common.constants.Intents
import com.github.kr328.clash.common.log.Log
import java.util.*
@@ -88,7 +90,7 @@ class Broadcasts(private val context: Application) {
return
try {
context.registerReceiver(broadcastReceiver, IntentFilter().apply {
val filter = IntentFilter().apply {
addAction(Intents.ACTION_SERVICE_RECREATED)
addAction(Intents.ACTION_CLASH_STARTED)
addAction(Intents.ACTION_CLASH_STOPPED)
@@ -96,7 +98,15 @@ class Broadcasts(private val context: Application) {
addAction(Intents.ACTION_PROFILE_UPDATE_COMPLETED)
addAction(Intents.ACTION_PROFILE_UPDATE_FAILED)
addAction(Intents.ACTION_PROFILE_LOADED)
})
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.registerReceiver(broadcastReceiver, filter, Context.RECEIVER_EXPORTED)
} else {
context.registerReceiver(broadcastReceiver, filter)
}
registered = true
clashRunning = StatusClient(context).currentProfile() != null
} catch (e: Exception) {
@@ -110,10 +120,11 @@ class Broadcasts(private val context: Application) {
try {
context.unregisterReceiver(broadcastReceiver)
registered = false
clashRunning = false
} catch (e: Exception) {
Log.w("Unregister global receiver: $e", e)
}
}
}
}
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/white" />
<item>
<bitmap
android:src="@drawable/appstore"
android:gravity="center" />
</item>
</layer-list>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#1e1f22"
android:pathData="M480,640Q555,640 607.5,587.5Q660,535 660,460Q660,385 607.5,332.5Q555,280 480,280Q405,280 352.5,332.5Q300,385 300,460Q300,535 352.5,587.5Q405,640 480,640ZM480,568Q435,568 403.5,536.5Q372,505 372,460Q372,415 403.5,383.5Q435,352 480,352Q525,352 556.5,383.5Q588,415 588,460Q588,505 556.5,536.5Q525,568 480,568ZM480,760Q334,760 214,678.5Q94,597 40,460Q94,323 214,241.5Q334,160 480,160Q626,160 746,241.5Q866,323 920,460Q866,597 746,678.5Q626,760 480,760ZM480,460Q480,460 480,460Q480,460 480,460Q480,460 480,460Q480,460 480,460Q480,460 480,460Q480,460 480,460Q480,460 480,460Q480,460 480,460ZM480,680Q593,680 687.5,620.5Q782,561 832,460Q782,359 687.5,299.5Q593,240 480,240Q367,240 272.5,299.5Q178,359 128,460Q178,561 272.5,620.5Q367,680 480,680Z"/>
</vector>
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#1e1f22"
android:pathData="M644,532L586,474Q595,427 559,386Q523,345 466,354L408,296Q425,288 442.5,284Q460,280 480,280Q555,280 607.5,332.5Q660,385 660,460Q660,480 656,497.5Q652,515 644,532ZM772,658L714,602Q752,573 781.5,538.5Q811,504 832,460Q782,359 688.5,299.5Q595,240 480,240Q451,240 423,244Q395,248 368,256L306,194Q347,177 390,168.5Q433,160 480,160Q631,160 749,243.5Q867,327 920,460Q897,519 859.5,569.5Q822,620 772,658ZM792,904L624,738Q589,749 553.5,754.5Q518,760 480,760Q329,760 211,676.5Q93,593 40,460Q61,407 93,361.5Q125,316 166,280L56,168L112,112L848,848L792,904ZM222,336Q193,362 169,393Q145,424 128,460Q178,561 271.5,620.5Q365,680 480,680Q500,680 519,677.5Q538,675 558,672L522,634Q511,637 501,638.5Q491,640 480,640Q405,640 352.5,587.5Q300,535 300,460Q300,449 301.5,439Q303,429 306,418L222,336ZM541,429L541,429Q541,429 541,429Q541,429 541,429Q541,429 541,429Q541,429 541,429Q541,429 541,429Q541,429 541,429ZM390,504Q390,504 390,504Q390,504 390,504L390,504Q390,504 390,504Q390,504 390,504Q390,504 390,504Q390,504 390,504Z"/>
</vector>
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/appicon"/>
<monochrome android:drawable="@drawable/appicon" />
</adaptive-icon>
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/appicon"/>
<monochrome android:drawable="@drawable/appicon" />
</adaptive-icon>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 16 KiB

@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="BootstrapTheme" parent="AppThemeDark" />
</resources>
<style name="SplashTheme" parent="BootstrapTheme" />
</resources>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="SplashTheme" parent="BootstrapTheme">
<item name="android:windowSplashScreenBackground">@color/white</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/appstore</item>
<item name="android:windowSplashScreenIconBackgroundColor">@color/white</item>
</style>
</resources>
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="BootstrapTheme" parent="AppThemeLight" />
<style name="SplashTheme" parent="BootstrapTheme" />
</resources>
+24 -17
View File
@@ -12,6 +12,7 @@ buildscript {
maven("https://raw.githubusercontent.com/MetaCubeX/maven-backup/main/releases")
}
dependencies {
classpath(libs.build.android)
classpath(libs.build.kotlin.common)
classpath(libs.build.kotlin.serialization)
@@ -34,33 +35,39 @@ subprojects {
extensions.configure<BaseExtension> {
defaultConfig {
if (isApp) {
applicationId = "com.github.metacubex.clash"
applicationId = "com.litevpn.fast"
}
multiDexEnabled = true
minSdk = 24
compileSdkVersion(33)
// targetSdkVersion(33)
versionName = "2.11.1"
versionCode = 211001
compileSdkVersion(34)
targetSdkVersion(34)
versionName = "4.0.0"
versionCode = 423001
resValue("string", "release_name", "v$versionName")
resValue("integer", "release_code", "$versionCode")
externalNativeBuild {
cmake {
abiFilters("arm64-v8a", "armeabi-v7a",)
abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64")
}
}
// compileOptions{
// isCoreLibraryDesugaringEnabled = false
// }
if (!isApp) {
consumerProguardFiles("consumer-rules.pro")
} else {
setProperty("archivesBaseName", "LiteVPN-$versionName")
setProperty("archivesBaseName", "UUVPN-$versionName")
}
}
ndkVersion = "23.0.7599858"
// ndkVersion = "23.0.7599858"
ndkVersion = "27.0.12077973"
// compileSdkVersion(defaultConfig.targetSdk!!)
if (isApp) {
@@ -81,8 +88,8 @@ subprojects {
buildConfigField("boolean", "PREMIUM", "Boolean.parseBoolean(\"false\")")
resValue("string", "launch_name", "LiteVPN")
resValue("string", "application_name", "LiteVPN")
resValue("string", "launch_name", "UUVPN")
resValue("string", "application_name", "UUVPN")
if (isApp) {
applicationIdSuffix = ".alpha"
@@ -92,15 +99,15 @@ subprojects {
create("meta") {
dimension = flavorDimensionList[0]
versionNameSuffix = ".Meta"
// versionNameSuffix = ".Meta"
buildConfigField("boolean", "PREMIUM", "Boolean.parseBoolean(\"false\")")
resValue("string", "launch_name", "LiteVPN")//@string/launch_name_meta
resValue("string", "application_name", "LiteVPN")//@string/application_name_meta
resValue("string", "launch_name", "UUVPN")//@string/launch_name_meta
resValue("string", "application_name", "UUVPN")//@string/application_name_meta
if (isApp) {
applicationIdSuffix = ".meta"
// applicationIdSuffix = ".meta"
}
}
}
@@ -132,8 +139,8 @@ subprojects {
buildTypes {
named("release") {
isMinifyEnabled = isApp
isShrinkResources = isApp
isMinifyEnabled = false
isShrinkResources = false
signingConfig = signingConfigs.findByName("release")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
@@ -141,7 +148,7 @@ subprojects {
)
}
named("debug") {
versionNameSuffix = ".debug"
// versionNameSuffix = ".debug"
}
}
@@ -1,7 +1,7 @@
package com.github.kr328.clash.common.log
object Log {
private const val TAG = "ClashMetaForAndroid"
private const val TAG = "UUVPN"
fun i(message: String, throwable: Throwable? = null) =
android.util.Log.i(TAG, message, throwable)
Binary file not shown.
@@ -1,254 +0,0 @@
{
"inputs" :
[
{
"path" : "CMakeLists.txt"
},
{
"path" : "version.h.in"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeDetermineSystem.cmake"
},
{
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/build/cmake/android.toolchain.cmake"
},
{
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/build/cmake/android-legacy.toolchain.cmake"
},
{
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/build/cmake/platforms.cmake"
},
{
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/build/cmake/compiler_id.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Platform/Android-Determine.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeSystem.cmake.in"
},
{
"isGenerated" : true,
"path" : "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a/CMakeFiles/3.18.1-g262b901/CMakeSystem.cmake"
},
{
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/build/cmake/android.toolchain.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeSystemSpecificInitialize.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Platform/Android-Initialize.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeDetermineCCompiler.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeDetermineCompiler.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Platform/Android-Determine-C.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Platform/Android/Determine-Compiler.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeFindBinUtils.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Compiler/Clang-FindBinUtils.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeCCompiler.cmake.in"
},
{
"isGenerated" : true,
"path" : "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a/CMakeFiles/3.18.1-g262b901/CMakeCCompiler.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeSystemSpecificInformation.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeGenericSystem.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeInitializeConfigs.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Platform/Android.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Platform/Linux.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Platform/UnixPaths.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeCInformation.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeLanguageInformation.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Compiler/Clang-C.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Compiler/Clang.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Compiler/CMakeCommonCompilerMacros.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Compiler/GNU.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Compiler/CMakeCommonCompilerMacros.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Internal/CMakeCheckCompilerFlag.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeCheckCompilerFlagCommonPatterns.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Platform/Android-Clang-C.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Platform/Android-Clang.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeCommonLanguageInclude.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeTestCCompiler.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeTestCompilerCommon.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeDetermineCompilerABI.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeParseImplicitIncludeInfo.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeParseImplicitLinkInfo.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeTestCompilerCommon.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeCCompilerABI.c"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeDetermineCompileFeatures.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/Internal/FeatureTesting.cmake"
},
{
"isCMake" : true,
"isExternal" : true,
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18/Modules/CMakeCCompiler.cmake.in"
},
{
"isGenerated" : true,
"path" : "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a/CMakeFiles/3.18.1-g262b901/CMakeCCompiler.cmake"
}
],
"kind" : "cmakeFiles",
"paths" :
{
"build" : "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a",
"source" : "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp"
},
"version" :
{
"major" : 1,
"minor" : 0
}
}
@@ -1,59 +0,0 @@
{
"configurations" :
[
{
"directories" :
[
{
"build" : ".",
"minimumCMakeVersion" :
{
"string" : "3.6.0"
},
"projectIndex" : 0,
"source" : ".",
"targetIndexes" :
[
0
]
}
],
"name" : "Debug",
"projects" :
[
{
"directoryIndexes" :
[
0
],
"name" : "clash-bridge",
"targetIndexes" :
[
0
]
}
],
"targets" :
[
{
"directoryIndex" : 0,
"id" : "bridge::@6890427a1f51a3e7e1df",
"jsonFile" : "target-bridge-Debug-a88af6526146ee2dd726.json",
"name" : "bridge",
"projectIndex" : 0
}
]
}
],
"kind" : "codemodel",
"paths" :
{
"build" : "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a",
"source" : "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp"
},
"version" :
{
"major" : 2,
"minor" : 1
}
}
@@ -1,92 +0,0 @@
{
"cmake" :
{
"generator" :
{
"multiConfig" : false,
"name" : "Ninja"
},
"paths" :
{
"cmake" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/cmake",
"cpack" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/cpack",
"ctest" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/ctest",
"root" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18"
},
"version" :
{
"isDirty" : false,
"major" : 3,
"minor" : 18,
"patch" : 1,
"string" : "3.18.1-g262b901",
"suffix" : "g262b901"
}
},
"objects" :
[
{
"jsonFile" : "codemodel-v2-86c91a08090b1cd48c81.json",
"kind" : "codemodel",
"version" :
{
"major" : 2,
"minor" : 1
}
},
{
"jsonFile" : "cache-v2-1139a18846e2431585f8.json",
"kind" : "cache",
"version" :
{
"major" : 2,
"minor" : 0
}
},
{
"jsonFile" : "cmakeFiles-v1-d5eba61b07fa62ce8c42.json",
"kind" : "cmakeFiles",
"version" :
{
"major" : 1,
"minor" : 0
}
}
],
"reply" :
{
"client-agp" :
{
"cache-v2" :
{
"jsonFile" : "cache-v2-1139a18846e2431585f8.json",
"kind" : "cache",
"version" :
{
"major" : 2,
"minor" : 0
}
},
"cmakeFiles-v1" :
{
"jsonFile" : "cmakeFiles-v1-d5eba61b07fa62ce8c42.json",
"kind" : "cmakeFiles",
"version" :
{
"major" : 1,
"minor" : 0
}
},
"codemodel-v2" :
{
"jsonFile" : "codemodel-v2-86c91a08090b1cd48c81.json",
"kind" : "codemodel",
"version" :
{
"major" : 2,
"minor" : 1
}
}
}
}
}
@@ -1,174 +0,0 @@
{
"artifacts" :
[
{
"path" : "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/intermediates/cxx/Debug/1t22401e/obj/arm64-v8a/libbridge.so"
}
],
"backtrace" : 1,
"backtraceGraph" :
{
"commands" :
[
"add_library",
"link_directories",
"target_link_libraries",
"include_directories"
],
"files" :
[
"CMakeLists.txt"
],
"nodes" :
[
{
"file" : 0
},
{
"command" : 0,
"file" : 0,
"line" : 68,
"parent" : 0
},
{
"command" : 1,
"file" : 0,
"line" : 66,
"parent" : 0
},
{
"command" : 2,
"file" : 0,
"line" : 69,
"parent" : 0
},
{
"command" : 3,
"file" : 0,
"line" : 63,
"parent" : 0
},
{
"command" : 3,
"file" : 0,
"line" : 64,
"parent" : 0
}
]
},
"compileGroups" :
[
{
"compileCommandFragments" :
[
{
"fragment" : "-g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -O0 -fno-limit-debug-info -fPIC"
}
],
"defines" :
[
{
"define" : "bridge_EXPORTS"
}
],
"includes" :
[
{
"backtrace" : 4,
"path" : "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/outputs/golang/alphaDebug/arm64-v8a"
},
{
"backtrace" : 5,
"path" : "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/golang/native"
}
],
"language" : "C",
"sourceIndexes" :
[
0,
1,
2
],
"sysroot" :
{
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot"
}
}
],
"id" : "bridge::@6890427a1f51a3e7e1df",
"link" :
{
"commandFragments" :
[
{
"fragment" : "-static-libstdc++ -Wl,--build-id=sha1 -Wl,--no-rosegment -Wl,--fatal-warnings -Wl,--no-undefined -Qunused-arguments",
"role" : "flags"
},
{
"backtrace" : 2,
"fragment" : "-L/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/outputs/golang/alphaDebug/arm64-v8a",
"role" : "libraryPath"
},
{
"backtrace" : 3,
"fragment" : "-llog",
"role" : "libraries"
},
{
"backtrace" : 3,
"fragment" : "-lclash",
"role" : "libraries"
},
{
"fragment" : "-latomic -lm",
"role" : "libraries"
}
],
"language" : "C",
"sysroot" :
{
"path" : "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot"
}
},
"name" : "bridge",
"nameOnDisk" : "libbridge.so",
"paths" :
{
"build" : ".",
"source" : "."
},
"sourceGroups" :
[
{
"name" : "Source Files",
"sourceIndexes" :
[
0,
1,
2
]
}
],
"sources" :
[
{
"backtrace" : 1,
"compileGroupIndex" : 0,
"path" : "main.c",
"sourceGroupIndex" : 0
},
{
"backtrace" : 1,
"compileGroupIndex" : 0,
"path" : "jni_helper.c",
"sourceGroupIndex" : 0
},
{
"backtrace" : 1,
"compileGroupIndex" : 0,
"path" : "bridge_helper.c",
"sourceGroupIndex" : 0
}
],
"type" : "SHARED_LIBRARY"
}
@@ -1,12 +0,0 @@
# ninja log v5
2 93 1729063389 CMakeFiles/bridge.dir/bridge_helper.c.o f028609ec8edd0d1
1 114 1729063389 CMakeFiles/bridge.dir/jni_helper.c.o 83bc6809a04c5f79
0 141 1729063389 CMakeFiles/bridge.dir/main.c.o 8e73b8907825dc18
142 187 1729063389 /Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/intermediates/cxx/Debug/1t22401e/obj/arm64-v8a/libbridge.so 6c7340a32c892534
0 9 0 clean 609cc99e61b9952c
1 370 1729233248 CMakeFiles/bridge.dir/bridge_helper.c.o f028609ec8edd0d1
0 371 1729233248 CMakeFiles/bridge.dir/jni_helper.c.o 83bc6809a04c5f79
0 244 1729233373 CMakeFiles/bridge.dir/main.c.o 8e73b8907825dc18
244 586 1729233374 /Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/intermediates/cxx/Debug/1t22401e/obj/arm64-v8a/libbridge.so 6c7340a32c892534
1 63 1729693135 CMakeFiles/bridge.dir/main.c.o 8e73b8907825dc18
63 119 1729693135 /Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/intermediates/cxx/Debug/1t22401e/obj/arm64-v8a/libbridge.so 6c7340a32c892534
@@ -1,369 +0,0 @@
# This is the CMakeCache file.
# For build in directory: /Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a
# It was generated by CMake: /Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/cmake
# You can edit this file to change values found and used by cmake.
# If you do not want to change any of the values, simply exit the editor.
# If you do want to change a value, simply edit, save, and exit the editor.
# The syntax for the file is as follows:
# KEY:TYPE=VALUE
# KEY is the name of a variable in the cache.
# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
# VALUE is the current value for the KEY.
########################
# EXTERNAL cache entries
########################
//No help, variable specified on the command line.
ANDROID_ABI:UNINITIALIZED=arm64-v8a
//No help, variable specified on the command line.
ANDROID_NDK:UNINITIALIZED=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858
//No help, variable specified on the command line.
ANDROID_PLATFORM:UNINITIALIZED=android-24
//Path to a program.
CMAKE_ADDR2LINE:FILEPATH=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-addr2line
//No help, variable specified on the command line.
CMAKE_ANDROID_ARCH_ABI:UNINITIALIZED=arm64-v8a
//No help, variable specified on the command line.
CMAKE_ANDROID_NDK:UNINITIALIZED=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858
//Archiver
CMAKE_AR:FILEPATH=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ar
//Flags used by the compiler during all build types.
CMAKE_ASM_FLAGS:STRING=
//Flags used by the compiler during debug builds.
CMAKE_ASM_FLAGS_DEBUG:STRING=
//Flags used by the compiler during release builds.
CMAKE_ASM_FLAGS_RELEASE:STRING=
//Choose the type of build, options are: None Debug Release RelWithDebInfo
// MinSizeRel ...
CMAKE_BUILD_TYPE:STRING=Debug
//Flags used by the compiler during all build types.
CMAKE_CXX_FLAGS:STRING=
//Flags used by the compiler during debug builds.
CMAKE_CXX_FLAGS_DEBUG:STRING=
//Flags used by the compiler during release builds.
CMAKE_CXX_FLAGS_RELEASE:STRING=
//LLVM archiver
CMAKE_C_COMPILER_AR:FILEPATH=CMAKE_C_COMPILER_AR-NOTFOUND
//Generate index for LLVM archive
CMAKE_C_COMPILER_RANLIB:FILEPATH=CMAKE_C_COMPILER_RANLIB-NOTFOUND
//Flags used by the compiler during all build types.
CMAKE_C_FLAGS:STRING=
//Flags used by the compiler during debug builds.
CMAKE_C_FLAGS_DEBUG:STRING=
//Flags used by the C compiler during MINSIZEREL builds.
CMAKE_C_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
//Flags used by the compiler during release builds.
CMAKE_C_FLAGS_RELEASE:STRING=
//Flags used by the C compiler during RELWITHDEBINFO builds.
CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
//Libraries linked by default with all C applications.
CMAKE_C_STANDARD_LIBRARIES:STRING=-latomic -lm
//Path to a program.
CMAKE_DLLTOOL:FILEPATH=CMAKE_DLLTOOL-NOTFOUND
//Flags used by the linker.
CMAKE_EXE_LINKER_FLAGS:STRING=
//Flags used by the linker during DEBUG builds.
CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during MINSIZEREL builds.
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during RELEASE builds.
CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during RELWITHDEBINFO builds.
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//No help, variable specified on the command line.
CMAKE_EXPORT_COMPILE_COMMANDS:UNINITIALIZED=ON
//Install path prefix, prepended onto install directories.
CMAKE_INSTALL_PREFIX:PATH=/usr/local
//No help, variable specified on the command line.
CMAKE_LIBRARY_OUTPUT_DIRECTORY:UNINITIALIZED=/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/intermediates/cxx/Debug/1t22401e/obj/arm64-v8a
//Path to a program.
CMAKE_LINKER:FILEPATH=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/ld.lld
//No help, variable specified on the command line.
CMAKE_MAKE_PROGRAM:UNINITIALIZED=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/ninja
//Flags used by the linker during the creation of modules.
CMAKE_MODULE_LINKER_FLAGS:STRING=
//Flags used by the linker during the creation of modules during
// DEBUG builds.
CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during the creation of modules during
// MINSIZEREL builds.
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during the creation of modules during
// RELEASE builds.
CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during the creation of modules during
// RELWITHDEBINFO builds.
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//Path to a program.
CMAKE_NM:FILEPATH=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-nm
//Path to a program.
CMAKE_OBJCOPY:FILEPATH=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objcopy
//Path to a program.
CMAKE_OBJDUMP:FILEPATH=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump
//Value Computed by CMake
CMAKE_PROJECT_DESCRIPTION:STATIC=
//Value Computed by CMake
CMAKE_PROJECT_HOMEPAGE_URL:STATIC=
//Value Computed by CMake
CMAKE_PROJECT_NAME:STATIC=clash-bridge
//Ranlib
CMAKE_RANLIB:FILEPATH=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ranlib
//Path to a program.
CMAKE_READELF:FILEPATH=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-readelf
//No help, variable specified on the command line.
CMAKE_RUNTIME_OUTPUT_DIRECTORY:UNINITIALIZED=/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/intermediates/cxx/Debug/1t22401e/obj/arm64-v8a
//Flags used by the linker during the creation of dll's.
CMAKE_SHARED_LINKER_FLAGS:STRING=
//Flags used by the linker during the creation of shared libraries
// during DEBUG builds.
CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during the creation of shared libraries
// during MINSIZEREL builds.
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during the creation of shared libraries
// during RELEASE builds.
CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during the creation of shared libraries
// during RELWITHDEBINFO builds.
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//If set, runtime paths are not added when installing shared libraries,
// but are added when building.
CMAKE_SKIP_INSTALL_RPATH:BOOL=NO
//If set, runtime paths are not added when using shared libraries.
CMAKE_SKIP_RPATH:BOOL=NO
//Flags used by the linker during the creation of static libraries
// during all build types.
CMAKE_STATIC_LINKER_FLAGS:STRING=
//Flags used by the linker during the creation of static libraries
// during DEBUG builds.
CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING=
//Flags used by the linker during the creation of static libraries
// during MINSIZEREL builds.
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING=
//Flags used by the linker during the creation of static libraries
// during RELEASE builds.
CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING=
//Flags used by the linker during the creation of static libraries
// during RELWITHDEBINFO builds.
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING=
//Strip
CMAKE_STRIP:FILEPATH=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-strip
//No help, variable specified on the command line.
CMAKE_SYSTEM_NAME:UNINITIALIZED=Android
//No help, variable specified on the command line.
CMAKE_SYSTEM_VERSION:UNINITIALIZED=24
//The CMake toolchain file
CMAKE_TOOLCHAIN_FILE:FILEPATH=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/build/cmake/android.toolchain.cmake
//If this value is on, makefiles will be generated without the
// .SILENT directive, and all commands will be echoed to the console
// during the make. This is useful for debugging only. With Visual
// Studio IDE projects all commands are done without /nologo.
CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE
//No help, variable specified on the command line.
FLAVOR_NAME:STRING=alpha
//No help, variable specified on the command line.
GO_OUTPUT:STRING=/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/outputs/golang
//No help, variable specified on the command line.
GO_SOURCE:STRING=/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/golang/native
//Dependencies for the target
bridge_LIB_DEPENDS:STATIC=general;log;general;clash;
//Value Computed by CMake
clash-bridge_BINARY_DIR:STATIC=/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a
//Value Computed by CMake
clash-bridge_SOURCE_DIR:STATIC=/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp
########################
# INTERNAL cache entries
########################
//ADVANCED property for variable: CMAKE_ADDR2LINE
CMAKE_ADDR2LINE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_AR
CMAKE_AR-ADVANCED:INTERNAL=1
//This is the directory where this CMakeCache.txt was created
CMAKE_CACHEFILE_DIR:INTERNAL=/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a
//Major version of cmake used to create the current loaded cache
CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3
//Minor version of cmake used to create the current loaded cache
CMAKE_CACHE_MINOR_VERSION:INTERNAL=18
//Patch version of cmake used to create the current loaded cache
CMAKE_CACHE_PATCH_VERSION:INTERNAL=1
//Path to CMake executable.
CMAKE_COMMAND:INTERNAL=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/cmake
//Path to cpack program executable.
CMAKE_CPACK_COMMAND:INTERNAL=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/cpack
//Path to ctest program executable.
CMAKE_CTEST_COMMAND:INTERNAL=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/ctest
//ADVANCED property for variable: CMAKE_C_COMPILER_AR
CMAKE_C_COMPILER_AR-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_COMPILER_RANLIB
CMAKE_C_COMPILER_RANLIB-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS
CMAKE_C_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_MINSIZEREL
CMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_C_STANDARD_LIBRARIES
CMAKE_C_STANDARD_LIBRARIES-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_DLLTOOL
CMAKE_DLLTOOL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS
CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG
CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL
CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE
CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//Name of external makefile project generator.
CMAKE_EXTRA_GENERATOR:INTERNAL=
//Name of generator.
CMAKE_GENERATOR:INTERNAL=Ninja
//Generator instance identifier.
CMAKE_GENERATOR_INSTANCE:INTERNAL=
//Name of generator platform.
CMAKE_GENERATOR_PLATFORM:INTERNAL=
//Name of generator toolset.
CMAKE_GENERATOR_TOOLSET:INTERNAL=
//Source directory with the top level CMakeLists.txt file for this
// project
CMAKE_HOME_DIRECTORY:INTERNAL=/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp
//Install .so files without execute permission.
CMAKE_INSTALL_SO_NO_EXE:INTERNAL=0
//ADVANCED property for variable: CMAKE_LINKER
CMAKE_LINKER-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS
CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG
CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL
CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE
CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_NM
CMAKE_NM-ADVANCED:INTERNAL=1
//number of local generators
CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=1
//ADVANCED property for variable: CMAKE_OBJCOPY
CMAKE_OBJCOPY-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_OBJDUMP
CMAKE_OBJDUMP-ADVANCED:INTERNAL=1
//Platform information initialized
CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1
//ADVANCED property for variable: CMAKE_RANLIB
CMAKE_RANLIB-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_READELF
CMAKE_READELF-ADVANCED:INTERNAL=1
//Path to CMake installation.
CMAKE_ROOT:INTERNAL=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/share/cmake-3.18
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS
CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG
CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL
CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE
CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH
CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_SKIP_RPATH
CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS
CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG
CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL
CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE
CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO
CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
//ADVANCED property for variable: CMAKE_STRIP
CMAKE_STRIP-ADVANCED:INTERNAL=1
//uname command
CMAKE_UNAME:INTERNAL=/usr/bin/uname
//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE
CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1
@@ -1,77 +0,0 @@
set(CMAKE_C_COMPILER "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang")
set(CMAKE_C_COMPILER_ARG1 "")
set(CMAKE_C_COMPILER_ID "Clang")
set(CMAKE_C_COMPILER_VERSION "12.0.5")
set(CMAKE_C_COMPILER_VERSION_INTERNAL "")
set(CMAKE_C_COMPILER_WRAPPER "")
set(CMAKE_C_STANDARD_COMPUTED_DEFAULT "11")
set(CMAKE_C_COMPILE_FEATURES "c_std_90;c_function_prototypes;c_std_99;c_restrict;c_variadic_macros;c_std_11;c_static_assert")
set(CMAKE_C90_COMPILE_FEATURES "c_std_90;c_function_prototypes")
set(CMAKE_C99_COMPILE_FEATURES "c_std_99;c_restrict;c_variadic_macros")
set(CMAKE_C11_COMPILE_FEATURES "c_std_11;c_static_assert")
set(CMAKE_C_PLATFORM_ID "")
set(CMAKE_C_SIMULATE_ID "")
set(CMAKE_C_COMPILER_FRONTEND_VARIANT "GNU")
set(CMAKE_C_SIMULATE_VERSION "")
set(CMAKE_AR "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ar")
set(CMAKE_C_COMPILER_AR "CMAKE_C_COMPILER_AR-NOTFOUND")
set(CMAKE_RANLIB "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-ranlib")
set(CMAKE_C_COMPILER_RANLIB "CMAKE_C_COMPILER_RANLIB-NOTFOUND")
set(CMAKE_LINKER "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/ld.lld")
set(CMAKE_MT "")
set(CMAKE_COMPILER_IS_GNUCC )
set(CMAKE_C_COMPILER_LOADED 1)
set(CMAKE_C_COMPILER_WORKS TRUE)
set(CMAKE_C_ABI_COMPILED TRUE)
set(CMAKE_COMPILER_IS_MINGW )
set(CMAKE_COMPILER_IS_CYGWIN )
if(CMAKE_COMPILER_IS_CYGWIN)
set(CYGWIN 1)
set(UNIX 1)
endif()
set(CMAKE_C_COMPILER_ENV_VAR "CC")
if(CMAKE_COMPILER_IS_MINGW)
set(MINGW 1)
endif()
set(CMAKE_C_COMPILER_ID_RUN 1)
set(CMAKE_C_SOURCE_FILE_EXTENSIONS c;m)
set(CMAKE_C_IGNORE_EXTENSIONS h;H;o;O;obj;OBJ;def;DEF;rc;RC)
set(CMAKE_C_LINKER_PREFERENCE 10)
# Save compiler ABI information.
set(CMAKE_C_SIZEOF_DATA_PTR "8")
set(CMAKE_C_COMPILER_ABI "ELF")
set(CMAKE_C_LIBRARY_ARCHITECTURE "")
if(CMAKE_C_SIZEOF_DATA_PTR)
set(CMAKE_SIZEOF_VOID_P "${CMAKE_C_SIZEOF_DATA_PTR}")
endif()
if(CMAKE_C_COMPILER_ABI)
set(CMAKE_INTERNAL_PLATFORM_ABI "${CMAKE_C_COMPILER_ABI}")
endif()
if(CMAKE_C_LIBRARY_ARCHITECTURE)
set(CMAKE_LIBRARY_ARCHITECTURE "")
endif()
set(CMAKE_C_CL_SHOWINCLUDES_PREFIX "")
if(CMAKE_C_CL_SHOWINCLUDES_PREFIX)
set(CMAKE_CL_SHOWINCLUDES_PREFIX "${CMAKE_C_CL_SHOWINCLUDES_PREFIX}")
endif()
set(CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/local/include;/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/12.0.5/include;/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/aarch64-linux-android;/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include")
set(CMAKE_C_IMPLICIT_LINK_LIBRARIES "-l:libunwind.a;dl;c;-l:libunwind.a;dl")
set(CMAKE_C_IMPLICIT_LINK_DIRECTORIES "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/12.0.5/lib/linux/aarch64;/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/aarch64-linux-android/24;/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/aarch64-linux-android;/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib")
set(CMAKE_C_IMPLICIT_LINK_FRAMEWORK_DIRECTORIES "")
@@ -1,15 +0,0 @@
set(CMAKE_HOST_SYSTEM "Darwin-22.6.0")
set(CMAKE_HOST_SYSTEM_NAME "Darwin")
set(CMAKE_HOST_SYSTEM_VERSION "22.6.0")
set(CMAKE_HOST_SYSTEM_PROCESSOR "x86_64")
include("/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/build/cmake/android.toolchain.cmake")
set(CMAKE_SYSTEM "Android-1")
set(CMAKE_SYSTEM_NAME "Android")
set(CMAKE_SYSTEM_VERSION "1")
set(CMAKE_SYSTEM_PROCESSOR "aarch64")
set(CMAKE_CROSSCOMPILING "TRUE")
set(CMAKE_SYSTEM_LOADED 1)
@@ -1,3 +0,0 @@
/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a/CMakeFiles/rebuild_cache.dir
/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a/CMakeFiles/edit_cache.dir
/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a/CMakeFiles/bridge.dir
@@ -1 +0,0 @@
# This file is generated by cmake for dependency checking of the CMakeCache.txt file
@@ -1,64 +0,0 @@
# CMAKE generated file: DO NOT EDIT!
# Generated by "Ninja" Generator, CMake Version 3.18
# This file contains all the rules used to get the outputs files
# built from the input files.
# It is included in the main 'build.ninja'.
# =============================================================================
# Project: clash-bridge
# Configurations: Debug
# =============================================================================
# =============================================================================
#############################################
# Rule for running custom commands.
rule CUSTOM_COMMAND
command = $COMMAND
description = $DESC
#############################################
# Rule for compiling C files.
rule C_COMPILER__bridge_Debug
depfile = $DEP_FILE
deps = gcc
command = /Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang --target=aarch64-none-linux-android24 --gcc-toolchain=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64 --sysroot=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot $DEFINES $INCLUDES $FLAGS -MD -MT $out -MF $DEP_FILE -o $out -c $in
description = Building C object $out
#############################################
# Rule for linking C shared library.
rule C_SHARED_LIBRARY_LINKER__bridge_Debug
command = $PRE_LINK && /Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang --target=aarch64-none-linux-android24 --gcc-toolchain=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64 --sysroot=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -fPIC $LANGUAGE_COMPILE_FLAGS $ARCH_FLAGS $LINK_FLAGS -shared $SONAME_FLAG$SONAME -o $TARGET_FILE $in $LINK_PATH $LINK_LIBRARIES && $POST_BUILD
description = Linking C shared library $TARGET_FILE
restat = $RESTAT
#############################################
# Rule for re-running cmake.
rule RERUN_CMAKE
command = /Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/cmake --regenerate-during-build -S/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp -B/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a
description = Re-running CMake...
generator = 1
#############################################
# Rule for cleaning all built files.
rule CLEAN
command = /Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/ninja $FILE_ARG -t clean $TARGETS
description = Cleaning all built files...
#############################################
# Rule for printing all primary targets available.
rule HELP
command = /Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/ninja -t targets
description = All primary targets available:
@@ -1,38 +0,0 @@
{
"buildFiles": [
"/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp/CMakeLists.txt"
],
"cleanCommandsComponents": [
[
"/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/ninja",
"-C",
"/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a",
"clean"
]
],
"buildTargetsCommandComponents": [
"/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/ninja",
"-C",
"/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a",
"{LIST_OF_TARGETS_TO_BUILD}"
],
"libraries": {
"bridge::@6890427a1f51a3e7e1df": {
"toolchain": "toolchain",
"abi": "arm64-v8a",
"artifactName": "bridge",
"output": "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/intermediates/cxx/Debug/1t22401e/obj/arm64-v8a/libbridge.so",
"runtimeFiles": []
}
},
"toolchains": {
"toolchain": {
"cCompilerExecutable": "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang.lld",
"cppCompilerExecutable": "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++.lld"
}
},
"cFileExtensions": [
"c"
],
"cppFileExtensions": []
}
@@ -1,7 +0,0 @@
Executable : /Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/ninja
arguments :
-C
/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a
bridge
jvmArgs :
@@ -1,2 +0,0 @@
ninja: Entering directory `/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a'
ninja: no work to do.
File diff suppressed because one or more lines are too long
@@ -1,54 +0,0 @@
# Install script for directory: /Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp
# Set the install prefix
if(NOT DEFINED CMAKE_INSTALL_PREFIX)
set(CMAKE_INSTALL_PREFIX "/usr/local")
endif()
string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
# Set the install configuration name.
if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
if(BUILD_TYPE)
string(REGEX REPLACE "^[^A-Za-z0-9_]+" ""
CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}")
else()
set(CMAKE_INSTALL_CONFIG_NAME "Debug")
endif()
message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"")
endif()
# Set the component getting installed.
if(NOT CMAKE_INSTALL_COMPONENT)
if(COMPONENT)
message(STATUS "Install component: \"${COMPONENT}\"")
set(CMAKE_INSTALL_COMPONENT "${COMPONENT}")
else()
set(CMAKE_INSTALL_COMPONENT)
endif()
endif()
# Install shared libraries without execute permission?
if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)
set(CMAKE_INSTALL_SO_NO_EXE "0")
endif()
# Is this installation the result of a crosscompile?
if(NOT DEFINED CMAKE_CROSSCOMPILING)
set(CMAKE_CROSSCOMPILING "TRUE")
endif()
# Set default install directory permissions.
if(NOT DEFINED CMAKE_OBJDUMP)
set(CMAKE_OBJDUMP "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump")
endif()
if(CMAKE_INSTALL_COMPONENT)
set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt")
else()
set(CMAKE_INSTALL_MANIFEST "install_manifest.txt")
endif()
string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT
"${CMAKE_INSTALL_MANIFEST_FILES}")
file(WRITE "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a/${CMAKE_INSTALL_MANIFEST}"
"${CMAKE_INSTALL_MANIFEST_CONTENT}")
@@ -1,17 +0,0 @@
[
{
"directory": "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a",
"command": "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang --target=aarch64-none-linux-android24 --gcc-toolchain=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64 --sysroot=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -Dbridge_EXPORTS -I/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/outputs/golang/alphaDebug/arm64-v8a -I/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/golang/native -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -O0 -fno-limit-debug-info -fPIC -o CMakeFiles/bridge.dir/main.c.o -c /Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp/main.c",
"file": "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp/main.c"
},
{
"directory": "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a",
"command": "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang --target=aarch64-none-linux-android24 --gcc-toolchain=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64 --sysroot=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -Dbridge_EXPORTS -I/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/outputs/golang/alphaDebug/arm64-v8a -I/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/golang/native -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -O0 -fno-limit-debug-info -fPIC -o CMakeFiles/bridge.dir/jni_helper.c.o -c /Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp/jni_helper.c",
"file": "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp/jni_helper.c"
},
{
"directory": "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a",
"command": "/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang --target=aarch64-none-linux-android24 --gcc-toolchain=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64 --sysroot=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/sysroot -Dbridge_EXPORTS -I/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/outputs/golang/alphaDebug/arm64-v8a -I/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/golang/native -g -DANDROID -fdata-sections -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security -O0 -fno-limit-debug-info -fPIC -o CMakeFiles/bridge.dir/bridge_helper.c.o -c /Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp/bridge_helper.c",
"file": "/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp/bridge_helper.c"
}
]
@@ -1,26 +0,0 @@
Executable : /Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/cmake
arguments :
-H/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/cpp
-DCMAKE_SYSTEM_NAME=Android
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
-DCMAKE_SYSTEM_VERSION=24
-DANDROID_PLATFORM=android-24
-DANDROID_ABI=arm64-v8a
-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a
-DANDROID_NDK=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858
-DCMAKE_ANDROID_NDK=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858
-DCMAKE_TOOLCHAIN_FILE=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/ndk/23.0.7599858/build/cmake/android.toolchain.cmake
-DCMAKE_MAKE_PROGRAM=/Applications/Unity/Hub/Editor/2022.3.32f1/PlaybackEngines/AndroidPlayer/SDK/cmake/3.18.1/bin/ninja
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/intermediates/cxx/Debug/1t22401e/obj/arm64-v8a
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/intermediates/cxx/Debug/1t22401e/obj/arm64-v8a
-DCMAKE_BUILD_TYPE=Debug
-B/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/.cxx/Debug/1t22401e/arm64-v8a
-GNinja
-DGO_SOURCE:STRING=/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/src/main/golang/native
-DGO_OUTPUT:STRING=/Users/zeus/Documents/GitHub/ClashMetaForAndroid/core/build/outputs/golang
-DFLAVOR_NAME:STRING=alpha
jvmArgs :
Build command args: []
Version: 2
@@ -1,4 +0,0 @@
{
"enabled": false,
"packages": []
}

Some files were not shown because too many files have changed in this diff Show More