拉取全量和增量人脸数据,增加已采集数据提示
This commit is contained in:
+11
-1
@@ -68,5 +68,15 @@ dependencies {
|
|||||||
implementation(libs.accompanist.permissions)
|
implementation(libs.accompanist.permissions)
|
||||||
implementation(project(":lib_face"))
|
implementation(project(":lib_face"))
|
||||||
|
|
||||||
implementation("com.github.Dimezis:BlurView:version-3.2.0")
|
// implementation("com.github.Dimezis:BlurView:version-3.2.0")
|
||||||
|
|
||||||
|
|
||||||
|
// retrofit网络请求
|
||||||
|
implementation(libs.retrofit)
|
||||||
|
implementation(libs.converter.gson)
|
||||||
|
// okhttp
|
||||||
|
implementation(libs.okhttp)
|
||||||
|
implementation(libs.logging.interceptor)
|
||||||
|
// gson
|
||||||
|
implementation(libs.gson)
|
||||||
}
|
}
|
||||||
@@ -22,15 +22,18 @@ import com.sw.face.collect.ext.dp
|
|||||||
import com.sw.face.collect.ext.gone
|
import com.sw.face.collect.ext.gone
|
||||||
import com.sw.face.collect.ext.toast
|
import com.sw.face.collect.ext.toast
|
||||||
import com.sw.face.collect.ext.visible
|
import com.sw.face.collect.ext.visible
|
||||||
|
import com.sw.face.collect.model.UserFaceModel
|
||||||
import com.sw.face.collect.socket.LanServer
|
import com.sw.face.collect.socket.LanServer
|
||||||
import com.sw.face.collect.socket.TcpClient
|
import com.sw.face.collect.socket.TcpClient
|
||||||
import com.sw.face.collect.utils.Base64
|
import com.sw.face.collect.utils.Base64
|
||||||
import com.sw.face.collect.utils.BitmapUtils
|
import com.sw.face.collect.utils.BitmapUtils
|
||||||
import com.sw.face.collect.utils.FaceEngineUtils
|
import com.sw.face.collect.utils.FaceEngineUtils
|
||||||
import com.sw.face.collect.utils.IntervalExecutor
|
import com.sw.face.collect.utils.IntervalExecutor
|
||||||
|
import com.sw.face.collect.utils.SpTool
|
||||||
import com.sw.face.collect.utils.countDownByFlow
|
import com.sw.face.collect.utils.countDownByFlow
|
||||||
import com.sw.face.collect.view.LanServerListenerImpl
|
import com.sw.face.collect.view.LanServerListenerImpl
|
||||||
import com.sw.face.collect.view.TcpClientListenerImpl
|
import com.sw.face.collect.view.TcpClientListenerImpl
|
||||||
|
import com.sw.face.collect.viewmodel.MainViewModel
|
||||||
import com.sw.plate.utils.L
|
import com.sw.plate.utils.L
|
||||||
import com.sw.plate.utils.NV21ToBitmap
|
import com.sw.plate.utils.NV21ToBitmap
|
||||||
import com.sw.plate.utils.ToastUtils
|
import com.sw.plate.utils.ToastUtils
|
||||||
@@ -46,12 +49,15 @@ import com.sw.plate.utils.arcface.face.constants.LivenessType
|
|||||||
import com.sw.plate.utils.arcface.face.model.CompareResult
|
import com.sw.plate.utils.arcface.face.model.CompareResult
|
||||||
import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
|
import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
|
||||||
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
|
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
|
||||||
|
import com.sw.plate.utils.arcface.facedb.FaceDatabase
|
||||||
|
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
|
||||||
import com.sw.plate.utils.arcface.model.UserFaceInfo
|
import com.sw.plate.utils.arcface.model.UserFaceInfo
|
||||||
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
|
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
|
||||||
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel.REGISTER_STATUS_READY
|
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel.REGISTER_STATUS_READY
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
import kotlin.getValue
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
class MainActivity : BaseActivity<ActivityMainBinding>(), ViewTreeObserver.OnGlobalLayoutListener {
|
class MainActivity : BaseActivity<ActivityMainBinding>(), ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
@@ -79,6 +85,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), ViewTreeObserver.OnGlo
|
|||||||
|
|
||||||
private val recognizeViewModel: RecognizeViewModel by lazy { viewModels<RecognizeViewModel>().value }
|
private val recognizeViewModel: RecognizeViewModel by lazy { viewModels<RecognizeViewModel>().value }
|
||||||
|
|
||||||
|
private val mainViewModel by viewModels<MainViewModel>()
|
||||||
|
|
||||||
|
private var currentUserId: String? = null
|
||||||
|
|
||||||
override fun inflateViewBinding() = ActivityMainBinding.inflate(layoutInflater)
|
override fun inflateViewBinding() = ActivityMainBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
override fun onGlobalLayout() {
|
override fun onGlobalLayout() {
|
||||||
@@ -87,8 +97,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), ViewTreeObserver.OnGlo
|
|||||||
openCamera()
|
openCamera()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var pageNo = 1
|
||||||
override fun initialize() {
|
override fun initialize() {
|
||||||
super.initialize()
|
super.initialize()
|
||||||
|
mainViewModel.getUserFaceCache(pageNo = pageNo)
|
||||||
FaceEngineUtils.activeEngine()
|
FaceEngineUtils.activeEngine()
|
||||||
setupArcCamera()
|
setupArcCamera()
|
||||||
binding.btnCollectFace.setOnClickListener {
|
binding.btnCollectFace.setOnClickListener {
|
||||||
@@ -110,6 +122,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), ViewTreeObserver.OnGlo
|
|||||||
binding.layoutState.gone()
|
binding.layoutState.gone()
|
||||||
|
|
||||||
resumeCamera()
|
resumeCamera()
|
||||||
|
startFaceTask()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -206,6 +219,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), ViewTreeObserver.OnGlo
|
|||||||
|
|
||||||
// private var imageBitmap: Bitmap? = null
|
// private var imageBitmap: Bitmap? = null
|
||||||
private fun collectFace() {
|
private fun collectFace() {
|
||||||
|
currentUserId = null
|
||||||
userFaceInfo = null
|
userFaceInfo = null
|
||||||
recognizeViewModel.updateRegisterStatus(REGISTER_STATUS_READY)
|
recognizeViewModel.updateRegisterStatus(REGISTER_STATUS_READY)
|
||||||
//showWaitingDialog("人脸信息中……")
|
//showWaitingDialog("人脸信息中……")
|
||||||
@@ -234,6 +248,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), ViewTreeObserver.OnGlo
|
|||||||
private fun getFaceData(block: (String) -> Unit) {
|
private fun getFaceData(block: (String) -> Unit) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
job = executor.startIntervalTask(5) {
|
job = executor.startIntervalTask(5) {
|
||||||
|
if (currentUserId != null && currentUserId!!.isNotBlank()) {
|
||||||
|
toast("您已采集过人脸信息")
|
||||||
|
return@startIntervalTask
|
||||||
|
}
|
||||||
if (userFaceInfo == null) {
|
if (userFaceInfo == null) {
|
||||||
return@startIntervalTask
|
return@startIntervalTask
|
||||||
}
|
}
|
||||||
@@ -335,11 +353,11 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), ViewTreeObserver.OnGlo
|
|||||||
TAG,
|
TAG,
|
||||||
"recognizeUserId observe compareResult = ${compareResult.trackId}, userId = ${compareResult.faceEntity.userName}"
|
"recognizeUserId observe compareResult = ${compareResult.trackId}, userId = ${compareResult.faceEntity.userName}"
|
||||||
)
|
)
|
||||||
// val lastFaceTrackId = compareResult.trackId
|
val lastFaceTrackId = compareResult.trackId
|
||||||
// val faceEntity = compareResult.faceEntity
|
val faceEntity = compareResult.faceEntity
|
||||||
// val currentUserId = faceEntity.userName
|
currentUserId = faceEntity.userName
|
||||||
// if (!currentUserId.isNullOrBlank()) {
|
// if (!currentUserId.isNullOrBlank()) {
|
||||||
// toast("当前用户已采集过人脸信息")
|
//// toast("您已采集过人脸信息")
|
||||||
// return@Observer
|
// return@Observer
|
||||||
// }
|
// }
|
||||||
// //未识别到,拍摄照片
|
// //未识别到,拍摄照片
|
||||||
@@ -711,6 +729,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), ViewTreeObserver.OnGlo
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
countDownJob?.cancel()
|
countDownJob?.cancel()
|
||||||
|
faceTaskJob?.cancel()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -727,7 +746,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), ViewTreeObserver.OnGlo
|
|||||||
},
|
},
|
||||||
onFinish = {
|
onFinish = {
|
||||||
//binding.tvCollectState.text = "采集完成"
|
//binding.tvCollectState.text = "采集完成"
|
||||||
|
currentUserId = null
|
||||||
binding.layoutState.gone()
|
binding.layoutState.gone()
|
||||||
binding.tvFaceTip.visible()
|
binding.tvFaceTip.visible()
|
||||||
binding.btnCollectFace.visible()
|
binding.btnCollectFace.visible()
|
||||||
@@ -735,4 +754,72 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), ViewTreeObserver.OnGlo
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val intervalExecutor by lazy { IntervalExecutor() }
|
||||||
|
private var faceTaskJob: Job? = null
|
||||||
|
|
||||||
|
// private val initialDelay = 5 * 60 * 1000L
|
||||||
|
// private val dealyMillis = 10 * 60 * 1000L
|
||||||
|
private val initialDelay = 60 * 1000L
|
||||||
|
private val dealyMillis = 30 * 1000L
|
||||||
|
private var taskPageNo = 1
|
||||||
|
fun startFaceTask() {
|
||||||
|
faceTaskJob =
|
||||||
|
intervalExecutor.startIntervalTaskWithInitialDelay(initialDelay, dealyMillis) {
|
||||||
|
val timestamp = SpTool.getLastFaceTimestamp()
|
||||||
|
if (timestamp == 0L) {
|
||||||
|
return@startIntervalTaskWithInitialDelay
|
||||||
|
}
|
||||||
|
mainViewModel.getFaceIncrementList(
|
||||||
|
pageNo = taskPageNo,
|
||||||
|
timestamp = timestamp,
|
||||||
|
onAllQueryFinished = {
|
||||||
|
taskPageNo = 1
|
||||||
|
SpTool.setLastFaceTimestamp(System.currentTimeMillis())
|
||||||
|
},
|
||||||
|
onPageQueryFinished = { list ->
|
||||||
|
runOnUiThread {
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
return@runOnUiThread
|
||||||
|
}
|
||||||
|
updateFaceData(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateFaceData(list: List<UserFaceModel>) {
|
||||||
|
Thread {
|
||||||
|
val faceList = mutableListOf<FaceEntity>()
|
||||||
|
try {
|
||||||
|
list.forEach { model ->
|
||||||
|
if (model.faceDeleted == true) {
|
||||||
|
//删除数据
|
||||||
|
FaceDatabase.getInstance(this).faceDao().deleteFaceById(model.userId)
|
||||||
|
} else {
|
||||||
|
//保存数据
|
||||||
|
val faceEntity = FaceEntity(
|
||||||
|
model.userId,
|
||||||
|
null,
|
||||||
|
Base64.decode(model.faceFeatureStr)
|
||||||
|
).also {
|
||||||
|
it.userType = "1"
|
||||||
|
}
|
||||||
|
faceList.add(faceEntity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (faceList.isNotEmpty()) {
|
||||||
|
FaceDatabase.getInstance(this).faceDao().insert(faceList)
|
||||||
|
}
|
||||||
|
recognizeViewModel.refreshFaceList();
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ class MyApp: App() {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "MyApp"
|
private const val TAG = "MyApp"
|
||||||
|
var DEBUG = true
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
var instance: Context? = null
|
var instance: Context? = null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.sw.face.collect.model
|
||||||
|
|
||||||
|
data class ApiResponse<T>(
|
||||||
|
val code: String,
|
||||||
|
val msg: String? = "",
|
||||||
|
val data: T? = null,
|
||||||
|
val result: T? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
package com.sw.face.collect.model
|
||||||
|
|
||||||
|
data class RespCodeMsg(
|
||||||
|
val code: String?,
|
||||||
|
val msg: String?
|
||||||
|
)
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.sw.face.collect.model
|
||||||
|
|
||||||
|
//import android.os.Parcelable
|
||||||
|
//import com.google.gson.annotations.SerializedName
|
||||||
|
//import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户人脸信息
|
||||||
|
*/
|
||||||
|
//@Parcelize
|
||||||
|
data class UserFaceModel(
|
||||||
|
// @SerializedName("faceFeature")
|
||||||
|
// val faceFeature: String? = "",
|
||||||
|
// @SerializedName("faceFeatureString")
|
||||||
|
// val faceFeatureString: String? = "",
|
||||||
|
// @SerializedName("faceType")
|
||||||
|
// val faceType: String? = "",
|
||||||
|
// @SerializedName("userFaceId")
|
||||||
|
// val userFaceId: String? = "",
|
||||||
|
// @SerializedName("userId")
|
||||||
|
val userId: String? = "",
|
||||||
|
val faceFeatureStr: String? = "",
|
||||||
|
|
||||||
|
val faceDeleted: Boolean? = false
|
||||||
|
)
|
||||||
|
// : Parcelable
|
||||||
|
|
||||||
|
//@Parcelize
|
||||||
|
data class UserFaceModel2(
|
||||||
|
val userId: String? = "",
|
||||||
|
val faceFeatureString: String? = "",
|
||||||
|
val face: String? = ""
|
||||||
|
)
|
||||||
|
// : Parcelable
|
||||||
|
|
||||||
|
data class FaceData(
|
||||||
|
val nextPageIndex: Int,
|
||||||
|
val total: Int,
|
||||||
|
val size: Int,
|
||||||
|
val current: Int,
|
||||||
|
val pages: Int,
|
||||||
|
val records: List<UserFaceModel2>? = null
|
||||||
|
)
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.sw.dualscreen.network
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.sw.face.collect.network.api.ApiService
|
||||||
|
import com.sw.dualscreen.network.interceptor.RequestInterceptor
|
||||||
|
import com.sw.face.collect.MyApp
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
|
import retrofit2.Retrofit
|
||||||
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
object ApiClient {
|
||||||
|
private const val BASE_URL = "http://device.shuziweidao.com:8889/"
|
||||||
|
|
||||||
|
//private const val TIME_OUT = 30L // 超时时间(秒)
|
||||||
|
//todo 临时测试改为5秒
|
||||||
|
private const val TIME_OUT = 5L // 超时时间(秒)
|
||||||
|
|
||||||
|
private val okHttpClient = OkHttpClient.Builder()
|
||||||
|
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||||
|
.readTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||||
|
.writeTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||||
|
.addNetworkInterceptor(HttpLoggingInterceptor(logger = {
|
||||||
|
Log.d("ApiClient","okhttp logger ==>${it}")
|
||||||
|
}).apply {
|
||||||
|
level = if (MyApp.DEBUG) {
|
||||||
|
HttpLoggingInterceptor.Level.BODY
|
||||||
|
} else {
|
||||||
|
HttpLoggingInterceptor.Level.NONE
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.addInterceptor(RequestInterceptor())
|
||||||
|
.build()
|
||||||
|
|
||||||
|
private val retrofit = Retrofit.Builder()
|
||||||
|
.baseUrl(BASE_URL)
|
||||||
|
.client(okHttpClient)
|
||||||
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
// .addCallAdapterFactory(CoroutineCallAdapterFactory()) // 协程适配器
|
||||||
|
.build()
|
||||||
|
|
||||||
|
val apiService: ApiService by lazy {
|
||||||
|
retrofit.create(ApiService::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.sw.face.collect.network.api
|
||||||
|
|
||||||
|
import com.sw.face.collect.base.GlobalData
|
||||||
|
import com.sw.face.collect.model.ApiResponse
|
||||||
|
import com.sw.face.collect.model.UserFaceModel
|
||||||
|
import okhttp3.MultipartBody
|
||||||
|
import okhttp3.RequestBody
|
||||||
|
import retrofit2.http.Body
|
||||||
|
import retrofit2.http.GET
|
||||||
|
import retrofit2.http.Multipart
|
||||||
|
import retrofit2.http.POST
|
||||||
|
import retrofit2.http.Part
|
||||||
|
import retrofit2.http.PartMap
|
||||||
|
import retrofit2.http.Query
|
||||||
|
import retrofit2.http.Url
|
||||||
|
|
||||||
|
interface ApiService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取人脸数据
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
suspend fun getUserFaceCache(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/common/app/faceFeature/list",
|
||||||
|
@Body param: Map<String, Int>
|
||||||
|
): ApiResponse<List<UserFaceModel>?>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取人脸增量数据
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
suspend fun getFaceIncrementList(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/common/app/faceFeature/increment/list",
|
||||||
|
@Body param: Map<String, Long>
|
||||||
|
): ApiResponse<List<UserFaceModel>?>
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package com.sw.dualscreen.network.interceptor
|
||||||
|
|
||||||
|
import android.text.TextUtils
|
||||||
|
import com.sw.dualscreen.GlobalData
|
||||||
|
import com.sw.dualscreen.GlobalKey
|
||||||
|
import com.sw.dualscreen.utils.SPUtil
|
||||||
|
import com.sw.plate.App
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.Request
|
||||||
|
import okhttp3.Response
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求拦截器
|
||||||
|
*/
|
||||||
|
class RequestInterceptor : Interceptor {
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
val originalRequest = chain.request()
|
||||||
|
val requestBuilder = originalRequest.newBuilder()
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
// .header("Authorization", "Bearer ${getToken()}")
|
||||||
|
// .header("X-Access-Token", getToken(originalRequest))
|
||||||
|
.header("X-Access-Token", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjYW50ZWVuSWQiOiJiZTE1NDgzMS0zNDY2LTNiYTItYTJlYS01NzY1MmM5MTlmZWQiLCJ0eXBlIjoiNCIsInVzZXJJZCI6IjAwMDAwMDAwMDAwMDAwMDAwMDEifQ.sN40cOC-O5WQFrF4IDUs8fFlkNdUKLbJt_rHyTsgYYM")
|
||||||
|
// .header("X-DEVICE-CODE", "bcf396ed-78f6-3864-9837-7c37c5b2ec41")
|
||||||
|
.header("X-DEVICE-CODE", GlobalData.deviceId)
|
||||||
|
.header("authorization", "57ee87183f2a4fa59683ec9ef41c8f5d")
|
||||||
|
|
||||||
|
val newRequest = requestBuilder.build()
|
||||||
|
|
||||||
|
return chain.proceed(newRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getToken(originRequest: Request): String {
|
||||||
|
val tokenParam = originRequest.header("X-Access-Token")
|
||||||
|
if (!TextUtils.isEmpty(tokenParam)) return tokenParam!!
|
||||||
|
// 从本地获取token的逻辑
|
||||||
|
val spUtil = SPUtil.getInstance(context = App.getContext())
|
||||||
|
return spUtil.get(GlobalKey.KEY_TOKEN, "") as String
|
||||||
|
// return "eyJhbGciOiJIUzUxMiJ9.eyJpZCI6MTQ2LCJ1c2VyTmFtZSI6IjEzNjgxNDQ4ODU2IiwibmFtZSI6IuW-kOejiiIsInBhc3N3b3JkIjoiOTllOTQ1ZmVjZmZjNWIzNDI4MmUwNDRlODYyMzdjM2UxZjU5OWY5OCIsInNhbHQiOiI0NmEzMzUzYWU4OTA0MDYxYjMzODU5ZWNlYTBlMGE2NyIsInBob25lIjoiMTM2ODE0NDg4NTYiLCJzdGF0dXMiOjEsInVzZXJUeXBlIjoyLCJjcmVhdGVVc2VyTm8iOiIxNDEiLCJjcmVhdGVUaW1lIjoxNjI2OTQzNDE2MDAwLCJ1cGRhdGVVc2VyTm8iOiIxNDEiLCJ1cGRhdGVUaW1lIjoxNjI2OTQzNDE2MDAwLCJpc0RlbCI6ZmFsc2UsImVhSWQiOjk5LCJlYUlkTGlzdCI6Ijk5IiwiaXNTaG9wTWFuYWdlciI6dHJ1ZSwidXNlck5vIjoiMWY5Nzk5ZWMtODlkYi00MWYyLTk1YTEtY2UzNTA3Y2QyMTU2In0.f7wImPgBOYMV0AqRchnXGPkUWZN9dFJ9gLPsaB8uNldd21IfXLjJl8y-FiWVuVUvlwUvGpgqGDFR1JKj5H7amw"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.sw.dualscreen.repository
|
||||||
|
|
||||||
|
import com.google.gson.JsonParseException
|
||||||
|
import com.sw.face.collect.model.ApiResponse
|
||||||
|
import com.sw.face.collect.model.RespCodeMsg
|
||||||
|
import com.sw.face.collect.utils.GsonUtils
|
||||||
|
import retrofit2.HttpException
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.ConnectException
|
||||||
|
import java.net.SocketTimeoutException
|
||||||
|
import javax.net.ssl.SSLHandshakeException
|
||||||
|
|
||||||
|
abstract class BaseRepository {
|
||||||
|
suspend fun <T> safeApiCall(apiCall: suspend () -> ApiResponse<T>): ApiResponse<T> {
|
||||||
|
return try {
|
||||||
|
apiCall()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// Timber.e("safeApiCall Exception: ${e.stackTraceToString()}")
|
||||||
|
|
||||||
|
when (e) {
|
||||||
|
is HttpException -> {
|
||||||
|
val respData = e.response()?.errorBody()?.string()
|
||||||
|
val respCodeMsg = GsonUtils.fromJson(respData, RespCodeMsg::class.java)
|
||||||
|
if (respCodeMsg?.msg.isNullOrBlank()) {
|
||||||
|
ApiResponse(code = "${e.code()}", msg = e.message())
|
||||||
|
} else {
|
||||||
|
ApiResponse(code = respCodeMsg.code ?:"-10", msg = respCodeMsg.msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is SocketTimeoutException -> {
|
||||||
|
ApiResponse(code = "-2", msg = "请求超时: ${e.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
is ConnectException -> {
|
||||||
|
ApiResponse(code = "-3", msg = "连接失败: ${e.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
is SSLHandshakeException -> {
|
||||||
|
ApiResponse(code = "-4", msg = "SSL握手失败: ${e.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
is JsonParseException -> {
|
||||||
|
ApiResponse(code = "-5", msg = "JSON解析错误: ${e.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
is IOException -> {
|
||||||
|
ApiResponse(code = "-6", msg = "网络IO错误: ${e.message}")
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
ApiResponse(code = "-1", msg = "未知错误: ${e.message ?: "无错误信息"}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package com.sw.face.collect.repository
|
||||||
|
|
||||||
|
import com.sw.dualscreen.repository.BaseRepository
|
||||||
|
import com.sw.face.collect.model.ApiResponse
|
||||||
|
import com.sw.face.collect.model.UserFaceModel
|
||||||
|
import com.sw.face.collect.network.api.ApiService
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 远程数据处理
|
||||||
|
*/
|
||||||
|
class RemoteRepository constructor(
|
||||||
|
private val apiService: ApiService
|
||||||
|
) : BaseRepository() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取人脸数据
|
||||||
|
*/
|
||||||
|
suspend fun getUserFaceCache(
|
||||||
|
pageNum: Int,
|
||||||
|
pageSize: Int = 100,
|
||||||
|
): ApiResponse<List<UserFaceModel>?> {
|
||||||
|
return safeApiCall {
|
||||||
|
apiService.getUserFaceCache(
|
||||||
|
param = mapOf(
|
||||||
|
"pageNum" to pageNum,
|
||||||
|
"pageSize" to pageSize
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取人脸数据
|
||||||
|
*/
|
||||||
|
suspend fun getFaceIncrementList(
|
||||||
|
pageNum: Long,
|
||||||
|
pageSize: Long = 100L,
|
||||||
|
timestamp: Long
|
||||||
|
): ApiResponse<List<UserFaceModel>?> {
|
||||||
|
return safeApiCall {
|
||||||
|
apiService.getFaceIncrementList(
|
||||||
|
param = mapOf(
|
||||||
|
"pageNum" to pageNum,
|
||||||
|
"pageSize" to pageSize,
|
||||||
|
"timestamp" to timestamp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package com.sw.face.collect.utils
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
import java.lang.reflect.Type
|
||||||
|
import kotlin.jvm.java
|
||||||
|
import kotlin.text.isNullOrEmpty
|
||||||
|
|
||||||
|
object GsonUtils {
|
||||||
|
|
||||||
|
// 默认的 Gson 实例
|
||||||
|
private val defaultGson: Gson by lazy {
|
||||||
|
GsonBuilder()
|
||||||
|
.setDateFormat("yyyy-MM-dd HH:mm:ss") // 设置日期格式
|
||||||
|
// .disableHtmlEscaping() // 禁止转义HTML标签
|
||||||
|
.create()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取默认配置的 Gson 实例
|
||||||
|
*/
|
||||||
|
fun getGson(): Gson = defaultGson
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将对象转换为 JSON 字符串
|
||||||
|
* @param obj 要转换的对象
|
||||||
|
* @return JSON 字符串
|
||||||
|
*/
|
||||||
|
fun toJson(obj: Any?): String {
|
||||||
|
return if (obj == null) "" else defaultGson.toJson(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 JSON 字符串转换为对象
|
||||||
|
* @param json JSON 字符串
|
||||||
|
* @param clazz 目标类
|
||||||
|
* @return 转换后的对象
|
||||||
|
*/
|
||||||
|
fun <T> fromJson(json: String?, clazz: Class<T>): T? {
|
||||||
|
if (json.isNullOrEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
defaultGson.fromJson(json, clazz)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 JSON 字符串转换为对象 (支持泛型)
|
||||||
|
* @param json JSON 字符串
|
||||||
|
* @param type 类型令牌,用于获取泛型类型
|
||||||
|
* @return 转换后的对象
|
||||||
|
*/
|
||||||
|
fun <T> fromJson(json: String?, type: Type): T? {
|
||||||
|
if (json.isNullOrEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
defaultGson.fromJson(json, type)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 JSON 字符串转换为 List 对象
|
||||||
|
* @param json JSON 字符串
|
||||||
|
* @param clazz List 中的元素类型
|
||||||
|
* @return 转换后的 List 对象
|
||||||
|
*/
|
||||||
|
fun <T> fromJsonList(json: String?, clazz: Class<T>): List<T>? {
|
||||||
|
if (json.isNullOrEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
val type = TypeToken.getParameterized(List::class.java, clazz).type
|
||||||
|
defaultGson.fromJson<List<T>>(json, type)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 JSON 字符串转换为 Map 对象
|
||||||
|
* @param json JSON 字符串
|
||||||
|
* @param keyClazz Map 的 key 类型
|
||||||
|
* @param valueClazz Map 的 value 类型
|
||||||
|
* @return 转换后的 Map 对象
|
||||||
|
*/
|
||||||
|
fun <K, V> fromJsonMap(
|
||||||
|
json: String?,
|
||||||
|
keyClazz: Class<K>,
|
||||||
|
valueClazz: Class<V>
|
||||||
|
): Map<K, V>? {
|
||||||
|
if (json.isNullOrEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return try {
|
||||||
|
val type = TypeToken.getParameterized(Map::class.java, keyClazz, valueClazz).type
|
||||||
|
defaultGson.fromJson<Map<K, V>>(json, type)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将对象转换为另一种类型的对象
|
||||||
|
* @param obj 源对象
|
||||||
|
* @param clazz 目标类型
|
||||||
|
* @return 转换后的对象
|
||||||
|
*/
|
||||||
|
fun <T> convert(obj: Any?, clazz: Class<T>): T? {
|
||||||
|
if (obj == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return fromJson(toJson(obj), clazz)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
package com.sw.dualscreen.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import com.sw.plate.App
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 持久化工具
|
||||||
|
*/
|
||||||
|
class SPUtil private constructor(context: Context, private val spName: String) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@Volatile
|
||||||
|
private var instance: SPUtil? = null
|
||||||
|
|
||||||
|
fun getInstance(
|
||||||
|
context: Context = App.getContext(),
|
||||||
|
spName: String = "default_sp"
|
||||||
|
): SPUtil {
|
||||||
|
return instance ?: synchronized(this) {
|
||||||
|
instance ?: SPUtil(context.applicationContext, spName).also { instance = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val sharedPreferences by lazy {
|
||||||
|
context.getSharedPreferences(spName, Context.MODE_PRIVATE)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基础存储方法
|
||||||
|
fun put(key: String, value: Any?) {
|
||||||
|
when (value) {
|
||||||
|
null -> remove(key) // 存入null视为删除
|
||||||
|
is String -> sharedPreferences.edit { putString(key, value) }
|
||||||
|
is Int -> sharedPreferences.edit { putInt(key, value) }
|
||||||
|
is Long -> sharedPreferences.edit { putLong(key, value) }
|
||||||
|
is Float -> sharedPreferences.edit { putFloat(key, value) }
|
||||||
|
is Boolean -> sharedPreferences.edit { putBoolean(key, value) }
|
||||||
|
is Set<*> -> sharedPreferences.edit { putStringSet(key, value as Set<String>) }
|
||||||
|
else -> throw IllegalArgumentException("Unsupported type: ${value.javaClass.name}")
|
||||||
|
}
|
||||||
|
notifyDataChanged(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <T> get(key: String, defaultValue: T? = null): T? {
|
||||||
|
return when (defaultValue) {
|
||||||
|
is String -> sharedPreferences.getString(key, defaultValue) as T
|
||||||
|
is Int -> sharedPreferences.getInt(key, defaultValue) as T
|
||||||
|
is Long -> sharedPreferences.getLong(key, defaultValue) as T
|
||||||
|
is Float -> sharedPreferences.getFloat(key, defaultValue) as T
|
||||||
|
is Boolean -> sharedPreferences.getBoolean(key, defaultValue) as T
|
||||||
|
is Set<*> -> sharedPreferences.getStringSet(key, defaultValue as Set<String>) as T
|
||||||
|
null -> when {
|
||||||
|
sharedPreferences.contains(key) -> get(key, "") as? T // 尝试作为String获取
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw IllegalArgumentException("Unsupported type: ${defaultValue.javaClass.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(key: String) {
|
||||||
|
if (sharedPreferences.contains(key)) {
|
||||||
|
sharedPreferences.edit { remove(key) }
|
||||||
|
notifyDataChanged(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun clear() {
|
||||||
|
sharedPreferences.edit { clear() }
|
||||||
|
notifyDataChanged(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun contains(key: String): Boolean {
|
||||||
|
return sharedPreferences.contains(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听变化
|
||||||
|
private val dataChangeFlow = MutableStateFlow(0)
|
||||||
|
|
||||||
|
private fun notifyDataChanged(key: String?) {
|
||||||
|
dataChangeFlow.value++
|
||||||
|
}
|
||||||
|
|
||||||
|
fun observeKey(key: String): Flow<Any?> {
|
||||||
|
return dataChangeFlow.map { get(key) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 属性委托支持
|
||||||
|
fun int(key: String, default: Int = 0) = SpProperty(key, default)
|
||||||
|
fun long(key: String, default: Long = 0L) = SpProperty(key, default)
|
||||||
|
fun float(key: String, default: Float = 0f) = SpProperty(key, default)
|
||||||
|
fun boolean(key: String, default: Boolean = false) = SpProperty(key, default)
|
||||||
|
fun string(key: String, default: String = "") = SpProperty(key, default)
|
||||||
|
fun stringSet(key: String, default: Set<String> = emptySet()) = SpProperty(key, default)
|
||||||
|
|
||||||
|
inner class SpProperty<T>(private val key: String, private val defaultValue: T) :
|
||||||
|
ReadWriteProperty<Any?, T> {
|
||||||
|
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||||
|
return get(key, defaultValue) ?: defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||||
|
put(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.sw.face.collect.utils;
|
||||||
|
|
||||||
|
import com.sw.dualscreen.utils.SPUtil;
|
||||||
|
import com.sw.face.collect.MyApp;
|
||||||
|
|
||||||
|
public class SpTool {
|
||||||
|
|
||||||
|
public static final String LAST_FACE_TIMESTAMP = "faceTimestamp";
|
||||||
|
|
||||||
|
public static long getLastFaceTimestamp() {
|
||||||
|
return SPUtil.Companion.getInstance(MyApp.getContext(), "default_sp").get(LAST_FACE_TIMESTAMP, 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setLastFaceTimestamp(long timestamp) {
|
||||||
|
SPUtil.Companion.getInstance(MyApp.getContext(), "default_sp").put(LAST_FACE_TIMESTAMP, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package com.sw.face.collect.viewmodel
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.sw.dualscreen.network.ApiClient
|
||||||
|
import com.sw.dualscreen.utils.SPUtil
|
||||||
|
import com.sw.face.collect.base.GlobalKey
|
||||||
|
import com.sw.face.collect.model.ApiResponse
|
||||||
|
import com.sw.face.collect.model.UserFaceModel
|
||||||
|
import com.sw.face.collect.repository.RemoteRepository
|
||||||
|
import com.sw.face.collect.utils.SpTool
|
||||||
|
import com.sw.plate.utils.Base64
|
||||||
|
import com.sw.plate.utils.ToastUtils
|
||||||
|
import com.sw.plate.utils.arcface.FaceApi
|
||||||
|
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class MainViewModel() : ViewModel() {
|
||||||
|
companion object {
|
||||||
|
const val PAGE_SIZE = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
private val faceApi: FaceApi = FaceApi()
|
||||||
|
protected val repository = RemoteRepository(ApiClient.apiService)
|
||||||
|
|
||||||
|
protected open fun parseResponse(response: ApiResponse<*>): Boolean {
|
||||||
|
val code = response.code
|
||||||
|
if (code == "00000" || code == "200" || code == "0") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
val message = response.msg
|
||||||
|
// Timber.d("msg = ${message}, code = $code")
|
||||||
|
ToastUtils.showToast("${message}(${code})")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取人脸数据
|
||||||
|
*/
|
||||||
|
fun getUserFaceCache(pageNo: Int = 1, pageSize: Int = PAGE_SIZE) {
|
||||||
|
var currentPageNo = pageNo
|
||||||
|
// Timber.tag(TAG).d("getUserFaceCache index = $currentPageNo")
|
||||||
|
viewModelScope.launch {
|
||||||
|
val response = repository.getUserFaceCache(currentPageNo)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
// 获取成功一次后缓存状态
|
||||||
|
SPUtil.getInstance().put(GlobalKey.KEY_FIRST_RUN, true)
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
val list: List<UserFaceModel> = response.data ?: emptyList()
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
SpTool.setLastFaceTimestamp(System.currentTimeMillis())
|
||||||
|
ToastUtils.showToast("查询人脸数据为空")
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
val faceEntity = list.map {
|
||||||
|
FaceEntity(it.userId, null, Base64.decode(it.faceFeatureStr))
|
||||||
|
}
|
||||||
|
faceApi.updateFaceData(currentPageNo, faceEntity)
|
||||||
|
if (list.size >= pageSize) {
|
||||||
|
currentPageNo++
|
||||||
|
getUserFaceCache(currentPageNo)
|
||||||
|
} else {
|
||||||
|
SpTool.setLastFaceTimestamp(System.currentTimeMillis())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取人脸数据
|
||||||
|
*/
|
||||||
|
fun getFaceIncrementList(
|
||||||
|
pageNo: Int = 1,
|
||||||
|
pageSize: Int = PAGE_SIZE,
|
||||||
|
timestamp: Long,
|
||||||
|
onAllQueryFinished:()->Unit,
|
||||||
|
onPageQueryFinished: (List<UserFaceModel>) -> Unit
|
||||||
|
) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val response = repository.getFaceIncrementList(
|
||||||
|
pageNum = pageNo.toLong(),
|
||||||
|
pageSize = pageSize.toLong(),
|
||||||
|
timestamp = timestamp
|
||||||
|
)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
val list: List<UserFaceModel> = response.data ?: emptyList()
|
||||||
|
onPageQueryFinished(list)
|
||||||
|
if (list.size >= pageSize) {
|
||||||
|
getFaceIncrementList(
|
||||||
|
pageNo = pageNo + 1,
|
||||||
|
pageSize = pageSize,
|
||||||
|
timestamp = timestamp,
|
||||||
|
onAllQueryFinished = onAllQueryFinished,
|
||||||
|
onPageQueryFinished = onPageQueryFinished
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
onAllQueryFinished()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -14,6 +14,9 @@ cameraCore = "1.3.0"
|
|||||||
kotlinxCoroutinesAndroid = "1.6.4"
|
kotlinxCoroutinesAndroid = "1.6.4"
|
||||||
lifecycleViewmodelKtx = "2.8.3"
|
lifecycleViewmodelKtx = "2.8.3"
|
||||||
lifecycleRuntimeKtx = "2.8.3"
|
lifecycleRuntimeKtx = "2.8.3"
|
||||||
|
retrofit = "3.0.0"
|
||||||
|
okhttp = "4.12.0"
|
||||||
|
gson = "2.13.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
@@ -36,6 +39,11 @@ kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutine
|
|||||||
androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
|
||||||
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
|
androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" }
|
||||||
|
|
||||||
|
logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
|
||||||
|
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
|
||||||
|
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
|
||||||
|
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
|
||||||
|
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ dependencies {
|
|||||||
// implementation("com.licheedev:android-serialport:2.1.5")
|
// implementation("com.licheedev:android-serialport:2.1.5")
|
||||||
|
|
||||||
val roomVersion = "2.2.5"
|
val roomVersion = "2.2.5"
|
||||||
implementation("androidx.room:room-runtime:$roomVersion")
|
api("androidx.room:room-runtime:$roomVersion")
|
||||||
annotationProcessor("androidx.room:room-compiler:$roomVersion")
|
annotationProcessor("androidx.room:room-compiler:$roomVersion")
|
||||||
|
|
||||||
implementation("io.reactivex.rxjava2:rxandroid:2.0.1")
|
implementation("io.reactivex.rxjava2:rxandroid:2.0.1")
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ package com.sw.plate.utils.arcface.facedb.entity;
|
|||||||
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.room.ColumnInfo;
|
import androidx.room.ColumnInfo;
|
||||||
import androidx.room.Entity;
|
import androidx.room.Entity;
|
||||||
|
import androidx.room.Ignore;
|
||||||
import androidx.room.PrimaryKey;
|
import androidx.room.PrimaryKey;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -42,7 +44,14 @@ public class FaceEntity implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
@ColumnInfo(name = "register_time")
|
@ColumnInfo(name = "register_time")
|
||||||
private long registerTime;
|
private long registerTime;
|
||||||
|
/**
|
||||||
|
* 用户类型:1-普通会员、2-临时用户、3-内部员工、或者其它待定类型
|
||||||
|
*/
|
||||||
|
@ColumnInfo(name = "user_type")
|
||||||
|
private String userType;
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
private int trackId;//人脸追踪ID
|
||||||
|
|
||||||
public FaceEntity(String userName, String imagePath, byte[] featureData) {
|
public FaceEntity(String userName, String imagePath, byte[] featureData) {
|
||||||
this.userName = userName;
|
this.userName = userName;
|
||||||
@@ -120,6 +129,22 @@ public class FaceEntity implements Parcelable {
|
|||||||
this.registerTime = registerTime;
|
this.registerTime = registerTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getTrackId() {
|
||||||
|
return trackId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrackId(int trackId) {
|
||||||
|
this.trackId = trackId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUserType() {
|
||||||
|
return userType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUserType(String userType) {
|
||||||
|
this.userType = userType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -132,8 +157,11 @@ public class FaceEntity implements Parcelable {
|
|||||||
dest.writeString(userName);
|
dest.writeString(userName);
|
||||||
dest.writeString(imagePath);
|
dest.writeString(imagePath);
|
||||||
dest.writeByteArray(featureData);
|
dest.writeByteArray(featureData);
|
||||||
|
dest.writeString(userType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) {
|
if (this == o) {
|
||||||
@@ -143,16 +171,17 @@ public class FaceEntity implements Parcelable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
FaceEntity that = (FaceEntity) o;
|
FaceEntity that = (FaceEntity) o;
|
||||||
return faceId == that.faceId &&
|
return this.faceId == that.faceId &&
|
||||||
registerTime == that.registerTime &&
|
this.registerTime == that.registerTime &&
|
||||||
userName.equals(that.userName) &&
|
TextUtils.equals(this.userName, that.userName) &&
|
||||||
imagePath.equals(that.imagePath) &&
|
TextUtils.equals(this.imagePath, that.imagePath) &&
|
||||||
Arrays.equals(featureData, that.featureData);
|
Arrays.equals(featureData, that.featureData) &&
|
||||||
|
TextUtils.equals(this.userType, that.userType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = Objects.hash(faceId, registerTime, userName, imagePath);
|
int result = Objects.hash(faceId, registerTime, userName, imagePath, userType);
|
||||||
result = 31 * result + Arrays.hashCode(featureData);
|
result = 31 * result + Arrays.hashCode(featureData);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user