增加初始化功能、采样菜品搜索功能优化、布局移动到layout目录

This commit is contained in:
2025-09-11 16:00:04 +08:00
parent 8c6d1bae99
commit a3690e7ef0
61 changed files with 1455 additions and 243 deletions
+2 -2
View File
@@ -4,10 +4,10 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-08-18T06:18:22.594579400Z">
<DropdownSelection timestamp="2025-09-10T07:28:30.961571900Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="Default" identifier="serial=192.168.1.185:5555;connection=2158168e" />
<DeviceId pluginId="Default" identifier="serial=192.168.1.21:5555;connection=bc7af9a5" />
</handle>
</Target>
</DropdownSelection>
+10 -25
View File
@@ -17,13 +17,11 @@ android {
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
ndk {
//设置支持的SO库架构
abiFilters.addAll(listOf("armeabi", "armeabi-v7a"))
//, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
}
resConfigs("sw800dp")
// ndk {
// //设置支持的SO库架构
// abiFilters.addAll(listOf("armeabi", "armeabi-v7a"))
// //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
// }
}
signingConfigs {
create("debug_507") {
@@ -71,15 +69,7 @@ android {
)
}
}
lint {
disable.add("MissingDefaultResource")
}
// productFlavors {
// create("sw800dp") {
// dimension = "screen"
//// resConfigs("en", "xxhdpi") // 可选额外配置
// }
// }
// li
}
dependencies {
@@ -88,6 +78,8 @@ dependencies {
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.gridlayout)
implementation(libs.android.core)
implementation(libs.core)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
@@ -113,15 +105,8 @@ dependencies {
implementation("io.github.scwang90:refresh-header-classics:3.0.0-alpha")
//经典刷新头
implementation("io.github.scwang90:refresh-header-radar:3.0.0-alpha")
// //雷达刷新头
// implementation("io.github.scwang90:refresh-header-falsify:3.0.0-alpha")
// //虚拟刷新头
// implementation("io.github.scwang90:refresh-header-material:3.0.0-alpha")
// //谷歌刷新头
// implementation("io.github.scwang90:refresh-header-two-level:3.0.0-alpha")
// //二级刷新头
// implementation("io.github.scwang90:refresh-footer-ball:3.0.0-alpha")
//球脉冲加载
implementation("io.github.scwang90:refresh-footer-classics:3.0.0-alpha")
//经典加载
//implementation("com.geyifeng.immersionbar:immersionbar:3.2.2")
}
Binary file not shown.
Binary file not shown.
+10 -4
View File
@@ -7,8 +7,11 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
@@ -27,16 +30,16 @@
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:icon="@mipmap/ic_logo512"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher_round"
android:roundIcon="@mipmap/ic_logo512"
android:supportsRtl="true"
android:theme="@style/Theme.DishMatch"
android:usesCleartextTraffic="true">
<activity
android:name=".ui.HomeActivity"
android:name=".ui.InitActivity"
android:exported="true"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity">
@@ -46,6 +49,9 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".ui.HomeActivity"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".ui.SelectDishActivity"
@@ -99,7 +99,7 @@ class TextCellAdapter(var list: MutableList<SeasoningEntity>) :
}
fun getTextSpan(item: SeasoningEntity): SpannableStringBuilder {
val endIndex = if (item.sort == 10 || item.sort == 14) 10 else 6
val endIndex = if (item.sort == 9) 10 else 6
var name = if (item.goodsName.isNullOrBlank()) "未配置" else item.goodsName!!
if (name.length > endIndex) {
name = "${name.substring(0, endIndex)}"
@@ -14,9 +14,6 @@ class BaseApp : Application() {
val database by lazy { DatabaseProvider(this).instance }
var token: String? = null
var androidId: String? = null
override fun onCreate() {
super.onCreate()
instance = this
@@ -29,6 +26,10 @@ class BaseApp : Application() {
// const val canteenId = "1678234139391512577"
var canteenId = "0"
var configUrl = ""
var token: String? = null
var deviceId: String? = null
@Volatile
private var sharedPref: SharedPreferences? = null
@@ -30,7 +30,8 @@ import com.shuwei.dish.match.utils.ext.toast
* @param goodsType 1-调料,0-食材
*/
class BottomDialog(
private var goodsType: Int = 1
private var goodsType: Int = 1,
private var defGoodsName:String? = null
) {
companion object {
const val TAG = "BottomDialog"
@@ -115,6 +116,13 @@ class BottomDialog(
}
binding.refreshLayout.setEnableRefresh(false)
binding.refreshLayout.setEnableLoadMore(false)
if (defGoodsName.isNullOrBlank().not()) {
binding.etSheetInput.let {
it.setText(defGoodsName)
searchGoods(it)
}
}
}
private fun searchGoods(v: View) {
@@ -17,7 +17,6 @@ import androidx.core.view.isEmpty
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.aithings.Weigher
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.SeasoningSearchAdapter
@@ -130,7 +129,7 @@ class BottomDialog2(
}
binding.refreshLayout.setEnableRefresh(false)
binding.tvClear.setOnClickListener {
Weigher.tareTwo(weighAddress)
WeightUtil.tareTwo(weighAddress)
}
WeightUtil.addWeightListener { address, state, weight ->
if (weighAddress == address) {
@@ -14,14 +14,18 @@ object Loading {
private var dialog: LoadingDialog? = null
fun show(context: Context) {
if (dialog?.isShowing == true) {
dialog?.dismiss()
}
try {
if (dialog?.isShowing == true) {
dialog?.dismiss()
}
if (dialog == null) {
dialog = LoadingDialog(context)
if (dialog == null) {
dialog = LoadingDialog(context)
}
dialog?.show()
} catch (e: Exception) {
e.printStackTrace()
}
dialog?.show()
}
fun dismiss() {
@@ -1,5 +1,6 @@
package com.shuwei.dish.match.http
import android.annotation.SuppressLint
import android.os.Handler
import android.os.Looper
import android.util.Log
@@ -7,9 +8,11 @@ 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.http.HttpUtil.runMainThread
import com.shuwei.dish.match.utils.SpTool
import com.shuwei.dish.match.utils.ext.put
import com.shuwei.dish.match.utils.ext.toType
import com.shuwei.dish.match.utils.ext.toast
import kotlinx.coroutines.Runnable
import okhttp3.Call
import okhttp3.Callback
import okhttp3.HttpUrl
@@ -18,6 +21,8 @@ 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 {
@@ -31,50 +36,57 @@ object HttpUtil {
// GET请求(HTTPS
fun get(
url: String,
header: Map<String, String>?=null,
doSuccess: (data: Any) -> Unit,
doFailure: (code: Int?, msg: String?) -> Unit
) {
val token = getToken()
val request = Request.Builder()
.url(url)
.apply {
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)
if (header != null) {
header.forEach {
addHeader(it.value, 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) {
@@ -110,57 +122,79 @@ object HttpUtil {
.let { HttpClient.instance.newCall(it).enqueue(CallbackImpl(doSuccess, doFailure)) }
}
}
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}")
val executor = Executors.newSingleThreadScheduledExecutor()
var getTokenIsRunning = false
private fun exeTokenThread() {
if (getTokenIsRunning.not()) {
executor.scheduleWithFixedDelay(
task, 0, 1, TimeUnit.MINUTES
)
getTokenIsRunning = true
}
}
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)
if (baseReq.code == 500 || baseReq.code == 401) {
getAppToken()
}
return@runCatching
}
doSuccess(baseReq.result ?: "")
}.onFailure {
it.printStackTrace()
doFailure(-1, "解析异常")
}
val task = Runnable {
if (BaseApp.token.isNullOrBlank()) {
getAppToken()
} else {
executor.shutdown()
}
}
}
private fun getToken() = BaseApp.instance?.token ?:""
fun getToken() = SpTool.getString(SpTool.TOKEN)
//BaseApp.instance?.token ?:""
//BaseApp.getSharedPref()?.getString("token", "") ?: ""
private fun getAppToken() {
val url = "${UrlConfig.GET_TOKEN}?qrcodeId=${BaseApp.instance?.androidId}"
fun getAppToken() {
val url = "${UrlConfig.GET_TOKEN}?qrcodeId=${SpTool.getString(SpTool.DEVICE_ID)}"
// Log.d(TAG, "getToken: url = $url")
HttpUtil.get(url = url, doSuccess = {
BaseApp.getSharedPref()?.put("token" to it)
BaseApp.instance?.token = it.toString()
}) { code, msg ->
//toast(msg)
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)
if (baseReq.code == 500 || baseReq.code == 401) {
exeTokenThread()
}
return@runCatching
}
doSuccess(baseReq.result ?: "")
}.onFailure {
it.printStackTrace()
doFailure(-1, "解析异常")
}
}
}
}
}
@@ -1,24 +1,36 @@
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"
const val DISH_DETAIL = "$BASE_URL/food/stFoodInfoMatching/queryById"
var DISH_DETAIL = "${BaseApp.configUrl}/food/stFoodInfoMatching/queryById"
// "$BASE_URL/scales/generateToken?deviceId=1111111111111111111111111111"
const val GET_TOKEN = "$BASE_URL/restaurant/equipment/stEquipment/getEquipmentToken"
const val SUBMIT_DISH = "$BASE_URL/food/stFoodInfoMatching/saveoredit"
var GET_TOKEN = "${BaseApp.configUrl}/restaurant/equipment/stEquipment/getEquipmentToken"
var SUBMIT_DISH = "${BaseApp.configUrl}/food/stFoodInfoMatching/saveoredit"
const val QUERY_GOODS_LIST = "$BASE_URL/food/stFoodInfoMatching/queryGoodsInfoList"
const val QUERY_FOOD_LIST = "$BASE_URL/food/stFoodInfoMatching/list"
const val SAMPLING_LIST = "$BASE_URL/food/stFoodInfoMatching/queryHistorical/goodsInfoList"
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"
}
@@ -35,6 +35,7 @@ import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.TextCellAdapter
import com.shuwei.dish.match.databinding.ActivityDeviceConfigBinding
import com.shuwei.dish.match.dialog.BottomDialog2
import com.shuwei.dish.match.utils.SpTool
import com.shuwei.dish.match.utils.ext.clickWithDebounce
class DeviceConfigActivity : BaseActivity() {
@@ -112,6 +113,7 @@ class DeviceConfigActivity : BaseActivity() {
}
binding.rbModeCook.setOnClickListener {
SpTool.put(SpTool.LAUNCH_PAGE_TYPE, 0)
binding.rbModeCook.isChecked = true
binding.rbModeSampling.isChecked = false
loadCookSpan(isClicked = true)
@@ -120,6 +122,7 @@ class DeviceConfigActivity : BaseActivity() {
finish()
}
binding.rbModeSampling.setOnClickListener {
SpTool.put(SpTool.LAUNCH_PAGE_TYPE, 1)
binding.rbModeCook.isChecked = false
binding.rbModeSampling.isChecked = true
loadCookSpan()
@@ -7,7 +7,6 @@ import android.util.Log
import android.view.View
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.aithings.Weigher
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.DishPartAdapter
import com.shuwei.dish.match.base.BaseActivity
@@ -157,35 +156,35 @@ class DishSamplingActivity : BaseActivity() {
LinearLayoutManager(this@DishSamplingActivity, LinearLayoutManager.VERTICAL, false)
adapter = dishAdapter
}
binding.tvInputDishType.run {
clickWithDebounce {
BottomDialog(goodsType = 0).show(this@DishSamplingActivity) { item ->
val filterResult = list.firstOrNull { it.goodsName == item.goodsName }
if (filterResult != null) {
toast("不允许重复添加同一食材")
return@show
}
text = item.goodsName
tempDishEntity = CookFoodGoodsEntity().apply {
goodsId = item.goodsId
goodsName = item.goodsName
relateionType = item.relateionType ?: 0
goodsOrRelationCode = item.goodsOrRelationCode
binding.ivDishTypeSearch.setOnClickListener {
val inputText = binding.etInputDishType.text.toString().trim()
BottomDialog(goodsType = 0, defGoodsName = inputText).show(this@DishSamplingActivity) { item ->
val filterResult = list.firstOrNull { it.goodsName == item.goodsName }
if (filterResult != null) {
toast("不允许重复添加同一食材")
return@show
}
binding.etInputDishType.setText(item.goodsName)
tempDishEntity = CookFoodGoodsEntity().apply {
goodsId = item.goodsId
goodsName = item.goodsName
// TODO: 待定
//allEdible = item.allEdible
}
relateionType = item.relateionType ?: 0
goodsOrRelationCode = item.goodsOrRelationCode
// TODO: 待定
//allEdible = item.allEdible
}
}
}
binding.ivWeightClear.setOnClickListener {
Weigher.tareTwo(AddressUtil.ONE)
WeightUtil.tareTwo(AddressUtil.ONE)
}
WeightUtil.addWeightListener(getWeight = { address, state, weight ->
Log.d(TAG, "addViewClickListener: address=$address,state=$state,weight=$weight")
if (address == 2) {
if (address == AddressUtil.ONE) {
tempDishEntity?.useWeight = weight.toDouble()
binding.tvDishPartWeight.text = "${weight}"
}
@@ -200,10 +199,14 @@ class DishSamplingActivity : BaseActivity() {
return@setOnClickListener
}
val realWeight = tempDishEntity!!.useWeight ?: 0.0
if (realWeight < 0.toDouble()) {
if (realWeight <= 0.toDouble()) {
toast("食材用量需要大于0")
return@setOnClickListener
}
if (tempDishEntity?.goodsName != binding.etInputDishType.text.toString().trim()) {
toast("所选择的食材名称与输入框内容不一致")
return@setOnClickListener
}
tempDishEntity!!.apply {
isSamplingPage = true
materialType = dishMaterialType
@@ -216,7 +219,7 @@ class DishSamplingActivity : BaseActivity() {
list.add(tempDishEntity!!)
dishAdapter.notifyDataSetChanged()
tempDishEntity = null
binding.tvInputDishType.text = ""
binding.etInputDishType.setText("")
binding.rgSampling.clearCheck()
}
binding.rgSampling.setOnCheckedChangeListener { group, checkedId ->
@@ -236,7 +239,11 @@ class DishSamplingActivity : BaseActivity() {
return@clickWithDebounce
}
val food = FoodRecord()
food.foodId = foodId
binding.etInputDish.let {
if (it.tag!=null && it.tag.toString() == it.text.toString()) {
food.foodId = foodId
}
}
food.foodName = showFoodName
food.cookMode = 1
Log.d(TAG, "goToSubmit: goodsList:${list.toJsonString()}")
@@ -262,6 +269,7 @@ class DishSamplingActivity : BaseActivity() {
foodName = it.getStringExtra(FoodSearchActivity.FOOD_NAME)
binding.etInputDish.run {
setText(foodName)
tag = foodName
setSelection(text.length)
}
}
@@ -2,11 +2,8 @@ package com.shuwei.dish.match.ui
import android.annotation.SuppressLint
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import com.aithings.Weigher
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.HomeModeAdapter
import com.shuwei.dish.match.base.BaseActivity
@@ -15,9 +12,8 @@ import com.shuwei.dish.match.databinding.ActivityHomeBinding
import com.shuwei.dish.match.db.AppRepository
import com.shuwei.dish.match.entity.HomeModeBean
import com.shuwei.dish.match.http.HttpUtil
import com.shuwei.dish.match.http.UrlConfig
import com.shuwei.dish.match.utils.SpTool
import com.shuwei.dish.match.utils.WeightUtil
import com.shuwei.dish.match.utils.ext.put
import com.shuwei.dish.match.utils.ext.startActivity
import com.shuwei.dish.match.utils.ext.toast
import com.shuwei.dish.match.viewmodel.AppViewModel
@@ -31,18 +27,18 @@ class HomeActivity : BaseActivity() {
private lateinit var binding: ActivityHomeBinding
private var connect: Boolean = false
// private var connect: Boolean = false
@SuppressLint("HardwareIds")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
statusBarDarkFont(enable = true)
initViewModel()
// 获取 ANDROID_ID
val deviceId =
Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
Log.d(TAG, "onCreate: deviceId=$deviceId")
BaseApp.instance?.androidId = deviceId
// // 获取 ANDROID_ID
// val deviceId =
// Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
// Log.d(TAG, "onCreate: deviceId=$deviceId")
// BaseApp.instance?.androidId = deviceId
binding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(binding.root)
@@ -56,13 +52,13 @@ class HomeActivity : BaseActivity() {
}
binding.radioGroup?.check(R.id.radioZk)
initRecyclerView()
getAppToken()
WeightUtil.connect { connect ->
this.connect = connect
}
Weigher.init()
Weigher.getWeight()
Weigher.startContinuousRead()
// HttpUtil.getAppToken()
// WeightUtil.connect { connect ->
// this.connect = connect
// }
// WeightUtil.init()
// WeightUtil.getWeight()
// WeightUtil.startContinuousRead()
// binding.tvTopText.setOnClickListener {
// startActivity<SamplingListActivity>()
// }
@@ -111,6 +107,7 @@ class HomeActivity : BaseActivity() {
modeDesc = "菜品快速制作,记录熟重",
modeIconId = R.drawable.ic_mode_cook,
onClick = {
SpTool.put(SpTool.LAUNCH_PAGE_TYPE, 0)
startActivity<SelectDishActivity>()
finish()
})
@@ -121,6 +118,7 @@ class HomeActivity : BaseActivity() {
modeDesc = "菜品快速制作,记录熟重",
modeIconId = R.drawable.ic_mode_sampling,
onClick = {
SpTool.put(SpTool.LAUNCH_PAGE_TYPE, 1)
goSampling()
})
)
@@ -158,15 +156,5 @@ class HomeActivity : BaseActivity() {
}
}
private fun getAppToken() {
val url = "${UrlConfig.GET_TOKEN}?qrcodeId=${BaseApp.instance?.androidId}"
// Log.d(TAG, "getToken: url = $url")
HttpUtil.get(url = url, doSuccess = {
BaseApp.getSharedPref()?.put("token" to it)
BaseApp.instance?.token = it.toString()
}) { code, msg ->
toast(msg)
}
}
}
@@ -0,0 +1,198 @@
package com.shuwei.dish.match.ui
import android.annotation.SuppressLint
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.databinding.ActivityInitBinding
import com.shuwei.dish.match.db.AppRepository
import com.shuwei.dish.match.http.HttpUtil
import com.shuwei.dish.match.http.UrlConfig
import com.shuwei.dish.match.utils.AppUtil
import com.shuwei.dish.match.utils.QRCodeUtil
import com.shuwei.dish.match.utils.SpTool
import com.shuwei.dish.match.utils.WeightUtil
import com.shuwei.dish.match.utils.ext.dp
import com.shuwei.dish.match.utils.ext.invisible
import com.shuwei.dish.match.utils.ext.startActivity
import com.shuwei.dish.match.utils.ext.toJsonString
import com.shuwei.dish.match.utils.ext.toObject
import com.shuwei.dish.match.utils.ext.toast
import com.shuwei.dish.match.utils.ext.visible
import com.shuwei.dish.match.viewmodel.AppViewModel
import com.shuwei.dish.match.viewmodel.factory.AppFactory
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
class InitActivity : BaseActivity() {
companion object {
const val TAG = "InitActivity"
}
private lateinit var binding: ActivityInitBinding
private lateinit var appViewModel: AppViewModel
@SuppressLint("HardwareIds")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
statusBarDarkFont(enable = true)
binding = ActivityInitBinding.inflate(layoutInflater)
setContentView(binding.root)
setHeaderBackground(isHomePage = true)
// 获取 ANDROID_ID
val androidId =
Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
BaseApp.deviceId = androidId
SpTool.put(SpTool.DEVICE_ID, androidId)
// TODO: 以上保存deviceId用于临时使用,后续改为下面注释方式
// var deviceId = AppUtil.getUDID(this)
// Log.d(TAG, "onCreate: deviceId=$deviceId")
//// deviceId = "39a7abdd06b3c7ab"
//// deviceId = "3ea47dc0-3cf0-3c2f-909c-265a9a65572e"
//
// BaseApp.deviceId = deviceId
//
// SpTool.put(SpTool.DEVICE_ID, deviceId)
// val deviceConfigCache = SpTool.getString(SpTool.DEVICE_CONFIG_CACHE)
// val checkResult = checkConfigData(deviceConfigCache)
// if (checkResult.not()) {
// binding.ivQrCode.visible()
// binding.btnInit.visible()
// //进行初始化操作
// initConfig()
// return
// }
binding.ivQrCode.invisible()
binding.btnInit.invisible()
initViewModel()
WeightUtil.init()
WeightUtil.connect {}
WeightUtil.getWeight()
WeightUtil.startContinuousRead()
HttpUtil.getAppToken()
countDown()
}
private fun initViewModel() {
val db = BaseApp.instance!!.database
val factory = AppFactory(AppRepository(db.appDao()))
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
}
private fun startNextPage() {
val launchPageType = SpTool.getInt(SpTool.LAUNCH_PAGE_TYPE, -1)
when (launchPageType) {
0 -> {
startActivity<SelectDishActivity>()
}
1 -> {
goSampling()
}
else -> {
startActivity<HomeActivity>()
}
}
}
private fun goSampling() {
appViewModel.countCookFood(cookMode = 1) {
if (it > 0) {
startActivity<SamplingListActivity>()
finish()
return@countCookFood
}
startActivity<DishSamplingActivity> {
putExtra(DishSamplingActivity.PAGE_FROM, DishSamplingActivity.HOME)
}
finish()
}
}
private fun countDown() {
lifecycleScope.launch {
flow {
(2 downTo 1).forEach {
delay(1000)
emit(it)
}
}.collect {
// 倒计时结束执行跳转
if (it == 1) {
startNextPage()
finish()
}
}
}
}
private fun initConfig() {
binding.ivQrCode.setImageBitmap(
QRCodeUtil.generateQRCode(
content = BaseApp.deviceId?:"",
size = 200.dp
)
)
binding.btnInit.setOnClickListener {
getDeviceConfig()
}
}
private fun getDeviceConfig() {
val tokenUrl = "${UrlConfig.DEVICE_TOKEN}?qrcodeId=${BaseApp.deviceId}&appVersion=1"
HttpUtil.get(url = tokenUrl, doSuccess = { token ->
Log.d(TAG, "initConfig: $token")
getConfig(token.toString())
}, doFailure = { code, msg ->
Log.d(TAG, "initConfig: $code,$msg")
})
}
private fun getConfig(token: String) {
val deviceConfigUrl = "${UrlConfig.DEVICE_CONFIG}?equipmentCode=${BaseApp.deviceId}&&appVersion=1"
HttpUtil.get(url = deviceConfigUrl, header = mutableMapOf(
"X-Access-Token" to token
), doSuccess = {
Log.d(TAG, "getDeviceConfig: $it")
val data = it.toJsonString()
val checkResult = checkConfigData(data)
if (checkResult.not()) {
toast("初始化设备失败,请稍后重试")
return@get
}
SpTool.put(SpTool.DEVICE_CONFIG_CACHE, data)
startActivity<HomeActivity>()
finish()
}, doFailure = { code, msg ->
Log.d(TAG, "getDeviceConfig: $code,$msg")
toast("初始化设备失败,请稍后重试,code=${code},msg=${msg}")
})
}
data class DeviceConfigBean(
var appPackageUrl:String? = null,
var canteenId:String? = null
)
private fun checkConfigData(data: String): Boolean {
if (data.isBlank()) {
return false
}
val config = data.toObject<DeviceConfigBean?>()
if (config == null) {
return false
}
BaseApp.configUrl = config.appPackageUrl?:""
BaseApp.canteenId = config.canteenId?:""
return true
}
}
@@ -7,7 +7,6 @@ import android.os.Bundle
import android.util.Log
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import com.aithings.Weigher
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.DishPartAdapter
import com.shuwei.dish.match.base.BaseActivity
@@ -75,11 +74,11 @@ class PrepareCookActivity : BaseActivity() {
private fun addViewClickListener() {
binding.ivWeightClear.setOnClickListener {
Weigher.tareTwo(AddressUtil.ONE)
WeightUtil.tareTwo(AddressUtil.ONE)
}
WeightUtil.addWeightListener(getWeight = { address, state, weight ->
Log.d(TAG, "addViewClickListener: address=$address,state=$state,weight=$weight")
if (address == 2) {
if (address == AddressUtil.ONE) {
binding.tvDishPartWeight.text = "${weight}"
}
})
@@ -8,7 +8,6 @@ import android.util.SparseIntArray
import android.widget.TextView
import androidx.core.util.forEach
import androidx.lifecycle.ViewModelProvider
import com.aithings.Weigher
import com.google.gson.reflect.TypeToken
import com.shuwei.dish.match.R
import com.shuwei.dish.match.adapter.TextCellAdapter
@@ -150,7 +149,7 @@ class SubmitFoodActivity : BaseActivity() {
private fun addViewListener() {
addWeightListener()
binding.btnWeightClear.setOnClickListener { Weigher.tareTwo(AddressUtil.TWO) }
binding.btnWeightClear.setOnClickListener { WeightUtil.tareTwo(AddressUtil.TWO) }
binding.btnCook.clickWithDebounce {
cook()
@@ -167,7 +166,7 @@ class SubmitFoodActivity : BaseActivity() {
private fun addWeightListener() {
WeightUtil.addWeightListener(getWeight = { address, state, weight ->
if (address == 1) {
if (address == AddressUtil.TWO) {
cookFoodEntity.foodWeight = weight.toDouble()
binding.tvTotalWeight.text = "${weight / 1000f}"
}
@@ -207,19 +207,23 @@ class DishListFragment : Fragment() {
@SuppressLint("NotifyDataSetChanged")
private fun loadEmptyView() {
list.clear()
dishAdapter.notifyDataSetChanged()
if (emptyViewBinding == null) {
emptyViewBinding = LayoutEmptyViewBinding.inflate(
LayoutInflater.from(requireContext()),
binding.rvDishList,
false
)
try {
list.clear()
dishAdapter.notifyDataSetChanged()
if (emptyViewBinding == null) {
emptyViewBinding = LayoutEmptyViewBinding.inflate(
LayoutInflater.from(requireContext()),
binding.rvDishList,
false
)
}
emptyViewBinding!!.tvContent.text = "暂无数据"
emptyViewBinding!!.tvSubContent.text = "获取菜品数据失败,请检查网络设置或稍后重试"
dishAdapter.stateView = emptyViewBinding!!.root
//dishAdapter.setStateViewLayout(requireContext(), R.layout-sw800dp.layout_empty_view)
} catch (e: Exception) {
e.printStackTrace()
}
emptyViewBinding!!.tvContent.text = "暂无数据"
emptyViewBinding!!.tvSubContent.text = "获取菜品数据失败,请检查网络设置或稍后重试"
dishAdapter.stateView = emptyViewBinding!!.root
//dishAdapter.setStateViewLayout(requireContext(), R.layout-sw800dp.layout_empty_view)
}
private fun finishRefresh() {
@@ -0,0 +1,750 @@
package com.shuwei.dish.match.utils;
import static android.content.Context.TELEPHONY_SERVICE;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.core.content.FileProvider;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.net.NetworkInterface;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
public class AppUtil {
public static String getAppPackageName(Context context) {
String packageName = "";
try {
PackageManager pm = context.getPackageManager();
PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
packageName = pi.packageName;
if (AppUtil.isEmpty(packageName)) {
return "";
}
} catch (Exception e) {
e.printStackTrace();
}
return packageName;
}
public static String getAppVersionName(Context context) {
String versionName = "";
// int versioncode=1;
try {
PackageManager pm = context.getPackageManager();
PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
versionName = pi.versionName;
// versioncode = pi.versionCode;表示更新了多少次
if (versionName == null || versionName.length() <= 0) {
return "";
}
} catch (Exception e) {
e.printStackTrace();
}
return versionName;
}
public static int getAppVersionCode(Context context) {
int versioncode = 1;
try {
PackageManager pm = context.getPackageManager();
PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
versioncode = pi.versionCode;
} catch (Exception e) {
e.printStackTrace();
}
return versioncode;
}
//判断微信是否安装
public static boolean isWeixinInstalled(Context context) {
final PackageManager packageManager = context.getPackageManager();// 获取packagemanager
List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);// 获取所有已安装程序的包信息
if (pinfo != null) {
for (int i = 0; i < pinfo.size(); i++) {
String pn = pinfo.get(i).packageName;
if (pn.equals("com.tencent.mm")) {
return true;
}
}
}
return false;
}
/**
* 打电话
* <p>
* Intent.ACTION_DIAL Intent.ACTION_CALL
*
* @param context
* @param mobile
*/
public static void callUp(Context context, String mobile) {
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:"
+ mobile));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
/**
* 获取设备ID
*
* @param context
* @return
*/
public static String getDevId(Context context) {
TelephonyManager TelephonyMgr = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
return TelephonyMgr.getDeviceId();
}
/**
* 姓名脱敏
*
* @param fullName
* @return
*/
public static String desensitizedName(String fullName) {
if (fullName == null || fullName.length() <= 1) {
return fullName;
}
char[] nameArr = fullName.toCharArray();
if (nameArr.length > 2) {
for (int i = 1; i < nameArr.length - 1; i++) {
nameArr[i] = '*';
}
} else {
nameArr[1] = '*';
}
return new String(nameArr);
}
public static String formatDateGetFull(String date) {
if (isEmpty(date)) {
return "";
}
Date d = new Date(Long.parseLong(date));
SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm");
return dateFormat1.format(d);
}
public static String formatDateGetCurrentTime() {
Date d = new Date(System.currentTimeMillis());
SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//.SSS
return dateFormat1.format(d);
}
public static String formatDateGetFull(long date) {
if (date == 0) {
return "";
}
Date d = new Date(date);
SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
return dateFormat1.format(d);
}
public static String formatDateGetDay(long date) {
if (date == 0) {
return "";
}
Date d = new Date(date);
SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd");
return dateFormat1.format(d);
}
public static boolean isEmpty(String s) {
if (TextUtils.isEmpty(s) || s.trim().equals("null")) {
return true;
} else {
return false;
}
}
/**
* 格式化浮点型
*
* @param data
* @return
*/
public static String formatDouble(double data) {
return new DecimalFormat("0.00").format(data);
}
/**
* 格式化分钟
*
* @param minutes
* @return
*/
public static String formatMinutes(int minutes) {
int hour = minutes / 60;
int minute = minutes % 60;
if (hour > 0 && minute > 0) {
return hour + "小时" + minute + "分钟";
} else if (hour > 0) {
return hour + "小时";
} else {
return minute + "分钟";
}
}
//com.fawan.news
public static void goToMarket(Context context, String packageName) {
Uri uri = Uri.parse("market://details?id=" + packageName);
Intent goToMarket = new Intent(Intent.ACTION_VIEW, uri);
try {
context.startActivity(goToMarket);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
}
/**
* true为存在,false为不存在
*
* @param context
* @param packageName
* @return
*/
public static boolean isInstallApp(Context context, String packageName) {
try {
context.getPackageManager().getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
return true;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
/**
* 格式化float 保留两位小数
*
* @param data
* @return
*/
public static float formatFloat2(float data) {
// DecimalFormat decimalFormat = new DecimalFormat("0.00");//构造方法的字符格式这里如果小数不足2位,会以0补足.
// return decimalFormat.format(data);//返回字符串
int scale = 1;//设置位数
int roundingMode = 4;//表示四舍五入,可以选择其他舍值方式,例如去尾,等等.
BigDecimal bd = new BigDecimal((double) data);
bd = bd.setScale(scale, roundingMode);
data = bd.floatValue();
return data;
}
/**
* Android 6.0 之前(不包括6.0)获取mac地址
* 必须的权限 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
*
* @param context * @return
*/
public static String getMacDefault(Context context) {
String mac = "";
if (context == null) {
return mac;
}
WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo info = null;
try {
info = wifi.getConnectionInfo();
} catch (Exception e) {
e.printStackTrace();
}
if (info == null) {
return null;
}
mac = info.getMacAddress();
if (!TextUtils.isEmpty(mac)) {
mac = mac.toUpperCase(Locale.ENGLISH);
}
return mac;
}
/**
* Android 6.0-Android 7.0 获取mac地址
*/
public static String getMacAddress() {
String macSerial = null;
String str = "";
try {
Process pp = Runtime.getRuntime().exec("cat/sys/class/net/wlan0/address");
InputStreamReader ir = new InputStreamReader(pp.getInputStream());
LineNumberReader input = new LineNumberReader(ir);
while (null != str) {
str = input.readLine();
if (str != null) {
macSerial = str.trim();//去空格
break;
}
}
} catch (IOException ex) {
// 赋予默认值
ex.printStackTrace();
}
return macSerial;
}
/**
* Android 7.0之后获取Mac地址
* 遍历循环所有的网络接口,找到接口是 wlan0
* 必须的权限 <uses-permission android:name="android.permission.INTERNET"></uses-permission>
*
* @return
*/
public static String getMacFromHardware() {
try {
ArrayList<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
for (NetworkInterface nif : all) {
if (!nif.getName().equals("wlan0"))
continue;
byte[] macBytes = nif.getHardwareAddress();
if (macBytes == null) return "";
StringBuilder res1 = new StringBuilder();
for (Byte b : macBytes) {
res1.append(String.format("%02X:", b));
}
if (!TextUtils.isEmpty(res1)) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
/**
* 获取mac地址(适配所有Android版本)
*
* @return
*/
public static String getMac(Context context) {
String mac = "";
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
mac = getMacDefault(context);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
mac = getMacAddress();
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
mac = getMacFromHardware();
}
return mac;
}
//把String转化为float
public static double convertToFloat(String number, double defaultValue) {
if (TextUtils.isEmpty(number)) {
return defaultValue;
}
try {
return Double.parseDouble(number);
} catch (Exception e) {
return defaultValue;
}
}
/**
* 获取AndroidId
*
* @param context
* @return
*/
public static String getAndroidId(Context context) {
String androidId = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
return androidId;
}
/**
* 获取设备唯一 UDID
*
* @param context
* @return
*/
@SuppressLint("MissingPermission")
public static String getUDID(Context context) {
// String androidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
// L.e("androidID===" + androidID);
// return androidID;
String androidID = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
if (!androidID.equals("")) {
try {
if (!"9774d56d682e549c".equals(androidID)) {
androidID = UUID.nameUUIDFromBytes(androidID.getBytes("utf8")).toString();
} else {
@SuppressLint("MissingPermission") final String deviceId = ((TelephonyManager) context.getSystemService(TELEPHONY_SERVICE)).getDeviceId();
androidID = deviceId != null ? UUID.nameUUIDFromBytes(deviceId.getBytes("utf8")).toString() : UUID.randomUUID().toString();
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
return androidID;
}
//需要权限 android.permission.READ_PHONE_STATE
TelephonyManager TelephonyMgr = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
String szImei = TelephonyMgr.getDeviceId();
if (!szImei.equals("")) {
return szImei;
}
//需要权限 android.permission.ACCESS_WIFI_STATE
WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
String m_szWLANMAC = wm.getConnectionInfo().getMacAddress();
if (!m_szWLANMAC.equals("")) {
return m_szWLANMAC;
}
//需要权限 android.permission.BLUETOOTH
BluetoothAdapter m_BluetoothAdapter = null;
m_BluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
String m_szBTMAC = m_BluetoothAdapter.getAddress();
if (!m_szBTMAC.equals("")) {
return m_szBTMAC;
}
return getUniquePsuedoID();
}
//获得 Psuedo ID
public static String getUniquePsuedoID() {
String serial = null;
String m_szDevIDShort = "35" +
Build.BOARD.length() % 10 + Build.BRAND.length() % 10 +
Build.CPU_ABI.length() % 10 + Build.DEVICE.length() % 10 +
Build.DISPLAY.length() % 10 + Build.HOST.length() % 10 +
Build.ID.length() % 10 + Build.MANUFACTURER.length() % 10 +
Build.MODEL.length() % 10 + Build.PRODUCT.length() % 10 +
Build.TAGS.length() % 10 + Build.TYPE.length() % 10 +
Build.USER.length() % 10; //13 位
try {
serial = Build.class.getField("SERIAL").get(null).toString();
//API>=9 使用serial号
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
} catch (Exception exception) {
//serial需要一个初始化,随意值
serial = "serial";
}
//使用硬件信息拼凑出来的15位号码
return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();
}
public static String getCPUSerial() {
String line = "";
String TAG = "aaa";
Log.e(TAG, " get_quck_Sn() ");
Class<?> c = null;
try {
c = Class.forName("android.os.SystemProperties");
Method get = c.getMethod("get", String.class);
line = (String) get.invoke(c, "ro.serialno");
} catch (ClassNotFoundException | NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Log.e(TAG, " get_quck_Sn() " + line);
System.out.println("设备串号" + line);
return line;
}
/**
* 判断网络连接状态
*
* @param context
* @return
*/
public static boolean isNetworkConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
if (mNetworkInfo != null) {
return mNetworkInfo.isAvailable();
}
}
return false;
}
/**
* 判断WiFi连接状态
*
* @param context
* @return
*/
public static boolean isWifiConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mWiFiNetworkInfo = mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
if (mWiFiNetworkInfo != null) {
return mWiFiNetworkInfo.isAvailable();
}
}
return false;
}
/**
* 判断移动网络状态
*
* @param context
* @return
*/
public static boolean isMobileConnected(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mMobileNetworkInfo = mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
if (mMobileNetworkInfo != null) {
return mMobileNetworkInfo.isAvailable();
}
}
return false;
}
/**
* 获取网络连接类型
*
* @param context
* @return
*/
public static int getConnectedType(Context context) {
if (context != null) {
ConnectivityManager mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
if (mNetworkInfo != null && mNetworkInfo.isAvailable()) {
return mNetworkInfo.getType();
}
}
return -1;
}
/**
* 根据字符的起始和结束索引提取子串
*
* @param input 原始字符串
* @param startIndex 起始索引(包含,从0开始)
* @param endIndex 结束索引(不包含)
* @return 子串,若输入无效或索引越界则返回空字符串
*/
public static String getSubstringByIndices(String input, int startIndex, int endIndex) {
if (input == null) {
return "";
}
// 处理索引越界问题
int safeStart = Math.max(startIndex, 0);
int safeEnd = Math.min(endIndex, input.length());
if (safeStart > safeEnd) {
return "";
}
return input.substring(safeStart, safeEnd);
}
public static String getSubstringByIndex(String input, int startIndex, int length) {
if (input == null) {
return "";
}
// 处理索引越界问题
int safeStart = Math.max(startIndex, 0);
int safeEnd = Math.min(startIndex + length, input.length());
if (safeStart > safeEnd) {
return "";
}
return input.substring(safeStart, safeEnd);
}
/**
* 十进制转十六进制
*
* @param decimal
* @return
*/
public static String decimalToHexWithPadding(int decimal, int padding) {
// 将十进制转换为十六进制,并转换为字符串
String hex = Integer.toHexString(decimal);
// 确保字符串长度为至少4位,不足部分前面补0
while (hex.length() < padding) {
hex = "0" + hex;
}
return hex.toUpperCase(); // 返回大写形式的十六进制字符串
}
/**
* 十进制转二进制,且返回的二进制为至少7位数
*
* @param decimal
* @return
*/
public static String decimalToBinary(int decimal) {
// 如果输入为0,直接返回"0"
if (decimal == 0) {
return "0";
}
StringBuilder binary = new StringBuilder();
// 除2取余法,将余数加入二进制字符串
while (decimal > 0) {
int remainder = decimal % 2;
binary.insert(0, remainder);
decimal = decimal / 2;
}
int length = binary.length();
if (length < 7) {
int padding = 7 - length;
for (int i = 0; i < padding; i++) {
binary.insert(0, '0');
}
}
return binary.toString();
}
/**
* 将二进制字符串转换为十六进制字符串,每8位转换为两位十六进制,不足两位前面补零
*
* @param binaryStr 输入的二进制字符串(仅包含0和1)
* @return 转换后的十六进制字符串
* @throws IllegalArgumentException 如果输入不是有效的二进制字符串
*/
public static String binaryToHex(String binaryStr) {
// 校验输入合法性
if (binaryStr == null || !binaryStr.matches("[01]+")) {
throw new IllegalArgumentException("Invalid binary string");
}
// 补前导零使长度成为8的倍数
int length = binaryStr.length();
int padding = (8 - (length % 8)) % 8; // 计算需要补零的数量
StringBuilder paddedBinary = new StringBuilder();
for (int i = 0; i < padding; i++) {
paddedBinary.append('0');
}
paddedBinary.append(binaryStr);
// 每8位转换为两位十六进制
StringBuilder hexStr = new StringBuilder();
for (int i = 0; i < paddedBinary.length(); i += 8) {
String byteStr = paddedBinary.substring(i, i + 8);
int decimalValue = Integer.parseInt(byteStr, 2);
hexStr.append(String.format("%02X", decimalValue & 0xFF));
}
return hexStr.toString();
}
/**
* 数据校验 异或处理
*/
public static String getXor(String content) {
int a = 0;
for (int i = 0; i < content.length() / 2; i++) {
a = a ^ Integer.parseInt(content.substring(i * 2, (i * 2) + 2), 16);
}
String result = Integer.toHexString(a).toUpperCase();
if (result.length() == 1) {
return "0" + result;
} else {
return result;
}
}
public static double formatPersonInfo(String input, int startIndex, int length) {
if (input == null) {
return 0;
}
// 处理索引越界问题
int safeStart = Math.max(startIndex, 0);
int safeEnd = Math.min(startIndex + length, input.length());
if (safeStart > safeEnd) {
return 0;
}
String result = input.substring(safeStart, safeEnd);
double num = Integer.parseInt(result, 16);
return num;
}
/**
* 安装apk
*
* @param activity
* @param apkFile
*/
public static void installApk(Context activity, File apkFile) {
//文件有所有者概念,现在是属于当前进程的,需要把这个文件暴露给系统安装程序(其他进程)去安装
//因此,可能会存在权限问题,需要做下面的设置
//如果文件是sdcard上的,就不需要这个操作了
try {
apkFile.setExecutable(true, false);
apkFile.setReadable(true, false);
apkFile.setWritable(true, false);
} catch (Exception e) {
e.printStackTrace();
}
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Intent.ACTION_VIEW);
Uri uri;
//TODO N FileProvider
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileProvider", apkFile);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
} else {
uri = Uri.fromFile(apkFile);
}
intent.setDataAndType(uri, "application/vnd.android.package-archive");
activity.startActivity(intent);
//TODO 0 INSTALL PERMISSION
//在AndroidManifest中加入权限即可
}
}
@@ -0,0 +1,100 @@
package com.shuwei.dish.match.utils
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.WriterException
import com.google.zxing.common.BitMatrix
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
/**
* 二维码生成工具类
*/
object QRCodeUtil {
/**
* 生成二维码(默认大小)
* @param content 二维码内容
* @return 生成的二维码Bitmap
*/
@JvmOverloads
fun generateQRCode(content: String, size: Int = 500): Bitmap? {
return generateQRCode(content, size, Color.BLACK, Color.WHITE)
}
/**
* 生成二维码(自定义颜色)
* @param content 二维码内容
* @param size 二维码边长(像素)
* @param colorCode 二维码颜色
* @param backgroundColor 背景颜色
* @return 生成的二维码Bitmap
*/
fun generateQRCode(
content: String,
size: Int,
colorCode: Int,
backgroundColor: Int
): Bitmap? {
if (content.isEmpty()) {
return null
}
return try {
val hints = mutableMapOf<EncodeHintType, Any>().apply {
put(EncodeHintType.CHARACTER_SET, "UTF-8")
put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H) // 纠错级别
put(EncodeHintType.MARGIN, 1) // 边距
}
val bitMatrix = QRCodeWriter().encode(
content,
BarcodeFormat.QR_CODE,
size,
size,
hints
)
val pixels = IntArray(size * size).apply {
for (y in 0 until size) {
for (x in 0 until size) {
this[y * size + x] = if (bitMatrix.get(x, y)) colorCode else backgroundColor
}
}
}
Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888).apply {
setPixels(pixels, 0, size, 0, 0, size, size)
}
} catch (e: WriterException) {
e.printStackTrace()
null
}
}
/**
* 生成带Logo的二维码
* @param content 二维码内容
* @param size 二维码边长(像素)
* @param logo Logo Bitmap
* @return 带Logo的二维码Bitmap
*/
fun generateQRCodeWithLogo(content: String, size: Int, logo: Bitmap?): Bitmap? {
val qrCode = generateQRCode(content, size) ?: return null
logo ?: return qrCode
val logoSize = size / 5 // Logo大小约为二维码的1/5
val scaledLogo = Bitmap.createScaledBitmap(logo, logoSize, logoSize, false)
val offset = (size - logoSize) / 2
return Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888).apply {
val canvas = Canvas(this)
canvas.drawBitmap(qrCode, 0f, 0f, null)
canvas.drawBitmap(scaledLogo, offset.toFloat(), offset.toFloat(), null)
}
}
}
@@ -0,0 +1,30 @@
package com.shuwei.dish.match.utils
import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.utils.ext.put
object SpTool {
const val DEVICE_CONFIG_CACHE = "deviceConfigCache"
const val TOKEN = "token"
const val LAUNCH_PAGE_TYPE = "launchPageType"
const val DEVICE_ID = "deviceId"
val pref = BaseApp.getSharedPref()!!
fun put(key: String, value: Any) {
pref.put(key to value)
}
fun getInt(key: String, defValue: Int = 0): Int {
return pref.getInt(key, defValue)
}
fun getString(key: String, defValue: String? = ""): String {
return pref.getString(key, defValue) ?: ""
}
fun getBoolean(key: String, defValue: Boolean = false): Boolean {
return pref.getBoolean(key, defValue)
}
}
@@ -4,15 +4,26 @@ import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
import android.util.SparseIntArray
import com.aithings.Weigher
import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.utils.ext.toast
import com.wabon.wbintelligenthardwaresdk.api.SensorScale
object WeightUtil {
// val weightArray = SparseIntArray()
fun init() {
Weigher.init()
}
fun getWeight() {
Weigher.getWeight()
}
fun startContinuousRead() {
Weigher.startContinuousRead()
}
fun tareTwo(address: Int) {
Weigher.tareTwo(address)
}
fun connect(initCallback: (connect: Boolean) -> Unit) {
Weigher.setListener(object : WeightListenerImpl() {
@@ -34,17 +45,17 @@ object WeightUtil {
}
override fun onGetWeight(address: Int, state: Int, weight: Int) {
// runOnUiThread {
// val stateStr = when (state) {
// SensorScale.STATE_STABLE -> "稳定"
// SensorScale.STATE_UNSTABLE -> "不稳定"
// SensorScale.STATE_OVER_WEIGHT -> "量程溢出"
// else -> ""
// }
// val result1 = String.format("$stateStr 重量:%s kg", weight / 1000f)
// //Log.d("WeightUtil", "ttlReturn, address:$address,$result1")
runOnUiThread {
val stateStr = when (state) {
SensorScale.STATE_STABLE -> "稳定"
SensorScale.STATE_UNSTABLE -> "不稳定"
SensorScale.STATE_OVER_WEIGHT -> "量程溢出"
else -> ""
}
val result1 = String.format("$stateStr 重量:%s kg", weight / 1000f)
Log.d("WeightUtil", "ttlReturn, address:$address,$result1")
// weightArray.put(address, weight)
// }
}
}
override fun onReadIdentify(rate: Int) {
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/ripple_color">
<item>
<shape android:shape="rectangle">
<solid android:color="#ff08C6DC" />
<corners android:radius="8dp" />
</shape>
</item>
</ripple>
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:width="828dp" android:height="1768dp">
<shape android:shape="rectangle">
<solid android:color="@color/bg_color"/>
</shape>
</item>
<item android:gravity="top" android:width="828dp" android:height="414dp">
<bitmap android:src="@drawable/bg_home_page"/>
</item>
</layer-list>
Binary file not shown.

After

Width:  |  Height:  |  Size: 999 KiB

@@ -43,13 +43,6 @@
android:textSize="28sp" />
</FrameLayout>
<!-- <com.google.android.material.divider.MaterialDivider-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="1dp"-->
<!-- android:layout_marginStart="30dp"-->
<!-- android:layout_marginEnd="30dp"-->
<!-- android:background="@color/gray_eb" />-->
<LinearLayout
android:id="@+id/modeRadioGroup"
android:layout_width="match_parent"
@@ -69,13 +62,6 @@
android:textSize="36sp"
tools:text="制作模式" />
<!-- <com.google.android.material.divider.MaterialDivider-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="1dp"-->
<!-- android:layout_marginStart="30dp"-->
<!-- android:layout_marginEnd="30dp"-->
<!-- android:background="@color/gray_eb" />-->
<RadioButton
android:id="@+id/rbModeSampling"
android:layout_width="match_parent"
@@ -89,13 +75,6 @@
android:textSize="36sp"
tools:text="采样模式" />
<!-- <com.google.android.material.divider.MaterialDivider-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="1dp"-->
<!-- android:layout_marginStart="30dp"-->
<!-- android:layout_marginEnd="30dp"-->
<!-- android:background="@color/gray_eb" />-->
<RadioButton
android:id="@+id/rbModeQuality"
android:layout_width="match_parent"
@@ -74,22 +74,29 @@
android:textColor="@color/black666"
android:textSize="28sp" />
<TextView
android:id="@+id/tvInputDishType"
<EditText
android:id="@+id/etInputDishType"
android:layout_width="wrap_content"
android:layout_height="80dp"
android:background="@color/white"
android:gravity="center_vertical|end"
android:gravity="center_vertical"
android:hint="@string/dish_search_hint2"
android:textColor="@color/black333"
android:textColorHint="@color/gray_c8"
android:textSize="28sp"
android:maxLines="1"
android:ellipsize="end"
android:drawablePadding="30dp"
android:paddingEnd="30dp"
android:paddingStart="30dp"
app:drawableEndCompat="@drawable/ic_search_green" />
android:paddingEnd="6dp"
android:paddingStart="6dp"
android:ellipsize="end"/>
<ImageView
android:id="@+id/ivDishTypeSearch"
android:layout_width="80dp"
android:layout_height="80dp"
android:padding="24dp"
android:layout_marginEnd="6dp"
android:src="@drawable/ic_search_green"
tools:ignore="ContentDescription" />
<!-- <ImageView-->
<!-- android:id="@+id/ivDishTypeSearch"-->
+49
View File
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:layout_width="500dp"
android:layout_height="500dp"
android:layout_marginStart="50dp"
android:layout_marginTop="250dp"
android:layout_marginEnd="50dp"
android:layout_gravity="center_horizontal"
android:src="@drawable/img_init"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/btnInit"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="160dp"
android:background="@drawable/bg_init_button"
android:gravity="center"
android:padding="10dp"
android:text="设备初始化"
android:textColor="@color/white"
android:textSize="30sp"
android:textStyle="bold"
tools:ignore="HardcodedText"
android:visibility="visible"/>
<Space
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"/>
<ImageView
android:id="@+id/ivQrCode"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="50dp"
tools:src="@mipmap/ic_logo1024"
tools:ignore="ContentDescription" />
</LinearLayout>
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

+5 -1
View File
@@ -10,8 +10,12 @@
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<!-- Customize your theme here. -->
<!-- <item name="android:layout_width">828dp</item>-->
<item name="android:fitsSystemWindows">false</item>
<item name="android:windowBackground">@drawable/bg_splash</item>
<item name="android:windowFullscreen">true</item>
</style>
</resources>
+5 -1
View File
@@ -10,9 +10,13 @@
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<!-- Customize your theme here. -->
<!-- <item name="android:layout_width">828dp</item>-->
<item name="android:fitsSystemWindows">false</item>
<item name="android:windowBackground">@drawable/bg_splash</item>
<item name="android:windowFullscreen">true</item>
</style>
<style name="CookButtonStyle" parent="Widget.MaterialComponents.Button">
@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">vip.shuziweidao.com</domain>
<domain includeSubdomains="true">192.168.1.207</domain>
</domain-config>
<!-- <domain-config cleartextTrafficPermitted="true">-->
<!-- <domain includeSubdomains="true">vip.shuziweidao.com</domain>-->
<!-- <domain includeSubdomains="true">192.168.1.207</domain>-->
<!-- <domain includeSubdomains="true">device.shuziweidao.com</domain>-->
<!-- </domain-config>-->
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
+4
View File
@@ -9,8 +9,12 @@ espressoCore = "3.5.1"
appcompat = "1.6.1"
material = "1.10.0"
gridlayout = "1.1.0"
core = "3.4.1"
androidCore = "3.3.0"
[libraries]
android-core = { module = "com.google.zxing:android-core", version.ref = "androidCore" }
core = { module = "com.google.zxing:core", version.ref = "core" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }