6 Commits
Author SHA1 Message Date
lvmengandClaude Sonnet 4.6 0d0fcd24b7 refactor(activity): 优化启动页跳转延迟及子设备页面稳定性,修复导航栏闪烁问题
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 14:31:29 +08:00
lvmeng bb32ed84fe refactor(device): 优化设备角色判断和串口配置逻辑
- 移除 Weigher2 的初始化代码从 InitActivity 到 BaseApp
- 将设备角色判断与串口配置逻辑合并到应用启动流程中
- 主设备配置为 /dev/ttyS7,子设备配置为 /dev/ttyS4
- 简化设备角色分配的条件判断逻辑
2026-04-13 09:40:28 +08:00
lvmeng 6a1a1f3c5c feat(activity): 新增启动页网络检测功能,60秒倒计时后显示连接网络入口 2026-04-13 09:36:06 +08:00
lvmeng 9311d13b90 refactor(activity): 清理 InitActivity 中的注释代码,保留有效初始化逻辑 2026-04-13 09:08:05 +08:00
lvmeng 143d24923b chore(global): 更新 .gitignore,忽略 .idea、.claude、app/debug、app/release 目录 2026-04-13 09:06:48 +08:00
lvmeng 9a77733d24 fix(scale): 修复主设备无法发现子设备及IP显示未知的问题
- UdpBroadcastReceiver 补充 socket.broadcast=true,修复广播包被内核丢弃
- MdnsDiscoveryManager 改为串行解析队列,修复多设备并发解析报 FAILURE_ALREADY_ACTIVE
- ScaleWebSocketServer 推送数据时动态填充本机 IP,解决子设备 IP 始终显示未知
2026-04-13 09:00:51 +08:00
31 changed files with 614 additions and 343 deletions
+4 -6
View File
@@ -1,15 +1,13 @@
*.iml *.iml
.gradle .gradle
/local.properties /local.properties
/.idea/caches /.idea/
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store .DS_Store
/build /build
/captures /captures
.externalNativeBuild .externalNativeBuild
.cxx .cxx
local.properties local.properties
.claude/
app/debug/
app/release/
-3
View File
@@ -1,3 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>
-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-03-16T07:12:10.978585600Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="Default" identifier="serial=192.168.1.115:5555;connection=8c23341a" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>
-13
View File
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>
-19
View File
@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>
-12
View File
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MaterialThemeProjectNewConfig">
<option name="metadata">
<MTProjectMetadataState>
<option name="migrated" value="true" />
<option name="pristineConfig" value="false" />
<option name="userId" value="4ab8791:19ce5d9ffb1:-7ffe" />
</MTProjectMetadataState>
</option>
</component>
</project>
-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>
-9
View File
@@ -1,9 +0,0 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
-17
View File
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>
Generated
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
+14 -3
View File
@@ -45,7 +45,6 @@
<activity <activity
android:name=".ui.InitActivity" android:name=".ui.InitActivity"
android:exported="true" android:exported="true"
android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity"> tools:ignore="DiscouragedApi,LockedOrientationActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@@ -54,24 +53,29 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".ui.HomeActivity" <activity android:name=".ui.HomeActivity"
android:theme="@style/Theme.DishMatch.NoSplash"
android:screenOrientation="portrait" android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" /> tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity <activity
android:name=".ui.SelectDishActivity" android:name=".ui.SelectDishActivity"
android:theme="@style/Theme.DishMatch.NoSplash"
android:launchMode="singleTask" android:launchMode="singleTask"
android:screenOrientation="portrait" android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" /> tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity <activity
android:name=".ui.PrepareCookActivity" android:name=".ui.PrepareCookActivity"
android:theme="@style/Theme.DishMatch.NoSplash"
android:screenOrientation="portrait" android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" /> tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity <activity
android:name=".ui.DishSamplingActivity" android:name=".ui.DishSamplingActivity"
android:theme="@style/Theme.DishMatch.NoSplash"
android:screenOrientation="portrait" android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" /> tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity <activity
android:name=".ui.SamplingListActivity" android:name=".ui.SamplingListActivity"
android:theme="@style/Theme.DishMatch.NoSplash"
android:launchMode="singleTask" android:launchMode="singleTask"
android:screenOrientation="portrait" android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" /> tools:ignore="DiscouragedApi,LockedOrientationActivity" />
@@ -86,19 +90,26 @@
<!-- tools:ignore="DiscouragedApi,LockedOrientationActivity" />--> <!-- tools:ignore="DiscouragedApi,LockedOrientationActivity" />-->
<activity <activity
android:name=".ui.FoodSearchActivity" android:name=".ui.FoodSearchActivity"
android:theme="@style/Theme.DishMatch.NoSplash"
android:screenOrientation="portrait" android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" /> tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity android:name="com.shuwei.dish.match.ui.DeviceConfigActivity" /> <activity android:name="com.shuwei.dish.match.ui.DeviceConfigActivity"
android:theme="@style/Theme.DishMatch.NoSplash" />
<activity android:name="com.shuwei.dish.match.ui.SettingActivity" <activity android:name="com.shuwei.dish.match.ui.SettingActivity"
android:theme="@style/Theme.DishMatch.NoSplash"
android:screenOrientation="portrait" android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" /> tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity android:name="com.shuwei.dish.match.ui.SubmitFoodActivity" /> <activity android:name="com.shuwei.dish.match.ui.SubmitFoodActivity"
android:theme="@style/Theme.DishMatch.NoSplash" />
<activity <activity
android:name="com.shuwei.dish.match.ui.MasterScaleActivity" android:name="com.shuwei.dish.match.ui.MasterScaleActivity"
android:theme="@style/Theme.DishMatch.NoSplash"
android:screenOrientation="portrait" android:screenOrientation="portrait"
tools:ignore="DiscouragedApi,LockedOrientationActivity" /> tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity <activity
android:name="com.shuwei.dish.match.ui.SlaveActivity" android:name="com.shuwei.dish.match.ui.SlaveActivity"
android:theme="@style/Theme.DishMatch.NoSplash"
android:launchMode="singleTask"
android:screenOrientation="landscape" android:screenOrientation="landscape"
tools:ignore="DiscouragedApi,LockedOrientationActivity" /> tools:ignore="DiscouragedApi,LockedOrientationActivity" />
@@ -1,6 +1,7 @@
package com.shuwei.dish.match.base package com.shuwei.dish.match.base
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@@ -43,9 +44,16 @@ open class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// 提前隐藏系统栏,避免窗口创建时导航栏闪烁
hideSystemBars()
// 主设备竖屏,子设备横屏
requestedOrientation = if (GlobalData.deviceRole == DeviceRole.MASTER)
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
else
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
ActivityManager.addActivity(this) ActivityManager.addActivity(this)
binding = ActivityBaseBinding.inflate(layoutInflater) binding = ActivityBaseBinding.inflate(layoutInflater)
if (this !is InitActivity) { if (this !is InitActivity || GlobalData.deviceRole == DeviceRole.SLAVE) {
binding.root.setBackgroundColor(ContextCompat.getColor(this, R.color.bg_color)) binding.root.setBackgroundColor(ContextCompat.getColor(this, R.color.bg_color))
} }
setContentView(binding.root) setContentView(binding.root)
@@ -129,7 +137,7 @@ open class BaseActivity : AppCompatActivity() {
binding.tvLeftTime.text = arr[1] binding.tvLeftTime.text = arr[1]
} }
private val handler = Handler(Looper.getMainLooper()) val handler = Handler(Looper.getMainLooper())
private val updateTask = object : Runnable { private val updateTask = object : Runnable {
override fun run() { override fun run() {
updateDateTime() updateDateTime()
@@ -13,6 +13,7 @@ import com.shuwei.dish.match.ui.InitActivity.Companion.TAG
import com.shuwei.dish.match.utils.AppUtil import com.shuwei.dish.match.utils.AppUtil
import com.shuwei.dish.match.utils.BootReceiver import com.shuwei.dish.match.utils.BootReceiver
import com.shuwei.dish.match.utils.CrashHandler import com.shuwei.dish.match.utils.CrashHandler
import com.shuwei.dish.match.utils.Weigher2
class BaseApp : Application() { class BaseApp : Application() {
@@ -28,10 +29,13 @@ class BaseApp : Application() {
GlobalData.deviceId = deviceId GlobalData.deviceId = deviceId
// 根据设备 ID 判断角色:指定 ID 为主设备,其余为子设备 // 根据设备 ID 判断角色:指定 ID 为主设备,其余为子设备
GlobalData.deviceRole = if (deviceId == "8fc2ab34-2137-3112-acca-f884ea8736d4") { // 主设备是/dev/ttyS7,子设备是/dev/ttyS4
DeviceRole.MASTER if (deviceId == "8fc2ab34-2137-3112-acca-f884ea8736d4") {
GlobalData.deviceRole = DeviceRole.MASTER
Weigher2.setDevicePort("/dev/ttyS7")
} else { } else {
DeviceRole.SLAVE GlobalData.deviceRole = DeviceRole.SLAVE
Weigher2.setDevicePort("/dev/ttyS4")
} }
Log.d(TAG, "设备角色: ${GlobalData.deviceRole}") Log.d(TAG, "设备角色: ${GlobalData.deviceRole}")
@@ -4,10 +4,14 @@ import android.content.Context
import android.net.nsd.NsdManager import android.net.nsd.NsdManager
import android.net.nsd.NsdServiceInfo import android.net.nsd.NsdServiceInfo
import android.util.Log import android.util.Log
import java.util.LinkedList
/** /**
* mDNS 设备发现管理器(仅主设备运行) * mDNS 设备发现管理器(仅主设备运行)
* 持续扫描局域网,发现或丢失 DishMatch 子设备时通过回调通知连接管理器 * 持续扫描局域网,发现或丢失 DishMatch 子设备时通过回调通知连接管理器
*
* 注意:Android NsdManager.resolveService 不支持并发调用,多台子设备同时被发现时
* 会报 FAILURE_ALREADY_ACTIVE(3) 导致解析失败。此处使用串行队列逐一解析。
*/ */
class MdnsDiscoveryManager(private val context: Context) { class MdnsDiscoveryManager(private val context: Context) {
@@ -22,6 +26,11 @@ class MdnsDiscoveryManager(private val context: Context) {
private var discoveryListener: NsdManager.DiscoveryListener? = null private var discoveryListener: NsdManager.DiscoveryListener? = null
private var isDiscovering = false private var isDiscovering = false
/** 待解析队列,串行处理避免并发解析失败 */
private val resolveQueue = LinkedList<NsdServiceInfo>()
@Volatile
private var isResolving = false
/** /**
* 发现新设备时的回调:(deviceId, host, port) * 发现新设备时的回调:(deviceId, host, port)
*/ */
@@ -62,7 +71,7 @@ class MdnsDiscoveryManager(private val context: Context) {
// 只处理 DishMatch 服务 // 只处理 DishMatch 服务
if (!service.serviceName.startsWith("DishMatch-")) return if (!service.serviceName.startsWith("DishMatch-")) return
Log.d(TAG, "发现设备: ${service.serviceName}") Log.d(TAG, "发现设备: ${service.serviceName}")
resolveService(service) enqueueResolve(service)
} }
override fun onServiceLost(service: NsdServiceInfo) { override fun onServiceLost(service: NsdServiceInfo) {
@@ -87,18 +96,45 @@ class MdnsDiscoveryManager(private val context: Context) {
nsdManager.resolveService(service, object : NsdManager.ResolveListener { nsdManager.resolveService(service, object : NsdManager.ResolveListener {
override fun onResolveFailed(info: NsdServiceInfo, errorCode: Int) { override fun onResolveFailed(info: NsdServiceInfo, errorCode: Int) {
Log.e(TAG, "解析设备地址失败: ${info.serviceName}, errorCode=$errorCode") Log.e(TAG, "解析设备地址失败: ${info.serviceName}, errorCode=$errorCode")
// 解析结束(失败),继续处理队列中下一个
processNextResolve()
} }
override fun onServiceResolved(info: NsdServiceInfo) { override fun onServiceResolved(info: NsdServiceInfo) {
val deviceId = info.serviceName.removePrefix("DishMatch-") val deviceId = info.serviceName.removePrefix("DishMatch-")
val host = info.host?.hostAddress ?: return val host = info.host?.hostAddress ?: run {
processNextResolve()
return
}
val port = info.port val port = info.port
Log.d(TAG, "设备地址已解析: $deviceId -> $host:$port") Log.d(TAG, "设备地址已解析: $deviceId -> $host:$port")
onDeviceFound?.invoke(deviceId, host, port) onDeviceFound?.invoke(deviceId, host, port)
// 解析结束(成功),继续处理队列中下一个
processNextResolve()
} }
}) })
} }
/** 将待解析服务加入队列,若当前无解析任务则立即开始 */
@Synchronized
private fun enqueueResolve(service: NsdServiceInfo) {
resolveQueue.add(service)
if (!isResolving) {
processNextResolve()
}
}
/** 从队列取出下一个服务进行解析 */
@Synchronized
private fun processNextResolve() {
val next = resolveQueue.poll() ?: run {
isResolving = false
return
}
isResolving = true
resolveService(next)
}
/** /**
* 停止扫描,在应用退出时调用 * 停止扫描,在应用退出时调用
*/ */
@@ -112,5 +148,9 @@ class MdnsDiscoveryManager(private val context: Context) {
} }
} }
discoveryListener = null discoveryListener = null
synchronized(this) {
resolveQueue.clear()
isResolving = false
}
} }
} }
@@ -54,6 +54,10 @@ object ScaleServiceManager {
get() = wsServer?.onConnectionChanged get() = wsServer?.onConnectionChanged
set(value) { wsServer?.onConnectionChanged = value } set(value) { wsServer?.onConnectionChanged = value }
/** 当前是否有主设备连接(仅子设备使用) */
val isMasterConnected: Boolean
get() = wsServer?.isConnected == true
/** /**
* 启动所有秤服务,在 Application.onCreate() 中调用 * 启动所有秤服务,在 Application.onCreate() 中调用
*/ */
@@ -63,7 +67,7 @@ object ScaleServiceManager {
Log.d(TAG, "启动秤服务, deviceId=$deviceId, role=$role") Log.d(TAG, "启动秤服务, deviceId=$deviceId, role=$role")
// 所有设备:启动 WebSocket 服务端 + mDNS 注册 // 所有设备:启动 WebSocket 服务端 + mDNS 注册
wsServer = ScaleWebSocketServer(deviceId).also { it.start() } wsServer = ScaleWebSocketServer(deviceId, context).also { it.start() }
mdnsRegister = MdnsRegisterManager(context).also { it.register(deviceId) } mdnsRegister = MdnsRegisterManager(context).also { it.register(deviceId) }
if (role == DeviceRole.MASTER) { if (role == DeviceRole.MASTER) {
@@ -1,7 +1,9 @@
package com.shuwei.dish.match.scale package com.shuwei.dish.match.scale
import android.content.Context
import android.util.Log import android.util.Log
import com.google.gson.Gson import com.google.gson.Gson
import com.shuwei.dish.match.utils.NetworkUtil
import com.shuwei.dish.match.utils.WeightUtil import com.shuwei.dish.match.utils.WeightUtil
import org.java_websocket.WebSocket import org.java_websocket.WebSocket
import org.java_websocket.handshake.ClientHandshake import org.java_websocket.handshake.ClientHandshake
@@ -12,7 +14,7 @@ import java.net.InetSocketAddress
* 秤数据 WebSocket 服务端(主设备和子设备均运行) * 秤数据 WebSocket 服务端(主设备和子设备均运行)
* 监听 WeightUtil 回调,将本机秤数据实时推送给所有已连接的主设备客户端 * 监听 WeightUtil 回调,将本机秤数据实时推送给所有已连接的主设备客户端
*/ */
class ScaleWebSocketServer(private val deviceId: String) { class ScaleWebSocketServer(private val deviceId: String, private val context: Context) {
companion object { companion object {
private const val TAG = "ScaleWebSocketServer" private const val TAG = "ScaleWebSocketServer"
@@ -26,6 +28,10 @@ class ScaleWebSocketServer(private val deviceId: String) {
/** 主设备连接状态变化回调:true=已连接,false=已断开;在主线程外调用,需自行切换线程 */ /** 主设备连接状态变化回调:true=已连接,false=已断开;在主线程外调用,需自行切换线程 */
var onConnectionChanged: ((connected: Boolean) -> Unit)? = null var onConnectionChanged: ((connected: Boolean) -> Unit)? = null
/** 当前是否有主设备连接 */
val isConnected: Boolean
get() = server?.connections?.isNotEmpty() == true
/** 每个秤地址的最近一次推送时间,用于节流 */ /** 每个秤地址的最近一次推送时间,用于节流 */
private val lastPushTime = mutableMapOf<Int, Long>() private val lastPushTime = mutableMapOf<Int, Long>()
@@ -53,7 +59,8 @@ class ScaleWebSocketServer(private val deviceId: String) {
address = address, address = address,
weight = weight, weight = weight,
state = state, state = state,
ts = now ts = now,
ip = NetworkUtil.getLocalIpAddress(context)
) )
latestData[address] = data latestData[address] = data
broadcast(gson.toJson(data)) broadcast(gson.toJson(data))
@@ -47,7 +47,7 @@ class UdpBroadcastReceiver {
private fun listenLoop() { private fun listenLoop() {
try { try {
socket = DatagramSocket(UdpBroadcastSender.BROADCAST_PORT) socket = DatagramSocket(UdpBroadcastSender.BROADCAST_PORT).also { it.broadcast = true }
val buf = ByteArray(BUFFER_SIZE) val buf = ByteArray(BUFFER_SIZE)
while (running) { while (running) {
val packet = DatagramPacket(buf, buf.size) val packet = DatagramPacket(buf, buf.size)
@@ -1,8 +1,15 @@
package com.shuwei.dish.match.ui package com.shuwei.dish.match.ui
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent
import android.util.Log
import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.os.SystemClock
import android.view.KeyEvent
import android.view.animation.RotateAnimation
import androidx.activity.OnBackPressedCallback
import androidx.core.graphics.toColorInt
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.shuwei.dish.match.base.BaseActivity import com.shuwei.dish.match.base.BaseActivity
@@ -11,19 +18,15 @@ 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.ActivityInitBinding import com.shuwei.dish.match.databinding.ActivityInitBinding
import com.shuwei.dish.match.db.AppRepository import com.shuwei.dish.match.db.AppRepository
import com.shuwei.dish.match.http.UrlConfig
import com.shuwei.dish.match.utils.AppUtil import com.shuwei.dish.match.utils.AppUtil
import com.shuwei.dish.match.utils.QRCodeUtil import com.shuwei.dish.match.utils.NetworkUtils
import com.shuwei.dish.match.utils.SpTool import com.shuwei.dish.match.utils.SpTool
import com.shuwei.dish.match.utils.WeightUtil
import com.shuwei.dish.match.utils.Weigher2 import com.shuwei.dish.match.utils.Weigher2
import com.shuwei.dish.match.utils.ext.dp import com.shuwei.dish.match.utils.WeightUtil
import com.shuwei.dish.match.utils.ext.startActivity import com.shuwei.dish.match.utils.ext.startActivity
import com.shuwei.dish.match.utils.ext.toObject
import com.shuwei.dish.match.viewmodel.AppViewModel import com.shuwei.dish.match.viewmodel.AppViewModel
import com.shuwei.dish.match.viewmodel.factory.AppFactory import com.shuwei.dish.match.viewmodel.factory.AppFactory
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class InitActivity : BaseActivity() { class InitActivity : BaseActivity() {
@@ -33,10 +36,13 @@ class InitActivity : BaseActivity() {
private lateinit var binding: ActivityInitBinding private lateinit var binding: ActivityInitBinding
private lateinit var appViewModel: AppViewModel private lateinit var appViewModel: AppViewModel
private var startTime = 0L // 倒计时开始时间
private var lastNetworkCheckTime = 0L // 上次检测网络的时间
@SuppressLint("HardwareIds") @SuppressLint("HardwareIds")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
addBackKeyListener()
statusBarDarkFont(enable = true) statusBarDarkFont(enable = true)
// useBackground(false) // useBackground(false)
binding = ActivityInitBinding.inflate(layoutInflater) binding = ActivityInitBinding.inflate(layoutInflater)
@@ -48,55 +54,18 @@ class InitActivity : BaseActivity() {
setHeaderBgVisible(false) setHeaderBgVisible(false)
BaseApp.appVersion = AppUtil.getAppVersionCode(this).toString() BaseApp.appVersion = AppUtil.getAppVersionCode(this).toString()
// 获取 ANDROID_ID
// var androidId = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
// androidId = "39a7abdd06b3c7ab"
// BaseApp.deviceId = androidId
// SpTool.put(SpTool.DEVICE_ID, androidId)
// BaseApp.configUrl = UrlConfig.BASE_URL
BaseApp.canteenId = "0" BaseApp.canteenId = "0"
// // TODO: 以上保存deviceId用于临时使用,后续改为下面注释方式
// var deviceId = AppUtil.getUDID(this)
// Log.d(TAG, "onCreate: deviceId=$deviceId")
//// deviceId = "39a7abdd06b3c7ab"
// deviceId = "3ea47dc0-3cf0-3c2f-909c-265a9a65572e"
//// deviceId = "6ce7cd59-b875-38b2-b73d-88a570be3212"
//
// 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() initViewModel()
// 若有保存的串口配置则先应用(子设备串口可能与主设备不同)
// val savedPort = SpTool.getString(SpTool.SCALE_DEVICE_PORT, "")
// if (savedPort.isNotEmpty()) {
// Weigher2.setDevicePort(savedPort)
// }
// TODO: 测试不同的串口配置---------------------2、5、3
Weigher2.setDevicePort(
if (GlobalData.deviceId == "8fc2ab34-2137-3112-acca-f884ea8736d4") {
"/dev/ttyS7"
} else {
"/dev/ttyS4"
}
)
WeightUtil.init() WeightUtil.init()
WeightUtil.getWeight() WeightUtil.getWeight()
WeightUtil.startContinuousRead() WeightUtil.startContinuousRead()
countDown()
initViews()
val dpi = resources.displayMetrics.density
val metrics = resources.displayMetrics.toString()
Log.d(TAG, "dpi=$dpi, $metrics")
} }
private fun initViewModel() { private fun initViewModel() {
@@ -106,24 +75,27 @@ class InitActivity : BaseActivity() {
} }
private fun startNextPage() { private fun startNextPage() {
// 子设备:直接进入小屏专属页面,不走原有大屏业务流程 lifecycleScope.launch {
if (GlobalData.deviceRole == DeviceRole.SLAVE) { delay(2000)
startActivity<SlaveActivity>() // 子设备:直接进入小屏专属页面,不走原有大屏业务流程
return if (GlobalData.deviceRole == DeviceRole.SLAVE) {
} startActivity<SlaveActivity>()
// 主设备:走原有路由逻辑 return@launch
val launchPageType = SpTool.getInt(SpTool.LAUNCH_PAGE_TYPE, -1)
when (launchPageType) {
0 -> {
startActivity<SelectDishActivity>()
} }
// 主设备:走原有路由逻辑
val launchPageType = SpTool.getInt(SpTool.LAUNCH_PAGE_TYPE, -1)
when (launchPageType) {
0 -> {
startActivity<SelectDishActivity>()
}
1 -> { 1 -> {
goSampling() goSampling()
} }
else -> { else -> {
startActivity<HomeActivity>() startActivity<HomeActivity>()
}
} }
} }
} }
@@ -133,112 +105,152 @@ class InitActivity : BaseActivity() {
if (it.isEmpty()) { if (it.isEmpty()) {
//无调料信息打开采样历史列表页面,点击设置配置调料信息 //无调料信息打开采样历史列表页面,点击设置配置调料信息
startActivity<SamplingListActivity>() startActivity<SamplingListActivity>()
// finish()
return@loadSeasoning return@loadSeasoning
} }
appViewModel.countCookFood(cookMode = 1) { count -> appViewModel.countCookFood(cookMode = 1) { count ->
if (count > 0) { if (count > 0) {
//有烹饪中数据打开采样历史列表页面 //有烹饪中数据打开采样历史列表页面
startActivity<SamplingListActivity>() startActivity<SamplingListActivity>()
// finish()
return@countCookFood return@countCookFood
} }
//无烹饪数据打开新增采样页面 //无烹饪数据打开新增采样页面
startActivity<DishSamplingActivity> { startActivity<DishSamplingActivity> {
putExtra(DishSamplingActivity.PAGE_FROM, DishSamplingActivity.HOME) 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 {
//// HttpUtil.loopGetToken = false
//// getDeviceConfig()
// }
// }
// private fun getDeviceConfig() {
// val tokenUrl =
// "${UrlConfig.DEVICE_TOKEN}?qrcodeId=${BaseApp.deviceId}&appVersion=${BaseApp.appVersion}"
// 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=${BaseApp.appVersion}"
// 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
// }
// HttpUtil.loopGetToken = true
// 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
// }
override fun onDestroy() { override fun onDestroy() {
WeightUtil.stopContinuousRead() WeightUtil.stopContinuousRead()
super.onDestroy() super.onDestroy()
} }
/**
* 初始化控件事件
*/
private fun initViews() {
// 连接网络按钮点击事件
binding.btnConnectNetwork.setOnClickListener {
openNetworkSettings()
}
// 启动加载动画 - 持续旋转
startLoadingAnimation()
}
/**
* 启动加载动画 - ImageView 持续旋转
*/
private fun startLoadingAnimation() {
val rotateAnimation = RotateAnimation(
0f, 360f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f
).apply {
duration = 1200 // 旋转周期 1.2 秒
repeatCount = RotateAnimation.INFINITE // 无限循环
repeatMode = RotateAnimation.RESTART
}
binding.ivLoading.startAnimation(rotateAnimation)
}
override fun onResume() {
super.onResume()
if (NetworkUtils.isNetworkConnected(this)) {
startNextPage()
return
}
startCountdown()
}
override fun onPause() {
super.onPause()
// 页面不可见时停止倒计时
handler.removeCallbacksAndMessages(null)
}
/**
* 启动 60 秒倒计时
*/
private fun startCountdown() {
startTime = SystemClock.elapsedRealtime()
lastNetworkCheckTime = 0
// binding.llNetwork.setBackgroundColor(Color.TRANSPARENT)
binding.llLoading.visibility = android.view.View.VISIBLE
binding.llNetworkButton.visibility = android.view.View.GONE
binding.tvCountdown.text = "60秒"
scheduleCountdown()
}
/**
* 定时更新倒计时
* 使用 SystemClock.elapsedRealtime() 确保精确计时
*/
private fun scheduleCountdown() {
handler.postDelayed({
val elapsedTime = SystemClock.elapsedRealtime() - startTime
val remainingTime = 60 - (elapsedTime / 1000).toInt()
val currentNetworkCheckTime = (elapsedTime / 1000).toInt()
// 每 10 秒检测一次网络
if (currentNetworkCheckTime > 0 && currentNetworkCheckTime % 10 == 0 && currentNetworkCheckTime != lastNetworkCheckTime.toInt()) {
lastNetworkCheckTime = currentNetworkCheckTime.toLong()
checkNetworkConnection()
}
// 倒计时未结束,继续更新 UI
if (remainingTime > 0) {
binding.tvCountdown.text = "${remainingTime}"
scheduleCountdown()
} else {
// 倒计时结束,最后检测一次网络
if (NetworkUtils.isNetworkConnected(this)) {
startNextPage()
} else {
// 网络未连接,显示"连接网络"按钮
showNetworkButton()
}
}
}, 100) // 每 100ms 检查一次,确保精确性
}
/**
* 检测网络连接
*/
private fun checkNetworkConnection() {
if (NetworkUtils.isNetworkConnected(this)) {
// 网络连接成功,停止倒计时并跳转到 HomeActivity
handler.removeCallbacksAndMessages(null)
startNextPage()
}
}
/**
* 显示"连接网络"按钮
*/
private fun showNetworkButton() {
// binding.llNetwork.setBackgroundColor("#5C77F7".toColorInt())
binding.llLoading.visibility = android.view.View.GONE
binding.llNetworkButton.visibility = android.view.View.VISIBLE
}
/**
* 打开系统网络设置页面
*/
private fun openNetworkSettings() {
val intent = Intent(android.provider.Settings.ACTION_WIFI_SETTINGS)
startActivity(intent) {}
}
private fun addBackKeyListener() {
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// 禁用返回键,不执行任何操作
}
})
}
} }
@@ -6,6 +6,7 @@ import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.activity.OnBackPressedCallback
import com.shuwei.dish.match.base.BaseActivity import com.shuwei.dish.match.base.BaseActivity
import com.shuwei.dish.match.base.GlobalData import com.shuwei.dish.match.base.GlobalData
import com.shuwei.dish.match.databinding.ActivitySlaveBinding import com.shuwei.dish.match.databinding.ActivitySlaveBinding
@@ -44,9 +45,21 @@ class SlaveActivity : BaseActivity() {
binding.rvScaleList.itemAnimator = null // 关闭默认动画,避免数据频繁更新时出现闪烁错乱 binding.rvScaleList.itemAnimator = null // 关闭默认动画,避免数据频繁更新时出现闪烁错乱
binding.rvScaleList.adapter = adapter binding.rvScaleList.adapter = adapter
updateMasterStatus(false) updateMasterStatus(ScaleServiceManager.isMasterConnected)
listenLocalScales() listenLocalScales()
listenMasterConnection() listenMasterConnection()
addBackKeyListener()
}
/**
* 禁用返回键,防止退回 InitActivity 后重复打开本页
*/
private fun addBackKeyListener() {
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// 禁用返回键,不执行任何操作
}
})
} }
/** /**
@@ -0,0 +1,24 @@
package com.shuwei.dish.match.utils
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
/**
* 网络连接检测工具类
* 提供检测设备网络连接状态的功能
*/
object NetworkUtils {
/**
* 检测设备是否连接到网络
* @param context 应用上下文
* @return true 表示已连接网络,false 表示未连接
*/
fun isNetworkConnected(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = connectivityManager.activeNetwork ?: return false
val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}
}
@@ -7,6 +7,7 @@ import android.util.Log
import com.shuwei.dish.match.utils.ext.roundedOneDecimalPlace import com.shuwei.dish.match.utils.ext.roundedOneDecimalPlace
//import com.aithings.Weigher //import com.aithings.Weigher
import com.wabon.wbintelligenthardwaresdk.api.SensorScale import com.wabon.wbintelligenthardwaresdk.api.SensorScale
import java.util.concurrent.ConcurrentHashMap
typealias WeightCallback = (address: Int, state: Int, weight: Double) -> Unit typealias WeightCallback = (address: Int, state: Int, weight: Double) -> Unit
@@ -17,8 +18,7 @@ object WeightUtil {
const val STATE_OVER_WEIGHT = SensorScale.STATE_OVER_WEIGHT const val STATE_OVER_WEIGHT = SensorScale.STATE_OVER_WEIGHT
var isConnected = false var isConnected = false
var weightFuncMap: MutableMap<String, WeightCallback?>? = val weightFuncMap: ConcurrentHashMap<String, WeightCallback?> = ConcurrentHashMap()
null
fun init() { fun init() {
Weigher2.init() Weigher2.init()
@@ -38,7 +38,7 @@ object WeightUtil {
} }
val useWeight = (weight*1000).roundedOneDecimalPlace() val useWeight = (weight*1000).roundedOneDecimalPlace()
Log.d(TAG, "readWeight, address=$address,state=$stateStr, weight=$useWeight") Log.d(TAG, "readWeight, address=$address,state=$stateStr, weight=$useWeight")
weightFuncMap?.forEach { (key, value) -> weightFuncMap.forEach { (key, value) ->
value?.invoke(address, state, useWeight) value?.invoke(address, state, useWeight)
} }
@@ -82,18 +82,15 @@ object WeightUtil {
} }
} }
fun removeWeightListener(weightKey: String){ fun removeWeightListener(weightKey: String) {
weightFuncMap?.remove(weightKey) weightFuncMap.remove(weightKey)
} }
fun addWeightListener( fun addWeightListener(
weightKey: String, weightKey: String,
getWeight: WeightCallback = { _, _, _ -> } getWeight: WeightCallback = { _, _, _ -> }
) { ) {
if (weightFuncMap == null) { weightFuncMap[weightKey] = getWeight
weightFuncMap = mutableMapOf()
}
weightFuncMap?.put(weightKey, getWeight)
} }
} }
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 横屏专用启动背景,适配子设备 800×480 屏幕 -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 背景色铺满全屏 -->
<item>
<shape android:shape="rectangle">
<solid android:color="@color/bg_color"/>
</shape>
</item>
<!-- 图片居中显示,适配横屏比例 -->
<item
android:width="200dp"
android:height="190dp"
android:gravity="center">
<bitmap android:src="@drawable/img_init" android:scaleType="fitCenter"/>
</item>
</layer-list>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#4ECDC4" />
<corners android:radius="12dp" />
<stroke
android:width="2dp"
android:color="#2BA39F" />
</shape>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="80dp"
android:height="80dp"
android:viewportWidth="24"
android:viewportHeight="24">
<!-- 网络断开的图标 -->
<path
android:fillColor="#FF6B6B"
android:pathData="M1,9l2,2c4.97,-4.97 13.03,-4.97 18,0l2,-2C16.93,2.93 7.08,2.93 1,9zM5,13l2,2c2.76,-2.76 7.24,-2.76 10,0l2,-2c-4.42,-4.42 -11.58,-4.42 -16,0zM9,17h6v2h-6z" />
</vector>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="ring"
android:innerRadiusRatio="3"
android:thicknessRatio="8"
android:useLevel="false">
<gradient
android:type="sweep"
android:startColor="#FF6B6B"
android:centerColor="#4ECDC4"
android:endColor="#FF6B6B"
android:angle="0" />
</shape>
@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 横屏 800×480 专用布局,View ID 与 activity_init.xml 完全一致,供子设备使用 -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/bg_color">
<!-- 主内容区:水平居中排列加载区和网络错误区 -->
<LinearLayout
android:id="@+id/llNetwork"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center">
<!-- 加载动画容器 -->
<LinearLayout
android:id="@+id/llLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:visibility="visible">
<!-- 加载动画 -->
<ImageView
android:id="@+id/ivLoading"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/loading_spinner"
android:contentDescription="加载中" />
<!-- 倒计时文字 -->
<TextView
android:id="@+id/tvCountdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="60秒"
android:textSize="32sp"
android:textColor="@color/black"
android:textStyle="bold"
android:layout_marginTop="16dp" />
<!-- 提示文字 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="网络连接检测中,请稍后……"
android:textSize="16sp"
android:textColor="#555555"
android:layout_marginTop="10dp"
tools:ignore="HardcodedText" />
</LinearLayout>
<!-- 连接网络按钮容器 -->
<LinearLayout
android:id="@+id/llNetworkButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:visibility="gone">
<!-- 网络错误图标 -->
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@drawable/ic_network_error"
android:contentDescription="网络连接失败"
android:layout_marginBottom="16dp" />
<!-- 连接失败提示 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="网络连接失败"
android:textSize="28sp"
android:textColor="@color/black"
android:textStyle="bold"
android:layout_marginBottom="20dp"
tools:ignore="HardcodedText" />
<!-- 连接网络按钮 -->
<Button
android:id="@+id/btnConnectNetwork"
android:layout_width="260dp"
android:layout_height="56dp"
android:text="连接网络"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="@android:color/white"
android:background="@drawable/btn_connect_network"
tools:ignore="HardcodedText" />
</LinearLayout>
</LinearLayout>
<!-- 以下 View 在此布局中不使用,保留 ID 确保 ActivityInitBinding.bind() 不崩溃 -->
<TextView
android:id="@+id/btnInit"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="gone" />
<ImageView
android:id="@+id/ivQrCode"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="gone"
tools:ignore="ContentDescription" />
</FrameLayout>
+136 -42
View File
@@ -1,51 +1,145 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
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="match_parent" android:layout_height="match_parent">
android:orientation="vertical">
<ImageView <LinearLayout
android:layout_width="450dp" android:layout_width="match_parent"
android:layout_height="430dp"
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"
android:visibility="invisible"/>
<TextView
android:id="@+id/btnInit"
android:layout_width="300dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:orientation="vertical">
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="invisible"/>
<Space <ImageView
android:layout_width="wrap_content" android:layout_width="450dp"
android:layout_height="0dp" android:layout_height="430dp"
android:layout_weight="1"/> 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"
android:visibility="invisible"/>
<ImageView <TextView
android:id="@+id/ivQrCode" android:id="@+id/btnInit"
android:layout_width="200dp" android:layout_width="300dp"
android:layout_height="200dp" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom" android:layout_gravity="center_horizontal"
android:layout_marginBottom="50dp" android:layout_marginTop="160dp"
tools:src="@mipmap/ic_logo1024" android:background="@drawable/bg_init_button"
tools:ignore="ContentDescription" android:gravity="center"
android:visibility="invisible"/> android:padding="10dp"
android:text="设备初始化"
android:textColor="@color/white"
android:textSize="30sp"
android:textStyle="bold"
tools:ignore="HardcodedText"
android:visibility="invisible"/>
</LinearLayout> <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"
android:visibility="invisible"/>
</LinearLayout>
<LinearLayout
android:id="@+id/llNetwork"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<!-- Loading 动画容器 -->
<LinearLayout
android:id="@+id/llLoading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:visibility="visible"
android:layout_marginTop="260dp">
<!-- 加载动画 -->
<ImageView
android:id="@+id/ivLoading"
android:layout_width="160dp"
android:layout_height="160dp"
android:src="@drawable/loading_spinner"
android:contentDescription="加载中" />
<!-- 倒计时文字 -->
<TextView
android:id="@+id/tvCountdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="60秒"
android:textSize="40sp"
android:textColor="@color/black"
android:textStyle="bold"
android:layout_marginTop="50dp" />
<!-- 提示文字 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="网络连接检测中,请稍后……"
android:textSize="22sp"
android:textColor="#555555"
android:layout_marginTop="20dp" />
</LinearLayout>
<!-- 连接网络按钮容器 -->
<LinearLayout
android:id="@+id/llNetworkButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center"
android:visibility="gone">
<!-- 网络错误图标 -->
<ImageView
android:layout_width="140dp"
android:layout_height="140dp"
android:src="@drawable/ic_network_error"
android:contentDescription="网络连接失败"
android:layout_marginBottom="30dp" />
<!-- 连接失败提示 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="网络连接失败"
android:textSize="36sp"
android:textColor="@color/black"
android:textStyle="bold"
android:layout_marginBottom="40dp" />
<!-- 连接网络按钮 -->
<Button
android:id="@+id/btnConnectNetwork"
android:layout_width="350dp"
android:layout_height="70dp"
android:text="连接网络"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="@android:color/white"
android:background="@drawable/btn_connect_network" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
+2
View File
@@ -11,6 +11,8 @@
<item name="colorOnSecondary">@color/black</item> <item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor">@android:color/transparent</item> <item name="android:statusBarColor">@android:color/transparent</item>
<!-- 导航栏颜色与背景一致,避免启动时黑色闪烁;上滑 transient 导航栏由系统控制不受影响 -->
<item name="android:navigationBarColor">@color/bg_color</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
<!-- <item name="android:layout_width">828dp</item>--> <!-- <item name="android:layout_width">828dp</item>-->
<item name="android:fitsSystemWindows">false</item> <item name="android:fitsSystemWindows">false</item>
+7
View File
@@ -11,6 +11,8 @@
<item name="colorOnSecondary">@color/black</item> <item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. --> <!-- Status bar color. -->
<item name="android:statusBarColor">@android:color/transparent</item> <item name="android:statusBarColor">@android:color/transparent</item>
<!-- 导航栏颜色与背景一致,避免启动时黑色闪烁;上滑 transient 导航栏由系统控制不受影响 -->
<item name="android:navigationBarColor">@color/bg_color</item>
<!-- Customize your theme here. --> <!-- Customize your theme here. -->
<!-- <item name="android:layout_width">828dp</item>--> <!-- <item name="android:layout_width">828dp</item>-->
<item name="android:fitsSystemWindows">false</item> <item name="android:fitsSystemWindows">false</item>
@@ -19,6 +21,11 @@
</style> </style>
<!-- 非启动页主题:windowBackground 为纯色,避免 bg_splash 闪烁 -->
<style name="Theme.DishMatch.NoSplash" parent="Theme.DishMatch">
<item name="android:windowBackground">@color/bg_color</item>
</style>
<style name="CookButtonStyle" parent="Widget.MaterialComponents.Button"> <style name="CookButtonStyle" parent="Widget.MaterialComponents.Button">
<!-- 禁用默认背景色 --> <!-- 禁用默认背景色 -->
<item name="backgroundTint">@null</item> <item name="backgroundTint">@null</item>