It is my first time when I am playing around with Hilt. I have created a demo app with BottomNavigationView. I have added #AndroidEntryPoint and #HiltAndroidApp. I created a Module class and injected the ViewModel but I am still getting the same error over and over again.
Class <ViewModel> has no zero argument constructor
Some code:
#HiltAndroidApp
class MyApplication: Application() {
}
<application
android:name=".MyApplication"
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
navView.setupWithNavController(navController)
}
}
#AndroidEntryPoint
class CampaignsFragment : Fragment() {
private var _binding: FragmentCampaignsBinding? = null
private val binding get() = _binding!!
private val campaignsViewModel: CampaignsViewModel by navGraphViewModels(R.id.mobile_navigation) {
defaultViewModelProviderFactory
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentCampaignsBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
campaignsViewModel.getCampaigns()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
class CampaignsViewModel #ViewModelInject constructor(
private val db: FirebaseFirestore,
#Assisted private val savedStateHandle: SavedStateHandle
): ViewModel() {
fun getCampaigns() {
db.collection("campaigns")
.get()
.addOnSuccessListener {
for (document in it) {
Log.d(BuildConfig.BUILD_TYPE, "${document.id} => ${document.data}")
}
}
.addOnFailureListener {
}
}
}
#Module
#InstallIn(FragmentComponent::class)
class ApplicationModule {
#Provides
fun provideFirebaseFirestore(): FirebaseFirestore {
return Firebase.firestore
}
}
I have added all the code I am using. Am I missing something? I couldn't find any example or missing errors. I did read the hilt documentation about ViewModel and the implementation.
app gradle file content:
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'com.google.gms.google-services'
id 'kotlin-android-extensions'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "ro.marianpavel.partidulverde"
minSdkVersion 21
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
buildFeatures {
viewBinding true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
implementation platform('com.google.firebase:firebase-bom:25.12.0')
implementation 'com.google.firebase:firebase-analytics-ktx'
implementation 'com.google.firebase:firebase-firestore-ktx'
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation "com.google.dagger:hilt-android:2.29-alpha"
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
kapt "com.google.dagger:hilt-android-compiler:2.29-alpha"
}
I think this might have something to do with the way you provide your firebase dependency. With FragmentComponent::class your ViewModel can't access this dependency. This should work when changing it with ApplicationComponent::class.
Another problem could be that you are missing kapt "com.google.dagger:hilt-android-compiler:$dagger_hilt_version" as well as kapt "androidx.hilt:hilt-compiler:$dagger_hilt_lc_version" in your gradle
Related
I am trying to create MVVM app using Openweathermap API and hilt. I tried to inject my repository into my viewmodel primary constractor and creating a ViewModelFactory class, in order to pass the parameters from viewmodel class to my main activity class it did not work with 'by viewmodels()' itself. Sadly it did not work and I am getting the following message "has no zero argument constructor". It worth to mention that I also tried to inject the repository into my secondary constractor.
This is my MainActivity
#AndroidEntryPoint
class MainActivity
#Inject constructor(var repository:WeatherRepositoryInterface) : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var previewAdapter: PreviewAdapter
lateinit var viewModel:WeatherViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
initViewItems()
}
fun initViewItems() {
viewModel= ViewModelProvider(this#MainActivity,FactoryViewModel(application,repository))[WeatherViewModel::class.java]
binding.recyclerview.apply {
layoutManager = LinearLayoutManager(this#MainActivity)
previewAdapter = PreviewAdapter(viewModel.loadCities().value!!)
previewAdapter.let {
it.setOnItemClickListener(object: OnClickInterface{
override fun onClickItem(position: Int) {
var intent=Intent(this#MainActivity,DetailActivity::class.java)
intent.putExtra("name",viewModel.loadCities().value!![position].cityName)
intent.putExtra("img",viewModel.loadCities().value!![position].cityImg)
startActivity(intent)
}
})
}
adapter = previewAdapter
}
}
}
My ViewModel class
#HiltViewModel
class WeatherViewModel #Inject constructor(application: Application, var repository: WeatherRepositoryInterface)
: AndroidViewModel(application) ,LifecycleObserver{
companion object{
private val VIEWMODEL_STRING="WeatherViewModel.class"
}
private var list: MutableLiveData<List<Preview>> = MutableLiveData()
fun getCityInfo(q:String) =
liveData(Dispatchers.IO){
emit(com.example.yourweatherapp.Resources.Resource.loading(data = null))
try {
emit(com.example.yourweatherapp.Resources.Resource.success(data = repository.getWeather(q=q)))
} catch (e: Exception) {
emit(e.message?.let { com.example.yourweatherapp.Resources.Resource.error(data = null, message = it) })
e.message?.let { Log.e(VIEWMODEL_STRING, it) }
}
}
fun loadCities():MutableLiveData<List<Preview>>{
list.value= listOf(
Preview(
CityList.santorini,
CityList.santoriniImg),
Preview(CityList.bern,
CityList.bernImg),
Preview(CityList.venice,
CityList.veniceImg),
Preview("",CityList.myLocationImg)
)
return list
}
}
My ViewModelFactory class:
class FactoryViewModel(
var application:Application,
var repository: WeatherRepositoryInterface
): ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T =
WeatherViewModel(application,repository) as T
}
My Gradle dependency file:
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
}
android {
compileSdk 31
defaultConfig {
applicationId "com.example.yourweatherapp"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildFeatures {
dataBinding = true
viewBinding = true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation "com.squareup.retrofit2:converter-gson:2.9.0"
implementation "com.squareup.retrofit2:retrofit:2.9.0"
implementation("androidx.cardview:cardview:1.0.0")
implementation 'com.github.bumptech.glide:glide:4.12.0'
implementation("androidx.cardview:cardview:1.0.0")
implementation("androidx.recyclerview:recyclerview:1.2.1")
// implementation ("androidx.lifecycle:lifecycle-livedata:2.5.0-alpha01")
//http3
implementation(platform("com.squareup.okhttp3:okhttp-bom:4.9.3"))
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.0-alpha01"
implementation "androidx.activity:activity-ktx:1.1.0"
implementation("com.squareup.okhttp3:okhttp")
implementation("com.squareup.okhttp3:logging-interceptor")
//lifecycle
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0"
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0")
kapt("androidx.lifecycle:lifecycle-compiler:2.5.0-alpha01")
implementation("androidx.lifecycle:lifecycle-common-java8:2.5.0-alpha01")
implementation("androidx.lifecycle:lifecycle-service:2.5.0-alpha01")
//fragment
implementation("androidx.fragment:fragment-ktx:1.4.1")
implementation("androidx.navigation:navigation-fragment-ktx:2.4.1")
implementation("androidx.navigation:navigation-ui-ktx:2.4.1")
//hilt dagger
implementation("com.google.dagger:hilt-android:2.38.1")
kapt("com.google.dagger:hilt-android-compiler:2.38.1")
//implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.2"
implementation 'androidx.hilt:hilt-navigation-compose:1.0.0'
implementation 'androidx.hilt:hilt-navigation-compose:1.0.0'
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
kapt 'androidx.hilt:hilt-compiler:1.0.0'
}
kapt {
javacOptions {
// These options are normally set automatically via the Hilt Gradle plugin, but we
// set them manually to workaround a bug in the Kotlin 1.5.20
option("-Adagger.fastInit=ENABLED")
option("-Adagger.hilt.android.internal.disableAndroidSuperclassValidation=true")
}
}
Just remove this line from your dependencies:
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
I found this in migrations doc and solved the issue for my case:
Remove this line from gradle:
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03'
I'm using latest versions of all dependencies, make sure to check and apply all related and necessary migrations
I am trying Dagger 2 for the first time using tutorial. When I start application on some stage there appears an error I can't resolve:
Execution failed for task ':app:kaptDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask$KaptExecutionWorkAction
> java.lang.reflect.InvocationTargetException (no error message)
The code is extremely simple but I have no idea what is wrong there. There is MainActivity class with default code and some additional classes
MainActivity.kt:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
AppComponent.kt:
import dagger.Component
import dagger.Module
import dagger.Provides
#Component(modules = [AppModule::class])
interface AppComponent {
fun computer(): Computer
}
#Module
object AppModule {
#Provides
fun provideComputer(
processor: Processor, ram: RAM, motherboard: Motherboard
): Computer {
return Computer(processor, ram, motherboard)
}
#Provides
fun provideProcessor() = Processor()
#Provides
fun provideRam(): RAM = RAM()
#Provides
fun provideMotherboard(): Motherboard = Motherboard()
}
Computer.kt file with other small classes:
import javax.inject.Inject
data class Computer #Inject constructor(
val processor: Processor,
val ram: RAM,
val motherboard: Motherboard
)
class Processor #Inject constructor() {
override fun toString() = "AB2022"
}
class RAM #Inject constructor() {
override fun toString() = "16GB"
}
class Motherboard #Inject constructor() {
override fun toString() = "X7 3000"
}
App gradle dependencies:
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-kapt'
}
android {
compileSdk 31
defaultConfig {
applicationId "com.example.daggertutorial"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
}
dependencies {
implementation 'com.google.dagger:dagger:2.36'
kapt 'com.google.dagger:dagger-compiler:2.36'
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
code on github: https://github.com/faritowich/DaggerTutorial
I'm trying to call the await() function in the
bookList = bookViewModel.getBookList().await()
in the Main Activity but it gives the error in the header.
Main Activity
#AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val bookViewModel: BookViewModel by viewModels()
private var bookList : BookList? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GlobalScope.launch(Dispatchers.IO) {
bookList = bookViewModel.getBookList().await()
}
setContent {
LOTRAppTheme {
}
}
}
}
ViewModel
package com.example.lotrapp
import androidx.lifecycle.ViewModel
import com.example.lotrapp.models.BookList
import com.example.lotrapp.repository.BookRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
#HiltViewModel
class BookViewModel #Inject constructor(
private val repository: BookRepository
) : ViewModel() {
suspend fun getBookList() : BookList? {
return repository.getBookList()
}
}
Repository
package com.example.lotrapp.repository
import com.example.lotrapp.models.BookList
import com.example.lotrapp.network.RetrofitApi
import javax.inject.Inject
class BookRepository #Inject constructor(
private val retrofitApi: RetrofitApi
) {
suspend fun getBookList() : BookList?
{
return retrofitApi.getBookList().body()
}
}
Api
package com.example.lotrapp.network
import com.example.lotrapp.models.BookList
import retrofit2.Response
import retrofit2.http.GET
interface RetrofitApi {
#GET("book")
suspend fun getBookList() : Response<BookList>
}
Gradle:
plugins {
id 'com.android.application'
id 'kotlin-android'
id("dagger.hilt.android.plugin")
id("kotlin-kapt")
}
android {
compileSdk 31
defaultConfig {
applicationId "com.example.lotrapp"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
useIR = true
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
kotlinCompilerVersion '1.5.10'
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
def compose_version = "1.0.2"
def lifecycle_version = "2.3.1"
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
// ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
// LiveData
implementation("androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version")
// Lifecycles only (without ViewModel or LiveData)
implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version")
// Saved state module for ViewModel
implementation("androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version")
// Annotation processor
kapt("androidx.lifecycle:lifecycle-compiler:$lifecycle_version")
// alternately - if using Java8, use the following instead of lifecycle-compiler
implementation("androidx.lifecycle:lifecycle-common-java8:$lifecycle_version")
implementation 'androidx.activity:activity-compose:1.3.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
//Hilt
implementation("com.google.dagger:hilt-android:2.38.1")
kapt("com.google.dagger:hilt-android-compiler:2.38.1")
//Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2")
}
kapt {
correctErrorTypes true
}
First I want to pull the list from the api and then show it in the UI with a Composable function (with LazyColumn) in setContent. It is enough to solve this error, but if you have better code suggestions, I would appreciate it. Thank you!
It doesn't really seem you need to invoke await() there. getBookList() returns BookList? directly, so just remove await() and it should be fine.
Additionally, you said you need to use this bookList in setContent. In that case you need to move setContent into launch block, below getBookList(). Right now setContent really executes before bookList is acquired. It is hard to provide more details without the full contents of setContent.
please help me, I trying make an application with phone authentication with firebase. I have activated the phone sign-in method
and this is my app gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.google.gms.google-services'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.lumbung_inovasi.policehealthcare"
minSdkVersion 22
targetSdkVersion 29
versionCode 1
versionName "1.0"
buildConfigField("String", "BASE_API", '"http://ludes.in:5001/"')
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
// To inline the bytecode built with JVM target 1.8 into
// bytecode that is being built with JVM target 1.6. (e.g. navArgs)
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "androidx.fragment:fragment:1.2.5"
implementation 'androidx.navigation:navigation-fragment:2.2.2'
implementation 'androidx.navigation:navigation-ui:2.2.2'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.2.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.2.2'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.google.firebase:firebase-core:17.4.3'
implementation 'com.google.firebase:firebase-auth:19.3.1'
testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'de.hdodenhof:circleimageview:2.1.0'
implementation 'com.tbuonomo.andrui:viewpagerdotsindicator:4.1.2'
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
implementation 'com.github.alahammad:android-OTP-View:1.0.2'
// Retrofit & OkHttp
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.6.1'
implementation('com.facebook.stetho:stetho-okhttp3:1.5.1') {
exclude group: 'com.facebook.stetho'
}
}
I trying to create OTP layout with fragment, and I using sms catch library to be able read new message with OTP code. this is my OTP fragment code
class OtpFragment : Fragment(), OTPListener, OnSmsCatchListener<String> {
private lateinit var smsCatcher: SmsVerifyCatcher
private lateinit var phoneAuth: FirebaseAuth
private var mResendToken: ForceResendingToken? = null
private var mVerificationId: String = ""
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
otp.setOnOtpFinished(this)
phoneAuth = FirebaseAuth.getInstance()
smsCatcher = SmsVerifyCatcher(activity, this)
val phoneNumber = "+62 85157233868"
requestOTP(phoneNumber)
resend_button.setOnClickListener {
resendOTPCode(phoneNumber)
}
}
private fun requestOTP(phoneNumber: String){
PhoneAuthProvider.getInstance().verifyPhoneNumber(phoneNumber, 60, TimeUnit.SECONDS, this.requireActivity(), callbacks())
}
private fun callbacks(): OnVerificationStateChangedCallbacks {
return object : OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(phoneAuthCredential: PhoneAuthCredential) {
Log.e("onVerificationCompleted", "${phoneAuthCredential.smsCode}")
phoneAuth.signInWithCredential(phoneAuthCredential).addOnCompleteListener {
if (it.isSuccessful){
Log.e("sign-in", "berhasil")
} else{
Log.e("sign-in", "${it.result}")
}
}
}
override fun onVerificationFailed(e: FirebaseException) {
if (e is FirebaseAuthInvalidCredentialsException) {
Log.e("invalidCredential", e.toString())
} else if (e is FirebaseTooManyRequestsException) {
Log.e("out of quota", e.toString())
}
}
override fun onCodeAutoRetrievalTimeOut(s: String) {
super.onCodeAutoRetrievalTimeOut(s)
Log.e("", s)
}
override fun onCodeSent(
verificationId: String,
forceResendingToken: ForceResendingToken
) {
super.onCodeSent(verificationId, forceResendingToken)
Log.e("code-sent", "onCodeSent:$verificationId")
mVerificationId = verificationId
mResendToken = forceResendingToken
}
}
}
private fun signInWithCredential(credential: PhoneAuthCredential){
phoneAuth.signInWithCredential(credential)
.addOnCompleteListener {
val intent = Intent(activity, MainActivity::class.java)
startActivity(intent)
activity?.overridePendingTransition(0,0)
}
.addOnFailureListener {
Log.e("login-fail", "${it.message}")
}
}
private fun resendOTPCode(phoneNumber: String){
if(mResendToken != null) {
PhoneAuthProvider.getInstance().verifyPhoneNumber(
phoneNumber,
120,
TimeUnit.SECONDS,
this.requireActivity(),
callbacks(),
mResendToken
)
Toast.makeText(activity, "code otp dikirim ulang", Toast.LENGTH_SHORT).show()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_otp, container, false)
}
override fun otpFinished(p0: String?) {
Toast.makeText(activity, "otp finish $p0", Toast.LENGTH_SHORT).show()
p0?.let {
val credential = PhoneAuthProvider.getCredential(mVerificationId, it)
signInWithCredential(credential)
}
}
override fun onSmsCatch(p0: String?) {
Toast.makeText(activity, "sms catch $p0", Toast.LENGTH_SHORT).show()
}
}
for information, I using xiaomi redmi note 7 with internet connection and sim card is strill active
I am trying to communicate with viewmodel from xml and vice versa using MVVM pattern. I have worked on databinding before and successfully worked with Live Data - Dagger - MVVM .
Recently, I have tried to create a new project and since then I cannot track the response with XML and viewmodel. Neither the onClick from XML -> ViewModel nor the assigning value to the textview from View -> XML is working. But there is no crash or anything, just it is not working. I have added all associated files [ MainActivity, activity_main, viewModel, Dagger Module, build.gradle ]
I will really appreciate if anyone can tell me what is going wrong here.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="viewmodel"
type="aveek.com.vm.ui.home.MainActivityViewModel"/>
</data>
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#{viewmodel.balanceText}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:clickable="true"
android:onClick="#{() -> viewmodel.clickData()}"
app:layout_constraintTop_toTopOf="parent" />
...
</layout>
MainActivity.kt
class MainActivity : AppCompatActivity(),LifecycleOwner {
#Inject
lateinit var binding : ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding.setLifecycleOwner(this)
with(binding){
this.viewmodel?.let {
it.balanceText.set( "Aveek testing")
it.data.observe(this#MainActivity, Observer {
Toast.makeText(this#MainActivity, "Data is now : $it", Toast.LENGTH_SHORT).show()
})
}
}
}
MainActivityViewModel.kt
class MainActivityViewModel : ViewModel() {
val data = MutableLiveData<Boolean>()
val balanceText = ObservableField<String>()
fun clickData(){
data.value = false
}
}
MainActivityModule.kt
#Module
class MainActivityModule{
/**
* provides binding to Main Activity from respective XML
* #property viewModel
* #property context
* #return binding of the view
*/
#Provides
fun binding(context: MainActivity, viewModel : MainActivityViewModel) :
ActivityMainBinding {
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(context,
R.layout.activity_main)
binding.viewmodel = viewModel
return binding
}
}
build.gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 27
defaultConfig {
applicationId "aveek.test"
minSdkVersion 15
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
dataBinding {
enabled = true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
kapt {
generateStubs = true
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:multidex:1.0.2'
implementation 'com.android.support:design:27.1.1'
implementation "android.arch.lifecycle:extensions:1.1.0"
// annotationProcessor "android.arch.lifecycle:compiler:1.1.0"
kapt "com.android.databinding:compiler:$androidPluginVersion"
annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion"
annotationProcessor "com.google.dagger:dagger-android-processor:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
kapt "com.google.dagger:dagger-android-processor:$daggerVersion"
implementation "com.google.dagger:dagger:$daggerVersion"
implementation "com.google.dagger:dagger-android:$daggerVersion"
implementation "com.google.dagger:dagger-android-support:$daggerVersion"
kapt "com.google.dagger:dagger-compiler:$daggerVersion"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-
core:3.0.2'
}
ext{
kotlin_version = '1.2.31'
androidPluginVersion = '3.1.0'
daggerVersion = '2.13'
}
you won't be able to use any generated methods to set your variable to the binding, as there's no common superclass besides the ViewDataBinding,so you will be forced to use reflection, or you can use the convenience method setVariable():
binding.setVariable(BR.viewModel, viewModel);
As I suspected, the problem is with Dagger injection. Maybe I have implemented in a wrong approach but the viewmodel is perfectly fine if running without DI.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProviders.of(this).get(MainActivityViewModel::class.java)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.viewmodel = viewModel
binding.setLifecycleOwner(this)
mLifecycleRegistry = LifecycleRegistry(this).apply {
markState(Lifecycle.State.CREATED)
}
with(binding){
this.viewmodel?.let {
it.balanceText.set( "Aveek testing")
it.data.observe(this#MainActivity, Observer {
Toast.makeText(this#MainActivity, "Data is now : $it",
Toast.LENGTH_SHORT).show()
})
}
}
}
So, I have gone through the DI code and found out that in MainActivity.onCreate()
I was doing this
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
which basically overriding the databinding behaviour. So I have updated the code like this
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
//setContentView(R.layout.activity_main)
// and initiated binding here as injecting binding from module before setting content won't be effective
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
And removed
// #Inject
// lateinit var binding : ActivityMainBinding
Now it works perfectly fine..