Browse Source

添加人脸配置代码

RandyWei 5 years ago
parent
commit
cbcf99bba9

+ 115 - 33
android/src/main/kotlin/dev/bughub/plugin/fltbdface/FltbdfacePlugin.kt

@@ -1,7 +1,11 @@
 package dev.bughub.plugin.fltbdface
 
+import android.app.Activity
 import androidx.annotation.NonNull;
+import dev.bughub.plugin.fltbdface.face.FaceDelegate
 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.MethodCall
 import io.flutter.plugin.common.MethodChannel
 import io.flutter.plugin.common.MethodChannel.MethodCallHandler
@@ -9,37 +13,115 @@ import io.flutter.plugin.common.MethodChannel.Result
 import io.flutter.plugin.common.PluginRegistry.Registrar
 
 /** FltbdfacePlugin */
-public class FltbdfacePlugin: FlutterPlugin, MethodCallHandler {
-  override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
-    val channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "fltbdface")
-    channel.setMethodCallHandler(FltbdfacePlugin());
-  }
-
-  // This static function is optional and equivalent to onAttachedToEngine. It supports the old
-  // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting
-  // plugin registration via this function while apps migrate to use the new Android APIs
-  // post-flutter-1.12 via https://flutter.dev/go/android-project-migration.
-  //
-  // It is encouraged to share logic between onAttachedToEngine and registerWith to keep
-  // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called
-  // depending on the user's project. onAttachedToEngine or registerWith must both be defined
-  // in the same class.
-  companion object {
-    @JvmStatic
-    fun registerWith(registrar: Registrar) {
-      val channel = MethodChannel(registrar.messenger(), "fltbdface")
-      channel.setMethodCallHandler(FltbdfacePlugin())
-    }
-  }
-
-  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
-    if (call.method == "getPlatformVersion") {
-      result.success("Android ${android.os.Build.VERSION.RELEASE}")
-    } else {
-      result.notImplemented()
-    }
-  }
-
-  override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
-  }
+public class FltbdfacePlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
+
+    private var activity: Activity? = null
+    private var delegate: FaceDelegate? = null
+    private var activityBinding: ActivityPluginBinding? = null
+    private var flutterBinding: FlutterPlugin.FlutterPluginBinding? = null
+
+    override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
+        flutterBinding = flutterPluginBinding
+        val channel = MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "fltbdface")
+        channel.setMethodCallHandler(FltbdfacePlugin());
+    }
+
+    // This static function is optional and equivalent to onAttachedToEngine. It supports the old
+    // pre-Flutter-1.12 Android projects. You are encouraged to continue supporting
+    // plugin registration via this function while apps migrate to use the new Android APIs
+    // post-flutter-1.12 via https://flutter.dev/go/android-project-migration.
+    //
+    // It is encouraged to share logic between onAttachedToEngine and registerWith to keep
+    // them functionally equivalent. Only one of onAttachedToEngine or registerWith will be called
+    // depending on the user's project. onAttachedToEngine or registerWith must both be defined
+    // in the same class.
+    companion object {
+        @JvmStatic
+        fun registerWith(registrar: Registrar) {
+            val channel = MethodChannel(registrar.messenger(), "fltbdface")
+            channel.setMethodCallHandler(FltbdfacePlugin())
+        }
+    }
+
+    override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
+        if (activity == null) {
+            result.error("no_activity", "face plugin requires a foreground activity.", null)
+            return
+        }
+
+        when (call.method) {
+            "initialize" -> {
+                val licenseId = call.argument<String>("licenseId") ?: ""
+                val licenseFileName = call.argument<String>("licenseFileName") ?: ""
+                delegate?.initialize(licenseId, licenseFileName)
+                result.success(null)
+            }
+            "setFaceConfig" -> {
+                val livenessTypeList = call.argument<List<Int>>("livenessTypeList") ?: arrayListOf()
+
+                val livenessRandom: Boolean = call.argument("isLivenessRandom") ?: false
+
+                val blurnessValue: Float? = call.argument<Double>("blurnessValue")?.toFloat()
+
+                val brightnessValue: Float? = call.argument<Double>("brightnessValue")?.toFloat()
+
+                val cropFaceValue: Int? = call.argument<Int>("cropFaceValue")
+
+                val headPitchValue: Int? = call.argument<Int>("headPitchValue")
+                val headRollValue: Int? = call.argument<Int>("headRollValue")
+                val headYawValue: Int? = call.argument<Int>("headYawValue")
+                val minFaceSize: Int? = call.argument<Int>("minFaceSize")
+                val notFaceValue: Float? = call.argument<Double>("notFaceValue")?.toFloat()
+                val occlusionValue: Float? = call.argument<Double>("occlusionValue")?.toFloat()
+                val checkFaceQuality: Boolean? = call.argument<Boolean>("checkFaceQuality")
+                val faceDecodeNumberOfThreads: Int? = call.argument<Int>("faceDecodeNumberOfThreads")
+                val livenessRandomCount: Int? = call.argument<Int>("livenessRandomCount")
+
+
+                delegate?.setFaceConfig(livenessTypeList, livenessRandom = livenessRandom, blurnessValue = blurnessValue, brightnessValue = brightnessValue,
+                        cropFaceValue = cropFaceValue, headPitchValue = headPitchValue, headRollValue = headRollValue, headYawValue = headYawValue, minFaceSize = minFaceSize,
+                        notFaceValue = notFaceValue, occlusionValue = occlusionValue, checkFaceQuality = checkFaceQuality, faceDecodeNumberOfThreads = faceDecodeNumberOfThreads, livenessRandomCount = livenessRandomCount)
+
+                result.success(null)
+            }
+            "startFaceLiveness" -> {
+                flutterBinding?.binaryMessenger?.let { delegate?.startFaceLiveness(it) }
+                result.success(null)
+            }
+        }
+    }
+
+    private fun tearDown() {
+
+        delegate?.let {
+            activityBinding?.removeActivityResultListener(it)
+            delegate = null
+            activityBinding = null
+        }
+
+    }
+
+    override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
+        flutterBinding = null
+    }
+
+    override fun onDetachedFromActivity() {
+        tearDown()
+    }
+
+    override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
+        onAttachedToActivity(binding)
+    }
+
+    override fun onAttachedToActivity(binding: ActivityPluginBinding) {
+        activityBinding = binding
+        activity = binding.activity
+        delegate = FaceDelegate(binding.activity)
+        activityBinding?.addActivityResultListener(delegate!!)
+        activityBinding?.addRequestPermissionsResultListener(delegate!!)
+    }
+
+    override fun onDetachedFromActivityForConfigChanges() {
+        onDetachedFromActivity()
+    }
 }

