设备(1)

This commit is contained in:
2026-05-18 17:39:30 +08:00
parent a9fb39cfde
commit 45828c7e9b
23 changed files with 895 additions and 67 deletions

View File

@@ -1,18 +1,26 @@
package com.example.defenseapplication.home
import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.GridLayoutManager
import com.example.defenseapplication.R
import com.example.defenseapplication.adapter.AddDeviceAdapter
import com.example.defenseapplication.adapter.Device
import com.example.defenseapplication.databinding.AddDeviceActivityBinding
import com.gyf.immersionbar.ImmersionBar
import com.maning.mlkitscanner.scan.MNScanManager
import com.maning.mlkitscanner.scan.callback.act.MNScanCallback
//添加设备页面
class AddDeviceActivity : AppCompatActivity() {
@@ -20,6 +28,86 @@ class AddDeviceActivity : AppCompatActivity() {
private lateinit var binding: AddDeviceActivityBinding
private lateinit var adapter: AddDeviceAdapter
private val mainHandler = Handler(Looper.getMainLooper())
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(this, R.string.permission_denied, Toast.LENGTH_SHORT).show()
}
}
private fun checkPermissionsAndScan() {
val cameraPermission = ContextCompat.checkSelfPermission(
this,
Manifest.permission.CAMERA
)
val storagePermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
ContextCompat.checkSelfPermission(
this,
Manifest.permission.READ_MEDIA_IMAGES
)
} else {
ContextCompat.checkSelfPermission(
this,
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(this, 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(this@AddDeviceActivity, "扫码成功: $result")
} else {
ToastUtils.showToast(this@AddDeviceActivity, "扫码结果为空")
}
}
MNScanManager.RESULT_FAIL -> {
val error = data?.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR)
ToastUtils.showToast(this@AddDeviceActivity, "扫码失败: $error")
}
MNScanManager.RESULT_CANCLE -> {
ToastUtils.showToast(this@AddDeviceActivity, "扫码取消")
}
}
}
})
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = AddDeviceActivityBinding.inflate(layoutInflater)
@@ -32,6 +120,10 @@ class AddDeviceActivity : AppCompatActivity() {
finish()
}
binding.btnScanAdd.setOnClickListener {
checkPermissionsAndScan()
}
// 开始播放扫描动画,动画结束后再显示设备列表
startScanAnimationThenShowList()
}

View File

