Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate signin #4

Closed
wants to merge 6 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Implement Android GoogleSignIn
bpedryc committed Jul 10, 2023
commit df3514ff6e3ddfb71cfa957724a9e712b45d825a
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -56,4 +56,5 @@ dependencies {
testImplementation(libs.junit)
implementation(libs.compose.navigation)
implementation(libs.koin.compose)
implementation(libs.google.services)
}
2 changes: 2 additions & 0 deletions app/src/main/kotlin/co/touchlab/kampkit/android/MainApp.kt
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import co.touchlab.kampkit.core.AppInfo
import co.touchlab.kampkit.core.initKoin
import co.touchlab.kampkit.ui.breedDetails.BreedDetailsViewModel
import co.touchlab.kampkit.ui.breeds.BreedsViewModel
import co.touchlab.kampkit.ui.signin.SignInViewModel
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.parameter.parametersOf
import org.koin.dsl.module
@@ -27,6 +28,7 @@ class MainApp : Application() {
params.get(), get(), get { parametersOf("BreedDetailsViewModel") }
)
}
viewModel { SignInViewModel() }
single<SharedPreferences> {
get<Context>().getSharedPreferences("KAMPSTARTER_SETTINGS", MODE_PRIVATE)
}
Original file line number Diff line number Diff line change
@@ -16,10 +16,13 @@ private const val BREEDS = "breeds"
private const val BREED_DETAILS = "breedDetails"
private const val BREED_ID_ARG = "breedId"

private const val SIGN_IN = "signIn"

@Composable
fun MainNavCoordinator() {
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "breeds") {
NavHost(navController = navController, startDestination = SIGN_IN) {
composable(SIGN_IN) { SignInScreen(koinViewModel()) }
composable(BREEDS) {
BreedsScreen(
viewModel = koinViewModel(),
76 changes: 76 additions & 0 deletions app/src/main/kotlin/co/touchlab/kampkit/android/ui/SignInScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package co.touchlab.kampkit.android.ui

import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Column
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import co.touchlab.kampkit.ui.signin.GoogleSignInData
import co.touchlab.kampkit.ui.signin.SignInViewModel
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.api.ApiException

@Composable
fun SignInScreen(viewModel: SignInViewModel) {
val signInLauncher =
rememberLauncherForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { signInActivityResult ->
val signInData = signInActivityResult.extractGoogleSignInData()
viewModel.handleSignIn(signInData)
}

val state by viewModel.state.collectAsStateWithLifecycle()
Column {
state.error?.let { error ->
Text(error, color = Color.Red)
}

if (state.isUserLoggedIn) {
Text("Logged in as ${state.currentUserName}")
Button(onClick = { viewModel.handleSignOut() }) {
Text("Log out")
}
} else {
val context = LocalContext.current
Button(
onClick = { signInLauncher.launch(getGoogleSignInIntent(context)) }
) {
Text("Google Sign In")
}
}
}
}

private fun ActivityResult.extractGoogleSignInData(): GoogleSignInData {
if (resultCode != Activity.RESULT_OK) {
return GoogleSignInData(error = "Google SignIn activity error")
}
return try {
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
val account = task.getResult(ApiException::class.java)
GoogleSignInData(email = account.email)
} catch (exception: ApiException) {
GoogleSignInData(error = exception.message)
}
}

private fun getGoogleSignInIntent(context: Context): Intent {
val signInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.requestProfile()
.build()
val client = GoogleSignIn.getClient(context, signInOptions)
return client.signInIntent
}

2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@ koin = "3.2.0"
multiplatformSettings = "1.0.0-alpha01"
turbine = "0.12.1"
sqlDelight = "1.5.5"
googleServices = "20.5.0"

[libraries]
android-desugaring = { module = "com.android.tools:desugar_jdk_libs", version.ref = "android-desugaring" }
@@ -92,6 +93,7 @@ touchlab-stately = { module = "co.touchlab:stately-common", version.ref = "state

turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
google-services = { module = "com.google.android.gms:play-services-auth", version.ref = "googleServices"}

[plugins]
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint-gradle" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package co.touchlab.kampkit.ui.signin

data class GoogleSignInData(
val email: String? = null,
val error: String? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package co.touchlab.kampkit.ui.signin

import co.touchlab.kampkit.core.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update

class SignInViewModel : ViewModel() {

private val _state = MutableStateFlow(SignInViewState())
val state: StateFlow<SignInViewState> = _state

fun handleSignIn(signInData: GoogleSignInData) {
_state.update {
it.copy(
currentUserName = signInData.email,
error = signInData.error
)
}
}

fun handleSignOut() {
_state.update {
it.copy(currentUserName = null)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package co.touchlab.kampkit.ui.signin

data class SignInViewState(
val currentUserName: String? = null,
val error: String? = null
) {
val isUserLoggedIn
get() = currentUserName != null
}