+ 71 - 0
android/src/main/kotlin/dev/bughub/plugin/fltbdface/QueuingEventSink.kt

@@ -0,0 +1,71 @@
+package com.chinahrt.app.pharmacist
+
+import io.flutter.plugin.common.EventChannel
+import java.util.ArrayList
+
+/**
+ * 这个类是从Google官方copy的
+ *
+ * And implementation of {@link EventChannel.EventSink} which can wrap an underlying sink.
+ *
+ * <p>It delivers messages immediately when downstream is available, but it queues messages before
+ * the delegate event sink is set with setDelegate.
+ *
+ * <p>This class is not thread-safe. All calls must be done on the same thread or synchronized
+ * externally.
+ */
+class QueuingEventSink : EventChannel.EventSink {
+
+    private var delegate: EventChannel.EventSink? = null
+    private val eventQueue = ArrayList<Any>()
+    private var done = false
+
+    fun setDelegate(delegate: EventChannel.EventSink?) {
+        this.delegate = delegate
+        maybeFlush()
+    }
+
+    override fun endOfStream() {
+        enqueue(EndOfStreamEvent())
+        maybeFlush()
+        done = true
+    }
+
+    override fun error(code: String, message: String, details: Any) {
+        enqueue(ErrorEvent(code, message, details))
+        maybeFlush()
+    }
+
+    override fun success(event: Any) {
+        enqueue(event)
+        maybeFlush()
+    }
+
+    private fun enqueue(event: Any) {
+        if (done) {
+            return
+        }
+        eventQueue.add(event)
+    }
+
+    private fun maybeFlush() {
+        if (delegate == null) {
+            return
+        }
+        for (event in eventQueue) {
+            when (event) {
+                is EndOfStreamEvent -> delegate?.endOfStream()
+                is ErrorEvent -> {
+                    val errorEvent = event as ErrorEvent
+                    delegate?.error(errorEvent.code, errorEvent.message, errorEvent.details)
+                }
+                else -> delegate?.success(event)
+            }
+        }
+        eventQueue.clear()
+    }
+
+    private class EndOfStreamEvent
+
+    private class ErrorEvent internal constructor(internal var code: String, internal var message: String, internal var details: Any)
+}

