diff --git a/shared/data/build.gradle.kts b/shared/data/build.gradle.kts index f946abda..d91461b3 100644 --- a/shared/data/build.gradle.kts +++ b/shared/data/build.gradle.kts @@ -30,7 +30,12 @@ kotlin { api(libs.androidx.datastore.preferences) api(libs.firebase.auth) + } + } + androidMain { + dependencies { + implementation(libs.play.services.wearable) } } } diff --git a/shared/data/src/androidMain/kotlin/fr/androidmakers/store/wear/WearMessagingImpl.kt b/shared/data/src/androidMain/kotlin/fr/androidmakers/store/wear/WearMessagingImpl.kt new file mode 100644 index 00000000..e56b0895 --- /dev/null +++ b/shared/data/src/androidMain/kotlin/fr/androidmakers/store/wear/WearMessagingImpl.kt @@ -0,0 +1,26 @@ +package fr.androidmakers.store.wear + +import android.content.Context +import android.util.Log +import com.google.android.gms.wearable.Wearable +import kotlinx.coroutines.tasks.await + +class WearMessagingImpl(context: Context) : WearMessaging { + companion object { + const val MESSAGE_SYNC_BOOKMARKS = "syncBookmarks" + } + + private val nodeClient = Wearable.getNodeClient(context) + private val messageClient = Wearable.getMessageClient(context) + + override suspend fun sendSyncBookmarksMessage() { + try { + val allNodeIds = nodeClient.connectedNodes.await().map { it.id } + for (nodeId in allNodeIds) { + messageClient.sendMessage(nodeId, MESSAGE_SYNC_BOOKMARKS, null).await() + } + } catch (t: Throwable) { + Log.w("WearMessagingImpl", "Error sending message", t) + } + } +} diff --git a/shared/data/src/commonMain/kotlin/fr/androidmakers/store/wear/WearMessaging.kt b/shared/data/src/commonMain/kotlin/fr/androidmakers/store/wear/WearMessaging.kt new file mode 100644 index 00000000..78d5ed98 --- /dev/null +++ b/shared/data/src/commonMain/kotlin/fr/androidmakers/store/wear/WearMessaging.kt @@ -0,0 +1,5 @@ +package fr.androidmakers.store.wear + +interface WearMessaging { + suspend fun sendSyncBookmarksMessage() +} diff --git a/shared/data/src/commonMain/kotlin/fr/androidmakers/store/wear/WearMessagingRepository.kt b/shared/data/src/commonMain/kotlin/fr/androidmakers/store/wear/WearMessagingRepository.kt new file mode 100644 index 00000000..e0c72aee --- /dev/null +++ b/shared/data/src/commonMain/kotlin/fr/androidmakers/store/wear/WearMessagingRepository.kt @@ -0,0 +1,9 @@ +package fr.androidmakers.store.wear + +import fr.androidmakers.domain.repo.MessagingRepository + +class WearMessagingRepository(private val wearMessaging: WearMessaging) : MessagingRepository { + override suspend fun sendSyncBookmarksMessage() { + wearMessaging.sendSyncBookmarksMessage() + } +} diff --git a/shared/data/src/iosMain/kotlin/fr/androidmakers/store/wear/WearMessagingImpl.kt b/shared/data/src/iosMain/kotlin/fr/androidmakers/store/wear/WearMessagingImpl.kt new file mode 100644 index 00000000..271d2eb3 --- /dev/null +++ b/shared/data/src/iosMain/kotlin/fr/androidmakers/store/wear/WearMessagingImpl.kt @@ -0,0 +1,7 @@ +package fr.androidmakers.store.wear + +class WearMessagingImpl : WearMessaging { + override suspend fun sendSyncBookmarksMessage() { + // No-op on iOS + } +} diff --git a/shared/di/src/androidMain/kotlin/fr/androidmakers/di/DataModule.android.kt b/shared/di/src/androidMain/kotlin/fr/androidmakers/di/DataModule.android.kt index 1cc8ea36..5285dc41 100644 --- a/shared/di/src/androidMain/kotlin/fr/androidmakers/di/DataModule.android.kt +++ b/shared/di/src/androidMain/kotlin/fr/androidmakers/di/DataModule.android.kt @@ -5,6 +5,8 @@ import androidx.datastore.preferences.core.Preferences import com.apollographql.apollo3.cache.normalized.sql.SqlNormalizedCacheFactory import fr.androidmakers.store.graphql.ApolloClient import fr.androidmakers.store.local.createDataStore +import fr.androidmakers.store.wear.WearMessaging +import fr.androidmakers.store.wear.WearMessagingImpl import org.koin.android.ext.koin.androidContext import org.koin.dsl.module @@ -21,4 +23,8 @@ actual val dataPlatformModule = module { androidContext().filesDir.resolve("bookmarks.preferences_pb").absolutePath } } + + single { + WearMessagingImpl(get()) + } } diff --git a/shared/di/src/commonMain/kotlin/fr/androidmakers/di/DataModule.kt b/shared/di/src/commonMain/kotlin/fr/androidmakers/di/DataModule.kt index f456bf5d..cf76cb06 100644 --- a/shared/di/src/commonMain/kotlin/fr/androidmakers/di/DataModule.kt +++ b/shared/di/src/commonMain/kotlin/fr/androidmakers/di/DataModule.kt @@ -1,6 +1,7 @@ package fr.androidmakers.di import fr.androidmakers.domain.repo.BookmarksRepository +import fr.androidmakers.domain.repo.MessagingRepository import fr.androidmakers.domain.repo.PartnersRepository import fr.androidmakers.domain.repo.RoomsRepository import fr.androidmakers.domain.repo.SessionsRepository @@ -14,6 +15,7 @@ import fr.androidmakers.store.graphql.SessionsGraphQLRepository import fr.androidmakers.store.graphql.SpeakersGraphQLRepository import fr.androidmakers.store.graphql.VenueGraphQLRepository import fr.androidmakers.store.local.BookmarksDataStoreRepository +import fr.androidmakers.store.wear.WearMessagingRepository import org.koin.core.module.Module import org.koin.dsl.module @@ -29,4 +31,5 @@ val dataModule = module { single { VenueGraphQLRepository(get()) } single { BookmarksDataStoreRepository(get()) } + single { WearMessagingRepository(get()) } } diff --git a/shared/di/src/commonMain/kotlin/fr/androidmakers/di/DomainModule.kt b/shared/di/src/commonMain/kotlin/fr/androidmakers/di/DomainModule.kt index 571e5e99..d8377e2e 100644 --- a/shared/di/src/commonMain/kotlin/fr/androidmakers/di/DomainModule.kt +++ b/shared/di/src/commonMain/kotlin/fr/androidmakers/di/DomainModule.kt @@ -6,6 +6,7 @@ import fr.androidmakers.domain.interactor.GetAgendaUseCase import fr.androidmakers.domain.interactor.GetConferenceVenueUseCase import fr.androidmakers.domain.interactor.GetFavoriteSessionsUseCase import fr.androidmakers.domain.interactor.GetPartnersUseCase +import fr.androidmakers.domain.interactor.MergeBookmarksUseCase import fr.androidmakers.domain.interactor.OpenCocUseCase import fr.androidmakers.domain.interactor.OpenFaqUseCase import fr.androidmakers.domain.interactor.OpenLinkUseCase @@ -14,7 +15,6 @@ import fr.androidmakers.domain.interactor.OpenXAccountUseCase import fr.androidmakers.domain.interactor.OpenXHashtagUseCase import fr.androidmakers.domain.interactor.OpenYoutubeUseCase import fr.androidmakers.domain.interactor.SetSessionBookmarkUseCase -import fr.androidmakers.domain.interactor.MergeBookmarksUseCase import org.koin.core.module.Module import org.koin.dsl.module @@ -30,7 +30,7 @@ val domainModule = module { factory { OpenYoutubeUseCase(get()) } factory { OpenXHashtagUseCase(get()) } factory { OpenXAccountUseCase(get()) } - factory { SetSessionBookmarkUseCase(get(), get(), get()) } + factory { SetSessionBookmarkUseCase(get(), get(), get(), get()) } factory { GetPartnersUseCase(get()) } factory { GetFavoriteSessionsUseCase(get()) } factory { OpenPartnerLinkUseCase(get()) } diff --git a/shared/di/src/iosMain/kotlin/fr/androidmakers/di/DataModule.ios.kt b/shared/di/src/iosMain/kotlin/fr/androidmakers/di/DataModule.ios.kt index bdea2011..90604c68 100644 --- a/shared/di/src/iosMain/kotlin/fr/androidmakers/di/DataModule.ios.kt +++ b/shared/di/src/iosMain/kotlin/fr/androidmakers/di/DataModule.ios.kt @@ -5,6 +5,8 @@ import androidx.datastore.preferences.core.Preferences import com.apollographql.apollo3.cache.normalized.sql.SqlNormalizedCacheFactory import fr.androidmakers.store.graphql.ApolloClient import fr.androidmakers.store.local.createDataStore +import fr.androidmakers.store.wear.WearMessaging +import fr.androidmakers.store.wear.WearMessagingImpl import kotlinx.cinterop.ExperimentalForeignApi import org.koin.dsl.module import platform.Foundation.NSDocumentDirectory @@ -24,13 +26,17 @@ actual val dataPlatformModule = module { single> { createDataStore { val documentDirectory: NSURL? = NSFileManager.defaultManager.URLForDirectory( - directory = NSDocumentDirectory, - inDomain = NSUserDomainMask, - appropriateForURL = null, - create = false, - error = null, + directory = NSDocumentDirectory, + inDomain = NSUserDomainMask, + appropriateForURL = null, + create = false, + error = null, ) requireNotNull(documentDirectory).path + "/bookmarks.preferences_pb" } } + + single { + WearMessagingImpl() + } } diff --git a/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/interactor/SetSessionBookmarkUseCase.kt b/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/interactor/SetSessionBookmarkUseCase.kt index e21d724d..c8e9a363 100644 --- a/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/interactor/SetSessionBookmarkUseCase.kt +++ b/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/interactor/SetSessionBookmarkUseCase.kt @@ -1,18 +1,21 @@ package fr.androidmakers.domain.interactor import fr.androidmakers.domain.repo.BookmarksRepository +import fr.androidmakers.domain.repo.MessagingRepository import fr.androidmakers.domain.repo.SessionsRepository import fr.androidmakers.domain.repo.UserRepository class SetSessionBookmarkUseCase( - private val userRepository: UserRepository, - private val sessionsRepository: SessionsRepository, - private val bookmarksRepository: BookmarksRepository + private val userRepository: UserRepository, + private val sessionsRepository: SessionsRepository, + private val bookmarksRepository: BookmarksRepository, + private val messagingRepository: MessagingRepository, ) { suspend operator fun invoke(sessionId: String, isBookmark: Boolean) { bookmarksRepository.setBookmarked(sessionId, isBookmark) userRepository.getUser()?.id?.let { token -> sessionsRepository.setBookmark(token, sessionId, isBookmark) } + messagingRepository.sendSyncBookmarksMessage() } } diff --git a/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/repo/MessagingRepository.kt b/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/repo/MessagingRepository.kt new file mode 100644 index 00000000..b66604be --- /dev/null +++ b/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/repo/MessagingRepository.kt @@ -0,0 +1,5 @@ +package fr.androidmakers.domain.repo + +interface MessagingRepository { + suspend fun sendSyncBookmarksMessage() +} diff --git a/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/main/MainViewModel.kt b/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/main/MainViewModel.kt index b2cc1ee4..5c33d6b7 100644 --- a/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/main/MainViewModel.kt +++ b/wearApp/src/main/java/fr/paug/androidmakers/wear/ui/main/MainViewModel.kt @@ -6,11 +6,11 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.google.android.gms.auth.api.signin.GoogleSignIn import com.google.android.gms.auth.api.signin.GoogleSignInOptions +import com.google.android.gms.wearable.Wearable import dev.gitlive.firebase.Firebase import dev.gitlive.firebase.auth.auth import fr.androidmakers.domain.interactor.GetAgendaUseCase import fr.androidmakers.domain.interactor.GetFavoriteSessionsUseCase -import fr.androidmakers.domain.interactor.MergeBookmarksUseCase import fr.androidmakers.domain.model.Agenda import fr.androidmakers.domain.model.User import fr.androidmakers.domain.repo.BookmarksRepository @@ -69,6 +69,14 @@ class MainViewModel( maybeSyncBookmarks() refreshSignal.send(Unit) } + + val messageClient = Wearable.getMessageClient(application) + messageClient.addListener { + Log.d(TAG, "Received syncBookmarks message") + viewModelScope.launch { + maybeSyncBookmarks() + } + } } private suspend fun maybeSyncBookmarks() {