Как удалить устройства из списка RecyclerView, если они не активны?
Есть вот такая задача: у меня есть Bluetooth Low Energy сервис, который ищет устройства Bluetooth поблизости. Мне необходимо создать List, куда я буду складывать найденные устройства, а если устройство не активно, то удалять его оттуда. Данный List я скармливаю в RecyclerView, чтобы отобразить результаты.
У BLE поиска есть особенность -- слать бесконечным потоком устройства находящиеся поблизости, даже те, которые мгновение назад были найдены, будут опять попадать в результаты поиска. Так же у найденного устройства есть такой параметр как RSSI, который показывает качество сигнала найденного BLE устройства. Думаю поэтому они и идут сплошным потоком и меняется там только качество сигнала RSSI.
Привожу код:
MainActivity.kt
class MainActivity : BaseActivity() {
private lateinit var mBinding: ActivityMainBinding
private lateinit var mAdapter: DeviceRecyclerAdapter
private lateinit var mHandler: Handler
private val mNewData: MutableList<Pair<BluetoothDevice, Boolean>> = mutableListOf()
private val mBluetoothAdapter: BluetoothAdapter by lazy {
BluetoothAdapter.getDefaultAdapter()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(mBinding.root)
mHandler = Handler()
initViews()
}
private val mBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Log.d(TAG, "onReceive: ${intent?.action}")
when (intent?.action) {
Constants.BLE.ACTION_START_SCAN -> {
mHandler.postDelayed(mRunnable, 1000)
mAdapter.clearDevices()
}
Constants.BLE.ACTION_STOP_SCAN -> {
mHandler.removeCallbacks(mRunnable)
}
Constants.BLE.ACTION_FOUND_DEVICE -> {
val device: BluetoothDevice? =
intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
addDevice(device)
}
}
}
}
private val mBluetoothDeviceAdapterCallback =
object : DeviceRecyclerAdapter.OnDeviceRecyclerAdapterCallback {
override fun onConnect(device: BluetoothDevice) {
connectToDevice(device)
}
}
override fun onStart() {
super.onStart()
registerBluetoothServiceBroadcast()
}
override fun onResume() {
super.onResume()
updateUi()
}
override fun onStop() {
super.onStop()
unregisterBluetoothServiceBroadcast()
}
override fun onServiceBound() {}
private fun initViews() {
val data = mutableListOf<Pair<BluetoothDevice, Boolean>>()
mBluetoothAdapter.bondedDevices.map { bondedDevice ->
data.add(Pair(bondedDevice, false))
}
mAdapter = DeviceRecyclerAdapter(
object : DeviceRecyclerAdapter.OnItemClickListener {
override fun onItemClick(bluetoothDevice: BluetoothDevice) {
}
},
data
)
// mAdapter.addCallback(mBluetoothDeviceAdapterCallback)
with(mBinding) {
with(recyclerDeviceView) {
setHasFixedSize(true)
adapter = mAdapter
addItemDecoration(
DividerItemDecoration(
this@MainActivity,
DividerItemDecoration.VERTICAL
)
)
}
fabSearch.setOnClickListener {
if (isPermissionGranted()) {
searchDevices()
} else {
requestPermissions()
}
}
}
}
private fun registerBluetoothServiceBroadcast() {
val filter = IntentFilter().apply {
addAction(Constants.BLE.ACTION_START_SCAN)
addAction(Constants.BLE.ACTION_STOP_SCAN)
addAction(Constants.BLE.ACTION_FOUND_ERROR)
addAction(Constants.BLE.ACTION_FOUND_DEVICE)
}
registerReceiver(mBroadcastReceiver, filter)
}
private fun unregisterBluetoothServiceBroadcast() {
unregisterReceiver(mBroadcastReceiver)
}
private fun isPermissionGranted() =
ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_COARSE_LOCATION
) == PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
private fun requestPermissions() {
ActivityCompat.requestPermissions(
this,
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
),
REQUEST_PERMISSION_LOCATION
)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == REQUEST_PERMISSION_LOCATION
&& grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED
) {
searchDevices()
} else {
requestPermissions()
}
}
private fun searchDevices() {
if (mBound) {
if (mService.isScanning)
mService.stopSearch()
else
mService.searchDevice()
}
updateUi()
}
private fun updateUi() {
if (mBound && mService.isScanning) {
mBinding.fabSearch.setImageDrawable(
ResourcesCompat.getDrawable(
resources,
R.drawable.ic_btn_stop,
theme
)
)
} else {
mBinding.fabSearch.setImageDrawable(
ResourcesCompat.getDrawable(
resources,
R.drawable.ic_btn_search,
theme
)
)
}
}
private fun addDevice(device: BluetoothDevice?) {
if (device != null) {
val index = mNewData.firstOrNull { it.first.address == device.address }
if (index == null) { mNewData.add(Pair(device, false)) }
}
}
private val mRunnable = object : Runnable {
override fun run() {
mHandler.postDelayed(this, 2000)
if (mNewData.isNotEmpty())
mAdapter.setItems(mNewData)
}
}
private fun connectToDevice(device: BluetoothDevice) {
mService.stopSearch()
ControlActivity.start(this, device)
}
companion object {
const val TAG = "MainActivity"
const val REQUEST_PERMISSION_LOCATION = 10
}
}
DeviceRecyclerAdapter.kt
class DeviceRecyclerAdapter(
private val mOnItemClickListener: OnItemClickListener,
private val mData: MutableList<Pair<BluetoothDevice, Boolean>>
) :RecyclerView.Adapter<BaseViewHolder>() {
private val mNewData = mutableListOf<Pair<BluetoothDevice, Boolean>>()
fun appendDevice(device: BluetoothDevice) {
mData.add(Pair(device, false))
notifyItemInserted(itemCount - 1)
}
fun clearDevices() {
mData.clear()
mNewData.clear()
notifyDataSetChanged()
}
fun setItems(newItems: List<Pair<BluetoothDevice, Boolean>>) {
val result = DiffUtil.calculateDiff(DiffUtilCallback(newItems, mData))
result.dispatchUpdatesTo(this)
mData.clear()
mData.addAll(newItems)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
val binding = RowDeviceBinding.inflate(LayoutInflater.from(parent.context))
return DeviceViewHolder(binding.root)
}
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
holder.bind(mData[position])
}
override fun onBindViewHolder(
holder: BaseViewHolder,
position: Int,
payloads: MutableList<Any>
) {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads)
} else {
if (payloads.isEmpty()) {
super.onBindViewHolder(holder, position, payloads)
} else {
// TODO: 14.12.2020 Реализовать: Информация о батарее
/*val combinedChange =
createCombinedPayload(payloads as List<Change<Pair<BluetoothDevice, Boolean>>>)
val oldData = combinedChange.oldData
val newData = combinedChange.newData
if (newData.first.name != oldData.first.name) {
holder.itemView.tv_device_name.text = newData.first.name ?: holder.itemView.context.getString(R.string.device_name_unnamed)
}*/
}
}
}
override fun getItemCount(): Int = mData.size
inner class DeviceViewHolder(view: View) : BaseViewHolder(view) {
override fun bind(dataItem: Pair<BluetoothDevice, Boolean>) {
itemView.setOnClickListener { mOnItemClickListener.onItemClick(dataItem.first) }
itemView.tv_device_name.text = dataItem.first.name ?: itemView.context.getString(R.string.device_name_unnamed)
itemView.tv_device_mac_address.text = dataItem.first.address
}
}
inner class DiffUtilCallback(
private var oldItems: List<Pair<BluetoothDevice, Boolean>>,
private var newItems: List<Pair<BluetoothDevice, Boolean>>
): DiffUtil.Callback() {
override fun getOldListSize(): Int = oldItems.size
override fun getNewListSize(): Int = newItems.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldItems[oldItemPosition].first.address == newItems[newItemPosition].first.address
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldItems[oldItemPosition].first.name == newItems[newItemPosition].first.name
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return Change(
oldItem,
newItem
)
}
}
interface OnItemClickListener {
fun onItemClick(bluetoothDevice: BluetoothDevice)
}
interface OnDeviceRecyclerAdapterCallback {
fun onConnect(device: BluetoothDevice)
}
}
Подскажите как сделать так, чтобы из списка удалялось устройство, если оно не находится в сети?