fix: 考勤页面交互全面优化
1. 半屏下拉面板(不是全屏页面) 2. 去掉状态文字,按钮即状态 3. 点击空白收回面板 4. 增加时间+日期显示(每秒更新) 5. NFC开启时播放语音+振动(planId=8开启/planId=4成功/planId=7失败) 6. NFC扫描时按钮变"NFC扫描中..."(不弹窗) 7. 点击扫描中的按钮可取消扫描 8. 10秒超时自动关闭+振动提示 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,8 @@ import com.xiaoqu.watch.data.punch.PunchStatus
|
|||||||
import com.xiaoqu.watch.databinding.FragmentPunchBinding
|
import com.xiaoqu.watch.databinding.FragmentPunchBinding
|
||||||
import com.xiaoqu.watch.device.nfc.NfcController
|
import com.xiaoqu.watch.device.nfc.NfcController
|
||||||
import com.xiaoqu.watch.device.screen.ScreenController
|
import com.xiaoqu.watch.device.screen.ScreenController
|
||||||
|
import com.xiaoqu.watch.device.sensor.VibrationController
|
||||||
|
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.network.ApiResult
|
import com.xiaoqu.watch.network.ApiResult
|
||||||
@@ -20,22 +22,19 @@ import com.xiaoqu.watch.network.safeApiCall
|
|||||||
import com.xiaoqu.watch.ui.common.BaseFragment
|
import com.xiaoqu.watch.ui.common.BaseFragment
|
||||||
import com.xiaoqu.watch.ui.widget.QuConfirmDialog
|
import com.xiaoqu.watch.ui.widget.QuConfirmDialog
|
||||||
import com.xiaoqu.watch.ui.widget.QuTipDialog
|
import com.xiaoqu.watch.ui.widget.QuTipDialog
|
||||||
|
import com.xiaoqu.watch.util.DateUtil
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 考勤打卡页面(NFC 方式)
|
* 考勤打卡(半屏下拉面板)
|
||||||
*
|
* - 点击空白收回
|
||||||
* 流程:
|
* - NFC 扫描时按钮变状态,不弹窗
|
||||||
* 1. 进入页面 → GET myCurrentAttendance → 显示考勤状态
|
* - 打卡有语音+振动反馈
|
||||||
* 2. 点击「上班打卡」→ 开启 NFC → "请将手表贴近打卡信标"
|
|
||||||
* 3. NFC 读到卡号 → 上班弹确认 / 下班直接提交
|
|
||||||
* 4. POST nfcOnAndOffPunch {nfcId, punchType}
|
|
||||||
* 5. 成功 → 提示 + 更新工作状态 + 屏幕亮度
|
|
||||||
*
|
|
||||||
* 来源:v1.2.5 punchApis.js nfcOnAndOffPunch + 用户确认
|
|
||||||
*/
|
*/
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class PunchFragment : BaseFragment<FragmentPunchBinding>() {
|
class PunchFragment : BaseFragment<FragmentPunchBinding>() {
|
||||||
@@ -43,18 +42,12 @@ class PunchFragment : BaseFragment<FragmentPunchBinding>() {
|
|||||||
@Inject lateinit var punchApi: PunchApi
|
@Inject lateinit var punchApi: PunchApi
|
||||||
@Inject lateinit var nfcController: NfcController
|
@Inject lateinit var nfcController: NfcController
|
||||||
@Inject lateinit var screenController: ScreenController
|
@Inject lateinit var screenController: ScreenController
|
||||||
|
@Inject lateinit var vibrationController: VibrationController
|
||||||
@Inject lateinit var eventBus: EventBus
|
@Inject lateinit var eventBus: EventBus
|
||||||
|
|
||||||
/** 当前考勤状态 */
|
|
||||||
private var punchStatus: PunchStatus? = null
|
|
||||||
|
|
||||||
/** 当前打卡类型(0=上班, 1=下班) */
|
|
||||||
private var currentPunchType = 0
|
private var currentPunchType = 0
|
||||||
|
private var isScanning = false
|
||||||
/** 提示弹窗 */
|
|
||||||
private lateinit var tipDialog: QuTipDialog
|
private lateinit var tipDialog: QuTipDialog
|
||||||
|
|
||||||
/** 确认弹窗 */
|
|
||||||
private lateinit var confirmDialog: QuConfirmDialog
|
private lateinit var confirmDialog: QuConfirmDialog
|
||||||
|
|
||||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentPunchBinding {
|
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentPunchBinding {
|
||||||
@@ -64,86 +57,70 @@ class PunchFragment : BaseFragment<FragmentPunchBinding>() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
// 初始化弹窗
|
|
||||||
val dialogContainer = requireActivity().findViewById<FrameLayout>(R.id.dialog_container)
|
val dialogContainer = requireActivity().findViewById<FrameLayout>(R.id.dialog_container)
|
||||||
tipDialog = QuTipDialog(dialogContainer)
|
tipDialog = QuTipDialog(dialogContainer)
|
||||||
confirmDialog = QuConfirmDialog(dialogContainer)
|
confirmDialog = QuConfirmDialog(dialogContainer)
|
||||||
|
|
||||||
|
// 点击空白收回
|
||||||
|
binding.dismissArea.setOnClickListener {
|
||||||
|
if (!isScanning) findNavController().popBackStack()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示时间并每秒更新
|
||||||
|
updateTime()
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
while (isActive) { delay(1000); updateTime() }
|
||||||
|
}
|
||||||
|
|
||||||
// 获取考勤状态
|
// 获取考勤状态
|
||||||
fetchAttendance()
|
fetchAttendance()
|
||||||
|
|
||||||
// 上滑返回手势
|
|
||||||
setupSwipeUpToBack()
|
|
||||||
|
|
||||||
// 监听系统状态事件
|
|
||||||
observeEvents()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
// 离开页面时关闭 NFC
|
stopNfc()
|
||||||
nfcController.stopScan()
|
|
||||||
if (nfcController.isOpen()) {
|
|
||||||
nfcController.close()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 数据获取 =====
|
// ===== 时间 =====
|
||||||
|
|
||||||
|
private fun updateTime() {
|
||||||
|
binding.tvTime.text = DateUtil.formatTimeShort()
|
||||||
|
val info = DateUtil.getDateInfo()
|
||||||
|
binding.tvDate.text = "${info.month}\u6708${info.day}\u65E5 ${info.week}"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 考勤状态 =====
|
||||||
|
|
||||||
/** 获取当前考勤状态 */
|
|
||||||
private fun fetchAttendance() {
|
private fun fetchAttendance() {
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
val result = safeApiCall { punchApi.getAttendance() }
|
val result = safeApiCall { punchApi.getAttendance() }
|
||||||
when (result) {
|
when (result) {
|
||||||
is ApiResult.Success -> {
|
is ApiResult.Success -> displayStatus(result.data ?: PunchStatus())
|
||||||
punchStatus = result.data
|
else -> displayStatus(PunchStatus())
|
||||||
displayStatus(result.data ?: PunchStatus())
|
|
||||||
}
|
|
||||||
is ApiResult.Error -> {
|
|
||||||
Timber.w("考勤: API 错误 ${result.code}")
|
|
||||||
displayStatus(PunchStatus())
|
|
||||||
}
|
|
||||||
is ApiResult.NetworkError -> {
|
|
||||||
Timber.w("考勤: 网络异常")
|
|
||||||
displayStatus(PunchStatus())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== UI 显示 =====
|
/** 按钮即状态 */
|
||||||
|
|
||||||
/** 根据考勤状态更新页面(基于业务逻辑矩阵) */
|
|
||||||
private fun displayStatus(status: PunchStatus) {
|
private fun displayStatus(status: PunchStatus) {
|
||||||
binding.btnRevoke.visibility = View.GONE
|
binding.btnRevoke.visibility = View.GONE
|
||||||
binding.lowPowerHint.visibility = View.GONE
|
binding.tvLowPower.visibility = View.GONE
|
||||||
|
|
||||||
when {
|
when {
|
||||||
// 未上班
|
|
||||||
!status.isOnDuty -> {
|
!status.isOnDuty -> {
|
||||||
binding.tvPunchStatus.text = "未上班"
|
|
||||||
binding.tvPunchStatus.setTextColor(requireContext().getColor(R.color.text_secondary))
|
|
||||||
binding.btnPunch.text = "上班打卡"
|
binding.btnPunch.text = "上班打卡"
|
||||||
binding.btnPunch.setBackgroundColor(requireContext().getColor(R.color.primary))
|
binding.btnPunch.setBackgroundColor(requireContext().getColor(R.color.primary))
|
||||||
binding.btnPunch.setOnClickListener { startNfcPunch(0) }
|
binding.btnPunch.setOnClickListener { startNfcPunch(0) }
|
||||||
}
|
}
|
||||||
// 已上班 + 已下班
|
|
||||||
status.isOnDuty && status.isOffDuty -> {
|
status.isOnDuty && status.isOffDuty -> {
|
||||||
val timeText = if (!status.offPunchTime.isNullOrEmpty()) "已下班 ${status.offPunchTime}" else "已下班"
|
|
||||||
binding.tvPunchStatus.text = timeText
|
|
||||||
binding.tvPunchStatus.setTextColor(requireContext().getColor(R.color.text_secondary))
|
|
||||||
binding.btnPunch.text = "下班打卡"
|
binding.btnPunch.text = "下班打卡"
|
||||||
binding.btnPunch.setBackgroundColor(requireContext().getColor(R.color.primary))
|
binding.btnPunch.setBackgroundColor(requireContext().getColor(R.color.primary))
|
||||||
binding.btnPunch.setOnClickListener { startNfcPunch(1) }
|
binding.btnPunch.setOnClickListener { startNfcPunch(1) }
|
||||||
binding.btnRevoke.visibility = View.VISIBLE
|
binding.btnRevoke.visibility = View.VISIBLE
|
||||||
binding.btnRevoke.setOnClickListener { doRevoke() }
|
binding.btnRevoke.setOnClickListener { doRevoke() }
|
||||||
binding.lowPowerHint.visibility = View.VISIBLE
|
binding.tvLowPower.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
// 已上班 + 未下班
|
|
||||||
status.isOnDuty && !status.isOffDuty -> {
|
status.isOnDuty && !status.isOffDuty -> {
|
||||||
val timeText = if (!status.onPunchTime.isNullOrEmpty()) "已上班 ${status.onPunchTime}" else "已上班"
|
|
||||||
binding.tvPunchStatus.text = timeText
|
|
||||||
binding.tvPunchStatus.setTextColor(requireContext().getColor(R.color.success))
|
|
||||||
binding.btnPunch.text = "下班打卡"
|
binding.btnPunch.text = "下班打卡"
|
||||||
binding.btnPunch.setBackgroundColor(requireContext().getColor(R.color.primary))
|
binding.btnPunch.setBackgroundColor(requireContext().getColor(R.color.primary))
|
||||||
binding.btnPunch.setOnClickListener { startNfcPunch(1) }
|
binding.btnPunch.setOnClickListener { startNfcPunch(1) }
|
||||||
@@ -151,135 +128,123 @@ class PunchFragment : BaseFragment<FragmentPunchBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== NFC 打卡操作 =====
|
// ===== NFC 打卡 =====
|
||||||
|
|
||||||
/**
|
|
||||||
* 开始 NFC 打卡
|
|
||||||
* 1. 开启 NFC
|
|
||||||
* 2. 显示"请将手表贴近打卡信标"
|
|
||||||
* 3. NFC 读到卡号后回调
|
|
||||||
*/
|
|
||||||
private fun startNfcPunch(punchType: Int) {
|
private fun startNfcPunch(punchType: Int) {
|
||||||
|
if (isScanning) return
|
||||||
currentPunchType = punchType
|
currentPunchType = punchType
|
||||||
|
isScanning = true
|
||||||
|
|
||||||
// 开启 NFC
|
// 按钮变为扫描状态(不弹窗)
|
||||||
|
binding.btnPunch.text = "NFC 扫描中..."
|
||||||
|
binding.btnPunch.setBackgroundColor(requireContext().getColor(R.color.warning))
|
||||||
|
binding.btnPunch.setOnClickListener { stopNfc(); resetButton() }
|
||||||
|
|
||||||
|
// NFC 开启语音+振动(planId=8)
|
||||||
|
VibrationDefaults.getPattern(8)?.let { vibrationController.executePattern(it) }
|
||||||
|
|
||||||
|
// 开启 NFC 扫描
|
||||||
nfcController.open()
|
nfcController.open()
|
||||||
|
|
||||||
// 显示扫描提示
|
|
||||||
tipDialog.show(
|
|
||||||
status = QuTipDialog.Status.LOCATION,
|
|
||||||
title = "请将手表贴近打卡信标",
|
|
||||||
back = true,
|
|
||||||
step = 0,
|
|
||||||
countdown = 10, // 10秒超时自动关闭
|
|
||||||
onBack = {
|
|
||||||
// 用户取消或超时 → 关闭 NFC
|
|
||||||
nfcController.stopScan()
|
|
||||||
nfcController.close()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 开始 NFC 扫描,读到卡号后回调
|
|
||||||
nfcController.startScan { nfcId ->
|
nfcController.startScan { nfcId ->
|
||||||
Timber.d("考勤: NFC 读到卡号 $nfcId")
|
Timber.d("考勤: NFC 读到 $nfcId")
|
||||||
// 关闭 NFC 和提示
|
stopNfc()
|
||||||
nfcController.stopScan()
|
|
||||||
nfcController.close()
|
|
||||||
tipDialog.dismiss()
|
|
||||||
|
|
||||||
// 上班弹确认 / 下班直接提交
|
|
||||||
if (currentPunchType == 0) {
|
if (currentPunchType == 0) {
|
||||||
confirmDialog.showText(
|
confirmDialog.showText(
|
||||||
text = "确定上班打卡?",
|
text = "确定上班打卡?",
|
||||||
onConfirm = { doPunch(nfcId) },
|
onConfirm = { doPunch(nfcId) },
|
||||||
onCancel = { /* 取消,不打卡 */ }
|
onCancel = { resetButton() }
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
doPunch(nfcId)
|
doPunch(nfcId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 10秒超时
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
delay(10000)
|
||||||
|
if (isScanning) {
|
||||||
|
stopNfc()
|
||||||
|
resetButton()
|
||||||
|
VibrationDefaults.getPattern(7)?.let { vibrationController.executePattern(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopNfc() {
|
||||||
|
isScanning = false
|
||||||
|
nfcController.stopScan()
|
||||||
|
if (nfcController.isOpen()) nfcController.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetButton() {
|
||||||
|
isScanning = false
|
||||||
|
fetchAttendance()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 执行 NFC 打卡 API */
|
|
||||||
private fun doPunch(nfcId: String) {
|
private fun doPunch(nfcId: String) {
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
val params = hashMapOf<String, Any>(
|
val params = hashMapOf<String, Any>("nfcId" to nfcId, "punchType" to currentPunchType)
|
||||||
"nfcId" to nfcId,
|
|
||||||
"punchType" to currentPunchType
|
|
||||||
)
|
|
||||||
val result = safeApiCall { punchApi.nfcOnAndOffPunch(params) }
|
val result = safeApiCall { punchApi.nfcOnAndOffPunch(params) }
|
||||||
|
|
||||||
when (result) {
|
when (result) {
|
||||||
is ApiResult.Success -> {
|
is ApiResult.Success -> {
|
||||||
Timber.d("考勤: NFC 打卡成功 punchType=$currentPunchType")
|
// 成功语音+振动(planId=4)
|
||||||
|
VibrationDefaults.getPattern(4)?.let { vibrationController.executePattern(it) }
|
||||||
tipDialog.show(
|
tipDialog.show(
|
||||||
status = QuTipDialog.Status.SUCCESS,
|
status = QuTipDialog.Status.SUCCESS, title = "打卡成功",
|
||||||
title = "打卡成功",
|
|
||||||
back = true, step = 0, countdown = 2
|
back = true, step = 0, countdown = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// 副作用:更新工作状态和屏幕亮度
|
|
||||||
if (currentPunchType == 0) {
|
if (currentPunchType == 0) {
|
||||||
screenController.turnOn()
|
screenController.turnOn(); emitWorkState(true)
|
||||||
emitWorkState(true)
|
|
||||||
} else {
|
} else {
|
||||||
screenController.turnOff()
|
screenController.turnOff(); emitWorkState(false)
|
||||||
emitWorkState(false)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fetchAttendance()
|
fetchAttendance()
|
||||||
}
|
}
|
||||||
is ApiResult.Error -> {
|
is ApiResult.Error -> {
|
||||||
|
VibrationDefaults.getPattern(7)?.let { vibrationController.executePattern(it) }
|
||||||
tipDialog.show(
|
tipDialog.show(
|
||||||
status = QuTipDialog.Status.ERROR,
|
status = QuTipDialog.Status.ERROR, title = "打卡失败",
|
||||||
title = "打卡失败",
|
desc = result.message, back = true, step = 0, countdown = 3
|
||||||
desc = result.message,
|
|
||||||
back = true, step = 0, countdown = 3
|
|
||||||
)
|
)
|
||||||
|
resetButton()
|
||||||
}
|
}
|
||||||
is ApiResult.NetworkError -> {
|
is ApiResult.NetworkError -> {
|
||||||
tipDialog.show(
|
tipDialog.show(
|
||||||
status = QuTipDialog.Status.ERROR,
|
status = QuTipDialog.Status.ERROR, title = "网络异常",
|
||||||
title = "网络异常",
|
|
||||||
back = true, step = 0, countdown = 3
|
back = true, step = 0, countdown = 3
|
||||||
)
|
)
|
||||||
|
resetButton()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 撤销打卡 */
|
|
||||||
private fun doRevoke() {
|
private fun doRevoke() {
|
||||||
confirmDialog.showText(
|
confirmDialog.showText(
|
||||||
text = "确定撤销打卡?",
|
text = "确定撤销打卡?",
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
val params = hashMapOf<String, Any>()
|
val result = safeApiCall { punchApi.revokePunch(hashMapOf()) }
|
||||||
val result = safeApiCall { punchApi.revokePunch(params) }
|
|
||||||
|
|
||||||
when (result) {
|
when (result) {
|
||||||
is ApiResult.Success -> {
|
is ApiResult.Success -> {
|
||||||
|
VibrationDefaults.getPattern(4)?.let { vibrationController.executePattern(it) }
|
||||||
tipDialog.show(
|
tipDialog.show(
|
||||||
status = QuTipDialog.Status.SUCCESS,
|
status = QuTipDialog.Status.SUCCESS, title = "撤销成功",
|
||||||
title = "撤销成功",
|
|
||||||
back = true, step = 0, countdown = 2
|
back = true, step = 0, countdown = 2
|
||||||
)
|
)
|
||||||
screenController.turnOn()
|
screenController.turnOn(); emitWorkState(true); fetchAttendance()
|
||||||
emitWorkState(true)
|
|
||||||
fetchAttendance()
|
|
||||||
}
|
}
|
||||||
is ApiResult.Error -> {
|
is ApiResult.Error -> {
|
||||||
tipDialog.show(
|
tipDialog.show(
|
||||||
status = QuTipDialog.Status.ERROR,
|
status = QuTipDialog.Status.ERROR, title = "撤销失败",
|
||||||
title = "撤销失败",
|
desc = result.message, back = true, step = 0, countdown = 3
|
||||||
desc = result.message,
|
|
||||||
back = true, step = 0, countdown = 3
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
is ApiResult.NetworkError -> {
|
is ApiResult.NetworkError -> {
|
||||||
tipDialog.show(
|
tipDialog.show(
|
||||||
status = QuTipDialog.Status.ERROR,
|
status = QuTipDialog.Status.ERROR, title = "网络异常",
|
||||||
title = "网络异常",
|
|
||||||
back = true, step = 0, countdown = 3
|
back = true, step = 0, countdown = 3
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -289,53 +254,9 @@ class PunchFragment : BaseFragment<FragmentPunchBinding>() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 辅助 =====
|
|
||||||
|
|
||||||
/** 发送工作状态变更事件 */
|
|
||||||
private fun emitWorkState(isWorking: Boolean) {
|
private fun emitWorkState(isWorking: Boolean) {
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
eventBus.emit(AppEvent.WorkStateChanged(isWorking))
|
eventBus.emit(AppEvent.WorkStateChanged(isWorking))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 监听系统状态事件 */
|
|
||||||
private fun observeEvents() {
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
|
||||||
eventBus.events.collect { event ->
|
|
||||||
when (event) {
|
|
||||||
is AppEvent.BatteryChanged -> {
|
|
||||||
binding.statusBar.updateBattery(event.level, event.isCharging)
|
|
||||||
}
|
|
||||||
is AppEvent.BluetoothStateChanged -> {
|
|
||||||
binding.statusBar.updateBluetooth(event.isOn)
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 上滑返回首页 */
|
|
||||||
@android.annotation.SuppressLint("ClickableViewAccessibility")
|
|
||||||
private fun setupSwipeUpToBack() {
|
|
||||||
var startY = 0f
|
|
||||||
|
|
||||||
binding.contentArea.setOnTouchListener { _, event ->
|
|
||||||
when (event.action) {
|
|
||||||
android.view.MotionEvent.ACTION_DOWN -> {
|
|
||||||
startY = event.y
|
|
||||||
true
|
|
||||||
}
|
|
||||||
android.view.MotionEvent.ACTION_UP -> {
|
|
||||||
val dy = event.y - startY
|
|
||||||
// 上滑(dy < -50)→ 返回
|
|
||||||
if (dy < -50) {
|
|
||||||
findNavController().popBackStack()
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
else -> true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +1,55 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!-- 考勤打卡页面(按原型图V3)
|
<!-- 考勤打卡(半屏下拉面板)
|
||||||
顶部:状态栏
|
上半:时间+按钮(黑色背景)
|
||||||
中间:时间 + 考勤状态 + 按钮
|
下半:透明,点击收回 -->
|
||||||
底部:低耗电提示 -->
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
<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="#00000000">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<!-- 固定状态栏 -->
|
<!-- 点击空白收回 -->
|
||||||
<com.xiaoqu.watch.ui.widget.StatusBarView
|
<View
|
||||||
android:id="@+id/statusBar"
|
android:id="@+id/dismissArea"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="24dp"
|
android:layout_height="match_parent" />
|
||||||
android:layout_marginStart="21dp"
|
|
||||||
android:layout_marginTop="27dp"
|
|
||||||
android:layout_marginEnd="21dp" />
|
|
||||||
|
|
||||||
<!-- 居中内容区(可上滑返回) -->
|
<!-- 上半屏面板 -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/contentArea"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_gravity="top"
|
||||||
android:gravity="center"
|
android:background="@color/background"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingStart="21dp"
|
android:paddingStart="21dp"
|
||||||
android:paddingEnd="21dp">
|
android:paddingTop="27dp"
|
||||||
|
android:paddingEnd="21dp"
|
||||||
|
android:paddingBottom="21dp">
|
||||||
|
|
||||||
<!-- 考勤状态("未上班" / "已上班 07:02" / "已下班 17:05") -->
|
<!-- 时间 -->
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvPunchStatus"
|
android:id="@+id/tvTime"
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textSize="26sp"
|
|
||||||
android:layout_marginBottom="27dp" />
|
|
||||||
|
|
||||||
<!-- 主按钮区域 -->
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:orientation="vertical">
|
android:textColor="@color/text_primary"
|
||||||
|
android:textSize="48sp"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:layout_marginBottom="5dp" />
|
||||||
|
|
||||||
<!-- 主按钮(上班打卡 / 下班打卡) -->
|
<!-- 日期 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvDate"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="@color/text_secondary"
|
||||||
|
android:textSize="20sp"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<!-- 主按钮 -->
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/btnPunch"
|
android:id="@+id/btnPunch"
|
||||||
android:layout_width="250dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:padding="16dp"
|
android:padding="16dp"
|
||||||
@@ -55,10 +57,10 @@
|
|||||||
android:textSize="26sp"
|
android:textSize="26sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<!-- 撤销按钮(已上班+已下班时显示) -->
|
<!-- 撤销按钮 -->
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/btnRevoke"
|
android:id="@+id/btnRevoke"
|
||||||
android:layout_width="250dp"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:padding="14dp"
|
android:padding="14dp"
|
||||||
@@ -66,41 +68,21 @@
|
|||||||
android:textColor="@color/error"
|
android:textColor="@color/error"
|
||||||
android:textSize="22sp"
|
android:textSize="22sp"
|
||||||
android:text="撤销打卡"
|
android:text="撤销打卡"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<!-- 低耗电提示 -->
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvLowPower"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="低耗电模式已开启"
|
||||||
|
android:textColor="@color/warning"
|
||||||
|
android:textSize="18sp"
|
||||||
android:layout_marginTop="11dp"
|
android:layout_marginTop="11dp"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- 低耗电模式提示(下班后显示) -->
|
</FrameLayout>
|
||||||
<LinearLayout
|
|
||||||
android:id="@+id/lowPowerHint"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_marginTop="21dp"
|
|
||||||
android:visibility="gone">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="低耗电模式"
|
|
||||||
android:textColor="@color/warning"
|
|
||||||
android:textSize="22sp"
|
|
||||||
android:textStyle="bold" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="蓝牙扫描已停止\nNFC 已关闭"
|
|
||||||
android:textColor="@color/text_secondary"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:gravity="center"
|
|
||||||
android:lineSpacingMultiplier="1.5"
|
|
||||||
android:layout_marginTop="8dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user