Issue
I am try to learning android jetpack compose, I have simple app, and I want to use mobile authentication with firebase for my project. I am using MVVM in the project, so when I debug the project, it throw an error like
You must specify an Activity on your PhoneAuthOptions. Please call #setActivity()
for the .build()
in the view model, how can I arrange my viewmodel, I did not find any solution on internet, any idea?
Navigation:
@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) { _, _ ->
}
}
}}
viewmodel:
@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)
}
}
screen1:
@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,
)
)
}
}
screen2:
@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,
)
)
}
}
Solution
I'm pretty sure that it should be possible to inject activity context using Hilt, and hope to see the solution by @Alex Mamo or someone else.
For now here's hacky solution:
@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...
}
}
In your view:
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
}
Answered By - Pylyp Dukhov
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.