Ich habe ein Problem mit der Implementierung meiner Google-Anmeldung in einer Android-Anwendung. Ich habe zwei Codeteile: Der erste funktioniert ordnungsgemäß, während der zweite beim Aufruf von credentialManager.getCredential(context = context, request = request).
fehlschlägt Erster Code (funktioniert)
fun NavGraphBuilder.loginComposable(
onNavigateToGenderAndAgeGroup: (Map) -> Unit,
onNavigateToQuote: () -> Unit
) {
composable(route = "login") {
val loginViewModel: LoginViewModel = hiltViewModel()
val pendingUserMap = loginViewModel.pendingUserMap.collectAsState().value
LoginScreen(
loginViewModel = loginViewModel,
onNavigateToGenderAndAgeGroup = {
pendingUserMap?.let { onNavigateToGenderAndAgeGroup(it) }
},
onNavigateToQuote = onNavigateToQuote
)
}
}
----------------
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideContext(@ApplicationContext context: Context): Context {
return context
}
@Provides
@Singleton
fun provideClientId(@ApplicationContext context: Context): String {
return context.getString(R.string.client_id)
}
@Provides
@Singleton
fun provideCredentialManager(@ApplicationContext context: Context): CredentialManager {
return CredentialManager.create(context)
}
}
--------------------
@Composable
fun LoginScreen(
loginViewModel: LoginViewModel,
onNavigateToGenderAndAgeGroup: () -> Unit,
onNavigateToQuote: () -> Unit
) {
val context = LocalContext.current
val authState by loginViewModel.authState.collectAsState()
when (authState) {
is AuthState.Loading -> {
CircularProgressIndicator(modifier = Modifier.fillMaxSize())
}
is AuthState.SignedOut -> {
LoginScreenContent(onGoogleLoginClick = {
loginViewModel.onGoogleLoginClicked()
})
}
is AuthState.SignedIn -> {
LaunchedEffect(Unit) {
onNavigateToQuote()
}
}
is AuthState.SignUpRequired -> {
LaunchedEffect(Unit) {
onNavigateToGenderAndAgeGroup()
}
}
is AuthState.Error -> {
val errorMessage = (authState as AuthState.Error).message
Toast.makeText(context, errorMessage, Toast.LENGTH_SHORT).show()
}
}
}
@Composable
fun LoginScreenContent(
onGoogleLoginClick: () -> Unit
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(BlackBackground),
) {
Column(
modifier = Modifier
.offset(x = 34.dp, y = 99.dp)
.align(Alignment.TopStart),
) {
}
Column(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 80.dp),
verticalArrangement = Arrangement.spacedBy(12.dp, Alignment.Top),
horizontalAlignment = Alignment.Start,
) {
GoogleLoginButton(onGoogleLoginClick)
}
}
}
@Composable
private fun GoogleLoginButton(
onGoogleLoginClick: () -> Unit
) {
Row(
modifier = Modifier
.width(345.dp)
.height(54.dp)
.background(color = GrayScaleWhite, shape = RoundedCornerShape(size = 10.dp))
.padding(start = 17.dp)
.clickable(onClick = onGoogleLoginClick),
horizontalArrangement = Arrangement.spacedBy(79.dp, Alignment.Start),
verticalAlignment = Alignment.CenterVertically,
) {
Image(
modifier = Modifier
.width(24.dp)
.height(24.dp)
.background(color = GrayScaleWhite),
painter = painterResource(id = R.drawable.google_icon),
contentDescription = "google_icon",
contentScale = ContentScale.None,
)
Text(
text = stringResource(id = R.string.google_login),
style = TextStyle(
fontSize = 18.sp,
fontFamily = FontFamily(Font(R.font.inter_18pt_regular)),
fontWeight = FontWeight(400),
color = Color(0xFF000000),
),
)
}
}
---------------------------
@HiltViewModel
class LoginViewModel @Inject constructor(
private val credentialManager: CredentialManager,
private val clientId: String,
@ApplicationContext private val applicationContext: Context // 애플리케이션 컨텍스트 추가
) : ViewModel() {
private val tag = "LoginViewModel: "
private val firebaseAuth: FirebaseAuth = Firebase.auth
private val firestore: FirebaseFirestore = Firebase.firestore
private val _authState = MutableStateFlow(AuthState.SignedOut)
val authState: StateFlow = _authState.asStateFlow()
private val _pendingUserMap = MutableStateFlow(null)
val pendingUserMap: StateFlow = _pendingUserMap.asStateFlow()
init {
checkInitialAuthState()
}
private fun checkInitialAuthState() {
viewModelScope.launch {
_authState.value = if (isSignedIn()) {
val currentUser = getCurrentUser()
if (currentUser != null) AuthState.SignedIn(currentUser) else AuthState.SignedOut
} else {
AuthState.SignedOut
}
}
}
fun isSignedIn(): Boolean = firebaseAuth.currentUser != null
suspend fun getCurrentUser(): User? {
val currentUser = firebaseAuth.currentUser ?: return null
return try {
val userDoc = firestore.collection("users")
.document(currentUser.uid)
.get()
.await()
userDoc.toObject(User::class.java)
} catch (e: Exception) {
if (e is CancellationException) throw e
logError("Error fetching current user: ${e.message}")
null
}
}
fun onGoogleLoginClicked() {
viewModelScope.launch {
val state = signIn()
_authState.value = state
}
}
private suspend fun signIn(): AuthState {
return try {
if (isSignedIn()) {
val currentUser = getCurrentUser()
Log.d(tag, "이미 로그인 상태입니다. 사용자: $currentUser")
return AuthState.SignedIn(currentUser)
}
val result = buildCredentialRequest()
Log.d(tag, "Credential 요청 성공: $result")
handleSignIn(result)
} catch (e: Exception) {
Log.e(tag, "Sign-in failed: ${e.message}", e)
AuthState.Error("Sign-in failed: ${e.message}")
}
}
private suspend fun buildCredentialRequest(): GetCredentialResponse {
val nonce = generateNonce()
val signInWithGoogleOption = GetSignInWithGoogleOption.Builder(clientId)
.setNonce(nonce)
.build()
val request = GetCredentialRequest.Builder()
.addCredentialOption(signInWithGoogleOption)
.build()
return credentialManager.getCredential(context = applicationContext, request = request)
}
private fun generateNonce(): String {
val randomBytes = ByteArray(32)
SecureRandom().nextBytes(randomBytes)
return randomBytes.joinToString("") { "%02x".format(it) }
}
private fun logError(message: String) {
Log.e(tag, message)
}
private suspend fun handleSignIn(result: GetCredentialResponse): AuthState {
Log.d(tag, "{handleSignIn 진입 완료}")
val credential = result.credential
if (credential is CustomCredential &&
credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL
) {
try {
val tokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
val authCredential = GoogleAuthProvider.getCredential(
tokenCredential.idToken, null
)
val authResult = firebaseAuth.signInWithCredential(authCredential).await()
val firebaseUser = authResult.user
return if (firebaseUser != null) {
val userExists = checkUserExistsInFirestore(firebaseUser.uid)
if (!userExists) {
val initialProfile = createInitialProfile(firebaseUser)
_pendingUserMap.value = initialProfile
AuthState.SignUpRequired
} else {
val userDocument = firestore.collection("users")
.document(firebaseUser.uid).get().await()
val user = userDocument.toObject(User::class.java)
AuthState.SignedIn(user)
}
} else {
AuthState.SignedOut
}
} catch (e: Exception) {
Log.e(tag, "Sign-in error: ${e.message}", e)
return AuthState.Error("Sign-in failed: ${e.message}")
}
} else {
Log.e(tag, "credential is not GoogleIdTokenCredential")
return AuthState.SignedOut
}
}
private suspend fun checkUserExistsInFirestore(userId: String): Boolean {
return try {
firestore.collection("users").document(userId).get().await().exists()
} catch (e: Exception) {
e.printStackTrace()
false
}
}
private fun createInitialProfile(firebaseUser: FirebaseUser): Map {
return mapOf(
"uid" to firebaseUser.uid,
"email" to firebaseUser.email,
"displayName" to firebaseUser.displayName
)
}
}
Wenn ich im zweiten Code credentialManager.getCredential(context = context, request = request) aufrufe, wird ein Fehler ausgegeben. Ich habe jedoch überprüft, ob die GCP-Einstellungen und SHA-1-Konfigurationen korrekt sind, da der erste Code im selben Projekt einwandfrei funktioniert.
Was könnte die Ursache sein? credentialManager.getCredential-Aufruf schlägt im zweiten Code fehl?
Der in Logcat angezeigte Fehler lautet „[28444] Die Entwicklerkonsole ist nicht korrekt eingerichtet.“
Beliebig Erkenntnisse oder Vorschläge zur Behebung dieses Problems Problem wäre sehr dankbar!
Ich habe ein Problem mit der Implementierung meiner Google-Anmeldung in einer Android-Anwendung. Ich habe zwei Codeteile: Der erste funktioniert ordnungsgemäß, während der zweite beim Aufruf von credentialManager.getCredential(context = context, request = request). fehlschlägt Erster Code (funktioniert) [code]class GoogleAuthClient( private val context: Context, private val credentialManager: CredentialManager, private val onSignInComplete: () -> Unit, // 로그인 완료 콜백 추가 private val onSignOutComplete: () -> Unit // 로그아웃 완료 콜백 추가 ) { companion object { private const val TAG = "GoogleAuthClient" }
fun handleSignIn(result: GetCredentialResponse) { val credential = result.credential
when { credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL -> { try { val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
val idToken = googleIdTokenCredential.idToken val email = googleIdTokenCredential.id val displayName = googleIdTokenCredential.displayName
@Composable private fun GoogleLoginButton( onGoogleLoginClick: () -> Unit ) { Row( modifier = Modifier .width(345.dp) .height(54.dp) .background(color = GrayScaleWhite, shape = RoundedCornerShape(size = 10.dp)) .padding(start = 17.dp) .clickable(onClick = onGoogleLoginClick), horizontalArrangement = Arrangement.spacedBy(79.dp, Alignment.Start), verticalAlignment = Alignment.CenterVertically, ) { Image( modifier = Modifier .width(24.dp) .height(24.dp) .background(color = GrayScaleWhite), painter = painterResource(id = R.drawable.google_icon), contentDescription = "google_icon", contentScale = ContentScale.None, ) Text( text = stringResource(id = R.string.google_login), style = TextStyle( fontSize = 18.sp, fontFamily = FontFamily(Font(R.font.inter_18pt_regular)), fontWeight = FontWeight(400), color = Color(0xFF000000), ), ) } } --------------------------- @HiltViewModel class LoginViewModel @Inject constructor( private val credentialManager: CredentialManager, private val clientId: String, @ApplicationContext private val applicationContext: Context // 애플리케이션 컨텍스트 추가 ) : ViewModel() { private val tag = "LoginViewModel: " private val firebaseAuth: FirebaseAuth = Firebase.auth private val firestore: FirebaseFirestore = Firebase.firestore
private val _authState = MutableStateFlow(AuthState.SignedOut) val authState: StateFlow = _authState.asStateFlow()
private val _pendingUserMap = MutableStateFlow(null) val pendingUserMap: StateFlow = _pendingUserMap.asStateFlow()
init { checkInitialAuthState() }
private fun checkInitialAuthState() { viewModelScope.launch { _authState.value = if (isSignedIn()) { val currentUser = getCurrentUser() if (currentUser != null) AuthState.SignedIn(currentUser) else AuthState.SignedOut } else { AuthState.SignedOut } } }
fun isSignedIn(): Boolean = firebaseAuth.currentUser != null
suspend fun getCurrentUser(): User? { val currentUser = firebaseAuth.currentUser ?: return null
return try { val userDoc = firestore.collection("users") .document(currentUser.uid) .get() .await()
userDoc.toObject(User::class.java) } catch (e: Exception) { if (e is CancellationException) throw e logError("Error fetching current user: ${e.message}") null } }
fun onGoogleLoginClicked() { viewModelScope.launch { val state = signIn() _authState.value = state } }
private suspend fun signIn(): AuthState { return try { if (isSignedIn()) { val currentUser = getCurrentUser() Log.d(tag, "이미 로그인 상태입니다. 사용자: $currentUser") return AuthState.SignedIn(currentUser) }
val result = buildCredentialRequest() Log.d(tag, "Credential 요청 성공: $result")
private fun createInitialProfile(firebaseUser: FirebaseUser): Map { return mapOf( "uid" to firebaseUser.uid, "email" to firebaseUser.email, "displayName" to firebaseUser.displayName ) } }
[/code] Wenn ich im zweiten Code credentialManager.getCredential(context = context, request = request) aufrufe, wird ein Fehler ausgegeben. [b]Ich habe jedoch überprüft, ob die GCP-Einstellungen und SHA-1-Konfigurationen korrekt sind, da der erste Code im selben Projekt einwandfrei funktioniert.[/b] Was könnte die Ursache sein? credentialManager.getCredential-Aufruf schlägt im zweiten Code fehl? Der in Logcat angezeigte Fehler lautet „[28444] Die Entwicklerkonsole ist nicht korrekt eingerichtet.“ Beliebig Erkenntnisse oder Vorschläge zur Behebung dieses Problems Problem wäre sehr dankbar!
Ich versuche, eine Beispiel-App mit Android-Architekturkomponenten zu schreiben, aber selbst nach tagelangen Versuchen konnte ich es nicht zum Laufen bringen. Es gibt mir die obige Ausnahme....
Ich versuche, drei Werte an mein UserViewModel zu senden, aber selbst wenn ich das savedStateHandle
In meiner Aktivität sende, habe ich
private val viewModel: UserViewModel by viewModels()
Projekt: .NET 9 MAUI
Paket: reactiveUi.maui, reactiveUi.fody, Shiny.jobs, Shiny.hosting.maui
Ich habe einen Vordergrundjob, das Als Singleton läuft regelmäßig ein Singleton von...
Ich verwende MVVM. Ich möchte Firebase Auth in der App implementieren. Aber um es umzusetzen, benötige ich einen Aktivitätskontext in meiner Repo-Klasse. Wie kann ich es von ViewModel erhalten oder...