From 36b43c3ca907ba8cab425dac78ab64e210fc5432 Mon Sep 17 00:00:00 2001 From: rain_ Date: Wed, 8 Jul 2026 19:10:24 +0800 Subject: [PATCH 01/12] android: add vpn lifecycle controls --- .../java/com/proxy/android/MainActivity.kt | 79 ++++++++++- .../java/com/proxy/android/PlaneVpnService.kt | 125 ++++++++++++++---- .../app/src/main/res/layout/activity_main.xml | 12 +- .../app/src/main/res/values/strings.xml | 7 +- 4 files changed, 193 insertions(+), 30 deletions(-) diff --git a/android-app/app/src/main/java/com/proxy/android/MainActivity.kt b/android-app/app/src/main/java/com/proxy/android/MainActivity.kt index 0dbdcd5..68b2af9 100644 --- a/android-app/app/src/main/java/com/proxy/android/MainActivity.kt +++ b/android-app/app/src/main/java/com/proxy/android/MainActivity.kt @@ -1,6 +1,9 @@ package com.proxy.android +import android.content.BroadcastReceiver +import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.net.VpnService import android.os.Bundle import android.widget.Button @@ -12,21 +15,40 @@ import androidx.core.content.ContextCompat /** * 主界面。 * - * A1 职责: + * 当前职责: * 1. 显示 native(plane-core)版本号,证明 `.so` 已加载、JNI 可调用。 - * 2. 一个开关按钮,走 [VpnService.prepare]:若未授权则拉起系统 VPN 授权框, + * 2. 连接前走 [VpnService.prepare]:若未授权则拉起系统 VPN 授权框, * 授权通过后启动前台 [PlaneVpnService]。 + * 3. 接收 [PlaneVpnService] 的状态广播,驱动按钮与状态文本,保证 UI 不自行猜测 + * Service 生命周期。 * - * 后续阶段(B6)会把界面扩展为节点/规则配置与状态展示,这里只保留最小可验证骨架。 + * 后续阶段(B6)会把界面扩展为节点/规则配置与统计展示。 */ class MainActivity : AppCompatActivity() { + private lateinit var toggleVpnButton: Button + private lateinit var vpnStatusText: TextView + + /** Activity 只保存 UI 状态;真实生命周期以 PlaneVpnService 广播为准。 */ + private var vpnRunning: Boolean = false + + private val statusReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + if (intent?.action != PlaneVpnService.ACTION_STATUS_CHANGED) return + val running = intent.getBooleanExtra(PlaneVpnService.EXTRA_RUNNING, false) + val message = intent.getStringExtra(PlaneVpnService.EXTRA_STATUS_MESSAGE) + updateVpnUi(running, message) + } + } + // 系统 VPN 授权对话框的结果回调:用户点「允许」后启动 VPN 服务。 private val prepareLauncher = registerForActivityResult( ActivityResultContracts.StartActivityForResult(), ) { result -> if (result.resultCode == RESULT_OK) { startVpn() + } else { + updateVpnUi(false, getString(R.string.vpn_status_idle)) } } @@ -37,7 +59,27 @@ class MainActivity : AppCompatActivity() { val versionView = findViewById(R.id.nativeVersionText) versionView.text = getString(R.string.native_version_label, readNativeVersion()) - findViewById