+ 98 - 0
android/src/main/kotlin/dev/bughub/plugin/fltbdface/face/BdFaceLivenessActivity.kt

@@ -0,0 +1,98 @@
+package dev.bughub.plugin.fltbdface.face
+
+import android.Manifest
+import android.annotation.TargetApi
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Bundle
+import android.widget.Toast
+import com.baidu.idl.face.platform.FaceStatusEnum
+import com.baidu.idl.face.platform.ui.FaceLivenessActivity
+import java.util.*
+
+
+class BdFaceLivenessActivity : FaceLivenessActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        requestPermissions(99, Manifest.permission.CAMERA)
+        //人脸检测的活体动作
+    }
+
+    override fun onLivenessCompletion(status: FaceStatusEnum, message: String, base64ImageMap: HashMap<String, String>?) {
+        super.onLivenessCompletion(status, message, base64ImageMap)
+        if (status == FaceStatusEnum.OK && mIsCompletion) {
+            base64ImageMap?.let { baseImages ->
+                baseImages["bestImage0"]?.let {
+                    val data = Intent()
+                    data.putExtra("data", it)
+                    setResult(10001, data)
+                    finish()
+//                    ApiBdFace().doFaceMatch(loginName, it, object : Network.OnResponseBaseListener {
+//                        override fun onSuccess() {
+//                            isFaceLived = true
+//                            isFaceLivedBack = false
+//                            toastResult.text = "人脸识别成功"
+//                            Handler().postDelayed({ finish() }, 2000)
+//                            setResult(Activity.RESULT_OK)
+//                        }
+//
+//                        override fun onFailure(status: Int, message: String?) {
+//                            isFaceLived = false
+//                            isFaceLivedBack = true
+//                            toastResult.text = "人脸识别失败$message"
+//                            Handler().postDelayed({ finish() }, 3000)
+//                        }
+//
+//                        override fun onError(message: String?) {
+//                            isFaceLived = false
+//                            isFaceLivedBack = true
+//                            toastResult.text = "人脸识别失败$message"
+//                            Handler().postDelayed({ finish() }, 3000)
+//                        }
+//                    })
+                }
+            }
+
+        } else if (status == FaceStatusEnum.Error_DetectTimeout ||
+                status == FaceStatusEnum.Error_LivenessTimeout ||
+                status == FaceStatusEnum.Error_Timeout) {
+            Toast.makeText(this, "采集超时", Toast.LENGTH_LONG).show()
+        }
+    }
+
+    private fun requestPermissions(requestCode: Int, permission: String?) {
+        if (permission != null && permission.isNotEmpty()) {
+            try {
+                if (Build.VERSION.SDK_INT >= 23) {
+                    // 检查是否有权限
+                    val hasPer = checkSelfPermission(permission)
+                    if (hasPer != PackageManager.PERMISSION_GRANTED) {
+                        // 是否应该显示权限请求
+                        // val isShould = shouldShowRequestPermissionRationale(permission)
+                        requestPermissions(arrayOf(permission), requestCode)
+                    }
+                } else {
+
+                }
+            } catch (ex: Exception) {
+                ex.printStackTrace()
+            }
+
+        }
+    }
+
+    @TargetApi(Build.VERSION_CODES.M)
+    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
+                                            grantResults: IntArray) {
+        var flag = false
+        for (i in permissions.indices) {
+            if (PackageManager.PERMISSION_GRANTED == grantResults[i]) {
+                flag = true
+            }
+        }
+        if (!flag) {
+            requestPermissions(99, Manifest.permission.CAMERA)
+        }
+    }
+}

