feat: 设备绑定与配对模块
新增: - SplashFragment 启动分发(初始化+绑定检查+导航到Home或Bind) - BindFragment 二维码配对页面(ZXing生成QR码+MQTT绑定监听) - WatchBindInfo 绑定信息数据类 修改: - nav_main.xml startDestination改为SplashFragment,添加导航action - HomeFragment 移除初始化逻辑到Splash,添加MQTT解绑处理 - CommonApi getWatchByImei返回类型改为WatchBindInfo Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
package com.xiaoqu.watch.data.device
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备绑定信息数据类
|
||||||
|
* 对应 getWatchByImei API 返回和 MQTT messageType=2 消息中的用户信息
|
||||||
|
*/
|
||||||
|
data class WatchBindInfo(
|
||||||
|
@SerializedName("userId") val userId: Long = 0,
|
||||||
|
@SerializedName("mobile") val mobile: String = "",
|
||||||
|
@SerializedName("userName") val userName: String = "",
|
||||||
|
@SerializedName("headUrl") val headUrl: String = ""
|
||||||
|
)
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.xiaoqu.watch.network.api
|
package com.xiaoqu.watch.network.api
|
||||||
|
|
||||||
|
import com.xiaoqu.watch.data.device.WatchBindInfo
|
||||||
import com.xiaoqu.watch.network.ApiResponse
|
import com.xiaoqu.watch.network.ApiResponse
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
@@ -16,9 +17,9 @@ interface CommonApi {
|
|||||||
@POST("watch/bindWatchConfirm")
|
@POST("watch/bindWatchConfirm")
|
||||||
suspend fun bindWatchConfirm(@Body params: Map<String, Any>): ApiResponse<Any>
|
suspend fun bindWatchConfirm(@Body params: Map<String, Any>): ApiResponse<Any>
|
||||||
|
|
||||||
/** 根据 IMEI 查询手表信息 */
|
/** 根据 IMEI 查询手表绑定信息(返回用户数据则已绑定,否则未绑定) */
|
||||||
@GET("watch/getWatchByImei")
|
@GET("watch/getWatchByImei")
|
||||||
suspend fun getWatchByImei(@Query("imei") imei: String): ApiResponse<Any>
|
suspend fun getWatchByImei(@Query("imei") imei: String): ApiResponse<WatchBindInfo>
|
||||||
|
|
||||||
/** 检查版本更新 */
|
/** 检查版本更新 */
|
||||||
@GET("newAppVersion/queryWatch")
|
@GET("newAppVersion/queryWatch")
|
||||||
|
|||||||
@@ -1,15 +1,158 @@
|
|||||||
package com.xiaoqu.watch.ui.bind
|
package com.xiaoqu.watch.ui.bind
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.zxing.BarcodeFormat
|
||||||
|
import com.google.zxing.MultiFormatWriter
|
||||||
|
import com.google.zxing.common.BitMatrix
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import com.xiaoqu.watch.R
|
||||||
|
import com.xiaoqu.watch.data.device.WatchBindInfo
|
||||||
|
import com.xiaoqu.watch.data.prefs.DevicePrefs
|
||||||
|
import com.xiaoqu.watch.data.prefs.UserPrefs
|
||||||
import com.xiaoqu.watch.databinding.FragmentBindBinding
|
import com.xiaoqu.watch.databinding.FragmentBindBinding
|
||||||
|
import com.xiaoqu.watch.event.AppEvent
|
||||||
|
import com.xiaoqu.watch.event.EventBus
|
||||||
|
import com.xiaoqu.watch.network.api.CommonApi
|
||||||
|
import com.xiaoqu.watch.network.safeApiCall
|
||||||
import com.xiaoqu.watch.ui.common.BaseFragment
|
import com.xiaoqu.watch.ui.common.BaseFragment
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备绑定页面
|
||||||
|
* 显示二维码供手机 APP 扫码绑定
|
||||||
|
* 监听 MQTT messageType=2 绑定成功消息
|
||||||
|
*/
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class BindFragment : BaseFragment<FragmentBindBinding>() {
|
class BindFragment : BaseFragment<FragmentBindBinding>() {
|
||||||
|
|
||||||
|
@Inject lateinit var devicePrefs: DevicePrefs
|
||||||
|
@Inject lateinit var userPrefs: UserPrefs
|
||||||
|
@Inject lateinit var eventBus: EventBus
|
||||||
|
@Inject lateinit var commonApi: CommonApi
|
||||||
|
@Inject lateinit var gson: Gson
|
||||||
|
|
||||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBindBinding {
|
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentBindBinding {
|
||||||
return FragmentBindBinding.inflate(inflater, container, false)
|
return FragmentBindBinding.inflate(inflater, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
// 生成并显示二维码
|
||||||
|
generateQrCode()
|
||||||
|
|
||||||
|
// 监听 MQTT 绑定消息
|
||||||
|
observeBindEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成二维码
|
||||||
|
* 编码设备信息 JSON:{bluetoothName, imei, serial, power}
|
||||||
|
*/
|
||||||
|
private fun generateQrCode() {
|
||||||
|
try {
|
||||||
|
// 构建设备信息 JSON(与旧版 codeScanPair.vue 一致)
|
||||||
|
val qrData = gson.toJson(mapOf(
|
||||||
|
"bluetoothName" to devicePrefs.bluetoothName,
|
||||||
|
"imei" to devicePrefs.imei,
|
||||||
|
"serial" to devicePrefs.serial,
|
||||||
|
"power" to 100 // TODO: 接入实时电量
|
||||||
|
))
|
||||||
|
Timber.d("绑定: QR 数据=$qrData")
|
||||||
|
|
||||||
|
// 使用 ZXing 生成二维码 Bitmap
|
||||||
|
val size = 500 // 生成 500×500 像素,ImageView 会缩放
|
||||||
|
val bitMatrix: BitMatrix = MultiFormatWriter().encode(
|
||||||
|
qrData, BarcodeFormat.QR_CODE, size, size
|
||||||
|
)
|
||||||
|
val bitmap = bitMatrixToBitmap(bitMatrix)
|
||||||
|
binding.ivQrCode.setImageBitmap(bitmap)
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "绑定: 二维码生成失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 ZXing BitMatrix 转换为 Android Bitmap
|
||||||
|
* 黑色像素用黑色,白色像素用白色
|
||||||
|
*/
|
||||||
|
private fun bitMatrixToBitmap(matrix: BitMatrix): Bitmap {
|
||||||
|
val width = matrix.width
|
||||||
|
val height = matrix.height
|
||||||
|
val pixels = IntArray(width * height)
|
||||||
|
for (y in 0 until height) {
|
||||||
|
for (x in 0 until width) {
|
||||||
|
pixels[y * width + x] = if (matrix[x, y]) {
|
||||||
|
0xFF000000.toInt() // 黑色
|
||||||
|
} else {
|
||||||
|
0xFFFFFFFF.toInt() // 白色
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Bitmap.createBitmap(pixels, width, height, Bitmap.Config.ARGB_8888)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听 MQTT 绑定成功消息(messageType=2)
|
||||||
|
* 收到后存储用户信息 → 调用 API 确认 → 导航到首页
|
||||||
|
*/
|
||||||
|
private fun observeBindEvent() {
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
eventBus.events.collect { event ->
|
||||||
|
if (event is AppEvent.MqttMessageReceived && event.type == 2) {
|
||||||
|
handleBindSuccess(event.rawJson)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理绑定成功
|
||||||
|
* 1. 解析用户信息并存入 UserPrefs
|
||||||
|
* 2. 调用 bindWatchConfirm API 确认
|
||||||
|
* 3. 导航到首页
|
||||||
|
*/
|
||||||
|
private fun handleBindSuccess(rawJson: String) {
|
||||||
|
try {
|
||||||
|
Timber.d("绑定: 收到绑定消息 $rawJson")
|
||||||
|
|
||||||
|
// 解析用户信息
|
||||||
|
val bindInfo = gson.fromJson(rawJson, WatchBindInfo::class.java)
|
||||||
|
|
||||||
|
// 存入 UserPrefs
|
||||||
|
userPrefs.saveUser(
|
||||||
|
userId = bindInfo.userId,
|
||||||
|
mobile = bindInfo.mobile,
|
||||||
|
userName = bindInfo.userName,
|
||||||
|
headUrl = bindInfo.headUrl
|
||||||
|
)
|
||||||
|
|
||||||
|
// 调用 API 确认绑定
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
val params = mapOf<String, Any>(
|
||||||
|
"imei" to devicePrefs.imei,
|
||||||
|
"userId" to bindInfo.userId
|
||||||
|
)
|
||||||
|
safeApiCall { commonApi.bindWatchConfirm(params) }
|
||||||
|
Timber.d("绑定: 确认 API 已调用")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导航到首页
|
||||||
|
Timber.d("绑定: 绑定成功,导航到首页")
|
||||||
|
findNavController().navigate(R.id.action_bind_to_home)
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "绑定: 处理绑定消息异常")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
136
app/src/main/java/com/xiaoqu/watch/ui/common/SplashFragment.kt
Normal file
136
app/src/main/java/com/xiaoqu/watch/ui/common/SplashFragment.kt
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
package com.xiaoqu.watch.ui.common
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
|
import com.xiaoqu.watch.R
|
||||||
|
import com.xiaoqu.watch.data.prefs.DevicePrefs
|
||||||
|
import com.xiaoqu.watch.data.prefs.UserPrefs
|
||||||
|
import com.xiaoqu.watch.network.ApiResult
|
||||||
|
import com.xiaoqu.watch.network.api.CommonApi
|
||||||
|
import com.xiaoqu.watch.network.safeApiCall
|
||||||
|
import com.xiaoqu.watch.service.manager.MqttManager
|
||||||
|
import com.xiaoqu.watch.util.DeviceUtil
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启动分发页面
|
||||||
|
* 职责:初始化设备信息 → 连接 MQTT → 检查绑定状态 → 导航到首页或绑定页
|
||||||
|
* 纯逻辑,无 UI(黑色背景,用户看不到此页面)
|
||||||
|
*/
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class SplashFragment : Fragment() {
|
||||||
|
|
||||||
|
@Inject lateinit var devicePrefs: DevicePrefs
|
||||||
|
@Inject lateinit var userPrefs: UserPrefs
|
||||||
|
@Inject lateinit var mqttManager: MqttManager
|
||||||
|
@Inject lateinit var commonApi: CommonApi
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View {
|
||||||
|
return inflater.inflate(R.layout.fragment_splash, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
// 1. 初始化设备信息(首次启动时写入 SP)
|
||||||
|
initDevicePrefs()
|
||||||
|
|
||||||
|
// 2. 连接 MQTT(需要 IMEI,所以必须在 initDevicePrefs 之后)
|
||||||
|
mqttManager.connect()
|
||||||
|
|
||||||
|
// 3. 检查绑定状态并导航
|
||||||
|
checkBindStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 首次启动时初始化设备信息到 device_prefs */
|
||||||
|
private fun initDevicePrefs() {
|
||||||
|
if (!devicePrefs.isInitialized) {
|
||||||
|
val info = DeviceUtil.getDeviceInfo(requireContext())
|
||||||
|
devicePrefs.saveDeviceInfo(
|
||||||
|
imei = info.imei, serial = info.serial,
|
||||||
|
bluetoothName = info.bluetoothName, bluetoothMac = info.bluetoothMac,
|
||||||
|
brand = info.brand, model = info.model,
|
||||||
|
osVersion = info.osVersion, totalMemory = info.totalMemory
|
||||||
|
)
|
||||||
|
Timber.d("Splash: 设备信息已初始化")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查绑定状态:
|
||||||
|
* 1. 调用 API 获取最新绑定状态
|
||||||
|
* 2. 有数据 → 已绑定 → 更新 UserPrefs → 导航到首页
|
||||||
|
* 3. 无数据 → 未绑定 → 导航到绑定页
|
||||||
|
* 4. 网络异常 → 用本地 isBound 兜底
|
||||||
|
*/
|
||||||
|
private fun checkBindStatus() {
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
val imei = devicePrefs.imei
|
||||||
|
|
||||||
|
// 调用 API 检查绑定状态
|
||||||
|
val result = safeApiCall { commonApi.getWatchByImei(imei) }
|
||||||
|
|
||||||
|
when (result) {
|
||||||
|
is ApiResult.Success -> {
|
||||||
|
// API 返回数据 → 已绑定
|
||||||
|
Timber.d("Splash: API 返回已绑定")
|
||||||
|
// TODO: 解析返回数据更新 UserPrefs(等确认实际字段后完善)
|
||||||
|
navigateToHome()
|
||||||
|
}
|
||||||
|
is ApiResult.Error -> {
|
||||||
|
if (result.code == 1) {
|
||||||
|
// code=1 未绑定
|
||||||
|
Timber.d("Splash: API 返回未绑定")
|
||||||
|
navigateToBind()
|
||||||
|
} else {
|
||||||
|
// 其他错误,用本地状态兜底
|
||||||
|
Timber.w("Splash: API 错误 code=${result.code}, 用本地状态兜底")
|
||||||
|
navigateByLocalState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ApiResult.NetworkError -> {
|
||||||
|
// 网络异常,用本地状态兜底
|
||||||
|
Timber.w("Splash: 网络异常, 用本地状态兜底")
|
||||||
|
navigateByLocalState()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据本地 UserPrefs 绑定状态导航(离线兜底) */
|
||||||
|
private fun navigateByLocalState() {
|
||||||
|
if (userPrefs.isBound) {
|
||||||
|
navigateToHome()
|
||||||
|
} else {
|
||||||
|
navigateToBind()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导航到首页(popUpTo splash 防止返回) */
|
||||||
|
private fun navigateToHome() {
|
||||||
|
Timber.d("Splash: → HomeFragment")
|
||||||
|
findNavController().navigate(
|
||||||
|
R.id.action_splash_to_home
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 导航到绑定页(popUpTo splash 防止返回) */
|
||||||
|
private fun navigateToBind() {
|
||||||
|
Timber.d("Splash: → BindFragment")
|
||||||
|
findNavController().navigate(
|
||||||
|
R.id.action_splash_to_bind
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,12 +16,10 @@ import com.xiaoqu.watch.device.sensor.VibrationController
|
|||||||
import com.xiaoqu.watch.device.sensor.VibrationDefaults
|
import com.xiaoqu.watch.device.sensor.VibrationDefaults
|
||||||
import com.xiaoqu.watch.event.AppEvent
|
import com.xiaoqu.watch.event.AppEvent
|
||||||
import com.xiaoqu.watch.event.EventBus
|
import com.xiaoqu.watch.event.EventBus
|
||||||
import com.xiaoqu.watch.service.manager.MqttManager
|
|
||||||
import com.xiaoqu.watch.ui.common.BaseFragment
|
import com.xiaoqu.watch.ui.common.BaseFragment
|
||||||
import com.xiaoqu.watch.ui.widget.NavBarHelper
|
import com.xiaoqu.watch.ui.widget.NavBarHelper
|
||||||
import com.xiaoqu.watch.ui.widget.QuTipDialog
|
import com.xiaoqu.watch.ui.widget.QuTipDialog
|
||||||
import com.xiaoqu.watch.util.DateUtil
|
import com.xiaoqu.watch.util.DateUtil
|
||||||
import com.xiaoqu.watch.util.DeviceUtil
|
|
||||||
import com.xiaoqu.watch.util.NetworkUtil
|
import com.xiaoqu.watch.util.NetworkUtil
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -43,7 +41,6 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||||||
@Inject lateinit var nfcController: NfcController
|
@Inject lateinit var nfcController: NfcController
|
||||||
@Inject lateinit var vibrationController: VibrationController
|
@Inject lateinit var vibrationController: VibrationController
|
||||||
@Inject lateinit var eventBus: EventBus
|
@Inject lateinit var eventBus: EventBus
|
||||||
@Inject lateinit var mqttManager: MqttManager
|
|
||||||
|
|
||||||
/** 提示弹窗 */
|
/** 提示弹窗 */
|
||||||
private lateinit var tipDialog: QuTipDialog
|
private lateinit var tipDialog: QuTipDialog
|
||||||
@@ -62,15 +59,9 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
// 初始化设备信息
|
// 设置 NavBar 为首页模式(initDevicePrefs 和 MQTT 已在 SplashFragment 完成)
|
||||||
initDevicePrefs()
|
|
||||||
|
|
||||||
// 设置 NavBar 为首页模式
|
|
||||||
NavBarHelper.setupHomePage(binding.root)
|
NavBarHelper.setupHomePage(binding.root)
|
||||||
|
|
||||||
// 连接 MQTT(必须在 initDevicePrefs 之后,确保 IMEI 可用)
|
|
||||||
mqttManager.connect()
|
|
||||||
|
|
||||||
// 初始化弹窗
|
// 初始化弹窗
|
||||||
val dialogContainer = requireActivity().findViewById<FrameLayout>(R.id.dialog_container)
|
val dialogContainer = requireActivity().findViewById<FrameLayout>(R.id.dialog_container)
|
||||||
tipDialog = QuTipDialog(dialogContainer)
|
tipDialog = QuTipDialog(dialogContainer)
|
||||||
@@ -97,19 +88,6 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 首次启动时初始化设备信息到 device_prefs */
|
|
||||||
private fun initDevicePrefs() {
|
|
||||||
if (!devicePrefs.isInitialized) {
|
|
||||||
val info = DeviceUtil.getDeviceInfo(requireContext())
|
|
||||||
devicePrefs.saveDeviceInfo(
|
|
||||||
imei = info.imei, serial = info.serial,
|
|
||||||
bluetoothName = info.bluetoothName, bluetoothMac = info.bluetoothMac,
|
|
||||||
brand = info.brand, model = info.model,
|
|
||||||
osVersion = info.osVersion, totalMemory = info.totalMemory
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 更新状态信息显示(try-catch 保护,防止系统关机时 DeadSystemException) */
|
/** 更新状态信息显示(try-catch 保护,防止系统关机时 DeadSystemException) */
|
||||||
private fun updateStatus() {
|
private fun updateStatus() {
|
||||||
try {
|
try {
|
||||||
@@ -224,6 +202,12 @@ class HomeFragment : BaseFragment<FragmentHomeBinding>() {
|
|||||||
is AppEvent.MqttDisconnected -> updateStatus()
|
is AppEvent.MqttDisconnected -> updateStatus()
|
||||||
is AppEvent.MqttMessageReceived -> {
|
is AppEvent.MqttMessageReceived -> {
|
||||||
Timber.d("MQTT消息: type=${event.type}")
|
Timber.d("MQTT消息: type=${event.type}")
|
||||||
|
// messageType=3: 解绑 → 清除用户数据,导航到绑定页
|
||||||
|
if (event.type == 3) {
|
||||||
|
userPrefs.clear()
|
||||||
|
findMainNavController().navigate(R.id.action_home_to_bind)
|
||||||
|
return@collect
|
||||||
|
}
|
||||||
updateStatus()
|
updateStatus()
|
||||||
}
|
}
|
||||||
else -> {} // 其他事件不处理
|
else -> {} // 其他事件不处理
|
||||||
|
|||||||
@@ -1,9 +1,45 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<!-- BindFragment:二维码配对页面(全屏,无 NavBar) -->
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:background="@color/background">
|
android:background="@color/background"
|
||||||
|
android:gravity="center"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="@dimen/safe_area_left">
|
||||||
|
|
||||||
<!-- TODO: 二维码 + 设备信息 -->
|
<!-- 二维码图片 -->
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/ivQrCode"
|
||||||
|
android:layout_width="160dp"
|
||||||
|
android:layout_height="160dp"
|
||||||
|
android:layout_marginBottom="@dimen/spacing_lg"
|
||||||
|
android:contentDescription="配对二维码" />
|
||||||
|
|
||||||
</FrameLayout>
|
<!-- 标题 -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="扫码绑定手表"
|
||||||
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="@dimen/text_title"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:layout_marginBottom="@dimen/spacing_md" />
|
||||||
|
|
||||||
|
<!-- 说明文字 -->
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="1. 下载小趣智品APP"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="@dimen/text_caption"
|
||||||
|
android:layout_marginBottom="@dimen/spacing_xs" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="2. 在APP中扫此码添加手表"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="@dimen/text_caption" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|||||||
6
app/src/main/res/layout/fragment_splash.xml
Normal file
6
app/src/main/res/layout/fragment_splash.xml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- SplashFragment:启动分发页,纯逻辑无 UI(黑色背景) -->
|
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/background" />
|
||||||
@@ -2,19 +2,56 @@
|
|||||||
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
|
<navigation 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"
|
||||||
android:id="@+id/nav_main"
|
android:id="@+id/nav_main"
|
||||||
app:startDestination="@id/homeFragment">
|
app:startDestination="@id/splashFragment">
|
||||||
|
|
||||||
|
<!-- 启动分发页(检查绑定状态 → Home 或 Bind) -->
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/splashFragment"
|
||||||
|
android:name="com.xiaoqu.watch.ui.common.SplashFragment"
|
||||||
|
android:label="启动">
|
||||||
|
|
||||||
|
<!-- splash → home(已绑定) -->
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_splash_to_home"
|
||||||
|
app:destination="@id/homeFragment"
|
||||||
|
app:popUpTo="@id/splashFragment"
|
||||||
|
app:popUpToInclusive="true" />
|
||||||
|
|
||||||
|
<!-- splash → bind(未绑定) -->
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_splash_to_bind"
|
||||||
|
app:destination="@id/bindFragment"
|
||||||
|
app:popUpTo="@id/splashFragment"
|
||||||
|
app:popUpToInclusive="true" />
|
||||||
|
</fragment>
|
||||||
|
|
||||||
<!-- 首页(含 ViewPager2 左右滑动:设置页 / 主页) -->
|
<!-- 首页(含 ViewPager2 左右滑动:设置页 / 主页) -->
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/homeFragment"
|
android:id="@+id/homeFragment"
|
||||||
android:name="com.xiaoqu.watch.ui.home.HomeFragment"
|
android:name="com.xiaoqu.watch.ui.home.HomeFragment"
|
||||||
android:label="首页" />
|
android:label="首页">
|
||||||
|
|
||||||
|
<!-- home → bind(解绑后) -->
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_home_to_bind"
|
||||||
|
app:destination="@id/bindFragment"
|
||||||
|
app:popUpTo="@id/homeFragment"
|
||||||
|
app:popUpToInclusive="true" />
|
||||||
|
</fragment>
|
||||||
|
|
||||||
<!-- 设备绑定页(全屏二维码) -->
|
<!-- 设备绑定页(全屏二维码) -->
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/bindFragment"
|
android:id="@+id/bindFragment"
|
||||||
android:name="com.xiaoqu.watch.ui.bind.BindFragment"
|
android:name="com.xiaoqu.watch.ui.bind.BindFragment"
|
||||||
android:label="设备绑定" />
|
android:label="设备绑定">
|
||||||
|
|
||||||
|
<!-- bind → home(绑定成功后) -->
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_bind_to_home"
|
||||||
|
app:destination="@id/homeFragment"
|
||||||
|
app:popUpTo="@id/bindFragment"
|
||||||
|
app:popUpToInclusive="true" />
|
||||||
|
</fragment>
|
||||||
|
|
||||||
<!-- 任务列表 -->
|
<!-- 任务列表 -->
|
||||||
<fragment
|
<fragment
|
||||||
|
|||||||
Reference in New Issue
Block a user