Compare commits
5
Commits
f3d57177b8
...
a2f8fb903a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2f8fb903a | ||
|
|
13fc7c7e0f | ||
|
|
fed161b088 | ||
|
|
594c551a98 | ||
|
|
617ccfb456 |
@@ -93,7 +93,7 @@ class Seasoning18GridAdapter(
|
|||||||
holder.b.tvState.text = stateStr(item.state)
|
holder.b.tvState.text = stateStr(item.state)
|
||||||
}
|
}
|
||||||
if (position == highlightedPosition) {
|
if (position == highlightedPosition) {
|
||||||
holder.b.root.setBackgroundColor(ContextCompat.getColor(holder.b.root.context, R.color.red_ff4444))
|
holder.b.root.setBackgroundResource(R.drawable.shape_scale_cell_highlight)
|
||||||
val white = ContextCompat.getColor(holder.b.root.context, R.color.white)
|
val white = ContextCompat.getColor(holder.b.root.context, R.color.white)
|
||||||
holder.b.tvAddress.setTextColor(white)
|
holder.b.tvAddress.setTextColor(white)
|
||||||
holder.b.tvName.setTextColor(white)
|
holder.b.tvName.setTextColor(white)
|
||||||
|
|||||||
@@ -15,21 +15,24 @@ import com.shuwei.dish.match.R
|
|||||||
import com.shuwei.dish.match.databinding.ListItemScale22RowBinding
|
import com.shuwei.dish.match.databinding.ListItemScale22RowBinding
|
||||||
import com.shuwei.dish.match.scale.ScaleData
|
import com.shuwei.dish.match.scale.ScaleData
|
||||||
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||||
|
import com.shuwei.dish.match.utils.SizeTool
|
||||||
import com.shuwei.dish.match.utils.WeightUtil
|
import com.shuwei.dish.match.utils.WeightUtil
|
||||||
import com.shuwei.dish.match.utils.ext.dp
|
import com.shuwei.dish.match.utils.ext.dp
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 22格网格 Adapter,同时服务于调料配置页和主设备秤总览页。
|
* 22格网格 Adapter,同时服务于调料配置页和主设备秤总览页。
|
||||||
*
|
*
|
||||||
* @param largeSize 前4个大格子的尺寸(px)
|
* @param largeSize 前4个大格子的宽度(px)
|
||||||
* @param smallSize 其余小格子的尺寸(px)
|
* @param smallSize 其余小格子的宽度(px)
|
||||||
* @param showNameOnly true = 调料配置模式,仅显示 tvName,隐藏 tvWeight/tvState;
|
* @param cellAspectRatio 格子高宽比(height / width)
|
||||||
* false = 秤总览模式,三行全部显示
|
* @param showNameOnly true = 调料配置模式,仅显示 tvName,隐藏 tvWeight/tvState;
|
||||||
* @param showAddress true = 显示秤地址行(tvAddress),仅 MasterScaleActivity 使用
|
* false = 秤总览模式,三行全部显示
|
||||||
|
* @param showAddress true = 显示秤地址行(tvAddress),仅 MasterScaleActivity 使用
|
||||||
*/
|
*/
|
||||||
class Seasoning22GridAdapter(
|
class Seasoning22GridAdapter(
|
||||||
private val largeSize: Int,
|
private val largeSize: Int,
|
||||||
private val smallSize: Int,
|
private val smallSize: Int,
|
||||||
|
private val cellAspectRatio: Float = SizeTool.DEFAULT_CELL_ASPECT_RATIO,
|
||||||
private val showNameOnly: Boolean = false,
|
private val showNameOnly: Boolean = false,
|
||||||
private val showAddress: Boolean = false
|
private val showAddress: Boolean = false
|
||||||
) : BaseQuickAdapter<ScaleData, Seasoning22GridAdapter.VH>(mutableListOf()) {
|
) : BaseQuickAdapter<ScaleData, Seasoning22GridAdapter.VH>(mutableListOf()) {
|
||||||
@@ -51,13 +54,14 @@ class Seasoning22GridAdapter(
|
|||||||
|
|
||||||
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH {
|
||||||
val b = ListItemScale22RowBinding.inflate(LayoutInflater.from(context), parent, false)
|
val b = ListItemScale22RowBinding.inflate(LayoutInflater.from(context), parent, false)
|
||||||
val cellSize = if (viewType == 1) largeSize else smallSize
|
val cellWidth = if (viewType == 1) largeSize else smallSize
|
||||||
|
val cellHeight = (cellWidth * cellAspectRatio).toInt()
|
||||||
b.root.updateLayoutParams<FlexboxLayoutManager.LayoutParams> {
|
b.root.updateLayoutParams<FlexboxLayoutManager.LayoutParams> {
|
||||||
width = cellSize
|
width = cellWidth
|
||||||
height = cellSize
|
height = cellHeight
|
||||||
setMargins(2.dp, 2.dp, 2.dp, 2.dp)
|
setMargins(2.dp, 2.dp, 2.dp, 2.dp)
|
||||||
}
|
}
|
||||||
val baseSp = (cellSize / parent.resources.displayMetrics.density).toInt()
|
val baseSp = (cellWidth / parent.resources.displayMetrics.density).toInt()
|
||||||
b.tvAddress.textSize = (baseSp * 0.14f).coerceIn(8f, 12f)
|
b.tvAddress.textSize = (baseSp * 0.14f).coerceIn(8f, 12f)
|
||||||
b.tvName.textSize = (baseSp * 0.18f).coerceIn(10f, 16f)
|
b.tvName.textSize = (baseSp * 0.18f).coerceIn(10f, 16f)
|
||||||
b.tvWeight.textSize = (baseSp * 0.22f).coerceIn(12f, 20f)
|
b.tvWeight.textSize = (baseSp * 0.22f).coerceIn(12f, 20f)
|
||||||
@@ -95,7 +99,7 @@ class Seasoning22GridAdapter(
|
|||||||
}
|
}
|
||||||
// 高亮格子:红底白字;其余恢复默认
|
// 高亮格子:红底白字;其余恢复默认
|
||||||
if (position == highlightedPosition) {
|
if (position == highlightedPosition) {
|
||||||
holder.b.root.setBackgroundColor(ContextCompat.getColor(holder.b.root.context, R.color.red_ff4444))
|
holder.b.root.setBackgroundResource(R.drawable.shape_scale_cell_highlight)
|
||||||
val white = ContextCompat.getColor(holder.b.root.context, R.color.white)
|
val white = ContextCompat.getColor(holder.b.root.context, R.color.white)
|
||||||
holder.b.tvAddress.setTextColor(white)
|
holder.b.tvAddress.setTextColor(white)
|
||||||
holder.b.tvName.setTextColor(white)
|
holder.b.tvName.setTextColor(white)
|
||||||
|
|||||||
@@ -137,6 +137,23 @@ class DbRepository {
|
|||||||
suspend fun upsertAllSeasoningSlots(slots: List<SeasoningSlotEntity>) = withContext(Dispatchers.IO) {
|
suspend fun upsertAllSeasoningSlots(slots: List<SeasoningSlotEntity>) = withContext(Dispatchers.IO) {
|
||||||
db.seasoningSlotDao().upsertAll(slots)
|
db.seasoningSlotDao().upsertAll(slots)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定设备的指定槽位配置
|
||||||
|
* @param deviceId 目标设备 ID
|
||||||
|
* @param address 秤硬件地址
|
||||||
|
*/
|
||||||
|
suspend fun deleteSeasoningSlot(deviceId: String, address: Int) = withContext(Dispatchers.IO) {
|
||||||
|
db.seasoningSlotDao().deleteByDeviceIdAndAddress(deviceId, address)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定设备的全部槽位配置
|
||||||
|
* @param deviceId 目标设备 ID
|
||||||
|
*/
|
||||||
|
suspend fun deleteAllSlotsByDeviceId(deviceId: String) = withContext(Dispatchers.IO) {
|
||||||
|
db.seasoningSlotDao().deleteAllByDeviceId(deviceId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
//class SeasoningRepository(val seasoningDao: SeasoningDao) {
|
//class SeasoningRepository(val seasoningDao: SeasoningDao) {
|
||||||
|
|||||||
@@ -222,6 +222,21 @@ class DbViewModel : ViewModel() {
|
|||||||
suspend fun upsertAllSeasoningSlots(slots: List<SeasoningSlotEntity>) =
|
suspend fun upsertAllSeasoningSlots(slots: List<SeasoningSlotEntity>) =
|
||||||
rep.upsertAllSeasoningSlots(slots)
|
rep.upsertAllSeasoningSlots(slots)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定设备的指定槽位配置
|
||||||
|
* @param deviceId 目标设备 ID
|
||||||
|
* @param address 秤硬件地址
|
||||||
|
*/
|
||||||
|
suspend fun deleteSeasoningSlot(deviceId: String, address: Int) =
|
||||||
|
rep.deleteSeasoningSlot(deviceId, address)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定设备的全部槽位配置(子设备接收全量广播时先清空再写入)
|
||||||
|
* @param deviceId 目标设备 ID
|
||||||
|
*/
|
||||||
|
suspend fun deleteAllSlotsByDeviceId(deviceId: String) =
|
||||||
|
rep.deleteAllSlotsByDeviceId(deviceId)
|
||||||
|
|
||||||
// fun updateAll(list: MutableList<SeasoningEntity>) {
|
// fun updateAll(list: MutableList<SeasoningEntity>) {
|
||||||
// viewModelScope.launch {
|
// viewModelScope.launch {
|
||||||
// list.forEach {
|
// list.forEach {
|
||||||
|
|||||||
@@ -37,4 +37,19 @@ interface SeasoningSlotDao {
|
|||||||
*/
|
*/
|
||||||
@Query("SELECT * FROM dm_seasoning_slot")
|
@Query("SELECT * FROM dm_seasoning_slot")
|
||||||
suspend fun queryAll(): List<SeasoningSlotEntity>
|
suspend fun queryAll(): List<SeasoningSlotEntity>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定设备的指定槽位配置
|
||||||
|
* @param deviceId 目标设备 ID
|
||||||
|
* @param address 秤硬件地址
|
||||||
|
*/
|
||||||
|
@Query("DELETE FROM dm_seasoning_slot WHERE deviceId = :deviceId AND address = :address")
|
||||||
|
suspend fun deleteByDeviceIdAndAddress(deviceId: String, address: Int)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定设备的全部槽位配置(子设备接收全量广播时先清空再写入)
|
||||||
|
* @param deviceId 目标设备 ID
|
||||||
|
*/
|
||||||
|
@Query("DELETE FROM dm_seasoning_slot WHERE deviceId = :deviceId")
|
||||||
|
suspend fun deleteAllByDeviceId(deviceId: String)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
package com.shuwei.dish.match.dialog
|
package com.shuwei.dish.match.dialog
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.graphics.Rect
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.view.Gravity
|
import android.view.Gravity
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.Window
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.core.view.isEmpty
|
|
||||||
import androidx.core.widget.addTextChangedListener
|
import androidx.core.widget.addTextChangedListener
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||||
import com.shuwei.dish.match.R
|
import com.shuwei.dish.match.R
|
||||||
import com.shuwei.dish.match.adapter.SeasoningSearchAdapter
|
import com.shuwei.dish.match.adapter.SeasoningSearchAdapter
|
||||||
@@ -30,7 +31,6 @@ import com.shuwei.dish.match.utils.ext.toast
|
|||||||
*/
|
*/
|
||||||
class SeasoningSelectDialog(
|
class SeasoningSelectDialog(
|
||||||
private val activity: BaseActivity,
|
private val activity: BaseActivity,
|
||||||
private val clickIndex: Int,
|
|
||||||
private val onItemSelected: (item: SeasoningEntity) -> Unit
|
private val onItemSelected: (item: SeasoningEntity) -> Unit
|
||||||
) : BottomSheetDialog(activity, R.style.BottomSheet) {
|
) : BottomSheetDialog(activity, R.style.BottomSheet) {
|
||||||
|
|
||||||
@@ -84,18 +84,10 @@ class SeasoningSelectDialog(
|
|||||||
addOnActionSearchListener { searchGoods(this) }
|
addOnActionSearchListener { searchGoods(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecyclerView 初始化及滑动冲突处理
|
// RecyclerView 初始化
|
||||||
binding.recyclerView.run {
|
binding.recyclerView.run {
|
||||||
layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.VERTICAL, false)
|
layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.VERTICAL, false)
|
||||||
adapter = this@SeasoningSelectDialog.adapter
|
adapter = this@SeasoningSelectDialog.adapter
|
||||||
addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
|
||||||
override fun onScrolled(rv: RecyclerView, dx: Int, dy: Int) {
|
|
||||||
super.onScrolled(rv, dx, dy)
|
|
||||||
// 解决 RecyclerView 与 SmartRefreshLayout 滑动冲突
|
|
||||||
val topRowVerticalPosition = if (rv.isEmpty()) 0 else rv.getChildAt(0).top
|
|
||||||
binding.refreshLayout.setNestedScrollingEnabled(topRowVerticalPosition >= 0)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.refreshLayout.run {
|
binding.refreshLayout.run {
|
||||||
@@ -109,11 +101,32 @@ class SeasoningSelectDialog(
|
|||||||
binding.root.setOnClickListener { KeyboardUtil.hideKeyboard(it) }
|
binding.root.setOnClickListener { KeyboardUtil.hideKeyboard(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 弹窗显示时自动加载默认调料列表 */
|
/** 弹窗显示时自动加载默认调料列表,并在 Window.Callback 层提前拦截触摸以解决滑动冲突 */
|
||||||
override fun show() {
|
override fun show() {
|
||||||
super.show()
|
super.show()
|
||||||
pageNo = 1
|
pageNo = 1
|
||||||
getGoodsList()
|
getGoodsList()
|
||||||
|
// Window.Callback.dispatchTouchEvent 在整个 View 树的 onInterceptTouchEvent 之前执行,
|
||||||
|
// 是最早能感知触摸的时机。在此处判断触摸点位置来控制 behavior.isDraggable,
|
||||||
|
// 从而确保 BottomSheetBehavior 在 CoordinatorLayout.onInterceptTouchEvent 时
|
||||||
|
// 已经是禁用状态,不会拦截列表区域的滑动手势。
|
||||||
|
val originalCallback = window?.callback ?: return
|
||||||
|
window?.callback = object : Window.Callback by originalCallback {
|
||||||
|
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
|
||||||
|
when (event.actionMasked) {
|
||||||
|
MotionEvent.ACTION_DOWN -> {
|
||||||
|
val rect = Rect()
|
||||||
|
binding.refreshLayout.getGlobalVisibleRect(rect)
|
||||||
|
// 触摸点落在列表区域内则禁止 BottomSheet 拖拽
|
||||||
|
behavior.isDraggable = !rect.contains(event.rawX.toInt(), event.rawY.toInt())
|
||||||
|
}
|
||||||
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||||
|
behavior.isDraggable = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return originalCallback.dispatchTouchEvent(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
//package com.shuwei.dish.match.http
|
|
||||||
//
|
|
||||||
//import android.annotation.SuppressLint
|
|
||||||
//import android.util.Log
|
|
||||||
//import okhttp3.Call
|
|
||||||
//import okhttp3.Interceptor
|
|
||||||
//import okhttp3.OkHttpClient
|
|
||||||
//import okhttp3.Request
|
|
||||||
//import okhttp3.Response
|
|
||||||
//import java.util.concurrent.TimeUnit
|
|
||||||
//
|
|
||||||
//import javax.net.ssl.*
|
|
||||||
//import java.security.SecureRandom
|
|
||||||
//import java.security.cert.X509Certificate
|
|
||||||
//
|
|
||||||
//class HttpClient private constructor() {
|
|
||||||
// private val client: OkHttpClient by lazy {
|
|
||||||
// OkHttpClient.Builder()
|
|
||||||
// .apply {
|
|
||||||
// connectTimeout(15, TimeUnit.SECONDS)
|
|
||||||
// readTimeout(30, TimeUnit.SECONDS)
|
|
||||||
// writeTimeout(15, TimeUnit.SECONDS)
|
|
||||||
// sslSocketFactory(createSSLSocketFactory(), TrustAllCerts())
|
|
||||||
// hostnameVerifier { _, _ -> true }
|
|
||||||
// //if (BuildConfig.Debug) {
|
|
||||||
// addInterceptor(LoggingInterceptor())
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
// .build()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// companion object {
|
|
||||||
// val instance by lazy { HttpClient() }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun newCall(request: Request): Call = client.newCall(request)
|
|
||||||
//
|
|
||||||
// inner class LoggingInterceptor : Interceptor {
|
|
||||||
// override fun intercept(chain: Interceptor.Chain): Response {
|
|
||||||
// val request = chain.request()
|
|
||||||
// // 打印请求日志
|
|
||||||
// Log.d("OkHttp", "--> ${request.method} ${request.url}")
|
|
||||||
//
|
|
||||||
// val response = chain.proceed(request)
|
|
||||||
// // 打印响应日志
|
|
||||||
// Log.d("OkHttp", "<-- ${response.code} ${response.request.url}")
|
|
||||||
//
|
|
||||||
// return response
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // 信任所有证书的TrustManager实现
|
|
||||||
// @SuppressLint("CustomX509TrustManager")
|
|
||||||
// class TrustAllCerts : X509TrustManager {
|
|
||||||
// @SuppressLint("TrustAllX509TrustManager")
|
|
||||||
// override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
|
|
||||||
// @SuppressLint("TrustAllX509TrustManager")
|
|
||||||
// override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {}
|
|
||||||
// override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // 创建信任所有证书的SSLSocketFactory
|
|
||||||
// fun createSSLSocketFactory(): SSLSocketFactory {
|
|
||||||
// val sslContext = SSLContext.getInstance("TLS").apply {
|
|
||||||
// init(null, arrayOf(TrustAllCerts()), SecureRandom())
|
|
||||||
// }
|
|
||||||
// return sslContext.socketFactory
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
@@ -1,199 +0,0 @@
|
|||||||
//package com.shuwei.dish.match.http
|
|
||||||
//
|
|
||||||
//import android.os.Handler
|
|
||||||
//import android.os.Looper
|
|
||||||
//import android.util.Log
|
|
||||||
//import com.google.gson.reflect.TypeToken
|
|
||||||
//import com.shuwei.dish.match.base.BaseApp
|
|
||||||
//import com.shuwei.dish.match.base.BaseReq
|
|
||||||
//import com.shuwei.dish.match.utils.SpTool
|
|
||||||
//import com.shuwei.dish.match.utils.ext.toType
|
|
||||||
//import okhttp3.Call
|
|
||||||
//import okhttp3.Callback
|
|
||||||
//import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
|
||||||
//import okhttp3.Request
|
|
||||||
//import okhttp3.RequestBody.Companion.toRequestBody
|
|
||||||
//import okhttp3.Response
|
|
||||||
//import java.io.IOException
|
|
||||||
//import java.util.concurrent.Executors
|
|
||||||
//import java.util.concurrent.TimeUnit
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//object HttpUtil {
|
|
||||||
//
|
|
||||||
// var loopGetToken = true
|
|
||||||
//
|
|
||||||
// fun runMainThread(action: () -> Unit) {
|
|
||||||
// Handler(Looper.getMainLooper()).post {
|
|
||||||
// action()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // GET请求(HTTPS)
|
|
||||||
// fun get(
|
|
||||||
// url: String,
|
|
||||||
// header: Map<String, String>?=null,
|
|
||||||
// doSuccess: (data: Any) -> Unit,
|
|
||||||
// doFailure: (code: Int?, msg: String?) -> Unit
|
|
||||||
// ) {
|
|
||||||
// val request = Request.Builder()
|
|
||||||
// .url(if (url.isHttpApi()) url else UrlConfig.BASE_URL + url)
|
|
||||||
// .apply {
|
|
||||||
// if (header != null) {
|
|
||||||
// header.forEach {
|
|
||||||
// addHeader(it.key, it.value)
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// val token = getToken()
|
|
||||||
// if (token.isNotBlank()) {
|
|
||||||
// addHeader("X-Access-Token", token)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .get()
|
|
||||||
// .build()
|
|
||||||
// HttpClient.instance.newCall(request).enqueue(CallbackImpl(doSuccess, doFailure))
|
|
||||||
// }
|
|
||||||
////
|
|
||||||
//// fun get(
|
|
||||||
//// isHttps: Boolean = false,
|
|
||||||
//// host: String = "vip.shuziweidao.com",
|
|
||||||
//// pathSegmentList: List<String>,
|
|
||||||
//// queryParams: Map<String, String>,
|
|
||||||
//// doSuccess: (data: Any) -> Unit,
|
|
||||||
//// doFailure: (code: Int?, msg: String?) -> Unit
|
|
||||||
//// ) {
|
|
||||||
//// val token = getToken()
|
|
||||||
//// val url = HttpUrl.Builder()
|
|
||||||
//// .scheme(if (isHttps) "https" else "http")
|
|
||||||
//// .host(host)
|
|
||||||
//// .apply {
|
|
||||||
//// pathSegmentList.forEach { addPathSegment(it) }
|
|
||||||
//// queryParams.forEach { (key, value) -> addQueryParameter(key, value) }
|
|
||||||
//// }
|
|
||||||
//// .build()
|
|
||||||
//// val request = Request.Builder()
|
|
||||||
//// .url(url)
|
|
||||||
//// .apply {
|
|
||||||
//// if (token.isNotBlank()) {
|
|
||||||
//// addHeader("Authorization", token)
|
|
||||||
//// }
|
|
||||||
//// }
|
|
||||||
//// .get()
|
|
||||||
//// .build()
|
|
||||||
//// HttpClient.instance.newCall(request).enqueue(CallbackImpl(doSuccess, doFailure))
|
|
||||||
//// }
|
|
||||||
//
|
|
||||||
// // POST表单(HTTPS)
|
|
||||||
//// fun postForm(url: String, params: Map<String, String>, callback: Callback) {
|
|
||||||
//// val formBody = FormBody.Builder().apply {
|
|
||||||
//// params.forEach { (k, v) -> add(k, v) }
|
|
||||||
//// }.build()
|
|
||||||
////
|
|
||||||
//// Request.Builder()
|
|
||||||
//// .url(url)
|
|
||||||
//// .post(formBody)
|
|
||||||
//// .build().let { HttpClient.instance.newCall(it).enqueue(callback) }
|
|
||||||
//// }
|
|
||||||
//
|
|
||||||
// // POST JSON(HTTPS)
|
|
||||||
// fun postJson(
|
|
||||||
// url: String,
|
|
||||||
// json: String,
|
|
||||||
// doSuccess: (data: Any) -> Unit,
|
|
||||||
// doFailure: (code: Int?, msg: String?) -> Unit
|
|
||||||
// ) {
|
|
||||||
// val body = json
|
|
||||||
// .toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
|
|
||||||
// val token = getToken()
|
|
||||||
// Request.Builder()
|
|
||||||
// .url(if (url.isHttpApi()) url else UrlConfig.BASE_URL + url)
|
|
||||||
// .apply {
|
|
||||||
// if (token.isNotBlank()) {
|
|
||||||
// addHeader("X-Access-Token", token)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .post(body)
|
|
||||||
// .build()
|
|
||||||
// .let { HttpClient.instance.newCall(it).enqueue(CallbackImpl(doSuccess, doFailure)) }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// val executor = Executors.newSingleThreadScheduledExecutor()
|
|
||||||
// var getTokenIsRunning = false
|
|
||||||
// private fun exeTokenThread() {
|
|
||||||
// if (getTokenIsRunning.not()) {
|
|
||||||
// executor.scheduleWithFixedDelay(
|
|
||||||
// task, 0, 1, TimeUnit.MINUTES
|
|
||||||
// )
|
|
||||||
// getTokenIsRunning = true
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// val task = Runnable {
|
|
||||||
// if (BaseApp.token.isNullOrBlank()) {
|
|
||||||
// getAppToken()
|
|
||||||
// } else {
|
|
||||||
// executor.shutdown()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun getToken() = SpTool.getString(SpTool.TOKEN)
|
|
||||||
////BaseApp.instance?.token ?:""
|
|
||||||
////BaseApp.getSharedPref()?.getString("token", "") ?: ""
|
|
||||||
//
|
|
||||||
// fun getAppToken() {
|
|
||||||
// val url = "${UrlConfig.GET_TOKEN}?qrcodeId=${SpTool.getString(SpTool.DEVICE_ID)}"
|
|
||||||
//// Log.d(TAG, "getToken: url = $url")
|
|
||||||
// get(url = url, doSuccess = {
|
|
||||||
// SpTool.put(SpTool.TOKEN, it)
|
|
||||||
// BaseApp.token = it.toString()
|
|
||||||
// }) { code, msg ->
|
|
||||||
// //toast(msg)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// class CallbackImpl(
|
|
||||||
// private val doSuccess: (data: Any) -> Unit,
|
|
||||||
// private val doFailure: (code: Int?, msg: String?) -> Unit
|
|
||||||
// ) :
|
|
||||||
// Callback {
|
|
||||||
// override fun onFailure(call: Call, e: IOException) {
|
|
||||||
// runMainThread {
|
|
||||||
// e.printStackTrace()
|
|
||||||
// doFailure(-1, "服务异常,${e.message}")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// override fun onResponse(call: Call, response: Response) {
|
|
||||||
// val respData = response.body?.string()
|
|
||||||
// runMainThread {
|
|
||||||
// Log.d("HttpUtil", respData ?: "")
|
|
||||||
// runCatching {
|
|
||||||
// val typeToken = object : TypeToken<BaseReq<Any>>() {}
|
|
||||||
// val baseReq = respData?.toType<BaseReq<Any>>(typeToken = typeToken)
|
|
||||||
// if (baseReq == null) {
|
|
||||||
// doFailure(-1, "查询数据失败")
|
|
||||||
// return@runCatching
|
|
||||||
// }
|
|
||||||
// if (baseReq.code != 200) {
|
|
||||||
// doFailure(baseReq.code, baseReq.message)
|
|
||||||
// //baseReq.code == 500 ||
|
|
||||||
// if (baseReq.code == 401 && loopGetToken) {
|
|
||||||
// exeTokenThread()
|
|
||||||
// }
|
|
||||||
// return@runCatching
|
|
||||||
// }
|
|
||||||
// doSuccess(baseReq.result ?: "")
|
|
||||||
// }.onFailure {
|
|
||||||
// it.printStackTrace()
|
|
||||||
// doFailure(-1, "解析异常")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
//fun String.isHttpApi() = this.startsWith("http://") || this.startsWith("https://")
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package com.shuwei.dish.match.http
|
|
||||||
|
|
||||||
//import com.shuwei.dish.match.base.BaseApp
|
|
||||||
|
|
||||||
object UrlConfig {
|
|
||||||
// private const val DEVICE_BASE_URL = "http://device.shuziweidao.com:8889"
|
|
||||||
// /**
|
|
||||||
// * device获取token
|
|
||||||
// */
|
|
||||||
// const val DEVICE_TOKEN = "${DEVICE_BASE_URL}/sys/getEquipmentToken"
|
|
||||||
// /**
|
|
||||||
// *获取配置信息
|
|
||||||
// */
|
|
||||||
// const val DEVICE_CONFIG = "${DEVICE_BASE_URL}/equipment/stEquipment/queryByEquipmentCode"
|
|
||||||
|
|
||||||
// const val BASE_URL = "http://vip.shuziweidao.com/shuwei-zhct"
|
|
||||||
// const val DISH_DETAIL = "$BASE_URL/scales/goodsUseList?foodId={foodId}&foodWeight={foodWeight}"
|
|
||||||
// const val DISH_LIST = "$BASE_URL/scales/getRestInfoFoods?eaId=99&type=0&foodName"
|
|
||||||
|
|
||||||
// const val BASE_URL = "http://192.168.1.207:9102"
|
|
||||||
// const val BASE_URL = "https://yyjk.shuziweidao.com/gateway"
|
|
||||||
const val BASE_URL = "https://yyjk.shuziweidao.com/gateway/"
|
|
||||||
// const val DISH_LIST = "$BASE_URL/scales/getRestInfoFoods?eaId=99&type=0&foodName"
|
|
||||||
|
|
||||||
|
|
||||||
// var DISH_DETAIL = "${BaseApp.configUrl}/food/stFoodInfoMatching/queryById"
|
|
||||||
//
|
|
||||||
// // "$BASE_URL/scales/generateToken?deviceId=1111111111111111111111111111"
|
|
||||||
//
|
|
||||||
// var GET_TOKEN = "${BaseApp.configUrl}/restaurant/equipment/stEquipment/getEquipmentToken"
|
|
||||||
// var SUBMIT_DISH = "${BaseApp.configUrl}/food/stFoodInfoMatching/saveoredit"
|
|
||||||
//
|
|
||||||
// var QUERY_GOODS_LIST = "${BaseApp.configUrl}/food/stFoodInfoMatching/queryGoodsInfoList"
|
|
||||||
// var QUERY_FOOD_LIST = "${BaseApp.configUrl}/food/stFoodInfoMatching/list"
|
|
||||||
// var SAMPLING_LIST = "${BaseApp.configUrl}/food/stFoodInfoMatching/queryHistorical/goodsInfoList"
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ package com.shuwei.dish.match.net
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.shuwei.dish.match.http.UrlConfig
|
import com.shuwei.dish.match.base.GlobalData
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
@@ -16,7 +16,7 @@ import kotlin.apply
|
|||||||
import kotlin.jvm.java
|
import kotlin.jvm.java
|
||||||
|
|
||||||
val apiService: ApiService = Retrofit.Builder()
|
val apiService: ApiService = Retrofit.Builder()
|
||||||
.baseUrl(UrlConfig.BASE_URL)
|
.baseUrl(GlobalData.appBaseUrl)
|
||||||
// .baseUrl(UrlConfig.DEVICE_BASE_URL)
|
// .baseUrl(UrlConfig.DEVICE_BASE_URL)
|
||||||
.client(HttpManager.instance.client)
|
.client(HttpManager.instance.client)
|
||||||
.addConverterFactory(GsonConverterFactory.create())
|
.addConverterFactory(GsonConverterFactory.create())
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import com.shuwei.dish.match.databinding.ActivityFoodRecognizeBinding
|
|||||||
import com.shuwei.dish.match.dialog.FoodSearchDialog
|
import com.shuwei.dish.match.dialog.FoodSearchDialog
|
||||||
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
|
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
|
||||||
import com.shuwei.dish.match.utils.ActivityManager
|
import com.shuwei.dish.match.utils.ActivityManager
|
||||||
import com.shuwei.dish.match.utils.AddressUtil
|
|
||||||
import com.shuwei.dish.match.utils.WeightUtil
|
import com.shuwei.dish.match.utils.WeightUtil
|
||||||
import com.shuwei.dish.match.utils.ext.clickWithDebounce
|
import com.shuwei.dish.match.utils.ext.clickWithDebounce
|
||||||
import com.shuwei.dish.match.utils.ext.gone
|
import com.shuwei.dish.match.utils.ext.gone
|
||||||
@@ -28,6 +27,7 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "FoodRecognizeActivity"
|
private const val TAG = "FoodRecognizeActivity"
|
||||||
private const val EXTRA_IMAGE_URI = "extra_image_uri"
|
private const val EXTRA_IMAGE_URI = "extra_image_uri"
|
||||||
|
private const val EXTRA_CURRENT_WEIGHT = "extra_current_weight"
|
||||||
const val EXTRA_SELECTED_ITEM = "extra_selected_item"
|
const val EXTRA_SELECTED_ITEM = "extra_selected_item"
|
||||||
const val IS_MANUAL_CANCEL = "isManualCancel"
|
const val IS_MANUAL_CANCEL = "isManualCancel"
|
||||||
|
|
||||||
@@ -44,12 +44,14 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
fun start(
|
fun start(
|
||||||
activity: BaseActivity,
|
activity: BaseActivity,
|
||||||
imageUri: String? = null,
|
imageUri: String? = null,
|
||||||
|
currentWeight: Double = 0.0,
|
||||||
goodsList: ArrayList<CookFoodGoodsEntity> = arrayListOf(),
|
goodsList: ArrayList<CookFoodGoodsEntity> = arrayListOf(),
|
||||||
finishCallback: (Boolean, CookFoodGoodsEntity?) -> Unit = { _, _ -> }
|
finishCallback: (Boolean, CookFoodGoodsEntity?) -> Unit = { _, _ -> }
|
||||||
) {
|
) {
|
||||||
pendingGoodsList = goodsList
|
pendingGoodsList = goodsList
|
||||||
val launchIntent = Intent(activity, FoodRecognizeActivity::class.java).apply {
|
val launchIntent = Intent(activity, FoodRecognizeActivity::class.java).apply {
|
||||||
putExtra(EXTRA_IMAGE_URI, imageUri)
|
putExtra(EXTRA_IMAGE_URI, imageUri)
|
||||||
|
putExtra(EXTRA_CURRENT_WEIGHT, currentWeight)
|
||||||
}
|
}
|
||||||
activity.startActivity(launchIntent) { resultIntent ->
|
activity.startActivity(launchIntent) { resultIntent ->
|
||||||
val isManualCancel = resultIntent?.getBooleanExtra(IS_MANUAL_CANCEL, false) ?: false
|
val isManualCancel = resultIntent?.getBooleanExtra(IS_MANUAL_CANCEL, false) ?: false
|
||||||
@@ -145,6 +147,8 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
if (!imageUri.isNullOrBlank()) {
|
if (!imageUri.isNullOrBlank()) {
|
||||||
binding.ivFoodPhoto.setImageURI(imageUri.toUri())
|
binding.ivFoodPhoto.setImageURI(imageUri.toUri())
|
||||||
}
|
}
|
||||||
|
currentWeight = intent.getDoubleExtra(EXTRA_CURRENT_WEIGHT, 0.0)
|
||||||
|
binding.tvWeight.text = "${currentWeight}g"
|
||||||
|
|
||||||
// 取出内存缓存的食材列表,取用后立即清空
|
// 取出内存缓存的食材列表,取用后立即清空
|
||||||
val goodsList = pendingGoodsList?.also { pendingGoodsList = null }
|
val goodsList = pendingGoodsList?.also { pendingGoodsList = null }
|
||||||
@@ -153,6 +157,7 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
// 默认选中第一项
|
// 默认选中第一项
|
||||||
selectedPosition = 0
|
selectedPosition = 0
|
||||||
list[0].isClicked = true
|
list[0].isClicked = true
|
||||||
|
list[0].useWeight = currentWeight
|
||||||
binding.tvSelectedFood.text = list[0].goodsName ?: "-"
|
binding.tvSelectedFood.text = list[0].goodsName ?: "-"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,40 +227,40 @@ class FoodRecognizeActivity : BaseActivity() {
|
|||||||
* 低于阈值说明食材已被取走,保留上次有效值等待页面关闭
|
* 低于阈值说明食材已被取走,保留上次有效值等待页面关闭
|
||||||
*/
|
*/
|
||||||
private fun registerWeightListener() {
|
private fun registerWeightListener() {
|
||||||
WeightUtil.addWeightListener(TAG) { address, state, weight ->
|
// WeightUtil.addWeightListener(TAG) { address, state, weight ->
|
||||||
if (address == AddressUtil.ONE && state == WeightUtil.STATE_STABLE) {
|
// if (address == AddressUtil.ONE && state == WeightUtil.STATE_STABLE) {
|
||||||
runOnUiThread {
|
// runOnUiThread {
|
||||||
currentWeight = weight
|
// currentWeight = weight
|
||||||
binding.tvWeight.run {
|
// binding.tvWeight.run {
|
||||||
if (tag != weight) {
|
// if (tag != weight) {
|
||||||
text = "$weight g"
|
// text = "$weight g"
|
||||||
tag = weight
|
// tag = weight
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if (weight >= PrepareCookActivity.WEIGHT_RECOGNIZE_VALUE) {
|
// if (weight >= PrepareCookActivity.WEIGHT_RECOGNIZE_VALUE) {
|
||||||
adapter.items.firstOrNull { it.isClicked }?.let { item ->
|
// adapter.items.firstOrNull { it.isClicked }?.let { item ->
|
||||||
if (pendingWeight != weight) {
|
// if (pendingWeight != weight) {
|
||||||
// 无论增减,均延迟更新,避免手拿物品时的瞬时增重干扰
|
// // 无论增减,均延迟更新,避免手拿物品时的瞬时增重干扰
|
||||||
cancelPendingWeightUpdate()
|
// cancelPendingWeightUpdate()
|
||||||
pendingWeight = weight
|
// pendingWeight = weight
|
||||||
pendingWeightRunnable = Runnable {
|
// pendingWeightRunnable = Runnable {
|
||||||
item.useWeight = weight
|
// item.useWeight = weight
|
||||||
pendingWeightRunnable = null
|
// pendingWeightRunnable = null
|
||||||
}.also { handler.postDelayed(it, 3000) }
|
// }.also { handler.postDelayed(it, 3000) }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消待执行的延迟重量更新任务
|
* 取消待执行的延迟重量更新任务
|
||||||
*/
|
*/
|
||||||
private fun cancelPendingWeightUpdate() {
|
private fun cancelPendingWeightUpdate() {
|
||||||
pendingWeightRunnable?.let { handler.removeCallbacks(it) }
|
// pendingWeightRunnable?.let { handler.removeCallbacks(it) }
|
||||||
pendingWeightRunnable = null
|
// pendingWeightRunnable = null
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ class PrepareCookActivity : BaseActivity() {
|
|||||||
if (weight < WEIGHT_RECOGNIZE_VALUE) {
|
if (weight < WEIGHT_RECOGNIZE_VALUE) {
|
||||||
showRecognizePage = false
|
showRecognizePage = false
|
||||||
manualCancelFlag = false
|
manualCancelFlag = false
|
||||||
|
lastWeight = weight
|
||||||
FoodRecognizeActivity.close()
|
FoodRecognizeActivity.close()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -515,9 +516,11 @@ class PrepareCookActivity : BaseActivity() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
lastWeight = currentWeight
|
||||||
FoodRecognizeActivity.start(
|
FoodRecognizeActivity.start(
|
||||||
activity = this,
|
activity = this,
|
||||||
imageUri = lastPhotoUri?.toString(),
|
imageUri = lastPhotoUri?.toString(),
|
||||||
|
currentWeight = currentWeight,
|
||||||
goodsList = ArrayList(foodList),
|
goodsList = ArrayList(foodList),
|
||||||
finishCallback = { isManualCancel, entity ->
|
finishCallback = { isManualCancel, entity ->
|
||||||
Log.d(TAG, "queryFood: isManualCancel=$isManualCancel, entity=${entity.toJsonString()}")
|
Log.d(TAG, "queryFood: isManualCancel=$isManualCancel, entity=${entity.toJsonString()}")
|
||||||
|
|||||||
@@ -100,7 +100,11 @@ class SlaveActivity : BaseActivity() {
|
|||||||
flexWrap = FlexWrap.WRAP
|
flexWrap = FlexWrap.WRAP
|
||||||
alignItems = AlignItems.FLEX_START
|
alignItems = AlignItems.FLEX_START
|
||||||
}
|
}
|
||||||
scale22Adapter = Seasoning22GridAdapter(size.largeSize, size.smallSize, showNameOnly = false).also {
|
scale22Adapter = Seasoning22GridAdapter(
|
||||||
|
size.largeSize,
|
||||||
|
size.smallSize,
|
||||||
|
showNameOnly = false
|
||||||
|
).also {
|
||||||
it.onItemClick = { scale, _ -> showTareDialog(scale.address) }
|
it.onItemClick = { scale, _ -> showTareDialog(scale.address) }
|
||||||
}
|
}
|
||||||
binding.rvScaleList.adapter = scale22Adapter
|
binding.rvScaleList.adapter = scale22Adapter
|
||||||
@@ -158,7 +162,7 @@ class SlaveActivity : BaseActivity() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 监听主设备下发的调料配置同步事件
|
* 监听主设备下发的调料配置同步事件
|
||||||
* 收到后更新 adapter 并写入本地 Room
|
* 收到后以全量替换方式更新本机 DB(先删后插),再刷新 UI
|
||||||
*/
|
*/
|
||||||
private fun listenSeasoningConfig() {
|
private fun listenSeasoningConfig() {
|
||||||
ScaleServiceManager.onSeasoningConfig = onSeasoningConfig@{ event ->
|
ScaleServiceManager.onSeasoningConfig = onSeasoningConfig@{ event ->
|
||||||
@@ -167,6 +171,8 @@ class SlaveActivity : BaseActivity() {
|
|||||||
?.map { SeasoningSlotEntity(it.deviceId, it.address, it.goodsId, it.goodsName) }
|
?.map { SeasoningSlotEntity(it.deviceId, it.address, it.goodsId, it.goodsName) }
|
||||||
?: return@onSeasoningConfig
|
?: return@onSeasoningConfig
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
// 全量替换:先删除本机所有旧槽位,再写入最新配置
|
||||||
|
appViewModel.deleteAllSlotsByDeviceId(GlobalData.deviceId)
|
||||||
appViewModel.upsertAllSeasoningSlots(mySlots)
|
appViewModel.upsertAllSeasoningSlots(mySlots)
|
||||||
applySlots(mySlots)
|
applySlots(mySlots)
|
||||||
}
|
}
|
||||||
@@ -175,9 +181,38 @@ class SlaveActivity : BaseActivity() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 将槽位配置应用到对应 adapter(按地址匹配格子位置)
|
* 将槽位配置应用到对应 adapter(按地址匹配格子位置)
|
||||||
* 同时更新 slotNameMap,确保后续重量回调构造 ScaleData 时携带正确名称
|
* 同时以传入列表为准全量刷新 slotNameMap:
|
||||||
|
* - 新列表中存在的地址:更新名称
|
||||||
|
* - 新列表中不存在的地址:从 slotNameMap 移除,并清空对应格子名称
|
||||||
*/
|
*/
|
||||||
private fun applySlots(slots: List<SeasoningSlotEntity>) {
|
private fun applySlots(slots: List<SeasoningSlotEntity>) {
|
||||||
|
val newAddresses = slots.map { it.address }.toSet()
|
||||||
|
|
||||||
|
// 清除已被删除的槽位:从 slotNameMap 移除并清空 adapter 对应格子
|
||||||
|
val removedAddresses = slotNameMap.keys.filter { it !in newAddresses }
|
||||||
|
removedAddresses.forEach { addr ->
|
||||||
|
slotNameMap.remove(addr)
|
||||||
|
when {
|
||||||
|
scale22Adapter != null -> {
|
||||||
|
val pos = ScaleDeviceConfig.SCALE_ORDER_22.indexOf(addr)
|
||||||
|
if (pos >= 0) scale22Adapter!!.updateItemName(pos, "")
|
||||||
|
}
|
||||||
|
scale18Adapter != null -> {
|
||||||
|
val pos = ScaleDeviceConfig.SCALE_ORDER_18.indexOf(addr)
|
||||||
|
if (pos >= 0) scale18Adapter!!.updateItemName(pos, "")
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val list = linearAdapter.items.toMutableList()
|
||||||
|
val idx = list.indexOfFirst { it.address == addr }
|
||||||
|
if (idx >= 0) {
|
||||||
|
list[idx] = list[idx].copy(name = "")
|
||||||
|
linearAdapter.submitList(list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新新列表中存在的槽位
|
||||||
slots.forEach { slot ->
|
slots.forEach { slot ->
|
||||||
slotNameMap[slot.address] = slot.goodsName
|
slotNameMap[slot.address] = slot.goodsName
|
||||||
when {
|
when {
|
||||||
@@ -190,7 +225,6 @@ class SlaveActivity : BaseActivity() {
|
|||||||
if (pos >= 0) scale18Adapter!!.updateItemName(pos, slot.goodsName)
|
if (pos >= 0) scale18Adapter!!.updateItemName(pos, slot.goodsName)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// 线性列表模式(子设备1):刷新已显示条目的名称
|
|
||||||
val list = linearAdapter.items.toMutableList()
|
val list = linearAdapter.items.toMutableList()
|
||||||
val idx = list.indexOfFirst { it.address == slot.address }
|
val idx = list.indexOfFirst { it.address == slot.address }
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
@@ -290,7 +324,7 @@ class SlaveActivity : BaseActivity() {
|
|||||||
/** 重量增加阈值(克),超过此值才触发高亮 */
|
/** 重量增加阈值(克),超过此值才触发高亮 */
|
||||||
private const val WEIGHT_DELTA_THRESHOLD = 10.0
|
private const val WEIGHT_DELTA_THRESHOLD = 10.0
|
||||||
/** 高亮自动恢复时间(毫秒) */
|
/** 高亮自动恢复时间(毫秒) */
|
||||||
private const val HIGHLIGHT_DURATION_MS = 15_000L
|
private const val HIGHLIGHT_DURATION_MS = 5_000L
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ import com.shuwei.dish.match.base.BaseFragment
|
|||||||
import com.shuwei.dish.match.base.DeviceRole
|
import com.shuwei.dish.match.base.DeviceRole
|
||||||
import com.shuwei.dish.match.base.GlobalData
|
import com.shuwei.dish.match.base.GlobalData
|
||||||
import com.shuwei.dish.match.databinding.FragmentSeasoningConfigBinding
|
import com.shuwei.dish.match.databinding.FragmentSeasoningConfigBinding
|
||||||
|
import com.shuwei.dish.match.dialog.CommonDialog
|
||||||
import com.shuwei.dish.match.dialog.SeasoningSelectDialog
|
import com.shuwei.dish.match.dialog.SeasoningSelectDialog
|
||||||
import com.shuwei.dish.match.entity.SeasoningEntity
|
|
||||||
import com.shuwei.dish.match.entity.SeasoningSlotEntity
|
import com.shuwei.dish.match.entity.SeasoningSlotEntity
|
||||||
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
import com.shuwei.dish.match.scale.ScaleDeviceConfig
|
||||||
import com.shuwei.dish.match.scale.ScaleEvent
|
import com.shuwei.dish.match.scale.ScaleEvent
|
||||||
@@ -39,7 +39,7 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
|||||||
private const val WEIGHT_DELTA_THRESHOLD = 10.0
|
private const val WEIGHT_DELTA_THRESHOLD = 10.0
|
||||||
|
|
||||||
/** 高亮自动恢复时间(毫秒) */
|
/** 高亮自动恢复时间(毫秒) */
|
||||||
private const val HIGHLIGHT_DURATION_MS = 15_000L
|
private const val HIGHLIGHT_DURATION_MS = 5_000L
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun inflateBinding(
|
override fun inflateBinding(
|
||||||
@@ -80,82 +80,164 @@ class SeasoningConfigFragment : BaseFragment<FragmentSeasoningConfigBinding>() {
|
|||||||
/** 绑定油盆格子点击,弹出调料选择弹窗并保存到 DEVICE_ID_1 */
|
/** 绑定油盆格子点击,弹出调料选择弹窗并保存到 DEVICE_ID_1 */
|
||||||
private fun bindOilSlotClick() {
|
private fun bindOilSlotClick() {
|
||||||
binding.tvOilSlot.setOnClickListener {
|
binding.tvOilSlot.setOnClickListener {
|
||||||
openSeasoningSelectDialog(0) { item ->
|
handleSlotClick(
|
||||||
binding.tvOilSlot.text = item.goodsName ?: "-"
|
currentName = binding.tvOilSlot.text.toString().takeIf { it != "-" },
|
||||||
saveSlotAndBroadcast(
|
onSelect = {
|
||||||
SeasoningSlotEntity(
|
selectAndSave(ScaleDeviceConfig.DEVICE_ID_1, 1) { name ->
|
||||||
deviceId = ScaleDeviceConfig.DEVICE_ID_1,
|
binding.tvOilSlot.text = name.ifBlank { "-" }
|
||||||
address = 1,
|
}
|
||||||
goodsId = item.goodsId,
|
},
|
||||||
goodsName = item.goodsName ?: ""
|
onClear = {
|
||||||
)
|
binding.tvOilSlot.text = "-"
|
||||||
)
|
deleteSlotAndBroadcast(ScaleDeviceConfig.DEVICE_ID_1, 1)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 绑定两个 Adapter 的格子点击,弹出调料选择弹窗 */
|
/** 绑定两个 Adapter 的格子点击,弹出调料选择弹窗 */
|
||||||
private fun bindAdapterClicks() {
|
private fun bindAdapterClicks() {
|
||||||
adapter22.onItemClick = { scaleData, position ->
|
bindSlotAdapterClick(
|
||||||
openSeasoningSelectDialog(position) { item ->
|
adapter = adapter22,
|
||||||
adapter22.updateItemName(position, item.goodsName ?: "")
|
deviceId = ScaleDeviceConfig.DEVICE_ID_22
|
||||||
saveSlotAndBroadcast(
|
)
|
||||||
SeasoningSlotEntity(
|
bindSlotAdapterClick(
|
||||||
deviceId = ScaleDeviceConfig.DEVICE_ID_22,
|
adapter = adapter18,
|
||||||
address = scaleData.address,
|
deviceId = ScaleDeviceConfig.DEVICE_ID_18
|
||||||
goodsId = item.goodsId,
|
)
|
||||||
goodsName = item.goodsName ?: ""
|
}
|
||||||
)
|
|
||||||
|
/** 为指定 Adapter 绑定格子点击逻辑(已配置弹操作弹窗,未配置直接选择) */
|
||||||
|
private fun bindSlotAdapterClick(
|
||||||
|
adapter: Any,
|
||||||
|
deviceId: String
|
||||||
|
) {
|
||||||
|
when (adapter) {
|
||||||
|
is Seasoning22GridAdapter -> adapter.onItemClick = { scaleData, position ->
|
||||||
|
handleSlotClick(
|
||||||
|
currentName = scaleData.name,
|
||||||
|
onSelect = {
|
||||||
|
selectAndSave(deviceId, scaleData.address) { name ->
|
||||||
|
adapter.updateItemName(position, name)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClear = {
|
||||||
|
adapter.updateItemName(position, "")
|
||||||
|
deleteSlotAndBroadcast(deviceId, scaleData.address)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is Seasoning18GridAdapter -> adapter.onItemClick = { scaleData, position ->
|
||||||
|
handleSlotClick(
|
||||||
|
currentName = scaleData.name,
|
||||||
|
onSelect = {
|
||||||
|
selectAndSave(deviceId, scaleData.address) { name ->
|
||||||
|
adapter.updateItemName(position, name)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClear = {
|
||||||
|
adapter.updateItemName(position, "")
|
||||||
|
deleteSlotAndBroadcast(deviceId, scaleData.address)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
adapter18.onItemClick = { scaleData, position ->
|
}
|
||||||
openSeasoningSelectDialog(position) { item ->
|
|
||||||
adapter18.updateItemName(position, item.goodsName ?: "")
|
/**
|
||||||
saveSlotAndBroadcast(
|
* 格子点击分发:已配置则弹操作弹窗,未配置直接执行选择
|
||||||
SeasoningSlotEntity(
|
* @param currentName 格子当前调料名称
|
||||||
deviceId = ScaleDeviceConfig.DEVICE_ID_18,
|
* @param onSelect 执行选择(新增或重新选择)
|
||||||
address = scaleData.address,
|
* @param onClear 执行清除
|
||||||
goodsId = item.goodsId,
|
*/
|
||||||
goodsName = item.goodsName ?: ""
|
private fun handleSlotClick(
|
||||||
)
|
currentName: String?,
|
||||||
)
|
onSelect: () -> Unit,
|
||||||
}
|
onClear: () -> Unit
|
||||||
|
) {
|
||||||
|
if (!currentName.isNullOrBlank()) {
|
||||||
|
showSlotActionDialog(goodsName = currentName, onReselect = onSelect, onClear = onClear)
|
||||||
|
} else {
|
||||||
|
onSelect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 打开调料选择弹窗
|
* 打开调料选择弹窗
|
||||||
*
|
|
||||||
* @param position 格子位置
|
|
||||||
* @param callback 调料选择回调
|
* @param callback 调料选择回调
|
||||||
*/
|
*/
|
||||||
private fun openSeasoningSelectDialog(
|
/**
|
||||||
position: Int,
|
* 打开选择弹窗,选中后更新 UI 并保存槽位到 Room 广播
|
||||||
callback: (item: SeasoningEntity) -> Unit
|
* @param deviceId 目标设备 ID
|
||||||
|
* @param address 秤硬件地址
|
||||||
|
* @param onUpdateName 选中后更新格子显示名称的回调
|
||||||
|
*/
|
||||||
|
private fun selectAndSave(
|
||||||
|
deviceId: String,
|
||||||
|
address: Int,
|
||||||
|
onUpdateName: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
SeasoningSelectDialog(
|
SeasoningSelectDialog(
|
||||||
activity = currentActivity,
|
activity = currentActivity,
|
||||||
clickIndex = position,
|
onItemSelected = { item ->
|
||||||
onItemSelected = callback
|
val name = item.goodsName ?: ""
|
||||||
|
onUpdateName(name)
|
||||||
|
saveSlotAndBroadcast(
|
||||||
|
SeasoningSlotEntity(
|
||||||
|
deviceId = deviceId,
|
||||||
|
address = address,
|
||||||
|
goodsId = item.goodsId,
|
||||||
|
goodsName = name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** 将单个槽位写入 Room,再广播全量配置给子设备 */
|
||||||
* 将单个槽位写入 Room,再读取全量配置广播给子设备
|
|
||||||
* 仅主设备执行广播,子设备不调用此方法
|
|
||||||
*/
|
|
||||||
private fun saveSlotAndBroadcast(slot: SeasoningSlotEntity) {
|
private fun saveSlotAndBroadcast(slot: SeasoningSlotEntity) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
currentActivity.appViewModel.upsertSeasoningSlot(slot)
|
currentActivity.appViewModel.upsertSeasoningSlot(slot)
|
||||||
// 读取全量配置,通过 wsClient 推送给所有已连接子设备
|
broadcastAllSlots()
|
||||||
val allSlots = currentActivity.appViewModel.loadSeasoningSlot().map {
|
|
||||||
SlotConfig(it.deviceId, it.address, it.goodsId, it.goodsName)
|
|
||||||
}
|
|
||||||
ScaleServiceManager.sendSeasoningConfig(allSlots)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 弹出已配置格子的操作选择弹窗(3按钮)
|
||||||
|
* @param goodsName 当前格子已配置的调料名称
|
||||||
|
* @param onReselect 用户点击「重新选择」后的回调
|
||||||
|
* @param onClear 用户点击「清除配置」后的回调
|
||||||
|
*/
|
||||||
|
private fun showSlotActionDialog(
|
||||||
|
goodsName: String,
|
||||||
|
onReselect: () -> Unit,
|
||||||
|
onClear: () -> Unit
|
||||||
|
) {
|
||||||
|
CommonDialog(currentActivity)
|
||||||
|
.setTitle("调料操作")
|
||||||
|
.setContent("当前配置:$goodsName")
|
||||||
|
.setNegativeButton("取消")
|
||||||
|
.setNeutralButton("重新选择") { onReselect() }
|
||||||
|
.setPositiveButton("清除配置") { onClear() }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 从 Room 删除指定槽位,再广播全量配置给子设备 */
|
||||||
|
private fun deleteSlotAndBroadcast(deviceId: String, address: Int) {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
currentActivity.appViewModel.deleteSeasoningSlot(deviceId, address)
|
||||||
|
broadcastAllSlots()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 读取全量槽位配置并通过 wsClient 推送给所有已连接子设备 */
|
||||||
|
private suspend fun broadcastAllSlots() {
|
||||||
|
val allSlots = currentActivity.appViewModel.loadSeasoningSlot().map {
|
||||||
|
SlotConfig(it.deviceId, it.address, it.goodsId, it.goodsName)
|
||||||
|
}
|
||||||
|
ScaleServiceManager.sendSeasoningConfig(allSlots)
|
||||||
|
}
|
||||||
|
|
||||||
// /** 初始化 ViewModel */
|
// /** 初始化 ViewModel */
|
||||||
// private fun initViewModel() {
|
// private fun initViewModel() {
|
||||||
// val factory = AppFactory(DbRepository(BaseApp.instance!!.database.appDao()))
|
// val factory = AppFactory(DbRepository(BaseApp.instance!!.database.appDao()))
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.shuwei.dish.match.utils
|
|||||||
|
|
||||||
import com.shuwei.dish.match.base.BaseApp
|
import com.shuwei.dish.match.base.BaseApp
|
||||||
import com.shuwei.dish.match.utils.ext.dp
|
import com.shuwei.dish.match.utils.ext.dp
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
data class FlexLayoutSize(
|
data class FlexLayoutSize(
|
||||||
val smallSize: Int,
|
val smallSize: Int,
|
||||||
@@ -11,14 +12,28 @@ data class FlexLayoutSize(
|
|||||||
|
|
||||||
object SizeTool {
|
object SizeTool {
|
||||||
|
|
||||||
fun getFlexLayoutSize(leftRightSpace: Int = 10.dp): FlexLayoutSize {
|
const val DEFAULT_CELL_ASPECT_RATIO = 1.26f
|
||||||
|
|
||||||
|
fun getFlexLayoutSize(
|
||||||
|
leftRightSpace: Int,
|
||||||
|
cellAspectRatio: Float = DEFAULT_CELL_ASPECT_RATIO,
|
||||||
|
cellMargin: Int = 2.dp
|
||||||
|
): FlexLayoutSize {
|
||||||
// 上层:FlexboxLayoutManager,仿 MasterScaleActivity 中 Scale22VH 的布局方式
|
// 上层:FlexboxLayoutManager,仿 MasterScaleActivity 中 Scale22VH 的布局方式
|
||||||
// rvTop 高度 = 小格子尺寸 * 3行 + 3 * 间距,与 Scale22VH 保持一致
|
// rvTop 高度需覆盖:
|
||||||
|
// 1) 小格列(3行)总高度
|
||||||
|
// 2) 大格列(2行)总高度(考虑四舍五入误差)
|
||||||
val screenWidth = BaseApp.instance!!.resources.displayMetrics.widthPixels
|
val screenWidth = BaseApp.instance!!.resources.displayMetrics.widthPixels
|
||||||
val usable = screenWidth - 8 * 4.dp - leftRightSpace
|
val usable = screenWidth - 8 * 4.dp - leftRightSpace
|
||||||
val smallSize = usable / 9
|
val smallSize = usable / 9
|
||||||
val largeSize = (usable - smallSize * 6) / 2
|
val largeSize = (usable - smallSize * 6) / 2
|
||||||
val rvHeight = smallSize * 3 + 3 * 4.dp
|
val smallHeight = (smallSize * cellAspectRatio).toInt()
|
||||||
|
val largeHeight = (largeSize * cellAspectRatio).toInt()
|
||||||
|
|
||||||
|
val smallColumnHeight = smallHeight * 3 + cellMargin * 6
|
||||||
|
val largeColumnHeight = largeHeight * 2 + cellMargin * 4
|
||||||
|
val rvHeight = max(smallColumnHeight, largeColumnHeight)
|
||||||
|
|
||||||
return FlexLayoutSize(smallSize, largeSize, rvHeight)
|
return FlexLayoutSize(smallSize, largeSize, rvHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- 秤格子背景:白色底 + 灰色描边 + 圆角,与页面蓝色背景形成对比 -->
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="#FA9523" />
|
||||||
|
<corners android:radius="8dp" />
|
||||||
|
</shape>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
tools:background="@color/white"
|
tools:background="@color/white"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
@@ -15,6 +15,16 @@
|
|||||||
android:layout_marginTop="30dp"
|
android:layout_marginTop="30dp"
|
||||||
android:background="@drawable/shape_gray_dc_5" />
|
android:background="@drawable/shape_gray_dc_5" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvSheetName"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="30dp"
|
||||||
|
android:textColor="@color/black333"
|
||||||
|
android:textSize="36sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="调料检索" />
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="100dp"
|
android:layout_height="100dp"
|
||||||
@@ -57,8 +67,10 @@
|
|||||||
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
<com.scwang.smart.refresh.layout.SmartRefreshLayout
|
||||||
android:id="@+id/refreshLayout"
|
android:id="@+id/refreshLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="700dp"
|
||||||
android:layout_marginTop="10dp"
|
android:layout_marginTop="10dp"
|
||||||
|
android:layout_marginStart="45dp"
|
||||||
|
android:layout_marginEnd="45dp"
|
||||||
app:srlEnableOverScrollDrag="false">
|
app:srlEnableOverScrollDrag="false">
|
||||||
|
|
||||||
<com.scwang.smart.refresh.header.ClassicsHeader
|
<com.scwang.smart.refresh.header.ClassicsHeader
|
||||||
@@ -69,11 +81,8 @@
|
|||||||
android:id="@+id/recyclerView"
|
android:id="@+id/recyclerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginStart="45dp"
|
|
||||||
android:layout_marginEnd="45dp"
|
|
||||||
android:layout_marginTop="15dp"
|
android:layout_marginTop="15dp"
|
||||||
android:layout_marginBottom="15dp"
|
android:layout_marginBottom="15dp"
|
||||||
android:minHeight="380dp"
|
|
||||||
android:nestedScrollingEnabled="true"
|
android:nestedScrollingEnabled="true"
|
||||||
android:overScrollMode="never"
|
android:overScrollMode="never"
|
||||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/llScaleContainer"
|
android:id="@+id/llScaleContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="260dp"
|
android:layout_height="200dp"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
|
|||||||
Reference in New Issue
Block a user