523 lines
20 KiB
Kotlin
523 lines
20 KiB
Kotlin
package com.example.defenseapplication.home
|
|
|
|
import android.Manifest
|
|
import android.app.Activity
|
|
import android.content.Intent
|
|
import android.content.pm.PackageManager
|
|
import android.graphics.Color
|
|
import android.os.Build
|
|
import android.graphics.Rect
|
|
import android.graphics.drawable.GradientDrawable
|
|
import android.os.Bundle
|
|
import android.util.Log
|
|
import android.util.TypedValue
|
|
import android.widget.Toast
|
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
import androidx.core.content.ContextCompat
|
|
import androidx.fragment.app.Fragment
|
|
import android.view.LayoutInflater
|
|
import android.view.View
|
|
import android.view.ViewGroup
|
|
import androidx.recyclerview.widget.GridLayoutManager
|
|
import androidx.recyclerview.widget.LinearLayoutManager
|
|
import androidx.recyclerview.widget.RecyclerView
|
|
import com.example.defenseapplication.R
|
|
import com.example.defenseapplication.adapter.DeviceAdapter
|
|
import com.example.defenseapplication.adapter.DeviceUi
|
|
import com.example.defenseapplication.adapter.FamilyAdapter
|
|
import com.example.defenseapplication.bean.DeviceBean
|
|
import com.example.defenseapplication.bean.FamilyBean
|
|
import com.example.defenseapplication.bean.SwitchFamilyRequest
|
|
import com.example.defenseapplication.databinding.HomeMenuFragmentBinding
|
|
import com.example.defenseapplication.utils.DeviceIdEvent
|
|
import com.example.defenseapplication.utils.FamilySwitchEvent
|
|
import com.example.defenseapplication.utils.MessageStatusManager
|
|
import com.example.defenseapplication.utils.RetrofitNetwork
|
|
import com.example.defenseapplication.utils.UserinfoUtil
|
|
import com.example.defenseapplication.view.DefenseDialogs
|
|
import com.example.defenseapplication.view.showDefenseDialog
|
|
import com.gyf.immersionbar.ImmersionBar
|
|
import com.maning.mlkitscanner.scan.MNScanManager
|
|
import com.maning.mlkitscanner.scan.callback.act.MNScanCallback
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.Dispatchers
|
|
import kotlinx.coroutines.launch
|
|
import kotlinx.coroutines.withContext
|
|
//主页碎片
|
|
class HomeMenuFragment : Fragment() {
|
|
// ViewBinding
|
|
private var _binding: HomeMenuFragmentBinding? = null
|
|
private val binding get() = _binding!!
|
|
private val familyAdapter by lazy { FamilyAdapter(::onFamilySelected) }
|
|
private val deviceAdapter by lazy { DeviceAdapter(::onDeviceClicked) }
|
|
|
|
private var isFamilyExpanded = false
|
|
private lateinit var currentFamily: String
|
|
private var currentPage = 1
|
|
private val pageSize = 10
|
|
private var hasMore = true
|
|
private var isLoading = false
|
|
|
|
private val requestPermissionsLauncher = registerForActivityResult(
|
|
ActivityResultContracts.RequestMultiplePermissions()
|
|
) { permissions ->
|
|
val cameraGranted = permissions[Manifest.permission.CAMERA] ?: false
|
|
|
|
val storageGranted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
permissions[Manifest.permission.READ_MEDIA_IMAGES] ?: false
|
|
} else {
|
|
permissions[Manifest.permission.READ_EXTERNAL_STORAGE] ?: false
|
|
}
|
|
|
|
if (cameraGranted && storageGranted) {
|
|
startScan()
|
|
} else {
|
|
Toast.makeText(requireContext(), R.string.permission_denied, Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
|
|
private fun checkPermissionsAndScan() {
|
|
val cameraPermission = ContextCompat.checkSelfPermission(
|
|
requireContext(),
|
|
Manifest.permission.CAMERA
|
|
)
|
|
|
|
val storagePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
ContextCompat.checkSelfPermission(
|
|
requireContext(),
|
|
Manifest.permission.READ_MEDIA_IMAGES
|
|
)
|
|
} else {
|
|
ContextCompat.checkSelfPermission(
|
|
requireContext(),
|
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
)
|
|
}
|
|
|
|
if (cameraPermission == PackageManager.PERMISSION_GRANTED &&
|
|
storagePermission == PackageManager.PERMISSION_GRANTED) {
|
|
startScan()
|
|
} else {
|
|
val permissionsToRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
arrayOf(
|
|
Manifest.permission.CAMERA,
|
|
Manifest.permission.READ_MEDIA_IMAGES
|
|
)
|
|
} else {
|
|
arrayOf(
|
|
Manifest.permission.CAMERA,
|
|
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
|
)
|
|
}
|
|
requestPermissionsLauncher.launch(permissionsToRequest)
|
|
}
|
|
}
|
|
|
|
private fun startScan() {
|
|
MNScanManager.startScan(requireContext() as Activity, object: MNScanCallback {
|
|
override fun onActivityResult(resultCode: Int, data: Intent?) {
|
|
when (resultCode) {
|
|
MNScanManager.RESULT_SUCCESS -> {
|
|
val result = data?.getStringArrayListExtra(MNScanManager.INTENT_KEY_RESULT_SUCCESS)
|
|
if (!result.isNullOrEmpty()) {
|
|
ToastUtils.showToast(requireContext(), "扫码成功: $result")
|
|
} else {
|
|
ToastUtils.showToast(requireContext(), "扫码结果为空")
|
|
}
|
|
}
|
|
MNScanManager.RESULT_FAIL -> {
|
|
val error = data?.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR)
|
|
ToastUtils.showToast(requireContext(), "扫码失败: $error")
|
|
}
|
|
MNScanManager.RESULT_CANCLE -> {
|
|
ToastUtils.showToast(requireContext(), "扫码取消")
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
private val unreadCountChangeListener = object : MessageStatusManager.UnreadCountChangeListener {
|
|
override fun onUnreadCountChanged(count: Int) {
|
|
updateUnreadCountUI(count)
|
|
}
|
|
}
|
|
|
|
private val familySwitchListener = object : FamilySwitchEvent.FamilySwitchListener {
|
|
override fun onFamilySwitched(familyName: String) {
|
|
if (familyName != currentFamily) {
|
|
currentFamily = familyName
|
|
updateFamilyDisplayText(familyName)
|
|
familyAdapter.submitList(familyBeanList, currentFamily)
|
|
switchToFamily(familyName)
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun updateFamilyDisplayText(familyName: String) {
|
|
val familyBean = familyBeanList.find { it.parentName == familyName }
|
|
val isAdmin = familyBean?.role == "appadmin"
|
|
binding.tvCurrentFamily.text = if (isAdmin) {
|
|
getString(R.string.my_family)
|
|
} else {
|
|
"${familyName}${getString(R.string.family_with_normal)}"
|
|
}
|
|
}
|
|
|
|
private var familyList: List<String> = emptyList()
|
|
private var familyBeanList: List<FamilyBean> = emptyList()
|
|
private var deviceBeanList: List<DeviceBean> = emptyList()
|
|
private var canViewDevice = false
|
|
|
|
private val currentFamilyBean: FamilyBean?
|
|
get() = familyBeanList.find { it.parentName == currentFamily }
|
|
|
|
override fun onCreateView(
|
|
inflater: LayoutInflater,
|
|
container: ViewGroup?,
|
|
savedInstanceState: Bundle?
|
|
): View {
|
|
_binding = HomeMenuFragmentBinding.inflate(inflater, container, false)
|
|
ImmersionBar.with(this)
|
|
.statusBarDarkFont(true)
|
|
.titleBar(binding.main)
|
|
.init()
|
|
initView()
|
|
return binding.root
|
|
}
|
|
|
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
super.onViewCreated(view, savedInstanceState)
|
|
|
|
// 为 LinearLayout 设置渐变背景
|
|
setGradientBackground(
|
|
view = binding.gradientLayout,
|
|
startColor = Color.parseColor("#EBDCED"),
|
|
endColor = Color.parseColor("#CAF4FF")
|
|
)
|
|
binding.message.setOnClickListener {
|
|
val intent = Intent(requireContext(), MessageActivity::class.java)
|
|
startActivity(intent)
|
|
}
|
|
binding.add.setOnClickListener {
|
|
val intent = Intent(requireContext(), AddDeviceActivity::class.java)
|
|
startActivity(intent)
|
|
}
|
|
binding.addFamily.setOnClickListener {
|
|
checkPermissionsAndScan()
|
|
}
|
|
|
|
fetchFamilyList()
|
|
fetchUnreadMessageCount()
|
|
|
|
MessageStatusManager.addListener(unreadCountChangeListener)
|
|
FamilySwitchEvent.addListener(familySwitchListener)
|
|
updateUnreadCountUI(MessageStatusManager.getUnreadCount())
|
|
}
|
|
|
|
private fun initView() {
|
|
binding.rvFamilyList.layoutManager = LinearLayoutManager(requireContext())
|
|
binding.rvFamilyList.adapter = familyAdapter
|
|
|
|
binding.rvDeviceList.layoutManager = GridLayoutManager(requireContext(), 2)
|
|
binding.rvDeviceList.adapter = deviceAdapter
|
|
if (binding.rvDeviceList.itemDecorationCount == 0) {
|
|
binding.rvDeviceList.addItemDecoration(
|
|
GridHorizontalSpaceItemDecoration(horizontalSpace = dpToPx(8))
|
|
)
|
|
}
|
|
|
|
binding.swipeRefreshLayout.setOnRefreshListener {
|
|
refreshDeviceList()
|
|
}
|
|
|
|
binding.rvDeviceList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
|
super.onScrolled(recyclerView, dx, dy)
|
|
val layoutManager = recyclerView.layoutManager as GridLayoutManager
|
|
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
|
val totalItemCount = layoutManager.itemCount
|
|
|
|
if (!isLoading && hasMore && lastVisibleItemPosition >= totalItemCount - 1 && dy > 0) {
|
|
loadMoreDevices()
|
|
}
|
|
}
|
|
})
|
|
|
|
binding.familyHeaderLayout.setOnClickListener {
|
|
toggleFamilyList()
|
|
}
|
|
binding.familyPopupMask.setOnClickListener {
|
|
if (isFamilyExpanded) {
|
|
toggleFamilyList()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun fetchUnreadMessageCount() {
|
|
CoroutineScope(Dispatchers.IO).launch {
|
|
try {
|
|
val response = RetrofitNetwork.getNetworkService().getMessage()
|
|
withContext(Dispatchers.Main) {
|
|
if (response.code == 200 && response.data != null) {
|
|
val messageGroups = response.data as List<*>
|
|
var unreadCount = 0
|
|
messageGroups.forEach { group ->
|
|
if (group is Map<*, *>) {
|
|
val list = group["list"] as? List<*>
|
|
list?.let { unreadCount += it.size }
|
|
}
|
|
}
|
|
MessageStatusManager.setUnreadCount(unreadCount)
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
e.printStackTrace()
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun updateUnreadCountUI(count: Int) {
|
|
if (count > 0) {
|
|
binding.tvUnreadCount.visibility = View.VISIBLE
|
|
binding.tvUnreadCount.text = if (count > 99) "99+" else count.toString()
|
|
} else {
|
|
binding.tvUnreadCount.visibility = View.GONE
|
|
}
|
|
}
|
|
|
|
private fun fetchFamilyList() {
|
|
CoroutineScope(Dispatchers.IO).launch {
|
|
try {
|
|
val response = RetrofitNetwork.getNetworkService().getFamilyList()
|
|
withContext(Dispatchers.Main) {
|
|
if (response.code == 200 && response.data != null) {
|
|
familyBeanList = response.data.reversed()
|
|
familyList = response.data.map { it.parentName }.reversed()
|
|
if (familyList.isNotEmpty()) {
|
|
currentFamily = familyList.first()
|
|
updateFamilyDisplayText(currentFamily)
|
|
familyAdapter.submitList(familyBeanList, currentFamily)
|
|
|
|
val userInfo = UserinfoUtil.getUserInfo()
|
|
val currentFamilyData = familyBeanList.firstOrNull()
|
|
canViewDevice = userInfo?.role == "appadmin" && currentFamilyData?.role != "member"
|
|
updateAdminUI()
|
|
|
|
switchToFamily(currentFamily)
|
|
}
|
|
} else {
|
|
Toast.makeText(requireContext(), response.msg, Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
withContext(Dispatchers.Main) {
|
|
Toast.makeText(requireContext(), R.string.network_error, Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun updateAdminUI() {
|
|
Log.d("HomeMenuFragment", "canViewDevice = $canViewDevice")
|
|
if (canViewDevice) {
|
|
binding.tvDeviceTitle.visibility = View.VISIBLE
|
|
binding.rvDeviceList.visibility = View.VISIBLE
|
|
binding.message.visibility = View.VISIBLE
|
|
binding.addFamily.visibility = View.VISIBLE
|
|
binding.add.visibility = View.VISIBLE
|
|
binding.layoutNoPermission.visibility = View.GONE
|
|
} else {
|
|
binding.tvDeviceTitle.visibility = View.GONE
|
|
binding.rvDeviceList.visibility = View.GONE
|
|
binding.message.visibility = View.GONE
|
|
binding.addFamily.visibility = View.GONE
|
|
binding.add.visibility = View.GONE
|
|
binding.layoutNoPermission.visibility = View.VISIBLE
|
|
}
|
|
}
|
|
|
|
private fun switchToFamily(familyName: String) {
|
|
val familyBean = familyBeanList.find { it.parentName == familyName } ?: return
|
|
|
|
CoroutineScope(Dispatchers.IO).launch {
|
|
try {
|
|
val userInfo = UserinfoUtil.getUserInfo()
|
|
val request = SwitchFamilyRequest(
|
|
clientId = "428a8310cd442757ae699df5d894f051",
|
|
grantType = "switch",
|
|
id = familyBean.id,
|
|
userType = "app_user",
|
|
role = userInfo?.role ?: ""
|
|
)
|
|
val response = RetrofitNetwork.getNetworkService().switchFamily(request)
|
|
withContext(Dispatchers.Main) {
|
|
if (response.code == 200) {
|
|
val userInfo = UserinfoUtil.getUserInfo()
|
|
canViewDevice = userInfo?.role == "appadmin" && familyBean.role != "member"
|
|
updateAdminUI()
|
|
updateFamilyDisplayText(familyName)
|
|
fetchDeviceList()
|
|
FamilySwitchEvent.notifyFamilySwitched(familyName)
|
|
} else {
|
|
Toast.makeText(requireContext(), response.msg, Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
} catch (e: Exception) {
|
|
withContext(Dispatchers.Main) {
|
|
Toast.makeText(requireContext(), R.string.network_error, Toast.LENGTH_SHORT).show()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun fetchDeviceList(page: Int = 1, isLoadMore: Boolean = false) {
|
|
if (!canViewDevice || isLoading) return
|
|
|
|
isLoading = true
|
|
|
|
CoroutineScope(Dispatchers.IO).launch {
|
|
try {
|
|
val response = RetrofitNetwork.getNetworkService().getDeviceList()
|
|
withContext(Dispatchers.Main) {
|
|
Log.d("DeviceList", "code = ${response.code}, data = ${response.data}")
|
|
if (response.code == 200 && response.data != null) {
|
|
val newDevices = response.data
|
|
if (isLoadMore) {
|
|
deviceBeanList = deviceBeanList + newDevices
|
|
hasMore = newDevices.size >= pageSize
|
|
} else {
|
|
deviceBeanList = newDevices
|
|
hasMore = newDevices.size >= pageSize
|
|
}
|
|
Log.d("DeviceList", "deviceBeanList size = ${deviceBeanList.size}")
|
|
updateDeviceList()
|
|
} else {
|
|
Toast.makeText(requireContext(), response.msg, Toast.LENGTH_SHORT).show()
|
|
if (isLoadMore) {
|
|
hasMore = false
|
|
}
|
|
}
|
|
isLoading = false
|
|
}
|
|
} catch (e: Exception) {
|
|
withContext(Dispatchers.Main) {
|
|
Toast.makeText(requireContext(), R.string.network_error, Toast.LENGTH_SHORT).show()
|
|
isLoading = false
|
|
if (isLoadMore) {
|
|
hasMore = false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun refreshDeviceList() {
|
|
currentPage = 1
|
|
hasMore = true
|
|
fetchDeviceList(currentPage, false)
|
|
binding.swipeRefreshLayout.isRefreshing = false
|
|
}
|
|
|
|
private fun loadMoreDevices() {
|
|
if (isLoading || !hasMore) return
|
|
currentPage++
|
|
fetchDeviceList(currentPage, true)
|
|
}
|
|
|
|
private fun toggleFamilyList() {
|
|
isFamilyExpanded = !isFamilyExpanded
|
|
val visibility = if (isFamilyExpanded) View.VISIBLE else View.GONE
|
|
binding.familyPopupMask.visibility = visibility
|
|
binding.familyPopupCard.visibility = visibility
|
|
binding.ivFamilyArrow.animate()
|
|
.rotation(if (isFamilyExpanded) 180f else 0f)
|
|
.setDuration(200L)
|
|
.start()
|
|
}
|
|
|
|
private fun onFamilySelected(familyName: String) {
|
|
currentFamily = familyName
|
|
binding.tvCurrentFamily.text = familyName
|
|
familyAdapter.submitList(familyBeanList, currentFamily)
|
|
switchToFamily(familyName)
|
|
if (isFamilyExpanded) {
|
|
toggleFamilyList()
|
|
}
|
|
}
|
|
|
|
private fun updateDeviceList() {
|
|
val deviceUiList = deviceBeanList.map {
|
|
DeviceUi(it.deviceName, it.status, it.id)
|
|
}
|
|
|
|
if (deviceUiList.isEmpty()) {
|
|
deviceAdapter.submitList(listOf(
|
|
DeviceUi(getString(R.string.no_device), "0")
|
|
))
|
|
} else {
|
|
deviceAdapter.submitList(deviceUiList)
|
|
}
|
|
}
|
|
|
|
private fun onDeviceClicked(device: DeviceUi) {
|
|
if (device.isOnline) {
|
|
DeviceIdEvent.setDeviceId(device.deviceId)
|
|
val intent = Intent(requireContext(), com.example.defenseapplication.liveVideo.LiveVideoActivity::class.java)
|
|
intent.putExtra("device_name", device.name)
|
|
startActivity(intent)
|
|
} else {
|
|
showDefenseDialog(
|
|
config = DefenseDialogs.deviceOfflineConfig(requireContext())
|
|
)
|
|
}
|
|
}
|
|
|
|
private fun dpToPx(dp: Int): Int {
|
|
return TypedValue.applyDimension(
|
|
TypedValue.COMPLEX_UNIT_DIP,
|
|
dp.toFloat(),
|
|
resources.displayMetrics
|
|
).toInt()
|
|
}
|
|
|
|
private class GridHorizontalSpaceItemDecoration(
|
|
private val horizontalSpace: Int
|
|
) : RecyclerView.ItemDecoration() {
|
|
override fun getItemOffsets(
|
|
outRect: Rect,
|
|
view: View,
|
|
parent: RecyclerView,
|
|
state: RecyclerView.State
|
|
) {
|
|
val position = parent.getChildAdapterPosition(view)
|
|
if (position == RecyclerView.NO_POSITION) return
|
|
|
|
if (position % 2 == 0) {
|
|
outRect.right = horizontalSpace / 2
|
|
} else {
|
|
outRect.left = horizontalSpace / 2
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun setGradientBackground(
|
|
view: View,
|
|
startColor: Int,
|
|
endColor: Int,
|
|
orientation: GradientDrawable.Orientation = GradientDrawable.Orientation.TL_BR
|
|
) {
|
|
val gradientDrawable = GradientDrawable(orientation, intArrayOf(startColor, endColor))
|
|
gradientDrawable.gradientType = GradientDrawable.LINEAR_GRADIENT
|
|
view.background = gradientDrawable
|
|
}
|
|
|
|
override fun onDestroyView() {
|
|
super.onDestroyView()
|
|
binding.rvFamilyList.adapter = null
|
|
binding.rvDeviceList.adapter = null
|
|
MessageStatusManager.removeListener(unreadCountChangeListener)
|
|
FamilySwitchEvent.removeListener(familySwitchListener)
|
|
_binding = null // 避免内存泄漏
|
|
}
|
|
} |