为什么我无法在 Jetpack Compose 中使用 Firebase 的移动身份验证?
Why I am not able to use mobile authentication with firebase in jetpack compose?
我正在尝试学习 android jetpack compose,我有一个简单的应用程序,我想在我的项目中使用带有 firebase 的移动身份验证。我在项目中使用了MVVM,所以当我调试项目时,它会抛出类似
的错误
You must specify an Activity on your PhoneAuthOptions. Please call
#setActivity()
对于视图模型中的.build()
,我该如何安排我的视图模型,我在网上没有找到任何解决方案,有什么想法吗?
导航:
@OptIn(ExperimentalPagerApi::class)
@Composable
fun NavScreen(
) {
val modelAuthentication =
hiltViewModel<AuthenticationViewModel>.()
val navController = rememberNavController()
NotificationMessage(viewModel = viewModel())
NavHost(navController = navController, startDestination =
"phone") {
composable("phone") {
PhoneScreen(navController = navController,
modelAuthentication = modelAuthentication) { _, _ ->
}
}
composable("phoneVerify") {
PhoneVerifyScreen(navController = navController,
modelAuthentication = modelAuthentication) { _, _ ->
}
}
}}
视图模型:
@HiltViewModel
class AuthenticationViewModel @Inject constructor(
) : ViewModel() {
private val mAuth = FirebaseAuth.getInstance()
var verificationOtp = ""
var popNotification = mutableStateOf<Event<String>?>(null)
private lateinit var baseBuilder: PhoneAuthOptions.Builder
fun setActivity(activity: Activity) {
baseBuilder =
PhoneAuthOptions.newBuilder().setActivity(activity)
}
fun send(mobileNum: String) {
val options = baseBuilder
.setPhoneNumber("+91$mobileNum")
.setTimeout(60L, TimeUnit.SECONDS)
.setCallbacks(object :
PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(p0: PhoneAuthCredential) {
handledException(customMessage = "Verification Completed")
}
override fun onVerificationFailed(p0: FirebaseException) {
handledException(customMessage = "Verification Failed")
}
override fun onCodeSent(otp: String, p1: PhoneAuthProvider.ForceResendingToken) {
super.onCodeSent(otp, p1)
verificationOtp = otp
handledException(customMessage = "Otp Send Successfully")
}
}).build()
PhoneAuthProvider.verifyPhoneNumber(options)
}
fun otpVerification(otp: String) {
val credential = PhoneAuthProvider.getCredential(verificationOtp, otp)
FirebaseAuth.getInstance().signInWithCredential(credential)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
handledException(customMessage = "Verification Successful")
} else {
handledException(customMessage = "Wrong Otp")
}
}
}
private fun handledException(exception: Exception? = null, customMessage: String = "") {
exception?.printStackTrace()
val errorMsg = exception?.message ?: ""
val message = if (customMessage.isEmpty()) {
errorMsg
} else {
"$customMessage: $errorMsg"
}
popNotification.value = Event(message)
}
}
屏幕1:
@Composable
fun PhoneScreen(
navController: NavController,
modelAuthentication: AuthenticationViewModel,
onClick: (mobileNum: String, otp: String) -> Unit
) {
val phoneNumber = remember { mutableStateOf("") }
val context = LocalContext.current
LaunchedEffect(Unit) {
println("found activity? ${context.findActivity()}")
val activity = context.findActivity() ?:
return@LaunchedEffect
modelAuthentication.setActivity(activity)
}
OutlinedTextField(
value = phoneNumber.value,
colors = TextFieldDefaults.textFieldColors(
backgroundColor = white,
focusedIndicatorColor = Grey,
unfocusedIndicatorColor = Grey,
focusedLabelColor = Grey,
unfocusedLabelColor = Grey,
),
onValueChange = { phoneNumber.value = it },
label = { Text(text = "Phone Number") },
placeholder = { Text(text = "Phone Number") },
singleLine = true,
modifier = Modifier.fillMaxWidth(0.8f)
)
Button(
modifier = Modifier
.width(285.dp)
.height(55.dp),
onClick = {
modelAuthentication.send(phoneNumber.value)
navController.navigate("phoneVerify")
},
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Custom
),
shape = RoundedCornerShape(60)
) {
Text(
text = "Next",
style = TextStyle(
fontSize = 18.sp,
color = white,
)
)
}
}
屏幕2:
@Composable
fun PhoneVerifyScreen(
navController: NavController,
modelAuthentication: AuthenticationViewModel,
onClick: (mobileNum: String, otp: String) -> Unit
) {
val phoneNumberOtp = remember { mutableStateOf("") }
val context = LocalContext.current
LaunchedEffect(Unit) {
println("found activity? ${context.findActivity()}")
val activity = context.findActivity() ?:
return@LaunchedEffect
modelAuthentication.setActivity(activity)
}
OutlinedTextField(
value = phoneNumberOtp.value,
colors = TextFieldDefaults.textFieldColors(
backgroundColor = white,
focusedIndicatorColor = Grey,
unfocusedIndicatorColor = Grey,
focusedLabelColor = Grey,
unfocusedLabelColor = Grey,
),
onValueChange = { phoneNumberOtp.value = it },
label = { Text(text = "code") },
placeholder = { Text(text = "code") },
singleLine = true,
modifier = Modifier.fillMaxWidth(0.8f)
)
Button(
modifier = Modifier
.width(285.dp)
.height(55.dp),
onClick = {
modelAuthentication.otpVerification(phoneNumberOtp.value)
navController.navigate("home")
},
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Custom
),
shape = RoundedCornerShape(60)
) {
Text(
text = "Next",
style = TextStyle(
fontSize = 18.sp,
color = white,
)
)
}
}
您需要 specify Activity 因为
它是构建器的必需参数。
您必须将构建器的代码移动到您的 Activity,因为您不允许在 ViewModel 中使用 link 到 Activity。
在验证结果回调中可以调用ViewModel的方法传递结果或异常。
我很确定应该可以使用 Hilt 注入 activity 上下文,并希望看到@Alex Mamo 或其他人的解决方案。
现在这里是 hacky 解决方案:
@HiltViewModel
class AuthenticationViewModel @Inject constructor(
) : ViewModel() {
private lateinit var baseBuilder: PhoneAuthOptions.Builder
fun setActivity(activity: Activity) {
baseBuilder = PhoneAuthOptions.newBuilder().setActivity(activity)
}
fun send(mobileNum: String) {
val options = baseBuilder
//.setPhoneNumber...
}
}
在您看来:
val viewModel = viewModel<AuthenticationViewModel>()
val context = LocalContext.current
LaunchedEffect(Unit) {
val activity = context.findActivity() ?: return@LaunchedEffect
viewModel.setActivity(activity)
}
findActivity
:
fun Context.findActivity(): Activity? = when (this) {
is Activity -> this
is ContextWrapper -> baseContext.findActivity()
else -> null
}
我正在尝试学习 android jetpack compose,我有一个简单的应用程序,我想在我的项目中使用带有 firebase 的移动身份验证。我在项目中使用了MVVM,所以当我调试项目时,它会抛出类似
的错误You must specify an Activity on your PhoneAuthOptions. Please call #setActivity()
对于视图模型中的.build()
,我该如何安排我的视图模型,我在网上没有找到任何解决方案,有什么想法吗?
导航:
@OptIn(ExperimentalPagerApi::class)
@Composable
fun NavScreen(
) {
val modelAuthentication =
hiltViewModel<AuthenticationViewModel>.()
val navController = rememberNavController()
NotificationMessage(viewModel = viewModel())
NavHost(navController = navController, startDestination =
"phone") {
composable("phone") {
PhoneScreen(navController = navController,
modelAuthentication = modelAuthentication) { _, _ ->
}
}
composable("phoneVerify") {
PhoneVerifyScreen(navController = navController,
modelAuthentication = modelAuthentication) { _, _ ->
}
}
}}
视图模型:
@HiltViewModel
class AuthenticationViewModel @Inject constructor(
) : ViewModel() {
private val mAuth = FirebaseAuth.getInstance()
var verificationOtp = ""
var popNotification = mutableStateOf<Event<String>?>(null)
private lateinit var baseBuilder: PhoneAuthOptions.Builder
fun setActivity(activity: Activity) {
baseBuilder =
PhoneAuthOptions.newBuilder().setActivity(activity)
}
fun send(mobileNum: String) {
val options = baseBuilder
.setPhoneNumber("+91$mobileNum")
.setTimeout(60L, TimeUnit.SECONDS)
.setCallbacks(object :
PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(p0: PhoneAuthCredential) {
handledException(customMessage = "Verification Completed")
}
override fun onVerificationFailed(p0: FirebaseException) {
handledException(customMessage = "Verification Failed")
}
override fun onCodeSent(otp: String, p1: PhoneAuthProvider.ForceResendingToken) {
super.onCodeSent(otp, p1)
verificationOtp = otp
handledException(customMessage = "Otp Send Successfully")
}
}).build()
PhoneAuthProvider.verifyPhoneNumber(options)
}
fun otpVerification(otp: String) {
val credential = PhoneAuthProvider.getCredential(verificationOtp, otp)
FirebaseAuth.getInstance().signInWithCredential(credential)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
handledException(customMessage = "Verification Successful")
} else {
handledException(customMessage = "Wrong Otp")
}
}
}
private fun handledException(exception: Exception? = null, customMessage: String = "") {
exception?.printStackTrace()
val errorMsg = exception?.message ?: ""
val message = if (customMessage.isEmpty()) {
errorMsg
} else {
"$customMessage: $errorMsg"
}
popNotification.value = Event(message)
}
}
屏幕1:
@Composable
fun PhoneScreen(
navController: NavController,
modelAuthentication: AuthenticationViewModel,
onClick: (mobileNum: String, otp: String) -> Unit
) {
val phoneNumber = remember { mutableStateOf("") }
val context = LocalContext.current
LaunchedEffect(Unit) {
println("found activity? ${context.findActivity()}")
val activity = context.findActivity() ?:
return@LaunchedEffect
modelAuthentication.setActivity(activity)
}
OutlinedTextField(
value = phoneNumber.value,
colors = TextFieldDefaults.textFieldColors(
backgroundColor = white,
focusedIndicatorColor = Grey,
unfocusedIndicatorColor = Grey,
focusedLabelColor = Grey,
unfocusedLabelColor = Grey,
),
onValueChange = { phoneNumber.value = it },
label = { Text(text = "Phone Number") },
placeholder = { Text(text = "Phone Number") },
singleLine = true,
modifier = Modifier.fillMaxWidth(0.8f)
)
Button(
modifier = Modifier
.width(285.dp)
.height(55.dp),
onClick = {
modelAuthentication.send(phoneNumber.value)
navController.navigate("phoneVerify")
},
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Custom
),
shape = RoundedCornerShape(60)
) {
Text(
text = "Next",
style = TextStyle(
fontSize = 18.sp,
color = white,
)
)
}
}
屏幕2:
@Composable
fun PhoneVerifyScreen(
navController: NavController,
modelAuthentication: AuthenticationViewModel,
onClick: (mobileNum: String, otp: String) -> Unit
) {
val phoneNumberOtp = remember { mutableStateOf("") }
val context = LocalContext.current
LaunchedEffect(Unit) {
println("found activity? ${context.findActivity()}")
val activity = context.findActivity() ?:
return@LaunchedEffect
modelAuthentication.setActivity(activity)
}
OutlinedTextField(
value = phoneNumberOtp.value,
colors = TextFieldDefaults.textFieldColors(
backgroundColor = white,
focusedIndicatorColor = Grey,
unfocusedIndicatorColor = Grey,
focusedLabelColor = Grey,
unfocusedLabelColor = Grey,
),
onValueChange = { phoneNumberOtp.value = it },
label = { Text(text = "code") },
placeholder = { Text(text = "code") },
singleLine = true,
modifier = Modifier.fillMaxWidth(0.8f)
)
Button(
modifier = Modifier
.width(285.dp)
.height(55.dp),
onClick = {
modelAuthentication.otpVerification(phoneNumberOtp.value)
navController.navigate("home")
},
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Custom
),
shape = RoundedCornerShape(60)
) {
Text(
text = "Next",
style = TextStyle(
fontSize = 18.sp,
color = white,
)
)
}
}
您需要 specify Activity 因为 它是构建器的必需参数。
您必须将构建器的代码移动到您的 Activity,因为您不允许在 ViewModel 中使用 link 到 Activity。
在验证结果回调中可以调用ViewModel的方法传递结果或异常。
我很确定应该可以使用 Hilt 注入 activity 上下文,并希望看到@Alex Mamo 或其他人的解决方案。
现在这里是 hacky 解决方案:
@HiltViewModel
class AuthenticationViewModel @Inject constructor(
) : ViewModel() {
private lateinit var baseBuilder: PhoneAuthOptions.Builder
fun setActivity(activity: Activity) {
baseBuilder = PhoneAuthOptions.newBuilder().setActivity(activity)
}
fun send(mobileNum: String) {
val options = baseBuilder
//.setPhoneNumber...
}
}
在您看来:
val viewModel = viewModel<AuthenticationViewModel>()
val context = LocalContext.current
LaunchedEffect(Unit) {
val activity = context.findActivity() ?: return@LaunchedEffect
viewModel.setActivity(activity)
}
findActivity
:
fun Context.findActivity(): Activity? = when (this) {
is Activity -> this
is ContextWrapper -> baseContext.findActivity()
else -> null
}