优化
This commit is contained in:
Generated
+2
-2
@@ -4,10 +4,10 @@
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
<DropdownSelection timestamp="2026-01-09T06:05:40.833191600Z">
|
||||
<DropdownSelection timestamp="2026-02-02T06:44:20.183314400Z">
|
||||
<Target type="DEFAULT_BOOT">
|
||||
<handle>
|
||||
<DeviceId pluginId="Default" identifier="serial=192.168.1.35:5555;connection=d6972001" />
|
||||
<DeviceId pluginId="Default" identifier="serial=192.168.1.32:5555;connection=c1793c60" />
|
||||
</handle>
|
||||
</Target>
|
||||
</DropdownSelection>
|
||||
|
||||
@@ -20,7 +20,7 @@ object GlobalData {
|
||||
/**
|
||||
* 具体业务baseurl
|
||||
*/
|
||||
// var appBaseUrl: String = "http://192.168.1.201:14801"
|
||||
var appBaseUrl2: String = "http://192.168.1.201:14801"
|
||||
var appBaseUrl: String = "https://dev.yixiong-tech.com:8081"
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,6 +24,7 @@ class MyApp : App() {
|
||||
Timber.d("deviceId = $deviceId")
|
||||
// deviceId = "3ea47dc0-3cf0-3c2f-909c-265a9a65572e"
|
||||
GlobalData.deviceId = deviceId
|
||||
GlobalData.appBaseUrl = if ("2987f0c5-5754-33e9-b00a-251db5e2e55f" == deviceId) GlobalData.appBaseUrl2 else GlobalData.appBaseUrl
|
||||
ObjectBox.init(this)
|
||||
// 初始化崩溃处理器
|
||||
CrashHandler.init(this)
|
||||
|
||||
@@ -18,12 +18,14 @@ import com.sw.dualscreen.R
|
||||
import com.sw.dualscreen.databinding.DialogWaitingBinding
|
||||
import com.sw.dualscreen.model.IActivityResult
|
||||
import com.sw.dualscreen.model.response.PostEvent
|
||||
import com.sw.dualscreen.utils.FileUtil
|
||||
import com.sw.dualscreen.view.CustomDialog
|
||||
import com.sw.dualscreen.viewmodel.BaseViewModel
|
||||
import com.sw.plate.utils.ToastUtils
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import timber.log.Timber
|
||||
|
||||
abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
|
||||
@@ -131,6 +133,11 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
|
||||
launchPermissionCallback?.callback(result)
|
||||
}
|
||||
|
||||
fun log(msg: String) {
|
||||
Timber.d(msg)
|
||||
FileUtil.saveLog(msg)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onPostEvent(event: PostEvent) {
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.sw.dualscreen.activity
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.sw.dualscreen.GlobalData
|
||||
import com.sw.dualscreen.R
|
||||
@@ -13,16 +14,12 @@ import com.sw.dualscreen.ext.addOnActionSearchListener
|
||||
import com.sw.dualscreen.ext.gone
|
||||
import com.sw.dualscreen.ext.hideKeyboard
|
||||
import com.sw.dualscreen.ext.visible
|
||||
import com.sw.dualscreen.model.response.ResetBoxEvent
|
||||
import com.sw.dualscreen.objbox.CollectedFoodInfo
|
||||
import com.sw.dualscreen.objbox.Food
|
||||
import com.sw.dualscreen.objbox.ObjectBox
|
||||
import com.sw.dualscreen.viewmodel.BaseViewModel
|
||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
||||
import com.sw.plate.utils.ToastUtils
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
||||
|
||||
@@ -51,8 +48,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
||||
|
||||
override fun initialize() {
|
||||
super.initialize()
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
//binding.root.setOnClickListener { it.hideKeyboard() }
|
||||
binding.root.setOnClickListener { it.hideKeyboard() }
|
||||
binding.rvFoodList.let {
|
||||
it.layoutManager = LinearLayoutManager(this)
|
||||
it.adapter = adapter
|
||||
@@ -103,9 +99,6 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
||||
getCollectGoods()
|
||||
}
|
||||
|
||||
|
||||
private lateinit var box: Box<Food>
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun getCollectGoods(searchName: String? = null) {
|
||||
// if (box == null) {
|
||||
@@ -199,16 +192,13 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
||||
showWaitingDialog("加载中……")
|
||||
viewModel.deleteCollectFood(food.foodId, GlobalData.foodModelVersion) { deleteSuccess ->
|
||||
if (deleteSuccess) {
|
||||
Thread {
|
||||
val filterFoodList = box.all.filter { it.foodName == food.foodName }
|
||||
// Thread {}.start()
|
||||
lifecycleScope.launch {
|
||||
val filterFoodList = ObjectBox.filter(food.foodName)
|
||||
filterFoodList.forEach { it.isDel = true }
|
||||
box.put(filterFoodList)
|
||||
// val filterIdList = filterFoodList.map { it.id }
|
||||
////// ObjectBox.boxStore.runInTx {
|
||||
//// box.removeByIds(filterIdList)
|
||||
////// EventBus.getDefault().post(ResetBoxEvent())
|
||||
////// }
|
||||
|
||||
if (filterFoodList.isNotEmpty()) {
|
||||
ObjectBox.putAll(filterFoodList)
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
hideWaitingDialog()
|
||||
@@ -218,7 +208,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
||||
loadEmptyView()
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.sw.dualscreen.activity
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.sw.dualscreen.GlobalData
|
||||
import com.sw.dualscreen.databinding.ActivityInitBinding
|
||||
import com.sw.dualscreen.model.response.FoodVector
|
||||
@@ -10,13 +11,13 @@ import com.sw.dualscreen.objbox.FoodModule
|
||||
import com.sw.dualscreen.objbox.ObjectBox
|
||||
import com.sw.dualscreen.viewmodel.BaseViewModel
|
||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class InitActivity : BaseActivity<ActivityInitBinding>() {
|
||||
companion object {
|
||||
private const val PAGE_SIZE = 100
|
||||
}
|
||||
|
||||
private val userViewModel by viewModels<UserViewModel>()
|
||||
private var pageNum = 1
|
||||
|
||||
@@ -28,12 +29,14 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
|
||||
return ActivityInitBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
private fun getCollectedFoodVector(block:()-> Unit) {
|
||||
userViewModel.getCollectedFoodVector(param = mutableMapOf(
|
||||
private fun getCollectedFoodVector(block: () -> Unit) {
|
||||
userViewModel.getCollectedFoodVector(
|
||||
param = mutableMapOf(
|
||||
"pageNum" to "$pageNum",
|
||||
"pageSize" to "$PAGE_SIZE",
|
||||
"version" to GlobalData.foodModelVersion
|
||||
)) { items ->
|
||||
)
|
||||
) { items ->
|
||||
val list = items ?: emptyList()
|
||||
if (pageNum == 1 && list.isEmpty()) {
|
||||
block()
|
||||
@@ -51,10 +54,13 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
|
||||
}
|
||||
}
|
||||
}
|
||||
// private val box by lazy { ObjectBox.boxStore.boxFor(Food::class) }
|
||||
|
||||
// private val box by lazy { ObjectBox.boxStore.boxFor(Food::class) }
|
||||
private fun saveFoodVector(list: List<FoodVector>) {
|
||||
lifecycleScope.launch {
|
||||
val vectorList = list.map { vt ->
|
||||
val foodVector = vt.foodVector?.removeSurrounding("[", "]")?.split(",")?.map { it.toFloatOrNull()?:0.0f }?.toFloatArray()
|
||||
val foodVector = vt.foodVector?.removeSurrounding("[", "]")?.split(",")
|
||||
?.map { it.toFloatOrNull() ?: 0.0f }?.toFloatArray()
|
||||
Food(
|
||||
collectId = vt.id,
|
||||
foodId = vt.foodId,
|
||||
@@ -63,14 +69,15 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
|
||||
foodVector = foodVector
|
||||
)
|
||||
}
|
||||
val box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
box.put(vectorList)
|
||||
ObjectBox.putAll(vectorList)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initialize() {
|
||||
super.initialize()
|
||||
showWaitingDialog("加载中……")
|
||||
FoodModule.init(this) {
|
||||
lifecycleScope.launch {
|
||||
FoodModule.init(this@InitActivity) {
|
||||
getCollectedFoodVector {
|
||||
runOnUiThread {
|
||||
hideWaitingDialog()
|
||||
@@ -80,6 +87,7 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// val isSuccess = viewModel.checkEquipmentInfo()
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.sw.dualscreen.activity
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Outline
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.net.Uri
|
||||
@@ -21,11 +22,11 @@ import androidx.camera.core.ImageCaptureException
|
||||
import androidx.camera.core.Preview
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import com.sw.dualscreen.GlobalData
|
||||
import com.sw.dualscreen.GlobalKey
|
||||
import com.sw.dualscreen.adapter.DialogSearchFoodAdapter
|
||||
import com.sw.dualscreen.adapter.MainFoodListAdapter
|
||||
import com.sw.dualscreen.databinding.ActivityMainBinding
|
||||
import com.sw.dualscreen.dialog.RemindDialog
|
||||
@@ -63,22 +64,21 @@ import com.sw.plate.utils.ToastUtils
|
||||
import com.sw.plate.utils.arcface.facedb.FaceDatabase
|
||||
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
|
||||
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import org.json.JSONObject
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
@@ -111,7 +111,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
searchFoodList[index].isChecked = index == position
|
||||
}
|
||||
val item = searchFoodList[position]
|
||||
Timber.d("itemClick ${item.foodName}, position = $position")
|
||||
log("itemClick ${item.foodName}, position = $position")
|
||||
item.photoUri = null
|
||||
checkedItem = item
|
||||
notifyDataSetChanged()
|
||||
@@ -149,7 +149,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
|
||||
override fun initialize() {
|
||||
super.initialize()
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
isPageVisible = true
|
||||
addBackEventListener()
|
||||
initView()
|
||||
@@ -177,6 +176,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
}
|
||||
updateDateTime()
|
||||
binding.ivSetting.setOnClickListener {
|
||||
isRefreshPage = false
|
||||
startActivity(Intent(this, SettingActivity::class.java))
|
||||
}
|
||||
binding.previewView.outlineProvider = object : ViewOutlineProvider() {
|
||||
@@ -221,8 +221,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
}
|
||||
pauseAnalysis()
|
||||
recognizeWeight = lastWeight
|
||||
Timber.tag(TAG)
|
||||
.d("registerDataChange,副屏updateFood调用前耗时:${System.currentTimeMillis() - startTime}")
|
||||
log("registerDataChange,副屏updateFood调用前耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
presentation?.updateFood(foodInfo)
|
||||
binding.tvFoodName.text = foodInfo.foodName
|
||||
@@ -239,7 +238,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
override fun registerDataChange() {
|
||||
super.registerDataChange()
|
||||
SensorScaleUtils.addWeightListener { weight ->
|
||||
Timber.d("registerDataChange weight = $weight")
|
||||
log("registerDataChange weight = $weight")
|
||||
if (isPageVisible.not() || isStartRecognize.not()) {
|
||||
if (ActivityManager.currentActivity() is MainActivity) {
|
||||
isPageVisible = true
|
||||
@@ -247,7 +246,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
}
|
||||
return@addWeightListener
|
||||
}
|
||||
val isWeightChange = weight - lastWeight > 0.05
|
||||
val isWeightChange = abs(weight - lastWeight) > 0.05
|
||||
log("recognizeByWeight---00001,weight=$weight,lastWeight=$lastWeight,isWeightChange=$isWeightChange")
|
||||
recognizeByWeight(weight, isWeightChange)
|
||||
}
|
||||
}
|
||||
@@ -256,16 +256,19 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
presentation?.updateWeight(weight)
|
||||
|
||||
if (weight <= 0.005) {
|
||||
log("recognizeByWeight---00002,未超过5克")
|
||||
////余量取餐,检测到秤上没有东西,重新启动识别菜品 && presentation?.mealPickupMode == 1
|
||||
isRecognitionFood = true
|
||||
return
|
||||
}
|
||||
|
||||
if (isWeightChange && isRecognitionFood) { // 大于50g
|
||||
log("recognizeByWeight---00003,mealPickupMode=${presentation?.mealPickupMode}")
|
||||
if (presentation?.mealPickupMode == 1) {
|
||||
isRecognitionFood = false
|
||||
}
|
||||
debouncer.debounce {
|
||||
log("recognizeByWeight---00004")
|
||||
recognizeFood()
|
||||
}
|
||||
} else {
|
||||
@@ -282,8 +285,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
LightManager.closeRedLight()
|
||||
LightManager.openGreenLight()
|
||||
showWaitingDialog("识别中,请稍后……")
|
||||
takePhoto(
|
||||
successCallback = { photoUri ->
|
||||
takePhoto(successCallback = { photoUri ->
|
||||
Thread {
|
||||
onTakePhotoSuccess(photoUri)
|
||||
}.start()
|
||||
@@ -291,7 +293,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
}, failureCallback = {
|
||||
runOnUiThread {
|
||||
if (failCount >= 10) {
|
||||
//ToastUtils.showToast("相机异常,请稍后重试")
|
||||
ToastUtils.showToast("相机异常,请稍后重试")
|
||||
shutdownCamera()
|
||||
setupCamera()
|
||||
return@runOnUiThread
|
||||
@@ -318,7 +320,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
private fun updateFoodInfo(list: MutableList<FoodInfo>, scoreList: List<IdNameScore>) {
|
||||
val queryData = GsonUtils.toJson(list)
|
||||
val recData = GsonUtils.toJson(scoreList)
|
||||
Timber.d("registerDataChange识别后查询接口数据:$queryData,识别数据:$recData")
|
||||
log("registerDataChange识别后查询接口数据:$queryData,识别数据:$recData")
|
||||
if (list.isEmpty()) {
|
||||
binding.tvToSearch.let {
|
||||
it.text = "未查询到,手动搜索"
|
||||
@@ -359,15 +361,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
}
|
||||
|
||||
private fun setupCamera() {
|
||||
Timber.d("setupCamera")
|
||||
log("setupCamera")
|
||||
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
|
||||
cameraProviderFuture!!.addListener({
|
||||
val cameraProvider = cameraProviderFuture!!.get()
|
||||
|
||||
// 1. 创建预览用例
|
||||
val preview = Preview.Builder()
|
||||
.build()
|
||||
.also {
|
||||
val preview = Preview.Builder().build().also {
|
||||
it.setSurfaceProvider(binding.previewView.surfaceProvider)
|
||||
}
|
||||
|
||||
@@ -375,8 +375,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
imageAnalysis = ImageAnalysis.Builder()
|
||||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||
// .setTargetResolution(Size(640, 480)) // 降低分辨率减轻负担
|
||||
.build()
|
||||
.also { analysis ->
|
||||
.build().also { analysis ->
|
||||
analysis.setAnalyzer(executor) { imageProxy ->
|
||||
// 仅在步骤1时传递
|
||||
if (isAnalyzing) {
|
||||
@@ -389,8 +388,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
}
|
||||
|
||||
// 3. 创建拍照用例
|
||||
imageCapture = ImageCapture.Builder()
|
||||
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
|
||||
imageCapture =
|
||||
ImageCapture.Builder().setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
|
||||
// .setTargetResolution(Size(1920, 1080)) // 设置拍照分辨率
|
||||
.build()
|
||||
|
||||
@@ -405,20 +404,20 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
cameraProvider.bindToLifecycle(
|
||||
this, cameraSelector, preview, imageAnalysis, imageCapture
|
||||
)
|
||||
} catch (exc: Exception) {
|
||||
Timber.e(exc.message)
|
||||
} catch (e: Exception) {
|
||||
log("setupCamera方法异常:${e.message}")
|
||||
}
|
||||
}, ContextCompat.getMainExecutor(this))
|
||||
}
|
||||
|
||||
fun pauseAnalysis() {
|
||||
Timber.d("pauseAnalysis")
|
||||
log("pauseAnalysis")
|
||||
isAnalyzing = false
|
||||
imageAnalysis?.clearAnalyzer()
|
||||
}
|
||||
|
||||
fun resumeAnalysis() {
|
||||
Timber.d("resumeAnalysis isAnalyzing = $isAnalyzing")
|
||||
log("resumeAnalysis isAnalyzing = $isAnalyzing")
|
||||
if (isAnalyzing) return
|
||||
// isAnalyzing = true
|
||||
imageAnalysis?.setAnalyzer(executor) { imageProxy ->
|
||||
@@ -434,7 +433,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
* 停止相机识别
|
||||
*/
|
||||
private fun shutdownCamera() {
|
||||
Timber.d("shutdownCamera")
|
||||
log("shutdownCamera")
|
||||
try {
|
||||
// 1. 首先停止图像分析
|
||||
imageAnalysis?.clearAnalyzer()
|
||||
@@ -449,13 +448,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
// 4. 停止副屏更新
|
||||
isAnalyzing = false
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error shutting down camera")
|
||||
log("Error shutting down camera:${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// 拍照功能
|
||||
private fun takePhoto(successCallback: (Uri) -> Unit, failureCallback: () -> Unit) {
|
||||
Timber.d("takePhoto")
|
||||
log("takePhoto")
|
||||
// 临时停止分析以避免干扰
|
||||
isAnalyzing = false
|
||||
|
||||
@@ -463,21 +462,16 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
val executor = ContextCompat.getMainExecutor(this)
|
||||
val cacheDir = cacheDir
|
||||
val photoFile = File.createTempFile(
|
||||
"IMG_${System.currentTimeMillis()}",
|
||||
".jpg",
|
||||
cacheDir
|
||||
"IMG_${System.currentTimeMillis()}", ".jpg", cacheDir
|
||||
)
|
||||
|
||||
val cacheOutputOptions =
|
||||
ImageCapture.OutputFileOptions.Builder(photoFile).build()
|
||||
val cacheOutputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
|
||||
// 执行拍照
|
||||
imageCapture?.takePicture(
|
||||
cacheOutputOptions,
|
||||
executor,
|
||||
object : ImageCapture.OnImageSavedCallback {
|
||||
cacheOutputOptions, executor, object : ImageCapture.OnImageSavedCallback {
|
||||
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
|
||||
val savedUri = outputFileResults.savedUri
|
||||
Timber.d("takePhoto savedUri = $savedUri")
|
||||
log("takePhoto savedUri = $savedUri")
|
||||
if (savedUri != null) {
|
||||
successCallback(savedUri)
|
||||
}
|
||||
@@ -486,14 +480,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
}
|
||||
|
||||
override fun onError(exception: ImageCaptureException) {
|
||||
Timber.e("拍照失败: ${exception.message}")
|
||||
log("takePhoto拍照失败: ${exception.message}")
|
||||
failureCallback()
|
||||
//ToastUtils.showToast("拍照失败: ${exception.message}")
|
||||
// 恢复图像分析
|
||||
isAnalyzing = true
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
private fun getSecondaryDisplay(): Display? {
|
||||
@@ -522,7 +515,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
).apply {
|
||||
setStepChangeCallback { step ->
|
||||
if (step == 1) {
|
||||
Timber.d("setStepChangeCallback${step}")
|
||||
log("setStepChangeCallback${step}")
|
||||
updateCurrentFood(null)
|
||||
bottomSheetDialog?.dismiss()
|
||||
}
|
||||
@@ -560,12 +553,16 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
} else {
|
||||
//副屏未显示重新加载
|
||||
isRecognitionFood = true
|
||||
log("onResume-00001")
|
||||
if (presentation == null || !presentation!!.isShowing) {
|
||||
log("onResume-00002,副屏未显示,加载副屏")
|
||||
lastWeight = 0.0
|
||||
setupSecondaryDisplay()
|
||||
presentation?.step1FoodRecognizing()
|
||||
} else {
|
||||
log("onResume-00003,副屏显示,更新副屏, isRefreshPage=$isRefreshPage")
|
||||
if (isRefreshPage) {
|
||||
isRefreshPage = false
|
||||
//已显示副屏,则进行更新
|
||||
val weight = lastWeight
|
||||
lastWeight = 0.0
|
||||
@@ -576,7 +573,6 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
recognizeByWeight(weight, true)
|
||||
}, 500)
|
||||
}
|
||||
isRefreshPage = !isRefreshPage
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,7 +623,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
notEnoughOneBlock: () -> Unit = {},
|
||||
block: () -> Unit
|
||||
) {
|
||||
Timber.tag(TAG).d("foodWeight=$foodWeight,eatWeight=$eatWeight")
|
||||
log("foodWeight=$foodWeight,eatWeight=$eatWeight")
|
||||
val pickupMode = SPUtil.getInstance().get(GlobalKey.KEY_PICKUP_MODE, 0) ?: 0
|
||||
// if (pickupMode == 0) {
|
||||
// if (foodWeight <= 5) {
|
||||
@@ -735,10 +731,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
|
||||
private var isRefreshPage = false
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onClickBackEvent(event: ClickBackEvent) {
|
||||
isRefreshPage = false
|
||||
}
|
||||
// @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
// fun onClickBackEvent(event: ClickBackEvent) {
|
||||
// isRefreshPage = false
|
||||
// }
|
||||
|
||||
private fun clickPayButton() {
|
||||
if (checkedItem == null) {
|
||||
@@ -761,7 +757,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
//余量计量
|
||||
eatWeight = (recognizeWeight - lastWeight) * 1000
|
||||
foodWeight = recognizeWeight * 1000
|
||||
Timber.d("eatWeight=$eatWeight, recognizeWeight=$recognizeWeight, lastWeight=$lastWeight")
|
||||
log("eatWeight=$eatWeight, recognizeWeight=$recognizeWeight, lastWeight=$lastWeight")
|
||||
}
|
||||
} else {
|
||||
//不计费
|
||||
@@ -782,7 +778,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
eatNum = eatNum,
|
||||
userId = null,
|
||||
) {
|
||||
// TODO: 暂不关闭首页副屏
|
||||
isRefreshPage = false
|
||||
//presentation?.dismiss()
|
||||
startActivity(Intent(this, PayActivity::class.java).apply {
|
||||
putExtra(PayActivity.FOOD_INFO, checkedItem!!)
|
||||
@@ -814,13 +810,10 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
* 上传菜品信息
|
||||
*/
|
||||
private suspend fun uploadCollectFoodPics(
|
||||
foodInfo: FoodInfo,
|
||||
imageFile: File?,
|
||||
imageVector: FloatArray?
|
||||
foodInfo: FoodInfo, imageFile: File?, imageVector: FloatArray?
|
||||
) {
|
||||
if (imageFile == null || imageVector == null) {
|
||||
Timber.tag(TAG)
|
||||
.d("是否null判断,imageFile == null:${imageFile == null},imageVector == null:${imageVector == null}")
|
||||
log("是否null判断,imageFile == null:${imageFile == null},imageVector == null:${imageVector == null}")
|
||||
return
|
||||
}
|
||||
val foodId = foodInfo.foodId
|
||||
@@ -834,30 +827,28 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
val vectors = listOf(imageVector)
|
||||
val foodVectorList = vectors.map {
|
||||
it.joinToString(
|
||||
separator = ",",
|
||||
prefix = "[",
|
||||
postfix = "]"
|
||||
separator = ",", prefix = "[", postfix = "]"
|
||||
)
|
||||
}
|
||||
val foodVectorJson =
|
||||
foodVectorList.joinToString(separator = ",", prefix = "[", postfix = "]")
|
||||
Timber.tag(TAG).d("json=$foodVectorJson")
|
||||
log("json=$foodVectorJson")
|
||||
params["foodVector"] = foodVectorJson.toRequestBody()
|
||||
val idList = viewModel.uploadCollectFoodPics(files, params)
|
||||
if (idList.isNullOrEmpty()) {
|
||||
loadRemindDialog("上传失败,未返回向量数据id")
|
||||
Timber.tag(TAG).d("idList为空")
|
||||
log("idList为空")
|
||||
return
|
||||
}
|
||||
Timber.tag(TAG)
|
||||
.d("uploadCollectFoodPics-已采集向量总数:${box.all.filter { it.isDel.not() }.size}条")
|
||||
val filterList = box.all.filter { it.collectId == idList[0] }
|
||||
val all = ObjectBox.getAll()
|
||||
val size = all.filter { it.isDel.not() }.size
|
||||
log("uploadCollectFoodPics-已采集向量总数:${size}条")
|
||||
val filterList = all.filter { it.collectId == idList[0] }
|
||||
if (filterList.isNotEmpty()) {
|
||||
loadRemindDialog("返回的id:${idList[0]}已存在")
|
||||
return
|
||||
}
|
||||
Thread {
|
||||
box.put(
|
||||
ObjectBox.put(
|
||||
Food(
|
||||
collectId = idList[0],
|
||||
foodId = foodId,
|
||||
@@ -866,21 +857,15 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
version = foodModelVersion
|
||||
)
|
||||
)
|
||||
}.start()
|
||||
}
|
||||
|
||||
private fun loadRemindDialog(msg: String) {
|
||||
runOnUiThread {
|
||||
RemindDialog(
|
||||
context = context,
|
||||
content = msg,
|
||||
confirmBlock = {}
|
||||
).show()
|
||||
context = context, content = msg, confirmBlock = {}).show()
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var box: Box<Food>
|
||||
|
||||
private fun uri2File(uri: Uri, block: (File?, FloatArray?) -> Unit) {
|
||||
ImageUtil.uriToBitmap(this, uri)?.let { bitmap ->
|
||||
val imageVector = try {
|
||||
@@ -892,7 +877,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
val imageFile = BitmapSaver.saveToAppFilesDir(
|
||||
bitmap, this, "IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||
)
|
||||
Timber.d("${this.javaClass.simpleName}-cameraCallback-裁剪bitmap保存文件路径:${imageFile?.absolutePath}")
|
||||
log("${this.javaClass.simpleName}-cameraCallback-裁剪bitmap保存文件路径:${imageFile?.absolutePath}")
|
||||
block(imageFile, imageVector)
|
||||
if (bitmap.isRecycled.not()) {
|
||||
bitmap.recycle()
|
||||
@@ -913,8 +898,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
|
||||
// private val initialDelay = 5 * 60 * 1000L
|
||||
// private val dealyMillis = 10 * 60 * 1000L
|
||||
private val initialDelay = 60 * 1000L
|
||||
private val dealyMillis = 30 * 1000L
|
||||
private val initialDelay = 30 * 1000L
|
||||
private val dealyMillis = 60 * 1000L
|
||||
private var taskPageNo = 1
|
||||
fun startFaceTask() {
|
||||
faceTaskJob =
|
||||
@@ -936,8 +921,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
}
|
||||
updateFaceData(list)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -952,9 +936,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
} else {
|
||||
//保存数据
|
||||
val faceEntity = FaceEntity(
|
||||
model.userId,
|
||||
null,
|
||||
Base64.decode(model.faceFeatureStr)
|
||||
model.userId, null, Base64.decode(model.faceFeatureStr)
|
||||
).also {
|
||||
it.userType = "1"
|
||||
}
|
||||
@@ -988,34 +970,42 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
|
||||
private fun onTakePhotoSuccess(photoUri: Uri) {
|
||||
lastPhotoUri = photoUri
|
||||
Timber.tag(TAG).d("main,takePhoto耗时:${System.currentTimeMillis() - startTime}")
|
||||
log("registerDataChange main,takePhoto耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
Timber.d("registerDataChange photoUri = ${photoUri.path}")
|
||||
log("registerDataChange photoUri = ${photoUri.path}")
|
||||
// viewModel.getIdentifiedFoodList()
|
||||
ImageUtil.uriToBitmap(this, photoUri)?.let { bitmap ->
|
||||
Timber.tag(TAG).d("main,uriToBitmap耗时:${System.currentTimeMillis() - startTime}")
|
||||
lifecycleScope.launch {
|
||||
ImageUtil.uriToBitmap(this@MainActivity, photoUri)?.let { bitmap ->
|
||||
queryFoodData(bitmap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun queryFoodData(bitmap: Bitmap) {
|
||||
log("registerDataChange main,uriToBitmap耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
Timber.d("registerDataChange photoUri 拿到bitmap")
|
||||
log("registerDataChange photoUri 拿到bitmap")
|
||||
// val bmp = BitmapCropper.cropCenter(bitmap, 1300, 900)
|
||||
Timber.d("registerDataChange photoUri bitmap裁剪完成")
|
||||
log("registerDataChange registerDataChange photoUri bitmap裁剪完成")
|
||||
val file = BitmapSaver.saveToAppFilesDir(
|
||||
bitmap, this, "IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||
)
|
||||
Timber.tag(TAG)
|
||||
.d("main,saveToAppFilesDir耗时:${System.currentTimeMillis() - startTime}")
|
||||
log("registerDataChange main,saveToAppFilesDir耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
Timber.d("registerDataChange photoUri bitmap保存文件路径:${file?.absolutePath}")
|
||||
log("registerDataChange photoUri bitmap保存文件路径:${file?.absolutePath}")
|
||||
//val nameList = FoodModule.queryFood(bitmap)
|
||||
val scoreList = FoodModule.getFoodScoreList(bitmap)
|
||||
val foodName = if (scoreList.isNotEmpty()) {
|
||||
val recDataJson = GsonUtils.toJson(scoreList)
|
||||
Timber.tag(TAG).d("main,getFoodScoreList耗时:${System.currentTimeMillis() - startTime}")
|
||||
log("registerDataChange main,getFoodScoreList耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
Timber.d("registerDataChange photoUri 拿到识别数据:$recDataJson")
|
||||
log("registerDataChange photoUri 拿到识别数据:$recDataJson")
|
||||
val nameList = scoreList.map { it.name }
|
||||
val foodName = nameList.joinToString(separator = ",")
|
||||
nameList.joinToString(separator = ",")
|
||||
} else ""
|
||||
|
||||
hideWaitingDialog()
|
||||
Timber.d("registerDataChange photoUri 识别数据名称:$foodName")
|
||||
log("registerDataChange photoUri 识别数据名称:$foodName")
|
||||
runOnUiThread {
|
||||
binding.flPay.run {
|
||||
val chargeMode = SPUtil.getInstance().get(GlobalKey.KEY_CHARGE_MODE, 0)
|
||||
@@ -1040,15 +1030,12 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
viewModel.getFoodInfo(foodName) { list ->
|
||||
runOnUiThread {
|
||||
hideWaitingDialog()
|
||||
Timber.tag(TAG)
|
||||
.d("main,getFoodInfo耗时:${System.currentTimeMillis() - startTime}")
|
||||
log("registerDataChange main,getFoodInfo耗时:${System.currentTimeMillis() - startTime}")
|
||||
startTime = System.currentTimeMillis()
|
||||
updateFoodInfo(list.toMutableList(), scoreList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// @Subscribe(threadMode = ThreadMode.MAIN)
|
||||
|
||||
@@ -34,9 +34,9 @@ class SettingActivity : BaseActivity<ActivitySettingBinding>() {
|
||||
initBackDispatcher()
|
||||
binding.include.let {
|
||||
it.ivPageBack.setOnClickListener {
|
||||
// val intent = Intent(this, MainActivity::class.java)
|
||||
// startActivity(intent)
|
||||
EventBus.getDefault().post(ClickBackEvent())
|
||||
//// val intent = Intent(this, MainActivity::class.java)
|
||||
//// startActivity(intent)
|
||||
// EventBus.getDefault().post(ClickBackEvent())
|
||||
finish()
|
||||
}
|
||||
it.tvPageTitle.text = "设置"
|
||||
@@ -73,13 +73,13 @@ class SettingActivity : BaseActivity<ActivitySettingBinding>() {
|
||||
private lateinit var backPressedCallback: OnBackPressedCallback
|
||||
private fun initBackDispatcher() {
|
||||
// 创建回调,true表示初始启用状态
|
||||
backPressedCallback = object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
EventBus.getDefault().post(ClickBackEvent())
|
||||
}
|
||||
}
|
||||
// 注册回调,使用lifecycleOwner确保生命周期安全
|
||||
onBackPressedDispatcher.addCallback(this, backPressedCallback)
|
||||
// backPressedCallback = object : OnBackPressedCallback(true) {
|
||||
// override fun handleOnBackPressed() {
|
||||
//// EventBus.getDefault().post(ClickBackEvent())
|
||||
// }
|
||||
// }
|
||||
// // 注册回调,使用lifecycleOwner确保生命周期安全
|
||||
// onBackPressedDispatcher.addCallback(this, backPressedCallback)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import com.sw.dualscreen.activity.CollectedFoodActivity
|
||||
import com.sw.dualscreen.activity.SettingActivity
|
||||
import com.sw.dualscreen.adapter.CollectFoodListAdapter
|
||||
import com.sw.dualscreen.adapter.FoodCollectionAdapter
|
||||
import com.sw.dualscreen.adapter.DialogSearchFoodAdapter
|
||||
import com.sw.dualscreen.databinding.FragmentCollectBinding
|
||||
import com.sw.dualscreen.databinding.LayoutCameraPreviewBinding
|
||||
import com.sw.dualscreen.ext.clickWithDebounce
|
||||
@@ -35,14 +34,11 @@ import com.sw.dualscreen.utils.Debouncer
|
||||
import com.sw.dualscreen.utils.ImageUploader
|
||||
import com.sw.dualscreen.utils.ImageUtil
|
||||
import com.sw.plate.utils.ToastUtils
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import timber.log.Timber
|
||||
import kotlin.getValue
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
@@ -55,13 +51,6 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
private var selectedFoodId: String? = ""
|
||||
private var selectedFoodName: String? = ""
|
||||
|
||||
private var box: Box<Food>? = null
|
||||
fun initBox() {
|
||||
if (box == null) {
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
}
|
||||
}
|
||||
|
||||
private val foodCollectionList = mutableListOf<FoodCollectionBean>().apply {
|
||||
repeat(MAX_COUNT) {
|
||||
add(FoodCollectionBean(isShowCamera = true))
|
||||
@@ -138,6 +127,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
e.printStackTrace()
|
||||
settingActivity?.hideWaitingDialog()
|
||||
ToastUtils.showToast("程序异常${e.message}")
|
||||
settingActivity?.log("程序异常${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,13 +142,14 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
ToastUtils.showToast("操作失败")
|
||||
settingActivity?.log("操作失败:${e.message}")
|
||||
settingActivity?.hideWaitingDialog()
|
||||
return
|
||||
}
|
||||
val file = BitmapSaver.saveToAppFilesDir(
|
||||
bitmap, requireActivity(), "IMG_CROP_${System.currentTimeMillis()}.jpg"
|
||||
)
|
||||
Timber.d("${this.javaClass.simpleName}-cameraCallback-裁剪bitmap保存文件路径:${file?.absolutePath}")
|
||||
settingActivity?.log("${this.javaClass.simpleName}-cameraCallback-裁剪bitmap保存文件路径:${file?.absolutePath}")
|
||||
|
||||
activity?.runOnUiThread {
|
||||
foodCollectionList[index].let {
|
||||
@@ -187,6 +178,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
settingActivity?.hideWaitingDialog()
|
||||
if (cameraErrorCount >= 10) {
|
||||
ToastUtils.showToast("拍照异常,请重新操作")
|
||||
settingActivity?.log("拍照异常,请重新操作:$errMsg")
|
||||
cameraErrorCount = 0
|
||||
return@takePhoto
|
||||
}
|
||||
@@ -305,8 +297,7 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
||||
version = GlobalData.foodModelVersion
|
||||
)
|
||||
}
|
||||
initBox()
|
||||
box?.put(foodList)
|
||||
ObjectBox.putAll(foodList)
|
||||
activity?.runOnUiThread {
|
||||
batch.forEach { it.isFinish = true }
|
||||
collectionAdapter.notifyDataSetChanged()
|
||||
|
||||
@@ -54,27 +54,29 @@ class CashPayFragment : BaseFragment<FragmentCashPayBinding>() {
|
||||
override fun onHiddenChanged(hidden: Boolean) {
|
||||
super.onHiddenChanged(hidden)
|
||||
if (hidden) {
|
||||
delayDismiss()
|
||||
dismissSubScreen()
|
||||
return
|
||||
}
|
||||
//showSubScreen()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
delayDismiss()
|
||||
dismissSubScreen()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun delayDismiss() {
|
||||
presentation?.dismiss()
|
||||
//binding.root.postDelayed({
|
||||
// presentation?.dismiss()
|
||||
//}, 100)
|
||||
private fun dismissSubScreen() {
|
||||
presentation?.let {
|
||||
if (it.isShowing) {
|
||||
it.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSubScreen() {
|
||||
// 查找副屏(通常索引为1)
|
||||
if (displays.size > 1) {
|
||||
dismissSubScreen()
|
||||
presentation = CashPayPresentation(activity = payActivity, display = displays[1]) {
|
||||
presentation?.dismiss()
|
||||
}.also {
|
||||
|
||||
@@ -24,7 +24,7 @@ class FacePayFragment : BaseFragment<FragmentFacePayBinding>() {
|
||||
// }
|
||||
payActivity.foodInfo?.let {
|
||||
foodName = it.foodName
|
||||
payAmount = (it.vipPrice?:0.0) * payActivity.eatNum
|
||||
payAmount = (it.vipPrice ?: 0.0) * payActivity.eatNum
|
||||
}
|
||||
showSubScreen()
|
||||
}
|
||||
@@ -32,22 +32,23 @@ class FacePayFragment : BaseFragment<FragmentFacePayBinding>() {
|
||||
override fun onHiddenChanged(hidden: Boolean) {
|
||||
super.onHiddenChanged(hidden)
|
||||
if (hidden) {
|
||||
delayDismiss()
|
||||
dismissSubScreen()
|
||||
return
|
||||
}
|
||||
//showSubScreen()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
delayDismiss()
|
||||
dismissSubScreen()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun delayDismiss() {
|
||||
presentation?.dismiss()
|
||||
//binding.root.postDelayed({
|
||||
// presentation?.dismiss()
|
||||
//}, 100)
|
||||
private fun dismissSubScreen() {
|
||||
presentation?.let {
|
||||
if (it.isShowing) {
|
||||
it.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadBitmap(frame: Bitmap) {
|
||||
@@ -57,6 +58,7 @@ class FacePayFragment : BaseFragment<FragmentFacePayBinding>() {
|
||||
private fun showSubScreen() {
|
||||
// 查找副屏(通常索引为1)
|
||||
if (displays.size > 1) {
|
||||
dismissSubScreen()
|
||||
presentation = FacePayPresentation(
|
||||
activity = payActivity,
|
||||
display = displays[1],
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package com.sw.dualscreen.activity.fragment.pay
|
||||
|
||||
import com.sw.dualscreen.GlobalKey
|
||||
import com.sw.dualscreen.activity.PayActivity
|
||||
import com.sw.dualscreen.activity.fragment.BaseFragment
|
||||
import com.sw.dualscreen.databinding.FragmentNumberPayBinding
|
||||
import com.sw.dualscreen.ext.format2String
|
||||
import com.sw.dualscreen.ext.hideKeyboard
|
||||
import com.sw.dualscreen.model.response.MemberInfo
|
||||
import com.sw.dualscreen.presentation.pay.CashPayPresentation
|
||||
import com.sw.dualscreen.utils.SPUtil
|
||||
import com.sw.plate.utils.ToastUtils
|
||||
|
||||
class NumberPayFragment : BaseFragment<FragmentNumberPayBinding>() {
|
||||
@@ -85,27 +82,29 @@ class NumberPayFragment : BaseFragment<FragmentNumberPayBinding>() {
|
||||
override fun onHiddenChanged(hidden: Boolean) {
|
||||
super.onHiddenChanged(hidden)
|
||||
if (hidden) {
|
||||
delayDismiss()
|
||||
dismissSubScreen()
|
||||
return
|
||||
}
|
||||
//showSubScreen()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
delayDismiss()
|
||||
dismissSubScreen()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun delayDismiss() {
|
||||
presentation?.dismiss()
|
||||
//binding.root.postDelayed({
|
||||
// presentation?.dismiss()
|
||||
//}, 100)
|
||||
private fun dismissSubScreen() {
|
||||
presentation?.let {
|
||||
if (it.isShowing) {
|
||||
it.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSubScreen() {
|
||||
// 查找副屏(通常索引为1)
|
||||
if (displays.size > 1) {
|
||||
dismissSubScreen()
|
||||
presentation = CashPayPresentation(activity = payActivity, display = displays[1]) {
|
||||
presentation?.dismiss()
|
||||
}.also {
|
||||
|
||||
@@ -283,7 +283,7 @@ class PayResultFragment : BaseFragment<FragmentPayResultBinding>() {
|
||||
private val intervalExecutor by lazy { IntervalExecutor() }
|
||||
private var payTaskJob: Job? = null
|
||||
fun paySuccessCallback(callback: () -> Unit) {
|
||||
payTaskJob = intervalExecutor.startIntervalTaskWithInitialDelay(5000, 10000) {
|
||||
payTaskJob = intervalExecutor.startIntervalTaskWithInitialDelay(200, 500) {
|
||||
payActivity?.queryOrderState { paySuccess ->
|
||||
if (paySuccess) {
|
||||
callback()
|
||||
|
||||
@@ -5,7 +5,6 @@ import android.text.SpannedString
|
||||
import android.text.style.AbsoluteSizeSpan
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.sw.dualscreen.R
|
||||
import com.sw.dualscreen.activity.PayActivity
|
||||
import com.sw.dualscreen.activity.fragment.BaseFragment
|
||||
import com.sw.dualscreen.ext.load
|
||||
@@ -62,29 +61,31 @@ class ScanQrCodePayFragment : BaseFragment<FragmentScanQrcodePayBinding>() {
|
||||
override fun onHiddenChanged(hidden: Boolean) {
|
||||
super.onHiddenChanged(hidden)
|
||||
if (hidden) {
|
||||
delayDismiss()
|
||||
dismissSubScreen()
|
||||
return
|
||||
}
|
||||
//showSubScreen()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
delayDismiss()
|
||||
dismissSubScreen()
|
||||
countDownJob?.cancel() // 自动取消订阅,防止内存泄漏
|
||||
payTaskJob?.cancel()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun delayDismiss() {
|
||||
presentation?.dismiss()
|
||||
//binding.root.postDelayed({
|
||||
// presentation?.dismiss()
|
||||
//}, 100)
|
||||
private fun dismissSubScreen() {
|
||||
presentation?.let {
|
||||
if (it.isShowing) {
|
||||
it.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSubScreen() {
|
||||
// 查找副屏(通常索引为1)
|
||||
if (displays.size > 1) {
|
||||
dismissSubScreen()
|
||||
presentation =
|
||||
ScanQrCodePayPresentation(activity = payActivity, type = 0, display = displays[1]) {
|
||||
presentation?.dismiss()
|
||||
@@ -123,7 +124,7 @@ class ScanQrCodePayFragment : BaseFragment<FragmentScanQrcodePayBinding>() {
|
||||
private val intervalExecutor by lazy { IntervalExecutor() }
|
||||
private var payTaskJob: Job? = null
|
||||
fun paySuccessCallback(isVip: Boolean) {
|
||||
payTaskJob = intervalExecutor.startIntervalTaskWithInitialDelay(5000, 10000) {
|
||||
payTaskJob = intervalExecutor.startIntervalTaskWithInitialDelay(200, 500) {
|
||||
payActivity.queryOrderState { paySuccess ->
|
||||
if (paySuccess) {
|
||||
payActivity.showPaySuccess(isVip)
|
||||
|
||||
@@ -11,6 +11,8 @@ import com.sw.dualscreen.utils.ImageUtil
|
||||
import com.sw.plate.utils.ToastUtils
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.pytorch.IValue
|
||||
import org.pytorch.Module
|
||||
import org.pytorch.torchvision.TensorImageUtils
|
||||
@@ -25,8 +27,7 @@ object FoodModule {
|
||||
private const val THRESHOLD = 0.8
|
||||
|
||||
// private const val THRESHOLD = 0.0
|
||||
private var module: Module?=null
|
||||
private var box: Box<Food>?=null
|
||||
private var module: Module? = null
|
||||
|
||||
// private lateinit var embeddingsList: List<List<Float>>
|
||||
// private lateinit var labelsList: IntArray
|
||||
@@ -40,25 +41,21 @@ object FoodModule {
|
||||
private const val MODEL_INPUT_HEIGHT = 224
|
||||
|
||||
@SuppressLint("SuspiciousIndentation")
|
||||
fun init(context: Context, block: () -> Unit = {}) {
|
||||
Thread {
|
||||
suspend fun init(context: Context, block: () -> Unit = {}) {
|
||||
// Thread {}.start()
|
||||
withContext(Dispatchers.IO) {
|
||||
val modelPath = copyAssetToCache(context, "best_embedding_model_mobile.pt")
|
||||
// ToastUtils.showToast("modelPath==null:${modelPath == null}")
|
||||
module = Module.load(modelPath)
|
||||
// ToastUtils.showToast("module==null:${module == null}")
|
||||
box = ObjectBox.boxStore.boxFor(Food::class)
|
||||
//初始化默认重新拉取数据,先清空本地数据
|
||||
// ObjectBox.boxStore.runInTx {
|
||||
box?.run {
|
||||
if (all.isNotEmpty()) {
|
||||
removeAll()
|
||||
}
|
||||
val list = ObjectBox.getAll()
|
||||
if (list.isNotEmpty()) {
|
||||
ObjectBox.removeAll()
|
||||
}
|
||||
//if (box.all.isEmpty()) {
|
||||
// initDefFoodData(context)
|
||||
//}
|
||||
block()
|
||||
}.start()
|
||||
}
|
||||
}
|
||||
|
||||
// fun uri2FloatArray(uri: Uri): FloatArray? {
|
||||
@@ -125,24 +122,21 @@ object FoodModule {
|
||||
// return queryFoodNameScore(floatArray, queryCount)
|
||||
// }
|
||||
|
||||
fun queryFoodNameScore(floatArray: FloatArray?, queryCount: Int = 15): List<IdNameScore> {
|
||||
suspend fun queryFoodNameScore(
|
||||
floatArray: FloatArray?,
|
||||
queryCount: Int = 15
|
||||
): List<IdNameScore> {
|
||||
if (floatArray == null) return emptyList()
|
||||
val startTime = System.currentTimeMillis()
|
||||
Timber.tag(TAG)
|
||||
.d("queryFoodNameScore-已采集向量总数:${box?.all?.filter { it.isDel.not() }?.size}")
|
||||
//box.store.runInTx { }
|
||||
val query = box?.query()
|
||||
?.equal(Food_.isDel, false)
|
||||
?.and()
|
||||
?.nearestNeighbors(Food_.foodVector, floatArray, queryCount)
|
||||
?.build()
|
||||
val size = ObjectBox.getAll().filter { it.isDel.not() }.size
|
||||
Timber.tag(TAG).d("queryFoodNameScore-已采集向量总数:${size}")
|
||||
//查询比较分数
|
||||
// val tempList = query.findWithScores().sortedBy { it.score }.map { "${it.get().name}|${it.get().foodIdx}|${it.score}" }
|
||||
// val idScoreList = query.findIdsWithScores()
|
||||
val objScoreList = query?.findWithScores()
|
||||
val objScoreList = ObjectBox.query(floatArray, queryCount)
|
||||
Timber.tag(TAG).d("idScoreList:${GsonUtils.toJson(objScoreList)}")
|
||||
val nameScoreList = mutableListOf<IdNameScore>()
|
||||
objScoreList?.forEach {
|
||||
objScoreList.forEach {
|
||||
val food = it.get()
|
||||
nameScoreList.add(
|
||||
IdNameScore(
|
||||
@@ -153,13 +147,11 @@ object FoodModule {
|
||||
)
|
||||
}
|
||||
val nameScoreData = GsonUtils.toJson(nameScoreList)
|
||||
Timber.tag(TAG)
|
||||
.d("queryFood,耗时:${System.currentTimeMillis() - startTime},数据:$nameScoreData")
|
||||
query?.close()
|
||||
Timber.tag(TAG).d("queryFood,耗时:${System.currentTimeMillis() - startTime},数据:$nameScoreData")
|
||||
return nameScoreList
|
||||
}
|
||||
|
||||
fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
||||
suspend fun getFoodScoreList(bitmap: Bitmap, queryCount: Int = 15): List<IdNameScore> {
|
||||
val startTime = System.currentTimeMillis()
|
||||
val floatArray = bitmap2FloatArray(bitmap, false)
|
||||
Timber.tag(TAG).d("bitmap2FloatArray,耗时:${System.currentTimeMillis() - startTime}")
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
package com.sw.dualscreen.objbox
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import com.sw.plate.App
|
||||
import io.objectbox.Box
|
||||
import io.objectbox.BoxStore
|
||||
import io.objectbox.BoxStoreBuilder
|
||||
@@ -26,12 +28,22 @@ import io.objectbox.android.ObjectBoxLiveData
|
||||
import io.objectbox.config.DebugFlags
|
||||
import io.objectbox.exception.DbException
|
||||
import io.objectbox.exception.FileCorruptException
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import io.objectbox.query.Query
|
||||
import io.objectbox.sync.Sync
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.util.Date
|
||||
import java.util.zip.GZIPOutputStream
|
||||
import kotlin.also
|
||||
import kotlin.and
|
||||
import kotlin.io.copyTo
|
||||
import kotlin.io.inputStream
|
||||
import kotlin.io.outputStream
|
||||
@@ -52,8 +64,7 @@ object ObjectBox {
|
||||
|
||||
private const val TAG = "ObjectBox"
|
||||
|
||||
lateinit var boxStore: BoxStore
|
||||
private set
|
||||
var boxStore: BoxStore? = null
|
||||
|
||||
/**
|
||||
* If building the [boxStore] failed, contains the thrown error message.
|
||||
@@ -114,42 +125,166 @@ object ObjectBox {
|
||||
return true
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 安全获取 Food 实体 foodVector 字段的向量索引(推荐写法)
|
||||
// */
|
||||
// fun getFoodVectorIndex(box: Box<Food>): VectorIndex? {
|
||||
// return try {
|
||||
// // 核心修复:使用自动生成的 Food_.foodVector 而非字符串,避免拼写错误
|
||||
// val foodVectorProperty = Food_.foodVector
|
||||
// // 获取向量索引(适配 ObjectBox 3.5+ 所有版本)
|
||||
// foodVectorProperty.vectorIndex
|
||||
// } catch (e: Exception) {
|
||||
// // 容错处理:打印错误信息,避免崩溃
|
||||
// e.printStackTrace()
|
||||
// null
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 刷新索引的调用示例(结合你之前的更新场景)
|
||||
// fun refreshFoodVectorIndex(box: Box<Food>) {
|
||||
// val vectorIndex = getFoodVectorIndex(box)
|
||||
// vectorIndex?.run {
|
||||
// // 同步刷新索引(解决查询异常问题)
|
||||
// refresh()
|
||||
// // 等待索引构建完成(超时 5 秒,避免无限等待)
|
||||
// waitUntilBuilt(5000)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 更新 isDel 字段后的完整调用流程
|
||||
// fun markFoodAsDeleted(box: Box<Food>, foodId: Long) {
|
||||
// val food = box[foodId]
|
||||
// if (food != null) {
|
||||
// food.isDel = true
|
||||
// box.put(food)
|
||||
// // 更新后刷新索引
|
||||
// refreshFoodVectorIndex(box)
|
||||
// }
|
||||
// }
|
||||
|
||||
// 获取指定实体的Box,自动关联BoxStore
|
||||
inline fun <reified T> getBox(): Box<T>? = boxStore?.boxFor()
|
||||
|
||||
// 协程安全执行数据库操作(推荐所有操作使用此方法)
|
||||
suspend fun <T> safeDbOp(operation: suspend () -> T): T? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
dbMutex.withLock { operation() }
|
||||
} catch (e: FileCorruptException) {
|
||||
// 操作中触发损坏,尝试重建数据库
|
||||
//val context = boxStore.context
|
||||
//deleteDbFiles(context)
|
||||
init(App.getContext()!!)
|
||||
null
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun query(floatArray: FloatArray, queryCount: Int) = safeDbOp {
|
||||
val query: Query<Food>? = getBox<Food>()?.query()
|
||||
?.equal(Food_.isDel, false)
|
||||
?.and()
|
||||
?.nearestNeighbors(Food_.foodVector, floatArray, queryCount)
|
||||
?.build()
|
||||
try {
|
||||
query?.findWithScores()
|
||||
} finally {
|
||||
// 先关闭Query,释放Cursor
|
||||
query?.close()
|
||||
}
|
||||
} ?: emptyList()
|
||||
|
||||
suspend fun get(id: Long) = safeDbOp {
|
||||
getBox<Food>()?.get(id)
|
||||
}
|
||||
|
||||
suspend fun put(entity: Food) = safeDbOp {
|
||||
getBox<Food>()?.put(entity)
|
||||
}
|
||||
|
||||
suspend fun putAll(entities: List<Food>) = safeDbOp {
|
||||
getBox<Food>()?.put(entities)
|
||||
}
|
||||
|
||||
suspend fun getAll() = safeDbOp {
|
||||
getBox<Food>()?.all
|
||||
} ?: emptyList()
|
||||
|
||||
suspend fun filter(name: String?, isDistinct: Boolean = false) = safeDbOp {
|
||||
val list = if (isDistinct) {
|
||||
getBox<Food>()?.all?.distinctBy { it.foodName }
|
||||
} else {
|
||||
getBox<Food>()?.all
|
||||
}
|
||||
if (name.isNullOrBlank().not()) {
|
||||
list?.filter { it.foodName?.contains(name) == true }
|
||||
}
|
||||
list
|
||||
} ?: emptyList()
|
||||
|
||||
suspend fun removeAll() = safeDbOp {
|
||||
getBox<Food>()?.removeAll()
|
||||
}
|
||||
|
||||
suspend fun remove(name: String) = safeDbOp {
|
||||
getBox<Food>()?.run {
|
||||
val filterIdList = all.filter { it.foodName == name }.map { it.id }
|
||||
removeByIds(filterIdList)
|
||||
}
|
||||
}
|
||||
|
||||
private val dbMutex = Mutex() // 协程并发锁,保证写入操作原子性
|
||||
private const val DB_DIR_NAME = "objectbox" // ObjectBox 默认数据库目录
|
||||
private const val BACKUP_DIR_NAME = "objectbox_backup" // 备份目录
|
||||
|
||||
// 检查存储是否可读写(操作数据库前调用)
|
||||
fun isStorageAvailable(context: Context): Boolean {
|
||||
return try {
|
||||
val state = Environment.getExternalStorageState()
|
||||
val innerDir = context.filesDir
|
||||
Environment.MEDIA_MOUNTED == state && innerDir.canRead() && innerDir.canWrite()
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 备份数据库到应用私有目录(无权限要求,推荐)
|
||||
suspend fun backupDb(context: Context): Boolean = withContext(Dispatchers.IO) {
|
||||
if (!isStorageAvailable(context)) return@withContext false
|
||||
val dbDir = File(context.filesDir, DB_DIR_NAME)
|
||||
val backupDir = File(context.filesDir, BACKUP_DIR_NAME)
|
||||
return@withContext copyDir(dbDir, backupDir)
|
||||
}
|
||||
|
||||
// 从备份恢复数据库(恢复后会重建BoxStore)
|
||||
suspend fun restoreDb(context: Context): Boolean = withContext(Dispatchers.IO) {
|
||||
if (!isStorageAvailable(context)) return@withContext false
|
||||
val dbDir = File(context.filesDir, DB_DIR_NAME)
|
||||
val backupDir = File(context.filesDir, BACKUP_DIR_NAME)
|
||||
if (!backupDir.exists()) return@withContext false
|
||||
// 先关闭旧的BoxStore,删除损坏文件,再恢复备份
|
||||
boxStore?.close()
|
||||
deleteDbFiles(context)
|
||||
val isSuccess = copyDir(backupDir, dbDir)
|
||||
// 重新初始化
|
||||
init(context)
|
||||
return@withContext isSuccess
|
||||
}
|
||||
|
||||
// 递归删除数据库文件
|
||||
private fun deleteDbFiles(context: Context) {
|
||||
val dbDir = File(context.filesDir, DB_DIR_NAME)
|
||||
if (dbDir.exists()) deleteDirRecursively(dbDir)
|
||||
}
|
||||
|
||||
// 递归删除目录
|
||||
private fun deleteDirRecursively(file: File) {
|
||||
if (file.isDirectory) {
|
||||
file.listFiles()?.forEach { deleteDirRecursively(it) }
|
||||
}
|
||||
file.delete()
|
||||
}
|
||||
|
||||
// 递归复制目录(核心备份/恢复逻辑)
|
||||
private fun copyDir(srcDir: File, destDir: File): Boolean {
|
||||
return try {
|
||||
if (!srcDir.exists()) return false
|
||||
if (!destDir.exists()) destDir.mkdirs()
|
||||
srcDir.listFiles()?.forEach { srcFile ->
|
||||
val destFile = File(destDir, srcFile.name)
|
||||
if (srcFile.isDirectory) {
|
||||
copyDir(srcFile, destFile)
|
||||
} else {
|
||||
copyFile(srcFile, destFile)
|
||||
}
|
||||
}
|
||||
true
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 复制单个文件(使用NIO,高效稳定)
|
||||
private fun copyFile(srcFile: File, destFile: File) {
|
||||
FileInputStream(srcFile).channel.use { srcChannel ->
|
||||
FileOutputStream(destFile).channel.use { destChannel ->
|
||||
destChannel.transferFrom(srcChannel, 0, srcChannel.size())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭BoxStore(应用退出时调用,可选)
|
||||
fun close() {
|
||||
boxStore?.close()
|
||||
boxStore = null
|
||||
}
|
||||
|
||||
}
|
||||
@@ -133,7 +133,7 @@ class RemoteRepository constructor(
|
||||
suspend fun getRestInfoFoodsByType(
|
||||
foodName: String,
|
||||
pageNum: Int = 1,
|
||||
pageSize:Int = 100
|
||||
pageSize:Int = 50
|
||||
): ApiResponse<List<FoodInfo>> {
|
||||
return safeApiCall {
|
||||
apiService.getRestInfoFoodsByType(param = hashMapOf(
|
||||
|
||||
@@ -0,0 +1,697 @@
|
||||
package com.sw.dualscreen.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.sw.plate.App;
|
||||
import com.sw.plate.utils.AppUtil;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class FileUtil {
|
||||
private static boolean isSaveLog = true;
|
||||
public static final String FILE_STR_PATH = App.getContext().getExternalCacheDir().getAbsolutePath();
|
||||
|
||||
public static final int SIZETYPE_B = 1;//获取文件大小单位为B的double值
|
||||
public static final int SIZETYPE_KB = 2;//获取文件大小单位为KB的double值
|
||||
public static final int SIZETYPE_MB = 3;//获取文件大小单位为MB的double值
|
||||
public static final int SIZETYPE_GB = 4;//获取文件大小单位为GB的double值
|
||||
private static String urlNull = "原文件路径不存在";
|
||||
private static String isFile = "原文件不是文件";
|
||||
private static String canRead = "原文件不能读";
|
||||
private static String copyFalse = "备份失败!";
|
||||
private static String cFromFile = "创建原文件出错:";
|
||||
private static String ctoFile = "创建备份文件出错:";
|
||||
|
||||
|
||||
/**
|
||||
* 写入文件
|
||||
* FileUtil.byteWriteFile(getExternalFilesDir("123"),"test.jpg",nv21);
|
||||
*
|
||||
* @param filePath
|
||||
* @param fileName
|
||||
* @param bytes
|
||||
*/
|
||||
public static void byteWriteFile(File filePath, String fileName, byte[] bytes) {
|
||||
BufferedOutputStream bout = null;
|
||||
try {
|
||||
File file = new File(filePath, fileName);
|
||||
if (file.exists() == false) {
|
||||
if (file.getParentFile().exists() == false) {
|
||||
file.getParentFile().mkdirs();
|
||||
}
|
||||
file.createNewFile();
|
||||
}
|
||||
bout = new BufferedOutputStream(new FileOutputStream(file, false));
|
||||
bout.write(bytes);
|
||||
bout.flush();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (bout != null) {
|
||||
try {
|
||||
bout.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名
|
||||
*
|
||||
* @param path 路径
|
||||
* @return 文件名
|
||||
*/
|
||||
public static String getFileName(String path) {
|
||||
int index = path.lastIndexOf("/");
|
||||
return path.substring(index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件路径获取文件
|
||||
*
|
||||
* @param path 路径
|
||||
* @return
|
||||
*/
|
||||
public static File getFileByPath(String path) {
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
return new File(path);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名文件
|
||||
*
|
||||
* @param filePath 文件路径
|
||||
* @param newName 新名称
|
||||
* @return {@code true}: 重命名成功<br>{@code false}: 重命名失败
|
||||
*/
|
||||
public static boolean rename(String filePath, String newName) {
|
||||
return rename(getFileByPath(filePath), newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名文件
|
||||
*
|
||||
* @param file 文件
|
||||
* @param newName 新名称
|
||||
* @return {@code true}: 重命名成功<br>{@code false}: 重命名失败
|
||||
*/
|
||||
public static boolean rename(File file, String newName) {
|
||||
// 文件为空返回false
|
||||
if (file == null) return false;
|
||||
// 文件不存在返回false
|
||||
if (!file.exists()) return false;
|
||||
// 新的文件名为空返回false
|
||||
if (AppUtil.isEmpty(newName)) return false;
|
||||
// 如果文件名没有改变返回true
|
||||
if (newName.equals(file.getName())) return true;
|
||||
File newFile = new File(file.getParent() + File.separator + newName);
|
||||
// 如果重命名的文件已存在返回false
|
||||
return !newFile.exists()
|
||||
&& file.renameTo(newFile);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断文件是否存在
|
||||
*
|
||||
* @param path 文件的路径,含文件后缀和文件名
|
||||
* @return 是否存在
|
||||
*/
|
||||
public static boolean isFileExists(String path) {
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断文件是否存在
|
||||
*
|
||||
* @return 是否存在
|
||||
*/
|
||||
public static boolean isFileExists(File file) {
|
||||
if (file.exists()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件夹及文件夹下所有内容
|
||||
*
|
||||
* @param path 文件夹路径
|
||||
* @return 返回是否删除成功
|
||||
*/
|
||||
public static boolean deleteFiles(String path) {
|
||||
if (getFileByPath(path) != null) {
|
||||
return deleteFiles(getFileByPath(path));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件夹及文件夹下所有内容
|
||||
*
|
||||
* @param file 文件
|
||||
* @return 返回是否删除成功
|
||||
*/
|
||||
public static boolean deleteFiles(File file) {
|
||||
try {
|
||||
if (file.exists()) { // 判断文件是否存在
|
||||
if (file.isFile()) { // 判断是否是文件
|
||||
file.delete(); // delete()方法
|
||||
} else if (file.isDirectory()) { // 否则如果它是一个目录
|
||||
File files[] = file.listFiles(); // 声明目录下所有的文件 files[];
|
||||
for (int i = 0; i < files.length; i++) { // 遍历目录下所有的文件
|
||||
deleteFiles(files[i].getPath()); // 把每个文件 用这个方法进行迭代
|
||||
}
|
||||
file.delete();//删除目录
|
||||
}
|
||||
//file.delete();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件的Uri
|
||||
*
|
||||
* @param path 文件的路径
|
||||
* @return 文件的Uri
|
||||
*/
|
||||
public static Uri getUriFromFile(String path) {
|
||||
File file = new File(path);
|
||||
return Uri.fromFile(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件指定文件的指定单位的大小
|
||||
*
|
||||
* @param filePath 文件路径
|
||||
* @param sizeType 获取大小的类型1为B、2为KB、3为MB、4为GB
|
||||
* @return double值的大小
|
||||
*/
|
||||
public static double getFileOrFilesSize(String filePath, int sizeType) {
|
||||
File file = new File(filePath);
|
||||
long blockSize = 0;
|
||||
try {
|
||||
if (file.isDirectory()) {
|
||||
blockSize = getFileSizes(file);
|
||||
} else {
|
||||
blockSize = getFileSize(file);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
L.i("获取文件大小", "获取失败!");
|
||||
}
|
||||
return FormetFileSize(blockSize, sizeType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用此方法自动计算指定文件或指定文件夹的大小
|
||||
*
|
||||
* @param filePath 文件路径
|
||||
* @return 计算好的带B、KB、MB、GB的字符串
|
||||
*/
|
||||
public static String getAutoFileOrFilesSize(String filePath) {
|
||||
File file = new File(filePath);
|
||||
long blockSize = 0;
|
||||
try {
|
||||
if (file.isDirectory()) {
|
||||
blockSize = getFileSizes(file);
|
||||
} else {
|
||||
blockSize = getFileSize(file);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
L.i("获取文件大小", "获取失败!");
|
||||
}
|
||||
return FormetFileSize(blockSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定文件大小
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private static long getFileSize(File file) throws Exception {
|
||||
long size = 0;
|
||||
if (file.exists()) {
|
||||
FileInputStream fis = null;
|
||||
fis = new FileInputStream(file);
|
||||
size = fis.available();
|
||||
} else {
|
||||
// file.createNewFile();
|
||||
L.i("获取文件大小", "文件不存在!");
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目录下指定文件名的文件包括子目录
|
||||
* <p>大小写忽略</p>
|
||||
*
|
||||
* @param dirPath 目录路径
|
||||
* @param fileName 文件名
|
||||
* @return 文件链表
|
||||
*/
|
||||
public static List<File> searchFileInDir(String dirPath, String fileName) {
|
||||
return searchFileInDir(getFileByPath(dirPath), fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目录下指定文件名的文件包括子目录
|
||||
* <p>大小写忽略</p>
|
||||
*
|
||||
* @param dir 目录
|
||||
* @param fileName 文件名
|
||||
* @return 文件链表
|
||||
*/
|
||||
public static List<File> searchFileInDir(File dir, String fileName) {
|
||||
if (dir == null || !dir.isDirectory()) return null;
|
||||
List<File> list = new ArrayList<>();
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null && files.length != 0) {
|
||||
for (File file : files) {
|
||||
if (file.getName().toUpperCase().equals(fileName.toUpperCase())) {
|
||||
list.add(file);
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
list.addAll(searchFileInDir(file, fileName));
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定文件夹
|
||||
*
|
||||
* @param f
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
private static long getFileSizes(File f) throws Exception {
|
||||
long size = 0;
|
||||
File flist[] = f.listFiles();
|
||||
for (int i = 0; i < flist.length; i++) {
|
||||
if (flist[i].isDirectory()) {
|
||||
size = size + getFileSizes(flist[i]);
|
||||
} else {
|
||||
size = size + getFileSize(flist[i]);
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换文件大小
|
||||
*
|
||||
* @param fileS
|
||||
* @return
|
||||
*/
|
||||
private static String FormetFileSize(long fileS) {
|
||||
DecimalFormat df = new DecimalFormat("#.00");
|
||||
String fileSizeString = "";
|
||||
String wrongSize = "0B";
|
||||
if (fileS == 0) {
|
||||
return wrongSize;
|
||||
}
|
||||
if (fileS < 1024) {
|
||||
fileSizeString = df.format((double) fileS) + "B";
|
||||
} else if (fileS < 1048576) {
|
||||
fileSizeString = df.format((double) fileS / 1024) + "KB";
|
||||
} else if (fileS < 1073741824) {
|
||||
fileSizeString = df.format((double) fileS / 1048576) + "MB";
|
||||
} else {
|
||||
fileSizeString = df.format((double) fileS / 1073741824) + "GB";
|
||||
}
|
||||
return fileSizeString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换文件大小,指定转换的类型
|
||||
*
|
||||
* @param fileS
|
||||
* @param sizeType
|
||||
* @return
|
||||
*/
|
||||
private static double FormetFileSize(long fileS, int sizeType) {
|
||||
DecimalFormat df = new DecimalFormat("#.00");
|
||||
double fileSizeLong = 0;
|
||||
switch (sizeType) {
|
||||
case SIZETYPE_B:
|
||||
fileSizeLong = Double.valueOf(df.format((double) fileS));
|
||||
break;
|
||||
case SIZETYPE_KB:
|
||||
fileSizeLong = Double.valueOf(df.format((double) fileS / 1024));
|
||||
break;
|
||||
case SIZETYPE_MB:
|
||||
fileSizeLong = Double.valueOf(df.format((double) fileS / 1048576));
|
||||
break;
|
||||
case SIZETYPE_GB:
|
||||
fileSizeLong = Double.valueOf(df.format((double) fileS / 1073741824));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return fileSizeLong;
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制文件
|
||||
*
|
||||
* @param fromFilePath 旧文件地址和名称
|
||||
* @param toFilePath 新文件地址和名称
|
||||
* @return 返回备份文件的信息,ok是成功,其它就是错误
|
||||
*/
|
||||
public static File copyFile(String fromFilePath, String toFilePath) {
|
||||
File fromFile = null;
|
||||
File toFile = null;
|
||||
try {
|
||||
fromFile = new File(fromFilePath);
|
||||
} catch (Exception e) {
|
||||
L.i(cFromFile + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
toFile = new File(toFilePath);
|
||||
if (toFile.isDirectory()) {
|
||||
toFile = new File(toFilePath.endsWith("/") ? toFilePath + fromFile.getName() : toFilePath + "/" + fromFile.getName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
L.i(ctoFile + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!fromFile.exists()) {
|
||||
L.i(urlNull);
|
||||
return null;
|
||||
}
|
||||
if (!fromFile.isFile()) {
|
||||
L.i(isFile);
|
||||
return null;
|
||||
}
|
||||
if (!fromFile.canRead()) {
|
||||
L.i(canRead);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 复制到的路径如果不存在就创建
|
||||
if (!toFile.getParentFile().exists()) {
|
||||
toFile.getParentFile().mkdirs();
|
||||
}
|
||||
|
||||
if (toFile.exists()) {
|
||||
toFile.delete();
|
||||
}
|
||||
|
||||
if (!toFile.canWrite()) {
|
||||
//return notWrite;
|
||||
}
|
||||
|
||||
try {
|
||||
FileInputStream fosfrom = new FileInputStream(
|
||||
fromFile);
|
||||
FileOutputStream fosto = new FileOutputStream(toFile);
|
||||
byte bt[] = new byte[1024];
|
||||
int c;
|
||||
|
||||
while ((c = fosfrom.read(bt)) > 0) {
|
||||
fosto.write(bt, 0, c); // 将内容写到新文件当中
|
||||
}
|
||||
//关闭数据流
|
||||
fosfrom.close();
|
||||
fosto.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
L.i(copyFalse + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
return toFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件
|
||||
*
|
||||
* @param path 文件路径
|
||||
* @return 创建的文件
|
||||
*/
|
||||
public static synchronized File createNewFile(String path) {
|
||||
File file = new File(path);
|
||||
String parentPath = path.substring(0, path.lastIndexOf("/") + 1);
|
||||
File parentFile = new File(parentPath);
|
||||
if (!parentFile.exists()) {
|
||||
parentFile.mkdirs();
|
||||
}
|
||||
if (!file.exists()) {
|
||||
try {
|
||||
file.createNewFile();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/***
|
||||
* 根据文件后缀回去MIME类型
|
||||
****/
|
||||
|
||||
private static String getMIMEType(File file) {
|
||||
String type = "*/*";
|
||||
String fName = file.getName();
|
||||
//获取后缀名前的分隔符"."在fName中的位置。
|
||||
int dotIndex = fName.lastIndexOf(".");
|
||||
if (dotIndex < 0) {
|
||||
return type;
|
||||
}
|
||||
/* 获取文件的后缀名*/
|
||||
String end = fName.substring(dotIndex, fName.length()).toLowerCase();
|
||||
if (end == "") return type;
|
||||
//在MIME和文件类型的匹配表中找到对应的MIME类型。
|
||||
for (int i = 0; i < MIME_MapTable.length; i++) { //MIME_MapTable??在这里你一定有疑问,这个MIME_MapTable是什么?
|
||||
if (end.equals(MIME_MapTable[i][0]))
|
||||
type = MIME_MapTable[i][1];
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全路径中的文件拓展名
|
||||
*
|
||||
* @param file 文件
|
||||
* @return 文件拓展名
|
||||
*/
|
||||
public static String getFileExtension(File file) {
|
||||
if (file == null) return null;
|
||||
return getFileExtension(file.getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全路径中的文件拓展名
|
||||
*
|
||||
* @param filePath 文件路径
|
||||
* @return 文件拓展名
|
||||
*/
|
||||
public static String getFileExtension(String filePath) {
|
||||
if (AppUtil.isEmpty(filePath)) return filePath;
|
||||
int lastPoi = filePath.lastIndexOf('.');
|
||||
int lastSep = filePath.lastIndexOf(File.separator);
|
||||
if (lastPoi == -1 || lastSep >= lastPoi) return "";
|
||||
return filePath.substring(lastPoi + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用系统应用打开文件
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param file 文件
|
||||
*/
|
||||
public static void openFile(Context context, File file) {
|
||||
Intent intent = new Intent();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
//设置intent的Action属性
|
||||
intent.setAction(Intent.ACTION_VIEW);
|
||||
//获取文件file的MIME类型
|
||||
String type = getMIMEType(file);
|
||||
//设置intent的data和Type属性。
|
||||
intent.setDataAndType(Uri.fromFile(file), type);
|
||||
//跳转
|
||||
try {
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
L.i("找不到打开此文件的应用!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存文本文件
|
||||
* FileUtil.saveStrFile("测试", "log.txt", FILE_STR_PATH, true);
|
||||
*
|
||||
* @param res 写入内容
|
||||
* @param fileName 文件名
|
||||
* @param filePath 路径
|
||||
* @param append true新增 false替换
|
||||
* @return
|
||||
*/
|
||||
public static boolean saveStrFile(String res, String fileName, String filePath, boolean append) {
|
||||
boolean flag = true;
|
||||
BufferedReader bufferedReader = null;
|
||||
BufferedWriter bufferedWriter = null;
|
||||
try {
|
||||
File file = new File(filePath, fileName);
|
||||
|
||||
if (!file.exists()) {
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
}
|
||||
bufferedReader = new BufferedReader(new StringReader(res));
|
||||
bufferedWriter = new BufferedWriter(new FileWriter(file, append));
|
||||
char buf[] = new char[1024]; //字符缓冲区
|
||||
int len;
|
||||
while ((len = bufferedReader.read(buf)) != -1) {
|
||||
bufferedWriter.write(buf, 0, len);
|
||||
}
|
||||
bufferedWriter.flush();
|
||||
bufferedReader.close();
|
||||
bufferedWriter.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
flag = false;
|
||||
return flag;
|
||||
} finally {
|
||||
if (bufferedReader != null) {
|
||||
try {
|
||||
bufferedReader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
public static void saveLog(String... logs) {
|
||||
if (!isSaveLog) {
|
||||
return;
|
||||
}
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String log : logs) {
|
||||
sb.append("===")
|
||||
.append((AppUtil.formatDateGetCurrentTime()))
|
||||
.append("===")
|
||||
.append("\n")
|
||||
.append(log)
|
||||
.append("\n");
|
||||
}
|
||||
String fileName = "log" + AppUtil.formatDateGetDay(System.currentTimeMillis()) + ".txt";
|
||||
FileUtil.saveStrFile(sb.toString(), fileName, FILE_STR_PATH, true);
|
||||
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
private static final String[][] MIME_MapTable = {
|
||||
// {后缀名,MIME类型}
|
||||
{".3gp", "video/3gpp"},
|
||||
{".apk", "application/vnd.android.package-archive"},
|
||||
{".asf", "video/x-ms-asf"},
|
||||
{".avi", "video/x-msvideo"},
|
||||
{".bin", "application/octet-stream"},
|
||||
{".bmp", "image/bmp"},
|
||||
{".c", "text/plain"},
|
||||
{".class", "application/octet-stream"},
|
||||
{".conf", "text/plain"},
|
||||
{".cpp", "text/plain"},
|
||||
{".doc", "application/msword"},
|
||||
{".docx",
|
||||
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
|
||||
{".xls", "application/vnd.ms-excel"},
|
||||
{".xlsx",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
|
||||
{".exe", "application/octet-stream"},
|
||||
{".gif", "image/gif"},
|
||||
{".gtar", "application/x-gtar"},
|
||||
{".gz", "application/x-gzip"},
|
||||
{".h", "text/plain"},
|
||||
{".htm", "text/html"},
|
||||
{".html", "text/html"},
|
||||
{".jar", "application/java-archive"},
|
||||
{".java", "text/plain"},
|
||||
{".jpeg", "image/jpeg"},
|
||||
{".jpg", "image/jpeg"},
|
||||
{".js", "application/x-javascript"},
|
||||
{".log", "text/plain"},
|
||||
{".m3u", "audio/x-mpegurl"},
|
||||
{".m4a", "audio/mp4a-latm"},
|
||||
{".m4b", "audio/mp4a-latm"},
|
||||
{".m4p", "audio/mp4a-latm"},
|
||||
{".m4u", "video/vnd.mpegurl"},
|
||||
{".m4v", "video/x-m4v"},
|
||||
{".mov", "video/quicktime"},
|
||||
{".mp2", "audio/x-mpeg"},
|
||||
{".mp3", "audio/x-mpeg"},
|
||||
{".mp4", "video/mp4"},
|
||||
{".mpc", "application/vnd.mpohun.certificate"},
|
||||
{".mpe", "video/mpeg"},
|
||||
{".mpeg", "video/mpeg"},
|
||||
{".mpg", "video/mpeg"},
|
||||
{".mpg4", "video/mp4"},
|
||||
{".mpga", "audio/mpeg"},
|
||||
{".msg", "application/vnd.ms-outlook"},
|
||||
{".ogg", "audio/ogg"},
|
||||
{".pdf", "application/pdf"},
|
||||
{".png", "image/png"},
|
||||
{".pps", "application/vnd.ms-powerpoint"},
|
||||
{".ppt", "application/vnd.ms-powerpoint"},
|
||||
{".pptx",
|
||||
"application/vnd.openxmlformats-officedocument.presentationml.presentation"},
|
||||
{".prop", "text/plain"}, {".rc", "text/plain"},
|
||||
{".rmvb", "audio/x-pn-realaudio"}, {".rtf", "application/rtf"},
|
||||
{".sh", "text/plain"}, {".tar", "application/x-tar"},
|
||||
{".tgz", "application/x-compressed"}, {".txt", "text/plain"},
|
||||
{".wav", "audio/x-wav"}, {".wma", "audio/x-ms-wma"},
|
||||
{".wmv", "audio/x-ms-wmv"},
|
||||
{".wps", "application/vnd.ms-works"}, {".xml", "text/plain"},
|
||||
{".z", "application/x-compress"},
|
||||
{".zip", "application/x-zip-compressed"}, {"", "*/*"}};
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.sw.dualscreen.utils;
|
||||
|
||||
/**
|
||||
* Log统一管理类
|
||||
*/
|
||||
public class L {
|
||||
|
||||
private L() {
|
||||
/* cannot be instantiated */
|
||||
throw new UnsupportedOperationException("cannot be instantiated");
|
||||
}
|
||||
|
||||
public static boolean isDebug = true;// 是否需要打印bug,可以在application的onCreate函数里面初始化
|
||||
private static final String TAG = "mzf";
|
||||
|
||||
// 下面四个是默认tag的函数
|
||||
public static void i(String msg) {
|
||||
if (isDebug)
|
||||
android.util.Log.i(TAG, msg);
|
||||
}
|
||||
|
||||
public static void d(String msg) {
|
||||
if (isDebug)
|
||||
android.util.Log.d(TAG, msg);
|
||||
}
|
||||
|
||||
public static void e(String msg) {
|
||||
if (isDebug)
|
||||
android.util.Log.e(TAG, msg);
|
||||
}
|
||||
|
||||
public static void v(String msg) {
|
||||
if (isDebug)
|
||||
android.util.Log.v(TAG, msg);
|
||||
}
|
||||
|
||||
// 下面是传入自定义tag的函数
|
||||
public static void i(String tag, String msg) {
|
||||
if (isDebug)
|
||||
android.util.Log.i(tag, msg);
|
||||
}
|
||||
|
||||
public static void d(String tag, String msg) {
|
||||
if (isDebug)
|
||||
android.util.Log.d(tag, msg);
|
||||
}
|
||||
|
||||
public static void e(String tag, String msg) {
|
||||
if (isDebug)
|
||||
android.util.Log.e(tag, msg);
|
||||
}
|
||||
|
||||
public static void v(String tag, String msg) {
|
||||
if (isDebug)
|
||||
android.util.Log.v(tag, msg);
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,7 @@
|
||||
android:contentDescription="食物图预览"
|
||||
android:scaleType="centerCrop"
|
||||
android:visibility="gone"
|
||||
app:strokeColor="@android:color/transparent"
|
||||
app:shapeAppearance="@style/round10dpCornerStyle" />
|
||||
|
||||
<View
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginHorizontal="32dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:background="@drawable/bg_btn_save2"
|
||||
android:background="@drawable/bg_btn_confirm"
|
||||
android:text="已支付确认"
|
||||
android:enabled="true"
|
||||
android:textColor="@color/white"
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:src="@drawable/ic_avatar_default"
|
||||
app:strokeColor="@android:color/transparent"
|
||||
app:shapeAppearance="@style/CircleStyle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
@@ -93,6 +93,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
app:strokeColor="@android:color/transparent"
|
||||
app:shapeAppearance="@style/round10dpCornerStyle"
|
||||
tools:src="@mipmap/ic_launcher"/>
|
||||
|
||||
@@ -102,6 +103,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
tools:src="@mipmap/ic_launcher_round"
|
||||
app:strokeColor="@android:color/transparent"
|
||||
app:shapeAppearance="@style/round10dpCornerStyle"
|
||||
android:visibility="gone"/>
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@
|
||||
android:src="@drawable/ic_avatar_default"
|
||||
android:layout_marginTop="28dp"
|
||||
app:shapeAppearance="@style/CircleStyle"
|
||||
app:strokeColor="@android:color/transparent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user