From 8b0d49a07dd5e11bea528479353d058bb990e299 Mon Sep 17 00:00:00 2001 From: Antoine Robiez Date: Fri, 8 Mar 2024 09:24:50 +0100 Subject: [PATCH] Moved Google Firebase Auth to KMP --- androidApp/build.gradle.kts | 2 - .../androidmakers/AndroidMakersApplication.kt | 5 ++ .../fr/paug/androidmakers/ui/MainActivity.kt | 78 +++++++++---------- .../androidmakers/ui/components/AVALayout.kt | 5 +- .../androidmakers/ui/components/MainLayout.kt | 6 +- .../ui/components/SigninButton.kt | 4 +- gradle/libs.versions.toml | 9 +-- shared/data/build.gradle.kts | 8 +- .../store/firebase/FirebaseUserRepository.kt | 12 +++ .../androidmakers/store/firebase/mappers.kt | 8 ++ .../fr/androidmakers/domain/model/User.kt | 6 ++ .../domain/repo/UserRepository.kt | 7 ++ 12 files changed, 87 insertions(+), 63 deletions(-) create mode 100644 shared/data/src/commonMain/kotlin/fr/androidmakers/store/firebase/FirebaseUserRepository.kt create mode 100644 shared/data/src/commonMain/kotlin/fr/androidmakers/store/firebase/mappers.kt create mode 100644 shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/model/User.kt create mode 100644 shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/repo/UserRepository.kt diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 5e734c0b..c6e91b00 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -35,8 +35,6 @@ dependencies { // Firebase implementation(platform(libs.firebase.bom)) - implementation(libs.firebase.auth) - implementation(libs.firebase.config) implementation(libs.firebase.crashlytics.ktx) implementation(libs.firebase.inappmessaging) implementation(libs.firebase.messaging) diff --git a/androidApp/src/main/java/fr/paug/androidmakers/AndroidMakersApplication.kt b/androidApp/src/main/java/fr/paug/androidmakers/AndroidMakersApplication.kt index 87b89aea..40638078 100644 --- a/androidApp/src/main/java/fr/paug/androidmakers/AndroidMakersApplication.kt +++ b/androidApp/src/main/java/fr/paug/androidmakers/AndroidMakersApplication.kt @@ -11,6 +11,8 @@ import fr.androidmakers.domain.repo.PartnersRepository import fr.androidmakers.domain.repo.RoomsRepository import fr.androidmakers.domain.repo.SessionsRepository import fr.androidmakers.domain.repo.SpeakersRepository +import fr.androidmakers.domain.repo.UserRepository +import fr.androidmakers.store.firebase.FirebaseUserRepository import fr.androidmakers.store.graphql.ApolloClientBuilder import fr.androidmakers.store.graphql.PartnersGraphQLRepository import fr.androidmakers.store.graphql.RoomsGraphQLRepository @@ -33,6 +35,7 @@ class AndroidMakersApplication : Application() { lateinit var roomsRepository: RoomsRepository lateinit var sessionsRepository: SessionsRepository lateinit var speakersRepository: SpeakersRepository + lateinit var userRepository: UserRepository lateinit var bookmarksStore: BookmarksRepository @@ -68,6 +71,8 @@ class AndroidMakersApplication : Application() { filesDir.resolve("bookmarks.preferences_pb").absolutePath }) + userRepository = FirebaseUserRepository() + syncBookmarksUseCase = SyncBookmarksUseCase( bookmarksStore, sessionsRepository diff --git a/androidApp/src/main/java/fr/paug/androidmakers/ui/MainActivity.kt b/androidApp/src/main/java/fr/paug/androidmakers/ui/MainActivity.kt index 340b7a47..e5f4424d 100644 --- a/androidApp/src/main/java/fr/paug/androidmakers/ui/MainActivity.kt +++ b/androidApp/src/main/java/fr/paug/androidmakers/ui/MainActivity.kt @@ -23,47 +23,45 @@ import com.google.android.gms.auth.api.signin.GoogleSignInOptions import com.google.android.gms.common.api.ApiException import com.google.android.gms.tasks.OnCompleteListener import com.google.android.gms.tasks.Task -import com.google.firebase.auth.FirebaseAuth -import com.google.firebase.auth.FirebaseUser -import com.google.firebase.auth.GoogleAuthProvider import com.google.firebase.messaging.FirebaseMessaging +import dev.gitlive.firebase.Firebase +import dev.gitlive.firebase.auth.FirebaseAuth +import dev.gitlive.firebase.auth.GoogleAuthProvider +import dev.gitlive.firebase.auth.auth +import fr.androidmakers.domain.model.User +import fr.androidmakers.store.firebase.toUser import fr.paug.androidmakers.AndroidMakersApplication import fr.paug.androidmakers.R import fr.paug.androidmakers.ui.components.MainLayout import fr.paug.androidmakers.ui.components.about.AboutActions import fr.paug.androidmakers.ui.theme.AndroidMakersTheme import fr.paug.androidmakers.util.CustomTabUtil +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch val LocalActivity = staticCompositionLocalOf { throw NotImplementedError() } -data class AMUser( - private val firebaseUser: FirebaseUser -) { - val uid: String - get() = firebaseUser.uid - val photoUrl: String? - get() = firebaseUser.photoUrl?.toString() -} +class MainActivity : AppCompatActivity() { -private fun FirebaseUser.toAMUser(): AMUser { - return AMUser(this) -} + private val userRepository = AndroidMakersApplication.instance().userRepository -class MainActivity : AppCompatActivity() { - private val _user = MutableStateFlow(FirebaseAuth.getInstance().currentUser?.toAMUser()) + private val _user = MutableStateFlow(null) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + lifecycleScope.launch { + _user.emit(userRepository.getUser()) + } + val currentUser = _user.value if (currentUser != null) { lifecycleScope.launch { // fire & forget // This is racy but oh well... - mergeBookmarks(currentUser.uid) + mergeBookmarks(currentUser.id) } } @@ -189,32 +187,24 @@ class MainActivity : AppCompatActivity() { // with Firebase. // Got an ID token from Google. Use it to authenticate // with Firebase. - val firebaseCredential = GoogleAuthProvider.getCredential(idToken, null) - val auth = FirebaseAuth.getInstance() - - auth.signInWithCredential(firebaseCredential) - .addOnCompleteListener(this) { task -> - if (task.isSuccessful) { - // Sign in success, update UI with the signed-in user's information - lifecycleScope.launch { - _user.value = auth.currentUser?.toAMUser() - mergeBookmarks(auth.currentUser!!.uid) - } - } else { - // If sign in fails, display a message to the user. - task.exception?.printStackTrace() - _user.value = null - } - } + val firebaseCredential = GoogleAuthProvider.credential(idToken, null) + val auth = Firebase.auth + + CoroutineScope(Dispatchers.Default).launch { + val result = auth.signInWithCredential(firebaseCredential) + // Sign in success, update UI with the signed-in user's information + lifecycleScope.launch { + _user.value = result.user?.toUser() + mergeBookmarks(auth.currentUser!!.uid) + } + } } - else -> { - _user.value = null - // Shouldn't happen. - } + else -> {} } } catch (e: ApiException) { e.printStackTrace() + _user.value = null } } } @@ -239,10 +229,12 @@ class MainActivity : AppCompatActivity() { .build() val googleSignInClient = GoogleSignIn.getClient(activity, gso) - FirebaseAuth.getInstance().signOut() - googleSignInClient.signOut() - googleSignInClient.revokeAccess() - _user.value = null + lifecycleScope.launch { + Firebase.auth.signOut() + googleSignInClient.signOut() + googleSignInClient.revokeAccess() + _user.emit(null) + } } fun signin() { diff --git a/androidApp/src/main/java/fr/paug/androidmakers/ui/components/AVALayout.kt b/androidApp/src/main/java/fr/paug/androidmakers/ui/components/AVALayout.kt index 2f8962f6..ebe2eefa 100644 --- a/androidApp/src/main/java/fr/paug/androidmakers/ui/components/AVALayout.kt +++ b/androidApp/src/main/java/fr/paug/androidmakers/ui/components/AVALayout.kt @@ -51,12 +51,11 @@ import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import at.asitplus.KmmResult -import fr.androidmakers.domain.model.Partner import fr.androidmakers.domain.model.PartnerGroup import fr.androidmakers.domain.model.SpeakerId +import fr.androidmakers.domain.model.User import fr.paug.androidmakers.AndroidMakersApplication import fr.paug.androidmakers.R -import fr.paug.androidmakers.ui.AMUser import fr.paug.androidmakers.ui.MR import fr.paug.androidmakers.ui.components.about.AboutActions import fr.paug.androidmakers.ui.components.about.AboutLayout @@ -80,7 +79,7 @@ import kotlinx.coroutines.launch fun AVALayout( onSessionClick: (sessionId: String, roomId: String, startTimestamp: Long, endTimestamp: Long) -> Unit, aboutActions: AboutActions, - user: AMUser?, + user: User?, navigateToSpeakerDetails: (SpeakerId) -> Unit, ) { val avaNavController = rememberNavController() diff --git a/androidApp/src/main/java/fr/paug/androidmakers/ui/components/MainLayout.kt b/androidApp/src/main/java/fr/paug/androidmakers/ui/components/MainLayout.kt index 4faf29cc..5ab54057 100644 --- a/androidApp/src/main/java/fr/paug/androidmakers/ui/components/MainLayout.kt +++ b/androidApp/src/main/java/fr/paug/androidmakers/ui/components/MainLayout.kt @@ -12,7 +12,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import fr.androidmakers.domain.model.SpeakerId -import fr.paug.androidmakers.ui.AMUser +import fr.androidmakers.domain.model.User import fr.paug.androidmakers.ui.components.about.AboutActions import fr.paug.androidmakers.ui.components.session.SessionDetailLayout import fr.paug.androidmakers.ui.components.session.SessionDetailViewModel @@ -25,7 +25,7 @@ import fr.paug.androidmakers.ui.viewmodel.Lce * The main layout: entry point of the application */ @Composable -fun MainLayout(aboutActions: AboutActions, user: AMUser?) { +fun MainLayout(aboutActions: AboutActions, user: User?) { val mainNavController = rememberNavController() MainNavHost( mainNavController = mainNavController, @@ -45,7 +45,7 @@ private fun MainNavHost( mainNavController: NavHostController, onSessionClick: (sessionId: String, roomId: String, startTimestamp: Long, endTimestamp: Long) -> Unit, aboutActions: AboutActions, - user: AMUser?, + user: User?, navigateToSpeakerDetails: (SpeakerId) -> Unit, ) { NavHost( diff --git a/androidApp/src/main/java/fr/paug/androidmakers/ui/components/SigninButton.kt b/androidApp/src/main/java/fr/paug/androidmakers/ui/components/SigninButton.kt index aef31f9a..793aa325 100644 --- a/androidApp/src/main/java/fr/paug/androidmakers/ui/components/SigninButton.kt +++ b/androidApp/src/main/java/fr/paug/androidmakers/ui/components/SigninButton.kt @@ -16,14 +16,14 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import coil.compose.AsyncImage import coil.request.ImageRequest -import fr.paug.androidmakers.ui.AMUser +import fr.androidmakers.domain.model.User import fr.paug.androidmakers.ui.LocalActivity import fr.paug.androidmakers.ui.MR import fr.paug.androidmakers.ui.util.stringResource @Composable -fun SigninButton(user: AMUser?) { +fun SigninButton(user: User?) { val activity = LocalActivity.current val expandedState = remember { mutableStateOf(false) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f0c5a1de..fc13605f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] version-code = "1456" sdk-compile = "34" -sdk-min = "21" +sdk-min = "24" androidxActivity = "1.8.2" androidxAppCompat = "1.6.1" androidxLifecycle = "2.7.0" @@ -18,6 +18,7 @@ moko-graphics = "0.9.0" compose-mp = "1.5.12" datastore = "1.1.0-beta01" skie = "0.6.1" +firebase = "1.11.1" [libraries] androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidxActivity" } @@ -36,10 +37,7 @@ compose-material = { group = "androidx.compose.material", name = "material", ver compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" } espresso-core = "androidx.test.espresso:espresso-core:3.5.1" firebase-analytics-ktx = { module = "com.google.firebase:firebase-analytics-ktx" } -firebase-auth = { module = "com.google.firebase:firebase-auth" } -firebase-auth-ktx = { module = "com.google.firebase:firebase-auth-ktx" } firebase-bom = "com.google.firebase:firebase-bom:32.7.2" -firebase-config = { module = "com.google.firebase:firebase-config" } firebase-crashlytics-ktx = { module = "com.google.firebase:firebase-crashlytics-ktx" } firebase-inappmessaging = { module = "com.google.firebase:firebase-inappmessaging-display-ktx" } firebase-messaging = { module = "com.google.firebase:firebase-messaging" } @@ -64,7 +62,8 @@ moko-graphics = { module = "dev.icerock.moko:graphics", version.ref = "moko-grap compose-mp-icons = { module = "org.jetbrains.compose.material:material-icons-extended-desktop", version.ref = "compose-mp" } androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" } androidx-datastore-preferences-core = { group = "androidx.datastore", name = "datastore-preferences-core", version.ref = "datastore" } - +firebase-installations = { module = "dev.gitlive:firebase-installations", version.ref = "firebase" } +firebase-auth = { module = "dev.gitlive:firebase-auth", version.ref = "firebase" } android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "android-gradle-plugin" } firebase-crashlytics-gradlePlugin = { group = "com.google.firebase", name = "firebase-crashlytics-gradle", version.ref = "crashlytics-plugin" } diff --git a/shared/data/build.gradle.kts b/shared/data/build.gradle.kts index fc84afe7..682e422e 100644 --- a/shared/data/build.gradle.kts +++ b/shared/data/build.gradle.kts @@ -27,12 +27,10 @@ kotlin { implementation(libs.androidx.datastore.preferences.core) api(libs.androidx.datastore.preferences) - } - } - androidMain.dependencies { - implementation(libs.firebase.auth) - implementation(libs.firebase.auth.ktx) + api(libs.firebase.auth) + + } } } } diff --git a/shared/data/src/commonMain/kotlin/fr/androidmakers/store/firebase/FirebaseUserRepository.kt b/shared/data/src/commonMain/kotlin/fr/androidmakers/store/firebase/FirebaseUserRepository.kt new file mode 100644 index 00000000..3b21cd83 --- /dev/null +++ b/shared/data/src/commonMain/kotlin/fr/androidmakers/store/firebase/FirebaseUserRepository.kt @@ -0,0 +1,12 @@ +package fr.androidmakers.store.firebase + +import dev.gitlive.firebase.Firebase +import dev.gitlive.firebase.auth.auth +import fr.androidmakers.domain.model.User +import fr.androidmakers.domain.repo.UserRepository + +class FirebaseUserRepository : UserRepository { + override suspend fun getUser(): User? { + return Firebase.auth.currentUser?.toUser() + } +} diff --git a/shared/data/src/commonMain/kotlin/fr/androidmakers/store/firebase/mappers.kt b/shared/data/src/commonMain/kotlin/fr/androidmakers/store/firebase/mappers.kt new file mode 100644 index 00000000..3038ca76 --- /dev/null +++ b/shared/data/src/commonMain/kotlin/fr/androidmakers/store/firebase/mappers.kt @@ -0,0 +1,8 @@ +package fr.androidmakers.store.firebase + +import dev.gitlive.firebase.auth.FirebaseUser +import fr.androidmakers.domain.model.User + +fun FirebaseUser.toUser(): User { + return User(uid, photoURL) +} diff --git a/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/model/User.kt b/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/model/User.kt new file mode 100644 index 00000000..104af69b --- /dev/null +++ b/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/model/User.kt @@ -0,0 +1,6 @@ +package fr.androidmakers.domain.model + +data class User( + val id: String, + val photoUrl: String? +) diff --git a/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/repo/UserRepository.kt b/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/repo/UserRepository.kt new file mode 100644 index 00000000..7d034e85 --- /dev/null +++ b/shared/domain/src/commonMain/kotlin/fr/androidmakers/domain/repo/UserRepository.kt @@ -0,0 +1,7 @@ +package fr.androidmakers.domain.repo + +import fr.androidmakers.domain.model.User + +interface UserRepository { + suspend fun getUser(): User? +}