+ 274 - 0
android/src/main/kotlin/dev/bughub/plugin/fltbdface/face/FaceDelegate.kt

@@ -0,0 +1,274 @@
+package dev.bughub.plugin.fltbdface.face
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.util.Log
+import com.baidu.idl.face.platform.FaceEnvironment
+import com.baidu.idl.face.platform.FaceSDKManager
+import com.baidu.idl.face.platform.LivenessTypeEnum
+import com.chinahrt.app.pharmacist.QueuingEventSink
+import io.flutter.plugin.common.BinaryMessenger
+import io.flutter.plugin.common.EventChannel
+import io.flutter.plugin.common.PluginRegistry
+import java.util.*
+
+
+class FaceDelegate(var activity: Activity) : PluginRegistry.ActivityResultListener, PluginRegistry.RequestPermissionsResultListener {
+
+    val REQUEST_CAMERA_VIDEO_PERMISSION = 2355
+    val FACE_LIVENESS_REQUEST_CODE = 123
+    var permissionManager: PermissionManager? = null
+    private val eventSink = QueuingEventSink()
+
+
+//    init {
+//        permissionManager = object: PermissionManager{
+//            override fun isPermissionGranted(permissionName: String?): Boolean {
+//                return permissionName?.let { ActivityCompat.checkSelfPermission(activity, it) } == PackageManager.PERMISSION_GRANTED
+//            }
+//
+//            override fun askForPermission(permissionName: String?, requestCode: Int) {
+//                ActivityCompat.requestPermissions(activity, arrayOf(permissionName!!), requestCode)
+//            }
+//
+//            override fun needRequestCameraPermission(): Boolean {
+//                val greatOrEqualM = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
+//                return greatOrEqualM && isPermissionPresentInManifest(activity, Manifest.permission.CAMERA)
+//            }
+//
+//        }
+//    }
+
+//    private fun needRequestCameraPermission(): Boolean {
+//        return if (permissionManager == null) {
+//            false
+//        } else permissionManager!!.needRequestCameraPermission()
+//    }
+
+    fun initialize(licenseId: String, licenseFileName: String) {
+        FaceSDKManager.getInstance().initialize(activity, licenseId, licenseFileName)
+    }
+
+    fun setFaceConfig(livenessTypeList: List<Int>, livenessRandom: Boolean = false, blurnessValue: Float?,
+                      brightnessValue: Float?, cropFaceValue: Int?,
+                      headPitchValue: Int?, headRollValue: Int?,
+                      headYawValue: Int?, minFaceSize: Int?,
+                      notFaceValue: Float?, occlusionValue: Float?,
+                      checkFaceQuality: Boolean?, faceDecodeNumberOfThreads: Int?, livenessRandomCount: Int?) {
+        val config = FaceSDKManager.getInstance().faceConfig
+        // SDK初始化已经设置完默认参数(推荐参数),您也根据实际需求进行数值调整
+        val livenessList = arrayListOf<LivenessTypeEnum>()
+        livenessTypeList.forEach {
+            livenessList.add(LivenessTypeEnum.values()[it])
+        }
+
+        config.setLivenessTypeList(livenessList)
+
+        config.setLivenessRandom(livenessRandom)
+
+        config.setLivenessRandomCount(livenessRandomCount ?: 0)
+
+        if (null == blurnessValue)
+            config.setBlurnessValue(FaceEnvironment.VALUE_BLURNESS)
+        else
+            config.setBlurnessValue(blurnessValue)
+
+        if (null == brightnessValue)
+            config.setBrightnessValue(FaceEnvironment.VALUE_BRIGHTNESS)
+        else
+            config.setBrightnessValue(brightnessValue)
+
+        if (null == cropFaceValue)
+            config.setCropFaceValue(FaceEnvironment.VALUE_CROP_FACE_SIZE)
+        else
+            config.setCropFaceValue(cropFaceValue)
+
+        if (null == headPitchValue)
+            config.setHeadPitchValue(FaceEnvironment.VALUE_HEAD_PITCH)
+        else
+            config.setHeadPitchValue(headPitchValue)
+
+        if (null == headRollValue)
+            config.setHeadRollValue(FaceEnvironment.VALUE_HEAD_ROLL)
+        else
+            config.setHeadRollValue(headRollValue)
+
+        if (null == headYawValue)
+            config.setHeadYawValue(FaceEnvironment.VALUE_HEAD_YAW)
+        else
+            config.setHeadYawValue(headYawValue)
+
+        if (null == minFaceSize)
+            config.setMinFaceSize(FaceEnvironment.VALUE_MIN_FACE_SIZE)
+        else
+            config.setMinFaceSize(minFaceSize)
+
+        if (null == notFaceValue)
+            config.setNotFaceValue(FaceEnvironment.VALUE_NOT_FACE_THRESHOLD)
+        else
+            config.setNotFaceValue(notFaceValue)
+
+        if (null == occlusionValue)
+            config.setOcclusionValue(FaceEnvironment.VALUE_OCCLUSION)
+        else
+            config.setOcclusionValue(occlusionValue)
+
+
+        config.setCheckFaceQuality(checkFaceQuality ?: false)
+
+        config.setFaceDecodeNumberOfThreads(faceDecodeNumberOfThreads ?: 2)
+
+        FaceSDKManager.getInstance().faceConfig = config
+    }
+
+    fun startFaceLiveness(messenger: BinaryMessenger) {
+
+        EventChannel(messenger, "com.chinahrt.app.pharmacist/event").setStreamHandler(object : EventChannel.StreamHandler {
+            override fun onListen(o: Any?, sink: EventChannel.EventSink?) {
+                // 把eventSink存起来
+                eventSink.setDelegate(sink)
+            }
+
+            override fun onCancel(o: Any?) {
+                eventSink.setDelegate(null)
+            }
+        })
+
+        val intent = Intent(activity, BdFaceLivenessActivity::class.java)
+        activity.startActivityForResult(intent, FACE_LIVENESS_REQUEST_CODE)
+    }
+
+    fun start(actionNum: Int) {
+//        permissionManager?.let {
+//            if (needRequestCameraPermission()
+//                    && !it.isPermissionGranted(Manifest.permission.CAMERA)) {
+//                it.askForPermission(
+//                        Manifest.permission.CAMERA, REQUEST_CAMERA_VIDEO_PERMISSION)
+//                return
+//            }
+//        }
+
+
+        setFaceConfig(activity, actionNum)
+
+        val intent = Intent(activity, BdFaceLivenessActivity::class.java)
+        activity.startActivityForResult(intent, FACE_LIVENESS_REQUEST_CODE)
+    }
+
+    var livenessList = arrayListOf<LivenessTypeEnum>()
+    var isLivenessRandom = false
+
+    private fun initLivenessList() {
+        livenessList.clear()
+        livenessList.add(LivenessTypeEnum.Eye)
+        livenessList.add(LivenessTypeEnum.Mouth)
+        livenessList.add(LivenessTypeEnum.HeadUp)
+        livenessList.add(LivenessTypeEnum.HeadDown)
+        livenessList.add(LivenessTypeEnum.HeadLeft)
+        livenessList.add(LivenessTypeEnum.HeadRight)
+        livenessList.add(LivenessTypeEnum.HeadLeftOrRight)
+    }
+
+    /**
+     * 生成n个不同的随机数,且随机数区间为[0,7)
+     * @param n
+     * @return
+     */
+    private fun getRandomActions(n: Int): List<LivenessTypeEnum> {
+        initLivenessList()
+        val list = arrayListOf<LivenessTypeEnum>()
+        val rand = Random()
+        val bool = BooleanArray(7)
+        var num: Int
+        for (i in 0 until n) {
+            do {
+                // 如果产生的数相同继续循环
+                num = rand.nextInt(7)
+            } while (bool[num])
+            bool[num] = true
+            list.add(livenessList[num])
+        }
+        return list
+    }
+
+
+    /*人脸识别初始化参数设置*/
+    private fun setFaceConfig(activity: Activity, actionNum: Int) {
+        FaceSDKManager.getInstance().initialize(activity, "pharmacist-license-face-android", "idl-license.face-android")
+        val config = FaceSDKManager.getInstance().faceConfig
+        // SDK初始化已经设置完默认参数(推荐参数),您也根据实际需求进行数值调整
+        config.setLivenessTypeList(getRandomActions(actionNum))
+        config.setLivenessRandom(isLivenessRandom)
+        config.setBlurnessValue(FaceEnvironment.VALUE_BLURNESS)
+        config.setBrightnessValue(FaceEnvironment.VALUE_BRIGHTNESS)
+        config.setCropFaceValue(FaceEnvironment.VALUE_CROP_FACE_SIZE)
+        config.setHeadPitchValue(FaceEnvironment.VALUE_HEAD_PITCH)
+        config.setHeadRollValue(FaceEnvironment.VALUE_HEAD_ROLL)
+        config.setHeadYawValue(FaceEnvironment.VALUE_HEAD_YAW)
+        config.setMinFaceSize(FaceEnvironment.VALUE_MIN_FACE_SIZE)
+        config.setNotFaceValue(FaceEnvironment.VALUE_NOT_FACE_THRESHOLD)
+        config.setOcclusionValue(FaceEnvironment.VALUE_OCCLUSION)
+        config.setCheckFaceQuality(true)
+        config.setFaceDecodeNumberOfThreads(2)
+
+        FaceSDKManager.getInstance().faceConfig = config
+    }
+
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
+        if (requestCode == FACE_LIVENESS_REQUEST_CODE) {
+            return if (null == data) {
+                eventSink.error("10010", "采集失败", "采集失败")
+                Log.i("===", "采集失败")
+                true
+            } else {
+                val dataStr = data.getStringExtra("data")
+                eventSink.success(dataStr)
+                Log.i("===", dataStr)
+                true
+            }
+        }
+        return true
+
+    }
+
+    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>?, grantResults: IntArray?): Boolean {
+        val permissionGranted = grantResults!!.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED
+
+        when (requestCode) {
+            REQUEST_CAMERA_VIDEO_PERMISSION -> if (permissionGranted) {
+
+            }
+            else -> return false
+        }
+
+        if (!permissionGranted) {
+            when (requestCode) {
+            }
+        }
+
+        return true
+    }
+
+    /** returns true, if permission present in manifest, otherwise false  */
+    private fun isPermissionPresentInManifest(context: Context, permissionName: String): Boolean {
+        return try {
+            val packageManager = context.packageManager
+            val packageInfo = packageManager.getPackageInfo(context.packageName, PackageManager.GET_PERMISSIONS)
+            val requestedPermissions = packageInfo.requestedPermissions
+            listOf(*requestedPermissions).contains(permissionName)
+        } catch (e: PackageManager.NameNotFoundException) {
+            e.printStackTrace()
+            false
+        }
+    }
+
+
+    interface PermissionManager {
+        fun isPermissionGranted(permissionName: String?): Boolean
+        fun askForPermission(permissionName: String?, requestCode: Int)
+        fun needRequestCameraPermission(): Boolean
+    }
+}

