Assigned
Status Update
Comments
an...@heycharge.com <an...@heycharge.com> #2
Hi samsadch,
In the AndroidX PDF library, the search view is animated using the
Without this, the callback might be consumed at an upper level, preventing it from being received by the PdfViewerFragment's container.
Description
Version used: Car library 1.4.0
Devices/Android versions reproduced on: Android 12,13,14
We have a BroadcastReceiver which listens to Intent.ACTION_SCREEN_ON and Intent.ACTION_SCREEN_OFF
This is how we register a receiver
class HeyChargeService : CarAppService() {
private val screenOnReceiver = ScreenOnReceiver()
@SuppressLint("NotConstructor")
fun HeyChargeService() {
// Exported services must have an empty public constructor.
}
@SuppressLint("PrivateResource")
override fun createHostValidator(): HostValidator {
return if (applicationInfo.flags != 0 && ApplicationInfo.FLAG_DEBUGGABLE != 0) {
HostValidator.ALLOW_ALL_HOSTS_VALIDATOR
} else {
HostValidator.Builder(applicationContext)
.addAllowedHosts(androidx.car.app.R.array.hosts_allowlist_sample)
.build()
}
}
override fun onCreate() {
super.onCreate()
ApplicationContextWrapper.initialize(applicationContext)
SharedPrefsHelper.initialize(applicationContext)
ContextCompat.registerReceiver(applicationContext,screenOnReceiver, IntentFilter(Intent.ACTION_SCREEN_ON),ContextCompat.RECEIVER_NOT_EXPORTED)
}
override fun onDestroy() {
super.onDestroy()
try {
unregisterReceiver(screenOnReceiver)
}catch (e: Exception){
}
}
override fun onCreateSession(sessionInfo: SessionInfo): Session {
return HeyChargeSession()
}
}
This is the receiver:
class ScreenOnReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (context == null || intent == null) {
return
}
SharedPrefsHelper.getCurrentSession() ?: return
val favChargerId = SharedPrefsHelper.getFavoriteBluetoothChargerId() ?: return
if (Intent.ACTION_SCREEN_ON == intent.action) {
ChargerDetectedBroadcastReceiver.registerFavoriteCharger(favChargerId)
}
}
}
We have another receiver for ble events called ChargerDetectedBroadcastReceiver.
We call registerFavoriteCharger function
@SuppressLint("MissingPermission")
fun registerFavoriteCharger(chargerBluetoothId: String, retryTimes: Int = RETRIES_COUNT) {
val context = ApplicationContextWrapper.getApplicationContext()
handler.postDelayed({
try {
val bluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val adapter = bluetoothManager.adapter
GlobaLogsHandler.addLog("Bluetooth adapter state - ${adapter.state}")
val scanner = adapter.bluetoothLeScanner
?: if (retryTimes == 0) {
return@postDelayed
} else {
val newRetryCount = retryTimes - 1
registerFavoriteCharger(chargerBluetoothId, newRetryCount)
return@postDelayed
}
val intent =
Intent(context, ChargerDetectedBroadcastReceiver::class.java).apply {
action = BLE_SCAN_RESULT_ACTION
}
val pendingIntent =
PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_MUTABLE)
val filters: MutableList<ScanFilter> = ArrayList()
filters.add(
ScanFilter.Builder().setDeviceName(
chargerBluetoothId
).build()
)
val settings: ScanSettings =
ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
.build()
val startScanResult = scanner.startScan(filters, settings, pendingIntent)
GlobaLogsHandler.addLog("scanner.startScan result - $startScanResult")
} catch (e: Exception) {
GlobaLogsHandler.addLog("registerFavoriteCharger error, retry count $retryTimes, ${e.message}")
if (retryTimes == 0) {
return@postDelayed
} else {
val newRetryCount = retryTimes - 1
registerFavoriteCharger(chargerBluetoothId, newRetryCount)
}
}
}, RETRY_DELAY)
}
After that we expect the onReceive method in the ChargerDetectedBroadcastReceiver to catch BLE scan results
@SuppressLint("MissingPermission")
override fun onReceive(context: Context?, intent: Intent?) {
if (intent == null || context == null) return
if (intent.action != BLE_SCAN_RESULT_ACTION) return
val favoriteChargerBluetoothId = SharedPrefsHelper.getFavoriteBluetoothChargerId()
?: return
val results =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableArrayListExtra(
BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT,
ScanResult::class.java
)
} else {
intent.getParcelableArrayListExtra(BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT)
}
if (results.isNullOrEmpty()) {
return
}
results.firstOrNull { scanResult ->
val deviceName =
deviceName == favoriteChargerBluetoothId
} ?: return
GlobaLogsHandler.addLog("onReceive - fav charger $favoriteChargerBluetoothId,is discovered")
val lastTimeNotificationShown = SharedPrefsHelper.getLastTimeNotificationShown()
val difference = System.currentTimeMillis() - lastTimeNotificationShown
if (difference < SharedPrefsHelper.DEFAULT_NOTIFICATION_PERIOD) {
return
}
showNotification(context)
}
When the car is unlocked, we retrieve Intent.ACTION_SCREEN_ON event and register a BLE scanning process calling the method fun registerFavoriteCharger(chargerBluetoothId: String, retryTimes: Int = RETRIES_COUNT)
In case the app was in foreground already, we successfully register for BLE scanning events and we retrieve them and show notification, so the behaviour is correct.
But when the app is in the background, the adapter.bluetoothLeScanner is null for some period, something between 20 seconds and up to 1 minute approximately.
For that we implemented recursive retries in fun registerFavoriteCharger(chargerBluetoothId: String, retryTimes: Int = RETRIES_COUNT) method using Handler.postDelayed method.
Eventually, the adapter.bluetoothLeScanner is not null and we successfully register a BLE scanning process, but we don't get anything in the override fun onReceive(context: Context?, intent: Intent?) method.
I cannot attach the whole project because it's an internal project in our company, but I prepared a sample app.
I will attach it as a ZIP file.
Best regards,
Andrew