@@ -6,15 +6,23 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.net.wifi.ScanResult
import android.net.wifi.WifiConfiguration
import android.net.wifi.WifiManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.text.Editable
import android.text.TextWatcher
import android.view.Gravity
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.PopupWindow
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.recyclerview.widget.LinearLayoutManager
@@ -29,8 +37,13 @@ class AddFamilyActivity : AppCompatActivity() {
private lateinit var binding: AddFamilyActivityBinding
private lateinit var wifiManager: WifiManager
private lateinit var connectivityManager: ConnectivityManager
private lateinit var wifiScanReceiver: BroadcastReceiver
private var popupWindow: PopupWindow? = null
private val handler = Handler(Looper.getMainLooper())
private var isConnecting = false
private var targetWifiName: String = ""
private var networkCallback: ConnectivityManager.NetworkCallback? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -58,6 +71,7 @@ class AddFamilyActivity : AppCompatActivity() {
}
})
wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
wifiScanReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
@@ -68,11 +82,161 @@ class AddFamilyActivity : AppCompatActivity() {
}
}
binding.btnConfirm.setOnClickListener {
val intent=Intent(this, ConnectDeviceActivity::class.java)
startActivity( intent)
//startActivity(Intent(this, ConnectDeviceActivity::class.java))
if (isConnecting) {
return@setOnClickListener
}
val wifiName = binding.etWifiName.text.toString().trim()
val wifiPassword = binding.etWifiPassword.text.toString()
if (wifiName.isEmpty()) {
Toast.makeText(this, R.string.enter_wifi_name, Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
if (wifiPassword.isEmpty()) {
Toast.makeText(this, R.string.enter_wifi_password, Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
isConnecting = true
binding.btnConfirm.isEnabled = false
verifyWifiPassword(wifiName, wifiPassword)
}
}
private fun verifyWifiPassword(wifiName: String, wifiPassword: String) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, Manifest.permission.CHANGE_WIFI_STATE)
!= PackageManager.PERMISSION_GRANTED ||
ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_NETWORK_STATE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.CHANGE_WIFI_STATE,
Manifest.permission.ACCESS_NETWORK_STATE),
1002)
return
}
targetWifiName = wifiName
if (!wifiManager.isWifiEnabled) {
wifiManager.isWifiEnabled = true
}
val existingConfig = findExistingWifiConfig(wifiName)
var netId: Int
if (existingConfig != null) {
netId = existingConfig.networkId
existingConfig.preSharedKey = "\"$wifiPassword\""
wifiManager.updateNetwork(existingConfig)
} else {
val wifiConfig = createWifiConfig(wifiName, wifiPassword)
netId = wifiManager.addNetwork(wifiConfig)
}
if (netId == -1) {
isConnecting = false
binding.btnConfirm.isEnabled = true
ToastUtils.showError(this, getString(R.string.wifi_connection_failed))
return
}
networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
super.onAvailable(network)
checkWifiConnected()
}
override fun onCapabilitiesChanged(network: Network, capabilities: NetworkCapabilities) {
super.onCapabilitiesChanged(network, capabilities)
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
checkWifiConnected()
}
}
}
val request = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build()
connectivityManager.registerNetworkCallback(request, networkCallback!!)
wifiManager.enableNetwork(netId, true)
wifiManager.saveConfiguration()
handler.postDelayed({
if (isConnecting) {
isConnecting = false
binding.btnConfirm.isEnabled = true
networkCallback?.let {
try {
connectivityManager.unregisterNetworkCallback(it)
} catch (e: Exception) {
}
}
networkCallback = null
ToastUtils.showError(this, getString(R.string.wifi_connection_failed))
}
}, 15000)
}
private fun findExistingWifiConfig(wifiName: String): WifiConfiguration? {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
return null
}
val configuredNetworks = wifiManager.configuredNetworks
configuredNetworks?.forEach { config ->
if (config.SSID == "\"$wifiName\"") {
return config
}
}
return null
}
private fun checkWifiConnected() {
if (!isConnecting) return
val info = wifiManager.connectionInfo
if (info.ssid == "\"$targetWifiName\"" && info.networkId != -1) {
isConnecting = false
binding.btnConfirm.isEnabled = true
networkCallback?.let {
try {
connectivityManager.unregisterNetworkCallback(it)
} catch (e: Exception) {
}
}
networkCallback = null
runOnUiThread {
ToastUtils.showSuccess(this@AddFamilyActivity, getString(R.string.wifi_connection_success))
val intent = Intent(this@AddFamilyActivity, ConnectDeviceActivity::class.java)
intent.putExtra("wifi_name", targetWifiName)
startActivity(intent)
}
}
}
private fun createWifiConfig(ssid: String, password: String): WifiConfiguration {
val config = WifiConfiguration()
config.SSID = "\"$ssid\""
config.preSharedKey = "\"$password\""
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK)
config.allowedProtocols.set(WifiConfiguration.Protocol.RSN)
config.allowedProtocols.set(WifiConfiguration.Protocol.WPA)
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP)
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP)
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP)
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP)
return config
}
private fun showWifiListDialog() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED ||
@@ -151,5 +315,34 @@ class AddFamilyActivity : AppCompatActivity() {
super.onStop()
unregisterReceiver(wifiScanReceiver)
popupWindow?.dismiss()
networkCallback?.let {
try {
connectivityManager.unregisterNetworkCallback(it)
} catch (e: Exception) {
}
}
networkCallback = null
handler.removeCallbacksAndMessages(null)
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
1002 -> {
if (grantResults.isNotEmpty() &&
grantResults.size >= 3 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED &&
grantResults[1] == PackageManager.PERMISSION_GRANTED &&
grantResults[2] == PackageManager.PERMISSION_GRANTED) {
val wifiName = binding.etWifiName.text.toString().trim()
val wifiPassword = binding.etWifiPassword.text.toString()
verifyWifiPassword(wifiName, wifiPassword)
} else {
isConnecting = false
binding.btnConfirm.isEnabled = true
Toast.makeText(this, R.string.permission_denied, Toast.LENGTH_SHORT).show()
}
}
}
}
}

View File

@@ -29,6 +29,7 @@ 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
@@ -52,6 +53,10 @@ class HomeMenuFragment : Fragment() {
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()
@@ -222,6 +227,23 @@ class HomeMenuFragment : Fragment() {
)
}
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()
}
@@ -349,8 +371,10 @@ class HomeMenuFragment : Fragment() {
}
}
private fun fetchDeviceList() {
if (!canViewDevice) return
private fun fetchDeviceList(page: Int = 1, isLoadMore: Boolean = false) {
if (!canViewDevice || isLoading) return
isLoading = true
CoroutineScope(Dispatchers.IO).launch {
try {
@@ -358,21 +382,49 @@ class HomeMenuFragment : Fragment() {
withContext(Dispatchers.Main) {
Log.d("DeviceList", "code = ${response.code}, data = ${response.data}")
if (response.code == 200 && response.data != null) {
deviceBeanList = response.data
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
@@ -396,12 +448,12 @@ class HomeMenuFragment : Fragment() {
private fun updateDeviceList() {
val deviceUiList = deviceBeanList.map {
DeviceUi(it.deviceName, it.status == "0")
DeviceUi(it.deviceName, it.status, it.id)
}
if (deviceUiList.isEmpty()) {
deviceAdapter.submitList(listOf(
DeviceUi(getString(R.string.no_device), false)
DeviceUi(getString(R.string.no_device), "0")
))
} else {
deviceAdapter.submitList(deviceUiList)
@@ -409,10 +461,16 @@ class HomeMenuFragment : Fragment() {
}
private fun onDeviceClicked(device: DeviceUi) {
if (device.isOnline) return
showDefenseDialog(
config = DefenseDialogs.deviceOfflineConfig(requireContext())
)
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 {