+ 113 - 0
android/src/main/kotlin/dev/bughub/plugin/fltbdface/face/FacePlugin.kt

@@ -0,0 +1,113 @@
+package dev.bughub.plugin.fltbdface.face
+
+import android.app.Activity
+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.MethodCall
+import io.flutter.plugin.common.MethodChannel
+
+class FacePlugin:MethodChannel.MethodCallHandler,FlutterPlugin,ActivityAware{
+
+    private val CHANNEL_NAME = "com.chinahrt.app.pharmacist/face"
+
+    private var methodChannel: MethodChannel? = null
+    private var flutterBinding: FlutterPlugin.FlutterPluginBinding? = null
+    private var activityBinding:ActivityPluginBinding? = null
+    private var delegate: FaceDelegate? = null
+    private var activity:Activity? = null
+
+    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
+        if (activity == null) {
+            result.error("no_activity", "image_picker plugin requires a foreground activity.", null)
+            return
+        }
+
+        when (call.method) {
+            "initialize" -> {
+                val licenseId = call.argument<String>("licenseId")?:""
+                val licenseFileName = call.argument<String>("licenseFileName")?:""
+                delegate?.initialize(licenseId,licenseFileName)
+                result.success(null)
+            }
+            "setFaceConfig" -> {
+                val livenessTypeList = call.argument<List<Int>>("livenessTypeList")?: arrayListOf()
+
+                val livenessRandom:Boolean = call.argument("isLivenessRandom")?:false
+
+                val blurnessValue:Float? = call.argument<Double>("blurnessValue")?.toFloat()
+
+                val brightnessValue:Float? = call.argument<Double>("brightnessValue")?.toFloat()
+
+                val cropFaceValue:Int? = call.argument<Int>("cropFaceValue")
+
+                val headPitchValue:Int? = call.argument<Int>("headPitchValue")
+                val headRollValue:Int? =call.argument<Int>("headRollValue")
+                val headYawValue:Int? =call.argument<Int>("headYawValue")
+                val minFaceSize:Int? =call.argument<Int>("minFaceSize")
+                val notFaceValue:Float? =call.argument<Double>("notFaceValue")?.toFloat()
+                val occlusionValue:Float? =call.argument<Double>("occlusionValue")?.toFloat()
+                val checkFaceQuality:Boolean? =call.argument<Boolean>("checkFaceQuality")
+                val faceDecodeNumberOfThreads:Int? =call.argument<Int>("faceDecodeNumberOfThreads")
+                val livenessRandomCount:Int? =call.argument<Int>("livenessRandomCount")
+
+
+                delegate?.setFaceConfig(livenessTypeList,livenessRandom=livenessRandom,blurnessValue=blurnessValue,brightnessValue=brightnessValue,
+                        cropFaceValue=cropFaceValue,headPitchValue=headPitchValue,headRollValue=headRollValue,headYawValue=headYawValue,minFaceSize=minFaceSize,
+                        notFaceValue=notFaceValue,occlusionValue=occlusionValue,checkFaceQuality=checkFaceQuality,faceDecodeNumberOfThreads=faceDecodeNumberOfThreads,livenessRandomCount=livenessRandomCount)
+
+                result.success(null)
+            }
+            "startFaceLiveness" -> {
+                flutterBinding?.binaryMessenger?.let { delegate?.startFaceLiveness(it) }
+                result.success(null)
+            }
+        }
+    }
+
+    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
+        flutterBinding = binding
+    }
+
+    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
+        flutterBinding = null
+    }
+
+    private fun tearDown(){
+
+        delegate?.let {
+            activityBinding?.removeActivityResultListener(it)
+            delegate = null
+            activityBinding = null
+        }
+
+        methodChannel?.setMethodCallHandler(null)
+        methodChannel = null
+    }
+
+    override fun onDetachedFromActivity() {
+        tearDown()
+    }
+
+    override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
+        onAttachedToActivity(binding)
+    }
+
+    override fun onAttachedToActivity(binding: ActivityPluginBinding) {
+
+        activityBinding = binding
+
+        methodChannel = MethodChannel(flutterBinding?.binaryMessenger, CHANNEL_NAME)
+        methodChannel?.setMethodCallHandler(this)
+
+        activity = binding.activity
+        delegate = FaceDelegate(binding.activity)
+        activityBinding?.addActivityResultListener(delegate!!)
+
+        activityBinding?.addRequestPermissionsResultListener(delegate!!)
+    }
+
+    override fun onDetachedFromActivityForConfigChanges() {
+        onDetachedFromActivity()
+    }
+}