this weird run time exception keep coming up - kotlin

This is the runtime exception: "lateinit property name Session has not been initialized" that keeps coming up.
class GalasatyActivity : BaseActivity() {
private lateinit var binding: ActivityGalasatyBinding
private val list = ArrayList<Post>()
private lateinit var readingTypes: String
private lateinit var student :String
private lateinit var statue :String
private var sessionType by Delegates.notNull<Int>()
private lateinit var nameSession:String
private lateinit var mSharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityGalasatyBinding.inflate(layoutInflater)
setContentView(binding.root)
setUpActionBar()
setupGalasatyList()
mSharedPreferences = getSharedPreferences(Constants.PREFERENCE_NAME, Context.MODE_PRIVATE)
//nameSession = findViewById<TextView>(R.id.tv_galsa).toString()
readingTypes = findViewById<TextView>(R.id.tv_reading_type).toString()
student = findViewById<TextView>(R.id.tv_student).toString()
statue = findViewById<TextView>(R.id.tv_statue).toString()
sessionType = findViewById<TextView>(R.id.tv_session_type).toString().toInt()
nameSession = findViewById<TextView>(R.id.tv_galsa).toString()
}
private fun setUpActionBar(){
setSupportActionBar(binding.toolBarGalasaty)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
binding.toolBarGalasaty.setNavigationOnClickListener {
doubleBackToExit()
}
}
private fun setupGalasatyList(){
hideProgressBar()
//getGalasatDetails(name,readingTypes,student,statue,sessionType)
DataRetrofit.instance.getSessions(nameSession,readingTypes,student,statue,sessionType).enqueue(object :Callback<Post?>{
override fun onResponse(call: Call<Post?>, response: Response<Post?>) {
binding.rvGalasatyList.layoutManager = LinearLayoutManager(this#GalasatyActivity,LinearLayoutManager.VERTICAL,false)
binding.rvGalasatyList.setHasFixedSize(true)
val galsaAdapter = GalasatyItemListAdapter(this#GalasatyActivity, list)
binding.rvGalasatyList.adapter = galsaAdapter
galsaAdapter.setOnClickListener(object : GalasatyItemListAdapter.OnClickListener{
override fun onClick(position: Int, model: Post) {
startActivity(Intent(this#GalasatyActivity,GalsaActivity::class.java))
}
})
}
override fun onFailure(call: Call<Post?>, t: Throwable) {
Toast.makeText(this#GalasatyActivity, t.message, Toast.LENGTH_SHORT).show()
}
})
}
private fun getGalasatDetails(name:String, readingTypes: String, student:String, statement:String, sessionType:Int){
if (Constants.isInternetAvailable(this)){
val retrofit: retrofit2.Retrofit = retrofit2.Retrofit.Builder().baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create()).build()
val service: DataAPI = retrofit.create(DataAPI::class.java)
val listCall: Call<Post> = service.getSessions(name,readingTypes,student,statement,sessionType)
showProgressDialog(resources.getString(R.string.please_wait))
listCall.enqueue(object :Callback<Post?> {
#RequiresApi(Build.VERSION_CODES.N)
override fun onResponse(call: Call<Post?>, response: Response<Post?>) {
if (response.isSuccessful){
hideProgressBar()
val galasatList: Post? = response.body()
val sessionResponseJsonString = Gson().toJson(galasatList)
val editor = mSharedPreferences.edit()
editor.putString(Constants.SESSIONS_RESPONSE_DATA, sessionResponseJsonString)
editor.apply()
setupUI()
Log.i("Response result","$galasatList")
}else{
when(response.code()){
400->{
Log.e("Error 400","Bad connection")
}404->{
Log.e("Error 404","Not found")
}else->{
Log.e("Error","Generic error")
}
}
}
}
override fun onFailure(call: Call<Post?>, t: Throwable) {
hideProgressBar()
Log.e("erorrr", t.message.toString())
}
})
}else{
Toast.makeText(this,"No internet connection", Toast.LENGTH_SHORT).show()
}
}
private fun setupUI(){
val galasatResponseJsonString = mSharedPreferences.getString(Constants.SESSIONS_RESPONSE_DATA,"")
if (!galasatResponseJsonString.isNullOrEmpty()) {
val galasatList = Gson().fromJson(galasatResponseJsonString, Post::class.java)
for (i in Post.toString()) {
readingTypes = galasatList.readingType
student = galasatList.student
statue = galasatList.student
nameSession = galasatList.name
}
}
}
}

You're initializing a view into a string which is outrightly wrong.
See, here you've declared a variable as lateinit var nameSession: String and then you're initializing it with a view as nameSession = findViewById<TextView>(R.id.tv_galsa).toString() , that too as String which actually doesn't make sense.
Second, the error is about nameSession which as I said earlier is used first in the line no. 64 - DataRetrofit.instance.getSessions(nameSession,..., so the code breaks at this very point and you think that it will work with others, just not this which is again not the case because your code is wrong.
See, There are two cases, Either you want to pass the text value of the TextView nameSession and others, or the view in the getSessions().
To passs the view, declare it as lateinit var nameSession: TextView, initialize it as nameSession = findViewById<TextView>(R.id.tv_galsa) and pass it as DataRetrofit.instance.getSessions(nameSession,...).
To pass the text value of the TextView, declare and initialize it exactly as my first point, just pass it as DataRetrofit.instance.getSessions(nameSession.text,...).
Do the 2nd point for others as well. You DON'T convert the view to String, you convert the value of the view to String, and to get the value, you use the .text property of the TextView as I've used in the 2nd point.(setText() and getText() in Java).
So, convert all your variables as Views, and then pass their value using the .text property. One more of doing it will be nameSession = binding.tv_galsa.text.

Related

Getting list from viewmodel in observe event -MVVM

I have a issue in getting a list returned in observe event in my activity. i am developing a login screen in MVVM. viewmodel is as follows.
my problem is i can get returned data in observe call back into a UI control. but same data returned assign into a list variable is empty. in other words, list returned unable to pass into a a list variable in an activity.
class LoginViewModel #Inject internal constructor (private val loginRepository: LoginRepository,private val usersRepository: UsersRepository): ViewModel() {
private var _userEmail:MutableLiveData<String>
private var _userPassword:MutableLiveData<String>
private var _userLoginData:MutableLiveData<UserLoginData>
private var allUsers:MutableLiveData<List<Users>>
private var findUser:MutableLiveData<List<Users>>
init{
_userEmail= MutableLiveData()
_userPassword= MutableLiveData()
_userLoginData= MutableLiveData()
allUsers= MutableLiveData()
findUser= MutableLiveData()
}
fun getEmail():LiveData<String>{
return _userEmail
}
fun getPassword():MutableLiveData<String>{
return _userPassword
}
fun userLogin(userEmail:String,userPassword:String):MutableLiveData<UserLoginData>{
_userEmail.postValue(userEmail)
_userPassword.postValue(userPassword)
viewModelScope.launch(Dispatchers.IO) {
var userlogindata:UserLoginData=loginRepository.userLogin(userEmail,userPassword)
_userLoginData.postValue(userlogindata)
}
return _userLoginData
}
fun getAllUsers():MutableLiveData<List<Users>>{
//lateinit var _allUsers:List<Users>
viewModelScope.launch(Dispatchers.IO) {
val _allUsers:List<Users> =usersRepository.getUsers()
allUsers.postValue(_allUsers)
}
return allUsers
}
fun findUser(userEmail:String):MutableLiveData<List<Users>>{
//lateinit var finduser:List<Users>
viewModelScope.launch(Dispatchers.IO) {
val _findUser:List<Users> =usersRepository.findUser(userEmail)
findUser.postValue(_findUser)
}
return findUser
}
}
in an activity i am observing the users list and getting the list into a list variable in the activity. code in the activity:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var loginViewModel: LoginViewModel
lateinit var loginData:UserLoginData
var users:List<Users> = emptyList()
var findUser:List<Users> = emptyList()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
loginViewModel = ViewModelProvider(this).get(LoginViewModel::class.java)
/*observe users list*/
loginViewModel.getAllUsers().observe(this, {It->
users=It
binding.textView.text=It[0].email.toString()
})
loginViewModel.findUser(binding.loginEditTextTextEmailAddressTxt.toString().trim()).observe(this,{it->
findUser=it
})
This program failed if i use data in the users or findUser lists.
Kindly help me to find the best practice in getting the changed data from viewmodel into an activity
ViewModel:
data class User(
var name: String
)
private val _allUsers = MutableLiveData<List<User>>()
private val allUsers: LiveData<List<User>> get() = _allUsers
fun fetchAllUsers(): LiveData<List<User>> {
viewModelScope.launch {
//delay is simulating network request delay
delay(1000)
//listOf is simulating usersRepository.getUsers()
_allUsers.value = listOf(User("name1"), User("name2"), User("name3"))
}
return allUsers
}
Fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.fetchAllUsers().observe(viewLifecycleOwner) { userList ->
userList.forEach {
Log.d("user", it.name)
}
}
You can try this way but I do not prefer returning liveData with function because you have to observe liveData once. You need to be careful observe once.

Struggling to access Spinner outside of my recycler view

I have tried two different ways to access my spinner. Without success thus far.
I want to load the data for each driver as chosen.
To give an idea of my app.
Code for adapter:
class TableViewAdapter(var tripsheetlist: Tripsheetlist) : RecyclerView.Adapter<TableViewAdapter.RowViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RowViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.table_list_item, parent, false)
return RowViewHolder(itemView) }
override fun getItemCount(): Int { return tripsheetlist.videos.size + 1 // one more to add header row
}
override fun onBindViewHolder(holder: RowViewHolder, position: Int) {
val rowPos = holder.adapterPosition
if (rowPos == 0) {
// Header Cells. Main Headings appear here
holder.itemView.apply {
setHeaderBg(txtWOrder)
setHeaderBg(txtDElNote)
setHeaderBg(txtCompany)
// setHeaderBg(txtAddress)
setHeaderBg(txtWeight)
setHeaderBg(txtbutton1)
setHeaderBg(txtbutton2)
setHeaderBg(txttvdone)
txtWOrder.text = "WOrder"
txtDElNote.text = "DElNote"
txtCompany.text = "Company"
// txtAddress.text = "Address"
txtWeight.text = "Weight"
txtbutton1.text = "Delivered"
txtbutton2.text = "Exception"
txttvdone.text = ""
}
} else {
val modal = tripsheetlist.videos[rowPos -1]
holder.itemView.apply {
setContentBg(txtWOrder)
setContentBg(txtDElNote)
setContentBg(txtCompany)
setContentBg(txtWeight)
setContentBg(txtbutton1)
setContentBg(txtbutton2)
setContentBg(txttvdone)
val list : MutableList<String> = ArrayList()
list.add("Deon")
list.add("Leon")
list.add("David")
list.add("Dick")
println(list)
val spinner : Spinner = findViewById(R.id.spnDriver)
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
val item :String = list[p2]
if (item == "David")
{
txtWOrder.text = modal.WOrder.toString()
txtDElNote.text = modal.DElNote.toString()
txtCompany.text = modal.name.toString()
txtWeight.text = modal.id.toString()
}
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
I did it like this as a test for now. As I will get the drivers from my JSON. I don't have access to it yet so that is why the static values.
The problem I am getting now is: findViewById(R.id.spnDriver) must not be null
I first had my spinner class in my main activity and passed it over like this:
val list : MutableList<String> = ArrayList()
list.add("Deon")
list.add("Leon")
list.add("David")
list.add("Dick")
list.add("Jim")
list.add("Harry")
val adapter = ArrayAdapter( this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, list)
val spinner: Spinner = findViewById(R.id.spnDriver)
spinner.adapter = adapter
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
val item :String = list[p2]
Toast.makeText(this#MainActivity, "Driver $item selected", Toast.LENGTH_SHORT).show()
}
override fun onNothingSelected(p0: AdapterView<*>?) {
//empty
}
// insert code that activates data pull of tripsheet for driver= actifavte by method the class/object that activates the data pull. so datapuul(Driver)
}
limitDropDownHeight(spinner)
//drivers end
val btnLoadData: Button = findViewById(R.id.btnLoadData)
// weightsum(tvTotalweight, Tripsheetlist)
// totaldelNotes(tvTotaldelv,Tripsheetlist)
// setData(btnLoadData, Tripsheetlist )
fetchJson(spinner)
}
private fun fetchJson(spinner: Spinner) {
println("Attempting to Fetch JSON")
val url = "https://api.letsbuildthatapp.com/youtube/home_feed"
val request = Request.Builder().url(url).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object: Callback {
override fun onFailure(call: Call, e: IOException) {
println("Failed to execute request") }
override fun onResponse(call: Call, response: Response) {
val body = response.body?.string()
println(body)
val gson = GsonBuilder().create()
val tripsheetlist = gson.fromJson(body, Tripsheetlist::class.java)
runOnUiThread {
recyclerViewTripsheetlist.adapter = TableViewAdapter(tripsheetlist, spinner)
}
}
})
}
In my Adapter class I then called it with : val spinner = spnDriver
This led to a different error: AppCompatSpinner.setOnItemSelectedListener(android.widget.AdapterView$OnItemSelectedListener)' on a null object reference
But seems like it passed the val spinner =spnDriver without a problem.
Thank you for all input and help.
I found a solution. What I did was to keep the spinner inside my MainActivity and then just pass the result of the spinner to the adapter - where I wanted to use it.

lateinit property binding has not been initialized though i did not set it as lateinit

I faced that error when i was trying to update my views with new ViewBinding stuff. I don't define the value as "lateinit" but logccat says "lateinit property binding has not been initialized" why i m taking this ?
Thanks in advance.
The exception is on private val email and password rows.
class MainActivity : AppCompatActivity() {
private lateinit var auth : FirebaseAuth
private lateinit var binding: ActivityMainBinding
private val email = binding.emailText.text.toString()
private val password = binding.passwordText.text.toString()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
auth= FirebaseAuth.getInstance()
val guncelKullanici = auth.currentUser
if (guncelKullanici!= null) {
val intent = Intent(this, haber_akisi::class.java)
startActivity(intent)
finish()
}
}
fun girisYap ( view: View) {
if (email.isNotBlank() && password.isNotBlank()) {
auth.signInWithEmailAndPassword(email,password)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val intent = Intent(this,haber_akisi::class.java)
startActivity(intent)
finish()
}
}.addOnFailureListener { exception ->
Toast.makeText(this,exception.localizedMessage,Toast.LENGTH_LONG).show()
}}else {
Toast.makeText(this,"Lütfen E-mail ve Password alanlarını doldurunuz",Toast.LENGTH_LONG).show()
}
}
fun kayitOl ( view : View) {
if ( email.isNotBlank() && password.isNotBlank() ) {
auth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
val intent = Intent(this, haber_akisi::class.java)
startActivity(intent)
finish()
}
}.addOnFailureListener { exception ->
Toast.makeText(this, exception.localizedMessage, Toast.LENGTH_LONG).show()
}
}else {
Toast.makeText(this,"Lütfen E-mail ve Password alanlarını doldurunuz",Toast.LENGTH_LONG).show()
}
}
}
This is because private val email = binding.emailText.text.toString() is using the "binding" variable before it has been initialized. The error is saying that the "lateinit var binding" has not been initialized yet but you are accessing it on private val email = binding.emailText.text.toString()
Edit: One way to solve this is to make email and password as lateinit vars too. Another way is to not have an email and password class level properties and just access the binding where it's needed like in girisYap() and kayitOl()
You are accessing binding(ActivityMainBinding Identifier) in the next lines, after its declaration.
You need to initialize binding before using it.

