Parcelable array passed with kotlin is emitting last item - android

I'm trying to pass an array to my activity, but for some odd reason the last item of the array is passed as null
Here's the code
Helper function to create the intent:
#JvmStatic
fun newTutorProfile(context: Context, webinars: List<Webinar>): Intent {
val tutorActivity = Intent(context, TutorWebinarProfileActivity::class.java)
tutorActivity.putExtra(WEBINARS_ARG, webinars.toTypedArray()) // Array contains two valid elements
return tutorActivity
}
Extracting the array from the intent:
webinars = intent.getParcelableArrayExtra(WEBINARS_ARG).map { it as Webinar } // array contains two elemnts but the second one is now null

Related

How to send multiple items of recyclerview from one activity to another

I have an ArrayList of type Course. Course has id ( string ), name ( string ).
I want to display a list of courses on one screen. Give the user option to select multiple courses which they have completed and send these courses to next activity.
I am able to MultiSelect courses in a RecyclerView. But unable to send the data to another activity.
you can convert the data to JSON string & pass easily between activities using intent bundles...
create extension functions like these
// convert safely from json to class
fun <T> String.fromSafeJson(classOfT: Class<T>): T? {
return try {
Gson().fromJson(this, classOfT)
} catch (e: Exception) {
null
}
}
// convert any object to json
fun Any.toJson(): String {
return Gson().toJson(this)
}
after that you can convert any list of any type using yourList.toJson(), send it via bundle and in next activity get it from bundle and parse using stringName.fromSafeJson(YourListType)
remember to add Gson library... use can use the following
implementation 'com.google.code.gson:gson:2.9.0'

Can't get Extra data via Intent

I'm trying to put extra data (string) in an intent. I use startActivity to get my extra string data on the other side.
But I can't get why it doesn't work! Here's my code :
This is the destination Activity from where I want to parse my extra data -- Recipe Activity :
class RecipeActivity : AppCompatActivity() {
for (k in 0..name_arr.size-1) {
if(temp_arr[position]==name_arr[k]){
//to parse the id number of the selected recipe by the user to the next activity
val i = Intent(baseContext, ResRecActivity::class.java)
i.putExtra("ItemPosition",k)
startActivity(i)
}
}
}
This is the Target Activity for successful parsing of extra data -- ResRecActivity :
class ResRecActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_res_rec)
setSupportActionBar(toolbar)
var posit : String = intent.getStringExtra("ItemPosition") //wanting to get the extra data
//BELOW LINE IS THE ERROR LINE
*readJson(posit.toInt())* //Intending to parse the extra data in Integer datatype to readJson Function
}
}
Error that I am getting is :
E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.testarca/com.example.arca.ResRecActivity}: kotlin.KotlinNullPointerException
Caused by: *kotlin.KotlinNullPointerException*
at com.example.arca.ResRecActivity.onCreate(ResRecActivity.kt:37)
Main Aim :
I just want to parse the Integer "position" (in my case "k") from "RecipeActivity" to "ResRecActivity", for using that integer position in another function named "readJson" as a parameter to the function.
And I am trying to achieve that using Intent property for Handling between the activities.
Note: Also give me a similar solution to parse an ArrayList data from one activity to another using intent.
When you use i.putExtra("ItemPosition",k), you're putting an Int into your extras.
Therefore you must use getIntExtra to retrieve it:
val posit = intent.getIntExtra("ItemPosition", 0)
First
replace this line
val i = Intent(baseContext, ResRecActivity::class.java)
with
val i = Intent(this, ResRecActivity::class.java)
Second
You put Int in intent, so you should use intent.getIntExtra("ItemPosition", -1)
Hope this helps.

Is it possible to store data from two different parcelable data classes in one single intent to start a new Activitiy with it?

