Обратный вызов BroadcastReceiver.onReceive() работает. Теперь я хочу запустить службу переднего плана для подключения к найденному устройству. Однако после вызова ContextCompat.startForegroundService() ничего не происходит. Внутри GattClientService не возникает ошибок или точек останова.
- Может ли BroadcastReceiver запустить службу переднего плана при запуске PendingIntent?
- Если да, то почему GattClientService не вызывает onStartCommand?
@HiltViewModel
class BluetoothTestViewModel @Inject constructor() : BaseViewModel() {}
@AndroidEntryPoint
@RequiresApi(Build.VERSION_CODES.O)
class BluetoothTestFragment : BaseFragment(R.layout.fragment_bluetooth_test) {
override val viewModel: BluetoothTestViewModel by hiltNavGraphViewModels(R.id.bluetoothTestFragment)
private val bluetoothAdapter: BluetoothAdapter? by lazy {
val manager = requireContext().getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
manager.adapter
}
private val permissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { perms ->
val allGranted = perms.values.all { it }
if (allGranted) log("No perms")
else log("Error: no Bluetooth/Location")
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.btnStartAdvertising.setOnClickListener {
if (checkPermissions()) {
val intent = Intent(requireContext(), GattServerService::class.java)
ContextCompat.startForegroundService(requireContext(), intent)
}
}
binding.btnStartPendingScan.setOnClickListener {
if (checkPermissions()) {
startPendingIntentScan()
}
}
binding.btnStopAll.setOnClickListener {
stopAllServices()
}
}
private fun startPendingIntentScan() {
val scanner = bluetoothAdapter?.bluetoothLeScanner
if (scanner == null) {
return
}
val filter = ScanFilter.Builder()
.setServiceUuid(ParcelUuid(GattService.SERVICE_UUID))
.build()
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH or ScanSettings.CALLBACK_TYPE_MATCH_LOST)
.setMatchMode(ScanSettings.MATCH_MODE_STICKY)
.build()
val intent = Intent(requireContext(), BlePresenceReceiver::class.java)
intent.action = "com.example.ACTION_BEACON_FOUND"
val pendingIntent = PendingIntent.getBroadcast(
requireContext(),
100,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
try {
val result = scanner.startScan(listOf(filter), settings, pendingIntent)
} catch (e: SecurityException) {
}
}
override fun onDestroyView() {
super.onDestroyView()
stopAllServices()
}
private fun stopAllServices() {
requireContext().stopService(Intent(requireContext(), GattServerService::class.java))
requireContext().stopService(Intent(requireContext(), GattClientService::class.java))
stopPendingIntentScan()
}
@SuppressLint("MissingPermission")
private fun stopPendingIntentScan() {
if (!checkPermissions()) return
val scanner = bluetoothAdapter?.bluetoothLeScanner
if (scanner == null) {
return
}
val intent = Intent(requireContext(), BlePresenceReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
requireContext(),
100,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
)
try {
scanner.stopScan(pendingIntent)
} catch (e: Exception) {
log(e.message)
}
}
private fun checkPermissions(): Boolean {
val needed = mutableListOf()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
needed.add(Manifest.permission.BLUETOOTH_SCAN)
needed.add(Manifest.permission.BLUETOOTH_ADVERTISE)
needed.add(Manifest.permission.BLUETOOTH_CONNECT)
} else {
needed.add(Manifest.permission.ACCESS_FINE_LOCATION)
}
val missing = needed.filter {
ActivityCompat.checkSelfPermission(requireContext(), it) != PackageManager.PERMISSION_GRANTED
}
if (missing.isNotEmpty()) {
permissionLauncher.launch(missing.toTypedArray())
return false
}
return true
}
private fun log(msg: String) {
activity?.runOnUiThread {
_binding?.tvLog?.append("\n$msg\n")
}
}
}
@RequiresApi(Build.VERSION_CODES.O)
class BlePresenceReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val errorCode = intent.getIntExtra(BluetoothLeScanner.EXTRA_ERROR_CODE, -1)
if (errorCode != -1) {
return
}
val scanResult = intent.parcelableArrayList(BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT) ?: return
val device = scanResult.firstOrNull()?.device ?: return
val callbackType = intent.getIntExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE, -1)
val message = "Device: ${device.address}\nCallbackType: $callbackType"
println(message)
if (callbackType == CALLBACK_TYPE_FIRST_MATCH) {
val serviceIntent = Intent(context, GattClientService::class.java).apply {
putExtra(EXTRA_DEVICE, device)
}
ContextCompat.startForegroundService(context, serviceIntent)
}
}
}
@AndroidEntryPoint
class GattClientService : GattService() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// never calls
val device = intent?.parcelable(EXTRA_DEVICE)
if (device != null) {
connectToGattServer(device)
startForegroundCompat(NotificationConfig.BLUETOOTH_FOREGROUND_SERVICE, notificationRepository)
} else {
stopSelf()
}
return START_NOT_STICKY
}
// ...
}
open class GattService: Service() {
@Inject
lateinit var notificationRepository: NotificationRepository
override fun onBind(intent: Intent?): IBinder? {
return null
}
companion object {
val SERVICE_UUID: UUID = UUID.fromString("abc")
val CLIENT_TO_SERVER_CHAR_UUID: UUID = UUID.fromString("cba")
val SERVER_TO_CLIENT_CHAR_UUID: UUID = UUID.fromString("xyz")
}
}
Подробнее здесь: https://stackoverflow.com/questions/798 ... dingintent
Мобильная версия