Observer pattern is not working in Android MVVM

I am trying to update my view according to my data in my ViewModel, using MVVM
I need in the method onCacheReception to update my map whenever zones is changing
ViewModel
class MainViewModel constructor(application: Application) : AndroidViewModel(application),
CacheListener {
private val instance = Initializer.getInstance(application.applicationContext)
private val _zones = MutableLiveData<List<Zone>>()
val zones: LiveData<List<Zone>>
get() = _zones
init {
CacheDispatcher.addCacheListener(this)
}
override fun onCacheReception() {
val zonesFromDB: List<Zone>? = instance.fetchZonesInDatabase()
_zones.value = zonesFromDB
}
}
MainActivity
class MainActivity : AppCompatActivity(), EasyPermissions.PermissionCallbacks, OnMapReadyCallback {
private val mainViewModel: MainViewModel = ViewModelProvider(this).get(MainViewModel(application)::class.java)
private lateinit var initializer: Initializer
private lateinit var map: GoogleMap
private val REQUEST_CODE_LOCATIONS: Int = 100
private val permissionLocationsRationale: String = "Permissions for Fine & Coarse Locations"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (checkForLocationsPermission()) {
setUp()
mapSetUp()
}
mainViewModel.zones.observe(this, Observer { zones ->
zones.forEach {
Log.i("YES DATA", "Data has been updated")
val latLng = it.lat?.let { it1 -> it.lng?.let { it2 -> LatLng(it1, it2) } }
val markerOptions = latLng?.let { it1 -> MarkerOptions().position(it1) }
map.addMarker(markerOptions)
}
})
}
My Log is never displaying and it doesn't seem while debugging that mainView.zones.observe { } is called when I receive some new data in my ViewModel
In the onCacheReception(), replace:
_zones.value = zonesFromDB
by:
_zones.postValue(zonesFromDB)
in case your onCacheReception() function is called from a worker thread.