after researching a lot, I wasn't able to find the real solution for my problem. The most answers are based on attaching single parcelable object to an intent and starting a new activity from it.
In my case, I use data from two different parcelable classes. I have two different RecyclerView Adapter classes that live in the same activity.
So, when the user clicks on a data set from the first (parent) Adapter class, the View shows all available (child) data sets from the second Adapter class.
I want to save the parent object data set where the user clicked first, as well the child object where the user performed a long click at last. The both objects need to be attached on a same intent and when done, a new activity should be started. The data from the both objects (parent and child) has to be passed in the new acticity.
For the sake of clearness, I'm posting only the relevant parts of the code.
This is the part of the parent adapter class where I need to save the object data to a sort of "global" intent, my code would produce only a local intent instead (that's why it's commented only).
itemView.setOnClickListener {
notifyItemChanged(selectedPos)
selectedPos = adapterPosition
notifyItemChanged(selectedPos)
listener.onHeadItemClicked(weKopf.WENR, p1)
// save parent parcelable object data to "global" intent
// val intent = Intent(context, StorageGoodsActivity::class.java)
// intent.putExtra("weKopf", weKopf)
}
Here is the part of the code where I want to attach the child object data to the same intent and then start the new activity.
itemView.setOnLongClickListener{
notifyItemChanged(selectedPos)
selectedPos = adapterPosition
notifyItemChanged(selectedPos)
listener.onPositionLongClicked(wePos.lfdNr.toInt())
/**
* setting-up for passing objects between activities
*/
val intent = Intent(context, StorageGoodsActivity::class.java)
//intent.putExtra("weKopf", weKopf)
intent.putExtra("wePos", wePos)
context.startActivity(intent)
true
What is the most elegant way to do this? My idea was to store the intent into a shared component, but this intent should live as long as the user clicks the back button to return to the old activity and chooses new parent-child constellation after that. If so, will this created item be thrown away, or it still lives in the shared component?
I'm quite new with Android Studio and Kotlin and I appreciate every help or advice. Thank you so much in advance.
You can have as many extras as you want in an Intent, so long as each gets its own name. The only limitation is a limitation on the total size of an Intent (about 2 MB iirc).
The solution of this problem was sort of small work around. It was impossible to attach the data objects from both adapter classes in the same intent, because the new activity can be started from only one adapter.
In this case, I passed the data object from the first (parent) adapter to a SharedPreferences object, the object was converted with Gson into his JSON representation.
itemView.setOnClickListener {
notifyItemChanged(selectedPos)
selectedPos = adapterPosition
notifyItemChanged(selectedPos)
listener.onHeadItemClicked(weKopf.WENR, p1)
/**
* store object in SharedPreferences
*/
val sharedPreferences:SharedPreferences = context.getSharedPreferences("StoreHeadObj", MODE_PRIVATE)
val editor : SharedPreferences.Editor = sharedPreferences.edit()
val gson = Gson()
val json = gson.toJson(weKopf)
editor.putString("JSONString", json)
editor.commit()
The second (child) object was passed on an intent and the new Activity was started.
itemView.setOnLongClickListener{
notifyItemChanged(selectedPos)
selectedPos = adapterPosition
notifyItemChanged(selectedPos)
listener.onPositionLongClicked(wePos.lfdNr.toInt())
/**
* setting-up for passing objects between activities
*/
val intentPos = Intent(context, StorageGoodsActivity::class.java)
intentPos.putExtra("wePos",wePos)
context.startActivity(intentPos)
true
}
In the new Activity, I'm retrieving the data from both objects. First of all, I'm converting the JSON String to a data object, after that I'm taking the data out of the Intent. So, both of the parcelable data objects are now ready to use in the new Activity.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(getContentView())
/**
* get data from clicked weKopf-Object using Gson, at the end clear SharedPreferences
*/
val sharedPreferences:SharedPreferences = getSharedPreferences("StoreHeadObj", Context.MODE_PRIVATE)
val gson = Gson()
val json:String = sharedPreferences.getString("JSONString", "")
val weKopf:WeKopf = gson.fromJson(json, WeKopf::class.java)
/**
* get data from long-clicked wePos-Object using Intent
*/
val extras = getIntent().getExtras()
val wePos = extras.getParcelable<WePos>("wePos")
}

Populate a list in Kotlin with a for loop

It's been a while that I just started to learn how to develop in Kotlin.
There is this thing that I am working on, I am trying to parse a list into another type of list. Basically they are the same thing but with different names. But when I try to populate the new list with the data that I get from the list given as parameter in the function the list only gets populated with the first object.
Here is my function:
fun convertRoomClass(course: List<Course>) : List<Courses> {
lateinit var list : List<Courses>
course.forEach {
val id = it.pathID
val name = it.pathName
val desc = it.pathDescription
val crs : Courses = Courses(id, name!!, desc!!)
list = listOf(crs)
}
return list
}
The error in your code is that you are making a list in every iteration of the loop. You should make the list first and then add every item from the loop to it!
fun convertRoomClass(courses: List<Course>) : List<AnotherCourseClass> {
val newList = mutableListOf<AnotherCourseClass>()
courses.forEach {
newList += AnotherCourseClass(it.pathID, it.pathName, it.pathDescription)
}
return newList
}
A better solution is to use the map function
fun convertRoomClass(courses: List<Course>) = courses.map {
AnotherCourseClass(it.pathID, it. pathName, it.pathDescription)
}
You might be looking for Kotlin Map
Example:
course.map { Courses(it.pathID, it.pathName,it.pathDescription) }
You're getting the list with only on object, cause the function listOf(crs) returns a list of all objects that are passed as a parameters. Saying the same thing in Java you're doing something like this:
for (course: Courses) {
Course course = new Course(...);
List<Course> list = new ArrayList<>();
list.add(course);
return list;
}
As you can see the it created new list with a single object per iteration.
What you're trying to achieve, can be done with operator map{...} which simply transforms every object in the initial list using code passed inside map and returns list of transformed objects
course.map{ Courses(...) }
Also, I've noticed that you're using the !! operator when creating a Courses object. Probably because the Course can have nullable name, while Courses can't. I'm considering this as a bad practice, cause in this case you're saying
Please throw an Exception if the name is null.
I think that a much better approach is to provide an alternative, like:
val name = course.name ?: "default", saying
Please use name or "default" if the name is null.
or skip objects without name, or any other approach that suits your situation.
You could use MutableList instead of List. That enable you to append new element at the end of your list instead of replace the entire list by doing : list = listOf(crs)
So replace the type of your var lateinit var list : List<Courses> by lateinit var list : MutableList<Courses> then replace list = listOf(crs) by list.add(crs)
Hope it helps and have fun :)

How to launch an Android Activity when multiple pages of the App have Same Activity name in Kotlin?

I have a screen consisting list of Results. Clicking on each of the result takes me to different item but with the same Activity Name(TestResultActivityDetails).
How can I launch each Result as different Activity Launcher?
Is there any option to use index on list items to launch Activity?
val intent = Intent(TestResultActivityDetails::class.java)
startActivity(intent)
Here, TestResultActivityDetails::class.java is the activity name which is shared with all the list of Results.
To pass data to an Activity you need to use Intent extras. Your posted code would become as this:
val intent = Intent(TestResultActivityDetails::class.java).apply {
/* You pass data as extras, with a key and the actual value.
* KEY VALUE */
putExtras("clickedIndex", index)
}
startActivity(intent)
Then, in TestResultActivityDetails you would do:
override fun onCreate(saveInstance: Bundle?) {
...
/* You get your data back from the Intent extras, using previous key.
* KEY FALLBACK if not found */
val index = intent.extras?.getInt("clickedIndex") ?: -1
}

Categories

Resources