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

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