清理人脸识别代码;清理所有编译警告

This commit is contained in:
BBIT-Kai
2026-05-29 09:57:38 +08:00
parent 2b39f0d81a
commit c5a72e938f
33 changed files with 382 additions and 282 deletions
-1
View File
@@ -131,7 +131,6 @@ dependencies {
implementation("androidx.camera:camera-view:$cameraXVersion")
implementation("com.google.zxing:core:3.5.4")
implementation("com.google.mlkit:face-detection:16.1.7")
implementation("com.aliyun.dpa:oss-android-sdk:2.9.21")
implementation("com.aliyun:ocr_api20210707:3.1.3") {
exclude(group = "pull-parser", module = "pull-parser")
@@ -1,6 +1,7 @@
package com.bbitcn.f8.pad.base
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.AnimatedVisibilityScope
@@ -25,6 +26,7 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
@@ -54,11 +56,10 @@ import androidx.compose.material3.InputChipDefaults
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.SecondaryScrollableTabRow
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.TabRowDefaults
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material3.Text
import androidx.compose.material3.VerticalDivider
import androidx.compose.runtime.Composable
@@ -71,6 +72,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
@@ -95,6 +97,7 @@ import androidx.compose.ui.unit.sp
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import com.bbitcn.f8.pad.M
import com.bbitcn.f8.pad.MyApp
import com.bbitcn.f8.pad.R
import com.bbitcn.f8.pad.ui.screen.view.Toasty
import com.bbitcn.f8.pad.ui.screen.view.drawer.IconInfo
@@ -355,24 +358,18 @@ fun VipBadgePreview() {
@Composable
fun VipBadge(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
Box(modifier = modifier) {
// 内容显示区域
content()
// 显示 Badge
Box(
modifier = M
.align(Alignment.TopEnd) // 控制Badge的位置
.padding(2.5.dp) // 适当的内边距,避免和内容重叠
) {
// Badge(containerColor = MyColors.Transparent) {
Image(
painter = painterResource(id = R.drawable.vip),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier.size(20.dp)
)
// }
}
Image(
painter = painterResource(id = R.drawable.vip),
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier
.align(Alignment.TopEnd)
.offset(x = 7.dp, y = (-7).dp)
.size(18.dp)
.alpha(0.82f)
)
}
}
@@ -782,7 +779,8 @@ fun isLandscape(): Boolean {
}
fun isBluetoothEnabled(): Boolean {
val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
val bluetoothManager = MyApp.appContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val bluetoothAdapter = bluetoothManager.adapter
return bluetoothAdapter?.isEnabled == true
}
@@ -809,11 +807,11 @@ fun MyTabRowHorizontal(
) {
var curPage by rememberSaveable { mutableStateOf(0) }
Column {
TabRow(
PrimaryTabRow(
selectedTabIndex = curPage,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
M.tabIndicatorOffset(tabPositions[curPage]),
indicator = {
TabRowDefaults.PrimaryIndicator(
M.tabIndicatorOffset(curPage),
color = MyColors.BlueGreen
)
}
@@ -880,14 +878,14 @@ fun MyScrollableTabRow(
tabs: List<String>,
onValueChange: (Int) -> Unit = {}
) {
ScrollableTabRow(
SecondaryScrollableTabRow(
selectedTabIndex = position,
containerColor = MyColors.Transparent,
edgePadding = 5.dp,
modifier = moidifer,
indicator = { tabPositions ->
indicator = {
TabRowDefaults.SecondaryIndicator(
M.tabIndicatorOffset(tabPositions[position]),
M.tabIndicatorOffset(position),
color = MyColors.BlueGreen,
)
}
@@ -12,7 +12,7 @@ data class FaceRecognizeResponse(
@SerializedName("log_id")
val logId: Long = 0,
@SerializedName("result")
val result: Result = Result(),
val result: Result? = Result(),
@SerializedName("timestamp")
val timestamp: Long = 0
) {
@@ -1,3 +1,5 @@
@file:Suppress("DEPRECATION")
package com.bbitcn.f8.pad.receiver
import android.content.BroadcastReceiver
@@ -5,14 +7,17 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.os.BatteryManager
import android.os.Build
import android.telephony.PhoneStateListener
import android.telephony.SignalStrength
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import androidx.compose.runtime.mutableStateOf
import androidx.core.content.ContextCompat
import com.bbitcn.f8.pad.utils.PollingTask
import com.bbitcn.f8.pad.utils.log.MyLog
import com.bbitcn.f8.pad.utils.registerReceiverCompat
@@ -29,16 +34,43 @@ class SystemInfoReceiver(private val context: Context) {
private val _signalStrength = MutableStateFlow(0)
val signalStrength = _signalStrength.asStateFlow()
private val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private val telephonyManager =
context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
/**
* 信号强度监听器
*/
@Suppress("DEPRECATION")
private val networkStateListener = object : PhoneStateListener() {
@Deprecated("Only used for Android 11 compatibility.")
override fun onSignalStrengthsChanged(signalStrengths: SignalStrength) {
super.onSignalStrengthsChanged(signalStrengths)
_signalStrength.value = signalStrengths.level
}
}
private var telephonyCallback: TelephonyCallback? = null
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
refreshNetworkStatus()
}
override fun onCapabilitiesChanged(
network: Network,
networkCapabilities: NetworkCapabilities
) {
updateNetworkStatus(networkCapabilities)
}
override fun onLost(network: Network) {
refreshNetworkStatus()
}
}
private val batteryReceiver = object : BroadcastReceiver() {
override fun onReceive(ctx: Context?, intent: Intent?) {
val level = intent?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
@@ -46,54 +78,17 @@ class SystemInfoReceiver(private val context: Context) {
}
}
private val connectivityReceiver = object : BroadcastReceiver() {
override fun onReceive(ctx: Context?, intent: Intent?) {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = connectivityManager.activeNetwork
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)
networkCapabilities?.let {
when {
it.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> {
_networkType.value = "WIFI"
val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiInfo = wifiManager.connectionInfo
_signalStrength.value = WifiManager.calculateSignalLevel(wifiInfo.rssi, 5)
}
it.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> {
_networkType.value = "Cellular"
val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
telephonyManager.listen(networkStateListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS)
}
it.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> {
_networkType.value = "Ethernet"
_signalStrength.value = 4 // Ethernet typically has a strong connection
}
else -> {
_networkType.value = "Unknown"
_signalStrength.value = 0
}
}
} ?: run {
_networkType.value = "No Connection"
_signalStrength.value = 0
}
}
}
fun register() {
val batteryIntentFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
val connectivityIntentFilter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
context.registerReceiverCompat(
batteryReceiver,
batteryIntentFilter
)
context.registerReceiverCompat(
connectivityReceiver,
connectivityIntentFilter
)
connectivityManager.registerDefaultNetworkCallback(networkCallback)
registerSignalStrengthListener()
refreshNetworkStatus()
// 开始轮询网络状态 防止状态假死
PollingTask.getInstance("SystemInfoReceiver").startPollingTaskOnIOThread("NetworkStatusPolling",30_000) {
refreshNetworkStatus()
@@ -102,42 +97,106 @@ class SystemInfoReceiver(private val context: Context) {
fun unregister() {
context.unregisterReceiver(batteryReceiver)
context.unregisterReceiver(connectivityReceiver)
runCatching {
connectivityManager.unregisterNetworkCallback(networkCallback)
}.onFailure {
MyLog.appError("取消网络监听失败:${it.message}")
}
unregisterSignalStrengthListener()
PollingTask.getInstance("SystemInfoReceiver").stopTask("NetworkStatusPolling")
}
private fun refreshNetworkStatus() {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = connectivityManager.activeNetwork
val networkCapabilities = connectivityManager.getNetworkCapabilities(activeNetwork)
networkCapabilities?.let {
when {
it.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> {
_networkType.value = "WIFI"
val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
val wifiInfo = wifiManager.connectionInfo
val rssi = wifiInfo.rssi
if (rssi != -127) { // -127 通常是无效值
_signalStrength.value = WifiManager.calculateSignalLevel(rssi, 5)
}
}
it.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> {
_networkType.value = "Cellular"
// 通常需要 TelephonyManager.getSignalStrength,但这里只能靠原来 listener 补充
}
it.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> {
_networkType.value = "Ethernet"
_signalStrength.value = 4
}
else -> {
_networkType.value = "Unknown"
_signalStrength.value = 0
}
}
} ?: run {
if (networkCapabilities == null) {
_networkType.value = "No Connection"
_signalStrength.value = 0
} else {
updateNetworkStatus(networkCapabilities)
}
}
private fun updateNetworkStatus(networkCapabilities: NetworkCapabilities) {
when {
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> {
_networkType.value = "WIFI"
updateWifiSignalStrength(networkCapabilities)
}
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> {
_networkType.value = "Cellular"
}
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> {
_networkType.value = "Ethernet"
_signalStrength.value = 4
}
else -> {
_networkType.value = "Unknown"
_signalStrength.value = 0
}
}
}
private fun updateWifiSignalStrength(networkCapabilities: NetworkCapabilities) {
val rssi = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
(networkCapabilities.transportInfo as? WifiInfo)?.rssi
} else {
@Suppress("DEPRECATION")
val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
wifiManager.connectionInfo?.rssi
}
if (rssi != null && rssi != -127) {
_signalStrength.value = calculateSignalLevel(rssi)
}
}
private fun calculateSignalLevel(rssi: Int): Int {
return when {
rssi <= -100 -> 0
rssi >= -55 -> 4
else -> ((rssi + 100) * 4 / 45).coerceIn(0, 4)
}
}
private fun registerSignalStrengthListener() {
runCatching {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val callback = object : TelephonyCallback(), TelephonyCallback.SignalStrengthsListener {
override fun onSignalStrengthsChanged(signalStrength: SignalStrength) {
_signalStrength.value = signalStrength.level
}
}
telephonyCallback = callback
telephonyManager.registerTelephonyCallback(
ContextCompat.getMainExecutor(context),
callback
)
} else {
@Suppress("DEPRECATION")
telephonyManager.listen(
networkStateListener,
PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
)
}
}.onFailure {
MyLog.appError("注册信号强度监听失败:${it.message}")
}
}
private fun unregisterSignalStrengthListener() {
runCatching {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
telephonyCallback?.let {
telephonyManager.unregisterTelephonyCallback(it)
}
telephonyCallback = null
} else {
@Suppress("DEPRECATION")
telephonyManager.listen(networkStateListener, PhoneStateListener.LISTEN_NONE)
}
}.onFailure {
MyLog.appError("取消信号强度监听失败:${it.message}")
}
}
@@ -1,8 +1,7 @@
package com.bbitcn.f8.pad.ui
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import androidx.activity.OnBackPressedCallback
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.EnterTransition
@@ -26,6 +25,9 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.NavHost
@@ -71,6 +73,7 @@ class MainActivity : ComponentActivity() {
installSplashScreen()
super.onCreate(savedInstanceState)
setFullScreen(true)
registerBackPressedCallback()
// 初始化NfcAdapter
PrinterBT.init()
WaterCutMeterBT.init()
@@ -243,30 +246,35 @@ class MainActivity : ComponentActivity() {
}
fun setFullScreen(isFullScreen: Boolean) {
val controller = WindowCompat.getInsetsController(window, window.decorView)
if (isFullScreen) {
window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
val decorView = window.decorView
val uiOptions = (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_FULLSCREEN)
decorView.systemUiVisibility = uiOptions
WindowCompat.setDecorFitsSystemWindows(window, false)
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
controller.hide(WindowInsetsCompat.Type.systemBars())
} else {
window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN)
WindowCompat.setDecorFitsSystemWindows(window, true)
controller.show(WindowInsetsCompat.Type.systemBars())
}
}
private var lastBackPressedTime: Long = 0
override fun onBackPressed() {
val currentTime = System.currentTimeMillis()
if (currentTime - lastBackPressedTime < 2000) { // 如果两次点击时间间隔小于2秒
super.onBackPressed() // 调用系统默认的退出行为
} else {
Toasty.showToast("再按一次退出")
lastBackPressedTime = currentTime
}
private fun registerBackPressedCallback() {
onBackPressedDispatcher.addCallback(
this,
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
val currentTime = System.currentTimeMillis()
if (currentTime - lastBackPressedTime < 2000) {
finish()
} else {
Toasty.showToast("再按一次退出")
lastBackPressedTime = currentTime
}
}
}
)
}
}
@@ -25,9 +25,8 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.PrimaryTabRow
import androidx.compose.material3.TabRowDefaults
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
@@ -140,11 +139,11 @@ fun TabPagerScreen(
val pagerState = rememberPagerState(pageCount = { tabs.size })
val scope = rememberCoroutineScope()
Column {
TabRow(
PrimaryTabRow(
selectedTabIndex = pagerState.currentPage,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
M.tabIndicatorOffset(tabPositions[pagerState.currentPage]),
indicator = {
TabRowDefaults.PrimaryIndicator(
M.tabIndicatorOffset(pagerState.currentPage),
color = MyColors.BlueGreen,
)
}
@@ -375,7 +374,7 @@ fun PhoneLogin(loginViewModel: LoginViewModel) {
},
modifier = Modifier
.padding(top = 20.dp)
.fillMaxWidth()
.fillMaxWidth(),
)
}
}
@@ -398,7 +397,7 @@ fun FaceLogin(loginViewModel: LoginViewModel) {
Text(
text = "请保持光线充足,人脸无遮挡",
color = MyColors.Gray,
modifier = M.padding(vertical = 10.dp)
modifier = M.padding(vertical = 2.5.dp)
)
MyButton(
text = "开始识别登录",
@@ -406,7 +405,7 @@ fun FaceLogin(loginViewModel: LoginViewModel) {
loginViewModel.faceRecognize()
},
modifier = Modifier
.padding(top = 5.dp)
.padding(top = 20.dp)
.fillMaxWidth()
)
}
@@ -6,6 +6,7 @@ import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -27,12 +28,11 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBackIosNew
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
@@ -54,10 +54,12 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.LastBaseline
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -324,7 +326,7 @@ private fun AuthorNameTimestamp(msg: Message, isUserMe: Boolean) {
@Composable
private fun RowScope.DayHeaderLine() {
Divider(
HorizontalDivider(
modifier = M
.weight(1f)
.align(Alignment.CenterVertically),
@@ -360,20 +362,28 @@ fun ChatItemBubble(
text = chatRecord.content,
primary = isUserMe
)
ClickableText(
var textLayoutResult by remember { mutableStateOf<TextLayoutResult?>(null) }
Text(
text = styledMessage,
style = MaterialTheme.typography.bodyLarge.copy(color = textColor),
modifier = M.padding(16.dp),
onClick = {
styledMessage
.getStringAnnotations(start = it, end = it)
.firstOrNull()
?.let { annotation ->
when (annotation.tag) {
SymbolAnnotationType.LINK.name -> uriHandler.openUri(annotation.item)
else -> Unit
}
modifier = M
.padding(16.dp)
.pointerInput(styledMessage) {
detectTapGestures { position ->
val offset = textLayoutResult?.getOffsetForPosition(position) ?: return@detectTapGestures
styledMessage
.getStringAnnotations(start = offset, end = offset)
.firstOrNull()
?.let { annotation ->
when (annotation.tag) {
SymbolAnnotationType.LINK.name -> uriHandler.openUri(annotation.item)
else -> Unit
}
}
}
},
onTextLayout = {
textLayoutResult = it
}
)
}
@@ -9,6 +9,9 @@ import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
@@ -51,6 +54,7 @@ fun FaceDialog(
viewModel: FaceDialogViewModel = viewModel()
) {
val tips by viewModel.tips.collectAsState()
val countdownSeconds by viewModel.countdownSeconds.collectAsState()
MyDialog("人脸识别-${tips}",
info.showDialog,
onDismissRequest = { info.onDismiss() },
@@ -65,7 +69,11 @@ fun FaceDialog(
) {
LaunchedEffect(info.showDialog) {
if (info.showDialog){
viewModel.initializeCamera(info.isRegister,info.isSystemUser){ userId,faceToken->
viewModel.initializeCamera(
isRegister = info.isRegister,
isSystemUser = info.isSystemUser,
onAutoCapture = info.onDismiss
) { userId,faceToken->
// 识别成功的方法
info.onDismiss()
info.onRecognizeFace(userId, faceToken)
@@ -113,6 +121,24 @@ fun FaceDialog(
}
}
}
if (countdownSeconds > 0) {
Surface(
modifier = M
.align(Alignment.TopCenter)
.padding(top = 16.dp),
shape = MaterialTheme.shapes.large,
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.88f),
tonalElevation = 6.dp,
shadowElevation = 8.dp
) {
Text(
text = "${countdownSeconds}秒后开始识别",
modifier = M.padding(horizontal = 24.dp, vertical = 12.dp),
color = MaterialTheme.colorScheme.onPrimary,
style = MaterialTheme.typography.titleLarge
)
}
}
}
}
}
@@ -5,13 +5,11 @@ import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.net.Uri
import android.util.Size
import android.view.WindowManager
import androidx.annotation.OptIn
import androidx.camera.camera2.interop.Camera2CameraInfo
import androidx.camera.camera2.interop.ExperimentalCamera2Interop
import androidx.camera.core.Camera
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCaptureException
import androidx.camera.core.Preview
@@ -19,6 +17,7 @@ import androidx.camera.core.SurfaceRequest
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.viewModelScope
import com.alibaba.sdk.android.oss.ClientException
import com.alibaba.sdk.android.oss.ServiceException
import com.alibaba.sdk.android.oss.model.ObjectMetadata
@@ -31,16 +30,17 @@ import com.bbitcn.f8.pad.model.net.response.FarmerDetailResponse
import com.bbitcn.f8.pad.ui.screen.view.Toasty
import com.bbitcn.f8.pad.ui.screen.view.Toasty.showTipsDialog
import com.bbitcn.f8.pad.utils.MMKVUtil
import com.bbitcn.f8.pad.utils.MyUtil
import com.bbitcn.f8.pad.utils.externalModules.devices.reader.face.FaceRecognize
import com.bbitcn.f8.pad.utils.externalModules.devices.reader.face.OssUtils
import com.bbitcn.f8.pad.utils.global.RxTag
import com.bbitcn.f8.pad.utils.log.MyLog
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import java.io.File
import java.util.Random
@@ -49,6 +49,8 @@ class FaceDialogViewModel : BaseViewModel() {
private val _tips = MutableStateFlow("")
val tips = _tips.asStateFlow()
private val _countdownSeconds = MutableStateFlow(0)
val countdownSeconds = _countdownSeconds.asStateFlow()
private val _surfaceRequests = MutableStateFlow<SurfaceRequest?>(null)
val surfaceRequests: StateFlow<SurfaceRequest?> get() = _surfaceRequests.asStateFlow()
@@ -66,14 +68,14 @@ class FaceDialogViewModel : BaseViewModel() {
lateinit var curCameraInfo: CameraInfo
var myCamera: Camera? = null
private var autoRecognizeJob: Job? = null
init {
context = MyApp.appContext
lifecycleOwner = context as LifecycleOwner
// 获取当前设备的旋转角度
val rotation = (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager)
.defaultDisplay.rotation
val rotation = MyUtil.getDisplayRotation(context)
imageCapture = ImageCapture.Builder()
.setTargetRotation(rotation)
.build()
@@ -82,15 +84,20 @@ class FaceDialogViewModel : BaseViewModel() {
private var _isRegister = false
private var _isSystemUser = false
private var _onRecognizeFace: ((userId: String, faceToken: String) -> Unit) = { _, _ -> }
private var _onAutoCapture: () -> Unit = {}
fun initializeCamera(
isRegister: Boolean,
isSystemUser: Boolean,
onAutoCapture: () -> Unit = {},
onRecognizeFace: (userId: String, faceToken: String) -> Unit
) {
_isSystemUser = isSystemUser
_isRegister = isRegister
_onAutoCapture = onAutoCapture
_onRecognizeFace = onRecognizeFace
_tips.value = ""
_countdownSeconds.value = 0
val cameraManager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
val cameraIdList = cameraManager.cameraIdList
@@ -132,9 +139,10 @@ class FaceDialogViewModel : BaseViewModel() {
}
}
@OptIn(androidx.camera.core.ExperimentalGetImage::class)
fun setCameraSelector(cameraInfo: CameraInfo) {
MyLog.test("setCameraSelector: ${cameraInfo.cameraId}, ${cameraInfo.lensFacing}")
autoRecognizeJob?.cancel()
_showPreview.value = true
// 创建新的 CameraSelector
cameraSelector = CameraSelector.Builder()
.requireLensFacing(cameraInfo.lensFacing)
@@ -159,74 +167,43 @@ class FaceDialogViewModel : BaseViewModel() {
}
// 解绑所有之前的用例
cameraProvider?.unbindAll()
val imageAnalysis = ImageAnalysis.Builder()
// 默认情况下,ImageAnalysis 的输出图像格式是 YUV,适合用于高效的图像处理和计算,但如果你需要以 RGBA 格式输出图像(通常用于处理图像像素颜色、UI 渲染等),就可以启用这一行代码。
// .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
// .setTargetResolution(Size(1280, 720))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
// 高精度人脸检测
val highAccuracyOpts = FaceDetectorOptions.Builder()
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE)
.setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
.setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
.build()
// 实时人脸检测
val realTimeOpts = FaceDetectorOptions.Builder()
.setContourMode(FaceDetectorOptions.CONTOUR_MODE_ALL)
.build()
val detector = FaceDetection.getClient(realTimeOpts)
imageAnalysis.setAnalyzer(ContextCompat.getMainExecutor(context)) { imageProxy ->
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image =
InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
val result = detector.process(image)
.addOnSuccessListener { faces ->
// MyLog.face("检测到人脸数量: ${faces.size}")
if (faces.size == 1) {
_showPreview.value = false
if (!_isRegister) {
// 识别:自动
faceRecognize()
// 停止分析
imageAnalysis.clearAnalyzer()
}
} else if (faces.size > 1) {
_tips.value = "识别到多个人,请重试"
} else {
_tips.value = "未识别到人脸"
}
}
.addOnFailureListener { e ->
MyLog.face("没有人脸,${e.message}")
}
.addOnCompleteListener {
mediaImage.close()
imageProxy.close()
}
}
}
// 绑定选择的摄像头和预览用例
myCamera = cameraProvider?.bindToLifecycle(
lifecycleOwner,
cameraSelector!!,
imageCapture,
imageAnalysis,
previewUseCase!!
)
scheduleAutoRecognize()
}, ContextCompat.getMainExecutor(context))
}
private fun scheduleAutoRecognize() {
autoRecognizeJob?.cancel()
if (_isRegister) {
_countdownSeconds.value = 0
_tips.value = "请调整画面后点击确定注册"
return
}
autoRecognizeJob = viewModelScope.launch {
for (second in 3 downTo 1) {
_countdownSeconds.value = second
_tips.value = "${second}秒后自动识别,请保持光线充足,人脸无遮挡"
delay(1000L)
}
_countdownSeconds.value = 0
_tips.value = "正在拍照识别"
faceRecognize(onPhotoTaken = _onAutoCapture)
}
}
fun focusOnPoint(surfaceBounds: Size, x: Float, y: Float) {
}
fun takePicture(onFinish: (Uri) -> Unit = {}) {
val file = File(context.externalMediaDirs.first(), "${System.currentTimeMillis()}.jpg")
val file = MyUtil.createPictureFile(context)
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
val cameraExecutor = ContextCompat.getMainExecutor(context)
imageCapture.takePicture(outputFileOptions, cameraExecutor,
@@ -238,9 +215,9 @@ class FaceDialogViewModel : BaseViewModel() {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
// Toasty.success("拍照成功")
val savedUri = outputFileResults.savedUri
val savedUri = outputFileResults.savedUri ?: Uri.fromFile(file)
// _savedUri.value = savedUri
onFinish(savedUri!!)
onFinish(savedUri)
}
})
}
@@ -307,7 +284,7 @@ class FaceDialogViewModel : BaseViewModel() {
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++3.上传信息到服务器
val result3 = apiService.registerFaceForF8(
data = FaceRegisterF8Request(
baiduFaceToken = success1!!.result.faceToken,
baiduFaceToken = success1.result.faceToken,
ossBucketname = config.bucketName,
ossObjectname = objectName,
userid = userId,
@@ -333,8 +310,12 @@ class FaceDialogViewModel : BaseViewModel() {
}
}
fun faceRecognize() {
fun faceRecognize(onPhotoTaken: () -> Unit = {}) {
autoRecognizeJob?.cancel()
_countdownSeconds.value = 0
_showPreview.value = false
takePicture {
onPhotoTaken()
doInIoThreadThenUI("正在识别人脸", onIO = {
val accessToken = apiService.getFaceAccessToken()
val result = FaceRecognize.faceRecognize(
@@ -345,9 +326,11 @@ class FaceDialogViewModel : BaseViewModel() {
return@doInIoThreadThenUI result
}) { result ->
if (result.first == "false") {
// 重新启动摄像头
setCameraSelector(curCameraInfo)
_tips.value = result.second
_showPreview.value = true
Toasty.error("人脸识别失败:${result.second}")
} else {
_tips.value = ""
_onRecognizeFace(result.first, result.second)
}
}
@@ -5,7 +5,6 @@ import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.net.Uri
import android.util.Size
import android.view.WindowManager
import androidx.camera.camera2.interop.Camera2CameraInfo
import androidx.camera.camera2.interop.ExperimentalCamera2Interop
import androidx.camera.core.Camera
@@ -21,6 +20,7 @@ import com.bbitcn.f8.pad.MyApp
import com.bbitcn.f8.pad.base.BaseViewModel
import com.bbitcn.f8.pad.ui.screen.secondFunc.CameraInfo
import com.bbitcn.f8.pad.ui.screen.view.Toasty
import com.bbitcn.f8.pad.utils.MyUtil
import com.bbitcn.f8.pad.utils.log.MyLog
import com.bbitcn.f8.pad.utils.externalModules.ocr.ALiApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -57,8 +57,7 @@ class OCRDialogViewModel : BaseViewModel() {
doInIoThreadThenUI("初始化相机", onIO = {
lifecycleOwner = context as LifecycleOwner
// 获取当前设备的旋转角度
val rotation = (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager)
.defaultDisplay.rotation
val rotation = MyUtil.getDisplayRotation(context)
imageCapture = ImageCapture.Builder()
.setTargetRotation(rotation)
.build()
@@ -142,7 +141,7 @@ class OCRDialogViewModel : BaseViewModel() {
}
fun takePicture(onFinish: (Uri) -> Unit = {}) {
val file = File(context.externalMediaDirs.first(), "${System.currentTimeMillis()}.jpg")
val file = MyUtil.createPictureFile(context)
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
val cameraExecutor = ContextCompat.getMainExecutor(context)
imageCapture.takePicture(outputFileOptions, cameraExecutor,
@@ -153,10 +152,10 @@ class OCRDialogViewModel : BaseViewModel() {
}
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
val savedUri = outputFileResults.savedUri
val savedUri = outputFileResults.savedUri ?: Uri.fromFile(file)
// 拍照成功后关闭相机
cameraProvider?.unbindAll()
onFinish(savedUri!!)
onFinish(savedUri)
}
})
}
@@ -122,7 +122,9 @@ fun FundsScreen(
val dateStart =
SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse(start)
val dateEnd = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).parse(end)
fundsViewModel.updateDateRange(dateStart to dateEnd)
if (dateStart != null && dateEnd != null) {
fundsViewModel.updateDateRange(dateStart to dateEnd)
}
onFilterLikeChanged(queryInput)
}
DateRangePickTextFiled(M.fillMaxWidth(), dateRange = queryDateRange) {
@@ -22,10 +22,9 @@ import androidx.compose.material3.DatePickerDialog
import androidx.compose.material3.DateRangePicker
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.SecondaryScrollableTabRow
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRowDefaults
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberDateRangePickerState
@@ -147,15 +146,15 @@ fun PurchaseScreen(
purchaseViewModel.openAddTicketDialog(navController)
})
if (isLandscape()) {
ScrollableTabRow(
SecondaryScrollableTabRow(
modifier = M
.weight(1f),
selectedTabIndex = queryType,
containerColor = MyColors.Transparent,
edgePadding = 10.dp,
indicator = { tabPositions ->
indicator = {
TabRowDefaults.SecondaryIndicator(
M.tabIndicatorOffset(tabPositions[queryType]),
M.tabIndicatorOffset(queryType),
color = MyColors.BlueGreen,
)
}
@@ -202,14 +201,14 @@ fun PurchaseScreen(
}
}
if (!isLandscape()) {
ScrollableTabRow(
SecondaryScrollableTabRow(
selectedTabIndex = queryType,
containerColor = MyColors.Transparent,
edgePadding = 0.dp,
modifier = M,
indicator = { tabPositions ->
indicator = {
TabRowDefaults.SecondaryIndicator(
M.tabIndicatorOffset(tabPositions[queryType]),
M.tabIndicatorOffset(queryType),
color = MyColors.BlueGreen,
)
}
@@ -6,7 +6,6 @@ import androidx.compose.runtime.setValue
import com.alibaba.sdk.android.oss.OSS
import com.alibaba.sdk.android.oss.OSSClient
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider
import com.alibaba.sdk.android.oss.common.auth.OSSPlainTextAKSKCredentialProvider
import com.alibaba.sdk.android.oss.model.GeneratePresignedUrlRequest
import com.bbitcn.f8.pad.MyApp
import com.bbitcn.f8.pad.base.BaseViewModel
@@ -6,7 +6,6 @@ import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager
import android.net.Uri
import android.util.Size
import android.view.WindowManager
import androidx.camera.camera2.interop.Camera2CameraInfo
import androidx.camera.camera2.interop.ExperimentalCamera2Interop
import androidx.camera.core.Camera
@@ -23,6 +22,7 @@ import com.bbitcn.f8.pad.base.BaseViewModel
import com.bbitcn.f8.pad.ui.screen.dialog.CameraInfo
import com.bbitcn.f8.pad.ui.screen.view.Toasty
import com.bbitcn.f8.pad.utils.MMKVUtil
import com.bbitcn.f8.pad.utils.MyUtil
import com.bbitcn.f8.pad.utils.externalModules.devices.reader.face.FaceRecognize
import com.bbitcn.f8.pad.utils.log.MyLog
import com.bbitcn.f8.pad.utils.externalModules.ocr.ALiApi
@@ -60,8 +60,7 @@ class MyCameraViewModel : BaseViewModel() {
context = MyApp.appContext
lifecycleOwner = context as LifecycleOwner
// 获取当前设备的旋转角度
val rotation = (context.getSystemService(Context.WINDOW_SERVICE) as WindowManager)
.defaultDisplay.rotation
val rotation = MyUtil.getDisplayRotation(context)
imageCapture = ImageCapture.Builder()
.setTargetRotation(rotation)
.build()
@@ -151,7 +150,7 @@ class MyCameraViewModel : BaseViewModel() {
}
fun takePicture(onFinish: (Uri) -> Unit = {}) {
val file = File(context.externalMediaDirs.first(), "${System.currentTimeMillis()}.jpg")
val file = MyUtil.createPictureFile(context)
val outputFileOptions = ImageCapture.OutputFileOptions.Builder(file).build()
val cameraExecutor = ContextCompat.getMainExecutor(context)
imageCapture.takePicture(outputFileOptions, cameraExecutor,
@@ -163,9 +162,9 @@ class MyCameraViewModel : BaseViewModel() {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
Toasty.success("拍照成功")
val savedUri = outputFileResults.savedUri
val savedUri = outputFileResults.savedUri ?: Uri.fromFile(file)
_savedUri.value = savedUri
onFinish(savedUri!!)
onFinish(savedUri)
}
})
}
@@ -27,7 +27,6 @@ import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.*
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
@@ -89,14 +88,14 @@ fun PayOperate(modifier: Modifier, payViewModel: PayViewModel) {
}
MyCard(border = BorderStroke(1.dp, MyColors.Gray), elevation = 0.dp) {
Column {
ScrollableTabRow(
SecondaryScrollableTabRow(
selectedTabIndex = pagerState.currentPage,
containerColor = MyColors.Transparent,
edgePadding = 10.dp,
modifier = M.wrapContentHeight(),
indicator = { tabPositions ->
indicator = {
TabRowDefaults.SecondaryIndicator(
M.tabIndicatorOffset(tabPositions[pagerState.currentPage]),
M.tabIndicatorOffset(pagerState.currentPage),
color = MyColors.BlueGreen,
)
}
@@ -33,7 +33,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.*
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
@@ -191,14 +190,14 @@ fun WeightList(weightViewModel: WeightViewModel) {
val detailList by weightViewModel.detailList.collectAsState()
Column(modifier = M.padding(horizontal = 10.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
ScrollableTabRow(
SecondaryScrollableTabRow(
selectedTabIndex = pagerState.currentPage,
containerColor = MyColors.Transparent,
modifier = M
.weight(1f),
indicator = { tabPositions ->
indicator = {
TabRowDefaults.SecondaryIndicator(
M.tabIndicatorOffset(tabPositions[pagerState.currentPage]),
M.tabIndicatorOffset(pagerState.currentPage),
color = MyColors.BlueGreen,
)
}
@@ -29,7 +29,7 @@ import com.commandiron.wheel_picker_compose.WheelDateTimePicker
import com.commandiron.wheel_picker_compose.core.TimeFormat
import com.commandiron.wheel_picker_compose.core.WheelPickerDefaults
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -63,7 +63,7 @@ object Toasty : BaseViewModel() {
val drawerContent: StateFlow<@Composable () -> Unit> = _drawerContent.asStateFlow()
fun showOptionDrawer(title: String, options: List<String>, onClick: (String) -> Unit) {
GlobalScope.launch {
CoroutineScope(Dispatchers.Main).launch {
withContext(Dispatchers.IO) {
if (options.isEmpty()) {
Toasty.showToast("暂无选项")
@@ -215,7 +215,7 @@ fun TicketForDryStoreCocoon(
"芯片",
3,
{ if (it.rfid.length >= 6) ".." + it.rfid.takeLast(4) else it.rfid }),
MyTableData("状态", 2, { it.status.toString() }),
MyTableData("状态", 2, { it.status }),
MyTableData("毛重", 2, { it.maozhong.toString() }),
MyTableData("皮重", 2, { it.pizhong.toString() }),
MyTableData("净重", 2, { it.jingzhong.toString() }),
@@ -231,13 +231,13 @@ fun TicketForDryStoreCocoon(
Row(verticalAlignment = Alignment.CenterVertically) {
InfoText(
"乡镇",
it.xiangzhen.toString(),
it.xiangzhen,
M.weight(2f),
true
)
InfoText(
"芯片",
it.rfid.toString(),
it.rfid,
M.weight(3f),
true
)
@@ -251,13 +251,13 @@ fun TicketForDryStoreCocoon(
Row(verticalAlignment = Alignment.CenterVertically) {
InfoText(
"类型",
ex.type.toString(),
ex.type,
M.weight(2f),
true
)
InfoText(
"时间",
ex.time.toString(),
ex.time,
M.weight(3f),
true
)
@@ -265,7 +265,7 @@ fun TicketForDryStoreCocoon(
Row(verticalAlignment = Alignment.CenterVertically) {
InfoText(
"备注",
ex.memo.toString(),
ex.memo,
M.weight(3f),
true
)
@@ -35,7 +35,7 @@ object AudioPlayer {
return
}
val nextResId = audioQueue.poll()
val nextResId = audioQueue.removeFirst()
val player = MediaPlayer.create(MyApp.appContext, nextResId)
mediaPlayer = player
isPlaying = true
@@ -3,6 +3,8 @@ package com.bbitcn.f8.pad.utils
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap
import android.os.Build
import android.os.Environment
import android.text.format.DateUtils
import com.bbitcn.f8.pad.MyApp.Companion.appContext
import com.bbitcn.f8.pad.utils.global.Global
@@ -12,6 +14,7 @@ import com.blankj.utilcode.util.EncryptUtils
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.MultiFormatWriter
import java.io.File
import java.nio.charset.StandardCharsets
import java.text.SimpleDateFormat
import java.util.Date
@@ -50,13 +53,30 @@ object MyUtil {
val packageManager = appContext.packageManager
val packageInfo =
packageManager.getPackageInfo(appContext.packageName, 0)
return packageInfo.versionCode
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
packageInfo.longVersionCode.toInt()
} else {
@Suppress("DEPRECATION")
packageInfo.versionCode
}
} catch (e: Exception) {
e.printStackTrace()
return -1
}
}
fun getDisplayRotation(context: Context = appContext): Int {
return context.display.rotation
}
fun createPictureFile(context: Context = appContext): File {
val dir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) ?: context.filesDir
if (!dir.exists()) {
dir.mkdirs()
}
return File(dir, "${System.currentTimeMillis()}.jpg")
}
fun getVersionName(): String {
try {
val packageManager = appContext.packageManager
@@ -26,7 +26,7 @@ object JTPrinterUSB : UsbDeviceConnector(), PrinterInterface {
val listUsbPort = AutoReplyPrint.CP_Port_EnumUsb_Helper.EnumUsb()
if (listUsbPort != null) {
for (usbPort in listUsbPort) {
if (usbPort.contains(vId.toString()) && usbPort.contains(pId.toString())) {
if (usbPort.contains(vId) && usbPort.contains(pId)) {
// 检测到巨天打印机,视为连接成功
setVId(vId)
setPId(pId)
@@ -74,7 +74,7 @@ object JTPrinterUSB : UsbDeviceConnector(), PrinterInterface {
if (listUsbPort != null) {
for (usbPort in listUsbPort) {
// usbPort 格式为 "VID:0x4B43,PID:0x0FE6"
if (usbPort.contains(getVId().toString()) && usbPort.contains(getPId().toString())) {
if (usbPort.contains(getVId()) && usbPort.contains(getPId())) {
h = AutoReplyPrint.INSTANCE.CP_Port_OpenUsb(usbPort, 1)
AutoReplyPrint.INSTANCE.CP_Printer_AddOnPrinterStatusEvent({ handle: Pointer, printerErrorStatus: Long, printerInfoStatus: Long, privateData: Pointer ->
if (CP_RTSTATUS_Helper.CP_RTSTATUS_NOPAPER(printerErrorStatus)) {
@@ -10,8 +10,8 @@ import com.bbitcn.f8.pad.utils.externalModules.devices.printer.JTPrinterUSB.resi
import com.bbitcn.f8.pad.utils.externalModules.manager.bluetooth.MyBlueTooth
import com.blankj.utilcode.util.TimeUtils
import cpcl.PrinterHelper
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.ByteArrayOutputStream
@@ -344,7 +344,7 @@ object PrinterBT : MyBlueTooth(), PrinterInterface {
}
try {
setState(0)
GlobalScope.launch {
CoroutineScope(Dispatchers.Main).launch {
setState(withContext(Dispatchers.IO) {
val result =
if (PrinterHelper.portOpenBT(MyApp.appContext, mac) == 0) 1 else -1
@@ -70,7 +70,12 @@ object FaceRecognize {
image = base64,
)
)
val userList = result.result.userList
if (result.errorCode != 0) {
return "false" to result.errorMsg.ifBlank { "人脸识别失败" }
}
val faceResult = result.result
?: return "false" to result.errorMsg.ifBlank { "未识别到人脸" }
val userList = faceResult.userList
if (userList.size > 1) {
return "false" to "识别到多个人,请重新识别"
} else if (userList.isEmpty()) {
@@ -80,7 +85,7 @@ object FaceRecognize {
} else {
val user = userList[0]
MyLog.network("faceRecognize result: ${user.userId}")
return user.userId.replace("_", "-") to result.result.faceToken
return user.userId.replace("_", "-") to faceResult.faceToken
}
}
return "false" to "图片有误"
@@ -3,7 +3,6 @@ package com.bbitcn.f8.pad.utils.externalModules.devices.reader.face
import com.alibaba.sdk.android.oss.OSS
import com.alibaba.sdk.android.oss.OSSClient
import com.alibaba.sdk.android.oss.common.auth.OSSCredentialProvider
import com.alibaba.sdk.android.oss.common.auth.OSSPlainTextAKSKCredentialProvider
import com.alibaba.sdk.android.oss.common.auth.OSSStsTokenCredentialProvider
import com.bbitcn.f8.pad.MyApp
import com.bbitcn.f8.pad.ui.screen.view.Toasty.showTipsDialog
@@ -7,7 +7,6 @@ import com.bbitcn.f8.pad.utils.MMKVUtil
import com.bbitcn.f8.pad.utils.log.MyLog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
@@ -111,7 +110,7 @@ abstract class DeviceController {
onIO: suspend () -> T,
onUI: (T) -> Unit,
) {
GlobalScope.launch {
CoroutineScope(Dispatchers.Main).launch {
val result = runCatching {
withContext(Dispatchers.IO) {
if (showDialog) {
@@ -46,7 +46,7 @@ abstract class MyBlueTooth : BluetoothDeviceConnector() {
var devicesFound: MutableList<BluetoothDevice> = mutableStateListOf()
val mClient: BluetoothClient? by lazy {
MyApp.appContext?.let { BluetoothClient(MyApp.appContext) }
BluetoothClient(MyApp.appContext)
}
val mBleConnectStatusListener: BleConnectStatusListener = object : BleConnectStatusListener() {
override fun onConnectStatusChanged(mac: String, status: Int) {
@@ -14,7 +14,7 @@ object CrashHandlerUtil : Thread.UncaughtExceptionHandler {
Thread.setDefaultUncaughtExceptionHandler(this)
}
override fun uncaughtException(thread: Thread?, ex: Throwable) {
override fun uncaughtException(thread: Thread, ex: Throwable) {
ex.printStackTrace()
MyLog.appError("软件已崩溃:" + getFormattedException(ex))
Toasty.showConfirmDialog("软件已崩溃,请重启\n" + getFormattedException(ex)) {
@@ -15,7 +15,7 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Protocol
import okhttp3.Response
import okhttp3.ResponseBody
import okhttp3.ResponseBody.Companion.toResponseBody
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
@@ -70,8 +70,7 @@ object RetrofitClient {
e.printStackTrace()
MyLog.networkError("请求网络时发生异常:${e.message}")
MyLog.networkError("错误类型:${e.javaClass.simpleName},错误信息:${e.message}")
val responseBody =
ResponseBody.create("application/json".toMediaTypeOrNull(), "{}")
val responseBody = "{}".toResponseBody("application/json".toMediaTypeOrNull())
val fakeResponse: Response =
if (e is UnknownHostException || e is SocketTimeoutException || e is ConnectException) {
Response.Builder()
@@ -9,9 +9,9 @@ class DryCocoonAirDetailPagingSource(tlsysid: String) :
override suspend fun fetchData(
pageInfoJsonStr: String,
tlsysid: String
requestData: String
): List<DryCocoonAirDetailListResponse.Data> {
return apiService.getDryCocoonAirDetailList(pageInfoJsonStr, DryCocoonAirDetailListRequest(tlsysid)).data // 返回数据列表
return apiService.getDryCocoonAirDetailList(pageInfoJsonStr, DryCocoonAirDetailListRequest(requestData)).data // 返回数据列表
}
}
@@ -9,9 +9,9 @@ class DryCocoonAirPagingSource(request: DryCocoonAirListRequest) :
override suspend fun fetchData(
pageInfoJsonStr: String,
request: DryCocoonAirListRequest
requestData: DryCocoonAirListRequest
): List<DryCocoonAirListResponse.Data> {
return apiService.getDryCocoonAirList(pageInfoJsonStr, request).data
return apiService.getDryCocoonAirList(pageInfoJsonStr, requestData).data
}
}
@@ -9,9 +9,9 @@ class DryCocoonInDetailPagingSource(rksysid: String) :
override suspend fun fetchData(
pageInfoJsonStr: String,
rksysid: String
requestData: String
): List<DryCocoonInDetailResponse.Data> {
return apiService.getDryCocoonInDetailList(pageInfoJsonStr, rksysid).data // 返回数据列表
return apiService.getDryCocoonInDetailList(pageInfoJsonStr, requestData).data // 返回数据列表
}
}
@@ -10,9 +10,9 @@ class DryCocoonOutDetailPagingSource(rksysid: String) :
override suspend fun fetchData(
pageInfoJsonStr: String,
rksysid: String
requestData: String
): List<DryCocoonOutDetailResponse.Data> {
return apiService.getDryCocoonOutDetailList(pageInfoJsonStr, rksysid).data // 返回数据列表
return apiService.getDryCocoonOutDetailList(pageInfoJsonStr, requestData).data // 返回数据列表
}
}
@@ -9,9 +9,9 @@ class DryCocoonStoreDetailPagingSource(data: DryCocoonStoreDetailListRequest) :
override suspend fun fetchData(
pageInfoJsonStr: String,
data: DryCocoonStoreDetailListRequest,
requestData: DryCocoonStoreDetailListRequest,
): List<DryCocoonStoreDetailListResponse.Data> {
return apiService.getDryCocoonStoreDetailList(pageInfoJsonStr, data.tlsysid,data.like).data // 返回数据列表
return apiService.getDryCocoonStoreDetailList(pageInfoJsonStr, requestData.tlsysid,requestData.like).data // 返回数据列表
}
}