Как удалить устройства из списка 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)
    }
}

Подскажите как сделать так, чтобы из списка удалялось устройство, если оно не находится в сети?


Ответы (0 шт):