LiveData always returns LiveData<Object>?

I'm trying to put a recyclerview which get its data from room. My function getAllHomework returns LiveData<List<Homework>>, but when I tried to set the return value to the recyclerview adapter, it will always return this error
Type Mismatch.
Required: List<Homework>
Found: List<Homework>?
Here's my HomeworkViewModel class which has the function getAllHomework looks like:
class HomeworkViewModel : ViewModel() {
private var matrixNumber: String? = null
private var schoolID: Int = 0
lateinit var listAllHomework: LiveData<List<Homework>>
lateinit var homeworkRepository: HomeworkRepository
fun init(params: Map<String, String>) {
schoolID = Integer.parseInt(params["schoolID"])
homeworkRepository = HomeworkRepository()
listAllHomework = homeworkRepository.getAllHomework(1, "2018")
}
fun getAllHomework(): LiveData<List<Homework>>{
return listAllHomework
}
}
And below is the part in my Homework activity that tries to set the value into recyclerview adapter but will always return the type mismatch error.
class Homework : AppCompatActivity(), LifecycleOwner {
lateinit var linearLayoutManager: LinearLayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.homework)
linearLayoutManager = LinearLayoutManager(this)
rvHomeworks.layoutManager = linearLayoutManager
var adapter = AdapterHomework(this)
rvHomeworks.adapter = adapter
var homeworkViewModel = ViewModelProviders.of(this).get(HomeworkViewModel::class.java)
homeworkViewModel.init(params)
homeworkViewModel.getAllHomework().observe(this, Observer {
allHomework -> adapter.setHomeworkList(allHomework)
})
}
}
The line allHomework -> adapter.setHomeworkList(allHomework) above will show the Type Mismatch error I mentioned above.
Here's how my AdapterHomework looks like:
class AdapterHomework(context: Context): RecyclerView.Adapter<AdapterHomework.ViewHolder>() {
lateinit var homeworkList: List<Homework>
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder{
val v = LayoutInflater.from(parent.context).inflate(R.layout.rv_homeworks, parent, false)
return ViewHolder(v);
}
#JvmName("functionToSetTheHomeworkList")
fun setHomeworkList(myHomeworkList: List<Homework>){
homeworkList = myHomeworkList
notifyDataSetChanged()
}
}
I could not find where in my code did I ever return List<Homework>? instead of List<Homework>.
This has actually nothing to do with Room, but with how LiveData was designed - specifically Observer class:
public interface Observer<T> {
void onChanged(#Nullable T t);
}
as you can see T (in your case List<Homework>) is marked as #Nullable therefore you will always get it's Kotlin equivalent List<Homework>? as a parameter of onChanged in your Observer implementation.
I would recommend changing setHomeworkList to something like this:
fun setHomeworkList(myHomeworkList: List<Homework>?){
if(myHomeworkList != null){
homeworkList = myHomeworkList
notifyDataSetChanged()
}
}
You can also use let function for that like this:
fun setHomeworkList(myHomeworkList: List<Homework>?){
myHomeworkList?.let {
homeworkList = it
notifyDataSetChanged()
}
}