package com.sw.inbound.activity import android.annotation.SuppressLint import android.graphics.Bitmap import android.net.Uri import android.os.Bundle import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.viewModels import androidx.camera.view.PreviewView import androidx.core.view.updateLayoutParams import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import com.sw.inbound.R import com.sw.inbound.adapter.FoodCollectionAdapter import com.sw.inbound.adapter.GoodsSearchAdapter import com.sw.inbound.databinding.ActivityFoodCollectionBinding import com.sw.inbound.databinding.LayoutCameraPreviewBinding import com.sw.inbound.databinding.LayoutEmptySearchBinding import com.sw.inbound.dialog.CollectSearchDialog import com.sw.inbound.dialog.Loading import com.sw.inbound.model.response.SearchGoodsInfo import com.sw.inbound.objbox.Food import com.sw.inbound.objbox.FoodCollectionBean import com.sw.inbound.objbox.FoodModule import com.sw.inbound.objbox.ObjectBox import com.sw.inbound.utils.BitmapCropper import com.sw.inbound.utils.BitmapSaver import com.sw.inbound.utils.CameraUtils import com.sw.inbound.utils.ImageUtil import com.sw.inbound.utils.ext.addOnActionSearchListener import com.sw.inbound.utils.ext.clickWithDebounce import com.sw.inbound.utils.ext.dp import com.sw.inbound.utils.ext.gone import com.sw.inbound.utils.ext.hideKeyboard import com.sw.inbound.utils.ext.toast import com.sw.inbound.utils.ext.visible import com.sw.inbound.viewmodel.ReceiptViewModel import dagger.hilt.android.AndroidEntryPoint import io.objectbox.Box import io.objectbox.kotlin.boxFor import kotlinx.coroutines.runBlocking import timber.log.Timber @SuppressLint("NotifyDataSetChanged") @AndroidEntryPoint class FoodCollectionActivity : ComponentActivity() { private val TAG = "FoodCollectionActivity" private var box: Box? = null private val collectList: MutableList = mutableListOf() private lateinit var binding: ActivityFoodCollectionBinding private lateinit var previewView: PreviewView val viewModel: ReceiptViewModel by viewModels() val PAGE_SIZE = 30 private val cameraUtils: CameraUtils by lazy { CameraUtils(this) } private val collectionAdapter: FoodCollectionAdapter by lazy { FoodCollectionAdapter(collectList).apply { addOnItemChildClickListener(R.id.ivDelete) { _, _, position -> collectList[position].let { it.bitmap = null it.isFinish = false it.isShowCamera = true } collectionAdapter.notifyItemChanged(position) } } } override fun onResume() { super.onResume() cameraUtils.bind() binding.llCameraFlag.run { visible() postDelayed({ gone() }, 3000) } } override fun onPause() { super.onPause() cameraUtils.unbind() binding.llCameraFlag.visible() } @SuppressLint("NotifyDataSetChanged") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityFoodCollectionBinding.inflate(layoutInflater) setContentView(binding.root) cameraUtils.initCamera() binding.root.setOnClickListener { it.hideKeyboard() } val previewBinding = LayoutCameraPreviewBinding.inflate(layoutInflater, binding.flCameraPreview) previewView = previewBinding.previewView.also { it.updateLayoutParams { width = 420.dp height = 420.dp } } cameraUtils.setPreviewController(previewView) binding.btnBack.setOnClickListener { finish() } binding.rvFoodCollection.let { it.layoutManager = GridLayoutManager(this, 3, GridLayoutManager.VERTICAL, false) it.adapter = collectionAdapter } repeat(MAX_COUNT) { collectList.add(FoodCollectionBean(isShowCamera = true)) } collectionAdapter.notifyDataSetChanged() binding.ivGoodsSearch.setOnClickListener { searchGoods() } binding.etGoodsInput.let { v -> v.addOnActionSearchListener { searchGoods() } } binding.btnSave.setOnClickListener { if (clickIndex == -1) { Toast.makeText(this, "请选择物品名称", Toast.LENGTH_SHORT).show() return@setOnClickListener } val count = collectList.count {it.bitmap!=null} if (count == 0) { Toast.makeText(this, "请拍摄物品照片", Toast.LENGTH_SHORT).show() return@setOnClickListener } vectorThread() } binding.btnTakePhoto.clickWithDebounce { takePhoto() } binding.btnCollectedGoods.setOnClickListener { CollectSearchDialog(this@FoodCollectionActivity).show() } binding.btnClearData.setOnClickListener { clearData() } binding.rvSearch.let { it.layoutManager = GridLayoutManager(this, 2, LinearLayoutManager.VERTICAL, false) it.adapter = searchAdapter } binding.refreshLayout.let { it.setEnableRefresh(true) it.setEnableLoadMore(false) it.setOnRefreshListener { pageNo = 1 searchGoods() } it.setOnLoadMoreListener { searchGoods() } } loadEmptyView() } private var pageNo = 1 private var clickIndex = -1 private val searchGoodsList: MutableList = mutableListOf() private val searchAdapter by lazy { GoodsSearchAdapter(searchGoodsList).apply { isStateViewEnable = true setOnItemClickListener { adapter, view, position -> clickIndex = position if (list[position].isSelected) { return@setOnItemClickListener } list.forEach { item -> item.isSelected = false } list[position].isSelected = true notifyDataSetChanged() } } } private fun finishRefresh() { binding.refreshLayout.let { if (pageNo == 1) { it.finishRefresh(500) } else { it.finishLoadMore(500) } } } val MAX_COUNT = 100 @SuppressLint("NotifyDataSetChanged") private fun takePhoto() { val count = collectList.count { it.bitmap != null } if (count == MAX_COUNT) { toast("每次只允许保存${MAX_COUNT}条数据") return } cameraUtils.takePhoto(cameraCallback) } @SuppressLint("NotifyDataSetChanged") private fun clearData() { collectList.forEach { it.bitmap = null it.isShowCamera = true it.isFinish = false } collectionAdapter.notifyDataSetChanged() clickIndex = -1 binding.etGoodsInput.setText("") searchGoodsList.clear() searchAdapter.notifyDataSetChanged() loadEmptyView() } private val cameraCallback:(Uri) -> Unit = { uri-> val index = collectList.indexOfFirst { it.bitmap == null } if (index == -1) { toast("每次只允许保存6条数据") rerurn@cameraCallback } ImageUtil.uriToBitmap(this, uri)?.let { bitmap -> val cropBitmap = BitmapCropper.cropCenter( original = bitmap, targetWidth = 1000, targetHeight = 1300, offsetX = 30, offsetY = 100 ) val file = BitmapSaver.saveToAppFilesDir( cropBitmap, this, "IMG_CROP_${System.currentTimeMillis()}.jpg" ) Timber.d("${this.javaClass.simpleName}-searchFood-裁剪bitmap保存文件路径:${file?.absolutePath}") collectList[index].let { it.bitmap = cropBitmap it.isShowCamera = false } collectionAdapter.notifyItemChanged(index) } } private fun vectorThread() { Loading.show(this) Thread { collectList.filter { it.bitmap != null } .forEachIndexed { index, it -> image2VectorTask(it.bitmap!!, index) } runOnUiThread { window.decorView.postDelayed({ Loading.dismiss() toast("保存成功") }, 1000) } }.start() } private fun image2VectorTask(bitmap: Bitmap, position: Int) { initBox() val imageVector = FoodModule.bitmap2FloatArray(bitmap) box?.put(Food(name = searchGoodsList[clickIndex].goodsName, foodIdx = 0, foodVector = imageVector)) collectList[position].let { it.imageVector = imageVector it.isFinish = true } runOnUiThread { collectionAdapter.notifyItemChanged(position) } } @SuppressLint("NotifyDataSetChanged") private fun searchGoods() { val searchName = binding.etGoodsInput.text.toString().trim() if (searchName.isBlank()) { toast("请输入物品名称") finishRefresh() return } searchGoods( searchName = searchName, pageNo = pageNo ) { records -> finishRefresh() binding.etGoodsInput.hideKeyboard() if (pageNo == 1) { searchGoodsList.clear() } searchGoodsList.addAll(records) if (searchGoodsList.isEmpty()) { loadEmptyView() return@searchGoods } val enableLoadMore = records.size >= PAGE_SIZE binding.refreshLayout.setEnableLoadMore(enableLoadMore) if (enableLoadMore) { pageNo++ } binding.rvSearch.layoutManager = GridLayoutManager(this, 2, GridLayoutManager.VERTICAL, false) searchAdapter.notifyDataSetChanged() } } private var emptyBinding: LayoutEmptySearchBinding? = null private fun loadEmptyView() { binding.rvSearch.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) if (emptyBinding == null) { emptyBinding = LayoutEmptySearchBinding.inflate(layoutInflater, binding.rvSearch, false) } emptyBinding?.tvContent?.text = "请检查物品名称,或稍后重新搜索" emptyBinding?.root?.let { layout -> layout.setOnClickListener { layout.hideKeyboard() } searchAdapter.stateView = layout } } fun searchGoods( searchName: String, pageNo: Int, pageSize: Int = PAGE_SIZE, callback: (List) -> Unit ) { runBlocking { val records = viewModel.searchGoodsInfoList2( goodsName = searchName, pageNo = pageNo, pageSize = pageSize ) runOnUiThread { callback(records) } } } private fun initBox() { if (box == null) { box = ObjectBox.boxStore.boxFor(Food::class) } } }