Skip to content

Commit

Permalink
Use android embedding v2
Browse files Browse the repository at this point in the history
  • Loading branch information
ctrysbita committed Apr 7, 2020
1 parent 7dba0c1 commit 5b3ab39
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 95 deletions.
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ android {

defaultConfig {
minSdkVersion 16
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

lintOptions {
Expand All @@ -73,5 +72,6 @@ android {

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// TODO: Remove when custom icon available.
implementation 'androidx.appcompat:appcompat:1.1.0'
}
195 changes: 106 additions & 89 deletions android/src/main/kotlin/io/xdea/flutter_vpn/FlutterVpnPlugin.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2018 Jason C.H
* Copyright (C) 2018-2020 Jason C.H
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand All @@ -22,107 +22,124 @@ import android.content.ServiceConnection
import android.net.VpnService
import android.os.Bundle
import android.os.IBinder
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import io.flutter.plugin.common.PluginRegistry.Registrar
import org.strongswan.android.logic.VpnStateService

class FlutterVpnPlugin(private val registrar: Registrar) : MethodCallHandler {
private var _shouldUnbind: Boolean = false
private var _vpnStateService: VpnStateService? = null
private val _serviceConnection = object : ServiceConnection {
override fun onServiceDisconnected(name: ComponentName) {
_vpnStateService = null
VpnStateHandler.vpnStateService = null
}
/** FlutterVpnPlugin */
class FlutterVpnPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
private lateinit var activityBinding: ActivityPluginBinding

override fun onServiceConnected(name: ComponentName, service: IBinder) {
_vpnStateService = (service as VpnStateService.LocalBinder).service
VpnStateHandler.vpnStateService = _vpnStateService
_vpnStateService?.registerListener(VpnStateHandler)
}
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel: MethodChannel
private lateinit var eventChannel: EventChannel

private var vpnStateService: VpnStateService? = null
private val _serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
vpnStateService = (service as VpnStateService.LocalBinder).service
VpnStateHandler.vpnStateService = vpnStateService
vpnStateService?.registerListener(VpnStateHandler)
}

init {
// Load charon bridge
System.loadLibrary("androidbridge")

// Start and bind VpnStateService to current context.
if (registrar.activeContext().bindService(
Intent(registrar.activeContext(), VpnStateService::class.java),
_serviceConnection,
Service.BIND_AUTO_CREATE
)) {
_shouldUnbind = true
}
registrar.addViewDestroyListener {
if (_shouldUnbind) {
registrar.activeContext().unbindService(_serviceConnection)
_shouldUnbind = false
}
true
}
override fun onServiceDisconnected(name: ComponentName) {
vpnStateService = null
VpnStateHandler.vpnStateService = null
}
}

companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
// Register method channel.
val channel = MethodChannel(registrar.messenger(), "flutter_vpn")
channel.setMethodCallHandler(FlutterVpnPlugin(registrar))
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
// Load charon bridge
System.loadLibrary("androidbridge")

// Register event channel to handle state change.
val eventChannel = EventChannel(registrar.messenger(), "flutter_vpn_states")
eventChannel.setStreamHandler(VpnStateHandler)
}
}
// Register method channel.
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_vpn")
channel.setMethodCallHandler(this);

// Register event channel to handle state change.
eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "flutter_vpn_states")
eventChannel.setStreamHandler(VpnStateHandler)

flutterPluginBinding.applicationContext.bindService(
Intent(flutterPluginBinding.applicationContext, VpnStateService::class.java),
_serviceConnection,
Service.BIND_AUTO_CREATE
)
}

override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
channel.setMethodCallHandler(null)
eventChannel.setStreamHandler(null)
}

override fun onMethodCall(call: MethodCall, result: Result) {
when (call.method) {
"prepare" -> {
val intent = VpnService.prepare(registrar.activeContext())
if (intent != null) {
registrar.addActivityResultListener { req, res, _ ->
if (req == 0 && res == RESULT_OK)
result.success(null)
else
result.error("PrepareError", "Failed to prepare", false)
true
}
registrar.activity().startActivityForResult(intent, 0)
}
}
"connect" -> {
val intent = VpnService.prepare(registrar.activeContext())
if (intent != null) {
result.error("PrepareError", "Not prepared", false)
return
}

val map = call.arguments as HashMap<String, String>

val profileInfo = Bundle()
profileInfo.putString("Address", map["address"])
profileInfo.putString("UserName", map["username"])
profileInfo.putString("Password", map["password"])
profileInfo.putString("VpnType", "ikev2-eap")
profileInfo.putInt("MTU", map["mtu"]?.toInt() ?: 1400)

_vpnStateService?.connect(profileInfo, true)
result.success(null)
}
"getCurrentState" -> {
if (_vpnStateService?.errorState != VpnStateService.ErrorState.NO_ERROR)
result.success(4)
else
result.success(_vpnStateService?.state?.ordinal)
}
"getCharonErrorState" -> result.success(_vpnStateService?.errorState?.ordinal)
"disconnect" -> _vpnStateService?.disconnect()
else -> result.notImplemented()

override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activityBinding = binding
}

override fun onDetachedFromActivity() {
}

override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
activityBinding = binding
}

override fun onDetachedFromActivityForConfigChanges() {
}

override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
when (call.method) {
"prepare" -> {
val intent = VpnService.prepare(activityBinding.activity.applicationContext)
if (intent != null) {
activityBinding.addActivityResultListener { req, res, _ ->
if (req == 0 && res == RESULT_OK)
result.success(null)
else
result.error("PrepareError", "Failed to prepare", false)
true
}
activityBinding.activity.startActivityForResult(intent, 0)
}
}
"connect" -> {
val intent = VpnService.prepare(activityBinding.activity.applicationContext)
if (intent != null) {
result.error("PrepareError", "Not prepared", false)
return
}

val map = call.arguments as HashMap<String, String>

val profileInfo = Bundle()
profileInfo.putString("Address", map["address"])
profileInfo.putString("UserName", map["username"])
profileInfo.putString("Password", map["password"])
profileInfo.putString("VpnType", "ikev2-eap")
profileInfo.putInt("MTU", map["mtu"]?.toInt() ?: 1400)

vpnStateService?.connect(profileInfo, true)
result.success(null)
}
"getCurrentState" -> {
if (vpnStateService?.errorState != VpnStateService.ErrorState.NO_ERROR)
result.success(4)
else
result.success(vpnStateService?.state?.ordinal)
}
"getCharonErrorState" -> result.success(vpnStateService?.errorState?.ordinal)
"disconnect" -> vpnStateService?.disconnect()
else -> result.notImplemented()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (C) 2018 Jason C.H
* Copyright (C) 2018-2020 Jason C.H
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down
4 changes: 0 additions & 4 deletions example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ android {
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
Expand All @@ -68,7 +67,4 @@ flutter {

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
}

0 comments on commit 5b3ab39

Please sign in to comment.