How can I access to data class object from activity? - kotlin

I am trying to access to data class (Content)and I would like to use object(val isSelected: Boolean?)from PictureActivity. However, it causes UninitializedPropertyAccessException: lateinit property content has not been initialized. Do you know how to solve this situation? I used lateinit but I don't even know if using lateinit is the best way to access to data class(Content). If you know other way to access to it, please let me know.
The code is down below.
Content.kt
data class Content(
val id: Int,
val text: String,
val isSelected: Boolean?,
val url: String?
)
PictureActivity.kt
class PictureActivity : BaseActivity() {
private lateinit var binding: PictureActivityBinding
private lateinit var content: Content
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = PictureActivityBinding.inflate(layoutInflater)
setContentView(binding.root)
if(content.isSelected!!){
binding.button1.setOnClickListner{
startContentDownload(content.url!!)
return#setOnClickListener
}
}
private fun startContentDownload(url: String) {
//download image
}
}
}

lateinit keyword in Kotlin gives you an option to initialize it later but make sure you do it before you use.
To check if that variable is initialized or not, you can use below:
if(::content.isInitialized) {
// put your code here
}
In your case you have get data from somewhere(network call maybe) to fill in content data class, and then you will be able to use it.

You need to initialize the content variable first then only you can use it
content = Content(...)

Related

How to read properties as immutable strings in Kotlin

I have been trying to read the read the properties in the Kotlin code. The lateinit var gets the work done but since it is mutable, the value can be changed even after initialisation. I want to read a property from a file and to not worry about it being changed anywhere in the file. I want something like lateinit val which is not present in Kotlin; or you somehow able to add #Value inside by lazy block of code.
I am working with AWS Secret Manager so I am putting the same code here but my doubt is more generic and not specific to AWS.
#Value("\${aws.secretsManager.region}")
private lateinit var region: String
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(Region.of(region))
.build()
}
I tried doing the similar thing with by lazy:
#Value("\${aws.secretsManager.region}")
private lateinit var region: String
private val awsRegion: Region by lazy {
Region.of(region)
}
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(awsRegion)
.build()
}
The above codes are working fine but it would be much cleaner if there's a way to merge these 2 lines:
#Value("\${aws.secretsManager.region}")
private lateinit var region: String
private val awsRegion: Region by lazy {
Region.of(region)
}
In your specific case you can inject property directly into bean method as an argument (method arguments are immutable)
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(
#Value("\${aws.secretsManager.region}") region: String
): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(Region.of(region))
.build()
}
or if you need this property in multiple #Beans you can inject it into constructor of enclosing configuration class
#Confiuration
class SomeConfig(
#Value("\${aws.secretsManager.region}")
private val region: String
) {
#Bean(name = ["secretsManagerClient"])
fun secretsManagerClient(): SecretsManagerClient {
return SecretsManagerClient.builder()
.region(Region.of(region))
.build()
}
}

How to initialize lateinit var with 'by keyword' in kotlin

I want to initialize lateinit variable with 'by'.
How can I?
lateinit var test: int
fun main() {
test by something {} // error!
}
I tried using by in by lazy, and I tried using by in lateinit var, but it didn't work.
You don't need lateinit when using by lazy. Lazy means it'll be initialized the first time it's referenced. lateinit means you manually assign a value some time after construction.
So all you need is
val test by lazy { something() }
fun main() {
println(test) // runs the initializer and prints the value
}
Update:
Or, if you want to initialize an exising lateinit property:
lateinit var test: Type
fun main() {
val someting by other
test = something
}

How to instantiate a AdnroidViewModel in Linear Layout

