Add option for separate connected notification

Closes #28
Closes #18
This commit is contained in:
darken
2025-05-14 21:21:04 +02:00
committed by Matthias Urhahn
parent 79ec3ba932
commit eb8a9b3b46
7 changed files with 71 additions and 20 deletions
@@ -24,6 +24,9 @@ class GeneralSettings @Inject constructor(
override val preferences: SharedPreferences = context.getSharedPreferences("settings_general", Context.MODE_PRIVATE)
val monitorMode = preferences.createFlowPreference("core.monitor.mode", MonitorMode.AUTOMATIC, moshi)
val useExtraMonitorNotification = preferences.createFlowPreference("core.monitor.notification.connected", false)
val scannerMode = preferences.createFlowPreference("core.scanner.mode", ScannerMode.BALANCED, moshi)
val showAll = preferences.createFlowPreference("core.showall.enabled", true)
@@ -44,6 +47,7 @@ class GeneralSettings @Inject constructor(
override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper(
monitorMode,
useExtraMonitorNotification,
scannerMode,
showAll,
minimumSignalQuality,
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M23 4.5C23 6.43 21.43 8 19.5 8S16 6.43 16 4.5 17.57 1 19.5 1 23 2.57 23 4.5M19.5 10C16.47 10 14 7.53 14 4.5C14 4 14.08 3.5 14.21 3H5C3.89 3 3 3.89 3 5V19C3 20.11 3.9 21 5 21H19C20.11 21 21 20.11 21 19V9.79C20.5 9.92 20 10 19.5 10Z" />
</vector>
@@ -22,7 +22,6 @@ import eu.darken.capod.common.flow.throttleLatest
import eu.darken.capod.main.core.GeneralSettings
import eu.darken.capod.main.core.MonitorMode
import eu.darken.capod.main.core.PermissionTool
import eu.darken.capod.main.ui.widget.WidgetManager
import eu.darken.capod.monitor.core.MonitorCoroutineScope
import eu.darken.capod.monitor.core.PodMonitor
import eu.darken.capod.monitor.ui.MonitorNotifications
@@ -51,7 +50,7 @@ class MonitorWorker @AssistedInject constructor(
@Assisted private val context: Context,
@Assisted private val params: WorkerParameters,
private val dispatcherProvider: DispatcherProvider,
private val monitorNotifications: MonitorNotifications,
private val notifications: MonitorNotifications,
private val notificationManager: NotificationManager,
private val generalSettings: GeneralSettings,
private val permissionTool: PermissionTool,
@@ -61,7 +60,6 @@ class MonitorWorker @AssistedInject constructor(
private val autoConnect: AutoConnect,
private val popUpReaction: PopUpReaction,
private val popUpWindow: PopUpWindow,
private val widgetManager: WidgetManager,
) : CoroutineWorker(context, params) {
private val workerScope = MonitorCoroutineScope()
@@ -73,7 +71,7 @@ class MonitorWorker @AssistedInject constructor(
}
override suspend fun getForegroundInfo(): ForegroundInfo {
return monitorNotifications.getForegroundInfo(null)
return notifications.getForegroundInfo(null)
}
override suspend fun doWork(): Result = try {
@@ -106,7 +104,7 @@ class MonitorWorker @AssistedInject constructor(
return
}
setForeground(monitorNotifications.getForegroundInfo(null))
setForeground(notifications.getForegroundInfo(null))
val monitorJob = podMonitor.mainDevice
.setupCommonEventHandlers(TAG) { "PodMonitor" }
@@ -115,8 +113,14 @@ class MonitorWorker @AssistedInject constructor(
.onEach { currentDevice ->
notificationManager.notify(
MonitorNotifications.NOTIFICATION_ID,
monitorNotifications.getNotification(currentDevice)
notifications.getNotification(currentDevice),
)
if (generalSettings.useExtraMonitorNotification.value && currentDevice != null) {
notificationManager.notify(
MonitorNotifications.NOTIFICATION_ID_CONNECTED,
notifications.getNotificationConnected(currentDevice),
)
}
}
.catch {
log(TAG, WARN) { "Pod Flow failed:\n${it.asLog()}" }
@@ -128,20 +132,23 @@ class MonitorWorker @AssistedInject constructor(
if (missingPermsFlow.isNotEmpty()) {
log(TAG, WARN) { "Aborting, permissions are missing: $missingPermsFlow" }
workerScope.coroutineContext.cancelChildren()
return@flatMapLatest emptyFlow()
}
combine(
generalSettings.monitorMode.flow,
generalSettings.mainDeviceAddress.flow,
bluetoothManager.connectedDevices(),
) { monitorMode, mainAddress, connectedDevices ->
listOf(monitorMode, mainAddress, connectedDevices)
emptyFlow()
} else {
combine(
generalSettings.monitorMode.flow,
generalSettings.mainDeviceAddress.flow,
bluetoothManager.connectedDevices(),
) { monitorMode, mainAddress, connectedDevices ->
listOf(monitorMode, mainAddress, connectedDevices)
}
}
}
.setupCommonEventHandlers(TAG) { "MonitorMode" }
.flatMapLatest { arguments ->
val monitorMode = arguments[0] as MonitorMode
val mainAddress = arguments[1] as String?
@Suppress("UNCHECKED_CAST")
val devices = arguments[2] as Collection<BluetoothDevice2>
log(TAG) { "Monitor mode: $monitorMode" }
@@ -150,15 +157,18 @@ class MonitorWorker @AssistedInject constructor(
// Cancel worker, ui scans manually
workerScope.coroutineContext.cancelChildren()
}
MonitorMode.ALWAYS -> emptyFlow()
MonitorMode.AUTOMATIC -> flow {
when {
mainAddress == null && devices.isNotEmpty() -> {
log(TAG, WARN) { "Main device address not set, staying alive while any is connected" }
}
devices.any { it.address == mainAddress } -> {
log(TAG) { "Main device is connected ($mainAddress), aborting any timeout." }
}
else -> {
log(TAG) { "No known Pods are connected, canceling worker soon." }
delay(15 * 1000)
@@ -49,6 +49,11 @@ class MonitorNotifications @Inject constructor(
context.getString(R.string.notification_channel_device_status_label),
NotificationManager.IMPORTANCE_LOW
).run { notificationManager.createNotificationChannel(this) }
NotificationChannel(
NOTIFICATION_CHANNEL_ID_CONNECTED,
context.getString(R.string.notification_channel_device_status_connected_label),
NotificationManager.IMPORTANCE_LOW
).run { notificationManager.createNotificationChannel(this) }
val openIntent = Intent(context, MainActivity::class.java)
val openPi = PendingIntent.getActivity(
@@ -59,7 +64,6 @@ class MonitorNotifications @Inject constructor(
)
builder = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID).apply {
setChannelId(NOTIFICATION_CHANNEL_ID)
setContentIntent(openPi)
priority = NotificationCompat.PRIORITY_LOW
setSmallIcon(eu.darken.capod.common.R.drawable.devic_earbuds_generic_both)
@@ -145,11 +149,21 @@ class MonitorNotifications @Inject constructor(
}
suspend fun getNotification(podDevice: PodDevice?): Notification = builderLock.withLock {
getBuilder(podDevice).build()
getBuilder(podDevice).apply {
setChannelId(NOTIFICATION_CHANNEL_ID)
}.build()
}
suspend fun getNotificationConnected(podDevice: PodDevice?): Notification = builderLock.withLock {
getBuilder(podDevice).apply {
setChannelId(NOTIFICATION_CHANNEL_ID_CONNECTED)
}.build()
}
suspend fun getForegroundInfo(podDevice: PodDevice?): ForegroundInfo = builderLock.withLock {
getBuilder(podDevice).toForegroundInfo()
getBuilder(podDevice).apply {
setChannelId(NOTIFICATION_CHANNEL_ID)
}.toForegroundInfo()
}
@SuppressLint("InlinedApi")
@@ -168,8 +182,10 @@ class MonitorNotifications @Inject constructor(
companion object {
val TAG = logTag("Monitor", "Notifications")
private val NOTIFICATION_CHANNEL_ID =
"${BuildConfigWrap.APPLICATION_ID}.notification.channel.device.status"
private val NOTIFICATION_CHANNEL_ID = "${BuildConfigWrap.APPLICATION_ID}.notification.channel.device.status"
private val NOTIFICATION_CHANNEL_ID_CONNECTED =
"${BuildConfigWrap.APPLICATION_ID}.notification.channel.device.status.connected"
internal const val NOTIFICATION_ID = 1
internal const val NOTIFICATION_ID_CONNECTED = 2
}
}
+2 -1
View File
@@ -51,6 +51,7 @@
<string name="settings_popup_connected_label">Verbindungs-Popup anzeigen</string>
<string name="settings_popup_connected_description">Zeigt ein Popup an, wenn das Gerät zum ersten Mal eine Verbindung herstellt.</string>
<string name="notification_channel_device_status_label">Geräte Status</string>
<string name="notification_channel_device_status_connected_label">Verbundenes Gerät</string>
<string name="support_debuglog_label">Debug-Protokoll</string>
<string name="support_debuglog_desc">Zeichne alle Aktivitäten der App in einer Textdatei auf, die du weitergeben kannst.</string>
<string name="debug_debuglog_size_label">Größe</string>
@@ -77,7 +78,7 @@
<string name="settings_licenses_label">Lizenzen</string>
<string name="settings_category_other_label">Andere</string>
<string name="settings_general_label">Einstellungen</string>
<string name="settings_general_description">Allgemeine Optimierungen, die die gesamte App betreffen.</string>
<string name="settings_general_description" tools:ignore="Typos">Allgemeine Optimierungen, die die gesamte App betreffen.</string>
<string name="settings_acknowledgements_label">Danksagungen</string>
<string name="settings_debug_autoreports_label">Automatische Fehlerberichte</string>
<string name="settings_debug_autoreports_description">Meldet Probleme automatisch, z.B. Details zu einem App-Absturz, damit ich herausfinden kann, wie ich ihn beheben kann.</string>
+3
View File
@@ -13,6 +13,8 @@
<string name="settings_monitor_mode_label">Monitor mode</string>
<string name="settings_monitor_mode_description">Under which circumstances this app monitors Bluetooth data.</string>
<string name="settings_monitor_connected_notification_label">Extra notification</string>
<string name="settings_monitor_connected_notification_description">Shows an extra notification when a device is connected. This lets you hide the permanent \"No devices\" notification by disabling the \"Device status\" channel.</string>
<string name="settings_scanner_mode_label">Scanner mode</string>
<string name="settings_scanner_mode_description">Should the Bluetooth Low Energy data scanner prioritize performance or conserve energy?</string>
<string name="settings_autopause_label">Auto pause</string>
@@ -55,6 +57,7 @@
<string name="settings_popup_connected_description">Show a popup when the device connects for the first time.</string>
<string name="notification_channel_device_status_label">Device status</string>
<string name="notification_channel_device_status_connected_label">Connected device</string>
<string name="support_debuglog_label">Debug log</string>
<string name="support_debuglog_desc">Record everything the app is doing into a text file that you can share.</string>
@@ -70,6 +70,13 @@
</PreferenceCategory>
<PreferenceCategory android:title="@string/settings_category_other_label">
<eu.darken.capod.common.preferences.MaterialSwitchPreference
android:icon="@drawable/ic_checkbox_blank_badge_24"
android:key="core.monitor.notification.connected"
android:summary="@string/settings_monitor_connected_notification_description"
android:title="@string/settings_monitor_connected_notification_label" />
<Preference
android:fragment="eu.darken.capod.main.ui.settings.general.debug.DebugSettingsFragment"
android:icon="@drawable/ic_baseline_bug_report_24"