I have an interface which is passed to a viewmodel from an activity where its implemented. Then in the viewmodel, it calls the implemented interface method. Inside this method, all I want to do is display a Toast but I am getting null error trying to access the activity context. I do not understand why. Just to be clear, inside all otber overridden method such as onCreate, onStop, OnResume, etc. If I display the toast, it works fine. Its only when inside this interface method, activity context seems to be null. I have absolutely no idea.
This is the hilt module
@Module
@InstallIn(SingletonComponent::class)
abstract class ToastListenerModule {
@Binds
abstract fun bindToastMessageListener(activity: SplashActivity): ToastMessageListener
}
My SplashActivity.kt
@AndroidEntryPoint
class SplashActivity @Inject constructor() : AppCompatActivity(), ToastMessageListener {
private lateinit var binding: ActivitySplashBinding
@Inject
lateinit var auth: FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen()
binding = ActivitySplashBinding.inflate(layoutInflater)
setContentView(binding.root)
}
override fun showToast(message: String) {
//THIS IS WHERE THE PROBLEM IS WITH THE CONTEXT
Toast.makeText(this, "MESSAGE", Toast.LENGTH_SHORT)
}
}
Below I have my ViewModel:
@HiltViewModel
class AuthenticationViewModel @Inject constructor(
private val auth: FirebaseAuth,
private val toastListner: ToastMessageListener,
) : ViewModel() {
val emailText = ObservableField<String>("")
val passwordText = ObservableField<String>("")
val nameText = ObservableField<String>("")
fun onSubmitClicked(isLogin: Boolean) {
if (isLogin) {
val email = emailText.get()
val password = passwordText.get()
if (email.isNullOrEmpty() || password.isNullOrEmpty()) {
println("Email or password is empty")
return
}
auth.signInWithEmailAndPassword(emailText.get() ?: "", passwordText.get() ?: "")
.addOnCompleteListener() { task ->
if (task.isSuccessful) {
toastListner.showToast("Login Successful")
} else {
val exception = task.exception
//THIS IS WHERE I CALL showToast
//AND YES IT IS GETTING CALLED AND EVERYTHING
toastListner.showToast("Login Failed: ${exception?.message}")
}
}
} else {
println("SIGN Up BUTTON CLICKED ${emailText.get()} ${passwordText.get()}")
}
}
fun isValidInput(): Boolean {
val inputEm = emailText.get() ?: return false
val inputPass = passwordText.get() ?: return false
return inputEm.isNotEmpty() && inputPass.isNotEmpty()
}
}
My ERROR
FATAL EXCEPTION: main
Process: com.example.findroomies, PID: 27661
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Context.getPackageName()' on a null object referenceat android.content.ContextWrapper.getPackageName(ContextWrapper.java:173)
at android.widget.Toast.<init>(Toast.java:174)
at android.widget.Toast.makeText(Toast.java:498)
at android.widget.Toast.makeText(Toast.java:487)
at com.example.findroomies.ui.activities.SplashActivity.showToast(SplashActivity.kt:52)
at com.example.findroomies.ui.viewmodels.AuthenticationViewModel.onSubmitClicked$lambda$0(AuthenticationViewModel.kt:35)
at com.example.findroomies.ui.viewmodels.AuthenticationViewModel.$r8$lambda$b1GN3_M-m0Ij9SZCrkTUOa2z6PY(Unknown Source:0)
at com.example.findroomies.ui.viewmodels.AuthenticationViewModel$$ExternalSyntheticLambda0.onComplete(Unknown Source:2)
at com.google.android.gms.tasks.zzi.run(com.google.android.gms:play-services-tasks@@18.0.2:1)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7872)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
First of all u said that you are having problem in context. So you can solve it by injecting application context in the viewModel class and then pass the context to the function as an argument.
Here is an example for reference. But still to know the exact reason of nullPointerExeption. I think you should share the error logs.
@HiltViewModel
class AuthenticationViewModel @Inject constructor(
private val auth: FirebaseAuth,
private val toastListner: ToastMessageListener,
@ApplicationContext private val context: Context
) : ViewModel() {
Then in your activity you can add context as an argument.
override fun showToast(context : Context, message: String) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
Same way u can should pass that context in the viewModel class
toastListner.showToast(context,"Login Failed: ${exception?.message}")