diff --git a/android/shared/src/androidMain/kotlin/io/rebble/cobble/shared/di/AndroidModule.kt b/android/shared/src/androidMain/kotlin/io/rebble/cobble/shared/di/AndroidModule.kt index 4a199df6..8205e788 100644 --- a/android/shared/src/androidMain/kotlin/io/rebble/cobble/shared/di/AndroidModule.kt +++ b/android/shared/src/androidMain/kotlin/io/rebble/cobble/shared/di/AndroidModule.kt @@ -13,10 +13,7 @@ import io.rebble.cobble.shared.datastore.createDataStore import io.rebble.cobble.shared.domain.calendar.AndroidCalendarActionExecutor import io.rebble.cobble.shared.domain.calendar.PlatformCalendarActionExecutor import io.rebble.cobble.shared.domain.common.PebbleDevice -import io.rebble.cobble.shared.domain.notifications.AndroidNotificationActionExecutor -import io.rebble.cobble.shared.domain.notifications.CallNotificationProcessor -import io.rebble.cobble.shared.domain.notifications.NotificationProcessor -import io.rebble.cobble.shared.domain.notifications.PlatformNotificationActionExecutor +import io.rebble.cobble.shared.domain.notifications.* import io.rebble.cobble.shared.domain.voice.DictationService import io.rebble.cobble.shared.domain.voice.NullDictationService import io.rebble.cobble.shared.domain.voice.speechrecognizer.SpeechRecognizerDictationService @@ -63,7 +60,8 @@ val androidModule = module { AppMessageHandler(pebbleDevice), VoiceSessionHandler(pebbleDevice), AudioStreamHandler(pebbleDevice), - AppLogHandler(pebbleDevice) + AppLogHandler(pebbleDevice), + NotificationActionHandler(pebbleDevice) ) } diff --git a/android/shared/src/androidMain/kotlin/io/rebble/cobble/shared/domain/notifications/AndroidNotificationActionExecutor.kt b/android/shared/src/androidMain/kotlin/io/rebble/cobble/shared/domain/notifications/AndroidNotificationActionExecutor.kt index fe7fbf16..d5a30419 100644 --- a/android/shared/src/androidMain/kotlin/io/rebble/cobble/shared/domain/notifications/AndroidNotificationActionExecutor.kt +++ b/android/shared/src/androidMain/kotlin/io/rebble/cobble/shared/domain/notifications/AndroidNotificationActionExecutor.kt @@ -37,7 +37,10 @@ class AndroidNotificationActionExecutor(): PlatformNotificationActionExecutor, K override suspend fun handleMetaNotificationAction(action: MetaNotificationAction, itemId: Uuid, attributes: List): TimelineService.ActionResponse { val sbn = activeNotifsState.value[itemId] - ?: return TimelineService.ActionResponse(success = false) + ?: run { + Logging.w("Notification not found for action, could be notif before we started tracking them") + return TimelineService.ActionResponse(success = false) + } return when (action) { MetaNotificationAction.Dismiss -> actionIntent(sbn.notification.deleteIntent) .map { diff --git a/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/di/CalendarModule.kt b/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/di/CalendarModule.kt index 117387ff..b5a12a50 100644 --- a/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/di/CalendarModule.kt +++ b/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/di/CalendarModule.kt @@ -2,6 +2,7 @@ package io.rebble.cobble.shared.di import io.rebble.cobble.shared.domain.calendar.CalendarSync import io.rebble.cobble.shared.domain.calendar.PhoneCalendarSyncer +import io.rebble.cobble.shared.domain.notifications.NotificationActionHandler import io.rebble.cobble.shared.domain.timeline.TimelineActionManager import io.rebble.cobble.shared.domain.timeline.WatchTimelineSyncer import io.rebble.cobble.shared.errors.GlobalExceptionHandler diff --git a/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/domain/common/PebbleDevice.kt b/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/domain/common/PebbleDevice.kt index b0471293..860b6673 100644 --- a/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/domain/common/PebbleDevice.kt +++ b/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/domain/common/PebbleDevice.kt @@ -48,23 +48,6 @@ open class PebbleDevice( val appMessageTransactionSequence = AppMessageTransactionSequence().iterator() - // Required for the system handler - val systemService: SystemService by inject {parametersOf(protocolHandler)} - - init { - // This will init all the handlers by reading the lazy value causing them to be injected - negotiationScope.launch { - val initNHandlers = negotiationHandlers.joinToString { it::class.simpleName ?: "Unknown" } - Logging.i("Initialised negotiation handlers: $initNHandlers") - ConnectionStateManager.connectionState.first { it is ConnectionState.Connected && it.watch.address == address } - val connectionScope = connectionScope.filterNotNull().first() - connectionScope.launch { - val initHandlers = handlers.joinToString { it::class.simpleName ?: "Unknown" } - Logging.i("Initialised handlers: $initHandlers") - } - } - } - override fun toString(): String = "< PebbleDevice address=$address >" //TODO: Move to per-protocol handler services, so we can have multiple PebbleDevices, this is the first of many @@ -81,10 +64,25 @@ open class PebbleDevice( val appFetchService: AppFetchService by inject {parametersOf(protocolHandler)} val voiceService: VoiceService by inject {parametersOf(protocolHandler)} val audioStreamService: AudioStreamService by inject {parametersOf(protocolHandler)} + val systemService: SystemService by inject {parametersOf(protocolHandler)} val putBytesController: PutBytesController by inject {parametersOf(this)} val timelineActionManager: TimelineActionManager by inject {parametersOf(this)} + init { + // This will init all the handlers by reading the lazy value causing them to be injected + negotiationScope.launch { + val initNHandlers = negotiationHandlers.joinToString { it::class.simpleName ?: "Unknown" } + Logging.i("Initialised negotiation handlers: $initNHandlers") + ConnectionStateManager.connectionState.first { it is ConnectionState.Connected && it.watch.address == address } + val connectionScope = connectionScope.filterNotNull().first() + connectionScope.launch { + val initHandlers = handlers.joinToString { it::class.simpleName ?: "Unknown" } + Logging.i("Initialised handlers: $initHandlers") + } + } + } + override fun close() { negotiationScope.cancel("PebbleDevice closed") connectionScope.value?.cancel("PebbleDevice closed") diff --git a/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/domain/notifications/NotificationActionHandler.kt b/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/domain/notifications/NotificationActionHandler.kt index dd46fb95..7567c673 100644 --- a/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/domain/notifications/NotificationActionHandler.kt +++ b/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/domain/notifications/NotificationActionHandler.kt @@ -1,56 +1,57 @@ package io.rebble.cobble.shared.domain.notifications import io.rebble.cobble.shared.Logging +import io.rebble.cobble.shared.domain.common.PebbleDevice import io.rebble.cobble.shared.domain.timeline.TimelineActionManager import io.rebble.cobble.shared.handlers.CobbleHandler import io.rebble.libpebblecommon.services.blobdb.TimelineService import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject -class NotificationActionHandler(scope: CoroutineScope): KoinComponent, CobbleHandler { +class NotificationActionHandler(private val pebbleDevice: PebbleDevice): KoinComponent, CobbleHandler { companion object { const val NOTIFICATION_UUID_PREFIX = "cafecafe" } - private val timelineActionManager: TimelineActionManager by inject() private val notificationActionExecutor: PlatformNotificationActionExecutor by inject() - + private val timelineActionManager = pebbleDevice.timelineActionManager private val notificationActionFlow = timelineActionManager.actionFlow.filter { val (action, _) = it action.itemID.get().toString().startsWith(NOTIFICATION_UUID_PREFIX) } init { - notificationActionFlow.onEach { - val (action, deferred) = it - val itemId = action.itemID.get() - val response = try { - if (action.actionID.get().toInt() < MetaNotificationAction.metaActionLength) { - notificationActionExecutor.handleMetaNotificationAction( - MetaNotificationAction.entries[action.actionID.get().toInt()], - itemId, - action.attributes.list - ) - } else { - notificationActionExecutor.handlePlatformAction( - action.actionID.get().toInt(), - itemId, - action.attributes.list + pebbleDevice.negotiationScope.launch { + notificationActionFlow.onEach { + val (action, deferred) = it + val itemId = action.itemID.get() + Logging.v("Handling notification action for item $itemId") + val response = try { + if (action.actionID.get().toInt() < MetaNotificationAction.metaActionLength) { + notificationActionExecutor.handleMetaNotificationAction( + MetaNotificationAction.entries[action.actionID.get().toInt()], + itemId, + action.attributes.list + ) + } else { + notificationActionExecutor.handlePlatformAction( + action.actionID.get().toInt(), + itemId, + action.attributes.list + ) + } + } catch (e: NoSuchElementException) { + Logging.e("Error while handling notification action", e) + TimelineService.ActionResponse( + success = false ) } - } catch (e: NoSuchElementException) { - Logging.e("Error while handling notification action", e) - TimelineService.ActionResponse( - success = false - ) - } - deferred.complete(response) - }.catch { - Logging.e("Error while handling notification action", it) - }.launchIn(scope) + deferred.complete(response) + }.catch { + Logging.e("Error while handling notification action", it) + }.launchIn(pebbleDevice.connectionScope.filterNotNull().first()) + } } } \ No newline at end of file diff --git a/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/domain/timeline/TimelineActionManager.kt b/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/domain/timeline/TimelineActionManager.kt index 1eab2708..b0ecd2bb 100644 --- a/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/domain/timeline/TimelineActionManager.kt +++ b/android/shared/src/commonMain/kotlin/io/rebble/cobble/shared/domain/timeline/TimelineActionManager.kt @@ -4,6 +4,7 @@ import com.benasher44.uuid.Uuid import io.rebble.cobble.shared.Logging import io.rebble.cobble.shared.database.dao.TimelinePinDao import io.rebble.cobble.shared.domain.common.PebbleDevice +import io.rebble.cobble.shared.domain.notifications.NotificationActionHandler import io.rebble.libpebblecommon.packets.blobdb.TimelineAction import io.rebble.libpebblecommon.services.blobdb.TimelineService import kotlinx.coroutines.CompletableDeferred @@ -36,7 +37,9 @@ class TimelineActionManager(private val pebbleDevice: PebbleDevice): KoinCompone val (action, _) = it val itemId = action.itemID.get() val item = timelineDao.get(itemId) ?: run { - Logging.w("Received action for non-existent item $itemId") + if (!itemId.toString().startsWith(NotificationActionHandler.NOTIFICATION_UUID_PREFIX)) { + Logging.w("Received action for non-existent item $itemId") + } return@filter false } item.parentId == appId