This commit is contained in:
zxj
2025-07-29 11:09:59 +08:00
parent df5acf6b61
commit 56e0a85a4c
106 changed files with 10986 additions and 92 deletions
@@ -0,0 +1,11 @@
package com.sw.dualscreen
import android.app.Application
import timber.log.Timber
class App : Application() {
override fun onCreate() {
super.onCreate()
Timber.plant(Timber.DebugTree())
}
}
@@ -1,25 +0,0 @@
package com.sw.dualscreen
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.sw.dualscreen.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(R.layout.activity_main)
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
insets
}
}
}
@@ -0,0 +1,156 @@
package com.sw.dualscreen.activity
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.SurfaceTexture
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CaptureRequest
import android.os.Bundle
import android.util.Log
import android.view.Surface
import android.view.TextureView
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import com.sw.dualscreen.R
class Camera2Activity : AppCompatActivity() {
private lateinit var cameraManager: CameraManager
private var cameraIdList: Array<String> = arrayOf()
private var currentCameraIdIndex = 0
private lateinit var textureView: TextureView
private var cameraDevice: CameraDevice? = null
private var cameraCaptureSession: CameraCaptureSession? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera2)
textureView = findViewById(R.id.textureView)
cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
// 获取摄像头列表
cameraIdList = cameraManager.cameraIdList
if (cameraIdList.isEmpty()) {
Toast.makeText(this, "没有可用的摄像头", Toast.LENGTH_SHORT).show()
finish()
}
// 设置TextureView监听
textureView.surfaceTextureListener = surfaceTextureListener
// 切换摄像头按钮
findViewById<Button>(R.id.switchButton).setOnClickListener {
switchCamera()
}
}
private val surfaceTextureListener = object : TextureView.SurfaceTextureListener {
override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
openCamera(cameraIdList[currentCameraIdIndex])
}
override fun onSurfaceTextureSizeChanged(
surface: SurfaceTexture,
width: Int,
height: Int
) {
}
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean = true
override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {}
}
private fun openCamera(cameraId: String) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) !=
PackageManager.PERMISSION_GRANTED
) {
return
}
try {
cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() {
override fun onOpened(device: CameraDevice) {
cameraDevice = device
createCameraPreviewSession()
}
override fun onDisconnected(device: CameraDevice) {
device.close()
cameraDevice = null
}
override fun onError(device: CameraDevice, error: Int) {
device.close()
cameraDevice = null
}
}, null)
} catch (e: Exception) {
Log.e("Camera2", "打开摄像头失败", e)
}
}
private fun createCameraPreviewSession() {
val surfaceTexture = textureView.surfaceTexture
surfaceTexture?.setDefaultBufferSize(textureView.width, textureView.height)
val surface = Surface(surfaceTexture)
try {
val previewRequestBuilder = cameraDevice?.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW
)?.apply {
addTarget(surface)
}
cameraDevice?.createCaptureSession(
listOf(surface),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
cameraCaptureSession = session
previewRequestBuilder?.let { builder ->
builder.set(
CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
)
session.setRepeatingRequest(builder.build(), null, null)
}
}
override fun onConfigureFailed(session: CameraCaptureSession) {
Toast.makeText(this@Camera2Activity, "配置失败", Toast.LENGTH_SHORT).show()
}
},
null
)
} catch (e: Exception) {
Log.e("Camera2", "创建预览会话失败", e)
}
}
private fun switchCamera() {
if (cameraIdList.size <= 1) {
Toast.makeText(this, "只有一个摄像头", Toast.LENGTH_SHORT).show()
return
}
// 关闭当前摄像头
cameraCaptureSession?.close()
cameraCaptureSession = null
cameraDevice?.close()
cameraDevice = null
// 切换到下一个摄像头
currentCameraIdIndex = (currentCameraIdIndex + 1) % cameraIdList.size
openCamera(cameraIdList[currentCameraIdIndex])
}
override fun onDestroy() {
super.onDestroy()
cameraCaptureSession?.close()
cameraDevice?.close()
}
}
@@ -0,0 +1,100 @@
package com.sw.dualscreen.activity
import android.content.Context
import android.graphics.Outline
import android.hardware.display.DisplayManager
import android.os.Bundle
import android.view.View
import android.view.ViewOutlineProvider
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import com.google.common.util.concurrent.ListenableFuture
import com.sw.dualscreen.databinding.ActivityMainBinding
import com.sw.dualscreen.ext.dp
import com.sw.dualscreen.presentation.SecondaryScreenPresentation
import com.sw.dualscreen.viewmodel.MyViewModel
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
import timber.log.Timber
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
private val viewModel = MyViewModel()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.main)
initView()
startCamera()
setupSecondaryDisplay()
}
private fun initView() {
binding.previewView.outlineProvider = object : ViewOutlineProvider() {
override fun getOutline(view: View, outline: Outline) {
// outline.setRoundRect(0, 0, view.width, view.height, 12f.dp)
outline.setRoundRect(0, 0, view.width, view.height, 280f.dp)
}
}
binding.previewView.clipToOutline = true
}
private fun startCamera() {
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
// 创建预览用例
val preview = Preview.Builder()
.build()
.also {
it.setSurfaceProvider(binding.previewView.surfaceProvider)
}
// 选择后置摄像头
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
// 解绑所有用例
cameraProvider.unbindAll()
// 绑定到生命周期
cameraProvider.bindToLifecycle(
this, cameraSelector, preview
)
} catch (exc: Exception) {
Timber.e(exc, "Use case binding failed")
}
}, ContextCompat.getMainExecutor(this))
}
private fun setupSecondaryDisplay() {
val displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
val displays = displayManager.displays
// 查找副屏(通常索引为1
if (displays.size > 1) {
val secondaryDisplay = displays[1]
// 创建副屏的Presentation
val presentation =
SecondaryScreenPresentation(
activity = this,
this,
display = secondaryDisplay,
viewModel = viewModel,
recognizeViewModel = viewModels<RecognizeViewModel>().value
)
presentation.show()
}
}
}
@@ -0,0 +1,9 @@
package com.sw.dualscreen.adapter
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
abstract class BaseViewHolder<B : ViewBinding, T>(val binding: B) :
RecyclerView.ViewHolder(binding.root) {
abstract fun bind(item: T)
}
@@ -0,0 +1,31 @@
package com.sw.dualscreen.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
class GridRecyclerAdapter<T, B : ViewBinding>(
private val itemList: List<T>,
private val bindingInflater: (LayoutInflater, ViewGroup, Boolean) -> B,
private val bindHolder: (B, T) -> Unit,
private val onItemClick: ((T) -> Unit)? = null
) : RecyclerView.Adapter<BaseViewHolder<B, T>>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<B, T> {
val inflater = LayoutInflater.from(parent.context)
val binding = bindingInflater(inflater, parent, false)
return object : BaseViewHolder<B, T>(binding) {
override fun bind(item: T) = bindHolder(binding, item)
}
}
override fun onBindViewHolder(holder: BaseViewHolder<B, T>, position: Int) {
holder.bind(itemList[position])
holder.itemView.setOnClickListener {
onItemClick?.invoke(itemList[position])
}
}
override fun getItemCount(): Int = itemList.size
}
@@ -0,0 +1,36 @@
package com.sw.dualscreen.adapter
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
/**
* item间距
*/
class GridSpacingItemDecoration(
private val spanCount: Int,
private val spacing: Int,
private val includeEdge: Boolean
) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val position = parent.getChildAdapterPosition(view)
val column = position % spanCount
if (includeEdge) {
outRect.left = spacing - column * spacing / spanCount
outRect.right = (column + 1) * spacing / spanCount
if (position < spanCount) outRect.top = spacing
outRect.bottom = spacing
} else {
outRect.left = column * spacing / spanCount
outRect.right = spacing - (column + 1) * spacing / spanCount
if (position >= spanCount) outRect.top = spacing
}
}
}
@@ -0,0 +1,49 @@
package com.sw.dualscreen.ext
import android.content.res.Resources
import android.util.TypedValue
import androidx.annotation.Dimension
/**
* 将 Int 值转换为 dp 值
*/
val Int.dp: Int
@Dimension(unit = Dimension.DP)
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this.toFloat(),
Resources.getSystem().displayMetrics
).toInt()
/**
* 将 Float 值转换为 dp 值
*/
val Float.dp: Float
@Dimension(unit = Dimension.DP)
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this,
Resources.getSystem().displayMetrics
)
/**
* 将 Int 值转换为 sp 值
*/
val Int.sp: Float
@Dimension(unit = Dimension.SP)
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
this.toFloat(),
Resources.getSystem().displayMetrics
)
/**
* 将 Float 值转换为 sp 值
*/
val Float.sp: Float
@Dimension(unit = Dimension.SP)
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
this,
Resources.getSystem().displayMetrics
)
@@ -0,0 +1,30 @@
package com.sw.dualscreen.ext
import android.graphics.drawable.GradientDrawable
import android.view.View
import androidx.annotation.ColorInt
import androidx.core.graphics.toColorInt
/**
* 设置圆角边框
*/
fun View.setRoundedBorder(
@ColorInt solidColor: Int = "#19ECE7D0".toColorInt(), // 填充颜色
@ColorInt strokeColor: Int = "#ECE7D0".toColorInt(), // 边框颜色
strokeWidthDp: Float = 2f, // 边框宽度
cornerRadiusDp: Float = 24f, // 边框圆角
) {
background = GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
setColor(solidColor)
setStroke(
dpToPx(strokeWidthDp).toInt(),
strokeColor
)
this.cornerRadius = dpToPx(cornerRadiusDp)
}
}
private fun View.dpToPx(dp: Float): Float {
return dp * context.resources.displayMetrics.density
}
@@ -0,0 +1,434 @@
package com.sw.dualscreen.presentation
import android.Manifest
import android.app.Presentation
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Point
import android.hardware.Camera
import android.hardware.camera2.CameraCaptureSession
import android.hardware.camera2.CameraDevice
import android.hardware.camera2.CameraManager
import android.hardware.camera2.CaptureRequest
import android.os.Build
import android.os.Bundle
import android.util.DisplayMetrics
import android.util.Log
import android.view.Display
import android.view.Surface
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
import com.arcsoft.face.ErrorInfo
import com.sw.dualscreen.R
import com.sw.dualscreen.activity.MainActivity
import com.sw.dualscreen.databinding.PresentationSecondaryScreenBinding
import com.sw.dualscreen.viewmodel.MyViewModel
import com.sw.plate.utils.ToastUtils
import com.sw.plate.utils.arcface.ConfigUtil
import com.sw.plate.utils.arcface.ErrorCodeUtil
import com.sw.plate.utils.arcface.FaceRectTransformer
import com.sw.plate.utils.arcface.FaceRectView
import com.sw.plate.utils.arcface.FaceRectView.DrawInfo
import com.sw.plate.utils.arcface.PreviewConfig
import com.sw.plate.utils.arcface.camera.CameraListener
import com.sw.plate.utils.arcface.camera.DualCameraHelper
import com.sw.plate.utils.arcface.face.constants.LivenessType
import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
import timber.log.Timber
/**
* 用户屏幕
*/
class SecondaryScreenPresentation(
val activity: MainActivity,
context: Context,
display: Display,
viewModel: MyViewModel,
private val onDismissListener: () -> Unit = {},
val recognizeViewModel: RecognizeViewModel
) : Presentation(context, display), ViewTreeObserver.OnGlobalLayoutListener {
private lateinit var binding: PresentationSecondaryScreenBinding
private var cameraDevice: CameraDevice? = null
private var cameraCaptureSession: CameraCaptureSession? = null
private lateinit var cameraManager: CameraManager
private var cameraIdList: Array<String> = arrayOf()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = PresentationSecondaryScreenBinding.inflate(layoutInflater)
setContentView(binding.root)
window?.setBackgroundDrawableResource(android.R.color.transparent)
cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
// 获取摄像头列表
cameraIdList = cameraManager.cameraIdList
if (cameraIdList.isEmpty()) {
Toast.makeText(context, "没有可用的摄像头", Toast.LENGTH_SHORT).show()
return
}
initArcViewModel()
initArcView()
openRectInfoDraw = true
openCamera(cameraIdList.last())
}
override fun onDisplayRemoved() {
super.onDisplayRemoved()
onDismissListener()
}
private fun openCamera(cameraId: String) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) !=
PackageManager.PERMISSION_GRANTED
) {
return
}
try {
cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() {
override fun onOpened(device: CameraDevice) {
cameraDevice = device
// createCameraPreviewSession()
recognizeViewModel.init()
initRgbCamera()
resumeCamera()
}
override fun onDisconnected(device: CameraDevice) {
device.close()
cameraDevice = null
}
override fun onError(device: CameraDevice, error: Int) {
device.close()
cameraDevice = null
}
}, null)
} catch (e: Exception) {
Log.e("Camera2", "打开摄像头失败", e)
}
}
private fun createCameraPreviewSession() {
val textureView = binding.dualCameraTexturePreviewRgb
val surfaceTexture = textureView.surfaceTexture
surfaceTexture?.setDefaultBufferSize(textureView.width, textureView.height)
val surface = Surface(surfaceTexture)
try {
val previewRequestBuilder = cameraDevice?.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW
)?.apply {
addTarget(surface)
}
cameraDevice?.createCaptureSession(
listOf(surface),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
cameraCaptureSession = session
previewRequestBuilder?.let { builder ->
builder.set(
CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE
)
session.setRepeatingRequest(builder.build(), null, null)
}
}
override fun onConfigureFailed(session: CameraCaptureSession) {
Toast.makeText(context, "配置失败", Toast.LENGTH_SHORT).show()
}
},
null
)
} catch (e: Exception) {
Log.e("Camera2", "创建预览会话失败", e)
}
}
// 虹软人脸配置 ⬇
private var isRecognition = false
private var rgbCameraHelper: DualCameraHelper? = null
private var rgbFaceRectTransformer: FaceRectTransformer? = null
private val livenessType = LivenessType.RGB
private var openRectInfoDraw = false
private fun initArcViewModel() {
recognizeViewModel.setLiveType(livenessType)
recognizeViewModel.ftInitCode.observe(activity, Observer { ftInitCode: Int? ->
if (ftInitCode != ErrorInfo.MOK) {
val error: String? = context.getString(
R.string.specific_engine_init_failed, "ftEngine",
ftInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(ftInitCode!!)
)
Timber.e("ftInitCode observe = $error")
ToastUtils.showToast(error)
}
})
recognizeViewModel.frInitCode.observe(activity, Observer { frInitCode: Int? ->
if (frInitCode != ErrorInfo.MOK) {
val error: String? = context.getString(
R.string.specific_engine_init_failed, "frEngine",
frInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(frInitCode!!)
)
Timber.e("frInitCode observe = $error")
ToastUtils.showToast(error)
}
})
recognizeViewModel.flInitCode.observe(activity, Observer { flInitCode: Int? ->
if (flInitCode != ErrorInfo.MOK) {
val error: String? = context.getString(
R.string.specific_engine_init_failed, "flEngine",
flInitCode, ErrorCodeUtil.arcFaceErrorCodeToFieldName(flInitCode!!)
)
Timber.e("flInitCode observe = $error")
ToastUtils.showToast(error)
}
})
recognizeViewModel.recognizeConfiguration
.observe(activity, Observer { recognizeConfiguration: RecognizeConfiguration? ->
Timber.i("recognizeConfiguration: observe = ${recognizeConfiguration.toString()}")
})
recognizeViewModel.recognizeNotice.observe(activity, Observer { notice: String? ->
Timber.i("recognizeNotice observe notice = $notice")
})
recognizeViewModel.recognizeUserId.observe(activity, Observer { userId: String? ->
Timber.i("recognizeUserId observe userId = $userId")
// viewModel.getUserInfoById(memberId = userId?.toInt() ?: 0)
})
recognizeViewModel.drawRectInfoText.observe(activity, Observer { info ->
Timber.i("drawRectInfoText observe info = $info")
})
}
private fun initArcView() {
//在布局结束后才做初始化操作
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().addOnGlobalLayoutListener(this)
recognizeViewModel.getCompareResultList().getValue()
}
/**
* 调整View的宽高,使预览显示正常且采集框固定为380x380
*
* @param rgbPreview RGB预览View
* @param previewView 显示预览数据的view
* @param faceRectView 画框的view (380x380)
* @param previewSize 预览大小
* @param displayOrientation 相机旋转角度
* @param scale 缩放比例
* @return 调整后的LayoutParams
*/
private fun adjustPreviewViewSize(
rgbPreview: View,
previewView: View,
faceRectView: FaceRectView,
previewSize: Camera.Size,
displayOrientation: Int,
scale: Float
): ViewGroup.LayoutParams {
val layoutParams = previewView.layoutParams
val measuredWidth = previewView.measuredWidth
val measuredHeight = previewView.measuredHeight
var ratio = (previewSize.height.toFloat()) / previewSize.width.toFloat()
if (ratio > 1) {
ratio = 1 / ratio
}
if (displayOrientation % 180 == 0) {
layoutParams.width = measuredWidth
layoutParams.height = (measuredWidth * ratio).toInt()
} else {
layoutParams.height = measuredHeight
layoutParams.width = (measuredHeight * ratio).toInt()
}
if (scale < 1f) {
val rgbParam = rgbPreview.getLayoutParams()
layoutParams.width = (rgbParam.width * scale).toInt()
layoutParams.height = (rgbParam.height * scale).toInt()
} else {
layoutParams.width = (layoutParams.width * scale).toInt()
layoutParams.height = (layoutParams.height * scale).toInt()
}
val metrics = DisplayMetrics()
activity.windowManager.defaultDisplay.getMetrics(metrics)
if (layoutParams.width >= metrics.widthPixels) {
val viewRatio = layoutParams.width / (metrics.widthPixels.toFloat())
layoutParams.width = (layoutParams.width / viewRatio).toInt()
layoutParams.height = (layoutParams.height / viewRatio).toInt()
}
if (layoutParams.height >= metrics.heightPixels) {
val viewRatio = layoutParams.height / (metrics.heightPixels.toFloat())
layoutParams.width = (layoutParams.width / viewRatio).toInt()
layoutParams.height = (layoutParams.height / viewRatio).toInt()
}
previewView.setLayoutParams(layoutParams)
faceRectView.setLayoutParams(layoutParams)
return layoutParams
}
private fun initRgbCamera() {
val cameraListener: CameraListener = object : CameraListener {
override fun onCameraOpened(
camera: Camera,
cameraId: Int,
displayOrientation: Int,
isMirror: Boolean
) {
activity.runOnUiThread({
val previewSizeRgb = camera.getParameters().getPreviewSize()
val layoutParams = adjustPreviewViewSize(
binding.dualCameraTexturePreviewRgb,
binding.dualCameraTexturePreviewRgb, binding.dualCameraFaceRectView,
previewSizeRgb, displayOrientation, 1f
)
Timber.d("initRgbCamera previewSizeRgb = ${previewSizeRgb.width}-${previewSizeRgb.height}")
Timber.d("initRgbCamera layoutParams = ${layoutParams.width}-${layoutParams.height}")
rgbFaceRectTransformer = FaceRectTransformer(
previewSizeRgb.width,
previewSizeRgb.height,
layoutParams.width,
layoutParams.height,
displayOrientation,
cameraId,
isMirror,
ConfigUtil.isDrawRgbRectHorizontalMirror(context),
ConfigUtil.isDrawRgbRectVerticalMirror(context)
)
recognizeViewModel.onRgbCameraOpened(camera)
recognizeViewModel.setRgbFaceRectTransformer(rgbFaceRectTransformer)
})
}
@RequiresApi(api = Build.VERSION_CODES.Q)
override fun onPreview(nv21: ByteArray?, camera: Camera?) {
if (!isRecognition) {
return
}
binding.dualCameraFaceRectView.clearFaceInfo()
val facePreviewInfoList: MutableList<FacePreviewInfo?>? =
recognizeViewModel.onPreviewFrame(nv21, true)
if (facePreviewInfoList != null && rgbFaceRectTransformer != null) {
drawPreviewInfo(facePreviewInfoList)
}
recognizeViewModel.clearLeftFace(facePreviewInfoList)
}
override fun onCameraClosed() {
Timber.i("onCameraClosed: ")
}
override fun onCameraError(e: java.lang.Exception) {
Timber.i("onCameraError: %s", e.message)
e.printStackTrace()
}
override fun onCameraConfigurationChanged(cameraID: Int, displayOrientation: Int) {
Timber.i("onCameraConfigurationChanged: threadName = ${Thread.currentThread().name}")
if (rgbFaceRectTransformer != null) {
rgbFaceRectTransformer!!.cameraDisplayOrientation = displayOrientation
}
Timber.i("onCameraConfigurationChanged: $cameraID $displayOrientation")
}
}
val previewConfig: PreviewConfig = recognizeViewModel.previewConfig
rgbCameraHelper = DualCameraHelper.Builder()
.previewViewSize(
Point(
binding.dualCameraTexturePreviewRgb.measuredWidth,
binding.dualCameraTexturePreviewRgb.measuredHeight
)
)
.rotation(activity.windowManager.defaultDisplay.rotation)
.additionalRotation(0) // 角度
.previewSize(recognizeViewModel.loadPreviewSize())
.specificCameraId(previewConfig.rgbCameraId)
.isMirror(true)
.previewOn(binding.dualCameraTexturePreviewRgb)
.cameraListener(cameraListener)
.build()
rgbCameraHelper!!.init()
rgbCameraHelper!!.start()
}
/**
* 绘制RGB、IR画面的实时人脸信息
*
* @param facePreviewInfoList RGB画面的实时人脸信息
*/
private fun drawPreviewInfo(facePreviewInfoList: MutableList<FacePreviewInfo?>) {
if (rgbFaceRectTransformer != null) {
val rgbDrawInfoList: MutableList<DrawInfo?>? = recognizeViewModel.getDrawInfo(
facePreviewInfoList,
LivenessType.RGB,
openRectInfoDraw
)
// 识别成功
binding.dualCameraFaceRectView.drawRealtimeFaceInfo(rgbDrawInfoList)
}
}
fun openRectInfoDraw(view: View?) {
openRectInfoDraw = !openRectInfoDraw
recognizeViewModel.setDrawRectInfoTextValue(openRectInfoDraw)
}
override fun onGlobalLayout() {
Timber.d("onGlobalLayout")
binding.dualCameraTexturePreviewRgb.getViewTreeObserver().removeOnGlobalLayoutListener(this)
}
// override fun onResume() {
// super.onResume()
// resumeCamera()
// viewModel.resetUserInfo()
// }
//
// protected override fun onPause() {
// pauseCamera()
// super.onPause()
// }
override fun onStop() {
if (rgbCameraHelper != null) {
rgbCameraHelper!!.release()
rgbCameraHelper = null
}
recognizeViewModel.destroy()
super.onStop()
}
fun resumeCamera() {
isRecognition = true
if (rgbCameraHelper != null && rgbCameraHelper!!.isStopped) {
rgbCameraHelper!!.start()
}
}
fun pauseCamera() {
isRecognition = false
recognizeViewModel.onPreviewFrame(ByteArray(1382400), true)
}
}
@@ -0,0 +1,8 @@
package com.sw.dualscreen.viewmodel
import androidx.lifecycle.ViewModel
class MyViewModel : ViewModel() {
}