I am creating a Custom Linear Layout view to represent a custom calender. Ealier I used SQlitehelper to get the data. But now I am migrating to Room DB. But I couldn't instantiate AndroidViewModel and get data.
This is the AndroidViewModel
class AdminExpensesVM(application: Application):AndroidViewModel(application) {
private val repository:AdminExpensesRepo
val readAll :LiveData<List<AdminExpensesData>>
init {
val adminExpensesDB=AdminExpensesDatabase.getInstance(application).adminExpensesDao
repository= AdminExpensesRepo(adminExpensesDB)
readAll=repository.getAllExpenses()
}
}
This is the Custom LinearLayout
class Admin_Calender : LinearLayout {
private lateinit var adminExpensesVM: AdminExpensesVM;
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val view: View = inflater.inflate(R.layout.admin_calender, this)
adminExpensesVM=ViewModelProvider.AndroidViewModelFactory.getInstance(context as Application).create(AdminExpensesVM::class.java)
}
}
But this cannot be done becasue it get a error casting context to application. How can I do this? Is there anyway to get application to instantiate AndroidViewModel or is there any way to do it with only context?
Thank you
I don't really get your question but maybe this can help try to make it scoped with your activity
val adminExpensesVM: AdminExpensesVM by activityViewModels()

What to send as Content

In my application, I save data to Firebase and to local storage using the Room library. With Firebase, everything is clear to me. But with Rom I had questions. I can't figure out what to pass to the class parameter.
PacketsLocalDataSource.kt
class PacketsLocalDataSource(val context: Context) {
lateinit var db: PacketsDatabase
lateinit var dao: PacketDao
fun saveLocal(packet: Packet) {
db = PacketsDatabase.getInstance(context)
dao = db.packetDao()
dao.add(packet)
}
}
And further, in the code below, I want to save the data. But there is an error on the fifth line of the code: No value passed for parameter 'context'. Please let me know what I need to send here.
class PocketScoutContainer {
private val firebaseRealtimeDatabase = Firebase.database
private val packetsRemoteDataSource = PacketsRemoteDataSource(firebaseRealtimeDatabase)
private val packetsLocalDataSource = PacketsLocalDataSource()
val packetsRepository = PacketsRepository(packetsRemoteDataSource, packetsLocalDataSource)
}
You need to pass Context to PacketsLocalDataSource constructor. In turn, the context must be passed when creating an instance of the class PocketScoutContainer. So:
class PocketScoutContainer(context: Context) {
//...
private val packetsLocalDataSource = PacketsLocalDataSource(context)
//...
And when creating PocketScoutContainer instanse in some Activity:
val pocketScoutContainer = PocketScoutContainer(this.applicationContext)
If PocketScoutContainer instantiated somewhere outside an activity or a fragment, you will need to pass Context there.
This may help further: Dependency Injection

how should i access the lateinit property

please help me understand how to properly initialize "pets"
class Person:
class Person(
val height: Int,
val weight: Int,
val name: String,
) {
lateinit var pets: HashSet<Animal>
fun buyPet():Unit{
this.pets.add(Animal((0..100).random(), (0..100).random(), getRandomString((3..12).random())))
}
private fun getRandomString(length: Int) : String {
val allowedChars = ('A'..'Z') + ('a'..'z')
return (1..length)
.map { allowedChars.random() }
.joinToString("")
}
}
class Animal:
data class Animal(
val energy:Int,
val weight:Int,
val name:String) {
}
main:
fun main() {
val person1=Person(187, 85, "Denis")
person1.buyPet()
println(person1.pets)
}
I am getting this error
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property pets has not been initialized
at classes_06.Person.buyPet(Person.kt:35)
Replace
lateinit var pets: HashSet<Animal>
With
val pets = mutableSetOf<Animal>()
In the original code pets is declared, but never initialised, and lateinit is just telling the compiler "do not complain about this, I'll initialise it later". When doing Kotlin, try to avoid using lateinit as much as possible.
This other SO question might give you a bit more info in general terms about what is declaring and initialising a variable.
To complement Agusto’s answer:
Lateinit works in cases on which you can’t define the value of the variable immediately on the object construction, for any reason. Therefore, it expects you to define the value of the lateinit variable at some “late-r” point before you try to do anything else with it.
In your case it seems perfectly fine to define the variable “pets” in construction. So you can remove the lateinit modifier and define it right away as Agusto pointed out.
Or if need lateinit for any reason, you could check if it has been initialized before, and if not, define it in the buyPet() function before performing an “add” to it:
fun buyPet(): Unit{
if(!::pets.isInitialized) {
pets = mutableSetOf<Animal>()
}
this.pets.add(Animal((0..100).random(), (0..100).random(), getRandomString((3..12).random())))
}
See this question for further details about the isInitialized property