Add configurable deployment system with VPN and Tor support
Features: - Hidden config.json in assets for per-deployment customization - Configure target URL, app name, and branding - Optional WireGuard VPN with auto-connect - Optional Tor routing via Orbot - Custom theme colors - Configuration-driven app behavior Configuration file location: app/src/main/assets/config.json Example configuration: config.example.json 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
package uk.silverlabs.silverdroid.vpn
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.VpnService
|
||||
import android.util.Log
|
||||
import com.wireguard.android.backend.GoBackend
|
||||
import com.wireguard.config.Config
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import uk.silverlabs.silverdroid.config.VpnConfig
|
||||
|
||||
/**
|
||||
* Manages WireGuard VPN connections
|
||||
*/
|
||||
class WireGuardManager(private val context: Context) {
|
||||
|
||||
private val backend = GoBackend(context)
|
||||
private val _connectionState = MutableStateFlow(VpnState.DISCONNECTED)
|
||||
val connectionState: StateFlow<VpnState> = _connectionState
|
||||
|
||||
suspend fun connect(vpnConfig: VpnConfig): Result<Unit> {
|
||||
return try {
|
||||
// Check VPN permission
|
||||
val intent = VpnService.prepare(context)
|
||||
if (intent != null) {
|
||||
return Result.failure(Exception("VPN permission required"))
|
||||
}
|
||||
|
||||
// Build WireGuard config
|
||||
val configText = buildWireGuardConfig(vpnConfig)
|
||||
val config = Config.parse(configText.byteInputStream())
|
||||
|
||||
// Set up tunnel
|
||||
val tunnel = backend.tunnels.firstOrNull() ?: backend.createTunnel(
|
||||
"silverdroid_vpn",
|
||||
config,
|
||||
null
|
||||
)
|
||||
|
||||
// Connect
|
||||
backend.setState(tunnel, com.wireguard.android.backend.Tunnel.State.UP)
|
||||
|
||||
_connectionState.value = VpnState.CONNECTED
|
||||
Log.i(TAG, "WireGuard VPN connected successfully")
|
||||
|
||||
Result.success(Unit)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to connect VPN", e)
|
||||
_connectionState.value = VpnState.ERROR
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun disconnect() {
|
||||
try {
|
||||
backend.tunnels.forEach { tunnel ->
|
||||
backend.setState(tunnel, com.wireguard.android.backend.Tunnel.State.DOWN)
|
||||
}
|
||||
_connectionState.value = VpnState.DISCONNECTED
|
||||
Log.i(TAG, "WireGuard VPN disconnected")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error disconnecting VPN", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildWireGuardConfig(vpnConfig: VpnConfig): String {
|
||||
val peers = vpnConfig.peers.joinToString("\n\n") { peer ->
|
||||
"""
|
||||
[Peer]
|
||||
PublicKey = ${peer.publicKey}
|
||||
Endpoint = ${peer.endpoint}
|
||||
AllowedIPs = ${peer.allowedIps.joinToString(", ")}
|
||||
PersistentKeepalive = ${peer.persistentKeepalive}
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
return """
|
||||
[Interface]
|
||||
PrivateKey = ${vpnConfig.privateKey}
|
||||
Address = ${vpnConfig.address}
|
||||
${if (vpnConfig.dns.isNotEmpty()) "DNS = ${vpnConfig.dns.joinToString(", ")}" else ""}
|
||||
|
||||
$peers
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "WireGuardManager"
|
||||
}
|
||||
}
|
||||
|
||||
enum class VpnState {
|
||||
DISCONNECTED,
|
||||
CONNECTING,
|
||||
CONNECTED,
|
||||
DISCONNECTING,
|
||||
ERROR
|
||||
}
|
||||
Reference in New Issue
Block a user